File: closure.h

package info (click to toggle)
cjs 128.1-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 5,872 kB
  • sloc: cpp: 34,377; javascript: 27,762; ansic: 13,033; sh: 1,611; python: 780; xml: 116; makefile: 38
file content (138 lines) | stat: -rw-r--r-- 4,786 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2008 litl, LLC
// SPDX-FileCopyrightText: 2021 Canonical Ltd.
// SPDX-FileContributor: Marco Trevisan <marco.trevisan@canonical.com>

#ifndef GI_CLOSURE_H_
#define GI_CLOSURE_H_

#include <config.h>

#include <stddef.h>

#include <glib-object.h>

#include <js/TypeDecls.h>

#include "gi/utils-inl.h"
#include "cjs/jsapi-util-root.h"
#include "cjs/jsapi-util.h"
#include "cjs/macros.h"

class JSTracer;
namespace JS {
class HandleValueArray;
}

namespace Gjs {

class Closure : public GClosure {
 protected:
    Closure(JSContext*, JSObject* callable, bool root, const char* description);
    ~Closure() { unset_context(); }

    // Need to call this if inheriting from Closure to call the dtor
    template <class C>
    constexpr void add_finalize_notifier() {
        static_assert(std::is_base_of_v<Closure, C>);
        g_closure_add_finalize_notifier(
            this, nullptr,
            [](void*, GClosure* closure) { static_cast<C*>(closure)->~C(); });
    }

    void* operator new(size_t size) {
        return g_closure_new_simple(size, nullptr);
    }

    void operator delete(void* p) { unref(static_cast<Closure*>(p)); }

    static Closure* ref(Closure* self) {
        return static_cast<Closure*>(g_closure_ref(self));
    }
    static void unref(Closure* self) { g_closure_unref(self); }

 public:
    using Ptr = GjsAutoPointer<Closure, Closure, unref, ref>;

    [[nodiscard]] constexpr static Closure* for_gclosure(GClosure* gclosure) {
        // We need to do this in order to ensure this is a constant expression
        return static_cast<Closure*>(static_cast<void*>(gclosure));
    }

    [[nodiscard]] static Closure* create(JSContext* cx, JSObject* callable,
                                         const char* description, bool root) {
        auto* self = new Closure(cx, callable, root, description);
        self->add_finalize_notifier<Closure>();
        return self;
    }

    [[nodiscard]] static Closure* create_marshaled(JSContext* cx,
                                                   JSObject* callable,
                                                   const char* description,
                                                   bool root = true) {
        auto* self = new Closure(cx, callable, root, description);
        self->add_finalize_notifier<Closure>();
        g_closure_set_marshal(self, marshal_cb);
        return self;
    }

    [[nodiscard]] static Closure* create_for_signal(JSContext* cx,
                                                    JSObject* callable,
                                                    const char* description,
                                                    int signal_id) {
        auto* self = new Closure(cx, callable, false /* root */, description);
        self->add_finalize_notifier<Closure>();
        g_closure_set_meta_marshal(self, gjs_int_to_pointer(signal_id),
                                   marshal_cb);
        return self;
    }

    // COMPAT: constexpr in C++23
    JSObject* callable() const { return m_callable.get(); }
    [[nodiscard]] constexpr JSContext* context() const { return m_cx; }
    [[nodiscard]] constexpr bool is_valid() const { return !!m_cx; }
    GJS_JSAPI_RETURN_CONVENTION bool invoke(JS::HandleObject,
                                            const JS::HandleValueArray&,
                                            JS::MutableHandleValue);

    void trace(JSTracer* tracer) {
        if (m_callable)
            m_callable.trace(tracer, "signal connection");
    }

 private:
    void unset_context();

    void reset() {
        unset_context();
        m_callable.reset();
        m_cx = nullptr;
    }

    static void marshal_cb(GClosure* closure, GValue* ret, unsigned n_params,
                           const GValue* params, void* hint, void* data) {
        for_gclosure(closure)->marshal(ret, n_params, params, hint, data);
    }

    static void global_context_notifier_cb(JSContext*, void* data) {
        static_cast<Closure*>(data)->global_context_finalized();
    }

    void closure_invalidated();
    void closure_set_invalid();
    void global_context_finalized();
    void marshal(GValue* ret, unsigned n_parms, const GValue* params,
                 void* hint, void* data);

    //  The saved context is used for lifetime management, so that the closure
    //  will be torn down with the context that created it.
    //  The context could be attached to the default context of the runtime
    //  using if we wanted the closure to survive the context that created it.
    JSContext* m_cx;
    GjsMaybeOwned m_callable;
};

}  // namespace Gjs

#endif  // GI_CLOSURE_H_