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