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 <algorithm> // for find
8 : :
9 : : #include <glib.h>
10 : :
11 : : #include <js/AllocPolicy.h>
12 : : #include <js/ComparisonOperators.h>
13 : : #include <js/ErrorReport.h> // for JS_ReportOutOfMemory
14 : : #include <js/GCPolicyAPI.h> // for GCPolicy (ptr only), NonGCPointe...
15 : : #include <js/GCVector.h>
16 : : #include <js/RootingAPI.h>
17 : : #include <js/TracingAPI.h>
18 : : #include <js/TypeDecls.h>
19 : :
20 : : #include "gjs/jsapi-util.h"
21 : : #include "gjs/macros.h"
22 : : #include "gjs/objectbox.h"
23 : : #include "util/log.h"
24 : :
25 : : /* gjs/objectbox.cpp - GObject boxed type used to "box" a JS object so that it
26 : : * can be passed to or returned from a GObject signal, or used as the type of a
27 : : * GObject property.
28 : : */
29 : :
30 : : namespace JS {
31 : : template <>
32 : : struct GCPolicy<ObjectBox*> : NonGCPointerPolicy<ObjectBox*> {};
33 : : } // namespace JS
34 : :
35 : : namespace {
36 : : JS::PersistentRooted<JS::GCVector<ObjectBox*, 0, js::SystemAllocPolicy>>
37 : : m_wrappers;
38 : : }
39 : :
40 : : struct ObjectBox::impl {
41 : 32 : impl(ObjectBox* parent, JSObject* obj) : m_parent(parent), m_root(obj) {
42 : 32 : g_atomic_ref_count_init(&m_refcount);
43 : 32 : debug("Constructed");
44 : 32 : }
45 : :
46 : : GJS_JSAPI_RETURN_CONVENTION
47 : 32 : bool init(JSContext* cx) {
48 [ - + ]: 32 : if (!m_wrappers.append(m_parent)) {
49 : 0 : JS_ReportOutOfMemory(cx);
50 : 0 : return false;
51 : : }
52 : 32 : return true;
53 : : }
54 : :
55 : 32 : ~impl() {
56 : 32 : auto it = std::find(m_wrappers.begin(), m_wrappers.end(), m_parent);
57 : 32 : m_wrappers.erase(it);
58 : 32 : debug("Finalized");
59 : 32 : }
60 : :
61 : 47 : void ref() {
62 : 47 : debug("incref");
63 : 47 : g_atomic_ref_count_inc(&m_refcount);
64 : 47 : }
65 : :
66 : 79 : void unref() {
67 : 79 : debug("decref");
68 [ + + ]: 79 : if (g_atomic_ref_count_dec(&m_refcount))
69 [ + - ]: 32 : delete m_parent;
70 : 79 : }
71 : :
72 : 223 : void debug(const char* what GJS_USED_VERBOSE_LIFECYCLE) {
73 : : gjs_debug_lifecycle(GJS_DEBUG_GBOXED,
74 : : "%s: ObjectBox %p, JSObject %s", what, m_parent,
75 : : gjs_debug_object(m_root).c_str());
76 : 223 : }
77 : :
78 : : ObjectBox* m_parent;
79 : : JS::Heap<JSObject*> m_root;
80 : : gatomicrefcount m_refcount;
81 : : };
82 : :
83 : 32 : ObjectBox::ObjectBox(JSObject* obj) : m_impl(new ObjectBox::impl(this, obj)) {}
84 : :
85 : 35 : void ObjectBox::destroy(ObjectBox* object) { object->m_impl->unref(); }
86 : :
87 [ + - ]: 32 : void ObjectBox::destroy_impl(ObjectBox::impl* impl) { delete impl; }
88 : :
89 : 35 : ObjectBox::Ptr ObjectBox::boxed(JSContext* cx, JSObject* obj) {
90 : 35 : ObjectBox::Ptr box;
91 : :
92 : : ObjectBox** found =
93 : 35 : std::find_if(m_wrappers.begin(), m_wrappers.end(),
94 : 10 : [obj](ObjectBox* b) { return b->m_impl->m_root == obj; });
95 [ + + ]: 35 : if (found != m_wrappers.end()) {
96 : 3 : box = *found;
97 : 3 : box->m_impl->ref();
98 : 3 : box->m_impl->debug("Reusing box");
99 : : } else {
100 : 32 : box = new ObjectBox(obj);
101 [ - + ]: 32 : if (!box->m_impl->init(cx))
102 : 0 : return nullptr;
103 : : }
104 : :
105 : 35 : return box;
106 : 35 : }
107 : :
108 : 30 : JSObject* ObjectBox::object_for_c_ptr(JSContext* cx, ObjectBox* box) {
109 [ - + ]: 30 : if (!box) {
110 : 0 : gjs_throw(cx, "Cannot get JSObject for null ObjectBox pointer");
111 : 0 : return nullptr;
112 : : }
113 : :
114 : 30 : box->m_impl->debug("retrieved JSObject");
115 : 30 : return box->m_impl->m_root.get();
116 : : }
117 : :
118 : 44 : void* ObjectBox::boxed_copy(void* boxed) {
119 : 44 : auto* box = static_cast<ObjectBox*>(boxed);
120 : 44 : box->m_impl->ref();
121 : 44 : return box;
122 : : }
123 : :
124 : 44 : void ObjectBox::boxed_free(void* boxed) {
125 : 44 : auto* box = static_cast<ObjectBox*>(boxed);
126 : 44 : box->m_impl->unref();
127 : 44 : }
128 : :
129 : 536 : GType ObjectBox::gtype() {
130 : : // Initialization of static local variable guaranteed only once in C++11
131 : 102 : static GType type_id = g_boxed_type_register_static(
132 [ + + + - ]: 536 : "JSObject", &ObjectBox::boxed_copy, &ObjectBox::boxed_free);
133 : 536 : return type_id;
134 : : }
135 : :
136 : 0 : void ObjectBox::trace(JSTracer* trc) {
137 : 0 : JS::TraceEdge(trc, &m_impl->m_root, "object in ObjectBox");
138 : 0 : }
|