2f3c3fdbc6ce5e6f6beb9a9c4311901e74b01756
[gnome.seed] / libseed / seed-closure.c
1 /* -*- mode: C; indent-tabs-mode: t; tab-width: 8; c-basic-offset: 2; -*- */
2
3 /*
4  * This file is part of Seed, the GObject Introspection<->Javascript bindings.
5  *
6  * Seed is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation, either version 3 of
9  * the License, or (at your option) any later version.
10  * Seed is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with Seed.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Copyright (C) Robert Carr 2009 <carrr@rpi.edu>
18  */
19
20 #include "seed-private.h"
21 #include <sys/mman.h>
22
23 JSClassRef seed_native_callback_class;
24
25 static void
26 seed_closure_finalize (JSObjectRef object)
27 {
28   SeedNativeClosure *privates =
29     (SeedNativeClosure *) JSObjectGetPrivate (object);
30
31   SEED_NOTE (FINALIZATION, "Finalizing closure object %p with "
32              "GIBaseInfo: %s \n", object,
33              g_base_info_get_name ((GIBaseInfo *) privates->info));
34
35   g_free (privates->cif->arg_types);
36   g_free (privates->cif);
37   g_callable_info_free_closure (privates->info, privates->closure);
38   g_base_info_unref ((GIBaseInfo *) privates->info);
39
40   JSValueUnprotect (eng->context, object);
41 }
42
43 static void
44 seed_handle_closure (ffi_cif * cif, void *result, void **args, void *userdata)
45 {
46   SeedNativeClosure *privates = userdata;
47   gint num_args, i;
48   JSValueRef *jsargs;
49   JSValueRef return_value, exception = 0;
50   GITypeTag return_tag;
51   GIArgInfo *arg_info;
52   GITypeInfo *return_type;
53   GITypeInfo *arg_type;
54   GITypeTag tag;
55   GArgument rarg, return_arg;
56   JSContextRef ctx = JSGlobalContextCreateInGroup (context_group, 0);
57   GArgument *arg = &rarg;
58   gchar *mes;
59
60   seed_prepare_global_context (ctx);
61
62   SEED_NOTE (INVOCATION, "Invoking closure of type: %s \n",
63              g_base_info_get_name ((GIBaseInfo *) privates->info));
64
65   num_args = g_callable_info_get_n_args (privates->info);
66   return_type = g_callable_info_get_return_type (privates->info);
67   return_tag = g_type_info_get_tag (return_type);
68   jsargs = (JSValueRef *) g_newa (JSValueRef, num_args);
69
70   for (i = 0; i < num_args; i++)
71     {
72
73       arg_info = g_callable_info_get_arg (privates->info, i);
74       arg_type = g_arg_info_get_type (arg_info);
75       tag = g_type_info_get_tag (arg_type);
76
77       switch (tag)
78         {
79         case GI_TYPE_TAG_BOOLEAN:
80           arg->v_boolean = *(gboolean *) args[i];
81           break;
82         case GI_TYPE_TAG_INT8:
83           arg->v_int8 = *(gint8 *) args[i];
84           break;
85         case GI_TYPE_TAG_UINT8:
86           arg->v_uint8 = *(guint8 *) args[i];
87           break;
88         case GI_TYPE_TAG_INT16:
89           arg->v_int16 = *(gint16 *) args[i];
90           break;
91         case GI_TYPE_TAG_UINT16:
92           arg->v_uint16 = *(guint16 *) args[i];
93           break;
94         case GI_TYPE_TAG_INT32:
95           arg->v_int32 = *(gint32 *) args[i];
96           break;
97         case GI_TYPE_TAG_UINT32:
98           arg->v_uint32 = *(guint32 *) args[i];
99           break;
100         case GI_TYPE_TAG_LONG:
101         case GI_TYPE_TAG_INT64:
102           arg->v_int64 = *(glong *) args[i];
103           break;
104         case GI_TYPE_TAG_ULONG:
105         case GI_TYPE_TAG_UINT64:
106           arg->v_uint64 = *(glong *) args[i];
107           break;
108         case GI_TYPE_TAG_INT:
109         case GI_TYPE_TAG_SSIZE:
110         case GI_TYPE_TAG_SIZE:
111           arg->v_int32 = *(gint *) args[i];
112           break;
113         case GI_TYPE_TAG_UINT:
114           arg->v_uint32 = *(guint *) args[i];
115           break;
116         case GI_TYPE_TAG_FLOAT:
117           arg->v_float = *(gfloat *) args[i];
118           break;
119         case GI_TYPE_TAG_DOUBLE:
120           arg->v_double = *(gdouble *) args[i];
121           break;
122         case GI_TYPE_TAG_UTF8:
123           arg->v_string = *(gchar **) args[i];
124           break;
125         case GI_TYPE_TAG_INTERFACE:
126           {
127             GIBaseInfo *interface;
128             GIInfoType interface_type;
129
130             interface = g_type_info_get_interface (arg_type);
131             interface_type = g_base_info_get_type (interface);
132
133             if (interface_type == GI_INFO_TYPE_OBJECT ||
134                 interface_type == GI_INFO_TYPE_INTERFACE)
135               {
136                 arg->v_pointer = *(gpointer *) args[i];
137                 g_base_info_unref (interface);
138                 break;
139               }
140
141             else if (interface_type == GI_INFO_TYPE_ENUM ||
142                      interface_type == GI_INFO_TYPE_FLAGS)
143               {
144                 arg->v_double = *(double *) args[i];
145                 g_base_info_unref (interface);
146                 break;
147               }
148             else if (interface_type == GI_INFO_TYPE_STRUCT)
149               {
150                 arg->v_pointer = *(gpointer *) args[i];
151                 g_base_info_unref (interface);
152                 break;
153               }
154
155             g_base_info_unref (interface);
156           }
157         case GI_TYPE_TAG_GLIST:
158         case GI_TYPE_TAG_GSLIST:
159           arg->v_pointer = *(gpointer *) args[i];
160           break;
161         default:
162           arg->v_pointer = 0;
163         }
164       jsargs[i] = seed_gi_argument_make_js (ctx, arg, arg_type, 0);
165       seed_gi_release_in_arg (g_arg_info_get_ownership_transfer (arg_info),
166                               arg_type, arg);
167       g_base_info_unref ((GIBaseInfo *) arg_info);
168       g_base_info_unref ((GIBaseInfo *) arg_type);
169     }
170
171   return_value = (JSValueRef)
172     JSObjectCallAsFunction (ctx,
173                             (JSObjectRef) privates->function, 0,
174                             num_args, jsargs, &exception);
175
176   if (exception)
177     {
178       mes = seed_exception_to_string (ctx, exception);
179       g_warning ("Exception in closure marshal. %s \n", mes);
180       g_free (mes);
181       exception = 0;
182     }
183
184   seed_gi_make_argument (ctx, (JSValueRef) return_value, return_type,
185                          &return_arg, 0);
186   switch (return_tag)
187     {
188
189     case GI_TYPE_TAG_BOOLEAN:
190       *(gboolean *) result = return_arg.v_boolean;
191       break;
192     case GI_TYPE_TAG_INT8:
193       *(gint8 *) result = return_arg.v_int8;
194       break;
195     case GI_TYPE_TAG_UINT8:
196       *(guint8 *) result = return_arg.v_uint8;
197       break;
198     case GI_TYPE_TAG_INT16:
199       *(gint16 *) result = return_arg.v_int16;
200       break;
201     case GI_TYPE_TAG_UINT16:
202       return_arg.v_uint16 = *(guint16 *) args[i];
203       break;
204     case GI_TYPE_TAG_INT32:
205       *(gint32 *) result = return_arg.v_int32;
206       break;
207     case GI_TYPE_TAG_UINT32:
208       *(guint32 *) result = return_arg.v_uint32;
209       break;
210     case GI_TYPE_TAG_LONG:
211     case GI_TYPE_TAG_INT64:
212       *(glong *) result = return_arg.v_int64;
213       break;
214     case GI_TYPE_TAG_ULONG:
215     case GI_TYPE_TAG_UINT64:
216       *(glong *) result = return_arg.v_uint64;
217       break;
218     case GI_TYPE_TAG_INT:
219     case GI_TYPE_TAG_SSIZE:
220     case GI_TYPE_TAG_SIZE:
221       *(gint *) result = return_arg.v_int32;
222       break;
223     case GI_TYPE_TAG_UINT:
224       *(guint *) result = return_arg.v_uint32;
225       break;
226     case GI_TYPE_TAG_FLOAT:
227       *(gfloat *) result = return_arg.v_float;
228       break;
229     case GI_TYPE_TAG_DOUBLE:
230       *(gdouble *) result = return_arg.v_double;
231       break;
232     case GI_TYPE_TAG_UTF8:
233       *(gchar **) result = return_arg.v_string;
234       break;
235     case GI_TYPE_TAG_INTERFACE:
236       {
237         GIBaseInfo *interface;
238         GIInfoType interface_type;
239
240         interface = g_type_info_get_interface (return_type);
241         interface_type = g_base_info_get_type (interface);
242
243         if (interface_type == GI_INFO_TYPE_OBJECT ||
244             interface_type == GI_INFO_TYPE_INTERFACE)
245           {
246             *(gpointer *) result = return_arg.v_pointer;
247             break;
248           }
249
250         else if (interface_type == GI_INFO_TYPE_ENUM ||
251                  interface_type == GI_INFO_TYPE_FLAGS)
252           {
253             *(double *) result = return_arg.v_double;
254             break;
255           }
256         else if (interface_type == GI_INFO_TYPE_STRUCT)
257           {
258             *(gpointer *) result = return_arg.v_pointer;
259             break;
260           }
261       }
262     case GI_TYPE_TAG_GLIST:
263     case GI_TYPE_TAG_GSLIST:
264       *(gpointer *) result = return_arg.v_pointer;
265       break;
266     default:
267       *(gpointer *) result = 0;
268     }
269
270   g_base_info_unref ((GIBaseInfo *) return_type);
271   JSGlobalContextRelease ((JSGlobalContextRef) ctx);
272 }
273
274 SeedNativeClosure *
275 seed_make_native_closure (JSContextRef ctx,
276                           GICallableInfo * info,
277                           JSValueRef function)
278 {
279   ffi_cif *cif;
280   ffi_closure *closure;
281   ffi_type **arg_types;
282   GITypeInfo *return_type;
283   gint num_args;
284   SeedNativeClosure *privates;
285   JSObjectRef cached;
286
287   cached =
288     (JSObjectRef) seed_object_get_property (ctx, (JSObjectRef) function,
289                                             "__seed_native_closure");
290   if (cached
291       && JSValueIsObjectOfClass (ctx, cached, seed_native_callback_class))
292     {
293       return (SeedNativeClosure *) JSObjectGetPrivate (cached);
294     }
295
296   num_args = g_callable_info_get_n_args (info);
297   return_type = g_callable_info_get_return_type (info);
298   arg_types = (ffi_type **) g_new0 (ffi_type *, num_args + 1);
299   cif = g_new0 (ffi_cif, 1);
300
301   privates = g_new0 (SeedNativeClosure, 1);
302   privates->info = (GICallableInfo *) g_base_info_ref ((GIBaseInfo *) info);
303   privates->function = function;
304   privates->cif = cif;
305
306   closure =
307     g_callable_info_prepare_closure (info, cif, seed_handle_closure,
308                                      privates);
309   privates->closure = closure;
310
311   JSValueProtect (ctx, function);
312
313   seed_object_set_property (ctx, (JSObjectRef) function,
314                             "__seed_native_closure",
315                             (JSValueRef) JSObjectMake (ctx,
316                                                        seed_native_callback_class,
317                                                        privates));
318
319   g_base_info_unref ((GIBaseInfo *) return_type);
320
321   return privates;
322 }
323
324 static void
325 closure_invalidated (gpointer data, GClosure * c)
326 {
327   SeedClosure *closure = (SeedClosure *) c;
328
329   SEED_NOTE (FINALIZATION, "Finalizing closure.");
330   if (closure->user_data
331       && !JSValueIsUndefined (eng->context, closure->user_data))
332     JSValueUnprotect (eng->context, closure->user_data);
333   if (!JSValueIsUndefined (eng->context, closure->function))
334     JSValueUnprotect (eng->context, closure->function);
335
336   g_free (closure->description);
337
338 }
339
340 JSObjectRef
341 seed_closure_get_callable (GClosure * c)
342 {
343   return ((SeedClosure *) c)->function;
344 }
345
346 JSValueRef
347 seed_closure_invoke (GClosure * closure, JSValueRef * args, guint argc,
348                      JSValueRef * exception)
349 {
350   JSContextRef ctx = JSGlobalContextCreateInGroup (context_group, 0);
351   JSValueRef *real_args = g_newa (JSValueRef, argc + 1);
352   JSValueRef ret;
353   guint i;
354
355   seed_prepare_global_context (ctx);
356   for (i = 0; i < argc; i++)
357     real_args[i] = args[i];
358   args[argc] =
359     ((SeedClosure *) closure)->user_data ? ((SeedClosure *) closure)->
360     user_data : JSValueMakeNull (ctx);
361
362   ret =
363     JSObjectCallAsFunction (ctx, ((SeedClosure *) closure)->function, NULL,
364                             argc + 1, real_args, exception);
365   JSGlobalContextRelease ((JSGlobalContextRef) ctx);
366
367   return ret;
368 }
369
370 JSValueRef
371 seed_closure_invoke_with_context (JSContextRef ctx, GClosure * closure,
372                                   JSValueRef * args, guint argc,
373                                   JSValueRef * exception)
374 {
375   JSValueRef *real_args = g_newa (JSValueRef, argc + 1);
376   JSValueRef ret;
377   guint i;
378
379   for (i = 0; i < argc; i++)
380     real_args[i] = args[i];
381   args[argc] =
382     ((SeedClosure *) closure)->user_data ? ((SeedClosure *) closure)->
383     user_data : JSValueMakeNull (ctx);
384
385   ret =
386     JSObjectCallAsFunction (ctx, ((SeedClosure *) closure)->function, NULL,
387                             argc + 1, real_args, exception);
388
389   return ret;
390 }
391
392 GClosure *
393 seed_closure_new (JSContextRef ctx, JSObjectRef function,
394                   JSObjectRef user_data, const gchar * description)
395 {
396   GClosure *closure;
397
398   closure = g_closure_new_simple (sizeof (SeedClosure), 0);
399   g_closure_add_finalize_notifier (closure, 0, closure_invalidated);
400   g_closure_set_marshal (closure, seed_signal_marshal_func);
401
402   JSValueProtect (ctx, function);
403   ((SeedClosure *) closure)->function = function;
404   if (user_data && !JSValueIsNull (ctx, user_data))
405     {
406       ((SeedClosure *) closure)->user_data = user_data;
407       JSValueProtect (ctx, user_data);
408     }
409
410   if (description)
411     ((SeedClosure *) closure)->description = g_strdup (description);
412
413   return closure;
414 }
415
416 void
417 seed_closure_warn_exception (GClosure * c,
418                              JSContextRef ctx, JSValueRef exception)
419 {
420   JSObjectRef callable = seed_closure_get_callable (c);
421   gchar *name = seed_value_to_string (ctx,
422                                       seed_object_get_property (ctx, callable,
423                                                                 "name"),
424                                       NULL);
425   gchar *mes = seed_exception_to_string (ctx, exception);
426
427   g_warning ("Exception in closure (%p) for %s (handler %s). %s", c,
428              ((SeedClosure *) c)->description,
429              *name == '\0' ? "[anonymous]" : name, mes);
430
431   g_free (name);
432   g_free (mes);
433 }
434
435 JSClassDefinition seed_native_callback_def = {
436   0,                            /* Version, always 0 */
437   0,
438   "seed_native_callback",       /* Class Name */
439   0,                            /* Parent Class */
440   NULL,                         /* Static Values */
441   NULL,                         /* Static Functions */
442   NULL,
443   seed_closure_finalize,        /* Finalize */
444   NULL,                         /* Has Property */
445   NULL,                         /* Get Property */
446   NULL,                         /* Set Property */
447   NULL,                         /* Delete Property */
448   NULL,                         /* Get Property Names */
449   NULL,                         /* Call As Function */
450   NULL,                         /* Call As Constructor */
451   NULL,                         /* Has Instance */
452   NULL                          /* Convert To Type */
453 };
454
455 void
456 seed_closures_init (void)
457 {
458   seed_native_callback_class = JSClassCreate (&seed_native_callback_def);
459   JSClassRetain (seed_native_callback_class);
460 }