xjs — Context & Runtime Lifecycle
Introduction
Every JavaScript operation in xjs happens inside a global context, which in turn lives inside a context group. The group owns the JS runtime (GC heap, class table, module loader); the context owns the global object and the "value slot" pool used to hand xJSValueRef handles back to host code.
Both handles are reference-counted and mirror JavaScriptCore's JSContextGroupRef / JSGlobalContextRef semantics.
Object Model
xJSContextGroupRef (≈ JSRuntime)
│ - GC heap
│ - shared class registry
│ - module loader trampoline
│
└── xJSGlobalContextRef (≈ JSContext, 1..N per group)
│ - global object
│ - slot pool for xJSValueRef
│ - user module-load callback
│
└── xJSValueRef / xJSObjectRef / …
Most applications only need one group and one context; that is what xJSGlobalContextCreate(NULL) builds for you.
Creating and Destroying a Context
One-liner (single context)
xJSGlobalContextRef ctx = xJSGlobalContextCreate(NULL);
// …
xJSGlobalContextRelease(ctx);
xJSGlobalContextCreate allocates a fresh group internally, creates one context in it, and transfers group ownership to the context — so xJSGlobalContextRelease is the only teardown you need.
Multiple contexts sharing a heap
xJSContextGroupRef group = xJSContextGroupCreate();
xJSGlobalContextRef a = xJSGlobalContextCreateInGroup(group, NULL);
xJSGlobalContextRef b = xJSGlobalContextCreateInGroup(group, NULL);
// …
xJSGlobalContextRelease(a);
xJSGlobalContextRelease(b);
xJSContextGroupRelease(group);
Contexts in the same group share one GC heap — values can be moved between them cheaply — but must be driven from the same OS thread. Different groups are fully independent and may run on different threads.
Naming a context (for stack traces)
xJSStringRef name = xJSStringCreateWithUTF8CString("worker-42");
xJSGlobalContextSetName(ctx, name);
xJSStringRelease(name);
The name shows up in QuickJS error messages and makes multi-context deployments easier to debug.
Accessing the Global Object
xJSObjectRef g = xJSContextGetGlobalObject(ctx);
// install globals on `g` via xJSObjectSetProperty
xJSValueUnprotect(ctx, (xJSValueRef)g); // release our reference
xJSContextGetGlobalObject returns a new reference (as do all Get* helpers in xjs; see value.md for the lifetime rules).
Pumping Microtasks
QuickJS does not execute Promise reactions automatically between host invocations. Whenever host code does something that might settle a Promise (resolve a deferred, return from a native callback, complete an IO operation), call:
xJSValueRef exc = NULL;
int ran = xJSContextDrainPendingJobs(ctx, &exc);
if (ran < 0 && exc) {
// first job to throw; subsequent jobs still queued
}
The helper keeps executing pending jobs until the queue is empty or a job throws. xJSContextHasPendingJobs() is a cheap peek when you want to batch-drain only when needed.
For the common "evaluate a module, block until done" flow, use xJSAwaitPromise() instead — it drains on your behalf until a specific Promise settles.
Installing a Module Loader
xJSContextSetModuleLoader(ctx, my_loader, my_opaque);
See module.md for the loader contract (it is always installed internally; passing NULL just reverts to the built-in ReferenceError behaviour for every import).
API Surface
Context group
xJSContextGroupRef xJSContextGroupCreate(void);
xJSContextGroupRef xJSContextGroupRetain(xJSContextGroupRef group);
void xJSContextGroupRelease(xJSContextGroupRef group);
Global context
xJSGlobalContextRef xJSGlobalContextCreate(xJSClassRef globalObjectClass);
xJSGlobalContextRef xJSGlobalContextCreateInGroup(xJSContextGroupRef group,
xJSClassRef globalObjectClass);
xJSGlobalContextRef xJSGlobalContextRetain(xJSGlobalContextRef ctx);
void xJSGlobalContextRelease(xJSGlobalContextRef ctx);
xJSStringRef xJSGlobalContextCopyName(xJSGlobalContextRef ctx);
void xJSGlobalContextSetName(xJSGlobalContextRef ctx, xJSStringRef name);
xJSObjectRef xJSContextGetGlobalObject(xJSContextRef ctx);
xJSContextGroupRef xJSContextGetGroup(xJSContextRef ctx);
xJSGlobalContextRef xJSContextGetGlobalContext(xJSContextRef ctx);
Microtask pump
int xJSContextDrainPendingJobs(xJSContextRef ctx, xJSValueRef *exception);
bool xJSContextHasPendingJobs(xJSContextRef ctx);
Module loader
typedef xJSStringRef (*xJSModuleLoadCallback)(xJSContextRef ctx,
const char *normalizedName,
void *opaque);
void xJSContextSetModuleLoader(xJSGlobalContextRef ctx,
xJSModuleLoadCallback load, void *opaque);
Caveats
xJSGlobalContextCreate(xJSClassRef globalObjectClass)currently ignoresglobalObjectClass: customising the global object type is on the roadmap but not yet wired through. PassNULL.- Contexts are not thread-safe — every entry into
ctx(includingxJSValueUnprotect) must come from the thread that owns the group.