97f448d33f584b2de48f7f30990ebccb6ecfae03
[gnome.seed] / libseed / seed-types.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 <dlfcn.h>
22 #include <pthread.h>
23
24 JSClassRef gobject_class;
25 JSClassRef gobject_method_class;
26 JSClassRef gobject_constructor_class;
27 JSClassRef seed_callback_class;
28 JSClassRef gobject_init_method_class;
29 SeedEngine *eng;
30
31 static gboolean
32 seed_value_is_gobject (JSContextRef ctx, JSValueRef value)
33 {
34   if (!JSValueIsObject (ctx, value) || JSValueIsNull (ctx, value))
35     return FALSE;
36
37   return JSValueIsObjectOfClass (ctx, value, gobject_class);
38 }
39
40 void
41 seed_toggle_ref (gpointer data, GObject * object, gboolean is_last_ref)
42 {
43   JSValueRef wrapper;
44
45   if (!g_object_get_data (object, "js-ref"))
46     return;
47
48   wrapper = (JSValueRef) data;
49
50   if (is_last_ref)
51     {
52       JSValueUnprotect (eng->context, wrapper);
53     }
54   else
55     {
56       JSValueProtect (eng->context, wrapper);
57     }
58 }
59
60 static void
61 seed_gobject_destroyed (gpointer object)
62 {
63   JSValueUnprotect (eng->context, (JSValueRef) object);
64   JSObjectSetPrivate ((JSObjectRef) object, 0);
65 }
66
67 JSObjectRef
68 seed_make_wrapper_for_type (JSContextRef ctx, GType type)
69 {
70   JSClassRef class;
71   JSObjectRef ret;
72   JSValueRef prototype;
73
74   class = seed_gobject_get_class_for_gtype (ctx, type);
75
76   while (!class && (type = g_type_parent (type)))
77     class = seed_gobject_get_class_for_gtype (ctx, type);
78
79   prototype = seed_gobject_get_prototype_for_gtype (type);
80   ret = JSObjectMake (ctx, class, NULL);
81   if (prototype)
82     JSObjectSetPrototype (ctx, ret, prototype);
83   else
84     g_assert_not_reached ();
85
86   return ret;
87 }
88
89 static JSValueRef
90 seed_wrap_object (JSContextRef ctx, GObject * object)
91 {
92   JSValueRef user_data;
93   JSObjectRef js_ref;
94   GType type;
95
96   type = G_OBJECT_TYPE (object);
97
98   user_data = (JSValueRef) g_object_get_qdata (object, js_ref_quark);
99
100   if (user_data)
101     return user_data;
102
103   if (pthread_getspecific(seed_next_gobject_wrapper_key))
104     js_ref = pthread_getspecific(seed_next_gobject_wrapper_key);
105   else
106     js_ref = seed_make_wrapper_for_type (ctx, type);
107
108   JSObjectSetPrivate (js_ref, object);
109
110   g_object_set_qdata_full (object, js_ref_quark, (gpointer) js_ref,
111                            seed_gobject_destroyed);
112
113   JSValueProtect (eng->context, js_ref);
114   g_object_add_toggle_ref (object, seed_toggle_ref, (gpointer) js_ref);
115
116   seed_add_signals_to_object (ctx, js_ref, object);
117
118   pthread_setspecific(seed_next_gobject_wrapper_key, NULL);
119
120   return js_ref;
121 }
122
123 static gboolean
124 seed_release_arg (GITransfer transfer,
125                   GITypeInfo * type_info, GITypeTag type_tag, GArgument * arg)
126 {
127   GType gtype;
128   GITypeInfo *param_type;
129   GIBaseInfo *interface_info;
130   GValue *gval;
131
132   switch (type_tag)
133     {
134     case GI_TYPE_TAG_UTF8:
135     case GI_TYPE_TAG_FILENAME:
136       g_free (arg->v_string);
137       break;
138     case GI_TYPE_TAG_ARRAY:
139       if (arg->v_pointer)
140         {
141           param_type = g_type_info_get_param_type (type_info, 0);
142
143           switch (g_type_info_get_tag (param_type))
144             {
145             case GI_TYPE_TAG_UTF8:
146               if (transfer == GI_TRANSFER_EVERYTHING)
147                 g_strfreev (arg->v_pointer);
148               else if (transfer == GI_TRANSFER_CONTAINER)
149                 g_free (arg->v_pointer);
150               break;
151             case GI_TYPE_TAG_GTYPE:
152             case GI_TYPE_TAG_FLOAT:
153             case GI_TYPE_TAG_UINT8:
154               g_free (arg->v_pointer);
155               break;
156             case GI_TYPE_TAG_INTERFACE:
157               break;
158             default:
159               g_assert_not_reached ();
160             }
161
162           g_base_info_unref ((GIBaseInfo *) param_type);
163         }
164       break;
165     case GI_TYPE_TAG_INTERFACE:
166       {
167         if (arg->v_pointer)
168           {
169             interface_info = g_type_info_get_interface (type_info);
170
171             gtype =
172               g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *)
173                                                  interface_info);
174
175             if (g_type_is_a (gtype, G_TYPE_OBJECT)
176                 || g_type_is_a (gtype, G_TYPE_INTERFACE))
177               {
178                 SEED_NOTE (MISC,
179                            "Unreffing object of type: %s in"
180                            "argument release. Reference count: %d\n",
181                            g_type_name (G_OBJECT_TYPE
182                                         (G_OBJECT (arg->v_pointer))),
183                            G_OBJECT (arg->v_pointer)->ref_count);
184                 g_object_unref (G_OBJECT (arg->v_pointer));
185               }
186             else if (g_type_is_a (gtype, G_TYPE_VALUE))
187               {
188                 gval = (GValue *) arg->v_pointer;
189                 // Free/unref the GValue's contents.
190                 g_value_unset (gval);
191                 // Free the GValue.
192                 g_slice_free1 (sizeof (GValue), gval);
193               }
194             else if (g_type_is_a (gtype, G_TYPE_CLOSURE))
195               {
196                 g_closure_unref (arg->v_pointer);
197               }
198
199             g_base_info_unref (interface_info);
200           }
201         break;
202       }
203     default:
204       break;
205
206     }
207
208   return TRUE;
209 }
210
211 gboolean
212 seed_gi_release_arg (GITransfer transfer,
213                      GITypeInfo * type_info, GArgument * arg)
214 {
215   GITypeTag type_tag;
216
217   if (transfer == GI_TRANSFER_NOTHING)
218     return TRUE;
219
220   type_tag = g_type_info_get_tag ((GITypeInfo *) type_info);
221
222   return seed_release_arg (transfer, type_info, type_tag, arg);
223 }
224
225 gboolean
226 seed_gi_release_in_arg (GITransfer transfer,
227                         GITypeInfo * type_info, GArgument * arg)
228 {
229   GITypeTag type_tag;
230
231   if (transfer == GI_TRANSFER_EVERYTHING)
232     return TRUE;
233
234   type_tag = g_type_info_get_tag ((GITypeInfo *) type_info);
235
236   switch (type_tag)
237     {
238       // TODO: FIXME: Leaaaks?
239     case GI_TYPE_TAG_INTERFACE:
240       {
241         // TODO: FIXME: Need some safe way to look for GClosure.
242         break;
243       }
244     case GI_TYPE_TAG_UTF8:
245     case GI_TYPE_TAG_FILENAME:
246     case GI_TYPE_TAG_ARRAY:
247       return seed_release_arg (GI_TRANSFER_EVERYTHING,
248                                type_info, type_tag, arg);
249     default:
250       break;
251     }
252
253   return TRUE;
254 }
255
256 static JSValueRef
257 seed_gi_make_jsarray (JSContextRef ctx,
258                       void *array,
259                       GITypeInfo * param_type, JSValueRef * exception)
260 {
261   GITypeTag element_type;
262   JSValueRef *elements;
263   guint length, i;
264   gchar **str_array = (gchar **) array;
265   JSValueRef ret = JSValueMakeNull (ctx);
266
267   element_type = g_type_info_get_tag (param_type);
268
269   if (element_type == GI_TYPE_TAG_UTF8)
270     {
271
272       length = g_strv_length (str_array);
273       if (!length)
274         return ret;
275
276       elements = g_alloca (sizeof (JSValueRef) * length);
277       for (i = 0; i < length; ++i)
278         {
279           elements[i] = seed_value_from_string (ctx, str_array[i], exception);
280         }
281
282       ret = (JSValueRef) JSObjectMakeArray (ctx, length, elements, exception);
283     }
284
285   if (element_type == GI_TYPE_TAG_GTYPE)
286     {
287       GType* ptr = (GType*)array;
288       length = 0;
289       while (ptr[length])  length++;
290     
291       elements = g_alloca (sizeof (JSValueRef) * length);
292
293       for (i = 0; i < length; ++i)
294           elements[i] = seed_value_from_int (ctx, ptr[i], exception);
295         
296       return (JSValueRef) JSObjectMakeArray (ctx, length, elements, exception);
297
298     }
299
300   return ret;
301 }
302
303 static gboolean
304 seed_gi_make_array (JSContextRef ctx,
305                     JSValueRef array,
306                     guint length,
307                     GITypeInfo * param_type,
308                     void **array_p, JSValueRef * exception)
309 {
310   GITypeTag element_type;
311   JSValueRef elem;
312   guint i;
313
314   element_type = g_type_info_get_tag (param_type);
315
316   switch (element_type)
317     {
318     case GI_TYPE_TAG_UTF8:
319       {
320         gchar **strresult = g_new0 (gchar *, length + 1);
321
322         for (i = 0; i < length; i++)
323           {
324             elem = JSObjectGetPropertyAtIndex (ctx,
325                                                (JSObjectRef) array,
326                                                i, exception);
327             strresult[i] = seed_value_to_string (ctx, elem, exception);
328           }
329
330         *array_p = strresult;
331       }
332       break;
333     case GI_TYPE_TAG_GTYPE:
334       {
335         GType *typeresult;
336
337         typeresult = g_new0 (GType, length + 1);
338
339         for (i = 0; i < length; i++)
340           {
341             elem = JSObjectGetPropertyAtIndex (ctx,
342                                                (JSObjectRef) array,
343                                                i, exception);
344             typeresult[i] = seed_value_to_int (ctx, elem, exception);
345           }
346
347         *array_p = typeresult;
348       }
349       break;
350     case GI_TYPE_TAG_FLOAT:
351       {
352         gfloat *floatresult;
353
354         floatresult = g_new0 (gfloat, length + 1);
355
356         for (i = 0; i < length; i++)
357           {
358             elem = JSObjectGetPropertyAtIndex (ctx,
359                                                (JSObjectRef) array,
360                                                i, exception);
361             floatresult[i] = seed_value_to_float (ctx, elem, exception);
362           }
363
364         *array_p = floatresult;
365       }
366       break;
367     case GI_TYPE_TAG_DOUBLE:
368       {
369         gdouble *dblresult;
370
371         dblresult = g_new0 (gdouble, length + 1);
372
373         for (i = 0; i < length; i++)
374           {
375             elem = JSObjectGetPropertyAtIndex (ctx,
376                                                (JSObjectRef) array,
377                                                i, exception);
378             dblresult[i] = seed_value_to_double (ctx, elem, exception);
379           }
380
381         *array_p = dblresult;
382       }
383       break;
384     case GI_TYPE_TAG_INT:
385       {
386         gint *intresult;
387
388         intresult = g_new0 (gint, length + 1);
389
390         for (i = 0; i < length; i++)
391           {
392             elem = JSObjectGetPropertyAtIndex (ctx,
393                                                (JSObjectRef) array,
394                                                i, exception);
395             intresult[i] = seed_value_to_int (ctx, elem, exception);
396           }
397
398         *array_p = intresult;
399       }
400       break;
401     case GI_TYPE_TAG_UINT8:
402       {
403         guint8 *guint8result;
404
405         guint8result = g_new0 (guint8, length + 1);
406
407         for (i = 0; i < length; i++)
408           {
409             elem = JSObjectGetPropertyAtIndex (ctx,
410                                                (JSObjectRef) array,
411                                                i, exception);
412             guint8result[i] = seed_value_to_uchar (ctx, elem, exception);
413           }
414
415         *array_p = guint8result;
416       }
417       break;
418     case GI_TYPE_TAG_INTERFACE:
419       {
420         GIBaseInfo *interface = g_type_info_get_interface (param_type);
421         GIInfoType interface_type = g_base_info_get_type (interface);
422         if (interface_type == GI_INFO_TYPE_OBJECT
423             || interface_type == GI_INFO_TYPE_INTERFACE
424             || interface_type == GI_INFO_TYPE_STRUCT)
425           {
426             GType type =
427               g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *)
428                                                  interface);
429             if (g_type_is_a (type, G_TYPE_VALUE))
430               {
431                 GValue *gvalresult;
432
433                 // TODO:FIXME: Robb. Valgrind thinks there's a leak here,
434                 //             at least while running Same Seed.
435                 gvalresult = g_new0 (GValue, length + 1);
436
437                 for (i = 0; i < length; i++)
438                   {
439                     elem = JSObjectGetPropertyAtIndex (ctx,
440                                                        (JSObjectRef) array,
441                                                        i, exception);
442                     seed_gvalue_from_seed_value (ctx, elem,
443                                                  (GType) 0,
444                                                  &gvalresult[i], exception);
445                   }
446                 *array_p = gvalresult;
447
448                 g_base_info_unref (interface);
449                 break;
450               }
451           }
452
453         g_base_info_unref (interface);
454       }
455     default:
456       seed_make_exception (ctx, exception, "ArgumentError",
457                            "Unhandled array element type");
458       return FALSE;
459     }
460
461   return TRUE;
462 }
463
464 gboolean
465 seed_gi_make_argument (JSContextRef ctx,
466                        JSValueRef value,
467                        GITypeInfo * type_info,
468                        GArgument * arg,
469                        JSValueRef * exception)
470 {
471   GITypeTag gi_tag = g_type_info_get_tag (type_info);
472
473   // FIXME: Some types are not "nullable", also need to check if argument
474   // can be null before doing this.
475   if (!value || JSValueIsNull (ctx, value))
476     {
477       arg->v_pointer = 0;
478       return 1;
479     }
480
481   switch (gi_tag)
482     {
483     case GI_TYPE_TAG_VOID:
484       // things like gio.outputstream.write use void pointers
485       if (g_type_info_is_pointer (type_info))
486           arg->v_string = seed_value_to_string (ctx, value, exception);
487
488       break;
489     case GI_TYPE_TAG_BOOLEAN:
490       arg->v_boolean = seed_value_to_boolean (ctx, value, exception);
491       break;
492     case GI_TYPE_TAG_INT8:
493       arg->v_int8 = seed_value_to_char (ctx, value, exception);
494       break;
495     case GI_TYPE_TAG_UINT8:
496       arg->v_uint8 = seed_value_to_uchar (ctx, value, exception);
497       break;
498     case GI_TYPE_TAG_INT16:
499       arg->v_int16 = seed_value_to_int (ctx, value, exception);
500       break;
501     case GI_TYPE_TAG_UINT16:
502       arg->v_uint16 = seed_value_to_uint (ctx, value, exception);
503       break;
504     case GI_TYPE_TAG_INT32:
505       arg->v_int32 = seed_value_to_int (ctx, value, exception);
506       break;
507     case GI_TYPE_TAG_UINT32:
508       arg->v_uint32 = seed_value_to_uint (ctx, value, exception);
509       break;
510     case GI_TYPE_TAG_LONG:
511       arg->v_long = seed_value_to_long (ctx, value, exception);
512       break;
513     case GI_TYPE_TAG_INT64:
514       arg->v_int64 = seed_value_to_int64 (ctx, value, exception);
515       break;
516     case GI_TYPE_TAG_ULONG:
517       arg->v_ulong = seed_value_to_ulong (ctx, value, exception);
518       break;
519     case GI_TYPE_TAG_UINT64:
520       arg->v_uint64 = seed_value_to_uint64 (ctx, value, exception);
521       break;
522     case GI_TYPE_TAG_INT:
523       arg->v_int = seed_value_to_int (ctx, value, exception);
524       break;
525     case GI_TYPE_TAG_UINT:
526       arg->v_uint = seed_value_to_uint (ctx, value, exception);
527       break;
528     case GI_TYPE_TAG_SIZE:
529       arg->v_size = seed_value_to_size (ctx, value, exception);
530       break;
531     case GI_TYPE_TAG_SSIZE:
532       arg->v_ssize = seed_value_to_ssize (ctx, value, exception);
533       break;
534     case GI_TYPE_TAG_FLOAT:
535       arg->v_float = seed_value_to_float (ctx, value, exception);
536       break;
537     case GI_TYPE_TAG_DOUBLE:
538       arg->v_double = seed_value_to_double (ctx, value, exception);
539       break;
540     case GI_TYPE_TAG_UTF8:
541       arg->v_string = seed_value_to_string (ctx, value, exception);
542       break;
543     case GI_TYPE_TAG_FILENAME:
544       arg->v_string = seed_value_to_filename (ctx, value, exception);
545       break;
546     case GI_TYPE_TAG_GTYPE:
547       arg->v_int = seed_value_to_int (ctx, value, exception);
548       break;
549     case GI_TYPE_TAG_TIME_T:
550       arg->v_long = seed_value_to_time_t (ctx, value, exception);
551       break;
552     case GI_TYPE_TAG_INTERFACE:
553       {
554         GIBaseInfo *interface;
555         GIInfoType interface_type;
556         GType required_gtype;
557         GObject *gobject;
558
559         interface = g_type_info_get_interface (type_info);
560         interface_type = g_base_info_get_type (interface);
561
562         arg->v_pointer = NULL;
563
564         if (interface_type == GI_INFO_TYPE_OBJECT ||
565             interface_type == GI_INFO_TYPE_INTERFACE)
566           {
567             gobject = seed_value_to_object (ctx, value, exception);
568             required_gtype =
569               g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *)
570                                                  interface);
571
572             // FIXME: Not clear if the g_type_is_a check is desired here.
573             // Possibly 'breaks things' when we don't have introspection
574             // data for some things in an interface hierarchy. Hasn't
575             // provided any problems so far.
576             if (!gobject
577                 || !g_type_is_a (G_OBJECT_TYPE (gobject), required_gtype))
578               {
579                 g_base_info_unref (interface);
580                 return FALSE;
581               }
582
583             arg->v_pointer = gobject;
584             g_base_info_unref (interface);
585             break;
586           }
587         else if (interface_type == GI_INFO_TYPE_ENUM ||
588                  interface_type == GI_INFO_TYPE_FLAGS)
589           {
590             arg->v_long = seed_value_to_long (ctx, value, exception);
591             if (!(interface_type == GI_INFO_TYPE_FLAGS)
592                 && !seed_validate_enum ((GIEnumInfo *) interface,
593                                         arg->v_long))
594               {
595                 seed_make_exception (ctx, exception, "EnumRange",
596                                      "Enum value: %ld is out of range",
597                                      arg->v_long);
598                 g_base_info_unref (interface);
599
600                 return FALSE;
601               }
602
603             g_base_info_unref (interface);
604             break;
605           }
606         else if (interface_type == GI_INFO_TYPE_STRUCT ||
607                  interface_type == GI_INFO_TYPE_UNION)
608           {
609             if (JSValueIsObjectOfClass (ctx, value, seed_struct_class))
610               arg->v_pointer = seed_pointer_get_pointer (ctx, value);
611             else if (JSValueIsObjectOfClass (ctx, value, seed_union_class))
612               arg->v_pointer = seed_pointer_get_pointer (ctx, value);
613             else
614               {
615                 GType type =
616                   g_registered_type_info_get_g_type ((GIRegisteredTypeInfo
617                                                       *) interface);
618                 if (!type)
619                   {
620                     g_base_info_unref (interface);
621                     return FALSE;
622                   }
623                 else if (type == G_TYPE_VALUE)
624                   {
625                     GValue *gval = g_slice_alloc0 (sizeof (GValue));
626                     seed_gvalue_from_seed_value (ctx,
627                                                  value,
628                                                  (GType) NULL,
629                                                  gval, exception);
630                     arg->v_pointer = gval;
631
632                     g_base_info_unref (interface);
633                     break;
634                   }
635                 // Automatically convert between functions and
636                 // GClosures where expected.
637                 else if (g_type_is_a (type, G_TYPE_CLOSURE))
638                   {
639                     if (JSObjectIsFunction (ctx, (JSObjectRef) value))
640                       {
641                         arg->v_pointer =
642                           seed_closure_new (ctx, (JSObjectRef) value, NULL,
643                                             NULL);
644                       }
645                   }
646                 else
647                   {
648                     JSObjectRef strukt =
649                       seed_construct_struct_type_with_parameters (ctx,
650                                                                   interface,
651                                                                   (JSObjectRef) value,
652                                                                   exception);
653                     arg->v_pointer = seed_pointer_get_pointer (ctx, strukt);
654                   }
655               }
656             g_base_info_unref (interface);
657             break;
658           }
659         else if (interface_type == GI_INFO_TYPE_CALLBACK)
660           {
661             if (JSValueIsNull (ctx, value))
662               {
663                 arg->v_pointer = NULL;
664                 g_base_info_unref (interface);
665                 break;
666               }
667             // Someone passes in a wrapper around a method where a
668             // callback is expected, i.e Clutter.sine_inc_func, as an alpha
669             // Have to dlsym the symbol to be able to do this.
670             // NOTE: Some cases where dlsym(NULL, symbol) doesn't work depending
671             // On how libseed is loaded.
672             else if (JSValueIsObjectOfClass (ctx,
673                                              value, gobject_method_class))
674               {
675                 GIFunctionInfo *info =
676                   JSObjectGetPrivate ((JSObjectRef) value);
677                 const gchar *symbol = g_function_info_get_symbol (info);
678                 gchar *error;
679                 void *fp;
680
681                 dlerror ();
682                 fp = (void *) dlsym (0, symbol);
683                 if ((error = dlerror ()) != NULL)
684                   {
685                     g_critical ("dlerror: %s \n", error);
686                   }
687                 else
688                   {
689                     arg->v_pointer = fp;
690                     g_base_info_unref (interface);
691                     break;
692                   }
693               }
694             // Somewhat deprecated from when it was necessary to manually
695             // create closure objects...
696             else if (JSValueIsObjectOfClass (ctx,
697                                              value,
698                                              seed_native_callback_class))
699               {
700                 SeedNativeClosure *privates =
701                   (SeedNativeClosure *)
702                   JSObjectGetPrivate ((JSObjectRef) value);
703                 arg->v_pointer = privates->closure;
704                 g_base_info_unref (interface);
705                 break;
706               }
707             // Automagically create closure around function passed in as
708             // callback.
709             else if (JSObjectIsFunction (ctx, (JSObjectRef) value))
710               {
711                 SeedNativeClosure *privates = seed_make_native_closure (ctx,
712                                                                         (GICallableInfo *) interface,
713                                                                         value);
714                 arg->v_pointer = privates->closure;
715                 g_base_info_unref (interface);
716                 break;
717               }
718
719           }
720       }
721     case GI_TYPE_TAG_ARRAY:
722       {
723         if (JSValueIsNull (ctx, value))
724           {
725             arg->v_pointer = NULL;
726             break;
727           }
728         else if (!JSValueIsObject (ctx, value))
729           {
730             // TODO: FIXME: Is this right?
731             return FALSE;
732           }
733         else
734           {
735             GITypeInfo *param_type;
736             //TODO: FIXME: Better array test like the cool one on reddit.
737             guint length =
738               seed_value_to_int (ctx, seed_object_get_property (ctx,
739                                                                 (JSObjectRef)
740                                                                 value,
741                                                                 "length"),
742                                  exception);
743             if (!length)
744               {
745                 arg->v_pointer = NULL;
746                 break;
747               }
748
749             param_type = g_type_info_get_param_type (type_info, 0);
750             if (!seed_gi_make_array (ctx, value, length, param_type,
751                                      &arg->v_pointer, exception))
752               {
753                 g_base_info_unref ((GIBaseInfo *) param_type);
754                 return FALSE;
755               }
756             g_base_info_unref ((GIBaseInfo *) param_type);
757             break;
758           }
759       }
760     default:
761       return FALSE;
762
763     }
764   return TRUE;
765
766 }
767
768 JSValueRef
769 seed_gi_argument_make_js (JSContextRef ctx,
770                           GArgument * arg, GITypeInfo * type_info,
771                           JSValueRef * exception)
772 {
773   GITypeTag gi_tag = g_type_info_get_tag (type_info);
774   switch (gi_tag)
775     {
776     case GI_TYPE_TAG_VOID:
777       return JSValueMakeUndefined (ctx);
778     case GI_TYPE_TAG_BOOLEAN:
779       return seed_value_from_boolean (ctx, arg->v_boolean, exception);
780     case GI_TYPE_TAG_INT8:
781       return seed_value_from_char (ctx, arg->v_int8, exception);
782     case GI_TYPE_TAG_UINT8:
783       return seed_value_from_uchar (ctx, arg->v_uint8, exception);
784     case GI_TYPE_TAG_INT16:
785       return seed_value_from_int (ctx, arg->v_int16, exception);
786     case GI_TYPE_TAG_UINT16:
787       return seed_value_from_uint (ctx, arg->v_uint16, exception);
788     case GI_TYPE_TAG_INT32:
789       return seed_value_from_int (ctx, arg->v_int32, exception);
790     case GI_TYPE_TAG_UINT32:
791       return seed_value_from_uint (ctx, arg->v_uint32, exception);
792     case GI_TYPE_TAG_LONG:
793       return seed_value_from_long (ctx, arg->v_long, exception);
794     case GI_TYPE_TAG_INT64:
795       return seed_value_from_int64 (ctx, arg->v_int64, exception);
796     case GI_TYPE_TAG_ULONG:
797       return seed_value_from_ulong (ctx, arg->v_ulong, exception);
798     case GI_TYPE_TAG_UINT64:
799       return seed_value_from_uint64 (ctx, arg->v_uint64, exception);
800     case GI_TYPE_TAG_INT:
801       return seed_value_from_int (ctx, arg->v_int32, exception);
802     case GI_TYPE_TAG_UINT:
803       return seed_value_from_uint (ctx, arg->v_uint32, exception);
804     case GI_TYPE_TAG_SSIZE:
805       return seed_value_from_ssize (ctx, arg->v_ssize, exception);
806     case GI_TYPE_TAG_SIZE:
807       return seed_value_from_size (ctx, arg->v_size, exception);
808     case GI_TYPE_TAG_FLOAT:
809       return seed_value_from_float (ctx, arg->v_float, exception);
810     case GI_TYPE_TAG_DOUBLE:
811       return seed_value_from_double (ctx, arg->v_double, exception);
812     case GI_TYPE_TAG_UTF8:
813       return seed_value_from_string (ctx, arg->v_string, exception);
814     case GI_TYPE_TAG_FILENAME:
815       return seed_value_from_filename (ctx, arg->v_string, exception);
816     case GI_TYPE_TAG_GTYPE:
817       return seed_value_from_int (ctx, arg->v_int, exception);
818     case GI_TYPE_TAG_TIME_T:
819       return seed_value_from_time_t (ctx, arg->v_long, exception);
820     case GI_TYPE_TAG_ARRAY:
821       {
822         GITypeInfo *param_type;
823         JSValueRef ret;
824
825         if (arg->v_pointer == NULL)
826           return JSValueMakeNull (ctx);
827         if (!g_type_info_is_zero_terminated (type_info))
828           break;
829
830         param_type = g_type_info_get_param_type (type_info, 0);
831
832         ret = seed_gi_make_jsarray (ctx, arg->v_pointer, param_type,
833                                     exception);
834
835         g_base_info_unref ((GIBaseInfo *) param_type);
836
837         return ret;
838       }
839     case GI_TYPE_TAG_INTERFACE:
840       {
841         GIBaseInfo *interface;
842         GIInfoType interface_type;
843
844         interface = g_type_info_get_interface (type_info);
845         interface_type = g_base_info_get_type (interface);
846
847         if (interface_type == GI_INFO_TYPE_OBJECT ||
848             interface_type == GI_INFO_TYPE_INTERFACE)
849           {
850             if (arg->v_pointer == 0)
851               {
852                 g_base_info_unref (interface);
853                 return JSValueMakeNull (ctx);
854               }
855             g_base_info_unref (interface);
856             return seed_value_from_object (ctx, arg->v_pointer, exception);
857           }
858         else if (interface_type == GI_INFO_TYPE_ENUM
859                  || interface_type == GI_INFO_TYPE_FLAGS)
860           {
861             g_base_info_unref (interface);
862             return seed_value_from_long (ctx, arg->v_long, exception);
863           }
864         else if (interface_type == GI_INFO_TYPE_STRUCT)
865           {
866             JSValueRef strukt;
867
868             strukt = seed_make_struct (ctx, arg->v_pointer, interface);
869             g_base_info_unref (interface);
870
871             return strukt;
872           }
873       }
874     case GI_TYPE_TAG_GLIST:
875       {
876         GITypeInfo *list_type;
877         JSObjectRef ret;
878         GArgument larg;
879         gint i = 0;
880         GList *list = arg->v_pointer;
881
882         ret = JSObjectMakeArray (ctx, 0, NULL, exception);
883         list_type = g_type_info_get_param_type (type_info, 0);
884
885         for (; list != NULL; list = list->next)
886           {
887             JSValueRef ival;
888
889             larg.v_pointer = list->data;
890             ival =
891               (JSValueRef) seed_gi_argument_make_js (ctx, &larg,
892                                                      list_type, exception);
893             if (!ival)
894               ival = JSValueMakeNull (ctx);
895             JSObjectSetPropertyAtIndex (ctx, ret, i, ival, NULL);
896             i++;
897           }
898         return ret;
899
900       }
901     case GI_TYPE_TAG_GSLIST:
902       {
903         GITypeInfo *list_type;
904         JSObjectRef ret;
905         JSValueRef ival;
906         GArgument larg;
907         guint i = 0;
908         GSList *list = arg->v_pointer;
909
910         ret = JSObjectMakeArray (ctx, 0, NULL, exception);
911         list_type = g_type_info_get_param_type (type_info, 0);
912
913         for (; list != NULL; list = list->next)
914           {
915             larg.v_pointer = list->data;
916             ival =
917               (JSValueRef) seed_gi_argument_make_js (ctx, &larg,
918                                                      list_type, exception);
919             if (!ival)
920               ival = JSValueMakeNull (ctx);
921             JSObjectSetPropertyAtIndex (ctx, ret, i, ival, NULL);
922             i++;
923           }
924         return ret;
925       }
926     case GI_TYPE_TAG_ERROR:
927       {
928         JSValueRef ret;
929         seed_make_exception_from_gerror (ctx, &ret, (GError*) arg->v_pointer);
930         return ret;
931       }
932
933     default:
934       return FALSE;
935
936     }
937   return 0;
938 }
939
940 JSValueRef
941 seed_value_from_gvalue (JSContextRef ctx,
942                         GValue * gval, JSValueRef * exception)
943 {
944   if (!G_IS_VALUE (gval))
945     {
946       return false;
947     }
948   switch (G_VALUE_TYPE (gval))
949     {
950     case G_TYPE_BOOLEAN:
951       return seed_value_from_boolean (ctx,
952                                       g_value_get_boolean (gval), exception);
953     case G_TYPE_CHAR:
954       return seed_value_from_char (ctx, g_value_get_char (gval), exception);
955     case G_TYPE_UCHAR:
956       return seed_value_from_uchar (ctx, g_value_get_uchar (gval), exception);
957     case G_TYPE_INT:
958       return seed_value_from_int (ctx, g_value_get_int (gval), exception);
959     case G_TYPE_UINT:
960       return seed_value_from_uint (ctx, g_value_get_uint (gval), exception);
961     case G_TYPE_LONG:
962       return seed_value_from_long (ctx, g_value_get_long (gval), exception);
963     case G_TYPE_ULONG:
964       return seed_value_from_ulong (ctx, g_value_get_ulong (gval), exception);
965     case G_TYPE_INT64:
966       return seed_value_from_int64 (ctx, g_value_get_int64 (gval), exception);
967     case G_TYPE_UINT64:
968       return seed_value_from_uint64 (ctx, g_value_get_uint64 (gval),
969                                      exception);
970     case G_TYPE_FLOAT:
971       return seed_value_from_float (ctx, g_value_get_float (gval), exception);
972     case G_TYPE_DOUBLE:
973       return seed_value_from_double (ctx, g_value_get_double (gval),
974                                      exception);
975     case G_TYPE_STRING:
976       return seed_value_from_string (ctx, (gchar *)
977                                      g_value_get_string (gval), exception);
978     case G_TYPE_POINTER:
979       return seed_make_pointer (ctx, g_value_get_pointer (gval));
980     case G_TYPE_PARAM:
981       // Might need to dup and make a boxed.
982       return seed_make_pointer (ctx, g_value_get_param (gval));
983     }
984
985   if (g_type_is_a (G_VALUE_TYPE (gval), G_TYPE_ENUM) ||
986       g_type_is_a (G_VALUE_TYPE (gval), G_TYPE_FLAGS))
987     return seed_value_from_long (ctx, gval->data[0].v_long, exception);
988   else if (g_type_is_a (G_VALUE_TYPE (gval), G_TYPE_ENUM))
989     return seed_value_from_long (ctx, gval->data[0].v_long, exception);
990   else if (g_type_is_a (G_VALUE_TYPE (gval), G_TYPE_OBJECT))
991     {
992       GObject *obj = g_value_get_object (gval);
993       return seed_value_from_object (ctx, obj, exception);
994     }
995   else
996     {
997       GIBaseInfo *info;
998       GIInfoType type;
999
1000       info = g_irepository_find_by_gtype (0, G_VALUE_TYPE (gval));
1001       if (!info)
1002         return FALSE;
1003       type = g_base_info_get_type (info);
1004
1005       if (type == GI_INFO_TYPE_UNION)
1006         {
1007           return seed_make_union (ctx, g_value_peek_pointer (gval), info);
1008         }
1009       else if (type == GI_INFO_TYPE_STRUCT)
1010         {
1011           return seed_make_struct (ctx, g_value_peek_pointer (gval), info);
1012         }
1013       else if (type == GI_INFO_TYPE_BOXED)
1014         {
1015           return seed_make_boxed (ctx, g_value_dup_boxed (gval), info);
1016         }
1017
1018     }
1019
1020   return NULL;
1021 }
1022
1023 gboolean
1024 seed_gvalue_from_seed_value (JSContextRef ctx,
1025                              JSValueRef val,
1026                              GType type, GValue * ret, JSValueRef * exception)
1027 {
1028   if (G_IS_VALUE (ret))
1029     g_value_unset (ret);
1030
1031  if (type == G_TYPE_STRV)
1032     {
1033       gchar **result;
1034       JSValueRef jslen;
1035       guint length, i;
1036       
1037       if (JSValueIsNull (ctx, val) || !JSValueIsObject (ctx, val))
1038         return FALSE;
1039       
1040       jslen = seed_object_get_property (ctx, (JSObjectRef) val, "length");
1041       length = seed_value_to_uint (ctx, jslen, exception);
1042       
1043       result = g_new0 (gchar *, length+1);
1044       
1045       for (i = 0; i < length; i++)
1046         {
1047           result[i] = seed_value_to_string (ctx,
1048                                             JSObjectGetPropertyAtIndex (ctx,
1049                                                                         (JSObjectRef)
1050                                                                         val,
1051                                                                         i,
1052                                                                         exception),
1053                                             exception);
1054           
1055         }
1056       result[i] = 0;
1057       
1058       g_value_init (ret, G_TYPE_STRV);
1059       g_value_take_boxed (ret, result);
1060       
1061         return TRUE;
1062     }
1063   else if (g_type_is_a (type, G_TYPE_ENUM) && JSValueIsNumber (ctx, val))
1064     {
1065       g_value_init (ret, type);
1066       g_value_set_enum (ret, seed_value_to_long (ctx, val, exception));
1067       return TRUE;
1068     }
1069   else if (g_type_is_a (type, G_TYPE_FLAGS) && JSValueIsNumber (ctx, val))
1070     {
1071       g_value_init (ret, type);
1072       g_value_set_flags (ret, seed_value_to_long (ctx, val, exception));
1073       return TRUE;
1074     }
1075   else if (g_type_is_a (type, G_TYPE_OBJECT)
1076            && (JSValueIsNull (ctx, val) || seed_value_is_gobject (ctx, val)))
1077     {
1078       GObject *o = seed_value_to_object (ctx,
1079                                          val, exception);
1080
1081       if (o == NULL || g_type_is_a (G_OBJECT_TYPE (o), type))
1082         {
1083           g_value_init (ret, type);
1084           g_value_set_object (ret, o);
1085
1086           return TRUE;
1087         }
1088     }
1089   /* Boxed handling is broken. Will be fixed in struct overhall. */
1090   else if (g_type_is_a (type, G_TYPE_BOXED))
1091     {
1092       gpointer p = seed_pointer_get_pointer (ctx, val);
1093       if (p)
1094         {
1095           g_value_init (ret, type);
1096           g_value_set_boxed (ret, p);
1097           return TRUE;
1098         }
1099       else
1100         {
1101           if (JSValueIsObject (ctx, val))
1102             {
1103               GIBaseInfo *info = g_irepository_find_by_gtype (0, type);
1104               JSObjectRef new_struct;
1105               if (!info)
1106                 return FALSE;
1107
1108               new_struct =
1109                 seed_construct_struct_type_with_parameters (ctx,
1110                                                             info,
1111                                                             (JSObjectRef)
1112                                                             val, exception);
1113               p = seed_pointer_get_pointer (ctx, new_struct);
1114               if (p)
1115                 {
1116                   g_value_init (ret, type);
1117                   g_value_set_boxed (ret, p);
1118                   g_base_info_unref (info);
1119                   return TRUE;
1120                 }
1121               g_base_info_unref (info);
1122             }
1123         }
1124     }
1125
1126   switch (type)
1127     {
1128     case G_TYPE_BOOLEAN:
1129       {
1130         g_value_init (ret, G_TYPE_BOOLEAN);
1131         g_value_set_boolean (ret, seed_value_to_boolean (ctx,
1132                                                          val, exception));
1133         return TRUE;
1134       }
1135     case G_TYPE_INT:
1136     case G_TYPE_UINT:
1137       {
1138         g_value_init (ret, type);
1139         if (type == G_TYPE_INT)
1140           g_value_set_int (ret, seed_value_to_int (ctx, val, exception));
1141         else
1142           g_value_set_uint (ret, seed_value_to_uint (ctx, val, exception));
1143         return TRUE;
1144       }
1145     case G_TYPE_CHAR:
1146       {
1147         g_value_init (ret, G_TYPE_CHAR);
1148         g_value_set_char (ret, seed_value_to_char (ctx, val, exception));
1149         return TRUE;
1150       }
1151     case G_TYPE_UCHAR:
1152       {
1153         g_value_init (ret, G_TYPE_UCHAR);
1154         g_value_set_uchar (ret, seed_value_to_uchar (ctx, val, exception));
1155         return TRUE;
1156       }
1157     case G_TYPE_LONG:
1158     case G_TYPE_ULONG:
1159     case G_TYPE_INT64:
1160     case G_TYPE_UINT64:
1161     case G_TYPE_FLOAT:
1162     case G_TYPE_DOUBLE:
1163       {
1164         switch (type)
1165           {
1166           case G_TYPE_LONG:
1167             g_value_init (ret, G_TYPE_LONG);
1168             g_value_set_long (ret, seed_value_to_long (ctx, val, exception));
1169             break;
1170           case G_TYPE_ULONG:
1171             g_value_init (ret, G_TYPE_ULONG);
1172             g_value_set_ulong (ret, seed_value_to_ulong (ctx,
1173                                                          val, exception));
1174             break;
1175           case G_TYPE_INT64:
1176             g_value_init (ret, G_TYPE_INT64);
1177             g_value_set_int64 (ret, seed_value_to_int64 (ctx,
1178                                                          val, exception));
1179             break;
1180           case G_TYPE_UINT64:
1181             g_value_init (ret, G_TYPE_UINT64);
1182             g_value_set_uint64 (ret, seed_value_to_uint64 (ctx,
1183                                                            val, exception));
1184             break;
1185           case G_TYPE_FLOAT:
1186             g_value_init (ret, G_TYPE_FLOAT);
1187             g_value_set_float (ret, seed_value_to_float (ctx,
1188                                                          val, exception));
1189             break;
1190           case G_TYPE_DOUBLE:
1191             g_value_init (ret, G_TYPE_DOUBLE);
1192             g_value_set_double (ret, seed_value_to_double (ctx,
1193                                                            val, exception));
1194             break;
1195           }
1196         return TRUE;
1197       }
1198     case G_TYPE_STRING:
1199       {
1200         gchar *cval = seed_value_to_string (ctx, val, exception);
1201
1202         g_value_init (ret, G_TYPE_STRING);
1203         g_value_take_string (ret, cval);
1204
1205         return TRUE;
1206       }
1207     default:
1208       {
1209         // TODO: FIXME: This whole undefined type area
1210         // needs some heaaavy improvement.
1211
1212         // Support [GObject.TYPE_INT, 3]
1213         // TODO: FIXME: Might crash.
1214         if (type == 0 && JSValueIsObject (ctx, val))
1215           {
1216             // TODO: FIXME: Better array test like the cool one on reddit.
1217             guint length = seed_value_to_int (ctx,
1218                                               seed_object_get_property (ctx,
1219                                                                         (JSObjectRef) val,
1220                                                                         "length"),
1221                                               exception);
1222
1223             if (length)
1224               {
1225                 type =
1226                   seed_value_to_int (ctx,
1227                                      JSObjectGetPropertyAtIndex (ctx,
1228                                                                  (JSObjectRef)
1229                                                                  val, 0,
1230                                                                  exception),
1231                                      exception);
1232                 val =
1233                   JSObjectGetPropertyAtIndex (ctx, (JSObjectRef) val, 1,
1234                                               exception);
1235                 if (type)       // Prevents recursion.
1236                   {
1237                     return seed_gvalue_from_seed_value (ctx, val,
1238                                                         type, ret, exception);
1239                   }
1240                 // TODO: FIXME: Handle better?
1241                 else
1242                   g_assert_not_reached ();
1243               }
1244           }
1245         switch (JSValueGetType (ctx, val))
1246           {
1247           case kJSTypeBoolean:
1248             {
1249               g_value_init (ret, G_TYPE_BOOLEAN);
1250               g_value_set_boolean (ret,
1251                                    seed_value_to_boolean (ctx,
1252                                                           val, exception));
1253               return TRUE;
1254             }
1255           case kJSTypeNumber:
1256             {
1257               g_value_init (ret, G_TYPE_DOUBLE);
1258               g_value_set_double (ret,
1259                                   seed_value_to_double (ctx, val, exception));
1260               return TRUE;
1261             }
1262           case kJSTypeString:
1263             {
1264               gchar *cv = seed_value_to_string (ctx, val,
1265                                                 exception);
1266
1267               g_value_init (ret, G_TYPE_STRING);
1268               g_value_take_string (ret, cv);
1269               return TRUE;
1270             }
1271           default:
1272             break;
1273           }
1274         break;
1275       }
1276     }
1277
1278    return FALSE;
1279 }
1280
1281 /**
1282  * seed_object_get_property
1283  * @ctx: A #SeedContext
1284  * @object: A #SeedObject
1285  * @name: The property to get, should be a valid JavaScript identifier
1286  *
1287  * Returns: The value of the property or %NULL
1288  */
1289 JSValueRef
1290 seed_object_get_property (JSContextRef ctx,
1291                           JSObjectRef val, const gchar * name)
1292 {
1293
1294   JSStringRef jname = JSStringCreateWithUTF8CString (name);
1295   JSValueRef ret = JSObjectGetProperty (ctx,
1296                                         (JSObjectRef) val,
1297                                         jname, NULL);
1298
1299   JSStringRelease (jname);
1300
1301   return ret;
1302 }
1303
1304 /**
1305  * seed_object_set_property
1306  * @ctx: A #SeedContext
1307  * @object: A #SeedObject
1308  * @name: The property to set, should be a valid JavaScript identifier
1309  * @value: The value to set the property to.
1310  *
1311  * Returns: %TRUE on success, %FALSE otherwise.
1312  */
1313 gboolean
1314 seed_object_set_property (JSContextRef ctx, JSObjectRef object,
1315                           const gchar * name, JSValueRef value)
1316 {
1317   JSStringRef jname = JSStringCreateWithUTF8CString (name);
1318   JSValueRef exception = NULL;
1319
1320   if (value)
1321     {
1322       JSObjectSetProperty (ctx, (JSObjectRef) object, jname, value, 0,
1323                            &exception);
1324     }
1325
1326   JSStringRelease (jname);
1327
1328   return TRUE;
1329 }
1330
1331 /* TODO: Make some macros or something for making exceptions, code is littered
1332    with annoyingness right now */
1333
1334 /**
1335  * seed_value_to_boolean:
1336  * @ctx: A #SeedContext.
1337  * @val: The #SeedValue to convert.
1338  * @exception: A reference to a #SeedValue in which to store any exceptions.
1339  *             Pass %NULL to ignore exceptions.
1340  *
1341  * Converts the given #SeedValue into a #gboolean. Keep in mind that this will
1342  * not convert a JavaScript number type, only a boolean.
1343  *
1344  * Return value: The #gboolean represented by @val, or %NULL if an exception
1345  *               is raised during the conversion.
1346  *
1347  */
1348 gboolean
1349 seed_value_to_boolean (JSContextRef ctx,
1350                        JSValueRef val, JSValueRef * exception)
1351 {
1352   if (!JSValueIsBoolean (ctx, val) && !JSValueIsNumber (ctx, val))
1353     {
1354       if (!JSValueIsNull (ctx, val))
1355         {
1356           seed_make_exception (eng->context, exception, "ConversionError",
1357                                "Can not convert Javascript value to boolean");
1358           return FALSE;
1359         }
1360
1361       return FALSE;
1362     }
1363
1364   return JSValueToBoolean (ctx, val);
1365 }
1366
1367 /**
1368  * seed_value_from_boolean:
1369  * @ctx: A #SeedContext.
1370  * @val: The #gboolean to represent.
1371  * @exception: A reference to a #SeedValue in which to store any exceptions.
1372  *             Pass %NULL to ignore exceptions.
1373  *
1374  * Converts the given #gboolean into a #SeedValue.
1375  *
1376  * Return value: A #SeedValue which represents @val, or %NULL if an exception
1377  *               is raised during the conversion.
1378  *
1379  */
1380 JSValueRef
1381 seed_value_from_boolean (JSContextRef ctx,
1382                          gboolean val, JSValueRef * exception)
1383 {
1384   return JSValueMakeBoolean (ctx, val);
1385 }
1386
1387 /**
1388  * seed_value_to_uint:
1389  * @ctx: A #SeedContext.
1390  * @val: The #SeedValue to convert.
1391  * @exception: A reference to a #SeedValue in which to store any exceptions.
1392  *             Pass %NULL to ignore exceptions.
1393  *
1394  * Converts the given #SeedValue into a #guint.
1395  *
1396  * Return value: The #guint represented by @val, or %NULL if an exception
1397  *               is raised during the conversion.
1398  *
1399  */
1400 guint
1401 seed_value_to_uint (JSContextRef ctx, JSValueRef val, JSValueRef * exception)
1402 {
1403   if (!JSValueIsNumber (ctx, val) && !JSValueIsBoolean (ctx, val))
1404     {
1405       if (!JSValueIsNull (ctx, val))
1406         {
1407           seed_make_exception (ctx, exception, "ConversionError",
1408                                "Can not convert Javascript value to"
1409                                " boolean");
1410         }
1411       return 0;
1412     }
1413
1414   return (guint) JSValueToNumber (ctx, val, NULL);
1415 }
1416
1417 /**
1418  * seed_value_from_uint:
1419  * @ctx: A #SeedContext.
1420  * @val: The #guint to represent.
1421  * @exception: A reference to a #SeedValue in which to store any exceptions.
1422  *             Pass %NULL to ignore exceptions.
1423  *
1424  * Converts the given #guint into a #SeedValue.
1425  *
1426  * Return value: A #SeedValue which represents @val, or %NULL if an exception
1427  *               is raised during the conversion.
1428  *
1429  */
1430 JSValueRef
1431 seed_value_from_uint (JSContextRef ctx, guint val, JSValueRef * exception)
1432 {
1433   return JSValueMakeNumber (ctx, (gdouble) val);
1434 }
1435
1436 /**
1437  * seed_value_to_int:
1438  * @ctx: A #SeedContext.
1439  * @val: The #SeedValue to convert.
1440  * @exception: A reference to a #SeedValue in which to store any exceptions.
1441  *             Pass %NULL to ignore exceptions.
1442  *
1443  * Converts the given #SeedValue into a #gint.
1444  *
1445  * Return value: The #gint represented by @val, or %NULL if an exception
1446  *               is raised during the conversion.
1447  *
1448  */
1449 gint
1450 seed_value_to_int (JSContextRef ctx, JSValueRef val, JSValueRef * exception)
1451 {
1452   if (!JSValueIsNumber (ctx, val) && !JSValueIsBoolean (ctx, val))
1453     {
1454       if (!JSValueIsNull (ctx, val))
1455         seed_make_exception (ctx, exception, "ConversionError",
1456                              "Can not convert Javascript value to" " int");
1457       return 0;
1458     }
1459
1460   return (gint) JSValueToNumber (ctx, val, NULL);
1461 }
1462
1463 /**
1464  * seed_value_from_int:
1465  * @ctx: A #SeedContext.
1466  * @val: The #gint to represent.
1467  * @exception: A reference to a #SeedValue in which to store any exceptions.
1468  *             Pass %NULL to ignore exceptions.
1469  *
1470  * Converts the given #gint into a #SeedValue.
1471  *
1472  * Return value: A #SeedValue which represents @val, or %NULL if an exception
1473  *               is raised during the conversion.
1474  *
1475  */
1476 JSValueRef
1477 seed_value_from_int (JSContextRef ctx, gint val, JSValueRef * exception)
1478 {
1479   return JSValueMakeNumber (ctx, (gdouble) val);
1480 }
1481
1482 /**
1483  * seed_value_to_char:
1484  * @ctx: A #SeedContext.
1485  * @val: The #SeedValue to convert.
1486  * @exception: A reference to a #SeedValue in which to store any exceptions.
1487  *             Pass %NULL to ignore exceptions.
1488  *
1489  * Converts the given #SeedValue into a #gchar.
1490  *
1491  * Return value: The #gchar represented by @val, or %NULL if an exception
1492  *               is raised during the conversion.
1493  *
1494  */
1495 gchar
1496 seed_value_to_char (JSContextRef ctx, JSValueRef val, JSValueRef * exception)
1497 {
1498   gint cv;
1499
1500   if (!JSValueIsNumber (ctx, val))
1501     {
1502       if (!JSValueIsNull (ctx, val))
1503         seed_make_exception (ctx, exception, "ConversionError",
1504                              "Can not convert Javascript value to" " gchar");
1505       return 0;
1506     }
1507
1508   cv = JSValueToNumber (ctx, val, NULL);
1509
1510   if (cv < G_MININT8 || cv > G_MAXINT8)
1511     {
1512       seed_make_exception (ctx, exception, "ConversionError",
1513                            "Javascript number out of range of gchar");
1514       return 0;
1515     }
1516
1517   return (gchar) cv;
1518 }
1519
1520 /**
1521  * seed_value_from_char:
1522  * @ctx: A #SeedContext.
1523  * @val: The #gchar to represent.
1524  * @exception: A reference to a #SeedValue in which to store any exceptions.
1525  *             Pass %NULL to ignore exceptions.
1526  *
1527  * Converts the given #gchar into a #SeedValue.
1528  *
1529  * Return value: A #SeedValue which represents @val, or %NULL if an exception
1530  *               is raised during the conversion.
1531  *
1532  */
1533 JSValueRef
1534 seed_value_from_char (JSContextRef ctx, gchar val, JSValueRef * exception)
1535 {
1536   return JSValueMakeNumber (ctx, (gdouble) val);
1537 }
1538
1539 /**
1540  * seed_value_to_uchar:
1541  * @ctx: A #SeedContext.
1542  * @val: The #SeedValue to convert.
1543  * @exception: A reference to a #SeedValue in which to store any exceptions.
1544  *             Pass %NULL to ignore exceptions.
1545  *
1546  * Converts the given #SeedValue into a #guchar.
1547  *
1548  * Return value: The #guchar represented by @val, or %NULL if an exception
1549  *               is raised during the conversion.
1550  *
1551  */
1552 guchar
1553 seed_value_to_uchar (JSContextRef ctx, JSValueRef val, JSValueRef * exception)
1554 {
1555   guint cv;
1556
1557   if (!JSValueIsNumber (ctx, val))
1558     {
1559       if (!JSValueIsNull (ctx, val))
1560         seed_make_exception (ctx, exception, "ConversionError",
1561                              "Can not convert Javascript value to" " guchar");
1562       return 0;
1563     }
1564
1565   cv = JSValueToNumber (ctx, val, NULL);
1566
1567   if (cv > G_MAXUINT8)
1568     {
1569       seed_make_exception (ctx, exception, "ConversionError",
1570                            "Javascript number out of range of guchar");
1571       return 0;
1572     }
1573
1574   return (guchar) cv;
1575 }
1576
1577 /**
1578  * seed_value_from_uchar:
1579  * @ctx: A #SeedContext.
1580  * @val: The #guchar to represent.
1581  * @exception: A reference to a #SeedValue in which to store any exceptions.
1582  *             Pass %NULL to ignore exceptions.
1583  *
1584  * Converts the given #guchar into a #SeedValue.
1585  *
1586  * Return value: A #SeedValue which represents @val, or %NULL if an exception
1587  *               is raised during the conversion.
1588  *
1589  */
1590 JSValueRef
1591 seed_value_from_uchar (JSContextRef ctx, guchar val, JSValueRef * exception)
1592 {
1593   return JSValueMakeNumber (ctx, (gdouble) val);
1594 }
1595
1596 /**
1597  * seed_value_to_short:
1598  * @ctx: A #SeedContext.
1599  * @val: The #SeedValue to convert.
1600  * @exception: A reference to a #SeedValue in which to store any exceptions.
1601  *             Pass %NULL to ignore exceptions.
1602  *
1603  * Converts the given #SeedValue into a #gshort.
1604  *
1605  * Return value: The #gshort represented by @val, or %NULL if an exception
1606  *               is raised during the conversion.
1607  *
1608  */
1609 gshort
1610 seed_value_to_short (JSContextRef ctx, JSValueRef val, JSValueRef * exception)
1611 {
1612   if (!JSValueIsNumber (ctx, val) && !JSValueIsBoolean (ctx, val))
1613     {
1614       if (!JSValueIsNull (ctx, val))
1615         seed_make_exception (ctx, exception, "ConversionError",
1616                              "Can not convert Javascript value to" " short");
1617       return 0;
1618     }
1619
1620   return (gshort) JSValueToNumber (ctx, val, NULL);
1621 }
1622
1623 /**
1624  * seed_value_from_short:
1625  * @ctx: A #SeedContext.
1626  * @val: The #gshort to represent.
1627  * @exception: A reference to a #SeedValue in which to store any exceptions.
1628  *             Pass %NULL to ignore exceptions.
1629  *
1630  * Converts the given #gshort into a #SeedValue.
1631  *
1632  * Return value: A #SeedValue which represents @val, or %NULL if an exception
1633  *               is raised during the conversion.
1634  *
1635  */
1636 JSValueRef
1637 seed_value_from_short (JSContextRef ctx, gshort val, JSValueRef * exception)
1638 {
1639   return JSValueMakeNumber (ctx, (gdouble) val);
1640 }
1641
1642 /**
1643  * seed_value_to_ushort:
1644  * @ctx: A #SeedContext.
1645  * @val: The #SeedValue to convert.
1646  * @exception: A reference to a #SeedValue in which to store any exceptions.
1647  *             Pass %NULL to ignore exceptions.
1648  *
1649  * Converts the given #SeedValue into a #gushort.
1650  *
1651  * Return value: The #gushort represented by @val, or %NULL if an exception
1652  *               is raised during the conversion.
1653  *
1654  */
1655 gushort
1656 seed_value_to_ushort (JSContextRef ctx, JSValueRef val,
1657                       JSValueRef * exception)
1658 {
1659   if (!JSValueIsNumber (ctx, val) && !JSValueIsBoolean (ctx, val))
1660     {
1661       if (!JSValueIsNull (ctx, val))
1662         seed_make_exception (ctx, exception, "ConversionError",
1663                              "Can not convert Javascript value to" " ushort");
1664       return 0;
1665     }
1666
1667   return (gushort) JSValueToNumber (ctx, val, NULL);
1668 }
1669
1670 /**
1671  * seed_value_from_ushort:
1672  * @ctx: A #SeedContext.
1673  * @val: The #gushort to represent.
1674  * @exception: A reference to a #SeedValue in which to store any exceptions.
1675  *             Pass %NULL to ignore exceptions.
1676  *
1677  * Converts the given #gushort into a #SeedValue.
1678  *
1679  * Return value: A #SeedValue which represents @val, or %NULL if an exception
1680  *               is raised during the conversion.
1681  *
1682  */
1683 JSValueRef
1684 seed_value_from_ushort (JSContextRef ctx, gushort val, JSValueRef * exception)
1685 {
1686   return JSValueMakeNumber (ctx, (gdouble) val);
1687 }
1688
1689 /**
1690  * seed_value_to_long:
1691  * @ctx: A #SeedContext.
1692  * @val: The #SeedValue to convert.
1693  * @exception: A reference to a #SeedValue in which to store any exceptions.
1694  *             Pass %NULL to ignore exceptions.
1695  *
1696  * Converts the given #SeedValue into a #glong.
1697  *
1698  * Return value: The #glong represented by @val, or %NULL if an exception
1699  *               is raised during the conversion.
1700  *
1701  */
1702 glong
1703 seed_value_to_long (JSContextRef ctx, JSValueRef val, JSValueRef * exception)
1704 {
1705   if (!JSValueIsNumber (ctx, val) && !JSValueIsBoolean (ctx, val))
1706     {
1707       if (!JSValueIsNull (ctx, val))
1708         seed_make_exception (ctx, exception, "ConversionError",
1709                              "Can not convert Javascript value to" " long");
1710       return 0;
1711     }
1712
1713   return (glong) JSValueToNumber (ctx, val, NULL);
1714 }
1715
1716  /**
1717  * seed_value_from_long:
1718  * @ctx: A #SeedContext.
1719  * @val: The #glong to represent.
1720  * @exception: A reference to a #SeedValue in which to store any exceptions.
1721  *             Pass %NULL to ignore exceptions.
1722  *
1723  * Converts the given #glong into a #SeedValue.
1724  *
1725  * Return value: A #SeedValue which represents @val, or %NULL if an exception
1726  *               is raised during the conversion.
1727  *
1728  */
1729 JSValueRef
1730 seed_value_from_long (JSContextRef ctx, glong val, JSValueRef * exception)
1731 {
1732   return JSValueMakeNumber (ctx, (gdouble) val);
1733 }
1734
1735 /**
1736  * seed_value_to_ulong:
1737  * @ctx: A #SeedContext.
1738  * @val: The #SeedValue to convert.
1739  * @exception: A reference to a #SeedValue in which to store any exceptions.
1740  *             Pass %NULL to ignore exceptions.
1741  *
1742  * Converts the given #SeedValue into a #gulong.
1743  *
1744  * Return value: The #gulong represented by @val, or %NULL if an exception
1745  *               is raised during the conversion.
1746  *
1747  */
1748 gulong
1749 seed_value_to_ulong (JSContextRef ctx, JSValueRef val, JSValueRef * exception)
1750 {
1751   if (!JSValueIsNumber (ctx, val))
1752     {
1753       if (!JSValueIsNull (ctx, val))
1754         seed_make_exception (ctx, exception, "ConversionError",
1755                              "Can not convert Javascript value to" " ulong");
1756
1757       return 0;
1758     }
1759
1760   return (gulong) JSValueToNumber (ctx, val, NULL);
1761 }
1762
1763 /**
1764  * seed_value_from_ulong:
1765  * @ctx: A #SeedContext.
1766  * @val: The #gulong to represent.
1767  * @exception: A reference to a #SeedValue in which to store any exceptions.
1768  *             Pass %NULL to ignore exceptions.
1769  *
1770  * Converts the given #gulong into a #SeedValue.
1771  *
1772  * Return value: A #SeedValue which represents @val, or %NULL if an exception
1773  *               is raised during the conversion.
1774  *
1775  */
1776 JSValueRef
1777 seed_value_from_ulong (JSContextRef ctx, gulong val, JSValueRef * exception)
1778 {
1779   return JSValueMakeNumber (ctx, (gdouble) val);
1780 }
1781
1782 /**
1783  * seed_value_to_int64:
1784  * @ctx: A #SeedContext.
1785  * @val: The #SeedValue to convert.
1786  * @exception: A reference to a #SeedValue in which to store any exceptions.
1787  *             Pass %NULL to ignore exceptions.
1788  *
1789  * Converts the given #SeedValue into a #gint64.
1790  *
1791  * Return value: The #gint64 represented by @val, or %NULL if an exception
1792  *               is raised during the conversion.
1793  *
1794  */
1795 gint64
1796 seed_value_to_int64 (JSContextRef ctx, JSValueRef val, JSValueRef * exception)
1797 {
1798   if (!JSValueIsNumber (ctx, val) && !JSValueIsBoolean (ctx, val))
1799     {
1800       if (!JSValueIsNull (ctx, val))
1801         seed_make_exception (ctx, exception, "ConversionError",
1802                              "Can not convert Javascript value to" " gint64");
1803
1804       return 0;
1805     }
1806
1807   return (gint64) JSValueToNumber (ctx, val, NULL);
1808 }
1809
1810 /**
1811  * seed_value_from_int64:
1812  * @ctx: A #SeedContext.
1813  * @val: The #gint64 to represent.
1814  * @exception: A reference to a #SeedValue in which to store any exceptions.
1815  *             Pass %NULL to ignore exceptions.
1816  *
1817  * Converts the given #gint64 into a #SeedValue.
1818  *
1819  * Return value: A #SeedValue which represents @val, or %NULL if an exception
1820  *               is raised during the conversion.
1821  *
1822  */
1823 JSValueRef
1824 seed_value_from_int64 (JSContextRef ctx, gint64 val, JSValueRef * exception)
1825 {
1826   return JSValueMakeNumber (ctx, (gdouble) val);
1827 }
1828
1829 /**
1830  * seed_value_to_uint64:
1831  * @ctx: A #SeedContext.
1832  * @val: The #SeedValue to convert.
1833  * @exception: A reference to a #SeedValue in which to store any exceptions.
1834  *             Pass %NULL to ignore exceptions.
1835  *
1836  * Converts the given #SeedValue into a #guint64.
1837  *
1838  * Return value: The #guint64 represented by @val, or %NULL if an exception
1839  *               is raised during the conversion.
1840  *
1841  */
1842 guint64
1843 seed_value_to_uint64 (JSContextRef ctx,
1844                       JSValueRef val, JSValueRef * exception)
1845 {
1846   if (!JSValueIsNumber (ctx, val) && !JSValueIsBoolean (ctx, val))
1847     {
1848       if (!JSValueIsNull (ctx, val))
1849         seed_make_exception (ctx, exception, "ConversionError",
1850                              "Can not convert Javascript value to"
1851                              " guint64");
1852
1853       return 0;
1854     }
1855
1856   return (guint64) JSValueToNumber (ctx, val, NULL);
1857 }
1858
1859 /**
1860  * seed_value_from_uint64:
1861  * @ctx: A #SeedContext.
1862  * @val: The #guint64 to represent.
1863  * @exception: A reference to a #SeedValue in which to store any exceptions.
1864  *             Pass %NULL to ignore exceptions.
1865  *
1866  * Converts the given #guint64 into a #SeedValue.
1867  *
1868  * Return value: A #SeedValue which represents @val, or %NULL if an exception
1869  *               is raised during the conversion.
1870  *
1871  */
1872 JSValueRef
1873 seed_value_from_uint64 (JSContextRef ctx, guint64 val, JSValueRef * exception)
1874 {
1875   return JSValueMakeNumber (ctx, (gdouble) val);
1876 }
1877
1878 /**
1879  * seed_value_to_float:
1880  * @ctx: A #SeedContext.
1881  * @val: The #SeedValue to convert.
1882  * @exception: A reference to a #SeedValue in which to store any exceptions.
1883  *             Pass %NULL to ignore exceptions.
1884  *
1885  * Converts the given #SeedValue into a #gfloat.
1886  *
1887  * Return value: The #gfloat represented by @val, or %NULL if an exception
1888  *               is raised during the conversion.
1889  *
1890  */
1891 gfloat
1892 seed_value_to_float (JSContextRef ctx, JSValueRef val, JSValueRef * exception)
1893 {
1894   if (!JSValueIsNumber (ctx, val))
1895     {
1896       if (!JSValueIsNull (ctx, val))
1897         seed_make_exception (ctx, exception, "ConversionError",
1898                              "Can not convert Javascript value to" " gfloat");
1899       return 0;
1900     }
1901
1902   return (gfloat) JSValueToNumber (ctx, val, NULL);
1903 }
1904
1905 /**
1906  * seed_value_from_float:
1907  * @ctx: A #SeedContext.
1908  * @val: The #gfloat to represent.
1909  * @exception: A reference to a #SeedValue in which to store any exceptions.
1910  *             Pass %NULL to ignore exceptions.
1911  *
1912  * Converts the given #gfloat into a #SeedValue.
1913  *
1914  * Return value: A #SeedValue which represents @val, or %NULL if an exception
1915  *               is raised during the conversion.
1916  *
1917  */
1918 JSValueRef
1919 seed_value_from_float (JSContextRef ctx, gfloat val, JSValueRef * exception)
1920 {
1921   return JSValueMakeNumber (ctx, (gdouble) val);
1922 }
1923
1924 /**
1925  * seed_value_to_double:
1926  * @ctx: A #SeedContext.
1927  * @val: The #SeedValue to convert.
1928  * @exception: A reference to a #SeedValue in which to store any exceptions.
1929  *             Pass %NULL to ignore exceptions.
1930  *
1931  * Converts the given #SeedValue into a #gdouble.
1932  *
1933  * Return value: The #gdouble represented by @val, or %NULL if an exception
1934  *               is raised during the conversion.
1935  *
1936  */
1937 gdouble
1938 seed_value_to_double (JSContextRef ctx,
1939                       JSValueRef val, JSValueRef * exception)
1940 {
1941   if (!JSValueIsNumber (ctx, val))
1942     {
1943       if (!JSValueIsNull (ctx, val))
1944         seed_make_exception (ctx, exception, "ConversionError",
1945                              "Can not convert Javascript value to" " double");
1946       return 0;
1947     }
1948
1949   return (gdouble) JSValueToNumber (ctx, val, NULL);
1950 }
1951
1952 /**
1953  * seed_value_from_double:
1954  * @ctx: A #SeedContext.
1955  * @val: The #gdouble to represent.
1956  * @exception: A reference to a #SeedValue in which to store any exceptions.
1957  *             Pass %NULL to ignore exceptions.
1958  *
1959  * Converts the given #gdouble into a #SeedValue.
1960  *
1961  * Return value: A #SeedValue which represents @val, or %NULL if an exception
1962  *               is raised during the conversion.
1963  *
1964  */
1965 JSValueRef
1966 seed_value_from_double (JSContextRef ctx, gdouble val, JSValueRef * exception)
1967 {
1968   return JSValueMakeNumber (ctx, (gdouble) val);
1969 }
1970
1971 /**
1972  * seed_value_to_size:
1973  * @ctx: A #SeedContext.
1974  * @val: The #SeedValue to convert.
1975  * @exception: A reference to a #SeedValue in which to store any exceptions.
1976  *             Pass %NULL to ignore exceptions.
1977  *
1978  * Converts the given #SeedValue into a #gsize.
1979  *
1980  * Return value: The #gsize represented by @val, or %NULL if an exception
1981  *               is raised during the conversion.
1982  *
1983  */
1984 gsize
1985 seed_value_to_size (JSContextRef ctx, JSValueRef val, JSValueRef * exception)
1986 {
1987   if (!JSValueIsNumber (ctx, val) && !JSValueIsBoolean (ctx, val))
1988     {
1989       if (!JSValueIsNull (ctx, val))
1990         seed_make_exception (ctx, exception, "ConversionError",
1991                              "Can not convert Javascript value to" " gsize");
1992       return 0;
1993     }
1994
1995   return (gsize) JSValueToNumber (ctx, val, NULL);
1996 }
1997
1998 /**
1999  * seed_value_from_size:
2000  * @ctx: A #SeedContext.
2001  * @val: The #gsize to represent.
2002  * @exception: A reference to a #SeedValue in which to store any exceptions.
2003  *             Pass %NULL to ignore exceptions.
2004  *
2005  * Converts the given #gsize into a #SeedValue.
2006  *
2007  * Return value: A #SeedValue which represents @val, or %NULL if an exception
2008  *               is raised during the conversion.
2009  *
2010  */
2011 JSValueRef
2012 seed_value_from_size (JSContextRef ctx, gsize val, JSValueRef * exception)
2013 {
2014   return JSValueMakeNumber (ctx, (gdouble) val);
2015 }
2016
2017 /**
2018  * seed_value_to_ssize:
2019  * @ctx: A #SeedContext.
2020  * @val: The #SeedValue to convert.
2021  * @exception: A reference to a #SeedValue in which to store any exceptions.
2022  *             Pass %NULL to ignore exceptions.
2023  *
2024  * Converts the given #SeedValue into a #gssize.
2025  *
2026  * Return value: The #gssize represented by @val, or %NULL if an exception
2027  *               is raised during the conversion.
2028  *
2029  */
2030 gssize
2031 seed_value_to_ssize (JSContextRef ctx, JSValueRef val, JSValueRef * exception)
2032 {
2033   if (!JSValueIsNumber (ctx, val) && !JSValueIsBoolean (ctx, val))
2034     {
2035       if (!JSValueIsNull (ctx, val))
2036         seed_make_exception (ctx, exception, "ConversionError",
2037                              "Can not convert Javascript value to" " gssize");
2038       return 0;
2039     }
2040
2041   return (gssize) JSValueToNumber (ctx, val, NULL);
2042 }
2043
2044 /**
2045  * seed_value_from_ssize:
2046  * @ctx: A #SeedContext.
2047  * @val: The #gssize to represent.
2048  * @exception: A reference to a #SeedValue in which to store any exceptions.
2049  *             Pass %NULL to ignore exceptions.
2050  *
2051  * Converts the given #gssize into a #SeedValue.
2052  *
2053  * Return value: A #SeedValue which represents @val, or %NULL if an exception
2054  *               is raised during the conversion.
2055  *
2056  */
2057 JSValueRef
2058 seed_value_from_ssize (JSContextRef ctx, gssize val, JSValueRef * exception)
2059 {
2060   return JSValueMakeNumber (ctx, (gdouble) val);
2061 }
2062
2063 /**
2064  * seed_value_to_string:
2065  * @ctx: A #SeedContext.
2066  * @val: The #SeedValue to convert.
2067  * @exception: A reference to a #SeedValue in which to store any exceptions.
2068  *             Pass %NULL to ignore exceptions.
2069  *
2070  * Converts the given #SeedValue into a #gchar* string. Keep in mind that it's
2071  * up to the caller to free the string.
2072  *
2073  * If the #SeedValue represents JavaScript's undefined value, this returns
2074  * "[undefined]"; if it represents JavaScript's null value, this returns
2075  * "[null]".
2076  *
2077  * If the #SeedValue is a number or a boolean, it is printed as a double, with 
2078  * the printf format string "%.15g".
2079  *
2080  * If the #SeedValue is an object, the string returned is that obtained by
2081  * calling .toString() on said object.
2082  *
2083  * Return value: The #gchar* represented by @val, or %NULL if an exception
2084  *               is raised during the conversion.
2085  *
2086  */
2087 gchar *
2088 seed_value_to_string (JSContextRef ctx,
2089                       JSValueRef val, JSValueRef * exception)
2090 {
2091   JSStringRef jsstr = NULL;
2092   JSValueRef func, str;
2093   gchar *buf = NULL;
2094   gint length;
2095
2096   if (val == NULL)
2097     return NULL;
2098   else if (JSValueIsUndefined (ctx, val))
2099     {
2100       buf = g_strdup ("[undefined]");
2101     }
2102   else if (JSValueIsNull (ctx, val))
2103     {
2104       buf = g_strdup ("[null]");
2105     }
2106   else if (JSValueIsBoolean (ctx, val) || JSValueIsNumber (ctx, val))
2107     {
2108       buf = g_strdup_printf ("%.15g", JSValueToNumber (ctx, val, NULL));
2109     }
2110   else
2111     {
2112       if (!JSValueIsString (ctx, val))  // In this case,
2113         // it's an object
2114         {
2115           func =
2116             seed_object_get_property (ctx, (JSObjectRef) val, "toString");
2117           if (!JSValueIsNull (ctx, func) &&
2118               JSValueIsObject (ctx, func) &&
2119               JSObjectIsFunction (ctx, (JSObjectRef) func))
2120             str =
2121               JSObjectCallAsFunction (ctx, (JSObjectRef) func,
2122                                       (JSObjectRef) val, 0, NULL, NULL);
2123         }
2124       
2125       jsstr = JSValueToStringCopy (ctx, val, NULL);
2126       length = JSStringGetMaximumUTF8CStringSize (jsstr);
2127       if (length > 0)
2128         {
2129           buf = g_malloc (length * sizeof (gchar));
2130           JSStringGetUTF8CString (jsstr, buf, length);
2131         }
2132       if (jsstr)
2133         JSStringRelease (jsstr);
2134     }
2135
2136   return buf;
2137 }
2138
2139 /**
2140  * seed_value_from_string:
2141  * @ctx: A #SeedContext.
2142  * @val: The #gchar* to represent.
2143  * @exception: A reference to a #SeedValue in which to store any exceptions.
2144  *             Pass %NULL to ignore exceptions.
2145  *
2146  * Converts the given #gchar* string into a #SeedValue.
2147  *
2148  * Return value: A #SeedValue which represents @val, or %NULL if an exception
2149  *               is raised during the conversion.
2150  *
2151  */
2152 JSValueRef
2153 seed_value_from_string (JSContextRef ctx,
2154                         const gchar * val, JSValueRef * exception)
2155 {
2156   if (val == NULL)
2157     return JSValueMakeNull (ctx);
2158   else
2159     {
2160       JSStringRef jsstr = JSStringCreateWithUTF8CString (val);
2161       JSValueRef valstr = JSValueMakeString (ctx, jsstr);
2162       JSStringRelease (jsstr);
2163
2164       return valstr;
2165     }
2166 }
2167
2168 /**
2169  * seed_value_from_binary_string:
2170  * @ctx: A #SeedContext.
2171  * @bytes: A string of bytes to represent as a string.
2172  * @n_bytes: The number of bytes from @bytes to convert.
2173  * @exception: A reference to a #SeedValue in which to store any exceptions.
2174  *             Pass %NULL to ignore exceptions.
2175  *
2176  * Converts a string representation of the given binary string
2177  * into a #SeedValue.
2178  *
2179  * Return value: A #SeedValue which represents @bytes as a string, or %NULL
2180  *               if an exception is raised during the conversion.
2181  *
2182  */
2183 JSValueRef
2184 seed_value_from_binary_string (JSContextRef ctx,
2185                                const gchar * bytes,
2186                                gint n_bytes, JSValueRef * exception)
2187 {
2188   JSValueRef ret;
2189
2190   gchar *nstr = g_alloca ((n_bytes + 1) * sizeof (gchar));
2191   g_strlcpy (nstr, bytes, n_bytes);
2192   nstr[n_bytes] = '\0';
2193
2194   ret = seed_value_from_string (ctx, nstr, exception);
2195
2196   return ret;
2197 }
2198
2199 /**
2200  * seed_value_to_filename:
2201  * @ctx: A #SeedContext.
2202  * @val: The #SeedValue to convert.
2203  * @exception: A reference to a #SeedValue in which to store any exceptions.
2204  *             Pass %NULL to ignore exceptions.
2205  *
2206  * Converts the given #SeedValue into a #gchar*, properly converting to the 
2207  * character set used for filenames on the local machine.
2208  *
2209  * Return value: The #gchar* represented by @val, or %NULL if an exception
2210  *               is raised during the conversion.
2211  *
2212  */
2213 gchar *
2214 seed_value_to_filename (JSContextRef ctx,
2215                         JSValueRef val, JSValueRef * exception)
2216 {
2217   GError *e = NULL;
2218   gchar *utf8 = seed_value_to_string (ctx, val, exception);
2219   gchar *filename;
2220
2221   filename = g_filename_from_utf8 (utf8, -1, NULL, NULL, &e);
2222   g_free (utf8);
2223   if (e)
2224     {
2225       seed_make_exception_from_gerror (ctx, exception, e);
2226       g_error_free (e);
2227       return NULL;
2228     }
2229
2230   return filename;
2231 }
2232
2233 /**
2234  * seed_value_from_filename:
2235  * @ctx: A #SeedContext.
2236  * @val: The #gchar* filename to represent.
2237  * @exception: A reference to a #SeedValue in which to store any exceptions.
2238  *             Pass %NULL to ignore exceptions.
2239  *
2240  * Converts the given #gchar* filename into a #SeedValue, respecting the 
2241  * character set used for filenames on the local machine.
2242  *
2243  * Return value: A #SeedValue which represents @val, or %NULL if an exception
2244  *               is raised during the conversion.
2245  *
2246  */
2247 JSValueRef
2248 seed_value_from_filename (JSContextRef ctx,
2249                           const gchar * val, JSValueRef * exception)
2250 {
2251   GError *e = NULL;
2252   gchar *utf8;
2253   
2254   if (val == NULL)
2255     return JSValueMakeNull (ctx);
2256   else
2257     {
2258       utf8 = g_filename_to_utf8 (val, -1, NULL, NULL, &e);
2259
2260       if (e)
2261         {
2262           seed_make_exception_from_gerror (ctx, exception, e);
2263           g_error_free (e);
2264           return JSValueMakeNull (ctx);
2265         }
2266
2267       JSValueRef valstr = seed_value_from_string (ctx, utf8, exception);
2268
2269       g_free (utf8);
2270
2271       return valstr;
2272     }
2273 }
2274
2275 /**
2276  * seed_value_to_object:
2277  * @ctx: A #SeedContext.
2278  * @val: The #SeedValue to unwrap.
2279  * @exception: A reference to a #SeedValue in which to store any exceptions.
2280  *             Pass %NULL to ignore exceptions.
2281  *
2282  * Given a #SeedValue which is wrapping a #GObject, retrieve the wrapped
2283  * #GObject.
2284  *
2285  * Return value: The #GObject wrapped within @val, or %NULL if an exception
2286  *               is raised during the conversion.
2287  *
2288  */
2289 GObject *
2290 seed_value_to_object (JSContextRef ctx,
2291                       JSValueRef val, JSValueRef * exception)
2292 {
2293   GObject *gobject;
2294
2295   /*
2296    * Worth investigating if this is the best way to handle null. Some of
2297    * the existing code depends on null Objects not throwing an exception
2298    * however, needs testing at higher level if value can be null
2299    * (through GI)
2300    */
2301
2302   if (JSValueIsNull (ctx, val))
2303     return NULL;
2304   if (!seed_value_is_gobject (ctx, val))
2305     {
2306       seed_make_exception (ctx, exception, "ConversionError",
2307                            "Attempt to convert from"
2308                            " non GObject to GObject");
2309       return NULL;
2310     }
2311
2312   gobject = (GObject *) JSObjectGetPrivate ((JSObjectRef) val);
2313   g_assert (G_IS_OBJECT (gobject));
2314
2315   return gobject;
2316 }
2317
2318 /**
2319  * seed_value_from_object:
2320  * @ctx: A #SeedContext.
2321  * @val: The #GObject to wrap.
2322  * @exception: A reference to a #SeedValue in which to store any exceptions.
2323  *             Pass %NULL to ignore exceptions.
2324  *
2325  * Wraps @val in a #SeedValue.
2326  *
2327  * Return value: A #SeedValue which wraps @val, or %NULL if an exception
2328  *               is raised during the conversion.
2329  *
2330  */
2331 JSValueRef
2332 seed_value_from_object (JSContextRef ctx,
2333                         GObject * val, JSValueRef * exception)
2334 {
2335   if (val == NULL)
2336     return JSValueMakeNull (ctx);
2337   else
2338     return seed_wrap_object (ctx, val);
2339 }
2340
2341 gboolean
2342 seed_validate_enum (GIEnumInfo * info, long val)
2343 {
2344   gint n, i;
2345   GIValueInfo *value_info;
2346   gint value; // TODO: investigate glong/gint mismatch w/ g_value_info_get_value
2347
2348   n = g_enum_info_get_n_values (info);
2349   for (i = 0; i < n; i++)
2350     {
2351       value_info = g_enum_info_get_value (info, i);
2352       value = g_value_info_get_value (value_info);
2353
2354       g_base_info_unref ((GIBaseInfo *) value_info);
2355       if (value == val)
2356         return TRUE;
2357     }
2358
2359   return FALSE;
2360 }
2361
2362 JSValueRef
2363 seed_value_from_time_t (JSContextRef ctx, time_t time, JSValueRef * exception)
2364 {
2365   JSValueRef args[1];
2366
2367   args[0] = seed_value_from_double (ctx, ((gdouble) time) * 1000, exception);
2368   return JSObjectMakeDate (ctx, 1, args, exception);
2369 }
2370
2371 time_t
2372 seed_value_to_time_t (JSContextRef ctx,
2373                       JSValueRef value, JSValueRef * exception)
2374 {
2375   JSValueRef get_time_method;
2376   JSValueRef jstime;
2377   gdouble time;
2378
2379
2380   if (JSValueIsNumber (ctx, value))
2381     {
2382       return (unsigned long) seed_value_to_long (ctx, value, exception);
2383     }
2384   else if (JSValueIsObject (ctx, value))
2385     {
2386       get_time_method = seed_object_get_property (ctx, (JSObjectRef) value,
2387                                                   "getTime");
2388       if (JSValueIsNull (ctx, get_time_method) ||
2389           !JSValueIsObject (ctx, get_time_method))
2390         {
2391           goto out;
2392         }
2393       jstime = JSObjectCallAsFunction (ctx,
2394                                        (JSObjectRef) get_time_method,
2395                                        (JSObjectRef) value,
2396                                        0, NULL, exception);
2397       time = seed_value_to_double (ctx, jstime, exception);
2398       return (unsigned long) (time / 1000);
2399     }
2400
2401 out:
2402   seed_make_exception (ctx, exception,
2403                        "TypeError",
2404                        "Unable to convert JavaScript value to time_t");
2405   return 0;
2406 }