#include #include "lib/mlrutil.h" #include "lib/mlr_globals.h" #include "containers/local_stack.h" // ================================================================ static local_stack_frame_t* _local_stack_alloc(int size, int ephemeral) { local_stack_frame_t* pframe = mlr_malloc_or_die(sizeof(local_stack_frame_t)); pframe->in_use = FALSE; pframe->ephemeral = ephemeral; pframe->size = size; pframe->subframe_base = 0; pframe->pvars = mlr_malloc_or_die(size * sizeof(local_stack_frame_entry_t)); for (int i = 0; i < size; i++) { local_stack_frame_entry_t* pentry = &pframe->pvars[i]; pentry->xvalue = mlhmmv_xvalue_wrap_terminal(mv_absent()); pentry->name = NULL; // Any type can be written here, unless otherwise specified by a typed definition pentry->type_mask = TYPE_MASK_ANY; } return pframe; } // ---------------------------------------------------------------- local_stack_frame_t* local_stack_frame_alloc(int size) { return _local_stack_alloc(size, FALSE); } // ---------------------------------------------------------------- void local_stack_frame_free(local_stack_frame_t* pframe) { if (pframe == NULL) return; for (int i = 0; i < pframe->size; i++) { mlhmmv_xvalue_free(&pframe->pvars[i].xvalue); } free(pframe->pvars); free(pframe); } // ---------------------------------------------------------------- local_stack_frame_t* local_stack_frame_enter(local_stack_frame_t* pframe) { if (!pframe->in_use) { pframe->in_use = TRUE; LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME NON-EPH ENTER %p %d\n", pframe, pframe->size)); return pframe; } else { local_stack_frame_t* prv = _local_stack_alloc(pframe->size, TRUE); LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME EPH ENTER %p/%p %d\n", pframe, prv, pframe->size)); prv->in_use = TRUE; return prv; } } // ---------------------------------------------------------------- void local_stack_frame_exit (local_stack_frame_t* pframe) { MLR_INTERNAL_CODING_ERROR_UNLESS(mlhmmv_xvalue_is_absent_and_nonterminal(&pframe->pvars[0].xvalue)); if (!pframe->ephemeral) { pframe->in_use = FALSE; LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME NON-EPH EXIT %p %d\n", pframe, pframe->size)); } else { local_stack_frame_free(pframe); LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME EPH EXIT %p %d\n", pframe, pframe->size)); } } // ================================================================ local_stack_t* local_stack_alloc() { local_stack_t* pstack = mlr_malloc_or_die(sizeof(local_stack_t)); pstack->pframes = sllv_alloc(); return pstack; } // ---------------------------------------------------------------- void local_stack_free(local_stack_t* pstack) { if (pstack == NULL) return; for (sllve_t* pe = pstack->pframes->phead; pe != NULL; pe = pe->pnext) { local_stack_frame_free(pe->pvvalue); } sllv_free(pstack->pframes); free(pstack); } // ---------------------------------------------------------------- void local_stack_push(local_stack_t* pstack, local_stack_frame_t* pframe) { sllv_push(pstack->pframes, pframe); } local_stack_frame_t* local_stack_pop(local_stack_t* pstack) { return sllv_pop(pstack->pframes); } // ---------------------------------------------------------------- mv_t local_stack_frame_ref_terminal_from_indexed(local_stack_frame_t* pframe, int vardef_frame_relative_index, sllmv_t* pmvkeys) { LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME %p GET %d\n", pframe, vardef_frame_relative_index)); LOCAL_STACK_BOUNDS_CHECK(pframe, "GET", FALSE, vardef_frame_relative_index); local_stack_frame_entry_t* pentry = &pframe->pvars[vardef_frame_relative_index]; mlhmmv_xvalue_t* pbase_xval = &pentry->xvalue; if (!(TYPE_MASK_MAP & pentry->type_mask)) { local_stack_frame_throw_type_mismatch_for_read(pentry); } #ifdef LOCAL_STACK_TRACE_ENABLE if (pbase_xval == NULL) { printf("VALUE IS NULL\n"); } else if (pbase_xval->is_terminal) { char* s = mv_alloc_format_val(&pbase_xval->terminal_mlrval); printf("VALUE IS %s\n", s); free(s); } else if (pbase_xval->pnext_level == NULL) { LOCAL_STACK_TRACE(printf("VALUE IS EMPTY\n")); } else { printf("VALUE IS:\n"); printf("PTR IS %p\n", pbase_xval->pnext_level); mlhmmv_level_print_stacked(pbase_xval->pnext_level, 0, TRUE, TRUE, "", stdout); } #endif // xxx this is a mess; clean it up. int error = 0; // Maybe null mlhmmv_xvalue_t* pxval; if (pmvkeys == NULL || pmvkeys->length == 0) { pxval = pbase_xval; } else { if (pbase_xval->is_terminal) { return mv_absent(); } else { pxval = mlhmmv_level_look_up_and_ref_xvalue(pbase_xval->pnext_level, pmvkeys, &error); } } if (pxval != NULL && pxval->is_terminal) { return pxval->terminal_mlrval; } else { return mv_absent(); } } // ---------------------------------------------------------------- mlhmmv_xvalue_t* local_stack_frame_ref_extended_from_indexed(local_stack_frame_t* pframe, int vardef_frame_relative_index, sllmv_t* pmvkeys) { LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME %p GET %d\n", pframe, vardef_frame_relative_index)); LOCAL_STACK_BOUNDS_CHECK(pframe, "GET", FALSE, vardef_frame_relative_index); local_stack_frame_entry_t* pentry = &pframe->pvars[vardef_frame_relative_index]; mlhmmv_xvalue_t* pmvalue = &pentry->xvalue; if (!(TYPE_MASK_MAP & pentry->type_mask)) { local_stack_frame_throw_type_mismatch_for_read(pentry); } #ifdef LOCAL_STACK_TRACE_ENABLE if (pmvalue == NULL) { printf("VALUE IS NULL\n"); } else if (pmvalue->is_terminal) { char* s = mv_alloc_format_val(&pmvalue->terminal_mlrval); printf("VALUE IS %s\n", s); free(s); } else if (pmvalue->pnext_level == NULL) { LOCAL_STACK_TRACE(printf("VALUE IS EMPTY\n")); } else { printf("VALUE IS:\n"); printf("PTR IS %p\n", pmvalue->pnext_level); mlhmmv_level_print_stacked(pmvalue->pnext_level, 0, TRUE, TRUE, "", stdout); } #endif if (pmvkeys == NULL) { // base-level access return pmvalue; } else { int error = 0; // Maybe null return mlhmmv_level_look_up_and_ref_xvalue(pmvalue->pnext_level, pmvkeys, &error); } } // ---------------------------------------------------------------- void local_stack_frame_define_terminal(local_stack_frame_t* pframe, char* variable_name, int vardef_frame_relative_index, int type_mask, mv_t val) { LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME %p SET %d\n", pframe, vardef_frame_relative_index)); LOCAL_STACK_BOUNDS_CHECK(pframe, "DEFINE", TRUE, vardef_frame_relative_index); local_stack_frame_entry_t* pentry = &pframe->pvars[vardef_frame_relative_index]; pentry->name = variable_name; // no strdup, for performance -- caller must ensure extent pentry->type_mask = type_mask; if (!(type_mask_from_mv(&val) & pentry->type_mask)) { local_stack_frame_throw_type_mismatch_for_write(pentry, &val); } mlhmmv_xvalue_free(&pentry->xvalue); if (mv_is_absent(&val)) { mv_free(&val); // xxx doc ownership semantics at header file } else { pentry->xvalue = mlhmmv_xvalue_wrap_terminal(val); // xxx deep-copy? } } // ---------------------------------------------------------------- void local_stack_frame_define_extended(local_stack_frame_t* pframe, char* variable_name, int vardef_frame_relative_index, int type_mask, mlhmmv_xvalue_t xval) { LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME %p SET %d\n", pframe, vardef_frame_relative_index)); LOCAL_STACK_BOUNDS_CHECK(pframe, "ASSIGN", TRUE, vardef_frame_relative_index); local_stack_frame_entry_t* pentry = &pframe->pvars[vardef_frame_relative_index]; pentry->name = variable_name; // no strdup, for performance -- caller must ensure extent pentry->type_mask = type_mask; if (xval.is_terminal) { if (!(type_mask_from_mv(&xval.terminal_mlrval) & pentry->type_mask)) { local_stack_frame_throw_type_mismatch_for_write(pentry, &xval.terminal_mlrval); } } else { if (!(TYPE_MASK_MAP & pentry->type_mask)) { local_stack_frame_throw_type_mismatch_for_write(pentry, &xval.terminal_mlrval); } } if (!mlhmmv_xvalue_is_absent_and_nonterminal(&xval)) { mlhmmv_xvalue_free(&pentry->xvalue); pentry->xvalue = xval; } } // ---------------------------------------------------------------- void local_stack_frame_assign_terminal_indexed(local_stack_frame_t* pframe, int vardef_frame_relative_index, sllmv_t* pmvkeys, mv_t terminal_value) { LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME %p SET %d\n", pframe, vardef_frame_relative_index)); LOCAL_STACK_BOUNDS_CHECK(pframe, "ASSIGN", TRUE, vardef_frame_relative_index); local_stack_frame_entry_t* pentry = &pframe->pvars[vardef_frame_relative_index]; if (!(TYPE_MASK_MAP & pentry->type_mask)) { local_stack_frame_throw_type_mismatch_for_write(pentry, &terminal_value); } mlhmmv_xvalue_t* pmvalue = &pentry->xvalue; if (pmvalue->is_terminal) { mv_free(&pmvalue->terminal_mlrval); *pmvalue = mlhmmv_xvalue_alloc_empty_map(); } mlhmmv_level_put_terminal(pmvalue->pnext_level, pmvkeys->phead, &terminal_value); LOCAL_STACK_TRACE(printf("VALUE IS:\n")); LOCAL_STACK_TRACE(mlhmmv_level_print_stacked(pmvalue->pnext_level, 0, TRUE, TRUE, "", stdout)); } // ---------------------------------------------------------------- void local_stack_frame_assign_extended_nonindexed(local_stack_frame_t* pframe, int vardef_frame_relative_index, mlhmmv_xvalue_t xval) { LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME %p SET %d\n", pframe, vardef_frame_relative_index)); LOCAL_STACK_BOUNDS_CHECK(pframe, "ASSIGN", TRUE, vardef_frame_relative_index); local_stack_frame_entry_t* pentry = &pframe->pvars[vardef_frame_relative_index]; if (xval.is_terminal) { if (!(type_mask_from_mv(&xval.terminal_mlrval) & pentry->type_mask)) { local_stack_frame_throw_type_mismatch_for_write(pentry, &xval.terminal_mlrval); } } else { if (!(TYPE_MASK_MAP & pentry->type_mask)) { local_stack_frame_throw_type_mismatch_for_write(pentry, &xval.terminal_mlrval); } } mlhmmv_xvalue_free(&pentry->xvalue); pentry->xvalue = xval; } // ---------------------------------------------------------------- void local_stack_frame_assign_extended_indexed(local_stack_frame_t* pframe, int vardef_frame_relative_index, sllmv_t* pmvkeys, mlhmmv_xvalue_t new_value) { LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME %p SET %d\n", pframe, vardef_frame_relative_index)); LOCAL_STACK_BOUNDS_CHECK(pframe, "ASSIGN", TRUE, vardef_frame_relative_index); local_stack_frame_entry_t* pentry = &pframe->pvars[vardef_frame_relative_index]; if (!(TYPE_MASK_MAP & pentry->type_mask)) { local_stack_frame_throw_type_xmismatch_for_write(pentry, &new_value); } mlhmmv_xvalue_t* pmvalue = &pentry->xvalue; if (pmvalue->is_terminal) { mv_free(&pmvalue->terminal_mlrval); *pmvalue = mlhmmv_xvalue_alloc_empty_map(); } mlhmmv_level_put_xvalue(pmvalue->pnext_level, pmvkeys->phead, &new_value); LOCAL_STACK_TRACE(printf("VALUE IS:\n")); LOCAL_STACK_TRACE(mlhmmv_level_print_stacked(pmvalue->pnext_level, 0, TRUE, TRUE, "", stdout)); } // ---------------------------------------------------------------- static int local_stack_bounds_check_announce_first_call = TRUE; void local_stack_bounds_check(local_stack_frame_t* pframe, char* op, int set, int vardef_frame_relative_index) { if (local_stack_bounds_check_announce_first_call) { fprintf(stderr, "%s: local-stack bounds-checking is enabled\n", MLR_GLOBALS.bargv0); local_stack_bounds_check_announce_first_call = FALSE; } if (vardef_frame_relative_index < 0) { fprintf(stderr, "OP=%s FRAME=%p IDX=%d/%d STACK UNDERFLOW\n", op, pframe, vardef_frame_relative_index, pframe->size); exit(1); } if (set && vardef_frame_relative_index == 0) { fprintf(stderr, "OP=%s FRAME=%p IDX=%d/%d ABSENT WRITE\n", op, pframe, vardef_frame_relative_index, pframe->size); exit(1); } if (vardef_frame_relative_index >= pframe->size) { fprintf(stderr, "OP=%s FRAME=%p IDX=%d/%d STACK OVERFLOW\n", op, pframe, vardef_frame_relative_index, pframe->size); exit(1); } } // ---------------------------------------------------------------- void local_stack_frame_throw_type_mismatch_for_write(local_stack_frame_entry_t* pentry, mv_t* pval) { MLR_INTERNAL_CODING_ERROR_IF(pentry->name == NULL); char* sval = mv_alloc_format_val_quoting_strings(pval); fprintf(stderr, "%s: %s type assertion for variable %s unmet by value %s with type %s.\n", MLR_GLOBALS.bargv0, type_mask_to_desc(pentry->type_mask), pentry->name, sval, mt_describe_type_simple(pval->type)); free(sval); exit(1); } void local_stack_frame_throw_type_xmismatch_for_write(local_stack_frame_entry_t* pentry, mlhmmv_xvalue_t* pxval) { MLR_INTERNAL_CODING_ERROR_IF(pentry->name == NULL); char* sval = mv_alloc_format_val_quoting_strings(&pxval->terminal_mlrval); // xxx temp -- maybe not terminal fprintf(stderr, "%s: %s type assertion for variable %s unmet by value %s with type %s.\n", MLR_GLOBALS.bargv0, type_mask_to_desc(pentry->type_mask), pentry->name, sval, mlhmmv_xvalue_describe_type_simple(pxval)); free(sval); exit(1); } // ---------------------------------------------------------------- void local_stack_frame_throw_type_mismatch_for_read(local_stack_frame_entry_t* pentry) { MLR_INTERNAL_CODING_ERROR_IF(pentry->name == NULL); fprintf(stderr, "%s: %s type assertion for variable %s unmet on read.\n", MLR_GLOBALS.bargv0, type_mask_to_desc(pentry->type_mask), pentry->name); exit(1); } void local_stack_frame_throw_type_xmismatch_for_read(local_stack_frame_entry_t* pentry) { MLR_INTERNAL_CODING_ERROR_IF(pentry->name == NULL); fprintf(stderr, "%s: %s type assertion for variable %s unmet on read.\n", MLR_GLOBALS.bargv0, type_mask_to_desc(pentry->type_mask), pentry->name); exit(1); }