|
|
@ -17,7 +17,7 @@ use crate::{ |
|
|
|
value::{RcString, Value}, |
|
|
|
value::{RcString, Value}, |
|
|
|
BoaProfiler, Context, Result, |
|
|
|
BoaProfiler, Context, Result, |
|
|
|
}; |
|
|
|
}; |
|
|
|
use regress::{Flags, Regex}; |
|
|
|
use regress::Regex; |
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
#[cfg(test)] |
|
|
|
mod tests; |
|
|
|
mod tests; |
|
|
@ -32,7 +32,7 @@ pub struct RegExp { |
|
|
|
use_last_index: bool, |
|
|
|
use_last_index: bool, |
|
|
|
|
|
|
|
|
|
|
|
/// String of parsed flags.
|
|
|
|
/// String of parsed flags.
|
|
|
|
flags: String, |
|
|
|
flags: Box<str>, |
|
|
|
|
|
|
|
|
|
|
|
/// Flag 's' - dot matches newline characters.
|
|
|
|
/// Flag 's' - dot matches newline characters.
|
|
|
|
dot_all: bool, |
|
|
|
dot_all: bool, |
|
|
@ -52,10 +52,11 @@ pub struct RegExp { |
|
|
|
/// Flag 'u' - Unicode.
|
|
|
|
/// Flag 'u' - Unicode.
|
|
|
|
unicode: bool, |
|
|
|
unicode: bool, |
|
|
|
|
|
|
|
|
|
|
|
pub(crate) original_source: String, |
|
|
|
pub(crate) original_source: Box<str>, |
|
|
|
original_flags: String, |
|
|
|
original_flags: Box<str>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Only safe while regress::Regex doesn't implement Trace itself.
|
|
|
|
unsafe impl Trace for RegExp { |
|
|
|
unsafe impl Trace for RegExp { |
|
|
|
empty_trace!(); |
|
|
|
empty_trace!(); |
|
|
|
} |
|
|
|
} |
|
|
@ -99,26 +100,32 @@ impl RegExp { |
|
|
|
/// Create a new `RegExp`
|
|
|
|
/// Create a new `RegExp`
|
|
|
|
pub(crate) fn constructor(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> { |
|
|
|
pub(crate) fn constructor(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> { |
|
|
|
let arg = args.get(0).ok_or_else(Value::undefined)?; |
|
|
|
let arg = args.get(0).ok_or_else(Value::undefined)?; |
|
|
|
let mut regex_body = String::new(); |
|
|
|
|
|
|
|
let mut regex_flags = String::new(); |
|
|
|
let (regex_body, mut regex_flags) = match arg { |
|
|
|
match arg { |
|
|
|
|
|
|
|
Value::String(ref body) => { |
|
|
|
Value::String(ref body) => { |
|
|
|
// first argument is a string -> use it as regex pattern
|
|
|
|
// first argument is a string -> use it as regex pattern
|
|
|
|
regex_body = body.to_string(); |
|
|
|
( |
|
|
|
|
|
|
|
body.to_string().into_boxed_str(), |
|
|
|
|
|
|
|
String::new().into_boxed_str(), |
|
|
|
|
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
Value::Object(ref obj) => { |
|
|
|
Value::Object(ref obj) => { |
|
|
|
let obj = obj.borrow(); |
|
|
|
let obj = obj.borrow(); |
|
|
|
if let Some(regex) = obj.as_regexp() { |
|
|
|
if let Some(regex) = obj.as_regexp() { |
|
|
|
// first argument is another `RegExp` object, so copy its pattern and flags
|
|
|
|
// first argument is another `RegExp` object, so copy its pattern and flags
|
|
|
|
regex_body = regex.original_source.clone(); |
|
|
|
(regex.original_source.clone(), regex.original_flags.clone()) |
|
|
|
regex_flags = regex.original_flags.clone(); |
|
|
|
} else { |
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
String::new().into_boxed_str(), |
|
|
|
|
|
|
|
String::new().into_boxed_str(), |
|
|
|
|
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
_ => return Err(Value::undefined()), |
|
|
|
_ => return Err(Value::undefined()), |
|
|
|
} |
|
|
|
}; |
|
|
|
// if a second argument is given and it's a string, use it as flags
|
|
|
|
// if a second argument is given and it's a string, use it as flags
|
|
|
|
if let Some(Value::String(flags)) = args.get(1) { |
|
|
|
if let Some(Value::String(flags)) = args.get(1) { |
|
|
|
regex_flags = flags.to_string(); |
|
|
|
regex_flags = flags.to_string().into_boxed_str(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// parse flags
|
|
|
|
// parse flags
|
|
|
@ -154,12 +161,12 @@ impl RegExp { |
|
|
|
sorted_flags.push('y'); |
|
|
|
sorted_flags.push('y'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let matcher = Regex::newf(regex_body.as_str(), Flags::from(sorted_flags.as_str())) |
|
|
|
let matcher = Regex::with_flags(®ex_body, sorted_flags.as_str()) |
|
|
|
.expect("failed to create matcher"); |
|
|
|
.expect("failed to create matcher"); |
|
|
|
let regexp = RegExp { |
|
|
|
let regexp = RegExp { |
|
|
|
matcher, |
|
|
|
matcher, |
|
|
|
use_last_index: global || sticky, |
|
|
|
use_last_index: global || sticky, |
|
|
|
flags: sorted_flags, |
|
|
|
flags: sorted_flags.into_boxed_str(), |
|
|
|
dot_all, |
|
|
|
dot_all, |
|
|
|
global, |
|
|
|
global, |
|
|
|
ignore_case, |
|
|
|
ignore_case, |
|
|
@ -314,7 +321,7 @@ impl RegExp { |
|
|
|
let result = |
|
|
|
let result = |
|
|
|
if let Some(m) = regex.matcher.find_from(arg_str.as_str(), last_index).next() { |
|
|
|
if let Some(m) = regex.matcher.find_from(arg_str.as_str(), last_index).next() { |
|
|
|
if regex.use_last_index { |
|
|
|
if regex.use_last_index { |
|
|
|
last_index = m.total().end; |
|
|
|
last_index = m.end(); |
|
|
|
} |
|
|
|
} |
|
|
|
true |
|
|
|
true |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -355,7 +362,7 @@ impl RegExp { |
|
|
|
let result = { |
|
|
|
let result = { |
|
|
|
if let Some(m) = regex.matcher.find_from(arg_str.as_str(), last_index).next() { |
|
|
|
if let Some(m) = regex.matcher.find_from(arg_str.as_str(), last_index).next() { |
|
|
|
if regex.use_last_index { |
|
|
|
if regex.use_last_index { |
|
|
|
last_index = m.total().end; |
|
|
|
last_index = m.end(); |
|
|
|
} |
|
|
|
} |
|
|
|
let groups = m.captures.len() + 1; |
|
|
|
let groups = m.captures.len() + 1; |
|
|
|
let mut result = Vec::with_capacity(groups); |
|
|
|
let mut result = Vec::with_capacity(groups); |
|
|
@ -370,10 +377,7 @@ impl RegExp { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let result = Value::from(result); |
|
|
|
let result = Value::from(result); |
|
|
|
result.set_property( |
|
|
|
result.set_property("index", DataDescriptor::new(m.start(), Attribute::all())); |
|
|
|
"index", |
|
|
|
|
|
|
|
DataDescriptor::new(m.total().start, Attribute::all()), |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
result.set_property("input", DataDescriptor::new(arg_str, Attribute::all())); |
|
|
|
result.set_property("input", DataDescriptor::new(arg_str, Attribute::all())); |
|
|
|
result |
|
|
|
result |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -412,7 +416,7 @@ impl RegExp { |
|
|
|
if flags.contains('g') { |
|
|
|
if flags.contains('g') { |
|
|
|
let mut matches = Vec::new(); |
|
|
|
let mut matches = Vec::new(); |
|
|
|
for mat in matcher.find_iter(&arg) { |
|
|
|
for mat in matcher.find_iter(&arg) { |
|
|
|
matches.push(Value::from(&arg[mat.total()])); |
|
|
|
matches.push(Value::from(&arg[mat.range()])); |
|
|
|
} |
|
|
|
} |
|
|
|
if matches.is_empty() { |
|
|
|
if matches.is_empty() { |
|
|
|
return Ok(Value::null()); |
|
|
|
return Ok(Value::null()); |
|
|
@ -476,10 +480,7 @@ impl RegExp { |
|
|
|
|
|
|
|
|
|
|
|
let match_val = Value::from(match_vec); |
|
|
|
let match_val = Value::from(match_vec); |
|
|
|
|
|
|
|
|
|
|
|
match_val.set_property( |
|
|
|
match_val.set_property("index", DataDescriptor::new(mat.start(), Attribute::all())); |
|
|
|
"index", |
|
|
|
|
|
|
|
DataDescriptor::new(mat.total().start, Attribute::all()), |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
match_val.set_property( |
|
|
|
match_val.set_property( |
|
|
|
"input", |
|
|
|
"input", |
|
|
|
DataDescriptor::new(arg_str.clone(), Attribute::all()), |
|
|
|
DataDescriptor::new(arg_str.clone(), Attribute::all()), |
|
|
|