5821ad00c7cddad6d625c408dc604f9509544f5f
[gnome.seed] / libseed / seed-builtins.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 <unistd.h>
21 #include "seed-private.h"
22 #include <sys/mman.h>
23 #include <stdio.h>
24 #include <signal.h>
25
26 JSValueRef seed_print_ref;
27
28 static JSValueRef
29 seed_include (JSContextRef ctx,
30               JSObjectRef function,
31               JSObjectRef this_object,
32               size_t argumentCount,
33               const JSValueRef arguments[], JSValueRef * exception)
34 {
35   JSStringRef file_contents, file_name;
36
37   GDir *dir;
38   gchar *import_file, *abs_path;
39   gchar *buffer, *walk;
40   guint i, len;
41
42   if (argumentCount != 1)
43     {
44       seed_make_exception (ctx, exception, "ArgumentError",
45                            "Seed.include expected 1 argument, "
46                            "got %zd", argumentCount);
47       return JSValueMakeNull (ctx);
48     }
49
50   import_file = seed_value_to_string (ctx, arguments[0], exception);
51
52   /* just try current dir if no path set, or use the absolute path */
53   if (!eng->search_path || g_path_is_absolute (import_file))
54     g_file_get_contents (import_file, &buffer, 0, NULL);
55   else                          /* A search path is set and path given is not absolute.  */
56     {
57       len = g_strv_length (eng->search_path);
58       for (i = 0; i < len; ++i)
59         {
60           dir = g_dir_open (eng->search_path[i], 0, NULL);
61
62           if (!dir)             /* skip bad path entries */
63             continue;
64
65           abs_path =
66             g_build_filename (eng->search_path[i], import_file, NULL);
67
68           if (g_file_get_contents (abs_path, &buffer, 0, NULL))
69             {
70               g_free (abs_path);
71               g_dir_close (dir);
72               break;
73             }
74
75           g_dir_close (dir);
76           g_free (abs_path);
77         }
78     }
79
80   if (!buffer)
81     {
82       seed_make_exception (ctx, exception, "FileNotFound",
83                            "File not found: %s", import_file);
84
85       g_free (import_file);
86       g_free (buffer);
87       return JSValueMakeNull (ctx);
88     }
89
90   walk = buffer;
91
92   if (*walk == '#')
93     {
94       while (*walk != '\n')
95         walk++;
96       walk++;
97     }
98
99   walk = g_strdup (walk);
100   g_free (buffer);
101
102   file_contents = JSStringCreateWithUTF8CString (walk);
103   file_name = JSStringCreateWithUTF8CString (import_file);
104
105   JSEvaluateScript (ctx, file_contents, NULL, file_name, 0, exception);
106
107   JSStringRelease (file_contents);
108   JSStringRelease (file_name);
109   g_free (import_file);
110   g_free (walk);
111
112   return JSValueMakeUndefined (ctx);
113 }
114
115 static JSValueRef
116 seed_scoped_include (JSContextRef ctx,
117                      JSObjectRef function,
118                      JSObjectRef this_object,
119                      size_t argumentCount,
120                      const JSValueRef arguments[], JSValueRef * exception)
121 {
122   JSContextRef nctx;
123   JSObjectRef global;
124   JSStringRef file_contents, file_name;
125   GDir *dir;
126   gchar *import_file, *abs_path;
127   gchar *buffer, *walk;
128   guint i;
129
130   if (argumentCount != 1)
131     {
132       seed_make_exception (ctx, exception, "ArgumentError",
133                            "Seed.include expected 1 argument, "
134                            "got %zd", argumentCount);
135       return JSValueMakeNull (ctx);
136     }
137
138   import_file = seed_value_to_string (ctx, arguments[0], exception);
139
140   /* just try current dir if no path set, or use the absolute path */
141   if (!eng->search_path || g_path_is_absolute (import_file))
142     g_file_get_contents (import_file, &buffer, 0, NULL);
143   else                          /* A search path is set and path given is not absolute.  */
144     {
145       for (i = 0; i < g_strv_length (eng->search_path); ++i)
146         {
147           dir = g_dir_open (eng->search_path[i], 0, NULL);
148
149           if (!dir)             /* skip bad path entries */
150             continue;
151
152           abs_path =
153             g_build_filename (eng->search_path[i], import_file, NULL);
154
155           if (g_file_get_contents (abs_path, &buffer, 0, NULL))
156             {
157               g_free (abs_path);
158               break;
159             }
160
161           g_dir_close (dir);
162           g_free (abs_path);
163         }
164     }
165
166   if (!buffer)
167     {
168       seed_make_exception (ctx, exception, "FileNotFound",
169                            "File not found: %s", import_file);
170
171       g_free (import_file);
172       g_free (buffer);
173       return JSValueMakeNull (ctx);
174     }
175
176   walk = buffer;
177
178   if (*walk == '#')
179     {
180       while (*walk != '\n')
181         walk++;
182       walk++;
183     }
184
185   walk = g_strdup (walk);
186   g_free (buffer);
187
188   file_contents = JSStringCreateWithUTF8CString (walk);
189   file_name = JSStringCreateWithUTF8CString (import_file);
190
191
192   nctx = JSGlobalContextCreateInGroup (context_group, 0);
193   seed_prepare_global_context (nctx);
194
195   JSEvaluateScript (nctx, file_contents, NULL, file_name, 0, exception);
196
197   global = JSContextGetGlobalObject (nctx);
198
199   JSGlobalContextRelease ((JSGlobalContextRef) nctx);
200
201   JSStringRelease (file_contents);
202   JSStringRelease (file_name);
203   g_free (import_file);
204   g_free (walk);
205
206   return global;
207 }
208
209 static JSValueRef
210 seed_print (JSContextRef ctx,
211             JSObjectRef function,
212             JSObjectRef this_object,
213             size_t argumentCount,
214             const JSValueRef arguments[], JSValueRef * exception)
215 {
216   gchar *buf;
217   if (argumentCount != 1)
218     {
219       seed_make_exception (ctx, exception, "ArgumentError",
220                            "print expected 1 argument, got %zd",
221                            argumentCount);
222       return JSValueMakeNull (ctx);
223     }
224
225   buf = seed_value_to_string (ctx, arguments[0], exception);
226
227   puts (buf);
228   g_free (buf);
229
230   return JSValueMakeUndefined (ctx);
231 }
232
233 const gchar *
234 seed_g_type_name_to_string (GITypeInfo * type)
235 {
236   GITypeTag type_tag = g_type_info_get_tag (type);
237
238   const gchar *type_name;
239   GIBaseInfo *interface;
240
241   if (type_tag == GI_TYPE_TAG_INTERFACE)
242     {
243       interface = g_type_info_get_interface (type);
244
245       type_name = g_base_info_get_name (interface);
246       g_base_info_unref (interface);
247     }
248   else
249     {
250       type_name = g_type_tag_to_string (type_tag);
251     }
252
253   return type_name;
254 }
255
256 static JSValueRef
257 seed_introspect (JSContextRef ctx,
258                  JSObjectRef function,
259                  JSObjectRef this_object,
260                  size_t argumentCount,
261                  const JSValueRef arguments[], JSValueRef * exception)
262 {
263   // TODO: LEAKY!
264
265   GICallableInfo *info;
266   JSObjectRef data_obj, args_obj, argument;
267   guint i, nargs;
268
269   if (argumentCount != 1)
270     {
271       seed_make_exception (ctx, exception, "ArgumentError",
272                            "Seed.introspect expected 1 argument, "
273                            "got %zd", argumentCount);
274       return JSValueMakeNull (ctx);
275     }
276
277   if (!JSValueIsObject (ctx, arguments[0]))
278     return JSValueMakeNull (ctx);
279   if (!JSValueIsObjectOfClass (ctx, arguments[0], gobject_method_class))
280     return JSValueMakeNull (ctx);
281
282   info = (GICallableInfo *) JSObjectGetPrivate ((JSObjectRef) arguments[0]);
283   data_obj = JSObjectMake (ctx, NULL, NULL);
284
285   seed_object_set_property (ctx, data_obj, "name", (JSValueRef)
286                             seed_value_from_string (ctx, g_base_info_get_name
287                                                     ((GIBaseInfo *) info),
288                                                     exception));
289
290   seed_object_set_property (ctx, data_obj, "return_type",
291                             seed_value_from_string
292                             (ctx, seed_g_type_name_to_string
293                              (g_callable_info_get_return_type (info)),
294                              exception));
295
296   args_obj = JSObjectMake (ctx, NULL, NULL);
297
298   seed_object_set_property (ctx, data_obj, "args", args_obj);
299
300   nargs = g_callable_info_get_n_args (info);
301   for (i = 0; i < nargs; ++i)
302     {
303       argument = JSObjectMake (ctx, NULL, NULL);
304
305       const gchar *arg_name =
306         seed_g_type_name_to_string (g_arg_info_get_type
307                                     (g_callable_info_get_arg (info, i)));
308
309       seed_object_set_property (ctx, argument, "type",
310                                 seed_value_from_string (ctx,
311                                                         arg_name, exception));
312
313       JSObjectSetPropertyAtIndex (ctx, args_obj, i, argument, NULL);
314     }
315
316   return data_obj;
317 }
318
319 static JSValueRef
320 seed_check_syntax (JSContextRef ctx,
321                    JSObjectRef function,
322                    JSObjectRef this_object,
323                    size_t argumentCount,
324                    const JSValueRef arguments[], JSValueRef * exception)
325 {
326   JSStringRef jsstr;
327   if (argumentCount == 1)
328     {
329       jsstr = JSValueToStringCopy (ctx, arguments[0], exception);
330
331       JSCheckScriptSyntax (ctx, jsstr, 0, 0, exception);
332       if (jsstr)
333         JSStringRelease (jsstr);
334     }
335   else
336     {
337       seed_make_exception (ctx, exception, "ArgumentError",
338                            "Seed.check_syntax expected "
339                            "1 argument, got %zd", argumentCount);
340     }
341   return JSValueMakeNull (ctx);
342 }
343
344 static JSValueRef
345 seed_spawn (JSContextRef ctx,
346             JSObjectRef function,
347             JSObjectRef this_object,
348             size_t argumentCount,
349             const JSValueRef arguments[], JSValueRef * exception)
350 {
351   gchar *line, *stdoutstr, *stderrstr;
352   JSObjectRef ret;
353   GError *error = NULL;
354
355   if (argumentCount != 1)
356     {
357       // I am so lazy
358       seed_make_exception (ctx, exception, "ArgumentError",
359                            "Seed.spawn expected 1 argument");
360       return JSValueMakeNull (ctx);
361     }
362
363   line = seed_value_to_string (ctx, arguments[0], exception);
364   g_spawn_command_line_sync (line, &stdoutstr, &stderrstr, NULL, &error);
365   if (error)
366     {
367       seed_make_exception_from_gerror (ctx, exception, error);
368
369       g_free (line);
370       g_error_free (error);
371       return JSValueMakeNull (ctx);
372     }
373
374   ret = JSObjectMake (ctx, NULL, NULL);
375   seed_object_set_property (ctx, ret, "stdout",
376                             seed_value_from_string (ctx, stdoutstr,
377                                                     exception));
378   seed_object_set_property (ctx, ret, "stderr",
379                             seed_value_from_string (ctx, stderrstr,
380                                                     exception));
381
382   g_free (line);
383   g_free (stdoutstr);
384   g_free (stderrstr);
385
386   return ret;
387 }
388
389 static JSValueRef
390 seed_quit (JSContextRef ctx,
391            JSObjectRef function,
392            JSObjectRef this_object,
393            size_t argumentCount,
394            const JSValueRef arguments[], JSValueRef * exception)
395 {
396   if (argumentCount == 1)
397     {
398       exit (seed_value_to_int (ctx, arguments[0], NULL));
399     }
400   else if (argumentCount > 1)
401     {
402       seed_make_exception (ctx, exception, "ArgumentError",
403                            "Seed.quit expected " "1 argument, got %zd",
404                            argumentCount);
405     }
406
407   exit (EXIT_SUCCESS);
408 }
409
410 static JSValueRef
411 seed_breakpoint (JSContextRef ctx,
412                  JSObjectRef function,
413                  JSObjectRef this_object,
414                  size_t argumentCount,
415                  const JSValueRef arguments[], JSValueRef * exception)
416 {
417   G_BREAKPOINT ();
418   return JSValueMakeUndefined (ctx);
419 }
420
421
422 static JSValueRef
423 seed_argv_get_property (JSContextRef ctx,
424                         JSObjectRef object,
425                         JSStringRef property_name, JSValueRef * exception)
426 {
427   SeedArgvPrivates *priv;
428   gchar *cproperty_name;
429   gsize length;
430   guint index;
431
432   priv = JSObjectGetPrivate (object);
433   if (!priv->argc)
434     return JSValueMakeUndefined (ctx);
435   length = JSStringGetMaximumUTF8CStringSize (property_name);
436   cproperty_name = g_alloca (length * sizeof (gchar));
437   JSStringGetUTF8CString (property_name, cproperty_name, length);
438
439   if (!g_strcmp0 (cproperty_name, "length"))
440     {
441       return seed_value_from_int (ctx, priv->argc, exception);
442     }
443   index = atoi (cproperty_name);
444   return seed_value_from_string (ctx, priv->argv[index], exception);
445 }
446
447 JSClassDefinition seed_argv_def = {
448   0,                            /* Version, always 0 */
449   kJSClassAttributeNoAutomaticPrototype,        /* JSClassAttributes */
450   "argv_array",                 /* Class Name */
451   NULL,                         /* Parent Class */
452   NULL,                         /* Static Values */
453   NULL,
454   NULL,
455   NULL,
456   NULL,                         /* Has Property */
457   seed_argv_get_property,       /* Get Property */
458   NULL,
459   NULL,                         /* Delete Property */
460   NULL,                         /* Get Property Names */
461   NULL,                         /* Call As Function */
462   NULL,                         /* Call As Constructor */
463   NULL,                         /* Has Instance */
464   NULL                          /* Convert To Type */
465 };
466
467 JSClassRef seed_argv_class;
468
469 void
470 seed_init_builtins (SeedEngine * local_eng, gint * argc, gchar *** argv)
471 {
472   SeedArgvPrivates *priv;
473   JSObjectRef arrayObj;
474   JSObjectRef obj =
475     (JSObjectRef) seed_object_get_property (local_eng->context,
476                                             local_eng->global,
477                                             "Seed");
478
479   seed_create_function (local_eng->context, "include", &seed_include, obj);
480   seed_create_function (local_eng->context, "scoped_include",
481                         &seed_scoped_include, obj);
482
483   seed_print_ref =
484     JSObjectMakeFunctionWithCallback (local_eng->context, NULL, &seed_print);
485   seed_object_set_property (local_eng->context, obj, "print", seed_print_ref);
486   seed_object_set_property (local_eng->context, local_eng->global, "print",
487                             seed_print_ref);
488   JSValueProtect (local_eng->context, seed_print_ref);
489
490   seed_create_function (local_eng->context,
491                         "check_syntax", &seed_check_syntax, obj);
492   seed_create_function (local_eng->context,
493                         "introspect", &seed_introspect, obj);
494   seed_create_function (local_eng->context, "spawn", &seed_spawn, obj);
495   seed_create_function (local_eng->context, "quit", &seed_quit, obj);
496   seed_create_function (local_eng->context, "breakpoint",
497                         &seed_breakpoint, obj);
498
499   priv = g_new0 (SeedArgvPrivates, 1);
500   priv->argv = argv ? *argv : 0;
501   priv->argc = argc ? *argc : 0;
502
503   seed_argv_class = JSClassCreate (&seed_argv_def);
504   arrayObj = JSObjectMake (local_eng->context, seed_argv_class, priv);
505
506   seed_object_set_property (local_eng->context, obj, "argv", arrayObj);
507
508 }