migrate substitution keywords to SVN
[reactos.git] / reactos / lib / kjs / src / js.c
1 /*
2 * JavaScript interpreter main glue.
3 * Copyright (c) 1998-1999 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/js.c,v $
27 * $Id$
28 */
29
30 #include "js.h"
31 #include "jsint.h"
32
33 /*
34 * Types and definitions.
35 */
36
37 /* Context for js_global_method_stub. */
38 struct js_global_method_context_st
39 {
40 JSGlobalMethodProc proc;
41 void *context;
42 JSFreeProc free_proc;
43 JSInterpPtr interp;
44 };
45
46 typedef struct js_global_method_context_st JSGlobalMethodContext;
47
48 /* Context for user I/O function streams. */
49 struct js_user_io_func_ctx_st
50 {
51 JSIOFunc func;
52 void *context;
53 long position;
54 };
55
56 typedef struct js_user_io_func_ctx_st JSUserIOFuncCtx;
57
58 struct js_method_reg_st
59 {
60 JSSymbol sym;
61 char *name;
62 unsigned int flags;
63 JSMethodProc method;
64 };
65
66 typedef struct js_method_reg_st JSMethodReg;
67
68 struct js_property_reg_st
69 {
70 JSSymbol sym;
71 char *name;
72 unsigned int flags;
73 JSPropertyProc property;
74 };
75
76 typedef struct js_property_reg_st JSPropertyReg;
77
78 /* The class handle. */
79 struct js_class_st
80 {
81 char *name;
82 JSInterpPtr interp;
83
84 /* Flags. */
85 unsigned int no_auto_destroy : 1;
86 unsigned int interned : 1;
87
88 void *class_context;
89 JSFreeProc class_context_destructor;
90
91 JSConstructor constructor;
92
93 unsigned int num_methods;
94 JSMethodReg *methods;
95
96 unsigned int num_properties;
97 JSPropertyReg *properties;
98 };
99
100 /* Object instance context. */
101 struct js_object_instance_ctx_st
102 {
103 void *instance_context;
104 JSFreeProc instance_context_destructor;
105 };
106
107 typedef struct js_object_instance_ctx_st JSObjectInstanceCtx;
108
109
110 /*
111 * Prototypes for static functions.
112 */
113
114 /* The module for JS' core global methods. */
115 static void js_core_globals (JSInterpPtr interp);
116
117 /*
118 * Helper function to evaluate source <source> with compiler function
119 * <compiler_function>.
120 */
121 static int js_eval_source (JSInterpPtr interp, JSNode *source,
122 char *compiler_function);
123
124 /*
125 * Helper function to compile source <source> with compiler function
126 * <compiler_function>. If <assembler_file> is not NULL, the
127 * assembler listing of the compilation is saved to that file. If
128 * <byte_code_file> is not NULL, the byte_code data is saved to that
129 * file. If <bc_return> is not NULL, the resulting byte_code data is
130 * returned in it as a JavaScript string node.
131 */
132 static int js_compile_source (JSInterpPtr interp, JSNode *source,
133 char *compiler_function, char *assembler_file,
134 char *byte_code_file, JSNode *bc_return);
135
136 /*
137 * The stub function for global methods, created with the
138 * js_create_global_method() API function.
139 */
140 static void js_global_method_stub (JSVirtualMachine *vm,
141 JSBuiltinInfo *builtin_info,
142 void *instance_context,
143 JSNode *result_return,
144 JSNode *args);
145
146 /*
147 * Destructor for the global methods, created with the
148 * js_create_global_method() API function.
149 */
150 static void js_global_method_delete (JSBuiltinInfo *builtin_info,
151 void *instance_context);
152
153 static JSIOStream *iostream_iofunc (JSIOFunc func, void *context,
154 int readp, int writep);
155
156
157 /*
158 * Global functions.
159 */
160
161 const JSCharPtr
162 js_version ()
163 {
164 return VERSION;
165 }
166
167
168 void
169 js_init_default_options (JSInterpOptions *options)
170 {
171 memset (options, 0, sizeof (*options));
172
173 options->stack_size = 2048;
174 options->dispatch_method = JS_VM_DISPATCH_JUMPS;
175
176 options->warn_undef = 1;
177
178 options->optimize_peephole = 1;
179 options->optimize_jumps_to_jumps = 1;
180
181 options->fd_count = (unsigned long) -1;
182 }
183
184
185 JSInterpPtr
186 js_create_interp (JSInterpOptions *options)
187 {
188 JSInterpPtr interp = NULL;
189 JSByteCode *bc;
190 JSInterpOptions default_options;
191 JSIOStream *s_stdin = NULL;
192 JSIOStream *s_stdout = NULL;
193 JSIOStream *s_stderr = NULL;
194
195 /*
196 * Sanity check to assure that the js.h and jsint.h APIs share a
197 * same view to the world.
198 */
199 assert (sizeof (JSNode) == sizeof (JSType));
200
201 interp = js_calloc (NULL, 1, sizeof (*interp));
202 if (interp == NULL)
203 return NULL;
204
205 if (options == NULL)
206 {
207 js_init_default_options (&default_options);
208 options = &default_options;
209 }
210
211 memcpy (&interp->options, options, sizeof (*options));
212
213 /* The default system streams. */
214
215 if (options->s_stdin)
216 s_stdin = iostream_iofunc (options->s_stdin, options->s_context, 1, 0);
217 else
218 s_stdin = js_iostream_file (stdin, 1, 0, 0);
219
220 if (s_stdin == NULL)
221 goto error_out;
222
223 if (options->s_stdout)
224 s_stdout = iostream_iofunc (options->s_stdout, options->s_context, 0, 1);
225 else
226 s_stdout = js_iostream_file (stdout, 0, 1, 0);
227
228 if (s_stdout == NULL)
229 goto error_out;
230 s_stdout->autoflush = 1;
231
232 if (options->s_stderr)
233 s_stderr = iostream_iofunc (options->s_stderr, options->s_context, 0, 1);
234 else
235 s_stderr = js_iostream_file (stderr, 0, 1, 0);
236
237 if (s_stderr == NULL)
238 goto error_out;
239 s_stderr->autoflush = 1;
240
241 /* Create virtual machine. */
242 interp->vm = js_vm_create (options->stack_size,
243 options->dispatch_method,
244 options->verbose,
245 options->stacktrace_on_error,
246 s_stdin, s_stdout, s_stderr);
247 if (interp->vm == NULL)
248 goto error_out;
249
250 /* Set some options. */
251 interp->vm->warn_undef = options->warn_undef;
252
253 /* Set the security options. */
254
255 if (options->secure_builtin_file)
256 interp->vm->security |= JS_VM_SECURE_FILE;
257 if (options->secure_builtin_system)
258 interp->vm->security |= JS_VM_SECURE_SYSTEM;
259
260 /* Set the event hook. */
261 interp->vm->hook = options->hook;
262 interp->vm->hook_context = options->hook_context;
263 interp->vm->hook_operand_count_trigger = options->hook_operand_count_trigger;
264
265 /* The file descriptor limit. */
266 interp->vm->fd_count = options->fd_count;
267
268 if (!options->no_compiler)
269 {
270 int result;
271
272 /* Define compiler to the virtual machine. */
273 bc = js_bc_read_data (js_compiler_bytecode, js_compiler_bytecode_len);
274 if (bc == NULL)
275 goto error_out;
276
277 result = js_vm_execute (interp->vm, bc);
278 js_bc_free (bc);
279 if (!result)
280 goto error_out;
281 }
282
283 /* Initialize our extensions. */
284 if (!js_define_module (interp, js_core_globals))
285 goto error_out;
286
287 /* Ok, we'r done. */
288
289 #if 0
290 #if JS_DEBUG_MEMORY_LEAKS
291 /* Let's see how much memory an empty interpreter takes. */
292 js_alloc_dump_blocks ();
293 #endif /* JS_DEBUG_MEMORY_LEAKS */
294 #endif
295
296 return interp;
297
298
299 /*
300 * Error handling.
301 */
302
303 error_out:
304
305 if (interp)
306 {
307 if (interp->vm)
308 js_vm_destroy (interp->vm);
309 js_free (interp);
310 }
311
312 if (s_stdin)
313 js_iostream_close (s_stdin);
314 if (s_stdout)
315 js_iostream_close (s_stdout);
316 if (s_stderr)
317 js_iostream_close (s_stderr);
318
319 return NULL;
320 }
321
322
323 void
324 js_destroy_interp (JSInterpPtr interp)
325 {
326 js_vm_destroy (interp->vm);
327 js_free (interp);
328
329 #if 0
330 #if JS_DEBUG_MEMORY_LEAKS
331 /* Let's see how much memory we leak. */
332 js_alloc_dump_blocks ();
333 #endif /* JS_DEBUG_MEMORY_LEAKS */
334 #endif
335 }
336
337
338 const JSCharPtr
339 js_error_message (JSInterpPtr interp)
340 {
341 return interp->vm->error;
342 }
343
344
345 void
346 js_result (JSInterpPtr interp, JSType *result_return)
347 {
348 memcpy (result_return, &interp->vm->exec_result, sizeof (*result_return));
349 }
350
351
352 int
353 js_eval (JSInterpPtr interp, char *code)
354 {
355 JSNode source;
356
357 js_vm_make_static_string (interp->vm, &source, code, strlen (code));
358 return js_eval_source (interp, &source, "JSC$compile_string");
359 }
360
361
362 int
363 js_eval_data (JSInterpPtr interp, char *data, unsigned int datalen)
364 {
365 JSNode source;
366
367 js_vm_make_static_string (interp->vm, &source, data, datalen);
368 return js_eval_source (interp, &source, "JSC$compile_string");
369 }
370
371
372 int
373 js_eval_file (JSInterpPtr interp, char *filename)
374 {
375 char *cp;
376 int result;
377
378 cp = strrchr (filename, '.');
379 if (cp && strcmp (cp, ".jsc") == 0)
380 {
381 run_bytecode:
382 result = js_execute_byte_code_file (interp, filename);
383 }
384 else if (cp && strcmp (cp, ".js") == 0)
385 {
386 try_javascript:
387 result = js_eval_javascript_file (interp, filename);
388 }
389 else
390 {
391 FILE *fp;
392
393 /* Must look into the file. */
394
395 fp = fopen (filename, "r");
396 if (fp)
397 {
398 int ch;
399
400 if ((ch = getc (fp)) == '#')
401 {
402 /* Skip the first sh-command line. */
403 while ((ch = getc (fp)) != EOF && ch != '\n')
404 ;
405 if (ch == EOF)
406 {
407 fclose (fp);
408 goto try_javascript;
409 }
410 }
411 else
412 ungetc (ch, fp);
413
414 /* Check if we can read the file magic. */
415 ch = getc (fp);
416 if (ch == 0xc0)
417 {
418 ch = getc (fp);
419 if (ch == 0x01)
420 {
421 ch = getc (fp);
422 if (ch == 'J')
423 {
424 ch = getc (fp);
425 if (ch == 'S')
426 {
427 /* Got it. We find a valid byte-code file magic. */
428 fclose (fp);
429 goto run_bytecode;
430 }
431 }
432 }
433 }
434
435 fclose (fp);
436 /* FALLTHROUGH */
437 }
438
439 /*
440 * If nothing else helps, we assume that the file contains JavaScript
441 * source code that must be compiled.
442 */
443 goto try_javascript;
444 }
445
446 return result;
447 }
448
449
450 int
451 js_eval_javascript_file (JSInterpPtr interp, char *filename)
452 {
453 JSNode source;
454
455 js_vm_make_static_string (interp->vm, &source, filename, strlen (filename));
456 return js_eval_source (interp, &source, "JSC$compile_file");
457 }
458
459
460 int
461 js_execute_byte_code_file (JSInterpPtr interp, char *filename)
462 {
463 JSByteCode *bc;
464 FILE *fp;
465 int result;
466
467 fp = fopen (filename, "rb");
468 if (fp == NULL)
469 {
470 /* Let's borrow vm's error buffer. */
471 sprintf (interp->vm->error, "couldn't open byte-code file \"%s\": %s",
472 filename, strerror (errno));
473 return 0;
474 }
475
476 bc = js_bc_read_file (fp);
477 fclose (fp);
478
479 if (bc == NULL)
480 /* XXX Error message. */
481 return 0;
482
483 /* Execute it. */
484
485 result = js_vm_execute (interp->vm, bc);
486 js_bc_free (bc);
487
488 return result;
489 }
490
491
492 int
493 js_apply (JSInterpPtr interp, char *name, unsigned int argc, JSType *argv)
494 {
495 JSNode *args;
496 unsigned int ui;
497 int result;
498
499 args = js_malloc (NULL, (argc + 1) * sizeof (JSNode));
500 if (args == NULL)
501 {
502 sprintf (interp->vm->error, "VM: out of memory");
503 return 0;
504 }
505
506 /* Set the argument count. */
507 args[0].type = JS_INTEGER;
508 args[0].u.vinteger = argc;
509
510 /* Set the arguments. */
511 for (ui = 0; ui < argc; ui++)
512 JS_COPY (&args[ui + 1], (JSNode *) &argv[ui]);
513
514 /* Call it. */
515 result = js_vm_apply (interp->vm, name, NULL, argc + 1, args);
516
517 js_free (args);
518
519 return result;
520 }
521
522
523 int
524 js_compile (JSInterpPtr interp, char *input_file, char *assembler_file,
525 char *byte_code_file)
526 {
527 JSNode source;
528
529 js_vm_make_static_string (interp->vm, &source, input_file,
530 strlen (input_file));
531 return js_compile_source (interp, &source, "JSC$compile_file",
532 assembler_file, byte_code_file, NULL);
533 }
534
535
536 int
537 js_compile_to_byte_code (JSInterpPtr interp, char *input_file,
538 unsigned char **bc_return,
539 unsigned int *bc_len_return)
540 {
541 JSNode source;
542 int result;
543
544 js_vm_make_static_string (interp->vm, &source, input_file,
545 strlen (input_file));
546 result = js_compile_source (interp, &source, "JSC$compile_file",
547 NULL, NULL, &source);
548 if (result == 0)
549 return 0;
550
551 /* Pass the data to the caller. */
552 *bc_return = source.u.vstring->data;
553 *bc_len_return = source.u.vstring->len;
554
555 return result;
556 }
557
558
559 int
560 js_compile_data_to_byte_code (JSInterpPtr interp, char *data,
561 unsigned int datalen,
562 unsigned char **bc_return,
563 unsigned int *bc_len_return)
564 {
565 JSNode source;
566 int result;
567
568 js_vm_make_static_string (interp->vm, &source, data, datalen);
569 result = js_compile_source (interp, &source, "JSC$compile_string",
570 NULL, NULL, &source);
571 if (result == 0)
572 return 0;
573
574 /* Pass the data to the caller. */
575 *bc_return = source.u.vstring->data;
576 *bc_len_return = source.u.vstring->len;
577
578 return result;
579 }
580
581
582 int
583 js_execute_byte_code (JSInterpPtr interp, unsigned char *bc_data,
584 unsigned int bc_data_len)
585 {
586 JSByteCode *bc;
587 int result;
588
589 bc = js_bc_read_data (bc_data, bc_data_len);
590 if (bc == NULL)
591 /* Not a valid byte-code data. */
592 return 0;
593
594 /* Execute it. */
595 result = js_vm_execute (interp->vm, bc);
596 js_bc_free (bc);
597
598 return result;
599 }
600
601
602 /* Classes. */
603
604 JSClassPtr
605 js_class_create (void *class_context, JSFreeProc class_context_destructor,
606 int no_auto_destroy, JSConstructor constructor)
607 {
608 JSClassPtr cls;
609
610 cls = js_calloc (NULL, 1, sizeof (*cls));
611 if (cls == NULL)
612 return NULL;
613
614 cls->class_context = class_context;
615 cls->class_context_destructor = class_context_destructor;
616
617 cls->no_auto_destroy = no_auto_destroy;
618 cls->constructor = constructor;
619
620 return cls;
621 }
622
623
624 void
625 js_class_destroy (JSClassPtr cls)
626 {
627 if (cls == NULL)
628 return;
629
630 if (cls->class_context_destructor)
631 (*cls->class_context_destructor) (cls->class_context);
632
633 js_free (cls);
634 }
635
636
637 JSVoidPtr
638 js_class_context (JSClassPtr cls)
639 {
640 if (cls)
641 return cls->class_context;
642
643 return NULL;
644 }
645
646
647 int
648 js_class_define_method (JSClassPtr cls, char *name, unsigned int flags,
649 JSMethodProc method)
650 {
651 JSMethodReg *nmethods;
652
653 nmethods = js_realloc (NULL, cls->methods,
654 (cls->num_methods + 1) * sizeof (JSMethodReg));
655 if (nmethods == NULL)
656 return 0;
657
658 cls->methods = nmethods;
659
660 /*
661 * The names are interned to symbols when the class is defined to the
662 * interpreter.
663 */
664
665 cls->methods[cls->num_methods].name = js_strdup (NULL, name);
666 if (cls->methods[cls->num_methods].name == NULL)
667 return 0;
668
669 cls->methods[cls->num_methods].flags = flags;
670 cls->methods[cls->num_methods].method = method;
671
672 cls->num_methods++;
673
674 return 1;
675 }
676
677
678 int
679 js_class_define_property (JSClassPtr cls, char *name, unsigned int flags,
680 JSPropertyProc property)
681 {
682 JSPropertyReg *nprops;
683
684 nprops = js_realloc (NULL, cls->properties,
685 (cls->num_properties + 1) * sizeof (JSPropertyReg));
686 if (nprops == NULL)
687 return 0;
688
689 cls->properties = nprops;
690
691 cls->properties[cls->num_properties].name = js_strdup (NULL, name);
692 if (cls->properties[cls->num_properties].name == NULL)
693 return 0;
694
695 cls->properties[cls->num_properties].flags = flags;
696 cls->properties[cls->num_properties].property = property;
697
698 cls->num_properties++;
699
700 return 1;
701 }
702
703
704 /* The stub functions for JSClass built-in objects. */
705
706 /* Method proc. */
707 static int
708 cls_method (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
709 void *instance_context, JSSymbol method, JSNode *result_return,
710 JSNode *args)
711 {
712 JSClassPtr cls = builtin_info->obj_context;
713 JSObjectInstanceCtx *ictx = instance_context;
714 int i;
715 JSMethodResult result;
716 char msg[1024];
717
718 /* Let's see if we know the method. */
719 for (i = 0; i < cls->num_methods; i++)
720 if (cls->methods[i].sym == method)
721 {
722 /* Found it. */
723
724 /* Check flags. */
725 if ((cls->methods[i].flags & JS_CF_STATIC) == 0
726 && instance_context == NULL)
727 /* An instance method called from the `main' class. */
728 break;
729
730 result = (*cls->methods[i].method) (cls,
731 (ictx
732 ? ictx->instance_context
733 : NULL),
734 cls->interp, args[0].u.vinteger,
735 (JSType *) &args[1],
736 (JSType *) result_return,
737 msg);
738 if (result == JS_ERROR)
739 {
740 sprintf (vm->error, "%s.%s(): %s", cls->name,
741 cls->methods[i].name, msg);
742 js_vm_error (vm);
743 }
744
745 return JS_PROPERTY_FOUND;
746 }
747
748 return JS_PROPERTY_UNKNOWN;
749 }
750
751 /* Property proc. */
752 static int
753 cls_property (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
754 void *instance_context, JSSymbol property, int set, JSNode *node)
755 {
756 JSClassPtr cls = builtin_info->obj_context;
757 JSObjectInstanceCtx *ictx = instance_context;
758 JSMethodResult result;
759 char msg[1024];
760 int i;
761
762 /* Find the property. */
763 for (i = 0; i < cls->num_properties; i++)
764 if (cls->properties[i].sym == property)
765 {
766 /* Found it. */
767
768 /* Check flags. */
769
770 if ((cls->properties[i].flags & JS_CF_STATIC) == 0
771 && instance_context == NULL)
772 break;
773
774 if ((cls->properties[i].flags & JS_CF_IMMUTABLE) && set)
775 {
776 sprintf (vm->error, "%s.%s: immutable property",
777 cls->name, cls->properties[i].name);
778 js_vm_error (vm);
779 }
780
781 result = (*cls->properties[i].property) (cls,
782 (ictx
783 ? ictx->instance_context
784 : NULL),
785 cls->interp, set,
786 (JSType *) node, msg);
787 if (result == JS_ERROR)
788 {
789 sprintf (vm->error, "%s.%s: %s", cls->name,
790 cls->properties[i].name, msg);
791 js_vm_error (vm);
792 }
793
794 return JS_PROPERTY_FOUND;
795 }
796
797 if (!set)
798 node->type = JS_UNDEFINED;
799
800 return JS_PROPERTY_UNKNOWN;
801 }
802
803 /* New proc. */
804 static void
805 cls_new_proc (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info, JSNode *args,
806 JSNode *result_return)
807 {
808 JSClassPtr cls = builtin_info->obj_context;
809 JSMethodResult result;
810 char msg[1024];
811 void *instance_context;
812 JSFreeProc instance_context_destructor;
813 JSObjectInstanceCtx *ictx;
814
815 result = (*cls->constructor) (cls, cls->interp, args[0].u.vinteger,
816 (JSType *) &args[1], &instance_context,
817 &instance_context_destructor,
818 msg);
819 if (result == JS_ERROR)
820 {
821 sprintf (vm->error, "new %s(): %s", cls->name, msg);
822 js_vm_error (vm);
823 }
824
825 ictx = js_calloc (vm, 1, sizeof (*ictx));
826 ictx->instance_context = instance_context;
827 ictx->instance_context_destructor = instance_context_destructor;
828
829 js_vm_builtin_create (vm, result_return, builtin_info, ictx);
830 }
831
832
833 /* Delete proc. */
834 static void
835 cls_delete_proc (JSBuiltinInfo *builtin_info, void *instance_context)
836 {
837 JSObjectInstanceCtx *ictx = instance_context;
838
839 if (ictx)
840 {
841 if (ictx->instance_context_destructor)
842 (*ictx->instance_context_destructor) (ictx->instance_context);
843
844 js_free (ictx);
845 }
846 }
847
848 /*
849 * This is called to destroy the class handle, when there are no more
850 * references to it.
851 */
852 static void
853 js_class_destructor (void *context)
854 {
855 JSClassPtr cls = context;
856
857 if (cls->no_auto_destroy)
858 return;
859
860 js_class_destroy (cls);
861 }
862
863
864 static void
865 intern_symbols (JSVirtualMachine *vm, JSClassPtr cls)
866 {
867 int i;
868
869 for (i = 0; i < cls->num_methods; i++)
870 cls->methods[i].sym = js_vm_intern (vm, cls->methods[i].name);
871
872 for (i = 0; i < cls->num_properties; i++)
873 cls->properties[i].sym = js_vm_intern (vm, cls->properties[i].name);
874
875 cls->interned = 1;
876 }
877
878
879 static JSBuiltinInfo *
880 one_builtin_info_please (JSVirtualMachine *vm, JSClassPtr cls)
881 {
882 JSBuiltinInfo *info;
883
884 info = js_vm_builtin_info_create (vm);
885
886 info->method_proc = cls_method;
887 info->property_proc = cls_property;
888
889 if (cls->constructor)
890 {
891 info->new_proc = cls_new_proc;
892 info->delete_proc = cls_delete_proc;
893 }
894
895 info->obj_context = cls;
896 info->obj_context_delete = js_class_destructor;
897
898 return info;
899 }
900
901
902 int
903 js_define_class (JSInterpPtr interp, JSClassPtr cls, char *name)
904 {
905 JSNode *n;
906 JSVirtualMachine *vm = interp->vm;
907 JSBuiltinInfo *info;
908
909 /* XXX We need a top-level here */
910
911 cls->name = js_strdup (vm, name);
912 cls->interp = interp;
913
914 if (!cls->interned)
915 /* Intern the symbols and properties. */
916 intern_symbols (interp->vm, cls);
917
918 /* Define it to the interpreter. */
919
920 info = one_builtin_info_please (vm, cls);
921
922 n = &vm->globals[js_vm_intern (vm, name)];
923 js_vm_builtin_create (vm, n, info, NULL);
924
925 return 1;
926 }
927
928
929 int
930 js_instantiate_class (JSInterpPtr interp, JSClassPtr cls, void *ictx,
931 JSFreeProc ictx_destructor, JSType *result_return)
932 {
933 JSObjectInstanceCtx *instance;
934 JSVirtualMachine *vm = interp->vm;
935 JSBuiltinInfo *info;
936
937 if (!cls->interned)
938 /* Intern the symbols and properties. */
939 intern_symbols (vm, cls);
940
941 /* Create an instance. */
942 instance = js_calloc (vm, 1, sizeof (*instance));
943 instance->instance_context = ictx;
944 instance->instance_context_destructor = ictx_destructor;
945
946 /* Create a fresh builtin info. */
947 info = one_builtin_info_please (vm, cls);
948
949 /* And create it. */
950 js_vm_builtin_create (vm, (JSNode *) result_return, info, instance);
951
952 return 1;
953 }
954
955
956 const JSClassPtr
957 js_lookup_class (JSInterpPtr interp, char *name)
958 {
959 JSNode *n;
960 JSVirtualMachine *vm = interp->vm;
961
962 n = &vm->globals[js_vm_intern (vm, name)];
963 if (n->type != JS_BUILTIN)
964 return NULL;
965
966 if (n->u.vbuiltin->info->method_proc != cls_method)
967 /* This is a wrong built-in. */
968 return NULL;
969
970 return (JSClassPtr) n->u.vbuiltin->info->obj_context;
971 }
972
973
974 int
975 js_isa (JSInterpPtr interp, JSType *object, JSClassPtr cls,
976 void **instance_context_return)
977 {
978 JSNode *n = (JSNode *) object;
979 JSObjectInstanceCtx *instance;
980
981 if (n->type != JS_BUILTIN || n->u.vbuiltin->info->obj_context != cls
982 || n->u.vbuiltin->instance_context == NULL)
983 return 0;
984
985 if (instance_context_return)
986 {
987 instance = (JSObjectInstanceCtx *) n->u.vbuiltin->instance_context;
988 *instance_context_return = instance->instance_context;
989 }
990
991 return 1;
992 }
993
994
995
996 /* Type functions. */
997
998 void
999 js_type_make_string (JSInterpPtr interp, JSType *type, unsigned char *data,
1000 unsigned int length)
1001 {
1002 JSNode *n = (JSNode *) type;
1003
1004 js_vm_make_string (interp->vm, n, data, length);
1005 }
1006
1007
1008 void
1009 js_type_make_array (JSInterpPtr interp, JSType *type, unsigned int length)
1010 {
1011 JSNode *n = (JSNode *) type;
1012
1013 js_vm_make_array (interp->vm, n, length);
1014 }
1015
1016
1017 void
1018 js_set_var (JSInterpPtr interp, char *name, JSType *value)
1019 {
1020 JSNode *n = &interp->vm->globals[js_vm_intern (interp->vm, name)];
1021 JS_COPY (n, (JSNode *) value);
1022 }
1023
1024
1025 void
1026 js_get_var (JSInterpPtr interp, char *name, JSType *value)
1027 {
1028 JSNode *n = &interp->vm->globals[js_vm_intern (interp->vm, name)];
1029 JS_COPY ((JSNode *) value, n);
1030 }
1031
1032
1033 void
1034 js_get_options (JSInterpPtr interp, JSInterpOptions *options)
1035 {
1036 memcpy (options, &interp->options, sizeof (*options));
1037 }
1038
1039
1040 void
1041 js_set_options (JSInterpPtr interp, JSInterpOptions *options)
1042 {
1043 memcpy (&interp->options, options, sizeof (*options));
1044
1045 /* User can change the security options, */
1046
1047 if (interp->options.secure_builtin_file)
1048 interp->vm->security |= JS_VM_SECURE_FILE;
1049 else
1050 interp->vm->security &= ~JS_VM_SECURE_FILE;
1051
1052 if (interp->options.secure_builtin_system)
1053 interp->vm->security |= JS_VM_SECURE_SYSTEM;
1054 else
1055 interp->vm->security &= ~JS_VM_SECURE_SYSTEM;
1056
1057 /* and the event hook. */
1058 interp->vm->hook = options->hook;
1059 interp->vm->hook_context = options->hook_context;
1060 interp->vm->hook_operand_count_trigger = options->hook_operand_count_trigger;
1061 }
1062
1063
1064 int
1065 js_create_global_method (JSInterpPtr interp, char *name,
1066 JSGlobalMethodProc proc, void *context,
1067 JSFreeProc context_free_proc)
1068 {
1069 JSNode *n = &interp->vm->globals[js_vm_intern (interp->vm, name)];
1070 JSVirtualMachine *vm = interp->vm;
1071 int result = 1;
1072
1073 /* Need one toplevel here. */
1074 {
1075 JSErrorHandlerFrame handler;
1076
1077 /* We must create the toplevel ourself. */
1078 memset (&handler, 0, sizeof (handler));
1079 handler.next = vm->error_handler;
1080 vm->error_handler = &handler;
1081
1082 if (setjmp (vm->error_handler->error_jmp))
1083 /* An error occurred. */
1084 result = 0;
1085 else
1086 {
1087 JSBuiltinInfo *info;
1088 JSGlobalMethodContext *ctx;
1089
1090 /* Context. */
1091 ctx = js_calloc (vm, 1, sizeof (*ctx));
1092
1093 ctx->proc = proc;
1094 ctx->context = context;
1095 ctx->free_proc = context_free_proc;
1096 ctx->interp = interp;
1097
1098 /* Info. */
1099 info = js_vm_builtin_info_create (vm);
1100 info->global_method_proc = js_global_method_stub;
1101 info->delete_proc = js_global_method_delete;
1102
1103 /* Create the builtin. */
1104 js_vm_builtin_create (interp->vm, n, info, ctx);
1105 }
1106
1107 /* Pop the error handler. */
1108 vm->error_handler = vm->error_handler->next;
1109 }
1110
1111 return result;
1112 }
1113
1114
1115 int
1116 js_define_module (JSInterpPtr interp, JSModuleInitProc init_proc)
1117 {
1118 JSErrorHandlerFrame handler;
1119 JSVirtualMachine *vm = interp->vm;
1120 int result = 1;
1121
1122 /* Just call the init proc in a toplevel. */
1123
1124 memset (&handler, 0, sizeof (handler));
1125 handler.next = vm->error_handler;
1126 vm->error_handler = &handler;
1127
1128 if (setjmp (vm->error_handler->error_jmp))
1129 /* An error occurred. */
1130 result = 0;
1131 else
1132 /* Call the module init proc. */
1133 (*init_proc) (interp);
1134
1135 /* Pop the error handler. */
1136 vm->error_handler = vm->error_handler->next;
1137
1138 return result;
1139 }
1140
1141
1142 \f
1143 /*
1144 * Static functions.
1145 */
1146
1147 static int
1148 js_eval_source (JSInterpPtr interp, JSNode *source, char *compiler_function)
1149 {
1150 JSNode argv[5];
1151 int i = 0;
1152 int result;
1153 JSByteCode *bc;
1154
1155 /* Let's compile the code. */
1156
1157 /* Argument count. */
1158 argv[i].type = JS_INTEGER;
1159 argv[i].u.vinteger = 4;
1160 i++;
1161
1162 /* Source to compiler. */
1163 JS_COPY (&argv[i], source);
1164 i++;
1165
1166 /* Flags. */
1167 argv[i].type = JS_INTEGER;
1168 argv[i].u.vinteger = 0;
1169
1170 if (interp->options.verbose)
1171 argv[i].u.vinteger = JSC_FLAG_VERBOSE;
1172
1173 argv[i].u.vinteger |= JSC_FLAG_GENERATE_DEBUG_INFO;
1174
1175 argv[i].u.vinteger |= JSC_FLAG_OPTIMIZE_PEEPHOLE;
1176 argv[i].u.vinteger |= JSC_FLAG_OPTIMIZE_JUMPS;
1177 argv[i].u.vinteger |= JSC_FLAG_WARN_WITH_CLOBBER;
1178 i++;
1179
1180 /* Assembler file. */
1181 argv[i].type = JS_NULL;
1182 i++;
1183
1184 /* Byte-code file. */
1185 argv[i].type = JS_NULL;
1186 i++;
1187
1188 /* Call the compiler entry point. */
1189 result = js_vm_apply (interp->vm, compiler_function, NULL, i, argv);
1190 if (result == 0)
1191 return 0;
1192
1193 /*
1194 * The resulting byte-code file is now at vm->exec_result.
1195 *
1196 * Note! The byte-code is a string allocated form the vm heap.
1197 * The garbage collector can free it when it wants since the result
1198 * isn't protected. However, we have no risk here because we
1199 * first convert the byte-code data block to our internal
1200 * JSByteCode block that shares no memory with the original data.
1201 */
1202
1203 assert (interp->vm->exec_result.type == JS_STRING);
1204
1205 bc = js_bc_read_data (interp->vm->exec_result.u.vstring->data,
1206 interp->vm->exec_result.u.vstring->len);
1207
1208 /* And finally, execute it. */
1209 result = js_vm_execute (interp->vm, bc);
1210
1211 /* Free the byte-code. */
1212 js_bc_free (bc);
1213
1214 return result;
1215 }
1216
1217
1218 static int
1219 js_compile_source (JSInterpPtr interp, JSNode *source,
1220 char *compiler_function, char *assembler_file,
1221 char *byte_code_file, JSNode *bc_return)
1222 {
1223 JSNode argv[5];
1224 int i = 0;
1225 int result;
1226
1227 /* Init arguments. */
1228
1229 argv[i].type = JS_INTEGER;
1230 argv[i].u.vinteger = 4;
1231 i++;
1232
1233 /* Source to compiler. */
1234 JS_COPY (&argv[1], source);
1235 i++;
1236
1237 /* Flags. */
1238 argv[i].type = JS_INTEGER;
1239 argv[i].u.vinteger = 0;
1240
1241 if (interp->options.verbose)
1242 argv[i].u.vinteger |= JSC_FLAG_VERBOSE;
1243 if (interp->options.annotate_assembler)
1244 argv[i].u.vinteger |= JSC_FLAG_ANNOTATE_ASSEMBLER;
1245 if (interp->options.debug_info)
1246 argv[i].u.vinteger |= JSC_FLAG_GENERATE_DEBUG_INFO;
1247 if (interp->options.executable_bc_files)
1248 argv[i].u.vinteger |= JSC_FLAG_GENERATE_EXECUTABLE_BC_FILES;
1249
1250 if (interp->options.warn_unused_argument)
1251 argv[i].u.vinteger |= JSC_FLAG_WARN_UNUSED_ARGUMENT;
1252 if (interp->options.warn_unused_variable)
1253 argv[i].u.vinteger |= JSC_FLAG_WARN_UNUSED_VARIABLE;
1254 if (interp->options.warn_shadow)
1255 argv[i].u.vinteger |= JSC_FLAG_WARN_SHADOW;
1256 if (interp->options.warn_with_clobber)
1257 argv[i].u.vinteger |= JSC_FLAG_WARN_WITH_CLOBBER;
1258 if (interp->options.warn_missing_semicolon)
1259 argv[i].u.vinteger |= JSC_FLAG_WARN_MISSING_SEMICOLON;
1260 if (interp->options.warn_strict_ecma)
1261 argv[i].u.vinteger |= JSC_FLAG_WARN_STRICT_ECMA;
1262 if (interp->options.warn_deprecated)
1263 argv[i].u.vinteger |= JSC_FLAG_WARN_DEPRECATED;
1264
1265 if (interp->options.optimize_peephole)
1266 argv[i].u.vinteger |= JSC_FLAG_OPTIMIZE_PEEPHOLE;
1267 if (interp->options.optimize_jumps_to_jumps)
1268 argv[i].u.vinteger |= JSC_FLAG_OPTIMIZE_JUMPS;
1269 if (interp->options.optimize_bc_size)
1270 argv[i].u.vinteger |= JSC_FLAG_OPTIMIZE_BC_SIZE;
1271 if (interp->options.optimize_heavy)
1272 argv[i].u.vinteger |= JSC_FLAG_OPTIMIZE_HEAVY;
1273
1274 i++;
1275
1276 /* Assembler file. */
1277 if (assembler_file)
1278 js_vm_make_static_string (interp->vm, &argv[i], assembler_file,
1279 strlen (assembler_file));
1280 else
1281 argv[i].type = JS_NULL;
1282 i++;
1283
1284 /* Byte-code file. */
1285 if (byte_code_file)
1286 js_vm_make_static_string (interp->vm, &argv[i], byte_code_file,
1287 strlen (byte_code_file));
1288 else
1289 argv[i].type = JS_NULL;
1290 i++;
1291
1292 /* Call the compiler entry point. */
1293 result = js_vm_apply (interp->vm, compiler_function, NULL, i, argv);
1294 if (result == 0)
1295 return 0;
1296
1297 if (bc_return)
1298 /* User wanted to get the resulting byte-code data. Here it is. */
1299 JS_COPY (bc_return, &interp->vm->exec_result);
1300
1301 return result;
1302 }
1303
1304
1305 /*
1306 * Global methods.
1307 */
1308
1309 static void
1310 eval_global_method (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
1311 void *instance_context, JSNode *result_return,
1312 JSNode *args)
1313 {
1314 JSInterpPtr interp = instance_context;
1315
1316 if (args->u.vinteger != 1)
1317 {
1318 sprintf (vm->error, "eval(): illegal amount of arguments");
1319 js_vm_error (vm);
1320 }
1321 if (args[1].type != JS_STRING)
1322 {
1323 /* Return it to the caller. */
1324 JS_COPY (result_return, &args[1]);
1325 return;
1326 }
1327
1328 /*
1329 * Ok, we'r ready to eval it. The source strings is our argument, so,
1330 * it is in the stack and therefore, protected for gc.
1331 */
1332 if (!js_eval_source (interp, &args[1], "JSC$compile_string"))
1333 {
1334 /* The evaluation failed. Throw it as an error to our caller. */
1335 js_vm_error (vm);
1336 }
1337
1338 /* Pass the return value to our caller. */
1339 JS_COPY (result_return, &vm->exec_result);
1340 }
1341
1342
1343 static void
1344 load_global_method (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
1345 void *instance_context,
1346 JSNode *result_return, JSNode *args)
1347 {
1348 JSInterpPtr interp = instance_context;
1349 int i;
1350 int result;
1351
1352 if (args->u.vinteger == 0)
1353 {
1354 sprintf (vm->error, "load(): no arguments given");
1355 js_vm_error (vm);
1356 }
1357
1358 for (i = 1; i <= args->u.vinteger; i++)
1359 {
1360 char *cp;
1361
1362 if (args[i].type != JS_STRING)
1363 {
1364 sprintf (vm->error, "load(): illegal argument");
1365 js_vm_error (vm);
1366 }
1367
1368 cp = js_string_to_c_string (vm, &args[i]);
1369 result = js_eval_file (interp, cp);
1370 js_free (cp);
1371
1372 if (!result)
1373 js_vm_error (vm);
1374 }
1375
1376 result_return->type = JS_BOOLEAN;
1377 result_return->u.vboolean = 1;
1378 }
1379
1380
1381 static void
1382 load_class_global_method (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
1383 void *instance_context,
1384 JSNode *result_return, JSNode *args)
1385 {
1386 JSInterpPtr interp = instance_context;
1387 int i;
1388
1389 if (args->u.vinteger == 0)
1390 {
1391 sprintf (vm->error, "loadClass(): no arguments given");
1392 js_vm_error (vm);
1393 }
1394
1395 for (i = 1; i <= args->u.vinteger; i++)
1396 {
1397 char *cp, *cp2;
1398 void *lib;
1399 void (*func) (JSInterpPtr interp);
1400 char *func_name;
1401 char buf[512];
1402
1403 if (args[i].type != JS_STRING)
1404 {
1405 sprintf (vm->error, "loadClass(): illegal argument");
1406 js_vm_error (vm);
1407 }
1408
1409 cp = js_string_to_c_string (vm, &args[i]);
1410
1411 /* Extract the function name. */
1412 func_name = strrchr (cp, ':');
1413 if (func_name == NULL)
1414 {
1415 func_name = strrchr (cp, '/');
1416 if (func_name == NULL)
1417 func_name = cp;
1418 else
1419 func_name++;
1420 }
1421 else
1422 {
1423 *func_name = '\0';
1424 func_name++;
1425 }
1426
1427 /* Try to open the library. */
1428 lib = js_dl_open (cp, buf, sizeof (buf));
1429 if (lib == NULL)
1430 {
1431 sprintf (vm->error, "loadClass(): couldn't open library `%s': %s",
1432 cp, buf);
1433 js_vm_error (vm);
1434 }
1435
1436 /*
1437 * Strip all suffixes from the library name: if the <func_name>
1438 * is extracted from it, this will convert the library name
1439 * `foo.so.x.y' to the canonical entry point name `foo'.
1440 */
1441 cp2 = strchr (cp, '.');
1442 if (cp2)
1443 *cp2 = '\0';
1444
1445 func = js_dl_sym (lib, func_name, buf, sizeof (buf));
1446 if (func == NULL)
1447 {
1448 sprintf (vm->error,
1449 "loadClass(): couldn't find the init function `%s': %s",
1450 func_name, buf);
1451 js_vm_error (vm);
1452 }
1453
1454 /* All done with this argument. */
1455 js_free (cp);
1456
1457 /*
1458 * And finally, call the library entry point. All possible errors
1459 * will throw us to the containing top-level.
1460 */
1461 (*func) (interp);
1462 }
1463
1464 result_return->type = JS_UNDEFINED;
1465 }
1466
1467
1468 static void
1469 call_method_global_method (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
1470 void *instance_context,
1471 JSNode *result_return, JSNode *args)
1472 {
1473 JSInterpPtr interp = instance_context;
1474 JSNode *argv;
1475 int i;
1476 int result;
1477 char *cp;
1478
1479 if (args->u.vinteger != 3)
1480 {
1481 sprintf (vm->error, "callMethod(): illegal amount of arguments");
1482 js_vm_error (vm);
1483 }
1484 if (args[2].type != JS_STRING)
1485 {
1486 illegal_argument:
1487 sprintf (vm->error, "callMethod(): illegal argument");
1488 js_vm_error (vm);
1489 }
1490 if (args[3].type != JS_ARRAY)
1491 goto illegal_argument;
1492
1493 /* Create the argument array. */
1494 argv = js_malloc (vm, (args[3].u.varray->length + 1) * sizeof (JSNode));
1495
1496 /* The argument count. */
1497 argv[0].type = JS_INTEGER;
1498 argv[0].u.vinteger = args[3].u.varray->length;
1499
1500 for (i = 0; i < args[3].u.varray->length; i++)
1501 JS_COPY (&argv[i + 1], &args[3].u.varray->data[i]);
1502
1503 /* Method name to C string. */
1504 cp = js_string_to_c_string (vm, &args[2]);
1505
1506 /* Call it. */
1507 result = js_vm_call_method (vm, &args[1], cp, args[3].u.varray->length + 1,
1508 argv);
1509
1510 /* Cleanup. */
1511 js_free (cp);
1512 js_free (argv);
1513
1514 if (result)
1515 JS_COPY (result_return, &vm->exec_result);
1516 else
1517 /* The error message is already there. */
1518 js_vm_error (vm);
1519 }
1520
1521
1522 static void
1523 js_core_globals (JSInterpPtr interp)
1524 {
1525 JSNode *n;
1526 JSBuiltinInfo *info;
1527 JSVirtualMachine *vm = interp->vm;
1528
1529 if (!interp->options.no_compiler)
1530 {
1531 /* Command `eval'. */
1532
1533 info = js_vm_builtin_info_create (vm);
1534 info->global_method_proc = eval_global_method;
1535
1536 n = &interp->vm->globals[js_vm_intern (interp->vm, "eval")];
1537
1538 js_vm_builtin_create (interp->vm, n, info, interp);
1539 }
1540
1541 /* Command `load'. */
1542
1543 info = js_vm_builtin_info_create (vm);
1544 info->global_method_proc = load_global_method;
1545
1546 n = &interp->vm->globals[js_vm_intern (interp->vm, "load")];
1547 js_vm_builtin_create (interp->vm, n, info, interp);
1548
1549 /* Command `loadClass'. */
1550
1551 info = js_vm_builtin_info_create (vm);
1552 info->global_method_proc = load_class_global_method;
1553
1554 n = &interp->vm->globals[js_vm_intern (interp->vm, "loadClass")];
1555 js_vm_builtin_create (interp->vm, n, info, interp);
1556
1557 /* Command `callMethod'. */
1558
1559 info = js_vm_builtin_info_create (vm);
1560 info->global_method_proc = call_method_global_method;
1561
1562 n = &interp->vm->globals[js_vm_intern (interp->vm, "callMethod")];
1563 js_vm_builtin_create (interp->vm, n, info, interp);
1564 }
1565
1566
1567 static void
1568 js_global_method_stub (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
1569 void *instance_context, JSNode *result_return,
1570 JSNode *args)
1571 {
1572 JSMethodResult result;
1573 JSGlobalMethodContext *ctx = instance_context;
1574
1575 /* Set the default result. */
1576 result_return->type = JS_UNDEFINED;
1577
1578 /* Call the user supplied function. */
1579 result = (*ctx->proc) (ctx->context, ctx->interp, args->u.vinteger,
1580 (JSType *) &args[1], (JSType *) result_return,
1581 vm->error);
1582 if (result != JS_OK)
1583 js_vm_error (ctx->interp->vm);
1584 }
1585
1586
1587 static void
1588 js_global_method_delete (JSBuiltinInfo *builtin_info, void *instance_context)
1589 {
1590 JSGlobalMethodContext *ctx = instance_context;
1591
1592 if (ctx)
1593 {
1594 if (ctx->free_proc)
1595 (*ctx->free_proc) (ctx->context);
1596
1597 js_free (ctx);
1598 }
1599 }
1600
1601
1602 /* I/O Stream to user I/O function. */
1603
1604 static int
1605 iofunc_io (void *context, unsigned char *buffer, unsigned int todo,
1606 int *error_return)
1607 {
1608 JSUserIOFuncCtx *ctx = context;
1609 int moved;
1610
1611 *error_return = 0;
1612
1613 moved = (*ctx->func) (ctx->context, buffer, todo);
1614 if (moved >= 0)
1615 ctx->position += moved;
1616
1617 return moved;
1618 }
1619
1620
1621 static int
1622 iofunc_seek (void *context, long offset, int whence)
1623 {
1624 return -1;
1625 }
1626
1627
1628 static long
1629 iofunc_get_position (void *context)
1630 {
1631 JSUserIOFuncCtx *ctx = context;
1632
1633 return ctx->position;
1634 }
1635
1636
1637 static long
1638 iofunc_get_length (void *context)
1639 {
1640 return -1;
1641 }
1642
1643
1644 static void
1645 iofunc_close (void *context)
1646 {
1647 js_free (context);
1648 }
1649
1650
1651 static JSIOStream *
1652 iostream_iofunc (JSIOFunc func, void *context, int readp, int writep)
1653 {
1654 JSIOStream *stream = js_iostream_new ();
1655 JSUserIOFuncCtx *ctx;
1656
1657 if (stream == NULL)
1658 return NULL;
1659
1660 ctx = js_malloc (NULL, sizeof (*ctx));
1661 if (ctx == NULL)
1662 {
1663 (void) js_iostream_close (stream);
1664 return NULL;
1665 }
1666
1667 /* Init context. */
1668 ctx->func = func;
1669 ctx->context = context;
1670 ctx->position = 0;
1671
1672 if (readp)
1673 stream->read = iofunc_io;
1674 if (writep)
1675 stream->write = iofunc_io;
1676
1677 stream->seek = iofunc_seek;
1678 stream->get_position = iofunc_get_position;
1679 stream->get_length = iofunc_get_length;
1680 stream->close = iofunc_close;
1681 stream->context = ctx;
1682
1683 return stream;
1684 }