1use super::*;
4
5pub(crate) fn global_parse_int(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
14 let s = arg(a, 0).to_js_string();
15 let radix_arg = arg(a, 1).to_number();
16 let mut radix = if radix_arg.is_finite() && (2.0..=36.0).contains(&radix_arg) {
17 radix_arg as u32
18 } else {
19 0 };
21 let t = s.trim();
22 let (neg, t) = if let Some(rest) = t.strip_prefix('-') {
23 (true, rest)
24 } else if let Some(rest) = t.strip_prefix('+') {
25 (false, rest)
26 } else {
27 (false, t)
28 };
29 let has_hex_prefix = t.len() > 2 && (t.starts_with("0x") || t.starts_with("0X"));
30 let t = if has_hex_prefix && (radix == 0 || radix == 16) {
31 radix = 16;
32 t.get(2..).unwrap_or("")
33 } else {
34 t
35 };
36 if radix == 0 {
37 radix = 10;
38 }
39 let mut digits = String::new();
40 for c in t.chars() {
41 if c.is_digit(radix) {
42 digits.push(c);
43 } else {
44 break;
45 }
46 }
47 match i64::from_str_radix(&digits, radix) {
48 Ok(n) => Ok(Value::Number(if neg { -n as f64 } else { n as f64 })),
49 Err(_) => Ok(Value::Number(f64::NAN)),
50 }
51}
52pub(crate) fn global_parse_float(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
53 let s = arg(a, 0).to_js_string();
54 let t = s.trim();
55 let mut end = 0;
57 let bytes = t.as_bytes();
58 let mut seen_dot = false;
59 let mut seen_e = false;
60 while end < bytes.len() {
61 let c = bytes[end] as char;
62 if c.is_ascii_digit() {
63 end += 1;
64 } else if c == '.' && !seen_dot && !seen_e {
65 seen_dot = true;
66 end += 1;
67 } else if (c == 'e' || c == 'E') && !seen_e && end > 0 {
68 seen_e = true;
69 end += 1;
70 } else if (c == '+' || c == '-')
71 && end > 0
72 && (bytes[end - 1] == b'e' || bytes[end - 1] == b'E')
73 {
74 end += 1;
75 } else {
76 break;
77 }
78 }
79 match t.get(..end).unwrap_or("").parse::<f64>() {
80 Ok(n) => Ok(Value::Number(n)),
81 Err(_) => Ok(Value::Number(f64::NAN)),
82 }
83}
84pub(crate) fn global_is_nan(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
85 Ok(Value::Bool(arg(a, 0).to_number().is_nan()))
86}
87pub(crate) fn global_is_finite(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
88 Ok(Value::Bool(arg(a, 0).to_number().is_finite()))
89}
90
91pub(crate) fn global_eval(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
93 let first = arg(a, 0);
94 match &first {
95 Value::Str(s) => it.eval_source(s),
96 _ => Ok(first),
97 }
98}
99
100pub(crate) fn function_ctor(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
104 let (params, body): (String, String) = match a.len() {
105 0 => (String::new(), String::new()),
106 1 => (String::new(), a[0].to_js_string()),
107 n => {
108 let params = a[..n - 1]
109 .iter()
110 .map(|v| v.to_js_string())
111 .collect::<Vec<_>>()
112 .join(",");
113 (params, a[n - 1].to_js_string())
114 }
115 };
116 let source = format!("(function anonymous({}) {{\n{}\n}})", params, body);
117 it.eval_source(&source)
118}
119
120pub fn string_get(s: &str, key: &str) -> Value {
124 if key == "length" {
125 return Value::Number(s.chars().count() as f64);
126 }
127 if let Ok(idx) = key.parse::<usize>() {
128 return match s.chars().nth(idx) {
129 Some(c) => Value::str(c.to_string()),
130 None => Value::Undefined,
131 };
132 }
133 match key {
134 "charAt" => nv("charAt", str_char_at),
135 "charCodeAt" => nv("charCodeAt", str_char_code_at),
136 "codePointAt" => nv("codePointAt", str_code_point_at),
137 "localeCompare" => nv("localeCompare", str_locale_compare),
138 "indexOf" => nv("indexOf", str_index_of),
139 "includes" => nv("includes", str_includes),
140 "startsWith" => nv("startsWith", str_starts_with),
141 "endsWith" => nv("endsWith", str_ends_with),
142 "slice" => nv("slice", str_slice),
143 "substring" => nv("substring", str_substring),
144 "substr" => nv("substr", str_substr),
145 "toUpperCase" => nv("toUpperCase", str_to_upper),
146 "toLowerCase" => nv("toLowerCase", str_to_lower),
147 "trim" => nv("trim", str_trim),
148 "split" => nv("split", str_split),
149 "replace" => nv("replace", str_replace),
150 "replaceAll" => nv("replaceAll", str_replace_all),
151 "match" => nv("match", str_match),
152 "matchAll" => nv("matchAll", str_match_all),
153 "search" => nv("search", str_search),
154 "repeat" => nv("repeat", str_repeat),
155 "concat" => nv("concat", str_concat),
156 "padStart" => nv("padStart", str_pad_start),
157 "padEnd" => nv("padEnd", str_pad_end),
158 "at" => nv("at", str_at),
159 "toString" => nv("toString", str_identity),
160 "trimStart" => nv("trimStart", str_trim_start),
161 "trimEnd" => nv("trimEnd", str_trim_end),
162 "trimLeft" => nv("trimLeft", str_trim_start),
165 "trimRight" => nv("trimRight", str_trim_end),
166 "Symbol(Symbol.iterator)" => nv("[Symbol.iterator]", str_iterator),
167 "isWellFormed" => nv("isWellFormed", str_is_well_formed),
168 "toWellFormed" => nv("toWellFormed", str_to_well_formed),
169 "normalize" => nv("normalize", str_normalize),
170 "anchor" => nv("anchor", str_anchor),
173 "big" => nv("big", str_big),
174 "blink" => nv("blink", str_blink),
175 "bold" => nv("bold", str_bold),
176 "fixed" => nv("fixed", str_fixed),
177 "fontcolor" => nv("fontcolor", str_fontcolor),
178 "fontsize" => nv("fontsize", str_fontsize),
179 "italics" => nv("italics", str_italics),
180 "link" => nv("link", str_link),
181 "small" => nv("small", str_small),
182 "strike" => nv("strike", str_strike),
183 "sub" => nv("sub", str_sub),
184 "sup" => nv("sup", str_sup),
185 _ => Value::Undefined,
186 }
187}
188pub(crate) fn html_wrap_tag(s: &str, tag: &str) -> String {
190 alloc::format!("<{0}>{1}</{0}>", tag, s)
191}
192pub(crate) fn html_wrap_attr(s: &str, tag: &str, attr: &str, value: &str) -> String {
194 let escaped = value.replace('"', """);
195 alloc::format!("<{0} {1}=\"{2}\">{3}</{0}>", tag, attr, escaped, s)
196}
197pub(crate) fn str_anchor(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
198 Ok(Value::str(html_wrap_attr(&this_str(&t), "a", "name", &arg(a, 0).to_js_string())))
199}
200pub(crate) fn str_big(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
201 Ok(Value::str(html_wrap_tag(&this_str(&t), "big")))
202}
203pub(crate) fn str_blink(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
204 Ok(Value::str(html_wrap_tag(&this_str(&t), "blink")))
205}
206pub(crate) fn str_bold(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
207 Ok(Value::str(html_wrap_tag(&this_str(&t), "b")))
208}
209pub(crate) fn str_fixed(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
210 Ok(Value::str(html_wrap_tag(&this_str(&t), "tt")))
211}
212pub(crate) fn str_fontcolor(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
213 Ok(Value::str(html_wrap_attr(&this_str(&t), "font", "color", &arg(a, 0).to_js_string())))
214}
215pub(crate) fn str_fontsize(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
216 Ok(Value::str(html_wrap_attr(&this_str(&t), "font", "size", &arg(a, 0).to_js_string())))
217}
218pub(crate) fn str_italics(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
219 Ok(Value::str(html_wrap_tag(&this_str(&t), "i")))
220}
221pub(crate) fn str_link(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
222 Ok(Value::str(html_wrap_attr(&this_str(&t), "a", "href", &arg(a, 0).to_js_string())))
223}
224pub(crate) fn str_small(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
225 Ok(Value::str(html_wrap_tag(&this_str(&t), "small")))
226}
227pub(crate) fn str_strike(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
228 Ok(Value::str(html_wrap_tag(&this_str(&t), "strike")))
229}
230pub(crate) fn str_sub(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
231 Ok(Value::str(html_wrap_tag(&this_str(&t), "sub")))
232}
233pub(crate) fn str_sup(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
234 Ok(Value::str(html_wrap_tag(&this_str(&t), "sup")))
235}
236
237pub(crate) fn this_str(this: &Value) -> String {
238 this.to_js_string()
239}
240
241pub(crate) fn str_char_at(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
242 let s = this_str(&t);
243 let i = arg(a, 0).to_number() as usize;
244 Ok(Value::str(
245 s.chars().nth(i).map(|c| c.to_string()).unwrap_or_default(),
246 ))
247}
248pub(crate) fn str_char_code_at(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
249 let s = this_str(&t);
250 let i = arg(a, 0).to_number() as usize;
251 match s.chars().nth(i) {
252 Some(c) => Ok(Value::Number(c as u32 as f64)),
253 None => Ok(Value::Number(f64::NAN)),
254 }
255}
256pub(crate) fn str_code_point_at(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
261 let s = this_str(&t);
262 let i = arg(a, 0).to_number() as usize;
263 match s.chars().nth(i) {
264 Some(c) => Ok(Value::Number(c as u32 as f64)),
265 None => Ok(Value::Undefined),
266 }
267}
268pub(crate) fn str_locale_compare(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
271 let s = this_str(&t);
272 let other = arg(a, 0).to_js_string();
273 use core::cmp::Ordering;
274 Ok(Value::Number(match s.as_str().cmp(other.as_str()) {
275 Ordering::Less => -1.0,
276 Ordering::Equal => 0.0,
277 Ordering::Greater => 1.0,
278 }))
279}
280pub(crate) fn str_index_of(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
281 let s = this_str(&t);
282 let needle = arg(a, 0).to_js_string();
283 match s.find(&needle) {
284 Some(byte_idx) => Ok(Value::Number(
285 s.get(..byte_idx).unwrap_or("").chars().count() as f64,
286 )),
287 None => Ok(Value::Number(-1.0)),
288 }
289}
290pub(crate) fn reject_regexp_arg(it: &Interp, v: &Value, method: &str) -> Result<(), Value> {
298 if as_regexp(v).is_some() {
299 return Err(it.error(alloc::format!(
300 "First argument to String.prototype.{} must not be a regular expression",
301 method
302 )));
303 }
304 Ok(())
305}
306pub(crate) fn str_includes(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
307 reject_regexp_arg(it, &arg(a, 0), "includes")?;
308 Ok(Value::Bool(
309 this_str(&t).contains(&arg(a, 0).to_js_string()),
310 ))
311}
312pub(crate) fn str_starts_with(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
313 reject_regexp_arg(it, &arg(a, 0), "startsWith")?;
314 Ok(Value::Bool(
315 this_str(&t).starts_with(&arg(a, 0).to_js_string()),
316 ))
317}
318pub(crate) fn str_ends_with(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
319 reject_regexp_arg(it, &arg(a, 0), "endsWith")?;
320 Ok(Value::Bool(
321 this_str(&t).ends_with(&arg(a, 0).to_js_string()),
322 ))
323}
324pub(crate) fn str_slice(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
325 let chars: Vec<char> = this_str(&t).chars().collect();
326 let len = chars.len() as i64;
327 let start = norm_index(arg(a, 0).to_number(), len, 0);
328 let end = if matches!(arg(a, 1), Value::Undefined) {
329 len
330 } else {
331 norm_index(arg(a, 1).to_number(), len, len)
332 };
333 let (s, e) = (start.max(0) as usize, end.max(0) as usize);
334 Ok(Value::str(if s < e && s < chars.len() {
335 chars[s..e.min(chars.len())].iter().collect::<String>()
336 } else {
337 String::new()
338 }))
339}
340pub(crate) fn str_substring(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
341 let chars: Vec<char> = this_str(&t).chars().collect();
342 let len = chars.len();
343 let mut s = clamp_idx(arg(a, 0).to_number(), len);
344 let mut e = if matches!(arg(a, 1), Value::Undefined) {
345 len
346 } else {
347 clamp_idx(arg(a, 1).to_number(), len)
348 };
349 if s > e {
350 core::mem::swap(&mut s, &mut e);
351 }
352 Ok(Value::str(chars[s..e].iter().collect::<String>()))
353}
354pub(crate) fn str_substr(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
358 let chars: Vec<char> = this_str(&t).chars().collect();
359 let len = chars.len() as i64;
360 let raw_start = arg(a, 0).to_number();
361 let start = if raw_start < 0.0 {
362 (len + raw_start as i64).max(0)
363 } else {
364 (raw_start as i64).min(len)
365 };
366 let length = if matches!(arg(a, 1), Value::Undefined) {
367 len - start
368 } else {
369 (arg(a, 1).to_number() as i64).max(0)
370 };
371 let end = (start + length).min(len);
372 let s = start as usize;
373 let e = (end.max(start)) as usize;
374 Ok(Value::str(if s < e {
375 chars[s..e].iter().collect::<String>()
376 } else {
377 String::new()
378 }))
379}
380pub(crate) fn str_to_upper(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
381 Ok(Value::str(this_str(&t).to_uppercase()))
382}
383pub(crate) fn str_to_lower(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
384 Ok(Value::str(this_str(&t).to_lowercase()))
385}
386pub(crate) fn str_trim(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
387 Ok(Value::str(this_str(&t).trim().to_string()))
388}
389pub(crate) fn str_trim_start(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
390 Ok(Value::str(this_str(&t).trim_start().to_string()))
391}
392pub(crate) fn str_trim_end(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
393 Ok(Value::str(this_str(&t).trim_end().to_string()))
394}
395pub(crate) fn str_identity(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
396 Ok(Value::str(this_str(&t)))
397}
398pub(crate) fn str_iterator(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
405 let s = this_str(&t);
406 let items: Vec<Value> = s.chars().map(|c| Value::str(c.to_string())).collect();
407 Ok(Value::Object(make_iterator(items)))
408}
409pub(crate) fn str_is_well_formed(_: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
412 Ok(Value::Bool(true))
413}
414pub(crate) fn str_to_well_formed(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
416 Ok(Value::str(this_str(&t)))
417}
418pub(crate) const NFD_TABLE: &[(char, char, char)] = &[
429 ('À', 'A', '\u{0300}'), ('Á', 'A', '\u{0301}'), ('Â', 'A', '\u{0302}'),
430 ('Ã', 'A', '\u{0303}'), ('Ä', 'A', '\u{0308}'), ('Å', 'A', '\u{030A}'),
431 ('Ç', 'C', '\u{0327}'),
432 ('È', 'E', '\u{0300}'), ('É', 'E', '\u{0301}'), ('Ê', 'E', '\u{0302}'), ('Ë', 'E', '\u{0308}'),
433 ('Ì', 'I', '\u{0300}'), ('Í', 'I', '\u{0301}'), ('Î', 'I', '\u{0302}'), ('Ï', 'I', '\u{0308}'),
434 ('Ñ', 'N', '\u{0303}'),
435 ('Ò', 'O', '\u{0300}'), ('Ó', 'O', '\u{0301}'), ('Ô', 'O', '\u{0302}'),
436 ('Õ', 'O', '\u{0303}'), ('Ö', 'O', '\u{0308}'),
437 ('Ù', 'U', '\u{0300}'), ('Ú', 'U', '\u{0301}'), ('Û', 'U', '\u{0302}'), ('Ü', 'U', '\u{0308}'),
438 ('Ý', 'Y', '\u{0301}'),
439 ('à', 'a', '\u{0300}'), ('á', 'a', '\u{0301}'), ('â', 'a', '\u{0302}'),
440 ('ã', 'a', '\u{0303}'), ('ä', 'a', '\u{0308}'), ('å', 'a', '\u{030A}'),
441 ('ç', 'c', '\u{0327}'),
442 ('è', 'e', '\u{0300}'), ('é', 'e', '\u{0301}'), ('ê', 'e', '\u{0302}'), ('ë', 'e', '\u{0308}'),
443 ('ì', 'i', '\u{0300}'), ('í', 'i', '\u{0301}'), ('î', 'i', '\u{0302}'), ('ï', 'i', '\u{0308}'),
444 ('ñ', 'n', '\u{0303}'),
445 ('ò', 'o', '\u{0300}'), ('ó', 'o', '\u{0301}'), ('ô', 'o', '\u{0302}'),
446 ('õ', 'o', '\u{0303}'), ('ö', 'o', '\u{0308}'),
447 ('ù', 'u', '\u{0300}'), ('ú', 'u', '\u{0301}'), ('û', 'u', '\u{0302}'), ('ü', 'u', '\u{0308}'),
448 ('ý', 'y', '\u{0301}'), ('ÿ', 'y', '\u{0308}'),
449];
450pub(crate) fn japanese_voiced_sound_decompose(c: char) -> Option<(char, char)> {
451 let cp = c as u32;
452 match cp {
453 0x304C | 0x304E | 0x3050 | 0x3052 | 0x3054 |
454 0x3056 | 0x3058 | 0x305A | 0x305C | 0x305E |
455 0x3060 | 0x3062 | 0x3065 | 0x3067 | 0x3069 |
456 0x3070 | 0x3073 | 0x3076 | 0x3079 | 0x307C |
457 0x30AC | 0x30AE | 0x30B0 | 0x30B2 | 0x30B4 |
458 0x30B6 | 0x30B8 | 0x30BA | 0x30BC | 0x30BE |
459 0x30C0 | 0x30C2 | 0x30C5 | 0x30C7 | 0x30C9 |
460 0x30D0 | 0x30D3 | 0x30D6 | 0x30D9 | 0x30DC => {
461 char::from_u32(cp - 1).map(|b| (b, '\u{3099}'))
462 }
463 0x3071 | 0x3074 | 0x3077 | 0x307A | 0x307D |
464 0x30D1 | 0x30D4 | 0x30D7 | 0x30DA | 0x30DD => {
465 char::from_u32(cp - 2).map(|b| (b, '\u{309A}'))
466 }
467 0x30F4 => Some(('ウ', '\u{3099}')),
468 _ => None,
469 }
470}
471
472pub(crate) fn japanese_voiced_sound_compose(base: char, mark: char) -> Option<char> {
473 if mark != '\u{3099}' && mark != '\u{309A}' && mark != '\u{309B}' && mark != '\u{309C}' {
474 return None;
475 }
476 let b = base as u32;
477 let is_dakuten = mark == '\u{3099}' || mark == '\u{309B}';
478 let is_handakuten = mark == '\u{309A}' || mark == '\u{309C}';
479
480 match b {
481 0x304B | 0x304D | 0x304F | 0x3051 | 0x3053 |
482 0x3055 | 0x3057 | 0x3059 | 0x305B | 0x305D |
483 0x305F | 0x3061 | 0x3064 | 0x3066 | 0x3068 |
484 0x306F | 0x3072 | 0x3075 | 0x3078 | 0x307B |
485 0x30AB | 0x30AD | 0x30AF | 0x30B1 | 0x30B3 |
486 0x30B5 | 0x30B7 | 0x30B9 | 0x30BB | 0x30BD |
487 0x30BF | 0x30C1 | 0x30C4 | 0x30C6 | 0x30C8 |
488 0x30CF | 0x30D2 | 0x30D5 | 0x30D8 | 0x30DB if is_dakuten => {
489 char::from_u32(b + 1)
490 }
491 0x306F | 0x3072 | 0x3075 | 0x3078 | 0x307B |
492 0x30CF | 0x30D2 | 0x30D5 | 0x30D8 | 0x30DB if is_handakuten => {
493 char::from_u32(b + 2)
494 }
495 0x30A6 if is_dakuten => Some('ヴ'),
496 _ => None,
497 }
498}
499
500pub(crate) fn nfd_decompose(s: &str) -> String {
501 const HANGUL_S_BASE: u32 = 0xAC00;
502 const HANGUL_L_BASE: u32 = 0x1100;
503 const HANGUL_V_BASE: u32 = 0x1161;
504 const HANGUL_T_BASE: u32 = 0x11A7;
505 const HANGUL_N_COUNT: u32 = 588;
506 const HANGUL_T_COUNT: u32 = 28;
507 const HANGUL_S_COUNT: u32 = 11172;
508
509 let mut out = String::with_capacity(s.len());
510 for c in s.chars() {
511 let cp = c as u32;
512 if (HANGUL_S_BASE..HANGUL_S_BASE + HANGUL_S_COUNT).contains(&cp) {
513 let s_index = cp - HANGUL_S_BASE;
514 let l_index = s_index / HANGUL_N_COUNT;
515 let v_index = (s_index % HANGUL_N_COUNT) / HANGUL_T_COUNT;
516 let t_index = s_index % HANGUL_T_COUNT;
517 if let Some(l) = char::from_u32(HANGUL_L_BASE + l_index) {
518 out.push(l);
519 }
520 if let Some(v) = char::from_u32(HANGUL_V_BASE + v_index) {
521 out.push(v);
522 }
523 if t_index > 0 {
524 if let Some(t) = char::from_u32(HANGUL_T_BASE + t_index) {
525 out.push(t);
526 }
527 }
528 continue;
529 }
530 if let Some((base, mark)) = japanese_voiced_sound_decompose(c) {
531 out.push(base);
532 out.push(mark);
533 continue;
534 }
535 match NFD_TABLE.iter().find(|(pre, _, _)| *pre == c) {
536 Some((_, base, mark)) => {
537 out.push(*base);
538 out.push(*mark);
539 }
540 None => out.push(c),
541 }
542 }
543 out
544}
545pub(crate) fn nfc_compose(s: &str) -> String {
546 const HANGUL_S_BASE: u32 = 0xAC00;
547 const HANGUL_L_BASE: u32 = 0x1100;
548 const HANGUL_V_BASE: u32 = 0x1161;
549 const HANGUL_T_BASE: u32 = 0x11A7;
550 const HANGUL_L_COUNT: u32 = 19;
551 const HANGUL_V_COUNT: u32 = 21;
552 const HANGUL_T_COUNT: u32 = 28;
553 const HANGUL_N_COUNT: u32 = 588;
554
555 let decomposed = nfd_decompose(s);
556 let mut out = String::with_capacity(decomposed.len());
557 let mut chars = decomposed.chars().peekable();
558 while let Some(c) = chars.next() {
559 let cp = c as u32;
560 if (HANGUL_L_BASE..HANGUL_L_BASE + HANGUL_L_COUNT).contains(&cp) {
561 if let Some(&next) = chars.peek() {
562 let np = next as u32;
563 if (HANGUL_V_BASE..HANGUL_V_BASE + HANGUL_V_COUNT).contains(&np) {
564 chars.next();
565 let l_index = cp - HANGUL_L_BASE;
566 let v_index = np - HANGUL_V_BASE;
567 let mut s_index = l_index * HANGUL_N_COUNT + v_index * HANGUL_T_COUNT;
568 if let Some(&t_next) = chars.peek() {
569 let tp = t_next as u32;
570 if tp > HANGUL_T_BASE && tp < HANGUL_T_BASE + HANGUL_T_COUNT {
571 chars.next();
572 let t_index = tp - HANGUL_T_BASE;
573 s_index += t_index;
574 }
575 }
576 if let Some(hangul) = char::from_u32(HANGUL_S_BASE + s_index) {
577 out.push(hangul);
578 continue;
579 }
580 }
581 }
582 }
583 if let Some(&next) = chars.peek() {
584 if let Some(composed) = japanese_voiced_sound_compose(c, next) {
585 out.push(composed);
586 chars.next();
587 continue;
588 }
589 if let Some((pre, _, _)) = NFD_TABLE.iter().find(|(_, base, mark)| *base == c && *mark == next) {
590 out.push(*pre);
591 chars.next();
592 continue;
593 }
594 }
595 out.push(c);
596 }
597 out
598}
599pub(crate) fn str_normalize(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
600 let form = if matches!(arg(a, 0), Value::Undefined) {
601 String::from("NFC")
602 } else {
603 arg(a, 0).to_js_string()
604 };
605 let s = this_str(&t);
606 match form.as_str() {
607 "NFC" | "NFKC" => Ok(Value::str(nfc_compose(&s))),
608 "NFD" | "NFKD" => Ok(Value::str(nfd_decompose(&s))),
609 _ => Err(it.error("The normalization form should be one of NFC, NFD, NFKC, NFKD.")),
610 }
611}
612pub(crate) fn str_split(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
616 let rx = arg(a, 0);
617 if let Value::Object(_) = &rx {
618 let matcher = it.get_property(&rx, "Symbol(Symbol.split)")?;
619 if !matches!(matcher, Value::Undefined) {
620 return it.call_value(&matcher, rx.clone(), &[t, arg(a, 1)]);
621 }
622 }
623 let s = this_str(&t);
624 let limit = match arg(a, 1) {
625 Value::Undefined => usize::MAX,
626 v => {
627 let n = v.to_number();
628 if n.is_nan() || n < 0.0 {
629 usize::MAX
630 } else {
631 n as usize
632 }
633 }
634 };
635 if let Some(r) = as_regexp(&arg(a, 0)) {
637 let chars: Vec<char> = s.chars().collect();
638 let mut items = Vec::new();
639 let mut last = 0usize;
640 let mut pos = 0usize;
641 'outer: while pos <= chars.len() && items.len() < limit {
642 match r.borrow().re.find_at(&chars, pos) {
643 Some(m) if m.end > m.start => {
644 items.push(Value::str(chars[last..m.start].iter().collect::<String>()));
645 for cap in m.captures.iter().skip(1) {
651 if items.len() >= limit {
652 break 'outer;
653 }
654 let v = match cap {
655 Some((cs, ce)) => {
656 Value::str(chars[*cs..*ce].iter().collect::<String>())
657 }
658 None => Value::Undefined,
659 };
660 items.push(v);
661 }
662 last = m.end;
663 pos = m.end;
664 }
665 _ => break,
666 }
667 }
668 if items.len() < limit {
669 items.push(Value::str(chars[last..].iter().collect::<String>()));
670 }
671 items.truncate(limit);
672 return Ok(Value::Object(Obj::array(items)));
673 }
674 let mut items: Vec<Value> = match arg(a, 0) {
675 Value::Undefined => vec![Value::str(s)],
676 sep => {
677 let sep = sep.to_js_string();
678 if sep.is_empty() {
679 s.chars().map(|c| Value::str(c.to_string())).collect()
680 } else {
681 s.split(&sep as &str)
682 .map(|p| Value::str(p.to_string()))
683 .collect()
684 }
685 }
686 };
687 items.truncate(limit);
688 Ok(Value::Object(Obj::array(items)))
689}
690pub(crate) fn str_replace(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
691 let rx = arg(a, 0);
692 if let Value::Object(_) = &rx {
693 let matcher = it.get_property(&rx, "Symbol(Symbol.replace)")?;
694 if !matches!(matcher, Value::Undefined) {
695 return it.call_value(&matcher, rx.clone(), &[t, arg(a, 1)]);
696 }
697 }
698 let s = this_str(&t);
699 if let Some(r) = as_regexp(&arg(a, 0)) {
701 let global = r.borrow().re.global;
702 return Ok(Value::str(regex_replace(it, &s, &r, arg(a, 1), global)?));
703 }
704 let repl = arg(a, 1);
705 let from = arg(a, 0).to_js_string();
706 if let Some(f) = callable_or_none(repl.clone()) {
708 if let Some(idx) = s.find(&from) {
709 let before = s.get(..idx).unwrap_or("");
710 let after = s.get(idx + from.len()..).unwrap_or("");
711 let rep = it
712 .call_value(
713 &f,
714 Value::Undefined,
715 &[
716 Value::str(from.clone()),
717 Value::Number(s.get(..idx).unwrap_or("").chars().count() as f64),
718 Value::str(s.clone()),
719 ],
720 )?
721 .to_js_string();
722 return Ok(Value::str(format!("{}{}{}", before, rep, after)));
723 }
724 return Ok(Value::str(s));
725 }
726 let to = repl.to_js_string();
727 Ok(Value::str(s.replacen(&from, &to, 1)))
728}
729pub(crate) fn str_replace_all(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
734 let rx = arg(a, 0);
738 if let Some(r) = as_regexp(&rx) {
739 if !r.borrow().re.global {
740 return Err(it.error(
741 "String.prototype.replaceAll called with a non-global RegExp argument",
742 ));
743 }
744 }
745 if let Value::Object(_) = &rx {
748 let matcher = it.get_property(&rx, "Symbol(Symbol.replace)")?;
749 if !matches!(matcher, Value::Undefined) {
750 return it.call_value(&matcher, rx.clone(), &[t, arg(a, 1)]);
751 }
752 }
753 let s = this_str(&t);
754 if let Some(r) = as_regexp(&arg(a, 0)) {
756 if !r.borrow().re.global {
757 return Err(it.error(
758 "String.prototype.replaceAll called with a non-global RegExp argument",
759 ));
760 }
761 return Ok(Value::str(regex_replace(it, &s, &r, arg(a, 1), true)?));
762 }
763 let from = arg(a, 0).to_js_string();
764 let repl = arg(a, 1);
765 if let Some(f) = callable_or_none(repl.clone()) {
767 if from.is_empty() {
768 let chars: Vec<char> = s.chars().collect();
770 let mut out = String::new();
771 for (i, c) in chars.iter().enumerate() {
772 let rep = it
773 .call_value(
774 &f,
775 Value::Undefined,
776 &[Value::str(""), Value::Number(i as f64), Value::str(s.clone())],
777 )?
778 .to_js_string();
779 out.push_str(&rep);
780 out.push(*c);
781 }
782 let rep = it
783 .call_value(
784 &f,
785 Value::Undefined,
786 &[
787 Value::str(""),
788 Value::Number(chars.len() as f64),
789 Value::str(s.clone()),
790 ],
791 )?
792 .to_js_string();
793 out.push_str(&rep);
794 return Ok(Value::str(out));
795 }
796 let mut out = String::new();
797 let mut rest = s.as_str();
798 let mut consumed = 0usize;
799 while let Some(idx) = rest.find(&from) {
800 out.push_str(rest.get(..idx).unwrap_or(""));
801 let char_pos = s.get(..consumed + idx).unwrap_or("").chars().count();
802 let rep = it
803 .call_value(
804 &f,
805 Value::Undefined,
806 &[
807 Value::str(from.clone()),
808 Value::Number(char_pos as f64),
809 Value::str(s.clone()),
810 ],
811 )?
812 .to_js_string();
813 out.push_str(&rep);
814 consumed += idx + from.len();
815 rest = rest.get(idx + from.len()..).unwrap_or("");
816 }
817 out.push_str(rest);
818 return Ok(Value::str(out));
819 }
820 let to = repl.to_js_string();
821 Ok(Value::str(if from.is_empty() {
822 s
823 } else {
824 s.replace(&from, &to)
825 }))
826}
827pub(crate) fn str_repeat(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
833 let n = arg(a, 0).to_number();
834 if n.is_nan() {
835 return Ok(Value::str(String::new()));
836 }
837 if n < 0.0 || n == f64::INFINITY {
838 return Err(it.error("Invalid count value"));
839 }
840 Ok(Value::str(this_str(&t).repeat(n as usize)))
841}
842pub(crate) fn str_concat(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
843 let mut s = this_str(&t);
844 for v in a {
845 s.push_str(&v.to_js_string());
846 }
847 Ok(Value::str(s))
848}
849pub(crate) fn str_pad_start(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
850 let s = this_str(&t);
851 let target = arg(a, 0).to_number() as usize;
852 let pad = if matches!(arg(a, 1), Value::Undefined) {
853 String::from(" ")
854 } else {
855 arg(a, 1).to_js_string()
856 };
857 Ok(Value::str(pad_str(&s, target, &pad, true)))
858}
859pub(crate) fn str_pad_end(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
860 let s = this_str(&t);
861 let target = arg(a, 0).to_number() as usize;
862 let pad = if matches!(arg(a, 1), Value::Undefined) {
863 String::from(" ")
864 } else {
865 arg(a, 1).to_js_string()
866 };
867 Ok(Value::str(pad_str(&s, target, &pad, false)))
868}
869
870pub fn number_get(n: f64, key: &str) -> Value {
872 match key {
873 "toFixed" => nv("toFixed", num_to_fixed),
874 "toString" => nv("toString", num_to_string),
875 "toExponential" => nv("toExponential", num_to_exponential),
876 "toPrecision" => nv("toPrecision", num_to_precision),
877 "valueOf" => nv("valueOf", num_value_of),
878 "toLocaleString" => nv("toLocaleString", num_to_locale_string),
879 _ => {
880 let _ = n;
881 Value::Undefined
882 }
883 }
884}
885pub(crate) fn num_value_of(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
886 Ok(Value::Number(t.to_number()))
887}
888pub fn boolean_get(_b: bool, key: &str) -> Value {
895 match key {
896 "toString" => nv("toString", bool_to_string),
897 "valueOf" => nv("valueOf", bool_value_of),
898 _ => Value::Undefined,
899 }
900}
901pub(crate) fn bool_to_string(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
902 Ok(Value::str(t.to_js_string()))
903}
904pub(crate) fn bool_value_of(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
905 Ok(t)
906}
907pub(crate) fn normalize_mantissa(abs_n: f64) -> (f64, i32) {
910 if abs_n == 0.0 {
911 return (0.0, 0);
912 }
913 let mut exp = libm::floor(libm::log10(abs_n)) as i32;
914 let mut mantissa = abs_n / libm::pow(10.0, exp as f64);
915 if mantissa >= 10.0 {
916 mantissa /= 10.0;
917 exp += 1;
918 } else if mantissa < 1.0 {
919 mantissa *= 10.0;
920 exp -= 1;
921 }
922 (mantissa, exp)
923}
924pub(crate) fn num_to_exponential(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
928 let n = t.to_number();
929 if n.is_nan() {
930 return Ok(Value::str("NaN"));
931 }
932 if n.is_infinite() {
933 return Ok(Value::str(if n > 0.0 { "Infinity" } else { "-Infinity" }));
934 }
935 let neg = n < 0.0;
936 let (mantissa, mut exp) = normalize_mantissa(libm::fabs(n));
937 let d = if matches!(arg(a, 0), Value::Undefined) {
938 6
939 } else {
940 let dv = arg(a, 0).to_number();
941 if !dv.is_finite() || !(0.0..=100.0).contains(&dv) {
942 return Err(it.error("toExponential() argument must be between 0 and 100"));
943 }
944 dv as usize
945 };
946 let scale = libm::pow(10.0, d as f64);
947 let mut rounded = libm::trunc(mantissa * scale + 0.5) / scale;
948 if rounded >= 10.0 {
949 rounded /= 10.0;
950 exp += 1;
951 }
952 let mantissa_str = if d == 0 {
953 format!("{}", rounded as i64)
954 } else {
955 format_fixed(rounded, d)
956 };
957 let sign = if exp >= 0 { "+" } else { "-" };
958 Ok(Value::str(format!(
959 "{}{}e{}{}",
960 if neg { "-" } else { "" },
961 mantissa_str,
962 sign,
963 if exp >= 0 { exp } else { -exp }
964 )))
965}
966pub(crate) fn num_to_precision(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
970 let n = t.to_number();
971 if matches!(arg(a, 0), Value::Undefined) {
972 return Ok(Value::str(t.to_js_string()));
973 }
974 if n.is_nan() {
975 return Ok(Value::str("NaN"));
976 }
977 if n.is_infinite() {
978 return Ok(Value::str(if n > 0.0 { "Infinity" } else { "-Infinity" }));
979 }
980 let p = arg(a, 0).to_number();
981 if !p.is_finite() || !(1.0..=100.0).contains(&p) {
982 return Err(it.error("toPrecision() argument must be between 1 and 100"));
983 }
984 let p = p as usize;
985 if n == 0.0 {
986 return Ok(Value::str(if p == 1 {
987 String::from("0")
988 } else {
989 format!("0.{}", "0".repeat(p - 1))
990 }));
991 }
992 let neg = n < 0.0;
993 let (_, exp) = normalize_mantissa(libm::fabs(n));
994 if (exp as i64) < -6 || (exp as i64) >= p as i64 {
995 return num_to_exponential(it, t, &[Value::Number((p - 1) as f64)]);
997 }
998 let frac_digits = (p as i32 - 1 - exp).max(0) as usize;
1000 let scale = libm::pow(10.0, frac_digits as f64);
1001 let rounded = libm::trunc(libm::fabs(n) * scale + 0.5) / scale;
1002 let s = if frac_digits == 0 {
1003 format!("{}", rounded as i64)
1004 } else {
1005 format_fixed(rounded, frac_digits)
1006 };
1007 Ok(Value::str(if neg { format!("-{}", s) } else { s }))
1008}
1009pub(crate) fn num_to_fixed(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1015 let n = t.to_number();
1016 if n.is_nan() {
1017 return Ok(Value::str("NaN"));
1018 }
1019 let digits = arg(a, 0).to_number();
1020 let d = if matches!(arg(a, 0), Value::Undefined) {
1021 0
1022 } else if !digits.is_finite() || !(0.0..=100.0).contains(&digits) {
1023 return Err(it.error("toFixed() digits argument must be between 0 and 100"));
1024 } else {
1025 digits as usize
1026 };
1027 if n.is_infinite() {
1028 return Ok(Value::str(if n > 0.0 { "Infinity" } else { "-Infinity" }));
1029 }
1030 let scale = libm::pow(10.0, d as f64);
1032 let rounded = libm::trunc(n * scale + if n >= 0.0 { 0.5 } else { -0.5 }) / scale;
1033 if d == 0 {
1034 Ok(Value::str(format!("{}", rounded as i64)))
1035 } else {
1036 Ok(Value::str(format_fixed(rounded, d)))
1037 }
1038}
1039pub(crate) fn num_to_string(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1042 let radix = match arg(a, 0) {
1043 Value::Undefined => 10u32,
1044 v => v.to_number() as u32,
1045 };
1046 if radix == 10 {
1047 return Ok(Value::str(t.to_js_string()));
1048 }
1049 if !(2..=36).contains(&radix) {
1050 return Err(it.error("toString() radix must be between 2 and 36"));
1051 }
1052 Ok(Value::str(format_radix(t.to_number(), radix)))
1053}
1054pub(crate) fn num_to_locale_string(_it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1060 let n = t.to_number();
1061 if !n.is_finite() {
1062 return Ok(Value::str(t.to_js_string()));
1063 }
1064 let s = t.to_js_string();
1065 let (neg, s) = match s.strip_prefix('-') {
1066 Some(rest) => (true, rest),
1067 None => (false, s.as_str()),
1068 };
1069 let (int_part, frac_part) = match s.split_once('.') {
1070 Some((i, f)) => (i, Some(f)),
1071 None => (s, None),
1072 };
1073 let mut grouped = String::new();
1074 let len = int_part.len();
1075 for (i, c) in int_part.chars().enumerate() {
1076 if i > 0 && (len - i) % 3 == 0 {
1077 grouped.push(',');
1078 }
1079 grouped.push(c);
1080 }
1081 let mut out = String::new();
1082 if neg {
1083 out.push('-');
1084 }
1085 out.push_str(&grouped);
1086 if let Some(f) = frac_part {
1087 out.push('.');
1088 out.push_str(f);
1089 }
1090 Ok(Value::str(out))
1091}
1092pub(crate) fn intl_number_format_ctor(_it: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
1096 let obj = Obj::plain();
1097 obj.borrow_mut()
1098 .props
1099 .insert("format".into(), nv("format", intl_number_format_format));
1100 Ok(Value::Object(obj))
1101}
1102pub(crate) fn intl_number_format_format(it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
1103 num_to_locale_string(it, arg(a, 0), &[])
1104}
1105pub(crate) const RADIX_DIGITS: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";
1106pub(crate) fn format_radix(num: f64, radix: u32) -> String {
1107 if num.is_nan() {
1108 return String::from("NaN");
1109 }
1110 if num.is_infinite() {
1111 return String::from(if num > 0.0 { "Infinity" } else { "-Infinity" });
1112 }
1113 let neg = num < 0.0;
1114 let n = libm::fabs(num);
1115 let int_n = libm::floor(n);
1116 let mut frac = n - int_n;
1117 let mut int_digits = Vec::new();
1118 let mut i = int_n;
1119 if i == 0.0 {
1120 int_digits.push(b'0');
1121 }
1122 while i >= 1.0 {
1123 let rem = libm::fmod(i, radix as f64) as usize;
1124 int_digits.push(RADIX_DIGITS[rem.min(35)]);
1125 i = libm::floor(i / radix as f64);
1126 }
1127 int_digits.reverse();
1128 let mut s = String::from_utf8(int_digits).unwrap_or_default();
1129 if frac > 0.0 {
1130 s.push('.');
1131 let mut count = 0;
1132 while frac > 0.0 && count < 20 {
1133 frac *= radix as f64;
1134 let d = libm::floor(frac) as usize;
1135 s.push(RADIX_DIGITS[d.min(35)] as char);
1136 frac -= libm::floor(frac);
1137 count += 1;
1138 }
1139 }
1140 if neg {
1141 format!("-{}", s)
1142 } else {
1143 s
1144 }
1145}
1146
1147pub fn bigint_get(b: &alloc::rc::Rc<super::super::bigint::BigInt>, key: &str) -> Value {
1149 match key {
1150 "toString" => nv("toString", bigint_to_string),
1151 "valueOf" => nv("valueOf", bigint_value_of),
1152 _ => {
1153 let _ = b;
1154 Value::Undefined
1155 }
1156 }
1157}
1158pub(crate) fn bigint_to_string(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1159 Ok(Value::str(t.to_js_string()))
1160}
1161pub(crate) fn bigint_value_of(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1162 Ok(t)
1163}
1164
1165pub(crate) fn bigint_ctor(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1168 use super::super::bigint::BigInt;
1169 let arg = a.first().cloned().unwrap_or(Value::Undefined);
1170 let bi = match &arg {
1171 Value::BigInt(b) => (**b).clone(),
1172 Value::Number(n) => match BigInt::from_f64(*n) {
1173 Some(v) => v,
1174 None => {
1175 return Err(it.error("The number is not a safe integer / cannot convert to BigInt"));
1176 }
1177 },
1178 Value::Bool(b) => BigInt::from_i64(if *b { 1 } else { 0 }),
1179 Value::Str(s) => match BigInt::parse_str(s) {
1180 Some(v) => v,
1181 None => return Err(it.error("Cannot convert string to a BigInt")),
1182 },
1183 _ => return Err(it.error("Cannot convert value to a BigInt")),
1184 };
1185 Ok(Value::bigint(bi))
1186}
1187
1188pub(crate) fn bits_arg(it: &mut Interp, a: &[Value]) -> Result<u64, Value> {
1190 let n = a.first().map(|v| v.to_number()).unwrap_or(f64::NAN);
1191 if !n.is_finite() || n < 0.0 {
1192 return Err(it.error("Invalid bits value for BigInt.asIntN/asUintN"));
1193 }
1194 Ok(libm::trunc(n) as u64)
1195}
1196
1197pub(crate) fn bigint_arg(it: &mut Interp, a: &[Value]) -> Result<super::super::bigint::BigInt, Value> {
1199 match a.get(1) {
1200 Some(Value::BigInt(b)) => Ok((**b).clone()),
1201 _ => Err(it.error("BigInt.asIntN/asUintN requires a BigInt as the second argument")),
1202 }
1203}
1204
1205pub(crate) fn bigint_as_int_n(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1207 let bits = bits_arg(it, a)?;
1208 let v = bigint_arg(it, a)?;
1209 Ok(Value::bigint(v.as_int_n(bits)))
1210}
1211
1212pub(crate) fn bigint_as_uint_n(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1214 let bits = bits_arg(it, a)?;
1215 let v = bigint_arg(it, a)?;
1216 Ok(Value::bigint(v.as_uint_n(bits)))
1217}
1218
1219pub fn object_get(o: &ObjRef, key: &str) -> Value {
1223 let is_array = matches!(o.borrow().kind, ObjKind::Array(_));
1224 if is_array {
1225 match key {
1226 "push" => return nv("push", arr_push),
1227 "pop" => return nv("pop", arr_pop),
1228 "shift" => return nv("shift", arr_shift),
1229 "unshift" => return nv("unshift", arr_unshift),
1230 "slice" => return nv("slice", arr_slice),
1231 "indexOf" => return nv("indexOf", arr_index_of),
1232 "includes" => return nv("includes", arr_includes),
1233 "join" => return nv("join", arr_join),
1234 "concat" => return nv("concat", arr_concat),
1235 "reverse" => return nv("reverse", arr_reverse),
1236 "map" => return nv("map", arr_map),
1237 "filter" => return nv("filter", arr_filter),
1238 "forEach" => return nv("forEach", arr_for_each),
1239 "reduce" => return nv("reduce", arr_reduce),
1240 "find" => return nv("find", arr_find),
1241 "findIndex" => return nv("findIndex", arr_find_index),
1242 "some" => return nv("some", arr_some),
1243 "every" => return nv("every", arr_every),
1244 "sort" => return nv("sort", arr_sort),
1245 "fill" => return nv("fill", arr_fill),
1246 "flat" => return nv("flat", arr_flat),
1247 "flatMap" => return nv("flatMap", arr_flat_map),
1248 "at" => return nv("at", arr_at),
1249 "findLast" => return nv("findLast", arr_find_last),
1250 "findLastIndex" => return nv("findLastIndex", arr_find_last_index),
1251 "entries" => return nv("entries", arr_entries),
1252 "keys" => return nv("keys", arr_keys),
1253 "values" => return nv("values", arr_values),
1254 "Symbol(Symbol.iterator)" => return nv("[Symbol.iterator]", arr_values),
1260 "reduceRight" => return nv("reduceRight", arr_reduce_right),
1261 "toString" => return nv("toString", arr_to_string),
1262 "toLocaleString" => return nv("toLocaleString", arr_to_locale_string),
1263 "splice" => return nv("splice", arr_splice),
1264 "lastIndexOf" => return nv("lastIndexOf", arr_last_index_of),
1265 "copyWithin" => return nv("copyWithin", arr_copy_within),
1266 "toSorted" => return nv("toSorted", arr_to_sorted),
1267 "toReversed" => return nv("toReversed", arr_to_reversed),
1268 "toSpliced" => return nv("toSpliced", arr_to_spliced),
1269 "with" => return nv("with", arr_with),
1270 _ => {}
1271 }
1272 }
1273 match key {
1281 "hasOwnProperty" => nv("hasOwnProperty", obj_has_own),
1282 "toString" | "toLocaleString" => nv("toString", obj_to_string),
1283 "isPrototypeOf" => nv("isPrototypeOf", obj_is_prototype_of),
1284 "propertyIsEnumerable" => nv("propertyIsEnumerable", obj_property_is_enumerable),
1285 "__defineGetter__" => nv("__defineGetter__", obj_define_getter),
1290 "__defineSetter__" => nv("__defineSetter__", obj_define_setter),
1291 "__lookupGetter__" => nv("__lookupGetter__", obj_lookup_getter),
1292 "__lookupSetter__" => nv("__lookupSetter__", obj_lookup_setter),
1293 _ => Value::Undefined,
1294 }
1295}
1296pub(crate) fn obj_define_getter(_: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1297 if let Value::Object(o) = &this {
1298 use super::super::value::Accessor;
1299 let key = arg(a, 0).to_js_string();
1300 let mut b = o.borrow_mut();
1301 let entry = b.accessors.entry(key).or_insert(Accessor { get: None, set: None });
1302 entry.get = Some(arg(a, 1));
1303 }
1304 Ok(Value::Undefined)
1305}
1306pub(crate) fn obj_define_setter(_: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1307 if let Value::Object(o) = &this {
1308 use super::super::value::Accessor;
1309 let key = arg(a, 0).to_js_string();
1310 let mut b = o.borrow_mut();
1311 let entry = b.accessors.entry(key).or_insert(Accessor { get: None, set: None });
1312 entry.set = Some(arg(a, 1));
1313 }
1314 Ok(Value::Undefined)
1315}
1316pub(crate) fn obj_lookup_getter(_: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1317 if let Value::Object(o) = &this {
1318 let key = arg(a, 0).to_js_string();
1319 if let Some(g) = o.borrow().accessors.get(&key).and_then(|acc| acc.get.clone()) {
1320 return Ok(g);
1321 }
1322 }
1323 Ok(Value::Undefined)
1324}
1325pub(crate) fn obj_lookup_setter(_: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1326 if let Value::Object(o) = &this {
1327 let key = arg(a, 0).to_js_string();
1328 if let Some(s) = o.borrow().accessors.get(&key).and_then(|acc| acc.set.clone()) {
1329 return Ok(s);
1330 }
1331 }
1332 Ok(Value::Undefined)
1333}
1334
1335pub(crate) fn this_items(this: &Value) -> Vec<Value> {
1341 if let Value::Object(o) = this {
1342 let o = unwrap_proxy_target(o);
1343 let b = o.borrow();
1344 if let ObjKind::Array(items) = &b.kind {
1345 return items.clone();
1346 }
1347 }
1348 Vec::new()
1349}
1350pub(crate) fn this_objref(this: &Value) -> Option<ObjRef> {
1353 if let Value::Object(o) = this {
1354 Some(unwrap_proxy_target(o))
1355 } else {
1356 None
1357 }
1358}
1359
1360pub(crate) fn array_like_length(it: &mut Interp, t: &Value) -> usize {
1362 let len = it
1363 .get_property(t, "length")
1364 .map(|l| l.to_number())
1365 .unwrap_or(0.0);
1366 if len.is_finite() && len > 0.0 {
1367 len as usize
1368 } else {
1369 0
1370 }
1371}
1372
1373pub(crate) fn arr_push(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1381 if let Some(o) = this_objref(&t) {
1382 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
1383 for v in a {
1384 items.push(v.clone());
1385 }
1386 return Ok(Value::Number(items.len() as f64));
1387 }
1388 }
1389 let mut len = array_like_length(it, &t);
1390 for v in a {
1391 it.set_property(&t, &len.to_string(), v.clone());
1392 len += 1;
1393 }
1394 it.set_property(&t, "length", Value::Number(len as f64));
1395 Ok(Value::Number(len as f64))
1396}
1397pub(crate) fn arr_pop(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1398 if let Some(o) = this_objref(&t) {
1399 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
1400 return Ok(items.pop().unwrap_or(Value::Undefined));
1401 }
1402 }
1403 let len = array_like_length(it, &t);
1404 if len == 0 {
1405 it.set_property(&t, "length", Value::Number(0.0));
1406 return Ok(Value::Undefined);
1407 }
1408 let new_len = len - 1;
1409 let val = it
1410 .get_property(&t, &new_len.to_string())
1411 .unwrap_or(Value::Undefined);
1412 it.set_property(&t, "length", Value::Number(new_len as f64));
1413 Ok(val)
1414}
1415pub(crate) fn arr_shift(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1416 if let Some(o) = this_objref(&t) {
1417 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
1418 if items.is_empty() {
1419 return Ok(Value::Undefined);
1420 }
1421 return Ok(items.remove(0));
1422 }
1423 }
1424 let len = array_like_length(it, &t);
1425 if len == 0 {
1426 it.set_property(&t, "length", Value::Number(0.0));
1427 return Ok(Value::Undefined);
1428 }
1429 let first = it.get_property(&t, "0").unwrap_or(Value::Undefined);
1430 for i in 1..len {
1431 let v = it.get_property(&t, &i.to_string()).unwrap_or(Value::Undefined);
1432 it.set_property(&t, &(i - 1).to_string(), v);
1433 }
1434 it.set_property(&t, "length", Value::Number((len - 1) as f64));
1435 Ok(first)
1436}
1437pub(crate) fn arr_unshift(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1438 if let Some(o) = this_objref(&t) {
1439 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
1440 for (i, v) in a.iter().enumerate() {
1441 items.insert(i, v.clone());
1442 }
1443 return Ok(Value::Number(items.len() as f64));
1444 }
1445 }
1446 let len = array_like_length(it, &t);
1447 let shift = a.len();
1448 for i in (0..len).rev() {
1451 let v = it.get_property(&t, &i.to_string()).unwrap_or(Value::Undefined);
1452 it.set_property(&t, &(i + shift).to_string(), v);
1453 }
1454 for (i, v) in a.iter().enumerate() {
1455 it.set_property(&t, &i.to_string(), v.clone());
1456 }
1457 let new_len = len + shift;
1458 it.set_property(&t, "length", Value::Number(new_len as f64));
1459 Ok(Value::Number(new_len as f64))
1460}
1461pub(crate) fn arr_slice(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1466 let is_array = matches!(&t, Value::Object(o) if matches!(o.borrow().kind, ObjKind::Array(_)));
1467 let items = if is_array {
1468 this_items(&t)
1469 } else {
1470 array_like_items(it, &t)
1471 };
1472 let len = items.len() as i64;
1473 let start = norm_index(arg(a, 0).to_number(), len, 0).max(0) as usize;
1474 let end = if matches!(arg(a, 1), Value::Undefined) {
1475 len
1476 } else {
1477 norm_index(arg(a, 1).to_number(), len, len)
1478 }
1479 .max(0) as usize;
1480 let out = if start < end && start < items.len() {
1481 items[start..end.min(items.len())].to_vec()
1482 } else {
1483 Vec::new()
1484 };
1485 Ok(Value::Object(Obj::array(out)))
1486}
1487pub(crate) fn arr_from_index(a: &[Value], len: usize) -> usize {
1490 if matches!(arg(a, 1), Value::Undefined) {
1491 return 0;
1492 }
1493 let n = arg(a, 1).to_number();
1494 if n.is_nan() {
1495 return 0;
1496 }
1497 let len = len as i64;
1498 let idx = if n < 0.0 { (len + n as i64).max(0) } else { n as i64 };
1499 idx.min(len) as usize
1500}
1501pub(crate) fn arr_index_of(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1508 let items = generic_array_items(it, &t);
1509 let target = arg(a, 0);
1510 let from = arr_from_index(a, items.len());
1511 for (i, v) in items.iter().enumerate().skip(from) {
1512 if v.strict_eq(&target) {
1513 return Ok(Value::Number(i as f64));
1514 }
1515 }
1516 Ok(Value::Number(-1.0))
1517}
1518pub(crate) fn arr_includes(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1523 let items = generic_array_items(it, &t);
1524 let target = arg(a, 0);
1525 let from = arr_from_index(a, items.len());
1526 Ok(Value::Bool(
1527 items.iter().skip(from).any(|v| same_value_zero(v, &target)),
1528 ))
1529}
1530pub(crate) fn arr_join(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1536 let is_array = matches!(&t, Value::Object(o) if matches!(o.borrow().kind, ObjKind::Array(_)));
1537 let items = if is_array {
1538 this_items(&t)
1539 } else {
1540 array_like_items(it, &t)
1541 };
1542 let sep = if matches!(arg(a, 0), Value::Undefined) {
1543 String::from(",")
1544 } else {
1545 arg(a, 0).to_js_string()
1546 };
1547 let parts: Vec<String> = items
1548 .iter()
1549 .map(|v| match v {
1550 Value::Undefined | Value::Null => String::new(),
1551 _ => v.to_js_string(),
1552 })
1553 .collect();
1554 Ok(Value::str(parts.join(&sep)))
1555}
1556pub(crate) fn is_real_array(t: &Value) -> bool {
1561 matches!(t, Value::Object(o) if matches!(o.borrow().kind, ObjKind::Array(_)))
1562}
1563pub(crate) fn generic_array_items(it: &mut Interp, t: &Value) -> Vec<Value> {
1565 if is_real_array(t) {
1566 this_items(t)
1567 } else {
1568 array_like_items(it, t)
1569 }
1570}
1571pub(crate) fn array_like_items(it: &mut Interp, v: &Value) -> Vec<Value> {
1575 let len = it
1576 .get_property(v, "length")
1577 .map(|l| l.to_number())
1578 .unwrap_or(0.0);
1579 let len = if len.is_finite() && len > 0.0 { len as usize } else { 0 };
1580 let mut out = Vec::with_capacity(len);
1581 for i in 0..len {
1582 out.push(
1583 it.get_property(v, &i.to_string())
1584 .unwrap_or(Value::Undefined),
1585 );
1586 }
1587 out
1588}
1589
1590pub(crate) fn concat_push(it: &mut Interp, v: &Value, items: &mut Vec<Value>) -> Result<(), Value> {
1602 match v {
1603 Value::Object(o) => {
1604 let flag = it.get_property(v, "Symbol(Symbol.isConcatSpreadable)")?;
1605 let is_array = matches!(o.borrow().kind, ObjKind::Array(_));
1606 let spreadable = if matches!(flag, Value::Undefined) {
1607 is_array
1608 } else {
1609 flag.truthy()
1610 };
1611 if !spreadable {
1612 items.push(v.clone());
1613 } else if is_array {
1614 if let ObjKind::Array(other) = &o.borrow().kind {
1615 items.extend(other.clone());
1616 }
1617 } else {
1618 items.extend(array_like_items(it, v));
1619 }
1620 }
1621 other => items.push(other.clone()),
1622 }
1623 Ok(())
1624}
1625pub(crate) fn arr_concat(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1626 let mut items = Vec::new();
1627 concat_push(it, &t, &mut items)?;
1628 for v in a {
1629 concat_push(it, v, &mut items)?;
1630 }
1631 Ok(Value::Object(Obj::array(items)))
1632}
1633pub(crate) fn arr_reverse(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1634 if let Some(o) = this_objref(&t) {
1635 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
1636 items.reverse();
1637 }
1638 }
1639 Ok(t)
1640}
1641pub(crate) fn arr_to_string(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1642 Ok(Value::str(t.to_js_string()))
1643}
1644pub(crate) fn arr_to_locale_string(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1649 let items = generic_array_items(it, &t);
1650 let mut parts = Vec::with_capacity(items.len());
1651 for v in items {
1652 if matches!(v, Value::Undefined | Value::Null) {
1653 parts.push(String::new());
1654 continue;
1655 }
1656 let method = it.get_property(&v, "toLocaleString").unwrap_or(Value::Undefined);
1657 let s = if matches!(&method, Value::Object(f) if f.borrow().is_callable()) {
1658 it.call_value(&method, v.clone(), &[])?.to_js_string()
1659 } else {
1660 v.to_js_string()
1661 };
1662 parts.push(s);
1663 }
1664 Ok(Value::str(parts.join(",")))
1665}
1666pub(crate) fn arr_map(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1670 let items = generic_array_items(it, &t);
1671 let f = arg(a, 0);
1672 let this_arg = arg(a, 1);
1673 let mut out = Vec::with_capacity(items.len());
1674 for (i, v) in items.iter().enumerate() {
1675 let r = it.call_value(
1676 &f,
1677 this_arg.clone(),
1678 &[v.clone(), Value::Number(i as f64), t.clone()],
1679 )?;
1680 out.push(r);
1681 }
1682 Ok(same_kind_as(it, &t, out))
1686}
1687pub(crate) fn arr_filter(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1688 let items = generic_array_items(it, &t);
1689 let f = arg(a, 0);
1690 let this_arg = arg(a, 1);
1691 let mut out = Vec::new();
1692 for (i, v) in items.iter().enumerate() {
1693 if it
1694 .call_value(
1695 &f,
1696 this_arg.clone(),
1697 &[v.clone(), Value::Number(i as f64), t.clone()],
1698 )?
1699 .truthy()
1700 {
1701 out.push(v.clone());
1702 }
1703 }
1704 Ok(same_kind_as(it, &t, out))
1706}
1707pub(crate) fn arr_for_each(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1712 let items = generic_array_items(it, &t);
1713 let f = arg(a, 0);
1714 let this_arg = arg(a, 1);
1715 for (i, v) in items.iter().enumerate() {
1716 it.call_value(
1717 &f,
1718 this_arg.clone(),
1719 &[v.clone(), Value::Number(i as f64), t.clone()],
1720 )?;
1721 }
1722 Ok(Value::Undefined)
1723}
1724pub(crate) fn arr_reduce(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1728 let items = generic_array_items(it, &t);
1729 let f = arg(a, 0);
1730 let mut idx = 0;
1731 let mut acc = if a.len() >= 2 {
1732 arg(a, 1)
1733 } else {
1734 if items.is_empty() {
1735 return Err(it.error("Reduce of empty array with no initial value"));
1736 }
1737 idx = 1;
1738 items[0].clone()
1739 };
1740 while idx < items.len() {
1741 acc = it.call_value(
1742 &f,
1743 Value::Undefined,
1744 &[
1745 acc,
1746 items[idx].clone(),
1747 Value::Number(idx as f64),
1748 t.clone(),
1749 ],
1750 )?;
1751 idx += 1;
1752 }
1753 Ok(acc)
1754}
1755pub(crate) fn arr_find(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1756 let items = generic_array_items(it, &t);
1757 let f = arg(a, 0);
1758 let this_arg = arg(a, 1);
1759 for (i, v) in items.iter().enumerate() {
1760 if it
1761 .call_value(
1762 &f,
1763 this_arg.clone(),
1764 &[v.clone(), Value::Number(i as f64), t.clone()],
1765 )?
1766 .truthy()
1767 {
1768 return Ok(v.clone());
1769 }
1770 }
1771 Ok(Value::Undefined)
1772}
1773pub(crate) fn arr_find_index(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1774 let items = generic_array_items(it, &t);
1775 let f = arg(a, 0);
1776 let this_arg = arg(a, 1);
1777 for (i, v) in items.iter().enumerate() {
1778 if it
1779 .call_value(
1780 &f,
1781 this_arg.clone(),
1782 &[v.clone(), Value::Number(i as f64), t.clone()],
1783 )?
1784 .truthy()
1785 {
1786 return Ok(Value::Number(i as f64));
1787 }
1788 }
1789 Ok(Value::Number(-1.0))
1790}
1791pub(crate) fn arr_some(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1792 let items = generic_array_items(it, &t);
1793 let f = arg(a, 0);
1794 let this_arg = arg(a, 1);
1795 for (i, v) in items.iter().enumerate() {
1796 if it
1797 .call_value(
1798 &f,
1799 this_arg.clone(),
1800 &[v.clone(), Value::Number(i as f64), t.clone()],
1801 )?
1802 .truthy()
1803 {
1804 return Ok(Value::Bool(true));
1805 }
1806 }
1807 Ok(Value::Bool(false))
1808}
1809pub(crate) fn arr_every(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1810 let items = generic_array_items(it, &t);
1811 let f = arg(a, 0);
1812 let this_arg = arg(a, 1);
1813 for (i, v) in items.iter().enumerate() {
1814 if !it
1815 .call_value(
1816 &f,
1817 this_arg.clone(),
1818 &[v.clone(), Value::Number(i as f64), t.clone()],
1819 )?
1820 .truthy()
1821 {
1822 return Ok(Value::Bool(false));
1823 }
1824 }
1825 Ok(Value::Bool(true))
1826}
1827pub(crate) fn insertion_sort_excluding_undefined(
1833 it: &mut Interp,
1834 mut items: alloc::vec::Vec<Value>,
1835 cmp: &Value,
1836) -> Result<alloc::vec::Vec<Value>, Value> {
1837 let undefined_count = items.iter().filter(|v| matches!(v, Value::Undefined)).count();
1838 items.retain(|v| !matches!(v, Value::Undefined));
1839 let n = items.len();
1840 for i in 1..n {
1841 let mut j = i;
1842 while j > 0 {
1843 let ord = if matches!(cmp, Value::Undefined) {
1844 items[j - 1].to_js_string().cmp(&items[j].to_js_string())
1845 } else {
1846 let r = it
1847 .call_value(
1848 cmp,
1849 Value::Undefined,
1850 &[items[j - 1].clone(), items[j].clone()],
1851 )?
1852 .to_number();
1853 if r > 0.0 {
1854 core::cmp::Ordering::Greater
1855 } else if r < 0.0 {
1856 core::cmp::Ordering::Less
1857 } else {
1858 core::cmp::Ordering::Equal
1859 }
1860 };
1861 if ord == core::cmp::Ordering::Greater {
1862 items.swap(j - 1, j);
1863 j -= 1;
1864 } else {
1865 break;
1866 }
1867 }
1868 }
1869 for _ in 0..undefined_count {
1870 items.push(Value::Undefined);
1871 }
1872 Ok(items)
1873}
1874pub(crate) fn arr_sort(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1875 let items = this_items(&t);
1876 let cmp = arg(a, 0);
1877 let items = insertion_sort_excluding_undefined(it, items, &cmp)?;
1878 if let Some(o) = this_objref(&t) {
1879 if let ObjKind::Array(slot) = &mut o.borrow_mut().kind {
1880 *slot = items;
1881 }
1882 }
1883 Ok(t)
1884}
1885pub(crate) fn arr_fill(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1889 if let Some(o) = this_objref(&t) {
1890 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
1891 let v = arg(a, 0);
1892 let len = items.len() as i64;
1893 let start = norm_index(arg(a, 1).to_number(), len, 0).max(0) as usize;
1894 let end = if matches!(arg(a, 2), Value::Undefined) {
1895 len
1896 } else {
1897 norm_index(arg(a, 2).to_number(), len, len)
1898 }
1899 .max(0) as usize;
1900 let end = end.min(items.len());
1901 for slot in items.iter_mut().take(end).skip(start) {
1902 *slot = v.clone();
1903 }
1904 }
1905 }
1906 Ok(t)
1907}
1908pub(crate) fn arr_splice(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1909 let o = match this_objref(&t) {
1910 Some(o) => o,
1911 None => return Ok(Value::Object(Obj::array(alloc::vec![]))),
1912 };
1913 let mut items = if let ObjKind::Array(v) = &o.borrow().kind {
1914 v.clone()
1915 } else {
1916 return Ok(Value::Object(Obj::array(alloc::vec![])));
1917 };
1918 let len = items.len() as i64;
1919 let start_raw = arg(a, 0).to_number() as i64;
1920 let start = if start_raw < 0 {
1921 (len + start_raw).max(0) as usize
1922 } else {
1923 start_raw.min(len) as usize
1924 };
1925 let delete_count = if a.len() < 2 {
1926 items.len() - start
1927 } else {
1928 let dc = arg(a, 1).to_number() as i64;
1929 dc.max(0).min((len - start as i64).max(0)) as usize
1930 };
1931 let insert: alloc::vec::Vec<Value> = a.iter().skip(2).cloned().collect();
1932 let removed: alloc::vec::Vec<Value> = items.drain(start..start + delete_count).collect();
1933 for (i, v) in insert.into_iter().enumerate() {
1934 items.insert(start + i, v);
1935 }
1936 if let ObjKind::Array(slot) = &mut o.borrow_mut().kind {
1937 *slot = items;
1938 }
1939 Ok(Value::Object(Obj::array(removed)))
1940}
1941
1942pub(crate) fn arr_last_index_of(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1950 let items = generic_array_items(it, &t);
1951 let target = arg(a, 0);
1952 let len = items.len() as i64;
1953 if len == 0 {
1954 return Ok(Value::Number(-1.0));
1955 }
1956 let start = if matches!(arg(a, 1), Value::Undefined) {
1957 len - 1
1958 } else {
1959 let n = arg(a, 1).to_number();
1960 let n = if n.is_nan() { 0.0 } else { n };
1961 if n < 0.0 {
1962 len + n as i64
1963 } else {
1964 (n as i64).min(len - 1)
1965 }
1966 };
1967 if start < 0 {
1968 return Ok(Value::Number(-1.0));
1969 }
1970 for i in (0..=start as usize).rev() {
1971 if items[i].strict_eq(&target) {
1972 return Ok(Value::Number(i as f64));
1973 }
1974 }
1975 Ok(Value::Number(-1.0))
1976}
1977
1978pub(crate) fn arr_copy_within(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1979 let o = match this_objref(&t) {
1980 Some(o) => o,
1981 None => return Ok(t),
1982 };
1983 let mut items = if let ObjKind::Array(v) = &o.borrow().kind {
1984 v.clone()
1985 } else {
1986 return Ok(t);
1987 };
1988 let len = items.len() as i64;
1989 let to_raw = arg(a, 0).to_number() as i64;
1990 let to = if to_raw < 0 {
1991 (len + to_raw).max(0) as usize
1992 } else {
1993 to_raw.min(len) as usize
1994 };
1995 let from_raw = if matches!(arg(a, 1), Value::Undefined) { 0i64 } else { arg(a, 1).to_number() as i64 };
1996 let from = if from_raw < 0 {
1997 (len + from_raw).max(0) as usize
1998 } else {
1999 from_raw.min(len) as usize
2000 };
2001 let end_raw = if matches!(arg(a, 2), Value::Undefined) { len } else { arg(a, 2).to_number() as i64 };
2002 let end = if end_raw < 0 {
2003 (len + end_raw).max(0) as usize
2004 } else {
2005 end_raw.min(len) as usize
2006 };
2007 let count = if end > from { (end - from).min(len as usize - to) } else { 0 };
2008 let src: alloc::vec::Vec<Value> = items[from..from + count].to_vec();
2009 for (i, v) in src.into_iter().enumerate() {
2010 if to + i < items.len() {
2011 items[to + i] = v;
2012 }
2013 }
2014 if let ObjKind::Array(slot) = &mut o.borrow_mut().kind {
2015 *slot = items;
2016 }
2017 Ok(t)
2018}
2019
2020pub(crate) fn same_kind_as(it: &mut Interp, t: &Value, items: alloc::vec::Vec<Value>) -> Value {
2027 let ta_kind_tag = if let Value::Object(o) = t {
2028 o.borrow().props.get("_ta_kind").map(|v| v.to_js_string())
2029 } else {
2030 None
2031 };
2032 match ta_kind_tag {
2033 Some(tag) => {
2034 let kind = TaKind::from_name(&tag);
2038 let values: alloc::vec::Vec<f64> = items.iter().map(|v| kind.convert(v)).collect();
2039 make_typed_array(it, values, kind)
2040 }
2041 None => Value::Object(Obj::array(items)),
2042 }
2043}
2044pub(crate) fn arr_to_sorted(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2045 let items = generic_array_items(it, &t);
2046 let cmp = arg(a, 0);
2047 let items = insertion_sort_excluding_undefined(it, items, &cmp)?;
2048 Ok(same_kind_as(it, &t, items))
2049}
2050
2051pub(crate) fn arr_to_reversed(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
2052 let mut items = generic_array_items(it, &t);
2053 items.reverse();
2054 Ok(same_kind_as(it, &t, items))
2055}
2056
2057pub(crate) fn arr_to_spliced(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2058 let mut items = generic_array_items(it, &t);
2059 let len = items.len() as i64;
2060 let start_raw = arg(a, 0).to_number() as i64;
2061 let start = if start_raw < 0 {
2062 (len + start_raw).max(0) as usize
2063 } else {
2064 start_raw.min(len) as usize
2065 };
2066 let delete_count = if a.len() < 2 {
2067 items.len() - start
2068 } else {
2069 let dc = arg(a, 1).to_number() as i64;
2070 dc.max(0).min((len - start as i64).max(0)) as usize
2071 };
2072 let insert: alloc::vec::Vec<Value> = a.iter().skip(2).cloned().collect();
2073 items.drain(start..start + delete_count);
2074 for (i, v) in insert.into_iter().enumerate() {
2075 items.insert(start + i, v);
2076 }
2077 Ok(same_kind_as(it, &t, items))
2078}
2079
2080pub(crate) fn arr_with(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2083 let mut items = generic_array_items(it, &t);
2084 let len = items.len() as i64;
2085 let idx_raw = arg(a, 0).to_number() as i64;
2086 let idx = if idx_raw < 0 { len + idx_raw } else { idx_raw };
2087 if idx < 0 || idx >= len {
2088 return Err(it.error("Invalid index"));
2089 }
2090 items[idx as usize] = arg(a, 1);
2091 Ok(same_kind_as(it, &t, items))
2092}
2093
2094pub(crate) fn flatten_depth(items: Vec<Value>, depth: f64, out: &mut Vec<Value>, seen: &mut Vec<ObjRef>) {
2103 for v in items {
2104 match v {
2105 Value::Object(o) if depth > 0.0 && matches!(o.borrow().kind, ObjKind::Array(_)) => {
2106 if seen.iter().any(|s| Rc::ptr_eq(s, &o)) {
2107 out.push(Value::Object(o));
2108 continue;
2109 }
2110 seen.push(o.clone());
2111 let inner = match &o.borrow().kind {
2112 ObjKind::Array(inner) => inner.clone(),
2113 _ => Vec::new(),
2114 };
2115 flatten_depth(inner, depth - 1.0, out, seen);
2116 seen.pop();
2117 }
2118 other => out.push(other),
2119 }
2120 }
2121}
2122pub(crate) fn arr_flat(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2126 let items = generic_array_items(it, &t);
2127 let depth = if matches!(arg(a, 0), Value::Undefined) {
2128 1.0
2129 } else {
2130 arg(a, 0).to_number()
2131 };
2132 let mut out = Vec::new();
2133 flatten_depth(items, depth, &mut out, &mut Vec::new());
2134 Ok(Value::Object(Obj::array(out)))
2135}
2136pub(crate) fn has_own_property(o: &super::super::value::ObjRef, key: &str) -> bool {
2142 let b = o.borrow();
2143 if let ObjKind::Array(items) = &b.kind {
2144 if key == "length" {
2145 return true;
2146 }
2147 if let Ok(idx) = key.parse::<usize>() {
2148 return idx < items.len();
2149 }
2150 }
2151 b.props.contains_key(key) || b.accessors.contains_key(key)
2152}
2153pub(crate) fn obj_has_own(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2154 let key = arg(a, 0).to_js_string();
2155 if let Value::Object(o) = &t {
2156 return Ok(Value::Bool(has_own_property(o, &key)));
2157 }
2158 Ok(Value::Bool(false))
2159}
2160pub(crate) fn obj_to_string(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
2166 if let Value::Object(o) = &t {
2170 let mut cur = Some(o.clone());
2171 while let Some(c) = cur {
2172 let (own_tag, getter, next) = {
2173 let b = c.borrow();
2174 (
2175 b.props.get("Symbol(Symbol.toStringTag)").cloned(),
2176 b.accessors
2177 .get("Symbol(Symbol.toStringTag)")
2178 .and_then(|acc| acc.get.clone()),
2179 b.proto.clone(),
2180 )
2181 };
2182 if let Some(v) = own_tag {
2183 return Ok(Value::str(format!("[object {}]", v.to_js_string())));
2184 }
2185 if let Some(g) = getter {
2186 let v = it.call_value(&g, t.clone(), &[])?;
2187 return Ok(Value::str(format!("[object {}]", v.to_js_string())));
2188 }
2189 cur = next;
2190 }
2191 }
2192 let tag = match &t {
2193 Value::Undefined => "Undefined",
2194 Value::Null => "Null",
2195 Value::Object(o) => {
2196 let b = o.borrow();
2197 if b.is_callable() {
2198 "Function"
2199 } else {
2200 match &b.kind {
2201 ObjKind::Array(_) => "Array",
2202 ObjKind::DateObj(_) => "Date",
2203 ObjKind::RegExpObj(_) => "RegExp",
2204 ObjKind::MapObj(_) => "Map",
2205 ObjKind::SetObj(_) => "Set",
2206 ObjKind::PromiseObj(_) => "Promise",
2207 ObjKind::Generator(_) => "Generator",
2208 _ => "Object",
2209 }
2210 }
2211 }
2212 Value::Str(_) => "String",
2225 Value::Number(_) => "Number",
2226 Value::Bool(_) => "Boolean",
2227 Value::BigInt(_) => "BigInt",
2228 };
2229 Ok(Value::str(format!("[object {}]", tag)))
2230}
2231pub(crate) fn obj_is_prototype_of(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2234 let (Value::Object(target), Value::Object(candidate)) = (&t, &arg(a, 0)) else {
2235 return Ok(Value::Bool(false));
2236 };
2237 let mut cur = candidate.borrow().proto.clone();
2238 let mut guard = 0;
2239 while let Some(p) = cur {
2240 if Rc::ptr_eq(&p, target) {
2241 return Ok(Value::Bool(true));
2242 }
2243 cur = p.borrow().proto.clone();
2244 guard += 1;
2245 if guard > 1000 {
2246 break;
2247 }
2248 }
2249 Ok(Value::Bool(false))
2250}
2251pub(crate) fn obj_property_is_enumerable(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2255 obj_has_own(it, t, a)
2256}
2257
2258pub fn function_method(key: &str) -> Value {
2262 match key {
2263 "call" => nv("call", fn_call),
2264 "apply" => nv("apply", fn_apply),
2265 "bind" => nv("bind", fn_bind),
2266 _ => Value::Undefined,
2267 }
2268}
2269pub(crate) fn fn_call(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2270 let this_arg = arg(a, 0);
2271 let rest: Vec<Value> = a.get(1..).map(|s| s.to_vec()).unwrap_or_default();
2272 it.call_value(&t, this_arg, &rest)
2273}
2274pub(crate) fn fn_apply(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2279 let this_arg = arg(a, 0);
2280 let args_vec = match arg(a, 1) {
2281 Value::Undefined | Value::Null => Vec::new(),
2282 v => array_like_items(it, &v),
2283 };
2284 it.call_value(&t, this_arg, &args_vec)
2285}
2286pub(crate) fn fn_bind(_it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2287 let bound_this = arg(a, 0);
2288 let bound_args: Vec<Value> = a.get(1..).map(|s| s.to_vec()).unwrap_or_default();
2289 Ok(Value::Object(Obj::bound(t, bound_this, bound_args)))
2290}
2291
2292pub(crate) fn norm_index(v: f64, len: i64, default: i64) -> i64 {
2295 if v.is_nan() {
2296 return default;
2297 }
2298 let i = libm::trunc(v) as i64;
2299 if i < 0 {
2300 (len + i).max(0)
2301 } else {
2302 i.min(len)
2303 }
2304}
2305pub(crate) fn clamp_idx(v: f64, len: usize) -> usize {
2306 if v.is_nan() || v < 0.0 {
2307 0
2308 } else {
2309 (v as usize).min(len)
2310 }
2311}
2312pub(crate) fn pad_str(s: &str, target: usize, pad: &str, start: bool) -> String {
2313 let cur = s.chars().count();
2314 if cur >= target || pad.is_empty() {
2315 return String::from(s);
2316 }
2317 let mut padding = String::new();
2318 let pad_chars: Vec<char> = pad.chars().collect();
2319 let mut i = 0;
2320 while padding.chars().count() < target - cur {
2321 padding.push(pad_chars[i % pad_chars.len()]);
2322 i += 1;
2323 }
2324 if start {
2325 format!("{}{}", padding, s)
2326 } else {
2327 format!("{}{}", s, padding)
2328 }
2329}
2330pub(crate) fn format_fixed(n: f64, digits: usize) -> String {
2331 let neg = n < 0.0;
2332 let n = libm::fabs(n);
2333 let int_part = libm::trunc(n) as i64;
2334 let scale = libm::pow(10.0, digits as f64);
2335 let frac = libm::trunc((n - libm::trunc(n)) * scale + 0.5) as i64;
2336 let frac_str = format!("{:0width$}", frac, width = digits);
2337 format!("{}{}.{}", if neg { "-" } else { "" }, int_part, frac_str)
2338}
2339