source: mainline/uspace/app/sbi/src/run.c@ 1ef0fc3

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1ef0fc3 was 074444f, checked in by Jiri Svoboda <jiri@…>, 16 years ago

Update SBI to rev. 184.

  • Property mode set to 100644
File size: 31.2 KB
Line 
1/*
2 * Copyright (c) 2010 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @file Runner (executes the code). */
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <assert.h>
34#include "bigint.h"
35#include "builtin.h"
36#include "debug.h"
37#include "intmap.h"
38#include "list.h"
39#include "mytypes.h"
40#include "rdata.h"
41#include "run_expr.h"
42#include "run_texpr.h"
43#include "stree.h"
44#include "strtab.h"
45#include "symbol.h"
46#include "tdata.h"
47
48#include "run.h"
49
50static void run_block(run_t *run, stree_block_t *block);
51static void run_exps(run_t *run, stree_exps_t *exps, rdata_item_t **res);
52static void run_vdecl(run_t *run, stree_vdecl_t *vdecl);
53static void run_if(run_t *run, stree_if_t *if_s);
54static void run_while(run_t *run, stree_while_t *while_s);
55static void run_raise(run_t *run, stree_raise_t *raise_s);
56static void run_return(run_t *run, stree_return_t *return_s);
57static void run_wef(run_t *run, stree_wef_t *wef_s);
58
59static bool_t run_exc_match(run_t *run, stree_except_t *except_c);
60static stree_csi_t *run_exc_payload_get_csi(run_t *run);
61
62static rdata_var_t *run_aprop_get_tpos(run_t *run, rdata_address_t *aprop);
63
64static void run_aprop_read(run_t *run, rdata_addr_prop_t *addr_prop,
65 rdata_item_t **ritem);
66static void run_aprop_write(run_t *run, rdata_addr_prop_t *addr_prop,
67 rdata_value_t *value);
68
69/** Initialize runner instance. */
70void run_init(run_t *run)
71{
72 (void) run;
73}
74
75/** Run program */
76void run_program(run_t *run, stree_program_t *prog)
77{
78 stree_symbol_t *main_fun_sym;
79 stree_fun_t *main_fun;
80 stree_ident_t *fake_ident;
81 list_t main_args;
82 run_proc_ar_t *proc_ar;
83 rdata_item_t *res;
84
85 /* Note down link to program code. */
86 run->program = prog;
87
88 /* Initialize thread activation record. */
89 run->thread_ar = run_thread_ar_new();
90 list_init(&run->thread_ar->proc_ar);
91 run->thread_ar->bo_mode = bm_none;
92
93 /*
94 * Find entry point @c Main().
95 */
96 fake_ident = stree_ident_new();
97 fake_ident->sid = strtab_get_sid("Main");
98 main_fun_sym = symbol_find_epoint(prog, fake_ident);
99 if (main_fun_sym == NULL) {
100 printf("Error: Entry point 'Main' not found.\n");
101 exit(1);
102 }
103
104 main_fun = symbol_to_fun(main_fun_sym);
105 assert(main_fun != NULL);
106
107#ifdef DEBUG_RUN_TRACE
108 printf("Found function '"); symbol_print_fqn(main_fun_sym);
109 printf("'.\n");
110#endif
111
112 /* Run function @c main. */
113 list_init(&main_args);
114 run_proc_ar_create(run, NULL, main_fun->proc, &proc_ar);
115 run_proc_ar_set_args(run, proc_ar, &main_args);
116 run_proc(run, proc_ar, &res);
117
118 run_exc_check_unhandled(run);
119}
120
121/** Run procedure. */
122void run_proc(run_t *run, run_proc_ar_t *proc_ar, rdata_item_t **res)
123{
124 stree_proc_t *proc;
125 list_node_t *node;
126
127 proc = proc_ar->proc;
128
129#ifdef DEBUG_RUN_TRACE
130 printf("Start executing function '");
131 symbol_print_fqn(proc->outer_symbol);
132 printf("'.\n");
133#endif
134 /* Add procedure AR to the stack. */
135 list_append(&run->thread_ar->proc_ar, proc_ar);
136
137 /* Run main procedure block. */
138 if (proc->body != NULL) {
139 run_block(run, proc->body);
140 } else {
141 builtin_run_proc(run, proc);
142 }
143
144 /* Handle bailout. */
145 switch (run->thread_ar->bo_mode) {
146 case bm_stat:
147 printf("Error: Misplaced 'break' statement.\n");
148 exit(1);
149 case bm_proc:
150 run->thread_ar->bo_mode = bm_none;
151 break;
152 default:
153 break;
154 }
155
156#ifdef DEBUG_RUN_TRACE
157 printf("Done executing procedure '");
158 symbol_print_fqn(proc->outer_symbol);
159 printf("'.\n");
160
161 run_print_fun_bt(run);
162#endif
163 /* Remove procedure activation record from the stack. */
164 node = list_last(&run->thread_ar->proc_ar);
165 assert(list_node_data(node, run_proc_ar_t *) == proc_ar);
166 list_remove(&run->thread_ar->proc_ar, node);
167
168 /* Procedure should not return an address. */
169 assert(proc_ar->retval == NULL || proc_ar->retval->ic == ic_value);
170 *res = proc_ar->retval;
171}
172
173/** Run code block */
174static void run_block(run_t *run, stree_block_t *block)
175{
176 run_proc_ar_t *proc_ar;
177 run_block_ar_t *block_ar;
178 list_node_t *node;
179 stree_stat_t *stat;
180
181#ifdef DEBUG_RUN_TRACE
182 printf("Executing one code block.\n");
183#endif
184
185 /* Create block activation record. */
186 block_ar = run_block_ar_new();
187 intmap_init(&block_ar->vars);
188
189 /* Add block activation record to the stack. */
190 proc_ar = run_get_current_proc_ar(run);
191 list_append(&proc_ar->block_ar, block_ar);
192
193 node = list_first(&block->stats);
194 while (node != NULL) {
195 stat = list_node_data(node, stree_stat_t *);
196 run_stat(run, stat, NULL);
197
198 if (run->thread_ar->bo_mode != bm_none)
199 break;
200
201 node = list_next(&block->stats, node);
202 }
203
204#ifdef DEBUG_RUN_TRACE
205 printf("Done executing code block.\n");
206#endif
207
208 /* Remove block activation record from the stack. */
209 node = list_last(&proc_ar->block_ar);
210 assert(list_node_data(node, run_block_ar_t *) == block_ar);
211 list_remove(&proc_ar->block_ar, node);
212}
213
214/** Run statement.
215 *
216 * Executes a statement. If @a res is not NULL and the statement is an
217 * expression statement with a value, the value item will be stored to
218 * @a res.
219 *
220 * @param run Runner object.
221 * @param stat Statement to run.
222 * @param res Place to store exps result or NULL if not interested.
223 */
224void run_stat(run_t *run, stree_stat_t *stat, rdata_item_t **res)
225{
226#ifdef DEBUG_RUN_TRACE
227 printf("Executing one statement %p.\n", stat);
228#endif
229
230 if (res != NULL)
231 *res = NULL;
232
233 switch (stat->sc) {
234 case st_exps:
235 run_exps(run, stat->u.exp_s, res);
236 break;
237 case st_vdecl:
238 run_vdecl(run, stat->u.vdecl_s);
239 break;
240 case st_if:
241 run_if(run, stat->u.if_s);
242 break;
243 case st_while:
244 run_while(run, stat->u.while_s);
245 break;
246 case st_raise:
247 run_raise(run, stat->u.raise_s);
248 break;
249 case st_return:
250 run_return(run, stat->u.return_s);
251 break;
252 case st_wef:
253 run_wef(run, stat->u.wef_s);
254 break;
255 case st_for:
256 printf("Ignoring unimplemented statement type %d.\n", stat->sc);
257 break;
258 default:
259 assert(b_false);
260 }
261}
262
263/** Run expression statement.
264 *
265 * Executes an expression statement. If @a res is not NULL then the value
266 * of the expression (or NULL if it has no value) will be stored to @a res.
267 *
268 * @param run Runner object.
269 * @param exps Expression statement to run.
270 * @param res Place to store exps result or NULL if not interested.
271 */
272static void run_exps(run_t *run, stree_exps_t *exps, rdata_item_t **res)
273{
274 rdata_item_t *rexpr;
275
276#ifdef DEBUG_RUN_TRACE
277 printf("Executing expression statement.\n");
278#endif
279 run_expr(run, exps->expr, &rexpr);
280
281 if (res != NULL)
282 *res = rexpr;
283}
284
285/** Run variable declaration statement. */
286static void run_vdecl(run_t *run, stree_vdecl_t *vdecl)
287{
288 run_block_ar_t *block_ar;
289 rdata_var_t *var, *old_var;
290 rdata_int_t *int_v;
291
292#ifdef DEBUG_RUN_TRACE
293 printf("Executing variable declaration statement.\n");
294#endif
295
296 /* XXX Need to support other variables than int. */
297
298 var = rdata_var_new(vc_int);
299 int_v = rdata_int_new();
300
301 var->u.int_v = int_v;
302 bigint_init(&int_v->value, 0);
303
304 block_ar = run_get_current_block_ar(run);
305 old_var = (rdata_var_t *) intmap_get(&block_ar->vars, vdecl->name->sid);
306
307 if (old_var != NULL) {
308 printf("Error: Duplicate variable '%s'\n",
309 strtab_get_str(vdecl->name->sid));
310 exit(1);
311 }
312
313 intmap_set(&block_ar->vars, vdecl->name->sid, var);
314
315#ifdef DEBUG_RUN_TRACE
316 printf("Declared variable '%s'\n", strtab_get_str(vdecl->name->sid));
317#endif
318}
319
320/** Run @c if statement. */
321static void run_if(run_t *run, stree_if_t *if_s)
322{
323 rdata_item_t *rcond;
324
325#ifdef DEBUG_RUN_TRACE
326 printf("Executing if statement.\n");
327#endif
328 run_expr(run, if_s->cond, &rcond);
329 if (run_is_bo(run))
330 return;
331
332 if (run_item_boolean_value(run, rcond) == b_true) {
333#ifdef DEBUG_RUN_TRACE
334 printf("Taking true path.\n");
335#endif
336 run_block(run, if_s->if_block);
337 } else {
338#ifdef DEBUG_RUN_TRACE
339 printf("Taking false path.\n");
340#endif
341 if (if_s->else_block != NULL)
342 run_block(run, if_s->else_block);
343 }
344
345#ifdef DEBUG_RUN_TRACE
346 printf("If statement terminated.\n");
347#endif
348}
349
350/** Run @c while statement. */
351static void run_while(run_t *run, stree_while_t *while_s)
352{
353 rdata_item_t *rcond;
354
355#ifdef DEBUG_RUN_TRACE
356 printf("Executing while statement.\n");
357#endif
358 run_expr(run, while_s->cond, &rcond);
359 if (run_is_bo(run))
360 return;
361
362 while (run_item_boolean_value(run, rcond) == b_true) {
363 run_block(run, while_s->body);
364 run_expr(run, while_s->cond, &rcond);
365 if (run_is_bo(run))
366 return;
367
368 if (run->thread_ar->bo_mode != bm_none)
369 break;
370 }
371
372#ifdef DEBUG_RUN_TRACE
373 printf("While statement terminated.\n");
374#endif
375}
376
377/** Run @c raise statement. */
378static void run_raise(run_t *run, stree_raise_t *raise_s)
379{
380 rdata_item_t *rexpr;
381 rdata_item_t *rexpr_vi;
382
383#ifdef DEBUG_RUN_TRACE
384 printf("Executing raise statement.\n");
385#endif
386 run_expr(run, raise_s->expr, &rexpr);
387 if (run_is_bo(run))
388 return;
389
390 run_cvt_value_item(run, rexpr, &rexpr_vi);
391
392 /* Store expression result in thread AR. */
393 run->thread_ar->exc_payload = rexpr_vi->u.value;
394
395 /* Start exception bailout. */
396 run->thread_ar->bo_mode = bm_exc;
397}
398
399/** Run @c return statement. */
400static void run_return(run_t *run, stree_return_t *return_s)
401{
402 rdata_item_t *rexpr;
403 rdata_item_t *rexpr_vi;
404 run_proc_ar_t *proc_ar;
405
406#ifdef DEBUG_RUN_TRACE
407 printf("Executing return statement.\n");
408#endif
409 run_expr(run, return_s->expr, &rexpr);
410 if (run_is_bo(run))
411 return;
412
413 run_cvt_value_item(run, rexpr, &rexpr_vi);
414
415 /* Store expression result in function AR. */
416 proc_ar = run_get_current_proc_ar(run);
417 proc_ar->retval = rexpr_vi;
418
419 /* Force control to ascend and leave the procedure. */
420 if (run->thread_ar->bo_mode == bm_none)
421 run->thread_ar->bo_mode = bm_proc;
422}
423
424/** Run @c with-except-finally statement. */
425static void run_wef(run_t *run, stree_wef_t *wef_s)
426{
427 list_node_t *except_n;
428 stree_except_t *except_c;
429 rdata_value_t *exc_payload;
430 run_bailout_mode_t bo_mode;
431
432#ifdef DEBUG_RUN_TRACE
433 printf("Executing with-except-finally statement.\n");
434#endif
435 run_block(run, wef_s->with_block);
436
437 if (run->thread_ar->bo_mode == bm_exc) {
438#ifdef DEBUG_RUN_TRACE
439 printf("With statement detected exception.\n");
440#endif
441 /* Reset to normal execution. */
442 run->thread_ar->bo_mode = bm_none;
443
444 /* Look for an except block. */
445 except_n = list_first(&wef_s->except_clauses);
446 while (except_n != NULL) {
447 except_c = list_node_data(except_n, stree_except_t *);
448 if (run_exc_match(run, except_c))
449 break;
450
451 except_n = list_next(&wef_s->except_clauses, except_n);
452 }
453
454 /* If one was found, execute it. */
455 if (except_n != NULL)
456 run_block(run, except_c->block);
457
458 /* Execute finally block */
459 if (wef_s->finally_block != NULL) {
460 /* Put exception on the side temporarily. */
461 bo_mode = run->thread_ar->bo_mode;
462 exc_payload = run->thread_ar->exc_payload;
463
464 run->thread_ar->bo_mode = bm_none;
465 run->thread_ar->exc_payload = NULL;
466
467 run_block(run, wef_s->finally_block);
468
469 if (bo_mode == bm_exc) {
470 /*
471 * Restore the original exception. If another
472 * exception occured in the finally block (i.e.
473 * double fault), it is forgotten.
474 */
475 run->thread_ar->bo_mode = bm_exc;
476 run->thread_ar->exc_payload = exc_payload;
477 }
478 }
479 }
480
481#ifdef DEBUG_RUN_TRACE
482 printf("With-except-finally statement terminated.\n");
483#endif
484}
485
486/** Determine whether currently active exception matches @c except clause.
487 *
488 * Checks if the currently active exception in the runner object @c run
489 * matches except clause @c except_c.
490 *
491 * @param run Runner object.
492 * @param except_c @c except clause.
493 * @return @c b_true if there is a match, @c b_false otherwise.
494 */
495static bool_t run_exc_match(run_t *run, stree_except_t *except_c)
496{
497 stree_csi_t *exc_csi;
498 tdata_item_t *etype;
499
500 /* Get CSI of active exception. */
501 exc_csi = run_exc_payload_get_csi(run);
502
503 /* Evaluate type expression in except clause. */
504 run_texpr(run->program, run_get_current_csi(run), except_c->etype,
505 &etype);
506
507 /* Determine if active exc. is derived from type in exc. clause. */
508 return tdata_is_csi_derived_from_ti(exc_csi, etype);
509}
510
511/** Return CSI of the active exception.
512 *
513 * @param run Runner object.
514 * @return CSI of the active exception.
515 */
516static stree_csi_t *run_exc_payload_get_csi(run_t *run)
517{
518 rdata_value_t *payload;
519 rdata_var_t *payload_v;
520 rdata_object_t *payload_o;
521
522 payload = run->thread_ar->exc_payload;
523 assert(payload != NULL);
524
525 if (payload->var->vc != vc_ref) {
526 /* XXX Prevent this via static type checking. */
527 printf("Error: Exception payload must be an object "
528 "(found type %d).\n", payload->var->vc);
529 exit(1);
530 }
531
532 payload_v = payload->var->u.ref_v->vref;
533 if (payload_v->vc != vc_object) {
534 /* XXX Prevent this via static type checking. */
535 printf("Error: Exception payload must be an object "
536 "(found type %d).\n", payload_v->vc);
537 exit(1);
538 }
539
540 payload_o = payload_v->u.object_v;
541
542#ifdef DEBUG_RUN_TRACE
543 printf("Active exception: '");
544 symbol_print_fqn(payload_o->class_sym);
545 printf("'.\n");
546#endif
547 assert(payload_o->class_sym != NULL);
548 assert(payload_o->class_sym->sc == sc_csi);
549
550 return payload_o->class_sym->u.csi;
551}
552
553
554/** Check for unhandled exception.
555 *
556 * Checks whether there is an active exception. If so, it prints an
557 * error message and raises a run-time error.
558 *
559 * @param run Runner object.
560 */
561void run_exc_check_unhandled(run_t *run)
562{
563 stree_csi_t *exc_csi;
564
565 if (run->thread_ar->bo_mode != bm_none) {
566 assert(run->thread_ar->bo_mode == bm_exc);
567
568 exc_csi = run_exc_payload_get_csi(run);
569
570 printf("Error: Unhandled exception '");
571 symbol_print_fqn(csi_to_symbol(exc_csi));
572 printf("'.\n");
573
574 run_raise_error(run);
575 }
576}
577
578/** Raise an irrecoverable run-time error, start bailing out.
579 *
580 * Raises an error that cannot be handled by the user program.
581 */
582void run_raise_error(run_t *run)
583{
584 run->thread_ar->bo_mode = bm_error;
585 run->thread_ar->error = b_true;
586}
587
588/** Construct a special recovery item. */
589rdata_item_t *run_recovery_item(run_t *run)
590{
591 (void) run;
592 return NULL;
593}
594
595/** Find a local variable in the currently active function. */
596rdata_var_t *run_local_vars_lookup(run_t *run, sid_t name)
597{
598 run_proc_ar_t *proc_ar;
599 run_block_ar_t *block_ar;
600 rdata_var_t *var;
601 list_node_t *node;
602
603 proc_ar = run_get_current_proc_ar(run);
604 node = list_last(&proc_ar->block_ar);
605
606 /* Walk through all block activation records. */
607 while (node != NULL) {
608 block_ar = list_node_data(node, run_block_ar_t *);
609 var = intmap_get(&block_ar->vars, name);
610 if (var != NULL)
611 return var;
612
613 node = list_prev(&proc_ar->block_ar, node);
614 }
615
616 /* No match */
617 return NULL;
618}
619
620/** Get current function activation record. */
621run_proc_ar_t *run_get_current_proc_ar(run_t *run)
622{
623 list_node_t *node;
624
625 node = list_last(&run->thread_ar->proc_ar);
626 return list_node_data(node, run_proc_ar_t *);
627}
628
629/** Get current block activation record. */
630run_block_ar_t *run_get_current_block_ar(run_t *run)
631{
632 run_proc_ar_t *proc_ar;
633 list_node_t *node;
634
635 proc_ar = run_get_current_proc_ar(run);
636
637 node = list_last(&proc_ar->block_ar);
638 return list_node_data(node, run_block_ar_t *);
639}
640
641/** Get current CSI. */
642stree_csi_t *run_get_current_csi(run_t *run)
643{
644 run_proc_ar_t *proc_ar;
645
646 proc_ar = run_get_current_proc_ar(run);
647 return proc_ar->proc->outer_symbol->outer_csi;
648}
649
650/** Construct variable from a value item.
651 *
652 * XXX This should be in fact implemented using existing code as:
653 *
654 * (1) Create a variable of the desired type.
655 * (2) Initialize the variable with the provided value.
656 */
657void run_value_item_to_var(rdata_item_t *item, rdata_var_t **var)
658{
659 rdata_char_t *char_v;
660 rdata_int_t *int_v;
661 rdata_string_t *string_v;
662 rdata_ref_t *ref_v;
663 rdata_var_t *in_var;
664
665 assert(item->ic == ic_value);
666 in_var = item->u.value->var;
667
668 switch (in_var->vc) {
669 case vc_char:
670 *var = rdata_var_new(vc_char);
671 char_v = rdata_char_new();
672
673 (*var)->u.char_v = char_v;
674 bigint_clone(&item->u.value->var->u.char_v->value,
675 &char_v->value);
676 break;
677 case vc_int:
678 *var = rdata_var_new(vc_int);
679 int_v = rdata_int_new();
680
681 (*var)->u.int_v = int_v;
682 bigint_clone(&item->u.value->var->u.int_v->value,
683 &int_v->value);
684 break;
685 case vc_string:
686 *var = rdata_var_new(vc_string);
687 string_v = rdata_string_new();
688
689 (*var)->u.string_v = string_v;
690 string_v->value = item->u.value->var->u.string_v->value;
691 break;
692 case vc_ref:
693 *var = rdata_var_new(vc_ref);
694 ref_v = rdata_ref_new();
695
696 (*var)->u.ref_v = ref_v;
697 ref_v->vref = item->u.value->var->u.ref_v->vref;
698 break;
699 default:
700 printf("Error: Unimplemented argument type.\n");
701 exit(1);
702
703 }
704}
705
706/** Construct a function AR. */
707void run_proc_ar_create(run_t *run, rdata_var_t *obj, stree_proc_t *proc,
708 run_proc_ar_t **rproc_ar)
709{
710 run_proc_ar_t *proc_ar;
711 run_block_ar_t *block_ar;
712
713 (void) run;
714
715 /* Create function activation record. */
716 proc_ar = run_proc_ar_new();
717 proc_ar->obj = obj;
718 proc_ar->proc = proc;
719 list_init(&proc_ar->block_ar);
720
721 proc_ar->retval = NULL;
722
723 /* Create special block activation record to hold function arguments. */
724 block_ar = run_block_ar_new();
725 intmap_init(&block_ar->vars);
726 list_append(&proc_ar->block_ar, block_ar);
727
728 *rproc_ar = proc_ar;
729}
730
731/** Fill arguments in a procedure AR.
732 *
733 * When invoking a procedure this is used to store the argument values
734 * in the activation record.
735 */
736void run_proc_ar_set_args(run_t *run, run_proc_ar_t *proc_ar, list_t *arg_vals)
737{
738 stree_fun_t *fun;
739 stree_prop_t *prop;
740 list_t *args;
741 stree_proc_arg_t *varg;
742 stree_symbol_t *outer_symbol;
743
744 run_block_ar_t *block_ar;
745 list_node_t *block_ar_n;
746 list_node_t *rarg_n, *parg_n;
747 list_node_t *cn;
748 rdata_item_t *rarg;
749 stree_proc_arg_t *parg;
750 rdata_var_t *var;
751 rdata_var_t *ref_var;
752 rdata_ref_t *ref;
753 rdata_array_t *array;
754 int n_vargs, idx;
755
756 (void) run;
757
758 /* AR should have been created with run_proc_ar_create(). */
759 assert(proc_ar->proc != NULL);
760 outer_symbol = proc_ar->proc->outer_symbol;
761
762 /*
763 * The procedure being activated should belong to a member function or
764 * property getter/setter.
765 */
766 switch (outer_symbol->sc) {
767 case sc_fun:
768 fun = symbol_to_fun(outer_symbol);
769 args = &fun->args;
770 varg = fun->varg;
771 break;
772 case sc_prop:
773 prop = symbol_to_prop(outer_symbol);
774 args = &prop->args;
775 varg = prop->varg;
776 break;
777 default:
778 assert(b_false);
779 }
780
781 /* Fetch first block activation record. */
782 block_ar_n = list_first(&proc_ar->block_ar);
783 assert(block_ar_n != NULL);
784 block_ar = list_node_data(block_ar_n, run_block_ar_t *);
785
786 /* Declare local variables to hold argument values. */
787 rarg_n = list_first(arg_vals);
788 parg_n = list_first(args);
789
790 while (parg_n != NULL) {
791 if (rarg_n == NULL) {
792 printf("Error: Too few arguments to '");
793 symbol_print_fqn(outer_symbol);
794 printf("'.\n");
795 exit(1);
796 }
797
798 rarg = list_node_data(rarg_n, rdata_item_t *);
799 parg = list_node_data(parg_n, stree_proc_arg_t *);
800
801 assert(rarg->ic == ic_value);
802
803 /* Construct a variable from the argument value. */
804 run_value_item_to_var(rarg, &var);
805
806 /* Declare variable using name of formal argument. */
807 intmap_set(&block_ar->vars, parg->name->sid, var);
808
809 rarg_n = list_next(arg_vals, rarg_n);
810 parg_n = list_next(args, parg_n);
811 }
812
813 if (varg != NULL) {
814 /* Function is variadic. Count number of variadic arguments. */
815 cn = rarg_n;
816 n_vargs = 0;
817 while (cn != NULL) {
818 n_vargs += 1;
819 cn = list_next(arg_vals, cn);
820 }
821
822 /* Prepare array to store variadic arguments. */
823 array = rdata_array_new(1);
824 array->extent[0] = n_vargs;
825 rdata_array_alloc_element(array);
826
827 /* Read variadic arguments. */
828
829 idx = 0;
830 while (rarg_n != NULL) {
831 rarg = list_node_data(rarg_n, rdata_item_t *);
832 assert(rarg->ic == ic_value);
833
834 rdata_var_write(array->element[idx], rarg->u.value);
835
836 rarg_n = list_next(arg_vals, rarg_n);
837 idx += 1;
838 }
839
840 var = rdata_var_new(vc_array);
841 var->u.array_v = array;
842
843 /* Create reference to the new array. */
844 ref_var = rdata_var_new(vc_ref);
845 ref = rdata_ref_new();
846 ref_var->u.ref_v = ref;
847 ref->vref = var;
848
849 /* Declare variable using name of formal argument. */
850 intmap_set(&block_ar->vars, varg->name->sid,
851 ref_var);
852 }
853
854 /* Check for excess real parameters. */
855 if (rarg_n != NULL) {
856 printf("Error: Too many arguments to '");
857 symbol_print_fqn(outer_symbol);
858 printf("'.\n");
859 exit(1);
860 }
861}
862
863/** Fill setter argument in a procedure AR.
864 *
865 * When invoking a setter this is used to store its argument value in its
866 * procedure activation record.
867 */
868void run_proc_ar_set_setter_arg(run_t *run, run_proc_ar_t *proc_ar,
869 rdata_item_t *arg_val)
870{
871 stree_prop_t *prop;
872 run_block_ar_t *block_ar;
873 list_node_t *block_ar_n;
874 rdata_var_t *var;
875
876 (void) run;
877
878 /* AR should have been created with run_proc_ar_create(). */
879 assert(proc_ar->proc != NULL);
880
881 /* The procedure being activated should belong to a property setter. */
882 prop = symbol_to_prop(proc_ar->proc->outer_symbol);
883 assert(prop != NULL);
884 assert(proc_ar->proc == prop->setter);
885
886 /* Fetch first block activation record. */
887 block_ar_n = list_first(&proc_ar->block_ar);
888 assert(block_ar_n != NULL);
889 block_ar = list_node_data(block_ar_n, run_block_ar_t *);
890
891 assert(arg_val->ic == ic_value);
892
893 /* Construct a variable from the argument value. */
894 run_value_item_to_var(arg_val, &var);
895
896 /* Declare variable using name of formal argument. */
897 intmap_set(&block_ar->vars, prop->setter_arg->name->sid, var);
898}
899
900/** Print function activation backtrace. */
901void run_print_fun_bt(run_t *run)
902{
903 list_node_t *node;
904 run_proc_ar_t *proc_ar;
905
906 printf("Backtrace:\n");
907 node = list_last(&run->thread_ar->proc_ar);
908 while (node != NULL) {
909 printf(" * ");
910 proc_ar = list_node_data(node, run_proc_ar_t *);
911 symbol_print_fqn(proc_ar->proc->outer_symbol);
912 printf("\n");
913
914 node = list_prev(&run->thread_ar->proc_ar, node);
915 }
916}
917
918/** Convert item to value item.
919 *
920 * If @a item is a value, we just return a copy. If @a item is an address,
921 * we read from the address.
922 */
923void run_cvt_value_item(run_t *run, rdata_item_t *item, rdata_item_t **ritem)
924{
925 rdata_value_t *value;
926
927 /*
928 * This can happen when trying to use output of a function which
929 * does not return a value.
930 */
931 if (item == NULL) {
932 printf("Error: Sub-expression has no value.\n");
933 exit(1);
934 }
935
936 /* Address item. Perform read operation. */
937 if (item->ic == ic_address) {
938 run_address_read(run, item->u.address, ritem);
939 return;
940 }
941
942 /* It already is a value, we can share the @c var. */
943 value = rdata_value_new();
944 value->var = item->u.value->var;
945 *ritem = rdata_item_new(ic_value);
946 (*ritem)->u.value = value;
947}
948
949/** Get item var-class.
950 *
951 * Get var-class of @a item, regardless whether it is a value or address.
952 * (I.e. the var class of the value or variable at the given address).
953 */
954var_class_t run_item_get_vc(run_t *run, rdata_item_t *item)
955{
956 var_class_t vc;
957 rdata_var_t *tpos;
958
959 (void) run;
960
961 switch (item->ic) {
962 case ic_value:
963 vc = item->u.value->var->vc;
964 break;
965 case ic_address:
966 switch (item->u.address->ac) {
967 case ac_var:
968 vc = item->u.address->u.var_a->vref->vc;
969 break;
970 case ac_prop:
971 /* Prefetch the value of the property. */
972 tpos = run_aprop_get_tpos(run, item->u.address);
973 vc = tpos->vc;
974 break;
975 default:
976 assert(b_false);
977 }
978 break;
979 default:
980 assert(b_false);
981 }
982
983 return vc;
984}
985
986/** Get pointer to current var node in temporary copy in property address.
987 *
988 * A property address refers to a specific @c var node in a property.
989 * This function will fetch a copy of the property value (by running
990 * its getter) if there is not a temporary copy in the address yet.
991 * It returns a pointer to the relevant @c var node in the temporary
992 * copy.
993 *
994 * @param run Runner object.
995 * @param addr Address of class @c ac_prop.
996 * @param Pointer to var node.
997 */
998static rdata_var_t *run_aprop_get_tpos(run_t *run, rdata_address_t *addr)
999{
1000 rdata_item_t *ritem;
1001
1002 assert(addr->ac == ac_prop);
1003
1004 if (addr->u.prop_a->tvalue == NULL) {
1005 /* Fetch value of the property. */
1006 run_address_read(run, addr, &ritem);
1007 assert(ritem->ic == ic_value);
1008 addr->u.prop_a->tvalue = ritem->u.value;
1009 addr->u.prop_a->tpos = addr->u.prop_a->tvalue->var;
1010 }
1011
1012 return addr->u.prop_a->tpos;
1013}
1014
1015/** Read data from an address.
1016 *
1017 * Return value stored in a variable at the specified address.
1018 */
1019void run_address_read(run_t *run, rdata_address_t *address,
1020 rdata_item_t **ritem)
1021{
1022 (void) run;
1023
1024 switch (address->ac) {
1025 case ac_var:
1026 rdata_var_read(address->u.var_a->vref, ritem);
1027 break;
1028 case ac_prop:
1029 run_aprop_read(run, address->u.prop_a, ritem);
1030 break;
1031 }
1032
1033 assert((*ritem)->ic == ic_value);
1034}
1035
1036/** Write data to an address.
1037 *
1038 * Store @a value to the variable at @a address.
1039 */
1040void run_address_write(run_t *run, rdata_address_t *address,
1041 rdata_value_t *value)
1042{
1043 (void) run;
1044
1045 switch (address->ac) {
1046 case ac_var:
1047 rdata_var_write(address->u.var_a->vref, value);
1048 break;
1049 case ac_prop:
1050 run_aprop_write(run, address->u.prop_a, value);
1051 break;
1052 }
1053}
1054
1055static void run_aprop_read(run_t *run, rdata_addr_prop_t *addr_prop,
1056 rdata_item_t **ritem)
1057{
1058 rdata_deleg_t *deleg;
1059 rdata_var_t *obj;
1060 stree_symbol_t *prop_sym;
1061 stree_prop_t *prop;
1062
1063 run_proc_ar_t *proc_ar;
1064
1065 rdata_var_t *cvar;
1066
1067#ifdef DEBUG_RUN_TRACE
1068 printf("Read from property.\n");
1069#endif
1070 /*
1071 * If @c tvalue is present, we need to use the relevant part from that
1072 * instead of re-reading the whole thing.
1073 */
1074 if (addr_prop->tvalue != NULL) {
1075 /* Copy the value */
1076 rdata_var_copy(addr_prop->tpos, &cvar);
1077 *ritem = rdata_item_new(ic_value);
1078 (*ritem)->u.value = rdata_value_new();
1079 (*ritem)->u.value->var = cvar;
1080 return;
1081 }
1082
1083 if (addr_prop->apc == apc_named)
1084 deleg = addr_prop->u.named->prop_d;
1085 else
1086 deleg = addr_prop->u.indexed->object_d;
1087
1088 obj = deleg->obj;
1089 prop_sym = deleg->sym;
1090 prop = symbol_to_prop(prop_sym);
1091 assert(prop != NULL);
1092
1093 if (prop->getter == NULL) {
1094 printf("Error: Property is not readable.\n");
1095 exit(1);
1096 }
1097
1098 /* Create procedure activation record. */
1099 run_proc_ar_create(run, obj, prop->getter, &proc_ar);
1100
1101 /* Fill in arguments (indices). */
1102 if (addr_prop->apc == apc_indexed) {
1103 run_proc_ar_set_args(run, proc_ar,
1104 &addr_prop->u.indexed->args);
1105 }
1106
1107 /* Run getter. */
1108 run_proc(run, proc_ar, ritem);
1109
1110#ifdef DEBUG_RUN_TRACE
1111 printf("Getter returns ");
1112 rdata_item_print(*ritem);
1113 printf(".\n");
1114 printf("Done reading from property.\n");
1115#endif
1116}
1117
1118static void run_aprop_write(run_t *run, rdata_addr_prop_t *addr_prop,
1119 rdata_value_t *value)
1120{
1121 rdata_deleg_t *deleg;
1122 rdata_var_t *obj;
1123 stree_symbol_t *prop_sym;
1124 stree_prop_t *prop;
1125
1126 run_proc_ar_t *proc_ar;
1127 rdata_item_t *vitem;
1128 rdata_item_t *ritem;
1129
1130#ifdef DEBUG_RUN_TRACE
1131 printf("Write to property.\n");
1132#endif
1133 /* If @c tvalue is present, we need to modify it and write it back. */
1134 if (addr_prop->tvalue != NULL) {
1135 printf("Unimplemented: Read-modify-write property access.\n");
1136 exit(1);
1137 }
1138
1139 if (addr_prop->apc == apc_named)
1140 deleg = addr_prop->u.named->prop_d;
1141 else
1142 deleg = addr_prop->u.indexed->object_d;
1143
1144 obj = deleg->obj;
1145 prop_sym = deleg->sym;
1146 prop = symbol_to_prop(prop_sym);
1147 assert(prop != NULL);
1148
1149 if (prop->setter == NULL) {
1150 printf("Error: Property is not writable.\n");
1151 exit(1);
1152 }
1153
1154 vitem = rdata_item_new(ic_value);
1155 vitem->u.value = value;
1156
1157 /* Create procedure activation record. */
1158 run_proc_ar_create(run, obj, prop->setter, &proc_ar);
1159
1160 /* Fill in arguments (indices). */
1161 if (addr_prop->apc == apc_indexed) {
1162 run_proc_ar_set_args(run, proc_ar,
1163 &addr_prop->u.indexed->args);
1164 }
1165
1166 /* Fill in value argument for setter. */
1167 run_proc_ar_set_setter_arg(run, proc_ar, vitem);
1168
1169 /* Run setter. */
1170 run_proc(run, proc_ar, &ritem);
1171
1172 /* Setter should not return a value. */
1173 assert(ritem == NULL);
1174
1175#ifdef DEBUG_RUN_TRACE
1176 printf("Done writing to property.\n");
1177#endif
1178}
1179
1180/** Return reference to a variable.
1181 *
1182 * Constructs a reference (value item) pointing to @a var.
1183 */
1184void run_reference(run_t *run, rdata_var_t *var, rdata_item_t **res)
1185{
1186 rdata_ref_t *ref;
1187 rdata_var_t *ref_var;
1188 rdata_value_t *ref_value;
1189 rdata_item_t *ref_item;
1190
1191 (void) run;
1192
1193 /* Create reference to the variable. */
1194 ref = rdata_ref_new();
1195 ref_var = rdata_var_new(vc_ref);
1196 ref->vref = var;
1197 ref_var->u.ref_v = ref;
1198
1199 /* Construct value of the reference to return. */
1200 ref_item = rdata_item_new(ic_value);
1201 ref_value = rdata_value_new();
1202 ref_item->u.value = ref_value;
1203 ref_value->var = ref_var;
1204
1205 *res = ref_item;
1206}
1207
1208/** Return address of reference target.
1209 *
1210 * Takes a reference (address or value) and returns the address (item) of
1211 * the target of the reference.
1212 */
1213void run_dereference(run_t *run, rdata_item_t *ref, rdata_item_t **ritem)
1214{
1215 rdata_item_t *ref_val;
1216 rdata_item_t *item;
1217 rdata_address_t *address;
1218 rdata_addr_var_t *addr_var;
1219
1220#ifdef DEBUG_RUN_TRACE
1221 printf("run_dereference()\n");
1222#endif
1223 run_cvt_value_item(run, ref, &ref_val);
1224 assert(ref_val->u.value->var->vc == vc_ref);
1225
1226 item = rdata_item_new(ic_address);
1227 address = rdata_address_new(ac_var);
1228 addr_var = rdata_addr_var_new();
1229 item->u.address = address;
1230 address->u.var_a = addr_var;
1231 addr_var->vref = ref_val->u.value->var->u.ref_v->vref;
1232
1233 if (addr_var->vref == NULL) {
1234#ifdef DEBUG_RUN_TRACE
1235 printf("Error: Accessing null reference.\n");
1236#endif
1237 /* Raise Error.NilReference */
1238 run_raise_exc(run, run->program->builtin->error_nilreference);
1239 *ritem = run_recovery_item(run);
1240 return;
1241 }
1242
1243#ifdef DEBUG_RUN_TRACE
1244 printf("vref set to %p\n", addr_var->vref);
1245#endif
1246 *ritem = item;
1247}
1248
1249/** Raise an exception of the given class.
1250 *
1251 * Used when the interpreter generates an exception due to a run-time
1252 * error (not for the @c raise statement).
1253 *
1254 * @param run Runner object.
1255 * @param csi Exception class.
1256 */
1257void run_raise_exc(run_t *run, stree_csi_t *csi)
1258{
1259 rdata_item_t *exc_vi;
1260
1261 /* Create exception object. */
1262 run_new_csi_inst(run, csi, &exc_vi);
1263 assert(exc_vi->ic == ic_value);
1264
1265 /* Store exception object in thread AR. */
1266 run->thread_ar->exc_payload = exc_vi->u.value;
1267
1268 /* Start exception bailout. */
1269 run->thread_ar->bo_mode = bm_exc;
1270}
1271
1272/** Determine if we are bailing out. */
1273bool_t run_is_bo(run_t *run)
1274{
1275 return run->thread_ar->bo_mode != bm_none;
1276}
1277
1278run_thread_ar_t *run_thread_ar_new(void)
1279{
1280 run_thread_ar_t *thread_ar;
1281
1282 thread_ar = calloc(1, sizeof(run_thread_ar_t));
1283 if (thread_ar == NULL) {
1284 printf("Memory allocation failed.\n");
1285 exit(1);
1286 }
1287
1288 return thread_ar;
1289}
1290
1291run_proc_ar_t *run_proc_ar_new(void)
1292{
1293 run_proc_ar_t *proc_ar;
1294
1295 proc_ar = calloc(1, sizeof(run_proc_ar_t));
1296 if (proc_ar == NULL) {
1297 printf("Memory allocation failed.\n");
1298 exit(1);
1299 }
1300
1301 return proc_ar;
1302}
1303
1304run_block_ar_t *run_block_ar_new(void)
1305{
1306 run_block_ar_t *block_ar;
1307
1308 block_ar = calloc(1, sizeof(run_block_ar_t));
1309 if (block_ar == NULL) {
1310 printf("Memory allocation failed.\n");
1311 exit(1);
1312 }
1313
1314 return block_ar;
1315}
Note: See TracBrowser for help on using the repository browser.