2020#include " config.h"
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>
3035
@@ -53,6 +58,7 @@ typedef struct _JSCClassPrivate {
5358 JSCContext* context;
5459 CString name;
5560 JSClassRef jsClass;
61+ JSCClassVTable* vtable;
5662 GDestroyNotify destroyFunction;
5763 JSCClass* parentClass;
5864 JSC::Weak<JSC::JSObject> prototype;
@@ -71,6 +77,182 @@ struct _JSCClassClass {
7177
7278WEBKIT_DEFINE_TYPE (JSCClass, jsc_class, G_TYPE_OBJECT)
7379
80+ class VTableExceptionHandler {
81+ public:
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+
105+ private:
106+ JSCContext* m_context { nullptr };
107+ JSValueRef* m_exception { nullptr };
108+ GRefPtr<JSCException> m_savedException;
109+ };
110+
111+ static 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+
139+ static 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+
170+ static 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+
197+ static 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+
225+ static 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+ }
255+
74256static void jscClassGetProperty (GObject* object, guint propID, GValue* value, GParamSpec* paramSpec)
75257{
76258 JSCClass* jscClass = JSC_CLASS (object);
@@ -121,30 +303,9 @@ static void jscClassDispose(GObject* object)
121303 G_OBJECT_CLASS (jsc_class_parent_class)->dispose (object);
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;
150311 objClass->set_property = jscClassSetProperty;
@@ -192,10 +353,125 @@ static void jsc_class_class_init(JSCClassClass* klass)
192353 static_cast <GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
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+
437+ GRefPtr<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}
201477
0 commit comments