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
69 obj.set(ctx.intern("buffer"), buffer);
70 obj.set(
71 ctx.intern("byteOffset"),
72 JSValue::new_int(byte_offset as i64),
73 );
74 obj.set(
75 ctx.intern("byteLength"),
76 JSValue::new_int((element_length * bytes_per_element) as i64),
77 );
78 obj.set(
79 ctx.intern("length"),
80 JSValue::new_int(element_length as i64),
81 );
82
83 let ptr = Box::into_raw(Box::new(obj)) as usize;
84 Ok(JSValue::new_object(ptr))
85}
86
87fn typed_array_from_args(
88 ctx: &mut JSContext,
89 kind: TypedArrayKind,
90 args: &[JSValue],
91) -> Result<JSValue, String> {
92 if args.is_empty() {
93 let buffer = create_array_buffer(ctx, 0);
94 return create_typed_array(ctx, kind, buffer, 0, Some(0));
95 }
96
97 let first_arg = &args[0];
98
99 if first_arg.is_object() {
100 let obj = first_arg.as_object();
101
102 if obj.obj_type() == ObjectType::ArrayBuffer {
103 let byte_offset = if args.len() > 1 {
104 args[1].get_int() as usize
105 } else {
106 0
107 };
108 let length = if args.len() > 2 {
109 Some(args[2].get_int() as usize)
110 } else {
111 None
112 };
113 return create_typed_array(ctx, kind, *first_arg, byte_offset, length);
114 }
115
116 if obj.obj_type() == ObjectType::TypedArray {
117 let src_len = obj
118 .get(ctx.intern("length"))
119 .map(|v| v.get_int() as usize)
120 .unwrap_or(0);
121
122 let bytes_per_element = kind.bytes_per_element();
123 let buffer = create_array_buffer(ctx, src_len * bytes_per_element);
124 let result = create_typed_array(ctx, kind, buffer, 0, Some(src_len))?;
125
126 return Ok(result);
127 }
128
129 let len = obj.get_array_elements().map(|e| e.len()).unwrap_or(0);
130
131 let bytes_per_element = kind.bytes_per_element();
132 let buffer = create_array_buffer(ctx, len * bytes_per_element);
133 let result = create_typed_array(ctx, kind, buffer, 0, Some(len))?;
134
135 return Ok(result);
136 }
137
138 if first_arg.is_int() || first_arg.is_float() {
139 let length = first_arg.get_int() as usize;
140 let bytes_per_element = kind.bytes_per_element();
141 let buffer = create_array_buffer(ctx, length * bytes_per_element);
142 return create_typed_array(ctx, kind, buffer, 0, Some(length));
143 }
144
145 Err("Invalid TypedArray constructor argument".to_string())
146}
147
148fn array_buffer_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
149 let length = if args.is_empty() {
150 0
151 } else {
152 args[0].get_int() as usize
153 };
154 create_array_buffer(ctx, length)
155}
156
157fn data_view_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
158 if args.is_empty() || !args[0].is_object() {
159 return JSValue::undefined();
160 }
161
162 let buffer = &args[0];
163 let byte_offset = if args.len() > 1 {
164 args[1].get_int() as usize
165 } else {
166 0
167 };
168
169 let mut obj = JSObject::new_typed(ObjectType::DataView);
170 obj.set(ctx.intern("buffer"), *buffer);
171 obj.set(
172 ctx.intern("byteOffset"),
173 JSValue::new_int(byte_offset as i64),
174 );
175
176 if let Some(buffer_obj) = if buffer.is_object() {
177 Some(buffer.as_object())
178 } else {
179 None
180 } {
181 if let Some(data) = get_array_buffer_data(&buffer_obj) {
182 let byte_length = if args.len() > 2 {
183 args[2].get_int() as usize
184 } else {
185 data.data.len().saturating_sub(byte_offset)
186 };
187 obj.set(
188 ctx.intern("byteLength"),
189 JSValue::new_int(byte_length as i64),
190 );
191 }
192 }
193
194 let ptr = Box::into_raw(Box::new(obj)) as usize;
195 JSValue::new_object(ptr)
196}
197
198fn int8_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
199 typed_array_from_args(ctx, TypedArrayKind::Int8, args).unwrap_or(JSValue::undefined())
200}
201
202fn uint8_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
203 typed_array_from_args(ctx, TypedArrayKind::Uint8, args).unwrap_or(JSValue::undefined())
204}
205
206fn uint8_clamped_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
207 typed_array_from_args(ctx, TypedArrayKind::Uint8Clamped, args).unwrap_or(JSValue::undefined())
208}
209
210fn int16_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
211 typed_array_from_args(ctx, TypedArrayKind::Int16, args).unwrap_or(JSValue::undefined())
212}
213
214fn uint16_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
215 typed_array_from_args(ctx, TypedArrayKind::Uint16, args).unwrap_or(JSValue::undefined())
216}
217
218fn int32_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
219 typed_array_from_args(ctx, TypedArrayKind::Int32, args).unwrap_or(JSValue::undefined())
220}
221
222fn uint32_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
223 typed_array_from_args(ctx, TypedArrayKind::Uint32, args).unwrap_or(JSValue::undefined())
224}
225
226fn float32_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
227 typed_array_from_args(ctx, TypedArrayKind::Float32, args).unwrap_or(JSValue::undefined())
228}
229
230fn float64_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
231 typed_array_from_args(ctx, TypedArrayKind::Float64, args).unwrap_or(JSValue::undefined())
232}
233
234fn bigint64_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
235 typed_array_from_args(ctx, TypedArrayKind::BigInt64, args).unwrap_or(JSValue::undefined())
236}
237
238fn biguint64_array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
239 typed_array_from_args(ctx, TypedArrayKind::BigUint64, args).unwrap_or(JSValue::undefined())
240}
241
242fn typed_array_get_buffer(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
243 if args.is_empty() || !args[0].is_object() {
244 return JSValue::undefined();
245 }
246 let obj = args[0].as_object();
247 obj.get(ctx.intern("buffer"))
248 .unwrap_or(JSValue::undefined())
249}
250
251fn typed_array_get_byte_length(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
252 if args.is_empty() || !args[0].is_object() {
253 return JSValue::undefined();
254 }
255 let obj = args[0].as_object();
256 obj.get(ctx.intern("byteLength"))
257 .unwrap_or(JSValue::undefined())
258}
259
260fn typed_array_get_byte_offset(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
261 if args.is_empty() || !args[0].is_object() {
262 return JSValue::undefined();
263 }
264 let obj = args[0].as_object();
265 obj.get(ctx.intern("byteOffset"))
266 .unwrap_or(JSValue::undefined())
267}
268
269fn typed_array_get_length(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
270 if args.is_empty() || !args[0].is_object() {
271 return JSValue::undefined();
272 }
273 let obj = args[0].as_object();
274 obj.get(ctx.intern("length"))
275 .unwrap_or(JSValue::undefined())
276}
277
278fn array_buffer_get_byte_length(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
279 if args.is_empty() || !args[0].is_object() {
280 return JSValue::undefined();
281 }
282 let obj = args[0].as_object();
283 obj.get(ctx.intern("byteLength"))
284 .unwrap_or(JSValue::undefined())
285}
286
287fn data_view_get_buffer(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
288 if args.is_empty() || !args[0].is_object() {
289 return JSValue::undefined();
290 }
291 let obj = args[0].as_object();
292 obj.get(ctx.intern("buffer"))
293 .unwrap_or(JSValue::undefined())
294}
295
296fn data_view_get_byte_length(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
297 if args.is_empty() || !args[0].is_object() {
298 return JSValue::undefined();
299 }
300 let obj = args[0].as_object();
301 obj.get(ctx.intern("byteLength"))
302 .unwrap_or(JSValue::undefined())
303}
304
305fn data_view_get_byte_offset(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
306 if args.is_empty() || !args[0].is_object() {
307 return JSValue::undefined();
308 }
309 let obj = args[0].as_object();
310 obj.get(ctx.intern("byteOffset"))
311 .unwrap_or(JSValue::undefined())
312}
313
314fn data_view_get_int8(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
315 if args.len() < 2 {
316 return JSValue::undefined();
317 }
318 let view = &args[0];
319 let offset = args[1].get_int() as usize;
320
321 if !view.is_object() {
322 return JSValue::undefined();
323 }
324
325 let view_obj = view.as_object();
326 let buffer = view_obj.get(ctx.intern("buffer"));
327 let byte_offset = view_obj
328 .get(ctx.intern("byteOffset"))
329 .map(|v| v.get_int() as usize)
330 .unwrap_or(0);
331
332 if let Some(buf) = buffer {
333 if let Some(buf_obj) = if buf.is_object() {
334 Some(buf.as_object())
335 } else {
336 None
337 } {
338 if let Some(data) = get_array_buffer_data(&buf_obj) {
339 let idx = byte_offset + offset;
340 if idx < data.data.len() {
341 return JSValue::new_int(data.data[idx] as i8 as i64);
342 }
343 }
344 }
345 }
346 JSValue::undefined()
347}
348
349fn data_view_get_uint8(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
350 if args.len() < 2 {
351 return JSValue::undefined();
352 }
353 let view = &args[0];
354 let offset = args[1].get_int() as usize;
355
356 if !view.is_object() {
357 return JSValue::undefined();
358 }
359
360 let view_obj = view.as_object();
361 let buffer = view_obj.get(ctx.intern("buffer"));
362 let byte_offset = view_obj
363 .get(ctx.intern("byteOffset"))
364 .map(|v| v.get_int() as usize)
365 .unwrap_or(0);
366
367 if let Some(buf) = buffer {
368 if let Some(buf_obj) = if buf.is_object() {
369 Some(buf.as_object())
370 } else {
371 None
372 } {
373 if let Some(data) = get_array_buffer_data(&buf_obj) {
374 let idx = byte_offset + offset;
375 if idx < data.data.len() {
376 return JSValue::new_int(data.data[idx] as i64);
377 }
378 }
379 }
380 }
381 JSValue::undefined()
382}
383
384fn data_view_set_int8(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
385 if args.len() < 3 {
386 return JSValue::undefined();
387 }
388 let view = &args[0];
389 let offset = args[1].get_int() as usize;
390 let value = args[2].get_int() as i8 as u8;
391
392 if !view.is_object() {
393 return JSValue::undefined();
394 }
395
396 let view_obj = view.as_object();
397 let buffer = view_obj.get(ctx.intern("buffer"));
398 let byte_offset = view_obj
399 .get(ctx.intern("byteOffset"))
400 .map(|v| v.get_int() as usize)
401 .unwrap_or(0);
402
403 if let Some(buf) = buffer {
404 if let Some(buf_obj) = if buf.is_object() {
405 Some(buf.as_object())
406 } else {
407 None
408 } {
409 if let Some(data) = get_array_buffer_data(&buf_obj) {
410 let idx = byte_offset + offset;
411 if idx < data.data.len() {
412 data.data[idx] = value;
413 }
414 }
415 }
416 }
417 JSValue::undefined()
418}
419
420fn data_view_set_uint8(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
421 if args.len() < 3 {
422 return JSValue::undefined();
423 }
424 let view = &args[0];
425 let offset = args[1].get_int() as usize;
426 let value = args[2].get_int() as u8;
427
428 if !view.is_object() {
429 return JSValue::undefined();
430 }
431
432 let view_obj = view.as_object();
433 let buffer = view_obj.get(ctx.intern("buffer"));
434 let byte_offset = view_obj
435 .get(ctx.intern("byteOffset"))
436 .map(|v| v.get_int() as usize)
437 .unwrap_or(0);
438
439 if let Some(buf) = buffer {
440 if let Some(buf_obj) = if buf.is_object() {
441 Some(buf.as_object())
442 } else {
443 None
444 } {
445 if let Some(data) = get_array_buffer_data(&buf_obj) {
446 let idx = byte_offset + offset;
447 if idx < data.data.len() {
448 data.data[idx] = value;
449 }
450 }
451 }
452 }
453 JSValue::undefined()
454}
455
456fn create_builtin_function(ctx: &mut JSContext, name: &str) -> JSValue {
457 let mut func = crate::object::function::JSFunction::new_builtin(ctx.intern(name), 1);
458 func.set_builtin_marker(ctx, name);
459 let ptr = Box::into_raw(Box::new(func)) as usize;
460 ctx.runtime_mut().gc_heap_mut().track_function(ptr);
461 JSValue::new_function(ptr)
462}
463
464pub fn init_typed_array(ctx: &mut JSContext) {
465 let global = ctx.global();
466 if !global.is_object() {
467 return;
468 }
469 let global_obj = global.as_object_mut();
470
471 global_obj.set(
472 ctx.intern("ArrayBuffer"),
473 create_builtin_function(ctx, "ArrayBuffer"),
474 );
475
476 global_obj.set(
477 ctx.intern("DataView"),
478 create_builtin_function(ctx, "DataView"),
479 );
480
481 global_obj.set(
482 ctx.intern("Int8Array"),
483 create_builtin_function(ctx, "Int8Array"),
484 );
485 global_obj.set(
486 ctx.intern("Uint8Array"),
487 create_builtin_function(ctx, "Uint8Array"),
488 );
489 global_obj.set(
490 ctx.intern("Uint8ClampedArray"),
491 create_builtin_function(ctx, "Uint8ClampedArray"),
492 );
493 global_obj.set(
494 ctx.intern("Int16Array"),
495 create_builtin_function(ctx, "Int16Array"),
496 );
497 global_obj.set(
498 ctx.intern("Uint16Array"),
499 create_builtin_function(ctx, "Uint16Array"),
500 );
501 global_obj.set(
502 ctx.intern("Int32Array"),
503 create_builtin_function(ctx, "Int32Array"),
504 );
505 global_obj.set(
506 ctx.intern("Uint32Array"),
507 create_builtin_function(ctx, "Uint32Array"),
508 );
509 global_obj.set(
510 ctx.intern("Float32Array"),
511 create_builtin_function(ctx, "Float32Array"),
512 );
513 global_obj.set(
514 ctx.intern("Float64Array"),
515 create_builtin_function(ctx, "Float64Array"),
516 );
517 global_obj.set(
518 ctx.intern("BigInt64Array"),
519 create_builtin_function(ctx, "BigInt64Array"),
520 );
521 global_obj.set(
522 ctx.intern("BigUint64Array"),
523 create_builtin_function(ctx, "BigUint64Array"),
524 );
525}
526
527pub fn register_builtins(ctx: &mut JSContext) {
528 ctx.register_builtin(
529 "ArrayBuffer",
530 HostFunction::new("ArrayBuffer", 1, array_buffer_constructor),
531 );
532 ctx.register_builtin(
533 "DataView",
534 HostFunction::new("DataView", 1, data_view_constructor),
535 );
536
537 ctx.register_builtin(
538 "Int8Array",
539 HostFunction::new("Int8Array", 1, int8_array_constructor),
540 );
541 ctx.register_builtin(
542 "Uint8Array",
543 HostFunction::new("Uint8Array", 1, uint8_array_constructor),
544 );
545 ctx.register_builtin(
546 "Uint8ClampedArray",
547 HostFunction::new("Uint8ClampedArray", 1, uint8_clamped_array_constructor),
548 );
549 ctx.register_builtin(
550 "Int16Array",
551 HostFunction::new("Int16Array", 1, int16_array_constructor),
552 );
553 ctx.register_builtin(
554 "Uint16Array",
555 HostFunction::new("Uint16Array", 1, uint16_array_constructor),
556 );
557 ctx.register_builtin(
558 "Int32Array",
559 HostFunction::new("Int32Array", 1, int32_array_constructor),
560 );
561 ctx.register_builtin(
562 "Uint32Array",
563 HostFunction::new("Uint32Array", 1, uint32_array_constructor),
564 );
565 ctx.register_builtin(
566 "Float32Array",
567 HostFunction::new("Float32Array", 1, float32_array_constructor),
568 );
569 ctx.register_builtin(
570 "Float64Array",
571 HostFunction::new("Float64Array", 1, float64_array_constructor),
572 );
573 ctx.register_builtin(
574 "BigInt64Array",
575 HostFunction::new("BigInt64Array", 1, bigint64_array_constructor),
576 );
577 ctx.register_builtin(
578 "BigUint64Array",
579 HostFunction::new("BigUint64Array", 1, biguint64_array_constructor),
580 );
581
582 ctx.register_builtin(
583 "typedarray_buffer",
584 HostFunction::new("buffer", 0, typed_array_get_buffer),
585 );
586 ctx.register_builtin(
587 "typedarray_byteLength",
588 HostFunction::new("byteLength", 0, typed_array_get_byte_length),
589 );
590 ctx.register_builtin(
591 "typedarray_byteOffset",
592 HostFunction::new("byteOffset", 0, typed_array_get_byte_offset),
593 );
594 ctx.register_builtin(
595 "typedarray_length",
596 HostFunction::new("length", 0, typed_array_get_length),
597 );
598
599 ctx.register_builtin(
600 "arraybuffer_byteLength",
601 HostFunction::new("byteLength", 0, array_buffer_get_byte_length),
602 );
603
604 ctx.register_builtin(
605 "dataview_buffer",
606 HostFunction::new("buffer", 0, data_view_get_buffer),
607 );
608 ctx.register_builtin(
609 "dataview_byteLength",
610 HostFunction::new("byteLength", 0, data_view_get_byte_length),
611 );
612 ctx.register_builtin(
613 "dataview_byteOffset",
614 HostFunction::new("byteOffset", 0, data_view_get_byte_offset),
615 );
616 ctx.register_builtin(
617 "dataview_getInt8",
618 HostFunction::new("getInt8", 1, data_view_get_int8),
619 );
620 ctx.register_builtin(
621 "dataview_getUint8",
622 HostFunction::new("getUint8", 1, data_view_get_uint8),
623 );
624 ctx.register_builtin(
625 "dataview_setInt8",
626 HostFunction::new("setInt8", 2, data_view_set_int8),
627 );
628 ctx.register_builtin(
629 "dataview_setUint8",
630 HostFunction::new("setUint8", 2, data_view_set_uint8),
631 );
632}