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