⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 230753 in webkit


Ignore:
Timestamp:
Apr 17, 2018, 11:51:33 PM (8 years ago)
Author:
Carlos Garcia Campos
Message:

[GLIB] Make it possible to handle JSCClass external properties not added to the prototype
​https://bugs.webkit.org/show_bug.cgi?id=184687

Reviewed by Michael Catanzaro.

Source/JavaScriptCore:

Add JSCClassVTable that can be optionally passed to jsc_context_register_class() to provide implmentations for
JSClassDefinition. This is required to implement dynamic properties that can't be added with
jsc_class_add_property() for example to implement something like imports object in seed/gjs.

  • API/glib/JSCClass.cpp:

(VTableExceptionHandler::VTableExceptionHandler): Helper class to handle the exceptions in vtable functions that
can throw exceptions.
(VTableExceptionHandler::~VTableExceptionHandler):
(getProperty): Iterate the class chain to call get_property function.
(setProperty): Iterate the class chain to call set_property function.
(hasProperty): Iterate the class chain to call has_property function.
(deleteProperty): Iterate the class chain to call delete_property function.
(getPropertyNames): Iterate the class chain to call enumerate_properties function.
(jsc_class_class_init): Remove constructed implementation, since we need to initialize the JSClassDefinition in
jscClassCreate now.
(jscClassCreate): Receive an optional JSCClassVTable that is used to initialize the JSClassDefinition.

  • API/glib/JSCClass.h:
  • API/glib/JSCClassPrivate.h:
  • API/glib/JSCContext.cpp:

(jscContextGetRegisteredClass): Helper to get the JSCClass for a given JSClassRef.
(jsc_context_register_class): Add JSCClassVTable parameter.

  • API/glib/JSCContext.h:
  • API/glib/JSCContextPrivate.h:
  • API/glib/JSCWrapperMap.cpp:

(JSC::WrapperMap::registeredClass const): Get the JSCClass for a given JSClassRef.

  • API/glib/JSCWrapperMap.h:
  • API/glib/docs/jsc-glib-4.0-sections.txt: Add new symbols.

Tools:

Add test cases for the new API.

  • TestWebKitAPI/Tests/JavaScriptCore/glib/TestJSC.cpp:

(fooCreate):
(fooFree):
(fooGetProperty):
(fooSetProperty):
(testJSCPromises):
(testJSCGarbageCollector):
(testsJSCVirtualMachine):

  • TestWebKitAPI/Tests/WebKitGLib/WebProcessTest.cpp:

(windowObjectClearedCallback):

Location:
trunk
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/API/glib/JSCClass.cpp

    r229798 r230753  
    2121#include "JSCClass.h"
    2222
     23#include "APICast.h"
     24#include "JSAPIWrapperObject.h"
    2325#include "JSCCallbackFunction.h"
    2426#include "JSCClassPrivate.h"
    2527#include "JSCContextPrivate.h"
     28#include "JSCExceptionPrivate.h"
    2629#include "JSCInlines.h"
    2730#include "JSCValuePrivate.h"
     31#include "JSCallbackObject.h"
     32#include "JSRetainPtr.h"
    2833#include <wtf/glib/GUniquePtr.h>
    2934#include <wtf/glib/WTFGType.h>
    … …  
    5459    CString name;
    5560    JSClassRef jsClass;
     61    JSCClassVTable* vtable;
    5662    GDestroyNotify destroyFunction;
    5763    JSCClass* parentClass;
    … …  
    7177
    7278WEBKIT_DEFINE_TYPE(JSCClass, jsc_class, G_TYPE_OBJECT)
     79
     80class VTableExceptionHandler {
     81public:
     82    VTableExceptionHandler(JSCContext* context, JSValueRef* exception)
     83        : m_context(context)
     84        , m_exception(exception)
     85        , m_savedException(exception ? jsc_context_get_exception(m_context) : nullptr)
     86    {
     87    }
     88
     89    ~VTableExceptionHandler()
     90    {
     91        if (!m_exception)
     92            return;
     93
     94        auto* exception = jsc_context_get_exception(m_context);
     95        if (m_savedException.get() == exception)
     96            return;
     97
     98        *m_exception = jscExceptionGetJSValue(exception);
     99        if (m_savedException)
     100            jsc_context_throw_exception(m_context, m_savedException.get());
     101        else
     102            jsc_context_clear_exception(m_context);
     103    }
     104
     105private:
     106    JSCContext* m_context { nullptr };
     107    JSValueRef* m_exception { nullptr };
     108    GRefPtr<JSCException> m_savedException;
     109};
     110
     111static JSValueRef getProperty(JSContextRef callerContext, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
     112{
     113    JSC::JSLockHolder locker(toJS(callerContext));
     114    auto* jsObject = toJS(object);
     115    auto context = jscContextGetOrCreate(toGlobalRef(jsObject->globalObject()->globalExec()));
     116    auto* jsContext = jscContextGetJSContext(context.get());
     117    if (!jsObject->inherits<JSC::JSCallbackObject<JSC::JSAPIWrapperObject>>(toJS(jsContext)->vm()))
     118        return nullptr;
     119
     120    gpointer instance = jscContextWrappedObject(context.get(), object);
     121    if (!instance)
     122        return nullptr;
     123
     124    VTableExceptionHandler exceptionHandler(context.get(), exception);
     125
     126    JSClassRef jsClass = JSC::jsCast<JSC::JSCallbackObject<JSC::JSAPIWrapperObject>*>(jsObject)->classRef();
     127    for (auto* jscClass = jscContextGetRegisteredClass(context.get(), jsClass); jscClass; jscClass = jscClass->priv->parentClass) {
     128        if (!jscClass->priv->vtable)
     129            continue;
     130
     131        if (auto* getPropertyFunction = jscClass->priv->vtable->get_property) {
     132            if (GRefPtr<JSCValue> value = adoptGRef(getPropertyFunction(jscClass, context.get(), instance, propertyName->string().utf8().data())))
     133                return jscValueGetJSValue(value.get());
     134        }
     135    }
     136    return nullptr;
     137}
     138
     139static bool setProperty(JSContextRef callerContext, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
     140{
     141    JSC::JSLockHolder locker(toJS(callerContext));
     142    auto* jsObject = toJS(object);
     143    auto context = jscContextGetOrCreate(toGlobalRef(jsObject->globalObject()->globalExec()));
     144    auto* jsContext = jscContextGetJSContext(context.get());
     145    if (!jsObject->inherits<JSC::JSCallbackObject<JSC::JSAPIWrapperObject>>(toJS(jsContext)->vm()))
     146        return false;
     147
     148    gpointer instance = jscContextWrappedObject(context.get(), object);
     149    if (!instance)
     150        return false;
     151
     152    VTableExceptionHandler exceptionHandler(context.get(), exception);
     153
     154    GRefPtr<JSCValue> propertyValue;
     155    JSClassRef jsClass = JSC::jsCast<JSC::JSCallbackObject<JSC::JSAPIWrapperObject>*>(jsObject)->classRef();
     156    for (auto* jscClass = jscContextGetRegisteredClass(context.get(), jsClass); jscClass; jscClass = jscClass->priv->parentClass) {
     157        if (!jscClass->priv->vtable)
     158            continue;
     159
     160        if (auto* setPropertyFunction = jscClass->priv->vtable->set_property) {
     161            if (!propertyValue)
     162                propertyValue = jscContextGetOrCreateValue(context.get(), value);
     163            if (setPropertyFunction(jscClass, context.get(), instance, propertyName->string().utf8().data(), propertyValue.get()))
     164                return true;
     165        }
     166    }
     167    return false;
     168}
     169
     170static bool hasProperty(JSContextRef callerContext, JSObjectRef object, JSStringRef propertyName)
     171{
     172    JSC::JSLockHolder locker(toJS(callerContext));
     173    auto* jsObject = toJS(object);
     174    auto context = jscContextGetOrCreate(toGlobalRef(jsObject->globalObject()->globalExec()));
     175    auto* jsContext = jscContextGetJSContext(context.get());
     176    if (!jsObject->inherits<JSC::JSCallbackObject<JSC::JSAPIWrapperObject>>(toJS(jsContext)->vm()))
     177        return false;
     178
     179    gpointer instance = jscContextWrappedObject(context.get(), object);
     180    if (!instance)
     181        return false;
     182
     183    JSClassRef jsClass = JSC::jsCast<JSC::JSCallbackObject<JSC::JSAPIWrapperObject>*>(jsObject)->classRef();
     184    for (auto* jscClass = jscContextGetRegisteredClass(context.get(), jsClass); jscClass; jscClass = jscClass->priv->parentClass) {
     185        if (!jscClass->priv->vtable)
     186            continue;
     187
     188        if (auto* hasPropertyFunction = jscClass->priv->vtable->has_property) {
     189            if (hasPropertyFunction(jscClass, context.get(), instance, propertyName->string().utf8().data()))
     190                return true;
     191        }
     192    }
     193
     194    return false;
     195}
     196
     197static bool deleteProperty(JSContextRef callerContext, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
     198{
     199    JSC::JSLockHolder locker(toJS(callerContext));
     200    auto* jsObject = toJS(object);
     201    auto context = jscContextGetOrCreate(toGlobalRef(jsObject->globalObject()->globalExec()));
     202    auto* jsContext = jscContextGetJSContext(context.get());
     203    if (!jsObject->inherits<JSC::JSCallbackObject<JSC::JSAPIWrapperObject>>(toJS(jsContext)->vm()))
     204        return false;
     205
     206    gpointer instance = jscContextWrappedObject(context.get(), object);
     207    if (!instance)
     208        return false;
     209
     210    VTableExceptionHandler exceptionHandler(context.get(), exception);
     211
     212    JSClassRef jsClass = JSC::jsCast<JSC::JSCallbackObject<JSC::JSAPIWrapperObject>*>(jsObject)->classRef();
     213    for (auto* jscClass = jscContextGetRegisteredClass(context.get(), jsClass); jscClass; jscClass = jscClass->priv->parentClass) {
     214        if (!jscClass->priv->vtable)
     215            continue;
     216
     217        if (auto* deletePropertyFunction = jscClass->priv->vtable->delete_property) {
     218            if (deletePropertyFunction(jscClass, context.get(), instance, propertyName->string().utf8().data()))
     219                return true;
     220        }
     221    }
     222    return false;
     223}
     224
     225static void getPropertyNames(JSContextRef callerContext, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames)
     226{
     227    JSC::JSLockHolder locker(toJS(callerContext));
     228    auto* jsObject = toJS(object);
     229    auto context = jscContextGetOrCreate(toGlobalRef(jsObject->globalObject()->globalExec()));
     230    auto* jsContext = jscContextGetJSContext(context.get());
     231    if (!jsObject->inherits<JSC::JSCallbackObject<JSC::JSAPIWrapperObject>>(toJS(jsContext)->vm()))
     232        return;
     233
     234    gpointer instance = jscContextWrappedObject(context.get(), object);
     235    if (!instance)
     236        return;
     237
     238    JSClassRef jsClass = JSC::jsCast<JSC::JSCallbackObject<JSC::JSAPIWrapperObject>*>(jsObject)->classRef();
     239    for (auto* jscClass = jscContextGetRegisteredClass(context.get(), jsClass); jscClass; jscClass = jscClass->priv->parentClass) {
     240        if (!jscClass->priv->vtable)
     241            continue;
     242
     243        if (auto* enumeratePropertiesFunction = jscClass->priv->vtable->enumerate_properties) {
     244            GUniquePtr<char*> properties(enumeratePropertiesFunction(jscClass, context.get(), instance));
     245            if (properties) {
     246                unsigned i = 0;
     247                while (const auto* name = properties.get()[i++]) {
     248                    JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString(name));
     249                    JSPropertyNameAccumulatorAddName(propertyNames, propertyName.get());
     250                }
     251            }
     252        }
     253    }
     254}
    73255
    74256static void jscClassGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* paramSpec)
    … …  
    122304}
    123305
    124 static void jscClassConstructed(GObject* object)
    125 {
    126     G_OBJECT_CLASS(jsc_class_parent_class)->constructed(object);
    127 
    128     JSCClassPrivate* priv = JSC_CLASS(object)->priv;
    129     JSClassDefinition definition = kJSClassDefinitionEmpty;
    130     definition.className = priv->name.data();
    131     priv->jsClass = JSClassCreate(&definition);
    132 
    133     GUniquePtr<char> prototypeName(g_strdup_printf("%sPrototype", priv->name.data()));
    134     JSClassDefinition prototypeDefinition = kJSClassDefinitionEmpty;
    135     prototypeDefinition.className = prototypeName.get();
    136     JSClassRef prototypeClass = JSClassCreate(&prototypeDefinition);
    137     priv->prototype = jscContextGetOrCreateJSWrapper(priv->context, prototypeClass);
    138     JSClassRelease(prototypeClass);
    139 
    140     if (priv->parentClass)
    141         JSObjectSetPrototype(jscContextGetJSContext(priv->context), toRef(priv->prototype.get()), toRef(priv->parentClass->priv->prototype.get()));
    142 }
    143 
    144306static void jsc_class_class_init(JSCClassClass* klass)
    145307{
    146308    GObjectClass* objClass = G_OBJECT_CLASS(klass);
    147     objClass->constructed = jscClassConstructed;
    148309    objClass->dispose = jscClassDispose;
    149310    objClass->get_property = jscClassGetProperty;
    … …  
    193354}
    194355
    195 GRefPtr<JSCClass> jscClassCreate(JSCContext* context, const char* name, JSCClass* parentClass, GDestroyNotify destroyFunction)
     356/**
     357 * JSCClassGetPropertyFunction:
     358 * @jsc_class: a #JSCClass
     359 * @context: a #JSCContext
     360 * @instance: the @jsc_class instance
     361 * @name: the property name
     362 *
     363 * The type of get_property in #JSCClassVTable. This is only required when you need to handle
     364 * external properties not added to the prototype.
     365 *
     366 * Returns: (transfer full) (nullable): a #JSCValue or %NULL to forward the request to
     367 *    the parent class or prototype chain
     368 */
     369
     370/**
     371 * JSCClassSetPropertyFunction:
     372 * @jsc_class: a #JSCClass
     373 * @context: a #JSCContext
     374 * @instance: the @jsc_class instance
     375 * @name: the property name
     376 * @value: the #JSCValue to set
     377 *
     378 * The type of set_property in #JSCClassVTable. This is only required when you need to handle
     379 * external properties not added to the prototype.
     380 *
     381 * Returns: %TRUE if handled or %FALSE to forward the request to the parent class or prototype chain.
     382 */
     383
     384/**
     385 * JSCClassHasPropertyFunction:
     386 * @jsc_class: a #JSCClass
     387 * @context: a #JSCContext
     388 * @instance: the @jsc_class instance
     389 * @name: the property name
     390 *
     391 * The type of has_property in #JSCClassVTable. This is only required when you need to handle
     392 * external properties not added to the prototype.
     393 *
     394 * Returns: %TRUE if @instance has a property with @name or %FALSE to forward the request
     395 *    to the parent class or prototype chain.
     396 */
     397
     398/**
     399 * JSCClassDeletePropertyFunction:
     400 * @jsc_class: a #JSCClass
     401 * @context: a #JSCContext
     402 * @instance: the @jsc_class instance
     403 * @name: the property name
     404 *
     405 * The type of delete_property in #JSCClassVTable. This is only required when you need to handle
     406 * external properties not added to the prototype.
     407 *
     408 * Returns: %TRUE if handled or %FALSE to to forward the request to the parent class or prototype chain.
     409 */
     410
     411/**
     412 * JSCClassEnumeratePropertiesFunction:
     413 * @jsc_class: a #JSCClass
     414 * @context: a #JSCContext
     415 * @instance: the @jsc_class instance
     416 *
     417 * The type of enumerate_properties in #JSCClassVTable. This is only required when you need to handle
     418 * external properties not added to the prototype.
     419 *
     420 * Returns: (array zero-terminated=1) (transfer full) (nullable): a %NULL-terminated array of strings
     421 *    containing the property names, or %NULL if @instance doesn't have enumerable properties.
     422 */
     423
     424/**
     425 * JSCClassVTable:
     426 * @get_property: a #JSCClassGetPropertyFunction for getting a property.
     427 * @set_property: a #JSCClassSetPropertyFunction for setting a property.
     428 * @has_property: a #JSCClassHasPropertyFunction for querying a property.
     429 * @delete_property: a #JSCClassDeletePropertyFunction for deleting a property.
     430 * @enumerate_properties: a #JSCClassEnumeratePropertiesFunction for enumerating properties.
     431 *
     432 * Virtual table for a JSCClass. This can be optionally used when registering a #JSCClass in a #JSCContext
     433 * to provide a custom implementation for the class. All virtual functions are optional and can be set to
     434 * %NULL to fallback to the default implementation.
     435 */
     436
     437GRefPtr<JSCClass> jscClassCreate(JSCContext* context, const char* name, JSCClass* parentClass, JSCClassVTable* vtable, GDestroyNotify destroyFunction)
    196438{
    197439    GRefPtr<JSCClass> jscClass = adoptGRef(JSC_CLASS(g_object_new(JSC_TYPE_CLASS, "context", context, "name", name, "parent", parentClass, nullptr)));
    198     jscClass->priv->destroyFunction = destroyFunction;
     440
     441    JSCClassPrivate* priv = jscClass->priv;
     442    priv->vtable = vtable;
     443    priv->destroyFunction = destroyFunction;
     444
     445    JSClassDefinition definition = kJSClassDefinitionEmpty;
     446    definition.className = priv->name.data();
     447
     448#define SET_IMPL_IF_NEEDED(definitionFunc, vtableFunc) \
     449    for (auto* klass = jscClass.get(); klass; klass = klass->priv->parentClass) { \
     450        if (klass->priv->vtable && klass->priv->vtable->vtableFunc) { \
     451            definition.definitionFunc = definitionFunc; \
     452            break; \
     453        } \
     454    }
     455
     456    SET_IMPL_IF_NEEDED(getProperty, get_property);
     457    SET_IMPL_IF_NEEDED(setProperty, set_property);
     458    SET_IMPL_IF_NEEDED(hasProperty, has_property);
     459    SET_IMPL_IF_NEEDED(deleteProperty, delete_property);
     460    SET_IMPL_IF_NEEDED(getPropertyNames, enumerate_properties);
     461
     462#undef SET_IMPL_IF_NEEDED
     463
     464    priv->jsClass = JSClassCreate(&definition);
     465
     466    GUniquePtr<char> prototypeName(g_strdup_printf("%sPrototype", priv->name.data()));
     467    JSClassDefinition prototypeDefinition = kJSClassDefinitionEmpty;
     468    prototypeDefinition.className = prototypeName.get();
     469    JSClassRef prototypeClass = JSClassCreate(&prototypeDefinition);
     470    priv->prototype = jscContextGetOrCreateJSWrapper(priv->context, prototypeClass);
     471    JSClassRelease(prototypeClass);
     472
     473    if (priv->parentClass)
     474        JSObjectSetPrototype(jscContextGetJSContext(priv->context), toRef(priv->prototype.get()), toRef(priv->parentClass->priv->prototype.get()));
    199475    return jscClass;
    200476}
  • trunk/Source/JavaScriptCore/API/glib/JSCClass.h

    r229799 r230753  
    3939
    4040typedef struct _JSCContext JSCContext;
     41
     42typedef JSCValue *(*JSCClassGetPropertyFunction)        (JSCClass   *jsc_class,
     43                                                         JSCContext *context,
     44                                                         gpointer    instance,
     45                                                         const char *name);
     46typedef gboolean (*JSCClassSetPropertyFunction)         (JSCClass   *jsc_class,
     47                                                         JSCContext *context,
     48                                                         gpointer    instance,
     49                                                         const char *name,
     50                                                         JSCValue   *value);
     51typedef gboolean (*JSCClassHasPropertyFunction)         (JSCClass   *jsc_class,
     52                                                         JSCContext *context,
     53                                                         gpointer    instance,
     54                                                         const char *name);
     55typedef gboolean (*JSCClassDeletePropertyFunction)      (JSCClass   *jsc_class,
     56                                                         JSCContext *context,
     57                                                         gpointer    instance,
     58                                                         const char *name);
     59typedef gchar  **(*JSCClassEnumeratePropertiesFunction) (JSCClass   *jsc_class,
     60                                                         JSCContext *context,
     61                                                         gpointer    instance);
     62
     63
     64typedef struct {
     65    JSCClassGetPropertyFunction get_property;
     66    JSCClassSetPropertyFunction set_property;
     67    JSCClassHasPropertyFunction has_property;
     68    JSCClassDeletePropertyFunction delete_property;
     69    JSCClassEnumeratePropertiesFunction enumerate_properties;
     70
     71    /*< private >*/
     72    void (*_jsc_reserved0) (void);
     73    void (*_jsc_reserved1) (void);
     74    void (*_jsc_reserved2) (void);
     75    void (*_jsc_reserved3) (void);
     76} JSCClassVTable;
    4177
    4278JSC_API GType
  • trunk/Source/JavaScriptCore/API/glib/JSCClassPrivate.h

    r229798 r230753  
    2626#include <wtf/glib/GRefPtr.h>
    2727
    28 GRefPtr<JSCClass> jscClassCreate(JSCContext*, const char*, JSCClass*, GDestroyNotify);
     28GRefPtr<JSCClass> jscClassCreate(JSCContext*, const char*, JSCClass*, JSCClassVTable*, GDestroyNotify);
    2929JSClassRef jscClassGetJSClass(JSCClass*);
    3030JSC::JSObject* jscClassGetOrCreateJSWrapper(JSCClass*, gpointer);
  • trunk/Source/JavaScriptCore/API/glib/JSCContext.cpp

    r230705 r230753  
    239239{
    240240    return wrapperMap(context).wrappedObject(context->priv->jsContext.get(), jsObject);
     241}
     242
     243JSCClass* jscContextGetRegisteredClass(JSCContext* context, JSClassRef jsClass)
     244{
     245    return wrapperMap(context).registeredClass(jsClass);
    241246}
    242247
    … …  
    763768 * @context: a #JSCContext
    764769 * @name: the class name
    765  * @parent_class: (nullable): a #JSCClass
     770 * @parent_class: (nullable): a #JSCClass or %NULL
     771 * @vtable: (nullable): an optional #JSCClassVTable or %NULL
    766772 * @destroy_notify: (nullable): a destroy notifier for class instances
    767773 *
    768774 * Register a custom class in @context using the given @name. If the new class inherits from
    769775 * another #JSCClass, the parent should be passed as @parent_class, otherwise %NULL should be
    770  * used. When an instance of the #JSCClass is cleared in the context, @destroy_notify is
    771  * called with the instance as parameter.
     776 * used. The optional @vtable parameter allows to provide a custom implementation for handling
     777 * the class, for example, to handle external properties not added to the prototype.
     778 * When an instance of the #JSCClass is cleared in the context, @destroy_notify is called with
     779 * the instance as parameter.
    772780 *
    773781 * Returns: (transfer none): a #JSCClass
    774782 */
    775 JSCClass* jsc_context_register_class(JSCContext* context, const char* name, JSCClass* parentClass, GDestroyNotify destroyFunction)
     783JSCClass* jsc_context_register_class(JSCContext* context, const char* name, JSCClass* parentClass, JSCClassVTable* vtable, GDestroyNotify destroyFunction)
    776784{
    777785    g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
    … …  
    779787    g_return_val_if_fail(!parentClass || JSC_IS_CLASS(parentClass), nullptr);
    780788
    781     auto jscClass = jscClassCreate(context, name, parentClass, destroyFunction);
     789    auto jscClass = jscClassCreate(context, name, parentClass, vtable, destroyFunction);
    782790    wrapperMap(context).registerClass(jscClass.get());
    783791    return jscClass.get();
  • trunk/Source/JavaScriptCore/API/glib/JSCContext.h

    r230705 r230753  
    127127                                      const char         *name,
    128128                                      JSCClass           *parent_class,
     129                                      JSCClassVTable     *vtable,
    129130                                      GDestroyNotify      destroy_notify);
    130131
  • trunk/Source/JavaScriptCore/API/glib/JSCContextPrivate.h

    r229798 r230753  
    3333JSC::JSObject* jscContextGetOrCreateJSWrapper(JSCContext*, JSClassRef, JSValueRef prototype = nullptr, gpointer = nullptr, GDestroyNotify = nullptr);
    3434gpointer jscContextWrappedObject(JSCContext*, JSObjectRef);
     35JSCClass* jscContextGetRegisteredClass(JSCContext*, JSClassRef);
    3536
    3637struct CallbackData {
  • trunk/Source/JavaScriptCore/API/glib/JSCWrapperMap.cpp

    r229800 r230753  
    6969}
    7070
     71JSCClass* WrapperMap::registeredClass(JSClassRef jsClass) const
     72{
     73    return m_classMap.get(jsClass);
     74}
     75
    7176JSObject* WrapperMap::createJSWrappper(JSGlobalContextRef jsContext, JSClassRef jsClass, JSValueRef prototype, gpointer wrappedObject, GDestroyNotify destroyFunction)
    7277{
  • trunk/Source/JavaScriptCore/API/glib/JSCWrapperMap.h

    r229798 r230753  
    4646
    4747    void registerClass(JSCClass*);
     48    JSCClass* registeredClass(JSClassRef) const;
    4849
    4950    JSObject* createJSWrappper(JSGlobalContextRef, JSClassRef, JSValueRef prototype, gpointer, GDestroyNotify);
  • trunk/Source/JavaScriptCore/API/glib/docs/jsc-glib-4.0-sections.txt

    r230705 r230753  
    163163<TITLE>JSCClass</TITLE>
    164164JSCClass
     165JSCClassGetPropertyFunction
     166JSCClassSetPropertyFunction
     167JSCClassHasPropertyFunction
     168JSCClassDeletePropertyFunction
     169JSCClassEnumeratePropertiesFunction
     170JSCClassVTable
    165171jsc_class_get_name
    166172jsc_class_get_parent
  • trunk/Source/JavaScriptCore/ChangeLog

    r230748 r230753  
     12018-04-17  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [GLIB] Make it possible to handle JSCClass external properties not added to the prototype
     4        https://bugs.webkit.org/show_bug.cgi?id=184687
     5
     6        Reviewed by Michael Catanzaro.
     7
     8        Add JSCClassVTable that can be optionally passed to jsc_context_register_class() to provide implmentations for
     9        JSClassDefinition. This is required to implement dynamic properties that can't be added with
     10        jsc_class_add_property() for example to implement something like imports object in seed/gjs.
     11
     12        * API/glib/JSCClass.cpp:
     13        (VTableExceptionHandler::VTableExceptionHandler): Helper class to handle the exceptions in vtable functions that
     14        can throw exceptions.
     15        (VTableExceptionHandler::~VTableExceptionHandler):
     16        (getProperty): Iterate the class chain to call get_property function.
     17        (setProperty): Iterate the class chain to call set_property function.
     18        (hasProperty): Iterate the class chain to call has_property function.
     19        (deleteProperty): Iterate the class chain to call delete_property function.
     20        (getPropertyNames): Iterate the class chain to call enumerate_properties function.
     21        (jsc_class_class_init): Remove constructed implementation, since we need to initialize the JSClassDefinition in
     22        jscClassCreate now.
     23        (jscClassCreate): Receive an optional JSCClassVTable that is used to initialize the JSClassDefinition.
     24        * API/glib/JSCClass.h:
     25        * API/glib/JSCClassPrivate.h:
     26        * API/glib/JSCContext.cpp:
     27        (jscContextGetRegisteredClass): Helper to get the JSCClass for a given JSClassRef.
     28        (jsc_context_register_class): Add JSCClassVTable parameter.
     29        * API/glib/JSCContext.h:
     30        * API/glib/JSCContextPrivate.h:
     31        * API/glib/JSCWrapperMap.cpp:
     32        (JSC::WrapperMap::registeredClass const): Get the JSCClass for a given JSClassRef.
     33        * API/glib/JSCWrapperMap.h:
     34        * API/glib/docs/jsc-glib-4.0-sections.txt: Add new symbols.
     35
    1362018-04-17  Mark Lam  <mark.lam@apple.com>
    237
  • trunk/Tools/ChangeLog

    r230752 r230753  
     12018-04-17  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [GLIB] Make it possible to handle JSCClass external properties not added to the prototype
     4        https://bugs.webkit.org/show_bug.cgi?id=184687
     5
     6        Reviewed by Michael Catanzaro.
     7
     8        Add test cases for the new API.
     9
     10        * TestWebKitAPI/Tests/JavaScriptCore/glib/TestJSC.cpp:
     11        (fooCreate):
     12        (fooFree):
     13        (fooGetProperty):
     14        (fooSetProperty):
     15        (testJSCPromises):
     16        (testJSCGarbageCollector):
     17        (testsJSCVirtualMachine):
     18        * TestWebKitAPI/Tests/WebKitGLib/WebProcessTest.cpp:
     19        (windowObjectClearedCallback):
     20
    1212018-04-17  Wenson Hsieh  <wenson_hsieh@apple.com>
    222
  • trunk/Tools/TestWebKitAPI/Tests/JavaScriptCore/glib/TestJSC.cpp

    r230705 r230753  
    945945struct _Foo {
    946946    int foo;
     947    HashMap<CString, int> properties;
    947948    Foo* sibling;
    948949};
    … …  
    950951static Foo* fooCreate()
    951952{
    952     return g_new0(Foo, 1);
     953    Foo* foo = g_new0(Foo, 1);
     954    new (foo) Foo();
     955    return foo;
    953956}
    954957
    … …  
    962965static void fooFree(Foo* foo)
    963966{
     967    foo->~Foo();
    964968    g_free(foo);
    965969}
    … …  
    988992{
    989993    foo->foo *= multiplier;
     994}
     995
     996static int fooGetProperty(Foo* foo, const char* name)
     997{
     998    auto addResult = foo->properties.add(name, 0);
     999    return addResult.iterator->value;
     1000}
     1001
     1002static void fooSetProperty(Foo* foo, const char* name, int value)
     1003{
     1004    auto addResult = foo->properties.add(name, value);
     1005    if (!addResult.isNewEntry)
     1006        addResult.iterator->value = value;
    9901007}
    9911008
    … …  
    10411058}
    10421059
     1060static JSCClassVTable fooVTable = {
     1061    // get_property
     1062    [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name) -> JSCValue* {
     1063        auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker"));
     1064        checker->watch(context);
     1065
     1066        if (!g_str_has_prefix(name, "prop_"))
     1067            return nullptr;
     1068
     1069        if (!g_strcmp0(name, "prop_throw_on_get")) {
     1070            jsc_context_throw(context, "Invalid property");
     1071            return jsc_value_new_undefined(context);
     1072        }
     1073
     1074        auto* foo = static_cast<Foo*>(instance);
     1075        auto* returnValue = jsc_value_new_number(context, fooGetProperty(foo, name));
     1076        checker->watch(returnValue);
     1077        return returnValue;
     1078    },
     1079    // set_property
     1080    [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name, JSCValue* value) -> gboolean {
     1081        auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker"));
     1082        checker->watch(context);
     1083        checker->watch(value);
     1084
     1085        if (!g_str_has_prefix(name, "prop_"))
     1086            return FALSE;
     1087
     1088        if (!jsc_value_is_number(value)) {
     1089            jsc_context_throw(context, "Invalid value set: only numbers are allowed");
     1090            return TRUE;
     1091        }
     1092
     1093        auto* foo = static_cast<Foo*>(instance);
     1094        fooSetProperty(foo, name, jsc_value_to_int32(value));
     1095        return true;
     1096    },
     1097    // has_property
     1098    [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name) -> gboolean {
     1099        auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker"));
     1100        checker->watch(context);
     1101        return g_str_has_prefix(name, "prop_");
     1102    },
     1103    // delete_property
     1104    [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name) -> gboolean {
     1105        auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker"));
     1106        checker->watch(context);
     1107
     1108        if (!g_strcmp0(name, "prop_cant_delete"))
     1109            return FALSE;
     1110
     1111        if (!g_strcmp0(name, "prop_throw_on_delete")) {
     1112            jsc_context_throw(context, "Invalid property");
     1113            return TRUE;
     1114        }
     1115
     1116        auto* foo = static_cast<Foo*>(instance);
     1117        if (!foo->properties.contains(name))
     1118            return FALSE;
     1119
     1120        foo->properties.remove(name);
     1121        return TRUE;
     1122    },
     1123    // enumerate_properties
     1124    [](JSCClass* jscClass, JSCContext* context, gpointer instance) -> char** {
     1125        auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker"));
     1126        checker->watch(context);
     1127
     1128        auto* foo = static_cast<Foo*>(instance);
     1129        GRefPtr<GPtrArray> properties = adoptGRef(g_ptr_array_new_with_free_func(g_free));
     1130        Vector<CString> names = copyToVector(foo->properties.keys());
     1131        std::sort(names.begin(), names.end());
     1132        for (const auto& name : names) {
     1133            if (g_str_has_prefix(name.data(), "prop_enum_"))
     1134                g_ptr_array_add(properties.get(), g_strdup(name.data()));
     1135        }
     1136        if (!properties->len)
     1137            return nullptr;
     1138
     1139        g_ptr_array_add(properties.get(), nullptr);
     1140        return reinterpret_cast<char**>(g_ptr_array_free(properties.leakRef(), FALSE));
     1141    },
     1142    // padding
     1143    nullptr, nullptr, nullptr, nullptr
     1144};
     1145
    10431146static void testJSCClass()
    10441147{
    … …  
    10491152        ExceptionHandler exceptionHandler(context.get());
    10501153
    1051         JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
     1154        JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
    10521155        checker.watch(jscClass);
    10531156        g_assert_false(jsc_class_get_parent(jscClass));
    … …  
    11401243        g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 52);
    11411244
    1142         JSCClass* otherClass = jsc_context_register_class(context.get(), "Baz", nullptr, g_free);
     1245        JSCClass* otherClass = jsc_context_register_class(context.get(), "Baz", nullptr, nullptr, g_free);
    11431246        checker.watch(otherClass);
    11441247        g_assert_false(jsc_class_get_parent(otherClass));
    … …  
    11651268        ExceptionHandler exceptionHandler(context.get());
    11661269
    1167         JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
     1270        JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
    11681271        checker.watch(jscClass);
    11691272
    … …  
    12061309        ExceptionHandler exceptionHandler(context.get());
    12071310
    1208         JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
     1311        JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
    12091312        checker.watch(jscClass);
    12101313
    … …  
    12411344        g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 42);
    12421345
    1243         JSCClass* bazClass = jsc_context_register_class(context.get(), "Baz", nullptr, g_free);
     1346        JSCClass* bazClass = jsc_context_register_class(context.get(), "Baz", nullptr, nullptr, g_free);
    12441347        checker.watch(bazClass);
    12451348
    … …  
    12861389        ExceptionHandler exceptionHandler(context.get());
    12871390
    1288         JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
     1391        JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
    12891392        checker.watch(jscClass);
    12901393
    … …  
    13281431        ExceptionHandler exceptionHandler(context.get());
    13291432
    1330         JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
     1433        JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
    13311434        checker.watch(jscClass);
    13321435        g_assert_false(jsc_class_get_parent(jscClass));
    … …  
    13671470        ExceptionHandler exceptionHandler(context.get());
    13681471
    1369         JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
     1472        JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
    13701473        checker.watch(jscClass);
    13711474        g_assert_false(jsc_class_get_parent(jscClass));
    … …  
    13951498        jsc_context_set_value(context.get(), "wk", jsNamespace.get());
    13961499
    1397         JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
     1500        JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
    13981501        checker.watch(jscClass);
    13991502
    … …  
    14721575        g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 62);
    14731576    }
     1577
     1578    {
     1579        LeakChecker checker;
     1580        GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
     1581        checker.watch(context.get());
     1582        ExceptionHandler exceptionHandler(context.get());
     1583
     1584        JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, &fooVTable, reinterpret_cast<GDestroyNotify>(fooFree));
     1585        checker.watch(jscClass);
     1586        g_object_set_data(G_OBJECT(jscClass), "leak-checker", &checker);
     1587
     1588        GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
     1589        checker.watch(constructor.get());
     1590        g_assert_true(jsc_value_is_constructor(constructor.get()));
     1591        jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get());
     1592        jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr);
     1593
     1594        GRefPtr<JSCValue> foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1));
     1595        checker.watch(foo.get());
     1596        g_assert_true(jsc_value_is_object(foo.get()));
     1597        g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
     1598
     1599        g_assert_true(jsc_value_object_has_property(foo.get(), "prop_whatever"));
     1600        g_assert_false(jsc_value_object_has_property(foo.get(), "whatever_prop"));
     1601
     1602        GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
     1603        g_assert_null(properties.get());
     1604
     1605        GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_1", -1));
     1606        checker.watch(result.get());
     1607        g_assert_true(jsc_value_is_number(result.get()));
     1608        g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 0);
     1609
     1610        GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_get_property(foo.get(), "prop_1"));
     1611        checker.watch(value.get());
     1612        g_assert_true(value.get() == result.get());
     1613
     1614        result = adoptGRef(jsc_context_evaluate(context.get(), "f.foo", -1));
     1615        checker.watch(result.get());
     1616        g_assert_true(jsc_value_is_number(result.get()));
     1617        g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 0);
     1618
     1619        value = adoptGRef(jsc_value_object_get_property(foo.get(), "foo"));
     1620        checker.watch(value.get());
     1621        g_assert_true(value.get() == result.get());
     1622
     1623        result = adoptGRef(jsc_context_evaluate(context.get(), "'foo' in f.__proto__", -1));
     1624        checker.watch(result.get());
     1625        g_assert_true(jsc_value_is_boolean(result.get()));
     1626        g_assert_true(jsc_value_to_boolean(result.get()));
     1627
     1628        result = adoptGRef(jsc_context_evaluate(context.get(), "'foo' in f", -1));
     1629        checker.watch(result.get());
     1630        g_assert_true(jsc_value_is_boolean(result.get()));
     1631        g_assert_true(jsc_value_to_boolean(result.get()));
     1632
     1633        result = adoptGRef(jsc_context_evaluate(context.get(), "'prop_1' in f.__proto__", -1));
     1634        checker.watch(result.get());
     1635        g_assert_true(jsc_value_is_boolean(result.get()));
     1636        g_assert_false(jsc_value_to_boolean(result.get()));
     1637
     1638        result = adoptGRef(jsc_context_evaluate(context.get(), "'prop_1' in f", -1));
     1639        checker.watch(result.get());
     1640        g_assert_true(jsc_value_is_boolean(result.get()));
     1641        g_assert_true(jsc_value_to_boolean(result.get()));
     1642
     1643        result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_1 = 25", -1));
     1644        checker.watch(result.get());
     1645        g_assert_true(jsc_value_is_number(result.get()));
     1646        g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 25);
     1647
     1648        g_assert_true(jsc_value_object_delete_property(foo.get(), "prop_1"));
     1649        g_assert_true(jsc_value_object_has_property(foo.get(), "prop_1"));
     1650        value = adoptGRef(jsc_value_object_get_property(foo.get(), "prop_1"));
     1651        checker.watch(value.get());
     1652        g_assert_true(jsc_value_is_number(value.get()));
     1653        g_assert_cmpuint(jsc_value_to_int32(value.get()), ==, 0);
     1654
     1655        result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_cant_delete = 125", -1));
     1656        checker.watch(result.get());
     1657        g_assert_true(jsc_value_is_number(result.get()));
     1658        g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 125);
     1659        jsc_value_object_delete_property(foo.get(), "prop_cant_delete");
     1660        g_assert_true(jsc_value_object_has_property(foo.get(), "prop_cant_delete"));
     1661        value = adoptGRef(jsc_value_object_get_property(foo.get(), "prop_cant_delete"));
     1662        checker.watch(value.get());
     1663        g_assert_true(jsc_value_is_number(value.get()));
     1664        g_assert_cmpuint(jsc_value_to_int32(value.get()), ==, 125);
     1665
     1666        value = adoptGRef(jsc_value_new_number(context.get(), 42));
     1667        checker.watch(value.get());
     1668        jsc_value_object_set_property(foo.get(), "prop_1", value.get());
     1669        result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_1", -1));
     1670        checker.watch(result.get());
     1671        g_assert_true(value.get() == result.get());
     1672
     1673        result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_2 = 35", -1));
     1674        checker.watch(result.get());
     1675        g_assert_true(jsc_value_is_number(result.get()));
     1676        g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 35);
     1677
     1678        result = adoptGRef(jsc_context_evaluate(context.get(), "'prop_2' in f.__proto__", -1));
     1679        checker.watch(result.get());
     1680        g_assert_true(jsc_value_is_boolean(result.get()));
     1681        g_assert_false(jsc_value_to_boolean(result.get()));
     1682
     1683        result = adoptGRef(jsc_context_evaluate(context.get(), "'prop_2' in f", -1));
     1684        checker.watch(result.get());
     1685        g_assert_true(jsc_value_is_boolean(result.get()));
     1686        g_assert_true(jsc_value_to_boolean(result.get()));
     1687
     1688        result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_enum_1 = 250", -1));
     1689        checker.watch(result.get());
     1690        g_assert_true(jsc_value_is_number(result.get()));
     1691        g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 250);
     1692        result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_enum_2 = 450", -1));
     1693        checker.watch(result.get());
     1694        g_assert_true(jsc_value_is_number(result.get()));
     1695        g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 450);
     1696
     1697        properties.reset(jsc_value_object_enumerate_properties(foo.get()));
     1698        g_assert_cmpuint(g_strv_length(properties.get()), ==, 2);
     1699        g_assert_cmpstr(properties.get()[0], ==, "prop_enum_1");
     1700        g_assert_cmpstr(properties.get()[1], ==, "prop_enum_2");
     1701        g_assert_null(properties.get()[2]);
     1702
     1703        g_assert_null(jsc_context_get_exception(context.get()));
     1704        bool didThrow = false;
     1705        g_assert_throw_begin(exceptionHandler, didThrow);
     1706        result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_throw_on_get", -1));
     1707        checker.watch(result.get());
     1708        g_assert_true(jsc_value_is_undefined(result.get()));
     1709        g_assert_did_throw(exceptionHandler, didThrow);
     1710        g_assert_null(jsc_context_get_exception(context.get()));
     1711
     1712        didThrow = false;
     1713        g_assert_throw_begin(exceptionHandler, didThrow);
     1714        result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_3 = 'not a number'", -1));
     1715        checker.watch(result.get());
     1716        g_assert_true(jsc_value_is_undefined(result.get()));
     1717        g_assert_did_throw(exceptionHandler, didThrow);
     1718        g_assert_null(jsc_context_get_exception(context.get()));
     1719
     1720        didThrow = false;
     1721        g_assert_throw_begin(exceptionHandler, didThrow);
     1722        jsc_value_object_delete_property(foo.get(), "prop_throw_on_delete");
     1723        g_assert_did_throw(exceptionHandler, didThrow);
     1724        g_assert_null(jsc_context_get_exception(context.get()));
     1725
     1726        jsc_context_throw(context.get(), "Fake exception");
     1727        GRefPtr<JSCException> previousException = jsc_context_get_exception(context.get());
     1728        checker.watch(previousException.get());
     1729        didThrow = false;
     1730        g_assert_throw_begin(exceptionHandler, didThrow);
     1731        result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_throw_on_get", -1));
     1732        checker.watch(result.get());
     1733        g_assert_true(jsc_value_is_undefined(result.get()));
     1734        g_assert_did_throw(exceptionHandler, didThrow);
     1735        g_assert_true(jsc_context_get_exception(context.get()) == previousException.get());
     1736
     1737        didThrow = false;
     1738        g_assert_throw_begin(exceptionHandler, didThrow);
     1739        result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_3 = 'not a number'", -1));
     1740        checker.watch(result.get());
     1741        g_assert_true(jsc_value_is_undefined(result.get()));
     1742        g_assert_did_throw(exceptionHandler, didThrow);
     1743        g_assert_true(jsc_context_get_exception(context.get()) == previousException.get());
     1744
     1745        didThrow = false;
     1746        g_assert_throw_begin(exceptionHandler, didThrow);
     1747        jsc_value_object_delete_property(foo.get(), "prop_throw_on_delete");
     1748        g_assert_did_throw(exceptionHandler, didThrow);
     1749        g_assert_true(jsc_context_get_exception(context.get()) == previousException.get());
     1750    }
    14741751}
    14751752
    … …  
    14991776}
    15001777
     1778static JSCClassVTable barVTable = {
     1779    // get_property
     1780    nullptr,
     1781    // set_property
     1782    nullptr,
     1783    // has_property
     1784    nullptr,
     1785    // delete_property
     1786    nullptr,
     1787    // enumerate_properties
     1788    [](JSCClass* jscClass, JSCContext* context, gpointer instance) -> char** {
     1789        auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker"));
     1790        checker->watch(context);
     1791
     1792        auto* properties = static_cast<char**>(g_malloc0(2 * sizeof(char*)));
     1793        properties[0] = g_strdup("bar");
     1794        return properties;
     1795    },
     1796    // padding
     1797    nullptr, nullptr, nullptr, nullptr
     1798};
     1799
    15011800static void testJSCPrototypes()
    15021801{
    … …  
    15071806        ExceptionHandler exceptionHandler(context.get());
    15081807
    1509         JSCClass* fooClass = jsc_context_register_class(context.get(), "Foo", nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
     1808        JSCClass* fooClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
    15101809        checker.watch(fooClass);
    15111810        g_assert_false(jsc_class_get_parent(fooClass));
    … …  
    15171816        jsc_class_add_property(fooClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr);
    15181817
    1519         JSCClass* barClass = jsc_context_register_class(context.get(), "Bar", fooClass, reinterpret_cast<GDestroyNotify>(barFree));
     1818        JSCClass* barClass = jsc_context_register_class(context.get(), "Bar", fooClass, nullptr, reinterpret_cast<GDestroyNotify>(barFree));
    15201819        checker.watch(barClass);
    15211820        g_assert_true(jsc_class_get_parent(barClass) == fooClass);
    … …  
    15971896        g_assert_true(jsc_value_is_number(value.get()));
    15981897        g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 84);
     1898    }
     1899
     1900    {
     1901        LeakChecker checker;
     1902        GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
     1903        checker.watch(context.get());
     1904        ExceptionHandler exceptionHandler(context.get());
     1905
     1906        JSCClass* fooClass = jsc_context_register_class(context.get(), "Foo", nullptr, &fooVTable, reinterpret_cast<GDestroyNotify>(fooFree));
     1907        checker.watch(fooClass);
     1908        g_object_set_data(G_OBJECT(fooClass), "leak-checker", &checker);
     1909
     1910        GRefPtr<JSCValue> fooConstructor = adoptGRef(jsc_class_add_constructor(fooClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
     1911        checker.watch(fooConstructor.get());
     1912        g_assert_true(jsc_value_is_constructor(fooConstructor.get()));
     1913        jsc_context_set_value(context.get(), jsc_class_get_name(fooClass), fooConstructor.get());
     1914        jsc_class_add_property(fooClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr);
     1915
     1916        JSCClass* barClass = jsc_context_register_class(context.get(), "Bar", fooClass, &barVTable, reinterpret_cast<GDestroyNotify>(barFree));
     1917        checker.watch(barClass);
     1918        g_object_set_data(G_OBJECT(barClass), "leak-checker", &checker);
     1919        g_assert_true(jsc_class_get_parent(barClass) == fooClass);
     1920        GRefPtr<JSCValue> barConstructor = adoptGRef(jsc_class_add_constructor(barClass, nullptr, G_CALLBACK(barCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
     1921        checker.watch(barConstructor.get());
     1922        g_assert_true(jsc_value_is_constructor(barConstructor.get()));
     1923        jsc_context_set_value(context.get(), jsc_class_get_name(barClass), barConstructor.get());
     1924        jsc_class_add_property(barClass, "bar", G_TYPE_INT, G_CALLBACK(getBar), G_CALLBACK(setBar), nullptr, nullptr);
     1925
     1926        GRefPtr<JSCValue> bar = adoptGRef(jsc_context_evaluate(context.get(), "b = new Bar();", -1));
     1927        checker.watch(bar.get());
     1928        g_assert_true(jsc_value_is_object(bar.get()));
     1929        g_assert_true(jsc_value_object_has_property(bar.get(), "bar"));
     1930        g_assert_true(jsc_value_object_has_property(bar.get(), "foo"));
     1931
     1932        g_assert_true(jsc_value_object_has_property(bar.get(), "prop_whatever"));
     1933        g_assert_false(jsc_value_object_has_property(bar.get(), "whatever_prop"));
     1934
     1935        GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(bar.get()));
     1936        g_assert_cmpuint(g_strv_length(properties.get()), ==, 1);
     1937        g_assert_cmpstr(properties.get()[0], ==, "bar");
     1938        g_assert_null(properties.get()[1]);
     1939
     1940        GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "b.prop_1", -1));
     1941        checker.watch(result.get());
     1942        g_assert_true(jsc_value_is_number(result.get()));
     1943        g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 0);
     1944
     1945        GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_get_property(bar.get(), "prop_1"));
     1946        checker.watch(value.get());
     1947        g_assert_true(value.get() == result.get());
     1948
     1949        g_assert_true(jsc_value_object_delete_property(bar.get(), "prop_1"));
     1950        g_assert_true(jsc_value_object_has_property(bar.get(), "prop_1"));
     1951        value = adoptGRef(jsc_value_object_get_property(bar.get(), "prop_1"));
     1952        checker.watch(value.get());
     1953        g_assert_true(jsc_value_is_number(value.get()));
     1954        g_assert_cmpuint(jsc_value_to_int32(value.get()), ==, 0);
     1955
     1956        result = adoptGRef(jsc_context_evaluate(context.get(), "b.prop_cant_delete = 125", -1));
     1957        checker.watch(result.get());
     1958        g_assert_true(jsc_value_is_number(result.get()));
     1959        g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 125);
     1960        jsc_value_object_delete_property(bar.get(), "prop_cant_delete");
     1961        g_assert_true(jsc_value_object_has_property(bar.get(), "prop_cant_delete"));
     1962        value = adoptGRef(jsc_value_object_get_property(bar.get(), "prop_cant_delete"));
     1963        checker.watch(value.get());
     1964        g_assert_true(jsc_value_is_number(value.get()));
     1965        g_assert_cmpuint(jsc_value_to_int32(value.get()), ==, 125);
     1966
     1967        result = adoptGRef(jsc_context_evaluate(context.get(), "b.prop_enum_1 = 250", -1));
     1968        checker.watch(result.get());
     1969        g_assert_true(jsc_value_is_number(result.get()));
     1970        g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 250);
     1971        result = adoptGRef(jsc_context_evaluate(context.get(), "b.prop_enum_2 = 450", -1));
     1972        checker.watch(result.get());
     1973        g_assert_true(jsc_value_is_number(result.get()));
     1974        g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 450);
     1975
     1976        properties.reset(jsc_value_object_enumerate_properties(bar.get()));
     1977        g_assert_cmpuint(g_strv_length(properties.get()), ==, 3);
     1978        g_assert_cmpstr(properties.get()[0], ==, "bar");
     1979        g_assert_cmpstr(properties.get()[1], ==, "prop_enum_1");
     1980        g_assert_cmpstr(properties.get()[2], ==, "prop_enum_2");
     1981        g_assert_null(properties.get()[3]);
    15991982    }
    16001983}
    … …  
    17972180        ExceptionHandler exceptionHandler(context.get());
    17982181
    1799         JSCClass* fooClass = jsc_context_register_class(context.get(), "Foo", nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
     2182        JSCClass* fooClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
    18002183        checker.watch(fooClass);
    18012184        g_assert_false(jsc_class_get_parent(fooClass));
    … …  
    18912274        s_fooWasFreed = false;
    18922275
    1893         JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, reinterpret_cast<GDestroyNotify>(fooFreeAndLog));
     2276        JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFreeAndLog));
    18942277        checker.watch(jscClass);
    18952278        g_assert_false(jsc_class_get_parent(jscClass));
    … …  
    21092492
    21102493        auto thread = Thread::create("JSCVirtualMachineTest", [&] {
    2111             JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, reinterpret_cast<GDestroyNotify>(fooFreeAndLog));
     2494            JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFreeAndLog));
    21122495            checker.watch(jscClass);
    21132496            g_assert_false(jsc_class_get_parent(jscClass));
  • trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/WebProcessTest.cpp

    r229973 r230753  
    9292    GRefPtr<JSCContext> context = adoptGRef(webkit_frame_get_js_context_for_script_world(frame, world));
    9393    WebProcessTest::assertObjectIsDeletedWhenTestFinishes(G_OBJECT(context.get()));
    94     auto* jsClass = jsc_context_register_class(context.get(), "WebProcessTestRunner", nullptr, reinterpret_cast<GDestroyNotify>(webProcessTestRunnerFinalize));
     94    auto* jsClass = jsc_context_register_class(context.get(), "WebProcessTestRunner", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(webProcessTestRunnerFinalize));
    9595    WebProcessTest::assertObjectIsDeletedWhenTestFinishes(G_OBJECT(jsClass));
    9696    jsc_class_add_method(jsClass, "runTest", G_CALLBACK(runTest), NULL, NULL, G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
Note: See TracChangeset for help on using the changeset viewer.