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

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

Fix cstyle: make ccheck-fix and commit only files where all the changes are good.

  • 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
812/** Check for unhandled exception.
813 *
814 * Checks whether there is an active exception. If so, it prints an
815 * error message and raises a run-time error.
816 *
817 * @param run Runner object
818 */
819void run_exc_check_unhandled(run_t *run)
820{
821 stree_csi_t *exc_csi;
822
823 if (run->thread_ar->bo_mode != bm_none) {
824 assert(run->thread_ar->bo_mode == bm_exc);
825
826 exc_csi = run_exc_payload_get_csi(run);
827
828 if (run->thread_ar->exc_cspan != NULL) {
829 cspan_print(run->thread_ar->exc_cspan);
830 putchar(' ');
831 }
832
833 printf("Error: Unhandled exception '");
834 symbol_print_fqn(csi_to_symbol(exc_csi));
835 printf("'.\n");
836
837 run_raise_error(run);
838 }
839}
840
841/** Raise an irrecoverable run-time error, start bailing out.
842 *
843 * Raises an error that cannot be handled by the user program.
844 *
845 * @param run Runner object
846 */
847void run_raise_error(run_t *run)
848{
849 run->thread_ar->bo_mode = bm_error;
850 run->thread_ar->error = b_true;
851}
852
853/** Construct a special recovery item.
854 *
855 * @param run Runner object
856 */
857rdata_item_t *run_recovery_item(run_t *run)
858{
859 (void) run;
860 return NULL;
861}
862
863/** Find a local variable in the currently active function.
864 *
865 * @param run Runner object
866 * @param name Name SID of the local variable
867 * @return Pointer to var node or @c NULL if not found
868 */
869rdata_var_t *run_local_vars_lookup(run_t *run, sid_t name)
870{
871 run_proc_ar_t *proc_ar;
872 run_block_ar_t *block_ar;
873 rdata_var_t *var;
874 list_node_t *node;
875
876 proc_ar = run_get_current_proc_ar(run);
877 node = list_last(&proc_ar->block_ar);
878
879 /* Walk through all block activation records. */
880 while (node != NULL) {
881 block_ar = list_node_data(node, run_block_ar_t *);
882 var = intmap_get(&block_ar->vars, name);
883 if (var != NULL)
884 return var;
885
886 node = list_prev(&proc_ar->block_ar, node);
887 }
888
889 /* No match */
890 return NULL;
891}
892
893/** Get current procedure activation record.
894 *
895 * @param run Runner object
896 * @return Active procedure AR
897 */
898run_proc_ar_t *run_get_current_proc_ar(run_t *run)
899{
900 list_node_t *node;
901
902 node = list_last(&run->thread_ar->proc_ar);
903 return list_node_data(node, run_proc_ar_t *);
904}
905
906/** Get current block activation record.
907 *
908 * @param run Runner object
909 * @return Active block AR
910 */
911run_block_ar_t *run_get_current_block_ar(run_t *run)
912{
913 run_proc_ar_t *proc_ar;
914 list_node_t *node;
915
916 proc_ar = run_get_current_proc_ar(run);
917
918 node = list_last(&proc_ar->block_ar);
919 return list_node_data(node, run_block_ar_t *);
920}
921
922/** Get current CSI.
923 *
924 * @param run Runner object
925 * @return Active CSI
926 */
927stree_csi_t *run_get_current_csi(run_t *run)
928{
929 run_proc_ar_t *proc_ar;
930
931 proc_ar = run_get_current_proc_ar(run);
932 return proc_ar->proc->outer_symbol->outer_csi;
933}
934
935/** Get static object (i.e. class object).
936 *
937 * Looks for a child static object named @a name in static object @a pobject.
938 * If the child does not exist yet, it is created.
939 *
940 * @param run Runner object
941 * @param csi CSI of the static object named @a name
942 * @param pobject Parent static object
943 * @param name Static object name
944 *
945 * @return Static (class) object of the given name
946 */
947rdata_var_t *run_sobject_get(run_t *run, stree_csi_t *csi,
948 rdata_var_t *pobj_var, sid_t name)
949{
950 rdata_object_t *pobject;
951 rdata_var_t *mbr_var;
952 rdata_var_t *rvar;
953 stree_ident_t *ident;
954
955 assert(pobj_var->vc == vc_object);
956 pobject = pobj_var->u.object_v;
957#ifdef DEBUG_RUN_TRACE
958 printf("Get static object '%s' in '", strtab_get_str(name));
959 if (pobject->class_sym != NULL)
960 symbol_print_fqn(pobject->class_sym);
961 else
962 printf("global");
963#endif
964
965 assert(pobject->static_obj == sn_static);
966
967 mbr_var = intmap_get(&pobject->fields, name);
968 if (mbr_var != NULL) {
969#ifdef DEBUG_RUN_TRACE
970 printf("Return exising static object (mbr_var=%p).\n", mbr_var);
971#endif
972 /* Return existing object. */
973 return mbr_var;
974 }
975
976 /* Construct new object. */
977#ifdef DEBUG_RUN_TRACE
978 printf("Construct new static object.\n");
979#endif
980 ident = stree_ident_new();
981 ident->sid = name;
982
983 run_new_csi_inst(run, csi, sn_static, &rvar);
984
985 /* Store static object reference for future use. */
986 intmap_set(&pobject->fields, name, rvar);
987
988 return rvar;
989}
990
991/** Get static object for CSI.
992 *
993 * In situations where we do not have the parent static object and need
994 * to find static object for a CSI from the gdata root we use this.
995 *
996 * This is only used in special cases such as when invoking the entry
997 * point. This is too slow to use during normal execution.
998 *
999 * @param run Runner object
1000 * @param csi CSI to get static object of
1001 *
1002 * @return Static (class) object
1003 */
1004rdata_var_t *run_sobject_find(run_t *run, stree_csi_t *csi)
1005{
1006 rdata_var_t *pobj_var;
1007
1008 if (csi == NULL)
1009 return run->gdata;
1010
1011 assert(csi->ancr_state == ws_visited);
1012 pobj_var = run_sobject_find(run, csi_to_symbol(csi)->outer_csi);
1013
1014 return run_sobject_get(run, csi, pobj_var, csi->name->sid);
1015}
1016
1017/** Get static object for CSI containing function.
1018 *
1019 * This is used to obtain active object for invoking static function
1020 * @a fun. Only used in cases where we don't already have the object such
1021 * as when running the entry point. Otherwise this would be slow.
1022 *
1023 * @param run Runner object
1024 * @param fun Function to get static class object of
1025 *
1026 * @return Static (class) object
1027 */
1028rdata_var_t *run_fun_sobject_find(run_t *run, stree_fun_t *fun)
1029{
1030 return run_sobject_find(run, fun_to_symbol(fun)->outer_csi);
1031}
1032
1033/** Construct variable from a value item.
1034 *
1035 * XXX This should be in fact implemented using existing code as:
1036 *
1037 * (1) Create a variable of the desired type.
1038 * (2) Initialize the variable with the provided value.
1039 *
1040 * @param item Value item (initial value for variable).
1041 * @param var Place to store new var node.
1042 */
1043void run_value_item_to_var(rdata_item_t *item, rdata_var_t **var)
1044{
1045 rdata_bool_t *bool_v;
1046 rdata_char_t *char_v;
1047 rdata_deleg_t *deleg_v;
1048 rdata_enum_t *enum_v;
1049 rdata_int_t *int_v;
1050 rdata_string_t *string_v;
1051 rdata_ref_t *ref_v;
1052 rdata_var_t *in_var;
1053
1054 assert(item->ic == ic_value);
1055 in_var = item->u.value->var;
1056
1057 switch (in_var->vc) {
1058 case vc_bool:
1059 *var = rdata_var_new(vc_bool);
1060 bool_v = rdata_bool_new();
1061
1062 (*var)->u.bool_v = bool_v;
1063 bool_v->value = item->u.value->var->u.bool_v->value;
1064 break;
1065 case vc_char:
1066 *var = rdata_var_new(vc_char);
1067 char_v = rdata_char_new();
1068
1069 (*var)->u.char_v = char_v;
1070 bigint_clone(&item->u.value->var->u.char_v->value,
1071 &char_v->value);
1072 break;
1073 case vc_deleg:
1074 *var = rdata_var_new(vc_deleg);
1075 deleg_v = rdata_deleg_new();
1076
1077 (*var)->u.deleg_v = deleg_v;
1078 deleg_v->obj = item->u.value->var->u.deleg_v->obj;
1079 deleg_v->sym = item->u.value->var->u.deleg_v->sym;
1080 break;
1081 case vc_enum:
1082 *var = rdata_var_new(vc_enum);
1083 enum_v = rdata_enum_new();
1084
1085 (*var)->u.enum_v = enum_v;
1086 enum_v->value = item->u.value->var->u.enum_v->value;
1087 break;
1088 case vc_int:
1089 *var = rdata_var_new(vc_int);
1090 int_v = rdata_int_new();
1091
1092 (*var)->u.int_v = int_v;
1093 bigint_clone(&item->u.value->var->u.int_v->value,
1094 &int_v->value);
1095 break;
1096 case vc_string:
1097 *var = rdata_var_new(vc_string);
1098 string_v = rdata_string_new();
1099
1100 (*var)->u.string_v = string_v;
1101 string_v->value = item->u.value->var->u.string_v->value;
1102 break;
1103 case vc_ref:
1104 *var = rdata_var_new(vc_ref);
1105 ref_v = rdata_ref_new();
1106
1107 (*var)->u.ref_v = ref_v;
1108 ref_v->vref = item->u.value->var->u.ref_v->vref;
1109 break;
1110 default:
1111 printf("Error: Unimplemented argument type.\n");
1112 exit(1);
1113
1114 }
1115}
1116
1117/** Construct a procedure AR.
1118 *
1119 * @param run Runner object
1120 * @param obj Object whose procedure is being activated
1121 * @param proc Procedure that is being activated
1122 * @param rproc_ar Place to store pointer to new activation record
1123 */
1124void run_proc_ar_create(run_t *run, rdata_var_t *obj, stree_proc_t *proc,
1125 run_proc_ar_t **rproc_ar)
1126{
1127 run_proc_ar_t *proc_ar;
1128 run_block_ar_t *block_ar;
1129
1130 (void) run;
1131
1132 /* Create procedure activation record. */
1133 proc_ar = run_proc_ar_new();
1134 proc_ar->obj = obj;
1135 proc_ar->proc = proc;
1136 list_init(&proc_ar->block_ar);
1137
1138 proc_ar->retval = NULL;
1139
1140 /* Create special block activation record to hold function arguments. */
1141 block_ar = run_block_ar_new();
1142 intmap_init(&block_ar->vars);
1143 list_append(&proc_ar->block_ar, block_ar);
1144
1145 *rproc_ar = proc_ar;
1146}
1147
1148/** Destroy a procedure AR.
1149 *
1150 * @param run Runner object
1151 * @param proc_ar Pointer to procedure activation record
1152 */
1153void run_proc_ar_destroy(run_t *run, run_proc_ar_t *proc_ar)
1154{
1155 list_node_t *ar_node;
1156 run_block_ar_t *block_ar;
1157
1158 (void) run;
1159
1160 /* Destroy special block activation record. */
1161 ar_node = list_first(&proc_ar->block_ar);
1162 block_ar = list_node_data(ar_node, run_block_ar_t *);
1163 list_remove(&proc_ar->block_ar, ar_node);
1164 run_block_ar_destroy(run, block_ar);
1165
1166 /* Destroy procedure activation record. */
1167 proc_ar->obj = NULL;
1168 proc_ar->proc = NULL;
1169 list_fini(&proc_ar->block_ar);
1170 proc_ar->retval = NULL;
1171 run_proc_ar_delete(proc_ar);
1172}
1173
1174
1175/** Fill arguments in a procedure AR.
1176 *
1177 * When invoking a procedure this is used to store the argument values
1178 * in the activation record.
1179 *
1180 * @param run Runner object
1181 * @param proc_ar Existing procedure activation record where to store
1182 * the values
1183 * @param arg_vals List of value items (rdata_item_t *) -- real
1184 * argument values
1185 */
1186void run_proc_ar_set_args(run_t *run, run_proc_ar_t *proc_ar, list_t *arg_vals)
1187{
1188 stree_ctor_t *ctor;
1189 stree_fun_t *fun;
1190 stree_prop_t *prop;
1191 list_t *args;
1192 stree_proc_arg_t *varg;
1193 stree_symbol_t *outer_symbol;
1194
1195 run_block_ar_t *block_ar;
1196 list_node_t *block_ar_n;
1197 list_node_t *rarg_n, *parg_n;
1198 list_node_t *cn;
1199 rdata_item_t *rarg;
1200 stree_proc_arg_t *parg;
1201 rdata_var_t *var;
1202 rdata_var_t *ref_var;
1203 rdata_ref_t *ref;
1204 rdata_array_t *array;
1205 rdata_var_t *elem_var;
1206 int n_vargs, idx;
1207
1208 (void) run;
1209
1210 /* AR should have been created with run_proc_ar_create(). */
1211 assert(proc_ar->proc != NULL);
1212 outer_symbol = proc_ar->proc->outer_symbol;
1213
1214 /* Make compiler happy. */
1215 args = NULL;
1216 varg = NULL;
1217
1218 /*
1219 * The procedure being activated should belong to a member function or
1220 * property getter/setter.
1221 */
1222 switch (outer_symbol->sc) {
1223 case sc_ctor:
1224 ctor = symbol_to_ctor(outer_symbol);
1225 args = &ctor->sig->args;
1226 varg = ctor->sig->varg;
1227 break;
1228 case sc_fun:
1229 fun = symbol_to_fun(outer_symbol);
1230 args = &fun->sig->args;
1231 varg = fun->sig->varg;
1232 break;
1233 case sc_prop:
1234 prop = symbol_to_prop(outer_symbol);
1235 args = &prop->args;
1236 varg = prop->varg;
1237 break;
1238 case sc_csi:
1239 case sc_deleg:
1240 case sc_enum:
1241 case sc_var:
1242 assert(b_false);
1243 }
1244
1245 /* Fetch first block activation record. */
1246 block_ar_n = list_first(&proc_ar->block_ar);
1247 assert(block_ar_n != NULL);
1248 block_ar = list_node_data(block_ar_n, run_block_ar_t *);
1249
1250 /* Declare local variables to hold argument values. */
1251 rarg_n = list_first(arg_vals);
1252 parg_n = list_first(args);
1253
1254 while (parg_n != NULL) {
1255 if (rarg_n == NULL) {
1256 printf("Error: Too few arguments to '");
1257 symbol_print_fqn(outer_symbol);
1258 printf("'.\n");
1259 exit(1);
1260 }
1261
1262 rarg = list_node_data(rarg_n, rdata_item_t *);
1263 parg = list_node_data(parg_n, stree_proc_arg_t *);
1264
1265 assert(rarg->ic == ic_value);
1266
1267 /* Construct a variable from the argument value. */
1268 run_value_item_to_var(rarg, &var);
1269
1270 /* Declare variable using name of formal argument. */
1271 intmap_set(&block_ar->vars, parg->name->sid, var);
1272
1273 rarg_n = list_next(arg_vals, rarg_n);
1274 parg_n = list_next(args, parg_n);
1275 }
1276
1277 if (varg != NULL) {
1278 /* Function is variadic. Count number of variadic arguments. */
1279 cn = rarg_n;
1280 n_vargs = 0;
1281 while (cn != NULL) {
1282 n_vargs += 1;
1283 cn = list_next(arg_vals, cn);
1284 }
1285
1286 /* Prepare array to store variadic arguments. */
1287 array = rdata_array_new(1);
1288 array->extent[0] = n_vargs;
1289 rdata_array_alloc_element(array);
1290
1291 /* Read variadic arguments. */
1292
1293 idx = 0;
1294 while (rarg_n != NULL) {
1295 rarg = list_node_data(rarg_n, rdata_item_t *);
1296 assert(rarg->ic == ic_value);
1297
1298 run_value_item_to_var(rarg, &elem_var);
1299 array->element[idx] = elem_var;
1300
1301 rarg_n = list_next(arg_vals, rarg_n);
1302 idx += 1;
1303 }
1304
1305 var = rdata_var_new(vc_array);
1306 var->u.array_v = array;
1307
1308 /* Create reference to the new array. */
1309 ref_var = rdata_var_new(vc_ref);
1310 ref = rdata_ref_new();
1311 ref_var->u.ref_v = ref;
1312 ref->vref = var;
1313
1314 /* Declare variable using name of formal argument. */
1315 intmap_set(&block_ar->vars, varg->name->sid,
1316 ref_var);
1317 }
1318
1319 /* Check for excess real parameters. */
1320 if (rarg_n != NULL) {
1321 printf("Error: Too many arguments to '");
1322 symbol_print_fqn(outer_symbol);
1323 printf("'.\n");
1324 exit(1);
1325 }
1326}
1327
1328/** Fill setter argument in a procedure AR.
1329 *
1330 * When invoking a setter this is used to store its argument value in its
1331 * procedure activation record.
1332 *
1333 * @param run Runner object
1334 * @param proc_ar Existing procedure activation record where to store
1335 * the setter argument
1336 * @param arg_val Value items (rdata_item_t *) -- real argument value
1337 */
1338void run_proc_ar_set_setter_arg(run_t *run, run_proc_ar_t *proc_ar,
1339 rdata_item_t *arg_val)
1340{
1341 stree_prop_t *prop;
1342 run_block_ar_t *block_ar;
1343 list_node_t *block_ar_n;
1344 rdata_var_t *var;
1345
1346 (void) run;
1347
1348 /* AR should have been created with run_proc_ar_create(). */
1349 assert(proc_ar->proc != NULL);
1350
1351 /* The procedure being activated should belong to a property setter. */
1352 prop = symbol_to_prop(proc_ar->proc->outer_symbol);
1353 assert(prop != NULL);
1354 assert(proc_ar->proc == prop->setter);
1355
1356 /* Fetch first block activation record. */
1357 block_ar_n = list_first(&proc_ar->block_ar);
1358 assert(block_ar_n != NULL);
1359 block_ar = list_node_data(block_ar_n, run_block_ar_t *);
1360
1361 assert(arg_val->ic == ic_value);
1362
1363 /* Construct a variable from the argument value. */
1364 run_value_item_to_var(arg_val, &var);
1365
1366 /* Declare variable using name of formal argument. */
1367 intmap_set(&block_ar->vars, prop->setter_arg->name->sid, var);
1368}
1369
1370/** Print function activation backtrace.
1371 *
1372 * Prints a backtrace of activated functions for debugging purposes.
1373 *
1374 * @param run Runner object
1375 */
1376void run_print_fun_bt(run_t *run)
1377{
1378 list_node_t *node;
1379 run_proc_ar_t *proc_ar;
1380
1381 printf("Backtrace:\n");
1382 node = list_last(&run->thread_ar->proc_ar);
1383 while (node != NULL) {
1384 printf(" * ");
1385 proc_ar = list_node_data(node, run_proc_ar_t *);
1386 symbol_print_fqn(proc_ar->proc->outer_symbol);
1387 printf("\n");
1388
1389 node = list_prev(&run->thread_ar->proc_ar, node);
1390 }
1391}
1392
1393/** Destroy a block AR.
1394 *
1395 * @param run Runner object
1396 * @param proc_ar Pointer to block activation record
1397 */
1398void run_block_ar_destroy(run_t *run, run_block_ar_t *block_ar)
1399{
1400 map_elem_t *elem;
1401 rdata_var_t *var;
1402 int key;
1403
1404 (void) run;
1405
1406 elem = intmap_first(&block_ar->vars);
1407 while (elem != NULL) {
1408 /* Destroy the variable */
1409 var = intmap_elem_get_value(elem);
1410 rdata_var_destroy(var);
1411
1412 /* Remove the map element */
1413 key = intmap_elem_get_key(elem);
1414 intmap_set(&block_ar->vars, key, NULL);
1415
1416 elem = intmap_first(&block_ar->vars);
1417 }
1418
1419 intmap_fini(&block_ar->vars);
1420 run_block_ar_delete(block_ar);
1421}
1422
1423/** Convert item to value item.
1424 *
1425 * If @a item is a value, we just return a copy. If @a item is an address,
1426 * we read from the address.
1427 *
1428 * @param run Runner object
1429 * @param item Input item (value or address)
1430 * @param ritem Place to store pointer to new value item
1431 */
1432void run_cvt_value_item(run_t *run, rdata_item_t *item, rdata_item_t **ritem)
1433{
1434 rdata_value_t *value;
1435
1436 /*
1437 * This can happen when trying to use output of a function which
1438 * does not return a value.
1439 */
1440 if (item == NULL) {
1441 printf("Error: Sub-expression has no value.\n");
1442 exit(1);
1443 }
1444
1445 /* Address item. Perform read operation. */
1446 if (item->ic == ic_address) {
1447 run_address_read(run, item->u.address, ritem);
1448 return;
1449 }
1450
1451 /* Make a copy of the var node within. */
1452 value = rdata_value_new();
1453 rdata_var_copy(item->u.value->var, &value->var);
1454 *ritem = rdata_item_new(ic_value);
1455 (*ritem)->u.value = value;
1456}
1457
1458/** Get item var-class.
1459 *
1460 * Get var-class of @a item, regardless whether it is a value or address.
1461 * (I.e. the var class of the value or variable at the given address).
1462 *
1463 * @param run Runner object
1464 * @param item Value or address item
1465 * @return Varclass of @a item
1466 */
1467var_class_t run_item_get_vc(run_t *run, rdata_item_t *item)
1468{
1469 var_class_t vc;
1470 rdata_var_t *tpos;
1471
1472 (void) run;
1473
1474 switch (item->ic) {
1475 case ic_value:
1476 vc = item->u.value->var->vc;
1477 break;
1478 case ic_address:
1479 switch (item->u.address->ac) {
1480 case ac_var:
1481 vc = item->u.address->u.var_a->vref->vc;
1482 break;
1483 case ac_prop:
1484 /* Prefetch the value of the property. */
1485 tpos = run_aprop_get_tpos(run, item->u.address);
1486 vc = tpos->vc;
1487 break;
1488 default:
1489 assert(b_false);
1490 }
1491 break;
1492 default:
1493 assert(b_false);
1494 }
1495
1496 return vc;
1497}
1498
1499/** Get pointer to current var node in temporary copy in property address.
1500 *
1501 * A property address refers to a specific @c var node in a property.
1502 * This function will fetch a copy of the property value (by running
1503 * its getter) if there is not a temporary copy in the address yet.
1504 * It returns a pointer to the relevant @c var node in the temporary
1505 * copy.
1506 *
1507 * @param run Runner object
1508 * @param addr Address of class @c ac_prop
1509 * @return Pointer to var node
1510 */
1511static rdata_var_t *run_aprop_get_tpos(run_t *run, rdata_address_t *addr)
1512{
1513 rdata_item_t *ritem;
1514
1515 assert(addr->ac == ac_prop);
1516
1517 if (addr->u.prop_a->tvalue == NULL) {
1518 /* Fetch value of the property. */
1519 run_address_read(run, addr, &ritem);
1520 assert(ritem->ic == ic_value);
1521 addr->u.prop_a->tvalue = ritem->u.value;
1522 addr->u.prop_a->tpos = addr->u.prop_a->tvalue->var;
1523 }
1524
1525 return addr->u.prop_a->tpos;
1526}
1527
1528/** Read data from an address.
1529 *
1530 * Read value from the specified address.
1531 *
1532 * @param run Runner object
1533 * @param address Address to read
1534 * @param ritem Place to store pointer to the value that was read
1535 */
1536void run_address_read(run_t *run, rdata_address_t *address,
1537 rdata_item_t **ritem)
1538{
1539 (void) run;
1540 assert(ritem != NULL);
1541
1542 switch (address->ac) {
1543 case ac_var:
1544 rdata_var_read(address->u.var_a->vref, ritem);
1545 break;
1546 case ac_prop:
1547 run_aprop_read(run, address->u.prop_a, ritem);
1548 break;
1549 }
1550
1551 assert(*ritem == NULL || (*ritem)->ic == ic_value);
1552}
1553
1554/** Write data to an address.
1555 *
1556 * Store value @a value at address @a address.
1557 *
1558 * @param run Runner object
1559 * @param address Address to write
1560 * @param value Value to store at the address
1561 */
1562void run_address_write(run_t *run, rdata_address_t *address,
1563 rdata_value_t *value)
1564{
1565 (void) run;
1566
1567 switch (address->ac) {
1568 case ac_var:
1569 rdata_var_write(address->u.var_a->vref, value);
1570 break;
1571 case ac_prop:
1572 run_aprop_write(run, address->u.prop_a, value);
1573 break;
1574 }
1575}
1576
1577/** Read data from a property address.
1578 *
1579 * This involves invoking the property getter procedure.
1580 *
1581 * @param run Runner object.
1582 * @param addr_prop Property address to read.
1583 * @param ritem Place to store pointer to the value that was read.
1584 */
1585static void run_aprop_read(run_t *run, rdata_addr_prop_t *addr_prop,
1586 rdata_item_t **ritem)
1587{
1588 rdata_deleg_t *deleg;
1589 rdata_var_t *obj;
1590 stree_symbol_t *prop_sym;
1591 stree_prop_t *prop;
1592
1593 run_proc_ar_t *proc_ar;
1594
1595 rdata_var_t *cvar;
1596
1597#ifdef DEBUG_RUN_TRACE
1598 printf("Read from property.\n");
1599#endif
1600 /*
1601 * If @c tvalue is present, we need to use the relevant part from that
1602 * instead of re-reading the whole thing.
1603 */
1604 if (addr_prop->tvalue != NULL) {
1605 /* Copy the value */
1606 rdata_var_copy(addr_prop->tpos, &cvar);
1607 *ritem = rdata_item_new(ic_value);
1608 (*ritem)->u.value = rdata_value_new();
1609 (*ritem)->u.value->var = cvar;
1610 return;
1611 }
1612
1613 if (addr_prop->apc == apc_named)
1614 deleg = addr_prop->u.named->prop_d;
1615 else
1616 deleg = addr_prop->u.indexed->object_d;
1617
1618 obj = deleg->obj;
1619 prop_sym = deleg->sym;
1620 prop = symbol_to_prop(prop_sym);
1621 assert(prop != NULL);
1622
1623 if (prop->getter == NULL) {
1624 printf("Error: Property is not readable.\n");
1625 exit(1);
1626 }
1627
1628 /* Create procedure activation record. */
1629 run_proc_ar_create(run, obj, prop->getter, &proc_ar);
1630
1631 /* Fill in arguments (indices). */
1632 if (addr_prop->apc == apc_indexed) {
1633 run_proc_ar_set_args(run, proc_ar,
1634 &addr_prop->u.indexed->args);
1635 }
1636
1637 /* Run getter. */
1638 run_proc(run, proc_ar, ritem);
1639
1640 /* Destroy procedure activation record. */
1641 run_proc_ar_destroy(run, proc_ar);
1642
1643#ifdef DEBUG_RUN_TRACE
1644 printf("Getter returns ");
1645 rdata_item_print(*ritem);
1646 printf(".\n");
1647 printf("Done reading from property.\n");
1648#endif
1649}
1650
1651/** Write data to a property address.
1652 *
1653 * This involves invoking the property setter procedure.
1654 *
1655 * @param run Runner object
1656 * @param addr_prop Property address to write
1657 * @param value Value to store at the address
1658 */
1659static void run_aprop_write(run_t *run, rdata_addr_prop_t *addr_prop,
1660 rdata_value_t *value)
1661{
1662 rdata_deleg_t *deleg;
1663 rdata_var_t *obj;
1664 stree_symbol_t *prop_sym;
1665 stree_prop_t *prop;
1666
1667 run_proc_ar_t *proc_ar;
1668 rdata_item_t *vitem;
1669 rdata_item_t *ritem;
1670
1671#ifdef DEBUG_RUN_TRACE
1672 printf("Write to property.\n");
1673#endif
1674 /* If @c tvalue is present, we need to modify it and write it back. */
1675 if (addr_prop->tvalue != NULL) {
1676 printf("Unimplemented: Read-modify-write property access.\n");
1677 exit(1);
1678 }
1679
1680 if (addr_prop->apc == apc_named)
1681 deleg = addr_prop->u.named->prop_d;
1682 else
1683 deleg = addr_prop->u.indexed->object_d;
1684
1685 obj = deleg->obj;
1686 prop_sym = deleg->sym;
1687 prop = symbol_to_prop(prop_sym);
1688 assert(prop != NULL);
1689
1690 if (prop->setter == NULL) {
1691 printf("Error: Property is not writable.\n");
1692 exit(1);
1693 }
1694
1695 vitem = rdata_item_new(ic_value);
1696 vitem->u.value = value;
1697
1698 /* Create procedure activation record. */
1699 run_proc_ar_create(run, obj, prop->setter, &proc_ar);
1700
1701 /* Fill in arguments (indices). */
1702 if (addr_prop->apc == apc_indexed) {
1703 run_proc_ar_set_args(run, proc_ar,
1704 &addr_prop->u.indexed->args);
1705 }
1706
1707 /* Fill in value argument for setter. */
1708 run_proc_ar_set_setter_arg(run, proc_ar, vitem);
1709
1710 /* Run setter. */
1711 run_proc(run, proc_ar, &ritem);
1712
1713 /* Setter should not return a value. */
1714 assert(ritem == NULL);
1715
1716 /* Destroy procedure activation record. */
1717 run_proc_ar_destroy(run, proc_ar);
1718
1719#ifdef DEBUG_RUN_TRACE
1720 printf("Done writing to property.\n");
1721#endif
1722}
1723
1724/** Return reference to a variable.
1725 *
1726 * Constructs a reference (value item) pointing to @a var.
1727 *
1728 * @param run Runner object
1729 * @param var Variable node that is being referenced
1730 * @param res Place to store pointer to new reference.
1731 */
1732void run_reference(run_t *run, rdata_var_t *var, rdata_item_t **res)
1733{
1734 rdata_ref_t *ref;
1735 rdata_var_t *ref_var;
1736 rdata_value_t *ref_value;
1737 rdata_item_t *ref_item;
1738
1739 (void) run;
1740
1741 /* Create reference to the variable. */
1742 ref = rdata_ref_new();
1743 ref_var = rdata_var_new(vc_ref);
1744 ref->vref = var;
1745 ref_var->u.ref_v = ref;
1746
1747 /* Construct value of the reference to return. */
1748 ref_item = rdata_item_new(ic_value);
1749 ref_value = rdata_value_new();
1750 ref_item->u.value = ref_value;
1751 ref_value->var = ref_var;
1752
1753 *res = ref_item;
1754}
1755
1756/** Return address of reference target.
1757 *
1758 * Takes a reference (address or value) and returns the address (item) of
1759 * the target of the reference.
1760 *
1761 * @param run Runner object
1762 * @param ref Reference
1763 * @param cspan Cspan to put into exception if reference is nil
1764 * or @c NULL if no cspan is provided.
1765 * @param rtitem Place to store pointer to the resulting address.
1766 */
1767void run_dereference(run_t *run, rdata_item_t *ref, cspan_t *cspan,
1768 rdata_item_t **ritem)
1769{
1770 rdata_item_t *ref_val;
1771 rdata_item_t *item;
1772 rdata_address_t *address;
1773 rdata_addr_var_t *addr_var;
1774
1775#ifdef DEBUG_RUN_TRACE
1776 printf("run_dereference()\n");
1777#endif
1778 run_cvt_value_item(run, ref, &ref_val);
1779 if (run_is_bo(run)) {
1780 *ritem = run_recovery_item(run);
1781 return;
1782 }
1783
1784 assert(ref_val->u.value->var->vc == vc_ref);
1785
1786 item = rdata_item_new(ic_address);
1787 address = rdata_address_new(ac_var);
1788 addr_var = rdata_addr_var_new();
1789 item->u.address = address;
1790 address->u.var_a = addr_var;
1791 addr_var->vref = ref_val->u.value->var->u.ref_v->vref;
1792
1793 rdata_item_destroy(ref_val);
1794
1795 if (addr_var->vref == NULL) {
1796#ifdef DEBUG_RUN_TRACE
1797 printf("Error: Accessing null reference.\n");
1798#endif
1799 /* Raise Error.NilReference */
1800 run_raise_exc(run, run->program->builtin->error_nilreference,
1801 cspan);
1802 *ritem = run_recovery_item(run);
1803 return;
1804 }
1805
1806#ifdef DEBUG_RUN_TRACE
1807 printf("vref set to %p\n", addr_var->vref);
1808#endif
1809 *ritem = item;
1810}
1811
1812/** Raise an exception of the given class.
1813 *
1814 * Used when the interpreter generates an exception due to a run-time
1815 * error (not for the @c raise statement).
1816 *
1817 * @param run Runner object
1818 * @param csi Exception class
1819 * @param cspan Cspan of code that caused exception (for debugging)
1820 */
1821void run_raise_exc(run_t *run, stree_csi_t *csi, cspan_t *cspan)
1822{
1823 rdata_item_t *exc_vi;
1824
1825 /* Store exception cspan in thread AR. */
1826 run->thread_ar->exc_cspan = cspan;
1827
1828 /* Create exception object. */
1829 run_new_csi_inst_ref(run, csi, sn_nonstatic, &exc_vi);
1830 assert(exc_vi->ic == ic_value);
1831
1832 /* Store exception object in thread AR. */
1833 run->thread_ar->exc_payload = exc_vi->u.value;
1834
1835 /* Start exception bailout. */
1836 run->thread_ar->bo_mode = bm_exc;
1837}
1838
1839/** Determine if we are bailing out.
1840 *
1841 * @param run Runner object
1842 * @return @c b_true if we are bailing out, @c b_false otherwise
1843 */
1844bool_t run_is_bo(run_t *run)
1845{
1846 return run->thread_ar->bo_mode != bm_none;
1847}
1848
1849/** Construct a new variable of the given type.
1850 *
1851 * The variable is allocated and initialized with a default value
1852 * based on type item @a ti. For reference types the default value
1853 * is a null reference. At this point this does not work for generic
1854 * types (we need RTTI).
1855 *
1856 * @param run Runner object
1857 * @param ti Type of variable to create (type item)
1858 * @param rvar Place to store pointer to new variable
1859 */
1860void run_var_new(run_t *run, tdata_item_t *ti, rdata_var_t **rvar)
1861{
1862 rdata_var_t *var;
1863
1864 switch (ti->tic) {
1865 case tic_tprimitive:
1866 run_var_new_tprimitive(run, ti->u.tprimitive, rvar);
1867 break;
1868 case tic_tobject:
1869 case tic_tarray:
1870 run_var_new_null_ref(run, rvar);
1871 break;
1872 case tic_tdeleg:
1873 run_var_new_deleg(run, rvar);
1874 break;
1875 case tic_tebase:
1876 /*
1877 * One cannot declare variable of ebase type. It is just
1878 * type of expressions referring to enum types.
1879 */
1880 assert(b_false);
1881 /* Fallthrough */
1882 case tic_tenum:
1883 run_var_new_enum(run, ti->u.tenum, rvar);
1884 break;
1885 case tic_tfun:
1886 run_var_new_deleg(run, rvar);
1887 break;
1888 case tic_tvref:
1889 /*
1890 * XXX Need to obtain run-time value of type argument to
1891 * initialize variable properly.
1892 */
1893 var = rdata_var_new(vc_int);
1894 var->u.int_v = rdata_int_new();
1895 bigint_init(&var->u.int_v->value, 0);
1896 *rvar = var;
1897 break;
1898 case tic_ignore:
1899 assert(b_false);
1900 }
1901}
1902
1903/** Construct a new variable of primitive type.
1904 *
1905 * The variable is allocated and initialized with a default value
1906 * based on primitive type item @a tprimitive.
1907 *
1908 * @param run Runner object
1909 * @param ti Primitive type of variable to create
1910 * @param rvar Place to store pointer to new variable
1911 */
1912static void run_var_new_tprimitive(run_t *run, tdata_primitive_t *tprimitive,
1913 rdata_var_t **rvar)
1914{
1915 rdata_var_t *var;
1916
1917 (void) run;
1918
1919 /* Make compiler happy. */
1920 var = NULL;
1921
1922 switch (tprimitive->tpc) {
1923 case tpc_bool:
1924 var = rdata_var_new(vc_bool);
1925 var->u.bool_v = rdata_bool_new();
1926 var->u.bool_v->value = b_false;
1927 break;
1928 case tpc_char:
1929 var = rdata_var_new(vc_char);
1930 var->u.char_v = rdata_char_new();
1931 bigint_init(&var->u.char_v->value, 0);
1932 break;
1933 case tpc_int:
1934 var = rdata_var_new(vc_int);
1935 var->u.int_v = rdata_int_new();
1936 bigint_init(&var->u.int_v->value, 0);
1937 break;
1938 case tpc_nil:
1939 assert(b_false);
1940 /* Fallthrough */
1941 case tpc_string:
1942 var = rdata_var_new(vc_string);
1943 var->u.string_v = rdata_string_new();
1944 var->u.string_v->value = "";
1945 break;
1946 case tpc_resource:
1947 var = rdata_var_new(vc_resource);
1948 var->u.resource_v = rdata_resource_new();
1949 var->u.resource_v->data = NULL;
1950 break;
1951 }
1952
1953 *rvar = var;
1954}
1955
1956/** Construct a new variable containing null reference.
1957 *
1958 * @param run Runner object
1959 * @param rvar Place to store pointer to new variable
1960 */
1961static void run_var_new_null_ref(run_t *run, rdata_var_t **rvar)
1962{
1963 rdata_var_t *var;
1964
1965 (void) run;
1966
1967 /* Return null reference. */
1968 var = rdata_var_new(vc_ref);
1969 var->u.ref_v = rdata_ref_new();
1970
1971 *rvar = var;
1972}
1973
1974/** Construct a new variable containing invalid delegate.
1975 *
1976 * @param run Runner object
1977 * @param rvar Place to store pointer to new variable
1978 */
1979static void run_var_new_deleg(run_t *run, rdata_var_t **rvar)
1980{
1981 rdata_var_t *var;
1982
1983 (void) run;
1984
1985 /* Return null reference. */
1986 var = rdata_var_new(vc_deleg);
1987 var->u.deleg_v = rdata_deleg_new();
1988
1989 *rvar = var;
1990}
1991
1992/** Construct a new variable containing default value of an enum type.
1993 *
1994 * @param run Runner object
1995 * @param rvar Place to store pointer to new variable
1996 */
1997static void run_var_new_enum(run_t *run, tdata_enum_t *tenum,
1998 rdata_var_t **rvar)
1999{
2000 rdata_var_t *var;
2001 list_node_t *embr_n;
2002 stree_embr_t *embr;
2003
2004 (void) run;
2005
2006 /* Get first member of enum which will serve as default value. */
2007 embr_n = list_first(&tenum->enum_d->members);
2008 assert(embr_n != NULL);
2009
2010 embr = list_node_data(embr_n, stree_embr_t *);
2011
2012 /* Return null reference. */
2013 var = rdata_var_new(vc_enum);
2014 var->u.enum_v = rdata_enum_new();
2015 var->u.enum_v->value = embr;
2016
2017 *rvar = var;
2018}
2019
2020/** Construct a new thread activation record.
2021 *
2022 * @param run Runner object
2023 * @return New thread AR.
2024 */
2025run_thread_ar_t *run_thread_ar_new(void)
2026{
2027 run_thread_ar_t *thread_ar;
2028
2029 thread_ar = calloc(1, sizeof(run_thread_ar_t));
2030 if (thread_ar == NULL) {
2031 printf("Memory allocation failed.\n");
2032 exit(1);
2033 }
2034
2035 return thread_ar;
2036}
2037
2038/** Allocate a new procedure activation record.
2039 *
2040 * @return New procedure AR.
2041 */
2042run_proc_ar_t *run_proc_ar_new(void)
2043{
2044 run_proc_ar_t *proc_ar;
2045
2046 proc_ar = calloc(1, sizeof(run_proc_ar_t));
2047 if (proc_ar == NULL) {
2048 printf("Memory allocation failed.\n");
2049 exit(1);
2050 }
2051
2052 return proc_ar;
2053}
2054
2055/** Deallocate a procedure activation record.
2056 *
2057 * @return New procedure AR.
2058 */
2059void run_proc_ar_delete(run_proc_ar_t *proc_ar)
2060{
2061 assert(proc_ar != NULL);
2062 free(proc_ar);
2063}
2064
2065/** Allocate a new block activation record.
2066 *
2067 * @param run Runner object
2068 * @return New block AR.
2069 */
2070run_block_ar_t *run_block_ar_new(void)
2071{
2072 run_block_ar_t *block_ar;
2073
2074 block_ar = calloc(1, sizeof(run_block_ar_t));
2075 if (block_ar == NULL) {
2076 printf("Memory allocation failed.\n");
2077 exit(1);
2078 }
2079
2080 return block_ar;
2081}
2082
2083/** Deallocate a new block activation record.
2084 *
2085 * @param run Runner object
2086 * @return New block AR.
2087 */
2088void run_block_ar_delete(run_block_ar_t *block_ar)
2089{
2090 assert(block_ar != NULL);
2091 free(block_ar);
2092}
Note: See TracBrowser for help on using the repository browser.