xjs — Objects, Functions & Promises
Introduction
xJSObjectRef is a specialisation of xJSValueRef restricted to the JavaScript Object type — arrays, dates, errors, regexps, functions, constructors, promises, and native class instances all show up as xJSObjectRef. Every xJSObjectRef is binary-compatible with xJSValueRef and follows the same value lifetime rules.
Creating Objects
Generic object
xJSObjectRef xJSObjectMake(xJSContextRef ctx, xJSClassRef cls, void *data);
cls == NULL produces a plain {}. Pass a class created by xJSClassCreate to wrap a C struct — data is stored in the object's private slot and retrieved via xJSObjectGetPrivate.
Host-callable function
xJSObjectRef xJSObjectMakeFunctionWithCallback(
xJSContextRef ctx, xJSStringRef name,
xJSObjectCallAsFunctionCallback cb);
The returned object is indistinguishable from a JS function (typeof fn === "function", callable from user code).
static xJSValueRef add(xJSContextRef ctx, xJSObjectRef fn, xJSObjectRef thiz,
size_t argc, const xJSValueRef argv[],
xJSValueRef *exc) {
(void)fn; (void)thiz;
double a = argc > 0 ? xJSValueToNumber(ctx, argv[0], exc) : 0;
double b = argc > 1 ? xJSValueToNumber(ctx, argv[1], exc) : 0;
return xJSValueMakeNumber(ctx, a + b);
}
xJSStringRef name = xJSStringCreateWithUTF8CString("add");
xJSObjectRef fn = xJSObjectMakeFunctionWithCallback(ctx, name, add);
xJSStringRelease(name);
Constructor for a native class
xJSObjectRef xJSObjectMakeConstructor(
xJSContextRef ctx, xJSClassRef cls,
xJSObjectCallAsConstructorCallback ctor);
Registers cls against the context's runtime on first use, then returns a function that — when invoked with new — calls ctor. See class.md for the full flow.
Compile-at-runtime function
xJSObjectRef xJSObjectMakeFunction(
xJSContextRef ctx, xJSStringRef name,
unsigned parameterCount, const xJSStringRef parameterNames[],
xJSStringRef body, xJSStringRef sourceURL, int startingLineNumber,
xJSValueRef *exception);
Equivalent to new Function(...parameterNames, body). Compile errors surface via *exception and a NULL return.
Built-in specialisations
xJSObjectRef xJSObjectMakeArray (xJSContextRef, size_t argc, const xJSValueRef argv[], xJSValueRef *exc);
xJSObjectRef xJSObjectMakeDate (xJSContextRef, size_t argc, const xJSValueRef argv[], xJSValueRef *exc);
xJSObjectRef xJSObjectMakeError (xJSContextRef, size_t argc, const xJSValueRef argv[], xJSValueRef *exc);
xJSObjectRef xJSObjectMakeRegExp(xJSContextRef, size_t argc, const xJSValueRef argv[], xJSValueRef *exc);
Each is a thin shortcut for new Array(...) / new Date(...) / etc.
Deferred promise (for async host work)
xJSObjectRef xJSObjectMakeDeferredPromise(
xJSContextRef ctx,
xJSObjectRef *resolve, xJSObjectRef *reject,
xJSValueRef *exception);
Returns a pending Promise plus its resolve/reject functions. The typical flow:
- Kick off async work in host land; capture
ctx,resolve,reject. - Return the promise to JavaScript.
- When the work completes, call
xJSObjectCallAsFunction(ctx, resolve, NULL, 1, &result, &exc);(orreject). - Call
xJSContextDrainPendingJobs(ctx, …)so the.thenreactions run. - Release the three
xJSObjectRefhandles once you no longer need them.
Accessing Object Properties
By string key
bool xJSObjectHasProperty (xJSContextRef, xJSObjectRef, xJSStringRef);
xJSValueRef xJSObjectGetProperty (xJSContextRef, xJSObjectRef, xJSStringRef, xJSValueRef *exc);
void xJSObjectSetProperty (xJSContextRef, xJSObjectRef, xJSStringRef,
xJSValueRef value,
xJSPropertyAttributes attrs,
xJSValueRef *exc);
bool xJSObjectDeleteProperty (xJSContextRef, xJSObjectRef, xJSStringRef, xJSValueRef *exc);
Attribute flags (bit-ORed into attrs):
kXJSPropertyAttributeNone = 0
kXJSPropertyAttributeReadOnly = 1 << 1
kXJSPropertyAttributeDontEnum = 1 << 2
kXJSPropertyAttributeDontDelete = 1 << 3
By integer index
xJSValueRef xJSObjectGetPropertyAtIndex(xJSContextRef, xJSObjectRef,
unsigned idx, xJSValueRef *exc);
void xJSObjectSetPropertyAtIndex(xJSContextRef, xJSObjectRef,
unsigned idx, xJSValueRef value,
xJSValueRef *exc);
Faster than the string variant for arrays and typed arrays.
Enumeration
xJSPropertyNameArrayRef names = xJSObjectCopyPropertyNames(ctx, obj);
size_t n = xJSPropertyNameArrayGetCount(names);
for (size_t i = 0; i < n; ++i) {
xJSStringRef k = xJSPropertyNameArrayGetNameAtIndex(names, i);
// … inspect k …
}
xJSPropertyNameArrayRelease(names);
Only own, enumerable, string-keyed properties are listed (matching Object.keys). Symbol keys require lowering into JS (Reflect.ownKeys(...)).
Prototype
xJSValueRef xJSObjectGetPrototype(xJSContextRef, xJSObjectRef);
void xJSObjectSetPrototype(xJSContextRef, xJSObjectRef, xJSValueRef proto);
Pass xJSValueMakeNull(ctx) to detach the prototype.
Calling Functions and Constructors
bool xJSObjectIsFunction (xJSContextRef, xJSObjectRef);
xJSValueRef xJSObjectCallAsFunction(xJSContextRef, xJSObjectRef fn,
xJSObjectRef thisObj,
size_t argc, const xJSValueRef argv[],
xJSValueRef *exception);
bool xJSObjectIsConstructor (xJSContextRef, xJSObjectRef);
xJSObjectRef xJSObjectCallAsConstructor(xJSContextRef, xJSObjectRef ctor,
size_t argc, const xJSValueRef argv[],
xJSValueRef *exception);
Passing thisObj == NULL in CallAsFunction uses globalThis as this, matching JSC.
Private Data
For instances of a class created via xJSClassCreate, an opaque void * slot is available:
void *xJSObjectGetPrivate(xJSObjectRef obj);
bool xJSObjectSetPrivate(xJSObjectRef obj, void *data);
Set returns false when called on a plain object (no class → no private slot). The private pointer is handed back from the finalize callback so you can free it; xjs does not take ownership of it.
Worked Example — Call a JS function from C
// const x = 5; add(x, 7) → 12
xJSStringRef nameK = xJSStringCreateWithUTF8CString("add");
xJSObjectRef g = xJSContextGetGlobalObject(ctx);
xJSValueRef fn = xJSObjectGetProperty(ctx, g, nameK, NULL);
xJSValueUnprotect(ctx, (xJSValueRef)g);
xJSStringRelease(nameK);
xJSValueRef args[2] = {
xJSValueMakeNumber(ctx, 5),
xJSValueMakeNumber(ctx, 7),
};
xJSValueRef exc = NULL;
xJSValueRef r = xJSObjectCallAsFunction(ctx, (xJSObjectRef)fn, NULL,
2, args, &exc);
for (int i = 0; i < 2; ++i) xJSValueUnprotect(ctx, args[i]);
xJSValueUnprotect(ctx, fn);
if (!r) { /* exc populated */ }
else {
printf("add(5,7) = %g\n", xJSValueToNumber(ctx, r, NULL));
xJSValueUnprotect(ctx, r);
}
Caveats
- Native callbacks are invoked synchronously from JS. Long-running work must be offloaded to a host thread and surfaced via
xJSObjectMakeDeferredPromiseso JS stays responsive. - Returning one of the incoming
argv[i](orthisObject/function) from a callback is supported — xjs detects the aliasing and does not double-release. Returning a freshly built value is also fine; the wrapper extracts the underlyingJSValueand releases the slot for you. xJSPropertyNameArrayRefowns a retained copy of each name; the strings returned byGetNameAtIndexare alive for as long as the array is. Do notxJSStringReleasethem directly.