/* * Copyright (c) 2011 Jiri Svoboda * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file Runner (executes the code). */ #include #include #include #include "bigint.h" #include "builtin.h" #include "cspan.h" #include "debug.h" #include "intmap.h" #include "list.h" #include "mytypes.h" #include "rdata.h" #include "run_expr.h" #include "run_texpr.h" #include "stree.h" #include "strtab.h" #include "symbol.h" #include "tdata.h" #include "run.h" static void run_block(run_t *run, stree_block_t *block); static void run_exps(run_t *run, stree_exps_t *exps, rdata_item_t **res); static void run_vdecl(run_t *run, stree_vdecl_t *vdecl); static void run_if(run_t *run, stree_if_t *if_s); static void run_switch(run_t *run, stree_switch_t *switch_s); static void run_while(run_t *run, stree_while_t *while_s); static void run_raise(run_t *run, stree_raise_t *raise_s); static void run_break(run_t *run, stree_break_t *break_s); static void run_return(run_t *run, stree_return_t *return_s); static void run_wef(run_t *run, stree_wef_t *wef_s); static bool_t run_exc_match(run_t *run, stree_except_t *except_c); static stree_csi_t *run_exc_payload_get_csi(run_t *run); static rdata_var_t *run_aprop_get_tpos(run_t *run, rdata_address_t *aprop); static void run_aprop_read(run_t *run, rdata_addr_prop_t *addr_prop, rdata_item_t **ritem); static void run_aprop_write(run_t *run, rdata_addr_prop_t *addr_prop, rdata_value_t *value); static void run_var_new_tprimitive(run_t *run, tdata_primitive_t *tprimitive, rdata_var_t **rvar); static void run_var_new_null_ref(run_t *run, rdata_var_t **rvar); static void run_var_new_deleg(run_t *run, rdata_var_t **rvar); static void run_var_new_enum(run_t *run, tdata_enum_t *tenum, rdata_var_t **rvar); /** Initialize runner instance. * * @param run Runner object */ void run_init(run_t *run) { (void) run; } /** Run program. * * Associates the program @a prog with the runner object and executes * it. If a run-time error occurs during the execution (e.g. an unhandled * exception), @a run->error will be set to @c b_true when this function * returns. * * @param run Runner object * @param prog Program to run */ void run_program(run_t *run, stree_program_t *prog) { stree_symbol_t *main_fun_sym; stree_fun_t *main_fun; rdata_var_t *main_obj; stree_ident_t *fake_ident; list_t main_args; run_proc_ar_t *proc_ar; rdata_item_t *res; /* Note down link to program code. */ run->program = prog; /* Initialize thread activation record. */ run->thread_ar = run_thread_ar_new(); list_init(&run->thread_ar->proc_ar); run->thread_ar->bo_mode = bm_none; /* Initialize global data structure. */ run_gdata_init(run); /* * Find entry point @c Main(). */ fake_ident = stree_ident_new(); fake_ident->sid = strtab_get_sid("Main"); main_fun_sym = symbol_find_epoint(prog, fake_ident); if (main_fun_sym == NULL) { printf("Error: Entry point 'Main' not found.\n"); exit(1); } main_fun = symbol_to_fun(main_fun_sym); assert(main_fun != NULL); main_obj = run_fun_sobject_find(run, main_fun); #ifdef DEBUG_RUN_TRACE printf("Found function '"); symbol_print_fqn(main_fun_sym); printf("'.\n"); #endif /* Run function @c main. */ list_init(&main_args); run_proc_ar_create(run, main_obj, main_fun->proc, &proc_ar); run_proc_ar_set_args(run, proc_ar, &main_args); run_proc(run, proc_ar, &res); run_proc_ar_destroy(run, proc_ar); run_exc_check_unhandled(run); } /** Initialize global data. * * @param run Runner object */ void run_gdata_init(run_t *run) { rdata_object_t *gobject; run->gdata = rdata_var_new(vc_object); gobject = rdata_object_new(); run->gdata->u.object_v = gobject; gobject->class_sym = NULL; gobject->static_obj = sn_static; intmap_init(&gobject->fields); } /** Run procedure. * * Inserts the provided procedure AR @a proc_ar on the execution stack * (in the thread AR) and executes the procedure. The return value * of the procedure is stored to *(@a res). @c NULL is stored if the * procedure returns no value. * * If the procedure execution bails out due to an exception, this * can be determined by looking at @c bo_mode in thread AR. Also, * in this case @c NULL is stored into *(@a res). * * @param run Runner object * @param proc_ar Procedure activation record * @param res Place to store procedure return value */ void run_proc(run_t *run, run_proc_ar_t *proc_ar, rdata_item_t **res) { stree_proc_t *proc; list_node_t *node; proc = proc_ar->proc; #ifdef DEBUG_RUN_TRACE printf("Start executing function '"); symbol_print_fqn(proc->outer_symbol); printf("'.\n"); #endif /* Add procedure AR to the stack. */ list_append(&run->thread_ar->proc_ar, proc_ar); /* Run main procedure block. */ if (proc->body != NULL) { run_block(run, proc->body); } else { builtin_run_proc(run, proc); } /* Handle bailout. */ switch (run->thread_ar->bo_mode) { case bm_stat: /* Break bailout was not caught. */ assert(b_false); case bm_proc: run->thread_ar->bo_mode = bm_none; break; default: break; } #ifdef DEBUG_RUN_TRACE printf("Done executing procedure '"); symbol_print_fqn(proc->outer_symbol); printf("'.\n"); run_print_fun_bt(run); #endif /* Remove procedure activation record from the stack. */ node = list_last(&run->thread_ar->proc_ar); assert(list_node_data(node, run_proc_ar_t *) == proc_ar); list_remove(&run->thread_ar->proc_ar, node); /* Procedure should not return an address. */ assert(proc_ar->retval == NULL || proc_ar->retval->ic == ic_value); *res = proc_ar->retval; } /** Run code block. * * @param run Runner object * @param block Block to run */ static void run_block(run_t *run, stree_block_t *block) { run_proc_ar_t *proc_ar; run_block_ar_t *block_ar; list_node_t *node; stree_stat_t *stat; #ifdef DEBUG_RUN_TRACE printf("Executing one code block.\n"); #endif /* Create block activation record. */ block_ar = run_block_ar_new(); intmap_init(&block_ar->vars); /* Add block activation record to the stack. */ proc_ar = run_get_current_proc_ar(run); list_append(&proc_ar->block_ar, block_ar); node = list_first(&block->stats); while (node != NULL) { stat = list_node_data(node, stree_stat_t *); run_stat(run, stat, NULL); if (run->thread_ar->bo_mode != bm_none) break; node = list_next(&block->stats, node); } #ifdef DEBUG_RUN_TRACE printf("Done executing code block.\n"); #endif /* Remove block activation record from the stack. */ node = list_last(&proc_ar->block_ar); assert(list_node_data(node, run_block_ar_t *) == block_ar); list_remove(&proc_ar->block_ar, node); /* Deallocate block activation record. */ run_block_ar_destroy(run, block_ar); } /** Run statement. * * Executes a statement. If @a res is not NULL and the statement is an * expression statement with a value, the value item will be stored to * @a res. * * @param run Runner object * @param stat Statement to run * @param res Place to store exps result or NULL if not interested */ void run_stat(run_t *run, stree_stat_t *stat, rdata_item_t **res) { #ifdef DEBUG_RUN_TRACE printf("Executing one statement %p.\n", stat); #endif if (res != NULL) *res = NULL; switch (stat->sc) { case st_exps: run_exps(run, stat->u.exp_s, res); break; case st_vdecl: run_vdecl(run, stat->u.vdecl_s); break; case st_if: run_if(run, stat->u.if_s); break; case st_switch: run_switch(run, stat->u.switch_s); break; case st_while: run_while(run, stat->u.while_s); break; case st_raise: run_raise(run, stat->u.raise_s); break; case st_break: run_break(run, stat->u.break_s); break; case st_return: run_return(run, stat->u.return_s); break; case st_wef: run_wef(run, stat->u.wef_s); break; case st_for: printf("Ignoring unimplemented statement type %d.\n", stat->sc); break; } } /** Run expression statement. * * Executes an expression statement. If @a res is not NULL then the value * of the expression (or NULL if it has no value) will be stored to @a res. * * @param run Runner object * @param exps Expression statement to run * @param res Place to store exps result or NULL if not interested */ static void run_exps(run_t *run, stree_exps_t *exps, rdata_item_t **res) { rdata_item_t *rexpr; #ifdef DEBUG_RUN_TRACE printf("Executing expression statement.\n"); #endif run_expr(run, exps->expr, &rexpr); /* * If the expression has a value, the caller should have asked for it. */ assert(res != NULL || rexpr == NULL); if (res != NULL) *res = rexpr; } /** Run variable declaration statement. * * @param run Runner object * @param vdecl Variable declaration statement to run */ static void run_vdecl(run_t *run, stree_vdecl_t *vdecl) { run_block_ar_t *block_ar; rdata_var_t *var, *old_var; #ifdef DEBUG_RUN_TRACE printf("Executing variable declaration statement.\n"); #endif /* Create variable and initialize with default value. */ run_var_new(run, vdecl->titem, &var); block_ar = run_get_current_block_ar(run); old_var = (rdata_var_t *) intmap_get(&block_ar->vars, vdecl->name->sid); if (old_var != NULL) { printf("Error: Duplicate variable '%s'\n", strtab_get_str(vdecl->name->sid)); exit(1); } intmap_set(&block_ar->vars, vdecl->name->sid, var); #ifdef DEBUG_RUN_TRACE printf("Declared variable '%s'\n", strtab_get_str(vdecl->name->sid)); #endif } /** Run @c if statement. * * @param run Runner object * @param if_s If statement to run */ static void run_if(run_t *run, stree_if_t *if_s) { rdata_item_t *rcond; list_node_t *ifc_node; stree_if_clause_t *ifc; bool_t rcond_b, clause_fired; #ifdef DEBUG_RUN_TRACE printf("Executing if statement.\n"); #endif clause_fired = b_false; ifc_node = list_first(&if_s->if_clauses); /* Walk through all if/elif clauses and see if they fire. */ while (ifc_node != NULL) { /* Get if/elif clause */ ifc = list_node_data(ifc_node, stree_if_clause_t *); run_expr(run, ifc->cond, &rcond); if (run_is_bo(run)) return; rcond_b = run_item_boolean_value(run, rcond); rdata_item_destroy(rcond); if (rcond_b == b_true) { #ifdef DEBUG_RUN_TRACE printf("Taking non-default path.\n"); #endif run_block(run, ifc->block); clause_fired = b_true; break; } ifc_node = list_next(&if_s->if_clauses, ifc_node); } /* If no if/elif clause fired, invoke the else clause. */ if (clause_fired == b_false && if_s->else_block != NULL) { #ifdef DEBUG_RUN_TRACE printf("Taking default path.\n"); #endif run_block(run, if_s->else_block); } #ifdef DEBUG_RUN_TRACE printf("If statement terminated.\n"); #endif } /** Run @c switch statement. * * @param run Runner object * @param switch_s Switch statement to run */ static void run_switch(run_t *run, stree_switch_t *switch_s) { rdata_item_t *rsexpr, *rsexpr_vi; rdata_item_t *rwexpr, *rwexpr_vi; list_node_t *whenc_node; stree_when_t *whenc; list_node_t *expr_node; stree_expr_t *expr; bool_t clause_fired; bool_t equal; #ifdef DEBUG_RUN_TRACE printf("Executing switch statement.\n"); #endif rsexpr_vi = NULL; /* Evaluate switch expression */ run_expr(run, switch_s->expr, &rsexpr); if (run_is_bo(run)) goto cleanup; /* Convert to value item */ run_cvt_value_item(run, rsexpr, &rsexpr_vi); rdata_item_destroy(rsexpr); if (run_is_bo(run)) goto cleanup; clause_fired = b_false; whenc_node = list_first(&switch_s->when_clauses); /* Walk through all when clauses and see if they fire. */ while (whenc_node != NULL) { /* Get when clause */ whenc = list_node_data(whenc_node, stree_when_t *); expr_node = list_first(&whenc->exprs); /* Walk through all expressions in the when clause */ while (expr_node != NULL) { /* Get expression */ expr = list_node_data(expr_node, stree_expr_t *); /* Evaluate expression */ run_expr(run, expr, &rwexpr); if (run_is_bo(run)) goto cleanup; /* Convert to value item */ run_cvt_value_item(run, rwexpr, &rwexpr_vi); rdata_item_destroy(rwexpr); if (run_is_bo(run)) { rdata_item_destroy(rwexpr_vi); goto cleanup; } /* Check if values are equal ('==') */ run_equal(run, rsexpr_vi->u.value, rwexpr_vi->u.value, &equal); rdata_item_destroy(rwexpr_vi); if (run_is_bo(run)) goto cleanup; if (equal) { #ifdef DEBUG_RUN_TRACE printf("Taking non-default path.\n"); #endif run_block(run, whenc->block); clause_fired = b_true; break; } expr_node = list_next(&whenc->exprs, expr_node); } if (clause_fired) break; whenc_node = list_next(&switch_s->when_clauses, whenc_node); } /* If no when clause fired, invoke the else clause. */ if (clause_fired == b_false && switch_s->else_block != NULL) { #ifdef DEBUG_RUN_TRACE printf("Taking default path.\n"); #endif run_block(run, switch_s->else_block); } cleanup: if (rsexpr_vi != NULL) rdata_item_destroy(rsexpr_vi); #ifdef DEBUG_RUN_TRACE printf("Switch statement terminated.\n"); #endif } /** Run @c while statement. * * @param run Runner object * @param while_s While statement to run */ static void run_while(run_t *run, stree_while_t *while_s) { rdata_item_t *rcond; #ifdef DEBUG_RUN_TRACE printf("Executing while statement.\n"); #endif run_expr(run, while_s->cond, &rcond); if (run_is_bo(run)) return; while (run_item_boolean_value(run, rcond) == b_true) { rdata_item_destroy(rcond); run_block(run, while_s->body); run_expr(run, while_s->cond, &rcond); if (run_is_bo(run)) break; } if (rcond != NULL) rdata_item_destroy(rcond); if (run->thread_ar->bo_mode == bm_stat) { /* Bailout due to break statement */ run->thread_ar->bo_mode = bm_none; } #ifdef DEBUG_RUN_TRACE printf("While statement terminated.\n"); #endif } /** Run @c raise statement. * * @param run Runner object * @param raise_s Raise statement to run */ static void run_raise(run_t *run, stree_raise_t *raise_s) { rdata_item_t *rexpr; rdata_item_t *rexpr_vi; #ifdef DEBUG_RUN_TRACE printf("Executing raise statement.\n"); #endif run_expr(run, raise_s->expr, &rexpr); if (run_is_bo(run)) return; run_cvt_value_item(run, rexpr, &rexpr_vi); rdata_item_destroy(rexpr); if (run_is_bo(run)) return; /* Store expression cspan in thread AR. */ run->thread_ar->exc_cspan = raise_s->expr->cspan; /* Store expression result in thread AR. */ /* XXX rexpr_vi is leaked here, we only return ->u.value */ run->thread_ar->exc_payload = rexpr_vi->u.value; /* Start exception bailout. */ run->thread_ar->bo_mode = bm_exc; } /** Run @c break statement. * * Forces control to return from the active breakable statement by setting * bailout mode to @c bm_stat. * * @param run Runner object * @param break_s Break statement to run */ static void run_break(run_t *run, stree_break_t *break_s) { #ifdef DEBUG_RUN_TRACE printf("Executing 'break' statement.\n"); #endif (void) break_s; /* Force control to ascend and leave the procedure. */ if (run->thread_ar->bo_mode == bm_none) run->thread_ar->bo_mode = bm_stat; } /** Run @c return statement. * * Sets the return value in procedure AR and forces control to return * from the function by setting bailout mode to @c bm_proc. * * @param run Runner object * @param return_s Return statement to run */ static void run_return(run_t *run, stree_return_t *return_s) { rdata_item_t *rexpr; rdata_item_t *rexpr_vi; run_proc_ar_t *proc_ar; #ifdef DEBUG_RUN_TRACE printf("Executing return statement.\n"); #endif if (return_s->expr != NULL) { run_expr(run, return_s->expr, &rexpr); if (run_is_bo(run)) return; run_cvt_value_item(run, rexpr, &rexpr_vi); rdata_item_destroy(rexpr); if (run_is_bo(run)) return; /* Store expression result in procedure AR. */ proc_ar = run_get_current_proc_ar(run); proc_ar->retval = rexpr_vi; } /* Force control to ascend and leave the procedure. */ if (run->thread_ar->bo_mode == bm_none) run->thread_ar->bo_mode = bm_proc; } /** Run @c with-except-finally statement. * * Note: 'With' clause is not implemented. * * @param run Runner object * @param wef_s With-except-finally statement to run */ static void run_wef(run_t *run, stree_wef_t *wef_s) { list_node_t *except_n; stree_except_t *except_c; rdata_value_t *exc_payload; run_bailout_mode_t bo_mode; #ifdef DEBUG_RUN_TRACE printf("Executing with-except-finally statement.\n"); #endif run_block(run, wef_s->with_block); if (run->thread_ar->bo_mode == bm_exc) { #ifdef DEBUG_RUN_TRACE printf("With statement detected exception.\n"); #endif /* Reset to normal execution. */ run->thread_ar->bo_mode = bm_none; /* Look for an except block. */ except_n = list_first(&wef_s->except_clauses); while (except_n != NULL) { except_c = list_node_data(except_n, stree_except_t *); if (run_exc_match(run, except_c)) break; except_n = list_next(&wef_s->except_clauses, except_n); } /* If one was found, execute it. */ if (except_n != NULL) run_block(run, except_c->block); /* Execute finally block */ if (wef_s->finally_block != NULL) { /* Put exception on the side temporarily. */ bo_mode = run->thread_ar->bo_mode; exc_payload = run->thread_ar->exc_payload; run->thread_ar->bo_mode = bm_none; run->thread_ar->exc_payload = NULL; run_block(run, wef_s->finally_block); if (bo_mode == bm_exc) { /* * Restore the original exception. If another * exception occured in the finally block (i.e. * double fault), it is forgotten. */ run->thread_ar->bo_mode = bm_exc; run->thread_ar->exc_payload = exc_payload; } } } #ifdef DEBUG_RUN_TRACE printf("With-except-finally statement terminated.\n"); #endif } /** Determine whether currently active exception matches @c except clause. * * Checks if the currently active exception in the runner object @c run * matches except clause @c except_c. * * @param run Runner object * @param except_c @c except clause * @return @c b_true if there is a match, @c b_false otherwise */ static bool_t run_exc_match(run_t *run, stree_except_t *except_c) { stree_csi_t *exc_csi; /* Get CSI of active exception. */ exc_csi = run_exc_payload_get_csi(run); /* Determine if active exc. is derived from type in exc. clause. */ /* XXX This is wrong, it does not work with generics. */ return tdata_is_csi_derived_from_ti(exc_csi, except_c->titem); } /** Return CSI of the active exception. * * @param run Runner object * @return CSI of the active exception */ static stree_csi_t *run_exc_payload_get_csi(run_t *run) { rdata_value_t *payload; rdata_var_t *payload_v; rdata_object_t *payload_o; payload = run->thread_ar->exc_payload; assert(payload != NULL); if (payload->var->vc != vc_ref) { /* XXX Prevent this via static type checking. */ printf("Error: Exception payload must be an object " "(found type %d).\n", payload->var->vc); exit(1); } payload_v = payload->var->u.ref_v->vref; if (payload_v->vc != vc_object) { /* XXX Prevent this via static type checking. */ printf("Error: Exception payload must be an object " "(found type %d).\n", payload_v->vc); exit(1); } payload_o = payload_v->u.object_v; #ifdef DEBUG_RUN_TRACE printf("Active exception: '"); symbol_print_fqn(payload_o->class_sym); printf("'.\n"); #endif assert(payload_o->class_sym != NULL); assert(payload_o->class_sym->sc == sc_csi); return payload_o->class_sym->u.csi; } /** Check for unhandled exception. * * Checks whether there is an active exception. If so, it prints an * error message and raises a run-time error. * * @param run Runner object */ void run_exc_check_unhandled(run_t *run) { stree_csi_t *exc_csi; if (run->thread_ar->bo_mode != bm_none) { assert(run->thread_ar->bo_mode == bm_exc); exc_csi = run_exc_payload_get_csi(run); if (run->thread_ar->exc_cspan != NULL) { cspan_print(run->thread_ar->exc_cspan); putchar(' '); } printf("Error: Unhandled exception '"); symbol_print_fqn(csi_to_symbol(exc_csi)); printf("'.\n"); run_raise_error(run); } } /** Raise an irrecoverable run-time error, start bailing out. * * Raises an error that cannot be handled by the user program. * * @param run Runner object */ void run_raise_error(run_t *run) { run->thread_ar->bo_mode = bm_error; run->thread_ar->error = b_true; } /** Construct a special recovery item. * * @param run Runner object */ rdata_item_t *run_recovery_item(run_t *run) { (void) run; return NULL; } /** Find a local variable in the currently active function. * * @param run Runner object * @param name Name SID of the local variable * @return Pointer to var node or @c NULL if not found */ rdata_var_t *run_local_vars_lookup(run_t *run, sid_t name) { run_proc_ar_t *proc_ar; run_block_ar_t *block_ar; rdata_var_t *var; list_node_t *node; proc_ar = run_get_current_proc_ar(run); node = list_last(&proc_ar->block_ar); /* Walk through all block activation records. */ while (node != NULL) { block_ar = list_node_data(node, run_block_ar_t *); var = intmap_get(&block_ar->vars, name); if (var != NULL) return var; node = list_prev(&proc_ar->block_ar, node); } /* No match */ return NULL; } /** Get current procedure activation record. * * @param run Runner object * @return Active procedure AR */ run_proc_ar_t *run_get_current_proc_ar(run_t *run) { list_node_t *node; node = list_last(&run->thread_ar->proc_ar); return list_node_data(node, run_proc_ar_t *); } /** Get current block activation record. * * @param run Runner object * @return Active block AR */ run_block_ar_t *run_get_current_block_ar(run_t *run) { run_proc_ar_t *proc_ar; list_node_t *node; proc_ar = run_get_current_proc_ar(run); node = list_last(&proc_ar->block_ar); return list_node_data(node, run_block_ar_t *); } /** Get current CSI. * * @param run Runner object * @return Active CSI */ stree_csi_t *run_get_current_csi(run_t *run) { run_proc_ar_t *proc_ar; proc_ar = run_get_current_proc_ar(run); return proc_ar->proc->outer_symbol->outer_csi; } /** Get static object (i.e. class object). * * Looks for a child static object named @a name in static object @a pobject. * If the child does not exist yet, it is created. * * @param run Runner object * @param csi CSI of the static object named @a name * @param pobject Parent static object * @param name Static object name * * @return Static (class) object of the given name */ rdata_var_t *run_sobject_get(run_t *run, stree_csi_t *csi, rdata_var_t *pobj_var, sid_t name) { rdata_object_t *pobject; rdata_var_t *mbr_var; rdata_var_t *rvar; stree_ident_t *ident; assert(pobj_var->vc == vc_object); pobject = pobj_var->u.object_v; #ifdef DEBUG_RUN_TRACE printf("Get static object '%s' in '", strtab_get_str(name)); if (pobject->class_sym != NULL) symbol_print_fqn(pobject->class_sym); else printf("global"); #endif assert(pobject->static_obj == sn_static); mbr_var = intmap_get(&pobject->fields, name); if (mbr_var != NULL) { #ifdef DEBUG_RUN_TRACE printf("Return exising static object (mbr_var=%p).\n", mbr_var); #endif /* Return existing object. */ return mbr_var; } /* Construct new object. */ #ifdef DEBUG_RUN_TRACE printf("Construct new static object.\n"); #endif ident = stree_ident_new(); ident->sid = name; run_new_csi_inst(run, csi, sn_static, &rvar); /* Store static object reference for future use. */ intmap_set(&pobject->fields, name, rvar); return rvar; } /** Get static object for CSI. * * In situations where we do not have the parent static object and need * to find static object for a CSI from the gdata root we use this. * * This is only used in special cases such as when invoking the entry * point. This is too slow to use during normal execution. * * @param run Runner object * @param csi CSI to get static object of * * @return Static (class) object */ rdata_var_t *run_sobject_find(run_t *run, stree_csi_t *csi) { rdata_var_t *pobj_var; if (csi == NULL) return run->gdata; assert(csi->ancr_state == ws_visited); pobj_var = run_sobject_find(run, csi_to_symbol(csi)->outer_csi); return run_sobject_get(run, csi, pobj_var, csi->name->sid); } /** Get static object for CSI containing function. * * This is used to obtain active object for invoking static function * @a fun. Only used in cases where we don't already have the object such * as when running the entry point. Otherwise this would be slow. * * @param run Runner object * @param fun Function to get static class object of * * @return Static (class) object */ rdata_var_t *run_fun_sobject_find(run_t *run, stree_fun_t *fun) { return run_sobject_find(run, fun_to_symbol(fun)->outer_csi); } /** Construct variable from a value item. * * XXX This should be in fact implemented using existing code as: * * (1) Create a variable of the desired type. * (2) Initialize the variable with the provided value. * * @param item Value item (initial value for variable). * @param var Place to store new var node. */ void run_value_item_to_var(rdata_item_t *item, rdata_var_t **var) { rdata_bool_t *bool_v; rdata_char_t *char_v; rdata_deleg_t *deleg_v; rdata_enum_t *enum_v; rdata_int_t *int_v; rdata_string_t *string_v; rdata_ref_t *ref_v; rdata_var_t *in_var; assert(item->ic == ic_value); in_var = item->u.value->var; switch (in_var->vc) { case vc_bool: *var = rdata_var_new(vc_bool); bool_v = rdata_bool_new(); (*var)->u.bool_v = bool_v; bool_v->value = item->u.value->var->u.bool_v->value; break; case vc_char: *var = rdata_var_new(vc_char); char_v = rdata_char_new(); (*var)->u.char_v = char_v; bigint_clone(&item->u.value->var->u.char_v->value, &char_v->value); break; case vc_deleg: *var = rdata_var_new(vc_deleg); deleg_v = rdata_deleg_new(); (*var)->u.deleg_v = deleg_v; deleg_v->obj = item->u.value->var->u.deleg_v->obj; deleg_v->sym = item->u.value->var->u.deleg_v->sym; break; case vc_enum: *var = rdata_var_new(vc_enum); enum_v = rdata_enum_new(); (*var)->u.enum_v = enum_v; enum_v->value = item->u.value->var->u.enum_v->value; break; case vc_int: *var = rdata_var_new(vc_int); int_v = rdata_int_new(); (*var)->u.int_v = int_v; bigint_clone(&item->u.value->var->u.int_v->value, &int_v->value); break; case vc_string: *var = rdata_var_new(vc_string); string_v = rdata_string_new(); (*var)->u.string_v = string_v; string_v->value = item->u.value->var->u.string_v->value; break; case vc_ref: *var = rdata_var_new(vc_ref); ref_v = rdata_ref_new(); (*var)->u.ref_v = ref_v; ref_v->vref = item->u.value->var->u.ref_v->vref; break; default: printf("Error: Unimplemented argument type.\n"); exit(1); } } /** Construct a procedure AR. * * @param run Runner object * @param obj Object whose procedure is being activated * @param proc Procedure that is being activated * @param rproc_ar Place to store pointer to new activation record */ void run_proc_ar_create(run_t *run, rdata_var_t *obj, stree_proc_t *proc, run_proc_ar_t **rproc_ar) { run_proc_ar_t *proc_ar; run_block_ar_t *block_ar; (void) run; /* Create procedure activation record. */ proc_ar = run_proc_ar_new(); proc_ar->obj = obj; proc_ar->proc = proc; list_init(&proc_ar->block_ar); proc_ar->retval = NULL; /* Create special block activation record to hold function arguments. */ block_ar = run_block_ar_new(); intmap_init(&block_ar->vars); list_append(&proc_ar->block_ar, block_ar); *rproc_ar = proc_ar; } /** Destroy a procedure AR. * * @param run Runner object * @param proc_ar Pointer to procedure activation record */ void run_proc_ar_destroy(run_t *run, run_proc_ar_t *proc_ar) { list_node_t *ar_node; run_block_ar_t *block_ar; (void) run; /* Destroy special block activation record. */ ar_node = list_first(&proc_ar->block_ar); block_ar = list_node_data(ar_node, run_block_ar_t *); list_remove(&proc_ar->block_ar, ar_node); run_block_ar_destroy(run, block_ar); /* Destroy procedure activation record. */ proc_ar->obj = NULL; proc_ar->proc = NULL; list_fini(&proc_ar->block_ar); proc_ar->retval = NULL; run_proc_ar_delete(proc_ar); } /** Fill arguments in a procedure AR. * * When invoking a procedure this is used to store the argument values * in the activation record. * * @param run Runner object * @param proc_ar Existing procedure activation record where to store * the values * @param arg_vals List of value items (rdata_item_t *) -- real * argument values */ void run_proc_ar_set_args(run_t *run, run_proc_ar_t *proc_ar, list_t *arg_vals) { stree_ctor_t *ctor; stree_fun_t *fun; stree_prop_t *prop; list_t *args; stree_proc_arg_t *varg; stree_symbol_t *outer_symbol; run_block_ar_t *block_ar; list_node_t *block_ar_n; list_node_t *rarg_n, *parg_n; list_node_t *cn; rdata_item_t *rarg; stree_proc_arg_t *parg; rdata_var_t *var; rdata_var_t *ref_var; rdata_ref_t *ref; rdata_array_t *array; rdata_var_t *elem_var; int n_vargs, idx; (void) run; /* AR should have been created with run_proc_ar_create(). */ assert(proc_ar->proc != NULL); outer_symbol = proc_ar->proc->outer_symbol; /* Make compiler happy. */ args = NULL; varg = NULL; /* * The procedure being activated should belong to a member function or * property getter/setter. */ switch (outer_symbol->sc) { case sc_ctor: ctor = symbol_to_ctor(outer_symbol); args = &ctor->sig->args; varg = ctor->sig->varg; break; case sc_fun: fun = symbol_to_fun(outer_symbol); args = &fun->sig->args; varg = fun->sig->varg; break; case sc_prop: prop = symbol_to_prop(outer_symbol); args = &prop->args; varg = prop->varg; break; case sc_csi: case sc_deleg: case sc_enum: case sc_var: assert(b_false); } /* Fetch first block activation record. */ block_ar_n = list_first(&proc_ar->block_ar); assert(block_ar_n != NULL); block_ar = list_node_data(block_ar_n, run_block_ar_t *); /* Declare local variables to hold argument values. */ rarg_n = list_first(arg_vals); parg_n = list_first(args); while (parg_n != NULL) { if (rarg_n == NULL) { printf("Error: Too few arguments to '"); symbol_print_fqn(outer_symbol); printf("'.\n"); exit(1); } rarg = list_node_data(rarg_n, rdata_item_t *); parg = list_node_data(parg_n, stree_proc_arg_t *); assert(rarg->ic == ic_value); /* Construct a variable from the argument value. */ run_value_item_to_var(rarg, &var); /* Declare variable using name of formal argument. */ intmap_set(&block_ar->vars, parg->name->sid, var); rarg_n = list_next(arg_vals, rarg_n); parg_n = list_next(args, parg_n); } if (varg != NULL) { /* Function is variadic. Count number of variadic arguments. */ cn = rarg_n; n_vargs = 0; while (cn != NULL) { n_vargs += 1; cn = list_next(arg_vals, cn); } /* Prepare array to store variadic arguments. */ array = rdata_array_new(1); array->extent[0] = n_vargs; rdata_array_alloc_element(array); /* Read variadic arguments. */ idx = 0; while (rarg_n != NULL) { rarg = list_node_data(rarg_n, rdata_item_t *); assert(rarg->ic == ic_value); run_value_item_to_var(rarg, &elem_var); array->element[idx] = elem_var; rarg_n = list_next(arg_vals, rarg_n); idx += 1; } var = rdata_var_new(vc_array); var->u.array_v = array; /* Create reference to the new array. */ ref_var = rdata_var_new(vc_ref); ref = rdata_ref_new(); ref_var->u.ref_v = ref; ref->vref = var; /* Declare variable using name of formal argument. */ intmap_set(&block_ar->vars, varg->name->sid, ref_var); } /* Check for excess real parameters. */ if (rarg_n != NULL) { printf("Error: Too many arguments to '"); symbol_print_fqn(outer_symbol); printf("'.\n"); exit(1); } } /** Fill setter argument in a procedure AR. * * When invoking a setter this is used to store its argument value in its * procedure activation record. * * @param run Runner object * @param proc_ar Existing procedure activation record where to store * the setter argument * @param arg_val Value items (rdata_item_t *) -- real argument value */ void run_proc_ar_set_setter_arg(run_t *run, run_proc_ar_t *proc_ar, rdata_item_t *arg_val) { stree_prop_t *prop; run_block_ar_t *block_ar; list_node_t *block_ar_n; rdata_var_t *var; (void) run; /* AR should have been created with run_proc_ar_create(). */ assert(proc_ar->proc != NULL); /* The procedure being activated should belong to a property setter. */ prop = symbol_to_prop(proc_ar->proc->outer_symbol); assert(prop != NULL); assert(proc_ar->proc == prop->setter); /* Fetch first block activation record. */ block_ar_n = list_first(&proc_ar->block_ar); assert(block_ar_n != NULL); block_ar = list_node_data(block_ar_n, run_block_ar_t *); assert(arg_val->ic == ic_value); /* Construct a variable from the argument value. */ run_value_item_to_var(arg_val, &var); /* Declare variable using name of formal argument. */ intmap_set(&block_ar->vars, prop->setter_arg->name->sid, var); } /** Print function activation backtrace. * * Prints a backtrace of activated functions for debugging purposes. * * @param run Runner object */ void run_print_fun_bt(run_t *run) { list_node_t *node; run_proc_ar_t *proc_ar; printf("Backtrace:\n"); node = list_last(&run->thread_ar->proc_ar); while (node != NULL) { printf(" * "); proc_ar = list_node_data(node, run_proc_ar_t *); symbol_print_fqn(proc_ar->proc->outer_symbol); printf("\n"); node = list_prev(&run->thread_ar->proc_ar, node); } } /** Destroy a block AR. * * @param run Runner object * @param proc_ar Pointer to block activation record */ void run_block_ar_destroy(run_t *run, run_block_ar_t *block_ar) { map_elem_t *elem; rdata_var_t *var; int key; (void) run; elem = intmap_first(&block_ar->vars); while (elem != NULL) { /* Destroy the variable */ var = intmap_elem_get_value(elem); rdata_var_destroy(var); /* Remove the map element */ key = intmap_elem_get_key(elem); intmap_set(&block_ar->vars, key, NULL); elem = intmap_first(&block_ar->vars); } intmap_fini(&block_ar->vars); run_block_ar_delete(block_ar); } /** Convert item to value item. * * If @a item is a value, we just return a copy. If @a item is an address, * we read from the address. * * @param run Runner object * @param item Input item (value or address) * @param ritem Place to store pointer to new value item */ void run_cvt_value_item(run_t *run, rdata_item_t *item, rdata_item_t **ritem) { rdata_value_t *value; /* * This can happen when trying to use output of a function which * does not return a value. */ if (item == NULL) { printf("Error: Sub-expression has no value.\n"); exit(1); } /* Address item. Perform read operation. */ if (item->ic == ic_address) { run_address_read(run, item->u.address, ritem); return; } /* Make a copy of the var node within. */ value = rdata_value_new(); rdata_var_copy(item->u.value->var, &value->var); *ritem = rdata_item_new(ic_value); (*ritem)->u.value = value; } /** Get item var-class. * * Get var-class of @a item, regardless whether it is a value or address. * (I.e. the var class of the value or variable at the given address). * * @param run Runner object * @param item Value or address item * @return Varclass of @a item */ var_class_t run_item_get_vc(run_t *run, rdata_item_t *item) { var_class_t vc; rdata_var_t *tpos; (void) run; switch (item->ic) { case ic_value: vc = item->u.value->var->vc; break; case ic_address: switch (item->u.address->ac) { case ac_var: vc = item->u.address->u.var_a->vref->vc; break; case ac_prop: /* Prefetch the value of the property. */ tpos = run_aprop_get_tpos(run, item->u.address); vc = tpos->vc; break; default: assert(b_false); } break; default: assert(b_false); } return vc; } /** Get pointer to current var node in temporary copy in property address. * * A property address refers to a specific @c var node in a property. * This function will fetch a copy of the property value (by running * its getter) if there is not a temporary copy in the address yet. * It returns a pointer to the relevant @c var node in the temporary * copy. * * @param run Runner object * @param addr Address of class @c ac_prop * @return Pointer to var node */ static rdata_var_t *run_aprop_get_tpos(run_t *run, rdata_address_t *addr) { rdata_item_t *ritem; assert(addr->ac == ac_prop); if (addr->u.prop_a->tvalue == NULL) { /* Fetch value of the property. */ run_address_read(run, addr, &ritem); assert(ritem->ic == ic_value); addr->u.prop_a->tvalue = ritem->u.value; addr->u.prop_a->tpos = addr->u.prop_a->tvalue->var; } return addr->u.prop_a->tpos; } /** Read data from an address. * * Read value from the specified address. * * @param run Runner object * @param address Address to read * @param ritem Place to store pointer to the value that was read */ void run_address_read(run_t *run, rdata_address_t *address, rdata_item_t **ritem) { (void) run; assert(ritem != NULL); switch (address->ac) { case ac_var: rdata_var_read(address->u.var_a->vref, ritem); break; case ac_prop: run_aprop_read(run, address->u.prop_a, ritem); break; } assert(*ritem == NULL || (*ritem)->ic == ic_value); } /** Write data to an address. * * Store value @a value at address @a address. * * @param run Runner object * @param address Address to write * @param value Value to store at the address */ void run_address_write(run_t *run, rdata_address_t *address, rdata_value_t *value) { (void) run; switch (address->ac) { case ac_var: rdata_var_write(address->u.var_a->vref, value); break; case ac_prop: run_aprop_write(run, address->u.prop_a, value); break; } } /** Read data from a property address. * * This involves invoking the property getter procedure. * * @param run Runner object. * @param addr_prop Property address to read. * @param ritem Place to store pointer to the value that was read. */ static void run_aprop_read(run_t *run, rdata_addr_prop_t *addr_prop, rdata_item_t **ritem) { rdata_deleg_t *deleg; rdata_var_t *obj; stree_symbol_t *prop_sym; stree_prop_t *prop; run_proc_ar_t *proc_ar; rdata_var_t *cvar; #ifdef DEBUG_RUN_TRACE printf("Read from property.\n"); #endif /* * If @c tvalue is present, we need to use the relevant part from that * instead of re-reading the whole thing. */ if (addr_prop->tvalue != NULL) { /* Copy the value */ rdata_var_copy(addr_prop->tpos, &cvar); *ritem = rdata_item_new(ic_value); (*ritem)->u.value = rdata_value_new(); (*ritem)->u.value->var = cvar; return; } if (addr_prop->apc == apc_named) deleg = addr_prop->u.named->prop_d; else deleg = addr_prop->u.indexed->object_d; obj = deleg->obj; prop_sym = deleg->sym; prop = symbol_to_prop(prop_sym); assert(prop != NULL); if (prop->getter == NULL) { printf("Error: Property is not readable.\n"); exit(1); } /* Create procedure activation record. */ run_proc_ar_create(run, obj, prop->getter, &proc_ar); /* Fill in arguments (indices). */ if (addr_prop->apc == apc_indexed) { run_proc_ar_set_args(run, proc_ar, &addr_prop->u.indexed->args); } /* Run getter. */ run_proc(run, proc_ar, ritem); /* Destroy procedure activation record. */ run_proc_ar_destroy(run, proc_ar); #ifdef DEBUG_RUN_TRACE printf("Getter returns "); rdata_item_print(*ritem); printf(".\n"); printf("Done reading from property.\n"); #endif } /** Write data to a property address. * * This involves invoking the property setter procedure. * * @param run Runner object * @param addr_prop Property address to write * @param value Value to store at the address */ static void run_aprop_write(run_t *run, rdata_addr_prop_t *addr_prop, rdata_value_t *value) { rdata_deleg_t *deleg; rdata_var_t *obj; stree_symbol_t *prop_sym; stree_prop_t *prop; run_proc_ar_t *proc_ar; rdata_item_t *vitem; rdata_item_t *ritem; #ifdef DEBUG_RUN_TRACE printf("Write to property.\n"); #endif /* If @c tvalue is present, we need to modify it and write it back. */ if (addr_prop->tvalue != NULL) { printf("Unimplemented: Read-modify-write property access.\n"); exit(1); } if (addr_prop->apc == apc_named) deleg = addr_prop->u.named->prop_d; else deleg = addr_prop->u.indexed->object_d; obj = deleg->obj; prop_sym = deleg->sym; prop = symbol_to_prop(prop_sym); assert(prop != NULL); if (prop->setter == NULL) { printf("Error: Property is not writable.\n"); exit(1); } vitem = rdata_item_new(ic_value); vitem->u.value = value; /* Create procedure activation record. */ run_proc_ar_create(run, obj, prop->setter, &proc_ar); /* Fill in arguments (indices). */ if (addr_prop->apc == apc_indexed) { run_proc_ar_set_args(run, proc_ar, &addr_prop->u.indexed->args); } /* Fill in value argument for setter. */ run_proc_ar_set_setter_arg(run, proc_ar, vitem); /* Run setter. */ run_proc(run, proc_ar, &ritem); /* Setter should not return a value. */ assert(ritem == NULL); /* Destroy procedure activation record. */ run_proc_ar_destroy(run, proc_ar); #ifdef DEBUG_RUN_TRACE printf("Done writing to property.\n"); #endif } /** Return reference to a variable. * * Constructs a reference (value item) pointing to @a var. * * @param run Runner object * @param var Variable node that is being referenced * @param res Place to store pointer to new reference. */ void run_reference(run_t *run, rdata_var_t *var, rdata_item_t **res) { rdata_ref_t *ref; rdata_var_t *ref_var; rdata_value_t *ref_value; rdata_item_t *ref_item; (void) run; /* Create reference to the variable. */ ref = rdata_ref_new(); ref_var = rdata_var_new(vc_ref); ref->vref = var; ref_var->u.ref_v = ref; /* Construct value of the reference to return. */ ref_item = rdata_item_new(ic_value); ref_value = rdata_value_new(); ref_item->u.value = ref_value; ref_value->var = ref_var; *res = ref_item; } /** Return address of reference target. * * Takes a reference (address or value) and returns the address (item) of * the target of the reference. * * @param run Runner object * @param ref Reference * @param cspan Cspan to put into exception if reference is nil * or @c NULL if no cspan is provided. * @param rtitem Place to store pointer to the resulting address. */ void run_dereference(run_t *run, rdata_item_t *ref, cspan_t *cspan, rdata_item_t **ritem) { rdata_item_t *ref_val; rdata_item_t *item; rdata_address_t *address; rdata_addr_var_t *addr_var; #ifdef DEBUG_RUN_TRACE printf("run_dereference()\n"); #endif run_cvt_value_item(run, ref, &ref_val); if (run_is_bo(run)) { *ritem = run_recovery_item(run); return; } assert(ref_val->u.value->var->vc == vc_ref); item = rdata_item_new(ic_address); address = rdata_address_new(ac_var); addr_var = rdata_addr_var_new(); item->u.address = address; address->u.var_a = addr_var; addr_var->vref = ref_val->u.value->var->u.ref_v->vref; rdata_item_destroy(ref_val); if (addr_var->vref == NULL) { #ifdef DEBUG_RUN_TRACE printf("Error: Accessing null reference.\n"); #endif /* Raise Error.NilReference */ run_raise_exc(run, run->program->builtin->error_nilreference, cspan); *ritem = run_recovery_item(run); return; } #ifdef DEBUG_RUN_TRACE printf("vref set to %p\n", addr_var->vref); #endif *ritem = item; } /** Raise an exception of the given class. * * Used when the interpreter generates an exception due to a run-time * error (not for the @c raise statement). * * @param run Runner object * @param csi Exception class * @param cspan Cspan of code that caused exception (for debugging) */ void run_raise_exc(run_t *run, stree_csi_t *csi, cspan_t *cspan) { rdata_item_t *exc_vi; /* Store exception cspan in thread AR. */ run->thread_ar->exc_cspan = cspan; /* Create exception object. */ run_new_csi_inst_ref(run, csi, sn_nonstatic, &exc_vi); assert(exc_vi->ic == ic_value); /* Store exception object in thread AR. */ run->thread_ar->exc_payload = exc_vi->u.value; /* Start exception bailout. */ run->thread_ar->bo_mode = bm_exc; } /** Determine if we are bailing out. * * @param run Runner object * @return @c b_true if we are bailing out, @c b_false otherwise */ bool_t run_is_bo(run_t *run) { return run->thread_ar->bo_mode != bm_none; } /** Construct a new variable of the given type. * * The variable is allocated and initialized with a default value * based on type item @a ti. For reference types the default value * is a null reference. At this point this does not work for generic * types (we need RTTI). * * @param run Runner object * @param ti Type of variable to create (type item) * @param rvar Place to store pointer to new variable */ void run_var_new(run_t *run, tdata_item_t *ti, rdata_var_t **rvar) { rdata_var_t *var; switch (ti->tic) { case tic_tprimitive: run_var_new_tprimitive(run, ti->u.tprimitive, rvar); break; case tic_tobject: case tic_tarray: run_var_new_null_ref(run, rvar); break; case tic_tdeleg: run_var_new_deleg(run, rvar); break; case tic_tebase: /* * One cannot declare variable of ebase type. It is just * type of expressions referring to enum types. */ assert(b_false); case tic_tenum: run_var_new_enum(run, ti->u.tenum, rvar); break; case tic_tfun: run_var_new_deleg(run, rvar); break; case tic_tvref: /* * XXX Need to obtain run-time value of type argument to * initialize variable properly. */ var = rdata_var_new(vc_int); var->u.int_v = rdata_int_new(); bigint_init(&var->u.int_v->value, 0); *rvar = var; break; case tic_ignore: assert(b_false); } } /** Construct a new variable of primitive type. * * The variable is allocated and initialized with a default value * based on primitive type item @a tprimitive. * * @param run Runner object * @param ti Primitive type of variable to create * @param rvar Place to store pointer to new variable */ static void run_var_new_tprimitive(run_t *run, tdata_primitive_t *tprimitive, rdata_var_t **rvar) { rdata_var_t *var; (void) run; /* Make compiler happy. */ var = NULL; switch (tprimitive->tpc) { case tpc_bool: var = rdata_var_new(vc_bool); var->u.bool_v = rdata_bool_new(); var->u.bool_v->value = b_false; break; case tpc_char: var = rdata_var_new(vc_char); var->u.char_v = rdata_char_new(); bigint_init(&var->u.char_v->value, 0); break; case tpc_int: var = rdata_var_new(vc_int); var->u.int_v = rdata_int_new(); bigint_init(&var->u.int_v->value, 0); break; case tpc_nil: assert(b_false); case tpc_string: var = rdata_var_new(vc_string); var->u.string_v = rdata_string_new(); var->u.string_v->value = ""; break; case tpc_resource: var = rdata_var_new(vc_resource); var->u.resource_v = rdata_resource_new(); var->u.resource_v->data = NULL; break; } *rvar = var; } /** Construct a new variable containing null reference. * * @param run Runner object * @param rvar Place to store pointer to new variable */ static void run_var_new_null_ref(run_t *run, rdata_var_t **rvar) { rdata_var_t *var; (void) run; /* Return null reference. */ var = rdata_var_new(vc_ref); var->u.ref_v = rdata_ref_new(); *rvar = var; } /** Construct a new variable containing invalid delegate. * * @param run Runner object * @param rvar Place to store pointer to new variable */ static void run_var_new_deleg(run_t *run, rdata_var_t **rvar) { rdata_var_t *var; (void) run; /* Return null reference. */ var = rdata_var_new(vc_deleg); var->u.deleg_v = rdata_deleg_new(); *rvar = var; } /** Construct a new variable containing default value of an enum type. * * @param run Runner object * @param rvar Place to store pointer to new variable */ static void run_var_new_enum(run_t *run, tdata_enum_t *tenum, rdata_var_t **rvar) { rdata_var_t *var; list_node_t *embr_n; stree_embr_t *embr; (void) run; /* Get first member of enum which will serve as default value. */ embr_n = list_first(&tenum->enum_d->members); assert(embr_n != NULL); embr = list_node_data(embr_n, stree_embr_t *); /* Return null reference. */ var = rdata_var_new(vc_enum); var->u.enum_v = rdata_enum_new(); var->u.enum_v->value = embr; *rvar = var; } /** Construct a new thread activation record. * * @param run Runner object * @return New thread AR. */ run_thread_ar_t *run_thread_ar_new(void) { run_thread_ar_t *thread_ar; thread_ar = calloc(1, sizeof(run_thread_ar_t)); if (thread_ar == NULL) { printf("Memory allocation failed.\n"); exit(1); } return thread_ar; } /** Allocate a new procedure activation record. * * @return New procedure AR. */ run_proc_ar_t *run_proc_ar_new(void) { run_proc_ar_t *proc_ar; proc_ar = calloc(1, sizeof(run_proc_ar_t)); if (proc_ar == NULL) { printf("Memory allocation failed.\n"); exit(1); } return proc_ar; } /** Deallocate a procedure activation record. * * @return New procedure AR. */ void run_proc_ar_delete(run_proc_ar_t *proc_ar) { assert(proc_ar != NULL); free(proc_ar); } /** Allocate a new block activation record. * * @param run Runner object * @return New block AR. */ run_block_ar_t *run_block_ar_new(void) { run_block_ar_t *block_ar; block_ar = calloc(1, sizeof(run_block_ar_t)); if (block_ar == NULL) { printf("Memory allocation failed.\n"); exit(1); } return block_ar; } /** Deallocate a new block activation record. * * @param run Runner object * @return New block AR. */ void run_block_ar_delete(run_block_ar_t *block_ar) { assert(block_ar != NULL); free(block_ar); }