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