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