From 603bbd18a0096d7781bb7203e8b48f6f4c4bb50c Mon Sep 17 00:00:00 2001 From: SUZUKI Sosuke Date: Sun, 28 Dec 2025 08:02:11 +0900 Subject: [PATCH] Enable `CHECK_REF_COUNTED_LIFECYCLE` in WebKit (#25705) ### What does this PR do? Enables `CHECK_REF_COUNTED_LIFECYCLE` in WebKit ( https://github.com/oven-sh/WebKit/pull/121 ) See also https://github.com/WebKit/WebKit/commit/a978fae61986c0d3056e523f6552f28fc7b19b9f #### `CHECK_REF_COUNTED_LIFECYCLE`? A compile-time macro that enables lifecycle validation for reference-counted objects in debug builds. **Definition** ```cpp #if ASSERT_ENABLED || ENABLE(SECURITY_ASSERTIONS) #define CHECK_REF_COUNTED_LIFECYCLE 1 #else #define CHECK_REF_COUNTED_LIFECYCLE 0 #endif ``` **Purpose** Detects three categories of bugs: 1. Missing adoption - Objects stored in RefPtr without using adoptRef() 2. Ref during destruction - ref() called while destructor is running (causes dangling pointers) 3. Thread safety violations - Unsafe ref/deref across threads **Implementation** When enabled, RefCountDebugger adds two tracking flags: - m_deletionHasBegun - Set when destructor starts - m_adoptionIsRequired - Cleared when adoptRef() is called These flags are checked on every ref()/deref() call, with assertions failing on violations. **Motivation** Refactored debug code into a separate RefCountDebugger class to: - Improve readability of core refcount logic - Eliminate duplicate code across RefCounted, ThreadSafeRefCounted, etc. - Simplify adding new refcount classes **Overhead** Zero in release builds - the flags and checks are compiled out entirely. ### How did you verify your code works? --------- Co-authored-by: Claude Opus 4.5 --- cmake/tools/SetupWebKit.cmake | 2 +- src/bun.js/bindings/Serialization.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/tools/SetupWebKit.cmake b/cmake/tools/SetupWebKit.cmake index 85bac91e7b..6e2ce7d677 100644 --- a/cmake/tools/SetupWebKit.cmake +++ b/cmake/tools/SetupWebKit.cmake @@ -2,7 +2,7 @@ option(WEBKIT_VERSION "The version of WebKit to use") option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading") if(NOT WEBKIT_VERSION) - set(WEBKIT_VERSION 2317e1413969bff5e86d240f9f50aa2120776414) + set(WEBKIT_VERSION 863778130931e0081a688f48e8479b8ee61b9507) endif() string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX) diff --git a/src/bun.js/bindings/Serialization.cpp b/src/bun.js/bindings/Serialization.cpp index 5385e153db..caaec599f8 100644 --- a/src/bun.js/bindings/Serialization.cpp +++ b/src/bun.js/bindings/Serialization.cpp @@ -54,7 +54,9 @@ extern "C" SerializedValueSlice Bun__serializeJSValue(JSGlobalObject* globalObje extern "C" void Bun__SerializedScriptSlice__free(SerializedScriptValue* value) { - delete value; + // Use deref() instead of delete to properly handle CHECK_REF_COUNTED_LIFECYCLE. + // The value was leaked via leakRef() which leaves refcount at 1, so deref() will delete it. + value->deref(); } extern "C" EncodedJSValue Bun__JSValue__deserialize(JSGlobalObject* globalObject, const uint8_t* bytes, size_t size)