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

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

Update SBI to rev. 344 from upstream. What's new:

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