/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2013 Giovanni Campagna // SPDX-FileCopyrightText: 2020 Marco Trevisan #ifndef GI_ARG_CACHE_H_ #define GI_ARG_CACHE_H_ #include #include #include #include #include #include #include #include "gi/arg.h" #include "gjs/enum-utils.h" #include "gjs/macros.h" class GjsFunctionCallState; enum NotIntrospectableReason : uint8_t { CALLBACK_OUT, DESTROY_NOTIFY_NO_CALLBACK, DESTROY_NOTIFY_NO_USER_DATA, INTERFACE_TRANSFER_CONTAINER, OUT_CALLER_ALLOCATES_NON_STRUCT, UNREGISTERED_BOXED_WITH_TRANSFER, UNREGISTERED_UNION, UNSUPPORTED_TYPE, LAST_REASON }; namespace Gjs { namespace Arg { using ReturnValue = struct GenericOut; struct Instance; enum class Kind { NORMAL, INSTANCE, RETURN_VALUE, }; } // namespace Arg struct Argument { using UniquePtr = std::unique_ptr; virtual ~Argument() = default; GJS_JSAPI_RETURN_CONVENTION virtual bool in(JSContext* cx, GjsFunctionCallState*, GIArgument* in_argument, JS::HandleValue value); GJS_JSAPI_RETURN_CONVENTION virtual bool out(JSContext* cx, GjsFunctionCallState*, GIArgument* out_argument, JS::MutableHandleValue value); GJS_JSAPI_RETURN_CONVENTION virtual bool release(JSContext* cx, GjsFunctionCallState*, GIArgument* in_argument, GIArgument* out_argument); virtual GjsArgumentFlags flags() const { GjsArgumentFlags flags = GjsArgumentFlags::NONE; if (m_skip_in) flags |= GjsArgumentFlags::SKIP_IN; else flags |= GjsArgumentFlags::ARG_IN; if (m_skip_out) flags |= GjsArgumentFlags::SKIP_OUT; else flags |= GjsArgumentFlags::ARG_OUT; return flags; } // Introspected functions can have up to 253 arguments. The callback // closure or destroy notify parameter may have a value of 255 to indicate // that it is absent. static constexpr uint8_t MAX_ARGS = std::numeric_limits::max() - 2; static constexpr uint8_t ABSENT = std::numeric_limits::max(); constexpr const char* arg_name() const { return m_arg_name; } constexpr bool skip_in() const { return m_skip_in; } constexpr bool skip_out() const { return m_skip_out; } protected: constexpr Argument() : m_skip_in(false), m_skip_out(false) {} virtual const Arg::ReturnValue* as_return_value() const { return nullptr; } virtual const Arg::Instance* as_instance() const { return nullptr; } constexpr void set_instance_parameter() { m_arg_name = "instance parameter"; m_skip_out = true; } constexpr void set_return_value() { m_arg_name = "return value"; } bool invalid(JSContext*, const char* func = nullptr) const; const char* m_arg_name = nullptr; bool m_skip_in : 1; bool m_skip_out : 1; private: friend struct ArgsCache; template static std::unique_ptr make(uint8_t index, const char* name, GITypeInfo* type_info, GITransfer transfer, GjsArgumentFlags flags, Args&&... args); }; // This is a trick to print out the sizes of the structs at compile time, in // an error message: // template struct Measure; // Measure arg_cache_size; #if defined(__x86_64__) && defined(__clang__) && !defined(_MSC_VER) # define GJS_DO_ARGUMENTS_SIZE_CHECK 1 // This isn't meant to be comprehensive, but should trip on at least one CI job // if sizeof(Gjs::Argument) is increased. // Note that this check is not applicable for clang-cl builds, as Windows is // an LLP64 system static_assert(sizeof(Argument) <= 24, "Think very hard before increasing the size of Gjs::Argument. " "One is allocated for every argument to every introspected " "function."); #endif // x86-64 clang struct ArgsCache { GJS_JSAPI_RETURN_CONVENTION bool initialize(JSContext* cx, GICallableInfo* callable); ArgsCache(); ~ArgsCache(); void clear(); bool initialized() { return m_args != nullptr; } void build_arg(uint8_t gi_index, GIDirection, GIArgInfo*, GICallableInfo*, bool* inc_counter_out); void build_return(GICallableInfo* callable, bool* inc_counter_out); void build_instance(GICallableInfo* callable); Argument* argument(uint8_t index) const { return arg_get(index).get(); } Argument* instance() const; GType instance_type() const; GITypeInfo* return_type() const; Argument* return_value() const; private: void build_normal_in_arg(uint8_t gi_index, GITypeInfo*, GIArgInfo*, GjsArgumentFlags); template void build_interface_in_arg(uint8_t gi_index, GITypeInfo*, GIBaseInfo*, GITransfer, const char* name, GjsArgumentFlags); template T* set_argument(uint8_t index, const char* name, GITypeInfo*, GITransfer, GjsArgumentFlags flags, Args&&... args); template T* set_argument(uint8_t index, const char* name, GITransfer, GjsArgumentFlags flags, Args&&... args); template T* set_argument_auto(Args&&... args); template T* set_argument_auto(Tuple&& tuple, Args&&... args); template void set_array_argument(GICallableInfo* callable, uint8_t gi_index, GITypeInfo*, GIDirection, GIArgInfo*, GjsArgumentFlags flags, int length_pos); template T* set_return(GITypeInfo*, GITransfer, GjsArgumentFlags); template T* set_instance(GITransfer, GjsArgumentFlags flags = GjsArgumentFlags::NONE); void set_skip_all(uint8_t index, const char* name = nullptr); template constexpr uint8_t arg_index(uint8_t index [[maybe_unused]] = Argument::MAX_ARGS) const { if constexpr (ArgKind == Arg::Kind::RETURN_VALUE) return 0; else if constexpr (ArgKind == Arg::Kind::INSTANCE) return (m_has_return ? 1 : 0); else if constexpr (ArgKind == Arg::Kind::NORMAL) return (m_has_return ? 1 : 0) + (m_is_method ? 1 : 0) + index; } template inline Argument::UniquePtr& arg_get( uint8_t index = Argument::MAX_ARGS) const { return m_args[arg_index(index)]; } private: std::unique_ptr m_args; bool m_is_method : 1; bool m_has_return : 1; }; } // namespace Gjs #endif // GI_ARG_CACHE_H_