Skip to content
File

Blob: src/rust/jsg-test/tests/arrays.rs

rust1444 lines
1// Copyright (c) 2026 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5use jsg::Number;
6use jsg::ToJS;
7use jsg_macros::jsg_method;
8use jsg_macros::jsg_resource;
9use jsg_macros::jsg_struct;
10 
11#[jsg_struct]
12struct Person {
13 pub name: String,
14 pub age: Number,
15}
16 
17#[jsg_resource]
18struct ArrayResource;
19 
20#[jsg_resource]
21impl ArrayResource {
22 #[jsg_method]
23 pub fn sum(&self, numbers: Vec<Number>) -> Number {
24 Number::new(numbers.iter().map(jsg::Number::value).sum())
25 }
26 
27 #[jsg_method]
28 pub fn sum_slice(&self, numbers: &[Number]) -> Number {
29 Number::new(numbers.iter().map(jsg::Number::value).sum())
30 }
31 
32 #[jsg_method]
33 pub fn join_strings(&self, strings: &[String]) -> String {
34 strings.join("-")
35 }
36 
37 #[jsg_method]
38 pub fn double(&self, numbers: Vec<Number>) -> Vec<Number> {
39 numbers
40 .into_iter()
41 .map(|n| Number::new(n.value() * 2.0))
42 .collect()
43 }
44 
45 #[jsg_method]
46 pub fn concat_strings(&self, strings: Vec<String>) -> String {
47 strings.join(", ")
48 }
49 
50 #[jsg_method]
51 pub fn split_string(&self, s: &str) -> Vec<String> {
52 s.split(',').map(|s| s.trim().to_owned()).collect()
53 }
54 
55 #[jsg_method]
56 pub fn filter_positive(&self, numbers: Vec<Number>) -> Vec<Number> {
57 numbers.into_iter().filter(|n| n.value() > 0.0).collect()
58 }
59 
60 #[jsg_method]
61 pub fn reverse_bytes(&self, bytes: Vec<u8>) -> Vec<u8> {
62 bytes.into_iter().rev().collect()
63 }
64 
65 #[jsg_method]
66 pub fn reverse_bytes_slice(&self, bytes: &[u8]) -> Vec<u8> {
67 bytes.iter().copied().rev().collect()
68 }
69 
70 #[jsg_method]
71 pub fn sum_i32(&self, numbers: Vec<i32>) -> Number {
72 Number::new(numbers.iter().map(|&n| f64::from(n)).sum())
73 }
74 
75 #[jsg_method]
76 pub fn sum_i32_slice(&self, numbers: &[i32]) -> Number {
77 Number::new(numbers.iter().map(|&n| f64::from(n)).sum())
78 }
79 
80 #[jsg_method]
81 pub fn filter_adults(&self, people: Vec<Person>) -> Vec<Person> {
82 people
83 .into_iter()
84 .filter(|p| p.age.value() >= 18.0)
85 .collect()
86 }
87 
88 // Float32Array methods
89 #[jsg_method]
90 pub fn sum_f32(&self, numbers: Vec<f32>) -> Number {
91 Number::new(numbers.iter().map(|&n| f64::from(n)).sum())
92 }
93 
94 #[jsg_method]
95 pub fn sum_f32_slice(&self, numbers: &[f32]) -> Number {
96 Number::new(numbers.iter().map(|&n| f64::from(n)).sum())
97 }
98 
99 #[jsg_method]
100 pub fn double_f32(&self, numbers: Vec<f32>) -> Vec<f32> {
101 numbers.into_iter().map(|n| n * 2.0).collect()
102 }
103 
104 // Float64Array methods
105 #[jsg_method]
106 pub fn sum_f64(&self, numbers: Vec<f64>) -> Number {
107 Number::new(numbers.iter().sum())
108 }
109 
110 #[jsg_method]
111 pub fn sum_f64_slice(&self, numbers: &[f64]) -> Number {
112 Number::new(numbers.iter().sum())
113 }
114 
115 #[jsg_method]
116 pub fn double_f64(&self, numbers: Vec<f64>) -> Vec<f64> {
117 numbers.into_iter().map(|n| n * 2.0).collect()
118 }
119 
120 // BigInt64Array methods
121 #[jsg_method]
122 pub fn sum_i64(&self, numbers: Vec<i64>) -> Number {
123 #[expect(clippy::cast_precision_loss)]
124 Number::new(numbers.iter().map(|&n| n as f64).sum())
125 }
126 
127 #[jsg_method]
128 pub fn sum_i64_slice(&self, numbers: &[i64]) -> Number {
129 #[expect(clippy::cast_precision_loss)]
130 Number::new(numbers.iter().map(|&n| n as f64).sum())
131 }
132 
133 #[jsg_method]
134 pub fn double_i64(&self, numbers: Vec<i64>) -> Vec<i64> {
135 numbers.into_iter().map(|n| n * 2).collect()
136 }
137 
138 // BigUint64Array methods
139 #[jsg_method]
140 pub fn sum_u64(&self, numbers: Vec<u64>) -> Number {
141 #[expect(clippy::cast_precision_loss)]
142 Number::new(numbers.iter().map(|&n| n as f64).sum())
143 }
144 
145 #[jsg_method]
146 pub fn sum_u64_slice(&self, numbers: &[u64]) -> Number {
147 #[expect(clippy::cast_precision_loss)]
148 Number::new(numbers.iter().map(|&n| n as f64).sum())
149 }
150 
151 #[jsg_method]
152 pub fn double_u64(&self, numbers: Vec<u64>) -> Vec<u64> {
153 numbers.into_iter().map(|n| n * 2).collect()
154 }
155}
156 
157#[test]
158fn resource_accepts_array_parameter() {
159 let harness = crate::Harness::new();
160 harness.run_in_context(|lock, ctx| {
161 let resource = jsg::Rc::new(ArrayResource);
162 let wrapped = resource.to_js(lock);
163 ctx.set_global("arr", wrapped);
164 
165 let result: Number = ctx.eval(lock, "arr.sum([1, 2, 3, 4, 5])").unwrap();
166 assert!((result.value() - 15.0).abs() < f64::EPSILON);
167 
168 let result: String = ctx
169 .eval(lock, "arr.concatStrings(['hello', 'world'])")
170 .unwrap();
171 assert_eq!(result, "hello, world");
172 Ok(())
173 });
174}
175 
176#[test]
177fn resource_accepts_slice_parameter() {
178 let harness = crate::Harness::new();
179 harness.run_in_context(|lock, ctx| {
180 let resource = jsg::Rc::new(ArrayResource);
181 let wrapped = resource.to_js(lock);
182 ctx.set_global("arr", wrapped);
183 
184 let result: Number = ctx.eval(lock, "arr.sumSlice([1, 2, 3, 4, 5])").unwrap();
185 assert!((result.value() - 15.0).abs() < f64::EPSILON);
186 
187 let result: String = ctx.eval(lock, "arr.joinStrings(['a', 'b', 'c'])").unwrap();
188 assert_eq!(result, "a-b-c");
189 
190 let result: Number = ctx.eval(lock, "arr.sumSlice([])").unwrap();
191 assert!((result.value() - 0.0).abs() < f64::EPSILON);
192 Ok(())
193 });
194}
195 
196#[test]
197fn resource_returns_array() {
198 let harness = crate::Harness::new();
199 harness.run_in_context(|lock, ctx| {
200 let resource = jsg::Rc::new(ArrayResource);
201 let wrapped = resource.to_js(lock);
202 ctx.set_global("arr", wrapped);
203 
204 let result: Vec<Number> = ctx.eval(lock, "arr.double([1, 2, 3])").unwrap();
205 let values: Vec<f64> = result.iter().map(jsg::Number::value).collect();
206 assert_eq!(values, vec![2.0, 4.0, 6.0]);
207 
208 let result: Vec<String> = ctx.eval(lock, "arr.splitString('a, b, c')").unwrap();
209 assert_eq!(result, vec!["a", "b", "c"]);
210 
211 let result: Vec<Number> = ctx
212 .eval(lock, "arr.filterPositive([-1, 2, -3, 4, 0])")
213 .unwrap();
214 let values: Vec<f64> = result.iter().map(jsg::Number::value).collect();
215 assert_eq!(values, vec![2.0, 4.0]);
216 Ok(())
217 });
218}
219 
220#[test]
221fn resource_accepts_typed_array_parameter() {
222 let harness = crate::Harness::new();
223 harness.run_in_context(|lock, ctx| {
224 let resource = jsg::Rc::new(ArrayResource);
225 let wrapped = resource.to_js(lock);
226 ctx.set_global("arr", wrapped);
227 
228 let result: Vec<u8> = ctx
229 .eval(lock, "arr.reverseBytes(new Uint8Array([1, 2, 3]))")
230 .unwrap();
231 assert_eq!(result, vec![3, 2, 1]);
232 
233 let result: Number = ctx
234 .eval(lock, "arr.sumI32(new Int32Array([-10, 20, -5]))")
235 .unwrap();
236 assert!((result.value() - 5.0).abs() < f64::EPSILON);
237 Ok(())
238 });
239}
240 
241#[test]
242fn resource_returns_typed_array() {
243 let harness = crate::Harness::new();
244 harness.run_in_context(|lock, ctx| {
245 let resource = jsg::Rc::new(ArrayResource);
246 let wrapped = resource.to_js(lock);
247 ctx.set_global("arr", wrapped);
248 
249 let is_u8: bool = ctx
250 .eval(
251 lock,
252 "arr.reverseBytes(new Uint8Array([1, 2, 3])) instanceof Uint8Array",
253 )
254 .unwrap();
255 assert!(is_u8);
256 Ok(())
257 });
258}
259 
260#[test]
261fn resource_accepts_typed_array_slice_parameter() {
262 let harness = crate::Harness::new();
263 harness.run_in_context(|lock, ctx| {
264 let resource = jsg::Rc::new(ArrayResource);
265 let wrapped = resource.to_js(lock);
266 ctx.set_global("arr", wrapped);
267 
268 // Test &[u8] accepts Uint8Array
269 let result: Vec<u8> = ctx
270 .eval(lock, "arr.reverseBytesSlice(new Uint8Array([1, 2, 3]))")
271 .unwrap();
272 assert_eq!(result, vec![3, 2, 1]);
273 
274 // Test &[i32] accepts Int32Array
275 let result: Number = ctx
276 .eval(lock, "arr.sumI32Slice(new Int32Array([-10, 20, -5]))")
277 .unwrap();
278 assert!((result.value() - 5.0).abs() < f64::EPSILON);
279 
280 // Test empty TypedArray
281 let result: Vec<u8> = ctx
282 .eval(lock, "arr.reverseBytesSlice(new Uint8Array([]))")
283 .unwrap();
284 assert!(result.is_empty());
285 
286 Ok(())
287 });
288}
289 
290#[test]
291fn typed_array_slice_rejects_wrong_type() {
292 let harness = crate::Harness::new();
293 harness.run_in_context(|lock, ctx| {
294 let resource = jsg::Rc::new(ArrayResource);
295 let wrapped = resource.to_js(lock);
296 ctx.set_global("arr", wrapped);
297 
298 // &[u8] should reject Int8Array
299 let result: Result<Vec<u8>, _> =
300 ctx.eval(lock, "arr.reverseBytesSlice(new Int8Array([1, 2, 3]))");
301 assert!(result.is_err());
302 
303 // &[u8] should reject regular Array
304 let result: Result<Vec<u8>, _> = ctx.eval(lock, "arr.reverseBytesSlice([1, 2, 3])");
305 assert!(result.is_err());
306 
307 // &[i32] should reject Uint32Array
308 let result: Result<Number, _> =
309 ctx.eval(lock, "arr.sumI32Slice(new Uint32Array([1, 2, 3]))");
310 assert!(result.is_err());
311 
312 // &[i32] should reject regular Array
313 let result: Result<Number, _> = ctx.eval(lock, "arr.sumI32Slice([1, 2, 3])");
314 assert!(result.is_err());
315 
316 Ok(())
317 });
318}
319 
320#[test]
321fn resource_accepts_and_returns_struct_array() {
322 let harness = crate::Harness::new();
323 harness.run_in_context(|lock, ctx| {
324 let resource = jsg::Rc::new(ArrayResource);
325 let wrapped = resource.to_js(lock);
326 ctx.set_global("arr", wrapped);
327 
328 let result: Vec<Person> = ctx
329 .eval(
330 lock,
331 "arr.filterAdults([{name: 'Alice', age: 25}, {name: 'Bob', age: 15}, {name: 'Charlie', age: 30}])",
332 )
333 .unwrap();
334 assert_eq!(result.len(), 2);
335 assert_eq!(result[0].name, "Alice");
336 assert_eq!(result[1].name, "Charlie");
337 Ok(())
338 });
339}
340 
341#[test]
342fn vec_to_js_creates_array() {
343 let harness = crate::Harness::new();
344 harness.run_in_context(|lock, ctx| {
345 let vec = vec!["hello".to_owned(), "world".to_owned()];
346 let js_val = vec.to_js(lock);
347 ctx.set_global("arr", js_val);
348 
349 let is_array: bool = ctx.eval(lock, "Array.isArray(arr)").unwrap();
350 assert!(is_array);
351 let length: Number = ctx.eval(lock, "arr.length").unwrap();
352 assert!((length.value() - 2.0).abs() < f64::EPSILON);
353 let first: String = ctx.eval(lock, "arr[0]").unwrap();
354 assert_eq!(first, "hello");
355 
356 let vec = vec![Number::new(1.5), Number::new(2.5), Number::new(3.5)];
357 let js_val = vec.to_js(lock);
358 ctx.set_global("nums", js_val);
359 
360 let sum: Number = ctx.eval(lock, "nums[0] + nums[1] + nums[2]").unwrap();
361 assert!((sum.value() - 7.5).abs() < f64::EPSILON);
362 
363 let vec = vec![true, false, true];
364 let js_val = vec.to_js(lock);
365 ctx.set_global("bools", js_val);
366 
367 let result: Vec<bool> = ctx.eval(lock, "bools").unwrap();
368 assert_eq!(result, vec![true, false, true]);
369 Ok(())
370 });
371}
372 
373#[test]
374fn vec_from_js_parses_array() {
375 let harness = crate::Harness::new();
376 harness.run_in_context(|lock, ctx| {
377 let strings: Vec<String> = ctx.eval(lock, "['a', 'b', 'c']").unwrap();
378 assert_eq!(strings, vec!["a", "b", "c"]);
379 
380 let numbers: Vec<Number> = ctx.eval(lock, "[1, 2, 3]").unwrap();
381 let values: Vec<f64> = numbers.iter().map(jsg::Number::value).collect();
382 assert_eq!(values, vec![1.0, 2.0, 3.0]);
383 Ok(())
384 });
385}
386 
387#[test]
388fn vec_empty_roundtrip() {
389 let harness = crate::Harness::new();
390 harness.run_in_context(|lock, ctx| {
391 let vec: Vec<String> = vec![];
392 let js_val = vec.to_js(lock);
393 ctx.set_global("arr", js_val);
394 
395 let length: Number = ctx.eval(lock, "arr.length").unwrap();
396 assert!(length.value().abs() < f64::EPSILON);
397 
398 let result: Vec<String> = ctx.eval(lock, "arr").unwrap();
399 assert!(result.is_empty());
400 Ok(())
401 });
402}
403 
404#[test]
405fn vec_nested_arrays() {
406 let harness = crate::Harness::new();
407 harness.run_in_context(|lock, ctx| {
408 let nested = vec![
409 vec![Number::new(1.0), Number::new(2.0)],
410 vec![Number::new(3.0), Number::new(4.0)],
411 ];
412 let js_val = nested.to_js(lock);
413 ctx.set_global("matrix", js_val);
414 
415 let first_row_sum: Number = ctx.eval(lock, "matrix[0][0] + matrix[0][1]").unwrap();
416 assert!((first_row_sum.value() - 3.0).abs() < f64::EPSILON);
417 
418 let second_row_sum: Number = ctx.eval(lock, "matrix[1][0] + matrix[1][1]").unwrap();
419 assert!((second_row_sum.value() - 7.0).abs() < f64::EPSILON);
420 Ok(())
421 });
422}
423 
424#[test]
425fn vec_from_non_array_returns_error() {
426 let harness = crate::Harness::new();
427 harness.run_in_context(|lock, ctx| {
428 let result: Result<Vec<String>, _> = ctx.eval(lock, "'not an array'");
429 assert!(result.is_err());
430 Ok(())
431 });
432}
433 
434#[test]
435fn typed_array_to_js() {
436 let harness = crate::Harness::new();
437 harness.run_in_context(|lock, ctx| {
438 let vec: Vec<u8> = vec![1, 2, 255];
439 ctx.set_global("u8_arr", vec.to_js(lock));
440 let check: bool = ctx.eval(lock, "u8_arr instanceof Uint8Array").unwrap();
441 assert!(check);
442 let val: Number = ctx.eval(lock, "u8_arr[2]").unwrap();
443 assert!((val.value() - 255.0).abs() < f64::EPSILON);
444 
445 let vec: Vec<u16> = vec![1, 2, 65535];
446 ctx.set_global("u16_arr", vec.to_js(lock));
447 let check: bool = ctx.eval(lock, "u16_arr instanceof Uint16Array").unwrap();
448 assert!(check);
449 let val: Number = ctx.eval(lock, "u16_arr[2]").unwrap();
450 assert!((val.value() - 65535.0).abs() < f64::EPSILON);
451 
452 let vec: Vec<u32> = vec![1, 2, 4_294_967_295];
453 ctx.set_global("u32_arr", vec.to_js(lock));
454 let check: bool = ctx.eval(lock, "u32_arr instanceof Uint32Array").unwrap();
455 assert!(check);
456 let val: Number = ctx.eval(lock, "u32_arr[2]").unwrap();
457 assert!((val.value() - 4_294_967_295.0).abs() < f64::EPSILON);
458 
459 let vec: Vec<i8> = vec![-128, 0, 127];
460 ctx.set_global("i8_arr", vec.to_js(lock));
461 let check: bool = ctx.eval(lock, "i8_arr instanceof Int8Array").unwrap();
462 assert!(check);
463 let val: Number = ctx.eval(lock, "i8_arr[0]").unwrap();
464 assert!((val.value() - (-128.0)).abs() < f64::EPSILON);
465 
466 let vec: Vec<i16> = vec![-32768, 0, 32767];
467 ctx.set_global("i16_arr", vec.to_js(lock));
468 let check: bool = ctx.eval(lock, "i16_arr instanceof Int16Array").unwrap();
469 assert!(check);
470 let val: Number = ctx.eval(lock, "i16_arr[0]").unwrap();
471 assert!((val.value() - (-32768.0)).abs() < f64::EPSILON);
472 
473 let vec: Vec<i32> = vec![-2_147_483_648, 0, 2_147_483_647];
474 ctx.set_global("i32_arr", vec.to_js(lock));
475 let check: bool = ctx.eval(lock, "i32_arr instanceof Int32Array").unwrap();
476 assert!(check);
477 let val: Number = ctx.eval(lock, "i32_arr[0]").unwrap();
478 assert!((val.value() - (-2_147_483_648.0)).abs() < f64::EPSILON);
479 
480 let is_array: bool = ctx.eval(lock, "Array.isArray(u8_arr)").unwrap();
481 assert!(!is_array);
482 Ok(())
483 });
484}
485 
486#[test]
487fn typed_array_from_js() {
488 let harness = crate::Harness::new();
489 harness.run_in_context(|lock, ctx| {
490 let u8_arr: Vec<u8> = ctx.eval(lock, "new Uint8Array([10, 20, 255])").unwrap();
491 assert_eq!(u8_arr, vec![10, 20, 255]);
492 
493 let u16_arr: Vec<u16> = ctx
494 .eval(lock, "new Uint16Array([100, 200, 65535])")
495 .unwrap();
496 assert_eq!(u16_arr, vec![100, 200, 65535]);
497 
498 let u32_arr: Vec<u32> = ctx
499 .eval(lock, "new Uint32Array([1000, 2000, 4294967295])")
500 .unwrap();
501 assert_eq!(u32_arr, vec![1000, 2000, 4_294_967_295]);
502 
503 let i8_arr: Vec<i8> = ctx.eval(lock, "new Int8Array([-128, 0, 127])").unwrap();
504 assert_eq!(i8_arr, vec![-128, 0, 127]);
505 
506 let i16_arr: Vec<i16> = ctx
507 .eval(lock, "new Int16Array([-32768, 0, 32767])")
508 .unwrap();
509 assert_eq!(i16_arr, vec![-32768, 0, 32767]);
510 
511 let i32_arr: Vec<i32> = ctx
512 .eval(lock, "new Int32Array([-2147483648, 0, 2147483647])")
513 .unwrap();
514 assert_eq!(i32_arr, vec![-2_147_483_648, 0, 2_147_483_647]);
515 Ok(())
516 });
517}
518 
519#[test]
520fn typed_array_empty_roundtrip() {
521 let harness = crate::Harness::new();
522 harness.run_in_context(|lock, ctx| {
523 let vec: Vec<u8> = vec![];
524 ctx.set_global("arr", vec.to_js(lock));
525 
526 let length: Number = ctx.eval(lock, "arr.length").unwrap();
527 assert!(length.value().abs() < f64::EPSILON);
528 
529 let result: Vec<u8> = ctx.eval(lock, "arr").unwrap();
530 assert!(result.is_empty());
531 Ok(())
532 });
533}
534 
535#[test]
536fn typed_array_type_mismatch_returns_error() {
537 let harness = crate::Harness::new();
538 harness.run_in_context(|lock, ctx| {
539 let result: Result<Vec<u8>, _> = ctx.eval(lock, "new Int8Array([1, 2, 3])");
540 assert!(result.is_err());
541 
542 let result: Result<Vec<i32>, _> = ctx.eval(lock, "new Uint32Array([1, 2, 3])");
543 assert!(result.is_err());
544 
545 let result: Result<Vec<u8>, _> = ctx.eval(lock, "[1, 2, 3]");
546 assert!(result.is_err());
547 
548 let result: Result<Vec<u8>, _> = ctx.eval(lock, "'hello'");
549 assert!(result.is_err());
550 Ok(())
551 });
552}
553 
554#[test]
555fn large_typed_array_roundtrip() {
556 let harness = crate::Harness::new();
557 harness.run_in_context(|lock, ctx| {
558 let vec: Vec<u8> = (0_u32..65536).map(|i| (i % 256) as u8).collect();
559 ctx.set_global("arr", vec.clone().to_js(lock));
560 
561 let length: Number = ctx.eval(lock, "arr.length").unwrap();
562 assert!((length.value() - 65536.0).abs() < f64::EPSILON);
563 
564 let result: Vec<u8> = ctx.eval(lock, "arr").unwrap();
565 assert_eq!(result, vec);
566 Ok(())
567 });
568}
569 
570#[test]
571fn typed_array_iter_uint8() {
572 let harness = crate::Harness::new();
573 harness.run_in_context(|lock, _ctx| {
574 let data: Vec<u8> = vec![10, 20, 30];
575 let js_val = data.to_js(lock);
576 let typed: jsg::v8::Local<'_, jsg::v8::Uint8Array> =
577 // SAFETY: isolate is valid and locked, js_val is a valid Local.
578 unsafe { jsg::v8::Local::from_ffi(lock.isolate(), js_val.into_ffi()) };
579 
580 assert_eq!(typed.len(), 3);
581 assert!(!typed.is_empty());
582 
583 assert_eq!(typed.get(0), 10);
584 assert_eq!(typed.get(1), 20);
585 assert_eq!(typed.get(2), 30);
586 
587 let sum: u8 = typed.iter().fold(0u8, u8::wrapping_add);
588 assert_eq!(sum, 60);
589 
590 let collected: Vec<u8> = typed.iter().collect();
591 assert_eq!(collected, vec![10, 20, 30]);
592 
593 Ok(())
594 });
595}
596 
597#[test]
598fn typed_array_into_iter_uint8() {
599 let harness = crate::Harness::new();
600 harness.run_in_context(|lock, _ctx| {
601 let data: Vec<u8> = vec![1, 2, 3, 4, 5];
602 let js_val = data.to_js(lock);
603 let typed: jsg::v8::Local<'_, jsg::v8::Uint8Array> =
604 // SAFETY: isolate is valid and locked, js_val is a valid Local.
605 unsafe { jsg::v8::Local::from_ffi(lock.isolate(), js_val.into_ffi()) };
606 
607 let sum: u8 = typed.into_iter().sum();
608 assert_eq!(sum, 15);
609 
610 Ok(())
611 });
612}
613 
614#[test]
615fn typed_array_iter_int32() {
616 let harness = crate::Harness::new();
617 harness.run_in_context(|lock, _ctx| {
618 let data: Vec<i32> = vec![-100, 0, 100];
619 let js_val = data.to_js(lock);
620 let typed: jsg::v8::Local<'_, jsg::v8::Int32Array> =
621 // SAFETY: isolate is valid and locked, js_val is a valid Local.
622 unsafe { jsg::v8::Local::from_ffi(lock.isolate(), js_val.into_ffi()) };
623 
624 assert_eq!(typed.len(), 3);
625 assert_eq!(typed.get(0), -100);
626 assert_eq!(typed.get(1), 0);
627 assert_eq!(typed.get(2), 100);
628 
629 let sum: i32 = typed.iter().sum();
630 assert_eq!(sum, 0);
631 
632 Ok(())
633 });
634}
635 
636#[test]
637fn typed_array_iter_reverse() {
638 let harness = crate::Harness::new();
639 harness.run_in_context(|lock, _ctx| {
640 let data: Vec<u8> = vec![1, 2, 3, 4];
641 let js_val = data.to_js(lock);
642 let typed: jsg::v8::Local<'_, jsg::v8::Uint8Array> =
643 // SAFETY: isolate is valid and locked, js_val is a valid Local.
644 unsafe { jsg::v8::Local::from_ffi(lock.isolate(), js_val.into_ffi()) };
645 
646 let reversed: Vec<u8> = typed.iter().rev().collect();
647 assert_eq!(reversed, vec![4, 3, 2, 1]);
648 
649 Ok(())
650 });
651}
652 
653#[test]
654fn typed_array_empty() {
655 let harness = crate::Harness::new();
656 harness.run_in_context(|lock, _ctx| {
657 let data: Vec<u8> = vec![];
658 let js_val = data.to_js(lock);
659 let typed: jsg::v8::Local<'_, jsg::v8::Uint8Array> =
660 // SAFETY: isolate is valid and locked, js_val is a valid Local.
661 unsafe { jsg::v8::Local::from_ffi(lock.isolate(), js_val.into_ffi()) };
662 
663 assert_eq!(typed.len(), 0);
664 assert!(typed.is_empty());
665 assert_eq!(typed.iter().count(), 0);
666 
667 Ok(())
668 });
669}
670 
671// =============================================================================
672// Float32Array tests
673// =============================================================================
674 
675#[test]
676fn float32_array_parameter_and_return() {
677 let harness = crate::Harness::new();
678 harness.run_in_context(|lock, ctx| {
679 let resource = jsg::Rc::new(ArrayResource);
680 let wrapped = resource.to_js(lock);
681 ctx.set_global("arr", wrapped);
682 
683 // Test Vec<f32> parameter
684 let result: Number = ctx
685 .eval(lock, "arr.sumF32(new Float32Array([1.5, 2.5, 3.0]))")
686 .unwrap();
687 assert!((result.value() - 7.0).abs() < f64::EPSILON);
688 
689 // Test Vec<f32> return value
690 let is_f32: bool = ctx
691 .eval(
692 lock,
693 "arr.doubleF32(new Float32Array([1.0, 2.0])) instanceof Float32Array",
694 )
695 .unwrap();
696 assert!(is_f32);
697 
698 let result: Vec<f32> = ctx
699 .eval(lock, "arr.doubleF32(new Float32Array([1.0, 2.0, 3.0]))")
700 .unwrap();
701 assert_eq!(result, vec![2.0, 4.0, 6.0]);
702 
703 Ok(())
704 });
705}
706 
707#[test]
708fn float32_array_slice_parameter() {
709 let harness = crate::Harness::new();
710 harness.run_in_context(|lock, ctx| {
711 let resource = jsg::Rc::new(ArrayResource);
712 let wrapped = resource.to_js(lock);
713 ctx.set_global("arr", wrapped);
714 
715 // Test &[f32] parameter
716 let result: Number = ctx
717 .eval(lock, "arr.sumF32Slice(new Float32Array([1.5, 2.5, 3.0]))")
718 .unwrap();
719 assert!((result.value() - 7.0).abs() < f64::EPSILON);
720 
721 // Test empty Float32Array
722 let result: Number = ctx
723 .eval(lock, "arr.sumF32Slice(new Float32Array([]))")
724 .unwrap();
725 assert!((result.value() - 0.0).abs() < f64::EPSILON);
726 
727 Ok(())
728 });
729}
730 
731#[test]
732fn float32_array_rejects_wrong_type() {
733 let harness = crate::Harness::new();
734 harness.run_in_context(|lock, ctx| {
735 let resource = jsg::Rc::new(ArrayResource);
736 let wrapped = resource.to_js(lock);
737 ctx.set_global("arr", wrapped);
738 
739 // Float32Array should reject Float64Array
740 let result: Result<Number, _> =
741 ctx.eval(lock, "arr.sumF32Slice(new Float64Array([1.0, 2.0]))");
742 assert!(result.is_err());
743 
744 // Float32Array should reject regular Array
745 let result: Result<Number, _> = ctx.eval(lock, "arr.sumF32Slice([1.0, 2.0])");
746 assert!(result.is_err());
747 
748 Ok(())
749 });
750}
751 
752#[test]
753fn float32_array_to_js_and_from_js() {
754 let harness = crate::Harness::new();
755 harness.run_in_context(|lock, ctx| {
756 // Test ToJS: Vec<f32> -> Float32Array
757 let data: Vec<f32> = vec![1.5, 2.5, 3.5, 4.5];
758 let js_value = data.to_js(lock);
759 ctx.set_global("f32arr", js_value);
760 
761 let is_f32: bool = ctx.eval(lock, "f32arr instanceof Float32Array").unwrap();
762 assert!(is_f32);
763 
764 let len: Number = ctx.eval(lock, "f32arr.length").unwrap();
765 #[expect(clippy::cast_sign_loss)]
766 let len_usize = len.value() as usize;
767 assert_eq!(len_usize, 4);
768 
769 // Test FromJS: Float32Array -> Vec<f32>
770 let result: Vec<f32> = ctx.eval(lock, "f32arr").unwrap();
771 assert_eq!(result.len(), 4);
772 assert!((result[0] - 1.5).abs() < f32::EPSILON);
773 assert!((result[1] - 2.5).abs() < f32::EPSILON);
774 assert!((result[2] - 3.5).abs() < f32::EPSILON);
775 assert!((result[3] - 4.5).abs() < f32::EPSILON);
776 
777 // Test roundtrip with special values
778 let special: Vec<f32> = vec![f32::MIN, f32::MAX, 0.0, -0.0];
779 let js_special = special.to_js(lock);
780 ctx.set_global("special", js_special);
781 let roundtrip: Vec<f32> = ctx.eval(lock, "special").unwrap();
782 #[expect(clippy::float_cmp)]
783 {
784 assert_eq!(roundtrip[0], f32::MIN);
785 assert_eq!(roundtrip[1], f32::MAX);
786 }
787 
788 Ok(())
789 });
790}
791 
792// =============================================================================
793// Float64Array tests
794// =============================================================================
795 
796#[test]
797fn float64_array_parameter_and_return() {
798 let harness = crate::Harness::new();
799 harness.run_in_context(|lock, ctx| {
800 let resource = jsg::Rc::new(ArrayResource);
801 let wrapped = resource.to_js(lock);
802 ctx.set_global("arr", wrapped);
803 
804 // Test Vec<f64> parameter
805 let result: Number = ctx
806 .eval(lock, "arr.sumF64(new Float64Array([1.5, 2.5, 3.0]))")
807 .unwrap();
808 assert!((result.value() - 7.0).abs() < f64::EPSILON);
809 
810 // Test Vec<f64> return value
811 let is_f64: bool = ctx
812 .eval(
813 lock,
814 "arr.doubleF64(new Float64Array([1.0, 2.0])) instanceof Float64Array",
815 )
816 .unwrap();
817 assert!(is_f64);
818 
819 let result: Vec<f64> = ctx
820 .eval(lock, "arr.doubleF64(new Float64Array([1.0, 2.0, 3.0]))")
821 .unwrap();
822 assert_eq!(result, vec![2.0, 4.0, 6.0]);
823 
824 Ok(())
825 });
826}
827 
828#[test]
829fn float64_array_slice_parameter() {
830 let harness = crate::Harness::new();
831 harness.run_in_context(|lock, ctx| {
832 let resource = jsg::Rc::new(ArrayResource);
833 let wrapped = resource.to_js(lock);
834 ctx.set_global("arr", wrapped);
835 
836 // Test &[f64] parameter
837 let result: Number = ctx
838 .eval(lock, "arr.sumF64Slice(new Float64Array([1.5, 2.5, 3.0]))")
839 .unwrap();
840 assert!((result.value() - 7.0).abs() < f64::EPSILON);
841 
842 // Test empty Float64Array
843 let result: Number = ctx
844 .eval(lock, "arr.sumF64Slice(new Float64Array([]))")
845 .unwrap();
846 assert!((result.value() - 0.0).abs() < f64::EPSILON);
847 
848 Ok(())
849 });
850}
851 
852#[test]
853fn float64_array_rejects_wrong_type() {
854 let harness = crate::Harness::new();
855 harness.run_in_context(|lock, ctx| {
856 let resource = jsg::Rc::new(ArrayResource);
857 let wrapped = resource.to_js(lock);
858 ctx.set_global("arr", wrapped);
859 
860 // Float64Array should reject Float32Array
861 let result: Result<Number, _> =
862 ctx.eval(lock, "arr.sumF64Slice(new Float32Array([1.0, 2.0]))");
863 assert!(result.is_err());
864 
865 // Float64Array should reject regular Array
866 let result: Result<Number, _> = ctx.eval(lock, "arr.sumF64Slice([1.0, 2.0])");
867 assert!(result.is_err());
868 
869 Ok(())
870 });
871}
872 
873#[test]
874fn float64_array_to_js_and_from_js() {
875 let harness = crate::Harness::new();
876 harness.run_in_context(|lock, ctx| {
877 // Test Vec<f64> to JavaScript
878 let vec: Vec<f64> = vec![1.5, 2.5, -3.5];
879 ctx.set_global("f64_arr", vec.to_js(lock));
880 
881 let is_f64: bool = ctx.eval(lock, "f64_arr instanceof Float64Array").unwrap();
882 assert!(is_f64);
883 
884 let val: Number = ctx.eval(lock, "f64_arr[0]").unwrap();
885 assert!((val.value() - 1.5).abs() < f64::EPSILON);
886 
887 let val: Number = ctx.eval(lock, "f64_arr[2]").unwrap();
888 assert!((val.value() - (-3.5)).abs() < f64::EPSILON);
889 
890 // Test Float64Array from JavaScript
891 let result: Vec<f64> = ctx.eval(lock, "new Float64Array([1.1, 2.2, 3.3])").unwrap();
892 assert!((result[0] - 1.1).abs() < f64::EPSILON);
893 assert!((result[1] - 2.2).abs() < f64::EPSILON);
894 assert!((result[2] - 3.3).abs() < f64::EPSILON);
895 
896 Ok(())
897 });
898}
899 
900// =============================================================================
901// BigInt64Array tests
902// =============================================================================
903 
904#[test]
905fn bigint64_array_parameter_and_return() {
906 let harness = crate::Harness::new();
907 harness.run_in_context(|lock, ctx| {
908 let resource = jsg::Rc::new(ArrayResource);
909 let wrapped = resource.to_js(lock);
910 ctx.set_global("arr", wrapped);
911 
912 // Test Vec<i64> parameter
913 let result: Number = ctx
914 .eval(lock, "arr.sumI64(new BigInt64Array([1n, 2n, 3n]))")
915 .unwrap();
916 assert!((result.value() - 6.0).abs() < f64::EPSILON);
917 
918 // Test Vec<i64> return value
919 let is_i64: bool = ctx
920 .eval(
921 lock,
922 "arr.doubleI64(new BigInt64Array([1n, 2n])) instanceof BigInt64Array",
923 )
924 .unwrap();
925 assert!(is_i64);
926 
927 let result: Vec<i64> = ctx
928 .eval(lock, "arr.doubleI64(new BigInt64Array([1n, 2n, 3n]))")
929 .unwrap();
930 assert_eq!(result, vec![2, 4, 6]);
931 
932 // Test with negative values
933 let result: Number = ctx
934 .eval(lock, "arr.sumI64(new BigInt64Array([-10n, 20n, -5n]))")
935 .unwrap();
936 assert!((result.value() - 5.0).abs() < f64::EPSILON);
937 
938 Ok(())
939 });
940}
941 
942#[test]
943fn bigint64_array_slice_parameter() {
944 let harness = crate::Harness::new();
945 harness.run_in_context(|lock, ctx| {
946 let resource = jsg::Rc::new(ArrayResource);
947 let wrapped = resource.to_js(lock);
948 ctx.set_global("arr", wrapped);
949 
950 // Test &[i64] parameter
951 let result: Number = ctx
952 .eval(lock, "arr.sumI64Slice(new BigInt64Array([10n, 20n, 30n]))")
953 .unwrap();
954 assert!((result.value() - 60.0).abs() < f64::EPSILON);
955 
956 // Test empty BigInt64Array
957 let result: Number = ctx
958 .eval(lock, "arr.sumI64Slice(new BigInt64Array([]))")
959 .unwrap();
960 assert!((result.value() - 0.0).abs() < f64::EPSILON);
961 
962 Ok(())
963 });
964}
965 
966#[test]
967fn bigint64_array_rejects_wrong_type() {
968 let harness = crate::Harness::new();
969 harness.run_in_context(|lock, ctx| {
970 let resource = jsg::Rc::new(ArrayResource);
971 let wrapped = resource.to_js(lock);
972 ctx.set_global("arr", wrapped);
973 
974 // BigInt64Array should reject BigUint64Array
975 let result: Result<Number, _> =
976 ctx.eval(lock, "arr.sumI64Slice(new BigUint64Array([1n, 2n]))");
977 assert!(result.is_err());
978 
979 // BigInt64Array should reject Int32Array
980 let result: Result<Number, _> = ctx.eval(lock, "arr.sumI64Slice(new Int32Array([1, 2]))");
981 assert!(result.is_err());
982 
983 Ok(())
984 });
985}
986 
987#[test]
988fn bigint64_array_to_js_and_from_js() {
989 let harness = crate::Harness::new();
990 harness.run_in_context(|lock, ctx| {
991 // Test Vec<i64> to JavaScript
992 let vec: Vec<i64> = vec![100, -200, 300];
993 ctx.set_global("i64_arr", vec.to_js(lock));
994 
995 let is_i64: bool = ctx.eval(lock, "i64_arr instanceof BigInt64Array").unwrap();
996 assert!(is_i64);
997 
998 // BigInt64Array returns BigInt values, verify via string
999 let val: String = ctx.eval(lock, "String(i64_arr[0])").unwrap();
1000 assert_eq!(val, "100");
1001 
1002 let val: String = ctx.eval(lock, "String(i64_arr[1])").unwrap();
1003 assert_eq!(val, "-200");
1004 
1005 // Test BigInt64Array from JavaScript
1006 let result: Vec<i64> = ctx.eval(lock, "new BigInt64Array([1n, -2n, 3n])").unwrap();
1007 assert_eq!(result, vec![1, -2, 3]);
1008 
1009 Ok(())
1010 });
1011}
1012 
1013// =============================================================================
1014// as_slice / as_mut_slice / byte_offset / data / FromJS for Local<T>
1015// =============================================================================
1016 
1017/// Helper resource whose methods accept `Local<'_, T>` directly via `FromJS`.
1018#[jsg_resource]
1019struct SliceResource;
1020 
1021#[jsg_resource]
1022impl SliceResource {
1023 /// Accepts a `Local<Uint8Array>` directly and sums its elements via `as_slice`.
1024 #[jsg_method]
1025 pub fn sum_u8_local(&self, arr: jsg::v8::Local<jsg::v8::Uint8Array>) -> jsg::Number {
1026 jsg::Number::new(arr.as_slice().iter().map(|&x| f64::from(x)).sum())
1027 }
1028 
1029 /// Accepts a `Local<Int32Array>` directly and sums its elements via `as_slice`.
1030 #[jsg_method]
1031 pub fn sum_i32_local(&self, arr: jsg::v8::Local<jsg::v8::Int32Array>) -> jsg::Number {
1032 jsg::Number::new(arr.as_slice().iter().map(|&x| f64::from(x)).sum())
1033 }
1034 
1035 /// Accepts a `Local<Float64Array>` directly and sums its elements via `as_slice`.
1036 #[jsg_method]
1037 pub fn sum_f64_local(&self, arr: jsg::v8::Local<jsg::v8::Float64Array>) -> jsg::Number {
1038 jsg::Number::new(arr.as_slice().iter().copied().sum())
1039 }
1040 
1041 /// Writes `0xFF` into every byte via `as_mut_slice` and returns the length.
1042 #[jsg_method]
1043 pub fn fill_ff(
1044 &self,
1045 lock: &mut jsg::Lock,
1046 mut arr: jsg::v8::Local<jsg::v8::Uint8Array>,
1047 ) -> jsg::Number {
1048 // SAFETY: no other reference into this buffer is live during this call;
1049 // lock borrow prevents JS from running.
1050 unsafe { arr.as_mut_slice(lock) }.fill(0xFF);
1051 #[expect(clippy::cast_precision_loss)]
1052 jsg::Number::new(arr.len() as f64)
1053 }
1054}
1055 
1056#[test]
1057fn typed_array_as_slice_zero_copy_sum() {
1058 let harness = crate::Harness::new();
1059 harness.run_in_context(|lock, ctx| {
1060 let res = jsg::Rc::new(SliceResource);
1061 ctx.set_global("r", res.to_js(lock));
1062 
1063 // Uint8Array
1064 let result: jsg::Number = ctx
1065 .eval(lock, "r.sumU8Local(new Uint8Array([10, 20, 30]))")
1066 .unwrap();
1067 assert!((result.value() - 60.0).abs() < f64::EPSILON);
1068 
1069 // Int32Array
1070 let result: jsg::Number = ctx
1071 .eval(lock, "r.sumI32Local(new Int32Array([-1, 0, 1]))")
1072 .unwrap();
1073 assert!((result.value() - 0.0).abs() < f64::EPSILON);
1074 
1075 // Float64Array
1076 let result: jsg::Number = ctx
1077 .eval(lock, "r.sumF64Local(new Float64Array([1.5, 2.5]))")
1078 .unwrap();
1079 assert!((result.value() - 4.0).abs() < f64::EPSILON);
1080 
1081 Ok(())
1082 });
1083}
1084 
1085#[test]
1086fn typed_array_as_slice_empty() {
1087 let harness = crate::Harness::new();
1088 harness.run_in_context(|lock, _ctx| {
1089 let data: Vec<u8> = vec![];
1090 let js_val = data.to_js(lock);
1091 let typed: jsg::v8::Local<'_, jsg::v8::Uint8Array> =
1092 // SAFETY: isolate valid, js_val is a Uint8Array Local.
1093 unsafe { jsg::v8::Local::from_ffi(lock.isolate(), js_val.into_ffi()) };
1094 
1095 assert_eq!(typed.as_slice(), &[] as &[u8]);
1096 Ok(())
1097 });
1098}
1099 
1100#[test]
1101fn typed_array_as_mut_slice_mutations_visible_in_js() {
1102 let harness = crate::Harness::new();
1103 harness.run_in_context(|lock, ctx| {
1104 let res = jsg::Rc::new(SliceResource);
1105 ctx.set_global("r", res.to_js(lock));
1106 
1107 // fill_ff writes 0xFF into every element in-place.
1108 // The returned length tells us how many bytes were written.
1109 let len: jsg::Number = ctx
1110 .eval(lock, "r.fillFf(new Uint8Array([1, 2, 3]))")
1111 .unwrap();
1112 assert!((len.value() - 3.0).abs() < f64::EPSILON);
1113 
1114 // Independently verify with a captured reference: create an array in JS,
1115 // pass it in, then read back the same object.
1116 let check: jsg::Number = ctx
1117 .eval(
1118 lock,
1119 r"
1120 const buf = new Uint8Array([0, 0, 0]);
1121 r.fillFf(buf);
1122 buf[0] === 255 && buf[1] === 255 && buf[2] === 255 ? 1 : 0
1123 ",
1124 )
1125 .unwrap();
1126 assert!((check.value() - 1.0).abs() < f64::EPSILON);
1127 
1128 Ok(())
1129 });
1130}
1131 
1132#[test]
1133fn typed_array_byte_offset_non_zero() {
1134 let harness = crate::Harness::new();
1135 harness.run_in_context(|lock, ctx| {
1136 // Create a view that starts 4 bytes into an 8-byte buffer.
1137 // byte_offset() must return 4, len() must return 1.
1138 let js_val = ctx
1139 .eval_raw(
1140 r"
1141 const buf = new ArrayBuffer(8);
1142 const view = new Uint32Array(buf, 4, 1);
1143 view
1144 ",
1145 )
1146 .unwrap();
1147 let typed: jsg::v8::Local<'_, jsg::v8::Uint32Array> =
1148 // SAFETY: isolate valid; js_val is a Uint32Array Local.
1149 unsafe { jsg::v8::Local::from_ffi(lock.isolate(), js_val.into_ffi()) };
1150 
1151 assert_eq!(typed.byte_offset(), 4);
1152 assert_eq!(typed.byte_length(), 4); // 1 element × 4 bytes/u32
1153 assert_eq!(typed.len(), 1);
1154 Ok(())
1155 });
1156}
1157 
1158#[test]
1159fn typed_array_as_slice_with_byte_offset() {
1160 let harness = crate::Harness::new();
1161 harness.run_in_context(|lock, ctx| {
1162 // Build a 12-byte buffer [0,1,2,...,11], create a Uint8Array view
1163 // starting at offset 4 covering 4 bytes: [4, 5, 6, 7].
1164 let js_val = ctx
1165 .eval_raw(
1166 r"
1167 const buf = new ArrayBuffer(12);
1168 const all = new Uint8Array(buf);
1169 for (let i = 0; i < 12; i++) all[i] = i;
1170 new Uint8Array(buf, 4, 4)
1171 ",
1172 )
1173 .unwrap();
1174 let typed: jsg::v8::Local<'_, jsg::v8::Uint8Array> =
1175 // SAFETY: isolate valid; js_val is a Uint8Array Local.
1176 unsafe { jsg::v8::Local::from_ffi(lock.isolate(), js_val.into_ffi()) };
1177 
1178 assert_eq!(typed.byte_offset(), 4);
1179 assert_eq!(typed.len(), 4);
1180 assert_eq!(typed.as_slice(), &[4u8, 5, 6, 7]);
1181 Ok(())
1182 });
1183}
1184 
1185#[test]
1186fn typed_array_data_pointer_and_byte_offset() {
1187 let harness = crate::Harness::new();
1188 harness.run_in_context(|lock, _ctx| {
1189 let data: Vec<u8> = vec![10, 20, 30, 40];
1190 let js_val = data.to_js(lock);
1191 let typed: jsg::v8::Local<'_, jsg::v8::Uint8Array> =
1192 // SAFETY: isolate valid; js_val is a Uint8Array Local.
1193 unsafe { jsg::v8::Local::from_ffi(lock.isolate(), js_val.into_ffi()) };
1194 
1195 // A freshly created TypedArray has byte_offset == 0.
1196 assert_eq!(typed.byte_offset(), 0);
1197 
1198 // data() + byte_offset() must point at the first element.
1199 // as_slice() is derived from exactly this computation, so they must agree.
1200 let slice = typed.as_slice();
1201 // SAFETY: as_slice() contracts guarantee the pointer + len are valid here.
1202 let from_ptr: &[u8] = unsafe {
1203 std::slice::from_raw_parts(
1204 typed.data().byte_add(typed.byte_offset()).cast_const(),
1205 typed.len(),
1206 )
1207 };
1208 assert_eq!(slice, from_ptr);
1209 assert_eq!(from_ptr, &[10u8, 20, 30, 40]);
1210 Ok(())
1211 });
1212}
1213 
1214#[test]
1215fn typed_array_from_js_local_rejects_wrong_type() {
1216 let harness = crate::Harness::new();
1217 harness.run_in_context(|lock, ctx| {
1218 let res = jsg::Rc::new(SliceResource);
1219 ctx.set_global("r", res.to_js(lock));
1220 
1221 // sumU8Local expects Uint8Array — Int8Array must be rejected.
1222 let result: Result<jsg::Number, _> =
1223 ctx.eval(lock, "r.sumU8Local(new Int8Array([1, 2, 3]))");
1224 assert!(result.is_err());
1225 
1226 // Regular Array must also be rejected.
1227 let result: Result<jsg::Number, _> = ctx.eval(lock, "r.sumU8Local([1, 2, 3])");
1228 assert!(result.is_err());
1229 
1230 // sumI32Local expects Int32Array — Uint32Array must be rejected.
1231 let result: Result<jsg::Number, _> =
1232 ctx.eval(lock, "r.sumI32Local(new Uint32Array([1, 2, 3]))");
1233 assert!(result.is_err());
1234 
1235 Ok(())
1236 });
1237}
1238 
1239#[test]
1240fn typed_array_element_size_and_is_integer_type() {
1241 let harness = crate::Harness::new();
1242 harness.run_in_context(|lock, _ctx| {
1243 // u8 / Uint8Array: 1 byte per element, integer type.
1244 let js_val = vec![0u8, 0].to_js(lock);
1245 let arr: jsg::v8::Local<'_, jsg::v8::Uint8Array> =
1246 // SAFETY: isolate valid; js_val is a Uint8Array Local.
1247 unsafe { jsg::v8::Local::from_ffi(lock.isolate(), js_val.into_ffi()) };
1248 assert_eq!(arr.element_size(), 1);
1249 assert!(arr.is_integer_type());
1250 
1251 // u32 / Uint32Array: 4 bytes per element, integer type.
1252 let js_val = vec![0u32, 0].to_js(lock);
1253 let arr: jsg::v8::Local<'_, jsg::v8::Uint32Array> =
1254 // SAFETY: isolate valid; js_val is a Uint32Array Local.
1255 unsafe { jsg::v8::Local::from_ffi(lock.isolate(), js_val.into_ffi()) };
1256 assert_eq!(arr.element_size(), 4);
1257 assert!(arr.is_integer_type());
1258 
1259 // f32 / Float32Array: 4 bytes per element, NOT an integer type.
1260 let js_val = vec![0f32, 0.0].to_js(lock);
1261 let arr: jsg::v8::Local<'_, jsg::v8::Float32Array> =
1262 // SAFETY: isolate valid; js_val is a Float32Array Local.
1263 unsafe { jsg::v8::Local::from_ffi(lock.isolate(), js_val.into_ffi()) };
1264 assert_eq!(arr.element_size(), 4);
1265 assert!(!arr.is_integer_type());
1266 
1267 // f64 / Float64Array: 8 bytes per element, NOT an integer type.
1268 let js_val = vec![0f64, 0.0].to_js(lock);
1269 let arr: jsg::v8::Local<'_, jsg::v8::Float64Array> =
1270 // SAFETY: isolate valid; js_val is a Float64Array Local.
1271 unsafe { jsg::v8::Local::from_ffi(lock.isolate(), js_val.into_ffi()) };
1272 assert_eq!(arr.element_size(), 8);
1273 assert!(!arr.is_integer_type());
1274 
1275 Ok(())
1276 });
1277}
1278 
1279// =============================================================================
1280// BigUint64Array tests
1281// =============================================================================
1282 
1283#[test]
1284fn biguint64_array_parameter_and_return() {
1285 let harness = crate::Harness::new();
1286 harness.run_in_context(|lock, ctx| {
1287 let resource = jsg::Rc::new(ArrayResource);
1288 let wrapped = resource.to_js(lock);
1289 ctx.set_global("arr", wrapped);
1290 
1291 // Test Vec<u64> parameter
1292 let result: Number = ctx
1293 .eval(lock, "arr.sumU64(new BigUint64Array([1n, 2n, 3n]))")
1294 .unwrap();
1295 assert!((result.value() - 6.0).abs() < f64::EPSILON);
1296 
1297 // Test Vec<u64> return value
1298 let is_u64: bool = ctx
1299 .eval(
1300 lock,
1301 "arr.doubleU64(new BigUint64Array([1n, 2n])) instanceof BigUint64Array",
1302 )
1303 .unwrap();
1304 assert!(is_u64);
1305 
1306 let result: Vec<u64> = ctx
1307 .eval(lock, "arr.doubleU64(new BigUint64Array([1n, 2n, 3n]))")
1308 .unwrap();
1309 assert_eq!(result, vec![2, 4, 6]);
1310 
1311 Ok(())
1312 });
1313}
1314 
1315#[test]
1316fn biguint64_array_slice_parameter() {
1317 let harness = crate::Harness::new();
1318 harness.run_in_context(|lock, ctx| {
1319 let resource = jsg::Rc::new(ArrayResource);
1320 let wrapped = resource.to_js(lock);
1321 ctx.set_global("arr", wrapped);
1322 
1323 // Test &[u64] parameter
1324 let result: Number = ctx
1325 .eval(lock, "arr.sumU64Slice(new BigUint64Array([10n, 20n, 30n]))")
1326 .unwrap();
1327 assert!((result.value() - 60.0).abs() < f64::EPSILON);
1328 
1329 // Test empty BigUint64Array
1330 let result: Number = ctx
1331 .eval(lock, "arr.sumU64Slice(new BigUint64Array([]))")
1332 .unwrap();
1333 assert!((result.value() - 0.0).abs() < f64::EPSILON);
1334 
1335 Ok(())
1336 });
1337}
1338 
1339#[test]
1340fn biguint64_array_rejects_wrong_type() {
1341 let harness = crate::Harness::new();
1342 harness.run_in_context(|lock, ctx| {
1343 let resource = jsg::Rc::new(ArrayResource);
1344 let wrapped = resource.to_js(lock);
1345 ctx.set_global("arr", wrapped);
1346 
1347 // BigUint64Array should reject BigInt64Array
1348 let result: Result<Number, _> =
1349 ctx.eval(lock, "arr.sumU64Slice(new BigInt64Array([1n, 2n]))");
1350 assert!(result.is_err());
1351 
1352 // BigUint64Array should reject Uint32Array
1353 let result: Result<Number, _> = ctx.eval(lock, "arr.sumU64Slice(new Uint32Array([1, 2]))");
1354 assert!(result.is_err());
1355 
1356 Ok(())
1357 });
1358}
1359 
1360#[test]
1361fn biguint64_array_to_js_and_from_js() {
1362 let harness = crate::Harness::new();
1363 harness.run_in_context(|lock, ctx| {
1364 // Test Vec<u64> to JavaScript
1365 let vec: Vec<u64> = vec![100, 200, 300];
1366 ctx.set_global("u64_arr", vec.to_js(lock));
1367 
1368 let is_u64: bool = ctx.eval(lock, "u64_arr instanceof BigUint64Array").unwrap();
1369 assert!(is_u64);
1370 
1371 // BigUint64Array returns BigInt values, verify via string
1372 let val: String = ctx.eval(lock, "String(u64_arr[0])").unwrap();
1373 assert_eq!(val, "100");
1374 
1375 let val: String = ctx.eval(lock, "String(u64_arr[2])").unwrap();
1376 assert_eq!(val, "300");
1377 
1378 // Test BigUint64Array from JavaScript
1379 let result: Vec<u64> = ctx.eval(lock, "new BigUint64Array([1n, 2n, 3n])").unwrap();
1380 assert_eq!(result, vec![1, 2, 3]);
1381 
1382 Ok(())
1383 });
1384}
1385 
1386#[test]
1387fn uint8clamped_array_from_js() {
1388 let harness = crate::Harness::new();
1389 harness.run_in_context(|lock, ctx| {
1390 use jsg::v8::Local;
1391 use jsg::v8::Uint8ClampedArray;
1392 
1393 let val = ctx
1394 .eval_raw("new Uint8ClampedArray([0, 128, 255])")
1395 .unwrap();
1396 assert!(val.is_uint8clamped_array());
1397 assert!(!val.is_uint8_array());
1398 
1399 let arr: Local<Uint8ClampedArray> =
1400 // SAFETY: isolate valid; val is a Uint8ClampedArray Local.
1401 unsafe { Local::from_ffi(lock.isolate(), val.into_ffi()) };
1402 assert_eq!(arr.len(), 3);
1403 assert_eq!(arr.get(0), 0u8);
1404 assert_eq!(arr.get(1), 128u8);
1405 assert_eq!(arr.get(2), 255u8);
1406 assert_eq!(arr.as_slice(), &[0u8, 128, 255]);
1407 assert!(arr.is_integer_type());
1408 assert_eq!(arr.element_size(), 1);
1409 
1410 Ok(())
1411 });
1412}
1413 
1414#[test]
1415fn uint8clamped_array_rejects_wrong_type() {
1416 let harness = crate::Harness::new();
1417 harness.run_in_context(|lock, ctx| {
1418 use jsg::FromJS;
1419 use jsg::v8::Local;
1420 use jsg::v8::Uint8ClampedArray;
1421 
1422 let val = ctx.eval_raw("new Uint8Array([1, 2, 3])").unwrap();
1423 let result = Local::<Uint8ClampedArray>::from_js(lock, val);
1424 assert!(result.is_err());
1425 
1426 Ok(())
1427 });
1428}
1429 
1430#[test]
1431fn is_float16_array_check() {
1432 let harness = crate::Harness::new();
1433 harness.run_in_context(|_lock, ctx| {
1434 let f16_val = ctx.eval_raw("new Float16Array([1.0, 2.0])").unwrap();
1435 assert!(f16_val.is_float16_array());
1436 assert!(!f16_val.is_float32_array());
1437 
1438 let f32_val = ctx.eval_raw("new Float32Array([1.0])").unwrap();
1439 assert!(!f32_val.is_float16_array());
1440 
1441 Ok(())
1442 });
1443}