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