1use core::fmt;
2use std::ops::Deref;
3
4use super::{JSContext, JSObject, JSString, JSTypedArray, JSTypedArrayType};
5
6pub trait AsJSValue<'a>: Deref<Target = JSValue<'a>> + AsRef<JSValue<'a>> {
11 fn into_value(self) -> JSValue<'a>;
12 fn as_value(&self) -> &JSValue<'a>;
13}
14
15#[repr(u32)]
17#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
18pub enum JSType {
19 Undefined = ul_sys::JSType_kJSTypeUndefined,
20 Null = ul_sys::JSType_kJSTypeNull,
21 Boolean = ul_sys::JSType_kJSTypeBoolean,
22 Number = ul_sys::JSType_kJSTypeNumber,
23 String = ul_sys::JSType_kJSTypeString,
24 Object = ul_sys::JSType_kJSTypeObject,
25 Symbol = ul_sys::JSType_kJSTypeSymbol,
26}
27
28pub struct JSValue<'a> {
34 pub(crate) internal: ul_sys::JSValueRef,
35 pub(crate) ctx: &'a JSContext,
36}
37
38impl<'a> JSValue<'a> {
39 pub(crate) fn from_raw(ctx: &'a JSContext, value: ul_sys::JSValueRef) -> Self {
40 assert!(!value.is_null());
41
42 Self {
43 internal: value,
44 ctx,
45 }
46 }
47
48 pub(crate) fn copy_from_raw(ctx: &'a JSContext, value: ul_sys::JSValueRef) -> Self {
49 assert!(!value.is_null());
50
51 unsafe {
52 ctx.lib.ultralight().JSValueProtect(ctx.internal, value);
53 }
54
55 Self::from_raw(ctx, value)
56 }
57
58 pub(crate) fn into_raw(self) -> ul_sys::JSValueRef {
59 unsafe {
61 self.ctx
62 .lib
63 .ultralight()
64 .JSValueProtect(self.ctx.internal, self.internal);
65 }
66
67 self.internal
68 }
69}
70
71impl<'a> JSValue<'a> {
72 pub fn new_undefined(ctx: &'a JSContext) -> Self {
74 let value = unsafe { ctx.lib.ultralight().JSValueMakeUndefined(ctx.internal) };
75
76 Self {
77 internal: value,
78 ctx,
79 }
80 }
81
82 pub fn new_null(ctx: &'a JSContext) -> Self {
84 let value = unsafe { ctx.lib.ultralight().JSValueMakeNull(ctx.internal) };
85
86 Self {
87 internal: value,
88 ctx,
89 }
90 }
91
92 pub fn new_boolean(ctx: &'a JSContext, value: bool) -> Self {
94 let value = unsafe { ctx.lib.ultralight().JSValueMakeBoolean(ctx.internal, value) };
95
96 Self {
97 internal: value,
98 ctx,
99 }
100 }
101
102 pub fn new_number(ctx: &'a JSContext, value: f64) -> Self {
104 let value = unsafe { ctx.lib.ultralight().JSValueMakeNumber(ctx.internal, value) };
105
106 Self {
107 internal: value,
108 ctx,
109 }
110 }
111
112 pub fn new_symbol(ctx: &'a JSContext, description: &str) -> Self {
116 let value = JSString::new(ctx.lib.clone(), description);
117
118 let value = unsafe {
119 ctx.lib
120 .ultralight()
121 .JSValueMakeSymbol(ctx.internal, value.internal)
122 };
123
124 Self {
125 internal: value,
126 ctx,
127 }
128 }
129
130 pub fn from_jsstring(ctx: &'a JSContext, value: JSString) -> Self {
132 let value = unsafe {
133 ctx.lib
134 .ultralight()
135 .JSValueMakeString(ctx.internal, value.internal)
136 };
137
138 Self {
139 internal: value,
140 ctx,
141 }
142 }
143
144 pub fn new_string(ctx: &'a JSContext, value: &str) -> Self {
148 let value = JSString::new(ctx.lib.clone(), value);
149
150 let value = unsafe {
151 ctx.lib
152 .ultralight()
153 .JSValueMakeString(ctx.internal, value.internal)
154 };
155
156 Self {
157 internal: value,
158 ctx,
159 }
160 }
161
162 pub fn new_from_json(ctx: &'a JSContext, value: &str) -> Option<Self> {
166 let value = JSString::new(ctx.lib.clone(), value);
167
168 let value = unsafe {
169 ctx.lib
170 .ultralight()
171 .JSValueMakeFromJSONString(ctx.internal, value.internal)
172 };
173
174 if value.is_null() {
175 None
176 } else {
177 Some(Self {
178 internal: value,
179 ctx,
180 })
181 }
182 }
183}
184
185impl JSValue<'_> {
186 pub fn get_type(&self) -> JSType {
188 let ty = unsafe {
189 self.ctx
190 .lib
191 .ultralight()
192 .JSValueGetType(self.ctx.internal, self.internal)
193 };
194
195 match ty {
196 ul_sys::JSType_kJSTypeUndefined => JSType::Undefined,
197 ul_sys::JSType_kJSTypeNull => JSType::Null,
198 ul_sys::JSType_kJSTypeBoolean => JSType::Boolean,
199 ul_sys::JSType_kJSTypeNumber => JSType::Number,
200 ul_sys::JSType_kJSTypeString => JSType::String,
201 ul_sys::JSType_kJSTypeObject => JSType::Object,
202 ul_sys::JSType_kJSTypeSymbol => JSType::Symbol,
203 _ => panic!("Unknown JSValue type: {}", ty),
204 }
205 }
206
207 pub fn is_undefined(&self) -> bool {
209 unsafe {
210 self.ctx
211 .lib
212 .ultralight()
213 .JSValueIsUndefined(self.ctx.internal, self.internal)
214 }
215 }
216
217 pub fn is_null(&self) -> bool {
219 unsafe {
220 self.ctx
221 .lib
222 .ultralight()
223 .JSValueIsNull(self.ctx.internal, self.internal)
224 }
225 }
226
227 pub fn is_date(&self) -> bool {
229 unsafe {
230 self.ctx
231 .lib
232 .ultralight()
233 .JSValueIsDate(self.ctx.internal, self.internal)
234 }
235 }
236
237 pub fn is_array(&self) -> bool {
239 unsafe {
240 self.ctx
241 .lib
242 .ultralight()
243 .JSValueIsArray(self.ctx.internal, self.internal)
244 }
245 }
246
247 pub fn is_symbol(&self) -> bool {
249 unsafe {
250 self.ctx
251 .lib
252 .ultralight()
253 .JSValueIsSymbol(self.ctx.internal, self.internal)
254 }
255 }
256
257 pub fn is_object(&self) -> bool {
259 unsafe {
260 self.ctx
261 .lib
262 .ultralight()
263 .JSValueIsObject(self.ctx.internal, self.internal)
264 }
265 }
266
267 pub fn is_string(&self) -> bool {
269 unsafe {
270 self.ctx
271 .lib
272 .ultralight()
273 .JSValueIsString(self.ctx.internal, self.internal)
274 }
275 }
276
277 pub fn is_number(&self) -> bool {
279 unsafe {
280 self.ctx
281 .lib
282 .ultralight()
283 .JSValueIsNumber(self.ctx.internal, self.internal)
284 }
285 }
286
287 pub fn is_boolean(&self) -> bool {
289 unsafe {
290 self.ctx
291 .lib
292 .ultralight()
293 .JSValueIsBoolean(self.ctx.internal, self.internal)
294 }
295 }
296
297 pub fn is_typed_array(&self) -> bool {
299 let typed_array = JSTypedArray {
302 value: Self {
303 internal: self.internal,
304 ctx: self.ctx,
305 },
306 };
307
308 match typed_array.ty().ok() {
309 None | Some(JSTypedArrayType::None) => false,
310 Some(_) => true,
311 }
312 }
313}
314
315impl<'a> JSValue<'a> {
316 pub fn as_object(&self) -> Result<JSObject<'a>, JSValue<'a>> {
320 let mut exception = std::ptr::null();
321
322 let result = unsafe {
323 self.ctx.lib.ultralight().JSValueToObject(
324 self.ctx.internal,
325 self.internal,
326 &mut exception,
327 )
328 };
329
330 if !exception.is_null() {
331 Err(JSValue::from_raw(self.ctx, exception))
332 } else if result.is_null() {
333 Err(JSValue::new_string(
334 self.ctx,
335 "Failed to convert value to object",
336 ))
337 } else {
338 Ok(JSObject {
339 value: JSValue::from_raw(self.ctx, result),
340 })
341 }
342 }
343
344 pub fn as_string(&self) -> Result<JSString, JSValue<'a>> {
348 let mut exception = std::ptr::null();
349
350 let result = unsafe {
351 self.ctx.lib.ultralight().JSValueToStringCopy(
352 self.ctx.internal,
353 self.internal,
354 &mut exception,
355 )
356 };
357
358 if !exception.is_null() {
359 Err(JSValue::from_raw(self.ctx, exception))
360 } else if result.is_null() {
361 Err(JSValue::new_string(
362 self.ctx,
363 "Failed to convert value to string",
364 ))
365 } else {
366 Ok(JSString::copy_from_raw(self.ctx.lib.clone(), result))
367 }
368 }
369
370 pub fn as_number(&self) -> Result<f64, JSValue<'a>> {
374 let mut exception = std::ptr::null();
375
376 let result = unsafe {
377 self.ctx.lib.ultralight().JSValueToNumber(
378 self.ctx.internal,
379 self.internal,
380 &mut exception,
381 )
382 };
383
384 if !exception.is_null() {
385 Err(JSValue::from_raw(self.ctx, exception))
386 } else {
387 Ok(result)
388 }
389 }
390
391 pub fn as_boolean(&self) -> bool {
393 unsafe {
394 self.ctx
395 .lib
396 .ultralight()
397 .JSValueToBoolean(self.ctx.internal, self.internal)
398 }
399 }
400
401 pub fn as_typed_array(&self) -> Result<JSTypedArray<'a>, JSValue<'a>> {
405 if self.is_typed_array() {
406 let object = self.as_object()?;
407
408 Ok(JSTypedArray {
409 value: object.value,
410 })
411 } else {
412 Err(JSValue::new_string(self.ctx, "Value is not a typed array"))
413 }
414 }
415
416 pub fn to_json_string(&self) -> Result<JSString, JSValue<'a>> {
420 let mut exception = std::ptr::null();
421
422 let result = unsafe {
423 self.ctx.lib.ultralight().JSValueCreateJSONString(
424 self.ctx.internal,
425 self.internal,
426 0,
427 &mut exception,
428 )
429 };
430
431 if !exception.is_null() {
432 Err(JSValue::from_raw(self.ctx, exception))
433 } else if result.is_null() {
434 Err(JSValue::new_string(
435 self.ctx,
436 "Failed to convert value to JSON string",
437 ))
438 } else {
439 Ok(JSString::from_raw(self.ctx.lib.clone(), result))
440 }
441 }
442}
443
444impl Clone for JSValue<'_> {
445 fn clone(&self) -> Self {
446 unsafe {
447 self.ctx
448 .lib
449 .ultralight()
450 .JSValueProtect(self.ctx.internal, self.internal)
451 };
452
453 Self {
454 internal: self.internal,
455 ctx: self.ctx,
456 }
457 }
458}
459
460impl fmt::Debug for JSValue<'_> {
461 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
462 f.debug_struct("JSValue")
463 .field("type", &self.get_type())
464 .field("repr", &self.to_json_string())
465 .finish()
466 }
467}
468
469impl Drop for JSValue<'_> {
470 fn drop(&mut self) {
471 unsafe {
472 self.ctx
473 .lib
474 .ultralight()
475 .JSValueUnprotect(self.ctx.internal, self.internal);
476 }
477 }
478}