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 : :
5 : : #include <config.h>
6 : :
7 : : #include <string.h>
8 : :
9 : : #include <string>
10 : : #include <vector>
11 : :
12 : : #include <glib.h>
13 : :
14 : : #include <js/CallArgs.h>
15 : : #include <js/Class.h>
16 : : #include <js/ComparisonOperators.h>
17 : : #include <js/ErrorReport.h> // for JS_ReportOutOfMemory
18 : : #include <js/GCVector.h> // for MutableHandleIdVector
19 : : #include <js/Id.h>
20 : : #include <js/PropertyDescriptor.h> // for JSPROP_READONLY
21 : : #include <js/PropertySpec.h>
22 : : #include <js/RootingAPI.h>
23 : : #include <js/TypeDecls.h>
24 : : #include <js/Utility.h> // for UniqueChars
25 : : #include <jsapi.h> // for JS_NewObjectWithGivenProto
26 : : #include <mozilla/Maybe.h>
27 : :
28 : : #include "gi/cwrapper.h"
29 : : #include "gi/info.h"
30 : : #include "gi/ns.h"
31 : : #include "gi/repo.h"
32 : : #include "gjs/atoms.h"
33 : : #include "gjs/auto.h"
34 : : #include "gjs/context-private.h"
35 : : #include "gjs/global.h"
36 : : #include "gjs/jsapi-util.h"
37 : : #include "gjs/macros.h"
38 : : #include "gjs/mem-private.h"
39 : : #include "util/log.h"
40 : :
41 : : #if GLIB_CHECK_VERSION(2, 79, 2)
42 : : # include "gjs/deprecation.h"
43 : : #endif // GLib >= 2.79.2
44 : :
45 : : using mozilla::Maybe;
46 : :
47 : : class Ns : private Gjs::AutoChar, public CWrapper<Ns> {
48 : : friend CWrapperPointerOps<Ns>;
49 : : friend CWrapper<Ns>;
50 : :
51 : : #if GLIB_CHECK_VERSION(2, 79, 2)
52 : : bool m_is_gio_or_glib : 1;
53 : : #endif // GLib >= 2.79.2
54 : :
55 : : static constexpr auto PROTOTYPE_SLOT = GjsGlobalSlot::PROTOTYPE_ns;
56 : : static constexpr GjsDebugTopic DEBUG_TOPIC = GJS_DEBUG_GNAMESPACE;
57 : :
58 : 260 : explicit Ns(const char* ns_name)
59 : 260 : : Gjs::AutoChar(const_cast<char*>(ns_name), Gjs::TakeOwnership{}) {
60 : 260 : GJS_INC_COUNTER(ns);
61 : : #if GLIB_CHECK_VERSION(2, 79, 2)
62 : 260 : m_is_gio_or_glib =
63 [ + + + + ]: 260 : strcmp(ns_name, "Gio") == 0 || strcmp(ns_name, "GLib") == 0;
64 : : #endif // GLib >= 2.79.2
65 : 260 : }
66 : :
67 : 256 : ~Ns() { GJS_DEC_COUNTER(ns); }
68 : :
69 : : #if GLIB_CHECK_VERSION(2, 79, 2)
70 : : // helper function
71 : 10472 : void platform_specific_warning(JSContext* cx, const char* prefix,
72 : : const char* platform,
73 : : const char* resolved_name,
74 : : const char** exceptions = nullptr) {
75 [ + + ]: 10472 : if (!g_str_has_prefix(resolved_name, prefix))
76 : 10471 : return;
77 : :
78 : 7 : const char* base_name = resolved_name + strlen(prefix);
79 : : Gjs::AutoChar old_name{
80 : 7 : g_strdup_printf("%s.%s", this->get(), resolved_name)};
81 [ + - ]: 7 : if (exceptions) {
82 [ + + ]: 24 : for (const char** exception = exceptions; *exception; exception++) {
83 [ + + ]: 23 : if (strcmp(old_name, *exception) == 0)
84 : 6 : return;
85 : : }
86 : : }
87 : :
88 : : Gjs::AutoChar new_name{
89 : 1 : g_strdup_printf("%s%s.%s", this->get(), platform, base_name)};
90 [ + + ]: 7 : _gjs_warn_deprecated_once_per_callsite(
91 : : cx, GjsDeprecationMessageId::PlatformSpecificTypelib,
92 : 2 : {old_name.get(), new_name.get()});
93 [ + + ]: 7 : }
94 : : #endif // GLib >= 2.79.2
95 : :
96 : : // JSClass operations
97 : :
98 : : // The *resolved out parameter, on success, should be false to indicate that
99 : : // id was not resolved; and true if id was resolved.
100 : : GJS_JSAPI_RETURN_CONVENTION
101 : 13838 : bool resolve_impl(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
102 : : bool* resolved) {
103 [ + + ]: 13838 : if (!id.isString()) {
104 : 31 : *resolved = false;
105 : 31 : return true; // not resolved, but no error
106 : : }
107 : :
108 : : // let Object.prototype resolve these
109 : 13807 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
110 [ + + - + : 13807 : if (id == atoms.to_string() || id == atoms.value_of()) {
+ + ]
111 : 14 : *resolved = false;
112 : 14 : return true;
113 : : }
114 : :
115 : 13793 : JS::UniqueChars name;
116 [ - + ]: 13793 : if (!gjs_get_string_id(cx, id, &name))
117 : 0 : return false;
118 [ - + ]: 13793 : if (!name) {
119 : 0 : *resolved = false;
120 : 0 : return true; // not resolved, but no error
121 : : }
122 : :
123 : : Maybe<GI::AutoBaseInfo> info{
124 : 13793 : GI::Repository{}.find_by_name(get(), name.get())};
125 [ + + ]: 13793 : if (!info) {
126 : 4410 : *resolved = false; // No property defined, but no error either
127 : 4410 : return true;
128 : : }
129 : :
130 : 28149 : gjs_debug(GJS_DEBUG_GNAMESPACE,
131 : : "Found info type %s for '%s' in namespace '%s'",
132 : 28149 : info->type_string(), info->name(), info->ns());
133 : :
134 : : #if GLIB_CHECK_VERSION(2, 79, 2)
135 : : static const char* unix_types_exceptions[] = {
136 : : "Gio.UnixConnection",
137 : : "Gio.UnixCredentialsMessage",
138 : : "Gio.UnixFDList",
139 : : "Gio.UnixSocketAddress",
140 : : "Gio.UnixSocketAddressType",
141 : : nullptr};
142 : :
143 [ + + ]: 9383 : if (m_is_gio_or_glib) {
144 : 2618 : platform_specific_warning(cx, "Unix", "Unix", name.get(),
145 : : unix_types_exceptions);
146 : 2618 : platform_specific_warning(cx, "unix_", "Unix", name.get());
147 : 2618 : platform_specific_warning(cx, "Win32", "Win32", name.get());
148 : 2618 : platform_specific_warning(cx, "win32_", "Win32", name.get());
149 : : }
150 : : #endif // GLib >= 2.79.2
151 : :
152 : : bool defined;
153 [ - + ]: 9383 : if (!gjs_define_info(cx, obj, info.ref(), &defined)) {
154 : 0 : gjs_debug(GJS_DEBUG_GNAMESPACE, "Failed to define info '%s'",
155 : 0 : info->name());
156 : 0 : return false;
157 : : }
158 : :
159 : : // we defined the property in this object?
160 : 9383 : *resolved = defined;
161 : 9383 : return true;
162 : 13793 : }
163 : :
164 : : GJS_JSAPI_RETURN_CONVENTION
165 : 4 : bool new_enumerate_impl(JSContext* cx,
166 : : JS::HandleObject obj [[maybe_unused]],
167 : : JS::MutableHandleIdVector properties,
168 : : bool only_enumerable [[maybe_unused]]) {
169 : 4 : GI::Repository::Iterator infos{GI::Repository{}.infos(get())};
170 [ - + ]: 4 : if (!properties.reserve(properties.length() + infos.size())) {
171 : 0 : JS_ReportOutOfMemory(cx);
172 : 0 : return false;
173 : : }
174 : :
175 [ + + ]: 10036 : for (GI::AutoBaseInfo info : infos) {
176 [ + + ]: 10032 : if (!info.is_enumerable())
177 : 20 : continue;
178 : :
179 : 10012 : jsid id = gjs_intern_string_to_id(cx, info.name());
180 [ - + ]: 10012 : if (id.isVoid())
181 : 0 : return false;
182 : 10012 : properties.infallibleAppend(id);
183 [ + + - ]: 10032 : }
184 : :
185 : 4 : return true;
186 : : }
187 : :
188 : 256 : static void finalize_impl(JS::GCContext*, Ns* priv) {
189 : 256 : g_assert(priv && "Finalize called on wrong object");
190 [ + - ]: 256 : delete priv;
191 : 256 : }
192 : :
193 : : // Properties and methods
194 : :
195 : : GJS_JSAPI_RETURN_CONVENTION
196 : 2 : static bool get_name(JSContext* cx, unsigned argc, JS::Value* vp) {
197 [ - + - + ]: 2 : GJS_CHECK_WRAPPER_PRIV(cx, argc, vp, args, this_obj, Ns, priv);
198 : 2 : return gjs_string_from_utf8(cx, priv->get(), args.rval());
199 : 2 : }
200 : :
201 : : GJS_JSAPI_RETURN_CONVENTION
202 : 4 : static bool get_version(JSContext* cx, unsigned argc, JS::Value* vp) {
203 [ - + - + ]: 4 : GJS_CHECK_WRAPPER_PRIV(cx, argc, vp, args, this_obj, Ns, priv);
204 : 4 : const char* version = GI::Repository{}.get_version(priv->get());
205 : 4 : return gjs_string_from_utf8(cx, version, args.rval());
206 : 4 : }
207 : :
208 : : static constexpr JSClassOps class_ops = {
209 : : nullptr, // addProperty
210 : : nullptr, // deleteProperty
211 : : nullptr, // enumerate
212 : : &Ns::new_enumerate,
213 : : &Ns::resolve,
214 : : nullptr, // mayResolve
215 : : &Ns::finalize,
216 : : };
217 : :
218 : : // clang-format off
219 : : static constexpr JSPropertySpec proto_props[] = {
220 : : JS_STRING_SYM_PS(toStringTag, "GIRepositoryNamespace", JSPROP_READONLY),
221 : : JS_PSG("__name__", &Ns::get_name, GJS_MODULE_PROP_FLAGS),
222 : : JS_PSG("__version__", &Ns::get_version, GJS_MODULE_PROP_FLAGS & ~JSPROP_ENUMERATE),
223 : : JS_PS_END};
224 : : // clang-format on
225 : :
226 : : static constexpr js::ClassSpec class_spec = {
227 : : nullptr, // createConstructor
228 : : nullptr, // createPrototype
229 : : nullptr, // constructorFunctions
230 : : nullptr, // constructorProperties
231 : : nullptr, // prototypeFunctions
232 : : Ns::proto_props,
233 : : nullptr, // finishInit
234 : : js::ClassSpec::DontDefineConstructor};
235 : :
236 : : static constexpr JSClass klass = {
237 : : "GIRepositoryNamespace",
238 : : JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_FOREGROUND_FINALIZE,
239 : : &Ns::class_ops, &Ns::class_spec};
240 : :
241 : : public:
242 : : GJS_JSAPI_RETURN_CONVENTION
243 : 260 : static JSObject* create(JSContext* cx, const char* ns_name) {
244 : 260 : JS::RootedObject proto(cx, Ns::create_prototype(cx));
245 [ - + ]: 260 : if (!proto)
246 : 0 : return nullptr;
247 : :
248 : : JS::RootedObject ns(cx,
249 : 260 : JS_NewObjectWithGivenProto(cx, &Ns::klass, proto));
250 [ - + ]: 260 : if (!ns)
251 : 0 : return nullptr;
252 : :
253 : 260 : auto* priv = new Ns(ns_name);
254 : 260 : Ns::init_private(ns, priv);
255 : :
256 : : gjs_debug_lifecycle(GJS_DEBUG_GNAMESPACE,
257 : : "ns constructor, obj %p priv %p", ns.get(), priv);
258 : :
259 : 260 : return ns;
260 : 260 : }
261 : : };
262 : :
263 : : JSObject*
264 : 260 : gjs_create_ns(JSContext *context,
265 : : const char *ns_name)
266 : : {
267 : 260 : return Ns::create(context, ns_name);
268 : : }
|