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 if is_typed_array(&t) {
1486 return Ok(same_kind_as(it, &t, out));
1487 }
1488 let a_obj = array_species_create(it, &t, out.len())?;
1489 populate_array_species(it, &a_obj, out)
1490}
1491pub(crate) fn arr_from_index(a: &[Value], len: usize) -> usize {
1494 if matches!(arg(a, 1), Value::Undefined) {
1495 return 0;
1496 }
1497 let n = arg(a, 1).to_number();
1498 if n.is_nan() {
1499 return 0;
1500 }
1501 let len = len as i64;
1502 let idx = if n < 0.0 { (len + n as i64).max(0) } else { n as i64 };
1503 idx.min(len) as usize
1504}
1505pub(crate) fn arr_index_of(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1512 let items = generic_array_items(it, &t);
1513 let target = arg(a, 0);
1514 let from = arr_from_index(a, items.len());
1515 for (i, v) in items.iter().enumerate().skip(from) {
1516 if v.strict_eq(&target) {
1517 return Ok(Value::Number(i as f64));
1518 }
1519 }
1520 Ok(Value::Number(-1.0))
1521}
1522pub(crate) fn arr_includes(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1527 let items = generic_array_items(it, &t);
1528 let target = arg(a, 0);
1529 let from = arr_from_index(a, items.len());
1530 Ok(Value::Bool(
1531 items.iter().skip(from).any(|v| same_value_zero(v, &target)),
1532 ))
1533}
1534pub(crate) fn arr_join(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1540 let is_array = matches!(&t, Value::Object(o) if matches!(o.borrow().kind, ObjKind::Array(_)));
1541 let items = if is_array {
1542 this_items(&t)
1543 } else {
1544 array_like_items(it, &t)
1545 };
1546 let sep = if matches!(arg(a, 0), Value::Undefined) {
1547 String::from(",")
1548 } else {
1549 arg(a, 0).to_js_string()
1550 };
1551 let parts: Vec<String> = items
1552 .iter()
1553 .map(|v| match v {
1554 Value::Undefined | Value::Null => String::new(),
1555 _ => v.to_js_string(),
1556 })
1557 .collect();
1558 Ok(Value::str(parts.join(&sep)))
1559}
1560pub(crate) fn is_real_array(t: &Value) -> bool {
1565 matches!(t, Value::Object(o) if matches!(o.borrow().kind, ObjKind::Array(_)))
1566}
1567pub(crate) fn generic_array_items(it: &mut Interp, t: &Value) -> Vec<Value> {
1569 if is_real_array(t) {
1570 this_items(t)
1571 } else {
1572 array_like_items(it, t)
1573 }
1574}
1575pub(crate) fn array_like_items(it: &mut Interp, v: &Value) -> Vec<Value> {
1579 let len = it
1580 .get_property(v, "length")
1581 .map(|l| l.to_number())
1582 .unwrap_or(0.0);
1583 let len = if len.is_finite() && len > 0.0 { len as usize } else { 0 };
1584 let mut out = Vec::with_capacity(len);
1585 for i in 0..len {
1586 out.push(
1587 it.get_property(v, &i.to_string())
1588 .unwrap_or(Value::Undefined),
1589 );
1590 }
1591 out
1592}
1593
1594pub(crate) fn concat_push(it: &mut Interp, v: &Value, items: &mut Vec<Value>) -> Result<(), Value> {
1606 match v {
1607 Value::Object(o) => {
1608 let flag = it.get_property(v, "Symbol(Symbol.isConcatSpreadable)")?;
1609 let is_array = matches!(o.borrow().kind, ObjKind::Array(_));
1610 let spreadable = if matches!(flag, Value::Undefined) {
1611 is_array
1612 } else {
1613 flag.truthy()
1614 };
1615 if !spreadable {
1616 items.push(v.clone());
1617 } else if is_array {
1618 if let ObjKind::Array(other) = &o.borrow().kind {
1619 items.extend(other.clone());
1620 }
1621 } else {
1622 items.extend(array_like_items(it, v));
1623 }
1624 }
1625 other => items.push(other.clone()),
1626 }
1627 Ok(())
1628}
1629pub(crate) fn arr_concat(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1630 let mut items = Vec::new();
1631 concat_push(it, &t, &mut items)?;
1632 for v in a {
1633 concat_push(it, v, &mut items)?;
1634 }
1635 let a_obj = array_species_create(it, &t, 0)?;
1636 populate_array_species(it, &a_obj, items)
1637}
1638pub(crate) fn arr_reverse(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1639 if let Some(o) = this_objref(&t) {
1640 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
1641 items.reverse();
1642 }
1643 }
1644 Ok(t)
1645}
1646pub(crate) fn arr_to_string(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1647 Ok(Value::str(t.to_js_string()))
1648}
1649pub(crate) fn arr_to_locale_string(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1654 let items = generic_array_items(it, &t);
1655 let mut parts = Vec::with_capacity(items.len());
1656 for v in items {
1657 if matches!(v, Value::Undefined | Value::Null) {
1658 parts.push(String::new());
1659 continue;
1660 }
1661 let method = it.get_property(&v, "toLocaleString").unwrap_or(Value::Undefined);
1662 let s = if matches!(&method, Value::Object(f) if f.borrow().is_callable()) {
1663 it.call_value(&method, v.clone(), &[])?.to_js_string()
1664 } else {
1665 v.to_js_string()
1666 };
1667 parts.push(s);
1668 }
1669 Ok(Value::str(parts.join(",")))
1670}
1671pub(crate) fn arr_map(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1675 let items = generic_array_items(it, &t);
1676 let f = arg(a, 0);
1677 let this_arg = arg(a, 1);
1678 let mut out = Vec::with_capacity(items.len());
1679 for (i, v) in items.iter().enumerate() {
1680 let r = it.call_value(
1681 &f,
1682 this_arg.clone(),
1683 &[v.clone(), Value::Number(i as f64), t.clone()],
1684 )?;
1685 out.push(r);
1686 }
1687 if is_typed_array(&t) {
1688 return Ok(same_kind_as(it, &t, out));
1689 }
1690 let a_obj = array_species_create(it, &t, items.len())?;
1691 populate_array_species(it, &a_obj, out)
1692}
1693pub(crate) fn arr_filter(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1694 let items = generic_array_items(it, &t);
1695 let f = arg(a, 0);
1696 let this_arg = arg(a, 1);
1697 let mut out = Vec::new();
1698 for (i, v) in items.iter().enumerate() {
1699 if it
1700 .call_value(
1701 &f,
1702 this_arg.clone(),
1703 &[v.clone(), Value::Number(i as f64), t.clone()],
1704 )?
1705 .truthy()
1706 {
1707 out.push(v.clone());
1708 }
1709 }
1710 if is_typed_array(&t) {
1711 return Ok(same_kind_as(it, &t, out));
1712 }
1713 let a_obj = array_species_create(it, &t, 0)?;
1714 populate_array_species(it, &a_obj, out)
1715}
1716pub(crate) fn arr_for_each(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1721 let items = generic_array_items(it, &t);
1722 let f = arg(a, 0);
1723 let this_arg = arg(a, 1);
1724 for (i, v) in items.iter().enumerate() {
1725 it.call_value(
1726 &f,
1727 this_arg.clone(),
1728 &[v.clone(), Value::Number(i as f64), t.clone()],
1729 )?;
1730 }
1731 Ok(Value::Undefined)
1732}
1733pub(crate) fn arr_reduce(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1737 let items = generic_array_items(it, &t);
1738 let f = arg(a, 0);
1739 let mut idx = 0;
1740 let mut acc = if a.len() >= 2 {
1741 arg(a, 1)
1742 } else {
1743 if items.is_empty() {
1744 return Err(it.error("Reduce of empty array with no initial value"));
1745 }
1746 idx = 1;
1747 items[0].clone()
1748 };
1749 while idx < items.len() {
1750 acc = it.call_value(
1751 &f,
1752 Value::Undefined,
1753 &[
1754 acc,
1755 items[idx].clone(),
1756 Value::Number(idx as f64),
1757 t.clone(),
1758 ],
1759 )?;
1760 idx += 1;
1761 }
1762 Ok(acc)
1763}
1764pub(crate) fn arr_find(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1765 let items = generic_array_items(it, &t);
1766 let f = arg(a, 0);
1767 let this_arg = arg(a, 1);
1768 for (i, v) in items.iter().enumerate() {
1769 if it
1770 .call_value(
1771 &f,
1772 this_arg.clone(),
1773 &[v.clone(), Value::Number(i as f64), t.clone()],
1774 )?
1775 .truthy()
1776 {
1777 return Ok(v.clone());
1778 }
1779 }
1780 Ok(Value::Undefined)
1781}
1782pub(crate) fn arr_find_index(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1783 let items = generic_array_items(it, &t);
1784 let f = arg(a, 0);
1785 let this_arg = arg(a, 1);
1786 for (i, v) in items.iter().enumerate() {
1787 if it
1788 .call_value(
1789 &f,
1790 this_arg.clone(),
1791 &[v.clone(), Value::Number(i as f64), t.clone()],
1792 )?
1793 .truthy()
1794 {
1795 return Ok(Value::Number(i as f64));
1796 }
1797 }
1798 Ok(Value::Number(-1.0))
1799}
1800pub(crate) fn arr_some(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1801 let items = generic_array_items(it, &t);
1802 let f = arg(a, 0);
1803 let this_arg = arg(a, 1);
1804 for (i, v) in items.iter().enumerate() {
1805 if it
1806 .call_value(
1807 &f,
1808 this_arg.clone(),
1809 &[v.clone(), Value::Number(i as f64), t.clone()],
1810 )?
1811 .truthy()
1812 {
1813 return Ok(Value::Bool(true));
1814 }
1815 }
1816 Ok(Value::Bool(false))
1817}
1818pub(crate) fn arr_every(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1819 let items = generic_array_items(it, &t);
1820 let f = arg(a, 0);
1821 let this_arg = arg(a, 1);
1822 for (i, v) in items.iter().enumerate() {
1823 if !it
1824 .call_value(
1825 &f,
1826 this_arg.clone(),
1827 &[v.clone(), Value::Number(i as f64), t.clone()],
1828 )?
1829 .truthy()
1830 {
1831 return Ok(Value::Bool(false));
1832 }
1833 }
1834 Ok(Value::Bool(true))
1835}
1836pub(crate) fn insertion_sort_excluding_undefined(
1842 it: &mut Interp,
1843 mut items: alloc::vec::Vec<Value>,
1844 cmp: &Value,
1845) -> Result<alloc::vec::Vec<Value>, Value> {
1846 let undefined_count = items.iter().filter(|v| matches!(v, Value::Undefined)).count();
1847 items.retain(|v| !matches!(v, Value::Undefined));
1848 let n = items.len();
1849 for i in 1..n {
1850 let mut j = i;
1851 while j > 0 {
1852 let ord = if matches!(cmp, Value::Undefined) {
1853 items[j - 1].to_js_string().cmp(&items[j].to_js_string())
1854 } else {
1855 let r = it
1856 .call_value(
1857 cmp,
1858 Value::Undefined,
1859 &[items[j - 1].clone(), items[j].clone()],
1860 )?
1861 .to_number();
1862 if r > 0.0 {
1863 core::cmp::Ordering::Greater
1864 } else if r < 0.0 {
1865 core::cmp::Ordering::Less
1866 } else {
1867 core::cmp::Ordering::Equal
1868 }
1869 };
1870 if ord == core::cmp::Ordering::Greater {
1871 items.swap(j - 1, j);
1872 j -= 1;
1873 } else {
1874 break;
1875 }
1876 }
1877 }
1878 for _ in 0..undefined_count {
1879 items.push(Value::Undefined);
1880 }
1881 Ok(items)
1882}
1883pub(crate) fn arr_sort(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1884 let items = this_items(&t);
1885 let cmp = arg(a, 0);
1886 let items = insertion_sort_excluding_undefined(it, items, &cmp)?;
1887 if let Some(o) = this_objref(&t) {
1888 if let ObjKind::Array(slot) = &mut o.borrow_mut().kind {
1889 *slot = items;
1890 }
1891 }
1892 Ok(t)
1893}
1894pub(crate) fn arr_fill(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1898 if let Some(o) = this_objref(&t) {
1899 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
1900 let v = arg(a, 0);
1901 let len = items.len() as i64;
1902 let start = norm_index(arg(a, 1).to_number(), len, 0).max(0) as usize;
1903 let end = if matches!(arg(a, 2), Value::Undefined) {
1904 len
1905 } else {
1906 norm_index(arg(a, 2).to_number(), len, len)
1907 }
1908 .max(0) as usize;
1909 let end = end.min(items.len());
1910 for slot in items.iter_mut().take(end).skip(start) {
1911 *slot = v.clone();
1912 }
1913 }
1914 }
1915 Ok(t)
1916}
1917pub(crate) fn arr_splice(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1918 let o = match this_objref(&t) {
1919 Some(o) => o,
1920 None => return Ok(Value::Object(Obj::array(alloc::vec![]))),
1921 };
1922 let mut items = if let ObjKind::Array(v) = &o.borrow().kind {
1923 v.clone()
1924 } else {
1925 return Ok(Value::Object(Obj::array(alloc::vec![])));
1926 };
1927 let len = items.len() as i64;
1928 let start_raw = arg(a, 0).to_number() as i64;
1929 let start = if start_raw < 0 {
1930 (len + start_raw).max(0) as usize
1931 } else {
1932 start_raw.min(len) as usize
1933 };
1934 let delete_count = if a.len() < 2 {
1935 items.len() - start
1936 } else {
1937 let dc = arg(a, 1).to_number() as i64;
1938 dc.max(0).min((len - start as i64).max(0)) as usize
1939 };
1940 let insert: alloc::vec::Vec<Value> = a.iter().skip(2).cloned().collect();
1941 let removed: alloc::vec::Vec<Value> = items.drain(start..start + delete_count).collect();
1942 for (i, v) in insert.into_iter().enumerate() {
1943 items.insert(start + i, v);
1944 }
1945 if let ObjKind::Array(slot) = &mut o.borrow_mut().kind {
1946 *slot = items;
1947 }
1948 let a_obj = array_species_create(it, &t, removed.len())?;
1949 populate_array_species(it, &a_obj, removed)
1950}
1951
1952pub(crate) fn arr_last_index_of(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1960 let items = generic_array_items(it, &t);
1961 let target = arg(a, 0);
1962 let len = items.len() as i64;
1963 if len == 0 {
1964 return Ok(Value::Number(-1.0));
1965 }
1966 let start = if matches!(arg(a, 1), Value::Undefined) {
1967 len - 1
1968 } else {
1969 let n = arg(a, 1).to_number();
1970 let n = if n.is_nan() { 0.0 } else { n };
1971 if n < 0.0 {
1972 len + n as i64
1973 } else {
1974 (n as i64).min(len - 1)
1975 }
1976 };
1977 if start < 0 {
1978 return Ok(Value::Number(-1.0));
1979 }
1980 for i in (0..=start as usize).rev() {
1981 if items[i].strict_eq(&target) {
1982 return Ok(Value::Number(i as f64));
1983 }
1984 }
1985 Ok(Value::Number(-1.0))
1986}
1987
1988pub(crate) fn arr_copy_within(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1989 let o = match this_objref(&t) {
1990 Some(o) => o,
1991 None => return Ok(t),
1992 };
1993 let mut items = if let ObjKind::Array(v) = &o.borrow().kind {
1994 v.clone()
1995 } else {
1996 return Ok(t);
1997 };
1998 let len = items.len() as i64;
1999 let to_raw = arg(a, 0).to_number() as i64;
2000 let to = if to_raw < 0 {
2001 (len + to_raw).max(0) as usize
2002 } else {
2003 to_raw.min(len) as usize
2004 };
2005 let from_raw = if matches!(arg(a, 1), Value::Undefined) { 0i64 } else { arg(a, 1).to_number() as i64 };
2006 let from = if from_raw < 0 {
2007 (len + from_raw).max(0) as usize
2008 } else {
2009 from_raw.min(len) as usize
2010 };
2011 let end_raw = if matches!(arg(a, 2), Value::Undefined) { len } else { arg(a, 2).to_number() as i64 };
2012 let end = if end_raw < 0 {
2013 (len + end_raw).max(0) as usize
2014 } else {
2015 end_raw.min(len) as usize
2016 };
2017 let count = if end > from { (end - from).min(len as usize - to) } else { 0 };
2018 let src: alloc::vec::Vec<Value> = items[from..from + count].to_vec();
2019 for (i, v) in src.into_iter().enumerate() {
2020 if to + i < items.len() {
2021 items[to + i] = v;
2022 }
2023 }
2024 if let ObjKind::Array(slot) = &mut o.borrow_mut().kind {
2025 *slot = items;
2026 }
2027 Ok(t)
2028}
2029
2030pub(crate) fn is_typed_array(t: &Value) -> bool {
2037 if let Value::Object(o) = t {
2038 o.borrow().props.contains_key("_ta_kind")
2039 } else {
2040 false
2041 }
2042}
2043
2044pub(crate) fn array_species_create(
2049 it: &mut Interp,
2050 original: &Value,
2051 length: usize,
2052) -> Result<Value, Value> {
2053 let is_array = match original {
2054 Value::Object(o) => matches!(o.borrow().kind, ObjKind::Array(_)),
2055 _ => false,
2056 };
2057 if !is_array {
2058 return Ok(Value::Object(Obj::array(alloc::vec::Vec::with_capacity(length))));
2059 }
2060
2061 let ctor = match it.get_property(original, "constructor") {
2062 Ok(v) => v,
2063 Err(_) => return Ok(Value::Object(Obj::array(alloc::vec::Vec::with_capacity(length)))),
2064 };
2065
2066 let ctor_is_obj = matches!(ctor, Value::Object(_));
2067 if !ctor_is_obj {
2068 return Ok(Value::Object(Obj::array(alloc::vec::Vec::with_capacity(length))));
2069 }
2070
2071 let species_val = match it.get_property(&ctor, "Symbol(Symbol.species)") {
2073 Ok(v) => v,
2074 Err(_) => ctor.clone(),
2075 };
2076
2077 if matches!(species_val, Value::Null | Value::Undefined) {
2078 return Ok(Value::Object(Obj::array(alloc::vec::Vec::with_capacity(length))));
2079 }
2080
2081 if let Value::Object(ref so) = species_val {
2083 if let Some(Value::Object(ref ao)) = it.global.borrow().vars.get("Array") {
2084 if core::ptr::eq(so.as_ref(), ao.as_ref()) {
2085 return Ok(Value::Object(Obj::array(alloc::vec::Vec::with_capacity(length))));
2086 }
2087 }
2088 if so.borrow().is_callable() {
2089 return it.construct_value(&species_val, &[Value::Number(length as f64)]);
2090 }
2091 }
2092
2093 Err(it.type_error("Symbol.species is not a constructor"))
2094}
2095
2096pub(crate) fn populate_array_species(
2097 it: &mut Interp,
2098 target: &Value,
2099 items: alloc::vec::Vec<Value>,
2100) -> Result<Value, Value> {
2101 if let Value::Object(o) = target {
2102 let is_plain_array = matches!(o.borrow().kind, ObjKind::Array(_));
2103 if is_plain_array {
2104 let len = items.len();
2105 o.borrow_mut().kind = ObjKind::Array(items);
2106 o.borrow_mut().props.insert("length".into(), Value::Number(len as f64));
2107 return Ok(target.clone());
2108 }
2109 }
2110 let len = items.len();
2111 for (i, v) in items.into_iter().enumerate() {
2112 let _ = it.set_property(target, &i.to_string(), v);
2113 }
2114 let _ = it.set_property(target, "length", Value::Number(len as f64));
2115 Ok(target.clone())
2116}
2117
2118pub(crate) fn same_kind_as(it: &mut Interp, t: &Value, items: alloc::vec::Vec<Value>) -> Value {
2119 let ta_kind_tag = if let Value::Object(o) = t {
2120 o.borrow().props.get("_ta_kind").map(|v| v.to_js_string())
2121 } else {
2122 None
2123 };
2124 match ta_kind_tag {
2125 Some(tag) => {
2126 let kind = TaKind::from_name(&tag);
2130 let values: alloc::vec::Vec<f64> = items.iter().map(|v| kind.convert(v)).collect();
2131 make_typed_array(it, values, kind)
2132 }
2133 None => Value::Object(Obj::array(items)),
2134 }
2135}
2136pub(crate) fn arr_to_sorted(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2137 let items = generic_array_items(it, &t);
2138 let cmp = arg(a, 0);
2139 let items = insertion_sort_excluding_undefined(it, items, &cmp)?;
2140 Ok(same_kind_as(it, &t, items))
2141}
2142
2143pub(crate) fn arr_to_reversed(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
2144 let mut items = generic_array_items(it, &t);
2145 items.reverse();
2146 Ok(same_kind_as(it, &t, items))
2147}
2148
2149pub(crate) fn arr_to_spliced(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2150 let mut items = generic_array_items(it, &t);
2151 let len = items.len() as i64;
2152 let start_raw = arg(a, 0).to_number() as i64;
2153 let start = if start_raw < 0 {
2154 (len + start_raw).max(0) as usize
2155 } else {
2156 start_raw.min(len) as usize
2157 };
2158 let delete_count = if a.len() < 2 {
2159 items.len() - start
2160 } else {
2161 let dc = arg(a, 1).to_number() as i64;
2162 dc.max(0).min((len - start as i64).max(0)) as usize
2163 };
2164 let insert: alloc::vec::Vec<Value> = a.iter().skip(2).cloned().collect();
2165 items.drain(start..start + delete_count);
2166 for (i, v) in insert.into_iter().enumerate() {
2167 items.insert(start + i, v);
2168 }
2169 Ok(same_kind_as(it, &t, items))
2170}
2171
2172pub(crate) fn arr_with(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2175 let mut items = generic_array_items(it, &t);
2176 let len = items.len() as i64;
2177 let idx_raw = arg(a, 0).to_number() as i64;
2178 let idx = if idx_raw < 0 { len + idx_raw } else { idx_raw };
2179 if idx < 0 || idx >= len {
2180 return Err(it.error("Invalid index"));
2181 }
2182 items[idx as usize] = arg(a, 1);
2183 Ok(same_kind_as(it, &t, items))
2184}
2185
2186pub(crate) fn flatten_depth(items: Vec<Value>, depth: f64, out: &mut Vec<Value>, seen: &mut Vec<ObjRef>) {
2195 for v in items {
2196 match v {
2197 Value::Object(o) if depth > 0.0 && matches!(o.borrow().kind, ObjKind::Array(_)) => {
2198 if seen.iter().any(|s| Rc::ptr_eq(s, &o)) {
2199 out.push(Value::Object(o));
2200 continue;
2201 }
2202 seen.push(o.clone());
2203 let inner = match &o.borrow().kind {
2204 ObjKind::Array(inner) => inner.clone(),
2205 _ => Vec::new(),
2206 };
2207 flatten_depth(inner, depth - 1.0, out, seen);
2208 seen.pop();
2209 }
2210 other => out.push(other),
2211 }
2212 }
2213}
2214pub(crate) fn arr_flat(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2218 let items = generic_array_items(it, &t);
2219 let depth = if matches!(arg(a, 0), Value::Undefined) {
2220 1.0
2221 } else {
2222 arg(a, 0).to_number()
2223 };
2224 let mut out = Vec::new();
2225 flatten_depth(items, depth, &mut out, &mut Vec::new());
2226 Ok(Value::Object(Obj::array(out)))
2227}
2228pub(crate) fn has_own_property(o: &super::super::value::ObjRef, key: &str) -> bool {
2234 let b = o.borrow();
2235 if let ObjKind::Array(items) = &b.kind {
2236 if key == "length" {
2237 return true;
2238 }
2239 if let Ok(idx) = key.parse::<usize>() {
2240 return idx < items.len();
2241 }
2242 }
2243 b.props.contains_key(key) || b.accessors.contains_key(key)
2244}
2245pub(crate) fn obj_has_own(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2246 let key = arg(a, 0).to_js_string();
2247 if let Value::Object(o) = &t {
2248 return Ok(Value::Bool(has_own_property(o, &key)));
2249 }
2250 Ok(Value::Bool(false))
2251}
2252pub(crate) fn obj_to_string(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
2258 if let Value::Object(o) = &t {
2262 let mut cur = Some(o.clone());
2263 while let Some(c) = cur {
2264 let (own_tag, getter, next) = {
2265 let b = c.borrow();
2266 (
2267 b.props.get("Symbol(Symbol.toStringTag)").cloned(),
2268 b.accessors
2269 .get("Symbol(Symbol.toStringTag)")
2270 .and_then(|acc| acc.get.clone()),
2271 b.proto.clone(),
2272 )
2273 };
2274 if let Some(v) = own_tag {
2275 return Ok(Value::str(format!("[object {}]", v.to_js_string())));
2276 }
2277 if let Some(g) = getter {
2278 let v = it.call_value(&g, t.clone(), &[])?;
2279 return Ok(Value::str(format!("[object {}]", v.to_js_string())));
2280 }
2281 cur = next;
2282 }
2283 }
2284 let tag = match &t {
2285 Value::Undefined => "Undefined",
2286 Value::Null => "Null",
2287 Value::Object(o) => {
2288 let b = o.borrow();
2289 if b.is_callable() {
2290 "Function"
2291 } else {
2292 match &b.kind {
2293 ObjKind::Array(_) => "Array",
2294 ObjKind::DateObj(_) => "Date",
2295 ObjKind::RegExpObj(_) => "RegExp",
2296 ObjKind::MapObj(_) => "Map",
2297 ObjKind::SetObj(_) => "Set",
2298 ObjKind::PromiseObj(_) => "Promise",
2299 ObjKind::Generator(_) => "Generator",
2300 _ => "Object",
2301 }
2302 }
2303 }
2304 Value::Str(_) => "String",
2317 Value::Number(_) => "Number",
2318 Value::Bool(_) => "Boolean",
2319 Value::BigInt(_) => "BigInt",
2320 };
2321 Ok(Value::str(format!("[object {}]", tag)))
2322}
2323pub(crate) fn obj_is_prototype_of(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2326 let (Value::Object(target), Value::Object(candidate)) = (&t, &arg(a, 0)) else {
2327 return Ok(Value::Bool(false));
2328 };
2329 let mut cur = candidate.borrow().proto.clone();
2330 let mut guard = 0;
2331 while let Some(p) = cur {
2332 if Rc::ptr_eq(&p, target) {
2333 return Ok(Value::Bool(true));
2334 }
2335 cur = p.borrow().proto.clone();
2336 guard += 1;
2337 if guard > 1000 {
2338 break;
2339 }
2340 }
2341 Ok(Value::Bool(false))
2342}
2343pub(crate) fn obj_property_is_enumerable(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2347 obj_has_own(it, t, a)
2348}
2349
2350pub fn function_method(key: &str) -> Value {
2354 match key {
2355 "call" => nv("call", fn_call),
2356 "apply" => nv("apply", fn_apply),
2357 "bind" => nv("bind", fn_bind),
2358 _ => Value::Undefined,
2359 }
2360}
2361pub(crate) fn fn_to_string(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
2364 match &t {
2365 Value::Object(o) if o.borrow().is_callable() => Ok(Value::str(t.to_js_string())),
2366 _ => Err(it.type_error("Function.prototype.toString requires that 'this' be a Function")),
2367 }
2368}
2369pub(crate) fn fn_call(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2370 let this_arg = arg(a, 0);
2371 let rest: Vec<Value> = a.get(1..).map(|s| s.to_vec()).unwrap_or_default();
2372 it.call_value(&t, this_arg, &rest)
2373}
2374pub(crate) fn fn_apply(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2379 let this_arg = arg(a, 0);
2380 let args_vec = match arg(a, 1) {
2381 Value::Undefined | Value::Null => Vec::new(),
2382 v => array_like_items(it, &v),
2383 };
2384 it.call_value(&t, this_arg, &args_vec)
2385}
2386pub(crate) fn fn_bind(_it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2387 let bound_this = arg(a, 0);
2388 let bound_args: Vec<Value> = a.get(1..).map(|s| s.to_vec()).unwrap_or_default();
2389 Ok(Value::Object(Obj::bound(t, bound_this, bound_args)))
2390}
2391
2392pub(crate) fn norm_index(v: f64, len: i64, default: i64) -> i64 {
2395 if v.is_nan() {
2396 return default;
2397 }
2398 let i = libm::trunc(v) as i64;
2399 if i < 0 {
2400 (len + i).max(0)
2401 } else {
2402 i.min(len)
2403 }
2404}
2405pub(crate) fn clamp_idx(v: f64, len: usize) -> usize {
2406 if v.is_nan() || v < 0.0 {
2407 0
2408 } else {
2409 (v as usize).min(len)
2410 }
2411}
2412pub(crate) fn pad_str(s: &str, target: usize, pad: &str, start: bool) -> String {
2413 let cur = s.chars().count();
2414 if cur >= target || pad.is_empty() {
2415 return String::from(s);
2416 }
2417 let mut padding = String::new();
2418 let pad_chars: Vec<char> = pad.chars().collect();
2419 let mut i = 0;
2420 while padding.chars().count() < target - cur {
2421 padding.push(pad_chars[i % pad_chars.len()]);
2422 i += 1;
2423 }
2424 if start {
2425 format!("{}{}", padding, s)
2426 } else {
2427 format!("{}{}", s, padding)
2428 }
2429}
2430pub(crate) fn format_fixed(n: f64, digits: usize) -> String {
2431 let neg = n < 0.0;
2432 let n = libm::fabs(n);
2433 let int_part = libm::trunc(n) as i64;
2434 let scale = libm::pow(10.0, digits as f64);
2435 let frac = libm::trunc((n - libm::trunc(n)) * scale + 0.5) as i64;
2436 let frac_str = format!("{:0width$}", frac, width = digits);
2437 format!("{}{}.{}", if neg { "-" } else { "" }, int_part, frac_str)
2438}
2439