diff --git a/source/quickjs.c b/source/quickjs.c index 1a27bcd1..041d0bd0 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -313,7 +313,7 @@ struct JSFunctionBytecode; #define JS_VALUE_GET_OBJ(v) ((JSRecord *)chase (v)) #define JS_VALUE_GET_TEXT(v) ((JSText *)chase (v)) #define JS_VALUE_GET_BLOB(v) ((JSBlob *)JS_VALUE_GET_PTR (v)) -#define JS_VALUE_GET_FUNCTION(v) ((JSFunction *)JS_VALUE_GET_PTR (v)) +#define JS_VALUE_GET_FUNCTION(v) ((JSFunction *)chase (v)) #define JS_VALUE_GET_FRAME(v) ((JSFrame *)chase (v)) #define JS_VALUE_GET_CODE(v) ((JSFunctionBytecode *)JS_VALUE_GET_PTR (v)) #define JS_VALUE_GET_STRING(v) ((JSText *)chase (v)) @@ -24209,8 +24209,7 @@ static JSValue js_cell_object (JSContext *ctx, JSValue this_val, int argc, JSVal keys = JS_VALUE_GET_ARRAY (argv[1]); /* re-chase each iteration */ if (i >= (int)keys->len) break; JSValue key = keys->values[i]; - int key_tag = JS_VALUE_GET_TAG (key); - if (key_tag == JS_TAG_STRING || key_tag == JS_TAG_STRING_IMM) { + if (JS_IsText (key)) { /* Use JSValue key directly - create interned key */ JSValue prop_key = js_key_from_string (ctx, key); int has = JS_HasProperty (ctx, arg, prop_key); @@ -24234,38 +24233,57 @@ static JSValue js_cell_object (JSContext *ctx, JSValue this_val, int argc, JSVal JSArray *keys = JS_VALUE_GET_ARRAY (arg); int len = keys->len; - JSValue result = JS_NewObject (ctx); - if (JS_IsException (result)) return result; - JSGCRef result_ref; - int is_func = argc >= 2 && JS_IsFunction (argv[1]); + /* Root keys array and func/value BEFORE JS_NewObject which may trigger GC. + argv[] is on the C stack and is NOT a GC root, so after any allocation + that triggers GC, argv[] values become dangling pointers. */ + JSGCRef keys_ref, func_ref, result_ref; + JS_PushGCRef (ctx, &keys_ref); + keys_ref.val = arg; /* use already-read arg, not argv[0] */ + JS_PushGCRef (ctx, &func_ref); + func_ref.val = argc >= 2 ? argv[1] : JS_NULL; + JS_PushGCRef (ctx, &result_ref); + result_ref.val = JS_NULL; + + JSValue result = JS_NewObject (ctx); + if (JS_IsException (result)) { + JS_PopGCRef (ctx, &result_ref); + JS_PopGCRef (ctx, &func_ref); + JS_PopGCRef (ctx, &keys_ref); + return result; + } + result_ref.val = result; + for (int i = 0; i < len; i++) { - keys = JS_VALUE_GET_ARRAY (argv[0]); /* re-chase each iteration via argv */ + keys = JS_VALUE_GET_ARRAY (keys_ref.val); if (i >= (int)keys->len) break; JSValue key = keys->values[i]; - int key_tag = JS_VALUE_GET_TAG (key); - if (key_tag == JS_TAG_STRING || key_tag == JS_TAG_STRING_IMM) { + if (JS_IsText (key)) { /* Use JSValue key directly - create interned key */ JSValue prop_key = js_key_from_string (ctx, key); JSValue val; - if (argc < 2 || JS_IsNull (argv[1])) { + if (argc < 2 || JS_IsNull (func_ref.val)) { val = JS_TRUE; } else if (is_func) { JSValue arg_key = key; - JS_PUSH_VALUE (ctx, result); - val = JS_CallInternal (ctx, argv[1], JS_NULL, 1, &arg_key, 0); - JS_POP_VALUE (ctx, result); + val = JS_CallInternal (ctx, func_ref.val, JS_NULL, 1, &arg_key, 0); if (JS_IsException (val)) { + JS_PopGCRef (ctx, &result_ref); + JS_PopGCRef (ctx, &func_ref); + JS_PopGCRef (ctx, &keys_ref); return JS_EXCEPTION; } } else { - val = argv[1]; + val = func_ref.val; } - JS_SetProperty (ctx, result, prop_key, val); + JS_SetProperty (ctx, result_ref.val, prop_key, val); /* prop_key is interned, no need to free */ } } + result = JS_PopGCRef (ctx, &result_ref); + JS_PopGCRef (ctx, &func_ref); + JS_PopGCRef (ctx, &keys_ref); return result; }