From 55fb29667add625762e33398e8737626d1648a03 Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Tue, 17 Oct 2023 21:24:20 -0700 Subject: Add JSC.Weak --- src/bun.js/Weak.zig | 199 +++++++++++++++++++++++++++++++++++++++++++ src/bun.js/bindings/Weak.cpp | 94 ++++++++++++++++++++ 2 files changed, 293 insertions(+) create mode 100644 src/bun.js/Weak.zig create mode 100644 src/bun.js/bindings/Weak.cpp diff --git a/src/bun.js/Weak.zig b/src/bun.js/Weak.zig new file mode 100644 index 000000000..c1c5d0484 --- /dev/null +++ b/src/bun.js/Weak.zig @@ -0,0 +1,199 @@ +const bun = @import("root").bun; +const JSC = bun.JSC; +const std = @import("std"); + +/// This value must be kept in sync with Weak.cpp +pub const WeakRefFinalizerTag = *const fn (*anyopaque) callconv(.C) void; + +const WeakImpl = opaque { + pub fn init(ptr: *anyopaque, comptime tag: ?WeakRefFinalizerTag, value: JSC.JSValue) *WeakImpl { + JSC.markBinding(@src()); + return Bun__WeakRef__new(value, tag, ptr); + } + + pub fn get(this: *WeakImpl) JSC.JSValue { + JSC.markBinding(@src()); + return Bun__WeakRef__get(this); + } + + pub fn set(this: *WeakImpl, ptr: *anyopaque, comptime tag: ?WeakRefFinalizerTag, value: JSC.JSValue) void { + JSC.markBinding(@src()); + Bun__WeakRef__set(this, value, tag, ptr); + } + + pub fn clear(this: *WeakImpl) void { + JSC.markBinding(@src()); + Bun__WeakRef__clear(this); + } + + pub fn deinit( + this: *WeakImpl, + ) void { + JSC.markBinding(@src()); + Bun__WeakRef__delete(this); + } + + extern fn Bun__WeakRef__delete(this: *WeakImpl) void; + extern fn Bun__WeakRef__new(JSC.JSValue, ?WeakRefFinalizerTag, *anyopaque) *WeakImpl; + extern fn Bun__WeakRef__get(this: *WeakImpl) JSC.JSValue; + extern fn Bun__WeakRef__set(this: *WeakImpl, JSC.JSValue, ?WeakRefFinalizerTag, *anyopaque) void; + extern fn Bun__WeakRef__clear(this: *WeakImpl) void; +}; + +pub fn NewWeakFinalizer(comptime Context: type, comptime FinalizerFn: *const fn (*Context) callconv(.C) void) type { + return struct { + ref: ?*WeakImpl = null, + + const finalizer: WeakRefFinalizerTag = @ptrCast(FinalizerFn); + + pub const WeakFinalizer = @This(); + + pub fn init() WeakFinalizer { + return .{}; + } + + pub fn create( + ptr: *Context, + value: JSC.JSValue, + ) WeakFinalizer { + if (value != .zero) { + return .{ .ref = WeakImpl.init( + ptr, + finalizer, + value, + ) }; + } + + return .{}; + } + + pub fn get(this: *WeakFinalizer) ?JSC.JSValue { + var ref = this.ref orelse return null; + const result = ref.get(); + if (result == .zero) { + return null; + } + + return result; + } + + pub fn swap(this: *WeakFinalizer) JSC.JSValue { + var ref = this.ref orelse return .zero; + const result = ref.get(); + if (result == .zero) { + return .zero; + } + + ref.clear(); + return result; + } + + pub fn has(this: *WeakFinalizer) bool { + var ref = this.ref orelse return false; + return ref.get() != .zero; + } + + pub fn trySwap(this: *WeakFinalizer) ?JSC.JSValue { + const result = this.swap(); + if (result == .zero) { + return null; + } + + return result; + } + + pub fn set(this: *WeakFinalizer, ptr: *Context, value: JSC.JSValue) void { + var ref: *WeakImpl = this.ref orelse { + if (value == .zero) return; + this.ref = WeakImpl.init(ptr, finalizer, value); + return; + }; + ref.set(ptr, finalizer, value); + } + + pub fn clear(this: *WeakFinalizer) void { + var ref: *WeakImpl = this.ref orelse return; + ref.clear(); + } + + pub fn deinit(this: *WeakFinalizer) void { + var ref: *WeakImpl = this.ref orelse return; + this.ref = null; + ref.deinit(); + } + }; +} + +pub const Weak = struct { + ref: ?*WeakImpl = null, + + pub fn init() Weak { + return .{}; + } + + pub fn create( + ptr: *anyopaque, + value: JSC.JSValue, + ) Weak { + if (value != .zero) { + return .{ .ref = WeakImpl.init(ptr, value, null) }; + } + + return .{}; + } + + pub fn get(this: *Weak) ?JSC.JSValue { + var ref = this.ref orelse return null; + const result = ref.get(); + if (result == .zero) { + return null; + } + + return result; + } + + pub fn swap(this: *Weak) JSC.JSValue { + var ref = this.ref orelse return .zero; + const result = ref.get(); + if (result == .zero) { + return .zero; + } + + ref.clear(); + return result; + } + + pub fn has(this: *Weak) bool { + var ref = this.ref orelse return false; + return ref.get() != .zero; + } + + pub fn trySwap(this: *Weak) ?JSC.JSValue { + const result = this.swap(); + if (result == .zero) { + return null; + } + + return result; + } + + pub fn set(this: *Weak, ptr: *anyopaque, value: JSC.JSValue) void { + var ref: *WeakImpl = this.ref orelse { + if (value == .zero) return; + this.ref = WeakImpl.init(ptr, null, value); + return; + }; + ref.set(ptr, null, value); + } + + pub fn clear(this: *Weak) void { + var ref: *WeakImpl = this.ref orelse return; + ref.clear(); + } + + pub fn deinit(this: *Weak) void { + var ref: *WeakImpl = this.ref orelse return; + this.ref = null; + ref.deinit(); + } +}; diff --git a/src/bun.js/bindings/Weak.cpp b/src/bun.js/bindings/Weak.cpp new file mode 100644 index 000000000..a1b669343 --- /dev/null +++ b/src/bun.js/bindings/Weak.cpp @@ -0,0 +1,94 @@ +#include "root.h" +#include "BunClientData.h" +#include +#include + +namespace Bun { +using WeakRefFinalizerTag = uintptr_t; + +template +class WeakRefFinalizerClass : public JSC::WeakHandleOwner { +public: + WeakRefFinalizerClass() + : JSC::WeakHandleOwner() + { + } + + void finalize(JSC::Handle, void* context) + { + finalizer(context); + } + + static WeakHandleOwner& singleton() + { + static NeverDestroyed> s_singleton; + return s_singleton; + } +}; + +extern "C" void Bun__PostgreSQLQueryClient__target_onFinalize(void*); + +using PostgreSQLQueryClient__targetWeakRefFinalizer = WeakRefFinalizerClass; + +static inline JSC::WeakHandleOwner* getOwner(WeakRefFinalizerTag tag) +{ + if (tag == reinterpret_cast(Bun__PostgreSQLQueryClient__target_onFinalize)) + return &PostgreSQLQueryClient__targetWeakRefFinalizer::singleton(); + + if (tag == 0) + return nullptr; + + RELEASE_ASSERT_NOT_REACHED_WITH_MESSAGE("Unknown WeakRefFinalizerTag"); + return nullptr; +} + +class WeakRef { + WTF_MAKE_ISO_ALLOCATED(WeakRef); + +public: + WeakRef() + : m_weak() + { + } + + static inline WeakRef* create(JSC::JSObject* value, WeakRefFinalizerTag tag, void* context) + { + return new WeakRef(value, tag, context); + } + + WeakRef(JSC::JSObject* value, WeakRefFinalizerTag tag, void* context) + { + m_weak = JSC::Weak(value, getOwner(tag), context); + } + + JSC::Weak m_weak; +}; + +WTF_MAKE_ISO_ALLOCATED_IMPL(WeakRef); + +extern "C" void Bun__WeakRef__delete(Bun::WeakRef* ref) +{ + delete ref; +} + +extern "C" Bun::WeakRef* Bun__WeakRef__new(JSC::EncodedJSValue encodedValue, WeakRefFinalizerTag tag, void* context) +{ + return Bun::WeakRef::create(JSC::JSValue::decode(encodedValue).getObject(), tag, context); +} + +extern "C" JSC::EncodedJSValue Bun__WeakRef__get(Bun::WeakRef* weakRef) +{ + return JSC::JSValue::encode(weakRef->m_weak.get()); +} + +extern "C" void Bun__WeakRef__set(Bun::WeakRef* weakRef, JSC::EncodedJSValue encodedValue, WeakRefFinalizerTag tag, void* context) +{ + weakRef->m_weak = JSC::Weak(JSC::JSValue::decode(encodedValue).getObject(), getOwner(tag), context); +} + +extern "C" void Bun__WeakRef__clear(Bun::WeakRef* weakRef) +{ + weakRef->m_weak.clear(); +} + +} \ No newline at end of file -- cgit v1.2.3