ocaml style rooting macros

This commit is contained in:
2026-02-13 20:46:31 -06:00
parent e80e615634
commit 83263379bd
24 changed files with 418 additions and 220 deletions

View File

@@ -183,6 +183,25 @@ void JS_DeleteGCRef (JSContext *ctx, JSGCRef *ref) {
}
}
/* JS_FRAME/JS_ROOT/JS_LOCAL helper functions */
JSGCRef *JS_GetGCFrame (JSContext *ctx) {
return ctx->top_gc_ref;
}
JSLocalRef *JS_GetLocalFrame (JSContext *ctx) {
return ctx->top_local_ref;
}
void JS_PushLocalRef (JSContext *ctx, JSLocalRef *ref) {
ref->prev = ctx->top_local_ref;
ctx->top_local_ref = ref;
}
void JS_RestoreFrame (JSContext *ctx, JSGCRef *gc_frame, JSLocalRef *local_frame) {
ctx->top_gc_ref = gc_frame;
ctx->top_local_ref = local_frame;
}
void *ct_alloc (JSContext *ctx, size_t bytes, size_t align) {
/* Align the request */
bytes = (bytes + align - 1) & ~(align - 1);
@@ -1608,6 +1627,14 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
ref->val = gc_copy_value (ctx, ref->val, from_base, from_end, to_base, &to_free, to_end);
}
/* Copy JS_LOCAL roots (update C locals through pointers) */
#ifdef DUMP_GC_DETAIL
printf(" roots: top_local_ref\n"); fflush(stdout);
#endif
for (JSLocalRef *ref = ctx->top_local_ref; ref != NULL; ref = ref->prev) {
*ref->ptr = gc_copy_value (ctx, *ref->ptr, from_base, from_end, to_base, &to_free, to_end);
}
/* Copy JS_AddGCRef/JS_DeleteGCRef roots */
#ifdef DUMP_GC_DETAIL
printf(" roots: last_gc_ref\n"); fflush(stdout);
@@ -4383,6 +4410,10 @@ JSValue js_call_c_function (JSContext *ctx, JSValue func_obj, JSValue this_obj,
ctx->trace_hook (ctx, JS_HOOK_CALL, &dbg, ctx->trace_data);
}
#ifdef VALIDATE_GC
uint8_t *pre_heap_base = ctx->heap_base;
#endif
switch (cproto) {
case JS_CFUNC_generic:
ret_val = func.generic (ctx, this_obj, argc, arg_copy);
@@ -4449,6 +4480,21 @@ JSValue js_call_c_function (JSContext *ctx, JSValue func_obj, JSValue this_obj,
abort ();
}
#ifdef VALIDATE_GC
if (ctx->heap_base != pre_heap_base && JS_IsPtr (ret_val)) {
void *rp = JS_VALUE_GET_PTR (ret_val);
if (!is_ct_ptr (ctx, rp) &&
((uint8_t *)rp < ctx->heap_base || (uint8_t *)rp >= ctx->heap_free)) {
/* Note: f is stale after GC (func_obj was passed by value), so we
cannot read f->name here. Just report the pointer. */
fprintf (stderr, "VALIDATE_GC: C function returned stale ptr=%p "
"heap=[%p,%p) after GC\n", rp,
(void *)ctx->heap_base, (void *)ctx->heap_free);
fflush (stderr);
}
}
#endif
ctx->c_call_root = root.prev;
if (unlikely (ctx->trace_hook) && (ctx->trace_type & JS_HOOK_RET))
@@ -5356,19 +5402,27 @@ static JSValue cjson_to_jsvalue (JSContext *ctx, const cJSON *item) {
if (cJSON_IsString (item)) return JS_NewString (ctx, item->valuestring);
if (cJSON_IsArray (item)) {
int n = cJSON_GetArraySize (item);
JSValue arr = JS_NewArrayLen (ctx,n);
JSGCRef arr_ref;
JS_AddGCRef (ctx, &arr_ref);
arr_ref.val = JS_NewArrayLen (ctx, n);
for (int i = 0; i < n; i++) {
cJSON *child = cJSON_GetArrayItem (item, i);
JS_SetPropertyNumber (ctx, arr, i, cjson_to_jsvalue (ctx, child));
JS_SetPropertyNumber (ctx, arr_ref.val, i, cjson_to_jsvalue (ctx, child));
}
return arr;
JSValue result = arr_ref.val;
JS_DeleteGCRef (ctx, &arr_ref);
return result;
}
if (cJSON_IsObject (item)) {
JSValue obj = JS_NewObject (ctx);
JSGCRef obj_ref;
JS_AddGCRef (ctx, &obj_ref);
obj_ref.val = JS_NewObject (ctx);
for (cJSON *child = item->child; child; child = child->next) {
JS_SetPropertyStr (ctx, obj, child->string, cjson_to_jsvalue (ctx, child));
JS_SetPropertyStr (ctx, obj_ref.val, child->string, cjson_to_jsvalue (ctx, child));
}
return obj;
JSValue result = obj_ref.val;
JS_DeleteGCRef (ctx, &obj_ref);
return result;
}
return JS_NULL;
}
@@ -11879,9 +11933,13 @@ static const JSCFunctionListEntry js_math_radians_funcs[]
JS_CFUNC_DEF ("e", 1, js_math_e) };
JSValue js_math_radians_use (JSContext *ctx) {
JSValue obj = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj, js_math_radians_funcs, countof (js_math_radians_funcs));
return obj;
JSGCRef obj_ref;
JS_PushGCRef (ctx, &obj_ref);
obj_ref.val = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_radians_funcs, countof (js_math_radians_funcs));
JSValue result = obj_ref.val;
JS_PopGCRef (ctx, &obj_ref);
return result;
}
/* ============================================================================
@@ -11945,9 +12003,13 @@ static const JSCFunctionListEntry js_math_degrees_funcs[]
JS_CFUNC_DEF ("e", 1, js_math_e) };
JSValue js_math_degrees_use (JSContext *ctx) {
JSValue obj = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj, js_math_degrees_funcs, countof (js_math_degrees_funcs));
return obj;
JSGCRef obj_ref;
JS_PushGCRef (ctx, &obj_ref);
obj_ref.val = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_degrees_funcs, countof (js_math_degrees_funcs));
JSValue result = obj_ref.val;
JS_PopGCRef (ctx, &obj_ref);
return result;
}
/* ============================================================================
@@ -12010,9 +12072,13 @@ static const JSCFunctionListEntry js_math_cycles_funcs[]
JS_CFUNC_DEF ("e", 1, js_math_e) };
JSValue js_math_cycles_use (JSContext *ctx) {
JSValue obj = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj, js_math_cycles_funcs, countof (js_math_cycles_funcs));
return obj;
JSGCRef obj_ref;
JS_PushGCRef (ctx, &obj_ref);
obj_ref.val = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_cycles_funcs, countof (js_math_cycles_funcs));
JSValue result = obj_ref.val;
JS_PopGCRef (ctx, &obj_ref);
return result;
}
/* Public API: get stack trace as cJSON array */
cJSON *JS_GetStack(JSContext *ctx) {