use oxc_ast::ast::RegExpFlags;
use regex::bytes::{Regex, RegexBuilder};
use wtf8::Wtf8Buf;
use crate::{
ecmascript::{OrdinaryObject, PropertyDescriptor, String, Value, execution::Agent},
engine::bindable_handle,
heap::{CompactionLists, HeapMarkAndSweep, WorkQueues},
};
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub(crate) struct RegExpLastIndex(u32);
impl Default for RegExpLastIndex {
fn default() -> Self {
Self(u32::MAX)
}
}
impl RegExpLastIndex {
pub(crate) const ZERO: Self = Self(0);
const INVALID: Self = Self(u32::MAX);
pub(super) fn from_value(value: Value) -> Self {
let Value::Integer(value) = value else {
return Self::INVALID;
};
let value = value.into_i64();
if let Ok(value) = u32::try_from(value) {
if value == u32::MAX {
Self::INVALID
} else {
Self(value)
}
} else {
Self::INVALID
}
}
#[inline]
pub(super) fn is_valid(self) -> bool {
self.0 != u32::MAX
}
pub(super) fn get_value(self) -> Option<u32> {
if self.0 == u32::MAX {
None
} else {
Some(self.0)
}
}
pub(super) fn into_property_descriptor(self) -> PropertyDescriptor<'static> {
PropertyDescriptor {
value: Some(self.get_value().map_or(Value::Undefined, |i| i.into())),
writable: Some(true),
get: None,
set: None,
enumerable: Some(false),
configurable: Some(false),
}
}
}
impl From<usize> for RegExpLastIndex {
fn from(value: usize) -> Self {
if let Ok(value) = u32::try_from(value) {
if value == u32::MAX {
Self::INVALID
} else {
Self(value)
}
} else {
Self::INVALID
}
}
}
#[derive(Debug)]
pub(crate) struct RegExpHeapData<'a> {
pub(super) object_index: Option<OrdinaryObject<'a>>,
pub(super) reg_exp_matcher: Result<Regex, regex::Error>,
pub(super) original_source: String<'a>,
pub(super) original_flags: RegExpFlags,
pub(super) last_index: RegExpLastIndex,
}
impl<'a> RegExpHeapData<'a> {
pub(crate) fn compile_pattern(
pattern: &str,
flags: RegExpFlags,
) -> Result<Regex, regex::Error> {
RegexBuilder::new(pattern)
.dot_matches_new_line((flags & RegExpFlags::M).bits() > 0)
.case_insensitive((flags & RegExpFlags::I).bits() > 0)
.unicode(true)
.dot_matches_new_line((flags & RegExpFlags::S).bits() > 0)
.octal(false) .build()
}
pub(crate) fn new(agent: &Agent, source: String<'a>, flags: RegExpFlags) -> Self {
let str = source.to_string_lossy_(agent);
let regex = Self::compile_pattern(&str, flags);
Self {
object_index: None,
reg_exp_matcher: regex,
original_source: source,
original_flags: flags,
last_index: RegExpLastIndex::ZERO,
}
}
pub(super) fn create_regexp_string(&self, agent: &Agent) -> Wtf8Buf {
let string_length = self.original_source.len_(agent);
let flags_length = self.original_flags.bits().count_ones();
let mut regexp_string =
Wtf8Buf::with_capacity(1 + string_length + 1 + flags_length as usize);
regexp_string.push_char('/');
regexp_string.push_wtf8(self.original_source.as_wtf8_(agent));
regexp_string.push_char('/');
self.original_flags.iter_names().for_each(|(flag, _)| {
regexp_string.push_str(flag);
});
regexp_string
}
}
impl Default for RegExpHeapData<'_> {
fn default() -> Self {
Self {
object_index: Default::default(),
reg_exp_matcher: Err(regex::Error::CompiledTooBig(usize::MAX)),
original_source: String::EMPTY_STRING,
original_flags: RegExpFlags::empty(),
last_index: Default::default(),
}
}
}
bindable_handle!(RegExpHeapData);
impl HeapMarkAndSweep for RegExpHeapData<'static> {
fn mark_values(&self, queues: &mut WorkQueues) {
let Self {
object_index,
reg_exp_matcher: _,
original_source,
original_flags: _,
last_index: _,
} = self;
object_index.mark_values(queues);
original_source.mark_values(queues);
}
fn sweep_values(&mut self, compactions: &CompactionLists) {
let Self {
object_index,
reg_exp_matcher: _,
original_source,
original_flags: _,
last_index: _,
} = self;
object_index.sweep_values(compactions);
original_source.sweep_values(compactions);
}
}