Branch data Line data Source code
1 : : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 : : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
3 : : // SPDX-FileCopyrightText: 2008 litl, LLC
4 : :
5 : : #include <config.h>
6 : :
7 : : #include <girepository.h>
8 : :
9 : : #include <js/CallArgs.h>
10 : : #include <js/Class.h>
11 : : #include <js/RootingAPI.h>
12 : : #include <js/TypeDecls.h>
13 : : #include <js/Utility.h> // for UniqueChars
14 : : #include <js/Warnings.h>
15 : :
16 : : #include "gi/arg-inl.h"
17 : : #include "gi/function.h"
18 : : #include "gi/info.h"
19 : : #include "gi/repo.h"
20 : : #include "gi/union.h"
21 : : #include "gjs/jsapi-util.h"
22 : : #include "gjs/macros.h"
23 : : #include "gjs/mem-private.h"
24 : : #include "util/log.h"
25 : :
26 : 5 : UnionPrototype::UnionPrototype(GIUnionInfo* info, GType gtype)
27 : 5 : : GIWrapperPrototype(info, gtype) {
28 : 5 : GJS_INC_COUNTER(union_prototype);
29 : 5 : }
30 : :
31 : 5 : UnionPrototype::~UnionPrototype(void) { GJS_DEC_COUNTER(union_prototype); }
32 : :
33 : 10 : UnionInstance::UnionInstance(UnionPrototype* prototype, JS::HandleObject obj)
34 : 10 : : GIWrapperInstance(prototype, obj) {
35 : 10 : GJS_INC_COUNTER(union_instance);
36 : 10 : }
37 : :
38 : 10 : UnionInstance::~UnionInstance(void) {
39 [ + - ]: 10 : if (m_ptr) {
40 : 10 : g_boxed_free(gtype(), m_ptr);
41 : 10 : m_ptr = nullptr;
42 : : }
43 : 10 : GJS_DEC_COUNTER(union_instance);
44 : 10 : }
45 : :
46 : : // See GIWrapperBase::resolve().
47 : 9 : bool UnionPrototype::resolve_impl(JSContext* context, JS::HandleObject obj,
48 : : JS::HandleId id, bool* resolved) {
49 : 9 : JS::UniqueChars prop_name;
50 [ - + ]: 9 : if (!gjs_get_string_id(context, id, &prop_name))
51 : 0 : return false;
52 [ - + ]: 9 : if (!prop_name) {
53 : 0 : *resolved = false;
54 : 0 : return true; // not resolved, but no error
55 : : }
56 : :
57 : : // Look for methods and other class properties
58 : : GI::AutoFunctionInfo method_info{
59 : 9 : g_union_info_find_method(info(), prop_name.get())};
60 : :
61 [ + + ]: 9 : if (method_info) {
62 : : #if GJS_VERBOSE_ENABLE_GI_USAGE
63 : : _gjs_log_info_usage(method_info);
64 : : #endif
65 [ + - ]: 3 : if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) {
66 : 3 : gjs_debug(GJS_DEBUG_GBOXED,
67 : : "Defining method %s in prototype for %s",
68 : 6 : method_info.name(), format_name().c_str());
69 : :
70 : : /* obj is union proto */
71 [ - + ]: 3 : if (!gjs_define_function(context, obj, gtype(), method_info))
72 : 0 : return false;
73 : :
74 : 3 : *resolved = true; /* we defined the prop in object_proto */
75 : : } else {
76 : 0 : *resolved = false;
77 : : }
78 : : } else {
79 : 6 : *resolved = false;
80 : : }
81 : :
82 : 9 : return true;
83 : 9 : }
84 : :
85 : : GJS_JSAPI_RETURN_CONVENTION
86 : 1 : static void* union_new(JSContext* context, JS::HandleObject this_obj,
87 : : const JS::CallArgs& args, GIUnionInfo* info) {
88 : : int n_methods;
89 : : int i;
90 : :
91 : : /* Find a zero-args constructor and call it */
92 : :
93 : 1 : n_methods = g_union_info_get_n_methods(info);
94 : :
95 [ + - ]: 1 : for (i = 0; i < n_methods; ++i) {
96 : : GIFunctionInfoFlags flags;
97 : :
98 : 1 : GI::AutoFunctionInfo func_info{g_union_info_get_method(info, i)};
99 : :
100 : 1 : flags = g_function_info_get_flags(func_info);
101 [ + - + - : 2 : if ((flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0 &&
+ - ]
102 : 1 : g_callable_info_get_n_args((GICallableInfo*) func_info) == 0) {
103 : : GIArgument rval;
104 [ - + ]: 1 : if (!gjs_invoke_constructor_from_c(context, func_info, this_obj,
105 : : args, &rval))
106 : 0 : return nullptr;
107 : :
108 [ - + ]: 1 : if (!gjs_arg_get<void*>(&rval)) {
109 : 0 : gjs_throw(context,
110 : : "Unable to construct union type %s as its"
111 : : "constructor function returned null",
112 : : g_base_info_get_name(info));
113 : 0 : return nullptr;
114 : : }
115 : :
116 : 1 : return gjs_arg_get<void*>(&rval);
117 : : }
118 [ - + ]: 1 : }
119 : :
120 : 0 : gjs_throw(context, "Unable to construct union type %s since it has no zero-args <constructor>, can only wrap an existing one",
121 : : g_base_info_get_name((GIBaseInfo*) info));
122 : :
123 : 0 : return nullptr;
124 : : }
125 : :
126 : : // See GIWrapperBase::constructor().
127 : 1 : bool UnionInstance::constructor_impl(JSContext* context,
128 : : JS::HandleObject object,
129 : : const JS::CallArgs& args) {
130 [ - + - + ]: 1 : if (args.length() > 0 &&
131 [ # # ]: 0 : !JS::WarnUTF8(context, "Arguments to constructor of %s ignored",
132 : : name()))
133 : 0 : return false;
134 : :
135 : 1 : m_ptr = union_new(context, object, args, info());
136 : 1 : return !!m_ptr;
137 : : }
138 : :
139 : : // clang-format off
140 : : const struct JSClassOps UnionBase::class_ops = {
141 : : nullptr, // addProperty
142 : : nullptr, // deleteProperty
143 : : nullptr, // enumerate
144 : : nullptr, // newEnumerate
145 : : &UnionBase::resolve,
146 : : nullptr, // mayResolve
147 : : &UnionBase::finalize,
148 : : };
149 : :
150 : : const struct JSClass UnionBase::klass = {
151 : : "GObject_Union",
152 : : JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_FOREGROUND_FINALIZE,
153 : : &UnionBase::class_ops
154 : : };
155 : : // clang-format on
156 : :
157 : 5 : bool UnionPrototype::define_class(JSContext* context,
158 : : JS::HandleObject in_object,
159 : : GIUnionInfo* info) {
160 : : GType gtype;
161 : 5 : JS::RootedObject prototype(context), constructor(context);
162 : :
163 : : /* For certain unions, we may be able to relax this in the future by
164 : : * directly allocating union memory, as we do for structures in boxed.c
165 : : */
166 : 5 : gtype = g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) info);
167 [ - + ]: 5 : if (gtype == G_TYPE_NONE) {
168 : 0 : gjs_throw(context, "Unions must currently be registered as boxed types");
169 : 0 : return false;
170 : : }
171 : :
172 : 5 : return !!UnionPrototype::create_class(context, in_object, info, gtype,
173 : 5 : &constructor, &prototype);
174 : 5 : }
175 : :
176 : 9 : JSObject* UnionInstance::new_for_c_union(JSContext* context, GIUnionInfo* info,
177 : : void* gboxed) {
178 : : GType gtype;
179 : :
180 [ - + ]: 9 : if (!gboxed)
181 : 0 : return nullptr;
182 : :
183 : : /* For certain unions, we may be able to relax this in the future by
184 : : * directly allocating union memory, as we do for structures in boxed.c
185 : : */
186 : 9 : gtype = g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) info);
187 [ - + ]: 9 : if (gtype == G_TYPE_NONE) {
188 : 0 : gjs_throw(context, "Unions must currently be registered as boxed types");
189 : 0 : return nullptr;
190 : : }
191 : :
192 : : gjs_debug_marshal(GJS_DEBUG_GBOXED,
193 : : "Wrapping union %s %p with JSObject",
194 : : g_base_info_get_name((GIBaseInfo *)info), gboxed);
195 : :
196 : : JS::RootedObject obj(context,
197 : 9 : gjs_new_object_with_generic_prototype(context, info));
198 [ - + ]: 9 : if (!obj)
199 : 0 : return nullptr;
200 : :
201 : 9 : UnionInstance* priv = UnionInstance::new_for_js_object(context, obj);
202 : 9 : priv->copy_union(gboxed);
203 : :
204 : 9 : return obj;
205 : 9 : }
206 : :
207 : 0 : void* UnionInstance::copy_ptr(JSContext* cx, GType gtype, void* ptr) {
208 [ # # # # : 0 : if (g_type_is_a(gtype, G_TYPE_BOXED))
# # ]
209 : 0 : return g_boxed_copy(gtype, ptr);
210 : :
211 : 0 : gjs_throw(cx,
212 : : "Can't transfer ownership of a union type not registered as "
213 : : "boxed");
214 : 0 : return nullptr;
215 : : }
|