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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 05b59393 was dc12262, checked in by Martin Decky <martin@…>, 8 years ago

add standardized case fallthrough comment annotations, add actual missing breaks

GCC 7.1's attribute((fallthrough)) would be more elegant, but unfortunatelly this annotation is incompatible with previous versions of GCC (it generates an empty declaration error)

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