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 "lastIndexOf" => nv("lastIndexOf", str_last_index_of),
140 "includes" => nv("includes", str_includes),
141 "startsWith" => nv("startsWith", str_starts_with),
142 "endsWith" => nv("endsWith", str_ends_with),
143 "slice" => nv("slice", str_slice),
144 "substring" => nv("substring", str_substring),
145 "substr" => nv("substr", str_substr),
146 "toUpperCase" => nv("toUpperCase", str_to_upper),
147 "toLowerCase" => nv("toLowerCase", str_to_lower),
148 "trim" => nv("trim", str_trim),
149 "split" => nv("split", str_split),
150 "replace" => nv("replace", str_replace),
151 "replaceAll" => nv("replaceAll", str_replace_all),
152 "match" => nv("match", str_match),
153 "matchAll" => nv("matchAll", str_match_all),
154 "search" => nv("search", str_search),
155 "repeat" => nv("repeat", str_repeat),
156 "concat" => nv("concat", str_concat),
157 "padStart" => nv("padStart", str_pad_start),
158 "padEnd" => nv("padEnd", str_pad_end),
159 "at" => nv("at", str_at),
160 "toString" => nv("toString", str_identity),
161 "trimStart" => nv("trimStart", str_trim_start),
162 "trimEnd" => nv("trimEnd", str_trim_end),
163 "trimLeft" => nv("trimLeft", str_trim_start),
166 "trimRight" => nv("trimRight", str_trim_end),
167 "Symbol(Symbol.iterator)" => nv("[Symbol.iterator]", str_iterator),
168 "isWellFormed" => nv("isWellFormed", str_is_well_formed),
169 "toWellFormed" => nv("toWellFormed", str_to_well_formed),
170 "normalize" => nv("normalize", str_normalize),
171 "anchor" => nv("anchor", str_anchor),
174 "big" => nv("big", str_big),
175 "blink" => nv("blink", str_blink),
176 "bold" => nv("bold", str_bold),
177 "fixed" => nv("fixed", str_fixed),
178 "fontcolor" => nv("fontcolor", str_fontcolor),
179 "fontsize" => nv("fontsize", str_fontsize),
180 "italics" => nv("italics", str_italics),
181 "link" => nv("link", str_link),
182 "small" => nv("small", str_small),
183 "strike" => nv("strike", str_strike),
184 "sub" => nv("sub", str_sub),
185 "sup" => nv("sup", str_sup),
186 _ => Value::Undefined,
187 }
188}
189pub(crate) fn html_wrap_tag(s: &str, tag: &str) -> String {
191 alloc::format!("<{0}>{1}</{0}>", tag, s)
192}
193pub(crate) fn html_wrap_attr(s: &str, tag: &str, attr: &str, value: &str) -> String {
195 let escaped = value.replace('"', """);
196 alloc::format!("<{0} {1}=\"{2}\">{3}</{0}>", tag, attr, escaped, s)
197}
198pub(crate) fn str_anchor(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
199 Ok(Value::str(html_wrap_attr(&this_str(&t), "a", "name", &arg(a, 0).to_js_string())))
200}
201pub(crate) fn str_big(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
202 Ok(Value::str(html_wrap_tag(&this_str(&t), "big")))
203}
204pub(crate) fn str_blink(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
205 Ok(Value::str(html_wrap_tag(&this_str(&t), "blink")))
206}
207pub(crate) fn str_bold(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
208 Ok(Value::str(html_wrap_tag(&this_str(&t), "b")))
209}
210pub(crate) fn str_fixed(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
211 Ok(Value::str(html_wrap_tag(&this_str(&t), "tt")))
212}
213pub(crate) fn str_fontcolor(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
214 Ok(Value::str(html_wrap_attr(&this_str(&t), "font", "color", &arg(a, 0).to_js_string())))
215}
216pub(crate) fn str_fontsize(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
217 Ok(Value::str(html_wrap_attr(&this_str(&t), "font", "size", &arg(a, 0).to_js_string())))
218}
219pub(crate) fn str_italics(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
220 Ok(Value::str(html_wrap_tag(&this_str(&t), "i")))
221}
222pub(crate) fn str_link(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
223 Ok(Value::str(html_wrap_attr(&this_str(&t), "a", "href", &arg(a, 0).to_js_string())))
224}
225pub(crate) fn str_small(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
226 Ok(Value::str(html_wrap_tag(&this_str(&t), "small")))
227}
228pub(crate) fn str_strike(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
229 Ok(Value::str(html_wrap_tag(&this_str(&t), "strike")))
230}
231pub(crate) fn str_sub(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
232 Ok(Value::str(html_wrap_tag(&this_str(&t), "sub")))
233}
234pub(crate) fn str_sup(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
235 Ok(Value::str(html_wrap_tag(&this_str(&t), "sup")))
236}
237
238pub(crate) fn this_str(this: &Value) -> String {
239 this.to_js_string()
240}
241
242pub(crate) fn str_char_at(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
243 let s = this_str(&t);
244 let i = arg(a, 0).to_number() as usize;
245 Ok(Value::str(
246 s.chars().nth(i).map(|c| c.to_string()).unwrap_or_default(),
247 ))
248}
249pub(crate) fn str_char_code_at(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
250 let s = this_str(&t);
251 let i = arg(a, 0).to_number() as usize;
252 match s.chars().nth(i) {
253 Some(c) => Ok(Value::Number(c as u32 as f64)),
254 None => Ok(Value::Number(f64::NAN)),
255 }
256}
257pub(crate) fn str_code_point_at(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
262 let s = this_str(&t);
263 let i = arg(a, 0).to_number() as usize;
264 match s.chars().nth(i) {
265 Some(c) => Ok(Value::Number(c as u32 as f64)),
266 None => Ok(Value::Undefined),
267 }
268}
269pub(crate) fn str_locale_compare(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
272 let s = this_str(&t);
273 let other = arg(a, 0).to_js_string();
274 use core::cmp::Ordering;
275 Ok(Value::Number(match s.as_str().cmp(other.as_str()) {
276 Ordering::Less => -1.0,
277 Ordering::Equal => 0.0,
278 Ordering::Greater => 1.0,
279 }))
280}
281fn char_to_byte(s: &str, n: usize) -> usize {
283 s.char_indices().nth(n).map_or(s.len(), |(b, _)| b)
284}
285
286fn pos_arg(v: &Value, default: usize, len: usize) -> usize {
288 if matches!(v, Value::Undefined) {
289 return default;
290 }
291 let p = v.to_number();
292 if p.is_nan() {
293 default
294 } else if p <= 0.0 {
295 0
296 } else if p.is_finite() {
297 (p as usize).min(len)
298 } else {
299 len
300 }
301}
302
303pub(crate) fn str_index_of(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
306 let s = this_str(&t);
307 let needle = arg(a, 0).to_js_string();
308 let len = s.chars().count();
309 let start_char = pos_arg(&arg(a, 1), 0, len);
310 let start = char_to_byte(&s, start_char);
311 match s.get(start..).and_then(|rest| rest.find(&needle)) {
312 Some(off) => Ok(Value::Number((start_char + s.get(start..start + off).map_or(0, |t| t.chars().count())) as f64)),
313 None => Ok(Value::Number(-1.0)),
314 }
315}
316
317pub(crate) fn str_last_index_of(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
321 let s = this_str(&t);
322 let needle = arg(a, 0).to_js_string();
323 let len = s.chars().count();
324 let nlen = needle.chars().count();
325 if nlen > len {
326 return Ok(Value::Number(-1.0));
327 }
328 let start_char = pos_arg(&arg(a, 1), len - nlen, len).min(len - nlen);
330 let end = char_to_byte(&s, start_char + nlen);
331 match s.get(..end).and_then(|head| head.rfind(&needle)) {
332 Some(b) => Ok(Value::Number(s.get(..b).map_or(0, |t| t.chars().count()) as f64)),
333 None => Ok(Value::Number(-1.0)),
334 }
335}
336pub(crate) fn reject_regexp_arg(it: &Interp, v: &Value, method: &str) -> Result<(), Value> {
344 if as_regexp(v).is_some() {
345 return Err(it.error(alloc::format!(
346 "First argument to String.prototype.{} must not be a regular expression",
347 method
348 )));
349 }
350 Ok(())
351}
352pub(crate) fn str_includes(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
353 reject_regexp_arg(it, &arg(a, 0), "includes")?;
354 Ok(Value::Bool(
355 this_str(&t).contains(&arg(a, 0).to_js_string()),
356 ))
357}
358pub(crate) fn str_starts_with(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
359 reject_regexp_arg(it, &arg(a, 0), "startsWith")?;
360 Ok(Value::Bool(
361 this_str(&t).starts_with(&arg(a, 0).to_js_string()),
362 ))
363}
364pub(crate) fn str_ends_with(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
365 reject_regexp_arg(it, &arg(a, 0), "endsWith")?;
366 Ok(Value::Bool(
367 this_str(&t).ends_with(&arg(a, 0).to_js_string()),
368 ))
369}
370pub(crate) fn str_slice(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
371 let chars: Vec<char> = this_str(&t).chars().collect();
372 let len = chars.len() as i64;
373 let start = norm_index(arg(a, 0).to_number(), len, 0);
374 let end = if matches!(arg(a, 1), Value::Undefined) {
375 len
376 } else {
377 norm_index(arg(a, 1).to_number(), len, len)
378 };
379 let (s, e) = (start.max(0) as usize, end.max(0) as usize);
380 Ok(Value::str(if s < e && s < chars.len() {
381 chars[s..e.min(chars.len())].iter().collect::<String>()
382 } else {
383 String::new()
384 }))
385}
386pub(crate) fn str_substring(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
387 let chars: Vec<char> = this_str(&t).chars().collect();
388 let len = chars.len();
389 let mut s = clamp_idx(arg(a, 0).to_number(), len);
390 let mut e = if matches!(arg(a, 1), Value::Undefined) {
391 len
392 } else {
393 clamp_idx(arg(a, 1).to_number(), len)
394 };
395 if s > e {
396 core::mem::swap(&mut s, &mut e);
397 }
398 Ok(Value::str(chars[s..e].iter().collect::<String>()))
399}
400pub(crate) fn str_substr(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
404 let chars: Vec<char> = this_str(&t).chars().collect();
405 let len = chars.len() as i64;
406 let raw_start = arg(a, 0).to_number();
407 let start = if raw_start < 0.0 {
408 (len + raw_start as i64).max(0)
409 } else {
410 (raw_start as i64).min(len)
411 };
412 let length = if matches!(arg(a, 1), Value::Undefined) {
413 len - start
414 } else {
415 (arg(a, 1).to_number() as i64).max(0)
416 };
417 let end = (start + length).min(len);
418 let s = start as usize;
419 let e = (end.max(start)) as usize;
420 Ok(Value::str(if s < e {
421 chars[s..e].iter().collect::<String>()
422 } else {
423 String::new()
424 }))
425}
426pub(crate) fn str_to_upper(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
427 Ok(Value::str(this_str(&t).to_uppercase()))
428}
429pub(crate) fn str_to_lower(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
430 Ok(Value::str(this_str(&t).to_lowercase()))
431}
432pub(crate) fn str_trim(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
433 Ok(Value::str(this_str(&t).trim().to_string()))
434}
435pub(crate) fn str_trim_start(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
436 Ok(Value::str(this_str(&t).trim_start().to_string()))
437}
438pub(crate) fn str_trim_end(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
439 Ok(Value::str(this_str(&t).trim_end().to_string()))
440}
441pub(crate) fn str_identity(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
442 Ok(Value::str(this_str(&t)))
443}
444pub(crate) fn str_iterator(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
451 let s = this_str(&t);
452 let items: Vec<Value> = s.chars().map(|c| Value::str(c.to_string())).collect();
453 Ok(Value::Object(make_iterator(items)))
454}
455pub(crate) fn str_is_well_formed(_: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
458 Ok(Value::Bool(true))
459}
460pub(crate) fn str_to_well_formed(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
462 Ok(Value::str(this_str(&t)))
463}
464pub(crate) const NFD_TABLE: &[(char, char, char)] = &[
475 ('À', 'A', '\u{0300}'), ('Á', 'A', '\u{0301}'), ('Â', 'A', '\u{0302}'),
476 ('Ã', 'A', '\u{0303}'), ('Ä', 'A', '\u{0308}'), ('Å', 'A', '\u{030A}'),
477 ('Ç', 'C', '\u{0327}'),
478 ('È', 'E', '\u{0300}'), ('É', 'E', '\u{0301}'), ('Ê', 'E', '\u{0302}'), ('Ë', 'E', '\u{0308}'),
479 ('Ì', 'I', '\u{0300}'), ('Í', 'I', '\u{0301}'), ('Î', 'I', '\u{0302}'), ('Ï', 'I', '\u{0308}'),
480 ('Ñ', 'N', '\u{0303}'),
481 ('Ò', 'O', '\u{0300}'), ('Ó', 'O', '\u{0301}'), ('Ô', 'O', '\u{0302}'),
482 ('Õ', 'O', '\u{0303}'), ('Ö', 'O', '\u{0308}'),
483 ('Ù', 'U', '\u{0300}'), ('Ú', 'U', '\u{0301}'), ('Û', 'U', '\u{0302}'), ('Ü', 'U', '\u{0308}'),
484 ('Ý', 'Y', '\u{0301}'),
485 ('à', 'a', '\u{0300}'), ('á', 'a', '\u{0301}'), ('â', 'a', '\u{0302}'),
486 ('ã', 'a', '\u{0303}'), ('ä', 'a', '\u{0308}'), ('å', 'a', '\u{030A}'),
487 ('ç', 'c', '\u{0327}'),
488 ('è', 'e', '\u{0300}'), ('é', 'e', '\u{0301}'), ('ê', 'e', '\u{0302}'), ('ë', 'e', '\u{0308}'),
489 ('ì', 'i', '\u{0300}'), ('í', 'i', '\u{0301}'), ('î', 'i', '\u{0302}'), ('ï', 'i', '\u{0308}'),
490 ('ñ', 'n', '\u{0303}'),
491 ('ò', 'o', '\u{0300}'), ('ó', 'o', '\u{0301}'), ('ô', 'o', '\u{0302}'),
492 ('õ', 'o', '\u{0303}'), ('ö', 'o', '\u{0308}'),
493 ('ù', 'u', '\u{0300}'), ('ú', 'u', '\u{0301}'), ('û', 'u', '\u{0302}'), ('ü', 'u', '\u{0308}'),
494 ('ý', 'y', '\u{0301}'), ('ÿ', 'y', '\u{0308}'),
495];
496pub(crate) fn japanese_voiced_sound_decompose(c: char) -> Option<(char, char)> {
497 let cp = c as u32;
498 match cp {
499 0x304C | 0x304E | 0x3050 | 0x3052 | 0x3054 |
500 0x3056 | 0x3058 | 0x305A | 0x305C | 0x305E |
501 0x3060 | 0x3062 | 0x3065 | 0x3067 | 0x3069 |
502 0x3070 | 0x3073 | 0x3076 | 0x3079 | 0x307C |
503 0x30AC | 0x30AE | 0x30B0 | 0x30B2 | 0x30B4 |
504 0x30B6 | 0x30B8 | 0x30BA | 0x30BC | 0x30BE |
505 0x30C0 | 0x30C2 | 0x30C5 | 0x30C7 | 0x30C9 |
506 0x30D0 | 0x30D3 | 0x30D6 | 0x30D9 | 0x30DC => {
507 char::from_u32(cp - 1).map(|b| (b, '\u{3099}'))
508 }
509 0x3071 | 0x3074 | 0x3077 | 0x307A | 0x307D |
510 0x30D1 | 0x30D4 | 0x30D7 | 0x30DA | 0x30DD => {
511 char::from_u32(cp - 2).map(|b| (b, '\u{309A}'))
512 }
513 0x30F4 => Some(('ウ', '\u{3099}')),
514 _ => None,
515 }
516}
517
518pub(crate) fn japanese_voiced_sound_compose(base: char, mark: char) -> Option<char> {
519 if mark != '\u{3099}' && mark != '\u{309A}' && mark != '\u{309B}' && mark != '\u{309C}' {
520 return None;
521 }
522 let b = base as u32;
523 let is_dakuten = mark == '\u{3099}' || mark == '\u{309B}';
524 let is_handakuten = mark == '\u{309A}' || mark == '\u{309C}';
525
526 match b {
527 0x304B | 0x304D | 0x304F | 0x3051 | 0x3053 |
528 0x3055 | 0x3057 | 0x3059 | 0x305B | 0x305D |
529 0x305F | 0x3061 | 0x3064 | 0x3066 | 0x3068 |
530 0x306F | 0x3072 | 0x3075 | 0x3078 | 0x307B |
531 0x30AB | 0x30AD | 0x30AF | 0x30B1 | 0x30B3 |
532 0x30B5 | 0x30B7 | 0x30B9 | 0x30BB | 0x30BD |
533 0x30BF | 0x30C1 | 0x30C4 | 0x30C6 | 0x30C8 |
534 0x30CF | 0x30D2 | 0x30D5 | 0x30D8 | 0x30DB if is_dakuten => {
535 char::from_u32(b + 1)
536 }
537 0x306F | 0x3072 | 0x3075 | 0x3078 | 0x307B |
538 0x30CF | 0x30D2 | 0x30D5 | 0x30D8 | 0x30DB if is_handakuten => {
539 char::from_u32(b + 2)
540 }
541 0x30A6 if is_dakuten => Some('ヴ'),
542 _ => None,
543 }
544}
545
546pub(crate) fn nfd_decompose(s: &str) -> String {
547 const HANGUL_S_BASE: u32 = 0xAC00;
548 const HANGUL_L_BASE: u32 = 0x1100;
549 const HANGUL_V_BASE: u32 = 0x1161;
550 const HANGUL_T_BASE: u32 = 0x11A7;
551 const HANGUL_N_COUNT: u32 = 588;
552 const HANGUL_T_COUNT: u32 = 28;
553 const HANGUL_S_COUNT: u32 = 11172;
554
555 let mut out = String::with_capacity(s.len());
556 for c in s.chars() {
557 let cp = c as u32;
558 if (HANGUL_S_BASE..HANGUL_S_BASE + HANGUL_S_COUNT).contains(&cp) {
559 let s_index = cp - HANGUL_S_BASE;
560 let l_index = s_index / HANGUL_N_COUNT;
561 let v_index = (s_index % HANGUL_N_COUNT) / HANGUL_T_COUNT;
562 let t_index = s_index % HANGUL_T_COUNT;
563 if let Some(l) = char::from_u32(HANGUL_L_BASE + l_index) {
564 out.push(l);
565 }
566 if let Some(v) = char::from_u32(HANGUL_V_BASE + v_index) {
567 out.push(v);
568 }
569 if t_index > 0 {
570 if let Some(t) = char::from_u32(HANGUL_T_BASE + t_index) {
571 out.push(t);
572 }
573 }
574 continue;
575 }
576 if let Some((base, mark)) = japanese_voiced_sound_decompose(c) {
577 out.push(base);
578 out.push(mark);
579 continue;
580 }
581 match NFD_TABLE.iter().find(|(pre, _, _)| *pre == c) {
582 Some((_, base, mark)) => {
583 out.push(*base);
584 out.push(*mark);
585 }
586 None => out.push(c),
587 }
588 }
589 out
590}
591pub(crate) fn nfc_compose(s: &str) -> String {
592 const HANGUL_S_BASE: u32 = 0xAC00;
593 const HANGUL_L_BASE: u32 = 0x1100;
594 const HANGUL_V_BASE: u32 = 0x1161;
595 const HANGUL_T_BASE: u32 = 0x11A7;
596 const HANGUL_L_COUNT: u32 = 19;
597 const HANGUL_V_COUNT: u32 = 21;
598 const HANGUL_T_COUNT: u32 = 28;
599 const HANGUL_N_COUNT: u32 = 588;
600
601 let decomposed = nfd_decompose(s);
602 let mut out = String::with_capacity(decomposed.len());
603 let mut chars = decomposed.chars().peekable();
604 while let Some(c) = chars.next() {
605 let cp = c as u32;
606 if (HANGUL_L_BASE..HANGUL_L_BASE + HANGUL_L_COUNT).contains(&cp) {
607 if let Some(&next) = chars.peek() {
608 let np = next as u32;
609 if (HANGUL_V_BASE..HANGUL_V_BASE + HANGUL_V_COUNT).contains(&np) {
610 chars.next();
611 let l_index = cp - HANGUL_L_BASE;
612 let v_index = np - HANGUL_V_BASE;
613 let mut s_index = l_index * HANGUL_N_COUNT + v_index * HANGUL_T_COUNT;
614 if let Some(&t_next) = chars.peek() {
615 let tp = t_next as u32;
616 if tp > HANGUL_T_BASE && tp < HANGUL_T_BASE + HANGUL_T_COUNT {
617 chars.next();
618 let t_index = tp - HANGUL_T_BASE;
619 s_index += t_index;
620 }
621 }
622 if let Some(hangul) = char::from_u32(HANGUL_S_BASE + s_index) {
623 out.push(hangul);
624 continue;
625 }
626 }
627 }
628 }
629 if let Some(&next) = chars.peek() {
630 if let Some(composed) = japanese_voiced_sound_compose(c, next) {
631 out.push(composed);
632 chars.next();
633 continue;
634 }
635 if let Some((pre, _, _)) = NFD_TABLE.iter().find(|(_, base, mark)| *base == c && *mark == next) {
636 out.push(*pre);
637 chars.next();
638 continue;
639 }
640 }
641 out.push(c);
642 }
643 out
644}
645pub(crate) fn str_normalize(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
646 let form = if matches!(arg(a, 0), Value::Undefined) {
647 String::from("NFC")
648 } else {
649 arg(a, 0).to_js_string()
650 };
651 let s = this_str(&t);
652 match form.as_str() {
653 "NFC" | "NFKC" => Ok(Value::str(nfc_compose(&s))),
654 "NFD" | "NFKD" => Ok(Value::str(nfd_decompose(&s))),
655 _ => Err(it.error("The normalization form should be one of NFC, NFD, NFKC, NFKD.")),
656 }
657}
658pub(crate) fn str_split(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
662 let rx = arg(a, 0);
663 if let Value::Object(_) = &rx {
664 let matcher = it.get_property(&rx, "Symbol(Symbol.split)")?;
665 if !matches!(matcher, Value::Undefined) {
666 return it.call_value(&matcher, rx.clone(), &[t, arg(a, 1)]);
667 }
668 }
669 let s = this_str(&t);
670 let limit = match arg(a, 1) {
671 Value::Undefined => usize::MAX,
672 v => {
673 let n = v.to_number();
674 if n.is_nan() || n < 0.0 {
675 usize::MAX
676 } else {
677 n as usize
678 }
679 }
680 };
681 if let Some(r) = as_regexp(&arg(a, 0)) {
683 let chars: Vec<char> = s.chars().collect();
684 let mut items = Vec::new();
685 let mut last = 0usize;
686 let mut pos = 0usize;
687 'outer: while pos <= chars.len() && items.len() < limit {
688 match r.borrow().re.find_at(&chars, pos) {
689 Some(m) if m.end > m.start => {
690 items.push(Value::str(chars[last..m.start].iter().collect::<String>()));
691 for cap in m.captures.iter().skip(1) {
697 if items.len() >= limit {
698 break 'outer;
699 }
700 let v = match cap {
701 Some((cs, ce)) => {
702 Value::str(chars[*cs..*ce].iter().collect::<String>())
703 }
704 None => Value::Undefined,
705 };
706 items.push(v);
707 }
708 last = m.end;
709 pos = m.end;
710 }
711 _ => break,
712 }
713 }
714 if items.len() < limit {
715 items.push(Value::str(chars[last..].iter().collect::<String>()));
716 }
717 items.truncate(limit);
718 return Ok(Value::Object(Obj::array(items)));
719 }
720 let mut items: Vec<Value> = match arg(a, 0) {
721 Value::Undefined => vec![Value::str(s)],
722 sep => {
723 let sep = sep.to_js_string();
724 if sep.is_empty() {
725 s.chars().map(|c| Value::str(c.to_string())).collect()
726 } else {
727 s.split(&sep as &str)
728 .map(|p| Value::str(p.to_string()))
729 .collect()
730 }
731 }
732 };
733 items.truncate(limit);
734 Ok(Value::Object(Obj::array(items)))
735}
736pub(crate) fn str_replace(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
737 let rx = arg(a, 0);
738 if let Value::Object(_) = &rx {
739 let matcher = it.get_property(&rx, "Symbol(Symbol.replace)")?;
740 if !matches!(matcher, Value::Undefined) {
741 return it.call_value(&matcher, rx.clone(), &[t, arg(a, 1)]);
742 }
743 }
744 let s = this_str(&t);
745 if let Some(r) = as_regexp(&arg(a, 0)) {
747 let global = r.borrow().re.global;
748 return Ok(Value::str(regex_replace(it, &s, &r, arg(a, 1), global)?));
749 }
750 let repl = arg(a, 1);
751 let from = arg(a, 0).to_js_string();
752 if let Some(f) = callable_or_none(repl.clone()) {
754 if let Some(idx) = s.find(&from) {
755 let before = s.get(..idx).unwrap_or("");
756 let after = s.get(idx + from.len()..).unwrap_or("");
757 let rep = it
758 .call_value(
759 &f,
760 Value::Undefined,
761 &[
762 Value::str(from.clone()),
763 Value::Number(s.get(..idx).unwrap_or("").chars().count() as f64),
764 Value::str(s.clone()),
765 ],
766 )?
767 .to_js_string();
768 return Ok(Value::str(format!("{}{}{}", before, rep, after)));
769 }
770 return Ok(Value::str(s));
771 }
772 let to = repl.to_js_string();
773 Ok(Value::str(s.replacen(&from, &to, 1)))
774}
775pub(crate) fn str_replace_all(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
780 let rx = arg(a, 0);
784 if let Some(r) = as_regexp(&rx) {
785 if !r.borrow().re.global {
786 return Err(it.error(
787 "String.prototype.replaceAll called with a non-global RegExp argument",
788 ));
789 }
790 }
791 if let Value::Object(_) = &rx {
794 let matcher = it.get_property(&rx, "Symbol(Symbol.replace)")?;
795 if !matches!(matcher, Value::Undefined) {
796 return it.call_value(&matcher, rx.clone(), &[t, arg(a, 1)]);
797 }
798 }
799 let s = this_str(&t);
800 if let Some(r) = as_regexp(&arg(a, 0)) {
802 if !r.borrow().re.global {
803 return Err(it.error(
804 "String.prototype.replaceAll called with a non-global RegExp argument",
805 ));
806 }
807 return Ok(Value::str(regex_replace(it, &s, &r, arg(a, 1), true)?));
808 }
809 let from = arg(a, 0).to_js_string();
810 let repl = arg(a, 1);
811 if let Some(f) = callable_or_none(repl.clone()) {
813 if from.is_empty() {
814 let chars: Vec<char> = s.chars().collect();
816 let mut out = String::new();
817 for (i, c) in chars.iter().enumerate() {
818 let rep = it
819 .call_value(
820 &f,
821 Value::Undefined,
822 &[Value::str(""), Value::Number(i as f64), Value::str(s.clone())],
823 )?
824 .to_js_string();
825 out.push_str(&rep);
826 out.push(*c);
827 }
828 let rep = it
829 .call_value(
830 &f,
831 Value::Undefined,
832 &[
833 Value::str(""),
834 Value::Number(chars.len() as f64),
835 Value::str(s.clone()),
836 ],
837 )?
838 .to_js_string();
839 out.push_str(&rep);
840 return Ok(Value::str(out));
841 }
842 let mut out = String::new();
843 let mut rest = s.as_str();
844 let mut consumed = 0usize;
845 while let Some(idx) = rest.find(&from) {
846 out.push_str(rest.get(..idx).unwrap_or(""));
847 let char_pos = s.get(..consumed + idx).unwrap_or("").chars().count();
848 let rep = it
849 .call_value(
850 &f,
851 Value::Undefined,
852 &[
853 Value::str(from.clone()),
854 Value::Number(char_pos as f64),
855 Value::str(s.clone()),
856 ],
857 )?
858 .to_js_string();
859 out.push_str(&rep);
860 consumed += idx + from.len();
861 rest = rest.get(idx + from.len()..).unwrap_or("");
862 }
863 out.push_str(rest);
864 return Ok(Value::str(out));
865 }
866 let to = repl.to_js_string();
867 Ok(Value::str(if from.is_empty() {
868 s
869 } else {
870 s.replace(&from, &to)
871 }))
872}
873pub(crate) fn str_repeat(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
879 let n = arg(a, 0).to_number();
880 if n.is_nan() {
881 return Ok(Value::str(String::new()));
882 }
883 if n < 0.0 || n == f64::INFINITY {
884 return Err(it.error("Invalid count value"));
885 }
886 Ok(Value::str(this_str(&t).repeat(n as usize)))
887}
888pub(crate) fn str_concat(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
889 let mut s = this_str(&t);
890 for v in a {
891 s.push_str(&v.to_js_string());
892 }
893 Ok(Value::str(s))
894}
895pub(crate) fn str_pad_start(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
896 let s = this_str(&t);
897 let target = arg(a, 0).to_number() as usize;
898 let pad = if matches!(arg(a, 1), Value::Undefined) {
899 String::from(" ")
900 } else {
901 arg(a, 1).to_js_string()
902 };
903 Ok(Value::str(pad_str(&s, target, &pad, true)))
904}
905pub(crate) fn str_pad_end(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
906 let s = this_str(&t);
907 let target = arg(a, 0).to_number() as usize;
908 let pad = if matches!(arg(a, 1), Value::Undefined) {
909 String::from(" ")
910 } else {
911 arg(a, 1).to_js_string()
912 };
913 Ok(Value::str(pad_str(&s, target, &pad, false)))
914}
915
916pub fn number_get(n: f64, key: &str) -> Value {
918 match key {
919 "toFixed" => nv("toFixed", num_to_fixed),
920 "toString" => nv("toString", num_to_string),
921 "toExponential" => nv("toExponential", num_to_exponential),
922 "toPrecision" => nv("toPrecision", num_to_precision),
923 "valueOf" => nv("valueOf", num_value_of),
924 "toLocaleString" => nv("toLocaleString", num_to_locale_string),
925 _ => {
926 let _ = n;
927 Value::Undefined
928 }
929 }
930}
931pub(crate) fn num_value_of(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
932 Ok(Value::Number(t.to_number()))
933}
934pub fn boolean_get(_b: bool, key: &str) -> Value {
941 match key {
942 "toString" => nv("toString", bool_to_string),
943 "valueOf" => nv("valueOf", bool_value_of),
944 _ => Value::Undefined,
945 }
946}
947pub(crate) fn bool_to_string(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
948 Ok(Value::str(t.to_js_string()))
949}
950pub(crate) fn bool_value_of(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
951 Ok(t)
952}
953pub(crate) fn normalize_mantissa(abs_n: f64) -> (f64, i32) {
956 if abs_n == 0.0 {
957 return (0.0, 0);
958 }
959 let mut exp = libm::floor(libm::log10(abs_n)) as i32;
960 let mut mantissa = abs_n / libm::pow(10.0, exp as f64);
961 if mantissa >= 10.0 {
962 mantissa /= 10.0;
963 exp += 1;
964 } else if mantissa < 1.0 {
965 mantissa *= 10.0;
966 exp -= 1;
967 }
968 (mantissa, exp)
969}
970pub(crate) fn num_to_exponential(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
974 let n = t.to_number();
975 if n.is_nan() {
976 return Ok(Value::str("NaN"));
977 }
978 if n.is_infinite() {
979 return Ok(Value::str(if n > 0.0 { "Infinity" } else { "-Infinity" }));
980 }
981 let neg = n < 0.0;
982 let (mantissa, mut exp) = normalize_mantissa(libm::fabs(n));
983 let d = if matches!(arg(a, 0), Value::Undefined) {
984 6
985 } else {
986 let dv = arg(a, 0).to_number();
987 if !dv.is_finite() || !(0.0..=100.0).contains(&dv) {
988 return Err(it.error("toExponential() argument must be between 0 and 100"));
989 }
990 dv as usize
991 };
992 let scale = libm::pow(10.0, d as f64);
993 let mut rounded = libm::trunc(mantissa * scale + 0.5) / scale;
994 if rounded >= 10.0 {
995 rounded /= 10.0;
996 exp += 1;
997 }
998 let mantissa_str = if d == 0 {
999 format!("{}", rounded as i64)
1000 } else {
1001 format_fixed(rounded, d)
1002 };
1003 let sign = if exp >= 0 { "+" } else { "-" };
1004 Ok(Value::str(format!(
1005 "{}{}e{}{}",
1006 if neg { "-" } else { "" },
1007 mantissa_str,
1008 sign,
1009 if exp >= 0 { exp } else { -exp }
1010 )))
1011}
1012pub(crate) fn num_to_precision(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1016 let n = t.to_number();
1017 if matches!(arg(a, 0), Value::Undefined) {
1018 return Ok(Value::str(t.to_js_string()));
1019 }
1020 if n.is_nan() {
1021 return Ok(Value::str("NaN"));
1022 }
1023 if n.is_infinite() {
1024 return Ok(Value::str(if n > 0.0 { "Infinity" } else { "-Infinity" }));
1025 }
1026 let p = arg(a, 0).to_number();
1027 if !p.is_finite() || !(1.0..=100.0).contains(&p) {
1028 return Err(it.error("toPrecision() argument must be between 1 and 100"));
1029 }
1030 let p = p as usize;
1031 if n == 0.0 {
1032 return Ok(Value::str(if p == 1 {
1033 String::from("0")
1034 } else {
1035 format!("0.{}", "0".repeat(p - 1))
1036 }));
1037 }
1038 let neg = n < 0.0;
1039 let (_, exp) = normalize_mantissa(libm::fabs(n));
1040 if (exp as i64) < -6 || (exp as i64) >= p as i64 {
1041 return num_to_exponential(it, t, &[Value::Number((p - 1) as f64)]);
1043 }
1044 let frac_digits = (p as i32 - 1 - exp).max(0) as usize;
1046 let scale = libm::pow(10.0, frac_digits as f64);
1047 let rounded = libm::trunc(libm::fabs(n) * scale + 0.5) / scale;
1048 let s = if frac_digits == 0 {
1049 format!("{}", rounded as i64)
1050 } else {
1051 format_fixed(rounded, frac_digits)
1052 };
1053 Ok(Value::str(if neg { format!("-{}", s) } else { s }))
1054}
1055pub(crate) fn num_to_fixed(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1061 let n = t.to_number();
1062 if n.is_nan() {
1063 return Ok(Value::str("NaN"));
1064 }
1065 let digits = arg(a, 0).to_number();
1066 let d = if matches!(arg(a, 0), Value::Undefined) {
1067 0
1068 } else if !digits.is_finite() || !(0.0..=100.0).contains(&digits) {
1069 return Err(it.error("toFixed() digits argument must be between 0 and 100"));
1070 } else {
1071 digits as usize
1072 };
1073 if n.is_infinite() {
1074 return Ok(Value::str(if n > 0.0 { "Infinity" } else { "-Infinity" }));
1075 }
1076 let scale = libm::pow(10.0, d as f64);
1078 let rounded = libm::trunc(n * scale + if n >= 0.0 { 0.5 } else { -0.5 }) / scale;
1079 if d == 0 {
1080 Ok(Value::str(format!("{}", rounded as i64)))
1081 } else {
1082 Ok(Value::str(format_fixed(rounded, d)))
1083 }
1084}
1085pub(crate) fn num_to_string(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1088 let radix = match arg(a, 0) {
1089 Value::Undefined => 10u32,
1090 v => v.to_number() as u32,
1091 };
1092 if radix == 10 {
1093 return Ok(Value::str(t.to_js_string()));
1094 }
1095 if !(2..=36).contains(&radix) {
1096 return Err(it.error("toString() radix must be between 2 and 36"));
1097 }
1098 Ok(Value::str(format_radix(t.to_number(), radix)))
1099}
1100pub(crate) fn num_to_locale_string(_it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1106 let n = t.to_number();
1107 if !n.is_finite() {
1108 return Ok(Value::str(t.to_js_string()));
1109 }
1110 let s = t.to_js_string();
1111 let (neg, s) = match s.strip_prefix('-') {
1112 Some(rest) => (true, rest),
1113 None => (false, s.as_str()),
1114 };
1115 let (int_part, frac_part) = match s.split_once('.') {
1116 Some((i, f)) => (i, Some(f)),
1117 None => (s, None),
1118 };
1119 let mut grouped = String::new();
1120 let len = int_part.len();
1121 for (i, c) in int_part.chars().enumerate() {
1122 if i > 0 && (len - i) % 3 == 0 {
1123 grouped.push(',');
1124 }
1125 grouped.push(c);
1126 }
1127 let mut out = String::new();
1128 if neg {
1129 out.push('-');
1130 }
1131 out.push_str(&grouped);
1132 if let Some(f) = frac_part {
1133 out.push('.');
1134 out.push_str(f);
1135 }
1136 Ok(Value::str(out))
1137}
1138pub(crate) fn intl_number_format_ctor(_it: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
1142 let obj = Obj::plain();
1143 obj.borrow_mut()
1144 .props
1145 .insert("format".into(), nv("format", intl_number_format_format));
1146 Ok(Value::Object(obj))
1147}
1148pub(crate) fn intl_number_format_format(it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
1149 num_to_locale_string(it, arg(a, 0), &[])
1150}
1151pub(crate) const RADIX_DIGITS: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";
1152pub(crate) fn format_radix(num: f64, radix: u32) -> String {
1153 if num.is_nan() {
1154 return String::from("NaN");
1155 }
1156 if num.is_infinite() {
1157 return String::from(if num > 0.0 { "Infinity" } else { "-Infinity" });
1158 }
1159 let neg = num < 0.0;
1160 let n = libm::fabs(num);
1161 let int_n = libm::floor(n);
1162 let mut frac = n - int_n;
1163 let mut int_digits = Vec::new();
1164 let mut i = int_n;
1165 if i == 0.0 {
1166 int_digits.push(b'0');
1167 }
1168 while i >= 1.0 {
1169 let rem = libm::fmod(i, radix as f64) as usize;
1170 int_digits.push(RADIX_DIGITS[rem.min(35)]);
1171 i = libm::floor(i / radix as f64);
1172 }
1173 int_digits.reverse();
1174 let mut s = String::from_utf8(int_digits).unwrap_or_default();
1175 if frac > 0.0 {
1176 s.push('.');
1177 let mut count = 0;
1178 while frac > 0.0 && count < 20 {
1179 frac *= radix as f64;
1180 let d = libm::floor(frac) as usize;
1181 s.push(RADIX_DIGITS[d.min(35)] as char);
1182 frac -= libm::floor(frac);
1183 count += 1;
1184 }
1185 }
1186 if neg {
1187 format!("-{}", s)
1188 } else {
1189 s
1190 }
1191}
1192
1193pub fn bigint_get(b: &alloc::rc::Rc<super::super::bigint::BigInt>, key: &str) -> Value {
1195 match key {
1196 "toString" => nv("toString", bigint_to_string),
1197 "valueOf" => nv("valueOf", bigint_value_of),
1198 _ => {
1199 let _ = b;
1200 Value::Undefined
1201 }
1202 }
1203}
1204pub(crate) fn bigint_to_string(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1205 Ok(Value::str(t.to_js_string()))
1206}
1207pub(crate) fn bigint_value_of(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1208 Ok(t)
1209}
1210
1211pub(crate) fn bigint_ctor(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1214 use super::super::bigint::BigInt;
1215 let arg = a.first().cloned().unwrap_or(Value::Undefined);
1216 let bi = match &arg {
1217 Value::BigInt(b) => (**b).clone(),
1218 Value::Number(n) => match BigInt::from_f64(*n) {
1219 Some(v) => v,
1220 None => {
1221 return Err(it.error("The number is not a safe integer / cannot convert to BigInt"));
1222 }
1223 },
1224 Value::Bool(b) => BigInt::from_i64(if *b { 1 } else { 0 }),
1225 Value::Str(s) => match BigInt::parse_str(s) {
1226 Some(v) => v,
1227 None => return Err(it.error("Cannot convert string to a BigInt")),
1228 },
1229 _ => return Err(it.error("Cannot convert value to a BigInt")),
1230 };
1231 Ok(Value::bigint(bi))
1232}
1233
1234pub(crate) fn bits_arg(it: &mut Interp, a: &[Value]) -> Result<u64, Value> {
1236 let n = a.first().map(|v| v.to_number()).unwrap_or(f64::NAN);
1237 if !n.is_finite() || n < 0.0 {
1238 return Err(it.error("Invalid bits value for BigInt.asIntN/asUintN"));
1239 }
1240 Ok(libm::trunc(n) as u64)
1241}
1242
1243pub(crate) fn bigint_arg(it: &mut Interp, a: &[Value]) -> Result<super::super::bigint::BigInt, Value> {
1245 match a.get(1) {
1246 Some(Value::BigInt(b)) => Ok((**b).clone()),
1247 _ => Err(it.error("BigInt.asIntN/asUintN requires a BigInt as the second argument")),
1248 }
1249}
1250
1251pub(crate) fn bigint_as_int_n(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1253 let bits = bits_arg(it, a)?;
1254 let v = bigint_arg(it, a)?;
1255 Ok(Value::bigint(v.as_int_n(bits)))
1256}
1257
1258pub(crate) fn bigint_as_uint_n(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
1260 let bits = bits_arg(it, a)?;
1261 let v = bigint_arg(it, a)?;
1262 Ok(Value::bigint(v.as_uint_n(bits)))
1263}
1264
1265pub fn object_get(o: &ObjRef, key: &str) -> Value {
1269 let is_array = matches!(o.borrow().kind, ObjKind::Array(_) | ObjKind::Bytes(_));
1270 if is_array {
1271 match key {
1272 "push" => return nv("push", arr_push),
1273 "pop" => return nv("pop", arr_pop),
1274 "shift" => return nv("shift", arr_shift),
1275 "unshift" => return nv("unshift", arr_unshift),
1276 "slice" => return nv("slice", arr_slice),
1277 "indexOf" => return nv("indexOf", arr_index_of),
1278 "includes" => return nv("includes", arr_includes),
1279 "join" => return nv("join", arr_join),
1280 "concat" => return nv("concat", arr_concat),
1281 "reverse" => return nv("reverse", arr_reverse),
1282 "map" => return nv("map", arr_map),
1283 "filter" => return nv("filter", arr_filter),
1284 "forEach" => return nv("forEach", arr_for_each),
1285 "reduce" => return nv("reduce", arr_reduce),
1286 "find" => return nv("find", arr_find),
1287 "findIndex" => return nv("findIndex", arr_find_index),
1288 "some" => return nv("some", arr_some),
1289 "every" => return nv("every", arr_every),
1290 "sort" => return nv("sort", arr_sort),
1291 "fill" => return nv("fill", arr_fill),
1292 "flat" => return nv("flat", arr_flat),
1293 "flatMap" => return nv("flatMap", arr_flat_map),
1294 "at" => return nv("at", arr_at),
1295 "findLast" => return nv("findLast", arr_find_last),
1296 "findLastIndex" => return nv("findLastIndex", arr_find_last_index),
1297 "entries" => return nv("entries", arr_entries),
1298 "keys" => return nv("keys", arr_keys),
1299 "values" => return nv("values", arr_values),
1300 "Symbol(Symbol.iterator)" => return nv("[Symbol.iterator]", arr_values),
1306 "reduceRight" => return nv("reduceRight", arr_reduce_right),
1307 "toString" => return nv("toString", arr_to_string),
1308 "toLocaleString" => return nv("toLocaleString", arr_to_locale_string),
1309 "splice" => return nv("splice", arr_splice),
1310 "lastIndexOf" => return nv("lastIndexOf", arr_last_index_of),
1311 "copyWithin" => return nv("copyWithin", arr_copy_within),
1312 "toSorted" => return nv("toSorted", arr_to_sorted),
1313 "toReversed" => return nv("toReversed", arr_to_reversed),
1314 "toSpliced" => return nv("toSpliced", arr_to_spliced),
1315 "with" => return nv("with", arr_with),
1316 _ => {}
1317 }
1318 }
1319 match key {
1327 "hasOwnProperty" => nv("hasOwnProperty", obj_has_own),
1328 "toString" | "toLocaleString" => nv("toString", obj_to_string),
1329 "isPrototypeOf" => nv("isPrototypeOf", obj_is_prototype_of),
1330 "propertyIsEnumerable" => nv("propertyIsEnumerable", obj_property_is_enumerable),
1331 "__defineGetter__" => nv("__defineGetter__", obj_define_getter),
1336 "__defineSetter__" => nv("__defineSetter__", obj_define_setter),
1337 "__lookupGetter__" => nv("__lookupGetter__", obj_lookup_getter),
1338 "__lookupSetter__" => nv("__lookupSetter__", obj_lookup_setter),
1339 _ => Value::Undefined,
1340 }
1341}
1342pub(crate) fn obj_define_getter(_: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1343 if let Value::Object(o) = &this {
1344 use super::super::value::Accessor;
1345 let key = arg(a, 0).to_js_string();
1346 let mut b = o.borrow_mut();
1347 let entry = b.accessors.entry(key).or_insert(Accessor { get: None, set: None });
1348 entry.get = Some(arg(a, 1));
1349 }
1350 Ok(Value::Undefined)
1351}
1352pub(crate) fn obj_define_setter(_: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1353 if let Value::Object(o) = &this {
1354 use super::super::value::Accessor;
1355 let key = arg(a, 0).to_js_string();
1356 let mut b = o.borrow_mut();
1357 let entry = b.accessors.entry(key).or_insert(Accessor { get: None, set: None });
1358 entry.set = Some(arg(a, 1));
1359 }
1360 Ok(Value::Undefined)
1361}
1362pub(crate) fn obj_lookup_getter(_: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1363 if let Value::Object(o) = &this {
1364 let key = arg(a, 0).to_js_string();
1365 if let Some(g) = o.borrow().accessors.get(&key).and_then(|acc| acc.get.clone()) {
1366 return Ok(g);
1367 }
1368 }
1369 Ok(Value::Undefined)
1370}
1371pub(crate) fn obj_lookup_setter(_: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
1372 if let Value::Object(o) = &this {
1373 let key = arg(a, 0).to_js_string();
1374 if let Some(s) = o.borrow().accessors.get(&key).and_then(|acc| acc.set.clone()) {
1375 return Ok(s);
1376 }
1377 }
1378 Ok(Value::Undefined)
1379}
1380
1381pub(crate) fn this_items(this: &Value) -> Vec<Value> {
1387 if let Value::Object(o) = this {
1388 let o = unwrap_proxy_target(o);
1389 let b = o.borrow();
1390 if let ObjKind::Array(items) = &b.kind {
1391 return items.clone();
1392 }
1393 if let ObjKind::Bytes(st) = &b.kind {
1394 return st.values();
1395 }
1396 }
1397 Vec::new()
1398}
1399pub(crate) fn this_objref(this: &Value) -> Option<ObjRef> {
1402 if let Value::Object(o) = this {
1403 Some(unwrap_proxy_target(o))
1404 } else {
1405 None
1406 }
1407}
1408
1409pub(crate) fn array_like_length(it: &mut Interp, t: &Value) -> usize {
1411 let len = it
1412 .get_property(t, "length")
1413 .map(|l| l.to_number())
1414 .unwrap_or(0.0);
1415 if len.is_finite() && len > 0.0 {
1416 len as usize
1417 } else {
1418 0
1419 }
1420}
1421
1422pub(crate) fn arr_push(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1430 if let Some(o) = this_objref(&t) {
1431 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
1432 for v in a {
1433 items.push(v.clone());
1434 }
1435 return Ok(Value::Number(items.len() as f64));
1436 }
1437 }
1438 let mut len = array_like_length(it, &t);
1439 for v in a {
1440 it.set_property(&t, &len.to_string(), v.clone());
1441 len += 1;
1442 }
1443 it.set_property(&t, "length", Value::Number(len as f64));
1444 Ok(Value::Number(len as f64))
1445}
1446pub(crate) fn arr_pop(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1447 if let Some(o) = this_objref(&t) {
1448 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
1449 return Ok(items.pop().unwrap_or(Value::Undefined));
1450 }
1451 }
1452 let len = array_like_length(it, &t);
1453 if len == 0 {
1454 it.set_property(&t, "length", Value::Number(0.0));
1455 return Ok(Value::Undefined);
1456 }
1457 let new_len = len - 1;
1458 let val = it
1459 .get_property(&t, &new_len.to_string())
1460 .unwrap_or(Value::Undefined);
1461 it.set_property(&t, "length", Value::Number(new_len as f64));
1462 Ok(val)
1463}
1464pub(crate) fn arr_shift(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1465 if let Some(o) = this_objref(&t) {
1466 drop_holes(&o);
1467 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
1468 if items.is_empty() {
1469 return Ok(Value::Undefined);
1470 }
1471 return Ok(items.remove(0));
1472 }
1473 }
1474 let len = array_like_length(it, &t);
1475 if len == 0 {
1476 it.set_property(&t, "length", Value::Number(0.0));
1477 return Ok(Value::Undefined);
1478 }
1479 let first = it.get_property(&t, "0").unwrap_or(Value::Undefined);
1480 for i in 1..len {
1481 let v = it.get_property(&t, &i.to_string()).unwrap_or(Value::Undefined);
1482 it.set_property(&t, &(i - 1).to_string(), v);
1483 }
1484 it.set_property(&t, "length", Value::Number((len - 1) as f64));
1485 Ok(first)
1486}
1487pub(crate) fn arr_unshift(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1488 if let Some(o) = this_objref(&t) {
1489 drop_holes(&o);
1490 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
1491 for (i, v) in a.iter().enumerate() {
1492 items.insert(i, v.clone());
1493 }
1494 return Ok(Value::Number(items.len() as f64));
1495 }
1496 }
1497 let len = array_like_length(it, &t);
1498 let shift = a.len();
1499 for i in (0..len).rev() {
1502 let v = it.get_property(&t, &i.to_string()).unwrap_or(Value::Undefined);
1503 it.set_property(&t, &(i + shift).to_string(), v);
1504 }
1505 for (i, v) in a.iter().enumerate() {
1506 it.set_property(&t, &i.to_string(), v.clone());
1507 }
1508 let new_len = len + shift;
1509 it.set_property(&t, "length", Value::Number(new_len as f64));
1510 Ok(Value::Number(new_len as f64))
1511}
1512pub(crate) fn arr_slice(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1517 let is_array = matches!(&t, Value::Object(o) if matches!(o.borrow().kind, ObjKind::Array(_)));
1518 let items = if is_array {
1519 this_items(&t)
1520 } else {
1521 array_like_items(it, &t)
1522 };
1523 let len = items.len() as i64;
1524 let start = norm_index(arg(a, 0).to_number(), len, 0).max(0) as usize;
1525 let end = if matches!(arg(a, 1), Value::Undefined) {
1526 len
1527 } else {
1528 norm_index(arg(a, 1).to_number(), len, len)
1529 }
1530 .max(0) as usize;
1531 let out = if start < end && start < items.len() {
1532 items[start..end.min(items.len())].to_vec()
1533 } else {
1534 Vec::new()
1535 };
1536 if is_typed_array(&t) {
1537 return Ok(same_kind_as(it, &t, out));
1538 }
1539 let a_obj = array_species_create(it, &t, out.len())?;
1540 populate_array_species(it, &a_obj, out)
1541}
1542pub(crate) fn arr_from_index(a: &[Value], len: usize) -> usize {
1545 if matches!(arg(a, 1), Value::Undefined) {
1546 return 0;
1547 }
1548 let n = arg(a, 1).to_number();
1549 if n.is_nan() {
1550 return 0;
1551 }
1552 let len = len as i64;
1553 let idx = if n < 0.0 { (len + n as i64).max(0) } else { n as i64 };
1554 idx.min(len) as usize
1555}
1556pub(crate) fn arr_index_of(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1563 let items = generic_array_items(it, &t);
1564 let target = arg(a, 0);
1565 let from = arr_from_index(a, items.len());
1566 let holes = holes_of(&t);
1567 for (i, v) in items.iter().enumerate().skip(from) {
1568 if !is_hole_at(&holes, i) && v.strict_eq(&target) {
1569 return Ok(Value::Number(i as f64));
1570 }
1571 }
1572 Ok(Value::Number(-1.0))
1573}
1574pub(crate) fn arr_includes(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1579 let items = generic_array_items(it, &t);
1580 let target = arg(a, 0);
1581 let from = arr_from_index(a, items.len());
1582 Ok(Value::Bool(
1583 items.iter().skip(from).any(|v| same_value_zero(v, &target)),
1584 ))
1585}
1586pub(crate) fn arr_join(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1592 let is_array = matches!(&t, Value::Object(o) if matches!(o.borrow().kind, ObjKind::Array(_)));
1593 let items = if is_array {
1594 this_items(&t)
1595 } else {
1596 array_like_items(it, &t)
1597 };
1598 let sep = if matches!(arg(a, 0), Value::Undefined) {
1599 String::from(",")
1600 } else {
1601 arg(a, 0).to_js_string()
1602 };
1603 let parts: Vec<String> = items
1604 .iter()
1605 .map(|v| match v {
1606 Value::Undefined | Value::Null => String::new(),
1607 _ => v.to_js_string(),
1608 })
1609 .collect();
1610 Ok(Value::str(parts.join(&sep)))
1611}
1612pub(crate) fn is_real_array(t: &Value) -> bool {
1617 matches!(t, Value::Object(o) if matches!(o.borrow().kind, ObjKind::Array(_)))
1618}
1619pub(crate) fn holes_of(t: &Value) -> Vec<bool> {
1622 match t {
1623 Value::Object(o) => o.borrow().hole_mask(),
1624 _ => Vec::new(),
1625 }
1626}
1627fn is_hole_at(holes: &[bool], i: usize) -> bool {
1628 holes.get(i).copied().unwrap_or(false)
1629}
1630pub(crate) fn set_holes(v: &Value, mask: Vec<bool>) {
1632 if let Value::Object(o) = v {
1633 let mut b = o.borrow_mut();
1634 if matches!(b.kind, ObjKind::Array(_)) {
1635 b.holes = if mask.iter().any(|&h| h) { Some(alloc::boxed::Box::new(mask)) } else { None };
1636 }
1637 }
1638}
1639fn drop_holes(o: &ObjRef) {
1641 o.borrow_mut().holes = None;
1642}
1643pub(crate) fn generic_array_items(it: &mut Interp, t: &Value) -> Vec<Value> {
1644 if is_real_array(t) {
1645 this_items(t)
1646 } else {
1647 array_like_items(it, t)
1648 }
1649}
1650pub(crate) fn array_like_items(it: &mut Interp, v: &Value) -> Vec<Value> {
1654 let len = it
1655 .get_property(v, "length")
1656 .map(|l| l.to_number())
1657 .unwrap_or(0.0);
1658 let len = if len.is_finite() && len > 0.0 { len as usize } else { 0 };
1659 let mut out = Vec::with_capacity(len);
1660 for i in 0..len {
1661 out.push(
1662 it.get_property(v, &i.to_string())
1663 .unwrap_or(Value::Undefined),
1664 );
1665 }
1666 out
1667}
1668
1669pub(crate) fn concat_push(it: &mut Interp, v: &Value, items: &mut Vec<Value>) -> Result<(), Value> {
1681 match v {
1682 Value::Object(o) => {
1683 let flag = it.get_property(v, "Symbol(Symbol.isConcatSpreadable)")?;
1684 let is_array = matches!(o.borrow().kind, ObjKind::Array(_));
1685 let spreadable = if matches!(flag, Value::Undefined) {
1686 is_array
1687 } else {
1688 flag.truthy()
1689 };
1690 if !spreadable {
1691 items.push(v.clone());
1692 } else if is_array {
1693 if let ObjKind::Array(other) = &o.borrow().kind {
1694 items.extend(other.clone());
1695 }
1696 } else {
1697 items.extend(array_like_items(it, v));
1698 }
1699 }
1700 other => items.push(other.clone()),
1701 }
1702 Ok(())
1703}
1704pub(crate) fn arr_concat(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1705 let mut items = Vec::new();
1706 let mut mask: Vec<bool> = Vec::new();
1707 for v in core::iter::once(&t).chain(a.iter()) {
1708 let before = items.len();
1709 concat_push(it, v, &mut items)?;
1710 let added = items.len() - before;
1711 let h = holes_of(v);
1712 mask.extend((0..added).map(|i| added == h.len() && is_hole_at(&h, i)));
1713 }
1714 let a_obj = array_species_create(it, &t, 0)?;
1715 let r = populate_array_species(it, &a_obj, items)?;
1716 set_holes(&r, mask);
1717 Ok(r)
1718}
1719pub(crate) fn arr_reverse(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1720 if let Some(o) = this_objref(&t) {
1721 drop_holes(&o);
1722 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
1723 items.reverse();
1724 }
1725 }
1726 Ok(t)
1727}
1728pub(crate) fn arr_to_string(_: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1729 Ok(Value::str(t.to_js_string()))
1730}
1731pub(crate) fn arr_to_locale_string(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
1736 let items = generic_array_items(it, &t);
1737 let mut parts = Vec::with_capacity(items.len());
1738 for v in items {
1739 if matches!(v, Value::Undefined | Value::Null) {
1740 parts.push(String::new());
1741 continue;
1742 }
1743 let method = it.get_property(&v, "toLocaleString").unwrap_or(Value::Undefined);
1744 let s = if matches!(&method, Value::Object(f) if f.borrow().is_callable()) {
1745 it.call_value(&method, v.clone(), &[])?.to_js_string()
1746 } else {
1747 v.to_js_string()
1748 };
1749 parts.push(s);
1750 }
1751 Ok(Value::str(parts.join(",")))
1752}
1753pub(crate) fn arr_map(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1757 let items = generic_array_items(it, &t);
1758 let f = arg(a, 0);
1759 let this_arg = arg(a, 1);
1760 let holes = holes_of(&t);
1761 let mut out = Vec::with_capacity(items.len());
1762 for (i, v) in items.iter().enumerate() {
1763 if is_hole_at(&holes, i) {
1764 out.push(Value::Undefined);
1765 continue;
1766 }
1767 let r = it.call_value(
1768 &f,
1769 this_arg.clone(),
1770 &[v.clone(), Value::Number(i as f64), t.clone()],
1771 )?;
1772 out.push(r);
1773 }
1774 if is_typed_array(&t) {
1775 return Ok(same_kind_as(it, &t, out));
1776 }
1777 let a_obj = array_species_create(it, &t, items.len())?;
1778 let r = populate_array_species(it, &a_obj, out)?;
1779 set_holes(&r, holes);
1780 Ok(r)
1781}
1782pub(crate) fn arr_filter(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 let mut out = Vec::new();
1787 let holes = holes_of(&t);
1788 for (i, v) in items.iter().enumerate() {
1789 if is_hole_at(&holes, i) {
1790 continue;
1791 }
1792 if it
1793 .call_value(
1794 &f,
1795 this_arg.clone(),
1796 &[v.clone(), Value::Number(i as f64), t.clone()],
1797 )?
1798 .truthy()
1799 {
1800 out.push(v.clone());
1801 }
1802 }
1803 if is_typed_array(&t) {
1804 return Ok(same_kind_as(it, &t, out));
1805 }
1806 let a_obj = array_species_create(it, &t, 0)?;
1807 populate_array_species(it, &a_obj, out)
1808}
1809pub(crate) fn arr_for_each(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1814 let items = generic_array_items(it, &t);
1815 let f = arg(a, 0);
1816 let this_arg = arg(a, 1);
1817 let holes = holes_of(&t);
1818 for (i, v) in items.iter().enumerate() {
1819 if is_hole_at(&holes, i) {
1820 continue;
1821 }
1822 it.call_value(
1823 &f,
1824 this_arg.clone(),
1825 &[v.clone(), Value::Number(i as f64), t.clone()],
1826 )?;
1827 }
1828 Ok(Value::Undefined)
1829}
1830pub(crate) fn arr_reduce(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1834 let items = generic_array_items(it, &t);
1835 let f = arg(a, 0);
1836 let holes = holes_of(&t);
1837 let mut idx = 0;
1838 let mut acc = if a.len() >= 2 {
1839 arg(a, 1)
1840 } else {
1841 while is_hole_at(&holes, idx) {
1842 idx += 1;
1843 }
1844 if idx >= items.len() {
1845 return Err(it.error("Reduce of empty array with no initial value"));
1846 }
1847 idx += 1;
1848 items[idx - 1].clone()
1849 };
1850 while idx < items.len() {
1851 if is_hole_at(&holes, idx) {
1852 idx += 1;
1853 continue;
1854 }
1855 acc = it.call_value(
1856 &f,
1857 Value::Undefined,
1858 &[
1859 acc,
1860 items[idx].clone(),
1861 Value::Number(idx as f64),
1862 t.clone(),
1863 ],
1864 )?;
1865 idx += 1;
1866 }
1867 Ok(acc)
1868}
1869pub(crate) fn arr_find(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1870 let items = generic_array_items(it, &t);
1871 let f = arg(a, 0);
1872 let this_arg = arg(a, 1);
1873 for (i, v) in items.iter().enumerate() {
1874 if it
1875 .call_value(
1876 &f,
1877 this_arg.clone(),
1878 &[v.clone(), Value::Number(i as f64), t.clone()],
1879 )?
1880 .truthy()
1881 {
1882 return Ok(v.clone());
1883 }
1884 }
1885 Ok(Value::Undefined)
1886}
1887pub(crate) fn arr_find_index(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1888 let items = generic_array_items(it, &t);
1889 let f = arg(a, 0);
1890 let this_arg = arg(a, 1);
1891 for (i, v) in items.iter().enumerate() {
1892 if it
1893 .call_value(
1894 &f,
1895 this_arg.clone(),
1896 &[v.clone(), Value::Number(i as f64), t.clone()],
1897 )?
1898 .truthy()
1899 {
1900 return Ok(Value::Number(i as f64));
1901 }
1902 }
1903 Ok(Value::Number(-1.0))
1904}
1905pub(crate) fn arr_some(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1906 let items = generic_array_items(it, &t);
1907 let f = arg(a, 0);
1908 let this_arg = arg(a, 1);
1909 let holes = holes_of(&t);
1910 for (i, v) in items.iter().enumerate() {
1911 if is_hole_at(&holes, i) {
1912 continue;
1913 }
1914 if it
1915 .call_value(
1916 &f,
1917 this_arg.clone(),
1918 &[v.clone(), Value::Number(i as f64), t.clone()],
1919 )?
1920 .truthy()
1921 {
1922 return Ok(Value::Bool(true));
1923 }
1924 }
1925 Ok(Value::Bool(false))
1926}
1927pub(crate) fn arr_every(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
1928 let items = generic_array_items(it, &t);
1929 let f = arg(a, 0);
1930 let this_arg = arg(a, 1);
1931 let holes = holes_of(&t);
1932 for (i, v) in items.iter().enumerate() {
1933 if is_hole_at(&holes, i) {
1934 continue;
1935 }
1936 if !it
1937 .call_value(
1938 &f,
1939 this_arg.clone(),
1940 &[v.clone(), Value::Number(i as f64), t.clone()],
1941 )?
1942 .truthy()
1943 {
1944 return Ok(Value::Bool(false));
1945 }
1946 }
1947 Ok(Value::Bool(true))
1948}
1949pub(crate) fn sort_excluding_undefined(
1955 it: &mut Interp,
1956 mut items: alloc::vec::Vec<Value>,
1957 cmp: &Value,
1958) -> Result<alloc::vec::Vec<Value>, Value> {
1959 let undefined_count = items.iter().filter(|v| matches!(v, Value::Undefined)).count();
1963 items.retain(|v| !matches!(v, Value::Undefined));
1964 let n = items.len();
1965 let keys: Option<alloc::vec::Vec<String>> = if matches!(cmp, Value::Undefined) {
1966 Some(items.iter().map(|v| v.to_js_string()).collect())
1967 } else {
1968 None
1969 };
1970 let greater = |it: &mut Interp, a: usize, b: usize| -> Result<bool, Value> {
1972 match &keys {
1973 Some(k) => Ok(k[a] > k[b]),
1974 None => Ok(it
1975 .call_value(cmp, Value::Undefined, &[items[a].clone(), items[b].clone()])?
1976 .to_number()
1977 > 0.0),
1978 }
1979 };
1980 let mut idx: alloc::vec::Vec<usize> = (0..n).collect();
1981 let mut buf: alloc::vec::Vec<usize> = alloc::vec![0; n];
1982 let mut width = 1;
1983 while width < n {
1984 let mut lo = 0;
1985 while lo < n {
1986 let mid = (lo + width).min(n);
1987 let hi = (lo + 2 * width).min(n);
1988 let (mut l, mut r, mut k) = (lo, mid, lo);
1989 while l < mid && r < hi {
1990 if greater(it, idx[l], idx[r])? {
1991 buf[k] = idx[r];
1992 r += 1;
1993 } else {
1994 buf[k] = idx[l];
1995 l += 1;
1996 }
1997 k += 1;
1998 }
1999 buf[k..k + (mid - l)].copy_from_slice(&idx[l..mid]);
2000 k += mid - l;
2001 buf[k..k + (hi - r)].copy_from_slice(&idx[r..hi]);
2002 lo += 2 * width;
2003 }
2004 core::mem::swap(&mut idx, &mut buf);
2005 width *= 2;
2006 }
2007 let mut out: alloc::vec::Vec<Value> = idx.into_iter().map(|i| items[i].clone()).collect();
2008 for _ in 0..undefined_count {
2009 out.push(Value::Undefined);
2010 }
2011 Ok(out)
2012}
2013pub(crate) fn arr_sort(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2014 let items = this_items(&t);
2015 let cmp = arg(a, 0);
2016 let items = sort_excluding_undefined(it, items, &cmp)?;
2017 if let Some(o) = this_objref(&t) {
2018 drop_holes(&o);
2019 if let ObjKind::Array(slot) = &mut o.borrow_mut().kind {
2020 *slot = items;
2021 }
2022 }
2023 Ok(t)
2024}
2025pub(crate) fn arr_fill(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2029 if let Some(o) = this_objref(&t) {
2030 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
2031 let v = arg(a, 0);
2032 let len = items.len() as i64;
2033 let start = norm_index(arg(a, 1).to_number(), len, 0).max(0) as usize;
2034 let end = if matches!(arg(a, 2), Value::Undefined) {
2035 len
2036 } else {
2037 norm_index(arg(a, 2).to_number(), len, len)
2038 }
2039 .max(0) as usize;
2040 let end = end.min(items.len());
2041 for slot in items.iter_mut().take(end).skip(start) {
2042 *slot = v.clone();
2043 }
2044 }
2045 }
2046 Ok(t)
2047}
2048pub(crate) fn arr_splice(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2049 let o = match this_objref(&t) {
2050 Some(o) => o,
2051 None => return Ok(Value::Object(Obj::array(alloc::vec![]))),
2052 };
2053 let mut items = if let ObjKind::Array(v) = &o.borrow().kind {
2054 v.clone()
2055 } else {
2056 return Ok(Value::Object(Obj::array(alloc::vec![])));
2057 };
2058 let len = items.len() as i64;
2059 let start_raw = arg(a, 0).to_number() as i64;
2060 let start = if start_raw < 0 {
2061 (len + start_raw).max(0) as usize
2062 } else {
2063 start_raw.min(len) as usize
2064 };
2065 let delete_count = if a.len() < 2 {
2066 items.len() - start
2067 } else {
2068 let dc = arg(a, 1).to_number() as i64;
2069 dc.max(0).min((len - start as i64).max(0)) as usize
2070 };
2071 let insert: alloc::vec::Vec<Value> = a.iter().skip(2).cloned().collect();
2072 let removed: alloc::vec::Vec<Value> = items.drain(start..start + delete_count).collect();
2073 for (i, v) in insert.into_iter().enumerate() {
2074 items.insert(start + i, v);
2075 }
2076 drop_holes(&o);
2077 if let ObjKind::Array(slot) = &mut o.borrow_mut().kind {
2078 *slot = items;
2079 }
2080 let a_obj = array_species_create(it, &t, removed.len())?;
2081 populate_array_species(it, &a_obj, removed)
2082}
2083
2084pub(crate) fn arr_last_index_of(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2092 let items = generic_array_items(it, &t);
2093 let target = arg(a, 0);
2094 let len = items.len() as i64;
2095 if len == 0 {
2096 return Ok(Value::Number(-1.0));
2097 }
2098 let start = if matches!(arg(a, 1), Value::Undefined) {
2099 len - 1
2100 } else {
2101 let n = arg(a, 1).to_number();
2102 let n = if n.is_nan() { 0.0 } else { n };
2103 if n < 0.0 {
2104 len + n as i64
2105 } else {
2106 (n as i64).min(len - 1)
2107 }
2108 };
2109 if start < 0 {
2110 return Ok(Value::Number(-1.0));
2111 }
2112 for i in (0..=start as usize).rev() {
2113 if items[i].strict_eq(&target) {
2114 return Ok(Value::Number(i as f64));
2115 }
2116 }
2117 Ok(Value::Number(-1.0))
2118}
2119
2120pub(crate) fn arr_copy_within(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2121 let o = match this_objref(&t) {
2122 Some(o) => o,
2123 None => return Ok(t),
2124 };
2125 let mut items = if let ObjKind::Array(v) = &o.borrow().kind {
2126 v.clone()
2127 } else {
2128 return Ok(t);
2129 };
2130 let len = items.len() as i64;
2131 let to_raw = arg(a, 0).to_number() as i64;
2132 let to = if to_raw < 0 {
2133 (len + to_raw).max(0) as usize
2134 } else {
2135 to_raw.min(len) as usize
2136 };
2137 let from_raw = if matches!(arg(a, 1), Value::Undefined) { 0i64 } else { arg(a, 1).to_number() as i64 };
2138 let from = if from_raw < 0 {
2139 (len + from_raw).max(0) as usize
2140 } else {
2141 from_raw.min(len) as usize
2142 };
2143 let end_raw = if matches!(arg(a, 2), Value::Undefined) { len } else { arg(a, 2).to_number() as i64 };
2144 let end = if end_raw < 0 {
2145 (len + end_raw).max(0) as usize
2146 } else {
2147 end_raw.min(len) as usize
2148 };
2149 let count = if end > from { (end - from).min(len as usize - to) } else { 0 };
2150 let src: alloc::vec::Vec<Value> = items[from..from + count].to_vec();
2151 for (i, v) in src.into_iter().enumerate() {
2152 if to + i < items.len() {
2153 items[to + i] = v;
2154 }
2155 }
2156 if let ObjKind::Array(slot) = &mut o.borrow_mut().kind {
2157 *slot = items;
2158 }
2159 Ok(t)
2160}
2161
2162pub(crate) fn is_typed_array(t: &Value) -> bool {
2169 if let Value::Object(o) = t {
2170 o.borrow().props.contains_key("_ta_kind")
2171 } else {
2172 false
2173 }
2174}
2175
2176pub(crate) fn array_species_create(
2181 it: &mut Interp,
2182 original: &Value,
2183 length: usize,
2184) -> Result<Value, Value> {
2185 let is_array = match original {
2186 Value::Object(o) => matches!(o.borrow().kind, ObjKind::Array(_)),
2187 _ => false,
2188 };
2189 if !is_array {
2190 return Ok(Value::Object(Obj::array(alloc::vec::Vec::with_capacity(length))));
2191 }
2192
2193 let ctor = match it.get_property(original, "constructor") {
2194 Ok(v) => v,
2195 Err(_) => return Ok(Value::Object(Obj::array(alloc::vec::Vec::with_capacity(length)))),
2196 };
2197
2198 let ctor_is_obj = matches!(ctor, Value::Object(_));
2199 if !ctor_is_obj {
2200 return Ok(Value::Object(Obj::array(alloc::vec::Vec::with_capacity(length))));
2201 }
2202
2203 let species_val = match it.get_property(&ctor, "Symbol(Symbol.species)") {
2205 Ok(v) => v,
2206 Err(_) => ctor.clone(),
2207 };
2208
2209 if matches!(species_val, Value::Null | Value::Undefined) {
2210 return Ok(Value::Object(Obj::array(alloc::vec::Vec::with_capacity(length))));
2211 }
2212
2213 if let Value::Object(ref so) = species_val {
2215 if let Some(Value::Object(ref ao)) = it.global.borrow().vars.get("Array") {
2216 if core::ptr::eq(so.as_ref(), ao.as_ref()) {
2217 return Ok(Value::Object(Obj::array(alloc::vec::Vec::with_capacity(length))));
2218 }
2219 }
2220 if so.borrow().is_callable() {
2221 return it.construct_value(&species_val, &[Value::Number(length as f64)]);
2222 }
2223 }
2224
2225 Err(it.type_error("Symbol.species is not a constructor"))
2226}
2227
2228pub(crate) fn populate_array_species(
2229 it: &mut Interp,
2230 target: &Value,
2231 items: alloc::vec::Vec<Value>,
2232) -> Result<Value, Value> {
2233 if let Value::Object(o) = target {
2234 let is_plain_array = matches!(o.borrow().kind, ObjKind::Array(_));
2235 if is_plain_array {
2236 let len = items.len();
2237 o.borrow_mut().kind = ObjKind::Array(items);
2238 o.borrow_mut().props.insert("length".into(), Value::Number(len as f64));
2239 return Ok(target.clone());
2240 }
2241 }
2242 let len = items.len();
2243 for (i, v) in items.into_iter().enumerate() {
2244 let _ = it.set_property(target, &i.to_string(), v);
2245 }
2246 let _ = it.set_property(target, "length", Value::Number(len as f64));
2247 Ok(target.clone())
2248}
2249
2250pub(crate) fn same_kind_as(it: &mut Interp, t: &Value, items: alloc::vec::Vec<Value>) -> Value {
2251 let ta_kind_tag = if let Value::Object(o) = t {
2252 o.borrow().props.get("_ta_kind").map(|v| v.to_js_string())
2253 } else {
2254 None
2255 };
2256 match ta_kind_tag {
2257 Some(tag) => {
2258 let kind = TaKind::from_name(&tag);
2262 let values: alloc::vec::Vec<f64> = items.iter().map(|v| kind.convert(v)).collect();
2263 make_typed_array(it, values, kind)
2264 }
2265 None => Value::Object(Obj::array(items)),
2266 }
2267}
2268pub(crate) fn arr_to_sorted(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2269 let items = generic_array_items(it, &t);
2270 let cmp = arg(a, 0);
2271 let items = sort_excluding_undefined(it, items, &cmp)?;
2272 Ok(same_kind_as(it, &t, items))
2273}
2274
2275pub(crate) fn arr_to_reversed(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
2276 let mut items = generic_array_items(it, &t);
2277 items.reverse();
2278 Ok(same_kind_as(it, &t, items))
2279}
2280
2281pub(crate) fn arr_to_spliced(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2282 let mut items = generic_array_items(it, &t);
2283 let len = items.len() as i64;
2284 let start_raw = arg(a, 0).to_number() as i64;
2285 let start = if start_raw < 0 {
2286 (len + start_raw).max(0) as usize
2287 } else {
2288 start_raw.min(len) as usize
2289 };
2290 let delete_count = if a.len() < 2 {
2291 items.len() - start
2292 } else {
2293 let dc = arg(a, 1).to_number() as i64;
2294 dc.max(0).min((len - start as i64).max(0)) as usize
2295 };
2296 let insert: alloc::vec::Vec<Value> = a.iter().skip(2).cloned().collect();
2297 items.drain(start..start + delete_count);
2298 for (i, v) in insert.into_iter().enumerate() {
2299 items.insert(start + i, v);
2300 }
2301 Ok(same_kind_as(it, &t, items))
2302}
2303
2304pub(crate) fn arr_with(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2307 let mut items = generic_array_items(it, &t);
2308 let len = items.len() as i64;
2309 let idx_raw = arg(a, 0).to_number() as i64;
2310 let idx = if idx_raw < 0 { len + idx_raw } else { idx_raw };
2311 if idx < 0 || idx >= len {
2312 return Err(it.error("Invalid index"));
2313 }
2314 items[idx as usize] = arg(a, 1);
2315 Ok(same_kind_as(it, &t, items))
2316}
2317
2318pub(crate) fn flatten_depth(items: Vec<Value>, depth: f64, out: &mut Vec<Value>, seen: &mut Vec<ObjRef>) {
2327 for v in items {
2328 match v {
2329 Value::Object(o) if depth > 0.0 && matches!(o.borrow().kind, ObjKind::Array(_)) => {
2330 if seen.iter().any(|s| Rc::ptr_eq(s, &o)) {
2331 out.push(Value::Object(o));
2332 continue;
2333 }
2334 seen.push(o.clone());
2335 let inner = match &o.borrow().kind {
2336 ObjKind::Array(inner) => inner.clone(),
2337 _ => Vec::new(),
2338 };
2339 flatten_depth(inner, depth - 1.0, out, seen);
2340 seen.pop();
2341 }
2342 other => out.push(other),
2343 }
2344 }
2345}
2346pub(crate) fn arr_flat(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2350 let items = generic_array_items(it, &t);
2351 let depth = if matches!(arg(a, 0), Value::Undefined) {
2352 1.0
2353 } else {
2354 arg(a, 0).to_number()
2355 };
2356 let mut out = Vec::new();
2357 flatten_depth(items, depth, &mut out, &mut Vec::new());
2358 Ok(Value::Object(Obj::array(out)))
2359}
2360pub(crate) fn has_own_property(o: &super::super::value::ObjRef, key: &str) -> bool {
2366 let b = o.borrow();
2367 if let ObjKind::Array(items) = &b.kind {
2368 if key == "length" {
2369 return true;
2370 }
2371 if let Ok(idx) = key.parse::<usize>() {
2372 return idx < items.len();
2373 }
2374 }
2375 b.props.contains_key(key) || b.accessors.contains_key(key)
2376}
2377pub(crate) fn obj_has_own(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2378 let key = arg(a, 0).to_js_string();
2379 if let Value::Object(o) = &t {
2380 let dom_idx = match o.borrow().kind {
2382 ObjKind::DomElement(i) => Some(i),
2383 _ => None,
2384 };
2385 if let Some(i) = dom_idx {
2386 return Ok(Value::Bool(it.dom.borrow().expandos.get(&i).is_some_and(|m| m.contains_key(&key))));
2387 }
2388 return Ok(Value::Bool(has_own_property(o, &key)));
2389 }
2390 if let Value::Str(s) = &t {
2392 let n = s.chars().count();
2393 return Ok(Value::Bool(key == "length" || key.parse::<usize>().is_ok_and(|i| i < n)));
2394 }
2395 Ok(Value::Bool(false))
2396}
2397pub(crate) fn obj_to_string(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
2403 if let Value::Object(o) = &t {
2407 let mut cur = Some(o.clone());
2408 while let Some(c) = cur {
2409 let (own_tag, getter, next) = {
2410 let b = c.borrow();
2411 (
2412 b.props.get("Symbol(Symbol.toStringTag)").cloned(),
2413 b.accessors
2414 .get("Symbol(Symbol.toStringTag)")
2415 .and_then(|acc| acc.get.clone()),
2416 b.proto.clone(),
2417 )
2418 };
2419 if let Some(v) = own_tag {
2420 return Ok(Value::str(format!("[object {}]", v.to_js_string())));
2421 }
2422 if let Some(g) = getter {
2423 let v = it.call_value(&g, t.clone(), &[])?;
2424 return Ok(Value::str(format!("[object {}]", v.to_js_string())));
2425 }
2426 cur = next;
2427 }
2428 }
2429 let tag = match &t {
2430 Value::Undefined => "Undefined",
2431 Value::Null => "Null",
2432 Value::Object(o) => {
2433 let b = o.borrow();
2434 if b.is_callable() {
2435 "Function"
2436 } else {
2437 match &b.kind {
2438 ObjKind::Array(_) => "Array",
2439 ObjKind::DateObj(_) => "Date",
2440 ObjKind::RegExpObj(_) => "RegExp",
2441 ObjKind::MapObj(_) => "Map",
2442 ObjKind::SetObj(_) => "Set",
2443 ObjKind::PromiseObj(_) => "Promise",
2444 ObjKind::Generator(_) => "Generator",
2445 _ => "Object",
2446 }
2447 }
2448 }
2449 Value::Str(_) => "String",
2462 Value::Number(_) => "Number",
2463 Value::Bool(_) => "Boolean",
2464 Value::BigInt(_) => "BigInt",
2465 };
2466 Ok(Value::str(format!("[object {}]", tag)))
2467}
2468pub(crate) fn obj_is_prototype_of(_: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2471 let (Value::Object(target), Value::Object(candidate)) = (&t, &arg(a, 0)) else {
2472 return Ok(Value::Bool(false));
2473 };
2474 let mut cur = candidate.borrow().proto.clone();
2475 let mut guard = 0;
2476 while let Some(p) = cur {
2477 if Rc::ptr_eq(&p, target) {
2478 return Ok(Value::Bool(true));
2479 }
2480 cur = p.borrow().proto.clone();
2481 guard += 1;
2482 if guard > 1000 {
2483 break;
2484 }
2485 }
2486 Ok(Value::Bool(false))
2487}
2488pub(crate) fn obj_property_is_enumerable(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2492 obj_has_own(it, t, a)
2493}
2494
2495pub fn function_method(key: &str) -> Value {
2499 match key {
2500 "call" => nv("call", fn_call),
2501 "apply" => nv("apply", fn_apply),
2502 "bind" => nv("bind", fn_bind),
2503 _ => Value::Undefined,
2504 }
2505}
2506pub(crate) fn fn_to_string(it: &mut Interp, t: Value, _a: &[Value]) -> Result<Value, Value> {
2509 match &t {
2510 Value::Object(o) if o.borrow().is_callable() => Ok(Value::str(t.to_js_string())),
2511 _ => Err(it.type_error("Function.prototype.toString requires that 'this' be a Function")),
2512 }
2513}
2514pub(crate) fn fn_call(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2515 let this_arg = arg(a, 0);
2516 let rest: Vec<Value> = a.get(1..).map(|s| s.to_vec()).unwrap_or_default();
2517 it.call_value(&t, this_arg, &rest)
2518}
2519pub(crate) fn fn_apply(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2524 let this_arg = arg(a, 0);
2525 let args_vec = match arg(a, 1) {
2526 Value::Undefined | Value::Null => Vec::new(),
2527 v => array_like_items(it, &v),
2528 };
2529 it.call_value(&t, this_arg, &args_vec)
2530}
2531pub(crate) fn fn_bind(_it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
2532 let bound_this = arg(a, 0);
2533 let bound_args: Vec<Value> = a.get(1..).map(|s| s.to_vec()).unwrap_or_default();
2534 Ok(Value::Object(Obj::bound(t, bound_this, bound_args)))
2535}
2536
2537pub(crate) fn norm_index(v: f64, len: i64, default: i64) -> i64 {
2540 if v.is_nan() {
2541 return default;
2542 }
2543 let i = libm::trunc(v) as i64;
2544 if i < 0 {
2545 (len + i).max(0)
2546 } else {
2547 i.min(len)
2548 }
2549}
2550pub(crate) fn clamp_idx(v: f64, len: usize) -> usize {
2551 if v.is_nan() || v < 0.0 {
2552 0
2553 } else {
2554 (v as usize).min(len)
2555 }
2556}
2557pub(crate) fn pad_str(s: &str, target: usize, pad: &str, start: bool) -> String {
2558 let cur = s.chars().count();
2559 if cur >= target || pad.is_empty() {
2560 return String::from(s);
2561 }
2562 let mut padding = String::new();
2563 let pad_chars: Vec<char> = pad.chars().collect();
2564 let mut i = 0;
2565 while padding.chars().count() < target - cur {
2566 padding.push(pad_chars[i % pad_chars.len()]);
2567 i += 1;
2568 }
2569 if start {
2570 format!("{}{}", padding, s)
2571 } else {
2572 format!("{}{}", s, padding)
2573 }
2574}
2575pub(crate) fn format_fixed(n: f64, digits: usize) -> String {
2576 let neg = n < 0.0;
2577 let n = libm::fabs(n);
2578 let int_part = libm::trunc(n) as i64;
2579 let scale = libm::pow(10.0, digits as f64);
2580 let frac = libm::trunc((n - libm::trunc(n)) * scale + 0.5) as i64;
2581 let frac_str = format!("{:0width$}", frac, width = digits);
2582 format!("{}{}.{}", if neg { "-" } else { "" }, int_part, frac_str)
2583}
2584