1/*
2 * Copyright (C) 2018 Igalia S.L.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21#include "JSCValue.h"
22
23#include "APICast.h"
24#include "APIUtils.h"
25#include "JSCCallbackFunction.h"
26#include "JSCClassPrivate.h"
27#include "JSCContextPrivate.h"
28#include "JSCInlines.h"
29#include "JSCValuePrivate.h"
30#include "JSRetainPtr.h"
31#include "OpaqueJSString.h"
32#include <gobject/gvaluecollector.h>
33#include <wtf/glib/GRefPtr.h>
34#include <wtf/glib/GUniquePtr.h>
35#include <wtf/glib/WTFGType.h>
36
37/**
38 * SECTION: JSCValue
39 * @short_description: JavaScript value
40 * @title: JSCValue
41 * @see_also: JSCContext
42 *
43 * JSCValue represents a reference to a value in a #JSCContext. The JSCValue
44 * protects the referenced value from being garbage collected.
45 */
46
47enum {
48 PROP_0,
49
50 PROP_CONTEXT,
51};
52
53struct _JSCValuePrivate {
54 GRefPtr<JSCContext> context;
55 JSValueRef jsValue;
56};
57
58WEBKIT_DEFINE_TYPE(JSCValue, jsc_value, G_TYPE_OBJECT)
59
60static void jscValueGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* paramSpec)
61{
62 JSCValuePrivate* priv = JSC_VALUE(object)->priv;
63
64 switch (propID) {
65 case PROP_CONTEXT:
66 g_value_set_object(value, priv->context.get());
67 break;
68 default:
69 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
70 }
71}
72
73static void jscValueSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* paramSpec)
74{
75 JSCValuePrivate* priv = JSC_VALUE(object)->priv;
76
77 switch (propID) {
78 case PROP_CONTEXT:
79 priv->context = JSC_CONTEXT(g_value_get_object(value));
80 break;
81 default:
82 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
83 }
84}
85
86static void jscValueDispose(GObject* object)
87{
88 JSCValuePrivate* priv = JSC_VALUE(object)->priv;
89
90 if (priv->context) {
91 auto* jsContext = jscContextGetJSContext(priv->context.get());
92
93 JSValueUnprotect(jsContext, priv->jsValue);
94 jscContextValueDestroyed(priv->context.get(), priv->jsValue);
95 priv->jsValue = nullptr;
96 priv->context = nullptr;
97 }
98
99 G_OBJECT_CLASS(jsc_value_parent_class)->dispose(object);
100}
101
102static void jsc_value_class_init(JSCValueClass* klass)
103{
104 GObjectClass* objClass = G_OBJECT_CLASS(klass);
105
106 objClass->get_property = jscValueGetProperty;
107 objClass->set_property = jscValueSetProperty;
108 objClass->dispose = jscValueDispose;
109
110 /**
111 * JSCValue:context:
112 *
113 * The #JSCContext in which the value was created.
114 */
115 g_object_class_install_property(objClass,
116 PROP_CONTEXT,
117 g_param_spec_object(
118 "context",
119 "JSCContext",
120 "JSC Context",
121 JSC_TYPE_CONTEXT,
122 static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
123}
124
125JSValueRef jscValueGetJSValue(JSCValue* value)
126{
127 return value->priv->jsValue;
128}
129
130JSCValue* jscValueCreate(JSCContext* context, JSValueRef jsValue)
131{
132 auto* value = JSC_VALUE(g_object_new(JSC_TYPE_VALUE, "context", context, nullptr));
133 JSValueProtect(jscContextGetJSContext(context), jsValue);
134 value->priv->jsValue = jsValue;
135 return value;
136}
137
138/**
139 * jsc_value_get_context:
140 * @value: a #JSCValue
141 *
142 * Get the #JSCContext in which @value was created.
143 *
144 * Returns: (transfer none): the #JSCValue context.
145 */
146JSCContext* jsc_value_get_context(JSCValue* value)
147{
148 g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
149
150 return value->priv->context.get();
151}
152
153/**
154 * jsc_value_new_undefined:
155 * @context: a #JSCContext
156 *
157 * Create a new #JSCValue referencing <function>undefined</function> in @context.
158 *
159 * Returns: (transfer full): a #JSCValue.
160 */
161JSCValue* jsc_value_new_undefined(JSCContext* context)
162{
163 g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
164
165 return jscContextGetOrCreateValue(context, JSValueMakeUndefined(jscContextGetJSContext(context))).leakRef();
166}
167
168/**
169 * jsc_value_is_undefined:
170 * @value: a #JSCValue
171 *
172 * Get whether the value referenced by @value is <function>undefined</function>.
173 *
174 * Returns: whether the value is undefined.
175 */
176gboolean jsc_value_is_undefined(JSCValue* value)
177{
178 g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
179
180 JSCValuePrivate* priv = value->priv;
181 return JSValueIsUndefined(jscContextGetJSContext(priv->context.get()), priv->jsValue);
182}
183
184/**
185 * jsc_value_new_null:
186 * @context: a #JSCContext
187 *
188 * Create a new #JSCValue referencing <function>null</function> in @context.
189 *
190 * Returns: (transfer full): a #JSCValue.
191 */
192JSCValue* jsc_value_new_null(JSCContext* context)
193{
194 g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
195
196 return jscContextGetOrCreateValue(context, JSValueMakeNull(jscContextGetJSContext(context))).leakRef();
197}
198
199/**
200 * jsc_value_is_null:
201 * @value: a #JSCValue
202 *
203 * Get whether the value referenced by @value is <function>null</function>.
204 *
205 * Returns: whether the value is null.
206 */
207gboolean jsc_value_is_null(JSCValue* value)
208{
209 g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
210
211 JSCValuePrivate* priv = value->priv;
212 return JSValueIsNull(jscContextGetJSContext(priv->context.get()), priv->jsValue);
213}
214
215/**
216 * jsc_value_new_number:
217 * @context: a #JSCContext
218 * @number: a number
219 *
220 * Create a new #JSCValue from @number.
221 *
222 * Returns: (transfer full): a #JSCValue.
223 */
224JSCValue* jsc_value_new_number(JSCContext* context, double number)
225{
226 g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
227
228 return jscContextGetOrCreateValue(context, JSValueMakeNumber(jscContextGetJSContext(context), number)).leakRef();
229}
230
231/**
232 * jsc_value_is_number:
233 * @value: a #JSCValue
234 *
235 * Get whether the value referenced by @value is a number.
236 *
237 * Returns: whether the value is a number.
238 */
239gboolean jsc_value_is_number(JSCValue* value)
240{
241 g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
242
243 JSCValuePrivate* priv = value->priv;
244 return JSValueIsNumber(jscContextGetJSContext(priv->context.get()), priv->jsValue);
245}
246
247/**
248 * jsc_value_to_double:
249 * @value: a #JSCValue
250 *
251 * Convert @value to a double.
252 *
253 * Returns: a #gdouble result of the conversion.
254 */
255double jsc_value_to_double(JSCValue* value)
256{
257 g_return_val_if_fail(JSC_IS_VALUE(value), std::numeric_limits<double>::quiet_NaN());
258
259 JSCValuePrivate* priv = value->priv;
260 JSValueRef exception = nullptr;
261 auto result = JSValueToNumber(jscContextGetJSContext(priv->context.get()), priv->jsValue, &exception);
262 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
263 return std::numeric_limits<double>::quiet_NaN();
264
265 return result;
266}
267
268/**
269 * jsc_value_to_int32:
270 * @value: a #JSCValue
271 *
272 * Convert @value to a #gint32.
273 *
274 * Returns: a #gint32 result of the conversion.
275 */
276gint32 jsc_value_to_int32(JSCValue* value)
277{
278 return JSC::toInt32(jsc_value_to_double(value));
279}
280
281/**
282 * jsc_value_new_boolean:
283 * @context: a #JSCContext
284 * @value: a #gboolean
285 *
286 * Create a new #JSCValue from @value
287 *
288 * Returns: (transfer full): a #JSCValue.
289 */
290JSCValue* jsc_value_new_boolean(JSCContext* context, gboolean value)
291{
292 g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
293
294 return jscContextGetOrCreateValue(context, JSValueMakeBoolean(jscContextGetJSContext(context), value)).leakRef();
295}
296
297/**
298 * jsc_value_is_boolean:
299 * @value: a #JSCValue
300 *
301 * Get whether the value referenced by @value is a boolean.
302 *
303 * Returns: whether the value is a boolean.
304 */
305gboolean jsc_value_is_boolean(JSCValue* value)
306{
307 g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
308
309 JSCValuePrivate* priv = value->priv;
310 return JSValueIsBoolean(jscContextGetJSContext(priv->context.get()), priv->jsValue);
311}
312
313/**
314 * jsc_value_to_boolean:
315 * @value: a #JSCValue
316 *
317 * Convert @value to a boolean.
318 *
319 * Returns: a #gboolean result of the conversion.
320 */
321gboolean jsc_value_to_boolean(JSCValue* value)
322{
323 g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
324
325 JSCValuePrivate* priv = value->priv;
326 return JSValueToBoolean(jscContextGetJSContext(priv->context.get()), priv->jsValue);
327}
328
329/**
330 * jsc_value_new_string:
331 * @context: a #JSCContext
332 * @string: (nullable): a null-terminated string
333 *
334 * Create a new #JSCValue from @string. If you need to create a #JSCValue from a
335 * string containing null characters, use jsc_value_new_string_from_bytes() instead.
336 *
337 * Returns: (transfer full): a #JSCValue.
338 */
339JSCValue* jsc_value_new_string(JSCContext* context, const char* string)
340{
341 g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
342
343 JSValueRef jsStringValue;
344 if (string) {
345 JSRetainPtr<JSStringRef> jsString(Adopt, JSStringCreateWithUTF8CString(string));
346 jsStringValue = JSValueMakeString(jscContextGetJSContext(context), jsString.get());
347 } else
348 jsStringValue = JSValueMakeString(jscContextGetJSContext(context), nullptr);
349 return jscContextGetOrCreateValue(context, jsStringValue).leakRef();
350}
351
352/**
353 * jsc_value_new_string_from_bytes:
354 * @context: a #JSCContext
355 * @bytes: (nullable): a #GBytes
356 *
357 * Create a new #JSCValue from @bytes.
358 *
359 * Returns: (transfer full): a #JSCValue.
360 */
361JSCValue* jsc_value_new_string_from_bytes(JSCContext* context, GBytes* bytes)
362{
363 g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
364
365 if (!bytes)
366 return jsc_value_new_string(context, nullptr);
367
368 gsize dataSize;
369 const auto* data = static_cast<const char*>(g_bytes_get_data(bytes, &dataSize));
370 auto string = String::fromUTF8(data, dataSize);
371 JSRetainPtr<JSStringRef> jsString(Adopt, OpaqueJSString::tryCreate(WTFMove(string)).leakRef());
372 return jscContextGetOrCreateValue(context, JSValueMakeString(jscContextGetJSContext(context), jsString.get())).leakRef();
373}
374
375/**
376 * jsc_value_is_string:
377 * @value: a #JSCValue
378 *
379 * Get whether the value referenced by @value is a string
380 *
381 * Returns: whether the value is a string
382 */
383gboolean jsc_value_is_string(JSCValue* value)
384{
385 g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
386
387 JSCValuePrivate* priv = value->priv;
388 return JSValueIsString(jscContextGetJSContext(priv->context.get()), priv->jsValue);
389}
390
391/**
392 * jsc_value_to_string:
393 * @value: a #JSCValue
394 *
395 * Convert @value to a string. Use jsc_value_to_string_as_bytes() instead, if you need to
396 * handle strings containing null characters.
397 *
398 * Returns: (transfer full): a null-terminated string result of the conversion.
399 */
400char* jsc_value_to_string(JSCValue* value)
401{
402 g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
403
404 JSCValuePrivate* priv = value->priv;
405 JSValueRef exception = nullptr;
406 JSRetainPtr<JSStringRef> jsString(Adopt, JSValueToStringCopy(jscContextGetJSContext(priv->context.get()), priv->jsValue, &exception));
407 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
408 return nullptr;
409
410 if (!jsString)
411 return nullptr;
412
413 size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString.get());
414 auto* string = static_cast<char*>(g_malloc(maxSize));
415 if (!JSStringGetUTF8CString(jsString.get(), string, maxSize)) {
416 g_free(string);
417 return nullptr;
418 }
419
420 return string;
421}
422
423/**
424 * jsc_value_to_string_as_bytes:
425 * @value: a #JSCValue
426 *
427 * Convert @value to a string and return the results as #GBytes. This is needed
428 * to handle strings with null characters.
429 *
430 * Returns: (transfer full): a #GBytes with the result of the conversion.
431 */
432GBytes* jsc_value_to_string_as_bytes(JSCValue* value)
433{
434 g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
435
436 JSCValuePrivate* priv = value->priv;
437 JSValueRef exception = nullptr;
438 JSRetainPtr<JSStringRef> jsString(Adopt, JSValueToStringCopy(jscContextGetJSContext(priv->context.get()), priv->jsValue, &exception));
439 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
440 return nullptr;
441
442 if (!jsString)
443 return nullptr;
444
445 size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString.get());
446 if (maxSize == 1)
447 return g_bytes_new_static("", 0);
448
449 auto* string = static_cast<char*>(fastMalloc(maxSize));
450 auto stringSize = JSStringGetUTF8CString(jsString.get(), string, maxSize);
451 if (!stringSize) {
452 fastFree(string);
453 return nullptr;
454 }
455
456 // Ignore the null character added by JSStringGetUTF8CString.
457 return g_bytes_new_with_free_func(string, stringSize - 1, fastFree, string);
458}
459
460/**
461 * jsc_value_new_array: (skip)
462 * @context: a #JSCContext
463 * @first_item_type: #GType of first item, or %G_TYPE_NONE
464 * @...: value of the first item, followed optionally by more type/value pairs, followed by %G_TYPE_NONE.
465 *
466 * Create a new #JSCValue referencing an array with the given items. If @first_item_type
467 * is %G_TYPE_NONE an empty array is created.
468 *
469 * Returns: (transfer full): a #JSCValue.
470 */
471JSCValue* jsc_value_new_array(JSCContext* context, GType firstItemType, ...)
472{
473 g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
474
475 JSValueRef exception = nullptr;
476 auto* jsContext = jscContextGetJSContext(context);
477 auto* jsArray = JSObjectMakeArray(jsContext, 0, nullptr, &exception);
478 if (jscContextHandleExceptionIfNeeded(context, exception))
479 return nullptr;
480
481 auto* jsArrayObject = JSValueToObject(jsContext, jsArray, &exception);
482 if (jscContextHandleExceptionIfNeeded(context, exception))
483 return nullptr;
484
485 unsigned index = 0;
486 va_list args;
487 va_start(args, firstItemType);
488 GType itemType = firstItemType;
489 while (itemType != G_TYPE_NONE) {
490 GValue item;
491 GUniqueOutPtr<char> error;
492 G_VALUE_COLLECT_INIT(&item, itemType, args, G_VALUE_NOCOPY_CONTENTS, &error.outPtr());
493 if (error) {
494 exception = toRef(JSC::createTypeError(toJS(jsContext), makeString("failed to collect array item: ", error.get())));
495 jscContextHandleExceptionIfNeeded(context, exception);
496 jsArray = nullptr;
497 break;
498 }
499
500 auto* jsValue = jscContextGValueToJSValue(context, &item, &exception);
501 g_value_unset(&item);
502 if (jscContextHandleExceptionIfNeeded(context, exception)) {
503 jsArray = nullptr;
504 break;
505 }
506
507 JSObjectSetPropertyAtIndex(jsContext, jsArrayObject, index, jsValue, &exception);
508 if (jscContextHandleExceptionIfNeeded(context, exception)) {
509 jsArray = nullptr;
510 break;
511 }
512
513 itemType = va_arg(args, GType);
514 index++;
515 }
516 va_end(args);
517
518 return jsArray ? jscContextGetOrCreateValue(context, jsArray).leakRef() : nullptr;
519}
520
521/**
522 * jsc_value_new_array_from_garray:
523 * @context: a #JSCContext
524 * @array: (nullable) (element-type JSCValue): a #GPtrArray
525 *
526 * Create a new #JSCValue referencing an array with the items from @array. If @array
527 * is %NULL or empty a new empty array will be created. Elements of @array should be
528 * pointers to a #JSCValue.
529 *
530 * Returns: (transfer full): a #JSCValue.
531 */
532JSCValue* jsc_value_new_array_from_garray(JSCContext* context, GPtrArray* gArray)
533{
534 g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
535
536 if (!gArray || !gArray->len)
537 return jsc_value_new_array(context, G_TYPE_NONE);
538
539 JSValueRef exception = nullptr;
540 auto* jsArray = jscContextGArrayToJSArray(context, gArray, &exception);
541 if (jscContextHandleExceptionIfNeeded(context, exception))
542 return nullptr;
543
544 return jscContextGetOrCreateValue(context, jsArray).leakRef();
545}
546
547/**
548 * jsc_value_new_array_from_strv:
549 * @context: a #JSCContext
550 * @strv: (array zero-terminated=1) (element-type utf8): a %NULL-terminated array of strings
551 *
552 * Create a new #JSCValue referencing an array of strings with the items from @strv. If @array
553 * is %NULL or empty a new empty array will be created.
554 *
555 * Returns: (transfer full): a #JSCValue.
556 */
557JSCValue* jsc_value_new_array_from_strv(JSCContext* context, const char* const* strv)
558{
559 g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
560
561 auto strvLength = strv ? g_strv_length(const_cast<char**>(strv)) : 0;
562 if (!strvLength)
563 return jsc_value_new_array(context, G_TYPE_NONE);
564
565 GRefPtr<GPtrArray> gArray = adoptGRef(g_ptr_array_new_full(strvLength, g_object_unref));
566 for (unsigned i = 0; i < strvLength; i++)
567 g_ptr_array_add(gArray.get(), jsc_value_new_string(context, strv[i]));
568
569 return jsc_value_new_array_from_garray(context, gArray.get());
570}
571
572/**
573 * jsc_value_is_array:
574 * @value: a #JSCValue
575 *
576 * Get whether the value referenced by @value is an array.
577 *
578 * Returns: whether the value is an array.
579 */
580gboolean jsc_value_is_array(JSCValue* value)
581{
582 g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
583
584 JSCValuePrivate* priv = value->priv;
585 return JSValueIsArray(jscContextGetJSContext(priv->context.get()), priv->jsValue);
586}
587
588/**
589 * jsc_value_new_object:
590 * @context: a #JSCContext
591 * @instance: (nullable) (transfer full): an object instance or %NULL
592 * @jsc_class: (nullable): the #JSCClass of @instance
593 *
594 * Create a new #JSCValue from @instance. If @instance is %NULL a new empty object is created.
595 * When @instance is provided, @jsc_class must be provided too. @jsc_class takes ownership of
596 * @instance that will be freed by the #GDestroyNotify passed to jsc_context_register_class().
597 *
598 * Returns: (transfer full): a #JSCValue.
599 */
600JSCValue* jsc_value_new_object(JSCContext* context, gpointer instance, JSCClass* jscClass)
601{
602 g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
603 g_return_val_if_fail(!instance || JSC_IS_CLASS(jscClass), nullptr);
604
605 return jscContextGetOrCreateValue(context, instance ? toRef(jscClassGetOrCreateJSWrapper(jscClass, context, instance)) : JSObjectMake(jscContextGetJSContext(context), nullptr, nullptr)).leakRef();
606}
607
608/**
609 * jsc_value_is_object:
610 * @value: a #JSCValue
611 *
612 * Get whether the value referenced by @value is an object.
613 *
614 * Returns: whether the value is an object.
615 */
616gboolean jsc_value_is_object(JSCValue* value)
617{
618 g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
619
620 JSCValuePrivate* priv = value->priv;
621 return JSValueIsObject(jscContextGetJSContext(priv->context.get()), priv->jsValue);
622}
623
624/**
625 * jsc_value_object_is_instance_of:
626 * @value: a #JSCValue
627 * @name: a class name
628 *
629 * Get whether the value referenced by @value is an instance of class @name.
630 *
631 * Returns: whether the value is an object instance of class @name.
632 */
633gboolean jsc_value_object_is_instance_of(JSCValue* value, const char* name)
634{
635 g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
636 g_return_val_if_fail(name, FALSE);
637
638 JSCValuePrivate* priv = value->priv;
639 // We use evaluate here and not get_value because classes are not necessarily a property of the global object.
640 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-global-environment-records
641 GRefPtr<JSCValue> constructor = adoptGRef(jsc_context_evaluate(priv->context.get(), name, -1));
642 auto* jsContext = jscContextGetJSContext(priv->context.get());
643
644 JSValueRef exception = nullptr;
645 JSObjectRef object = JSValueToObject(jsContext, constructor->priv->jsValue, &exception);
646 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
647 return FALSE;
648
649 gboolean returnValue = JSValueIsInstanceOfConstructor(jsContext, priv->jsValue, object, &exception);
650 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
651 return FALSE;
652
653 return returnValue;
654}
655
656/**
657 * jsc_value_object_set_property:
658 * @value: a #JSCValue
659 * @name: the property name
660 * @property: the #JSCValue to set
661 *
662 * Set @property with @name on @value.
663 */
664void jsc_value_object_set_property(JSCValue* value, const char* name, JSCValue* property)
665{
666 g_return_if_fail(JSC_IS_VALUE(value));
667 g_return_if_fail(name);
668 g_return_if_fail(JSC_IS_VALUE(property));
669
670 JSCValuePrivate* priv = value->priv;
671 auto* jsContext = jscContextGetJSContext(priv->context.get());
672 JSValueRef exception = nullptr;
673 JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
674 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
675 return;
676
677 JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString(name));
678 JSObjectSetProperty(jsContext, object, propertyName.get(), property->priv->jsValue, kJSPropertyAttributeNone, &exception);
679 jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
680}
681
682/**
683 * jsc_value_object_get_property:
684 * @value: a #JSCValue
685 * @name: the property name
686 *
687 * Get property with @name from @value.
688 *
689 * Returns: (transfer full): the property #JSCValue.
690 */
691JSCValue* jsc_value_object_get_property(JSCValue* value, const char* name)
692{
693 g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
694 g_return_val_if_fail(name, nullptr);
695
696 JSCValuePrivate* priv = value->priv;
697 auto* jsContext = jscContextGetJSContext(priv->context.get());
698 JSValueRef exception = nullptr;
699 JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
700 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
701 return jsc_value_new_undefined(priv->context.get());
702
703 JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString(name));
704 JSValueRef result = JSObjectGetProperty(jsContext, object, propertyName.get(), &exception);
705 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
706 return jsc_value_new_undefined(priv->context.get());
707
708 return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
709}
710
711/**
712 * jsc_value_object_set_property_at_index:
713 * @value: a #JSCValue
714 * @index: the property index
715 * @property: the #JSCValue to set
716 *
717 * Set @property at @index on @value.
718 */
719void jsc_value_object_set_property_at_index(JSCValue* value, unsigned index, JSCValue* property)
720{
721 g_return_if_fail(JSC_IS_VALUE(value));
722 g_return_if_fail(JSC_IS_VALUE(property));
723
724 JSCValuePrivate* priv = value->priv;
725 auto* jsContext = jscContextGetJSContext(priv->context.get());
726 JSValueRef exception = nullptr;
727 JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
728 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
729 return;
730
731 JSObjectSetPropertyAtIndex(jsContext, object, index, property->priv->jsValue, &exception);
732 jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
733}
734
735/**
736 * jsc_value_object_get_property_at_index:
737 * @value: a #JSCValue
738 * @index: the property index
739 *
740 * Get property at @index from @value.
741 *
742 * Returns: (transfer full): the property #JSCValue.
743 */
744JSCValue* jsc_value_object_get_property_at_index(JSCValue* value, unsigned index)
745{
746 g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
747
748 JSCValuePrivate* priv = value->priv;
749 auto* jsContext = jscContextGetJSContext(priv->context.get());
750 JSValueRef exception = nullptr;
751 JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
752 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
753 return jsc_value_new_undefined(priv->context.get());
754
755 JSValueRef result = JSObjectGetPropertyAtIndex(jsContext, object, index, &exception);
756 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
757 return jsc_value_new_undefined(priv->context.get());
758
759 return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
760}
761
762/**
763 * jsc_value_object_has_property:
764 * @value: a #JSCValue
765 * @name: the property name
766 *
767 * Get whether @value has property with @name.
768 *
769 * Returns: %TRUE if @value has a property with @name, or %FALSE otherwise
770 */
771gboolean jsc_value_object_has_property(JSCValue* value, const char* name)
772{
773 g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
774 g_return_val_if_fail(name, FALSE);
775
776 JSCValuePrivate* priv = value->priv;
777 auto* jsContext = jscContextGetJSContext(priv->context.get());
778 JSValueRef exception = nullptr;
779 JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
780 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
781 return FALSE;
782
783 JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString(name));
784 return JSObjectHasProperty(jsContext, object, propertyName.get());
785}
786
787/**
788 * jsc_value_object_delete_property:
789 * @value: a #JSCValue
790 * @name: the property name
791 *
792 * Try to delete property with @name from @value. This function will return %FALSE if
793 * the property was defined without %JSC_VALUE_PROPERTY_CONFIGURABLE flag.
794 *
795 * Returns: %TRUE if the property was deleted, or %FALSE otherwise.
796 */
797gboolean jsc_value_object_delete_property(JSCValue* value, const char* name)
798{
799 g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
800 g_return_val_if_fail(name, FALSE);
801
802 JSCValuePrivate* priv = value->priv;
803 auto* jsContext = jscContextGetJSContext(priv->context.get());
804 JSValueRef exception = nullptr;
805 JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
806 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
807 return FALSE;
808
809 JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString(name));
810 gboolean result = JSObjectDeleteProperty(jsContext, object, propertyName.get(), &exception);
811 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
812 return FALSE;
813
814 return result;
815}
816
817/**
818 * jsc_value_object_enumerate_properties:
819 * @value: a #JSCValue
820 *
821 * Get the list of property names of @value. Only properties defined with %JSC_VALUE_PROPERTY_ENUMERABLE
822 * flag will be collected.
823 *
824 * Returns: (array zero-terminated=1) (transfer full) (nullable): a %NULL-terminated array of strings containing the
825 * property names, or %NULL if @value doesn't have enumerable properties. Use g_strfreev() to free.
826 */
827char** jsc_value_object_enumerate_properties(JSCValue* value)
828{
829 g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
830
831 JSCValuePrivate* priv = value->priv;
832 auto* jsContext = jscContextGetJSContext(priv->context.get());
833 JSValueRef exception = nullptr;
834 JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
835 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
836 return nullptr;
837
838 auto* propertiesArray = JSObjectCopyPropertyNames(jsContext, object);
839 if (!propertiesArray)
840 return nullptr;
841
842 auto propertiesArraySize = JSPropertyNameArrayGetCount(propertiesArray);
843 if (!propertiesArraySize) {
844 JSPropertyNameArrayRelease(propertiesArray);
845 return nullptr;
846 }
847
848 auto* result = static_cast<char**>(g_new0(char*, propertiesArraySize + 1));
849 for (unsigned i = 0; i < propertiesArraySize; ++i) {
850 auto* jsString = JSPropertyNameArrayGetNameAtIndex(propertiesArray, i);
851 size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString);
852 auto* string = static_cast<char*>(g_malloc(maxSize));
853 JSStringGetUTF8CString(jsString, string, maxSize);
854 result[i] = string;
855 }
856 JSPropertyNameArrayRelease(propertiesArray);
857
858 return result;
859}
860
861static JSValueRef jsObjectCall(JSGlobalContextRef jsContext, JSObjectRef function, JSC::JSCCallbackFunction::Type functionType, JSObjectRef thisObject, const Vector<JSValueRef>& arguments, JSValueRef* exception)
862{
863 switch (functionType) {
864 case JSC::JSCCallbackFunction::Type::Constructor:
865 return JSObjectCallAsConstructor(jsContext, function, arguments.size(), arguments.data(), exception);
866 break;
867 case JSC::JSCCallbackFunction::Type::Method:
868 ASSERT(thisObject);
869 FALLTHROUGH;
870 case JSC::JSCCallbackFunction::Type::Function:
871 return JSObjectCallAsFunction(jsContext, function, thisObject, arguments.size(), arguments.data(), exception);
872 break;
873 }
874 RELEASE_ASSERT_NOT_REACHED();
875}
876
877static GRefPtr<JSCValue> jscValueCallFunction(JSCValue* value, JSObjectRef function, JSC::JSCCallbackFunction::Type functionType, JSObjectRef thisObject, GType firstParameterType, va_list args)
878{
879 JSCValuePrivate* priv = value->priv;
880 auto* jsContext = jscContextGetJSContext(priv->context.get());
881
882 JSValueRef exception = nullptr;
883 Vector<JSValueRef> arguments;
884 GType parameterType = firstParameterType;
885 while (parameterType != G_TYPE_NONE) {
886 GValue parameter;
887 GUniqueOutPtr<char> error;
888 G_VALUE_COLLECT_INIT(&parameter, parameterType, args, G_VALUE_NOCOPY_CONTENTS, &error.outPtr());
889 if (error) {
890 exception = toRef(JSC::createTypeError(toJS(jsContext), makeString("failed to collect function paramater: ", error.get())));
891 jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
892 return adoptGRef(jsc_value_new_undefined(priv->context.get()));
893 }
894
895 auto* jsValue = jscContextGValueToJSValue(priv->context.get(), &parameter, &exception);
896 g_value_unset(&parameter);
897 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
898 return jscContextGetOrCreateValue(priv->context.get(), jsValue);
899
900 arguments.append(jsValue);
901 parameterType = va_arg(args, GType);
902 }
903
904 auto result = jsObjectCall(jsContext, function, functionType, thisObject, arguments, &exception);
905 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
906 return adoptGRef(jsc_value_new_undefined(priv->context.get()));
907
908 return jscContextGetOrCreateValue(priv->context.get(), result);
909}
910
911/**
912 * jsc_value_object_invoke_method: (skip)
913 * @value: a #JSCValue
914 * @name: the method name
915 * @first_parameter_type: #GType of first parameter, or %G_TYPE_NONE
916 * @...: value of the first parameter, followed optionally by more type/value pairs, followed by %G_TYPE_NONE
917 *
918 * Invoke method with @name on object referenced by @value, passing the given parameters. If
919 * @first_parameter_type is %G_TYPE_NONE no parameters will be passed to the method.
920 * The object instance will be handled automatically even when the method is a custom one
921 * registered with jsc_class_add_method(), so it should never be passed explicitly as parameter
922 * of this function.
923 *
924 * This function always returns a #JSCValue, in case of void methods a #JSCValue referencing
925 * <function>undefined</function> is returned.
926 *
927 * Returns: (transfer full): a #JSCValue with the return value of the method.
928 */
929JSCValue* jsc_value_object_invoke_method(JSCValue* value, const char* name, GType firstParameterType, ...)
930{
931 g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
932 g_return_val_if_fail(name, nullptr);
933
934 JSCValuePrivate* priv = value->priv;
935 auto* jsContext = jscContextGetJSContext(priv->context.get());
936 JSValueRef exception = nullptr;
937 JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
938 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
939 return jsc_value_new_undefined(priv->context.get());
940
941 JSRetainPtr<JSStringRef> methodName(Adopt, JSStringCreateWithUTF8CString(name));
942 JSValueRef functionValue = JSObjectGetProperty(jsContext, object, methodName.get(), &exception);
943 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
944 return jsc_value_new_undefined(priv->context.get());
945
946 JSObjectRef function = JSValueToObject(jsContext, functionValue, &exception);
947 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
948 return jsc_value_new_undefined(priv->context.get());
949
950 va_list args;
951 va_start(args, firstParameterType);
952 auto result = jscValueCallFunction(value, function, JSC::JSCCallbackFunction::Type::Method, object, firstParameterType, args);
953 va_end(args);
954
955 return result.leakRef();
956}
957
958/**
959 * jsc_value_object_invoke_methodv: (rename-to jsc_value_object_invoke_method)
960 * @value: a #JSCValue
961 * @name: the method name
962 * @n_parameters: the number of parameters
963 * @parameters: (nullable) (array length=n_parameters) (element-type JSCValue): the #JSCValue<!-- -->s to pass as parameters to the method, or %NULL
964 *
965 * Invoke method with @name on object referenced by @value, passing the given @parameters. If
966 * @n_parameters is 0 no parameters will be passed to the method.
967 * The object instance will be handled automatically even when the method is a custom one
968 * registered with jsc_class_add_method(), so it should never be passed explicitly as parameter
969 * of this function.
970 *
971 * This function always returns a #JSCValue, in case of void methods a #JSCValue referencing
972 * <function>undefined</function> is returned.
973 *
974 * Returns: (transfer full): a #JSCValue with the return value of the method.
975 */
976JSCValue* jsc_value_object_invoke_methodv(JSCValue* value, const char* name, unsigned parametersCount, JSCValue** parameters)
977{
978 g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
979 g_return_val_if_fail(name, nullptr);
980 g_return_val_if_fail(!parametersCount || parameters, nullptr);
981
982 JSCValuePrivate* priv = value->priv;
983 auto* jsContext = jscContextGetJSContext(priv->context.get());
984 JSValueRef exception = nullptr;
985 JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
986 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
987 return jsc_value_new_undefined(priv->context.get());
988
989 JSRetainPtr<JSStringRef> methodName(Adopt, JSStringCreateWithUTF8CString(name));
990 JSValueRef functionValue = JSObjectGetProperty(jsContext, object, methodName.get(), &exception);
991 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
992 return jsc_value_new_undefined(priv->context.get());
993
994 JSObjectRef function = JSValueToObject(jsContext, functionValue, &exception);
995 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
996 return jsc_value_new_undefined(priv->context.get());
997
998 Vector<JSValueRef> arguments;
999 if (parametersCount) {
1000 arguments.reserveInitialCapacity(parametersCount);
1001 for (unsigned i = 0; i < parametersCount; ++i)
1002 arguments.uncheckedAppend(jscValueGetJSValue(parameters[i]));
1003 }
1004
1005 auto result = jsObjectCall(jsContext, function, JSC::JSCCallbackFunction::Type::Method, object, arguments, &exception);
1006 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
1007 jsc_value_new_undefined(priv->context.get());
1008
1009 return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
1010}
1011
1012/**
1013 * JSCValuePropertyFlags:
1014 * @JSC_VALUE_PROPERTY_CONFIGURABLE: the type of the property descriptor may be changed and the
1015 * property may be deleted from the corresponding object.
1016 * @JSC_VALUE_PROPERTY_ENUMERABLE: the property shows up during enumeration of the properties on
1017 * the corresponding object.
1018 * @JSC_VALUE_PROPERTY_WRITABLE: the value associated with the property may be changed with an
1019 * assignment operator. This doesn't have any effect when passed to jsc_value_object_define_property_accessor().
1020 *
1021 * Flags used when defining properties with jsc_value_object_define_property_data() and
1022 * jsc_value_object_define_property_accessor().
1023 */
1024
1025/**
1026 * jsc_value_object_define_property_data:
1027 * @value: a #JSCValue
1028 * @property_name: the name of the property to define
1029 * @flags: #JSCValuePropertyFlags
1030 * @property_value: (nullable): the default property value
1031 *
1032 * Define or modify a property with @property_name in object referenced by @value. This is equivalent to
1033 * JavaScript <function>Object.defineProperty()</function> when used with a data descriptor.
1034 */
1035void jsc_value_object_define_property_data(JSCValue* value, const char* propertyName, JSCValuePropertyFlags flags, JSCValue* propertyValue)
1036{
1037 g_return_if_fail(JSC_IS_VALUE(value));
1038 g_return_if_fail(propertyName);
1039
1040 JSCValuePrivate* priv = value->priv;
1041 auto* jsContext = jscContextGetJSContext(priv->context.get());
1042 JSC::ExecState* exec = toJS(jsContext);
1043 JSC::VM& vm = exec->vm();
1044 JSC::JSLockHolder locker(vm);
1045 auto scope = DECLARE_CATCH_SCOPE(vm);
1046
1047 JSC::JSValue jsValue = toJS(exec, priv->jsValue);
1048 JSC::JSObject* object = jsValue.toObject(exec);
1049 JSValueRef exception = nullptr;
1050 if (handleExceptionIfNeeded(scope, exec, &exception) == ExceptionStatus::DidThrow) {
1051 jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
1052 return;
1053 }
1054
1055 auto name = OpaqueJSString::tryCreate(String::fromUTF8(propertyName));
1056 if (!name)
1057 return;
1058
1059 JSC::PropertyDescriptor descriptor;
1060 descriptor.setValue(toJS(exec, propertyValue->priv->jsValue));
1061 descriptor.setEnumerable(flags & JSC_VALUE_PROPERTY_ENUMERABLE);
1062 descriptor.setConfigurable(flags & JSC_VALUE_PROPERTY_CONFIGURABLE);
1063 descriptor.setWritable(flags & JSC_VALUE_PROPERTY_WRITABLE);
1064 object->methodTable(vm)->defineOwnProperty(object, exec, name->identifier(&vm), descriptor, true);
1065 if (handleExceptionIfNeeded(scope, exec, &exception) == ExceptionStatus::DidThrow) {
1066 jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
1067 return;
1068 }
1069}
1070
1071/**
1072 * jsc_value_object_define_property_accessor:
1073 * @value: a #JSCValue
1074 * @property_name: the name of the property to define
1075 * @flags: #JSCValuePropertyFlags
1076 * @property_type: the #GType of the property
1077 * @getter: (scope async) (nullable): a #GCallback to be called to get the property value
1078 * @setter: (scope async) (nullable): a #GCallback to be called to set the property value
1079 * @user_data: (closure): user data to pass to @getter and @setter
1080 * @destroy_notify: (nullable): destroy notifier for @user_data
1081 *
1082 * Define or modify a property with @property_name in object referenced by @value. When the
1083 * property value needs to be getted or set, @getter and @setter callbacks will be called.
1084 * When the property is cleared in the #JSCClass context, @destroy_notify is called with
1085 * @user_data as parameter. This is equivalent to JavaScript <function>Object.defineProperty()</function>
1086 * when used with an accessor descriptor.
1087 *
1088 * Note that the value returned by @getter must be fully transferred. In case of boxed types, you could use
1089 * %G_TYPE_POINTER instead of the actual boxed #GType to ensure that the instance owned by #JSCClass is used.
1090 * If you really want to return a new copy of the boxed type, use #JSC_TYPE_VALUE and return a #JSCValue created
1091 * with jsc_value_new_object() that receives the copy as instance parameter.
1092 */
1093void jsc_value_object_define_property_accessor(JSCValue* value, const char* propertyName, JSCValuePropertyFlags flags, GType propertyType, GCallback getter, GCallback setter, gpointer userData, GDestroyNotify destroyNotify)
1094{
1095 g_return_if_fail(JSC_IS_VALUE(value));
1096 g_return_if_fail(propertyName);
1097 g_return_if_fail(propertyType != G_TYPE_INVALID && propertyType != G_TYPE_NONE);
1098 g_return_if_fail(getter || setter);
1099
1100 JSCValuePrivate* priv = value->priv;
1101 auto* jsContext = jscContextGetJSContext(priv->context.get());
1102 JSC::ExecState* exec = toJS(jsContext);
1103 JSC::VM& vm = exec->vm();
1104 JSC::JSLockHolder locker(vm);
1105 auto scope = DECLARE_CATCH_SCOPE(vm);
1106
1107 JSC::JSValue jsValue = toJS(exec, priv->jsValue);
1108 JSC::JSObject* object = jsValue.toObject(exec);
1109 JSValueRef exception = nullptr;
1110 if (handleExceptionIfNeeded(scope, exec, &exception) == ExceptionStatus::DidThrow) {
1111 jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
1112 return;
1113 }
1114
1115 auto name = OpaqueJSString::tryCreate(String::fromUTF8(propertyName));
1116 if (!name)
1117 return;
1118
1119 JSC::PropertyDescriptor descriptor;
1120 descriptor.setEnumerable(flags & JSC_VALUE_PROPERTY_ENUMERABLE);
1121 descriptor.setConfigurable(flags & JSC_VALUE_PROPERTY_CONFIGURABLE);
1122 if (getter) {
1123 GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(getter, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
1124 auto function = JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), "get"_s,
1125 JSC::JSCCallbackFunction::Type::Method, nullptr, WTFMove(closure), propertyType, Vector<GType> { });
1126 descriptor.setGetter(function);
1127 }
1128 if (setter) {
1129 GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(setter, userData, getter ? nullptr : reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
1130 auto function = JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), "set"_s,
1131 JSC::JSCCallbackFunction::Type::Method, nullptr, WTFMove(closure), G_TYPE_NONE, Vector<GType> { propertyType });
1132 descriptor.setSetter(function);
1133 }
1134 object->methodTable(vm)->defineOwnProperty(object, exec, name->identifier(&vm), descriptor, true);
1135 if (handleExceptionIfNeeded(scope, exec, &exception) == ExceptionStatus::DidThrow) {
1136 jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
1137 return;
1138 }
1139}
1140
1141static GRefPtr<JSCValue> jscValueFunctionCreate(JSCContext* context, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, Optional<Vector<GType>>&& parameters)
1142{
1143 GRefPtr<GClosure> closure;
1144 // If the function doesn't have arguments, we need to swap the fake instance and user data to ensure
1145 // user data is the first parameter and fake instance ignored.
1146 if (parameters && parameters->isEmpty() && userData)
1147 closure = adoptGRef(g_cclosure_new_swap(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
1148 else
1149 closure = adoptGRef(g_cclosure_new(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
1150 JSC::ExecState* exec = toJS(jscContextGetJSContext(context));
1151 JSC::VM& vm = exec->vm();
1152 JSC::JSLockHolder locker(vm);
1153 auto* functionObject = toRef(JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), name ? String::fromUTF8(name) : "anonymous"_s,
1154 JSC::JSCCallbackFunction::Type::Function, nullptr, WTFMove(closure), returnType, WTFMove(parameters)));
1155 return jscContextGetOrCreateValue(context, functionObject);
1156}
1157
1158/**
1159 * jsc_value_new_function: (skip)
1160 * @context: a #JSCContext:
1161 * @name: (nullable): the function name or %NULL
1162 * @callback: (scope async): a #GCallback.
1163 * @user_data: (closure): user data to pass to @callback.
1164 * @destroy_notify: (nullable): destroy notifier for @user_data
1165 * @return_type: the #GType of the function return value, or %G_TYPE_NONE if the function is void.
1166 * @n_params: the number of parameter types to follow or 0 if the function doesn't receive parameters.
1167 * @...: a list of #GType<!-- -->s, one for each parameter.
1168 *
1169 * Create a function in @context. If @name is %NULL an anonymous function will be created.
1170 * When the function is called by JavaScript or jsc_value_function_call(), @callback is called
1171 * receiving the function parameters and then @user_data as last parameter. When the function is
1172 * cleared in @context, @destroy_notify is called with @user_data as parameter.
1173 *
1174 * Note that the value returned by @callback must be fully transferred. In case of boxed types, you could use
1175 * %G_TYPE_POINTER instead of the actual boxed #GType to ensure that the instance owned by #JSCClass is used.
1176 * If you really want to return a new copy of the boxed type, use #JSC_TYPE_VALUE and return a #JSCValue created
1177 * with jsc_value_new_object() that receives the copy as instance parameter.
1178 *
1179 * Returns: (transfer full): a #JSCValue.
1180 */
1181JSCValue* jsc_value_new_function(JSCContext* context, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, unsigned paramCount, ...)
1182{
1183 g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
1184 g_return_val_if_fail(callback, nullptr);
1185
1186 va_list args;
1187 va_start(args, paramCount);
1188 Vector<GType> parameters;
1189 if (paramCount) {
1190 parameters.reserveInitialCapacity(paramCount);
1191 for (unsigned i = 0; i < paramCount; ++i)
1192 parameters.uncheckedAppend(va_arg(args, GType));
1193 }
1194 va_end(args);
1195
1196 return jscValueFunctionCreate(context, name, callback, userData, destroyNotify, returnType, WTFMove(parameters)).leakRef();
1197}
1198
1199/**
1200 * jsc_value_new_functionv: (rename-to jsc_value_new_function)
1201 * @context: a #JSCContext
1202 * @name: (nullable): the function name or %NULL
1203 * @callback: (scope async): a #GCallback.
1204 * @user_data: (closure): user data to pass to @callback.
1205 * @destroy_notify: (nullable): destroy notifier for @user_data
1206 * @return_type: the #GType of the function return value, or %G_TYPE_NONE if the function is void.
1207 * @n_parameters: the number of parameters
1208 * @parameter_types: (nullable) (array length=n_parameters) (element-type GType): a list of #GType<!-- -->s, one for each parameter, or %NULL
1209 *
1210 * Create a function in @context. If @name is %NULL an anonymous function will be created.
1211 * When the function is called by JavaScript or jsc_value_function_call(), @callback is called
1212 * receiving the function parameters and then @user_data as last parameter. When the function is
1213 * cleared in @context, @destroy_notify is called with @user_data as parameter.
1214 *
1215 * Note that the value returned by @callback must be fully transferred. In case of boxed types, you could use
1216 * %G_TYPE_POINTER instead of the actual boxed #GType to ensure that the instance owned by #JSCClass is used.
1217 * If you really want to return a new copy of the boxed type, use #JSC_TYPE_VALUE and return a #JSCValue created
1218 * with jsc_value_new_object() that receives the copy as instance parameter.
1219 *
1220 * Returns: (transfer full): a #JSCValue.
1221 */
1222JSCValue* jsc_value_new_functionv(JSCContext* context, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, unsigned parametersCount, GType *parameterTypes)
1223{
1224 g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
1225 g_return_val_if_fail(callback, nullptr);
1226 g_return_val_if_fail(!parametersCount || parameterTypes, nullptr);
1227
1228 Vector<GType> parameters;
1229 if (parametersCount) {
1230 parameters.reserveInitialCapacity(parametersCount);
1231 for (unsigned i = 0; i < parametersCount; ++i)
1232 parameters.uncheckedAppend(parameterTypes[i]);
1233 }
1234
1235 return jscValueFunctionCreate(context, name, callback, userData, destroyNotify, returnType, WTFMove(parameters)).leakRef();
1236}
1237
1238/**
1239 * jsc_value_new_function_variadic:
1240 * @context: a #JSCContext
1241 * @name: (nullable): the function name or %NULL
1242 * @callback: (scope async): a #GCallback.
1243 * @user_data: (closure): user data to pass to @callback.
1244 * @destroy_notify: (nullable): destroy notifier for @user_data
1245 * @return_type: the #GType of the function return value, or %G_TYPE_NONE if the function is void.
1246 *
1247 * Create a function in @context. If @name is %NULL an anonymous function will be created.
1248 * When the function is called by JavaScript or jsc_value_function_call(), @callback is called
1249 * receiving an #GPtrArray of #JSCValue<!-- -->s with the arguments and then @user_data as last parameter.
1250 * When the function is cleared in @context, @destroy_notify is called with @user_data as parameter.
1251 *
1252 * Note that the value returned by @callback must be fully transferred. In case of boxed types, you could use
1253 * %G_TYPE_POINTER instead of the actual boxed #GType to ensure that the instance owned by #JSCClass is used.
1254 * If you really want to return a new copy of the boxed type, use #JSC_TYPE_VALUE and return a #JSCValue created
1255 * with jsc_value_new_object() that receives the copy as instance parameter.
1256 *
1257 * Returns: (transfer full): a #JSCValue.
1258 */
1259JSCValue* jsc_value_new_function_variadic(JSCContext* context, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType)
1260{
1261 g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
1262 g_return_val_if_fail(callback, nullptr);
1263
1264 return jscValueFunctionCreate(context, name, callback, userData, destroyNotify, returnType, WTF::nullopt).leakRef();
1265}
1266
1267/**
1268 * jsc_value_is_function:
1269 * @value: a #JSCValue
1270 *
1271 * Get whether the value referenced by @value is a function
1272 *
1273 * Returns: whether the value is a function.
1274 */
1275gboolean jsc_value_is_function(JSCValue* value)
1276{
1277 g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
1278
1279 JSCValuePrivate* priv = value->priv;
1280 auto* jsContext = jscContextGetJSContext(priv->context.get());
1281 JSValueRef exception = nullptr;
1282 JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
1283 return !exception ? JSObjectIsFunction(jsContext, object) : FALSE;
1284}
1285
1286/**
1287 * jsc_value_function_call: (skip)
1288 * @value: a #JSCValue
1289 * @first_parameter_type: #GType of first parameter, or %G_TYPE_NONE
1290 * @...: value of the first parameter, followed optionally by more type/value pairs, followed by %G_TYPE_NONE
1291 *
1292 * Call function referenced by @value, passing the given parameters. If @first_parameter_type
1293 * is %G_TYPE_NONE no parameters will be passed to the function.
1294 *
1295 * This function always returns a #JSCValue, in case of void functions a #JSCValue referencing
1296 * <function>undefined</function> is returned
1297 *
1298 * Returns: (transfer full): a #JSCValue with the return value of the function.
1299 */
1300JSCValue* jsc_value_function_call(JSCValue* value, GType firstParameterType, ...)
1301{
1302 g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
1303
1304 JSCValuePrivate* priv = value->priv;
1305 auto* jsContext = jscContextGetJSContext(priv->context.get());
1306 JSValueRef exception = nullptr;
1307 JSObjectRef function = JSValueToObject(jsContext, priv->jsValue, &exception);
1308 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
1309 return jsc_value_new_undefined(priv->context.get());
1310
1311 va_list args;
1312 va_start(args, firstParameterType);
1313 auto result = jscValueCallFunction(value, function, JSC::JSCCallbackFunction::Type::Function, nullptr, firstParameterType, args);
1314 va_end(args);
1315
1316 return result.leakRef();
1317}
1318
1319/**
1320 * jsc_value_function_callv: (rename-to jsc_value_function_call)
1321 * @value: a #JSCValue
1322 * @n_parameters: the number of parameters
1323 * @parameters: (nullable) (array length=n_parameters) (element-type JSCValue): the #JSCValue<!-- -->s to pass as parameters to the function, or %NULL
1324 *
1325 * Call function referenced by @value, passing the given @parameters. If @n_parameters
1326 * is 0 no parameters will be passed to the function.
1327 *
1328 * This function always returns a #JSCValue, in case of void functions a #JSCValue referencing
1329 * <function>undefined</function> is returned
1330 *
1331 * Returns: (transfer full): a #JSCValue with the return value of the function.
1332 */
1333JSCValue* jsc_value_function_callv(JSCValue* value, unsigned parametersCount, JSCValue** parameters)
1334{
1335 g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
1336 g_return_val_if_fail(!parametersCount || parameters, nullptr);
1337
1338 JSCValuePrivate* priv = value->priv;
1339 auto* jsContext = jscContextGetJSContext(priv->context.get());
1340 JSValueRef exception = nullptr;
1341 JSObjectRef function = JSValueToObject(jsContext, priv->jsValue, &exception);
1342 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
1343 return jsc_value_new_undefined(priv->context.get());
1344
1345 Vector<JSValueRef> arguments;
1346 if (parametersCount) {
1347 arguments.reserveInitialCapacity(parametersCount);
1348 for (unsigned i = 0; i < parametersCount; ++i)
1349 arguments.uncheckedAppend(jscValueGetJSValue(parameters[i]));
1350 }
1351
1352 auto result = jsObjectCall(jsContext, function, JSC::JSCCallbackFunction::Type::Function, nullptr, arguments, &exception);
1353 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
1354 return jsc_value_new_undefined(priv->context.get());
1355
1356 return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
1357}
1358
1359/**
1360 * jsc_value_is_constructor:
1361 * @value: a #JSCValue
1362 *
1363 * Get whether the value referenced by @value is a constructor.
1364 *
1365 * Returns: whether the value is a constructor.
1366 */
1367gboolean jsc_value_is_constructor(JSCValue* value)
1368{
1369 g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
1370
1371 JSCValuePrivate* priv = value->priv;
1372 auto* jsContext = jscContextGetJSContext(priv->context.get());
1373 JSValueRef exception = nullptr;
1374 JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
1375 return !exception ? JSObjectIsConstructor(jsContext, object) : FALSE;
1376}
1377
1378/**
1379 * jsc_value_constructor_call: (skip)
1380 * @value: a #JSCValue
1381 * @first_parameter_type: #GType of first parameter, or %G_TYPE_NONE
1382 * @...: value of the first parameter, followed optionally by more type/value pairs, followed by %G_TYPE_NONE
1383 *
1384 * Invoke <function>new</function> with constructor referenced by @value. If @first_parameter_type
1385 * is %G_TYPE_NONE no parameters will be passed to the constructor.
1386 *
1387 * Returns: (transfer full): a #JSCValue referencing the newly created object instance.
1388 */
1389JSCValue* jsc_value_constructor_call(JSCValue* value, GType firstParameterType, ...)
1390{
1391 g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
1392
1393 JSCValuePrivate* priv = value->priv;
1394 auto* jsContext = jscContextGetJSContext(priv->context.get());
1395 JSValueRef exception = nullptr;
1396 JSObjectRef function = JSValueToObject(jsContext, priv->jsValue, &exception);
1397 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
1398 return jsc_value_new_undefined(priv->context.get());
1399
1400 va_list args;
1401 va_start(args, firstParameterType);
1402 auto result = jscValueCallFunction(value, function, JSC::JSCCallbackFunction::Type::Constructor, nullptr, firstParameterType, args);
1403 va_end(args);
1404
1405 return result.leakRef();
1406}
1407
1408/**
1409 * jsc_value_constructor_callv: (rename-to jsc_value_constructor_call)
1410 * @value: a #JSCValue
1411 * @n_parameters: the number of parameters
1412 * @parameters: (nullable) (array length=n_parameters) (element-type JSCValue): the #JSCValue<!-- -->s to pass as parameters to the constructor, or %NULL
1413 *
1414 * Invoke <function>new</function> with constructor referenced by @value. If @n_parameters
1415 * is 0 no parameters will be passed to the constructor.
1416 *
1417 * Returns: (transfer full): a #JSCValue referencing the newly created object instance.
1418 */
1419JSCValue* jsc_value_constructor_callv(JSCValue* value, unsigned parametersCount, JSCValue** parameters)
1420{
1421 g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
1422 g_return_val_if_fail(!parametersCount || parameters, nullptr);
1423
1424 JSCValuePrivate* priv = value->priv;
1425 auto* jsContext = jscContextGetJSContext(priv->context.get());
1426 JSValueRef exception = nullptr;
1427 JSObjectRef function = JSValueToObject(jsContext, priv->jsValue, &exception);
1428 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
1429 return jsc_value_new_undefined(priv->context.get());
1430
1431 Vector<JSValueRef> arguments;
1432 if (parametersCount) {
1433 arguments.reserveInitialCapacity(parametersCount);
1434 for (unsigned i = 0; i < parametersCount; ++i)
1435 arguments.uncheckedAppend(jscValueGetJSValue(parameters[i]));
1436 }
1437
1438 auto result = jsObjectCall(jsContext, function, JSC::JSCCallbackFunction::Type::Constructor, nullptr, arguments, &exception);
1439 if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
1440 return jsc_value_new_undefined(priv->context.get());
1441
1442 return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
1443}
1444