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 <glib-object.h>
13 : : #include <stddef.h>
14 : :
15 : : #include <js/TypeDecls.h>
16 : :
17 : : #include "gi/utils-inl.h"
18 : : #include "gjs/jsapi-util-root.h"
19 : : #include "gjs/jsapi-util.h"
20 : : #include "gjs/macros.h"
21 : :
22 : : class JSTracer;
23 : : namespace JS {
24 : : class HandleValueArray;
25 : : }
26 : :
27 : : namespace Gjs {
28 : :
29 : : class Closure : public GClosure {
30 : : protected:
31 : : Closure(JSContext*, JSObject* callable, bool root, const char* description);
32 : 11569 : ~Closure() { unset_context(); }
33 : :
34 : : // Need to call this if inheriting from Closure to call the dtor
35 : : template <class C>
36 : 11591 : constexpr void add_finalize_notifier() {
37 : : static_assert(std::is_base_of_v<Closure, C>);
38 : 11591 : g_closure_add_finalize_notifier(
39 : : this, nullptr,
40 : 11569 : [](void*, GClosure* closure) { static_cast<C*>(closure)->~C(); });
41 : 11591 : }
42 : :
43 : 11591 : void* operator new(size_t size) {
44 : 11591 : return g_closure_new_simple(size, nullptr);
45 : : }
46 : :
47 : : void operator delete(void* p) { unref(static_cast<Closure*>(p)); }
48 : :
49 : 599 : static Closure* ref(Closure* self) {
50 : 599 : return static_cast<Closure*>(g_closure_ref(self));
51 : : }
52 : 708 : static void unref(Closure* self) { g_closure_unref(self); }
53 : :
54 : : public:
55 : : using Ptr = GjsAutoPointer<Closure, Closure, unref, ref>;
56 : :
57 : 8066 : [[nodiscard]] constexpr static Closure* for_gclosure(GClosure* gclosure) {
58 : : // We need to do this in order to ensure this is a constant expression
59 : 8066 : return static_cast<Closure*>(static_cast<void*>(gclosure));
60 : : }
61 : :
62 : : [[nodiscard]] static Closure* create(JSContext* cx, JSObject* callable,
63 : : const char* description, bool root) {
64 : : auto* self = new Closure(cx, callable, root, description);
65 : : self->add_finalize_notifier<Closure>();
66 : : return self;
67 : : }
68 : :
69 : 11071 : [[nodiscard]] static Closure* create_marshaled(JSContext* cx,
70 : : JSObject* callable,
71 : : const char* description) {
72 : 11071 : auto* self = new Closure(cx, callable, true /* root */, description);
73 : 11071 : self->add_finalize_notifier<Closure>();
74 : 11071 : g_closure_set_marshal(self, marshal_cb);
75 : 11071 : return self;
76 : : }
77 : :
78 : 216 : [[nodiscard]] static Closure* create_for_signal(JSContext* cx,
79 : : JSObject* callable,
80 : : const char* description,
81 : : int signal_id) {
82 : 216 : auto* self = new Closure(cx, callable, false /* root */, description);
83 : 216 : self->add_finalize_notifier<Closure>();
84 : 216 : g_closure_set_meta_marshal(self, gjs_int_to_pointer(signal_id),
85 : : marshal_cb);
86 : 216 : return self;
87 : : }
88 : :
89 : 8427 : constexpr JSObject* callable() const { return m_callable; }
90 : 695 : [[nodiscard]] constexpr JSContext* context() const { return m_cx; }
91 : 8625 : [[nodiscard]] constexpr bool is_valid() const { return !!m_cx; }
92 : : GJS_JSAPI_RETURN_CONVENTION bool invoke(JS::HandleObject,
93 : : const JS::HandleValueArray&,
94 : : JS::MutableHandleValue);
95 : :
96 : 110 : void trace(JSTracer* tracer) {
97 [ + - ]: 110 : if (m_callable)
98 : 110 : m_callable.trace(tracer, "signal connection");
99 : 110 : }
100 : :
101 : : private:
102 : : void unset_context();
103 : :
104 : 11587 : void reset() {
105 : 11587 : unset_context();
106 : 11587 : m_callable.reset();
107 : 11587 : m_cx = nullptr;
108 : 11587 : }
109 : :
110 : 7849 : static void marshal_cb(GClosure* closure, GValue* ret, unsigned n_params,
111 : : const GValue* params, void* hint, void* data) {
112 : 7849 : for_gclosure(closure)->marshal(ret, n_params, params, hint, data);
113 : 7848 : }
114 : :
115 : 219 : static void global_context_notifier_cb(JSContext*, void* data) {
116 : 219 : static_cast<Closure*>(data)->global_context_finalized();
117 : 219 : }
118 : :
119 : : void closure_invalidated();
120 : : void closure_set_invalid();
121 : : void global_context_finalized();
122 : : void marshal(GValue* ret, unsigned n_parms, const GValue* params,
123 : : void* hint, void* data);
124 : :
125 : : // The saved context is used for lifetime management, so that the closure
126 : : // will be torn down with the context that created it.
127 : : // The context could be attached to the default context of the runtime
128 : : // using if we wanted the closure to survive the context that created it.
129 : : JSContext* m_cx;
130 : : GjsMaybeOwned<JSObject*> m_callable;
131 : : };
132 : :
133 : : } // namespace Gjs
134 : :
135 : : #endif // GI_CLOSURE_H_
|