1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /*
3  * Copyright (c) 2008  litl, LLC
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 #ifndef GJS_JSAPI_UTIL_H_
25 #define GJS_JSAPI_UTIL_H_
26 
27 #include <config.h>
28 
29 #include <stddef.h>  // for size_t
30 #include <stdint.h>
31 #include <sys/types.h>  // for ssize_t
32 
33 #include <memory>  // for unique_ptr
34 #include <string>  // for string, u16string
35 #include <vector>
36 
37 #include <girepository.h>
38 #include <glib-object.h>
39 #include <glib.h>
40 
41 #include <js/GCPolicyAPI.h>  // for IgnoreGCPolicy
42 #include <js/Id.h>
43 #include <js/TypeDecls.h>
44 #include <js/Utility.h>  // for UniqueChars
45 #include <jspubtd.h>     // for JSProtoKey
46 
47 #include "cjs/macros.h"
48 
49 class JSErrorReport;
50 namespace JS {
51 class CallArgs;
52 }
53 
54 struct GjsAutoTakeOwnership {};
55 
56 template <typename T, typename F,
57           void (*free_func)(F*) = nullptr, F* (*ref_func)(F*) = nullptr>
58 struct GjsAutoPointer : std::unique_ptr<T, decltype(free_func)> {
59     GjsAutoPointer(T* ptr = nullptr)  // NOLINT(runtime/explicit)
60         : GjsAutoPointer::unique_ptr(ptr, *free_func) {}
GjsAutoPointerGjsAutoPointer61     GjsAutoPointer(T* ptr, const GjsAutoTakeOwnership&)
62         : GjsAutoPointer(nullptr) {
63         // FIXME: should use if constexpr (...), but that doesn't work with
64         // ubsan, which generates a null pointer check making it not a constexpr
65         // anymore: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71962 - Also a
66         // bogus warning, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94554
67         auto ref = ref_func;
68         this->reset(ptr && ref ? reinterpret_cast<T*>(ref(ptr)) : ptr);
69     }
70 
71     operator T*() const { return this->get(); }
72     T& operator[](size_t i) const { return static_cast<T*>(*this)[i]; }
73 
copyGjsAutoPointer74     [[nodiscard]] T* copy() const {
75         return reinterpret_cast<T*>(ref_func(this->get()));
76     }
77 
78     template <typename C>
asGjsAutoPointer79     [[nodiscard]] C* as() const {
80         return const_cast<C*>(reinterpret_cast<const C*>(this->get()));
81     }
82 };
83 
84 template <typename T>
85 using GjsAutoFree = GjsAutoPointer<T, void, g_free>;
86 
87 struct GjsAutoCharFuncs {
dupGjsAutoCharFuncs88     static char* dup(char* str) { return g_strdup(str); }
freeGjsAutoCharFuncs89     static void free(char* str) { g_free(str); }
90 };
91 using GjsAutoChar =
92     GjsAutoPointer<char, char, GjsAutoCharFuncs::free, GjsAutoCharFuncs::dup>;
93 
94 using GjsAutoStrv = GjsAutoPointer<char*, char*, g_strfreev>;
95 
96 template <typename T>
97 using GjsAutoUnref = GjsAutoPointer<T, void, g_object_unref, g_object_ref>;
98 
99 template <typename T = GTypeClass>
100 struct GjsAutoTypeClass : GjsAutoPointer<T, void, &g_type_class_unref> {
101     GjsAutoTypeClass(gpointer ptr = nullptr)  // NOLINT(runtime/explicit)
102         : GjsAutoPointer<T, void, g_type_class_unref>(static_cast<T*>(ptr)) {}
GjsAutoTypeClassGjsAutoTypeClass103     explicit GjsAutoTypeClass(GType gtype)
104         : GjsAutoTypeClass(g_type_class_ref(gtype)) {}
105 };
106 
107 // Use this class for owning a GIBaseInfo* of indeterminate type. Any type (e.g.
108 // GIFunctionInfo*, GIObjectInfo*) will fit. If you know that the info is of a
109 // certain type (e.g. you are storing the return value of a function that
110 // returns GIFunctionInfo*,) use one of the derived classes below.
111 struct GjsAutoBaseInfo : GjsAutoPointer<GIBaseInfo, GIBaseInfo,
112                                         g_base_info_unref, g_base_info_ref> {
113     GjsAutoBaseInfo(GIBaseInfo* ptr = nullptr)  // NOLINT(runtime/explicit)
GjsAutoPointerGjsAutoBaseInfo114         : GjsAutoPointer(ptr) {}
115 
nameGjsAutoBaseInfo116     [[nodiscard]] const char* name() const {
117         return g_base_info_get_name(*this);
118     }
nsGjsAutoBaseInfo119     [[nodiscard]] const char* ns() const {
120         return g_base_info_get_namespace(*this);
121     }
typeGjsAutoBaseInfo122     [[nodiscard]] GIInfoType type() const {
123         return g_base_info_get_type(*this);
124     }
125 };
126 
127 // Use GjsAutoInfo, preferably its typedefs below, when you know for sure that
128 // the info is either of a certain type or null.
129 template <GIInfoType TAG>
130 struct GjsAutoInfo : GjsAutoBaseInfo {
131     // Normally one-argument constructors should be explicit, but we are trying
132     // to conform to the interface of std::unique_ptr here.
133     GjsAutoInfo(GIBaseInfo* ptr = nullptr)  // NOLINT(runtime/explicit)
GjsAutoBaseInfoGjsAutoInfo134         : GjsAutoBaseInfo(ptr) {
135         validate();
136     }
137 
138     void reset(GIBaseInfo* other = nullptr) {
139         GjsAutoBaseInfo::reset(other);
140         validate();
141     }
142 
143     // You should not need this method, because you already know the answer.
144     GIInfoType type() = delete;
145 
146  private:
validateGjsAutoInfo147     void validate() const {
148         if (GIBaseInfo* base = *this)
149             g_assert(g_base_info_get_type(base) == TAG);
150     }
151 };
152 
153 using GjsAutoEnumInfo = GjsAutoInfo<GI_INFO_TYPE_ENUM>;
154 using GjsAutoFieldInfo = GjsAutoInfo<GI_INFO_TYPE_FIELD>;
155 using GjsAutoFunctionInfo = GjsAutoInfo<GI_INFO_TYPE_FUNCTION>;
156 using GjsAutoInterfaceInfo = GjsAutoInfo<GI_INFO_TYPE_INTERFACE>;
157 using GjsAutoObjectInfo = GjsAutoInfo<GI_INFO_TYPE_OBJECT>;
158 using GjsAutoPropertyInfo = GjsAutoInfo<GI_INFO_TYPE_PROPERTY>;
159 using GjsAutoStructInfo = GjsAutoInfo<GI_INFO_TYPE_STRUCT>;
160 using GjsAutoTypeInfo = GjsAutoInfo<GI_INFO_TYPE_TYPE>;
161 using GjsAutoValueInfo = GjsAutoInfo<GI_INFO_TYPE_VALUE>;
162 using GjsAutoVFuncInfo = GjsAutoInfo<GI_INFO_TYPE_VFUNC>;
163 
164 // GICallableInfo can be one of several tags, so we have to have a separate
165 // class, and use GI_IS_CALLABLE_INFO() to validate.
166 struct GjsAutoCallableInfo : GjsAutoBaseInfo {
167     GjsAutoCallableInfo(GIBaseInfo* ptr = nullptr)  // NOLINT(runtime/explicit)
GjsAutoBaseInfoGjsAutoCallableInfo168         : GjsAutoBaseInfo(ptr) {
169         validate();
170     }
171 
172     void reset(GIBaseInfo* other = nullptr) {
173         GjsAutoBaseInfo::reset(other);
174         validate();
175     }
176 
177  private:
validateGjsAutoCallableInfo178     void validate() const {
179         if (GIBaseInfo* base = *this)
180             g_assert(GI_IS_CALLABLE_INFO(base));
181     }
182 };
183 
184 /* For use of GjsAutoInfo<TAG> in GC hash maps */
185 namespace JS {
186 template <GIInfoType TAG>
187 struct GCPolicy<GjsAutoInfo<TAG>> : public IgnoreGCPolicy<GjsAutoInfo<TAG>> {};
188 }  // namespace JS
189 
190 using GjsAutoParam = GjsAutoPointer<GParamSpec, GParamSpec, g_param_spec_unref,
191                                     g_param_spec_ref>;
192 
193 /* For use of GjsAutoParam in GC hash maps */
194 namespace JS {
195 template <>
196 struct GCPolicy<GjsAutoParam> : public IgnoreGCPolicy<GjsAutoParam> {};
197 }  // namespace JS
198 
199 /* Flags that should be set on properties exported from native code modules.
200  * Basically set these on API, but do NOT set them on data.
201  *
202  * PERMANENT: forbid deleting the prop
203  * ENUMERATE: allows copyProperties to work among other reasons to have it
204  */
205 #define GJS_MODULE_PROP_FLAGS (JSPROP_PERMANENT | JSPROP_ENUMERATE)
206 
207 /*
208  * GJS_GET_THIS:
209  * @cx: JSContext pointer passed into JSNative function
210  * @argc: Number of arguments passed into JSNative function
211  * @vp: Argument value array passed into JSNative function
212  * @args: Name for JS::CallArgs variable defined by this code snippet
213  * @to: Name for JS::RootedObject variable referring to function's this
214  *
215  * A convenience macro for getting the 'this' object a function was called with.
216  * Use in any JSNative function.
217  */
218 #define GJS_GET_THIS(cx, argc, vp, args, to)          \
219     JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
220     JS::RootedObject to(cx);                          \
221     if (!args.computeThis(cx, &to))                   \
222         return false;
223 
224 [[nodiscard]] JSObject* gjs_get_import_global(JSContext* cx);
225 
226 void gjs_throw_constructor_error             (JSContext       *context);
227 
228 void gjs_throw_abstract_constructor_error(JSContext    *context,
229                                           JS::CallArgs& args);
230 
231 GJS_JSAPI_RETURN_CONVENTION
232 JSObject* gjs_build_string_array(JSContext* cx,
233                                  const std::vector<std::string>& strings);
234 
235 GJS_JSAPI_RETURN_CONVENTION
236 JSObject* gjs_define_string_array(JSContext* cx, JS::HandleObject obj,
237                                   const char* array_name,
238                                   const std::vector<std::string>& strings,
239                                   unsigned attrs);
240 
241 void        gjs_throw                        (JSContext       *context,
242                                               const char      *format,
243                                               ...)  G_GNUC_PRINTF (2, 3);
244 void        gjs_throw_custom                 (JSContext       *context,
245                                               JSProtoKey       error_kind,
246                                               const char      *error_name,
247                                               const char      *format,
248                                               ...)  G_GNUC_PRINTF (4, 5);
249 void        gjs_throw_literal                (JSContext       *context,
250                                               const char      *string);
251 bool gjs_throw_gerror_message(JSContext* cx, GError* error);
252 
253 bool        gjs_log_exception                (JSContext       *context);
254 
255 bool gjs_log_exception_uncaught(JSContext* cx);
256 
257 bool gjs_log_exception_full(JSContext* cx, JS::HandleValue exc,
258                             JS::HandleString message, GLogLevelFlags level);
259 
260 [[nodiscard]] char* gjs_value_debug_string(JSContext* cx,
261                                            JS::HandleValue value);
262 
263 void gjs_warning_reporter(JSContext*, JSErrorReport* report);
264 
265 GJS_JSAPI_RETURN_CONVENTION
266 JS::UniqueChars gjs_string_to_utf8(JSContext* cx, const JS::Value string_val);
267 GJS_JSAPI_RETURN_CONVENTION
268 bool gjs_string_from_utf8(JSContext             *context,
269                           const char            *utf8_string,
270                           JS::MutableHandleValue value_p);
271 GJS_JSAPI_RETURN_CONVENTION
272 bool gjs_string_from_utf8_n(JSContext             *cx,
273                             const char            *utf8_chars,
274                             size_t                 len,
275                             JS::MutableHandleValue out);
276 
277 GJS_JSAPI_RETURN_CONVENTION
278 bool gjs_string_to_filename(JSContext       *cx,
279                             const JS::Value  string_val,
280                             GjsAutoChar     *filename_string);
281 
282 GJS_JSAPI_RETURN_CONVENTION
283 bool gjs_string_from_filename(JSContext             *context,
284                               const char            *filename_string,
285                               ssize_t                n_bytes,
286                               JS::MutableHandleValue value_p);
287 
288 GJS_JSAPI_RETURN_CONVENTION
289 bool gjs_string_get_char16_data(JSContext       *cx,
290                                 JS::HandleString str,
291                                 char16_t       **data_p,
292                                 size_t          *len_p);
293 
294 GJS_JSAPI_RETURN_CONVENTION
295 bool gjs_string_to_ucs4(JSContext       *cx,
296                         JS::HandleString value,
297                         gunichar       **ucs4_string_p,
298                         size_t          *len_p);
299 GJS_JSAPI_RETURN_CONVENTION
300 bool gjs_string_from_ucs4(JSContext             *cx,
301                           const gunichar        *ucs4_string,
302                           ssize_t                n_chars,
303                           JS::MutableHandleValue value_p);
304 
305 GJS_JSAPI_RETURN_CONVENTION
306 bool gjs_get_string_id(JSContext* cx, jsid id, JS::UniqueChars* name_p);
307 GJS_JSAPI_RETURN_CONVENTION
308 jsid        gjs_intern_string_to_id          (JSContext       *context,
309                                               const char      *string);
310 
311 GJS_JSAPI_RETURN_CONVENTION
312 bool        gjs_unichar_from_string          (JSContext       *context,
313                                               JS::Value        string,
314                                               gunichar        *result);
315 
316 /* Functions intended for more "internal" use */
317 
318 void gjs_maybe_gc (JSContext *context);
319 void gjs_gc_if_needed(JSContext *cx);
320 
321 [[nodiscard]] std::u16string gjs_utf8_script_to_utf16(const char* script,
322                                                       ssize_t len);
323 
324 GJS_JSAPI_RETURN_CONVENTION
325 GjsAutoChar gjs_format_stack_trace(JSContext       *cx,
326                                    JS::HandleObject saved_frame);
327 
328 /* Overloaded functions, must be outside G_DECLS. More types are intended to be
329  * added as the opportunity arises. */
330 
331 GJS_JSAPI_RETURN_CONVENTION
332 bool gjs_object_require_property(JSContext             *context,
333                                  JS::HandleObject       obj,
334                                  const char            *obj_description,
335                                  JS::HandleId           property_name,
336                                  JS::MutableHandleValue value);
337 
338 GJS_JSAPI_RETURN_CONVENTION
339 bool gjs_object_require_property(JSContext       *cx,
340                                  JS::HandleObject obj,
341                                  const char      *description,
342                                  JS::HandleId     property_name,
343                                  bool            *value);
344 
345 GJS_JSAPI_RETURN_CONVENTION
346 bool gjs_object_require_property(JSContext       *cx,
347                                  JS::HandleObject obj,
348                                  const char      *description,
349                                  JS::HandleId     property_name,
350                                  int32_t         *value);
351 
352 GJS_JSAPI_RETURN_CONVENTION
353 bool gjs_object_require_property(JSContext* cx, JS::HandleObject obj,
354                                  const char* description,
355                                  JS::HandleId property_name,
356                                  JS::UniqueChars* value);
357 
358 GJS_JSAPI_RETURN_CONVENTION
359 bool gjs_object_require_property(JSContext              *cx,
360                                  JS::HandleObject        obj,
361                                  const char             *description,
362                                  JS::HandleId            property_name,
363                                  JS::MutableHandleObject value);
364 
365 GJS_JSAPI_RETURN_CONVENTION
366 bool gjs_object_require_converted_property(JSContext       *context,
367                                            JS::HandleObject obj,
368                                            const char      *description,
369                                            JS::HandleId     property_name,
370                                            uint32_t        *value);
371 
372 [[nodiscard]] std::string gjs_debug_string(JSString* str);
373 [[nodiscard]] std::string gjs_debug_symbol(JS::Symbol* const sym);
374 [[nodiscard]] std::string gjs_debug_object(JSObject* obj);
375 [[nodiscard]] std::string gjs_debug_value(JS::Value v);
376 [[nodiscard]] std::string gjs_debug_id(jsid id);
377 
378 [[nodiscard]] char* gjs_hyphen_to_underscore(const char* str);
379 
380 #if defined(G_OS_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1900))
381 [[nodiscard]] std::wstring gjs_win32_vc140_utf8_to_utf16(const char* str);
382 #endif
383 
384 #endif  // GJS_JSAPI_UTIL_H_
385