1#![no_std]
13#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
14#![warn(unused_import_braces)]
15#![cfg_attr(
16 feature = "cargo-clippy",
17 allow(clippy::new_without_default, clippy::new_without_default)
18)]
19#![cfg_attr(
20 feature = "cargo-clippy",
21 warn(
22 clippy::float_arithmetic,
23 clippy::mut_mut,
24 clippy::nonminimal_bool,
25 clippy::map_unwrap_or,
26 clippy::print_stdout,
27 )
28)]
29
30#[allow(non_upper_case_globals)]
31#[allow(non_camel_case_types)]
32#[allow(non_snake_case)]
33#[allow(dead_code)]
34#[allow(missing_docs)]
35#[allow(clippy::all)]
36mod sys {
37 include!(concat!(env!("OUT_DIR"), "/mjs.rs"));
38}
39
40use cstr_core::CStr;
41pub use sys::mjs;
43use sys::*;
44
45#[derive(Clone)]
47pub struct VM {
48 inner: *mut mjs,
49}
50
51pub struct Val {
53 vm: VM,
54 inner: mjs_val_t,
55}
56
57#[derive(Debug)]
59pub enum JSError<'a> {
60 NonNullTerminatedString,
62 NotAFunction,
64 TooManyArgs,
66 VMError(&'a str),
68}
69
70impl<'a> core::fmt::Display for JSError<'a> {
71 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
72 write!(f, "{:?}", self)
73 }
74}
75
76impl VM {
77 fn get_error<'a>(&mut self, err: mjs_err_t) -> JSError<'a> {
78 unsafe {
79 let msg = mjs_strerror(self.inner, err);
80 JSError::VMError(CStr::from_ptr(msg).to_str().unwrap())
81 }
82 }
83
84 pub fn create() -> VM {
86 VM {
87 inner: unsafe { mjs_create() },
88 }
89 }
90
91 pub fn destroy(self) {
93 unsafe { mjs_destroy(self.inner) }
94 }
95
96 pub fn from_inner(mjs: *mut mjs) -> VM {
98 VM { inner: mjs }
99 }
100
101 fn get_inner(&self) -> *mut mjs {
102 self.inner
103 }
104
105 pub fn exec(&mut self, source: &[u8]) -> Result<Val, JSError> {
107 if !matches!(source.last(), Some(0)) {
108 return Err(JSError::NonNullTerminatedString);
109 }
110 let mut ret: mjs_val_t = 0;
111 let err = unsafe { mjs_exec(self.inner, source.as_ptr() as _, &mut ret) };
112 if err != mjs_err_MJS_OK {
113 Err(self.get_error(err))
114 } else {
115 Ok(self.val(ret))
116 }
117 }
118
119 pub fn global(&mut self) -> Val {
121 self.val(unsafe { mjs_get_global(self.inner) })
122 }
123
124 pub fn this(&mut self) -> Val {
126 self.val(unsafe { mjs_get_this(self.inner) })
127 }
128
129 pub fn nargs(&mut self) -> i32 {
131 unsafe { mjs_nargs(self.inner) }
132 }
133
134 pub fn arg(&mut self, i: i32) -> Option<Val> {
136 unsafe {
137 let val = mjs_arg(self.inner, i);
138 if mjs_is_undefined(val) > 0 {
139 None
140 } else {
141 Some(self.val(val))
142 }
143 }
144 }
145
146 pub fn make_undefined(&mut self) -> Val {
148 self.val(unsafe { mjs_mk_undefined() })
149 }
150
151 #[allow(clippy::not_unsafe_ptr_arg_deref)]
153 pub fn make_foreign(&mut self, f: *mut cty::c_void) -> Val {
154 self.val(unsafe { mjs_mk_foreign(self.inner, f) })
155 }
156
157 pub fn make_number(&mut self, number: f64) -> Val {
159 self.val(unsafe { mjs_mk_number(self.inner, number) })
160 }
161
162 pub fn make_boolean(&mut self, b: bool) -> Val {
164 self.val(unsafe { mjs_mk_boolean(self.inner, if b { 1 } else { 0 }) })
165 }
166
167 pub fn make_object(&mut self) -> Val {
169 self.val(unsafe { mjs_mk_object(self.inner) })
170 }
171
172 pub fn make_string(&mut self, bytes: &'static [u8]) -> Result<Val, JSError> {
175 let s = unsafe { mjs_mk_string(self.inner, bytes.as_ptr() as _, bytes.len() as _, 0) };
176 Ok(self.val(s))
177 }
178
179 pub fn make_string_copy(&mut self, bytes: &[u8]) -> Result<Val, JSError> {
182 let s = unsafe { mjs_mk_string(self.inner, bytes.as_ptr() as _, bytes.len() as _, 1) };
183 Ok(self.val(s))
184 }
185
186 fn val(&self, val: mjs_val_t) -> Val {
187 Val {
188 vm: self.clone(),
189 inner: val,
190 }
191 }
192}
193
194impl Val {
195 pub fn own(&self) {
198 unsafe {
199 mjs_own(self.vm.get_inner(), &self.inner as *const mjs_val_t as _);
200 }
201 }
202
203 pub fn disown(&self) {
205 unsafe {
206 mjs_disown(self.vm.get_inner(), &self.inner as *const mjs_val_t as _);
207 }
208 }
209
210 pub fn is_number(&self) -> bool {
212 unsafe { mjs_is_number(self.inner) > 0 }
213 }
214
215 pub fn is_object(&self) -> bool {
217 unsafe { mjs_is_object(self.inner) > 0 }
218 }
219
220 pub fn is_string(&self) -> bool {
222 unsafe { mjs_is_string(self.inner) > 0 }
223 }
224
225 pub fn is_function(&self) -> bool {
227 unsafe { mjs_is_function(self.inner) > 0 }
228 }
229
230 pub fn is_foreign(&self) -> bool {
232 unsafe { mjs_is_foreign(self.inner) > 0 }
233 }
234
235 pub fn as_int(&self) -> Option<i32> {
237 if self.is_number() {
238 Some(unsafe { mjs_get_int(self.vm.get_inner(), self.inner) })
239 } else {
240 None
241 }
242 }
243
244 pub fn as_double(&self) -> Option<f64> {
246 if self.is_number() {
247 Some(unsafe { mjs_get_double(self.vm.get_inner(), self.inner) })
248 } else {
249 None
250 }
251 }
252
253 pub fn as_bytes(&self) -> Option<&[u8]> {
255 if self.is_string() {
256 let mut len = 0;
257 let ptr: *const mjs_val_t = &self.inner;
258 unsafe {
259 let ptr = mjs_get_string(self.vm.get_inner(), ptr as _, &mut len);
260 let slice = core::slice::from_raw_parts(ptr, len as _);
261 let slice = &*(slice as *const _ as *const [u8]);
262 Some(slice)
263 }
264 } else {
265 None
266 }
267 }
268
269 pub fn as_str(&self) -> Option<Result<&str, core::str::Utf8Error>> {
271 self.as_bytes().map(|b| core::str::from_utf8(b))
272 }
273
274 pub fn as_ptr(&self) -> Option<*const cty::c_void> {
276 if self.is_foreign() {
277 Some(unsafe { mjs_get_ptr(self.vm.get_inner(), self.inner) as _ })
278 } else {
279 None
280 }
281 }
282
283 pub fn delete(&self, name: &[u8]) {
285 unsafe {
286 mjs_del(
287 self.vm.get_inner(),
288 self.inner,
289 name.as_ptr() as _,
290 name.len() as _,
291 )
292 };
293 }
294
295 pub fn set(&mut self, name: &[u8], val: Val) -> Result<(), JSError> {
297 let err = unsafe {
298 mjs_set(
299 self.vm.get_inner(),
300 self.inner,
301 name.as_ptr() as _,
302 name.len() as _,
303 val.inner,
304 )
305 };
306 if err != mjs_err_MJS_OK {
307 Err(self.vm.get_error(err))
308 } else {
309 Ok(())
310 }
311 }
312
313 pub fn get(&self, name: &[u8]) -> Option<Val> {
315 let val = unsafe {
316 mjs_get(
317 self.vm.get_inner(),
318 self.inner,
319 name.as_ptr() as _,
320 name.len() as _,
321 )
322 };
323 if unsafe { mjs_is_undefined(val) } > 0 {
324 None
325 } else {
326 Some(Val {
327 vm: self.vm.clone(),
328 inner: val,
329 })
330 }
331 }
332
333 pub fn call(&mut self, this: Option<Val>, args: &[&Val]) -> Result<Val, JSError> {
335 if self.is_function() {
336 let mut ret: mjs_val_t = 0;
337
338 let this = this.unwrap_or_else(|| self.vm.make_undefined());
339
340 let mut mjs_args: [mjs_val_t; 8] = [0; 8];
341
342 if args.len() > mjs_args.len() {
343 return Err(JSError::TooManyArgs);
344 }
345
346 for (i, val) in args.iter().enumerate() {
347 mjs_args[i] = val.inner;
348 }
349
350 let err = unsafe {
351 mjs_apply(
352 self.vm.get_inner(),
353 &mut ret,
354 self.inner,
355 this.inner,
356 args.len() as _,
357 mjs_args.as_mut_ptr(),
358 )
359 };
360
361 if err != mjs_err_MJS_OK {
362 Err(self.vm.get_error(err))
363 } else {
364 Ok(Val {
365 vm: self.vm.clone(),
366 inner: ret,
367 })
368 }
369 } else {
370 Err(JSError::NotAFunction)
371 }
372 }
373}