diff --git a/source/quickjs-internal.h b/source/quickjs-internal.h index 497bc0ff..4e5143fa 100644 --- a/source/quickjs-internal.h +++ b/source/quickjs-internal.h @@ -95,6 +95,10 @@ void *js_mallocz (JSContext *ctx, size_t size); // #define DUMP_OBJECTS /* dump objects in JS_FreeContext */ // #define DUMP_READ_OBJECT // #define DUMP_ROPE_REBALANCE +/* dump GC ref stack push/pop with C backtrace on mismatch. + When HAVE_ASAN is also set, uses __sanitizer_print_stack_trace() + for symbolized output; otherwise falls back to execinfo backtrace(). */ +// #define DUMP_GC_REFS /* test the GC by forcing it before each object allocation */ // #define FORCE_GC_AT_MALLOC diff --git a/source/runtime.c b/source/runtime.c index e80443af..3dbc3294 100644 --- a/source/runtime.c +++ b/source/runtime.c @@ -155,9 +155,46 @@ JSValue *JS_PushGCRef (JSContext *ctx, JSGCRef *ref) { return &ref->val; } +#ifdef DUMP_GC_REFS +#ifdef HAVE_ASAN +void __sanitizer_print_stack_trace(void); +#else +#include +#endif +static void dump_gc_ref_backtrace(const char *label) { + fprintf(stderr, " [%s C backtrace]:\n", label); +#ifdef HAVE_ASAN + __sanitizer_print_stack_trace(); +#else + void *bt[16]; + int n = backtrace(bt, 16); + backtrace_symbols_fd(bt, n, 2); +#endif + fprintf(stderr, "\n"); +} +#endif + JSValue JS_PopGCRef (JSContext *ctx, JSGCRef *ref) { - if (ctx->top_gc_ref != ref) - fprintf(stderr, "WARN: JS_PopGCRef mismatch (expected %p, got %p)\n", (void*)ctx->top_gc_ref, (void*)ref); + if (ctx->top_gc_ref != ref) { + fprintf(stderr, "WARN: JS_PopGCRef mismatch (expected %p, got %p)\n", + (void *)ctx->top_gc_ref, (void *)ref); +#ifdef DUMP_GC_REFS + /* Walk the stack to show what's between top and the ref being popped */ + int depth = 0; + JSGCRef *walk = ctx->top_gc_ref; + while (walk && walk != ref && depth < 16) { + fprintf(stderr, " orphan #%d: ref=%p val=0x%llx\n", + depth, (void *)walk, (unsigned long long)walk->val); + walk = walk->prev; + depth++; + } + if (walk == ref) + fprintf(stderr, " (%d orphaned ref(s) between top and pop target)\n", depth); + else + fprintf(stderr, " (pop target not found in stack — chain is corrupt)\n"); + dump_gc_ref_backtrace("pop site"); +#endif + } ctx->top_gc_ref = ref->prev; return ref->val; } @@ -6009,6 +6046,7 @@ exception_ret: return -1; exception: + JS_PopGCRef (ctx, &v_ref); JS_PopGCRef (ctx, &prop_ref); JS_PopGCRef (ctx, &tab_ref); JS_PopGCRef (ctx, &sep1_ref); diff --git a/syntax_suite.ce b/syntax_suite.ce index 51a36662..17dd396b 100644 --- a/syntax_suite.ce +++ b/syntax_suite.ce @@ -387,7 +387,8 @@ run("function early return", function() { run("extra and missing args", function() { var fn = function(a, b) { return a + b } - assert_eq(fn(1, 2, 3), 3, "extra args ignored") + assert_eq(should_disrupt(function() { fn(1, 2, 3) }), true, "extra args disrupt") + assert_eq(fn(1, 2), 3, "exact args work") var fn2 = function(a, b) { return a } assert_eq(fn2(1), 1, "missing args ok") })