Because MQuickJS uses a compacting garbage collector, the address of objects can change whenever a JS allocation occurs.
Best Practices:
- Avoid storing
JSValue directly: Only use JSValue for temporary use between API calls. For long-lived references, use a pointer to a JSValue. - Use
JSGCRef for persistent references: To safely hold a reference to a JS object, use JS_PushGCRef() to get an opaque pointer that the engine automatically updates when objects move. You must release it with JS_PopGCRef(). - Debug with
DEBUG_GC: When running on a PC, define DEBUG_GC to force the allocator to move objects at every allocation, helping catch invalid JSValue usage.
JSValue my_js_func(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
{
JSGCRef obj1_ref, obj2_ref;
JSValue *obj1, *obj2, ret;
ret = JS_EXCEPTION;
obj1 = JS_PushGCRef(ctx, &obj1_ref);
obj2 = JS_PushGCRef(ctx, &obj2_ref);
*obj1 = JS_NewObject(ctx);
if (JS_IsException(*obj1))
goto fail;
*obj2 = JS_NewObject(ctx); // obj1 may move
if (JS_IsException(*obj2))
goto fail;
JS_SetPropertyStr(ctx, *obj1, "x", *obj2); // obj1 and obj2 may move
ret = *obj1;
fail:
JS_PopGCRef(ctx, &obj2_ref);
JS_PopGCRef(ctx, &obj1_ref);
return ret;
}