1use super::*;
4
5use super::super::regex::{self, RegExpData};
8
9pub(crate) fn as_regexp(v: &Value) -> Option<PromiseRc<PromiseRefCell<RegExpData>>> {
10 if let Value::Object(o) = v {
11 let o = unwrap_proxy_target(o);
12 let b = o.borrow();
13 if let ObjKind::RegExpObj(r) = &b.kind {
14 return Some(r.clone());
15 }
16 }
17 None
18}
19
20pub(crate) fn regexp_ctor(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
21 let first = arg(a, 0);
22 let (pattern, flags) = if let Some(r) = as_regexp(&first) {
23 let d = r.borrow();
24 let f = if a.len() > 1 && !matches!(arg(a, 1), Value::Undefined) {
25 arg(a, 1).to_js_string()
26 } else {
27 d.re.flags.clone()
28 };
29 (d.re.source.clone(), f)
30 } else {
31 let p = if matches!(first, Value::Undefined) {
32 String::new()
33 } else {
34 first.to_js_string()
35 };
36 let f = if a.len() > 1 && !matches!(arg(a, 1), Value::Undefined) {
37 arg(a, 1).to_js_string()
38 } else {
39 String::new()
40 };
41 (p, f)
42 };
43 let re = regex::Regex::new(&pattern, &flags);
44 Ok(Value::Object(Obj::regexp(RegExpData { re, last_index: 0 })))
45}
46
47pub(crate) fn regexp_escape_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
52 let s = arg(a, 0).to_js_string();
53 let mut out = String::new();
54 for c in s.chars() {
55 match c {
56 '^' | '$' | '\\' | '.' | '*' | '+' | '?' | '(' | ')' | '[' | ']' | '{' | '}' | '|'
57 | '/' | '-' => {
58 out.push('\\');
59 out.push(c);
60 }
61 ' ' => out.push_str("\\x20"),
62 '\t' => out.push_str("\\t"),
63 '\n' => out.push_str("\\n"),
64 '\r' => out.push_str("\\r"),
65 '\u{000B}' => out.push_str("\\v"),
66 '\u{000C}' => out.push_str("\\f"),
67 _ => out.push(c),
68 }
69 }
70 Ok(Value::str(out))
71}
72
73pub(crate) fn css_escape_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
79 let s = arg(a, 0).to_js_string();
80 let chars: Vec<char> = s.chars().collect();
81 let mut out = String::new();
82 for (i, &c) in chars.iter().enumerate() {
83 let is_digit = c.is_ascii_digit();
84 let needs_hex_escape = c == '\u{0000}'
85 || ('\u{0001}'..='\u{001f}').contains(&c)
86 || c == '\u{007f}'
87 || (i == 0 && is_digit)
88 || (i == 1 && is_digit && chars.first() == Some(&'-'));
89 if c == '\u{0000}' {
90 out.push('\u{fffd}');
91 } else if needs_hex_escape {
92 out.push('\\');
93 out.push_str(&alloc::format!("{:x} ", c as u32));
94 } else if i == 0 && chars.len() == 1 && c == '-' {
95 out.push_str("\\-");
96 } else if c.is_ascii_alphanumeric() || c == '_' || c == '-' || (c as u32) >= 0x80 {
97 out.push(c);
98 } else {
99 out.push('\\');
100 out.push(c);
101 }
102 }
103 Ok(Value::str(out))
104}
105
106pub(crate) fn css_supports_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
112 let cond = if a.len() >= 2 {
113 alloc::format!("({}: {})", arg(a, 0).to_js_string(), arg(a, 1).to_js_string())
114 } else {
115 arg(a, 0).to_js_string()
116 };
117 Ok(Value::Bool(crate::os_lib::css::supports_condition_matches(&cond)))
118}
119
120fn make_css_unit_value(value: f64, unit: &str) -> Value {
131 let o = Obj::plain();
132 {
133 let mut b = o.borrow_mut();
134 b.props.insert("value".into(), Value::Number(value));
135 b.props.insert("unit".into(), Value::str(unit));
136 b.props
137 .insert("toString".into(), nv("toString", css_unit_value_to_string));
138 }
139 Value::Object(o)
140}
141
142fn css_unit_value_to_string(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
143 if let Value::Object(o) = &this {
144 let b = o.borrow();
145 let value = b.props.get("value").cloned().unwrap_or(Value::Number(0.0));
146 let unit = b.props.get("unit").map(|v| v.to_js_string()).unwrap_or_default();
147 let suffix = match unit.as_str() {
148 "number" => alloc::string::String::new(),
149 "percent" => alloc::string::String::from("%"),
150 other => alloc::string::String::from(other),
151 };
152 return Ok(Value::str(alloc::format!("{}{}", value.to_js_string(), suffix)));
153 }
154 Ok(Value::str(""))
155}
156
157pub(crate) fn css_number_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
158 Ok(make_css_unit_value(arg(a, 0).to_number(), "number"))
159}
160pub(crate) fn css_percent_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
161 Ok(make_css_unit_value(arg(a, 0).to_number(), "percent"))
162}
163pub(crate) fn css_px_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
164 Ok(make_css_unit_value(arg(a, 0).to_number(), "px"))
165}
166pub(crate) fn css_em_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
167 Ok(make_css_unit_value(arg(a, 0).to_number(), "em"))
168}
169pub(crate) fn css_rem_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
170 Ok(make_css_unit_value(arg(a, 0).to_number(), "rem"))
171}
172pub(crate) fn css_deg_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
173 Ok(make_css_unit_value(arg(a, 0).to_number(), "deg"))
174}
175
176pub fn regexp_get(obj: &ObjRef, key: &str) -> Value {
178 let r = match &obj.borrow().kind {
179 ObjKind::RegExpObj(r) => r.clone(),
180 _ => return Value::Undefined,
181 };
182 match key {
183 "source" => Value::str(r.borrow().re.source.clone()),
184 "flags" => Value::str(r.borrow().re.flags.clone()),
185 "global" => Value::Bool(r.borrow().re.global),
186 "ignoreCase" => Value::Bool(r.borrow().re.ignorecase),
187 "multiline" => Value::Bool(r.borrow().re.multiline),
188 "dotAll" => Value::Bool(r.borrow().re.dotall),
189 "sticky" => Value::Bool(r.borrow().re.sticky),
190 "hasIndices" => Value::Bool(r.borrow().re.has_indices),
191 "unicode" => Value::Bool(r.borrow().re.flags.contains('u')),
198 "unicodeSets" => Value::Bool(r.borrow().re.flags.contains('v')),
199 "lastIndex" => Value::Number(r.borrow().last_index as f64),
200 "test" => nv("RegExp.test", re_test),
201 "exec" => nv("RegExp.exec", re_exec),
202 "toString" => nv("RegExp.toString", re_to_string),
203 "compile" => nv("RegExp.compile", re_compile),
204 "Symbol(Symbol.match)" => nv("[Symbol.match]", re_symbol_match),
205 "Symbol(Symbol.replace)" => nv("[Symbol.replace]", re_symbol_replace),
206 "Symbol(Symbol.search)" => nv("[Symbol.search]", re_symbol_search),
207 "Symbol(Symbol.split)" => nv("[Symbol.split]", re_symbol_split),
208 "Symbol(Symbol.matchAll)" => nv("[Symbol.matchAll]", re_symbol_match_all),
209 _ => Value::Undefined,
210 }
211}
212
213pub(crate) fn re_compile(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
219 if let Some(r) = as_regexp(&this) {
220 let first = arg(a, 0);
221 let (pattern, flags) = if let Some(src) = as_regexp(&first) {
222 let d = src.borrow();
223 let f = if a.len() > 1 && !matches!(arg(a, 1), Value::Undefined) {
224 arg(a, 1).to_js_string()
225 } else {
226 d.re.flags.clone()
227 };
228 (d.re.source.clone(), f)
229 } else {
230 let p = if matches!(first, Value::Undefined) {
231 String::new()
232 } else {
233 first.to_js_string()
234 };
235 let f = if a.len() > 1 && !matches!(arg(a, 1), Value::Undefined) {
236 arg(a, 1).to_js_string()
237 } else {
238 String::new()
239 };
240 (p, f)
241 };
242 let mut d = r.borrow_mut();
243 d.re = regex::Regex::new(&pattern, &flags);
244 d.last_index = 0;
245 }
246 Ok(this)
247}
248
249pub(crate) fn match_to_array(
251 m: ®ex::Match,
252 chars: &[char],
253 input: &str,
254 group_names: &[(String, usize)],
255) -> Value {
256 match_to_array_impl(m, chars, input, false, group_names)
257}
258
259pub(crate) fn build_named_groups(group_names: &[(String, usize)], values: &[Value]) -> Value {
262 if group_names.is_empty() {
263 return Value::Undefined;
264 }
265 let g = Obj::plain();
266 for (name, idx) in group_names {
267 let v = values.get(*idx).cloned().unwrap_or(Value::Undefined);
268 g.borrow_mut().props.insert(name.clone(), v);
269 }
270 Value::Object(g)
271}
272
273pub(crate) fn match_to_array_impl(
274 m: ®ex::Match,
275 chars: &[char],
276 input: &str,
277 has_indices: bool,
278 group_names: &[(String, usize)],
279) -> Value {
280 let mut items = Vec::new();
281 for cap in &m.captures {
282 match cap {
283 Some((s, e)) => items.push(Value::str(chars[*s..*e].iter().collect::<String>())),
284 None => items.push(Value::Undefined),
285 }
286 }
287 let groups_val = build_named_groups(group_names, &items);
291 let arr = Obj::array(items);
292 arr.borrow_mut()
293 .props
294 .insert("index".into(), Value::Number(m.start as f64));
295 arr.borrow_mut()
296 .props
297 .insert("input".into(), Value::str(String::from(input)));
298 arr.borrow_mut().props.insert("groups".into(), groups_val);
299 if has_indices {
300 let indices_items: Vec<Value> = m
303 .captures
304 .iter()
305 .map(|cap| match cap {
306 Some((s, e)) => {
307 Value::Object(Obj::array(alloc::vec![
308 Value::Number(*s as f64),
309 Value::Number(*e as f64)
310 ]))
311 }
312 None => Value::Undefined,
313 })
314 .collect();
315 let indices_groups = build_named_groups(group_names, &indices_items);
316 let indices_arr = Obj::array(indices_items);
317 indices_arr.borrow_mut().props.insert("groups".into(), indices_groups);
318 arr.borrow_mut()
319 .props
320 .insert("indices".into(), Value::Object(indices_arr));
321 }
322 Value::Object(arr)
323}
324
325pub(crate) fn re_test(_: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
326 let r = match as_regexp(&this) {
327 Some(r) => r,
328 None => return Ok(Value::Bool(false)),
329 };
330 let input = arg(a, 0).to_js_string();
331 let chars: Vec<char> = input.chars().collect();
332 let sticky = r.borrow().re.sticky;
333 let uses_last_index = r.borrow().re.global || sticky;
334 let start = if uses_last_index {
335 r.borrow().last_index.min(chars.len())
336 } else {
337 0
338 };
339 let found = if sticky {
340 r.borrow().re.find_exact_at(&chars, start)
341 } else {
342 r.borrow().re.find_at(&chars, start)
343 };
344 match found {
345 Some(m) => {
346 if uses_last_index {
347 r.borrow_mut().last_index = m.end;
348 }
349 Ok(Value::Bool(true))
350 }
351 None => {
352 if uses_last_index {
353 r.borrow_mut().last_index = 0;
354 }
355 Ok(Value::Bool(false))
356 }
357 }
358}
359
360pub(crate) fn re_exec(_: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
361 let r = match as_regexp(&this) {
362 Some(r) => r,
363 None => return Ok(Value::Null),
364 };
365 let input = arg(a, 0).to_js_string();
366 let chars: Vec<char> = input.chars().collect();
367 let sticky = r.borrow().re.sticky;
368 let uses_last_index = r.borrow().re.global || sticky;
369 let start = if uses_last_index {
370 r.borrow().last_index.min(chars.len())
371 } else {
372 0
373 };
374 let found = if sticky {
375 r.borrow().re.find_exact_at(&chars, start)
376 } else {
377 r.borrow().re.find_at(&chars, start)
378 };
379 match found {
380 Some(m) => {
381 if uses_last_index {
382 r.borrow_mut().last_index = if m.end > m.start { m.end } else { m.end + 1 };
383 }
384 let has_indices = r.borrow().re.has_indices;
385 let group_names = r.borrow().re.group_names.clone();
386 Ok(match_to_array_impl(&m, &chars, &input, has_indices, &group_names))
387 }
388 None => {
389 if uses_last_index {
390 r.borrow_mut().last_index = 0;
391 }
392 Ok(Value::Null)
393 }
394 }
395}
396
397pub(crate) fn re_to_string(_: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
398 Ok(Value::str(this.to_js_string()))
399}
400
401pub(crate) fn expand_dollar(
406 tpl: &str,
407 m: ®ex::Match,
408 chars: &[char],
409 group_names: &[(String, usize)],
410) -> String {
411 let t: Vec<char> = tpl.chars().collect();
412 let mut out = String::new();
413 let mut i = 0;
414 while i < t.len() {
415 if t[i] == '$' && i + 1 < t.len() {
416 let n = t[i + 1];
417 if n == '$' {
418 out.push('$');
419 i += 2;
420 continue;
421 }
422 if n == '&' {
423 out.push_str(&chars[m.start..m.end].iter().collect::<String>());
424 i += 2;
425 continue;
426 }
427 if n == '`' {
428 out.push_str(&chars[..m.start].iter().collect::<String>());
429 i += 2;
430 continue;
431 }
432 if n == '\'' {
433 out.push_str(&chars[m.end..].iter().collect::<String>());
434 i += 2;
435 continue;
436 }
437 if n == '<' {
438 if let Some(close) = t[i + 2..].iter().position(|&c| c == '>') {
439 let name: String = t[i + 2..i + 2 + close].iter().collect();
440 if let Some((_, gi)) = group_names.iter().find(|(gn, _)| *gn == name) {
441 if let Some(Some((s, e))) = m.captures.get(*gi) {
442 out.push_str(&chars[*s..*e].iter().collect::<String>());
443 }
444 }
445 i += 2 + close + 1;
446 continue;
447 }
448 }
449 if n.is_ascii_digit() {
450 let two_digit = if i + 2 < t.len() && t[i + 2].is_ascii_digit() {
452 let gi2: usize = format!("{}{}", n, t[i + 2]).parse().unwrap_or(0);
453 if gi2 > 0 && m.captures.get(gi2).is_some() {
454 Some((gi2, 3))
455 } else {
456 None
457 }
458 } else {
459 None
460 };
461 let (gi, adv) = two_digit.unwrap_or((n.to_digit(10).unwrap_or(0) as usize, 2));
462 if let Some(Some((s, e))) = m.captures.get(gi) {
463 out.push_str(&chars[*s..*e].iter().collect::<String>());
464 }
465 i += adv;
466 continue;
467 }
468 }
469 out.push(t[i]);
470 i += 1;
471 }
472 out
473}
474
475pub(crate) fn regex_replace(
477 it: &mut Interp,
478 s: &str,
479 r: &PromiseRc<PromiseRefCell<RegExpData>>,
480 repl: Value,
481 global: bool,
482) -> Result<String, Value> {
483 let chars: Vec<char> = s.chars().collect();
484 let replacer_fn = callable_or_none(repl.clone());
485 let group_names = r.borrow().re.group_names.clone();
486 let mut out = String::new();
487 let mut pos = 0usize;
488 loop {
489 let found = r.borrow().re.find_at(&chars, pos);
490 let m = match found {
491 Some(m) => m,
492 None => break,
493 };
494 out.push_str(&chars[pos..m.start].iter().collect::<String>());
495 let rep = if let Some(f) = &replacer_fn {
496 let mut args: Vec<Value> = Vec::new();
497 for cap in &m.captures {
498 match cap {
499 Some((cs, ce)) => {
500 args.push(Value::str(chars[*cs..*ce].iter().collect::<String>()))
501 }
502 None => args.push(Value::Undefined),
503 }
504 }
505 args.push(Value::Number(m.start as f64));
506 args.push(Value::str(String::from(s)));
507 if !group_names.is_empty() {
510 args.push(build_named_groups(&group_names, &args[..m.captures.len()]));
511 }
512 it.call_value(f, Value::Undefined, &args)?.to_js_string()
513 } else {
514 expand_dollar(&repl.to_js_string(), &m, &chars, &group_names)
515 };
516 out.push_str(&rep);
517 if m.end > m.start {
519 pos = m.end;
520 } else {
521 if m.start < chars.len() {
522 out.push(chars[m.start]);
523 }
524 pos = m.start + 1;
525 }
526 if !global || pos > chars.len() {
527 break;
528 }
529 }
530 out.push_str(&chars[pos.min(chars.len())..].iter().collect::<String>());
531 Ok(out)
532}
533
534pub(crate) fn str_match(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
535 let rx = arg(a, 0);
536 if let Value::Object(_) = &rx {
537 let matcher = it.get_property(&rx, "Symbol(Symbol.match)")?;
538 if !matches!(matcher, Value::Undefined) {
539 return it.call_value(&matcher, rx.clone(), &[t]);
540 }
541 }
542 let s = this_str(&t);
543 let chars: Vec<char> = s.chars().collect();
544 let r = match as_regexp(&arg(a, 0)) {
545 Some(r) => r,
546 None => {
547 let re = regex::Regex::new(&arg(a, 0).to_js_string(), "");
549 PromiseRc::new(PromiseRefCell::new(RegExpData { re, last_index: 0 }))
550 }
551 };
552 if r.borrow().re.global {
553 let mut items = Vec::new();
555 let mut pos = 0;
556 while let Some(m) = r.borrow().re.find_at(&chars, pos) {
557 items.push(Value::str(chars[m.start..m.end].iter().collect::<String>()));
558 pos = if m.end > m.start { m.end } else { m.end + 1 };
559 if pos > chars.len() {
560 break;
561 }
562 }
563 if items.is_empty() {
564 return Ok(Value::Null);
565 }
566 return Ok(Value::Object(Obj::array(items)));
567 }
568 let found = r.borrow().re.find_at(&chars, 0);
569 match found {
570 Some(m) => {
571 let group_names = r.borrow().re.group_names.clone();
572 Ok(match_to_array(&m, &chars, &s, &group_names))
573 }
574 None => Ok(Value::Null),
575 }
576}
577
578pub(crate) fn str_match_all(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
582 let rx = arg(a, 0);
585 if let Some(r) = as_regexp(&rx) {
586 if !r.borrow().re.global {
587 return Err(it.error(
588 "String.prototype.matchAll called with a non-global RegExp argument",
589 ));
590 }
591 }
592 if let Value::Object(_) = &rx {
596 let matcher = it.get_property(&rx, "Symbol(Symbol.matchAll)")?;
597 if !matches!(matcher, Value::Undefined) {
598 return it.call_value(&matcher, rx.clone(), &[t]);
599 }
600 }
601 let s = this_str(&t);
602 let chars: Vec<char> = s.chars().collect();
603 let r = match as_regexp(&arg(a, 0)) {
604 Some(r) => {
605 if !r.borrow().re.global {
606 return Err(it.error(
607 "String.prototype.matchAll called with a non-global RegExp argument",
608 ));
609 }
610 r
611 }
612 None => {
613 let re = regex::Regex::new(&arg(a, 0).to_js_string(), "g");
614 PromiseRc::new(PromiseRefCell::new(RegExpData { re, last_index: 0 }))
615 }
616 };
617 let group_names = r.borrow().re.group_names.clone();
618 let mut items = Vec::new();
619 let mut pos = 0;
620 while let Some(m) = r.borrow().re.find_at(&chars, pos) {
621 items.push(match_to_array(&m, &chars, &s, &group_names));
622 pos = if m.end > m.start { m.end } else { m.end + 1 };
623 if pos > chars.len() {
624 break;
625 }
626 }
627 Ok(Value::Object(Obj::array(items)))
628}
629
630pub(crate) fn str_search(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
631 let rx = arg(a, 0);
632 if let Value::Object(_) = &rx {
633 let matcher = it.get_property(&rx, "Symbol(Symbol.search)")?;
634 if !matches!(matcher, Value::Undefined) {
635 return it.call_value(&matcher, rx.clone(), &[t]);
636 }
637 }
638 let s = this_str(&t);
639 let chars: Vec<char> = s.chars().collect();
640 let r = match as_regexp(&arg(a, 0)) {
641 Some(r) => r,
642 None => {
643 let re = regex::Regex::new(&arg(a, 0).to_js_string(), "");
644 PromiseRc::new(PromiseRefCell::new(RegExpData { re, last_index: 0 }))
645 }
646 };
647 let found = r.borrow().re.find_at(&chars, 0);
648 match found {
649 Some(m) => Ok(Value::Number(m.start as f64)),
650 None => Ok(Value::Number(-1.0)),
651 }
652}
653
654pub(crate) fn re_symbol_match(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
656 let r = match as_regexp(&this) {
657 Some(r) => r,
658 None => return Err(it.error("RegExp.prototype[Symbol.match] called on non-RegExp")),
659 };
660 let s = arg(a, 0).to_js_string();
661 let chars: Vec<char> = s.chars().collect();
662 if r.borrow().re.global {
663 let mut items = Vec::new();
664 let mut pos = 0;
665 while let Some(m) = r.borrow().re.find_at(&chars, pos) {
666 items.push(Value::str(chars[m.start..m.end].iter().collect::<String>()));
667 pos = if m.end > m.start { m.end } else { m.end + 1 };
668 if pos > chars.len() {
669 break;
670 }
671 }
672 if items.is_empty() {
673 return Ok(Value::Null);
674 }
675 return Ok(Value::Object(Obj::array(items)));
676 }
677 let found = r.borrow().re.find_at(&chars, 0);
678 match found {
679 Some(m) => {
680 let group_names = r.borrow().re.group_names.clone();
681 Ok(match_to_array(&m, &chars, &s, &group_names))
682 }
683 None => Ok(Value::Null),
684 }
685}
686
687pub(crate) fn re_symbol_replace(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
689 let r = match as_regexp(&this) {
690 Some(r) => r,
691 None => return Err(it.error("RegExp.prototype[Symbol.replace] called on non-RegExp")),
692 };
693 let s = arg(a, 0).to_js_string();
694 let repl = arg(a, 1);
695 let global = r.borrow().re.global;
696 Ok(Value::str(regex_replace(it, &s, &r, repl, global)?))
697}
698
699pub(crate) fn re_symbol_search(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
701 let r = match as_regexp(&this) {
702 Some(r) => r,
703 None => return Err(it.error("RegExp.prototype[Symbol.search] called on non-RegExp")),
704 };
705 let s = arg(a, 0).to_js_string();
706 let chars: Vec<char> = s.chars().collect();
707 let found = r.borrow().re.find_at(&chars, 0);
708 match found {
709 Some(m) => Ok(Value::Number(m.start as f64)),
710 None => Ok(Value::Number(-1.0)),
711 }
712}
713
714pub(crate) fn re_symbol_split(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
716 let r = match as_regexp(&this) {
717 Some(r) => r,
718 None => return Err(it.error("RegExp.prototype[Symbol.split] called on non-RegExp")),
719 };
720 let s = arg(a, 0).to_js_string();
721 let limit = match arg(a, 1) {
722 Value::Undefined => usize::MAX,
723 v => {
724 let n = v.to_number();
725 if n.is_nan() || n < 0.0 {
726 usize::MAX
727 } else {
728 n as usize
729 }
730 }
731 };
732 let chars: Vec<char> = s.chars().collect();
733 let mut items = Vec::new();
734 let mut last = 0usize;
735 let mut pos = 0usize;
736 while pos <= chars.len() && items.len() < limit {
737 match r.borrow().re.find_at(&chars, pos) {
738 Some(m) if m.end > m.start => {
739 items.push(Value::str(chars[last..m.start].iter().collect::<String>()));
740 for cap in m.captures.iter().skip(1) {
748 if items.len() >= limit { break; }
749 match cap {
750 Some((cs, ce)) => items.push(Value::str(chars[*cs..*ce].iter().collect::<String>())),
751 None => items.push(Value::Undefined),
752 }
753 }
754 last = m.end;
755 pos = m.end;
756 }
757 Some(m) => {
758 pos = m.start + 1;
759 }
760 None => break,
761 }
762 }
763 if items.len() < limit {
764 items.push(Value::str(chars[last..].iter().collect::<String>()));
765 }
766 items.truncate(limit);
767 Ok(Value::Object(Obj::array(items)))
768}
769
770pub(crate) fn re_symbol_match_all(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
772 let r = match as_regexp(&this) {
773 Some(r) => r,
774 None => return Err(it.error("RegExp.prototype[Symbol.matchAll] called on non-RegExp")),
775 };
776 if !r.borrow().re.global {
777 return Err(it.error("RegExp.prototype[Symbol.matchAll] called with a non-global RegExp argument"));
778 }
779 let s = arg(a, 0).to_js_string();
780 let chars: Vec<char> = s.chars().collect();
781 let group_names = r.borrow().re.group_names.clone();
782 let mut items = Vec::new();
783 let mut pos = 0;
784 while let Some(m) = r.borrow().re.find_at(&chars, pos) {
785 items.push(match_to_array(&m, &chars, &s, &group_names));
786 pos = if m.end > m.start { m.end } else { m.end + 1 };
787 if pos > chars.len() {
788 break;
789 }
790 }
791 Ok(Value::Object(Obj::array(items)))
792}
793
794