/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Marco Trevisan #pragma once #include #include // for nullptr_t #include #include // for to_string #include #include #include // for GType #include // for gboolean #include // for HandleValue #include "gi/js-value-inl.h" #include "gi/utils-inl.h" #include "gjs/macros.h" // GIArgument accessor templates // // These are intended to make access to the GIArgument union more type-safe and // reduce bugs that occur from assigning to one member and reading from another. // (These bugs often work fine on one processor architecture but crash on // another.) // // gjs_arg_member(GIArgument*) - returns a reference to the appropriate union // member that would hold the type T. Rarely used, unless as a pointer to a // return location. // gjs_arg_get(GIArgument*) - returns the value of type T from the // appropriate union member. // gjs_arg_set(GIArgument*, T) - sets the appropriate union member for type T. // gjs_arg_unset(GIArgument*) - sets the appropriate zero value in the // appropriate union member for type T. // gjs_arg_steal(GIArgument*) - sets the appropriate zero value in the // appropriate union member for type T and returns the replaced value. template [[nodiscard]] constexpr inline decltype(auto) gjs_arg_member(GIArgument* arg) { return (arg->*member); } /* The tag is needed to disambiguate types such as gboolean and GType * which are in fact typedef's of other generic types. * Setting a tag for a type allows to perform proper specialization. */ template [[nodiscard]] constexpr inline decltype(auto) gjs_arg_member(GIArgument* arg) { if constexpr (TAG == GI_TYPE_TAG_VOID) { if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_boolean>(arg); if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_int8>(arg); if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_uint8>(arg); if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_int16>(arg); if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_uint16>(arg); if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_int32>(arg); if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_uint32>(arg); if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_int64>(arg); if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_uint64>(arg); // gunichar is stored in v_uint32 if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_uint32>(arg); if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_float>(arg); if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_double>(arg); if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_string>(arg); if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_pointer>(arg); if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_pointer>(arg); if constexpr (std::is_pointer()) { using NonconstPtrT = std::add_pointer_t< std::remove_const_t>>; return reinterpret_cast( gjs_arg_member<&GIArgument::v_pointer>(arg)); } } if constexpr (TAG == GI_TYPE_TAG_BOOLEAN && std::is_same_v) return gjs_arg_member<&GIArgument::v_boolean>(arg); if constexpr (TAG == GI_TYPE_TAG_GTYPE && std::is_same_v) { // GType is defined differently on 32-bit vs. 64-bit architectures. if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_size>(arg); else if constexpr (std::is_same_v) return gjs_arg_member<&GIArgument::v_ulong>(arg); } if constexpr (TAG == GI_TYPE_TAG_INTERFACE && std::is_integral_v) { if constexpr (std::is_signed_v) return gjs_arg_member<&GIArgument::v_int>(arg); else return gjs_arg_member<&GIArgument::v_uint>(arg); } } template constexpr inline void gjs_arg_set(GIArgument* arg, T v) { if constexpr (std::is_pointer_v) { using NonconstPtrT = std::add_pointer_t>>; gjs_arg_member(arg) = const_cast(v); } else { if constexpr (std::is_same_v || (std::is_same_v && TAG == GI_TYPE_TAG_BOOLEAN)) v = !!v; gjs_arg_member(arg) = v; } } // Store function pointers as void*. It is a requirement of GLib that your // compiler can do this template constexpr inline void gjs_arg_set(GIArgument* arg, ReturnT (*v)(Args...)) { gjs_arg_member(arg) = reinterpret_cast(v); } template constexpr inline std::enable_if_t> gjs_arg_set( GIArgument* arg, void* v) { gjs_arg_set(arg, gjs_pointer_to_int(v)); } template [[nodiscard]] constexpr inline T gjs_arg_get(GIArgument* arg) { if constexpr (std::is_same_v || (std::is_same_v && TAG == GI_TYPE_TAG_BOOLEAN)) return T(!!gjs_arg_member(arg)); return gjs_arg_member(arg); } template [[nodiscard]] constexpr inline void* gjs_arg_get_as_pointer(GIArgument* arg) { return gjs_int_to_pointer(gjs_arg_get(arg)); } template constexpr inline void gjs_arg_unset(GIArgument* arg) { if constexpr (std::is_pointer_v) gjs_arg_set(arg, nullptr); else gjs_arg_set(arg, static_cast(0)); } template [[nodiscard]] constexpr inline T gjs_arg_steal(GIArgument* arg) { auto val = gjs_arg_get(arg); gjs_arg_unset(arg); return val; } // Implementation to store rounded (u)int64_t numbers into double template [[nodiscard]] inline constexpr std::enable_if_t< std::is_integral_v && (std::numeric_limits::max() > std::numeric_limits::max()), double> gjs_arg_get_maybe_rounded(GIArgument* arg) { BigT val = gjs_arg_get(arg); if (val < Gjs::min_safe_big_number() || val > Gjs::max_safe_big_number()) { g_warning( "Value %s cannot be safely stored in a JS Number " "and may be rounded", std::to_string(val).c_str()); } return static_cast(val); } template GJS_JSAPI_RETURN_CONVENTION inline bool gjs_arg_set_from_js_value( JSContext* cx, const JS::HandleValue& value, GArgument* arg, bool* out_of_range) { if constexpr (Gjs::type_has_js_getter()) return Gjs::js_value_to_c(cx, value, &gjs_arg_member(arg)); Gjs::JsValueHolder::Relaxed val; if (!Gjs::js_value_to_c_checked(cx, value, &val, out_of_range)) return false; if (*out_of_range) return false; gjs_arg_set(arg, val); return true; } // A helper function to retrieve array lengths from a GIArgument (letting the // compiler generate good instructions in case of big endian machines) [[nodiscard]] constexpr size_t gjs_g_argument_get_array_length( GITypeTag tag, GIArgument* arg) { switch (tag) { case GI_TYPE_TAG_INT8: return gjs_arg_get(arg); case GI_TYPE_TAG_UINT8: return gjs_arg_get(arg); case GI_TYPE_TAG_INT16: return gjs_arg_get(arg); case GI_TYPE_TAG_UINT16: return gjs_arg_get(arg); case GI_TYPE_TAG_INT32: return gjs_arg_get(arg); case GI_TYPE_TAG_UINT32: return gjs_arg_get(arg); case GI_TYPE_TAG_INT64: return gjs_arg_get(arg); case GI_TYPE_TAG_UINT64: return gjs_arg_get(arg); default: g_assert_not_reached(); } }