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

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

Update SBI to rev. 174.

  • Property mode set to 100644
File size: 31.0 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_int_t *int_v;
660 rdata_string_t *string_v;
661 rdata_ref_t *ref_v;
662 rdata_var_t *in_var;
663
664 assert(item->ic == ic_value);
665 in_var = item->u.value->var;
666
667 switch (in_var->vc) {
668 case vc_int:
669 *var = rdata_var_new(vc_int);
670 int_v = rdata_int_new();
671
672 (*var)->u.int_v = int_v;
673 bigint_clone(&item->u.value->var->u.int_v->value,
674 &int_v->value);
675 break;
676 case vc_string:
677 *var = rdata_var_new(vc_string);
678 string_v = rdata_string_new();
679
680 (*var)->u.string_v = string_v;
681 string_v->value = item->u.value->var->u.string_v->value;
682 break;
683 case vc_ref:
684 *var = rdata_var_new(vc_ref);
685 ref_v = rdata_ref_new();
686
687 (*var)->u.ref_v = ref_v;
688 ref_v->vref = item->u.value->var->u.ref_v->vref;
689 break;
690 default:
691 printf("Error: Unimplemented argument type.\n");
692 exit(1);
693
694 }
695}
696
697/** Construct a function AR. */
698void run_proc_ar_create(run_t *run, rdata_var_t *obj, stree_proc_t *proc,
699 run_proc_ar_t **rproc_ar)
700{
701 run_proc_ar_t *proc_ar;
702 run_block_ar_t *block_ar;
703
704 (void) run;
705
706 /* Create function activation record. */
707 proc_ar = run_proc_ar_new();
708 proc_ar->obj = obj;
709 proc_ar->proc = proc;
710 list_init(&proc_ar->block_ar);
711
712 proc_ar->retval = NULL;
713
714 /* Create special block activation record to hold function arguments. */
715 block_ar = run_block_ar_new();
716 intmap_init(&block_ar->vars);
717 list_append(&proc_ar->block_ar, block_ar);
718
719 *rproc_ar = proc_ar;
720}
721
722/** Fill arguments in a procedure AR.
723 *
724 * When invoking a procedure this is used to store the argument values
725 * in the activation record.
726 */
727void run_proc_ar_set_args(run_t *run, run_proc_ar_t *proc_ar, list_t *arg_vals)
728{
729 stree_fun_t *fun;
730 stree_prop_t *prop;
731 list_t *args;
732 stree_proc_arg_t *varg;
733 stree_symbol_t *outer_symbol;
734
735 run_block_ar_t *block_ar;
736 list_node_t *block_ar_n;
737 list_node_t *rarg_n, *parg_n;
738 list_node_t *cn;
739 rdata_item_t *rarg;
740 stree_proc_arg_t *parg;
741 rdata_var_t *var;
742 rdata_var_t *ref_var;
743 rdata_ref_t *ref;
744 rdata_array_t *array;
745 int n_vargs, idx;
746
747 (void) run;
748
749 /* AR should have been created with run_proc_ar_create(). */
750 assert(proc_ar->proc != NULL);
751 outer_symbol = proc_ar->proc->outer_symbol;
752
753 /*
754 * The procedure being activated should belong to a member function or
755 * property getter/setter.
756 */
757 switch (outer_symbol->sc) {
758 case sc_fun:
759 fun = symbol_to_fun(outer_symbol);
760 args = &fun->args;
761 varg = fun->varg;
762 break;
763 case sc_prop:
764 prop = symbol_to_prop(outer_symbol);
765 args = &prop->args;
766 varg = prop->varg;
767 break;
768 default:
769 assert(b_false);
770 }
771
772 /* Fetch first block activation record. */
773 block_ar_n = list_first(&proc_ar->block_ar);
774 assert(block_ar_n != NULL);
775 block_ar = list_node_data(block_ar_n, run_block_ar_t *);
776
777 /* Declare local variables to hold argument values. */
778 rarg_n = list_first(arg_vals);
779 parg_n = list_first(args);
780
781 while (parg_n != NULL) {
782 if (rarg_n == NULL) {
783 printf("Error: Too few arguments to '");
784 symbol_print_fqn(outer_symbol);
785 printf("'.\n");
786 exit(1);
787 }
788
789 rarg = list_node_data(rarg_n, rdata_item_t *);
790 parg = list_node_data(parg_n, stree_proc_arg_t *);
791
792 assert(rarg->ic == ic_value);
793
794 /* Construct a variable from the argument value. */
795 run_value_item_to_var(rarg, &var);
796
797 /* Declare variable using name of formal argument. */
798 intmap_set(&block_ar->vars, parg->name->sid, var);
799
800 rarg_n = list_next(arg_vals, rarg_n);
801 parg_n = list_next(args, parg_n);
802 }
803
804 if (varg != NULL) {
805 /* Function is variadic. Count number of variadic arguments. */
806 cn = rarg_n;
807 n_vargs = 0;
808 while (cn != NULL) {
809 n_vargs += 1;
810 cn = list_next(arg_vals, cn);
811 }
812
813 /* Prepare array to store variadic arguments. */
814 array = rdata_array_new(1);
815 array->extent[0] = n_vargs;
816 rdata_array_alloc_element(array);
817
818 /* Read variadic arguments. */
819
820 idx = 0;
821 while (rarg_n != NULL) {
822 rarg = list_node_data(rarg_n, rdata_item_t *);
823 assert(rarg->ic == ic_value);
824
825 rdata_var_write(array->element[idx], rarg->u.value);
826
827 rarg_n = list_next(arg_vals, rarg_n);
828 idx += 1;
829 }
830
831 var = rdata_var_new(vc_array);
832 var->u.array_v = array;
833
834 /* Create reference to the new array. */
835 ref_var = rdata_var_new(vc_ref);
836 ref = rdata_ref_new();
837 ref_var->u.ref_v = ref;
838 ref->vref = var;
839
840 /* Declare variable using name of formal argument. */
841 intmap_set(&block_ar->vars, varg->name->sid,
842 ref_var);
843 }
844
845 /* Check for excess real parameters. */
846 if (rarg_n != NULL) {
847 printf("Error: Too many arguments to '");
848 symbol_print_fqn(outer_symbol);
849 printf("'.\n");
850 exit(1);
851 }
852}
853
854/** Fill setter argument in a procedure AR.
855 *
856 * When invoking a setter this is used to store its argument value in its
857 * procedure activation record.
858 */
859void run_proc_ar_set_setter_arg(run_t *run, run_proc_ar_t *proc_ar,
860 rdata_item_t *arg_val)
861{
862 stree_prop_t *prop;
863 run_block_ar_t *block_ar;
864 list_node_t *block_ar_n;
865 rdata_var_t *var;
866
867 (void) run;
868
869 /* AR should have been created with run_proc_ar_create(). */
870 assert(proc_ar->proc != NULL);
871
872 /* The procedure being activated should belong to a property setter. */
873 prop = symbol_to_prop(proc_ar->proc->outer_symbol);
874 assert(prop != NULL);
875 assert(proc_ar->proc == prop->setter);
876
877 /* Fetch first block activation record. */
878 block_ar_n = list_first(&proc_ar->block_ar);
879 assert(block_ar_n != NULL);
880 block_ar = list_node_data(block_ar_n, run_block_ar_t *);
881
882 assert(arg_val->ic == ic_value);
883
884 /* Construct a variable from the argument value. */
885 run_value_item_to_var(arg_val, &var);
886
887 /* Declare variable using name of formal argument. */
888 intmap_set(&block_ar->vars, prop->setter_arg->name->sid, var);
889}
890
891/** Print function activation backtrace. */
892void run_print_fun_bt(run_t *run)
893{
894 list_node_t *node;
895 run_proc_ar_t *proc_ar;
896
897 printf("Backtrace:\n");
898 node = list_last(&run->thread_ar->proc_ar);
899 while (node != NULL) {
900 printf(" * ");
901 proc_ar = list_node_data(node, run_proc_ar_t *);
902 symbol_print_fqn(proc_ar->proc->outer_symbol);
903 printf("\n");
904
905 node = list_prev(&run->thread_ar->proc_ar, node);
906 }
907}
908
909/** Convert item to value item.
910 *
911 * If @a item is a value, we just return a copy. If @a item is an address,
912 * we read from the address.
913 */
914void run_cvt_value_item(run_t *run, rdata_item_t *item, rdata_item_t **ritem)
915{
916 rdata_value_t *value;
917
918 /*
919 * This can happen when trying to use output of a function which
920 * does not return a value.
921 */
922 if (item == NULL) {
923 printf("Error: Sub-expression has no value.\n");
924 exit(1);
925 }
926
927 /* Address item. Perform read operation. */
928 if (item->ic == ic_address) {
929 run_address_read(run, item->u.address, ritem);
930 return;
931 }
932
933 /* It already is a value, we can share the @c var. */
934 value = rdata_value_new();
935 value->var = item->u.value->var;
936 *ritem = rdata_item_new(ic_value);
937 (*ritem)->u.value = value;
938}
939
940/** Get item var-class.
941 *
942 * Get var-class of @a item, regardless whether it is a value or address.
943 * (I.e. the var class of the value or variable at the given address).
944 */
945var_class_t run_item_get_vc(run_t *run, rdata_item_t *item)
946{
947 var_class_t vc;
948 rdata_var_t *tpos;
949
950 (void) run;
951
952 switch (item->ic) {
953 case ic_value:
954 vc = item->u.value->var->vc;
955 break;
956 case ic_address:
957 switch (item->u.address->ac) {
958 case ac_var:
959 vc = item->u.address->u.var_a->vref->vc;
960 break;
961 case ac_prop:
962 /* Prefetch the value of the property. */
963 tpos = run_aprop_get_tpos(run, item->u.address);
964 vc = tpos->vc;
965 break;
966 default:
967 assert(b_false);
968 }
969 break;
970 default:
971 assert(b_false);
972 }
973
974 return vc;
975}
976
977/** Get pointer to current var node in temporary copy in property address.
978 *
979 * A property address refers to a specific @c var node in a property.
980 * This function will fetch a copy of the property value (by running
981 * its getter) if there is not a temporary copy in the address yet.
982 * It returns a pointer to the relevant @c var node in the temporary
983 * copy.
984 *
985 * @param run Runner object.
986 * @param addr Address of class @c ac_prop.
987 * @param Pointer to var node.
988 */
989static rdata_var_t *run_aprop_get_tpos(run_t *run, rdata_address_t *addr)
990{
991 rdata_item_t *ritem;
992
993 assert(addr->ac == ac_prop);
994
995 if (addr->u.prop_a->tvalue == NULL) {
996 /* Fetch value of the property. */
997 run_address_read(run, addr, &ritem);
998 assert(ritem->ic == ic_value);
999 addr->u.prop_a->tvalue = ritem->u.value;
1000 addr->u.prop_a->tpos = addr->u.prop_a->tvalue->var;
1001 }
1002
1003 return addr->u.prop_a->tpos;
1004}
1005
1006/** Read data from an address.
1007 *
1008 * Return value stored in a variable at the specified address.
1009 */
1010void run_address_read(run_t *run, rdata_address_t *address,
1011 rdata_item_t **ritem)
1012{
1013 (void) run;
1014
1015 switch (address->ac) {
1016 case ac_var:
1017 rdata_var_read(address->u.var_a->vref, ritem);
1018 break;
1019 case ac_prop:
1020 run_aprop_read(run, address->u.prop_a, ritem);
1021 break;
1022 }
1023
1024 assert((*ritem)->ic == ic_value);
1025}
1026
1027/** Write data to an address.
1028 *
1029 * Store @a value to the variable at @a address.
1030 */
1031void run_address_write(run_t *run, rdata_address_t *address,
1032 rdata_value_t *value)
1033{
1034 (void) run;
1035
1036 switch (address->ac) {
1037 case ac_var:
1038 rdata_var_write(address->u.var_a->vref, value);
1039 break;
1040 case ac_prop:
1041 run_aprop_write(run, address->u.prop_a, value);
1042 break;
1043 }
1044}
1045
1046static void run_aprop_read(run_t *run, rdata_addr_prop_t *addr_prop,
1047 rdata_item_t **ritem)
1048{
1049 rdata_deleg_t *deleg;
1050 rdata_var_t *obj;
1051 stree_symbol_t *prop_sym;
1052 stree_prop_t *prop;
1053
1054 run_proc_ar_t *proc_ar;
1055
1056 rdata_var_t *cvar;
1057
1058#ifdef DEBUG_RUN_TRACE
1059 printf("Read from property.\n");
1060#endif
1061 /*
1062 * If @c tvalue is present, we need to use the relevant part from that
1063 * instead of re-reading the whole thing.
1064 */
1065 if (addr_prop->tvalue != NULL) {
1066 /* Copy the value */
1067 rdata_var_copy(addr_prop->tpos, &cvar);
1068 *ritem = rdata_item_new(ic_value);
1069 (*ritem)->u.value = rdata_value_new();
1070 (*ritem)->u.value->var = cvar;
1071 return;
1072 }
1073
1074 if (addr_prop->apc == apc_named)
1075 deleg = addr_prop->u.named->prop_d;
1076 else
1077 deleg = addr_prop->u.indexed->object_d;
1078
1079 obj = deleg->obj;
1080 prop_sym = deleg->sym;
1081 prop = symbol_to_prop(prop_sym);
1082 assert(prop != NULL);
1083
1084 if (prop->getter == NULL) {
1085 printf("Error: Property is not readable.\n");
1086 exit(1);
1087 }
1088
1089 /* Create procedure activation record. */
1090 run_proc_ar_create(run, obj, prop->getter, &proc_ar);
1091
1092 /* Fill in arguments (indices). */
1093 if (addr_prop->apc == apc_indexed) {
1094 run_proc_ar_set_args(run, proc_ar,
1095 &addr_prop->u.indexed->args);
1096 }
1097
1098 /* Run getter. */
1099 run_proc(run, proc_ar, ritem);
1100
1101#ifdef DEBUG_RUN_TRACE
1102 printf("Getter returns ");
1103 rdata_item_print(*ritem);
1104 printf(".\n");
1105 printf("Done reading from property.\n");
1106#endif
1107}
1108
1109static void run_aprop_write(run_t *run, rdata_addr_prop_t *addr_prop,
1110 rdata_value_t *value)
1111{
1112 rdata_deleg_t *deleg;
1113 rdata_var_t *obj;
1114 stree_symbol_t *prop_sym;
1115 stree_prop_t *prop;
1116
1117 run_proc_ar_t *proc_ar;
1118 rdata_item_t *vitem;
1119 rdata_item_t *ritem;
1120
1121#ifdef DEBUG_RUN_TRACE
1122 printf("Write to property.\n");
1123#endif
1124 /* If @c tvalue is present, we need to modify it and write it back. */
1125 if (addr_prop->tvalue != NULL) {
1126 printf("Unimplemented: Read-modify-write property access.\n");
1127 exit(1);
1128 }
1129
1130 if (addr_prop->apc == apc_named)
1131 deleg = addr_prop->u.named->prop_d;
1132 else
1133 deleg = addr_prop->u.indexed->object_d;
1134
1135 obj = deleg->obj;
1136 prop_sym = deleg->sym;
1137 prop = symbol_to_prop(prop_sym);
1138 assert(prop != NULL);
1139
1140 if (prop->setter == NULL) {
1141 printf("Error: Property is not writable.\n");
1142 exit(1);
1143 }
1144
1145 vitem = rdata_item_new(ic_value);
1146 vitem->u.value = value;
1147
1148 /* Create procedure activation record. */
1149 run_proc_ar_create(run, obj, prop->setter, &proc_ar);
1150
1151 /* Fill in arguments (indices). */
1152 if (addr_prop->apc == apc_indexed) {
1153 run_proc_ar_set_args(run, proc_ar,
1154 &addr_prop->u.indexed->args);
1155 }
1156
1157 /* Fill in value argument for setter. */
1158 run_proc_ar_set_setter_arg(run, proc_ar, vitem);
1159
1160 /* Run setter. */
1161 run_proc(run, proc_ar, &ritem);
1162
1163 /* Setter should not return a value. */
1164 assert(ritem == NULL);
1165
1166#ifdef DEBUG_RUN_TRACE
1167 printf("Done writing to property.\n");
1168#endif
1169}
1170
1171/** Return reference to a variable.
1172 *
1173 * Constructs a reference (value item) pointing to @a var.
1174 */
1175void run_reference(run_t *run, rdata_var_t *var, rdata_item_t **res)
1176{
1177 rdata_ref_t *ref;
1178 rdata_var_t *ref_var;
1179 rdata_value_t *ref_value;
1180 rdata_item_t *ref_item;
1181
1182 (void) run;
1183
1184 /* Create reference to the variable. */
1185 ref = rdata_ref_new();
1186 ref_var = rdata_var_new(vc_ref);
1187 ref->vref = var;
1188 ref_var->u.ref_v = ref;
1189
1190 /* Construct value of the reference to return. */
1191 ref_item = rdata_item_new(ic_value);
1192 ref_value = rdata_value_new();
1193 ref_item->u.value = ref_value;
1194 ref_value->var = ref_var;
1195
1196 *res = ref_item;
1197}
1198
1199/** Return address of reference target.
1200 *
1201 * Takes a reference (address or value) and returns the address (item) of
1202 * the target of the reference.
1203 */
1204void run_dereference(run_t *run, rdata_item_t *ref, rdata_item_t **ritem)
1205{
1206 rdata_item_t *ref_val;
1207 rdata_item_t *item;
1208 rdata_address_t *address;
1209 rdata_addr_var_t *addr_var;
1210
1211#ifdef DEBUG_RUN_TRACE
1212 printf("run_dereference()\n");
1213#endif
1214 run_cvt_value_item(run, ref, &ref_val);
1215 assert(ref_val->u.value->var->vc == vc_ref);
1216
1217 item = rdata_item_new(ic_address);
1218 address = rdata_address_new(ac_var);
1219 addr_var = rdata_addr_var_new();
1220 item->u.address = address;
1221 address->u.var_a = addr_var;
1222 addr_var->vref = ref_val->u.value->var->u.ref_v->vref;
1223
1224 if (addr_var->vref == NULL) {
1225#ifdef DEBUG_RUN_TRACE
1226 printf("Error: Accessing null reference.\n");
1227#endif
1228 /* Raise Error.NilReference */
1229 run_raise_exc(run, run->program->builtin->error_nilreference);
1230 *ritem = run_recovery_item(run);
1231 return;
1232 }
1233
1234#ifdef DEBUG_RUN_TRACE
1235 printf("vref set to %p\n", addr_var->vref);
1236#endif
1237 *ritem = item;
1238}
1239
1240/** Raise an exception of the given class.
1241 *
1242 * Used when the interpreter generates an exception due to a run-time
1243 * error (not for the @c raise statement).
1244 *
1245 * @param run Runner object.
1246 * @param csi Exception class.
1247 */
1248void run_raise_exc(run_t *run, stree_csi_t *csi)
1249{
1250 rdata_item_t *exc_vi;
1251
1252 /* Create exception object. */
1253 run_new_csi_inst(run, csi, &exc_vi);
1254 assert(exc_vi->ic == ic_value);
1255
1256 /* Store exception object in thread AR. */
1257 run->thread_ar->exc_payload = exc_vi->u.value;
1258
1259 /* Start exception bailout. */
1260 run->thread_ar->bo_mode = bm_exc;
1261}
1262
1263/** Determine if we are bailing out. */
1264bool_t run_is_bo(run_t *run)
1265{
1266 return run->thread_ar->bo_mode != bm_none;
1267}
1268
1269run_thread_ar_t *run_thread_ar_new(void)
1270{
1271 run_thread_ar_t *thread_ar;
1272
1273 thread_ar = calloc(1, sizeof(run_thread_ar_t));
1274 if (thread_ar == NULL) {
1275 printf("Memory allocation failed.\n");
1276 exit(1);
1277 }
1278
1279 return thread_ar;
1280}
1281
1282run_proc_ar_t *run_proc_ar_new(void)
1283{
1284 run_proc_ar_t *proc_ar;
1285
1286 proc_ar = calloc(1, sizeof(run_proc_ar_t));
1287 if (proc_ar == NULL) {
1288 printf("Memory allocation failed.\n");
1289 exit(1);
1290 }
1291
1292 return proc_ar;
1293}
1294
1295run_block_ar_t *run_block_ar_new(void)
1296{
1297 run_block_ar_t *block_ar;
1298
1299 block_ar = calloc(1, sizeof(run_block_ar_t));
1300 if (block_ar == NULL) {
1301 printf("Memory allocation failed.\n");
1302 exit(1);
1303 }
1304
1305 return block_ar;
1306}
Note: See TracBrowser for help on using the repository browser.