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