Branch data Line data Source code
1 : : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 : : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
3 : : // SPDX-FileCopyrightText: 2008 litl, LLC
4 : : // SPDX-FileCopyrightText: 2021 Canonical Ltd.
5 : : // SPDX-FileContributor: Marco Trevisan <marco.trevisan@canonical.com>
6 : :
7 : : #ifndef GI_CLOSURE_H_
8 : : #define GI_CLOSURE_H_
9 : :
10 : : #include <config.h>
11 : :
12 : : #include <stddef.h>
13 : :
14 : : #include <glib-object.h>
15 : :
16 : : #include <js/TypeDecls.h>
17 : :
18 : : #include "gi/utils-inl.h"
19 : : #include "gjs/auto.h"
20 : : #include "gjs/jsapi-util-root.h"
21 : : #include "gjs/macros.h"
22 : :
23 : : class JSTracer;
24 : : namespace JS {
25 : : class HandleValueArray;
26 : : }
27 : :
28 : : namespace Gjs {
29 : :
30 : : class Closure : public GClosure {
31 : : protected:
32 : : Closure(JSContext*, JSObject* callable, bool root, const char* description);
33 : 14132 : ~Closure() { unset_context(); }
34 : :
35 : : // Need to call this if inheriting from Closure to call the dtor
36 : : template <class C>
37 : 14154 : constexpr void add_finalize_notifier() {
38 : : static_assert(std::is_base_of_v<Closure, C>);
39 : 14154 : g_closure_add_finalize_notifier(
40 : : this, nullptr,
41 : 14132 : [](void*, GClosure* closure) { static_cast<C*>(closure)->~C(); });
42 : 14154 : }
43 : :
44 : 14154 : void* operator new(size_t size) {
45 : 14154 : return g_closure_new_simple(size, nullptr);
46 : : }
47 : :
48 : : void operator delete(void* p) { unref(static_cast<Closure*>(p)); }
49 : :
50 : 632 : static Closure* ref(Closure* self) {
51 : 632 : return static_cast<Closure*>(g_closure_ref(self));
52 : : }
53 : 790 : static void unref(Closure* self) { g_closure_unref(self); }
54 : :
55 : : public:
56 : 790 : using Ptr = Gjs::AutoPointer<Closure, Closure, unref, ref>;
57 : :
58 : 10012 : [[nodiscard]] constexpr static Closure* for_gclosure(GClosure* gclosure) {
59 : : // We need to do this in order to ensure this is a constant expression
60 : 10012 : return static_cast<Closure*>(static_cast<void*>(gclosure));
61 : : }
62 : :
63 : : [[nodiscard]] static Closure* create(JSContext* cx, JSObject* callable,
64 : : const char* description, bool root) {
65 : : auto* self = new Closure(cx, callable, root, description);
66 : : self->add_finalize_notifier<Closure>();
67 : : return self;
68 : : }
69 : :
70 : 13515 : [[nodiscard]] static Closure* create_marshaled(JSContext* cx,
71 : : JSObject* callable,
72 : : const char* description,
73 : : bool root = true) {
74 : 13515 : auto* self = new Closure(cx, callable, root, description);
75 : 13515 : self->add_finalize_notifier<Closure>();
76 : 13515 : g_closure_set_marshal(self, marshal_cb);
77 : 13515 : return self;
78 : : }
79 : :
80 : 323 : [[nodiscard]] static Closure* create_for_signal(JSContext* cx,
81 : : JSObject* callable,
82 : : const char* description,
83 : : int signal_id) {
84 : 323 : auto* self = new Closure(cx, callable, false /* root */, description);
85 : 323 : self->add_finalize_notifier<Closure>();
86 : 323 : g_closure_set_meta_marshal(self, gjs_int_to_pointer(signal_id),
87 : : marshal_cb);
88 : 323 : return self;
89 : : }
90 : :
91 : : // COMPAT: constexpr in C++23
92 : 10324 : JSObject* callable() const { return m_callable.get(); }
93 : 736 : [[nodiscard]] constexpr JSContext* context() const { return m_cx; }
94 : 10535 : [[nodiscard]] constexpr bool is_valid() const { return !!m_cx; }
95 : : GJS_JSAPI_RETURN_CONVENTION bool invoke(JS::HandleObject,
96 : : const JS::HandleValueArray&,
97 : : JS::MutableHandleValue);
98 : :
99 : 185 : void trace(JSTracer* tracer) {
100 [ + - ]: 185 : if (m_callable)
101 : 185 : m_callable.trace(tracer, "signal connection");
102 : 185 : }
103 : :
104 : : private:
105 : : void unset_context();
106 : :
107 : 14150 : void reset() {
108 : 14150 : unset_context();
109 : 14150 : m_callable.reset();
110 : 14150 : m_cx = nullptr;
111 : 14150 : }
112 : :
113 : 9720 : static void marshal_cb(GClosure* closure, GValue* ret, unsigned n_params,
114 : : const GValue* params, void* hint, void* data) {
115 : 9720 : for_gclosure(closure)->marshal(ret, n_params, params, hint, data);
116 : 9719 : }
117 : :
118 : 186 : static void global_context_notifier_cb(JSContext*, void* data) {
119 : 186 : static_cast<Closure*>(data)->global_context_finalized();
120 : 186 : }
121 : :
122 : : void closure_invalidated();
123 : : void closure_set_invalid();
124 : : void global_context_finalized();
125 : : void marshal(GValue* ret, unsigned n_parms, const GValue* params,
126 : : void* hint, void* data);
127 : :
128 : : // The saved context is used for lifetime management, so that the closure
129 : : // will be torn down with the context that created it.
130 : : // The context could be attached to the default context of the runtime
131 : : // using if we wanted the closure to survive the context that created it.
132 : : JSContext* m_cx;
133 : : GjsMaybeOwned m_callable;
134 : : };
135 : :
136 : : } // namespace Gjs
137 : :
138 : : #endif // GI_CLOSURE_H_
|