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 : : // SPDX-FileCopyrightText: 2018-2020 Canonical, Ltd
5 : :
6 : : #ifndef GJS_JSAPI_UTIL_H_
7 : : #define GJS_JSAPI_UTIL_H_
8 : :
9 : : #include <config.h>
10 : :
11 : : #include <stdint.h>
12 : : #include <stdlib.h> // for free
13 : : #include <sys/types.h> // for ssize_t
14 : :
15 : : #include <limits>
16 : : #include <string> // for string, u16string
17 : : #include <type_traits> // for enable_if_t, add_pointer_t, add_const_t
18 : : #include <utility> // IWYU pragma: keep
19 : : #include <vector>
20 : :
21 : : #include <girepository.h>
22 : : #include <glib-object.h>
23 : : #include <glib.h>
24 : :
25 : : #include <js/BigInt.h>
26 : : #include <js/ErrorReport.h> // for JSExnType
27 : : #include <js/GCAPI.h>
28 : : #include <js/GCPolicyAPI.h> // for IgnoreGCPolicy
29 : : #include <js/Id.h>
30 : : #include <js/TypeDecls.h>
31 : : #include <js/Utility.h> // for UniqueChars
32 : :
33 : : #include "gjs/macros.h"
34 : : #include "util/log.h"
35 : :
36 : : #if GJS_VERBOSE_ENABLE_MARSHAL
37 : : # include "gi/arg-types-inl.h" // for static_type_name
38 : : #endif
39 : :
40 : : namespace JS {
41 : : class CallArgs;
42 : :
43 : : struct Dummy {};
44 : : using GTypeNotUint64 =
45 : : std::conditional_t<!std::is_same_v<GType, uint64_t>, GType, Dummy>;
46 : :
47 : : // The GC sweep method should ignore FundamentalTable and GTypeTable's key types
48 : : // Forward declarations
49 : : template <>
50 : : struct GCPolicy<void*> : public IgnoreGCPolicy<void*> {};
51 : : // We need GCPolicy<GType> for GTypeTable. SpiderMonkey already defines
52 : : // GCPolicy<uint64_t> which is equal to GType on some systems; for others we
53 : : // need to define it. (macOS's uint64_t is unsigned long long, which is a
54 : : // different type from unsigned long, even if they are the same width)
55 : : template <>
56 : : struct GCPolicy<GTypeNotUint64> : public IgnoreGCPolicy<GTypeNotUint64> {};
57 : : } // namespace JS
58 : :
59 : : struct GjsAutoTakeOwnership {};
60 : :
61 : : template <typename F = void>
62 : : using GjsAutoPointerRefFunction = F* (*)(F*);
63 : :
64 : : template <typename F = void>
65 : : using GjsAutoPointerFreeFunction = void (*)(F*);
66 : :
67 : : template <typename T, typename F = void,
68 : : GjsAutoPointerFreeFunction<F> free_func = free,
69 : : GjsAutoPointerRefFunction<F> ref_func = nullptr>
70 : : struct GjsAutoPointer {
71 : : using Tp =
72 : : std::conditional_t<std::is_array_v<T>, std::remove_extent_t<T>, T>;
73 : : using Ptr = std::add_pointer_t<Tp>;
74 : : using ConstPtr = std::add_pointer_t<std::add_const_t<Tp>>;
75 : : using RvalueRef = std::add_lvalue_reference_t<Tp>;
76 : :
77 : : protected:
78 : : using BaseType = GjsAutoPointer<T, F, free_func, ref_func>;
79 : :
80 : : private:
81 : : template <typename FunctionType, FunctionType function>
82 : : static constexpr bool has_function() {
83 : : using NullType = std::integral_constant<FunctionType, nullptr>;
84 : : using ActualType = std::integral_constant<FunctionType, function>;
85 : :
86 : : return !std::is_same_v<ActualType, NullType>;
87 : : }
88 : :
89 : : public:
90 : : static constexpr bool has_free_function() {
91 : : return has_function<GjsAutoPointerFreeFunction<F>, free_func>();
92 : : }
93 : :
94 : : static constexpr bool has_ref_function() {
95 : : return has_function<GjsAutoPointerRefFunction<F>, ref_func>();
96 : : }
97 : :
98 : 658038 : constexpr GjsAutoPointer(Ptr ptr = nullptr) // NOLINT(runtime/explicit)
99 : 658038 : : m_ptr(ptr) {}
100 : : template <typename U, typename = std::enable_if_t<std::is_same_v<U, Tp> &&
101 : : std::is_array_v<T>>>
102 : : explicit constexpr GjsAutoPointer(U ptr[]) : m_ptr(ptr) {}
103 : :
104 : 15889 : constexpr GjsAutoPointer(Ptr ptr, const GjsAutoTakeOwnership&)
105 : 15889 : : GjsAutoPointer(ptr) {
106 : 15889 : m_ptr = copy();
107 : 15889 : }
108 : 15 : constexpr GjsAutoPointer(ConstPtr ptr, const GjsAutoTakeOwnership& o)
109 : 15 : : GjsAutoPointer(const_cast<Ptr>(ptr), o) {}
110 : 299 : constexpr GjsAutoPointer(GjsAutoPointer&& other) : GjsAutoPointer() {
111 : 299 : this->swap(other);
112 : 299 : }
113 : 164 : constexpr GjsAutoPointer(GjsAutoPointer const& other) : GjsAutoPointer() {
114 : 164 : *this = other;
115 : 164 : }
116 : :
117 : 208467 : constexpr GjsAutoPointer& operator=(Ptr ptr) {
118 : 208467 : reset(ptr);
119 : 208467 : return *this;
120 : : }
121 : :
122 : 24803 : constexpr GjsAutoPointer& operator=(GjsAutoPointer&& other) {
123 : 24803 : this->swap(other);
124 : 24803 : return *this;
125 : : }
126 : :
127 : 190 : constexpr GjsAutoPointer& operator=(GjsAutoPointer const& other) {
128 : 190 : GjsAutoPointer dup(other.get(), GjsAutoTakeOwnership());
129 : 190 : this->swap(dup);
130 : 380 : return *this;
131 : 190 : }
132 : :
133 : : template <typename U = T>
134 : 8476 : constexpr std::enable_if_t<!std::is_array_v<U>, Ptr> operator->() {
135 : 8476 : return m_ptr;
136 : : }
137 : :
138 : : template <typename U = T>
139 : 14 : constexpr std::enable_if_t<!std::is_array_v<U>, ConstPtr> operator->()
140 : : const {
141 : 14 : return m_ptr;
142 : : }
143 : :
144 : : template <typename U = T>
145 : 186949 : constexpr std::enable_if_t<std::is_array_v<U>, RvalueRef> operator[](
146 : : int index) {
147 : 186949 : return m_ptr[index];
148 : : }
149 : :
150 : : template <typename U = T>
151 : : constexpr std::enable_if_t<std::is_array_v<U>, std::add_const_t<RvalueRef>>
152 : 675833 : operator[](int index) const {
153 : 675833 : return m_ptr[index];
154 : : }
155 : :
156 : 1 : constexpr Tp operator*() const { return *m_ptr; }
157 : 926885 : constexpr operator Ptr() { return m_ptr; }
158 : 492924 : constexpr operator Ptr() const { return m_ptr; }
159 : 222 : constexpr operator ConstPtr() const { return m_ptr; }
160 : 6483 : constexpr operator bool() const { return m_ptr != nullptr; }
161 : :
162 : 548303 : constexpr Ptr get() const { return m_ptr; }
163 : 100980 : constexpr Ptr* out() { return &m_ptr; }
164 : : constexpr ConstPtr* out() const { return const_cast<ConstPtr*>(&m_ptr); }
165 : :
166 : 49868 : constexpr Ptr release() {
167 : 49868 : auto* ptr = m_ptr;
168 : 49868 : m_ptr = nullptr;
169 : 49868 : return ptr;
170 : : }
171 : :
172 : 928457 : constexpr void reset(Ptr ptr = nullptr) {
173 : 928457 : Ptr old_ptr = m_ptr;
174 : 928457 : m_ptr = ptr;
175 : :
176 : : if constexpr (has_free_function()) {
177 [ + + ]: 898672 : if (old_ptr)
178 : : free_func(reinterpret_cast<F*>(old_ptr));
179 : : }
180 : 928457 : }
181 : :
182 : 25295 : constexpr void swap(GjsAutoPointer& other) {
183 : 25295 : std::swap(this->m_ptr, other.m_ptr);
184 : 25295 : }
185 : :
186 : 704843 : /* constexpr */ ~GjsAutoPointer() { // one day, with -std=c++2a
187 : 704843 : reset();
188 : 704843 : }
189 : :
190 : : template <typename U = T>
191 : 15908 : [[nodiscard]] constexpr std::enable_if_t<!std::is_array_v<U>, Ptr> copy()
192 : : const {
193 : : static_assert(has_ref_function(), "No ref function provided");
194 [ + + ]: 15908 : return m_ptr ? reinterpret_cast<Ptr>(
195 : 15723 : ref_func(reinterpret_cast<F*>(m_ptr)))
196 : 15908 : : nullptr;
197 : : }
198 : :
199 : : template <typename C>
200 : 70863 : [[nodiscard]] constexpr C* as() const {
201 : 70863 : return const_cast<C*>(reinterpret_cast<const C*>(m_ptr));
202 : : }
203 : :
204 : : private:
205 : : Ptr m_ptr;
206 : : };
207 : :
208 : : template <typename T, GjsAutoPointerFreeFunction<T> free_func = free,
209 : : GjsAutoPointerRefFunction<T> ref_func = nullptr>
210 : : struct GjsAutoPointerSimple : GjsAutoPointer<T, T, free_func, ref_func> {
211 : : using GjsAutoPointer<T, T, free_func, ref_func>::GjsAutoPointer;
212 : : };
213 : :
214 : : template <typename T, typename F = void,
215 : : GjsAutoPointerFreeFunction<F> free_func,
216 : : GjsAutoPointerRefFunction<F> ref_func>
217 : 1 : constexpr bool operator==(
218 : : GjsAutoPointer<T, F, free_func, ref_func> const& lhs,
219 : : GjsAutoPointer<T, F, free_func, ref_func> const& rhs) {
220 : 1 : return lhs.get() == rhs.get();
221 : : }
222 : :
223 : : template <typename T>
224 : 57 : using GjsAutoFree = GjsAutoPointer<T>;
225 : :
226 : : struct GjsAutoCharFuncs {
227 : 488 : static char* dup(char* str) { return g_strdup(str); }
228 : 51819 : static void free(char* str) { g_free(str); }
229 : : };
230 : : using GjsAutoChar =
231 : 51819 : GjsAutoPointer<char, char, GjsAutoCharFuncs::free, GjsAutoCharFuncs::dup>;
232 : :
233 : : using GjsAutoChar16 = GjsAutoPointer<uint16_t, void, &g_free>;
234 : :
235 : : struct GjsAutoErrorFuncs {
236 : : static GError* error_copy(GError* error) { return g_error_copy(error); }
237 : : };
238 : :
239 : : struct GjsAutoError : GjsAutoPointer<GError, GError, g_error_free,
240 : 4509 : GjsAutoErrorFuncs::error_copy> {
241 : : using BaseType::BaseType;
242 : : using BaseType::operator=;
243 : :
244 : : constexpr BaseType::ConstPtr* operator&() // NOLINT(runtime/operator)
245 : : const {
246 : : return out();
247 : : }
248 : 74229 : constexpr BaseType::Ptr* operator&() { // NOLINT(runtime/operator)
249 : 74229 : return out();
250 : : }
251 : : };
252 : :
253 : 28740 : using GjsAutoStrv = GjsAutoPointer<char*, char*, g_strfreev, g_strdupv>;
254 : :
255 : : template <typename T>
256 : 24368 : using GjsAutoUnref = GjsAutoPointer<T, void, g_object_unref, g_object_ref>;
257 : :
258 : : using GjsAutoGVariant =
259 : : GjsAutoPointer<GVariant, GVariant, g_variant_unref, g_variant_ref>;
260 : :
261 : : template <typename V, typename T>
262 : 192437 : constexpr void GjsAutoPointerDeleter(T v) {
263 : : if constexpr (std::is_array_v<V>)
264 [ + - ]: 202170 : delete[] reinterpret_cast<std::remove_extent_t<V>*>(v);
265 : : else
266 [ + - ]: 30346 : delete v;
267 : 192437 : }
268 : :
269 : : template <typename T>
270 : 192437 : using GjsAutoCppPointer = GjsAutoPointer<T, T, GjsAutoPointerDeleter<T>>;
271 : :
272 : : template <typename T = GTypeClass>
273 : 1282 : struct GjsAutoTypeClass : GjsAutoPointer<T, void, &g_type_class_unref> {
274 : 1282 : GjsAutoTypeClass(gpointer ptr = nullptr) // NOLINT(runtime/explicit)
275 : 1282 : : GjsAutoPointer<T, void, g_type_class_unref>(static_cast<T*>(ptr)) {}
276 : 1282 : explicit GjsAutoTypeClass(GType gtype)
277 : 1282 : : GjsAutoTypeClass(g_type_class_ref(gtype)) {}
278 : : };
279 : :
280 : : // Use this class for owning a GIBaseInfo* of indeterminate type. Any type (e.g.
281 : : // GIFunctionInfo*, GIObjectInfo*) will fit. If you know that the info is of a
282 : : // certain type (e.g. you are storing the return value of a function that
283 : : // returns GIFunctionInfo*,) use one of the derived classes below.
284 : : struct GjsAutoBaseInfo : GjsAutoPointer<GIBaseInfo, GIBaseInfo,
285 : 220822 : g_base_info_unref, g_base_info_ref> {
286 : : using GjsAutoPointer::GjsAutoPointer;
287 : :
288 : 101746 : [[nodiscard]] const char* name() const {
289 : 101746 : return g_base_info_get_name(*this);
290 : : }
291 : 59231 : [[nodiscard]] const char* ns() const {
292 : 59231 : return g_base_info_get_namespace(*this);
293 : : }
294 : 60453 : [[nodiscard]] GIInfoType type() const {
295 : 60453 : return g_base_info_get_type(*this);
296 : : }
297 : : };
298 : :
299 : : // Use GjsAutoInfo, preferably its typedefs below, when you know for sure that
300 : : // the info is either of a certain type or null.
301 : : template <GIInfoType TAG>
302 : : struct GjsAutoInfo : GjsAutoBaseInfo {
303 : : using GjsAutoBaseInfo::GjsAutoBaseInfo;
304 : :
305 : : // Normally one-argument constructors should be explicit, but we are trying
306 : : // to conform to the interface of std::unique_ptr here.
307 : 162379 : GjsAutoInfo(GIBaseInfo* ptr = nullptr) // NOLINT(runtime/explicit)
308 : 162379 : : GjsAutoBaseInfo(ptr) {
309 : : #ifndef G_DISABLE_CAST_CHECKS
310 : 162379 : validate();
311 : : #endif
312 : 162379 : }
313 : :
314 : 7906 : void reset(GIBaseInfo* other = nullptr) {
315 : 7906 : GjsAutoBaseInfo::reset(other);
316 : : #ifndef G_DISABLE_CAST_CHECKS
317 : 7906 : validate();
318 : : #endif
319 : 7906 : }
320 : :
321 : : // You should not need this method, because you already know the answer.
322 : : GIInfoType type() = delete;
323 : :
324 : : private:
325 : 170285 : void validate() const {
326 [ + + ]: 170285 : if (GIBaseInfo* base = *this)
327 : 138564 : g_assert(g_base_info_get_type(base) == TAG);
328 : 170285 : }
329 : : };
330 : :
331 : : using GjsAutoArgInfo = GjsAutoInfo<GI_INFO_TYPE_ARG>;
332 : : using GjsAutoEnumInfo = GjsAutoInfo<GI_INFO_TYPE_ENUM>;
333 : : using GjsAutoFieldInfo = GjsAutoInfo<GI_INFO_TYPE_FIELD>;
334 : : using GjsAutoFunctionInfo = GjsAutoInfo<GI_INFO_TYPE_FUNCTION>;
335 : : using GjsAutoInterfaceInfo = GjsAutoInfo<GI_INFO_TYPE_INTERFACE>;
336 : : using GjsAutoObjectInfo = GjsAutoInfo<GI_INFO_TYPE_OBJECT>;
337 : : using GjsAutoPropertyInfo = GjsAutoInfo<GI_INFO_TYPE_PROPERTY>;
338 : : using GjsAutoStructInfo = GjsAutoInfo<GI_INFO_TYPE_STRUCT>;
339 : : using GjsAutoSignalInfo = GjsAutoInfo<GI_INFO_TYPE_SIGNAL>;
340 : : using GjsAutoTypeInfo = GjsAutoInfo<GI_INFO_TYPE_TYPE>;
341 : : using GjsAutoValueInfo = GjsAutoInfo<GI_INFO_TYPE_VALUE>;
342 : : using GjsAutoVFuncInfo = GjsAutoInfo<GI_INFO_TYPE_VFUNC>;
343 : :
344 : : // GICallableInfo can be one of several tags, so we have to have a separate
345 : : // class, and use GI_IS_CALLABLE_INFO() to validate.
346 : : struct GjsAutoCallableInfo : GjsAutoBaseInfo {
347 : : using GjsAutoBaseInfo::GjsAutoBaseInfo;
348 : :
349 : 1 : GjsAutoCallableInfo(GIBaseInfo* ptr = nullptr) // NOLINT(runtime/explicit)
350 : 1 : : GjsAutoBaseInfo(ptr) {
351 : 1 : validate();
352 : 1 : }
353 : :
354 : : void reset(GIBaseInfo* other = nullptr) {
355 : : GjsAutoBaseInfo::reset(other);
356 : : validate();
357 : : }
358 : :
359 : : private:
360 : 1 : void validate() const {
361 [ - + ]: 1 : if (*this)
362 : 0 : g_assert(GI_IS_CALLABLE_INFO(get()));
363 : 1 : }
364 : : };
365 : :
366 : : template <typename T>
367 : : struct GjsSmartPointer : GjsAutoPointer<T> {
368 : : using GjsAutoPointer<T>::GjsAutoPointer;
369 : : };
370 : :
371 : : template <>
372 : : struct GjsSmartPointer<char*> : GjsAutoStrv {
373 : : using GjsAutoStrv::GjsAutoPointer;
374 : : };
375 : :
376 : : template <>
377 : : struct GjsSmartPointer<GStrv> : GjsAutoStrv {
378 : : using GjsAutoStrv::GjsAutoPointer;
379 : : };
380 : :
381 : : template <>
382 : : struct GjsSmartPointer<GObject> : GjsAutoUnref<GObject> {
383 : : using GjsAutoUnref<GObject>::GjsAutoUnref;
384 : : };
385 : :
386 : : template <>
387 : : struct GjsSmartPointer<GIBaseInfo> : GjsAutoBaseInfo {
388 : : using GjsAutoBaseInfo::GjsAutoBaseInfo;
389 : : };
390 : :
391 : : template <>
392 : : struct GjsSmartPointer<GError> : GjsAutoError {
393 : : using GjsAutoError::GjsAutoError;
394 : : using GjsAutoError::operator=;
395 : : using GjsAutoError::operator&;
396 : : };
397 : :
398 : : template <>
399 : : struct GjsSmartPointer<GVariant> : GjsAutoGVariant {
400 : : using GjsAutoGVariant::GjsAutoPointer;
401 : : };
402 : :
403 : : template <>
404 : 86 : struct GjsSmartPointer<GList> : GjsAutoPointer<GList, GList, g_list_free> {
405 : : using GjsAutoPointer::GjsAutoPointer;
406 : : };
407 : :
408 : : template <>
409 : 45 : struct GjsSmartPointer<GSList> : GjsAutoPointer<GSList, GSList, g_slist_free> {
410 : : using GjsAutoPointer::GjsAutoPointer;
411 : : };
412 : :
413 : : /* For use of GjsAutoInfo<TAG> in GC hash maps */
414 : : namespace JS {
415 : : template <GIInfoType TAG>
416 : : struct GCPolicy<GjsAutoInfo<TAG>> : public IgnoreGCPolicy<GjsAutoInfo<TAG>> {};
417 : : } // namespace JS
418 : :
419 : : using GjsAutoParam = GjsAutoPointer<GParamSpec, GParamSpec, g_param_spec_unref,
420 : 378 : g_param_spec_ref>;
421 : :
422 : : /* For use of GjsAutoParam in GC hash maps */
423 : : namespace JS {
424 : : template <>
425 : : struct GCPolicy<GjsAutoParam> : public IgnoreGCPolicy<GjsAutoParam> {};
426 : : } // namespace JS
427 : :
428 : : /* Flags that should be set on properties exported from native code modules.
429 : : * Basically set these on API, but do NOT set them on data.
430 : : *
431 : : * PERMANENT: forbid deleting the prop
432 : : * ENUMERATE: allows copyProperties to work among other reasons to have it
433 : : */
434 : : #define GJS_MODULE_PROP_FLAGS (JSPROP_PERMANENT | JSPROP_ENUMERATE)
435 : :
436 : : /*
437 : : * GJS_GET_THIS:
438 : : * @cx: JSContext pointer passed into JSNative function
439 : : * @argc: Number of arguments passed into JSNative function
440 : : * @vp: Argument value array passed into JSNative function
441 : : * @args: Name for JS::CallArgs variable defined by this code snippet
442 : : * @to: Name for JS::RootedObject variable referring to function's this
443 : : *
444 : : * A convenience macro for getting the 'this' object a function was called with.
445 : : * Use in any JSNative function.
446 : : */
447 : : #define GJS_GET_THIS(cx, argc, vp, args, to) \
448 : : JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
449 : : JS::RootedObject to(cx); \
450 : : if (!args.computeThis(cx, &to)) \
451 : : return false;
452 : :
453 : : void gjs_throw_constructor_error (JSContext *context);
454 : :
455 : : void gjs_throw_abstract_constructor_error(JSContext* cx,
456 : : const JS::CallArgs& args);
457 : :
458 : : GJS_JSAPI_RETURN_CONVENTION
459 : : JSObject* gjs_build_string_array(JSContext* cx,
460 : : const std::vector<std::string>& strings);
461 : :
462 : : GJS_JSAPI_RETURN_CONVENTION
463 : : JSObject* gjs_define_string_array(JSContext* cx, JS::HandleObject obj,
464 : : const char* array_name,
465 : : const std::vector<std::string>& strings,
466 : : unsigned attrs);
467 : :
468 : : [[gnu::format(printf, 2, 3)]] void gjs_throw(JSContext* cx, const char* format,
469 : : ...);
470 : : [[gnu::format(printf, 4, 5)]] void gjs_throw_custom(JSContext* cx,
471 : : JSExnType error_kind,
472 : : const char* error_name,
473 : : const char* format, ...);
474 : : void gjs_throw_literal (JSContext *context,
475 : : const char *string);
476 : : bool gjs_throw_gerror_message(JSContext* cx, GjsAutoError const&);
477 : :
478 : : bool gjs_log_exception (JSContext *context);
479 : :
480 : : bool gjs_log_exception_uncaught(JSContext* cx);
481 : :
482 : : void gjs_log_exception_full(JSContext* cx, JS::HandleValue exc,
483 : : JS::HandleString message, GLogLevelFlags level);
484 : :
485 : : void gjs_warning_reporter(JSContext*, JSErrorReport* report);
486 : :
487 : : GJS_JSAPI_RETURN_CONVENTION
488 : : JS::UniqueChars gjs_string_to_utf8(JSContext* cx, const JS::Value string_val);
489 : : GJS_JSAPI_RETURN_CONVENTION
490 : : bool gjs_string_to_utf8_n(JSContext* cx, JS::HandleString str, JS::UniqueChars* output,
491 : : size_t* output_len);
492 : : GJS_JSAPI_RETURN_CONVENTION
493 : : JSString* gjs_lossy_string_from_utf8(JSContext* cx, const char* utf8_string);
494 : : GJS_JSAPI_RETURN_CONVENTION
495 : : JSString* gjs_lossy_string_from_utf8_n(JSContext* cx, const char* utf8_string,
496 : : size_t len);
497 : : GJS_JSAPI_RETURN_CONVENTION
498 : : bool gjs_string_from_utf8(JSContext *context,
499 : : const char *utf8_string,
500 : : JS::MutableHandleValue value_p);
501 : : GJS_JSAPI_RETURN_CONVENTION
502 : : bool gjs_string_from_utf8_n(JSContext *cx,
503 : : const char *utf8_chars,
504 : : size_t len,
505 : : JS::MutableHandleValue out);
506 : :
507 : : GJS_JSAPI_RETURN_CONVENTION
508 : : bool gjs_string_to_filename(JSContext *cx,
509 : : const JS::Value string_val,
510 : : GjsAutoChar *filename_string);
511 : :
512 : : GJS_JSAPI_RETURN_CONVENTION
513 : : bool gjs_string_from_filename(JSContext *context,
514 : : const char *filename_string,
515 : : ssize_t n_bytes,
516 : : JS::MutableHandleValue value_p);
517 : :
518 : : GJS_JSAPI_RETURN_CONVENTION
519 : : bool gjs_string_get_char16_data(JSContext *cx,
520 : : JS::HandleString str,
521 : : char16_t **data_p,
522 : : size_t *len_p);
523 : :
524 : : GJS_JSAPI_RETURN_CONVENTION
525 : : bool gjs_string_to_ucs4(JSContext *cx,
526 : : JS::HandleString value,
527 : : gunichar **ucs4_string_p,
528 : : size_t *len_p);
529 : : GJS_JSAPI_RETURN_CONVENTION
530 : : bool gjs_string_from_ucs4(JSContext *cx,
531 : : const gunichar *ucs4_string,
532 : : ssize_t n_chars,
533 : : JS::MutableHandleValue value_p);
534 : :
535 : : GJS_JSAPI_RETURN_CONVENTION
536 : : bool gjs_get_string_id(JSContext* cx, jsid id, JS::UniqueChars* name_p);
537 : : GJS_JSAPI_RETURN_CONVENTION
538 : : jsid gjs_intern_string_to_id (JSContext *context,
539 : : const char *string);
540 : :
541 : : GJS_JSAPI_RETURN_CONVENTION
542 : : bool gjs_unichar_from_string (JSContext *context,
543 : : JS::Value string,
544 : : gunichar *result);
545 : :
546 : : /* Functions intended for more "internal" use */
547 : :
548 : : void gjs_maybe_gc (JSContext *context);
549 : : void gjs_gc_if_needed(JSContext *cx);
550 : :
551 : : GJS_JSAPI_RETURN_CONVENTION
552 : : JS::UniqueChars format_saved_frame(JSContext* cx, JS::HandleObject saved_frame,
553 : : size_t indent = 0);
554 : :
555 : : /* Overloaded functions, must be outside G_DECLS. More types are intended to be
556 : : * added as the opportunity arises. */
557 : :
558 : : GJS_JSAPI_RETURN_CONVENTION
559 : : bool gjs_object_require_property(JSContext *context,
560 : : JS::HandleObject obj,
561 : : const char *obj_description,
562 : : JS::HandleId property_name,
563 : : JS::MutableHandleValue value);
564 : :
565 : : GJS_JSAPI_RETURN_CONVENTION
566 : : bool gjs_object_require_property(JSContext *cx,
567 : : JS::HandleObject obj,
568 : : const char *description,
569 : : JS::HandleId property_name,
570 : : bool *value);
571 : :
572 : : GJS_JSAPI_RETURN_CONVENTION
573 : : bool gjs_object_require_property(JSContext *cx,
574 : : JS::HandleObject obj,
575 : : const char *description,
576 : : JS::HandleId property_name,
577 : : int32_t *value);
578 : :
579 : : GJS_JSAPI_RETURN_CONVENTION
580 : : bool gjs_object_require_property(JSContext* cx, JS::HandleObject obj,
581 : : const char* description,
582 : : JS::HandleId property_name,
583 : : JS::UniqueChars* value);
584 : :
585 : : GJS_JSAPI_RETURN_CONVENTION
586 : : bool gjs_object_require_property(JSContext *cx,
587 : : JS::HandleObject obj,
588 : : const char *description,
589 : : JS::HandleId property_name,
590 : : JS::MutableHandleObject value);
591 : :
592 : : GJS_JSAPI_RETURN_CONVENTION
593 : : bool gjs_object_require_converted_property(JSContext *context,
594 : : JS::HandleObject obj,
595 : : const char *description,
596 : : JS::HandleId property_name,
597 : : uint32_t *value);
598 : :
599 : : [[nodiscard]] std::string gjs_debug_bigint(JS::BigInt* bi);
600 : : [[nodiscard]] std::string gjs_debug_string(JSString* str);
601 : : [[nodiscard]] std::string gjs_debug_symbol(JS::Symbol* const sym);
602 : : [[nodiscard]] std::string gjs_debug_object(JSObject* obj);
603 : : [[nodiscard]] std::string gjs_debug_callable(JSObject* callable);
604 : : [[nodiscard]] std::string gjs_debug_value(JS::Value v);
605 : : [[nodiscard]] std::string gjs_debug_id(jsid id);
606 : :
607 : : [[nodiscard]] GjsAutoChar gjs_hyphen_to_underscore(const char* str);
608 : : [[nodiscard]] GjsAutoChar gjs_hyphen_to_camel(const char* str);
609 : :
610 : : #if defined(G_OS_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1900))
611 : : [[nodiscard]] std::wstring gjs_win32_vc140_utf8_to_utf16(const char* str);
612 : : #endif
613 : :
614 : : // Custom GC reasons; SpiderMonkey includes a bunch of "Firefox reasons" which
615 : : // don't apply when embedding the JS engine, so we repurpose them for our own
616 : : // reasons.
617 : :
618 : : // clang-format off
619 : : #define FOREACH_GC_REASON(macro) \
620 : : macro(LINUX_RSS_TRIGGER, 0) \
621 : : macro(GJS_CONTEXT_DISPOSE, 1) \
622 : : macro(BIG_HAMMER, 2) \
623 : : macro(GJS_API_CALL, 3) \
624 : : macro(LOW_MEMORY, 4)
625 : : // clang-format on
626 : :
627 : : namespace Gjs {
628 : :
629 : : struct GCReason {
630 : : #define DEFINE_GC_REASON(name, ix) \
631 : : static constexpr JS::GCReason name = JS::GCReason( \
632 : : static_cast<int>(JS::GCReason::FIRST_FIREFOX_REASON) + ix);
633 : : FOREACH_GC_REASON(DEFINE_GC_REASON);
634 : : #undef DEFINE_GC_REASON
635 : :
636 : : #define COUNT_GC_REASON(name, ix) +1
637 : : static constexpr size_t N_REASONS = 0 FOREACH_GC_REASON(COUNT_GC_REASON);
638 : : #undef COUNT_GC_REASON
639 : : };
640 : :
641 : : template <typename T>
642 : 1511 : [[nodiscard]] bool bigint_is_out_of_range(JS::BigInt* bi, T* clamped) {
643 : : static_assert(sizeof(T) == 8, "64-bit types only");
644 : 1511 : g_assert(bi && "bigint cannot be null");
645 : 1511 : g_assert(clamped && "forgot out parameter");
646 : :
647 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
648 : : "Checking if BigInt %s is out of range for type %s",
649 : : gjs_debug_bigint(bi).c_str(), Gjs::static_type_name<T>());
650 : :
651 [ + + ]: 1511 : if (JS::BigIntFits(bi, clamped)) {
652 : : gjs_debug_marshal(
653 : : GJS_DEBUG_GFUNCTION, "BigInt %s is in the range of type %s",
654 : : std::to_string(*clamped).c_str(), Gjs::static_type_name<T>());
655 : 1504 : return false;
656 : : }
657 : :
658 [ + + ]: 7 : if (JS::BigIntIsNegative(bi)) {
659 : 3 : *clamped = std::numeric_limits<T>::min();
660 : : } else {
661 : 4 : *clamped = std::numeric_limits<T>::max();
662 : : }
663 : :
664 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
665 : : "BigInt %s is not in the range of type %s, clamped to %s",
666 : : gjs_debug_bigint(bi).c_str(), Gjs::static_type_name<T>(),
667 : : std::to_string(*clamped).c_str());
668 : 7 : return true;
669 : : }
670 : :
671 : : } // namespace Gjs
672 : :
673 : : [[nodiscard]] const char* gjs_explain_gc_reason(JS::GCReason reason);
674 : :
675 : : #endif // GJS_JSAPI_UTIL_H_
|