Skip to main content

pipa/builtins/
typedarray.rs

1use crate::host::HostFunction;
2use crate::object::object::{JSObject, ObjectType, TypedArrayKind};
3use crate::runtime::context::JSContext;
4use crate::value::JSValue;
5
6#[derive(Debug)]
7pub struct ArrayBufferData {
8    pub data: Vec<u8>,
9}
10
11fn create_array_buffer(ctx: &mut JSContext, length: usize) -> JSValue {
12    let mut obj = JSObject::new_typed(ObjectType::ArrayBuffer);
13    let data = ArrayBufferData {
14        data: vec![0; length],
15    };
16
17    obj.set(ctx.intern("byteLength"), JSValue::new_int(length as i64));
18
19    let data_ptr = Box::into_raw(Box::new(data));
20    obj.set_array_buffer_data(data_ptr as usize);
21    let ptr = Box::into_raw(Box::new(obj)) as usize;
22    JSValue::new_object(ptr)
23}
24
25fn get_array_buffer_data(obj: &JSObject) -> Option<&mut ArrayBufferData> {
26    obj.get_array_buffer_data()
27        .map(|ptr| unsafe { &mut *(ptr as *mut ArrayBufferData) })
28}
29
30fn create_typed_array(
31    ctx: &mut JSContext,
32    kind: TypedArrayKind,
33    buffer: JSValue,
34    byte_offset: usize,
35    length: Option<usize>,
36) -> Result<JSValue, String> {
37    if !buffer.is_object() {
38        return Err("TypedArray constructor requires ArrayBuffer".to_string());
39    }
40
41    let buffer_obj = buffer.as_object();
42    let buffer_data = get_array_buffer_data(&buffer_obj).ok_or("Invalid ArrayBuffer")?;
43
44    let bytes_per_element = kind.bytes_per_element();
45
46    if byte_offset % bytes_per_element != 0 {
47        return Err(format!(
48            "byteOffset must be a multiple of {}",
49            bytes_per_element
50        ));
51    }
52
53    let byte_length = buffer_data.data.len();
54    let remaining_bytes = byte_length.saturating_sub(byte_offset);
55
56    let element_length = match length {
57        Some(len) => len,
58        None => remaining_bytes / bytes_per_element,
59    };
60
61    let required_bytes = byte_offset + element_length * bytes_per_element;
62    if required_bytes > byte_length {
63        return Err("TypedArray extends beyond ArrayBuffer bounds".to_string());
64    }
65
66    let mut obj = JSObject::new_typed(ObjectType::TypedArray);
67    obj.set_typed_array_kind(kind);
68    if let Some(ta_proto_ptr) = ctx.get_typed_array_prototype() {
69        obj.prototype = Some(ta_proto_ptr);
70    }
71
72    obj.set(ctx.intern("buffer"), buffer);
73    obj.set(
74        ctx.intern("byteOffset"),
75        JSValue::new_int(byte_offset as i64),
76    );
77    obj.set(
78        ctx.intern("byteLength"),
79        JSValue::new_int((element_length * bytes_per_element) as i64),
80    );
81    obj.set(
82        ctx.intern("length"),
83        JSValue::new_int(element_length as i64),
84    );
85
86    let ptr = Box::into_raw(Box::new(obj)) as usize;
87    Ok(JSValue::new_object(ptr))
88}
89
90fn typed_array_from_args(
91    ctx: &mut JSContext,
92    kind: TypedArrayKind,
93    args: &[JSValue],
94) -> Result<JSValue, String> {
95    if args.is_empty() {
96        let buffer = create_array_buffer(ctx, 0);
97        return create_typed_array(ctx, kind, buffer, 0, Some(0));
98    }
99
100    let first_arg = &args[0];
101
102    if first_arg.is_object() {
103        let obj = first_arg.as_object();
104
105        if obj.obj_type() == ObjectType::ArrayBuffer {
106            let byte_offset = if args.len() > 1 {
107                args[1].get_int() as usize
108            } else {
109                0
110            };
111            let length = if args.len() > 2 {
112                Some(args[2].get_int() as usize)
113            } else {
114                None
115            };
116            return create_typed_array(ctx, kind, *first_arg, byte_offset, length);
117        }
118
119        if obj.obj_type() == ObjectType::TypedArray {
120            let src_len = obj
121                .get(ctx.intern("length"))
122                .map(|v| v.get_int() as usize)
123                .unwrap_or(0);
124
125            let bytes_per_element = kind.bytes_per_element();
126            let buffer = create_array_buffer(ctx, src_len * bytes_per_element);
127            let result = create_typed_array(ctx, kind, buffer, 0, Some(src_len))?;
128
129            return Ok(result);
130        }
131
132        let len = obj.get(ctx.common_atoms.length)
133            .map(|v| v.get_int() as usize)
134            .unwrap_or(0);
135
136        let bytes_per_element = kind.bytes_per_element();
137        let buffer = create_array_buffer(ctx, len * bytes_per_element);
138        let result = create_typed_array(ctx, kind, buffer, 0, Some(len))?;
139
140        if result.is_object() {
141            let mut result_obj = result.as_object_mut();
142            for i in 0..len {
143                let val = if obj.is_array() && obj.is_dense_array() {
144                    let ptr = obj as *const JSObject as usize;
145                    let arr = unsafe { &*(ptr as *const crate::object::array_obj::JSArrayObject) };
146                    arr.get(i)
147                } else {
148                    obj.get_indexed(i).or_else(|| {
149                        let key = ctx.int_atom_mut(i);
150                        obj.get(key)
151                    })
152                };
153                if let Some(v) = val {
154                    ta_set_element(ctx, &mut result_obj, i, v);
155                }
156            }
157        }
158
159        return Ok(result);
160    }
161
162    if first_arg.is_int() || first_arg.is_float() {
163        let length = first_arg.get_int() as usize;
164        let bytes_per_element = kind.bytes_per_element();
165        let buffer = create_array_buffer(ctx, length * bytes_per_element);
166        return create_typed_array(ctx, kind, buffer, 0, Some(length));
167    }
168
169    Err("Invalid TypedArray constructor argument".to_string())
170}
171
172fn array_buffer_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
173    let length = if args.is_empty() {
174        0
175    } else {
176        args[0].get_int() as usize
177    };
178    create_array_buffer(ctx, length)
179}
180
181fn data_view_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
182    if args.is_empty() || !args[0].is_object() {
183        return JSValue::undefined();
184    }
185
186    let buffer = &args[0];
187    let byte_offset = if args.len() > 1 {
188        args[1].get_int() as usize
189    } else {
190        0
191    };
192
193    let mut obj = JSObject::new_typed(ObjectType::DataView);
194    if let Some(dv_proto_ptr) = ctx.get_dataview_prototype() {
195        obj.prototype = Some(dv_proto_ptr);
196    }
197    obj.set(ctx.intern("buffer"), *buffer);
198    obj.set(
199        ctx.intern("byteOffset"),
200        JSValue::new_int(byte_offset as i64),
201    );
202
203    if let Some(buffer_obj) = if buffer.is_object() {
204        Some(buffer.as_object())
205    } else {
206        None
207    } {
208        if let Some(data) = get_array_buffer_data(&buffer_obj) {
209            let byte_length = if args.len() > 2 {
210                args[2].get_int() as usize
211            } else {
212                data.data.len().saturating_sub(byte_offset)
213            };
214            obj.set(
215                ctx.intern("byteLength"),
216                JSValue::new_int(byte_length as i64),
217            );
218        }
219    }
220
221    let ptr = Box::into_raw(Box::new(obj)) as usize;
222    JSValue::new_object(ptr)
223}
224
225fn typedarray_from(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
226    let source = args.get(1).copied().unwrap_or(JSValue::undefined());
227    let map_fn = args.get(2).copied().filter(|v| v.is_function());
228
229    if source.is_null_or_undefined() {
230        throw_type_error(ctx, "TypedArray.from requires an array-like object or iterable");
231        return JSValue::undefined();
232    }
233
234    let mut items: Vec<JSValue> = Vec::new();
235
236    if source.is_string() {
237        let chars: Vec<char> = {
238            let s = ctx.get_atom_str(source.get_atom());
239            s.chars().collect()
240        };
241        for ch in chars {
242            items.push(JSValue::new_string(ctx.intern(&ch.to_string())));
243        }
244    } else if source.is_object() {
245        let obj = source.as_object();
246
247        let sym_iter_atom = crate::builtins::symbol::get_symbol_iterator_atom(ctx);
248        if let Some(iter_val) = obj.get(sym_iter_atom) {
249            if iter_val.is_function() {
250                match crate::builtins::array::call_callback_with_this(ctx, iter_val, source, &[]) {
251                    Ok(iterator) => {
252                        if iterator.is_object() {
253                            let iter_obj = iterator.as_object();
254                            let next_fn = iter_obj.get(ctx.intern("next"));
255                            if let Some(next_val) = next_fn {
256                                if next_val.is_function() {
257                                    loop {
258                                        match crate::builtins::array::call_callback_with_this(ctx, next_val, iterator, &[]) {
259                                            Ok(result) => {
260                                                if result.is_object() {
261                                                    let robj = result.as_object();
262                                                    let done = robj
263                                                        .get(ctx.intern("done"))
264                                                        .map(|v| v.is_truthy())
265                                                        .unwrap_or(false);
266                                                    if done {
267                                                        break;
268                                                    }
269                                                    let value = robj
270                                                        .get(ctx.intern("value"))
271                                                        .unwrap_or(JSValue::undefined());
272                                                    if let Some(mf) = map_fn {
273                                                        match crate::builtins::array::call_callback(ctx, mf, &[value, JSValue::new_int(items.len() as i64), source]) {
274                                                            Ok(v) => items.push(v),
275                                                            Err(_) => return JSValue::undefined(),
276                                                        }
277                                                    } else {
278                                                        items.push(value);
279                                                    }
280                                                } else {
281                                                    break;
282                                                }
283                                            }
284                                            Err(_) => return JSValue::undefined(),
285                                        }
286                                    }
287                                }
288                            }
289                        }
290                    }
291                    Err(_) => return JSValue::undefined(),
292                }
293            }
294        } else {
295            let len = obj.get(ctx.common_atoms.length)
296                .map(|v| {
297                    if v.is_string() {
298                        let s = ctx.get_atom_str(v.get_atom());
299                        let n: f64 = s.parse().unwrap_or(f64::NAN);
300                        if n.is_nan() || n <= 0.0 { 0usize }
301                        else if n.is_infinite() { usize::MAX }
302                        else { n as usize }
303                    } else {
304                        crate::builtins::string::js_to_length(&v) as usize
305                    }
306                })
307                .unwrap_or(0);
308
309            for i in 0..len {
310                let val = crate::builtins::array::array_get(obj, i, ctx)
311                    .unwrap_or(JSValue::undefined());
312                if let Some(mf) = map_fn {
313                    match crate::builtins::array::call_callback(ctx, mf, &[val, JSValue::new_int(i as i64), source]) {
314                        Ok(v) => items.push(v),
315                        Err(_) => return JSValue::undefined(),
316                    }
317                } else {
318                    items.push(val);
319                }
320            }
321        }
322    }
323
324    let len = items.len();
325    let buffer = create_array_buffer(ctx, len * 8);
326    match create_typed_array(ctx, TypedArrayKind::Float64, buffer, 0, Some(len)) {
327        Ok(result) => {
328            if result.is_object() {
329                let mut result_obj = result.as_object_mut();
330                for (i, &val) in items.iter().enumerate() {
331                    ta_set_element(ctx, &mut result_obj, i, val);
332                }
333            }
334            result
335        }
336        Err(e) => {
337            throw_type_error(ctx, &e);
338            JSValue::undefined()
339        }
340    }
341}
342
343fn typedarray_of(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
344    let items: Vec<JSValue> = args[1..].to_vec();
345    let len = items.len();
346    let buffer = create_array_buffer(ctx, len * 8);
347    match create_typed_array(ctx, TypedArrayKind::Float64, buffer, 0, Some(len)) {
348        Ok(result) => {
349            if result.is_object() {
350                let mut result_obj = result.as_object_mut();
351                for (i, &val) in items.iter().enumerate() {
352                    ta_set_element(ctx, &mut result_obj, i, val);
353                }
354            }
355            result
356        }
357        Err(e) => {
358            throw_type_error(ctx, &e);
359            JSValue::undefined()
360        }
361    }
362}
363
364fn typedarray_species_get(_ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
365    if let Some(this_val) = args.first() {
366        *this_val
367    } else {
368        JSValue::undefined()
369    }
370}
371
372fn int8_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
373    typed_array_from_args(ctx, TypedArrayKind::Int8, args).unwrap_or(JSValue::undefined())
374}
375
376fn uint8_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
377    typed_array_from_args(ctx, TypedArrayKind::Uint8, args).unwrap_or(JSValue::undefined())
378}
379
380fn uint8_clamped_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
381    typed_array_from_args(ctx, TypedArrayKind::Uint8Clamped, args).unwrap_or(JSValue::undefined())
382}
383
384fn int16_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
385    typed_array_from_args(ctx, TypedArrayKind::Int16, args).unwrap_or(JSValue::undefined())
386}
387
388fn uint16_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
389    typed_array_from_args(ctx, TypedArrayKind::Uint16, args).unwrap_or(JSValue::undefined())
390}
391
392fn int32_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
393    typed_array_from_args(ctx, TypedArrayKind::Int32, args).unwrap_or(JSValue::undefined())
394}
395
396fn uint32_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
397    typed_array_from_args(ctx, TypedArrayKind::Uint32, args).unwrap_or(JSValue::undefined())
398}
399
400fn float32_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
401    typed_array_from_args(ctx, TypedArrayKind::Float32, args).unwrap_or(JSValue::undefined())
402}
403
404fn float64_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
405    typed_array_from_args(ctx, TypedArrayKind::Float64, args).unwrap_or(JSValue::undefined())
406}
407
408fn bigint64_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
409    typed_array_from_args(ctx, TypedArrayKind::BigInt64, args).unwrap_or(JSValue::undefined())
410}
411
412fn biguint64_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
413    typed_array_from_args(ctx, TypedArrayKind::BigUint64, args).unwrap_or(JSValue::undefined())
414}
415
416fn typed_array_get_buffer(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
417    if args.is_empty() || !args[0].is_object() {
418        return JSValue::undefined();
419    }
420    let obj = args[0].as_object();
421    obj.get(ctx.intern("buffer"))
422        .unwrap_or(JSValue::undefined())
423}
424
425fn typed_array_get_byte_length(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
426    if args.is_empty() || !args[0].is_object() {
427        return JSValue::undefined();
428    }
429    let obj = args[0].as_object();
430    obj.get(ctx.intern("byteLength"))
431        .unwrap_or(JSValue::undefined())
432}
433
434fn typed_array_get_byte_offset(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
435    if args.is_empty() || !args[0].is_object() {
436        return JSValue::undefined();
437    }
438    let obj = args[0].as_object();
439    obj.get(ctx.intern("byteOffset"))
440        .unwrap_or(JSValue::undefined())
441}
442
443fn typed_array_get_length(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
444    if args.is_empty() || !args[0].is_object() {
445        return JSValue::undefined();
446    }
447    let obj = args[0].as_object();
448    obj.get(ctx.intern("length"))
449        .unwrap_or(JSValue::undefined())
450}
451
452fn array_buffer_get_byte_length(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
453    if args.is_empty() || !args[0].is_object() {
454        return JSValue::undefined();
455    }
456    let obj = args[0].as_object();
457    obj.get(ctx.intern("byteLength"))
458        .unwrap_or(JSValue::undefined())
459}
460
461fn data_view_get_buffer(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
462    if !dv_check_this(args, ctx) {
463        return JSValue::undefined();
464    }
465    let obj = args[0].as_object();
466    obj.get(ctx.intern("buffer"))
467        .unwrap_or(JSValue::undefined())
468}
469
470fn data_view_get_byte_length(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
471    if !dv_check_this(args, ctx) {
472        return JSValue::undefined();
473    }
474    let obj = args[0].as_object();
475    obj.get(ctx.intern("byteLength"))
476        .unwrap_or(JSValue::undefined())
477}
478
479fn data_view_get_byte_offset(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
480    if !dv_check_this(args, ctx) {
481        return JSValue::undefined();
482    }
483    let obj = args[0].as_object();
484    obj.get(ctx.intern("byteOffset"))
485        .unwrap_or(JSValue::undefined())
486}
487
488fn data_view_get_int8(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
489    match dv_read_bytes_checked(args, ctx, 1) {
490        Some(b) => JSValue::new_int(b[0] as i8 as i64),
491        None => JSValue::undefined(),
492    }
493}
494
495fn data_view_get_uint8(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
496    match dv_read_bytes_checked(args, ctx, 1) {
497        Some(b) => JSValue::new_int(b[0] as i64),
498        None => JSValue::undefined(),
499    }
500}
501
502fn f16_to_f32(val: u16) -> f32 {
503    let sign = (val >> 15) as u32;
504    let exp = (val >> 10) & 0x1F;
505    let mantissa = val & 0x3FF;
506
507    if exp == 0 {
508        if mantissa == 0 {
509            f32::from_bits(sign << 31)
510        } else {
511            let mut m = mantissa as u32;
512            let mut e = 1;
513            while (m & 0x400) == 0 {
514                m <<= 1;
515                e -= 1;
516            }
517            m &= 0x3FF;
518            f32::from_bits((sign << 31) | (((112 + e) as u32) << 23) | (m << 13))
519        }
520    } else if exp == 31 {
521        f32::from_bits((sign << 31) | 0x7F800000 | ((mantissa as u32) << 13))
522    } else {
523        f32::from_bits((sign << 31) | (((exp as u32) + 112) << 23) | ((mantissa as u32) << 13))
524    }
525}
526
527fn f32_to_f16(val: f32) -> u16 {
528    let bits = val.to_bits();
529    let sign = (bits >> 16) as u16;
530    let exp = ((bits >> 23) & 0xFF) as i32;
531    let mantissa = bits & 0x7FFFFF;
532
533    if exp == 0xFF {
534        return sign | 0x7C00 | if mantissa != 0 { 0x200 } else { 0 };
535    }
536    if exp == 0 {
537        return sign | (mantissa >> 13) as u16;
538    }
539    let new_exp = exp - 127 + 15;
540    if new_exp >= 31 {
541        return sign | 0x7C00;
542    }
543    if new_exp <= 0 {
544        return sign | (mantissa >> (14 - new_exp)) as u16;
545    }
546    sign | ((new_exp as u16) << 10) | ((mantissa >> 13) as u16)
547}
548
549fn dv_extract_buffer_info(args: &[JSValue], ctx: &mut JSContext) -> Option<(Vec<u8>, usize)> {
550    if args.is_empty() || !args[0].is_object() {
551        return None;
552    }
553    let view_obj = args[0].as_object();
554    let byte_offset = view_obj
555        .get(ctx.intern("byteOffset"))
556        .map(|v| v.to_number() as usize)
557        .unwrap_or(0);
558    let buffer = view_obj.get(ctx.intern("buffer"))?;
559    if !buffer.is_object() { return None; }
560    let buf_obj = buffer.as_object();
561    let data = get_array_buffer_data(&buf_obj)?;
562    Some((data.data.clone(), byte_offset))
563}
564
565fn throw_type_error(ctx: &mut JSContext, msg: &str) {
566    let mut err = JSObject::new();
567    if let Some(proto_ptr) = ctx.get_type_error_prototype() {
568        err.prototype = Some(proto_ptr);
569    }
570    err.set(ctx.common_atoms.name, JSValue::new_string(ctx.intern("TypeError")));
571    err.set(ctx.common_atoms.message, JSValue::new_string(ctx.intern(msg)));
572    let ptr = Box::into_raw(Box::new(err)) as usize;
573    ctx.runtime_mut().gc_heap_mut().track(ptr);
574    ctx.pending_exception = Some(JSValue::new_object(ptr));
575}
576
577fn throw_range_error(ctx: &mut JSContext, msg: &str) {
578    let mut err = JSObject::new();
579    if let Some(proto_ptr) = ctx.get_range_error_prototype() {
580        err.prototype = Some(proto_ptr);
581    }
582    err.set(ctx.common_atoms.name, JSValue::new_string(ctx.intern("RangeError")));
583    err.set(ctx.common_atoms.message, JSValue::new_string(ctx.intern(msg)));
584    let ptr = Box::into_raw(Box::new(err)) as usize;
585    ctx.runtime_mut().gc_heap_mut().track(ptr);
586    ctx.pending_exception = Some(JSValue::new_object(ptr));
587}
588
589fn dv_check_this(args: &[JSValue], ctx: &mut JSContext) -> bool {
590    if args.is_empty() || !args[0].is_object() {
591        throw_type_error(ctx, "Method called on incompatible receiver");
592        return false;
593    }
594    let view_obj = args[0].as_object();
595    if view_obj.obj_type() != crate::object::object::ObjectType::DataView {
596        throw_type_error(ctx, "Method called on incompatible receiver");
597        return false;
598    }
599    true
600}
601
602fn dv_to_index(val: f64) -> Option<usize> {
603    if val.is_nan() || val < 0.0 || !val.is_finite() {
604        return None;
605    }
606    Some(val as usize)
607}
608
609fn dv_read_bytes_checked(args: &[JSValue], ctx: &mut JSContext, n: usize) -> Option<[u8; 8]> {
610    if !dv_check_this(args, ctx) { return None; }
611    if args.len() < 2 { return None; }
612    let raw_offset = args[1].to_number();
613    let offset = match dv_to_index(raw_offset) {
614        Some(o) => o,
615        None => {
616            throw_range_error(ctx, &format!("Offset is out of range: {}", raw_offset));
617            return None;
618        }
619    };
620    let little_endian = args.get(2).map_or(false, |v| v.is_truthy());
621    let (data, byte_offset) = dv_extract_buffer_info(args, ctx)?;
622    let idx = match byte_offset.checked_add(offset) {
623        Some(i) => i,
624        None => {
625            throw_range_error(ctx, "Offset is out of range");
626            return None;
627        }
628    };
629    if idx + n > data.len() {
630        throw_range_error(ctx, "Offset is out of range for DataView");
631        return None;
632    }
633    let mut buf = [0u8; 8];
634    buf[..n].copy_from_slice(&data[idx..idx+n]);
635    if little_endian {
636        buf[..n].reverse();
637    }
638    Some(buf)
639}
640
641fn dv_write_bytes_checked(args: &[JSValue], ctx: &mut JSContext, bytes: &[u8]) -> bool {
642    if !dv_check_this(args, ctx) { return false; }
643    if args.len() < 3 {
644        throw_type_error(ctx, "Not enough arguments");
645        return false;
646    }
647    let raw_offset = args[1].to_number();
648    let offset = match dv_to_index(raw_offset) {
649        Some(o) => o,
650        None => {
651            throw_range_error(ctx, &format!("Offset is out of range: {}", raw_offset));
652            return false;
653        }
654    };
655    let little_endian = args.get(3).map_or(false, |v| v.is_truthy());
656    let view_obj = args[0].as_object();
657    let byte_offset = view_obj
658        .get(ctx.intern("byteOffset"))
659        .map(|v| v.to_number() as usize)
660        .unwrap_or(0);
661    let buffer = match view_obj.get(ctx.intern("buffer")) {
662        Some(b) => b,
663        None => {
664            throw_type_error(ctx, "Detached buffer");
665            return false;
666        }
667    };
668    if !buffer.is_object() {
669        throw_type_error(ctx, "Detached buffer");
670        return false;
671    }
672    let buf_obj = buffer.as_object();
673    let data = match get_array_buffer_data(&buf_obj) {
674        Some(d) => d,
675        None => {
676            throw_type_error(ctx, "Detached buffer");
677            return false;
678        }
679    };
680    let idx = match byte_offset.checked_add(offset) {
681        Some(i) => i,
682        None => {
683            throw_range_error(ctx, "Offset is out of range");
684            return false;
685        }
686    };
687    if idx + bytes.len() > data.data.len() {
688        throw_range_error(ctx, "Offset is out of range for DataView");
689        return false;
690    }
691    if little_endian {
692        let mut rev = bytes.to_vec();
693        rev.reverse();
694        data.data[idx..idx+bytes.len()].copy_from_slice(&rev);
695    } else {
696        data.data[idx..idx+bytes.len()].copy_from_slice(bytes);
697    }
698    true
699}
700
701fn data_view_set_int8(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
702    if args.len() < 3 { return JSValue::undefined(); }
703    let val = args[2].get_int() as i8 as u8;
704    let bytes = [val];
705    dv_write_bytes_checked(args, ctx, &bytes);
706    JSValue::undefined()
707}
708
709fn data_view_set_uint8(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
710    if args.len() < 3 { return JSValue::undefined(); }
711    let val = args[2].get_int() as u8;
712    let bytes = [val];
713    dv_write_bytes_checked(args, ctx, &bytes);
714    JSValue::undefined()
715}
716
717fn data_view_set_int16(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
718    if args.len() < 3 { return JSValue::undefined(); }
719    let val = args[2].to_number() as i16;
720    let bytes = val.to_be_bytes();
721    dv_write_bytes_checked(args, ctx, &bytes);
722    JSValue::undefined()
723}
724
725fn data_view_set_uint16(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
726    if args.len() < 3 { return JSValue::undefined(); }
727    let val = args[2].to_number() as u16;
728    let bytes = val.to_be_bytes();
729    dv_write_bytes_checked(args, ctx, &bytes);
730    JSValue::undefined()
731}
732
733fn data_view_set_int32(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
734    if args.len() < 3 { return JSValue::undefined(); }
735    let val = args[2].to_number() as i32;
736    let bytes = val.to_be_bytes();
737    dv_write_bytes_checked(args, ctx, &bytes);
738    JSValue::undefined()
739}
740
741fn data_view_set_uint32(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
742    if args.len() < 3 { return JSValue::undefined(); }
743    let val = args[2].to_number() as u32;
744    let bytes = val.to_be_bytes();
745    dv_write_bytes_checked(args, ctx, &bytes);
746    JSValue::undefined()
747}
748
749fn data_view_set_float32(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
750    if args.len() < 3 { return JSValue::undefined(); }
751    let val = args[2].to_number() as f32;
752    let bytes = val.to_be_bytes();
753    dv_write_bytes_checked(args, ctx, &bytes);
754    JSValue::undefined()
755}
756
757fn data_view_set_float64(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
758    if args.len() < 3 { return JSValue::undefined(); }
759    let val = args[2].to_number();
760    let bytes = val.to_be_bytes();
761    dv_write_bytes_checked(args, ctx, &bytes);
762    JSValue::undefined()
763}
764
765fn data_view_set_float16(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
766    if args.len() < 3 { return JSValue::undefined(); }
767    let val = args[2].to_number() as f32;
768    let h = f32_to_f16(val);
769    let bytes = h.to_be_bytes();
770    dv_write_bytes_checked(args, ctx, &bytes);
771    JSValue::undefined()
772}
773
774fn data_view_get_int16(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
775    match dv_read_bytes_checked(args, ctx, 2) {
776        Some(b) => JSValue::new_int(i16::from_be_bytes([b[0], b[1]]) as i64),
777        None => JSValue::undefined(),
778    }
779}
780
781fn data_view_get_uint16(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
782    match dv_read_bytes_checked(args, ctx, 2) {
783        Some(b) => JSValue::new_int(u16::from_be_bytes([b[0], b[1]]) as i64),
784        None => JSValue::undefined(),
785    }
786}
787
788fn data_view_get_int32(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
789    match dv_read_bytes_checked(args, ctx, 4) {
790        Some(b) => JSValue::new_int(i32::from_be_bytes([b[0], b[1], b[2], b[3]]) as i64),
791        None => JSValue::undefined(),
792    }
793}
794
795fn data_view_get_uint32(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
796    match dv_read_bytes_checked(args, ctx, 4) {
797        Some(b) => {
798            let val = u32::from_be_bytes([b[0], b[1], b[2], b[3]]);
799            JSValue::new_int(val as i64)
800        }
801        None => JSValue::undefined(),
802    }
803}
804
805fn data_view_get_float32(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
806    match dv_read_bytes_checked(args, ctx, 4) {
807        Some(b) => JSValue::new_float(f32::from_be_bytes([b[0], b[1], b[2], b[3]]) as f64),
808        None => JSValue::undefined(),
809    }
810}
811
812fn data_view_get_float64(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
813    match dv_read_bytes_checked(args, ctx, 8) {
814        Some(b) => JSValue::new_float(f64::from_be_bytes([
815            b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
816        ])),
817        None => JSValue::undefined(),
818    }
819}
820
821fn data_view_get_float16(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
822    match dv_read_bytes_checked(args, ctx, 2) {
823        Some(b) => {
824            let val = u16::from_be_bytes([b[0], b[1]]);
825            JSValue::new_float(f16_to_f32(val) as f64)
826        }
827        None => JSValue::undefined(),
828    }
829}
830
831fn data_view_get_bigint64(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
832    match dv_read_bytes_checked(args, ctx, 8) {
833        Some(b) => {
834            let val = i64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]);
835            let mut obj = crate::object::object::JSObject::new_bigint();
836            obj.set_bigint_value(val as i128);
837            JSValue::new_bigint(Box::into_raw(Box::new(obj)) as usize)
838        }
839        None => JSValue::undefined(),
840    }
841}
842
843fn data_view_get_biguint64(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
844    match dv_read_bytes_checked(args, ctx, 8) {
845        Some(b) => {
846            let val = u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]);
847            let mut obj = crate::object::object::JSObject::new_bigint();
848            obj.set_bigint_value(val as i128);
849            JSValue::new_bigint(Box::into_raw(Box::new(obj)) as usize)
850        }
851        None => JSValue::undefined(),
852    }
853}
854
855fn data_view_set_bigint64(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
856    if args.len() < 3 { return JSValue::undefined(); }
857    let val = if args[2].is_bigint() {
858        let obj = args[2].as_object();
859        obj.get_bigint_value() as i64
860    } else {
861        args[2].to_number() as i64
862    };
863    let bytes = val.to_be_bytes();
864    dv_write_bytes_checked(args, ctx, &bytes);
865    JSValue::undefined()
866}
867
868fn data_view_set_biguint64(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
869    if args.len() < 3 { return JSValue::undefined(); }
870    let val = if args[2].is_bigint() {
871        let obj = args[2].as_object();
872        obj.get_bigint_value() as u64
873    } else {
874        args[2].to_number() as u64
875    };
876    let bytes = val.to_be_bytes();
877    dv_write_bytes_checked(args, ctx, &bytes);
878    JSValue::undefined()
879}
880
881fn create_builtin_function(ctx: &mut JSContext, name: &str) -> JSValue {
882    let arity = ctx.get_builtin_arity(name).unwrap_or(1);
883    let mut func = crate::object::function::JSFunction::new_builtin(ctx.intern(name), arity);
884    func.set_builtin_marker(ctx, name);
885    let ptr = Box::into_raw(Box::new(func)) as usize;
886    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
887    JSValue::new_function(ptr)
888}
889
890fn ta_check_this(args: &[JSValue], ctx: &mut JSContext) -> bool {
891    if args.is_empty() || !args[0].is_object() {
892        throw_type_error(ctx, "Method called on incompatible receiver");
893        return false;
894    }
895    let obj = args[0].as_object();
896    if obj.obj_type() != ObjectType::TypedArray {
897        throw_type_error(ctx, "Method called on incompatible receiver");
898        return false;
899    }
900    true
901}
902
903fn ta_get_length(ctx: &mut JSContext, obj: &JSObject) -> usize {
904    obj.get(ctx.common_atoms.length)
905        .map(|v| v.get_int() as usize)
906        .unwrap_or(0)
907}
908
909pub fn ta_get_element(ctx: &mut JSContext, obj: &JSObject, index: usize) -> Option<JSValue> {
910    let kind = obj.get_typed_array_kind()?;
911    let bpe = kind.bytes_per_element();
912    let byte_offset = obj.get(ctx.intern("byteOffset"))
913        .map(|v| v.get_int() as usize)
914        .unwrap_or(0);
915    let buffer = obj.get(ctx.intern("buffer"))?;
916    if !buffer.is_object() { return None; }
917    let buf_obj = buffer.as_object();
918    let data = get_array_buffer_data(&buf_obj)?;
919    let byte_index = byte_offset + index * bpe;
920    if byte_index + bpe > data.data.len() { return None; }
921    let bytes = &data.data[byte_index..byte_index + bpe];
922    Some(match kind {
923        TypedArrayKind::Int8 => JSValue::new_int(bytes[0] as i8 as i64),
924        TypedArrayKind::Uint8 | TypedArrayKind::Uint8Clamped => JSValue::new_int(bytes[0] as i64),
925        TypedArrayKind::Int16 => JSValue::new_int(i16::from_le_bytes([bytes[0], bytes[1]]) as i64),
926        TypedArrayKind::Uint16 => JSValue::new_int(u16::from_le_bytes([bytes[0], bytes[1]]) as i64),
927        TypedArrayKind::Int32 => JSValue::new_int(i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as i64),
928        TypedArrayKind::Uint32 => JSValue::new_int(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as i64),
929        TypedArrayKind::Float32 => JSValue::new_float(f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as f64),
930        TypedArrayKind::Float64 => JSValue::new_float(f64::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]])),
931        TypedArrayKind::BigInt64 => {
932            let val = i64::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]]);
933            let mut bi_obj = crate::object::object::JSObject::new_bigint();
934            bi_obj.set_bigint_value(val as i128);
935            JSValue::new_bigint(Box::into_raw(Box::new(bi_obj)) as usize)
936        }
937        TypedArrayKind::BigUint64 => {
938            let val = u64::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]]);
939            let mut bi_obj = crate::object::object::JSObject::new_bigint();
940            bi_obj.set_bigint_value(val as i128);
941            JSValue::new_bigint(Box::into_raw(Box::new(bi_obj)) as usize)
942        }
943    })
944}
945
946pub fn ta_set_element(ctx: &mut JSContext, obj: &mut JSObject, index: usize, value: JSValue) -> bool {
947    let kind = match obj.get_typed_array_kind() {
948        Some(k) => k,
949        None => return false,
950    };
951    let bpe = kind.bytes_per_element();
952    let byte_offset = obj.get(ctx.intern("byteOffset"))
953        .map(|v| v.get_int() as usize)
954        .unwrap_or(0);
955    let buffer = match obj.get(ctx.intern("buffer")) {
956        Some(b) => b,
957        None => return false,
958    };
959    if !buffer.is_object() { return false; }
960    let buf_obj = buffer.as_object();
961    let data = match get_array_buffer_data(&buf_obj) {
962        Some(d) => d,
963        None => return false,
964    };
965    let byte_index = byte_offset + index * bpe;
966    if byte_index + bpe > data.data.len() { return false; }
967    let num = value.to_number();
968    match kind {
969        TypedArrayKind::Int8 => {
970            let v = num as i8;
971            data.data[byte_index..byte_index + bpe].copy_from_slice(&v.to_le_bytes());
972        }
973        TypedArrayKind::Uint8 | TypedArrayKind::Uint8Clamped => {
974            let v = num as u8;
975            data.data[byte_index..byte_index + bpe].copy_from_slice(&v.to_le_bytes());
976        }
977        TypedArrayKind::Int16 => {
978            let v = num as i16;
979            data.data[byte_index..byte_index + bpe].copy_from_slice(&v.to_le_bytes());
980        }
981        TypedArrayKind::Uint16 => {
982            let v = num as u16;
983            data.data[byte_index..byte_index + bpe].copy_from_slice(&v.to_le_bytes());
984        }
985        TypedArrayKind::Int32 => {
986            let v = num as i32;
987            data.data[byte_index..byte_index + bpe].copy_from_slice(&v.to_le_bytes());
988        }
989        TypedArrayKind::Uint32 => {
990            let v = num as u32;
991            data.data[byte_index..byte_index + bpe].copy_from_slice(&v.to_le_bytes());
992        }
993        TypedArrayKind::Float32 => {
994            let v = num as f32;
995            data.data[byte_index..byte_index + bpe].copy_from_slice(&v.to_le_bytes());
996        }
997        TypedArrayKind::Float64 => {
998            let v = num;
999            data.data[byte_index..byte_index + bpe].copy_from_slice(&v.to_le_bytes());
1000        }
1001        TypedArrayKind::BigInt64 | TypedArrayKind::BigUint64 => {
1002            let v = num as i64;
1003            data.data[byte_index..byte_index + bpe].copy_from_slice(&v.to_le_bytes());
1004        }
1005    }
1006    true
1007}
1008
1009fn ta_call_callback(
1010    ctx: &mut JSContext,
1011    callback: JSValue,
1012    this_value: JSValue,
1013    args: &[JSValue],
1014) -> Result<JSValue, String> {
1015    if let Some(ptr) = ctx.get_register_vm_ptr() {
1016        let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
1017        vm.call_function_with_this(ctx, callback, this_value, args)
1018    } else {
1019        Err("call_callback: VM not available".to_string())
1020    }
1021}
1022
1023fn typed_array_for_each(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1024    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1025    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1026    if !callback.is_function() {
1027        throw_type_error(ctx, "Callback is not a function");
1028        return JSValue::undefined();
1029    }
1030    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1031    let obj = args[0].as_object();
1032    let len = ta_get_length(ctx, obj);
1033    for i in 0..len {
1034        if let Some(val) = ta_get_element(ctx, obj, i) {
1035            let cb_args = vec![val, JSValue::new_int(i as i64), args[0]];
1036            let _ = ta_call_callback(ctx, callback, this_arg, &cb_args);
1037        }
1038    }
1039    JSValue::undefined()
1040}
1041
1042fn typed_array_every(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1043    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1044    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1045    if !callback.is_function() {
1046        throw_type_error(ctx, "Callback is not a function");
1047        return JSValue::undefined();
1048    }
1049    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1050    let obj = args[0].as_object();
1051    let len = ta_get_length(ctx, obj);
1052    for i in 0..len {
1053        if let Some(val) = ta_get_element(ctx, obj, i) {
1054            let cb_args = vec![val, JSValue::new_int(i as i64), args[0]];
1055            match ta_call_callback(ctx, callback, this_arg, &cb_args) {
1056                Ok(result) => {
1057                    if !result.is_truthy() {
1058                        return JSValue::new_int(0);
1059                    }
1060                }
1061                Err(_) => return JSValue::undefined(),
1062            }
1063        }
1064    }
1065    JSValue::new_int(1)
1066}
1067
1068fn typed_array_some(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1069    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1070    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1071    if !callback.is_function() {
1072        throw_type_error(ctx, "Callback is not a function");
1073        return JSValue::undefined();
1074    }
1075    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1076    let obj = args[0].as_object();
1077    let len = ta_get_length(ctx, obj);
1078    for i in 0..len {
1079        if let Some(val) = ta_get_element(ctx, obj, i) {
1080            let cb_args = vec![val, JSValue::new_int(i as i64), args[0]];
1081            match ta_call_callback(ctx, callback, this_arg, &cb_args) {
1082                Ok(result) => {
1083                    if result.is_truthy() {
1084                        return JSValue::bool(true);
1085                    }
1086                }
1087                Err(_) => return JSValue::undefined(),
1088            }
1089        }
1090    }
1091    JSValue::new_int(0)
1092}
1093
1094fn typed_array_find(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1095    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1096    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1097    if !callback.is_function() {
1098        throw_type_error(ctx, "Callback is not a function");
1099        return JSValue::undefined();
1100    }
1101    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1102    let obj = args[0].as_object();
1103    let len = ta_get_length(ctx, obj);
1104    for i in 0..len {
1105        if let Some(val) = ta_get_element(ctx, obj, i) {
1106            let cb_args = vec![val, JSValue::new_int(i as i64), args[0]];
1107            match ta_call_callback(ctx, callback, this_arg, &cb_args) {
1108                Ok(result) => {
1109                    if result.is_truthy() {
1110                        return val;
1111                    }
1112                }
1113                Err(_) => return JSValue::undefined(),
1114            }
1115        }
1116    }
1117    JSValue::undefined()
1118}
1119
1120fn typed_array_find_index(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1121    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1122    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1123    if !callback.is_function() {
1124        throw_type_error(ctx, "Callback is not a function");
1125        return JSValue::undefined();
1126    }
1127    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1128    let obj = args[0].as_object();
1129    let len = ta_get_length(ctx, obj);
1130    for i in 0..len {
1131        if let Some(val) = ta_get_element(ctx, obj, i) {
1132            let cb_args = vec![val, JSValue::new_int(i as i64), args[0]];
1133            match ta_call_callback(ctx, callback, this_arg, &cb_args) {
1134                Ok(result) => {
1135                    if result.is_truthy() {
1136                        return JSValue::new_int(i as i64);
1137                    }
1138                }
1139                Err(_) => return JSValue::new_int(-1),
1140            }
1141        }
1142    }
1143    JSValue::new_int(-1)
1144}
1145
1146fn typed_array_find_last(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1147    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1148    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1149    if !callback.is_function() {
1150        throw_type_error(ctx, "Callback is not a function");
1151        return JSValue::undefined();
1152    }
1153    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1154    let obj = args[0].as_object();
1155    let len = ta_get_length(ctx, obj);
1156    let mut result = JSValue::undefined();
1157    for i in 0..len {
1158        if let Some(val) = ta_get_element(ctx, obj, i) {
1159            let cb_args = vec![val, JSValue::new_int(i as i64), args[0]];
1160            match ta_call_callback(ctx, callback, this_arg, &cb_args) {
1161                Ok(res) => {
1162                    if res.is_truthy() {
1163                        result = val;
1164                    }
1165                }
1166                Err(_) => return JSValue::undefined(),
1167            }
1168        }
1169    }
1170    result
1171}
1172
1173fn typed_array_find_last_index(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1174    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1175    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1176    if !callback.is_function() {
1177        throw_type_error(ctx, "Callback is not a function");
1178        return JSValue::undefined();
1179    }
1180    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1181    let obj = args[0].as_object();
1182    let len = ta_get_length(ctx, obj);
1183    let mut result = JSValue::new_int(-1);
1184    for i in 0..len {
1185        if let Some(val) = ta_get_element(ctx, obj, i) {
1186            let cb_args = vec![val, JSValue::new_int(i as i64), args[0]];
1187            match ta_call_callback(ctx, callback, this_arg, &cb_args) {
1188                Ok(res) => {
1189                    if res.is_truthy() {
1190                        result = JSValue::new_int(i as i64);
1191                    }
1192                }
1193                Err(_) => return JSValue::new_int(-1),
1194            }
1195        }
1196    }
1197    result
1198}
1199
1200fn typed_array_reduce(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1201    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1202    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1203    if !callback.is_function() {
1204        throw_type_error(ctx, "Callback is not a function");
1205        return JSValue::undefined();
1206    }
1207    let obj = args[0].as_object();
1208    let len = ta_get_length(ctx, obj);
1209    let mut acc = JSValue::undefined();
1210    let mut start = 0;
1211    if len == 0 {
1212        if args.len() < 3 {
1213            throw_type_error(ctx, "Reduce of empty array with no initial value");
1214            return JSValue::undefined();
1215        }
1216        acc = args[2];
1217    } else if args.len() >= 3 {
1218        acc = args[2];
1219    } else {
1220        start = 1;
1221        acc = ta_get_element(ctx, obj, 0).unwrap_or(JSValue::undefined());
1222    }
1223    for i in start..len {
1224        if let Some(val) = ta_get_element(ctx, obj, i) {
1225            let cb_args = vec![acc, val, JSValue::new_int(i as i64), args[0]];
1226            match ta_call_callback(ctx, callback, JSValue::undefined(), &cb_args) {
1227                Ok(res) => { acc = res; }
1228                Err(_) => return JSValue::undefined(),
1229            }
1230        }
1231    }
1232    acc
1233}
1234
1235fn typed_array_reduce_right(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1236    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1237    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1238    if !callback.is_function() {
1239        throw_type_error(ctx, "Callback is not a function");
1240        return JSValue::undefined();
1241    }
1242    let obj = args[0].as_object();
1243    let len = ta_get_length(ctx, obj);
1244    let mut acc = JSValue::undefined();
1245    let mut end = len;
1246    if len == 0 {
1247        if args.len() < 3 {
1248            throw_type_error(ctx, "Reduce of empty array with no initial value");
1249            return JSValue::undefined();
1250        }
1251        acc = args[2];
1252    } else if args.len() >= 3 {
1253        acc = args[2];
1254    } else {
1255        end -= 1;
1256        acc = ta_get_element(ctx, obj, end).unwrap_or(JSValue::undefined());
1257    }
1258    for i in (0..end).rev() {
1259        if let Some(val) = ta_get_element(ctx, obj, i) {
1260            let cb_args = vec![acc, val, JSValue::new_int(i as i64), args[0]];
1261            match ta_call_callback(ctx, callback, JSValue::undefined(), &cb_args) {
1262                Ok(res) => { acc = res; }
1263                Err(_) => return JSValue::undefined(),
1264            }
1265        }
1266    }
1267    acc
1268}
1269
1270fn typed_array_map(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1271    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1272    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1273    if !callback.is_function() {
1274        throw_type_error(ctx, "Callback is not a function");
1275        return JSValue::undefined();
1276    }
1277    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1278    let obj = args[0].as_object();
1279    let len = ta_get_length(ctx, obj);
1280    let kind = obj.get_typed_array_kind().unwrap_or(TypedArrayKind::Int32);
1281    let result = typed_array_from_args(ctx, kind, &[
1282        JSValue::new_int(len as i64),
1283    ]);
1284    match result {
1285        Ok(r) => {
1286            let result_obj = r.as_object_mut();
1287            for i in 0..len {
1288                if let Some(val) = ta_get_element(ctx, obj, i) {
1289                    let cb_args = vec![val, JSValue::new_int(i as i64), args[0]];
1290                    match ta_call_callback(ctx, callback, this_arg, &cb_args) {
1291                        Ok(mapped) => { ta_set_element(ctx, result_obj, i, mapped); }
1292                        Err(_) => return JSValue::undefined(),
1293                    }
1294                }
1295            }
1296            r
1297        }
1298        Err(_) => JSValue::undefined(),
1299    }
1300}
1301
1302fn typed_array_filter(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1303    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1304    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1305    if !callback.is_function() {
1306        throw_type_error(ctx, "Callback is not a function");
1307        return JSValue::undefined();
1308    }
1309    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1310    let obj = args[0].as_object();
1311    let len = ta_get_length(ctx, obj);
1312    let kind = obj.get_typed_array_kind().unwrap_or(TypedArrayKind::Int32);
1313    let mut result: Vec<JSValue> = Vec::new();
1314    for i in 0..len {
1315        if let Some(val) = ta_get_element(ctx, obj, i) {
1316            let cb_args = vec![val, JSValue::new_int(i as i64), args[0]];
1317            match ta_call_callback(ctx, callback, this_arg, &cb_args) {
1318                Ok(res) => {
1319                    if res.is_truthy() {
1320                        result.push(val);
1321                    }
1322                }
1323                Err(_) => return JSValue::undefined(),
1324            }
1325        }
1326    }
1327    let result_val = typed_array_from_args(ctx, kind, &[
1328        JSValue::new_int(result.len() as i64),
1329    ]);
1330    match result_val {
1331        Ok(r) => {
1332            let result_obj = r.as_object_mut();
1333            for (i, val) in result.iter().enumerate() {
1334                ta_set_element(ctx, result_obj, i, *val);
1335            }
1336            r
1337        }
1338        Err(_) => JSValue::undefined(),
1339    }
1340}
1341
1342fn typed_array_index_of(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1343    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1344    let search = args.get(1).copied().unwrap_or(JSValue::undefined());
1345    let obj = args[0].as_object();
1346    let len = ta_get_length(ctx, obj);
1347    let from_index = if args.len() >= 3 {
1348        let v = args[2].to_number() as i64;
1349        if v < 0 { (len as i64 + v).max(0) as usize } else { (v as usize).min(len) }
1350    } else {
1351        0
1352    };
1353    for i in from_index..len {
1354        if let Some(val) = ta_get_element(ctx, obj, i) {
1355            if val.strict_eq(&search) {
1356                return JSValue::new_int(i as i64);
1357            }
1358        }
1359    }
1360    JSValue::new_int(-1)
1361}
1362
1363fn typed_array_last_index_of(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1364    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1365    let search = args.get(1).copied().unwrap_or(JSValue::undefined());
1366    let obj = args[0].as_object();
1367    let len = ta_get_length(ctx, obj);
1368    let from_index = if args.len() >= 3 {
1369        let v = args[2].to_number() as i64;
1370        if v < 0 { 0 } else { (v as usize).min(len.saturating_sub(1)) }
1371    } else {
1372        len.saturating_sub(1)
1373    };
1374    for i in (0..=from_index).rev() {
1375        if let Some(val) = ta_get_element(ctx, obj, i) {
1376            if val.strict_eq(&search) {
1377                return JSValue::new_int(i as i64);
1378            }
1379        }
1380    }
1381    JSValue::new_int(-1)
1382}
1383
1384fn typed_array_includes(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1385    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1386    let search = args.get(1).copied().unwrap_or(JSValue::undefined());
1387    let obj = args[0].as_object();
1388    let len = ta_get_length(ctx, obj);
1389    let from_index = if args.len() >= 3 {
1390        let v = args[2].to_number() as i64;
1391        if v < 0 { (len as i64 + v).max(0) as usize } else { (v as usize).min(len) }
1392    } else {
1393        0
1394    };
1395    for i in from_index..len {
1396        if let Some(val) = ta_get_element(ctx, obj, i) {
1397            if val.strict_eq(&search) {
1398                return JSValue::bool(true);
1399            }
1400            if search.is_float() && val.is_float() {
1401                let sv = search.to_number();
1402                let vv = val.to_number();
1403                if sv.is_nan() && vv.is_nan() {
1404                    return JSValue::bool(true);
1405                }
1406            }
1407        }
1408    }
1409    JSValue::new_int(0)
1410}
1411
1412fn typed_array_join(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1413    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1414    let separator = args.get(1).copied().unwrap_or(JSValue::undefined());
1415    let sep_str = if separator.is_undefined() {
1416        ",".to_string()
1417    } else if separator.is_string() {
1418        ctx.get_atom_str(separator.get_atom()).to_string()
1419    } else if separator.is_int() {
1420        separator.get_int().to_string()
1421    } else if separator.is_float() {
1422        separator.get_float().to_string()
1423    } else if separator.is_bool() {
1424        separator.get_bool().to_string()
1425    } else if separator.is_null() {
1426        "null".to_string()
1427    } else {
1428        ",".to_string()
1429    };
1430    let obj = args[0].as_object();
1431    let len = ta_get_length(ctx, obj);
1432    let mut parts = Vec::new();
1433    for i in 0..len {
1434        if let Some(val) = ta_get_element(ctx, obj, i) {
1435            if val.is_undefined() || val.is_null() {
1436                parts.push(String::new());
1437            } else if val.is_string() {
1438                parts.push(ctx.get_atom_str(val.get_atom()).to_string());
1439            } else if val.is_int() {
1440                parts.push(val.get_int().to_string());
1441            } else if val.is_float() {
1442                parts.push(val.get_float().to_string());
1443            } else if val.is_bool() {
1444                parts.push(val.get_bool().to_string());
1445            } else {
1446                parts.push(String::new());
1447            }
1448        } else {
1449            parts.push(String::new());
1450        }
1451    }
1452    JSValue::new_string(ctx.intern(&parts.join(&sep_str)))
1453}
1454
1455fn typed_array_at(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1456    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1457    let obj = args[0].as_object();
1458    let len = ta_get_length(ctx, obj) as i64;
1459    let relative_index = args.get(1).copied().unwrap_or(JSValue::new_int(0)).to_number() as i64;
1460    let k = if relative_index >= 0 {
1461        relative_index
1462    } else {
1463        len + relative_index
1464    };
1465    if k < 0 || k >= len {
1466        return JSValue::undefined();
1467    }
1468    ta_get_element(ctx, obj, k as usize).unwrap_or(JSValue::undefined())
1469}
1470
1471fn typed_array_values(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1472    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1473    JSValue::undefined()
1474}
1475
1476fn typed_array_keys(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1477    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1478    JSValue::undefined()
1479}
1480
1481fn typed_array_entries(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1482    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1483    JSValue::undefined()
1484}
1485
1486fn typed_array_to_string(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1487    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1488    typed_array_join(ctx, &[
1489        args[0],
1490        JSValue::undefined(),
1491    ])
1492}
1493
1494fn typed_array_reverse(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1495    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1496    let obj = args[0].as_object_mut();
1497    let len = ta_get_length(ctx, obj);
1498    let mut values: Vec<JSValue> = Vec::with_capacity(len);
1499    for i in 0..len {
1500        values.push(ta_get_element(ctx, obj, i).unwrap_or(JSValue::undefined()));
1501    }
1502    values.reverse();
1503    for i in 0..len {
1504        ta_set_element(ctx, obj, i, values[i]);
1505    }
1506    args[0]
1507}
1508
1509fn typed_array_slice(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1510    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1511    let obj = args[0].as_object();
1512    let len = ta_get_length(ctx, obj) as i64;
1513    let start = {
1514        let v = args.get(1).copied().unwrap_or(JSValue::new_int(0)).to_number() as i64;
1515        if v < 0 { (len + v).max(0) } else { v.min(len) }
1516    };
1517    let end = {
1518        let v = args.get(2).copied().unwrap_or(JSValue::new_int(len)).to_number() as i64;
1519        if v < 0 { (len + v).max(0) } else { v.min(len) }
1520    };
1521    let new_len = (end - start).max(0) as usize;
1522    let kind = obj.get_typed_array_kind().unwrap_or(TypedArrayKind::Int32);
1523    let result = typed_array_from_args(ctx, kind, &[
1524        JSValue::new_int(new_len as i64),
1525    ]);
1526    match result {
1527        Ok(r) => {
1528            let result_obj = r.as_object_mut();
1529            for i in 0..new_len {
1530                if let Some(val) = ta_get_element(ctx, obj, (start as usize) + i) {
1531                    ta_set_element(ctx, result_obj, i, val);
1532                }
1533            }
1534            r
1535        }
1536        Err(_) => JSValue::undefined(),
1537    }
1538}
1539
1540fn typed_array_fill(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1541    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1542    let value = args.get(1).copied().unwrap_or(JSValue::undefined());
1543    let obj = args[0].as_object_mut();
1544    let len = ta_get_length(ctx, obj) as i64;
1545    let start = {
1546        let v = args.get(2).copied().unwrap_or(JSValue::new_int(0)).to_number() as i64;
1547        if v < 0 { (len + v).max(0) } else { v.min(len) }
1548    };
1549    let end = {
1550        let v = args.get(3).copied().unwrap_or(JSValue::new_int(len)).to_number() as i64;
1551        if v < 0 { (len + v).max(0) } else { v.min(len) }
1552    };
1553    for i in start..end {
1554        ta_set_element(ctx, obj, i as usize, value);
1555    }
1556    args[0]
1557}
1558
1559fn typed_array_copy_within(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1560    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1561    let obj = args[0].as_object_mut();
1562    let len = ta_get_length(ctx, obj) as i64;
1563    let target = {
1564        let v = args.get(1).copied().unwrap_or(JSValue::new_int(0)).to_number() as i64;
1565        if v < 0 { (len + v).max(0) } else { v.min(len) }
1566    };
1567    let start = {
1568        let v = args.get(2).copied().unwrap_or(JSValue::new_int(0)).to_number() as i64;
1569        if v < 0 { (len + v).max(0) } else { v.min(len) }
1570    };
1571    let end = {
1572        let v = args.get(3).copied().unwrap_or(JSValue::new_int(len)).to_number() as i64;
1573        if v < 0 { (len + v).max(0) } else { v.min(len) }
1574    };
1575    let count = (end - start).min(len - target).max(0) as usize;
1576    let mut values = Vec::with_capacity(count);
1577    for i in 0..count {
1578        values.push(ta_get_element(ctx, obj, (start as usize) + i).unwrap_or(JSValue::undefined()));
1579    }
1580    for (i, val) in values.into_iter().enumerate() {
1581        ta_set_element(ctx, obj, (target as usize) + i, val);
1582    }
1583    args[0]
1584}
1585
1586fn typed_array_sort(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1587    if !ta_check_this(args, ctx) { return JSValue::undefined(); }
1588    let compare_fn = args.get(1).copied().unwrap_or(JSValue::undefined());
1589    let obj = args[0].as_object_mut();
1590    let len = ta_get_length(ctx, obj);
1591    if len <= 1 {
1592        return args[0];
1593    }
1594    let mut values: Vec<JSValue> = Vec::with_capacity(len);
1595    for i in 0..len {
1596        values.push(ta_get_element(ctx, obj, i).unwrap_or(JSValue::undefined()));
1597    }
1598    let compare_is_fn = compare_fn.is_function();
1599    values.sort_by(|a, b| {
1600        if compare_is_fn {
1601            let cb_args = vec![*a, *b];
1602            if let Some(ptr) = ctx.get_register_vm_ptr() {
1603                let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
1604                if let Ok(result) = vm.call_function_with_this(ctx, compare_fn, JSValue::undefined(), &cb_args) {
1605                    let n = result.to_number();
1606                    if n < 0.0 { std::cmp::Ordering::Less }
1607                    else if n > 0.0 { std::cmp::Ordering::Greater }
1608                    else { std::cmp::Ordering::Equal }
1609                } else {
1610                    std::cmp::Ordering::Equal
1611                }
1612            } else {
1613                std::cmp::Ordering::Equal
1614            }
1615        } else {
1616            let a_str = a.to_number();
1617            let b_str = b.to_number();
1618            a_str.partial_cmp(&b_str).unwrap_or(std::cmp::Ordering::Equal)
1619        }
1620    });
1621    for (i, val) in values.into_iter().enumerate() {
1622        ta_set_element(ctx, obj, i, val);
1623    }
1624    args[0]
1625}
1626
1627fn typed_array_subarray(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1628    if args.is_empty() || !args[0].is_object() {
1629        throw_type_error(ctx, "Method called on incompatible receiver");
1630        return JSValue::undefined();
1631    }
1632    let obj = args[0].as_object();
1633    if obj.obj_type() != ObjectType::TypedArray {
1634        throw_type_error(ctx, "Method called on incompatible receiver");
1635        return JSValue::undefined();
1636    }
1637    let len = ta_get_length(ctx, obj) as i64;
1638    let buffer = obj.get(ctx.intern("buffer")).unwrap_or(JSValue::undefined());
1639    let byte_offset = obj.get(ctx.intern("byteOffset"))
1640        .map(|v| v.get_int())
1641        .unwrap_or(0);
1642    let element_size = obj.get_typed_array_kind()
1643        .map(|k| k.bytes_per_element() as i64)
1644        .unwrap_or(1);
1645    let start = {
1646        let v = args.get(1).copied().unwrap_or(JSValue::new_int(0)).to_number() as i64;
1647        if v < 0 { (len + v).max(0) } else { v.min(len) }
1648    };
1649    let end = {
1650        let v = args.get(2).copied().unwrap_or(JSValue::new_int(len)).to_number() as i64;
1651        if v < 0 { (len + v).max(0) } else { v.min(len) }
1652    };
1653    let new_len = (end - start).max(0) as usize;
1654    let new_byte_offset = byte_offset + start * element_size;
1655    let kind = obj.get_typed_array_kind().unwrap_or(TypedArrayKind::Int32);
1656    match create_typed_array(ctx, kind, buffer, new_byte_offset as usize, Some(new_len)) {
1657        Ok(v) => v,
1658        Err(_) => JSValue::undefined(),
1659    }
1660}
1661
1662pub fn init_typed_array(ctx: &mut JSContext) {
1663    let global = ctx.global();
1664    if !global.is_object() {
1665        return;
1666    }
1667    let global_obj = global.as_object_mut();
1668
1669    crate::builtins::global::set_non_enumerable(
1670        global_obj,
1671        ctx.intern("ArrayBuffer"),
1672        create_builtin_function(ctx, "ArrayBuffer"),
1673    );
1674
1675    let mut dv_proto = JSObject::new();
1676    dv_proto.set(ctx.intern("buffer"), create_builtin_function(ctx, "dataview_buffer"));
1677    dv_proto.set(
1678        ctx.intern("byteLength"),
1679        create_builtin_function(ctx, "dataview_byteLength"),
1680    );
1681    dv_proto.set(
1682        ctx.intern("byteOffset"),
1683        create_builtin_function(ctx, "dataview_byteOffset"),
1684    );
1685    dv_proto.set(
1686        ctx.intern("getInt8"),
1687        create_builtin_function(ctx, "dataview_getInt8"),
1688    );
1689    dv_proto.set(
1690        ctx.intern("getUint8"),
1691        create_builtin_function(ctx, "dataview_getUint8"),
1692    );
1693    dv_proto.set(
1694        ctx.intern("setInt8"),
1695        create_builtin_function(ctx, "dataview_setInt8"),
1696    );
1697    dv_proto.set(
1698        ctx.intern("setUint8"),
1699        create_builtin_function(ctx, "dataview_setUint8"),
1700    );
1701    dv_proto.set(
1702        ctx.intern("getInt16"),
1703        create_builtin_function(ctx, "dataview_getInt16"),
1704    );
1705    dv_proto.set(
1706        ctx.intern("getUint16"),
1707        create_builtin_function(ctx, "dataview_getUint16"),
1708    );
1709    dv_proto.set(
1710        ctx.intern("getInt32"),
1711        create_builtin_function(ctx, "dataview_getInt32"),
1712    );
1713    dv_proto.set(
1714        ctx.intern("getUint32"),
1715        create_builtin_function(ctx, "dataview_getUint32"),
1716    );
1717    dv_proto.set(
1718        ctx.intern("getFloat32"),
1719        create_builtin_function(ctx, "dataview_getFloat32"),
1720    );
1721    dv_proto.set(
1722        ctx.intern("getFloat64"),
1723        create_builtin_function(ctx, "dataview_getFloat64"),
1724    );
1725    dv_proto.set(
1726        ctx.intern("getFloat16"),
1727        create_builtin_function(ctx, "dataview_getFloat16"),
1728    );
1729    dv_proto.set(
1730        ctx.intern("setInt16"),
1731        create_builtin_function(ctx, "dataview_setInt16"),
1732    );
1733    dv_proto.set(
1734        ctx.intern("setUint16"),
1735        create_builtin_function(ctx, "dataview_setUint16"),
1736    );
1737    dv_proto.set(
1738        ctx.intern("setInt32"),
1739        create_builtin_function(ctx, "dataview_setInt32"),
1740    );
1741    dv_proto.set(
1742        ctx.intern("setUint32"),
1743        create_builtin_function(ctx, "dataview_setUint32"),
1744    );
1745    dv_proto.set(
1746        ctx.intern("setFloat32"),
1747        create_builtin_function(ctx, "dataview_setFloat32"),
1748    );
1749    dv_proto.set(
1750        ctx.intern("setFloat64"),
1751        create_builtin_function(ctx, "dataview_setFloat64"),
1752    );
1753    dv_proto.set(
1754        ctx.intern("setFloat16"),
1755        create_builtin_function(ctx, "dataview_setFloat16"),
1756    );
1757    dv_proto.set(
1758        ctx.intern("getBigInt64"),
1759        create_builtin_function(ctx, "dataview_getBigInt64"),
1760    );
1761    dv_proto.set(
1762        ctx.intern("getBigUint64"),
1763        create_builtin_function(ctx, "dataview_getBigUint64"),
1764    );
1765    dv_proto.set(
1766        ctx.intern("setBigInt64"),
1767        create_builtin_function(ctx, "dataview_setBigInt64"),
1768    );
1769    dv_proto.set(
1770        ctx.intern("setBigUint64"),
1771        create_builtin_function(ctx, "dataview_setBigUint64"),
1772    );
1773
1774    let to_string_tag_key = crate::builtins::symbol::get_symbol_to_string_tag_prop_key(ctx);
1775    dv_proto.set(to_string_tag_key, JSValue::new_string(ctx.intern("DataView")));
1776
1777    if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
1778        dv_proto.prototype = Some(obj_proto_ptr);
1779    }
1780
1781    let dv_proto_ptr = Box::into_raw(Box::new(dv_proto)) as usize;
1782    ctx.runtime_mut().gc_heap_mut().track(dv_proto_ptr);
1783    ctx.set_dataview_prototype(dv_proto_ptr);
1784
1785    let dv_ctor = create_builtin_function(ctx, "DataView");
1786    if dv_ctor.is_function() {
1787        let dv_ctor_obj = dv_ctor.as_object_mut();
1788        dv_ctor_obj.set(ctx.intern("prototype"), JSValue::new_object(dv_proto_ptr));
1789    }
1790    crate::builtins::global::set_non_enumerable(
1791        global_obj,
1792        ctx.intern("DataView"),
1793        dv_ctor,
1794    );
1795
1796    crate::builtins::global::set_non_enumerable(
1797        global_obj,
1798        ctx.intern("Int8Array"),
1799        create_builtin_function(ctx, "Int8Array"),
1800    );
1801    crate::builtins::global::set_non_enumerable(
1802        global_obj,
1803        ctx.intern("Uint8Array"),
1804        create_builtin_function(ctx, "Uint8Array"),
1805    );
1806    crate::builtins::global::set_non_enumerable(
1807        global_obj,
1808        ctx.intern("Uint8ClampedArray"),
1809        create_builtin_function(ctx, "Uint8ClampedArray"),
1810    );
1811    crate::builtins::global::set_non_enumerable(
1812        global_obj,
1813        ctx.intern("Int16Array"),
1814        create_builtin_function(ctx, "Int16Array"),
1815    );
1816    crate::builtins::global::set_non_enumerable(
1817        global_obj,
1818        ctx.intern("Uint16Array"),
1819        create_builtin_function(ctx, "Uint16Array"),
1820    );
1821    crate::builtins::global::set_non_enumerable(
1822        global_obj,
1823        ctx.intern("Int32Array"),
1824        create_builtin_function(ctx, "Int32Array"),
1825    );
1826    crate::builtins::global::set_non_enumerable(
1827        global_obj,
1828        ctx.intern("Uint32Array"),
1829        create_builtin_function(ctx, "Uint32Array"),
1830    );
1831    crate::builtins::global::set_non_enumerable(
1832        global_obj,
1833        ctx.intern("Float32Array"),
1834        create_builtin_function(ctx, "Float32Array"),
1835    );
1836    crate::builtins::global::set_non_enumerable(
1837        global_obj,
1838        ctx.intern("Float64Array"),
1839        create_builtin_function(ctx, "Float64Array"),
1840    );
1841    crate::builtins::global::set_non_enumerable(
1842        global_obj,
1843        ctx.intern("BigInt64Array"),
1844        create_builtin_function(ctx, "BigInt64Array"),
1845    );
1846    crate::builtins::global::set_non_enumerable(
1847        global_obj,
1848        ctx.intern("BigUint64Array"),
1849        create_builtin_function(ctx, "BigUint64Array"),
1850    );
1851
1852    let mut ta_proto = JSObject::new();
1853    ta_proto.set(ctx.intern("forEach"), create_builtin_function(ctx, "ta_forEach"));
1854    ta_proto.set(ctx.intern("every"), create_builtin_function(ctx, "ta_every"));
1855    ta_proto.set(ctx.intern("some"), create_builtin_function(ctx, "ta_some"));
1856    ta_proto.set(ctx.intern("find"), create_builtin_function(ctx, "ta_find"));
1857    ta_proto.set(ctx.intern("findIndex"), create_builtin_function(ctx, "ta_findIndex"));
1858    ta_proto.set(ctx.intern("findLast"), create_builtin_function(ctx, "ta_findLast"));
1859    ta_proto.set(ctx.intern("findLastIndex"), create_builtin_function(ctx, "ta_findLastIndex"));
1860    ta_proto.set(ctx.intern("reduce"), create_builtin_function(ctx, "ta_reduce"));
1861    ta_proto.set(ctx.intern("reduceRight"), create_builtin_function(ctx, "ta_reduceRight"));
1862    ta_proto.set(ctx.intern("map"), create_builtin_function(ctx, "ta_map"));
1863    ta_proto.set(ctx.intern("filter"), create_builtin_function(ctx, "ta_filter"));
1864    ta_proto.set(ctx.intern("indexOf"), create_builtin_function(ctx, "ta_indexOf"));
1865    ta_proto.set(ctx.intern("lastIndexOf"), create_builtin_function(ctx, "ta_lastIndexOf"));
1866    ta_proto.set(ctx.intern("includes"), create_builtin_function(ctx, "ta_includes"));
1867    ta_proto.set(ctx.intern("join"), create_builtin_function(ctx, "ta_join"));
1868    ta_proto.set(ctx.intern("at"), create_builtin_function(ctx, "ta_at"));
1869    ta_proto.set(ctx.intern("values"), create_builtin_function(ctx, "ta_values"));
1870    ta_proto.set(ctx.intern("keys"), create_builtin_function(ctx, "ta_keys"));
1871    ta_proto.set(ctx.intern("entries"), create_builtin_function(ctx, "ta_entries"));
1872    ta_proto.set(ctx.intern("toString"), create_builtin_function(ctx, "ta_toString"));
1873    ta_proto.set(ctx.intern("reverse"), create_builtin_function(ctx, "ta_reverse"));
1874    ta_proto.set(ctx.intern("slice"), create_builtin_function(ctx, "ta_slice"));
1875    ta_proto.set(ctx.intern("fill"), create_builtin_function(ctx, "ta_fill"));
1876    ta_proto.set(ctx.intern("copyWithin"), create_builtin_function(ctx, "ta_copyWithin"));
1877    ta_proto.set(ctx.intern("sort"), create_builtin_function(ctx, "ta_sort"));
1878    ta_proto.set(ctx.intern("subarray"), create_builtin_function(ctx, "ta_subarray"));
1879
1880    let to_string_tag_key = crate::builtins::symbol::get_symbol_to_string_tag_prop_key(ctx);
1881    ta_proto.set(to_string_tag_key, JSValue::new_string(ctx.intern("TypedArray")));
1882
1883    if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
1884        ta_proto.prototype = Some(obj_proto_ptr);
1885    }
1886    let ta_proto_ptr = Box::into_raw(Box::new(ta_proto)) as usize;
1887    ctx.runtime_mut().gc_heap_mut().track(ta_proto_ptr);
1888    ctx.set_typed_array_prototype(ta_proto_ptr);
1889
1890    let ta_names = [
1891        "Int8Array", "Uint8Array", "Uint8ClampedArray",
1892        "Int16Array", "Uint16Array", "Int32Array", "Uint32Array",
1893        "Float32Array", "Float64Array", "BigInt64Array", "BigUint64Array",
1894    ];
1895    for name in &ta_names {
1896        let atom = ctx.intern(name);
1897        if let Some(ctor_val) = global_obj.get(atom) {
1898            if ctor_val.is_function() {
1899                let ctor_obj = ctor_val.as_object_mut();
1900                ctor_obj.set(ctx.intern("prototype"), JSValue::new_object(ta_proto_ptr));
1901                ctor_obj.set(ctx.intern("from"), create_builtin_function(ctx, "typedarray_from"));
1902                ctor_obj.set(ctx.intern("of"), create_builtin_function(ctx, "typedarray_of"));
1903                ctor_obj.set(ctx.intern("name"), JSValue::new_string(ctx.intern(name)));
1904                ctor_obj.set(ctx.intern("length"), JSValue::new_int(1));
1905                let species_fn = create_builtin_function(ctx, "typedarray_species_get");
1906                let species_sym = crate::builtins::symbol::get_symbol_species(ctx);
1907                if !species_sym.is_undefined() {
1908                    ctor_obj.set(species_sym.get_atom(), species_fn);
1909                }
1910            }
1911        }
1912    }
1913
1914    let ta_sym_tag = crate::builtins::symbol::get_symbol_to_string_tag(ctx);
1915    if !ta_sym_tag.is_undefined() {
1916        if let Some(proto_obj) = ctx.get_typed_array_prototype() {
1917            unsafe {
1918                (*proto_obj).set(ta_sym_tag.get_atom(), JSValue::new_string(ctx.intern("TypedArray")));
1919            }
1920        }
1921    }
1922}
1923
1924pub fn register_builtins(ctx: &mut JSContext) {
1925    ctx.register_builtin(
1926        "ArrayBuffer",
1927        HostFunction::ctor("ArrayBuffer", 1, array_buffer_constructor),
1928    );
1929    ctx.register_builtin(
1930        "DataView",
1931        HostFunction::ctor("DataView", 1, data_view_constructor),
1932    );
1933
1934    ctx.register_builtin(
1935        "Int8Array",
1936        HostFunction::ctor("Int8Array", 1, int8_array_constructor),
1937    );
1938    ctx.register_builtin(
1939        "Uint8Array",
1940        HostFunction::ctor("Uint8Array", 1, uint8_array_constructor),
1941    );
1942    ctx.register_builtin(
1943        "Uint8ClampedArray",
1944        HostFunction::ctor("Uint8ClampedArray", 1, uint8_clamped_array_constructor),
1945    );
1946    ctx.register_builtin(
1947        "Int16Array",
1948        HostFunction::ctor("Int16Array", 1, int16_array_constructor),
1949    );
1950    ctx.register_builtin(
1951        "Uint16Array",
1952        HostFunction::ctor("Uint16Array", 1, uint16_array_constructor),
1953    );
1954    ctx.register_builtin(
1955        "Int32Array",
1956        HostFunction::ctor("Int32Array", 1, int32_array_constructor),
1957    );
1958    ctx.register_builtin(
1959        "Uint32Array",
1960        HostFunction::ctor("Uint32Array", 1, uint32_array_constructor),
1961    );
1962    ctx.register_builtin(
1963        "Float32Array",
1964        HostFunction::ctor("Float32Array", 1, float32_array_constructor),
1965    );
1966    ctx.register_builtin(
1967        "Float64Array",
1968        HostFunction::ctor("Float64Array", 1, float64_array_constructor),
1969    );
1970    ctx.register_builtin(
1971        "BigInt64Array",
1972        HostFunction::ctor("BigInt64Array", 1, bigint64_array_constructor),
1973    );
1974    ctx.register_builtin(
1975        "BigUint64Array",
1976        HostFunction::ctor("BigUint64Array", 1, biguint64_array_constructor),
1977    );
1978
1979    ctx.register_builtin(
1980        "typedarray_buffer",
1981        HostFunction::method("buffer", 0, typed_array_get_buffer),
1982    );
1983    ctx.register_builtin(
1984        "typedarray_byteLength",
1985        HostFunction::method("byteLength", 0, typed_array_get_byte_length),
1986    );
1987    ctx.register_builtin(
1988        "typedarray_byteOffset",
1989        HostFunction::method("byteOffset", 0, typed_array_get_byte_offset),
1990    );
1991    ctx.register_builtin(
1992        "typedarray_length",
1993        HostFunction::method("length", 0, typed_array_get_length),
1994    );
1995
1996    ctx.register_builtin(
1997        "arraybuffer_byteLength",
1998        HostFunction::method("byteLength", 0, array_buffer_get_byte_length),
1999    );
2000
2001    ctx.register_builtin(
2002        "dataview_buffer",
2003        HostFunction::method("buffer", 0, data_view_get_buffer),
2004    );
2005    ctx.register_builtin(
2006        "dataview_byteLength",
2007        HostFunction::method("byteLength", 0, data_view_get_byte_length),
2008    );
2009    ctx.register_builtin(
2010        "dataview_byteOffset",
2011        HostFunction::method("byteOffset", 0, data_view_get_byte_offset),
2012    );
2013    ctx.register_builtin(
2014        "dataview_getInt8",
2015        HostFunction::method("getInt8", 1, data_view_get_int8),
2016    );
2017    ctx.register_builtin(
2018        "dataview_getUint8",
2019        HostFunction::method("getUint8", 1, data_view_get_uint8),
2020    );
2021    ctx.register_builtin(
2022        "dataview_setInt8",
2023        HostFunction::method("setInt8", 2, data_view_set_int8),
2024    );
2025    ctx.register_builtin(
2026        "dataview_setUint8",
2027        HostFunction::method("setUint8", 2, data_view_set_uint8),
2028    );
2029    ctx.register_builtin(
2030        "dataview_getInt16",
2031        HostFunction::method("getInt16", 2, data_view_get_int16),
2032    );
2033    ctx.register_builtin(
2034        "dataview_getUint16",
2035        HostFunction::method("getUint16", 2, data_view_get_uint16),
2036    );
2037    ctx.register_builtin(
2038        "dataview_getInt32",
2039        HostFunction::method("getInt32", 2, data_view_get_int32),
2040    );
2041    ctx.register_builtin(
2042        "dataview_getUint32",
2043        HostFunction::method("getUint32", 2, data_view_get_uint32),
2044    );
2045    ctx.register_builtin(
2046        "dataview_getFloat32",
2047        HostFunction::method("getFloat32", 2, data_view_get_float32),
2048    );
2049    ctx.register_builtin(
2050        "dataview_getFloat64",
2051        HostFunction::method("getFloat64", 2, data_view_get_float64),
2052    );
2053    ctx.register_builtin(
2054        "dataview_getFloat16",
2055        HostFunction::method("getFloat16", 2, data_view_get_float16),
2056    );
2057    ctx.register_builtin(
2058        "dataview_setFloat16",
2059        HostFunction::method("setFloat16", 3, data_view_set_float16),
2060    );
2061    ctx.register_builtin(
2062        "dataview_setInt16",
2063        HostFunction::method("setInt16", 3, data_view_set_int16),
2064    );
2065    ctx.register_builtin(
2066        "dataview_setUint16",
2067        HostFunction::method("setUint16", 3, data_view_set_uint16),
2068    );
2069    ctx.register_builtin(
2070        "dataview_setInt32",
2071        HostFunction::method("setInt32", 3, data_view_set_int32),
2072    );
2073    ctx.register_builtin(
2074        "dataview_setUint32",
2075        HostFunction::method("setUint32", 3, data_view_set_uint32),
2076    );
2077    ctx.register_builtin(
2078        "dataview_setFloat32",
2079        HostFunction::method("setFloat32", 3, data_view_set_float32),
2080    );
2081    ctx.register_builtin(
2082        "dataview_setFloat64",
2083        HostFunction::method("setFloat64", 3, data_view_set_float64),
2084    );
2085    ctx.register_builtin(
2086        "dataview_getBigInt64",
2087        HostFunction::method("getBigInt64", 2, data_view_get_bigint64),
2088    );
2089    ctx.register_builtin(
2090        "dataview_getBigUint64",
2091        HostFunction::method("getBigUint64", 2, data_view_get_biguint64),
2092    );
2093    ctx.register_builtin(
2094        "dataview_setBigInt64",
2095        HostFunction::method("setBigInt64", 3, data_view_set_bigint64),
2096    );
2097    ctx.register_builtin(
2098        "dataview_setBigUint64",
2099        HostFunction::method("setBigUint64", 3, data_view_set_biguint64),
2100    );
2101    ctx.register_builtin(
2102        "ta_forEach",
2103        HostFunction::method("forEach", 1, typed_array_for_each),
2104    );
2105    ctx.register_builtin(
2106        "ta_every",
2107        HostFunction::method("every", 1, typed_array_every),
2108    );
2109    ctx.register_builtin(
2110        "ta_some",
2111        HostFunction::method("some", 1, typed_array_some),
2112    );
2113    ctx.register_builtin(
2114        "ta_find",
2115        HostFunction::method("find", 1, typed_array_find),
2116    );
2117    ctx.register_builtin(
2118        "ta_findIndex",
2119        HostFunction::method("findIndex", 1, typed_array_find_index),
2120    );
2121    ctx.register_builtin(
2122        "ta_findLast",
2123        HostFunction::method("findLast", 1, typed_array_find_last),
2124    );
2125    ctx.register_builtin(
2126        "ta_findLastIndex",
2127        HostFunction::method("findLastIndex", 1, typed_array_find_last_index),
2128    );
2129    ctx.register_builtin(
2130        "ta_reduce",
2131        HostFunction::method("reduce", 1, typed_array_reduce),
2132    );
2133    ctx.register_builtin(
2134        "ta_reduceRight",
2135        HostFunction::method("reduceRight", 1, typed_array_reduce_right),
2136    );
2137    ctx.register_builtin(
2138        "ta_map",
2139        HostFunction::method("map", 1, typed_array_map),
2140    );
2141    ctx.register_builtin(
2142        "ta_filter",
2143        HostFunction::method("filter", 1, typed_array_filter),
2144    );
2145    ctx.register_builtin(
2146        "ta_indexOf",
2147        HostFunction::method("indexOf", 1, typed_array_index_of),
2148    );
2149    ctx.register_builtin(
2150        "ta_lastIndexOf",
2151        HostFunction::method("lastIndexOf", 1, typed_array_last_index_of),
2152    );
2153    ctx.register_builtin(
2154        "ta_includes",
2155        HostFunction::method("includes", 1, typed_array_includes),
2156    );
2157    ctx.register_builtin(
2158        "ta_join",
2159        HostFunction::method("join", 1, typed_array_join),
2160    );
2161    ctx.register_builtin(
2162        "ta_at",
2163        HostFunction::method("at", 1, typed_array_at),
2164    );
2165    ctx.register_builtin(
2166        "ta_values",
2167        HostFunction::method("values", 0, typed_array_values),
2168    );
2169    ctx.register_builtin(
2170        "ta_keys",
2171        HostFunction::method("keys", 0, typed_array_keys),
2172    );
2173    ctx.register_builtin(
2174        "ta_entries",
2175        HostFunction::method("entries", 0, typed_array_entries),
2176    );
2177    ctx.register_builtin(
2178        "ta_toString",
2179        HostFunction::method("toString", 0, typed_array_to_string),
2180    );
2181    ctx.register_builtin(
2182        "ta_reverse",
2183        HostFunction::method("reverse", 0, typed_array_reverse),
2184    );
2185    ctx.register_builtin(
2186        "ta_slice",
2187        HostFunction::method("slice", 2, typed_array_slice),
2188    );
2189    ctx.register_builtin(
2190        "ta_fill",
2191        HostFunction::method("fill", 1, typed_array_fill),
2192    );
2193    ctx.register_builtin(
2194        "ta_copyWithin",
2195        HostFunction::method("copyWithin", 2, typed_array_copy_within),
2196    );
2197    ctx.register_builtin(
2198        "ta_sort",
2199        HostFunction::method("sort", 1, typed_array_sort),
2200    );
2201    ctx.register_builtin(
2202        "ta_subarray",
2203        HostFunction::method("subarray", 2, typed_array_subarray),
2204    );
2205    ctx.register_builtin(
2206        "typedarray_from",
2207        HostFunction::method("from", 1, typedarray_from),
2208    );
2209    ctx.register_builtin(
2210        "typedarray_of",
2211        HostFunction::method("of", 0, typedarray_of),
2212    );
2213    ctx.register_builtin(
2214        "typedarray_species_get",
2215        HostFunction::new("get [Symbol.species]", 0, typedarray_species_get),
2216    );
2217}