rust_jsc/
object.rs

1use std::ops::Deref;
2
3use rust_jsc_sys::{
4    JSContextRef, JSObjectCallAsConstructor, JSObjectCallAsFunction,
5    JSObjectCopyPropertyNames, JSObjectDeleteProperty, JSObjectDeletePropertyForKey,
6    JSObjectGetPrivate, JSObjectGetProperty, JSObjectGetPropertyAtIndex,
7    JSObjectGetPropertyForKey, JSObjectGetPrototype, JSObjectHasProperty,
8    JSObjectHasPropertyForKey, JSObjectIsConstructor, JSObjectIsFunction, JSObjectMake,
9    JSObjectRef, JSObjectSetAsyncIterator, JSObjectSetIterator, JSObjectSetPrivate,
10    JSObjectSetProperty, JSObjectSetPropertyAtIndex, JSObjectSetPropertyForKey,
11    JSObjectSetPrototype, JSPropertyNameArrayGetCount, JSPropertyNameArrayGetNameAtIndex,
12    JSPropertyNameArrayRef, JSPropertyNameArrayRelease, JSStringRetain, JSValueRef,
13};
14
15use crate::{
16    JSContext, JSError, JSObject, JSResult, JSString, JSValue, PrivateData, PropertyDescriptor
17};
18
19pub struct JSPropertyNameIter {
20    inner: JSPropertyNameArrayRef,
21    index: usize,
22}
23
24impl Iterator for JSPropertyNameIter {
25    type Item = JSString;
26
27    fn next(&mut self) -> Option<Self::Item> {
28        if self.index < unsafe { JSPropertyNameArrayGetCount(self.inner) } {
29            let name =
30                unsafe { JSPropertyNameArrayGetNameAtIndex(self.inner, self.index) };
31            self.index += 1;
32            Some(JSString {
33                inner: unsafe { JSStringRetain(name) },
34            })
35        } else {
36            None
37        }
38    }
39
40    fn size_hint(&self) -> (usize, Option<usize>) {
41        let sz = unsafe { JSPropertyNameArrayGetCount(self.inner) };
42        (sz - self.index, Some(sz))
43    }
44}
45
46impl Drop for JSPropertyNameIter {
47    fn drop(&mut self) {
48        unsafe { JSPropertyNameArrayRelease(self.inner) }
49    }
50}
51
52impl JSObject {
53    /// Creates a new `JSObject` object.
54    ///
55    /// Creates a new empty JavaScript object.
56    pub fn new(ctx: &JSContext) -> Self {
57        let inner = unsafe {
58            JSObjectMake(ctx.inner, std::ptr::null_mut(), std::ptr::null_mut())
59        };
60        let value = JSValue::new(inner, ctx.inner);
61        Self { inner, value }
62    }
63
64    pub fn from_ref(inner: JSObjectRef, ctx: JSContextRef) -> Self {
65        let value = JSValue::new(inner, ctx);
66        Self { inner, value }
67    }
68
69    /// Sets an object's async iterator.
70    /// This function is the same as performing "object[Symbol.asyncIterator] = iterator" from JavaScript.
71    /// The iterator object must have a "next" method that returns a promise.
72    /// The promise must resolve to an object with a "value" property that contains the next value,
73    /// and a "done" property that indicates whether the iterator is done.
74    /// The iterator object may have a "return" method that cleans up resources when the iterator is done.
75    /// The return method may return a promise.
76    ///
77    /// Doc: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols
78    ///
79    /// # Arguments
80    /// * `iterator` - The iterator object to set on the object.
81    /// * `descriptor` - The property descriptor to set on the object.
82    ///
83    /// # Example
84    /// ```
85    /// use rust_jsc::*;
86    ///
87    /// let ctx = JSContext::new();
88    /// let object = JSObject::new(&ctx);
89    /// let iterator = JSObject::new(&ctx);
90    ///
91    /// object.set_async_iterator(&iterator, PropertyDescriptor::default()).unwrap();
92    /// ```
93    ///
94    /// # Errors
95    /// Returns a `JSError` if the operation fails.
96    ///
97    pub fn set_async_iterator(
98        &self,
99        iterator: &JSObject,
100        descriptor: PropertyDescriptor,
101    ) -> JSResult<()> {
102        let mut exception: JSValueRef = std::ptr::null_mut();
103        unsafe {
104            JSObjectSetAsyncIterator(
105                self.ctx,
106                self.inner,
107                iterator.inner,
108                descriptor.attributes,
109                &mut exception,
110            );
111        };
112
113        if !exception.is_null() {
114            let value = JSValue::new(exception, self.value.ctx);
115            return Err(JSError::from(value));
116        }
117
118        Ok(())
119    }
120
121    /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols
122    /// Sets an object's iterator.
123    /// This function is the same as performing "object[Symbol.iterator] = iterator" from JavaScript.
124    /// The iterator object must have a "next" method that returns an object with a "value" property that contains the next value,
125    /// and a "done" property that indicates whether the iterator is done.
126    /// The iterator object may have a "return" method that cleans up resources when the iterator is done.
127    /// The return method may return an object with a "value" property that contains the return value.
128    /// The iterator object may have a "throw" method that cleans up resources when the iterator is done.
129    ///
130    /// # Arguments
131    /// * `iterator` - The iterator object to set on the object.
132    /// * `descriptor` - The property descriptor to set on the object.
133    ///
134    /// # Example
135    /// ```
136    /// use rust_jsc::*;
137    ///
138    /// let ctx = JSContext::new();
139    /// let object = JSObject::new(&ctx);
140    /// let iterator = JSObject::new(&ctx);
141    ///
142    /// object.set_iterator(&iterator, PropertyDescriptor::default()).unwrap();
143    /// ```
144    ///
145    /// # Errors
146    /// Returns a `JSError` if the operation fails.
147    pub fn set_iterator(
148        &self,
149        iterator: &JSObject,
150        descriptor: PropertyDescriptor,
151    ) -> JSResult<()> {
152        let mut exception: JSValueRef = std::ptr::null_mut();
153        unsafe {
154            JSObjectSetIterator(
155                self.ctx,
156                self.inner,
157                iterator.inner,
158                descriptor.attributes,
159                &mut exception,
160            );
161        };
162
163        if !exception.is_null() {
164            let value = JSValue::new(exception, self.value.ctx);
165            return Err(JSError::from(value));
166        }
167
168        Ok(())
169    }
170
171    /// Tests whether an object has a given property.
172    /// Returns true if the object has the property, otherwise false.
173    /// This function is the same as performing "property in object" from JavaScript.
174    ///
175    /// # Arguments
176    /// * `name` - The name of the property to test for in the object.
177    ///
178    /// # Example
179    /// ```no_run
180    /// use rust_jsc::*;
181    ///
182    /// let ctx = JSContext::new();
183    /// let object = JSObject::new(&ctx);
184    /// let value = JSValue::string(&ctx, "value");
185    ///
186    /// object.set_property("name", &value, PropertyDescriptor::default());
187    /// assert_eq!(object.has_property("name"), true);
188    /// ```
189    ///
190    /// # Returns
191    /// Returns boolean value indicating if the object has the property.
192    pub fn has_property(&self, name: impl Into<JSString>) -> bool {
193        unsafe { JSObjectHasProperty(self.value.ctx, self.inner, name.into().inner) }
194    }
195
196    /// Gets a property from an object using a JSString as the property key.
197    /// Returns the value of the property if it exists, otherwise returns undefined.
198    /// This function is the same as performing "object['name']" from JavaScript.
199    ///
200    /// # Arguments
201    /// * `name` - The name of the property to get from the object.
202    ///
203    /// # Example
204    /// ```no_run
205    /// use rust_jsc::*;
206    ///
207    /// let ctx = JSContext::new();
208    /// let object = JSObject::new(&ctx);
209    /// let value = JSValue::string(&ctx, "value");
210    ///
211    /// object.set_property("name", &value, PropertyDescriptor::default());
212    /// assert_eq!(object.get_property("name").unwrap(), value);
213    /// ```
214    ///
215    /// # Returns
216    /// Returns the value of the property if it exists, otherwise returns undefined.
217    pub fn get_property(&self, name: impl Into<JSString>) -> JSResult<JSValue> {
218        let mut exception: JSValueRef = std::ptr::null_mut();
219        let value = unsafe {
220            JSObjectGetProperty(
221                self.value.ctx,
222                self.inner,
223                name.into().inner,
224                &mut exception,
225            )
226        };
227
228        if !exception.is_null() {
229            let value = JSValue::new(exception, self.value.ctx);
230            return Err(JSError::from(value));
231        }
232
233        Ok(JSValue::new(value, self.value.ctx))
234    }
235
236    /// Gets a property from an object using an index as the property key
237    /// Returns the value of the property if it exists, otherwise returns undefined.
238    /// This function is the same as performing \"object[index]\" from JavaScript.
239    ///
240    /// # Arguments
241    /// * `index` - The index of the property to get from the object.
242    ///
243    /// # Example
244    /// ```no_run
245    /// use rust_jsc::*;
246    ///
247    /// let ctx = JSContext::new();
248    /// let array = ctx.evaluate_script("[1, 2, 3]", None).unwrap();
249    /// let value = JSValue::string(&ctx, "value");
250    /// let array = array.as_object().unwrap();
251    ///
252    /// array.set_property_at_index(0, &value);
253    /// assert_eq!(array.get_property_at_index(0).unwrap(), value);
254    /// ```
255    ///
256    /// # Returns
257    /// Returns the value of the property if it exists, otherwise returns undefined.
258    pub fn get_property_at_index(&self, index: u32) -> JSResult<JSValue> {
259        let mut exception: JSValueRef = std::ptr::null_mut();
260        let result = unsafe {
261            JSObjectGetPropertyAtIndex(self.value.ctx, self.inner, index, &mut exception)
262        };
263
264        if !exception.is_null() {
265            let value = JSValue::new(exception, self.value.ctx);
266            return Err(JSError::from(value));
267        }
268
269        Ok(JSValue::new(result, self.value.ctx))
270    }
271
272    /// Sets a property on an object using a JSValue as the property key
273    /// This function is the same as performing \"object[propertyKey] = value\" from JavaScript.
274    ///
275    /// # Arguments
276    /// * `key` - The key to set on the object.
277    /// * `value` - The value to set on the object.
278    /// * `descriptor` - The property descriptor to set on the object.
279    ///
280    /// # Example
281    /// ```no_run
282    /// use rust_jsc::*;
283    ///
284    /// let ctx = JSContext::new();
285    /// let object = JSObject::new(&ctx);
286    /// let key = JSValue::string(&ctx, "key");
287    /// let value = JSValue::string(&ctx, "value");
288    ///
289    /// object.set(&key, &value, PropertyDescriptor::default()).unwrap();
290    /// assert_eq!(object.get(&key).unwrap(), value);
291    /// ```
292    ///
293    /// # Errors
294    /// Returns a `JSError` if the operation fails.
295    pub fn set(
296        &self,
297        key: &JSValue,
298        value: &JSValue,
299        descriptor: PropertyDescriptor,
300    ) -> JSResult<()> {
301        let mut exception: JSValueRef = std::ptr::null_mut();
302        unsafe {
303            JSObjectSetPropertyForKey(
304                self.ctx,
305                self.inner,
306                key.inner,
307                value.inner,
308                descriptor.attributes,
309                &mut exception,
310            );
311        }
312
313        if !exception.is_null() {
314            let value = JSValue::new(exception, self.value.ctx);
315            return Err(JSError::from(value));
316        }
317
318        Ok(())
319    }
320
321    /// Gets a property from an object using a JSValue as the property key
322    /// Returns the value of the property if it exists, otherwise returns undefined.
323    /// This function is the same as performing \"object[propertyKey]\" from JavaScript.
324    ///
325    /// # Arguments
326    /// * `key` - The key to get from the object.
327    ///
328    /// # Returns
329    /// Returns the value of the property if it exists, otherwise returns undefined.
330    ///
331    /// # Example
332    /// ```no_run
333    /// use rust_jsc::*;
334    ///
335    /// let ctx = JSContext::new();
336    /// let object = JSObject::new(&ctx);
337    /// let key = JSValue::string(&ctx, "key");
338    /// let value = JSValue::string(&ctx, "value");
339    ///
340    /// object.set(&key, &value, PropertyDescriptor::default());
341    /// assert_eq!(object.get(&key).unwrap(), value);
342    /// ```
343    ///
344    pub fn get(&self, key: &JSValue) -> JSResult<JSValue> {
345        let mut exception: JSValueRef = std::ptr::null_mut();
346        let result = unsafe {
347            JSObjectGetPropertyForKey(self.ctx, self.inner, key.inner, &mut exception)
348        };
349
350        if !exception.is_null() {
351            let value = JSValue::new(exception, self.value.ctx);
352            return Err(JSError::from(value));
353        }
354
355        Ok(JSValue::new(result, self.ctx))
356    }
357
358    /// Tests whether an object has a given property using a JSValue as the property key
359    /// Returns true if the object has the property, otherwise false.
360    /// This function is the same as performing \"propertyKey in object\" from JavaScript.
361    ///
362    /// # Arguments
363    /// * `key` - The key to test for in the object.
364    ///
365    /// # Returns
366    /// Returns boolean value indicating if the object has the property.
367    ///
368    /// # Example
369    /// ```no_run
370    /// use rust_jsc::*;
371    ///
372    /// let ctx = JSContext::new();
373    /// let object = JSObject::new(&ctx);
374    /// let key = JSValue::string(&ctx, "key");
375    /// let value = JSValue::string(&ctx, "value");
376    ///
377    /// object.set(&key, &value, PropertyDescriptor::default());
378    /// assert_eq!(object.has(&key).unwrap(), true);
379    /// ```
380    ///
381    /// # Errors
382    /// Returns a `JSError` if the operation fails.
383    pub fn has(&self, key: &JSValue) -> JSResult<bool> {
384        let mut exception: JSValueRef = std::ptr::null_mut();
385        let result = unsafe {
386            JSObjectHasPropertyForKey(self.ctx, self.inner, key.inner, &mut exception)
387        };
388
389        if !exception.is_null() {
390            let value = JSValue::new(exception, self.value.ctx);
391            return Err(JSError::from(value));
392        }
393
394        Ok(result)
395    }
396
397    /// Deletes a property from an object where the key is a JSValue
398    /// Returns true if the delete operation succeeds, otherwise false
399    /// (for example, if the property is not configurable).\n
400    /// This function is the same as performing \"delete object[propertyKey]\" from JavaScript.
401    ///
402    /// # Arguments
403    /// * `key` - The key to delete from the object.
404    ///
405    /// # Returns
406    /// Returns boolean value indicating if the delete operation succeeded.
407    ///
408    /// # Example
409    /// ```no_run
410    /// use rust_jsc::*;
411    ///
412    /// let ctx = JSContext::new();
413    /// let object = JSObject::new(&ctx);
414    /// let key = JSValue::string(&ctx, "key");
415    /// let value = JSValue::string(&ctx, "value");
416    ///
417    /// object.set(&key, &value, PropertyDescriptor::default());
418    /// assert_eq!(object.has(&key).unwrap(), true);
419    /// assert_eq!(object.delete(&key).unwrap(), true);
420    /// assert_eq!(object.has(&key).unwrap(), false);
421    /// ```
422    ///
423    /// # Errors
424    /// Returns a `JSError` if the delete operation fails.
425    pub fn delete(&self, key: &JSValue) -> JSResult<bool> {
426        let mut exception: JSValueRef = std::ptr::null_mut();
427        let result = unsafe {
428            JSObjectDeletePropertyForKey(self.ctx, self.inner, key.inner, &mut exception)
429        };
430
431        if !exception.is_null() {
432            let value = JSValue::new(exception, self.value.ctx);
433            return Err(JSError::from(value));
434        }
435
436        Ok(result)
437    }
438
439    /// Sets a property on an object using a JSString as the property key
440    /// This function is the same as performing \"object['propertyKey'] = value\" from JavaScript.
441    ///
442    /// # Arguments
443    /// * `name` - The name of the property to set on the object.
444    /// * `value` - The value to set on the object.
445    /// * `descriptor` - The property descriptor to set on the object.
446    ///
447    /// # Example
448    /// ```
449    /// use rust_jsc::*;
450    ///
451    /// let ctx = JSContext::new();
452    /// let object = JSObject::new(&ctx);
453    /// let value = JSValue::string(&ctx, "value");
454    ///
455    /// object.set_property("name", &value, PropertyDescriptor::default()).unwrap();
456    /// assert_eq!(object.get_property("name").unwrap(), value);
457    /// ```
458    pub fn set_property(
459        &self,
460        name: impl Into<JSString>,
461        value: &JSValue,
462        descriptor: PropertyDescriptor,
463    ) -> JSResult<()> {
464        let mut exception: JSValueRef = std::ptr::null_mut();
465        unsafe {
466            JSObjectSetProperty(
467                self.value.ctx,
468                self.inner,
469                name.into().inner,
470                value.inner,
471                descriptor.attributes,
472                &mut exception,
473            );
474        }
475
476        Ok(())
477    }
478
479    /// Sets a property on an object using an index as the property key
480    /// This function is the same as performing \"object[index] = value\" from JavaScript.
481    ///
482    /// # Arguments
483    /// * `index` - The index of the property to set on the object.
484    /// * `value` - The value to set on the object.
485    ///
486    /// # Example
487    /// ```
488    /// use rust_jsc::*;
489    ///
490    /// let ctx = JSContext::new();
491    /// let array = ctx.evaluate_script("[1, 2, 3]", None).unwrap();
492    /// let value = JSValue::string(&ctx, "value");
493    /// let array = array.as_object().unwrap();
494    ///
495    /// array.set_property_at_index(0, &value);
496    /// assert_eq!(array.get_property_at_index(0).unwrap(), value);
497    /// ```
498    ///
499    /// # Errors
500    /// Returns a `JSError` if the operation fails.
501    pub fn set_property_at_index(&self, index: u32, value: &JSValue) -> JSResult<()> {
502        let mut exception: JSValueRef = std::ptr::null_mut();
503        unsafe {
504            JSObjectSetPropertyAtIndex(
505                self.value.ctx,
506                self.inner,
507                index,
508                value.inner,
509                &mut exception,
510            );
511        }
512
513        if !exception.is_null() {
514            let value = JSValue::new(exception, self.value.ctx);
515            return Err(JSError::from(value));
516        }
517
518        Ok(())
519    }
520
521    /// Deletes a property from an object where the key is a JSString
522    /// Returns true if the delete operation succeeds, otherwise false
523    /// (for example, if the property is not configurable).\n
524    /// This function is the same as performing \"delete object['propertyKey']\" from JavaScript.
525    ///
526    /// # Arguments
527    /// * `name` - The name of the property to delete from the object.
528    ///
529    /// # Example
530    /// ```no_run
531    /// use rust_jsc::*;
532    ///
533    /// let ctx = JSContext::new();
534    /// let object = JSObject::new(&ctx);
535    /// let value = JSValue::string(&ctx, "value");
536    ///
537    /// object.set_property("name", &value, PropertyDescriptor::default());
538    /// assert_eq!(object.has_property("name"), true);
539    /// assert_eq!(object.delete_property("name").unwrap(), true);
540    /// assert_eq!(object.has_property("name"), false);
541    /// ```
542    ///
543    /// # Returns
544    /// Returns boolean value indicating if the delete operation succeeded.
545    pub fn delete_property(&self, name: impl Into<JSString>) -> JSResult<bool> {
546        let mut exception: JSValueRef = std::ptr::null_mut();
547        let result = unsafe {
548            JSObjectDeleteProperty(
549                self.value.ctx,
550                self.inner,
551                name.into().inner,
552                &mut exception,
553            )
554        };
555
556        if !exception.is_null() {
557            let value = JSValue::new(exception, self.value.ctx);
558            return Err(JSError::from(value));
559        }
560
561        Ok(result)
562    }
563
564    /// Returns an iterator over the property names of the object.
565    /// The iterator will yield `JSString` objects.
566    /// The order of the property names is not guaranteed.
567    /// The iterator will be deallocated when it goes out of scope.
568    ///
569    /// # Example
570    /// ```no_run
571    /// use rust_jsc::*;
572    ///
573    /// let ctx = JSContext::new();
574    /// let object = JSObject::new(&ctx);
575    /// let key = JSValue::string(&ctx, "key");
576    /// let value = JSValue::string(&ctx, "value");
577    ///
578    /// object.set(&key, &value, PropertyDescriptor::default());
579    ///
580    /// for name in object.get_property_names() {
581    ///    println!("Property name: {}", name);
582    /// }
583    /// ```
584    ///
585    /// # Returns
586    /// Returns an iterator over the property names of the object.
587    pub fn get_property_names(&self) -> JSPropertyNameIter {
588        let property_name_array =
589            unsafe { JSObjectCopyPropertyNames(self.value.ctx, self.inner) };
590        JSPropertyNameIter {
591            inner: property_name_array,
592            index: 0,
593        }
594    }
595
596    /// Gets an object's prototype.
597    /// This function is the same as performing "Object.getPrototypeOf(object)" from JavaScript.
598    ///
599    /// # Example
600    /// ```no_run
601    /// use rust_jsc::*;
602    ///
603    /// let ctx = JSContext::new();
604    /// let object = JSObject::new(&ctx);
605    /// let prototype = object.get_prototype();
606    /// println!("Object's prototype: {:?}", prototype);
607    /// ```
608    ///
609    /// # Returns
610    /// JSValue that is the object's prototype.
611    pub fn get_prototype(&self) -> JSValue {
612        JSValue::new(
613            unsafe { JSObjectGetPrototype(self.value.ctx, self.inner) },
614            self.value.ctx,
615        )
616    }
617
618    /// Sets an object's prototype.
619    /// This function is the same as performing "Object.setPrototypeOf(object, prototype)" from JavaScript.
620    ///
621    /// # Arguments
622    /// * `prototype` - The prototype to set on the object.
623    ///
624    /// # Example
625    /// ```no_run
626    /// use rust_jsc::*;
627    ///
628    /// let ctx = JSContext::new();
629    /// let object = JSObject::new(&ctx);
630    /// let prototype = JSObject::new(&ctx);
631    /// object.set_prototype(&prototype);
632    /// ```
633    pub fn set_prototype(&self, prototype: &JSObject) {
634        unsafe {
635            JSObjectSetPrototype(self.ctx, self.inner, prototype.inner);
636        }
637    }
638
639    /// Sets a pointer to private data on an object.
640    /// The default object class does not allocate storage for private data.
641    /// Only objects created with a non-NULL JSClass can store private data.
642    ///
643    /// # Arguments
644    /// * `data` - The private data to set on the object.
645    ///
646    /// # Example
647    /// ```no_run
648    /// use rust_jsc::*;
649    ///
650    /// let ctx = JSContext::new();
651    /// let object = JSObject::new(&ctx);
652    /// let data = Box::new(42);
653    /// object.set_private_data(data);
654    ///
655    /// let private_data: Box<i32> = object.get_private_data().unwrap();
656    /// assert_eq!(*private_data, 42);
657    /// ```
658    ///
659    /// # Returns
660    /// Returns true if object can store private data, otherwise false.
661    pub fn set_private_data<T>(&self, data: Box<T>) -> bool {
662        let data_ptr = Box::into_raw(data);
663        unsafe { JSObjectSetPrivate(self.inner, data_ptr as _) }
664    }
665
666    /// Gets the private data from an object.
667    ///
668    /// # Example
669    /// ```no_run
670    /// use rust_jsc::*;
671    ///
672    /// let ctx = JSContext::new();
673    /// let object = JSObject::new(&ctx);
674    /// let data = Box::new(42);
675    /// object.set_private_data(data);
676    ///
677    /// let private_data: Box<i32> = object.get_private_data().unwrap();
678    /// assert_eq!(*private_data, 42);
679    /// ```
680    ///
681    /// # Returns
682    /// Returns the private data if it exists, otherwise None.
683    pub fn get_private_data<T>(&self) -> Option<Box<T>> {
684        let data_ptr = unsafe { JSObjectGetPrivate(self.inner) };
685
686        if data_ptr.is_null() {
687            return None;
688        }
689
690        Some(unsafe { Box::from_raw(data_ptr as *mut T) })
691    }
692
693    pub fn get_private_data_ptr(&self) -> Option<PrivateData> {
694        let data_ptr = unsafe { JSObjectGetPrivate(self.inner) };
695
696        if data_ptr.is_null() {
697            return None;
698        }
699
700        Some(data_ptr)
701    }
702
703    /// Tests whether an object is a constructor.
704    ///
705    /// # Example
706    /// ```no_run
707    /// use rust_jsc::*;
708    ///
709    /// let ctx = JSContext::new();
710    /// let object = JSObject::new(&ctx);
711    ///
712    /// assert_eq!(object.is_contructor(), false);
713    /// ```
714    ///
715    /// # Returns
716    /// Returns true if the object can be called as a constructor, otherwise false.
717    pub fn is_contructor(&self) -> bool {
718        unsafe { JSObjectIsConstructor(self.value.ctx, self.inner) }
719    }
720
721    /// Tests whether an object is a function.
722    ///
723    /// # Example
724    /// ```no_run
725    /// use rust_jsc::*;
726    ///
727    /// let ctx = JSContext::new();
728    /// let object = JSObject::new(&ctx);
729    ///
730    /// assert_eq!(object.is_function(), false);
731    /// ```
732    ///
733    /// # Returns
734    /// Returns true if the object is a function, otherwise false.
735    pub fn is_function(&self) -> bool {
736        unsafe { JSObjectIsFunction(self.value.ctx, self.inner) }
737    }
738
739    /// Calls an object as a constructor.
740    ///
741    /// # Arguments
742    /// * `args` - The arguments to pass to the constructor.
743    ///
744    /// # Example
745    /// ```no_run
746    /// use rust_jsc::*;
747    ///
748    /// let ctx = JSContext::new();
749    /// let object = JSObject::new(&ctx);
750    /// let result = object.call_as_constructor(&[]).unwrap();
751    /// ```
752    ///
753    /// # Returns
754    /// Returns a result JSObject of calling the object as a constructor.
755    ///
756    /// # Errors
757    /// Returns a `JSError` if the operation fails.
758    pub fn call_as_constructor(&self, args: &[JSValue]) -> JSResult<Self> {
759        let mut exception: JSValueRef = std::ptr::null_mut();
760        let args: Vec<JSValueRef> = args.iter().map(|arg| arg.inner).collect();
761        let result = unsafe {
762            JSObjectCallAsConstructor(
763                self.value.ctx,
764                self.inner,
765                args.len(),
766                args.as_ptr(),
767                &mut exception,
768            )
769        };
770
771        if !exception.is_null() {
772            let value = JSValue::new(exception, self.value.ctx);
773            return Err(JSError::from(value));
774        }
775
776        Ok(JSObject::from_ref(result, self.value.ctx))
777    }
778
779    /// Calls an object as a function.
780    ///
781    /// # Arguments
782    /// * `this` - The object to use as `this` when calling the function.
783    /// * `args` - The arguments to pass to the function.
784    ///
785    /// # Example
786    /// ```no_run
787    /// use rust_jsc::*;
788    ///
789    /// let ctx = JSContext::new();
790    /// let object = JSObject::new(&ctx);
791    /// let result = object.call(None, &[]).unwrap();
792    /// ```
793    ///
794    /// # Returns
795    /// Returns a result JSValue of calling the object as a function.
796    ///
797    /// # Errors
798    /// Returns a `JSError` if the operation fails.
799    pub fn call(&self, this: Option<&JSObject>, args: &[JSValue]) -> JSResult<JSValue> {
800        let mut exception: JSValueRef = std::ptr::null_mut();
801        let args: Vec<JSValueRef> = args.iter().map(|arg| arg.inner).collect();
802        let this_object = this.map_or(std::ptr::null_mut(), |this| this.inner);
803        let result = unsafe {
804            JSObjectCallAsFunction(
805                self.value.ctx,
806                self.inner,
807                this_object,
808                args.len(),
809                args.as_ptr(),
810                &mut exception,
811            )
812        };
813
814        if !exception.is_null() {
815            let value = JSValue::new(exception, self.value.ctx);
816            return Err(JSError::from(value));
817        }
818
819        Ok(JSValue::new(result, self.value.ctx))
820    }
821}
822
823impl std::fmt::Debug for JSObject {
824    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
825        f.debug_struct("JSObject").finish()
826    }
827}
828
829impl Deref for JSObject {
830    type Target = JSValue;
831
832    fn deref(&self) -> &JSValue {
833        &self.value
834    }
835}
836
837impl From<JSObject> for JSValue {
838    fn from(object: JSObject) -> Self {
839        object.value
840    }
841}
842
843impl From<JSObject> for JSObjectRef {
844    fn from(object: JSObject) -> Self {
845        object.inner
846    }
847}
848
849#[cfg(test)]
850mod tests {
851
852    use crate::{self as rust_jsc, JSString};
853    use rust_jsc_macros::callback;
854
855    use crate::{JSContext, JSFunction, JSObject, JSResult, JSValue, PropertyDescriptor};
856
857    #[test]
858    fn test_object() {
859        let ctx = JSContext::new();
860        let object = JSObject::new(&ctx);
861        let key = JSValue::string(&ctx, "key");
862        let value = JSValue::string(&ctx, "value");
863
864        object
865            .set(&key, &value, PropertyDescriptor::default())
866            .unwrap();
867        assert_eq!(object.get(&key).unwrap(), value);
868        assert_eq!(object.has(&key).unwrap(), true);
869        assert_eq!(object.delete(&key).unwrap(), true);
870        assert_eq!(object.has(&key).unwrap(), false);
871    }
872
873    #[test]
874    fn test_object_property() {
875        let ctx = JSContext::new();
876        let object = JSObject::new(&ctx);
877        let name = "name";
878        let value = JSValue::string(&ctx, "value");
879
880        object
881            .set_property(name, &value, PropertyDescriptor::default())
882            .unwrap();
883        assert_eq!(object.get_property(name).unwrap(), value);
884        assert_eq!(object.has_property(name), true);
885        assert_eq!(object.delete_property(name).unwrap(), true);
886        assert_eq!(object.has_property(name), false);
887    }
888
889    #[test]
890    fn test_object_prototype() {
891        let ctx = JSContext::new();
892        let object = JSObject::new(&ctx);
893        let prototype = JSObject::new(&ctx);
894
895        object.set_prototype(&prototype);
896        assert_eq!(object.get_prototype(), prototype.into());
897    }
898
899    #[test]
900    fn test_object_constructor() {
901        let ctx = JSContext::new();
902        let object = JSObject::new(&ctx);
903        let result = object.call_as_constructor(&[]).unwrap();
904        assert_eq!(result.is_contructor(), false);
905    }
906
907    #[test]
908    fn test_object_function() {
909        let ctx = JSContext::new();
910        let object = JSObject::new(&ctx);
911        assert_eq!(object.is_function(), false);
912
913        let function = ctx
914            .evaluate_script("function test() { return 42; }; test", None)
915            .unwrap();
916        assert_eq!(function.is_object(), true);
917
918        let function = function.as_object().unwrap();
919        assert_eq!(function.is_function(), true);
920
921        let result = function.call(None, &[]).unwrap();
922        assert_eq!(result.as_number().unwrap(), 42.0);
923    }
924
925    #[test]
926    fn test_object_property_names() {
927        let ctx = JSContext::new();
928        let object = JSObject::new(&ctx);
929        let key = JSValue::string(&ctx, "key");
930        let value = JSValue::string(&ctx, "value");
931
932        object
933            .set(&key, &value, PropertyDescriptor::default())
934            .unwrap();
935
936        let mut property_names = object.get_property_names();
937        assert_eq!(property_names.next(), Some(JSString::from("key")));
938        assert_eq!(property_names.next(), None);
939    }
940
941    #[test]
942    fn test_object_property_at_index() {
943        let ctx = JSContext::new();
944        let object = JSObject::new(&ctx);
945        let value = JSValue::string(&ctx, "value");
946
947        object.set_property_at_index(0, &value).unwrap();
948        assert_eq!(object.get_property_at_index(0).unwrap(), value);
949    }
950
951    #[test]
952    fn test_object_set_property() {
953        let ctx = JSContext::new();
954        let object = JSObject::new(&ctx);
955        let value = JSValue::string(&ctx, "value");
956
957        object
958            .set_property("name", &value, PropertyDescriptor::default())
959            .unwrap();
960        assert_eq!(object.get_property("name").unwrap(), value);
961    }
962
963    #[test]
964    fn test_object_set_property_at_index() {
965        let ctx = JSContext::new();
966        let object = JSObject::new(&ctx);
967        let value = JSValue::string(&ctx, "value");
968
969        object.set_property_at_index(0, &value).unwrap();
970        assert_eq!(object.get_property_at_index(0).unwrap(), value);
971    }
972
973    #[test]
974    fn test_object_delete_property() {
975        let ctx = JSContext::new();
976        let object = JSObject::new(&ctx);
977        let name = "name";
978        let value = JSValue::string(&ctx, "value");
979
980        object
981            .set_property(name, &value, PropertyDescriptor::default())
982            .unwrap();
983        assert_eq!(object.has_property(name), true);
984        assert_eq!(object.delete_property(name).unwrap(), true);
985        assert_eq!(object.has_property(name), false);
986    }
987
988    #[test]
989    fn test_object_has_property() {
990        let ctx = JSContext::new();
991        let object = JSObject::new(&ctx);
992        let name = JSString::from("name");
993        let value = JSValue::string(&ctx, "value");
994
995        object
996            .set_property(name, &value, PropertyDescriptor::default())
997            .unwrap();
998        assert_eq!(object.has_property("name"), true);
999    }
1000
1001    #[test]
1002    fn test_object_get_property() {
1003        let ctx = JSContext::new();
1004        let object = JSObject::new(&ctx);
1005        let value = JSValue::string(&ctx, "value");
1006
1007        object
1008            .set_property("name", &value, PropertyDescriptor::default())
1009            .unwrap();
1010        assert_eq!(object.get_property("name").unwrap(), value);
1011    }
1012
1013    #[test]
1014    fn test_object_get_property_at_index() {
1015        let ctx = JSContext::new();
1016        let object = JSObject::new(&ctx);
1017        let value = JSValue::string(&ctx, "value");
1018
1019        object.set_property_at_index(0, &value).unwrap();
1020        assert_eq!(object.get_property_at_index(0).unwrap(), value);
1021    }
1022
1023    #[test]
1024    fn test_object_set() {
1025        let ctx = JSContext::new();
1026        let object = JSObject::new(&ctx);
1027        let key = JSValue::string(&ctx, "key");
1028        let value = JSValue::string(&ctx, "value");
1029
1030        object
1031            .set(&key, &value, PropertyDescriptor::default())
1032            .unwrap();
1033        assert_eq!(object.get(&key).unwrap(), value);
1034    }
1035
1036    #[test]
1037    fn test_object_get() {
1038        let ctx = JSContext::new();
1039        let object = JSObject::new(&ctx);
1040        let key = JSValue::string(&ctx, "key");
1041        let value = JSValue::string(&ctx, "value");
1042
1043        object
1044            .set(&key, &value, PropertyDescriptor::default())
1045            .unwrap();
1046        assert_eq!(object.get(&key).unwrap(), value);
1047    }
1048
1049    #[test]
1050    fn test_object_has() {
1051        let ctx = JSContext::new();
1052        let object = JSObject::new(&ctx);
1053        let key = JSValue::string(&ctx, "key");
1054        let value = JSValue::string(&ctx, "value");
1055
1056        object
1057            .set(&key, &value, PropertyDescriptor::default())
1058            .unwrap();
1059        assert_eq!(object.has(&key).unwrap(), true);
1060    }
1061
1062    #[test]
1063    fn test_object_delete() {
1064        let ctx = JSContext::new();
1065        let object = JSObject::new(&ctx);
1066        let key = JSValue::string(&ctx, "key");
1067        let value = JSValue::string(&ctx, "value");
1068
1069        object
1070            .set(&key, &value, PropertyDescriptor::default())
1071            .unwrap();
1072        assert_eq!(object.has(&key).unwrap(), true);
1073        assert_eq!(object.delete(&key).unwrap(), true);
1074        assert_eq!(object.has(&key).unwrap(), false);
1075    }
1076
1077    #[test]
1078    fn test_object_get_prototype() {
1079        let ctx = JSContext::new();
1080        let object = JSObject::new(&ctx);
1081        let prototype = JSObject::new(&ctx);
1082
1083        object.set_prototype(&prototype);
1084
1085        assert_eq!(object.get_prototype(), prototype.into());
1086    }
1087
1088    #[test]
1089    fn test_object_set_prototype() {
1090        let ctx = JSContext::new();
1091        let object = JSObject::new(&ctx);
1092        let prototype = JSObject::new(&ctx);
1093
1094        object.set_prototype(&prototype);
1095        assert_eq!(object.get_prototype(), prototype.into());
1096    }
1097
1098    #[test]
1099    fn test_object_is_constructor() {
1100        let ctx = JSContext::new();
1101        let object = JSObject::new(&ctx);
1102        assert_eq!(object.is_contructor(), false);
1103    }
1104
1105    #[test]
1106    fn test_object_is_function() {
1107        let ctx = JSContext::new();
1108        let object = JSObject::new(&ctx);
1109        assert_eq!(object.is_function(), false);
1110    }
1111
1112    #[test]
1113    fn test_object_call_as_constructor() {
1114        let ctx = JSContext::new();
1115        let object = JSObject::new(&ctx);
1116        let result = object.call_as_constructor(&[]).unwrap();
1117        assert_eq!(result.is_contructor(), false);
1118    }
1119
1120    #[test]
1121    fn test_object_debug() {
1122        let ctx = JSContext::new();
1123        let object = JSObject::new(&ctx);
1124        assert_eq!(format!("{:?}", object), "JSObject".to_string());
1125    }
1126
1127    #[test]
1128    fn test_object_property_names_iter() {
1129        let ctx = JSContext::new();
1130        let object = JSObject::new(&ctx);
1131        let key = JSValue::string(&ctx, "key");
1132        let value = JSValue::string(&ctx, "value");
1133
1134        object
1135            .set(&key, &value, PropertyDescriptor::default())
1136            .unwrap();
1137
1138        let mut property_names = object.get_property_names();
1139        assert_eq!(property_names.next(), Some(JSString::from("key")));
1140        assert_eq!(property_names.next(), None);
1141    }
1142
1143    #[test]
1144    fn test_iterator() {
1145        #[callback]
1146        fn log_info(
1147            ctx: JSContext,
1148            _function: JSObject,
1149            _this: JSObject,
1150            arguments: &[JSValue],
1151        ) -> JSResult<JSValue> {
1152            let message = arguments.get(0).unwrap().as_string().unwrap();
1153            println!("INFO: {}", message);
1154
1155            Ok(JSValue::undefined(&ctx))
1156        }
1157
1158        let ctx = JSContext::new();
1159        let object = JSObject::new(&ctx);
1160        let iterator = r#"
1161        const myIterator = () => {
1162            let i = 0;
1163            return {
1164              next() {
1165                i++;
1166                console.log(`Returning ${i}`);
1167                if (i === 4) return { done: true };
1168                return { done: false, value: i };
1169              },
1170              return() {
1171                console.log("Closing");
1172                return { done: true };
1173              },
1174            };
1175        };
1176        myIterator
1177        "#;
1178        let iterator_object = ctx
1179            .evaluate_script(iterator, None)
1180            .unwrap()
1181            .as_object()
1182            .unwrap();
1183        object
1184            .set_iterator(&iterator_object, PropertyDescriptor::default())
1185            .unwrap();
1186
1187        let function = JSFunction::callback(&ctx, Some("log"), Some(log_info));
1188        object
1189            .set_property("log", &function, Default::default())
1190            .unwrap();
1191        ctx.global_object()
1192            .set_property("console", &object, Default::default())
1193            .unwrap();
1194        ctx.global_object()
1195            .set_property("myObjectIter", &object, PropertyDescriptor::default())
1196            .unwrap();
1197
1198        let evaluate_script = r#"
1199        let counter = 0;
1200        for (let i of myObjectIter) {
1201            console.log(i);
1202            counter += i;
1203        }
1204        counter
1205        "#;
1206
1207        let result = ctx.evaluate_script(evaluate_script, None);
1208
1209        assert_eq!(result.is_ok(), true);
1210        let result = result.unwrap();
1211        assert_eq!(result.as_number().unwrap(), 6.0);
1212    }
1213
1214    #[test]
1215    fn test_async_iterator() {
1216        let ctx = JSContext::new();
1217        let object = JSObject::new(&ctx);
1218        let async_iterator = r#"
1219        const myAsyncIterator = () => {
1220            let i = 0;
1221            return {
1222              async next() {
1223                i++;
1224                console.log(`Returning ${i}`);
1225                if (i === 4) return { done: true };
1226                return { done: false, value: i };
1227              },
1228              async return() {
1229                console.log("Closing");
1230                return { done: true };
1231              },
1232            };
1233        };
1234        myAsyncIterator
1235        "#;
1236
1237        let async_iterator_object = ctx
1238            .evaluate_script(async_iterator, None)
1239            .unwrap()
1240            .as_object()
1241            .unwrap();
1242        object
1243            .set_async_iterator(&async_iterator_object, PropertyDescriptor::default())
1244            .unwrap();
1245        ctx.global_object()
1246            .set_property("myObjectIter", &object, PropertyDescriptor::default())
1247            .unwrap();
1248
1249        let evaluate_script = r#"
1250        let counter = 0;
1251        (async function () {
1252            for await (let i of myObjectIter) {
1253                console.log(i);
1254                counter += i;
1255            }
1256        })();
1257        "#;
1258
1259        let result = ctx.evaluate_script(evaluate_script, None);
1260
1261        assert_eq!(result.is_ok(), true);
1262    }
1263}