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}