5e018fa8470cc377ff8fb0563b0f0258af38269e
[reactos.git] / reactos / lib / kjs / src / main.c
1 /*
2 * The JS shell
3 * Copyright (c) 1998 New Generation Software (NGS) Oy
4 *
5 * Author: Markku Rossi <mtr@ngs.fi>
6 */
7
8 /*
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the Free
21 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22 * MA 02111-1307, USA
23 */
24
25 /*
26 * $Source: /cygdrive/c/RCVS/CVS/ReactOS/reactos/lib/kjs/src/main.c,v $
27 * $Id$
28 */
29
30 #if HAVE_CONFIG_H
31 #include "jsconfig.h"
32 #endif
33
34 #include <stdio.h>
35 #include <assert.h>
36
37 #if HAVE_STDC_HEADERS
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <string.h>
41
42 #else /* not HAVE_STDC_HEADERS */
43
44 #if HAVE_STDLIB_H
45 #include <stdlib.h>
46 #endif
47
48 #if HAVE_STRING_H
49 #include <string.h>
50 #endif
51
52 #if HAVE_UNISTD_H
53 #include <unistd.h>
54 #endif
55
56 #endif /* not HAVE_STDC_HEADERS */
57
58 #include "js.h"
59 #include "getopt.h"
60
61 /*
62 * Global variables.
63 */
64
65 char *program;
66
67 /* Options. */
68
69 /*
70 * -a, --annotate-assembler
71 *
72 * Annotate generated assembler listing with the original source code.
73 */
74
75 int annotate_assembler = 0;
76
77 /*
78 * -c, --compile
79 *
80 * Compile all given JavaScript files to byte-code and save the result
81 * to file.
82 */
83
84 int compile = 0;
85
86 /*
87 * -d METHOD, --dispatch=METHOD
88 *
89 * Use byte-code instruction dispatch method METHOD. Possible values are
90 * `switch', `switch-basic' and `jumps'.
91 */
92
93 JSVMDispatchMethod dispatch_method = JS_VM_DISPATCH_JUMPS;
94
95 /*
96 * -e CODE, --eval=CODE
97 *
98 * Evaluate JavaScript code CODE.
99 */
100
101 /*
102 * -E, --events
103 *
104 * Print virtual machine events to the standard error.
105 */
106 int events = 0;
107
108 /*
109 * -f, --file
110 *
111 * Use the next argument as the main source file and pass all
112 * remaining arguments to it through the ARGS array.
113 */
114
115 /*
116 * -g, --debug
117 *
118 * Generate debugging information to the generated byte-code file.
119 */
120 int generate_debug_info = 0;
121
122 /*
123 * -h, --help
124 *
125 * Print short help and exit successfully.
126 */
127
128 /*
129 * -l, --load
130 *
131 * Treat all following arguments, up to option -f / --file, as
132 * byte-code or JavaScript files and execute them. When option -f, --file
133 * is encountered, the next argument is the actual main source file that
134 * is executed with the remaining arguments.
135 */
136
137 /*
138 * -N, --no-compiler
139 *
140 * Do not define compiler in the interpreter. Options makes the
141 * interpreter a pure virtual machine that can't compile any JavaScript
142 * code.
143 */
144
145 int no_compiler = 0;
146
147 /*
148 * -O [LEVEL], --optimize[=LEVEL]
149 *
150 * Optimize at level LEVEL. The default level for batch-compile is 1.
151 * Value 0 disable optimization.
152 */
153
154 int optimize = 1;
155
156 /*
157 * -r OPTION, --secure=OPTIONS
158 *
159 * Turn on security option OPTION.
160 */
161
162 int secure_builtin_file = 0;
163 int secure_builtin_system = 0;
164
165 /*
166 * -s SIZE, --stack-size=SIZE
167 *
168 * Set the virtual machine stack size to SIZE.
169 */
170 unsigned int stack_size = 2048;
171
172 /*
173 * -S, --no-assemble
174 *
175 * Compile al given JavaScript files to JavaScript assembler and save the
176 * result to file.
177 */
178
179 int no_assemble = 0;
180
181 /*
182 * -t, --stacktrace
183 *
184 * Print stack trace on error.
185 */
186
187 int stacktrace_on_error = 0;
188
189 /*
190 * -v, --verbose
191 *
192 * Tell more about what we do.
193 */
194
195 unsigned int verbose = 0;
196
197 /*
198 * -V, --version
199 *
200 * Print version information and exit successfully.
201 */
202
203 /*
204 * -W OPTION, --compiler-option=OPTION
205 *
206 * Set compiler option OPTION.
207 */
208
209 int warn_deprecated = 0;
210 int warn_unused_argument = 0;
211 int warn_unused_variable = 1;
212 int warn_undef = 1;
213 int warn_shadow = 1;
214 int warn_with_clobber = 1;
215 int warn_missing_semicolon = 0;
216 int warn_strict_ecma = 0;
217
218 /*
219 * -x, --executable
220 *
221 * Generate executable byte-code files.
222 */
223 int generate_executable_bc_files = 0;
224
225
226 /*
227 * Static variables.
228 */
229
230 static struct option long_options[] =
231 {
232 {"annotate-assembler", no_argument, 0, 'a'},
233 {"compile", no_argument, 0, 'c'},
234 {"dispatch", required_argument, 0, 'd'},
235 {"eval", required_argument, 0, 'e'},
236 {"events", no_argument, 0, 'E'},
237 {"file", no_argument, 0, 'f'},
238 {"debug", no_argument, 0, 'g'},
239 {"help", no_argument, 0, 'h'},
240 {"load", no_argument, 0, 'l'},
241 {"no-compiler", no_argument, 0, 'N'},
242 {"optimize", optional_argument, 0, 'O'},
243 {"secure", required_argument, 0, 'r'},
244 {"stack-size", required_argument, 0, 's'},
245 {"no-assemble", no_argument, 0, 'S'},
246 {"stacktrace", no_argument, 0, 't'},
247 {"verbose", no_argument, 0, 'v'},
248 {"version", no_argument, 0, 'V'},
249 {"compiler-option", required_argument, 0, 'W'},
250 {"executable", no_argument, 0, 'x'},
251
252 {NULL, 0, 0, 0},
253 };
254
255 /* Compiler options. */
256
257 /* Flags for options. */
258 #define JSC_RUNTIME 0x01
259 #define JSC_WALL 0x02
260 #define JSC_PEDANTIC 0x04
261 #define JSC_LINT 0x08
262
263 static struct
264 {
265 char *name;
266 int *option;
267 unsigned int flags;
268 } compiler_options[] =
269 {
270 {"deprecated", &warn_deprecated, JSC_WALL},
271 {"unused-argument", &warn_unused_argument, JSC_WALL},
272 {"unused-variable", &warn_unused_variable, JSC_WALL},
273 {"undefined", &warn_undef, JSC_RUNTIME},
274 {"shadow", &warn_shadow, JSC_WALL},
275 {"with-clobber", &warn_with_clobber, JSC_WALL},
276 {"missing-semicolon", &warn_missing_semicolon, JSC_PEDANTIC},
277 {"strict-ecma", &warn_strict_ecma, JSC_LINT},
278
279 {NULL, NULL, 0},
280 };
281
282
283 /*
284 * Prototypes for static functions.
285 */
286
287 static void handle_compiler_option (char *name);
288
289 static JSInterpPtr create_interp (void);
290
291 static void usage (void);
292 static void version (void);
293
294 /*
295 * Global functions.
296 */
297
298 int
299 main (int argc, char *argv[])
300 {
301 JSInterpPtr interp = NULL;
302 char *cp;
303 int do_load = 0;
304
305 /* Get program's name. */
306 program = strrchr (argv[0], '/');
307 if (program == NULL)
308 program = argv[0];
309 else
310 program++;
311
312 /* Make getopt_long() to use our modified program name. */
313 argv[0] = program;
314
315 /* Parse arguments. */
316 while (1)
317 {
318 int c;
319 int option_index = 0;
320
321 c = getopt_long (argc, argv, "acd:e:EfghlNO::r:s:StvVW:x",
322 long_options, &option_index);
323 if (c == EOF)
324 break;
325
326 switch (c)
327 {
328 case 'a': /* --annotate-assembler */
329 annotate_assembler = 1;
330 break;
331
332 case 'c': /* --compile */
333 compile = 1;
334 break;
335
336 case 'd': /* --dispatch */
337 if (strcmp (optarg, "switch-basic") == 0)
338 dispatch_method = JS_VM_DISPATCH_SWITCH_BASIC;
339 else if (strcmp (optarg, "switch") == 0)
340 dispatch_method = JS_VM_DISPATCH_SWITCH;
341 else if (strcmp (optarg, "jumps") == 0)
342 dispatch_method = JS_VM_DISPATCH_JUMPS;
343 else
344 {
345 fprintf (stderr, "%s: illegal dispatch method `%s'\n",
346 program, optarg);
347 exit (1);
348 }
349 break;
350
351 case 'e': /* --eval */
352 if (interp == NULL)
353 interp = create_interp ();
354
355 if (!js_eval (interp, optarg))
356 {
357 fprintf (stderr, "%s: eval failed: %s\n", program,
358 js_error_message (interp));
359 exit (1);
360 }
361 break;
362
363 case 'E': /* --events */
364 events = 1;
365 break;
366
367 case 'f': /* --file */
368 if (optind >= argc)
369 {
370 no_argument_for_file:
371 fprintf (stderr, "%s: no arguments after option --file\n",
372 program);
373 exit (1);
374 }
375 goto arguments_done;
376 break;
377
378 case 'g': /* --debug */
379 generate_debug_info = 1;
380 break;
381
382 case 'h': /* --help */
383 usage ();
384 exit (0);
385 break;
386
387 case 'l': /* --load */
388 do_load = 1;
389 goto arguments_done;
390 break;
391
392 case 'N': /* --no-compiler */
393 no_compiler = 1;
394 break;
395
396 case 'O': /* --optimize */
397 if (optarg)
398 optimize = atoi (optarg);
399 break;
400
401 case 'r': /* --secure */
402 if (strcmp (optarg, "file") == 0)
403 secure_builtin_file = 1;
404 else if (strcmp (optarg, "system") == 0)
405 secure_builtin_system = 1;
406 else
407 {
408 fprintf (stderr, "%s: unknown security option `%s'\n",
409 program, optarg);
410 exit (1);
411 }
412 break;
413
414 case 's': /* --stack-size */
415 stack_size = atoi (optarg);
416 break;
417
418 case 'S': /* --no-assemble */
419 no_assemble = 1;
420 break;
421
422 case 't': /* --stacktrace */
423 stacktrace_on_error = 1;
424 break;
425
426 case 'v': /* --verbose */
427 verbose++;
428 break;
429
430 case 'V': /* --version */
431 version ();
432 exit (0);
433 break;
434
435 case 'W': /* --compiler-option */
436 handle_compiler_option (optarg);
437 break;
438
439 case 'x': /* --executable */
440 generate_executable_bc_files = 1;
441 break;
442
443 case '?':
444 fprintf (stderr, "Try `%s --help' for more information.\n",
445 program);
446 exit (1);
447 break;
448
449 default:
450 printf ("Hey! main() didn't handle option \"%c\" (%d)", c, c);
451 if (optarg)
452 printf (" with arg %s", optarg);
453 printf ("\n");
454 abort ();
455 break;
456 }
457 }
458
459 arguments_done:
460
461 interp = create_interp ();
462
463 /* Handle loading here. */
464 if (do_load)
465 {
466 for (; optind < argc; optind++)
467 {
468 if (strcmp (argv[optind], "-f") == 0
469 || strcmp (argv[optind], "--file") == 0)
470 {
471 optind++;
472 if (optind >= argc)
473 goto no_argument_for_file;
474
475 break;
476 }
477
478 if (!js_eval_file (interp, argv[optind]))
479 {
480 fprintf (stderr, "%s: loading of file `%s' failed:\n%s\n",
481 program, argv[optind], js_error_message (interp));
482 exit (1);
483 }
484 }
485 }
486
487 /* Let's see what we have to do. */
488 if (compile)
489 {
490 char *jscname;
491
492 /*
493 * Treat all remaining arguments as JavaScript files and compile them.
494 */
495
496 for (; optind < argc; optind++)
497 {
498 /* Create name for the byte-code file. */
499
500 jscname = malloc (strlen (argv[optind]) + 5);
501 assert (jscname != NULL);
502 strcpy (jscname, argv[optind]);
503
504 cp = strrchr (jscname, '.');
505 if (cp)
506 strcpy (++cp, "jsc");
507 else
508 strcat (jscname, ".jsc");
509
510 if (verbose)
511 printf ("%s: compiling `%s' to `%s'\n", program,
512 argv[optind], jscname);
513
514 if (!js_compile (interp, argv[optind], NULL, jscname))
515 {
516 fprintf (stderr, "%s\n", js_error_message (interp));
517 exit (1);
518 }
519
520 free (jscname);
521 }
522 }
523 else if (no_assemble)
524 {
525 char *jasname;
526
527 /* Compile argument files to assembler. */
528 for (; optind < argc; optind++)
529 {
530 /* Create name for the assembler file. */
531
532 jasname = malloc (strlen (argv[optind]) + 5);
533 assert (jasname != NULL);
534 strcpy (jasname, argv[optind]);
535
536 cp = strrchr (jasname, '.');
537 if (cp)
538 strcpy (++cp, "jas");
539 else
540 strcat (jasname, ".jas");
541
542 if (verbose)
543 printf ("%s: compiling `%s' to `%s'\n", program,
544 argv[optind], jasname);
545
546 if (!js_compile (interp, argv[optind], jasname, NULL))
547 {
548 fprintf (stderr, "%s\n", js_error_message (interp));
549 exit (1);
550 }
551
552 free (jasname);
553 }
554 }
555 else if (optind < argc)
556 {
557 char *main_file = argv[optind];
558 JSType args;
559 int i;
560
561 /*
562 * Assume that <main_file> contains JavaScript (or byte-code) and
563 * execute it. All the remaining arguments are passed to the
564 * interpreter through the ARGS array.
565 */
566
567 /* Save all remaining arguments to ARGS */
568 js_type_make_array (interp, &args, argc - optind);
569
570 for (i = 0; optind + i < argc; i++)
571 js_type_make_string (interp, &args.u.array->data[i],
572 argv[optind + i], strlen (argv[optind + i]));
573
574 js_set_var (interp, "ARGS", &args);
575
576 if (!js_eval_file (interp, main_file))
577 {
578 fprintf (stderr, "%s: evaluation of file `%s' failed:\n%s\n",
579 program, main_file, js_error_message (interp));
580 exit (1);
581 }
582 }
583
584 js_destroy_interp (interp);
585
586 #if 0
587 /* Use with JS_DEBUG_MEMORY_LEAKS in jsint.h. */
588 js_alloc_dump_blocks ();
589 #endif
590
591 return 0;
592 }
593
594 \f
595 /*
596 * Static functions.
597 */
598
599 static inline int
600 is_prefix (char *prefix, char *str)
601 {
602 int i;
603
604 for (i = 0; prefix[i] && str[i] && prefix[i] == str[i]; i++)
605 ;
606 if (!prefix[i])
607 return 1;
608
609 return 0;
610 }
611
612
613 static void
614 handle_compiler_option (char *name)
615 {
616 int i;
617 int value = 1;
618 int nmatches = 0;
619 int did_match = 0;
620
621 if (name[0] == 'n' && name[1] == 'o' && name[2] == '-')
622 {
623 value = 0;
624 name += 3;
625 }
626
627 for (i = 0; compiler_options[i].name; i++)
628 {
629 int was_prefix = 0;
630
631 if ((was_prefix = is_prefix (name, compiler_options[i].name))
632 || (strcmp (name, "runtime") == 0
633 && (compiler_options[i].flags & JSC_RUNTIME))
634 || (strcmp (name, "all") == 0
635 && (compiler_options[i].flags & JSC_WALL))
636 || (strcmp (name, "pedantic") == 0
637 && (compiler_options[i].flags & (JSC_WALL | JSC_PEDANTIC))))
638 {
639 *compiler_options[i].option = value;
640
641 if (was_prefix)
642 nmatches++;
643
644 did_match = 1;
645 #if 0
646 fprintf (stderr, "set %s to %d\n", compiler_options[i].name,
647 value);
648 #endif
649 }
650 }
651
652 if (!did_match)
653 {
654 fprintf (stderr, "%s: unknown compiler option `-W%s%s'\n", program,
655 value ? "" : "no-", name);
656 exit (1);
657 }
658 if (nmatches > 1)
659 {
660 fprintf (stderr, "%s: ambiguous compiler option `-W%s%s'\n",
661 program, value ? "" : "no-", name);
662 exit (1);
663 }
664 }
665
666
667 static int
668 show_events_hook (int event, void *context)
669 {
670 char *event_name;
671
672 switch (event)
673 {
674 case JS_EVENT_OPERAND_COUNT:
675 event_name = "operand count";
676 break;
677
678 case JS_EVENT_GARBAGE_COLLECT:
679 event_name = "garbage collect";
680 break;
681
682 default:
683 event_name = "unknown";
684 break;
685 }
686
687 fprintf (stderr, "[%s: %s]\n", program, event_name);
688
689 return 0;
690 }
691
692
693 static JSInterpPtr
694 create_interp ()
695 {
696 JSInterpOptions options;
697 JSInterpPtr interp;
698
699 js_init_default_options (&options);
700
701 options.stack_size = stack_size;
702 options.dispatch_method = dispatch_method;
703 options.verbose = verbose;
704
705 options.no_compiler = no_compiler;
706 options.stacktrace_on_error = stacktrace_on_error;
707
708 options.secure_builtin_file = secure_builtin_file;
709 options.secure_builtin_system = secure_builtin_system;
710
711 options.annotate_assembler = annotate_assembler;
712 options.debug_info = generate_debug_info;
713 options.executable_bc_files = generate_executable_bc_files;
714
715 options.warn_unused_argument = warn_unused_argument;
716 options.warn_unused_variable = warn_unused_variable;
717 options.warn_undef = warn_undef;
718 options.warn_shadow = warn_shadow;
719 options.warn_with_clobber = warn_with_clobber;
720 options.warn_missing_semicolon = warn_missing_semicolon;
721 options.warn_strict_ecma = warn_strict_ecma;
722 options.warn_deprecated = warn_deprecated;
723
724 /* As a default, no optimization */
725 options.optimize_peephole = 0;
726 options.optimize_jumps_to_jumps = 0;
727 options.optimize_bc_size = 0;
728 options.optimize_heavy = 0;
729
730 if (optimize >= 1)
731 {
732 options.optimize_peephole = 1;
733 options.optimize_jumps_to_jumps = 1;
734 options.optimize_bc_size = 1;
735 }
736
737 if (optimize >= 2)
738 {
739 options.optimize_heavy = 1;
740 }
741
742 /* Show events? */
743 if (events)
744 {
745 options.hook = show_events_hook;
746 options.hook_operand_count_trigger = 1000000;
747 }
748
749 interp = js_create_interp (&options);
750 if (interp == NULL)
751 {
752 fprintf (stderr, "%s: couldn't create interpreter\n", program);
753 exit (1);
754 }
755
756 /* And finally, define the requested modules. */
757
758 #if WITH_JS
759 if (!js_define_module (interp, js_ext_JS))
760 fprintf (stderr, "%s: warning: couldn't create the JS extension\n",
761 program);
762 #endif
763
764 #if WITH_CURSES
765 if (!js_define_module (interp, js_ext_curses))
766 fprintf (stderr, "%s: warning: couldn't create the curses extension\n",
767 program);
768 #endif
769
770 #if WITH_MD5
771 if (!js_define_module (interp, js_ext_MD5))
772 fprintf (stderr, "%s: warning: couldn't create the MD5 extension\n",
773 program);
774 #endif
775
776 return interp;
777 }
778
779
780 static void
781 usage ()
782 {
783 printf ("\
784 Usage: %s [OPTION]... FILE [ARGUMENT]...\n\
785 Mandatory arguments to long options are mandatory for short options too.\n\
786 -a, --annotate-assembler annotate generated assembler listing with\n\
787 the original source code\n\
788 -c, --compile compile JavaScript input file to byte-code\n\
789 and save the result to the file `FILE.jsc'\n\
790 -d, --dispatch=METHOD use method METHOD for byte-code instruction\n\
791 dispatching\n\
792 -e, --eval=CODE evaluate JavaScript code CODE\n\
793 -E, --events print interpreter events\n\
794 -f, --file evaluate the next argument file and pass\n\
795 all remaining arguments to the interpreter\n\
796 through the ARGS array\n\
797 -g, --debug generate debugging information\n\
798 -h, --help print this help and exit\n\
799 -l, --load evaluate argument files until option `-f',\n\
800 `--file' is encountered\n\
801 -N, --no-compiler do not define compiler to the JavaScript\n\
802 interpreter\n\
803 -O, --optimize[=LEVEL] optimize at level LEVEL\n\
804 -r, --secure=OPTION turn on security option OPTION\n\
805 -s, --stack-size=SIZE set the interpreter stack size to SIZE nodes\n\
806 -S, --assembler compile JavaScript intput file to assembler\n\
807 and save the result to the file `FILE.jas'\n\
808 -t, --stacktrace print stacktrace on error\n\
809 -v, --verbose tell what the interpreter is doing\n\
810 -V, --version print version number\n\
811 -W, --compiler-option=OPTION\n\
812 set compilation option OPTION\n\
813 -x, --executable generate executable byte-code files\n",
814 program);
815
816 printf ("\nReport bugs to mtr@ngs.fi.\n");
817 }
818
819
820 static void
821 version ()
822 {
823 printf ("NGS JavaScript Interpter %s\n\
824 Copyright (C) 1998 New Generation Software (NGS) Oy.\n\
825 NGS JavaScript Interpreter comes with NO WARRANTY, to the extent\n\
826 permitted by law. You may redistribute copies of NGS JavaScript\n\
827 Interpreter under the terms of the GNU Library General Public License.\n\
828 For more information about these matters, see the files named COPYING.\n\
829 ",
830 VERSION);
831 }