#include "config.h"
#include "JSCValue.h"
#include "APICast.h"
#include "APIUtils.h"
#include "JSCCallbackFunction.h"
#include "JSCClassPrivate.h"
#include "JSCContextPrivate.h"
#include "JSCInlines.h"
#include "JSCValuePrivate.h"
#include "JSRetainPtr.h"
#include "LiteralParser.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);
auto* jsContext = jscContextGetJSContext(context);
JSC::JSGlobalObject* globalObject = toJS(jsContext);
JSC::JSLockHolder locker(globalObject);
JSValueRef exception = nullptr;
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(globalObject, 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, context, 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());
JSC::JSGlobalObject* globalObject = toJS(jsContext);
JSC::JSLockHolder locker(globalObject);
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(globalObject, 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;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSC::JSGlobalObject* globalObject = toJS(jsContext);
JSC::VM& vm = globalObject->vm();
JSC::JSLockHolder locker(vm);
auto scope = DECLARE_CATCH_SCOPE(vm);
JSC::JSValue jsValue = toJS(globalObject, priv->jsValue);
JSC::JSObject* object = jsValue.toObject(globalObject);
JSValueRef exception = nullptr;
if (handleExceptionIfNeeded(scope, jsContext, &exception) == ExceptionStatus::DidThrow) {
jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
return;
}
auto name = OpaqueJSString::tryCreate(String::fromUTF8(propertyName));
if (!name)
return;
JSC::PropertyDescriptor descriptor;
descriptor.setValue(toJS(globalObject, propertyValue->priv->jsValue));
descriptor.setEnumerable(flags & JSC_VALUE_PROPERTY_ENUMERABLE);
descriptor.setConfigurable(flags & JSC_VALUE_PROPERTY_CONFIGURABLE);
descriptor.setWritable(flags & JSC_VALUE_PROPERTY_WRITABLE);
object->methodTable(vm)->defineOwnProperty(object, globalObject, name->identifier(&vm), descriptor, true);
if (handleExceptionIfNeeded(scope, jsContext, &exception) == ExceptionStatus::DidThrow) {
jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
return;
}
}
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;
auto* jsContext = jscContextGetJSContext(priv->context.get());
JSC::JSGlobalObject* globalObject = toJS(jsContext);
JSC::VM& vm = globalObject->vm();
JSC::JSLockHolder locker(vm);
auto scope = DECLARE_CATCH_SCOPE(vm);
JSC::JSValue jsValue = toJS(globalObject, priv->jsValue);
JSC::JSObject* object = jsValue.toObject(globalObject);
JSValueRef exception = nullptr;
if (handleExceptionIfNeeded(scope, jsContext, &exception) == ExceptionStatus::DidThrow) {
jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
return;
}
auto name = OpaqueJSString::tryCreate(String::fromUTF8(propertyName));
if (!name)
return;
JSC::PropertyDescriptor descriptor;
descriptor.setEnumerable(flags & JSC_VALUE_PROPERTY_ENUMERABLE);
descriptor.setConfigurable(flags & JSC_VALUE_PROPERTY_CONFIGURABLE);
if (getter) {
GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(getter, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
auto function = JSC::JSCCallbackFunction::create(vm, globalObject, "get"_s,
JSC::JSCCallbackFunction::Type::Method, nullptr, WTFMove(closure), propertyType, Vector<GType> { });
descriptor.setGetter(function);
}
if (setter) {
GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(setter, userData, getter ? nullptr : reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
auto function = JSC::JSCCallbackFunction::create(vm, globalObject, "set"_s,
JSC::JSCCallbackFunction::Type::Method, nullptr, WTFMove(closure), G_TYPE_NONE, Vector<GType> { propertyType });
descriptor.setSetter(function);
}
object->methodTable(vm)->defineOwnProperty(object, globalObject, name->identifier(&vm), descriptor, true);
if (handleExceptionIfNeeded(scope, jsContext, &exception) == ExceptionStatus::DidThrow) {
jscContextHandleExceptionIfNeeded(priv->context.get(), exception);
return;
}
}
static GRefPtr<JSCValue> jscValueFunctionCreate(JSCContext* context, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, Optional<Vector<GType>>&& parameters)
{
GRefPtr<GClosure> closure;
if (parameters && parameters->isEmpty() && userData)
closure = adoptGRef(g_cclosure_new_swap(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
else
closure = adoptGRef(g_cclosure_new(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
JSC::JSGlobalObject* globalObject = toJS(jscContextGetJSContext(context));
JSC::VM& vm = globalObject->vm();
JSC::JSLockHolder locker(vm);
auto* functionObject = toRef(JSC::JSCCallbackFunction::create(vm, globalObject, 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();
}
JSCValue* jsc_value_new_from_json(JSCContext* context, const char* json)
{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
if (!json)
return jsc_value_new_null(context);
auto* jsContext = jscContextGetJSContext(context);
JSC::JSGlobalObject* globalObject = toJS(jsContext);
JSC::JSLockHolder locker(globalObject);
JSValueRef exception = nullptr;
JSC::JSValue jsValue;
String jsonString = String::fromUTF8(json);
if (jsonString.is8Bit()) {
JSC::LiteralParser<LChar> jsonParser(globalObject, jsonString.characters8(), jsonString.length(), JSC::StrictJSON);
jsValue = jsonParser.tryLiteralParse();
if (!jsValue)
exception = toRef(JSC::createSyntaxError(globalObject, jsonParser.getErrorMessage()));
} else {
JSC::LiteralParser<UChar> jsonParser(globalObject, jsonString.characters16(), jsonString.length(), JSC::StrictJSON);
jsValue = jsonParser.tryLiteralParse();
if (!jsValue)
exception = toRef(JSC::createSyntaxError(globalObject, jsonParser.getErrorMessage()));
}
if (exception) {
jscContextHandleExceptionIfNeeded(context, exception);
return nullptr;
}
return jsValue ? jscContextGetOrCreateValue(context, toRef(globalObject, jsValue)).leakRef() : nullptr;
}
char* jsc_value_to_json(JSCValue* value, unsigned indent)
{
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
JSCValuePrivate* priv = value->priv;
JSValueRef exception = nullptr;
JSRetainPtr<JSStringRef> jsJSON(Adopt, JSValueCreateJSONString(jscContextGetJSContext(priv->context.get()), priv->jsValue, indent, &exception));
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return nullptr;
if (!jsJSON)
return nullptr;
size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsJSON.get());
auto* json = static_cast<char*>(g_malloc(maxSize));
if (!JSStringGetUTF8CString(jsJSON.get(), json, maxSize)) {
g_free(json);
return nullptr;
}
return json;
}