ion/object/
regexp.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use std::ops::{Deref, DerefMut};
8use std::ptr::NonNull;
9
10use mozjs::glue::JS_GetRegExpFlags;
11use mozjs::jsapi::{
12	CheckRegExpSyntax, ExecuteRegExp, ExecuteRegExpNoStatics, GetRegExpSource, JSObject, NewUCRegExpObject,
13	ObjectIsRegExp, RegExpFlags as REFlags,
14};
15use mozjs::rooted;
16
17use crate::flags::RegExpFlags;
18use crate::{Context, Local, Object, Value};
19
20#[derive(Debug)]
21pub struct RegExp<'r> {
22	re: Local<'r, *mut JSObject>,
23}
24
25impl<'r> RegExp<'r> {
26	/// Creates a new [RegExp] object with the given source and flags.
27	pub fn new(cx: &'r Context, source: &str, flags: RegExpFlags) -> Option<RegExp<'r>> {
28		let source: Vec<u16> = source.encode_utf16().collect();
29		let regexp = unsafe { NewUCRegExpObject(cx.as_ptr(), source.as_ptr(), source.len(), flags.into()) };
30		NonNull::new(regexp).map(|re| RegExp { re: cx.root(re.as_ptr()) })
31	}
32
33	/// Creates a [RegExp] from an object.
34	/// Returns [None] if it is not a [RegExp].
35	pub fn from(cx: &Context, object: Local<'r, *mut JSObject>) -> Option<RegExp<'r>> {
36		RegExp::is_regexp(cx, &object).then_some(RegExp { re: object })
37	}
38
39	/// Creates a [RegExp] from an object.
40	///
41	/// ### Safety
42	/// Object must be a [RegExp].
43	pub unsafe fn from_unchecked(object: Local<'r, *mut JSObject>) -> RegExp<'r> {
44		RegExp { re: object }
45	}
46
47	pub fn source<'cx>(&self, cx: &'cx Context) -> crate::String<'cx> {
48		crate::String::from(cx.root(unsafe { GetRegExpSource(cx.as_ptr(), self.handle().into()) }))
49	}
50
51	pub fn flags(&self, cx: &Context) -> RegExpFlags {
52		let mut flags = REFlags { flags_: 0 };
53		unsafe {
54			JS_GetRegExpFlags(cx.as_ptr(), self.handle().into(), &raw mut flags);
55		}
56		flags.into()
57	}
58
59	pub fn to_string(&self, cx: &Context) -> crate::Result<String> {
60		Ok(format!("/{}/{}", self.source(cx).to_owned(cx)?, self.flags(cx)))
61	}
62
63	pub fn execute_test(&self, cx: &Context, string: &str, index: &mut usize) -> bool {
64		let mut rval = Value::null(cx);
65		self.execute(cx, string, index, true, &mut rval, true) && rval.handle().to_boolean()
66	}
67
68	pub fn execute_test_no_static(&self, cx: &Context, string: &str, index: &mut usize) -> bool {
69		let mut rval = Value::null(cx);
70		self.execute(cx, string, index, true, &mut rval, false) && rval.handle().to_boolean()
71	}
72
73	pub fn execute_match<'cx>(&self, cx: &'cx Context, string: &str, index: &mut usize) -> Option<Value<'cx>> {
74		let mut rval = Value::null(cx);
75		self.execute(cx, string, index, false, &mut rval, true).then_some(rval)
76	}
77
78	pub fn execute_match_no_static<'cx>(
79		&self, cx: &'cx Context, string: &str, index: &mut usize,
80	) -> Option<Value<'cx>> {
81		let mut rval = Value::null(cx);
82		self.execute(cx, string, index, false, &mut rval, false).then_some(rval)
83	}
84
85	fn execute<'cx>(
86		&self, cx: &'cx Context, string: &str, index: &mut usize, test: bool, rval: &mut Value<'cx>, with_static: bool,
87	) -> bool {
88		let string: Vec<u16> = string.encode_utf16().collect();
89		if with_static {
90			let global = Object::global(cx);
91			unsafe {
92				ExecuteRegExp(
93					cx.as_ptr(),
94					global.handle().into(),
95					self.handle().into(),
96					string.as_ptr(),
97					string.len(),
98					index,
99					test,
100					rval.handle_mut().into(),
101				)
102			}
103		} else {
104			unsafe {
105				ExecuteRegExpNoStatics(
106					cx.as_ptr(),
107					self.handle().into(),
108					string.as_ptr(),
109					string.len(),
110					index,
111					test,
112					rval.handle_mut().into(),
113				)
114			}
115		}
116	}
117
118	pub fn check_syntax<'cx>(cx: &'cx Context, source: &str, flags: RegExpFlags) -> Result<(), Value<'cx>> {
119		let source: Vec<u16> = source.encode_utf16().collect();
120		let mut error = Value::undefined(cx);
121		let check = unsafe {
122			CheckRegExpSyntax(
123				cx.as_ptr(),
124				source.as_ptr(),
125				source.len(),
126				flags.into(),
127				error.handle_mut().into(),
128			)
129		};
130		if check {
131			if error.handle().is_undefined() {
132				Ok(())
133			} else {
134				Err(error)
135			}
136		} else {
137			Err(error)
138		}
139	}
140
141	/// Checks if a [raw object](*mut JSObject) is a regexp.
142	pub fn is_regexp_raw(cx: &Context, object: *mut JSObject) -> bool {
143		rooted!(in(cx.as_ptr()) let object = object);
144		let mut is_regexp = false;
145		(unsafe { ObjectIsRegExp(cx.as_ptr(), object.handle().into(), &raw mut is_regexp) }) && is_regexp
146	}
147
148	/// Checks if an object is a regexp.
149	pub fn is_regexp(cx: &Context, object: &Local<*mut JSObject>) -> bool {
150		let mut is_regexp = false;
151		(unsafe { ObjectIsRegExp(cx.as_ptr(), object.handle().into(), &raw mut is_regexp) }) && is_regexp
152	}
153}
154
155impl<'r> Deref for RegExp<'r> {
156	type Target = Local<'r, *mut JSObject>;
157
158	fn deref(&self) -> &Self::Target {
159		&self.re
160	}
161}
162
163impl DerefMut for RegExp<'_> {
164	fn deref_mut(&mut self) -> &mut Self::Target {
165		&mut self.re
166	}
167}