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: 2019 Marco Trevisan <marco.trevisan@canonical.com>
4 : :
5 : : #include <config.h>
6 : :
7 : : #include <vector>
8 : :
9 : : #include <glib.h>
10 : :
11 : : #include <js/ComparisonOperators.h>
12 : : #include <js/RootingAPI.h>
13 : : #include <js/TypeDecls.h>
14 : :
15 : : #include "gjs/auto.h"
16 : : #include "gjs/jsapi-util.h"
17 : : #include "gjs/objectbox.h"
18 : : #include "util/log.h"
19 : :
20 : : /* gjs/objectbox.cpp - GObject boxed type used to "box" a JS object so that it
21 : : * can be passed to or returned from a GObject signal, or used as the type of a
22 : : * GObject property.
23 : : */
24 : :
25 : : static std::vector<ObjectBox*> s_wrappers;
26 : :
27 : : struct ObjectBox::impl {
28 : 33 : impl(JSContext* cx, ObjectBox* parent, JSObject* obj)
29 : 33 : : m_parent(parent), m_root(cx, obj) {
30 : 33 : g_atomic_ref_count_init(&m_refcount);
31 : 33 : s_wrappers.push_back(m_parent);
32 : 33 : debug("Constructed");
33 : 33 : }
34 : :
35 : 33 : ~impl() {
36 : 33 : std::erase(s_wrappers, m_parent);
37 : 33 : debug("Finalized");
38 : 33 : }
39 : :
40 : 50 : void ref() {
41 : 50 : debug("incref");
42 : 50 : g_atomic_ref_count_inc(&m_refcount);
43 : 50 : }
44 : :
45 : 83 : void unref() {
46 : 83 : debug("decref");
47 [ + + ]: 83 : if (g_atomic_ref_count_dec(&m_refcount))
48 [ + - ]: 33 : delete m_parent;
49 : 83 : }
50 : :
51 : : // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
52 : 233 : void debug(const char* what GJS_USED_VERBOSE_LIFECYCLE) {
53 : : gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "{}: ObjectBox {}, JSObject {}",
54 : : what, static_cast<void*>(m_parent), m_root.get());
55 : 233 : }
56 : :
57 : : ObjectBox* m_parent;
58 : : JS::PersistentRooted<JSObject*> m_root;
59 : : gatomicrefcount m_refcount;
60 : : };
61 : :
62 : 33 : ObjectBox::ObjectBox(JSContext* cx, JSObject* obj)
63 : 33 : : m_impl(new ObjectBox::impl(cx, this, obj)) {}
64 : :
65 : 36 : void ObjectBox::destroy(ObjectBox* object) { object->m_impl->unref(); }
66 : :
67 [ + - ]: 33 : void ObjectBox::destroy_impl(ObjectBox::impl* impl) { delete impl; }
68 : :
69 : 36 : ObjectBox::Ptr ObjectBox::boxed(JSContext* cx, JSObject* obj) {
70 [ + + ]: 79 : for (ObjectBox* box : s_wrappers) {
71 [ + + ]: 10 : if (box->m_impl->m_root == obj) {
72 : 3 : box->m_impl->ref();
73 : 3 : box->m_impl->debug("Reusing box");
74 : 3 : return box;
75 : : }
76 : : }
77 : :
78 : 33 : return new ObjectBox(cx, obj);
79 : : }
80 : :
81 : 31 : JSObject* ObjectBox::object_for_c_ptr(JSContext* cx, ObjectBox* box) {
82 [ - + ]: 31 : if (!box) {
83 : 0 : gjs_throw(cx, "Cannot get JSObject for null ObjectBox pointer");
84 : 0 : return nullptr;
85 : : }
86 : :
87 : 31 : JSObject* obj = box->m_impl->m_root.get();
88 [ - + ]: 31 : if (!obj) {
89 : 0 : gjs_throw(cx,
90 : : "JSObject for ObjectBox {} belongs to destroyed JSContext",
91 : 0 : static_cast<void*>(box));
92 : 0 : return nullptr;
93 : : }
94 : :
95 : 31 : box->m_impl->debug("retrieved JSObject");
96 : 31 : return obj;
97 : : }
98 : :
99 : 47 : void* ObjectBox::boxed_copy(void* boxed) {
100 : 47 : auto* box = static_cast<ObjectBox*>(boxed);
101 : 47 : box->m_impl->ref();
102 : 47 : return box;
103 : : }
104 : :
105 : 47 : void ObjectBox::boxed_free(void* boxed) {
106 : 47 : auto* box = static_cast<ObjectBox*>(boxed);
107 : 47 : box->m_impl->unref();
108 : 47 : }
109 : :
110 : 838 : GType ObjectBox::gtype() {
111 : : // Initialization of static local variable guaranteed only once in C++11
112 : 121 : static GType type_id = g_boxed_type_register_static(
113 [ + + + - ]: 838 : "JSObject", &ObjectBox::boxed_copy, &ObjectBox::boxed_free);
114 : 838 : return type_id;
115 : : }
|