doc gc bugs
This commit is contained in:
@@ -453,6 +453,53 @@ JSC_CCALL(mymod_make,
|
||||
)
|
||||
```
|
||||
|
||||
### C Argument Evaluation Order (critical)
|
||||
|
||||
In C, the **order of evaluation of function arguments is unspecified**. This interacts with the copying GC to create intermittent crashes that are extremely difficult to diagnose.
|
||||
|
||||
```c
|
||||
// UNSAFE — crashes intermittently:
|
||||
JS_FRAME(js);
|
||||
JS_ROOT(obj, JS_NewObject(js));
|
||||
JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "rgba32"));
|
||||
// ^^^^^^^ may be evaluated BEFORE JS_NewString runs
|
||||
// If JS_NewString triggers GC, the already-read obj.val is a dangling pointer.
|
||||
```
|
||||
|
||||
The compiler is free to evaluate `obj.val` into a register, *then* call `JS_NewString`. If `JS_NewString` triggers GC, the object moves to a new address. The rooted `obj` is updated by GC, but the **register copy** is not — it still holds the old address. `JS_SetPropertyStr` then writes to freed memory.
|
||||
|
||||
**Fix:** always separate the allocating call into a local variable:
|
||||
|
||||
```c
|
||||
// SAFE:
|
||||
JS_FRAME(js);
|
||||
JS_ROOT(obj, JS_NewObject(js));
|
||||
JSValue fmt = JS_NewString(js, "rgba32");
|
||||
JS_SetPropertyStr(js, obj.val, "format", fmt);
|
||||
// obj.val is read AFTER JS_NewString completes — guaranteed correct.
|
||||
```
|
||||
|
||||
This applies to **any** allocating function used as an argument when another argument references a rooted `.val`:
|
||||
|
||||
```c
|
||||
// ALL of these are UNSAFE:
|
||||
JS_SetPropertyStr(js, obj.val, "pixels", js_new_blob_stoned_copy(js, data, len));
|
||||
JS_SetPropertyStr(js, obj.val, "x", JS_NewFloat64(js, 3.14));
|
||||
JS_SetPropertyStr(js, obj.val, "name", JS_NewString(js, name));
|
||||
|
||||
// SAFE versions — separate the allocation:
|
||||
JSValue pixels = js_new_blob_stoned_copy(js, data, len);
|
||||
JS_SetPropertyStr(js, obj.val, "pixels", pixels);
|
||||
JSValue x = JS_NewFloat64(js, 3.14);
|
||||
JS_SetPropertyStr(js, obj.val, "x", x);
|
||||
JSValue s = JS_NewString(js, name);
|
||||
JS_SetPropertyStr(js, obj.val, "name", s);
|
||||
```
|
||||
|
||||
**Functions that allocate** (must be separated): `JS_NewString`, `JS_NewFloat64`, `JS_NewInt64`, `JS_NewObject`, `JS_NewArray`, `JS_NewCFunction`, `js_new_blob_stoned_copy`
|
||||
|
||||
**Functions that do NOT allocate** (safe inline): `JS_NewInt32`, `JS_NewUint32`, `JS_NewBool`, `JS_NULL`, `JS_TRUE`, `JS_FALSE`
|
||||
|
||||
### Macros
|
||||
|
||||
| Macro | Purpose |
|
||||
|
||||
Reference in New Issue
Block a user