|
Ultralight C API 2.0.0
|
Native class definitions and instance wrappers for JavaScript.
#include <Ultralight/CAPI/CAPI_JSClass.h>
A ULJSClass defines a JavaScript class backed by native C data and callbacks. Page script can instantiate the class with new and access its prototype methods and properties, while native code can wrap existing instances for JavaScript to use.
Define a class with a constructor, a destructor, and a property, then register it on an API:
Page script creates an instance and reads its property:
Method and property callbacks registered on a definition receive the native instance pointer directly as a void* argument, while static methods receive no instance.
You can modify a definition only before its first use. First use happens when you register the class on a ULJSAPI with ulJSAPIRegisterClass() or wrap an instance with ulCreateJSObjectWithClass() or ulCreateJSObjectWithClassHolder().
You'll need to keep the ULJSClass handle for wrapping native instances, detaching wrappers, and looking up instance pointers.
Each native instance has at most one JavaScript wrapper for a class in any single context. Passing the same instance pointer to ulCreateJSObjectWithClass() or ulCreateJSObjectWithClassHolder() for that class in the same context returns that existing wrapper, preserving JavaScript object identity (===).
Instance ownership depends on how the wrapper is created:
The library destroys owned instances on the Renderer's thread during a later ulUpdate() call, after their wrapper is garbage-collected or when the page goes away. If you don't set a destructor with ulJSClassSetDestructor(), owned instances without a holder are never destroyed.
Garbage collection runs at unpredictable times, so any class that manages a scarce native resource (such as an open file or network socket) should provide an explicit close method. Calling ulJSObjectDetachInstance() releases the native instance from its wrapper and returns the instance pointer. Later script calls to that wrapper's methods or properties throw a TypeError with code ULJS_DETACHED.
Detach an instance inside a close method to free native resources early:
Detach a non-owning wrapper with ulJSObjectDetachInstance() before freeing its native instance. Because each page has its own wrapper, detach the wrapper across every context the instance was passed to so script calls throw ULJS_DETACHED instead of accessing freed memory.
When native code continues using an instance after a callback returns (such as for asynchronous work), call ulJSObjectProtectInstance() to obtain a ULJSProtectedInstance token that keeps the native instance alive. While protected, the instance won't be destroyed even if its wrapper is garbage-collected, its page goes away, or ulJSObjectDetachInstance() detaches it. Once the native work finishes, pass the token to ulJSObjectUnprotectInstance() to allow cleanup during a later ulUpdate().
Functions | |
| ULJSClass | ulCreateJSClass (const char *name) |
| Create a new, empty class definition. | |
| void | ulDestroyJSClass (ULJSClass cls) |
| Destroy a class handle (NULL-safe). | |
| bool | ulJSClassSetConstructor (ULJSClass cls, ULJSConstructorCallback callback, void *user_data, ULUserDataDestroyCallback destroy_user_data) |
| Set the constructor invoked by new. | |
| bool | ulJSClassSetDestructor (ULJSClass cls, ULJSDestructorCallback callback, void *user_data, ULUserDataDestroyCallback destroy_user_data) |
| Set the destructor that destroys instances owned by their wrappers. | |
| bool | ulJSClassAddMethod (ULJSClass cls, const char *name, ULJSMethodCallback callback, void *user_data, ULUserDataDestroyCallback destroy_user_data, unsigned attributes) |
| Add an instance method. | |
| bool | ulJSClassAddProperty (ULJSClass cls, const char *name, ULJSPropertyGetterCallback getter, ULJSPropertySetterCallback setter, void *user_data, ULUserDataDestroyCallback destroy_user_data) |
| Add an instance accessor property. | |
| bool | ulJSClassAddStaticMethod (ULJSClass cls, const char *name, ULJSFunctionCallback callback, void *user_data, ULUserDataDestroyCallback destroy_user_data) |
| Add a static method (a function on the constructor itself, eg, myApp.Database.open()). | |
| ULJSValue | ulCreateJSObjectWithClass (ULJSContext ctx, ULJSClass cls, void *instance, bool adopt) |
| Get the wrapper for a native instance, creating it if needed. | |
| ULJSValue | ulCreateJSObjectWithClassHolder (ULJSContext ctx, ULJSClass cls, void *instance, void *holder, ULUserDataDestroyCallback destroy_holder, bool *out_holder_taken) |
| Get an owning wrapper for a native instance that owns it through a holder, creating the wrapper if needed. | |
| void * | ulJSObjectGetInstance (ULJSValue object, ULJSClass cls) |
| Get the native instance behind a wrapper. | |
| ULJSInstanceState | ulJSObjectGetInstanceState (ULJSValue object, ULJSClass cls) |
| Classify a JavaScript value against a class. | |
| void * | ulJSObjectGetInstanceHolder (ULJSValue object, ULJSClass cls) |
| Get the holder a wrapper owns its instance through. | |
| void * | ulJSObjectDetachInstance (ULJSValue object, ULJSClass cls, void **out_holder) |
| Detach the native instance from its wrapper, taking back ownership. | |
| ULJSProtectedInstance | ulJSObjectProtectInstance (ULJSValue object, ULJSClass cls) |
| Keep a wrapper's native instance alive until you unprotect it. | |
| void | ulJSObjectUnprotectInstance (ULJSProtectedInstance instance) |
| Stop protecting a native instance. | |
| bool | ulJSObjectSetExternalMemoryHint (ULJSValue object, size_t bytes) |
| Tell the garbage collector how much native memory an object keeps alive. | |
Macros | |
| #define | ULTRALIGHT_ULJSCLASS_DEFINED |
| Opaque handle to a native class definition. | |
Typedefs | |
| typedef struct C_JSClass * | ULJSClass |
| typedef struct C_JSProtectedInstance * | ULJSProtectedInstance |
| Opaque token that keeps a native instance alive (see ulJSObjectProtectInstance()). | |
| typedef void *(*) | ULJSConstructorCallback(void *user_data, ULJSContext ctx, const ULJSValue *args, size_t argc, void **out_holder, ULUserDataDestroyCallback *out_destroy_holder, ULJSValue *exception) |
| Callback invoked when page script constructs an instance with new. | |
| typedef void(*) | ULJSDestructorCallback(void *user_data, void *instance) |
| Callback invoked to destroy an instance owned by its wrapper. | |
| typedef ULJSValue(*) | ULJSMethodCallback(void *user_data, ULJSContext ctx, void *instance, ULJSValue this_value, const ULJSValue *args, size_t argc, ULJSValue *exception) |
| Callback invoked when page script calls a method on an instance. | |
| typedef ULJSValue(*) | ULJSPropertyGetterCallback(void *user_data, ULJSContext ctx, void *instance, ULJSValue *exception) |
| Callback invoked when page script reads an accessor property of an instance. | |
| typedef bool(*) | ULJSPropertySetterCallback(void *user_data, ULJSContext ctx, void *instance, ULJSValue value, ULJSValue *exception) |
| Callback invoked when page script assigns an accessor property of an instance. | |
Enumerations | |
| enum | ULJSInstanceState { kULJSInstanceState_NotAnInstance = 0 , kULJSInstanceState_Attached , kULJSInstanceState_Detached } |
| How a JavaScript value relates to a class. More... | |
| ULJSClass ulCreateJSClass | ( | const char * | name | ) |
Create a new, empty class definition.
You can change the definition until its first use (registering it with a JavaScript API or wrapping an instance with it). After that, the Set and Add functions log a warning, ignore the call, and return false.
| name | The class name as a null-terminated UTF-8 string. It appears as the constructor's name and in Object.prototype.toString() output. |
| ULJSValue ulCreateJSObjectWithClass | ( | ULJSContext | ctx, |
| ULJSClass | cls, | ||
| void * | instance, | ||
| bool | adopt ) |
Get the wrapper for a native instance, creating it if needed.
If the instance already has a wrapper for this class in this context, you get that wrapper.
| ctx | The JavaScript context. |
| cls | The class definition. |
| instance | The native instance (must not be NULL). |
| adopt | Pass true to have the wrapper own the instance (the class destructor then destroys it). Pass false for a non-owning wrapper. Adopting an instance whose wrapper is non-owning makes that wrapper owning. Adopting an instance that is already owned logs a warning and changes nothing. |
| ULJSValue ulCreateJSObjectWithClassHolder | ( | ULJSContext | ctx, |
| ULJSClass | cls, | ||
| void * | instance, | ||
| void * | holder, | ||
| ULUserDataDestroyCallback | destroy_holder, | ||
| bool * | out_holder_taken ) |
Get an owning wrapper for a native instance that owns it through a holder, creating the wrapper if needed.
Use this when ownership is a token you can't rebuild from the instance pointer (eg, a shared smart pointer or a managed-runtime handle). When the wrapper is garbage-collected or its page goes away, destroy_holder(holder) runs instead of the class destructor, on the Renderer's thread during a later ulUpdate().
| ctx | The JavaScript context. |
| cls | The class definition. |
| instance | The native instance (must not be NULL). |
| holder | The ownership token (must not be NULL). |
| destroy_holder | The callback that releases holder (must not be NULL). |
| out_holder_taken | Set to true if the wrapper took holder (a new wrapper, or a non-owning wrapper that now owns its instance). Set to false if the instance's wrapper already owns it, in which case holder is still yours to release. May be NULL. |
| void ulDestroyJSClass | ( | ULJSClass | cls | ) |
Destroy a class handle (NULL-safe).
The class definition itself stays alive while a JavaScript API has it registered or any of its wrappers exist.
| cls | The class definition. |
| bool ulJSClassAddMethod | ( | ULJSClass | cls, |
| const char * | name, | ||
| ULJSMethodCallback | callback, | ||
| void * | user_data, | ||
| ULUserDataDestroyCallback | destroy_user_data, | ||
| unsigned | attributes ) |
Add an instance method.
The method is a function on the class prototype, shared by all instances. Adding a name that already exists replaces it.
| cls | The class definition. |
| name | The method name as a null-terminated UTF-8 string (required). |
| callback | The callback to invoke when page script calls the method (required). |
| user_data | Opaque user data passed to every invocation of callback. |
| destroy_user_data | Callback invoked exactly once to destroy user_data when the class definition is destroyed, when the method is replaced, or right away if the call is ignored (may be NULL). It may run on any thread. |
| attributes | A logically ORed set of ULJSPropertyAttributes flags for the method on the prototype. Pass kULJSPropertyAttributes_DontEnum for standard JavaScript class-method behavior. |
| bool ulJSClassAddProperty | ( | ULJSClass | cls, |
| const char * | name, | ||
| ULJSPropertyGetterCallback | getter, | ||
| ULJSPropertySetterCallback | setter, | ||
| void * | user_data, | ||
| ULUserDataDestroyCallback | destroy_user_data ) |
Add an instance accessor property.
Reads call the getter and assignments call the setter, so the page always gets live native state. The property is enumerable (it appears in Object.keys() and JSON.stringify() output) and can't be deleted by script. Adding a name that already exists replaces it.
| cls | The class definition. |
| name | The property name as a null-terminated UTF-8 string (required). |
| getter | The getter callback (required). |
| setter | The setter callback, or NULL to make the property read-only (assignments from script then have no effect). |
| user_data | Opaque user data passed to every invocation of getter and setter. |
| destroy_user_data | Callback invoked exactly once to destroy user_data when the class definition is destroyed, when the property is replaced, or right away if the call is ignored (may be NULL). It may run on any thread. |
| bool ulJSClassAddStaticMethod | ( | ULJSClass | cls, |
| const char * | name, | ||
| ULJSFunctionCallback | callback, | ||
| void * | user_data, | ||
| ULUserDataDestroyCallback | destroy_user_data ) |
Add a static method (a function on the constructor itself, eg, myApp.Database.open()).
A static method receives no instance. Adding a name that already exists replaces it.
| cls | The class definition. |
| name | The method name as a null-terminated UTF-8 string (required). |
| callback | The callback to invoke when page script calls the method (required). |
| user_data | Opaque user data passed to every invocation of callback. |
| destroy_user_data | Callback invoked exactly once to destroy user_data when the class definition is destroyed, when the method is replaced, or right away if the call is ignored (may be NULL). It may run on any thread. |
| bool ulJSClassSetConstructor | ( | ULJSClass | cls, |
| ULJSConstructorCallback | callback, | ||
| void * | user_data, | ||
| ULUserDataDestroyCallback | destroy_user_data ) |
Set the constructor invoked by new.
Without a constructor, new throws a TypeError (code ULJS_NO_CTOR). The class still works for instanceof checks, static methods, and instances you wrap with ulCreateJSObjectWithClass().
| cls | The class definition. |
| callback | The constructor callback. |
| user_data | Opaque user data passed to every invocation of callback. |
| destroy_user_data | Callback invoked exactly once to destroy user_data when the class definition is destroyed, when the constructor is replaced, or right away if the call is ignored (may be NULL). It may run on any thread. |
| bool ulJSClassSetDestructor | ( | ULJSClass | cls, |
| ULJSDestructorCallback | callback, | ||
| void * | user_data, | ||
| ULUserDataDestroyCallback | destroy_user_data ) |
Set the destructor that destroys instances owned by their wrappers.
Instances owned through a holder (see ulCreateJSObjectWithClassHolder()) use their holder's destroy callback instead. Without a destructor, owned instances are never destroyed.
| cls | The class definition. |
| callback | The destructor callback. |
| user_data | Opaque user data passed to every invocation of callback. |
| destroy_user_data | Callback invoked exactly once to destroy user_data when the class definition is destroyed, when the destructor is replaced, or right away if the call is ignored (may be NULL). It may run on any thread. |
Detach the native instance from its wrapper, taking back ownership.
Afterward, script calls to the wrapper's methods and accessors throw a TypeError (code ULJS_DETACHED), and the class destructor never runs for this wrapper. Wrapping the same instance again creates a new wrapper. Use this to build close methods, and to free an instance that a non-owning wrapper refers to.
| object | The wrapper. |
| cls | The class definition. |
| out_holder | Receives the holder if the wrapper owned its instance through one (you then own the holder), or NULL otherwise. Pass NULL to have such a holder destroyed on the Renderer's thread during a later ulUpdate() instead. |
Get the native instance behind a wrapper.
| object | The wrapper. |
| cls | The class definition. |
Get the holder a wrapper owns its instance through.
| object | The wrapper. |
| cls | The class definition. |
| ULJSInstanceState ulJSObjectGetInstanceState | ( | ULJSValue | object, |
| ULJSClass | cls ) |
Classify a JavaScript value against a class.
Use this to tell a detached wrapper (which deserves a "was detached" error) from a value that was never an instance.
| object | The value to classify. |
| cls | The class definition. |
| ULJSProtectedInstance ulJSObjectProtectInstance | ( | ULJSValue | object, |
| ULJSClass | cls ) |
Keep a wrapper's native instance alive until you unprotect it.
Use this when native code keeps using an instance after the call that received it returns (eg, a method that finishes its work later). While the instance is protected, the wrapper's ownership isn't released, even if the wrapper is garbage-collected, its page goes away, or it is detached with ulJSObjectDetachInstance(). Those still happen as usual (a detached wrapper still throws), but if one of them would have released the instance, the release waits until the last ulJSObjectUnprotectInstance() and then runs on the Renderer's thread during a later ulUpdate().
You can protect an instance more than once. Each call returns a token that you must pass to ulJSObjectUnprotectInstance() exactly once.
This differs from JSValueProtect(), which keeps a JavaScript value from being garbage-collected. Protecting an instance keeps the native instance alive, not the wrapper, and it holds across every way the wrapper can release it.
| object | The wrapper. |
| cls | The class definition. |
| bool ulJSObjectSetExternalMemoryHint | ( | ULJSValue | object, |
| size_t | bytes ) |
Tell the garbage collector how much native memory an object keeps alive.
Use this when a small wrapper keeps a large native allocation alive, so the collector accounts for its true cost. A new hint replaces the previous one, and 0 clears it. The hint goes away when the object is collected.
| object | The object (usually a class instance's wrapper). |
| bytes | The number of bytes of native memory the object keeps alive. |
| void ulJSObjectUnprotectInstance | ( | ULJSProtectedInstance | instance | ) |
Stop protecting a native instance.
If the wrapper's ownership was released while the instance was protected and this was the last protection, the instance is released on the Renderer's thread during a later ulUpdate(). The token works after the wrapper's page is gone.
| instance | The token returned by ulJSObjectProtectInstance() (NULL does nothing). It is no longer valid after this call. |
| #define ULTRALIGHT_ULJSCLASS_DEFINED |
Opaque handle to a native class definition.
| typedef struct C_JSClass* ULJSClass |
| typedef void *(*) ULJSConstructorCallback(void *user_data, ULJSContext ctx, const ULJSValue *args, size_t argc, void **out_holder, ULUserDataDestroyCallback *out_destroy_holder, ULJSValue *exception) |
Callback invoked when page script constructs an instance with new.
Return a new native instance. Its wrapper owns it and destroys it with the class destructor, unless you store a holder in out_holder.
| user_data | The user data supplied to ulJSClassSetConstructor(). |
| ctx | The context the constructor belongs to. Owned by the library, valid only for the duration of the callback. |
| args | The constructor arguments. Owned by the library, valid only for the duration of the callback. |
| argc | The number of entries in args. |
| out_holder | Store a holder here to have the wrapper own the instance through it instead of the class destructor (as with ulCreateJSObjectWithClassHolder()). Leave it untouched to use the class destructor. |
| out_destroy_holder | The callback that destroys out_holder. You must store one whenever you store a holder. |
| exception | To fail construction with a specific error, set *exception to a handle you own (see ulCreateJSError()); ownership transfers to the library. |
| typedef void(*) ULJSDestructorCallback(void *user_data, void *instance) |
Callback invoked to destroy an instance owned by its wrapper.
Runs once per owning wrapper, on the Renderer's thread during a later ulUpdate(), after the wrapper is garbage-collected or its page goes away. Destructions still pending when the Renderer is destroyed run during its destruction.
| user_data | The user data supplied to ulJSClassSetDestructor(). |
| instance | The instance to destroy. |
| typedef ULJSValue(*) ULJSMethodCallback(void *user_data, ULJSContext ctx, void *instance, ULJSValue this_value, const ULJSValue *args, size_t argc, ULJSValue *exception) |
Callback invoked when page script calls a method on an instance.
| user_data | The user data supplied to ulJSClassAddMethod(). |
| ctx | The context the wrapper belongs to. Owned by the library, valid only for the duration of the callback. |
| instance | The native instance (this is never NULL). |
| this_value | The instance's wrapper. Owned by the library, valid only for the duration of the callback. |
| args | The call arguments. Owned by the library, valid only for the duration of the callback. |
| argc | The number of entries in args. |
| exception | To throw an exception into JavaScript, set *exception to a handle you own; ownership transfers to the library and the return value is ignored. |
| typedef ULJSValue(*) ULJSPropertyGetterCallback(void *user_data, ULJSContext ctx, void *instance, ULJSValue *exception) |
Callback invoked when page script reads an accessor property of an instance.
| user_data | The user data supplied to ulJSClassAddProperty(). |
| ctx | The context the wrapper belongs to. Owned by the library, valid only for the duration of the callback. |
| instance | The native instance (this is never NULL). |
| exception | To throw an exception into JavaScript, set *exception to a handle you own; ownership transfers to the library and the return value is ignored. |
| typedef bool(*) ULJSPropertySetterCallback(void *user_data, ULJSContext ctx, void *instance, ULJSValue value, ULJSValue *exception) |
Callback invoked when page script assigns an accessor property of an instance.
| user_data | The user data supplied to ulJSClassAddProperty(). |
| ctx | The context the wrapper belongs to. Owned by the library, valid only for the duration of the callback. |
| instance | The native instance (this is never NULL). |
| value | The assigned value. Owned by the library, valid only for the duration of the callback. |
| exception | To throw an exception into JavaScript, set *exception to a handle you own; ownership transfers to the library and the return value is ignored. |
| typedef struct C_JSProtectedInstance* ULJSProtectedInstance |
Opaque token that keeps a native instance alive (see ulJSObjectProtectInstance()).
| enum ULJSInstanceState |