1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 * GObject introspection: Invoke functionality
3 *
4 * Copyright (C) 2005 Matthias Clasen
5 *
6 * SPDX-License-Identifier: LGPL-2.1-or-later
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23
24 #include "config.h"
25
26 #include <stdlib.h>
27
28 #include <glib.h>
29 #include <glib-object.h>
30
31 #include <girepository/girepository.h>
32 #include "girffi.h"
33
34 /**
35 * value_to_ffi_type:
36 * @gvalue: (transfer none): a [type@GObject.Value] to convert
37 * @value: (out caller-allocates): return location for the ffi data
38 *
39 * Convert @gvalue to a format suitable for passing to ffi.
40 *
41 * @value is only valid as long as @gvalue is alive.
42 *
43 * Returns: pointer to the `ffi_type` associated with @value
44 * Since: 2.80
45 */
46 static ffi_type *
47 value_to_ffi_type (const GValue *gvalue, gpointer *value)
48 {
49 ffi_type *rettype = NULL;
50 GType type = g_type_fundamental (G_VALUE_TYPE (gvalue));
51 g_assert (type != G_TYPE_INVALID);
52
53 switch (type)
54 {
55 case G_TYPE_BOOLEAN:
56 case G_TYPE_CHAR:
57 case G_TYPE_INT:
58 rettype = &ffi_type_sint;
59 *value = (gpointer)&(gvalue->data[0].v_int);
60 break;
61 case G_TYPE_UCHAR:
62 case G_TYPE_UINT:
63 rettype = &ffi_type_uint;
64 *value = (gpointer)&(gvalue->data[0].v_uint);
65 break;
66 case G_TYPE_STRING:
67 case G_TYPE_OBJECT:
68 case G_TYPE_BOXED:
69 case G_TYPE_POINTER:
70 case G_TYPE_PARAM:
71 rettype = &ffi_type_pointer;
72 *value = (gpointer)&(gvalue->data[0].v_pointer);
73 break;
74 case G_TYPE_FLOAT:
75 rettype = &ffi_type_float;
76 *value = (gpointer)&(gvalue->data[0].v_float);
77 break;
78 case G_TYPE_DOUBLE:
79 rettype = &ffi_type_double;
80 *value = (gpointer)&(gvalue->data[0].v_double);
81 break;
82 case G_TYPE_LONG:
83 rettype = &ffi_type_slong;
84 *value = (gpointer)&(gvalue->data[0].v_long);
85 break;
86 case G_TYPE_ULONG:
87 rettype = &ffi_type_ulong;
88 *value = (gpointer)&(gvalue->data[0].v_ulong);
89 break;
90 case G_TYPE_INT64:
91 rettype = &ffi_type_sint64;
92 *value = (gpointer)&(gvalue->data[0].v_int64);
93 break;
94 case G_TYPE_UINT64:
95 rettype = &ffi_type_uint64;
96 *value = (gpointer)&(gvalue->data[0].v_uint64);
97 break;
98 default:
99 rettype = &ffi_type_pointer;
100 *value = NULL;
101 g_warning ("Unsupported fundamental type: %s", g_type_name (type));
102 break;
103 }
104 return rettype;
105 }
106
107 /**
108 * g_value_to_ffi_return_type:
109 * @gvalue: (transfer none): a [type@GObject.Value] to convert
110 * @ffi_value: (transfer none): a [type@GIRepository.Argument] containing the
111 * data to use
112 * @value: (out caller-allocates): return location for the ffi data
113 *
114 * Convert @ffi_value to a format suitable for passing to ffi, using the type
115 * data from @gvalue.
116 *
117 * @value is only valid as long as @gvalue and @ffi_value are alive.
118 *
119 * Returns: pointer to the `ffi_type` associated with @value
120 * Since: 2.80
121 */
122 static ffi_type *
123 g_value_to_ffi_return_type (const GValue *gvalue,
124 const GIArgument *ffi_value,
125 gpointer *value)
126 {
127 ffi_type *rettype = NULL;
128 GType type = g_type_fundamental (G_VALUE_TYPE (gvalue));
129 g_assert (type != G_TYPE_INVALID);
130
131 *value = (gpointer)&(ffi_value->v_long);
132
133 switch (type) {
134 case G_TYPE_CHAR:
135 rettype = &ffi_type_sint8;
136 break;
137 case G_TYPE_UCHAR:
138 rettype = &ffi_type_uint8;
139 break;
140 case G_TYPE_BOOLEAN:
141 case G_TYPE_INT:
142 rettype = &ffi_type_sint;
143 break;
144 case G_TYPE_UINT:
145 rettype = &ffi_type_uint;
146 break;
147 case G_TYPE_STRING:
148 case G_TYPE_OBJECT:
149 case G_TYPE_BOXED:
150 case G_TYPE_POINTER:
151 case G_TYPE_PARAM:
152 rettype = &ffi_type_pointer;
153 break;
154 case G_TYPE_FLOAT:
155 rettype = &ffi_type_float;
156 *value = (gpointer)&(ffi_value->v_float);
157 break;
158 case G_TYPE_DOUBLE:
159 rettype = &ffi_type_double;
160 *value = (gpointer)&(ffi_value->v_double);
161 break;
162 case G_TYPE_LONG:
163 rettype = &ffi_type_slong;
164 break;
165 case G_TYPE_ULONG:
166 rettype = &ffi_type_ulong;
167 break;
168 case G_TYPE_INT64:
169 rettype = &ffi_type_sint64;
170 *value = (gpointer)&(ffi_value->v_int64);
171 break;
172 case G_TYPE_UINT64:
173 rettype = &ffi_type_uint64;
174 *value = (gpointer)&(ffi_value->v_uint64);
175 break;
176 default:
177 rettype = &ffi_type_pointer;
178 *value = NULL;
179 g_warning ("Unsupported fundamental type: %s", g_type_name (type));
180 break;
181 }
182 return rettype;
183 }
184
185 /**
186 * g_value_from_ffi_value:
187 * @gvalue: (inout): a [type@GObject.Value] to set
188 * @value: (transfer none): ffi data to convert
189 *
190 * Convert @value to a [type@GObject.Value] according to the type already set
191 * on @gvalue.
192 *
193 * @gvalue is valid even after @value is finalised.
194 *
195 * Since: 2.80
196 */
197 static void
198 g_value_from_ffi_value (GValue *gvalue,
199 const GIArgument *value)
200 {
201 switch (g_type_fundamental (G_VALUE_TYPE (gvalue))) {
202 case G_TYPE_INT:
203 g_value_set_int (gvalue, (gint)value->v_long);
204 break;
205 case G_TYPE_FLOAT:
206 g_value_set_float (gvalue, (gfloat)value->v_float);
207 break;
208 case G_TYPE_DOUBLE:
209 g_value_set_double (gvalue, (gdouble)value->v_double);
210 break;
211 case G_TYPE_BOOLEAN:
212 g_value_set_boolean (gvalue, (gboolean)value->v_long);
213 break;
214 case G_TYPE_STRING:
215 g_value_set_string (gvalue, (gchar*)value->v_pointer);
216 break;
217 case G_TYPE_CHAR:
218 g_value_set_schar (gvalue, (gchar)value->v_long);
219 break;
220 case G_TYPE_UCHAR:
221 g_value_set_uchar (gvalue, (guchar)value->v_ulong);
222 break;
223 case G_TYPE_UINT:
224 g_value_set_uint (gvalue, (guint)value->v_ulong);
225 break;
226 case G_TYPE_POINTER:
227 g_value_set_pointer (gvalue, (gpointer)value->v_pointer);
228 break;
229 case G_TYPE_LONG:
230 g_value_set_long (gvalue, (glong)value->v_long);
231 break;
232 case G_TYPE_ULONG:
233 g_value_set_ulong (gvalue, (gulong)value->v_ulong);
234 break;
235 case G_TYPE_INT64:
236 g_value_set_int64 (gvalue, (gint64)value->v_int64);
237 break;
238 case G_TYPE_UINT64:
239 g_value_set_uint64 (gvalue, (guint64)value->v_uint64);
240 break;
241 case G_TYPE_BOXED:
242 g_value_set_boxed (gvalue, (gpointer)value->v_pointer);
243 break;
244 case G_TYPE_PARAM:
245 g_value_set_param (gvalue, (gpointer)value->v_pointer);
246 break;
247 default:
248 g_warning ("Unsupported fundamental type: %s",
249 g_type_name (g_type_fundamental (G_VALUE_TYPE (gvalue))));
250 }
251
252 }
253
254 /**
255 * gi_cclosure_marshal_generic: (skip)
256 * @closure: a [type@GObject.Closure]
257 * @return_gvalue: (optional) (out caller-allocates): return location for the
258 * return value from the closure, or `NULL` to ignore
259 * @n_param_values: number of param values
260 * @param_values: (array length=n_param_values): values to pass to the closure
261 * parameters
262 * @invocation_hint: invocation hint
263 * @marshal_data: marshal data
264 *
265 * A generic C closure marshal function using ffi and
266 * [type@GIRepository.Argument].
267 *
268 * Since: 2.80
269 */
270 void
271 gi_cclosure_marshal_generic (GClosure *closure,
272 GValue *return_gvalue,
273 guint n_param_values,
274 const GValue *param_values,
275 gpointer invocation_hint,
276 gpointer marshal_data)
277 {
278 GIArgument return_ffi_value = { 0, };
279 ffi_type *rtype;
280 void *rvalue;
281 int n_args;
282 ffi_type **atypes;
283 void **args;
284 int i;
285 ffi_cif cif;
286 GCClosure *cc = (GCClosure*) closure;
287
288 if (return_gvalue && G_VALUE_TYPE (return_gvalue))
289 {
290 rtype = g_value_to_ffi_return_type (return_gvalue, &return_ffi_value,
291 &rvalue);
292 }
293 else
294 {
295 rtype = &ffi_type_void;
296 rvalue = &return_ffi_value.v_long;
297 }
298
299 n_args = n_param_values + 1;
300 atypes = g_alloca (sizeof (ffi_type *) * n_args);
301 args = g_alloca (sizeof (gpointer) * n_args);
302
303 if (n_param_values > 0)
304 {
305 if (G_CCLOSURE_SWAP_DATA (closure))
306 {
307 atypes[n_args-1] = value_to_ffi_type (param_values + 0,
308 &args[n_args-1]);
309 atypes[0] = &ffi_type_pointer;
310 args[0] = &closure->data;
311 }
312 else
313 {
314 atypes[0] = value_to_ffi_type (param_values + 0, &args[0]);
315 atypes[n_args-1] = &ffi_type_pointer;
316 args[n_args-1] = &closure->data;
317 }
318 }
319 else
320 {
321 atypes[0] = &ffi_type_pointer;
322 args[0] = &closure->data;
323 }
324
325 for (i = 1; i < n_args - 1; i++)
326 atypes[i] = value_to_ffi_type (param_values + i, &args[i]);
327
328 if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_args, rtype, atypes) != FFI_OK)
329 return;
330
331 ffi_call (&cif, marshal_data ? marshal_data : cc->callback, rvalue, args);
332
333 if (return_gvalue && G_VALUE_TYPE (return_gvalue))
334 g_value_from_ffi_value (return_gvalue, &return_ffi_value);
335 }