Skip to content
Permalink
master
Go to file
 
 
Cannot retrieve contributors at this time
548 lines (446 sloc) 19.1 KB
/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2008 litl, LLC
// SPDX-FileCopyrightText: 2018-2020 Canonical, Ltd
#ifndef GJS_JSAPI_UTIL_H_
#define GJS_JSAPI_UTIL_H_
#include <config.h>
#include <stdint.h>
#include <stdlib.h> // for free
#include <sys/types.h> // for ssize_t
#include <string> // for string, u16string
#include <type_traits> // for enable_if_t, add_pointer_t, add_const_t
#include <utility> // IWYU pragma: keep
#include <vector>
#include <girepository.h>
#include <glib-object.h>
#include <glib.h>
#include <js/GCPolicyAPI.h> // for IgnoreGCPolicy
#include <js/Id.h>
#include <js/TypeDecls.h>
#include <js/Utility.h> // for UniqueChars
#include <jspubtd.h> // for JSProtoKey
#include "gjs/macros.h"
class JSErrorReport;
namespace JS {
class CallArgs;
}
struct GjsAutoTakeOwnership {};
template <typename F = void>
using GjsAutoPointerRefFunction = F* (*)(F*);
template <typename F = void>
using GjsAutoPointerFreeFunction = void (*)(F*);
template <typename T, typename F = void,
GjsAutoPointerFreeFunction<F> free_func = free,
GjsAutoPointerRefFunction<F> ref_func = nullptr>
struct GjsAutoPointer {
using Tp =
std::conditional_t<std::is_array_v<T>, std::remove_extent_t<T>, T>;
using Ptr = std::add_pointer_t<Tp>;
using ConstPtr = std::add_pointer_t<std::add_const_t<Tp>>;
private:
template <typename FunctionType, FunctionType function>
static constexpr bool has_function() {
using NullType = std::integral_constant<FunctionType, nullptr>;
using ActualType = std::integral_constant<FunctionType, function>;
return !std::is_same_v<ActualType, NullType>;
}
public:
static constexpr bool has_free_function() {
return has_function<GjsAutoPointerFreeFunction<F>, free_func>();
}
static constexpr bool has_ref_function() {
return has_function<GjsAutoPointerRefFunction<F>, ref_func>();
}
constexpr GjsAutoPointer(Ptr ptr = nullptr) // NOLINT(runtime/explicit)
: m_ptr(ptr) {}
template <typename U, typename = std::enable_if_t<std::is_same_v<U, Tp> &&
std::is_array_v<T>>>
explicit constexpr GjsAutoPointer(U ptr[]) : m_ptr(ptr) {}
constexpr GjsAutoPointer(Ptr ptr, const GjsAutoTakeOwnership&)
: GjsAutoPointer(ptr) {
m_ptr = copy();
}
constexpr GjsAutoPointer(ConstPtr ptr, const GjsAutoTakeOwnership& o)
: GjsAutoPointer(const_cast<Ptr>(ptr), o) {}
constexpr GjsAutoPointer(GjsAutoPointer&& other) : GjsAutoPointer() {
this->swap(other);
}
constexpr GjsAutoPointer(GjsAutoPointer const& other) : GjsAutoPointer() {
*this = other;
}
constexpr GjsAutoPointer& operator=(Ptr ptr) {
reset(ptr);
return *this;
}
GjsAutoPointer& operator=(GjsAutoPointer&& other) {
this->swap(other);
return *this;
}
GjsAutoPointer& operator=(GjsAutoPointer const& other) {
GjsAutoPointer dup(other.get(), GjsAutoTakeOwnership());
this->swap(dup);
return *this;
}
template <typename U = T>
constexpr std::enable_if_t<!std::is_array_v<U>, Ptr> operator->() {
return m_ptr;
}
template <typename U = T>
constexpr std::enable_if_t<!std::is_array_v<U>, ConstPtr> operator->()
const {
return m_ptr;
}
constexpr Tp operator*() const { return *m_ptr; }
constexpr operator Ptr() { return m_ptr; }
constexpr operator Ptr() const { return m_ptr; }
constexpr operator ConstPtr() const { return m_ptr; }
constexpr operator bool() const { return m_ptr != nullptr; }
constexpr Ptr get() const { return m_ptr; }
constexpr Ptr* out() { return &m_ptr; }
constexpr Ptr release() {
auto* ptr = m_ptr;
m_ptr = nullptr;
return ptr;
}
constexpr void reset(Ptr ptr = nullptr) {
Ptr old_ptr = m_ptr;
m_ptr = ptr;
if constexpr (has_free_function()) {
if (old_ptr) {
if constexpr (std::is_array_v<T>)
free_func(reinterpret_cast<T*>(old_ptr));
else
free_func(old_ptr);
}
}
}
constexpr void swap(GjsAutoPointer& other) {
std::swap(this->m_ptr, other.m_ptr);
}
/* constexpr */ ~GjsAutoPointer() { // one day, with -std=c++2a
reset();
}
template <typename U = T>
[[nodiscard]] constexpr std::enable_if_t<!std::is_array_v<U>, Ptr> copy()
const {
static_assert(has_ref_function(), "No ref function provided");
return m_ptr ? reinterpret_cast<Ptr>(ref_func(m_ptr)) : nullptr;
}
template <typename C>
[[nodiscard]] constexpr C* as() const {
return const_cast<C*>(reinterpret_cast<const C*>(m_ptr));
}
private:
Ptr m_ptr;
};
template <typename T, typename F = void,
GjsAutoPointerFreeFunction<F> free_func,
GjsAutoPointerRefFunction<F> ref_func>
constexpr bool operator==(
GjsAutoPointer<T, F, free_func, ref_func> const& lhs,
GjsAutoPointer<T, F, free_func, ref_func> const& rhs) {
return lhs.get() == rhs.get();
}
template <typename T>
using GjsAutoFree = GjsAutoPointer<T>;
struct GjsAutoCharFuncs {
static char* dup(char* str) { return g_strdup(str); }
static void free(char* str) { g_free(str); }
};
using GjsAutoChar =
GjsAutoPointer<char, char, GjsAutoCharFuncs::free, GjsAutoCharFuncs::dup>;
struct GjsAutoErrorFuncs {
static GError* error_copy(GError* error) { return g_error_copy(error); }
};
using GjsAutoError =
GjsAutoPointer<GError, GError, g_error_free, GjsAutoErrorFuncs::error_copy>;
using GjsAutoStrv = GjsAutoPointer<char*, char*, g_strfreev, g_strdupv>;
template <typename T>
using GjsAutoUnref = GjsAutoPointer<T, void, g_object_unref, g_object_ref>;
using GjsAutoGVariant =
GjsAutoPointer<GVariant, GVariant, g_variant_unref, g_variant_ref>;
template <typename V, typename T>
constexpr void GjsAutoPointerDeleter(T v) {
if constexpr (std::is_array_v<V>)
delete[] reinterpret_cast<std::remove_extent_t<V>*>(v);
else
delete v;
}
template <typename T>
using GjsAutoCppPointer = GjsAutoPointer<T, T, GjsAutoPointerDeleter<T>>;
template <typename T = GTypeClass>
struct GjsAutoTypeClass : GjsAutoPointer<T, void, &g_type_class_unref> {
GjsAutoTypeClass(gpointer ptr = nullptr) // NOLINT(runtime/explicit)
: GjsAutoPointer<T, void, g_type_class_unref>(static_cast<T*>(ptr)) {}
explicit GjsAutoTypeClass(GType gtype)
: GjsAutoTypeClass(g_type_class_ref(gtype)) {}
};
// Use this class for owning a GIBaseInfo* of indeterminate type. Any type (e.g.
// GIFunctionInfo*, GIObjectInfo*) will fit. If you know that the info is of a
// certain type (e.g. you are storing the return value of a function that
// returns GIFunctionInfo*,) use one of the derived classes below.
struct GjsAutoBaseInfo : GjsAutoPointer<GIBaseInfo, GIBaseInfo,
g_base_info_unref, g_base_info_ref> {
using GjsAutoPointer::GjsAutoPointer;
[[nodiscard]] const char* name() const {
return g_base_info_get_name(*this);
}
[[nodiscard]] const char* ns() const {
return g_base_info_get_namespace(*this);
}
[[nodiscard]] GIInfoType type() const {
return g_base_info_get_type(*this);
}
};
// Use GjsAutoInfo, preferably its typedefs below, when you know for sure that
// the info is either of a certain type or null.
template <GIInfoType TAG>
struct GjsAutoInfo : GjsAutoBaseInfo {
using GjsAutoBaseInfo::GjsAutoBaseInfo;
// Normally one-argument constructors should be explicit, but we are trying
// to conform to the interface of std::unique_ptr here.
GjsAutoInfo(GIBaseInfo* ptr = nullptr) // NOLINT(runtime/explicit)
: GjsAutoBaseInfo(ptr) {
validate();
}
void reset(GIBaseInfo* other = nullptr) {
GjsAutoBaseInfo::reset(other);
validate();
}
// You should not need this method, because you already know the answer.
GIInfoType type() = delete;
private:
void validate() const {
if (GIBaseInfo* base = *this)
g_assert(g_base_info_get_type(base) == TAG);
}
};
using GjsAutoEnumInfo = GjsAutoInfo<GI_INFO_TYPE_ENUM>;
using GjsAutoFieldInfo = GjsAutoInfo<GI_INFO_TYPE_FIELD>;
using GjsAutoFunctionInfo = GjsAutoInfo<GI_INFO_TYPE_FUNCTION>;
using GjsAutoInterfaceInfo = GjsAutoInfo<GI_INFO_TYPE_INTERFACE>;
using GjsAutoObjectInfo = GjsAutoInfo<GI_INFO_TYPE_OBJECT>;
using GjsAutoPropertyInfo = GjsAutoInfo<GI_INFO_TYPE_PROPERTY>;
using GjsAutoStructInfo = GjsAutoInfo<GI_INFO_TYPE_STRUCT>;
using GjsAutoTypeInfo = GjsAutoInfo<GI_INFO_TYPE_TYPE>;
using GjsAutoValueInfo = GjsAutoInfo<GI_INFO_TYPE_VALUE>;
using GjsAutoVFuncInfo = GjsAutoInfo<GI_INFO_TYPE_VFUNC>;
// GICallableInfo can be one of several tags, so we have to have a separate
// class, and use GI_IS_CALLABLE_INFO() to validate.
struct GjsAutoCallableInfo : GjsAutoBaseInfo {
using GjsAutoBaseInfo::GjsAutoBaseInfo;
GjsAutoCallableInfo(GIBaseInfo* ptr = nullptr) // NOLINT(runtime/explicit)
: GjsAutoBaseInfo(ptr) {
validate();
}
void reset(GIBaseInfo* other = nullptr) {
GjsAutoBaseInfo::reset(other);
validate();
}
private:
void validate() const {
if (*this)
g_assert(GI_IS_CALLABLE_INFO(get()));
}
};
template <typename T>
struct GjsSmartPointer : GjsAutoPointer<T> {
using GjsAutoPointer<T>::GjsAutoPointer;
};
template <>
struct GjsSmartPointer<char*> : GjsAutoStrv {
using GjsAutoStrv::GjsAutoPointer;
};
template <>
struct GjsSmartPointer<GStrv> : GjsAutoStrv {
using GjsAutoStrv::GjsAutoPointer;
};
template <>
struct GjsSmartPointer<GObject> : GjsAutoUnref<GObject> {
using GjsAutoUnref<GObject>::GjsAutoUnref;
};
template <>
struct GjsSmartPointer<GIBaseInfo> : GjsAutoBaseInfo {
using GjsAutoBaseInfo::GjsAutoBaseInfo;
};
template <>
struct GjsSmartPointer<GError> : GjsAutoError {
using GjsAutoError::GjsAutoError;
};
template <>
struct GjsSmartPointer<GVariant> : GjsAutoGVariant {
using GjsAutoGVariant::GjsAutoPointer;
};
template <>
struct GjsSmartPointer<GList> : GjsAutoPointer<GList, GList, g_list_free> {
using GjsAutoPointer::GjsAutoPointer;
};
template <>
struct GjsSmartPointer<GSList> : GjsAutoPointer<GSList, GSList, g_slist_free> {
using GjsAutoPointer::GjsAutoPointer;
};
/* For use of GjsAutoInfo<TAG> in GC hash maps */
namespace JS {
template <GIInfoType TAG>
struct GCPolicy<GjsAutoInfo<TAG>> : public IgnoreGCPolicy<GjsAutoInfo<TAG>> {};
} // namespace JS
using GjsAutoParam = GjsAutoPointer<GParamSpec, GParamSpec, g_param_spec_unref,
g_param_spec_ref>;
/* For use of GjsAutoParam in GC hash maps */
namespace JS {
template <>
struct GCPolicy<GjsAutoParam> : public IgnoreGCPolicy<GjsAutoParam> {};
} // namespace JS
/* Flags that should be set on properties exported from native code modules.
* Basically set these on API, but do NOT set them on data.
*
* PERMANENT: forbid deleting the prop
* ENUMERATE: allows copyProperties to work among other reasons to have it
*/
#define GJS_MODULE_PROP_FLAGS (JSPROP_PERMANENT | JSPROP_ENUMERATE)
/*
* GJS_GET_THIS:
* @cx: JSContext pointer passed into JSNative function
* @argc: Number of arguments passed into JSNative function
* @vp: Argument value array passed into JSNative function
* @args: Name for JS::CallArgs variable defined by this code snippet
* @to: Name for JS::RootedObject variable referring to function's this
*
* A convenience macro for getting the 'this' object a function was called with.
* Use in any JSNative function.
*/
#define GJS_GET_THIS(cx, argc, vp, args, to) \
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
JS::RootedObject to(cx); \
if (!args.computeThis(cx, &to)) \
return false;
[[nodiscard]] JSObject* gjs_get_import_global(JSContext* cx);
void gjs_throw_constructor_error (JSContext *context);
void gjs_throw_abstract_constructor_error(JSContext* cx,
const JS::CallArgs& args);
GJS_JSAPI_RETURN_CONVENTION
JSObject* gjs_build_string_array(JSContext* cx,
const std::vector<std::string>& strings);
GJS_JSAPI_RETURN_CONVENTION
JSObject* gjs_define_string_array(JSContext* cx, JS::HandleObject obj,
const char* array_name,
const std::vector<std::string>& strings,
unsigned attrs);
void gjs_throw (JSContext *context,
const char *format,
...) G_GNUC_PRINTF (2, 3);
void gjs_throw_custom (JSContext *context,
JSProtoKey error_kind,
const char *error_name,
const char *format,
...) G_GNUC_PRINTF (4, 5);
void gjs_throw_literal (JSContext *context,
const char *string);
bool gjs_throw_gerror_message(JSContext* cx, GError* error);
bool gjs_log_exception (JSContext *context);
bool gjs_log_exception_uncaught(JSContext* cx);
bool gjs_log_exception_full(JSContext* cx, JS::HandleValue exc,
JS::HandleString message, GLogLevelFlags level);
[[nodiscard]] std::string gjs_value_debug_string(JSContext* cx,
JS::HandleValue value);
void gjs_warning_reporter(JSContext*, JSErrorReport* report);
GJS_JSAPI_RETURN_CONVENTION
JS::UniqueChars gjs_string_to_utf8(JSContext* cx, const JS::Value string_val);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_string_from_utf8(JSContext *context,
const char *utf8_string,
JS::MutableHandleValue value_p);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_string_from_utf8_n(JSContext *cx,
const char *utf8_chars,
size_t len,
JS::MutableHandleValue out);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_string_to_filename(JSContext *cx,
const JS::Value string_val,
GjsAutoChar *filename_string);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_string_from_filename(JSContext *context,
const char *filename_string,
ssize_t n_bytes,
JS::MutableHandleValue value_p);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_string_get_char16_data(JSContext *cx,
JS::HandleString str,
char16_t **data_p,
size_t *len_p);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_string_to_ucs4(JSContext *cx,
JS::HandleString value,
gunichar **ucs4_string_p,
size_t *len_p);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_string_from_ucs4(JSContext *cx,
const gunichar *ucs4_string,
ssize_t n_chars,
JS::MutableHandleValue value_p);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_get_string_id(JSContext* cx, jsid id, JS::UniqueChars* name_p);
GJS_JSAPI_RETURN_CONVENTION
jsid gjs_intern_string_to_id (JSContext *context,
const char *string);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_unichar_from_string (JSContext *context,
JS::Value string,
gunichar *result);
/* Functions intended for more "internal" use */
void gjs_maybe_gc (JSContext *context);
void gjs_gc_if_needed(JSContext *cx);
[[nodiscard]] std::u16string gjs_utf8_script_to_utf16(const char* script,
ssize_t len);
GJS_JSAPI_RETURN_CONVENTION
GjsAutoChar gjs_format_stack_trace(JSContext *cx,
JS::HandleObject saved_frame);
/* Overloaded functions, must be outside G_DECLS. More types are intended to be
* added as the opportunity arises. */
GJS_JSAPI_RETURN_CONVENTION
bool gjs_object_require_property(JSContext *context,
JS::HandleObject obj,
const char *obj_description,
JS::HandleId property_name,
JS::MutableHandleValue value);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_object_require_property(JSContext *cx,
JS::HandleObject obj,
const char *description,
JS::HandleId property_name,
bool *value);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_object_require_property(JSContext *cx,
JS::HandleObject obj,
const char *description,
JS::HandleId property_name,
int32_t *value);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_object_require_property(JSContext* cx, JS::HandleObject obj,
const char* description,
JS::HandleId property_name,
JS::UniqueChars* value);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_object_require_property(JSContext *cx,
JS::HandleObject obj,
const char *description,
JS::HandleId property_name,
JS::MutableHandleObject value);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_object_require_converted_property(JSContext *context,
JS::HandleObject obj,
const char *description,
JS::HandleId property_name,
uint32_t *value);
[[nodiscard]] std::string gjs_debug_string(JSString* str);
[[nodiscard]] std::string gjs_debug_symbol(JS::Symbol* const sym);
[[nodiscard]] std::string gjs_debug_object(JSObject* obj);
[[nodiscard]] std::string gjs_debug_value(JS::Value v);
[[nodiscard]] std::string gjs_debug_id(jsid id);
[[nodiscard]] char* gjs_hyphen_to_underscore(const char* str);
#if defined(G_OS_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1900))
[[nodiscard]] std::wstring gjs_win32_vc140_utf8_to_utf16(const char* str);
#endif
#endif // GJS_JSAPI_UTIL_H_
You can’t perform that action at this time.