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

Last change on this file was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

  • Property mode set to 100644
File size: 50.1 KB
Line 
1/*
2 * Copyright (c) 2011 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 "cspan.h"
37#include "debug.h"
38#include "intmap.h"
39#include "list.h"
40#include "mytypes.h"
41#include "rdata.h"
42#include "run_expr.h"
43#include "run_texpr.h"
44#include "stree.h"
45#include "strtab.h"
46#include "symbol.h"
47#include "tdata.h"
48
49#include "run.h"
50
51static void run_block(run_t *run, stree_block_t *block);
52static void run_exps(run_t *run, stree_exps_t *exps, rdata_item_t **res);
53static void run_vdecl(run_t *run, stree_vdecl_t *vdecl);
54static void run_if(run_t *run, stree_if_t *if_s);
55static void run_switch(run_t *run, stree_switch_t *switch_s);
56static void run_while(run_t *run, stree_while_t *while_s);
57static void run_raise(run_t *run, stree_raise_t *raise_s);
58static void run_break(run_t *run, stree_break_t *break_s);
59static void run_return(run_t *run, stree_return_t *return_s);
60static void run_wef(run_t *run, stree_wef_t *wef_s);
61
62static bool_t run_exc_match(run_t *run, stree_except_t *except_c);
63static stree_csi_t *run_exc_payload_get_csi(run_t *run);
64
65static rdata_var_t *run_aprop_get_tpos(run_t *run, rdata_address_t *aprop);
66
67static void run_aprop_read(run_t *run, rdata_addr_prop_t *addr_prop,
68 rdata_item_t **ritem);
69static void run_aprop_write(run_t *run, rdata_addr_prop_t *addr_prop,
70 rdata_value_t *value);
71
72static void run_var_new_tprimitive(run_t *run, tdata_primitive_t *tprimitive,
73 rdata_var_t **rvar);
74static void run_var_new_null_ref(run_t *run, rdata_var_t **rvar);
75static void run_var_new_deleg(run_t *run, rdata_var_t **rvar);
76static void run_var_new_enum(run_t *run, tdata_enum_t *tenum,
77 rdata_var_t **rvar);
78
79/** Initialize runner instance.
80 *
81 * @param run Runner object
82 */
83void run_init(run_t *run)
84{
85 (void) run;
86}
87
88/** Run program.
89 *
90 * Associates the program @a prog with the runner object and executes
91 * it. If a run-time error occurs during the execution (e.g. an unhandled
92 * exception), @a run->error will be set to @c b_true when this function
93 * returns.
94 *
95 * @param run Runner object
96 * @param prog Program to run
97 */
98void run_program(run_t *run, stree_program_t *prog)
99{
100 stree_symbol_t *main_fun_sym;
101 stree_fun_t *main_fun;
102 rdata_var_t *main_obj;
103 stree_ident_t *fake_ident;
104 list_t main_args;
105 run_proc_ar_t *proc_ar;
106 rdata_item_t *res;
107
108 /* Note down link to program code. */
109 run->program = prog;
110
111 /* Initialize thread activation record. */
112 run->thread_ar = run_thread_ar_new();
113 list_init(&run->thread_ar->proc_ar);
114 run->thread_ar->bo_mode = bm_none;
115
116 /* Initialize global data structure. */
117 run_gdata_init(run);
118
119 /*
120 * Find entry point @c Main().
121 */
122 fake_ident = stree_ident_new();
123 fake_ident->sid = strtab_get_sid("Main");
124 main_fun_sym = symbol_find_epoint(prog, fake_ident);
125 if (main_fun_sym == NULL) {
126 printf("Error: Entry point 'Main' not found.\n");
127 exit(1);
128 }
129
130 main_fun = symbol_to_fun(main_fun_sym);
131 assert(main_fun != NULL);
132
133 main_obj = run_fun_sobject_find(run, main_fun);
134
135#ifdef DEBUG_RUN_TRACE
136 printf("Found function '");
137 symbol_print_fqn(main_fun_sym);
138 printf("'.\n");
139#endif
140
141 /* Run function @c main. */
142 list_init(&main_args);
143 run_proc_ar_create(run, main_obj, main_fun->proc, &proc_ar);
144 run_proc_ar_set_args(run, proc_ar, &main_args);
145 run_proc(run, proc_ar, &res);
146 run_proc_ar_destroy(run, proc_ar);
147
148 run_exc_check_unhandled(run);
149}
150
151/** Initialize global data.
152 *
153 * @param run Runner object
154 */
155void run_gdata_init(run_t *run)
156{
157 rdata_object_t *gobject;
158
159 run->gdata = rdata_var_new(vc_object);
160 gobject = rdata_object_new();
161 run->gdata->u.object_v = gobject;
162
163 gobject->class_sym = NULL;
164 gobject->static_obj = sn_static;
165 intmap_init(&gobject->fields);
166}
167
168/** Run procedure.
169 *
170 * Inserts the provided procedure AR @a proc_ar on the execution stack
171 * (in the thread AR) and executes the procedure. The return value
172 * of the procedure is stored to *(@a res). @c NULL is stored if the
173 * procedure returns no value.
174 *
175 * If the procedure execution bails out due to an exception, this
176 * can be determined by looking at @c bo_mode in thread AR. Also,
177 * in this case @c NULL is stored into *(@a res).
178 *
179 * @param run Runner object
180 * @param proc_ar Procedure activation record
181 * @param res Place to store procedure return value
182 */
183void run_proc(run_t *run, run_proc_ar_t *proc_ar, rdata_item_t **res)
184{
185 stree_proc_t *proc;
186 list_node_t *node;
187
188 proc = proc_ar->proc;
189
190#ifdef DEBUG_RUN_TRACE
191 printf("Start executing function '");
192 symbol_print_fqn(proc->outer_symbol);
193 printf("'.\n");
194#endif
195 /* Add procedure AR to the stack. */
196 list_append(&run->thread_ar->proc_ar, proc_ar);
197
198 /* Run main procedure block. */
199 if (proc->body != NULL) {
200 run_block(run, proc->body);
201 } else {
202 builtin_run_proc(run, proc);
203 }
204
205 /* Handle bailout. */
206 switch (run->thread_ar->bo_mode) {
207 case bm_stat:
208 /* Break bailout was not caught. */
209 assert(b_false);
210 /* Fallthrough */
211 case bm_proc:
212 run->thread_ar->bo_mode = bm_none;
213 break;
214 default:
215 break;
216 }
217
218#ifdef DEBUG_RUN_TRACE
219 printf("Done executing procedure '");
220 symbol_print_fqn(proc->outer_symbol);
221 printf("'.\n");
222
223 run_print_fun_bt(run);
224#endif
225 /* Remove procedure activation record from the stack. */
226 node = list_last(&run->thread_ar->proc_ar);
227 assert(list_node_data(node, run_proc_ar_t *) == proc_ar);
228 list_remove(&run->thread_ar->proc_ar, node);
229
230 /* Procedure should not return an address. */
231 assert(proc_ar->retval == NULL || proc_ar->retval->ic == ic_value);
232 *res = proc_ar->retval;
233}
234
235/** Run code block.
236 *
237 * @param run Runner object
238 * @param block Block to run
239 */
240static void run_block(run_t *run, stree_block_t *block)
241{
242 run_proc_ar_t *proc_ar;
243 run_block_ar_t *block_ar;
244 list_node_t *node;
245 stree_stat_t *stat;
246
247#ifdef DEBUG_RUN_TRACE
248 printf("Executing one code block.\n");
249#endif
250
251 /* Create block activation record. */
252 block_ar = run_block_ar_new();
253 intmap_init(&block_ar->vars);
254
255 /* Add block activation record to the stack. */
256 proc_ar = run_get_current_proc_ar(run);
257 list_append(&proc_ar->block_ar, block_ar);
258
259 node = list_first(&block->stats);
260 while (node != NULL) {
261 stat = list_node_data(node, stree_stat_t *);
262 run_stat(run, stat, NULL);
263
264 if (run->thread_ar->bo_mode != bm_none)
265 break;
266
267 node = list_next(&block->stats, node);
268 }
269
270#ifdef DEBUG_RUN_TRACE
271 printf("Done executing code block.\n");
272#endif
273
274 /* Remove block activation record from the stack. */
275 node = list_last(&proc_ar->block_ar);
276 assert(list_node_data(node, run_block_ar_t *) == block_ar);
277 list_remove(&proc_ar->block_ar, node);
278
279 /* Deallocate block activation record. */
280 run_block_ar_destroy(run, block_ar);
281}
282
283/** Run statement.
284 *
285 * Executes a statement. If @a res is not NULL and the statement is an
286 * expression statement with a value, the value item will be stored to
287 * @a res.
288 *
289 * @param run Runner object
290 * @param stat Statement to run
291 * @param res Place to store exps result or NULL if not interested
292 */
293void run_stat(run_t *run, stree_stat_t *stat, rdata_item_t **res)
294{
295#ifdef DEBUG_RUN_TRACE
296 printf("Executing one statement %p.\n", stat);
297#endif
298
299 if (res != NULL)
300 *res = NULL;
301
302 switch (stat->sc) {
303 case st_exps:
304 run_exps(run, stat->u.exp_s, res);
305 break;
306 case st_vdecl:
307 run_vdecl(run, stat->u.vdecl_s);
308 break;
309 case st_if:
310 run_if(run, stat->u.if_s);
311 break;
312 case st_switch:
313 run_switch(run, stat->u.switch_s);
314 break;
315 case st_while:
316 run_while(run, stat->u.while_s);
317 break;
318 case st_raise:
319 run_raise(run, stat->u.raise_s);
320 break;
321 case st_break:
322 run_break(run, stat->u.break_s);
323 break;
324 case st_return:
325 run_return(run, stat->u.return_s);
326 break;
327 case st_wef:
328 run_wef(run, stat->u.wef_s);
329 break;
330 case st_for:
331 printf("Ignoring unimplemented statement type %d.\n", stat->sc);
332 break;
333 }
334}
335
336/** Run expression statement.
337 *
338 * Executes an expression statement. If @a res is not NULL then the value
339 * of the expression (or NULL if it has no value) will be stored to @a res.
340 *
341 * @param run Runner object
342 * @param exps Expression statement to run
343 * @param res Place to store exps result or NULL if not interested
344 */
345static void run_exps(run_t *run, stree_exps_t *exps, rdata_item_t **res)
346{
347 rdata_item_t *rexpr;
348
349#ifdef DEBUG_RUN_TRACE
350 printf("Executing expression statement.\n");
351#endif
352 run_expr(run, exps->expr, &rexpr);
353
354 /*
355 * If the expression has a value, the caller should have asked for it.
356 */
357 assert(res != NULL || rexpr == NULL);
358
359 if (res != NULL)
360 *res = rexpr;
361}
362
363/** Run variable declaration statement.
364 *
365 * @param run Runner object
366 * @param vdecl Variable declaration statement to run
367 */
368static void run_vdecl(run_t *run, stree_vdecl_t *vdecl)
369{
370 run_block_ar_t *block_ar;
371 rdata_var_t *var, *old_var;
372
373#ifdef DEBUG_RUN_TRACE
374 printf("Executing variable declaration statement.\n");
375#endif
376 /* Create variable and initialize with default value. */
377 run_var_new(run, vdecl->titem, &var);
378
379 block_ar = run_get_current_block_ar(run);
380 old_var = (rdata_var_t *) intmap_get(&block_ar->vars, vdecl->name->sid);
381
382 if (old_var != NULL) {
383 printf("Error: Duplicate variable '%s'\n",
384 strtab_get_str(vdecl->name->sid));
385 exit(1);
386 }
387
388 intmap_set(&block_ar->vars, vdecl->name->sid, var);
389
390#ifdef DEBUG_RUN_TRACE
391 printf("Declared variable '%s'\n", strtab_get_str(vdecl->name->sid));
392#endif
393}
394
395/** Run @c if statement.
396 *
397 * @param run Runner object
398 * @param if_s If statement to run
399 */
400static void run_if(run_t *run, stree_if_t *if_s)
401{
402 rdata_item_t *rcond;
403 list_node_t *ifc_node;
404 stree_if_clause_t *ifc;
405 bool_t rcond_b, clause_fired;
406
407#ifdef DEBUG_RUN_TRACE
408 printf("Executing if statement.\n");
409#endif
410 clause_fired = b_false;
411 ifc_node = list_first(&if_s->if_clauses);
412
413 /* Walk through all if/elif clauses and see if they fire. */
414
415 while (ifc_node != NULL) {
416 /* Get if/elif clause */
417 ifc = list_node_data(ifc_node, stree_if_clause_t *);
418
419 run_expr(run, ifc->cond, &rcond);
420 if (run_is_bo(run))
421 return;
422
423 rcond_b = run_item_boolean_value(run, rcond);
424 rdata_item_destroy(rcond);
425
426 if (rcond_b == b_true) {
427#ifdef DEBUG_RUN_TRACE
428 printf("Taking non-default path.\n");
429#endif
430 run_block(run, ifc->block);
431 clause_fired = b_true;
432 break;
433 }
434
435 ifc_node = list_next(&if_s->if_clauses, ifc_node);
436 }
437
438 /* If no if/elif clause fired, invoke the else clause. */
439 if (clause_fired == b_false && if_s->else_block != NULL) {
440#ifdef DEBUG_RUN_TRACE
441 printf("Taking default path.\n");
442#endif
443 run_block(run, if_s->else_block);
444 }
445
446#ifdef DEBUG_RUN_TRACE
447 printf("If statement terminated.\n");
448#endif
449}
450
451/** Run @c switch statement.
452 *
453 * @param run Runner object
454 * @param switch_s Switch statement to run
455 */
456static void run_switch(run_t *run, stree_switch_t *switch_s)
457{
458 rdata_item_t *rsexpr, *rsexpr_vi;
459 rdata_item_t *rwexpr, *rwexpr_vi;
460 list_node_t *whenc_node;
461 stree_when_t *whenc;
462 list_node_t *expr_node;
463 stree_expr_t *expr;
464 bool_t clause_fired;
465 bool_t equal;
466
467#ifdef DEBUG_RUN_TRACE
468 printf("Executing switch statement.\n");
469#endif
470 rsexpr_vi = NULL;
471
472 /* Evaluate switch expression */
473 run_expr(run, switch_s->expr, &rsexpr);
474 if (run_is_bo(run))
475 goto cleanup;
476
477 /* Convert to value item */
478 run_cvt_value_item(run, rsexpr, &rsexpr_vi);
479 rdata_item_destroy(rsexpr);
480 if (run_is_bo(run))
481 goto cleanup;
482
483 clause_fired = b_false;
484 whenc_node = list_first(&switch_s->when_clauses);
485
486 /* Walk through all when clauses and see if they fire. */
487
488 while (whenc_node != NULL) {
489 /* Get when clause */
490 whenc = list_node_data(whenc_node, stree_when_t *);
491
492 expr_node = list_first(&whenc->exprs);
493
494 /* Walk through all expressions in the when clause */
495 while (expr_node != NULL) {
496 /* Get expression */
497 expr = list_node_data(expr_node, stree_expr_t *);
498
499 /* Evaluate expression */
500 run_expr(run, expr, &rwexpr);
501 if (run_is_bo(run))
502 goto cleanup;
503
504 /* Convert to value item */
505 run_cvt_value_item(run, rwexpr, &rwexpr_vi);
506 rdata_item_destroy(rwexpr);
507 if (run_is_bo(run)) {
508 rdata_item_destroy(rwexpr_vi);
509 goto cleanup;
510 }
511
512 /* Check if values are equal ('==') */
513 run_equal(run, rsexpr_vi->u.value,
514 rwexpr_vi->u.value, &equal);
515 rdata_item_destroy(rwexpr_vi);
516 if (run_is_bo(run))
517 goto cleanup;
518
519 if (equal) {
520#ifdef DEBUG_RUN_TRACE
521 printf("Taking non-default path.\n");
522#endif
523 run_block(run, whenc->block);
524 clause_fired = b_true;
525 break;
526 }
527
528 expr_node = list_next(&whenc->exprs, expr_node);
529 }
530
531 if (clause_fired)
532 break;
533
534 whenc_node = list_next(&switch_s->when_clauses, whenc_node);
535 }
536
537 /* If no when clause fired, invoke the else clause. */
538 if (clause_fired == b_false && switch_s->else_block != NULL) {
539#ifdef DEBUG_RUN_TRACE
540 printf("Taking default path.\n");
541#endif
542 run_block(run, switch_s->else_block);
543 }
544cleanup:
545 if (rsexpr_vi != NULL)
546 rdata_item_destroy(rsexpr_vi);
547
548#ifdef DEBUG_RUN_TRACE
549 printf("Switch statement terminated.\n");
550#endif
551}
552
553/** Run @c while statement.
554 *
555 * @param run Runner object
556 * @param while_s While statement to run
557 */
558static void run_while(run_t *run, stree_while_t *while_s)
559{
560 rdata_item_t *rcond;
561
562#ifdef DEBUG_RUN_TRACE
563 printf("Executing while statement.\n");
564#endif
565 run_expr(run, while_s->cond, &rcond);
566 if (run_is_bo(run))
567 return;
568
569 while (run_item_boolean_value(run, rcond) == b_true) {
570 rdata_item_destroy(rcond);
571 run_block(run, while_s->body);
572 run_expr(run, while_s->cond, &rcond);
573 if (run_is_bo(run))
574 break;
575 }
576
577 if (rcond != NULL)
578 rdata_item_destroy(rcond);
579
580 if (run->thread_ar->bo_mode == bm_stat) {
581 /* Bailout due to break statement */
582 run->thread_ar->bo_mode = bm_none;
583 }
584
585#ifdef DEBUG_RUN_TRACE
586 printf("While statement terminated.\n");
587#endif
588}
589
590/** Run @c raise statement.
591 *
592 * @param run Runner object
593 * @param raise_s Raise statement to run
594 */
595static void run_raise(run_t *run, stree_raise_t *raise_s)
596{
597 rdata_item_t *rexpr;
598 rdata_item_t *rexpr_vi;
599
600#ifdef DEBUG_RUN_TRACE
601 printf("Executing raise statement.\n");
602#endif
603 run_expr(run, raise_s->expr, &rexpr);
604 if (run_is_bo(run))
605 return;
606
607 run_cvt_value_item(run, rexpr, &rexpr_vi);
608 rdata_item_destroy(rexpr);
609 if (run_is_bo(run))
610 return;
611
612 /* Store expression cspan in thread AR. */
613 run->thread_ar->exc_cspan = raise_s->expr->cspan;
614
615 /* Store expression result in thread AR. */
616 /* XXX rexpr_vi is leaked here, we only return ->u.value */
617 run->thread_ar->exc_payload = rexpr_vi->u.value;
618
619 /* Start exception bailout. */
620 run->thread_ar->bo_mode = bm_exc;
621}
622
623/** Run @c break statement.
624 *
625 * Forces control to return from the active breakable statement by setting
626 * bailout mode to @c bm_stat.
627 *
628 * @param run Runner object
629 * @param break_s Break statement to run
630 */
631static void run_break(run_t *run, stree_break_t *break_s)
632{
633#ifdef DEBUG_RUN_TRACE
634 printf("Executing 'break' statement.\n");
635#endif
636 (void) break_s;
637
638 /* Force control to ascend and leave the procedure. */
639 if (run->thread_ar->bo_mode == bm_none)
640 run->thread_ar->bo_mode = bm_stat;
641}
642
643/** Run @c return statement.
644 *
645 * Sets the return value in procedure AR and forces control to return
646 * from the function by setting bailout mode to @c bm_proc.
647 *
648 * @param run Runner object
649 * @param return_s Return statement to run
650 */
651static void run_return(run_t *run, stree_return_t *return_s)
652{
653 rdata_item_t *rexpr;
654 rdata_item_t *rexpr_vi;
655 run_proc_ar_t *proc_ar;
656
657#ifdef DEBUG_RUN_TRACE
658 printf("Executing return statement.\n");
659#endif
660 if (return_s->expr != NULL) {
661 run_expr(run, return_s->expr, &rexpr);
662 if (run_is_bo(run))
663 return;
664
665 run_cvt_value_item(run, rexpr, &rexpr_vi);
666 rdata_item_destroy(rexpr);
667 if (run_is_bo(run))
668 return;
669
670 /* Store expression result in procedure AR. */
671 proc_ar = run_get_current_proc_ar(run);
672 proc_ar->retval = rexpr_vi;
673 }
674
675 /* Force control to ascend and leave the procedure. */
676 if (run->thread_ar->bo_mode == bm_none)
677 run->thread_ar->bo_mode = bm_proc;
678}
679
680/** Run @c with-except-finally statement.
681 *
682 * Note: 'With' clause is not implemented.
683 *
684 * @param run Runner object
685 * @param wef_s With-except-finally statement to run
686 */
687static void run_wef(run_t *run, stree_wef_t *wef_s)
688{
689 list_node_t *except_n;
690 stree_except_t *except_c;
691 rdata_value_t *exc_payload;
692 run_bailout_mode_t bo_mode;
693
694#ifdef DEBUG_RUN_TRACE
695 printf("Executing with-except-finally statement.\n");
696#endif
697 run_block(run, wef_s->with_block);
698
699 if (run->thread_ar->bo_mode == bm_exc) {
700#ifdef DEBUG_RUN_TRACE
701 printf("With statement detected exception.\n");
702#endif
703 /* Reset to normal execution. */
704 run->thread_ar->bo_mode = bm_none;
705
706 /* Look for an except block. */
707 except_n = list_first(&wef_s->except_clauses);
708 while (except_n != NULL) {
709 except_c = list_node_data(except_n, stree_except_t *);
710 if (run_exc_match(run, except_c))
711 break;
712
713 except_n = list_next(&wef_s->except_clauses, except_n);
714 }
715
716 /* If one was found, execute it. */
717 if (except_n != NULL)
718 run_block(run, except_c->block);
719
720 /* Execute finally block */
721 if (wef_s->finally_block != NULL) {
722 /* Put exception on the side temporarily. */
723 bo_mode = run->thread_ar->bo_mode;
724 exc_payload = run->thread_ar->exc_payload;
725
726 run->thread_ar->bo_mode = bm_none;
727 run->thread_ar->exc_payload = NULL;
728
729 run_block(run, wef_s->finally_block);
730
731 if (bo_mode == bm_exc) {
732 /*
733 * Restore the original exception. If another
734 * exception occured in the finally block (i.e.
735 * double fault), it is forgotten.
736 */
737 run->thread_ar->bo_mode = bm_exc;
738 run->thread_ar->exc_payload = exc_payload;
739 }
740 }
741 }
742
743#ifdef DEBUG_RUN_TRACE
744 printf("With-except-finally statement terminated.\n");
745#endif
746}
747
748/** Determine whether currently active exception matches @c except clause.
749 *
750 * Checks if the currently active exception in the runner object @c run
751 * matches except clause @c except_c.
752 *
753 * @param run Runner object
754 * @param except_c @c except clause
755 * @return @c b_true if there is a match, @c b_false otherwise
756 */
757static bool_t run_exc_match(run_t *run, stree_except_t *except_c)
758{
759 stree_csi_t *exc_csi;
760
761 /* Get CSI of active exception. */
762 exc_csi = run_exc_payload_get_csi(run);
763
764 /* Determine if active exc. is derived from type in exc. clause. */
765 /* XXX This is wrong, it does not work with generics. */
766 return tdata_is_csi_derived_from_ti(exc_csi, except_c->titem);
767}
768
769/** Return CSI of the active exception.
770 *
771 * @param run Runner object
772 * @return CSI of the active exception
773 */
774static stree_csi_t *run_exc_payload_get_csi(run_t *run)
775{
776 rdata_value_t *payload;
777 rdata_var_t *payload_v;
778 rdata_object_t *payload_o;
779
780 payload = run->thread_ar->exc_payload;
781 assert(payload != NULL);
782
783 if (payload->var->vc != vc_ref) {
784 /* XXX Prevent this via static type checking. */
785 printf("Error: Exception payload must be an object "
786 "(found type %d).\n", payload->var->vc);
787 exit(1);
788 }
789
790 payload_v = payload->var->u.ref_v->vref;
791 if (payload_v->vc != vc_object) {
792 /* XXX Prevent this via static type checking. */
793 printf("Error: Exception payload must be an object "
794 "(found type %d).\n", payload_v->vc);
795 exit(1);
796 }
797
798 payload_o = payload_v->u.object_v;
799
800#ifdef DEBUG_RUN_TRACE
801 printf("Active exception: '");
802 symbol_print_fqn(payload_o->class_sym);
803 printf("'.\n");
804#endif
805 assert(payload_o->class_sym != NULL);
806 assert(payload_o->class_sym->sc == sc_csi);
807
808 return payload_o->class_sym->u.csi;
809}
810
811/** Check for unhandled exception.
812 *
813 * Checks whether there is an active exception. If so, it prints an
814 * error message and raises a run-time error.
815 *
816 * @param run Runner object
817 */
818void run_exc_check_unhandled(run_t *run)
819{
820 stree_csi_t *exc_csi;
821
822 if (run->thread_ar->bo_mode != bm_none) {
823 assert(run->thread_ar->bo_mode == bm_exc);
824
825 exc_csi = run_exc_payload_get_csi(run);
826
827 if (run->thread_ar->exc_cspan != NULL) {
828 cspan_print(run->thread_ar->exc_cspan);
829 putchar(' ');
830 }
831
832 printf("Error: Unhandled exception '");
833 symbol_print_fqn(csi_to_symbol(exc_csi));
834 printf("'.\n");
835
836 run_raise_error(run);
837 }
838}
839
840/** Raise an irrecoverable run-time error, start bailing out.
841 *
842 * Raises an error that cannot be handled by the user program.
843 *
844 * @param run Runner object
845 */
846void run_raise_error(run_t *run)
847{
848 run->thread_ar->bo_mode = bm_error;
849 run->thread_ar->error = b_true;
850}
851
852/** Construct a special recovery item.
853 *
854 * @param run Runner object
855 */
856rdata_item_t *run_recovery_item(run_t *run)
857{
858 (void) run;
859 return NULL;
860}
861
862/** Find a local variable in the currently active function.
863 *
864 * @param run Runner object
865 * @param name Name SID of the local variable
866 * @return Pointer to var node or @c NULL if not found
867 */
868rdata_var_t *run_local_vars_lookup(run_t *run, sid_t name)
869{
870 run_proc_ar_t *proc_ar;
871 run_block_ar_t *block_ar;
872 rdata_var_t *var;
873 list_node_t *node;
874
875 proc_ar = run_get_current_proc_ar(run);
876 node = list_last(&proc_ar->block_ar);
877
878 /* Walk through all block activation records. */
879 while (node != NULL) {
880 block_ar = list_node_data(node, run_block_ar_t *);
881 var = intmap_get(&block_ar->vars, name);
882 if (var != NULL)
883 return var;
884
885 node = list_prev(&proc_ar->block_ar, node);
886 }
887
888 /* No match */
889 return NULL;
890}
891
892/** Get current procedure activation record.
893 *
894 * @param run Runner object
895 * @return Active procedure AR
896 */
897run_proc_ar_t *run_get_current_proc_ar(run_t *run)
898{
899 list_node_t *node;
900
901 node = list_last(&run->thread_ar->proc_ar);
902 return list_node_data(node, run_proc_ar_t *);
903}
904
905/** Get current block activation record.
906 *
907 * @param run Runner object
908 * @return Active block AR
909 */
910run_block_ar_t *run_get_current_block_ar(run_t *run)
911{
912 run_proc_ar_t *proc_ar;
913 list_node_t *node;
914
915 proc_ar = run_get_current_proc_ar(run);
916
917 node = list_last(&proc_ar->block_ar);
918 return list_node_data(node, run_block_ar_t *);
919}
920
921/** Get current CSI.
922 *
923 * @param run Runner object
924 * @return Active CSI
925 */
926stree_csi_t *run_get_current_csi(run_t *run)
927{
928 run_proc_ar_t *proc_ar;
929
930 proc_ar = run_get_current_proc_ar(run);
931 return proc_ar->proc->outer_symbol->outer_csi;
932}
933
934/** Get static object (i.e. class object).
935 *
936 * Looks for a child static object named @a name in static object @a pobject.
937 * If the child does not exist yet, it is created.
938 *
939 * @param run Runner object
940 * @param csi CSI of the static object named @a name
941 * @param pobject Parent static object
942 * @param name Static object name
943 *
944 * @return Static (class) object of the given name
945 */
946rdata_var_t *run_sobject_get(run_t *run, stree_csi_t *csi,
947 rdata_var_t *pobj_var, sid_t name)
948{
949 rdata_object_t *pobject;
950 rdata_var_t *mbr_var;
951 rdata_var_t *rvar;
952 stree_ident_t *ident;
953
954 assert(pobj_var->vc == vc_object);
955 pobject = pobj_var->u.object_v;
956#ifdef DEBUG_RUN_TRACE
957 printf("Get static object '%s' in '", strtab_get_str(name));
958 if (pobject->class_sym != NULL)
959 symbol_print_fqn(pobject->class_sym);
960 else
961 printf("global");
962#endif
963
964 assert(pobject->static_obj == sn_static);
965
966 mbr_var = intmap_get(&pobject->fields, name);
967 if (mbr_var != NULL) {
968#ifdef DEBUG_RUN_TRACE
969 printf("Return exising static object (mbr_var=%p).\n", mbr_var);
970#endif
971 /* Return existing object. */
972 return mbr_var;
973 }
974
975 /* Construct new object. */
976#ifdef DEBUG_RUN_TRACE
977 printf("Construct new static object.\n");
978#endif
979 ident = stree_ident_new();
980 ident->sid = name;
981
982 run_new_csi_inst(run, csi, sn_static, &rvar);
983
984 /* Store static object reference for future use. */
985 intmap_set(&pobject->fields, name, rvar);
986
987 return rvar;
988}
989
990/** Get static object for CSI.
991 *
992 * In situations where we do not have the parent static object and need
993 * to find static object for a CSI from the gdata root we use this.
994 *
995 * This is only used in special cases such as when invoking the entry
996 * point. This is too slow to use during normal execution.
997 *
998 * @param run Runner object
999 * @param csi CSI to get static object of
1000 *
1001 * @return Static (class) object
1002 */
1003rdata_var_t *run_sobject_find(run_t *run, stree_csi_t *csi)
1004{
1005 rdata_var_t *pobj_var;
1006
1007 if (csi == NULL)
1008 return run->gdata;
1009
1010 assert(csi->ancr_state == ws_visited);
1011 pobj_var = run_sobject_find(run, csi_to_symbol(csi)->outer_csi);
1012
1013 return run_sobject_get(run, csi, pobj_var, csi->name->sid);
1014}
1015
1016/** Get static object for CSI containing function.
1017 *
1018 * This is used to obtain active object for invoking static function
1019 * @a fun. Only used in cases where we don't already have the object such
1020 * as when running the entry point. Otherwise this would be slow.
1021 *
1022 * @param run Runner object
1023 * @param fun Function to get static class object of
1024 *
1025 * @return Static (class) object
1026 */
1027rdata_var_t *run_fun_sobject_find(run_t *run, stree_fun_t *fun)
1028{
1029 return run_sobject_find(run, fun_to_symbol(fun)->outer_csi);
1030}
1031
1032/** Construct variable from a value item.
1033 *
1034 * XXX This should be in fact implemented using existing code as:
1035 *
1036 * (1) Create a variable of the desired type.
1037 * (2) Initialize the variable with the provided value.
1038 *
1039 * @param item Value item (initial value for variable).
1040 * @param var Place to store new var node.
1041 */
1042void run_value_item_to_var(rdata_item_t *item, rdata_var_t **var)
1043{
1044 rdata_bool_t *bool_v;
1045 rdata_char_t *char_v;
1046 rdata_deleg_t *deleg_v;
1047 rdata_enum_t *enum_v;
1048 rdata_int_t *int_v;
1049 rdata_string_t *string_v;
1050 rdata_ref_t *ref_v;
1051 rdata_var_t *in_var;
1052
1053 assert(item->ic == ic_value);
1054 in_var = item->u.value->var;
1055
1056 switch (in_var->vc) {
1057 case vc_bool:
1058 *var = rdata_var_new(vc_bool);
1059 bool_v = rdata_bool_new();
1060
1061 (*var)->u.bool_v = bool_v;
1062 bool_v->value = item->u.value->var->u.bool_v->value;
1063 break;
1064 case vc_char:
1065 *var = rdata_var_new(vc_char);
1066 char_v = rdata_char_new();
1067
1068 (*var)->u.char_v = char_v;
1069 bigint_clone(&item->u.value->var->u.char_v->value,
1070 &char_v->value);
1071 break;
1072 case vc_deleg:
1073 *var = rdata_var_new(vc_deleg);
1074 deleg_v = rdata_deleg_new();
1075
1076 (*var)->u.deleg_v = deleg_v;
1077 deleg_v->obj = item->u.value->var->u.deleg_v->obj;
1078 deleg_v->sym = item->u.value->var->u.deleg_v->sym;
1079 break;
1080 case vc_enum:
1081 *var = rdata_var_new(vc_enum);
1082 enum_v = rdata_enum_new();
1083
1084 (*var)->u.enum_v = enum_v;
1085 enum_v->value = item->u.value->var->u.enum_v->value;
1086 break;
1087 case vc_int:
1088 *var = rdata_var_new(vc_int);
1089 int_v = rdata_int_new();
1090
1091 (*var)->u.int_v = int_v;
1092 bigint_clone(&item->u.value->var->u.int_v->value,
1093 &int_v->value);
1094 break;
1095 case vc_string:
1096 *var = rdata_var_new(vc_string);
1097 string_v = rdata_string_new();
1098
1099 (*var)->u.string_v = string_v;
1100 string_v->value = item->u.value->var->u.string_v->value;
1101 break;
1102 case vc_ref:
1103 *var = rdata_var_new(vc_ref);
1104 ref_v = rdata_ref_new();
1105
1106 (*var)->u.ref_v = ref_v;
1107 ref_v->vref = item->u.value->var->u.ref_v->vref;
1108 break;
1109 default:
1110 printf("Error: Unimplemented argument type.\n");
1111 exit(1);
1112
1113 }
1114}
1115
1116/** Construct a procedure AR.
1117 *
1118 * @param run Runner object
1119 * @param obj Object whose procedure is being activated
1120 * @param proc Procedure that is being activated
1121 * @param rproc_ar Place to store pointer to new activation record
1122 */
1123void run_proc_ar_create(run_t *run, rdata_var_t *obj, stree_proc_t *proc,
1124 run_proc_ar_t **rproc_ar)
1125{
1126 run_proc_ar_t *proc_ar;
1127 run_block_ar_t *block_ar;
1128
1129 (void) run;
1130
1131 /* Create procedure activation record. */
1132 proc_ar = run_proc_ar_new();
1133 proc_ar->obj = obj;
1134 proc_ar->proc = proc;
1135 list_init(&proc_ar->block_ar);
1136
1137 proc_ar->retval = NULL;
1138
1139 /* Create special block activation record to hold function arguments. */
1140 block_ar = run_block_ar_new();
1141 intmap_init(&block_ar->vars);
1142 list_append(&proc_ar->block_ar, block_ar);
1143
1144 *rproc_ar = proc_ar;
1145}
1146
1147/** Destroy a procedure AR.
1148 *
1149 * @param run Runner object
1150 * @param proc_ar Pointer to procedure activation record
1151 */
1152void run_proc_ar_destroy(run_t *run, run_proc_ar_t *proc_ar)
1153{
1154 list_node_t *ar_node;
1155 run_block_ar_t *block_ar;
1156
1157 (void) run;
1158
1159 /* Destroy special block activation record. */
1160 ar_node = list_first(&proc_ar->block_ar);
1161 block_ar = list_node_data(ar_node, run_block_ar_t *);
1162 list_remove(&proc_ar->block_ar, ar_node);
1163 run_block_ar_destroy(run, block_ar);
1164
1165 /* Destroy procedure activation record. */
1166 proc_ar->obj = NULL;
1167 proc_ar->proc = NULL;
1168 list_fini(&proc_ar->block_ar);
1169 proc_ar->retval = NULL;
1170 run_proc_ar_delete(proc_ar);
1171}
1172
1173/** Fill arguments in a procedure AR.
1174 *
1175 * When invoking a procedure this is used to store the argument values
1176 * in the activation record.
1177 *
1178 * @param run Runner object
1179 * @param proc_ar Existing procedure activation record where to store
1180 * the values
1181 * @param arg_vals List of value items (rdata_item_t *) -- real
1182 * argument values
1183 */
1184void run_proc_ar_set_args(run_t *run, run_proc_ar_t *proc_ar, list_t *arg_vals)
1185{
1186 stree_ctor_t *ctor;
1187 stree_fun_t *fun;
1188 stree_prop_t *prop;
1189 list_t *args;
1190 stree_proc_arg_t *varg;
1191 stree_symbol_t *outer_symbol;
1192
1193 run_block_ar_t *block_ar;
1194 list_node_t *block_ar_n;
1195 list_node_t *rarg_n, *parg_n;
1196 list_node_t *cn;
1197 rdata_item_t *rarg;
1198 stree_proc_arg_t *parg;
1199 rdata_var_t *var;
1200 rdata_var_t *ref_var;
1201 rdata_ref_t *ref;
1202 rdata_array_t *array;
1203 rdata_var_t *elem_var;
1204 int n_vargs, idx;
1205
1206 (void) run;
1207
1208 /* AR should have been created with run_proc_ar_create(). */
1209 assert(proc_ar->proc != NULL);
1210 outer_symbol = proc_ar->proc->outer_symbol;
1211
1212 /* Make compiler happy. */
1213 args = NULL;
1214 varg = NULL;
1215
1216 /*
1217 * The procedure being activated should belong to a member function or
1218 * property getter/setter.
1219 */
1220 switch (outer_symbol->sc) {
1221 case sc_ctor:
1222 ctor = symbol_to_ctor(outer_symbol);
1223 args = &ctor->sig->args;
1224 varg = ctor->sig->varg;
1225 break;
1226 case sc_fun:
1227 fun = symbol_to_fun(outer_symbol);
1228 args = &fun->sig->args;
1229 varg = fun->sig->varg;
1230 break;
1231 case sc_prop:
1232 prop = symbol_to_prop(outer_symbol);
1233 args = &prop->args;
1234 varg = prop->varg;
1235 break;
1236 case sc_csi:
1237 case sc_deleg:
1238 case sc_enum:
1239 case sc_var:
1240 assert(b_false);
1241 }
1242
1243 /* Fetch first block activation record. */
1244 block_ar_n = list_first(&proc_ar->block_ar);
1245 assert(block_ar_n != NULL);
1246 block_ar = list_node_data(block_ar_n, run_block_ar_t *);
1247
1248 /* Declare local variables to hold argument values. */
1249 rarg_n = list_first(arg_vals);
1250 parg_n = list_first(args);
1251
1252 while (parg_n != NULL) {
1253 if (rarg_n == NULL) {
1254 printf("Error: Too few arguments to '");
1255 symbol_print_fqn(outer_symbol);
1256 printf("'.\n");
1257 exit(1);
1258 }
1259
1260 rarg = list_node_data(rarg_n, rdata_item_t *);
1261 parg = list_node_data(parg_n, stree_proc_arg_t *);
1262
1263 assert(rarg->ic == ic_value);
1264
1265 /* Construct a variable from the argument value. */
1266 run_value_item_to_var(rarg, &var);
1267
1268 /* Declare variable using name of formal argument. */
1269 intmap_set(&block_ar->vars, parg->name->sid, var);
1270
1271 rarg_n = list_next(arg_vals, rarg_n);
1272 parg_n = list_next(args, parg_n);
1273 }
1274
1275 if (varg != NULL) {
1276 /* Function is variadic. Count number of variadic arguments. */
1277 cn = rarg_n;
1278 n_vargs = 0;
1279 while (cn != NULL) {
1280 n_vargs += 1;
1281 cn = list_next(arg_vals, cn);
1282 }
1283
1284 /* Prepare array to store variadic arguments. */
1285 array = rdata_array_new(1);
1286 array->extent[0] = n_vargs;
1287 rdata_array_alloc_element(array);
1288
1289 /* Read variadic arguments. */
1290
1291 idx = 0;
1292 while (rarg_n != NULL) {
1293 rarg = list_node_data(rarg_n, rdata_item_t *);
1294 assert(rarg->ic == ic_value);
1295
1296 run_value_item_to_var(rarg, &elem_var);
1297 array->element[idx] = elem_var;
1298
1299 rarg_n = list_next(arg_vals, rarg_n);
1300 idx += 1;
1301 }
1302
1303 var = rdata_var_new(vc_array);
1304 var->u.array_v = array;
1305
1306 /* Create reference to the new array. */
1307 ref_var = rdata_var_new(vc_ref);
1308 ref = rdata_ref_new();
1309 ref_var->u.ref_v = ref;
1310 ref->vref = var;
1311
1312 /* Declare variable using name of formal argument. */
1313 intmap_set(&block_ar->vars, varg->name->sid,
1314 ref_var);
1315 }
1316
1317 /* Check for excess real parameters. */
1318 if (rarg_n != NULL) {
1319 printf("Error: Too many arguments to '");
1320 symbol_print_fqn(outer_symbol);
1321 printf("'.\n");
1322 exit(1);
1323 }
1324}
1325
1326/** Fill setter argument in a procedure AR.
1327 *
1328 * When invoking a setter this is used to store its argument value in its
1329 * procedure activation record.
1330 *
1331 * @param run Runner object
1332 * @param proc_ar Existing procedure activation record where to store
1333 * the setter argument
1334 * @param arg_val Value items (rdata_item_t *) -- real argument value
1335 */
1336void run_proc_ar_set_setter_arg(run_t *run, run_proc_ar_t *proc_ar,
1337 rdata_item_t *arg_val)
1338{
1339 stree_prop_t *prop;
1340 run_block_ar_t *block_ar;
1341 list_node_t *block_ar_n;
1342 rdata_var_t *var;
1343
1344 (void) run;
1345
1346 /* AR should have been created with run_proc_ar_create(). */
1347 assert(proc_ar->proc != NULL);
1348
1349 /* The procedure being activated should belong to a property setter. */
1350 prop = symbol_to_prop(proc_ar->proc->outer_symbol);
1351 assert(prop != NULL);
1352 assert(proc_ar->proc == prop->setter);
1353
1354 /* Fetch first block activation record. */
1355 block_ar_n = list_first(&proc_ar->block_ar);
1356 assert(block_ar_n != NULL);
1357 block_ar = list_node_data(block_ar_n, run_block_ar_t *);
1358
1359 assert(arg_val->ic == ic_value);
1360
1361 /* Construct a variable from the argument value. */
1362 run_value_item_to_var(arg_val, &var);
1363
1364 /* Declare variable using name of formal argument. */
1365 intmap_set(&block_ar->vars, prop->setter_arg->name->sid, var);
1366}
1367
1368/** Print function activation backtrace.
1369 *
1370 * Prints a backtrace of activated functions for debugging purposes.
1371 *
1372 * @param run Runner object
1373 */
1374void run_print_fun_bt(run_t *run)
1375{
1376 list_node_t *node;
1377 run_proc_ar_t *proc_ar;
1378
1379 printf("Backtrace:\n");
1380 node = list_last(&run->thread_ar->proc_ar);
1381 while (node != NULL) {
1382 printf(" * ");
1383 proc_ar = list_node_data(node, run_proc_ar_t *);
1384 symbol_print_fqn(proc_ar->proc->outer_symbol);
1385 printf("\n");
1386
1387 node = list_prev(&run->thread_ar->proc_ar, node);
1388 }
1389}
1390
1391/** Destroy a block AR.
1392 *
1393 * @param run Runner object
1394 * @param proc_ar Pointer to block activation record
1395 */
1396void run_block_ar_destroy(run_t *run, run_block_ar_t *block_ar)
1397{
1398 map_elem_t *elem;
1399 rdata_var_t *var;
1400 int key;
1401
1402 (void) run;
1403
1404 elem = intmap_first(&block_ar->vars);
1405 while (elem != NULL) {
1406 /* Destroy the variable */
1407 var = intmap_elem_get_value(elem);
1408 rdata_var_destroy(var);
1409
1410 /* Remove the map element */
1411 key = intmap_elem_get_key(elem);
1412 intmap_set(&block_ar->vars, key, NULL);
1413
1414 elem = intmap_first(&block_ar->vars);
1415 }
1416
1417 intmap_fini(&block_ar->vars);
1418 run_block_ar_delete(block_ar);
1419}
1420
1421/** Convert item to value item.
1422 *
1423 * If @a item is a value, we just return a copy. If @a item is an address,
1424 * we read from the address.
1425 *
1426 * @param run Runner object
1427 * @param item Input item (value or address)
1428 * @param ritem Place to store pointer to new value item
1429 */
1430void run_cvt_value_item(run_t *run, rdata_item_t *item, rdata_item_t **ritem)
1431{
1432 rdata_value_t *value;
1433
1434 /*
1435 * This can happen when trying to use output of a function which
1436 * does not return a value.
1437 */
1438 if (item == NULL) {
1439 printf("Error: Sub-expression has no value.\n");
1440 exit(1);
1441 }
1442
1443 /* Address item. Perform read operation. */
1444 if (item->ic == ic_address) {
1445 run_address_read(run, item->u.address, ritem);
1446 return;
1447 }
1448
1449 /* Make a copy of the var node within. */
1450 value = rdata_value_new();
1451 rdata_var_copy(item->u.value->var, &value->var);
1452 *ritem = rdata_item_new(ic_value);
1453 (*ritem)->u.value = value;
1454}
1455
1456/** Get item var-class.
1457 *
1458 * Get var-class of @a item, regardless whether it is a value or address.
1459 * (I.e. the var class of the value or variable at the given address).
1460 *
1461 * @param run Runner object
1462 * @param item Value or address item
1463 * @return Varclass of @a item
1464 */
1465var_class_t run_item_get_vc(run_t *run, rdata_item_t *item)
1466{
1467 var_class_t vc;
1468 rdata_var_t *tpos;
1469
1470 (void) run;
1471
1472 switch (item->ic) {
1473 case ic_value:
1474 vc = item->u.value->var->vc;
1475 break;
1476 case ic_address:
1477 switch (item->u.address->ac) {
1478 case ac_var:
1479 vc = item->u.address->u.var_a->vref->vc;
1480 break;
1481 case ac_prop:
1482 /* Prefetch the value of the property. */
1483 tpos = run_aprop_get_tpos(run, item->u.address);
1484 vc = tpos->vc;
1485 break;
1486 default:
1487 assert(b_false);
1488 }
1489 break;
1490 default:
1491 assert(b_false);
1492 }
1493
1494 return vc;
1495}
1496
1497/** Get pointer to current var node in temporary copy in property address.
1498 *
1499 * A property address refers to a specific @c var node in a property.
1500 * This function will fetch a copy of the property value (by running
1501 * its getter) if there is not a temporary copy in the address yet.
1502 * It returns a pointer to the relevant @c var node in the temporary
1503 * copy.
1504 *
1505 * @param run Runner object
1506 * @param addr Address of class @c ac_prop
1507 * @return Pointer to var node
1508 */
1509static rdata_var_t *run_aprop_get_tpos(run_t *run, rdata_address_t *addr)
1510{
1511 rdata_item_t *ritem;
1512
1513 assert(addr->ac == ac_prop);
1514
1515 if (addr->u.prop_a->tvalue == NULL) {
1516 /* Fetch value of the property. */
1517 run_address_read(run, addr, &ritem);
1518 assert(ritem->ic == ic_value);
1519 addr->u.prop_a->tvalue = ritem->u.value;
1520 addr->u.prop_a->tpos = addr->u.prop_a->tvalue->var;
1521 }
1522
1523 return addr->u.prop_a->tpos;
1524}
1525
1526/** Read data from an address.
1527 *
1528 * Read value from the specified address.
1529 *
1530 * @param run Runner object
1531 * @param address Address to read
1532 * @param ritem Place to store pointer to the value that was read
1533 */
1534void run_address_read(run_t *run, rdata_address_t *address,
1535 rdata_item_t **ritem)
1536{
1537 (void) run;
1538 assert(ritem != NULL);
1539
1540 switch (address->ac) {
1541 case ac_var:
1542 rdata_var_read(address->u.var_a->vref, ritem);
1543 break;
1544 case ac_prop:
1545 run_aprop_read(run, address->u.prop_a, ritem);
1546 break;
1547 }
1548
1549 assert(*ritem == NULL || (*ritem)->ic == ic_value);
1550}
1551
1552/** Write data to an address.
1553 *
1554 * Store value @a value at address @a address.
1555 *
1556 * @param run Runner object
1557 * @param address Address to write
1558 * @param value Value to store at the address
1559 */
1560void run_address_write(run_t *run, rdata_address_t *address,
1561 rdata_value_t *value)
1562{
1563 (void) run;
1564
1565 switch (address->ac) {
1566 case ac_var:
1567 rdata_var_write(address->u.var_a->vref, value);
1568 break;
1569 case ac_prop:
1570 run_aprop_write(run, address->u.prop_a, value);
1571 break;
1572 }
1573}
1574
1575/** Read data from a property address.
1576 *
1577 * This involves invoking the property getter procedure.
1578 *
1579 * @param run Runner object.
1580 * @param addr_prop Property address to read.
1581 * @param ritem Place to store pointer to the value that was read.
1582 */
1583static void run_aprop_read(run_t *run, rdata_addr_prop_t *addr_prop,
1584 rdata_item_t **ritem)
1585{
1586 rdata_deleg_t *deleg;
1587 rdata_var_t *obj;
1588 stree_symbol_t *prop_sym;
1589 stree_prop_t *prop;
1590
1591 run_proc_ar_t *proc_ar;
1592
1593 rdata_var_t *cvar;
1594
1595#ifdef DEBUG_RUN_TRACE
1596 printf("Read from property.\n");
1597#endif
1598 /*
1599 * If @c tvalue is present, we need to use the relevant part from that
1600 * instead of re-reading the whole thing.
1601 */
1602 if (addr_prop->tvalue != NULL) {
1603 /* Copy the value */
1604 rdata_var_copy(addr_prop->tpos, &cvar);
1605 *ritem = rdata_item_new(ic_value);
1606 (*ritem)->u.value = rdata_value_new();
1607 (*ritem)->u.value->var = cvar;
1608 return;
1609 }
1610
1611 if (addr_prop->apc == apc_named)
1612 deleg = addr_prop->u.named->prop_d;
1613 else
1614 deleg = addr_prop->u.indexed->object_d;
1615
1616 obj = deleg->obj;
1617 prop_sym = deleg->sym;
1618 prop = symbol_to_prop(prop_sym);
1619 assert(prop != NULL);
1620
1621 if (prop->getter == NULL) {
1622 printf("Error: Property is not readable.\n");
1623 exit(1);
1624 }
1625
1626 /* Create procedure activation record. */
1627 run_proc_ar_create(run, obj, prop->getter, &proc_ar);
1628
1629 /* Fill in arguments (indices). */
1630 if (addr_prop->apc == apc_indexed) {
1631 run_proc_ar_set_args(run, proc_ar,
1632 &addr_prop->u.indexed->args);
1633 }
1634
1635 /* Run getter. */
1636 run_proc(run, proc_ar, ritem);
1637
1638 /* Destroy procedure activation record. */
1639 run_proc_ar_destroy(run, proc_ar);
1640
1641#ifdef DEBUG_RUN_TRACE
1642 printf("Getter returns ");
1643 rdata_item_print(*ritem);
1644 printf(".\n");
1645 printf("Done reading from property.\n");
1646#endif
1647}
1648
1649/** Write data to a property address.
1650 *
1651 * This involves invoking the property setter procedure.
1652 *
1653 * @param run Runner object
1654 * @param addr_prop Property address to write
1655 * @param value Value to store at the address
1656 */
1657static void run_aprop_write(run_t *run, rdata_addr_prop_t *addr_prop,
1658 rdata_value_t *value)
1659{
1660 rdata_deleg_t *deleg;
1661 rdata_var_t *obj;
1662 stree_symbol_t *prop_sym;
1663 stree_prop_t *prop;
1664
1665 run_proc_ar_t *proc_ar;
1666 rdata_item_t *vitem;
1667 rdata_item_t *ritem;
1668
1669#ifdef DEBUG_RUN_TRACE
1670 printf("Write to property.\n");
1671#endif
1672 /* If @c tvalue is present, we need to modify it and write it back. */
1673 if (addr_prop->tvalue != NULL) {
1674 printf("Unimplemented: Read-modify-write property access.\n");
1675 exit(1);
1676 }
1677
1678 if (addr_prop->apc == apc_named)
1679 deleg = addr_prop->u.named->prop_d;
1680 else
1681 deleg = addr_prop->u.indexed->object_d;
1682
1683 obj = deleg->obj;
1684 prop_sym = deleg->sym;
1685 prop = symbol_to_prop(prop_sym);
1686 assert(prop != NULL);
1687
1688 if (prop->setter == NULL) {
1689 printf("Error: Property is not writable.\n");
1690 exit(1);
1691 }
1692
1693 vitem = rdata_item_new(ic_value);
1694 vitem->u.value = value;
1695
1696 /* Create procedure activation record. */
1697 run_proc_ar_create(run, obj, prop->setter, &proc_ar);
1698
1699 /* Fill in arguments (indices). */
1700 if (addr_prop->apc == apc_indexed) {
1701 run_proc_ar_set_args(run, proc_ar,
1702 &addr_prop->u.indexed->args);
1703 }
1704
1705 /* Fill in value argument for setter. */
1706 run_proc_ar_set_setter_arg(run, proc_ar, vitem);
1707
1708 /* Run setter. */
1709 run_proc(run, proc_ar, &ritem);
1710
1711 /* Setter should not return a value. */
1712 assert(ritem == NULL);
1713
1714 /* Destroy procedure activation record. */
1715 run_proc_ar_destroy(run, proc_ar);
1716
1717#ifdef DEBUG_RUN_TRACE
1718 printf("Done writing to property.\n");
1719#endif
1720}
1721
1722/** Return reference to a variable.
1723 *
1724 * Constructs a reference (value item) pointing to @a var.
1725 *
1726 * @param run Runner object
1727 * @param var Variable node that is being referenced
1728 * @param res Place to store pointer to new reference.
1729 */
1730void run_reference(run_t *run, rdata_var_t *var, rdata_item_t **res)
1731{
1732 rdata_ref_t *ref;
1733 rdata_var_t *ref_var;
1734 rdata_value_t *ref_value;
1735 rdata_item_t *ref_item;
1736
1737 (void) run;
1738
1739 /* Create reference to the variable. */
1740 ref = rdata_ref_new();
1741 ref_var = rdata_var_new(vc_ref);
1742 ref->vref = var;
1743 ref_var->u.ref_v = ref;
1744
1745 /* Construct value of the reference to return. */
1746 ref_item = rdata_item_new(ic_value);
1747 ref_value = rdata_value_new();
1748 ref_item->u.value = ref_value;
1749 ref_value->var = ref_var;
1750
1751 *res = ref_item;
1752}
1753
1754/** Return address of reference target.
1755 *
1756 * Takes a reference (address or value) and returns the address (item) of
1757 * the target of the reference.
1758 *
1759 * @param run Runner object
1760 * @param ref Reference
1761 * @param cspan Cspan to put into exception if reference is nil
1762 * or @c NULL if no cspan is provided.
1763 * @param rtitem Place to store pointer to the resulting address.
1764 */
1765void run_dereference(run_t *run, rdata_item_t *ref, cspan_t *cspan,
1766 rdata_item_t **ritem)
1767{
1768 rdata_item_t *ref_val;
1769 rdata_item_t *item;
1770 rdata_address_t *address;
1771 rdata_addr_var_t *addr_var;
1772
1773#ifdef DEBUG_RUN_TRACE
1774 printf("run_dereference()\n");
1775#endif
1776 run_cvt_value_item(run, ref, &ref_val);
1777 if (run_is_bo(run)) {
1778 *ritem = run_recovery_item(run);
1779 return;
1780 }
1781
1782 assert(ref_val->u.value->var->vc == vc_ref);
1783
1784 item = rdata_item_new(ic_address);
1785 address = rdata_address_new(ac_var);
1786 addr_var = rdata_addr_var_new();
1787 item->u.address = address;
1788 address->u.var_a = addr_var;
1789 addr_var->vref = ref_val->u.value->var->u.ref_v->vref;
1790
1791 rdata_item_destroy(ref_val);
1792
1793 if (addr_var->vref == NULL) {
1794#ifdef DEBUG_RUN_TRACE
1795 printf("Error: Accessing null reference.\n");
1796#endif
1797 /* Raise Error.NilReference */
1798 run_raise_exc(run, run->program->builtin->error_nilreference,
1799 cspan);
1800 *ritem = run_recovery_item(run);
1801 return;
1802 }
1803
1804#ifdef DEBUG_RUN_TRACE
1805 printf("vref set to %p\n", addr_var->vref);
1806#endif
1807 *ritem = item;
1808}
1809
1810/** Raise an exception of the given class.
1811 *
1812 * Used when the interpreter generates an exception due to a run-time
1813 * error (not for the @c raise statement).
1814 *
1815 * @param run Runner object
1816 * @param csi Exception class
1817 * @param cspan Cspan of code that caused exception (for debugging)
1818 */
1819void run_raise_exc(run_t *run, stree_csi_t *csi, cspan_t *cspan)
1820{
1821 rdata_item_t *exc_vi;
1822
1823 /* Store exception cspan in thread AR. */
1824 run->thread_ar->exc_cspan = cspan;
1825
1826 /* Create exception object. */
1827 run_new_csi_inst_ref(run, csi, sn_nonstatic, &exc_vi);
1828 assert(exc_vi->ic == ic_value);
1829
1830 /* Store exception object in thread AR. */
1831 run->thread_ar->exc_payload = exc_vi->u.value;
1832
1833 /* Start exception bailout. */
1834 run->thread_ar->bo_mode = bm_exc;
1835}
1836
1837/** Determine if we are bailing out.
1838 *
1839 * @param run Runner object
1840 * @return @c b_true if we are bailing out, @c b_false otherwise
1841 */
1842bool_t run_is_bo(run_t *run)
1843{
1844 return run->thread_ar->bo_mode != bm_none;
1845}
1846
1847/** Construct a new variable of the given type.
1848 *
1849 * The variable is allocated and initialized with a default value
1850 * based on type item @a ti. For reference types the default value
1851 * is a null reference. At this point this does not work for generic
1852 * types (we need RTTI).
1853 *
1854 * @param run Runner object
1855 * @param ti Type of variable to create (type item)
1856 * @param rvar Place to store pointer to new variable
1857 */
1858void run_var_new(run_t *run, tdata_item_t *ti, rdata_var_t **rvar)
1859{
1860 rdata_var_t *var;
1861
1862 switch (ti->tic) {
1863 case tic_tprimitive:
1864 run_var_new_tprimitive(run, ti->u.tprimitive, rvar);
1865 break;
1866 case tic_tobject:
1867 case tic_tarray:
1868 run_var_new_null_ref(run, rvar);
1869 break;
1870 case tic_tdeleg:
1871 run_var_new_deleg(run, rvar);
1872 break;
1873 case tic_tebase:
1874 /*
1875 * One cannot declare variable of ebase type. It is just
1876 * type of expressions referring to enum types.
1877 */
1878 assert(b_false);
1879 /* Fallthrough */
1880 case tic_tenum:
1881 run_var_new_enum(run, ti->u.tenum, rvar);
1882 break;
1883 case tic_tfun:
1884 run_var_new_deleg(run, rvar);
1885 break;
1886 case tic_tvref:
1887 /*
1888 * XXX Need to obtain run-time value of type argument to
1889 * initialize variable properly.
1890 */
1891 var = rdata_var_new(vc_int);
1892 var->u.int_v = rdata_int_new();
1893 bigint_init(&var->u.int_v->value, 0);
1894 *rvar = var;
1895 break;
1896 case tic_ignore:
1897 assert(b_false);
1898 }
1899}
1900
1901/** Construct a new variable of primitive type.
1902 *
1903 * The variable is allocated and initialized with a default value
1904 * based on primitive type item @a tprimitive.
1905 *
1906 * @param run Runner object
1907 * @param ti Primitive type of variable to create
1908 * @param rvar Place to store pointer to new variable
1909 */
1910static void run_var_new_tprimitive(run_t *run, tdata_primitive_t *tprimitive,
1911 rdata_var_t **rvar)
1912{
1913 rdata_var_t *var;
1914
1915 (void) run;
1916
1917 /* Make compiler happy. */
1918 var = NULL;
1919
1920 switch (tprimitive->tpc) {
1921 case tpc_bool:
1922 var = rdata_var_new(vc_bool);
1923 var->u.bool_v = rdata_bool_new();
1924 var->u.bool_v->value = b_false;
1925 break;
1926 case tpc_char:
1927 var = rdata_var_new(vc_char);
1928 var->u.char_v = rdata_char_new();
1929 bigint_init(&var->u.char_v->value, 0);
1930 break;
1931 case tpc_int:
1932 var = rdata_var_new(vc_int);
1933 var->u.int_v = rdata_int_new();
1934 bigint_init(&var->u.int_v->value, 0);
1935 break;
1936 case tpc_nil:
1937 assert(b_false);
1938 /* Fallthrough */
1939 case tpc_string:
1940 var = rdata_var_new(vc_string);
1941 var->u.string_v = rdata_string_new();
1942 var->u.string_v->value = "";
1943 break;
1944 case tpc_resource:
1945 var = rdata_var_new(vc_resource);
1946 var->u.resource_v = rdata_resource_new();
1947 var->u.resource_v->data = NULL;
1948 break;
1949 }
1950
1951 *rvar = var;
1952}
1953
1954/** Construct a new variable containing null reference.
1955 *
1956 * @param run Runner object
1957 * @param rvar Place to store pointer to new variable
1958 */
1959static void run_var_new_null_ref(run_t *run, rdata_var_t **rvar)
1960{
1961 rdata_var_t *var;
1962
1963 (void) run;
1964
1965 /* Return null reference. */
1966 var = rdata_var_new(vc_ref);
1967 var->u.ref_v = rdata_ref_new();
1968
1969 *rvar = var;
1970}
1971
1972/** Construct a new variable containing invalid delegate.
1973 *
1974 * @param run Runner object
1975 * @param rvar Place to store pointer to new variable
1976 */
1977static void run_var_new_deleg(run_t *run, rdata_var_t **rvar)
1978{
1979 rdata_var_t *var;
1980
1981 (void) run;
1982
1983 /* Return null reference. */
1984 var = rdata_var_new(vc_deleg);
1985 var->u.deleg_v = rdata_deleg_new();
1986
1987 *rvar = var;
1988}
1989
1990/** Construct a new variable containing default value of an enum type.
1991 *
1992 * @param run Runner object
1993 * @param rvar Place to store pointer to new variable
1994 */
1995static void run_var_new_enum(run_t *run, tdata_enum_t *tenum,
1996 rdata_var_t **rvar)
1997{
1998 rdata_var_t *var;
1999 list_node_t *embr_n;
2000 stree_embr_t *embr;
2001
2002 (void) run;
2003
2004 /* Get first member of enum which will serve as default value. */
2005 embr_n = list_first(&tenum->enum_d->members);
2006 assert(embr_n != NULL);
2007
2008 embr = list_node_data(embr_n, stree_embr_t *);
2009
2010 /* Return null reference. */
2011 var = rdata_var_new(vc_enum);
2012 var->u.enum_v = rdata_enum_new();
2013 var->u.enum_v->value = embr;
2014
2015 *rvar = var;
2016}
2017
2018/** Construct a new thread activation record.
2019 *
2020 * @param run Runner object
2021 * @return New thread AR.
2022 */
2023run_thread_ar_t *run_thread_ar_new(void)
2024{
2025 run_thread_ar_t *thread_ar;
2026
2027 thread_ar = calloc(1, sizeof(run_thread_ar_t));
2028 if (thread_ar == NULL) {
2029 printf("Memory allocation failed.\n");
2030 exit(1);
2031 }
2032
2033 return thread_ar;
2034}
2035
2036/** Allocate a new procedure activation record.
2037 *
2038 * @return New procedure AR.
2039 */
2040run_proc_ar_t *run_proc_ar_new(void)
2041{
2042 run_proc_ar_t *proc_ar;
2043
2044 proc_ar = calloc(1, sizeof(run_proc_ar_t));
2045 if (proc_ar == NULL) {
2046 printf("Memory allocation failed.\n");
2047 exit(1);
2048 }
2049
2050 return proc_ar;
2051}
2052
2053/** Deallocate a procedure activation record.
2054 *
2055 * @return New procedure AR.
2056 */
2057void run_proc_ar_delete(run_proc_ar_t *proc_ar)
2058{
2059 assert(proc_ar != NULL);
2060 free(proc_ar);
2061}
2062
2063/** Allocate a new block activation record.
2064 *
2065 * @param run Runner object
2066 * @return New block AR.
2067 */
2068run_block_ar_t *run_block_ar_new(void)
2069{
2070 run_block_ar_t *block_ar;
2071
2072 block_ar = calloc(1, sizeof(run_block_ar_t));
2073 if (block_ar == NULL) {
2074 printf("Memory allocation failed.\n");
2075 exit(1);
2076 }
2077
2078 return block_ar;
2079}
2080
2081/** Deallocate a new block activation record.
2082 *
2083 * @param run Runner object
2084 * @return New block AR.
2085 */
2086void run_block_ar_delete(run_block_ar_t *block_ar)
2087{
2088 assert(block_ar != NULL);
2089 free(block_ar);
2090}
Note: See TracBrowser for help on using the repository browser.