#include "config.h"
#include "JSCValue.h"
#include "APICast.h"
#include "JSCCallbackFunction.h"
#include "JSCClassPrivate.h"
#include "JSCContextPrivate.h"
#include "JSCInlines.h"
#include "JSCValuePrivate.h"
#include "JSRetainPtr.h"
#include "OpaqueJSString.h"
#include <gobject/gvaluecollector.h>
#include <wtf/glib/GRefPtr.h>
#include <wtf/glib/GUniquePtr.h>
#include <wtf/glib/WTFGType.h>
enum {
PROP_0,
PROP_CONTEXT,
};
struct _JSCValuePrivate {
GRefPtr<JSCContext> context;
JSValueRef jsValue;
};
WEBKIT_DEFINE_TYPE(JSCValue, jsc_value, G_TYPE_OBJECT)
static void jscValueGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* paramSpec)
{
JSCValuePrivate* priv = JSC_VALUE(object)->priv;
switch (propID) {
case PROP_CONTEXT:
g_value_set_object(value, priv->context.get());
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
}
}
static void jscValueSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* paramSpec)
{
JSCValuePrivate* priv = JSC_VALUE(object)->priv;
switch (propID) {
case PROP_CONTEXT:
priv->context = JSC_CONTEXT(g_value_get_object(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
}
}
static void jscValueDispose(GObject* object)
{
JSCValuePrivate* priv = JSC_VALUE(object)->priv;
if (priv->context) {
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueUnprotect(jsContext, priv->jsValue);
jscContextValueDestroyed(priv->context.get(), priv->jsValue);
priv->jsValue = nullptr;
priv->context = nullptr;
}
G_OBJECT_CLASS(jsc_value_parent_class)->dispose(object);
}
static void jsc_value_class_init(JSCValueClass* klass)
{
GObjectClass* objClass = G_OBJECT_CLASS(klass);
objClass->get_property = jscValueGetProperty;
objClass->set_property = jscValueSetProperty;
objClass->dispose = jscValueDispose;
g_object_class_install_property(objClass,
PROP_CONTEXT,
g_param_spec_object(
"context",
"JSCContext",
"JSC Context",
JSC_TYPE_CONTEXT,
static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
}
JSValueRef jscValueGetJSValue(JSCValue* value)
{
return value->priv->jsValue;
}
JSCValue* jscValueCreate(JSCContext* context, JSValueRef jsValue)
{
auto* value = JSC_VALUE(g_object_new(JSC_TYPE_VALUE, "context", context, nullptr));
JSValueProtect(jscContextGetJSContext(context), jsValue);
value->priv->jsValue = jsValue;
return value;
}
JSCContext* jsc_value_get_context(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
return value->priv->context.get();
}
JSCValue* jsc_value_new_undefined(JSCContext* context)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
return jscContextGetOrCreateValue(context, JSValueMakeUndefined(jscContextGetJSContext(context))).leakRef();
}
gboolean jsc_value_is_undefined(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
JSCValuePrivate* priv = value->priv;
return JSValueIsUndefined(jscContextGetJSContext(priv->context.get()), priv->jsValue);
}
JSCValue* jsc_value_new_null(JSCContext* context)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
return jscContextGetOrCreateValue(context, JSValueMakeNull(jscContextGetJSContext(context))).leakRef();
}
gboolean jsc_value_is_null(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
JSCValuePrivate* priv = value->priv;
return JSValueIsNull(jscContextGetJSContext(priv->context.get()), priv->jsValue);
}
JSCValue* jsc_value_new_number(JSCContext* context, double number)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
return jscContextGetOrCreateValue(context, JSValueMakeNumber(jscContextGetJSContext(context), number)).leakRef();
}
gboolean jsc_value_is_number(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
JSCValuePrivate* priv = value->priv;
return JSValueIsNumber(jscContextGetJSContext(priv->context.get()), priv->jsValue);
}
double jsc_value_to_double(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), std::numeric_limits<double>::quiet_NaN());
JSCValuePrivate* priv = value->priv;
JSValueRef exception = nullptr;
auto result = JSValueToNumber(jscContextGetJSContext(priv->context.get()), priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return std::numeric_limits<double>::quiet_NaN();
return result;
}
gint32 jsc_value_to_int32(JSCValue* value)
{
return JSC::toInt32(jsc_value_to_double(value));
}
JSCValue* jsc_value_new_boolean(JSCContext* context, gboolean value)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
return jscContextGetOrCreateValue(context, JSValueMakeBoolean(jscContextGetJSContext(context), value)).leakRef();
}
gboolean jsc_value_is_boolean(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
JSCValuePrivate* priv = value->priv;
return JSValueIsBoolean(jscContextGetJSContext(priv->context.get()), priv->jsValue);
}
gboolean jsc_value_to_boolean(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
JSCValuePrivate* priv = value->priv;
return JSValueToBoolean(jscContextGetJSContext(priv->context.get()), priv->jsValue);
}
JSCValue* jsc_value_new_string(JSCContext* context, const char* string)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
JSValueRef jsStringValue;
if (string) {
JSRetainPtr<JSStringRef> jsString(Adopt, JSStringCreateWithUTF8CString(string));
jsStringValue = JSValueMakeString(jscContextGetJSContext(context), jsString.get());
} else
jsStringValue = JSValueMakeString(jscContextGetJSContext(context), nullptr);
return jscContextGetOrCreateValue(context, jsStringValue).leakRef();
}
JSCValue* jsc_value_new_string_from_bytes(JSCContext* context, GBytes* bytes)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
if (!bytes)
return jsc_value_new_string(context, nullptr);
gsize dataSize;
const auto* data = static_cast<const char*>(g_bytes_get_data(bytes, &dataSize));
auto string = String::fromUTF8(data, dataSize);
JSRetainPtr<JSStringRef> jsString(Adopt, OpaqueJSString::tryCreate(WTFMove(string)).leakRef());
return jscContextGetOrCreateValue(context, JSValueMakeString(jscContextGetJSContext(context), jsString.get())).leakRef();
}
gboolean jsc_value_is_string(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
JSCValuePrivate* priv = value->priv;
return JSValueIsString(jscContextGetJSContext(priv->context.get()), priv->jsValue);
}
char* jsc_value_to_string(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
JSCValuePrivate* priv = value->priv;
JSValueRef exception = nullptr;
JSRetainPtr<JSStringRef> jsString(Adopt, JSValueToStringCopy(jscContextGetJSContext(priv->context.get()), priv->jsValue, &exception));
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return nullptr;
if (!jsString)
return nullptr;
size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString.get());
auto* string = static_cast<char*>(g_malloc(maxSize));
if (!JSStringGetUTF8CString(jsString.get(), string, maxSize)) {
g_free(string);
return nullptr;
}
return string;
}
GBytes* jsc_value_to_string_as_bytes(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
JSCValuePrivate* priv = value->priv;
JSValueRef exception = nullptr;
JSRetainPtr<JSStringRef> jsString(Adopt, JSValueToStringCopy(jscContextGetJSContext(priv->context.get()), priv->jsValue, &exception));
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return nullptr;
if (!jsString)
return nullptr;
size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString.get());
if (maxSize == 1)
return g_bytes_new_static("", 0);
auto* string = static_cast<char*>(fastMalloc(maxSize));
auto stringSize = JSStringGetUTF8CString(jsString.get(), string, maxSize);
if (!stringSize) {
fastFree(string);
return nullptr;
}
return g_bytes_new_with_free_func(string, stringSize - 1, fastFree, string);
}
JSCValue* jsc_value_new_array(JSCContext* context, GType firstItemType, ...)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
JSValueRef exception = nullptr;
auto* jsContext = jscContextGetJSContext(context);
auto* jsArray = JSObjectMakeArray(jsContext, 0, nullptr, &exception);
if (jscContextHandleExceptionIfNeeded(context, exception))
return nullptr;
auto* jsArrayObject = JSValueToObject(jsContext, jsArray, &exception);
if (jscContextHandleExceptionIfNeeded(context, exception))
return nullptr;
unsigned index = 0;
va_list args;
va_start(args, firstItemType);
GType itemType = firstItemType;
while (itemType != G_TYPE_NONE) {
GValue item;
GUniqueOutPtr<char> error;
G_VALUE_COLLECT_INIT(&item, itemType, args, G_VALUE_NOCOPY_CONTENTS, &error.outPtr());
if (error) {
exception = toRef(JSC::createTypeError(toJS(jsContext), makeString("failed to collect array item: ", error.get())));
jscContextHandleExceptionIfNeeded(context, exception);
jsArray = nullptr;
break;
}
auto* jsValue = jscContextGValueToJSValue(context, &item, &exception);
g_value_unset(&item);
if (jscContextHandleExceptionIfNeeded(context, exception)) {
jsArray = nullptr;
break;
}
JSObjectSetPropertyAtIndex(jsContext, jsArrayObject, index, jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(context, exception)) {
jsArray = nullptr;
break;
}
itemType = va_arg(args, GType);
index++;
}
va_end(args);
return jsArray ? jscContextGetOrCreateValue(context, jsArray).leakRef() : nullptr;
}
JSCValue* jsc_value_new_array_from_garray(JSCContext* context, GPtrArray* gArray)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
if (!gArray || !gArray->len)
return jsc_value_new_array(context, G_TYPE_NONE);
JSValueRef exception = nullptr;
auto* jsArray = jscContextGArrayToJSArray(context, gArray, &exception);
if (jscContextHandleExceptionIfNeeded(context, exception))
return nullptr;
return jscContextGetOrCreateValue(context, jsArray).leakRef();
}
JSCValue* jsc_value_new_array_from_strv(JSCContext* context, const char* const* strv)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
auto strvLength = strv ? g_strv_length(const_cast<char**>(strv)) : 0;
if (!strvLength)
return jsc_value_new_array(context, G_TYPE_NONE);
GRefPtr<GPtrArray> gArray = adoptGRef(g_ptr_array_new_full(strvLength, g_object_unref));
for (unsigned i = 0; i < strvLength; i++)
g_ptr_array_add(gArray.get(), jsc_value_new_string(context, strv[i]));
return jsc_value_new_array_from_garray(context, gArray.get());
}
gboolean jsc_value_is_array(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
JSCValuePrivate* priv = value->priv;
return JSValueIsArray(jscContextGetJSContext(priv->context.get()), priv->jsValue);
}
JSCValue* jsc_value_new_object(JSCContext* context, gpointer instance, JSCClass* jscClass)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
g_return_val_if_fail(!instance || JSC_IS_CLASS(jscClass), nullptr);
return jscContextGetOrCreateValue(context, instance ? toRef(jscClassGetOrCreateJSWrapper(jscClass, instance)) : JSObjectMake(jscContextGetJSContext(context), nullptr, nullptr)).leakRef();
}
gboolean jsc_value_is_object(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
JSCValuePrivate* priv = value->priv;
return JSValueIsObject(jscContextGetJSContext(priv->context.get()), priv->jsValue);
}
gboolean jsc_value_object_is_instance_of(JSCValue* value, const char* name)
{
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
g_return_val_if_fail(name, FALSE);
JSCValuePrivate* priv = value->priv;
GRefPtr<JSCValue> constructor = adoptGRef(jsc_context_evaluate(priv->context.get(), name, -1));
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef object = JSValueToObject(jsContext, constructor->priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return FALSE;
gboolean returnValue = JSValueIsInstanceOfConstructor(jsContext, priv->jsValue, object, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return FALSE;
return returnValue;
}
void jsc_value_object_set_property(JSCValue* value, const char* name, JSCValue* property)
{
g_return_if_fail(JSC_IS_VALUE(value));
g_return_if_fail(name);
g_return_if_fail(JSC_IS_VALUE(property));
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return;
JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString(name));
JSObjectSetProperty(jsContext, object, propertyName.get(), property->priv->jsValue, kJSPropertyAttributeNone, &exception);
jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
}
JSCValue* jsc_value_object_get_property(JSCValue* value, const char* name)
{
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
g_return_val_if_fail(name, nullptr);
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString(name));
JSValueRef result = JSObjectGetProperty(jsContext, object, propertyName.get(), &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
}
void jsc_value_object_set_property_at_index(JSCValue* value, unsigned index, JSCValue* property)
{
g_return_if_fail(JSC_IS_VALUE(value));
g_return_if_fail(JSC_IS_VALUE(property));
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return;
JSObjectSetPropertyAtIndex(jsContext, object, index, property->priv->jsValue, &exception);
jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
}
JSCValue* jsc_value_object_get_property_at_index(JSCValue* value, unsigned index)
{
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
JSValueRef result = JSObjectGetPropertyAtIndex(jsContext, object, index, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
}
gboolean jsc_value_object_has_property(JSCValue* value, const char* name)
{
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
g_return_val_if_fail(name, FALSE);
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return FALSE;
JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString(name));
return JSObjectHasProperty(jsContext, object, propertyName.get());
}
gboolean jsc_value_object_delete_property(JSCValue* value, const char* name)
{
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
g_return_val_if_fail(name, FALSE);
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return FALSE;
JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString(name));
gboolean result = JSObjectDeleteProperty(jsContext, object, propertyName.get(), &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return FALSE;
return result;
}
char** jsc_value_object_enumerate_properties(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return nullptr;
auto* propertiesArray = JSObjectCopyPropertyNames(jsContext, object);
if (!propertiesArray)
return nullptr;
auto propertiesArraySize = JSPropertyNameArrayGetCount(propertiesArray);
if (!propertiesArraySize) {
JSPropertyNameArrayRelease(propertiesArray);
return nullptr;
}
auto* result = static_cast<char**>(g_new0(char*, propertiesArraySize + 1));
for (unsigned i = 0; i < propertiesArraySize; ++i) {
auto* jsString = JSPropertyNameArrayGetNameAtIndex(propertiesArray, i);
size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString);
auto* string = static_cast<char*>(g_malloc(maxSize));
JSStringGetUTF8CString(jsString, string, maxSize);
result[i] = string;
}
JSPropertyNameArrayRelease(propertiesArray);
return result;
}
static JSValueRef jsObjectCall(JSGlobalContextRef jsContext, JSObjectRef function, JSC::JSCCallbackFunction::Type functionType, JSObjectRef thisObject, const Vector<JSValueRef>& arguments, JSValueRef* exception)
{
switch (functionType) {
case JSC::JSCCallbackFunction::Type::Constructor:
return JSObjectCallAsConstructor(jsContext, function, arguments.size(), arguments.data(), exception);
break;
case JSC::JSCCallbackFunction::Type::Method:
ASSERT(thisObject);
FALLTHROUGH;
case JSC::JSCCallbackFunction::Type::Function:
return JSObjectCallAsFunction(jsContext, function, thisObject, arguments.size(), arguments.data(), exception);
break;
}
RELEASE_ASSERT_NOT_REACHED();
}
static GRefPtr<JSCValue> jscValueCallFunction(JSCValue* value, JSObjectRef function, JSC::JSCCallbackFunction::Type functionType, JSObjectRef thisObject, GType firstParameterType, va_list args)
{
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
Vector<JSValueRef> arguments;
GType parameterType = firstParameterType;
while (parameterType != G_TYPE_NONE) {
GValue parameter;
GUniqueOutPtr<char> error;
G_VALUE_COLLECT_INIT(¶meter, parameterType, args, G_VALUE_NOCOPY_CONTENTS, &error.outPtr());
if (error) {
exception = toRef(JSC::createTypeError(toJS(jsContext), makeString("failed to collect function paramater: ", error.get())));
jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
return adoptGRef(jsc_value_new_undefined(priv->context.get()));
}
auto* jsValue = jscContextGValueToJSValue(priv->context.get(), ¶meter, &exception);
g_value_unset(¶meter);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jscContextGetOrCreateValue(priv->context.get(), jsValue);
arguments.append(jsValue);
parameterType = va_arg(args, GType);
}
auto result = jsObjectCall(jsContext, function, functionType, thisObject, arguments, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return adoptGRef(jsc_value_new_undefined(priv->context.get()));
return jscContextGetOrCreateValue(priv->context.get(), result);
}
JSCValue* jsc_value_object_invoke_method(JSCValue* value, const char* name, GType firstParameterType, ...)
{
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
g_return_val_if_fail(name, nullptr);
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
JSRetainPtr<JSStringRef> methodName(Adopt, JSStringCreateWithUTF8CString(name));
JSValueRef functionValue = JSObjectGetProperty(jsContext, object, methodName.get(), &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
JSObjectRef function = JSValueToObject(jsContext, functionValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
va_list args;
va_start(args, firstParameterType);
auto result = jscValueCallFunction(value, function, JSC::JSCCallbackFunction::Type::Method, object, firstParameterType, args);
va_end(args);
return result.leakRef();
}
JSCValue* jsc_value_object_invoke_methodv(JSCValue* value, const char* name, unsigned parametersCount, JSCValue** parameters)
{
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
g_return_val_if_fail(name, nullptr);
g_return_val_if_fail(!parametersCount || parameters, nullptr);
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
JSRetainPtr<JSStringRef> methodName(Adopt, JSStringCreateWithUTF8CString(name));
JSValueRef functionValue = JSObjectGetProperty(jsContext, object, methodName.get(), &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
JSObjectRef function = JSValueToObject(jsContext, functionValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
Vector<JSValueRef> arguments;
if (parametersCount) {
arguments.reserveInitialCapacity(parametersCount);
for (unsigned i = 0; i < parametersCount; ++i)
arguments.uncheckedAppend(jscValueGetJSValue(parameters[i]));
}
auto result = jsObjectCall(jsContext, function, JSC::JSCCallbackFunction::Type::Method, object, arguments, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
jsc_value_new_undefined(priv->context.get());
return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
}
void jsc_value_object_define_property_data(JSCValue* value, const char* propertyName, JSCValuePropertyFlags flags, JSCValue* propertyValue)
{
g_return_if_fail(JSC_IS_VALUE(value));
g_return_if_fail(propertyName);
JSCValuePrivate* priv = value->priv;
GRefPtr<JSCValue> descriptor = adoptGRef(jsc_value_new_object(priv->context.get(), nullptr, nullptr));
GRefPtr<JSCValue> trueValue = adoptGRef(jsc_value_new_boolean(priv->context.get(), TRUE));
if (flags & JSC_VALUE_PROPERTY_CONFIGURABLE)
jsc_value_object_set_property(descriptor.get(), "configurable", trueValue.get());
if (flags & JSC_VALUE_PROPERTY_ENUMERABLE)
jsc_value_object_set_property(descriptor.get(), "enumerable", trueValue.get());
if (propertyValue)
jsc_value_object_set_property(descriptor.get(), "value", propertyValue);
if (flags & JSC_VALUE_PROPERTY_WRITABLE)
jsc_value_object_set_property(descriptor.get(), "writable", trueValue.get());
GRefPtr<JSCValue> object = adoptGRef(jsc_context_get_value(priv->context.get(), "Object"));
GRefPtr<JSCValue> result = adoptGRef(jsc_value_object_invoke_method(object.get(), "defineProperty",
JSC_TYPE_VALUE, value, G_TYPE_STRING, propertyName, JSC_TYPE_VALUE, descriptor.get(), G_TYPE_NONE));
}
void jsc_value_object_define_property_accessor(JSCValue* value, const char* propertyName, JSCValuePropertyFlags flags, GType propertyType, GCallback getter, GCallback setter, gpointer userData, GDestroyNotify destroyNotify)
{
g_return_if_fail(JSC_IS_VALUE(value));
g_return_if_fail(propertyName);
g_return_if_fail(propertyType != G_TYPE_INVALID && propertyType != G_TYPE_NONE);
g_return_if_fail(getter || setter);
JSCValuePrivate* priv = value->priv;
GRefPtr<JSCValue> descriptor = adoptGRef(jsc_value_new_object(priv->context.get(), nullptr, nullptr));
GRefPtr<JSCValue> trueValue = adoptGRef(jsc_value_new_boolean(priv->context.get(), TRUE));
if (flags & JSC_VALUE_PROPERTY_CONFIGURABLE)
jsc_value_object_set_property(descriptor.get(), "configurable", trueValue.get());
if (flags & JSC_VALUE_PROPERTY_ENUMERABLE)
jsc_value_object_set_property(descriptor.get(), "enumerable", trueValue.get());
JSC::ExecState* exec = toJS(jscContextGetJSContext(priv->context.get()));
JSC::VM& vm = exec->vm();
JSC::JSLockHolder locker(vm);
if (getter) {
GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(getter, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
auto* functionObject = toRef(JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), "get"_s,
JSC::JSCCallbackFunction::Type::Method, nullptr, WTFMove(closure), propertyType, Vector<GType> { }));
GRefPtr<JSCValue> function = jscContextGetOrCreateValue(priv->context.get(), functionObject);
jsc_value_object_set_property(descriptor.get(), "get", function.get());
}
if (setter) {
GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(setter, userData, getter ? nullptr : reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
auto* functionObject = toRef(JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), "set"_s,
JSC::JSCCallbackFunction::Type::Method, nullptr, WTFMove(closure), G_TYPE_NONE, Vector<GType> { propertyType }));
GRefPtr<JSCValue> function = jscContextGetOrCreateValue(priv->context.get(), functionObject);
jsc_value_object_set_property(descriptor.get(), "set", function.get());
}
GRefPtr<JSCValue> object = adoptGRef(jsc_context_get_value(priv->context.get(), "Object"));
GRefPtr<JSCValue> result = adoptGRef(jsc_value_object_invoke_method(object.get(), "defineProperty",
JSC_TYPE_VALUE, value, G_TYPE_STRING, propertyName, JSC_TYPE_VALUE, descriptor.get(), G_TYPE_NONE));
}
static GRefPtr<JSCValue> jscValueFunctionCreate(JSCContext* context, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, Optional<Vector<GType>>&& parameters)
{
GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
JSC::ExecState* exec = toJS(jscContextGetJSContext(context));
JSC::VM& vm = exec->vm();
JSC::JSLockHolder locker(vm);
auto* functionObject = toRef(JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), name ? String::fromUTF8(name) : "anonymous"_s,
JSC::JSCCallbackFunction::Type::Function, nullptr, WTFMove(closure), returnType, WTFMove(parameters)));
return jscContextGetOrCreateValue(context, functionObject);
}
JSCValue* jsc_value_new_function(JSCContext* context, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, unsigned paramCount, ...)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
g_return_val_if_fail(callback, nullptr);
va_list args;
va_start(args, paramCount);
Vector<GType> parameters;
if (paramCount) {
parameters.reserveInitialCapacity(paramCount);
for (unsigned i = 0; i < paramCount; ++i)
parameters.uncheckedAppend(va_arg(args, GType));
}
va_end(args);
return jscValueFunctionCreate(context, name, callback, userData, destroyNotify, returnType, WTFMove(parameters)).leakRef();
}
JSCValue* jsc_value_new_functionv(JSCContext* context, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, unsigned parametersCount, GType *parameterTypes)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
g_return_val_if_fail(callback, nullptr);
g_return_val_if_fail(!parametersCount || parameterTypes, nullptr);
Vector<GType> parameters;
if (parametersCount) {
parameters.reserveInitialCapacity(parametersCount);
for (unsigned i = 0; i < parametersCount; ++i)
parameters.uncheckedAppend(parameterTypes[i]);
}
return jscValueFunctionCreate(context, name, callback, userData, destroyNotify, returnType, WTFMove(parameters)).leakRef();
}
JSCValue* jsc_value_new_function_variadic(JSCContext* context, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
g_return_val_if_fail(callback, nullptr);
return jscValueFunctionCreate(context, name, callback, userData, destroyNotify, returnType, WTF::nullopt).leakRef();
}
gboolean jsc_value_is_function(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
return !exception ? JSObjectIsFunction(jsContext, object) : FALSE;
}
JSCValue* jsc_value_function_call(JSCValue* value, GType firstParameterType, ...)
{
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef function = JSValueToObject(jsContext, priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
va_list args;
va_start(args, firstParameterType);
auto result = jscValueCallFunction(value, function, JSC::JSCCallbackFunction::Type::Function, nullptr, firstParameterType, args);
va_end(args);
return result.leakRef();
}
JSCValue* jsc_value_function_callv(JSCValue* value, unsigned parametersCount, JSCValue** parameters)
{
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
g_return_val_if_fail(!parametersCount || parameters, nullptr);
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef function = JSValueToObject(jsContext, priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
Vector<JSValueRef> arguments;
if (parametersCount) {
arguments.reserveInitialCapacity(parametersCount);
for (unsigned i = 0; i < parametersCount; ++i)
arguments.uncheckedAppend(jscValueGetJSValue(parameters[i]));
}
auto result = jsObjectCall(jsContext, function, JSC::JSCCallbackFunction::Type::Function, nullptr, arguments, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
}
gboolean jsc_value_is_constructor(JSCValue* value)
{
g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
return !exception ? JSObjectIsConstructor(jsContext, object) : FALSE;
}
JSCValue* jsc_value_constructor_call(JSCValue* value, GType firstParameterType, ...)
{
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef function = JSValueToObject(jsContext, priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
va_list args;
va_start(args, firstParameterType);
auto result = jscValueCallFunction(value, function, JSC::JSCCallbackFunction::Type::Constructor, nullptr, firstParameterType, args);
va_end(args);
return result.leakRef();
}
JSCValue* jsc_value_constructor_callv(JSCValue* value, unsigned parametersCount, JSCValue** parameters)
{
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
g_return_val_if_fail(!parametersCount || parameters, nullptr);
JSCValuePrivate* priv = value->priv;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSValueRef exception = nullptr;
JSObjectRef function = JSValueToObject(jsContext, priv->jsValue, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
Vector<JSValueRef> arguments;
if (parametersCount) {
arguments.reserveInitialCapacity(parametersCount);
for (unsigned i = 0; i < parametersCount; ++i)
arguments.uncheckedAppend(jscValueGetJSValue(parameters[i]));
}
auto result = jsObjectCall(jsContext, function, JSC::JSCCallbackFunction::Type::Constructor, nullptr, arguments, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
}