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 <glib-object.h>
22 : : #include <glib.h>
23 : :
24 : : #include <js/BigInt.h>
25 : : #include <js/ErrorReport.h> // for JSExnType
26 : : #include <js/GCAPI.h>
27 : : #include <js/GCPolicyAPI.h> // for IgnoreGCPolicy
28 : : #include <js/Id.h>
29 : : #include <js/TypeDecls.h>
30 : : #include <js/Utility.h> // for UniqueChars
31 : :
32 : : #include "gjs/auto.h"
33 : : #include "gjs/gerror-result.h"
34 : : #include "gjs/macros.h"
35 : : #include "util/log.h"
36 : :
37 : : #if GJS_VERBOSE_ENABLE_MARSHAL
38 : : # include "gi/arg-types-inl.h" // for static_type_name
39 : : #endif
40 : :
41 : : namespace JS {
42 : : class CallArgs;
43 : :
44 : : struct Dummy {};
45 : : using GTypeNotUint64 =
46 : : std::conditional_t<!std::is_same_v<GType, uint64_t>, GType, Dummy>;
47 : :
48 : : // The GC sweep method should ignore FundamentalTable and GTypeTable's key types
49 : : // Forward declarations
50 : : template <>
51 : : struct GCPolicy<void*> : public IgnoreGCPolicy<void*> {};
52 : : // We need GCPolicy<GType> for GTypeTable. SpiderMonkey already defines
53 : : // GCPolicy<uint64_t> which is equal to GType on some systems; for others we
54 : : // need to define it. (macOS's uint64_t is unsigned long long, which is a
55 : : // different type from unsigned long, even if they are the same width)
56 : : template <>
57 : : struct GCPolicy<GTypeNotUint64> : public IgnoreGCPolicy<GTypeNotUint64> {};
58 : : } // namespace JS
59 : :
60 : : /* Flags that should be set on properties exported from native code modules.
61 : : * Basically set these on API, but do NOT set them on data.
62 : : *
63 : : * PERMANENT: forbid deleting the prop
64 : : * ENUMERATE: allows copyProperties to work among other reasons to have it
65 : : */
66 : : #define GJS_MODULE_PROP_FLAGS (JSPROP_PERMANENT | JSPROP_ENUMERATE)
67 : :
68 : : /*
69 : : * GJS_GET_THIS:
70 : : * @cx: JSContext pointer passed into JSNative function
71 : : * @argc: Number of arguments passed into JSNative function
72 : : * @vp: Argument value array passed into JSNative function
73 : : * @args: Name for JS::CallArgs variable defined by this code snippet
74 : : * @to: Name for JS::RootedObject variable referring to function's this
75 : : *
76 : : * A convenience macro for getting the 'this' object a function was called with.
77 : : * Use in any JSNative function.
78 : : */
79 : : #define GJS_GET_THIS(cx, argc, vp, args, to) \
80 : : JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
81 : : JS::RootedObject to(cx); \
82 : : if (!args.computeThis(cx, &to)) \
83 : : return false;
84 : :
85 : : void gjs_throw_constructor_error (JSContext *context);
86 : :
87 : : void gjs_throw_abstract_constructor_error(JSContext* cx,
88 : : const JS::CallArgs& args);
89 : :
90 : : GJS_JSAPI_RETURN_CONVENTION
91 : : JSObject* gjs_build_string_array(JSContext* cx,
92 : : const std::vector<std::string>& strings);
93 : :
94 : : GJS_JSAPI_RETURN_CONVENTION
95 : : JSObject* gjs_define_string_array(JSContext* cx, JS::HandleObject obj,
96 : : const char* array_name,
97 : : const std::vector<std::string>& strings,
98 : : unsigned attrs);
99 : :
100 : : [[gnu::format(printf, 2, 3)]] void gjs_throw(JSContext* cx, const char* format,
101 : : ...);
102 : : [[gnu::format(printf, 4, 5)]] void gjs_throw_custom(JSContext* cx,
103 : : JSExnType error_kind,
104 : : const char* error_name,
105 : : const char* format, ...);
106 : : void gjs_throw_literal (JSContext *context,
107 : : const char *string);
108 : : bool gjs_throw_gerror_message(JSContext* cx, Gjs::AutoError const&);
109 : :
110 : : bool gjs_log_exception (JSContext *context);
111 : :
112 : : bool gjs_log_exception_uncaught(JSContext* cx);
113 : :
114 : : void gjs_log_exception_full(JSContext* cx, JS::HandleValue exc,
115 : : JS::HandleString message, GLogLevelFlags level);
116 : :
117 : : void gjs_warning_reporter(JSContext*, JSErrorReport* report);
118 : :
119 : : GJS_JSAPI_RETURN_CONVENTION
120 : : JS::UniqueChars gjs_string_to_utf8(JSContext* cx, const JS::Value string_val);
121 : : GJS_JSAPI_RETURN_CONVENTION
122 : : bool gjs_string_to_utf8_n(JSContext* cx, JS::HandleString str, JS::UniqueChars* output,
123 : : size_t* output_len);
124 : : GJS_JSAPI_RETURN_CONVENTION
125 : : JSString* gjs_lossy_string_from_utf8(JSContext* cx, const char* utf8_string);
126 : : GJS_JSAPI_RETURN_CONVENTION
127 : : JSString* gjs_lossy_string_from_utf8_n(JSContext* cx, const char* utf8_string,
128 : : size_t len);
129 : : GJS_JSAPI_RETURN_CONVENTION
130 : : bool gjs_string_from_utf8(JSContext *context,
131 : : const char *utf8_string,
132 : : JS::MutableHandleValue value_p);
133 : : GJS_JSAPI_RETURN_CONVENTION
134 : : bool gjs_string_from_utf8_n(JSContext *cx,
135 : : const char *utf8_chars,
136 : : size_t len,
137 : : JS::MutableHandleValue out);
138 : :
139 : : GJS_JSAPI_RETURN_CONVENTION
140 : : bool gjs_string_to_filename(JSContext*, const JS::Value,
141 : : Gjs::AutoChar* filename_string);
142 : :
143 : : GJS_JSAPI_RETURN_CONVENTION
144 : : bool gjs_string_from_filename(JSContext *context,
145 : : const char *filename_string,
146 : : ssize_t n_bytes,
147 : : JS::MutableHandleValue value_p);
148 : :
149 : : GJS_JSAPI_RETURN_CONVENTION
150 : : bool gjs_string_get_char16_data(JSContext *cx,
151 : : JS::HandleString str,
152 : : char16_t **data_p,
153 : : size_t *len_p);
154 : :
155 : : GJS_JSAPI_RETURN_CONVENTION
156 : : bool gjs_string_to_ucs4(JSContext *cx,
157 : : JS::HandleString value,
158 : : gunichar **ucs4_string_p,
159 : : size_t *len_p);
160 : : GJS_JSAPI_RETURN_CONVENTION
161 : : bool gjs_string_from_ucs4(JSContext *cx,
162 : : const gunichar *ucs4_string,
163 : : ssize_t n_chars,
164 : : JS::MutableHandleValue value_p);
165 : :
166 : : GJS_JSAPI_RETURN_CONVENTION
167 : : bool gjs_get_string_id(JSContext* cx, jsid id, JS::UniqueChars* name_p);
168 : : GJS_JSAPI_RETURN_CONVENTION
169 : : jsid gjs_intern_string_to_id (JSContext *context,
170 : : const char *string);
171 : :
172 : : GJS_JSAPI_RETURN_CONVENTION
173 : : bool gjs_unichar_from_string (JSContext *context,
174 : : JS::Value string,
175 : : gunichar *result);
176 : :
177 : : /* Functions intended for more "internal" use */
178 : :
179 : : void gjs_maybe_gc (JSContext *context);
180 : : void gjs_gc_if_needed(JSContext *cx);
181 : :
182 : : GJS_JSAPI_RETURN_CONVENTION
183 : : JS::UniqueChars format_saved_frame(JSContext* cx, JS::HandleObject saved_frame,
184 : : size_t indent = 0);
185 : :
186 : : /* Overloaded functions, must be outside G_DECLS. More types are intended to be
187 : : * added as the opportunity arises. */
188 : :
189 : : GJS_JSAPI_RETURN_CONVENTION
190 : : bool gjs_object_require_property(JSContext *context,
191 : : JS::HandleObject obj,
192 : : const char *obj_description,
193 : : JS::HandleId property_name,
194 : : JS::MutableHandleValue value);
195 : :
196 : : GJS_JSAPI_RETURN_CONVENTION
197 : : bool gjs_object_require_property(JSContext *cx,
198 : : JS::HandleObject obj,
199 : : const char *description,
200 : : JS::HandleId property_name,
201 : : bool *value);
202 : :
203 : : GJS_JSAPI_RETURN_CONVENTION
204 : : bool gjs_object_require_property(JSContext *cx,
205 : : JS::HandleObject obj,
206 : : const char *description,
207 : : JS::HandleId property_name,
208 : : int32_t *value);
209 : :
210 : : GJS_JSAPI_RETURN_CONVENTION
211 : : bool gjs_object_require_property(JSContext* cx, JS::HandleObject obj,
212 : : const char* description,
213 : : JS::HandleId property_name,
214 : : JS::UniqueChars* value);
215 : :
216 : : GJS_JSAPI_RETURN_CONVENTION
217 : : bool gjs_object_require_property(JSContext *cx,
218 : : JS::HandleObject obj,
219 : : const char *description,
220 : : JS::HandleId property_name,
221 : : JS::MutableHandleObject value);
222 : :
223 : : GJS_JSAPI_RETURN_CONVENTION
224 : : bool gjs_object_require_converted_property(JSContext *context,
225 : : JS::HandleObject obj,
226 : : const char *description,
227 : : JS::HandleId property_name,
228 : : uint32_t *value);
229 : :
230 : : [[nodiscard]] std::string gjs_debug_bigint(JS::BigInt* bi);
231 : : [[nodiscard]] std::string gjs_debug_string(JSString* str);
232 : : [[nodiscard]] std::string gjs_debug_symbol(JS::Symbol* const sym);
233 : : [[nodiscard]] std::string gjs_debug_object(JSObject* obj);
234 : : [[nodiscard]] std::string gjs_debug_callable(JSObject* callable);
235 : : [[nodiscard]] std::string gjs_debug_value(JS::Value v);
236 : : [[nodiscard]] std::string gjs_debug_id(jsid id);
237 : :
238 : : [[nodiscard]] Gjs::AutoChar gjs_hyphen_to_underscore(const char* str);
239 : : [[nodiscard]] Gjs::AutoChar gjs_hyphen_to_camel(const char* str);
240 : :
241 : : #if defined(G_OS_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1900))
242 : : [[nodiscard]] std::wstring gjs_win32_vc140_utf8_to_utf16(const char* str);
243 : : #endif
244 : :
245 : : // Custom GC reasons; SpiderMonkey includes a bunch of "Firefox reasons" which
246 : : // don't apply when embedding the JS engine, so we repurpose them for our own
247 : : // reasons.
248 : :
249 : : // clang-format off
250 : : #define FOREACH_GC_REASON(macro) \
251 : : macro(LINUX_RSS_TRIGGER, 0) \
252 : : macro(GJS_CONTEXT_DISPOSE, 1) \
253 : : macro(BIG_HAMMER, 2) \
254 : : macro(GJS_API_CALL, 3) \
255 : : macro(LOW_MEMORY, 4)
256 : : // clang-format on
257 : :
258 : : namespace Gjs {
259 : :
260 : : struct GCReason {
261 : : #define DEFINE_GC_REASON(name, ix) \
262 : : static constexpr JS::GCReason name = JS::GCReason( \
263 : : static_cast<int>(JS::GCReason::FIRST_FIREFOX_REASON) + ix);
264 : : FOREACH_GC_REASON(DEFINE_GC_REASON);
265 : : #undef DEFINE_GC_REASON
266 : :
267 : : #define COUNT_GC_REASON(name, ix) +1
268 : : static constexpr size_t N_REASONS = 0 FOREACH_GC_REASON(COUNT_GC_REASON);
269 : : #undef COUNT_GC_REASON
270 : : };
271 : :
272 : : template <typename T>
273 : 1663 : [[nodiscard]] bool bigint_is_out_of_range(JS::BigInt* bi, T* clamped) {
274 : : static_assert(sizeof(T) == 8, "64-bit types only");
275 : 1663 : g_assert(bi && "bigint cannot be null");
276 : 1663 : g_assert(clamped && "forgot out parameter");
277 : :
278 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
279 : : "Checking if BigInt %s is out of range for type %s",
280 : : gjs_debug_bigint(bi).c_str(), Gjs::static_type_name<T>());
281 : :
282 [ + + ]: 1663 : if (JS::BigIntFits(bi, clamped)) {
283 : : gjs_debug_marshal(
284 : : GJS_DEBUG_GFUNCTION, "BigInt %s is in the range of type %s",
285 : : std::to_string(*clamped).c_str(), Gjs::static_type_name<T>());
286 : 1645 : return false;
287 : : }
288 : :
289 [ + + ]: 18 : if (JS::BigIntIsNegative(bi)) {
290 : 10 : *clamped = std::numeric_limits<T>::min();
291 : : } else {
292 : 8 : *clamped = std::numeric_limits<T>::max();
293 : : }
294 : :
295 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
296 : : "BigInt %s is not in the range of type %s, clamped to %s",
297 : : gjs_debug_bigint(bi).c_str(), Gjs::static_type_name<T>(),
298 : : std::to_string(*clamped).c_str());
299 : 18 : return true;
300 : : }
301 : :
302 : : } // namespace Gjs
303 : :
304 : : [[nodiscard]] const char* gjs_explain_gc_reason(JS::GCReason reason);
305 : :
306 : : #endif // GJS_JSAPI_UTIL_H_
|