nova_vm/ecmascript/builtins/
array_buffer.rs1mod abstract_operations;
8mod data;
9
10use std::collections::hash_map::Entry;
11
12pub(crate) use abstract_operations::*;
13pub(crate) use data::*;
14
15#[cfg(feature = "shared-array-buffer")]
16use super::shared_array_buffer::SharedArrayBuffer;
17#[cfg(feature = "shared-array-buffer")]
18use crate::ecmascript::types::SHARED_ARRAY_BUFFER_DISCRIMINANT;
19use crate::{
20 ecmascript::{
21 Agent, JsResult, ProtoIntrinsics,
22 types::{
23 ARRAY_BUFFER_DISCRIMINANT, InternalMethods, InternalSlots, Object, OrdinaryObject,
24 Value, Viewable, copy_data_block_bytes, create_byte_data_block,
25 },
26 },
27 engine::{Bindable, HeapRootData, NoGcScope, bindable_handle},
28 heap::{
29 ArenaAccess, ArenaAccessMut, BaseIndex, CompactionLists, CreateHeapData, Heap,
30 HeapIndexHandle, HeapMarkAndSweep, HeapSweepWeakReference, WorkQueues, arena_vec_access,
31 },
32};
33
34use ecmascript_atomics::Ordering;
35
36#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
45#[repr(transparent)]
46pub struct ArrayBuffer<'a>(BaseIndex<'a, ArrayBufferHeapData<'static>>);
47array_buffer_handle!(ArrayBuffer);
48arena_vec_access!(ArrayBuffer, 'a, ArrayBufferHeapData, array_buffers);
49
50impl<'ab> ArrayBuffer<'ab> {
51 pub fn new<'gc>(
53 agent: &mut Agent,
54 byte_length: usize,
55 gc: NoGcScope<'gc, '_>,
56 ) -> JsResult<'gc, ArrayBuffer<'gc>> {
57 let data_block = create_byte_data_block(agent, byte_length as u64, gc)?;
58 let block = data_block;
59 Ok(agent
60 .heap
61 .create(ArrayBufferHeapData::new_fixed_length(block))
62 .bind(gc))
63 }
64
65 #[inline]
67 pub fn is_detached(self, agent: &Agent) -> bool {
68 self.get(agent).is_detached()
69 }
70
71 #[inline]
74 pub fn is_resizable(self, agent: &Agent) -> bool {
75 self.get(agent).is_resizable()
76 }
77
78 #[inline]
80 pub fn byte_length(self, agent: &Agent) -> usize {
81 self.get(agent).byte_length()
82 }
83
84 #[inline]
87 pub fn max_byte_length(self, agent: &Agent) -> usize {
88 self.get(agent).max_byte_length()
89 }
90
91 #[inline]
92 pub(crate) fn get_detach_key(self, agent: &Agent) -> Option<DetachKey> {
93 agent.heap.array_buffer_detach_keys.get(&self).copied()
94 }
95
96 #[inline]
100 pub fn set_detach_key(self, agent: &mut Agent, key: DetachKey) {
101 match agent.heap.array_buffer_detach_keys.entry(self.unbind()) {
102 Entry::Occupied(_) => {
103 }
105 Entry::Vacant(e) => {
106 e.insert(key);
108 agent.heap.alloc_counter += core::mem::size_of::<(ArrayBuffer, DetachKey)>();
109 }
110 }
111 }
112
113 pub fn detach<'a>(
115 self,
116 agent: &mut Agent,
117 key: Option<DetachKey>,
118 gc: NoGcScope<'a, '_>,
119 ) -> JsResult<'a, ()> {
120 detach_array_buffer(agent, self, key, gc)
121 }
122
123 pub(crate) fn resize(self, agent: &mut Agent, new_byte_length: usize) {
127 self.get_mut(agent).resize(new_byte_length);
128 }
129
130 #[inline]
139 pub fn as_slice(self, agent: &'ab Agent) -> &'ab [u8] {
140 self.get(agent).get_data_block()
141 }
142
143 #[inline]
153 pub fn as_mut_slice(self, agent: &'ab mut Agent) -> &'ab mut [u8] {
154 self.get_mut(agent).buffer.get_data_block_mut()
155 }
156
157 pub(crate) fn as_viewable_slice<T: Viewable>(
161 self,
162 agent: &'ab Agent,
163 byte_offset: usize,
164 byte_length: Option<usize>,
165 ) -> &'ab [T] {
166 let byte_slice = self.as_slice(agent);
167 let byte_limit = byte_length.map(|byte_length| byte_offset.saturating_add(byte_length));
168 if byte_limit.unwrap_or(byte_offset) > byte_slice.len() {
169 return &[];
170 }
171 let byte_slice = if let Some(byte_limit) = byte_limit {
172 &byte_slice[byte_offset..byte_limit]
173 } else {
174 &byte_slice[byte_offset..]
175 };
176 let (head, slice, _) = unsafe { byte_slice.align_to::<T>() };
180 if !head.is_empty() {
181 panic!("ArrayBuffer is not properly aligned for T");
182 }
183 slice
184 }
185
186 pub(crate) fn as_mut_viewable_slice<T: Viewable>(
190 self,
191 agent: &'ab mut Agent,
192 byte_offset: usize,
193 byte_length: Option<usize>,
194 ) -> &'ab mut [T] {
195 let byte_slice = self.as_mut_slice(agent);
196 let byte_limit = byte_length.map(|byte_length| byte_offset.saturating_add(byte_length));
197 if byte_limit.unwrap_or(byte_offset) > byte_slice.len() {
198 return &mut [];
199 }
200 let byte_slice = if let Some(byte_limit) = byte_limit {
201 &mut byte_slice[byte_offset..byte_limit]
202 } else {
203 &mut byte_slice[byte_offset..]
204 };
205 let (head, slice, _) = unsafe { byte_slice.align_to_mut::<T>() };
209 if !head.is_empty() {
210 panic!("ArrayBuffer is not properly aligned for T");
211 }
212 slice
213 }
214
215 pub(crate) fn copy_array_buffer_data(
219 self,
220 agent: &mut Agent,
221 source: ArrayBuffer,
222 first: usize,
223 count: usize,
224 ) {
225 debug_assert_ne!(self, source);
226 let array_buffers = &mut *agent.heap.array_buffers;
227 let (source_data, target_data) = if self.get_index() > source.get_index() {
228 let (before, after) = array_buffers.split_at_mut(self.get_index());
229 (&before[source.get_index()], &mut after[0])
230 } else {
231 let (before, after) = array_buffers.split_at_mut(source.get_index());
232 (&after[0], &mut before[self.get_index()])
233 };
234 let source_data = source_data.buffer.get_data_block();
235 let target_data = target_data.buffer.get_data_block_mut();
236 copy_data_block_bytes(target_data, 0, source_data, first, count);
237 }
238}
239
240impl<'a> InternalSlots<'a> for ArrayBuffer<'a> {
241 const DEFAULT_PROTOTYPE: ProtoIntrinsics = ProtoIntrinsics::ArrayBuffer;
242
243 #[inline(always)]
244 fn get_backing_object(self, agent: &Agent) -> Option<OrdinaryObject<'static>> {
245 self.get(agent).object_index.unbind()
246 }
247
248 fn set_backing_object(self, agent: &mut Agent, backing_object: OrdinaryObject<'static>) {
249 assert!(
250 self.get_mut(agent)
251 .object_index
252 .replace(backing_object.unbind())
253 .is_none()
254 );
255 }
256}
257
258impl<'a> InternalMethods<'a> for ArrayBuffer<'a> {}
259
260impl HeapMarkAndSweep for ArrayBuffer<'static> {
261 fn mark_values(&self, queues: &mut WorkQueues) {
262 queues.array_buffers.push(*self);
263 }
264
265 fn sweep_values(&mut self, compactions: &CompactionLists) {
266 compactions.array_buffers.shift_index(&mut self.0);
267 }
268}
269
270impl HeapSweepWeakReference for ArrayBuffer<'static> {
271 fn sweep_weak_reference(self, compactions: &CompactionLists) -> Option<Self> {
272 compactions.array_buffers.shift_weak_index(self.0).map(Self)
273 }
274}
275
276impl<'a> CreateHeapData<ArrayBufferHeapData<'a>, ArrayBuffer<'a>> for Heap {
277 fn create(&mut self, data: ArrayBufferHeapData<'a>) -> ArrayBuffer<'a> {
278 self.array_buffers.push(data.unbind());
279 self.alloc_counter += core::mem::size_of::<ArrayBufferHeapData<'static>>();
280 ArrayBuffer(BaseIndex::last(&self.array_buffers))
281 }
282}
283
284#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
291#[repr(u8)]
292pub enum AnyArrayBuffer<'a> {
293 ArrayBuffer(ArrayBuffer<'a>) = ARRAY_BUFFER_DISCRIMINANT,
295 #[cfg(feature = "shared-array-buffer")]
296 SharedArrayBuffer(SharedArrayBuffer<'a>) = SHARED_ARRAY_BUFFER_DISCRIMINANT,
298}
299bindable_handle!(AnyArrayBuffer);
300
301impl<'ab> AnyArrayBuffer<'ab> {
302 #[inline(always)]
304 pub fn is_shared(self) -> bool {
305 match self {
306 Self::ArrayBuffer(_) => false,
307 #[cfg(feature = "shared-array-buffer")]
308 Self::SharedArrayBuffer(_) => true,
309 }
310 }
311
312 #[inline(always)]
314 pub fn is_detached(self, agent: &Agent) -> bool {
315 match self {
316 Self::ArrayBuffer(ta) => ta.is_detached(agent),
317 #[cfg(feature = "shared-array-buffer")]
318 Self::SharedArrayBuffer(_) => false,
319 }
320 }
321
322 #[inline(always)]
324 pub fn is_resizable(self, agent: &Agent) -> bool {
325 match self {
326 Self::ArrayBuffer(ta) => ta.is_resizable(agent),
327 #[cfg(feature = "shared-array-buffer")]
328 Self::SharedArrayBuffer(sta) => sta.is_growable(agent),
329 }
330 }
331
332 #[inline(always)]
334 pub fn byte_length(self, agent: &Agent, order: Ordering) -> usize {
335 #[cfg(not(feature = "shared-array-buffer"))]
336 let _ = order;
337 match self {
338 Self::ArrayBuffer(ta) => ta.byte_length(agent),
339 #[cfg(feature = "shared-array-buffer")]
340 Self::SharedArrayBuffer(sta) => sta.byte_length(agent, order),
341 }
342 }
343
344 #[inline(always)]
346 pub fn max_byte_length(self, agent: &Agent) -> usize {
347 match self {
348 Self::ArrayBuffer(ta) => ta.max_byte_length(agent),
349 #[cfg(feature = "shared-array-buffer")]
350 Self::SharedArrayBuffer(sta) => sta.max_byte_length(agent),
351 }
352 }
353}
354
355impl<'a> From<AnyArrayBuffer<'a>> for Object<'a> {
356 #[inline(always)]
357 fn from(value: AnyArrayBuffer<'a>) -> Self {
358 match value {
359 AnyArrayBuffer::ArrayBuffer(dv) => Self::ArrayBuffer(dv),
360 #[cfg(feature = "shared-array-buffer")]
361 AnyArrayBuffer::SharedArrayBuffer(sdv) => Self::SharedArrayBuffer(sdv),
362 }
363 }
364}
365
366impl<'a> From<AnyArrayBuffer<'a>> for Value<'a> {
367 #[inline(always)]
368 fn from(value: AnyArrayBuffer<'a>) -> Self {
369 match value {
370 AnyArrayBuffer::ArrayBuffer(dv) => Self::ArrayBuffer(dv),
371 #[cfg(feature = "shared-array-buffer")]
372 AnyArrayBuffer::SharedArrayBuffer(sdv) => Self::SharedArrayBuffer(sdv),
373 }
374 }
375}
376
377impl<'a> From<AnyArrayBuffer<'a>> for HeapRootData {
378 #[inline(always)]
379 fn from(value: AnyArrayBuffer<'a>) -> Self {
380 match value {
381 AnyArrayBuffer::ArrayBuffer(dv) => Self::from(dv),
382 #[cfg(feature = "shared-array-buffer")]
383 AnyArrayBuffer::SharedArrayBuffer(sdv) => Self::from(sdv),
384 }
385 }
386}
387
388impl<'a> TryFrom<Object<'a>> for AnyArrayBuffer<'a> {
389 type Error = ();
390
391 fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
392 match value {
393 Object::ArrayBuffer(ab) => Ok(Self::ArrayBuffer(ab)),
394 #[cfg(feature = "shared-array-buffer")]
395 Object::SharedArrayBuffer(sab) => Ok(Self::SharedArrayBuffer(sab)),
396 _ => Err(()),
397 }
398 }
399}
400
401impl<'a> TryFrom<Value<'a>> for AnyArrayBuffer<'a> {
402 type Error = ();
403
404 fn try_from(value: Value<'a>) -> Result<Self, Self::Error> {
405 match value {
406 Value::ArrayBuffer(ab) => Ok(Self::ArrayBuffer(ab)),
407 #[cfg(feature = "shared-array-buffer")]
408 Value::SharedArrayBuffer(sab) => Ok(Self::SharedArrayBuffer(sab)),
409 _ => Err(()),
410 }
411 }
412}
413
414impl TryFrom<HeapRootData> for AnyArrayBuffer<'_> {
415 type Error = ();
416
417 #[inline]
418 fn try_from(value: HeapRootData) -> Result<Self, Self::Error> {
419 match value {
420 HeapRootData::ArrayBuffer(dv) => Ok(AnyArrayBuffer::ArrayBuffer(dv)),
421 #[cfg(feature = "shared-array-buffer")]
422 HeapRootData::SharedArrayBuffer(sdv) => Ok(AnyArrayBuffer::SharedArrayBuffer(sdv)),
423 _ => Err(()),
424 }
425 }
426}
427
428macro_rules! array_buffer_handle {
429 ($name: ident) => {
430 crate::ecmascript::types::object_handle!($name);
431
432 impl<'a> From<$name<'a>> for crate::ecmascript::builtins::array_buffer::AnyArrayBuffer<'a> {
433 fn from(value: $name<'a>) -> Self {
434 Self::$name(value)
435 }
436 }
437
438 impl<'a> TryFrom<crate::ecmascript::builtins::array_buffer::AnyArrayBuffer<'a>>
439 for $name<'a>
440 {
441 type Error = ();
442
443 fn try_from(
444 value: crate::ecmascript::builtins::array_buffer::AnyArrayBuffer<'a>,
445 ) -> Result<Self, Self::Error> {
446 match value {
447 crate::ecmascript::builtins::array_buffer::AnyArrayBuffer::$name(data) => {
448 Ok(data)
449 }
450 _ => Err(()),
451 }
452 }
453 }
454 };
455}
456pub(crate) use array_buffer_handle;