migrate substitution keywords to SVN
[reactos.git] / reactos / lib / kjs / src / vmswitch.c
1 /*
2 * Optimized `switch' instruction dispatcher.
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/vmswitch.c,v $
27 * $Id$
28 */
29
30 #include "jsint.h"
31
32 /*
33 * Types and definitions.
34 */
35
36 #define SAVE_OP(a) \
37 reloc[cp - fixed_code - 1] = &f->code[cpos]; \
38 f->code[cpos++].u.op = (a)
39
40 #define SAVE_INT8(a) f->code[cpos++].u.i8 = (a)
41 #define SAVE_INT16(a) f->code[cpos++].u.i16 = (a)
42 #define SAVE_INT32(a) f->code[cpos++].u.i32 = (a)
43
44 #define ARG_INT32() f->code[cpos].u.i32
45
46 #define READ_INT8(var) (var) = (pc++)->u.i8
47 #define READ_INT16(var) (var) = (pc++)->u.i16
48 #define READ_INT32(var) (var) = (pc++)->u.i32
49
50 #define SETPC(ofs) pc = (ofs)
51 #define SETPC_RELATIVE(ofs) pc += (ofs)
52
53 #define CALL_USER_FUNC(f) pc = ((Function *) (f))->code
54
55 #define DONE() goto done
56
57 #define ERROR(msg) \
58 do { \
59 JS_SAVE_REGS (); \
60 strcpy (vm->error, (msg)); \
61 js_vm_error (vm); \
62 /* NOTREACHED */ \
63 } while (0)
64
65
66 struct compiled_st
67 {
68 union
69 {
70 void *ptr;
71 JSUInt8 op;
72 JSInt8 i8;
73 JSInt16 i16;
74 JSInt32 i32;
75 } u;
76 };
77
78 typedef struct compiled_st Compiled;
79
80 /* Debug information. */
81 struct debug_info_st
82 {
83 void *pc;
84 unsigned int linenum;
85 };
86
87 typedef struct debug_info_st DebugInfo;
88
89 struct function_st
90 {
91 JSHeapDestroyableCB destroy;
92
93 char *name;
94 Compiled *code;
95 unsigned int length;
96
97 struct
98 {
99 char *file;
100 unsigned int num_info;
101 DebugInfo *info;
102 } debug;
103 };
104
105 typedef struct function_st Function;
106
107
108 /*
109 * Prototypes for static functions.
110 */
111
112 static void function_destroy (void *ptr);
113
114 static Function *link_code (JSVirtualMachine *vm, unsigned char *code,
115 unsigned int code_len,
116 unsigned int consts_offset,
117 unsigned char *debug_info,
118 unsigned int debug_info_len,
119 unsigned int code_offset);
120
121 static void execute_code (JSVirtualMachine *vm, JSNode *object, Function *f,
122 unsigned int argc, JSNode *argv);
123
124
125 /*
126 * Global functions.
127 */
128
129 int
130 js_vm_switch_exec (JSVirtualMachine *vm, JSByteCode *bc, JSSymtabEntry *symtab,
131 unsigned int num_symtab_entries,
132 unsigned int consts_offset,
133 unsigned int anonymous_function_offset,
134 unsigned char *debug_info, unsigned int debug_info_len,
135 JSNode *object, JSNode *func,
136 unsigned int argc, JSNode *argv)
137 {
138 int i;
139 unsigned int ui;
140 Function *global_f = NULL;
141 Function *f;
142 unsigned char *code = NULL;
143 char buf[512];
144
145 if (bc)
146 {
147 /* Executing byte-code. */
148
149 /* Find the code section. */
150 for (i = 0; i < bc->num_sects; i++)
151 if (bc->sects[i].type == JS_BCST_CODE)
152 code = bc->sects[i].data;
153 assert (code != NULL);
154
155 /* Enter all functions to the known functions of the VM. */
156 for (i = 0; i < num_symtab_entries; i++)
157 {
158 /* Link the code to our environment. */
159 f = link_code (vm, code + symtab[i].offset,
160 symtab[i + 1].offset - symtab[i].offset,
161 consts_offset, debug_info, debug_info_len,
162 symtab[i].offset);
163 f->name = js_strdup (vm, symtab[i].name);
164
165 if (strcmp (symtab[i].name, JS_GLOBAL_NAME) == 0)
166 global_f = f;
167 else
168 {
169 int is_anonymous = 0;
170
171 /* Check for the anonymous function. */
172 if (symtab[i].name[0] == '.' && symtab[i].name[1] == 'F'
173 && symtab[i].name[2] == ':')
174 is_anonymous = 1;
175
176 if (vm->verbose > 3)
177 {
178 sprintf (buf, "VM: link: %s(): start=%d, length=%d",
179 symtab[i].name, symtab[i].offset,
180 symtab[i + 1].offset - symtab[i].offset);
181 if (is_anonymous)
182 sprintf (buf + strlen (buf),
183 ", relocating with offset %u",
184 anonymous_function_offset);
185 strcat (buf, JS_HOST_LINE_BREAK);
186 js_iostream_write (vm->s_stderr, buf, strlen (buf));
187 }
188
189 if (is_anonymous)
190 {
191 sprintf (buf, ".F:%u",
192 (unsigned int) atoi (symtab[i].name + 3)
193 + anonymous_function_offset);
194 ui = js_vm_intern (vm, buf);
195 }
196 else
197 ui = js_vm_intern (vm, symtab[i].name);
198
199 vm->globals[ui].type = JS_FUNC;
200 vm->globals[ui].u.vfunction = js_vm_make_function (vm, f);
201 }
202 }
203 }
204 else
205 {
206 /* Applying arguments to function. */
207
208 if (func->type != JS_FUNC)
209 {
210 sprintf (vm->error, "illegal function in apply");
211 return 0;
212 }
213
214 if (vm->verbose > 1)
215 {
216 sprintf (buf, "VM: calling function%s",
217 JS_HOST_LINE_BREAK);
218 js_iostream_write (vm->s_stderr, buf, strlen (buf));
219 }
220 f = func->u.vfunction->implementation;
221
222 execute_code (vm, object, f, argc, argv);
223 }
224
225 if (global_f)
226 {
227 if (vm->verbose > 1)
228 {
229 sprintf (buf, "VM: exec: %s%s", global_f->name,
230 JS_HOST_LINE_BREAK);
231 js_iostream_write (vm->s_stderr, buf, strlen (buf));
232 }
233
234 /* Execute. */
235 execute_code (vm, NULL, global_f, 0, NULL);
236 }
237
238 return 1;
239 }
240
241
242 const char *
243 js_vm_switch_func_name (JSVirtualMachine *vm, void *program_counter)
244 {
245 int i;
246 Function *f;
247 Compiled *pc = program_counter;
248 JSNode *sp = vm->sp;
249
250 /* Check the globals. */
251 for (i = 0; i < vm->num_globals; i++)
252 if (vm->globals[i].type == JS_FUNC)
253 {
254 f = (Function *) vm->globals[i].u.vfunction->implementation;
255 if (f->code < pc && pc < f->code + f->length)
256 return f->name;
257 }
258
259 /* No luck. Let's try the stack. */
260 for (sp++; sp < vm->stack + vm->stack_size; sp++)
261 if (sp->type == JS_FUNC)
262 {
263 f = (Function *) sp->u.vfunction->implementation;
264 if (f->code < pc && pc < f->code + f->length)
265 return f->name;
266 }
267
268 /* Still no matches. This shouldn't be reached... ok, who cares? */
269 return JS_GLOBAL_NAME;
270 }
271
272
273 const char *
274 js_vm_switch_debug_position (JSVirtualMachine *vm,
275 unsigned int *linenum_return)
276 {
277 int i;
278 Function *f;
279 void *program_counter = vm->pc;
280 Compiled *pc = vm->pc;
281 JSNode *sp = vm->sp;
282 unsigned int linenum = 0;
283
284 /* Check the globals. */
285 for (i = 0; i < vm->num_globals; i++)
286 if (vm->globals[i].type == JS_FUNC)
287 {
288 f = (Function *) vm->globals[i].u.vfunction->implementation;
289 if (f->code < pc && pc < f->code + f->length)
290 {
291 found:
292
293 /* Ok, found it. */
294 if (f->debug.file == NULL)
295 /* No debugging information available for this function. */
296 return NULL;
297
298 /* Find the correct pc position. */
299 for (i = 0; i < f->debug.num_info; i++)
300 {
301 if (f->debug.info[i].pc > program_counter)
302 break;
303
304 linenum = f->debug.info[i].linenum;
305 }
306
307 *linenum_return = linenum;
308 return f->debug.file;
309 }
310 }
311
312 /* No luck. Let's try the stack. */
313 for (sp++; sp < vm->stack + vm->stack_size; sp++)
314 if (sp->type == JS_FUNC)
315 {
316 f = (Function *) sp->u.vfunction->implementation;
317 if (f->code < pc && pc < f->code + f->length)
318 /* Found it. */
319 goto found;
320 }
321
322 /* Couldn't find the function we are executing. */
323 return NULL;
324 }
325
326 \f
327 /*
328 * Static functions.
329 */
330
331 static void
332 function_destroy (void *ptr)
333 {
334 Function *f = ptr;
335
336 js_free (f->name);
337 js_free (f->code);
338
339 if (f->debug.file)
340 js_free (f->debug.file);
341 if (f->debug.info)
342 js_free (f->debug.info);
343 }
344
345
346 static Function *
347 link_code (JSVirtualMachine *vm, unsigned char *code, unsigned int code_len,
348 unsigned int consts_offset, unsigned char *debug_info,
349 unsigned int debug_info_len, unsigned int code_offset)
350 {
351 unsigned char *cp, *end;
352 JSInt32 i;
353 Compiled **reloc;
354 unsigned int cpos;
355 Function *f;
356 unsigned char *fixed_code;
357 unsigned int ui;
358 char *debug_filename = "unknown";
359 char buf[512];
360
361 /* Terminate the code with op `done'. */
362 fixed_code = js_malloc (vm, code_len + 1);
363 memcpy (fixed_code, code, code_len);
364 fixed_code[code_len] = 1; /* op `done' */
365
366 cp = fixed_code;
367 end = fixed_code + code_len + 1;
368
369 /* Alloc function closure. */
370 f = js_vm_alloc_destroyable (vm, sizeof (*f));
371 f->destroy = function_destroy;
372
373 /* Allocate space for our compiled code. <length> is enought. */
374 f->code = js_malloc (vm, (code_len + 1) * sizeof (Compiled));
375 reloc = js_calloc (vm, code_len + 1, sizeof (Compiled *));
376
377 /* Link phase 1: constants and symbols. */
378 cpos = 0;
379 while (cp < end)
380 {
381 switch (*cp++)
382 {
383 /* include c1switch.h */
384 #include "c1switch.h"
385 /* end include c1switch.h */
386 }
387 }
388 f->length = cpos;
389
390 /* Link phase 2: relative jumps. */
391 cp = fixed_code;
392 cpos = 0;
393 while (cp < end)
394 {
395 switch (*cp++)
396 {
397 /* include c2switch.h */
398 #include "c2switch.h"
399 /* end include c2switch.h */
400 }
401 }
402 /* Handle debug info. */
403 if (debug_info)
404 {
405 unsigned int di_start = code_offset;
406 unsigned int di_end = code_offset + code_len;
407 unsigned int ln;
408
409 for (; debug_info_len > 0;)
410 {
411 switch (*debug_info)
412 {
413 case JS_DI_FILENAME:
414 debug_info++;
415 debug_info_len--;
416
417 JS_BC_READ_INT32 (debug_info, ui);
418 debug_info += 4;
419 debug_info_len -= 4;
420
421 f->debug.file = js_malloc (vm, ui + 1);
422 memcpy (f->debug.file, debug_info, ui);
423 f->debug.file[ui] = '\0';
424
425 debug_filename = f->debug.file;
426
427 debug_info += ui;
428 debug_info_len -= ui;
429 break;
430
431 case JS_DI_LINENUMBER:
432 JS_BC_READ_INT32 (debug_info + 1, ui);
433 if (ui > di_end)
434 goto debug_info_done;
435
436 /* This belongs to us (maybe). */
437 debug_info += 5;
438 debug_info_len -= 5;
439
440 JS_BC_READ_INT32 (debug_info, ln);
441 debug_info += 4;
442 debug_info_len -= 4;
443
444 if (di_start <= ui && ui <= di_end)
445 {
446 ui -= di_start;
447 f->debug.info = js_realloc (vm, f->debug.info,
448 (f->debug.num_info + 1)
449 * sizeof (DebugInfo));
450
451 f->debug.info[f->debug.num_info].pc = reloc[ui];
452 f->debug.info[f->debug.num_info].linenum = ln;
453 f->debug.num_info++;
454 }
455 break;
456
457 default:
458 sprintf (buf,
459 "VM: unknown debug information type %d%s",
460 *debug_info, JS_HOST_LINE_BREAK);
461
462 js_iostream_write (vm->s_stderr, buf, strlen (buf));
463 js_iostream_flush (vm->s_stderr);
464
465 abort ();
466 break;
467 }
468 }
469
470 debug_info_done:
471 if (f->debug.file == NULL)
472 f->debug.file = js_strdup (vm, debug_filename);
473 }
474
475 js_free (reloc);
476 js_free (fixed_code);
477
478 return f;
479 }
480
481 \f
482 /*
483 * Execute byte code by using the `switch' dispatch technique.
484 */
485
486 static void
487 execute_code (JSVirtualMachine *vm, JSNode *object, Function *f,
488 unsigned int argc, JSNode *argv)
489 {
490 JSNode *sp;
491 JSNode *fp;
492 JSNode *function;
493 JSNode builtin_result;
494 Compiled *pc;
495 JSInt32 i, j;
496 JSInt8 i8;
497 char buf[512];
498
499 /* Create the initial stack frame by hand. */
500 sp = vm->sp;
501
502 /* Protect the function from gc. */
503 JS_SP0->type = JS_FUNC;
504 JS_SP0->u.vfunction = js_vm_make_function (vm, f);
505 JS_PUSH ();
506
507 /* Push arguments to the stack. */
508 i = argc;
509 for (i--; i >= 0; i--)
510 {
511 JS_COPY (JS_SP0, &argv[i]);
512 JS_PUSH ();
513 }
514
515 /* This pointer. */
516 if (object)
517 JS_COPY (JS_SP0, object);
518 else
519 JS_SP0->type = JS_NULL;
520 JS_PUSH ();
521
522 /* Init fp and pc so our SUBROUTINE_CALL will work. */
523 fp = NULL;
524 pc = NULL;
525
526 JS_SUBROUTINE_CALL (f);
527
528 /* Ok, now we are ready to run. */
529
530 while (1)
531 {
532 switch ((pc++)->u.op)
533 {
534 /* include eswitch.h */
535 #include "eswitch.h"
536 /* end include eswitch.h */
537
538 default:
539 sprintf (buf, "execute_code: unknown opcode %d%s",
540 (pc - 1)->u.op, JS_HOST_LINE_BREAK);
541 js_iostream_write (vm->s_stderr, buf, strlen (buf));
542 js_iostream_flush (vm->s_stderr);
543
544 abort ();
545 break;
546 }
547 }
548
549 done:
550
551 /* All done. */
552
553 JS_COPY (&vm->exec_result, JS_SP1);
554 }