Skip to main content

shape_jit/ffi/call_method/
array.rs

1// Heap allocation audit (PR-9 V8 Gap Closure):
2//   Category A (NaN-boxed returns): 8 sites
3//     jit_box(HK_ARRAY, ...) — reverse, slice, concat, flatten, unique, sort, take, drop
4//     jit_box(HK_STRING, ...) — join
5//   Category B (intermediate/consumed): 0 sites
6//   Category C (heap islands): 0 sites
7//     (elements stored in JitArrays are existing u64 values copied from the
8//      source array — no new allocations within the element buffer)
9//!
10//! Array method implementations for JIT
11
12use crate::jit_array::JitArray;
13use crate::nan_boxing::*;
14
15/// Call a method on an array value
16#[inline(always)]
17pub fn call_array_method(receiver_bits: u64, method_name: &str, args: &[u64]) -> u64 {
18    unsafe {
19        let arr = jit_unbox::<JitArray>(receiver_bits);
20        let slice = arr.as_slice();
21
22        match method_name {
23            "length" | "len" => box_number(arr.len() as f64),
24            "first" => arr.first().copied().unwrap_or(TAG_NULL),
25            "last" => arr.last().copied().unwrap_or(TAG_NULL),
26            "includes" | "contains" => {
27                if args.is_empty() {
28                    return TAG_BOOL_FALSE;
29                }
30                let needle = args[0];
31                for &elem in slice.iter() {
32                    if elem == needle {
33                        return TAG_BOOL_TRUE;
34                    }
35                    if is_number(elem)
36                        && is_number(needle)
37                        && unbox_number(elem) == unbox_number(needle)
38                    {
39                        return TAG_BOOL_TRUE;
40                    }
41                }
42                TAG_BOOL_FALSE
43            }
44            "indexOf" => {
45                if args.is_empty() {
46                    return box_number(-1.0);
47                }
48                let needle = args[0];
49                for (i, &elem) in slice.iter().enumerate() {
50                    if elem == needle {
51                        return box_number(i as f64);
52                    }
53                    if is_number(elem)
54                        && is_number(needle)
55                        && unbox_number(elem) == unbox_number(needle)
56                    {
57                        return box_number(i as f64);
58                    }
59                }
60                box_number(-1.0)
61            }
62            "reverse" => {
63                let mut reversed = slice.to_vec();
64                reversed.reverse();
65                jit_box(HK_ARRAY, JitArray::from_vec(reversed))
66            }
67            "slice" => {
68                let len = arr.len() as i64;
69                let start = if !args.is_empty() && is_number(args[0]) {
70                    let s = unbox_number(args[0]) as i64;
71                    if s < 0 {
72                        (len + s).max(0) as usize
73                    } else {
74                        s.min(len) as usize
75                    }
76                } else {
77                    0
78                };
79                let end = if args.len() > 1 && is_number(args[1]) {
80                    let e = unbox_number(args[1]) as i64;
81                    if e < 0 {
82                        (len + e).max(0) as usize
83                    } else {
84                        e.min(len) as usize
85                    }
86                } else {
87                    arr.len()
88                };
89                let sliced = if start < end && start < arr.len() {
90                    JitArray::from_slice(&slice[start..end.min(arr.len())])
91                } else {
92                    JitArray::new()
93                };
94                jit_box(HK_ARRAY, sliced)
95            }
96            "join" => {
97                let separator = if !args.is_empty() {
98                    if is_heap_kind(args[0], HK_STRING) {
99                        jit_unbox::<String>(args[0]).clone()
100                    } else {
101                        ",".to_string()
102                    }
103                } else {
104                    ",".to_string()
105                };
106
107                let parts: Vec<String> = slice
108                    .iter()
109                    .map(|&elem| {
110                        if is_number(elem) {
111                            format!("{}", unbox_number(elem))
112                        } else if is_heap_kind(elem, HK_STRING) {
113                            jit_unbox::<String>(elem).clone()
114                        } else if elem == TAG_NULL {
115                            "null".to_string()
116                        } else if elem == TAG_BOOL_TRUE {
117                            "true".to_string()
118                        } else if elem == TAG_BOOL_FALSE {
119                            "false".to_string()
120                        } else {
121                            "[object]".to_string()
122                        }
123                    })
124                    .collect();
125
126                let joined = parts.join(&separator);
127                jit_box(HK_STRING, joined)
128            }
129            "sum" => {
130                let mut total = 0.0;
131                for &elem in slice.iter() {
132                    if is_number(elem) {
133                        total += unbox_number(elem);
134                    }
135                }
136                box_number(total)
137            }
138            "avg" | "mean" => {
139                if arr.is_empty() {
140                    return TAG_NULL;
141                }
142                let mut total = 0.0;
143                for &elem in slice.iter() {
144                    if is_number(elem) {
145                        total += unbox_number(elem);
146                    }
147                }
148                box_number(total / arr.len() as f64)
149            }
150            "min" => {
151                if arr.is_empty() {
152                    return TAG_NULL;
153                }
154                let mut min_val = f64::INFINITY;
155                for &elem in slice.iter() {
156                    if is_number(elem) {
157                        let v = unbox_number(elem);
158                        if v < min_val {
159                            min_val = v;
160                        }
161                    }
162                }
163                if min_val.is_finite() {
164                    box_number(min_val)
165                } else {
166                    TAG_NULL
167                }
168            }
169            "max" => {
170                if arr.is_empty() {
171                    return TAG_NULL;
172                }
173                let mut max_val = f64::NEG_INFINITY;
174                for &elem in slice.iter() {
175                    if is_number(elem) {
176                        let v = unbox_number(elem);
177                        if v > max_val {
178                            max_val = v;
179                        }
180                    }
181                }
182                if max_val.is_finite() {
183                    box_number(max_val)
184                } else {
185                    TAG_NULL
186                }
187            }
188            "take" => {
189                if args.is_empty() {
190                    return receiver_bits;
191                }
192                let count = if is_number(args[0]) {
193                    (unbox_number(args[0]) as usize).min(arr.len())
194                } else {
195                    return receiver_bits;
196                };
197                jit_box(HK_ARRAY, JitArray::from_slice(&slice[..count]))
198            }
199            "drop" => {
200                if args.is_empty() {
201                    return receiver_bits;
202                }
203                let count = if is_number(args[0]) {
204                    (unbox_number(args[0]) as usize).min(arr.len())
205                } else {
206                    return receiver_bits;
207                };
208                jit_box(HK_ARRAY, JitArray::from_slice(&slice[count..]))
209            }
210            "concat" => {
211                let mut result: Vec<u64> = slice.to_vec();
212                for arg in args.iter() {
213                    if is_heap_kind(*arg, HK_ARRAY) {
214                        let other = jit_unbox::<JitArray>(*arg);
215                        result.extend_from_slice(other.as_slice());
216                    } else {
217                        result.push(*arg);
218                    }
219                }
220                jit_box(HK_ARRAY, JitArray::from_vec(result))
221            }
222            "flatten" | "flat" => {
223                let mut result = Vec::new();
224                for &elem in slice.iter() {
225                    if is_heap_kind(elem, HK_ARRAY) {
226                        let inner = jit_unbox::<JitArray>(elem);
227                        result.extend_from_slice(inner.as_slice());
228                    } else {
229                        result.push(elem);
230                    }
231                }
232                jit_box(HK_ARRAY, JitArray::from_vec(result))
233            }
234            "unique" => {
235                let mut seen = Vec::new();
236                let mut result = Vec::new();
237                for &elem in slice.iter() {
238                    let is_dup = seen.iter().any(|&s| {
239                        if is_number(elem) && is_number(s) {
240                            unbox_number(elem) == unbox_number(s)
241                        } else {
242                            elem == s
243                        }
244                    });
245                    if !is_dup {
246                        seen.push(elem);
247                        result.push(elem);
248                    }
249                }
250                jit_box(HK_ARRAY, JitArray::from_vec(result))
251            }
252            "sort" | "sorted" => {
253                let mut sorted = slice.to_vec();
254                sorted.sort_by(|&a, &b| {
255                    if is_number(a) && is_number(b) {
256                        unbox_number(a)
257                            .partial_cmp(&unbox_number(b))
258                            .unwrap_or(std::cmp::Ordering::Equal)
259                    } else {
260                        std::cmp::Ordering::Equal
261                    }
262                });
263                jit_box(HK_ARRAY, JitArray::from_vec(sorted))
264            }
265            _ => TAG_NULL,
266        }
267    }
268}