/* -*- 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 #include #include #include #include #include #include #include // for UniqueChars #include #include "gi/arg-inl.h" #include "gi/function.h" #include "gi/repo.h" #include "gi/union.h" #include "gjs/jsapi-util.h" #include "gjs/macros.h" #include "gjs/mem-private.h" #include "util/log.h" UnionPrototype::UnionPrototype(GIUnionInfo* info, GType gtype) : GIWrapperPrototype(info, gtype) { GJS_INC_COUNTER(union_prototype); } UnionPrototype::~UnionPrototype(void) { GJS_DEC_COUNTER(union_prototype); } UnionInstance::UnionInstance(UnionPrototype* prototype, JS::HandleObject obj) : GIWrapperInstance(prototype, obj) { GJS_INC_COUNTER(union_instance); } UnionInstance::~UnionInstance(void) { if (m_ptr) { g_boxed_free(gtype(), m_ptr); m_ptr = nullptr; } GJS_DEC_COUNTER(union_instance); } // See GIWrapperBase::resolve(). bool UnionPrototype::resolve_impl(JSContext* context, JS::HandleObject obj, JS::HandleId id, bool* resolved) { JS::UniqueChars prop_name; if (!gjs_get_string_id(context, id, &prop_name)) return false; if (!prop_name) { *resolved = false; return true; // not resolved, but no error } // Look for methods and other class properties GjsAutoFunctionInfo method_info = g_union_info_find_method(info(), prop_name.get()); if (method_info) { #if GJS_VERBOSE_ENABLE_GI_USAGE _gjs_log_info_usage(method_info); #endif if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) { gjs_debug(GJS_DEBUG_GBOXED, "Defining method %s in prototype for %s.%s", method_info.name(), ns(), name()); /* obj is union proto */ if (!gjs_define_function(context, obj, gtype(), method_info)) return false; *resolved = true; /* we defined the prop in object_proto */ } else { *resolved = false; } } else { *resolved = false; } return true; } GJS_JSAPI_RETURN_CONVENTION static void* union_new(JSContext* context, JS::HandleObject this_obj, const JS::CallArgs& args, GIUnionInfo* info) { int n_methods; int i; /* Find a zero-args constructor and call it */ n_methods = g_union_info_get_n_methods(info); for (i = 0; i < n_methods; ++i) { GIFunctionInfoFlags flags; GjsAutoFunctionInfo func_info = g_union_info_get_method(info, i); flags = g_function_info_get_flags(func_info); if ((flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0 && g_callable_info_get_n_args((GICallableInfo*) func_info) == 0) { GIArgument rval; if (!gjs_invoke_constructor_from_c(context, func_info, this_obj, args, &rval)) return nullptr; if (!gjs_arg_get(&rval)) { gjs_throw(context, "Unable to construct union type %s as its" "constructor function returned null", g_base_info_get_name(info)); return nullptr; } return gjs_arg_get(&rval); } } gjs_throw(context, "Unable to construct union type %s since it has no zero-args , can only wrap an existing one", g_base_info_get_name((GIBaseInfo*) info)); return nullptr; } // See GIWrapperBase::constructor(). bool UnionInstance::constructor_impl(JSContext* context, JS::HandleObject object, const JS::CallArgs& args) { if (args.length() > 0 && !JS::WarnUTF8(context, "Arguments to constructor of %s ignored", name())) return false; m_ptr = union_new(context, object, args, info()); return !!m_ptr; } // clang-format off const struct JSClassOps UnionBase::class_ops = { nullptr, // addProperty nullptr, // deleteProperty nullptr, // enumerate nullptr, // newEnumerate &UnionBase::resolve, nullptr, // mayResolve &UnionBase::finalize, }; const struct JSClass UnionBase::klass = { "GObject_Union", JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_FOREGROUND_FINALIZE, &UnionBase::class_ops }; // clang-format on bool UnionPrototype::define_class(JSContext* context, JS::HandleObject in_object, GIUnionInfo* info) { GType gtype; JS::RootedObject prototype(context), constructor(context); /* For certain unions, we may be able to relax this in the future by * directly allocating union memory, as we do for structures in boxed.c */ gtype = g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) info); if (gtype == G_TYPE_NONE) { gjs_throw(context, "Unions must currently be registered as boxed types"); return false; } return !!UnionPrototype::create_class(context, in_object, info, gtype, &constructor, &prototype); } JSObject* UnionInstance::new_for_c_union(JSContext* context, GIUnionInfo* info, void* gboxed) { GType gtype; if (!gboxed) return nullptr; /* For certain unions, we may be able to relax this in the future by * directly allocating union memory, as we do for structures in boxed.c */ gtype = g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) info); if (gtype == G_TYPE_NONE) { gjs_throw(context, "Unions must currently be registered as boxed types"); return nullptr; } gjs_debug_marshal(GJS_DEBUG_GBOXED, "Wrapping union %s %p with JSObject", g_base_info_get_name((GIBaseInfo *)info), gboxed); JS::RootedObject obj(context, gjs_new_object_with_generic_prototype(context, info)); if (!obj) return nullptr; UnionInstance* priv = UnionInstance::new_for_js_object(context, obj); priv->copy_union(gboxed); return obj; } void* UnionInstance::copy_ptr(JSContext* cx, GType gtype, void* ptr) { if (g_type_is_a(gtype, G_TYPE_BOXED)) return g_boxed_copy(gtype, ptr); gjs_throw(cx, "Can't transfer ownership of a union type not registered as " "boxed"); return nullptr; }