void lvzwr_fini(zshow_out *out, int t) { int4 size; mval local; mname_entry temp_key; ht_ent_mname *tabent; mident_fixed m; zwr_output = out; assert(lvzwrite_block); if (zwr_patrn_mident == lvzwrite_block->zwr_intype) { /* Mident specified for "pattern" (fixed name, no pattern) */ size = (lvzwrite_block->pat->str.len <= MAX_MIDENT_LEN) ? lvzwrite_block->pat->str.len : MAX_MIDENT_LEN; temp_key.var_name = lvzwrite_block->pat->str; COMPUTE_HASH_MNAME(&temp_key); tabent = lookup_hashtab_mname(&curr_symval->h_symtab, &temp_key); if (!tabent || !LV_IS_VAL_DEFINED(tabent->value) && !LV_HAS_CHILD(tabent->value)) { lvzwrite_block->subsc_count = 0; rts_error(VARLSTCNT(4) ERR_UNDEF, 2, size, lvzwrite_block->pat->str.addr); } else { lvzwrite_block->curr_name = &tabent->key.var_name; lvzwr_var(((lv_val *)tabent->value), 0); } } else { /* mval specified for character "pattern" (pattern matching) */ assert(zwr_patrn_mval == lvzwrite_block->zwr_intype); memset(m.c, 0, SIZEOF(m.c)); local.mvtype = MV_STR; local.str.addr = &m.c[0]; local.str.len = 1; m.c[0] = '%'; /* Starting variable name for search (first possible varname) */ lvzwrite_block->fixed = FALSE; while (local.str.len) { if (do_pattern(&local, lvzwrite_block->pat)) { memset(&m.c[local.str.len], 0, SIZEOF(m.c) - local.str.len); temp_key.var_name = local.str; COMPUTE_HASH_MNAME(&temp_key); if (NULL != (tabent = lookup_hashtab_mname(&curr_symval->h_symtab, &temp_key))) { lvzwrite_block->curr_name = &tabent->key.var_name; lvzwr_var(((lv_val *)tabent->value), 0); } } op_fnlvname(&local, TRUE, &local); assert(local.str.len <= MAX_MIDENT_LEN); memcpy(&m.c[0], local.str.addr, local.str.len); local.str.addr = &m.c[0]; } } lvzwrite_block->curr_subsc = lvzwrite_block->subsc_count = 0; return; }
/* Operation - Kill an alias (unsubscripted variable) * * Look it up in the hash table and remove the pointer to the lv_val to destroy the * alias association. Will need to do this in any previous symtabs this var is in * as well. */ void op_killalias(int srcindx) { ht_ent_mname *tabent; mname_entry *varname; lv_val *lv; int4 symvlvl; active_lv = (lv_val *)NULL; /* if we get here, subscript set was successful. clear active_lv to avoid later * cleanup problems. */ varname = &(((mname_entry *)frame_pointer->vartab_ptr)[srcindx]); tabent = lookup_hashtab_mname(&curr_symval->h_symtab, varname); /* Retrieve hash tab entry this var */ if (tabent) { lv = (lv_val *)tabent->value; assert(lv); assert(LV_IS_BASE_VAR(lv)); symvlvl = (LV_GET_SYMVAL(lv))->symvlvl; /* Clone var if necessary */ if (dollar_tlevel && (NULL != lv->tp_var) && !lv->tp_var->var_cloned) TP_VAR_CLONE(lv); /* Decrement reference count and cleanup if necessary */ DECR_BASE_REF(tabent, lv, TRUE); MARK_ALIAS_ACTIVE(symvlvl); /* Mark this entry as aliasly active now */ } /* Else var has no hastable entry so this is a NOOP */ }
void op_indglvn(mval *v,mval *dst) { bool rval; mstr *obj, object; oprtype x; lv_val *a; icode_str indir_src; lv_val *lv; var_tabent targ_key; ht_ent_mname *tabent; error_def(ERR_INDMAXNEST); error_def(ERR_UNDEF); MV_FORCE_STR(v); indir_src.str = v->str; indir_src.code = indir_glvn; if (NULL == (obj = cache_get(&indir_src))) { if (valid_mname(&v->str)) { targ_key.var_name = v->str; COMPUTE_HASH_MNAME(&targ_key); tabent = lookup_hashtab_mname(&curr_symval->h_symtab, &targ_key); assert(NULL == tabent || NULL != tabent->value); if (!tabent || !MV_DEFINED(&((lv_val *)tabent->value)->v)) { if (undef_inhibit) { *dst = literal_null; return; } else rts_error(VARLSTCNT(4) ERR_UNDEF, 2, v->str.len, v->str.addr); } a = (lv_val *)tabent->value; *dst = a->v; return; } comp_init(&v->str); rval = glvn(&x); if (comp_fini(rval, &object, OC_IRETMVAL, &x, v->str.len)) { indir_src.str.addr = v->str.addr; cache_put(&indir_src, &object); *ind_result_sp++ = dst; if (ind_result_sp >= ind_result_top) rts_error(VARLSTCNT(1) ERR_INDMAXNEST); comp_indr(&object); } } else { *ind_result_sp++ = dst; if (ind_result_sp >= ind_result_top) rts_error(VARLSTCNT(1) ERR_INDMAXNEST); comp_indr(obj); } }
/* Restore given local variable from supplied TP restore entry into given symval. Note lvscan_anchor will only be non-NULL * for the final level we are restoring (but not unwinding). We don't need to restore counters for any vars except the * very last level. * * The return code is only used when unrolling the M stack runs into a trigger base frame which must be unrolled * by gtm_trigger. A non-zero return code signals to tp_unwind() that it needs to rethrow the tprestart error. */ int tp_unwind_restlv(lv_val *curr_lv, lv_val *save_lv, tp_var *restore_ent, lvscan_blk **lvscan_anchor, int *tprestart_rc) { ht_ent_mname *tabent; lv_val *inuse_lv; int elemindx; mv_stent *mvc; lvscan_blk *lvscan, *newlvscan; lvTree *lvt_child; boolean_t var_cloned; assert(curr_lv); assert(LV_IS_BASE_VAR(curr_lv)); assert(curr_lv->tp_var); DBGRFCT((stderr, "\ntp_unwind_restlv: Entered for varname: '%.*s' curr_lv: 0x"lvaddr" save_lv: 0x"lvaddr"\n", restore_ent->key.var_name.len, restore_ent->key.var_name.addr, curr_lv, save_lv)); DBGRFCT((stderr, "tp_unwind_restlv: tp_pointer/current: fp: 0x"lvaddr"/0x"lvaddr" mvc: 0x"lvaddr"/0x"lvaddr " symval: 0x"lvaddr"/0x"lvaddr"\n", tp_pointer->fp, frame_pointer, tp_pointer->mvc, mv_chain, tp_pointer->sym, curr_symval)); /* First get the stack in the position where we can actually process this entry. Need to make sure we are processing * the symbol table we need to be processing so unwind enough stuff to get there. */ if (curr_symval != tp_pointer->sym) { /* Unwind as many stackframes as are necessary up to the max */ while((curr_symval != tp_pointer->sym) && (frame_pointer < tp_pointer->fp)) { # ifdef GTM_TRIGGER if (SFT_TRIGR & frame_pointer->type) { /* We have encountered a trigger base frame. We cannot unroll it because there are C frames * associated with it so we must interrupt this tp_restart and return to gtm_trigger() so * it can unroll the base frame and rethrow the error to properly unroll the C stack. */ *tprestart_rc = ERR_TPRETRY; tprestart_state = TPRESTART_STATE_TPUNW; DBGTRIGR((stderr, "tp_unwind: Encountered trigger base frame during M-stack unwind - " "rethrowing\n")); return -1; } # endif op_unwind(); } if (curr_symval != tp_pointer->sym) { /* Unwind as many mv_stents as are necessary up to the max */ mvc = mv_chain; while((curr_symval != tp_pointer->sym) && (mvc < tp_pointer->mvc)) { unw_mv_ent(mvc); mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc); } mv_chain = mvc; /* Final check */ assertpro(curr_symval == tp_pointer->sym); } } var_cloned = curr_lv->tp_var->var_cloned; if (var_cloned) { /* Var/tree has been copied (and modified) -- see about restoring it */ DBGRFCT((stderr, "\ntp_unwind_restlv: curr_lv was modified and cloned -- needs restoration\n")); if (NULL != restore_ent->key.var_name.addr) { /* Restore data into a named variable (hash table entry) * Step 1 -- find its hash table address to see what lv_val is there now. */ tabent = lookup_hashtab_mname(&((tp_pointer->sym)->h_symtab), &restore_ent->key); assert(tabent); /* Step 2 -- If lv_val is NOT the same as it was, then we must replace the lv_val * currently in use. Decrement its use count (which will delete it and the tree if * it is no longer used) and replace with desired previous lv_val whose use count * was incremented when it was saved. */ if (curr_lv != (inuse_lv = (lv_val *)tabent->value)) /* Note assignment */ { if (inuse_lv) DECR_BASE_REF_RQ(tabent, inuse_lv, FALSE); DBGRFCT((stderr, "tp_unwind: hte 0x"lvaddr" being reset from 0x"lvaddr" to 0x"lvaddr"\n", tabent, tabent->value, curr_lv)); tabent->value = (void *)curr_lv; INCR_TREFCNT(curr_lv); /* Back in the hash table, bump its reference */ } } /* Else, if restoring orphaned data, just prune the old var and copy in the saved tree (if one existed) */ /* Step 3 -- We have the correct lv_val in the hash table now but it has the wrong value. * Get rid of its current tree if any. */ if (lvt_child = LV_GET_CHILD(curr_lv)) /* Note assignment */ { DBGRFCT((stderr, "\ntp_unwind_restlv: Killing children of curr_lv 0x"lvaddr"\n", curr_lv)); assert((lvTreeNode *)curr_lv == LVT_PARENT(lvt_child)); LV_CHILD(curr_lv) = NULL; /* prevent recursion due to alias containers */ lv_killarray(lvt_child, FALSE); } /* Step 4: Copy in the needed fields from the saved flavor lv_val back to curr_lv. * Preserve the ref counts of the current var since the copy's ref counts have not been kept up to date. */ DBGRFCT((stderr, "\ntp_unwind_restlv: Restoring value of lv 0x"lvaddr" back into lv 0x"lvaddr"\n", save_lv, curr_lv)); /* The following is optimized to do the initialization of just the needed structure members. For that it assumes a * particular "lv_val" structure layout. The assumed layout is asserted so any changes to the layout will * automatically show an issue here and cause the below initialization to be accordingly reworked. */ assert(0 == OFFSETOF(lv_val, v)); assert(OFFSETOF(lv_val, v) + SIZEOF(curr_lv->v) == OFFSETOF(lv_val, ptrs)); assert(OFFSETOF(lv_val, ptrs) + SIZEOF(curr_lv->ptrs) == OFFSETOF(lv_val, stats)); assert(OFFSETOF(lv_val, stats) + SIZEOF(curr_lv->stats) == OFFSETOF(lv_val, has_aliascont)); assert(OFFSETOF(lv_val, has_aliascont) + SIZEOF(curr_lv->has_aliascont) == OFFSETOF(lv_val, lvmon_mark)); assert(OFFSETOF(lv_val, lvmon_mark) + SIZEOF(curr_lv->lvmon_mark) == OFFSETOF(lv_val, tp_var)); assert(OFFSETOF(lv_val, tp_var) + SIZEOF(curr_lv->tp_var) == SIZEOF(lv_val)); /* save_lv -> curr_lv Copy begin */ curr_lv->v = save_lv->v; curr_lv->ptrs = save_lv->ptrs; assert(0 < curr_lv->stats.trefcnt); /* No need to copy "stats" as curr_lv is more uptodate */ assert(0 < curr_lv->stats.crefcnt); assert(8 == (OFFSETOF(lv_val, tp_var) - OFFSETOF(lv_val, has_aliascont))); curr_lv->has_aliascont = save_lv->has_aliascont; DBGALS_ONLY(curr_lv->lvmon_mark = save_lv->has_aliascont); assert(save_lv->tp_var == curr_lv->tp_var); /* no need to copy this field */ /* save_lv -> curr_lv Copy done */ /* Some fixup may need to be done if the variable was cloned (and thus moved around) */ curr_lv->tp_var->var_cloned = FALSE; if (lvt_child = LV_GET_CHILD(curr_lv)) { /* Some pointer fix up needs to be done since the owner of the restored tree changed */ assert(LVT_PARENT(lvt_child) == ((lvTreeNode *)curr_lv->tp_var->save_value)); LV_CHILD(save_lv) = NULL; /* now that curr_lv->tp_var->var_cloned has been reset */ LVT_PARENT(lvt_child) = (lvTreeNode *)curr_lv; if (curr_lv->has_aliascont && (NULL != lvscan_anchor)) { /* Some ref counts need to be restored for arrays this tree points to -- but only if the * array contains pointers (alias containers). */ DBGRFCT((stderr, "\ntp_unwind_restlv: Putting lv 0x:"lvaddr" on the lvscan list\n", curr_lv)); /* This array needs to have container pointer target reference counts reestablished. Record * the lv so this can happen after all vars are restored. */ lvscan = *lvscan_anchor; elemindx = lvscan->elemcnt++; /* Note post increment so elemindx has minus-one value */ if (ARY_SCNCNTNR_MAX < elemindx) { /* Present block is full so allocate a new one and chain it on */ lvscan->elemcnt--; /* New element ended up not being in that block.. */ newlvscan = (lvscan_blk *)malloc(SIZEOF(lvscan_blk)); newlvscan->next = lvscan; newlvscan->elemcnt = 1; /* Going to use first one *now* */ elemindx = 0; *lvscan_anchor = newlvscan; lvscan = newlvscan; } assert((ARY_SCNCNTNR_MAX >= elemindx) && (0 <= elemindx)); lvscan->ary_scncntnr[elemindx] = curr_lv; } } } else { DBGRFCT((stderr, "\ntp_unwind_restlv: curr_lv was NOT modified or cloned\n")); assert(NULL == LV_CHILD(save_lv)); assert(!save_lv->tp_var->var_cloned); /* We know that the subscript array underneath curr_lv did not change since saving it into save_lv. But the * unsubscripted lv could have changed (have no way of checking if that is the case) so restore it unconditionally. */ curr_lv->v = save_lv->v; /* No need to copy "save_lv->ptrs" as "ptrs" contains 2 fields both of which are already correct in "curr_lv" */ assert(save_lv->ptrs.val_ent.parent.sym == curr_lv->ptrs.val_ent.parent.sym); assert(NULL == save_lv->ptrs.val_ent.children); /* No need to copy "save_lv->stats" as "curr_lv->stats" is more uptodate */ assert(save_lv->has_aliascont == curr_lv->has_aliascont); /* no need to copy this field */ assert(save_lv->lvmon_mark == curr_lv->lvmon_mark); /* no need to copy this field */ assert(save_lv->tp_var == curr_lv->tp_var); /* no need to copy this field */ } if (NULL == lvscan_anchor) /* Means this is completely unwinding a nested level so we need to reset the tstartcycle in this * lvval so it gets handled correctly when this lv is encountered again after the restart completes. */ curr_lv->stats.tstartcycle = 0; return 0; }
void view_arg_convert(viewtab_entry *vtp, int vtp_parm, mval *parm, viewparm *parmblk, boolean_t is_dollar_view) { static int4 first_time = TRUE; char *cptr; char *strtokptr; gd_binding *gd_map; gd_region *gd_reg_start, *r_ptr, *r_top; gvnh_reg_t *gvnh_reg; gvnh_spanreg_t *gvspan; gv_namehead *tmp_gvt; ht_ent_mname *tabent; int n, reg_index; mident_fixed lcl_buff; mname_entry gvent, lvent; mstr namestr, tmpstr; unsigned char *c, *c_top, *dst, *dst_top, global_names[1024], *nextsrc, *src, *src_top, stashed, y; switch (vtp_parm) { case VTP_NULL: if (parm != 0) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword); break; case (VTP_NULL | VTP_VALUE): if (NULL == parm) { parmblk->value = (mval *)&literal_one; break; } /* caution: fall through */ case VTP_VALUE: if (NULL == parm) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword); parmblk->value = parm; break; case (VTP_NULL | VTP_DBREGION): if (!is_dollar_view && ((NULL == parm) || ((1 == parm->str.len) && ('*' == *parm->str.addr)))) { parmblk->gv_ptr = NULL; break; } /* caution: fall through */ case VTP_DBREGION: if (NULL == parm) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword); if (!gd_header) /* IF GD_HEADER ==0 THEN OPEN GBLDIR */ gvinit(); r_ptr = gd_header->regions; if (!parm->str.len && vtp->keycode == VTK_GVNEXT) /* "" => 1st region */ parmblk->gv_ptr = r_ptr; else { for (cptr = parm->str.addr, n = 0; n < parm->str.len; cptr++, n++) lcl_buff.c[n] = TOUPPER(*cptr); /* Region names are upper-case ASCII */ namestr.len = n; namestr.addr = &lcl_buff.c[0]; for (r_top = r_ptr + gd_header->n_regions; ; r_ptr++) { if (r_ptr >= r_top) { format2zwr((sm_uc_ptr_t)parm->str.addr, parm->str.len, global_names, &n); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NOREGION,2, n, global_names); } tmpstr.len = r_ptr->rname_len; tmpstr.addr = (char *)r_ptr->rname; MSTR_CMP(tmpstr, namestr, n); if (0 == n) break; } parmblk->gv_ptr = r_ptr; } break; case VTP_DBKEY: if (NULL == parm) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword); if (!parm->str.len) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NOTGBL, 2, parm->str.len, NULL); if (!gd_header) /* IF GD_HEADER ==0 THEN OPEN GBLDIR */ gvinit(); c = (unsigned char *)parm->str.addr; if ('^' != *c) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NOTGBL, 2, parm->str.len, c); c_top = c + parm->str.len; c++; /* skip initial '^' */ parmblk->str.addr = (char *)c; for ( ; (c < c_top) && ('(' != *c); c++) ; parmblk->str.len = (char *)c - parmblk->str.addr; if (MAX_MIDENT_LEN < parmblk->str.len) parmblk->str.len = MAX_MIDENT_LEN; if (!valid_mname(&parmblk->str)) { format2zwr((sm_uc_ptr_t)parm->str.addr, parm->str.len, global_names, &n); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWGVN, 2, n, global_names); } break; case VTP_RTNAME: if (NULL == parm) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword); memset(&parmblk->ident.c[0], 0, SIZEOF(parmblk->ident)); if (parm->str.len > 0) memcpy(&parmblk->ident.c[0], parm->str.addr, (parm->str.len <= MAX_MIDENT_LEN ? parm->str.len : MAX_MIDENT_LEN)); break; case VTP_NULL | VTP_DBKEYLIST: if (NULL == parm || 0 == parm->str.len) { parmblk->ni_list.gvnh_list = NULL; parmblk->ni_list.type = NOISOLATION_NULL; break; } /* caution : explicit fall through */ case VTP_DBKEYLIST: if (NULL == parm) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword); if (!gd_header) gvinit(); if (first_time) { noisolation_buddy_list = (buddy_list *)malloc(SIZEOF(buddy_list)); initialize_list(noisolation_buddy_list, SIZEOF(noisolation_element), NOISOLATION_INIT_ALLOC); gvt_pending_buddy_list = (buddy_list *)malloc(SIZEOF(buddy_list)); initialize_list(gvt_pending_buddy_list, SIZEOF(gvt_container), NOISOLATION_INIT_ALLOC); first_time = FALSE; } assertpro(SIZEOF(global_names) > parm->str.len); tmpstr.len = parm->str.len; /* we need to change len and should not change parm->str, so take a copy */ tmpstr.addr = parm->str.addr; if (0 != tmpstr.len) { switch (*tmpstr.addr) { case '+' : parmblk->ni_list.type = NOISOLATION_PLUS; tmpstr.addr++; tmpstr.len--; break; case '-' : parmblk->ni_list.type = NOISOLATION_MINUS; tmpstr.addr++; tmpstr.len--; break; default : parmblk->ni_list.type = NOISOLATION_NULL; break; } if (!tmpstr.len) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWGVN, 2, tmpstr.len, NULL); memcpy(global_names, tmpstr.addr, tmpstr.len); global_names[tmpstr.len] = '\0'; src = (unsigned char *)STRTOK_R((char *)global_names, ",", &strtokptr); REINITIALIZE_LIST(noisolation_buddy_list); /* reinitialize the noisolation buddy_list */ parmblk->ni_list.gvnh_list = NULL; for ( ; src < &global_names[tmpstr.len + 1]; src = nextsrc) { nextsrc = (unsigned char *)STRTOK_R(NULL, ",", &strtokptr); if (NULL == nextsrc) nextsrc = &global_names[tmpstr.len + 1]; if (nextsrc - src >= 2 && '^' == *src) { namestr.addr = (char *)src + 1; /* skip initial '^' */ namestr.len = INTCAST(nextsrc - src - 2); /* don't count initial ^ and trailing 0 */ if (namestr.len > MAX_MIDENT_LEN) namestr.len = MAX_MIDENT_LEN; if (valid_mname(&namestr)) { memcpy(&lcl_buff.c[0], namestr.addr, namestr.len); gvent.var_name.len = namestr.len; } else { memcpy(&lcl_buff.c[0], src, nextsrc - src - 1); format2zwr((sm_uc_ptr_t)&lcl_buff.c, nextsrc - src - 1, global_names, &n); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWGVN, 2, n, global_names); } } else { memcpy(&lcl_buff.c[0], src, nextsrc - src - 1); format2zwr((sm_uc_ptr_t)&lcl_buff.c, nextsrc - src - 1, global_names, &n); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWGVN, 2, n, global_names); } tmp_gvt = NULL; gvent.var_name.addr = &lcl_buff.c[0]; COMPUTE_HASH_MNAME(&gvent); if (NULL != (tabent = lookup_hashtab_mname(gd_header->tab_ptr, &gvent))) { gvnh_reg = (gvnh_reg_t *)tabent->value; assert(NULL != gvnh_reg); tmp_gvt = gvnh_reg->gvt; } else { gd_map = gv_srch_map(gd_header, gvent.var_name.addr, gvent.var_name.len, SKIP_BASEDB_OPEN_FALSE); r_ptr = gd_map->reg.addr; tmp_gvt = (gv_namehead *)targ_alloc(r_ptr->max_key_size, &gvent, r_ptr); GVNH_REG_INIT(gd_header, gd_header->tab_ptr, gd_map, tmp_gvt, r_ptr, gvnh_reg, tabent); /* In case of a global spanning multiple regions, the gvt pointer corresponding to * the region where the unsubscripted global reference maps to is stored in TWO * locations (one in gvnh_reg->gvspan->gvt_array[index] and one in gvnh_reg->gvt. * So pass in both these pointer addresses to be stored in the pending list in * case this gvt gets reallocated (due to different keysizes between gld and db). */ if (NULL == (gvspan = gvnh_reg->gvspan)) { ADD_TO_GVT_PENDING_LIST_IF_REG_NOT_OPEN(r_ptr, &gvnh_reg->gvt, NULL); } else { gd_reg_start = &gd_header->regions[0]; GET_REG_INDEX(gd_header, gd_reg_start, r_ptr, reg_index); /* the above sets "reg_index" */ assert(reg_index >= gvspan->min_reg_index); assert(reg_index <= gvspan->max_reg_index); reg_index -= gvspan->min_reg_index; ADD_TO_GVT_PENDING_LIST_IF_REG_NOT_OPEN(r_ptr, &gvspan->gvt_array[reg_index], &gvnh_reg->gvt); } } ADD_GVT_TO_VIEW_NOISOLATION_LIST(tmp_gvt, parmblk); if (!is_dollar_view && (NULL != gvnh_reg->gvspan)) { /* Global spans multiple regions. Make sure gv_targets corresponding to ALL * spanned regions are allocated so NOISOLATION status can be set in all of * them even if the corresponding regions are not open yet. Do this only for * VIEW "NOISOLATION" commands which change the noisolation characteristic. * $VIEW("NOISOLATION") only examines the characteristics and so no need to * allocate all the gv-targets in that case. Just one is enough. */ gvnh_spanreg_subs_gvt_init(gvnh_reg, gd_header, parmblk); } } } else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWGVN, 2, tmpstr.len, tmpstr.addr); break; case VTP_LVN: if (NULL == parm) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword); if (0 < parm->str.len) { lvent.var_name.addr = parm->str.addr; lvent.var_name.len = parm->str.len; if (lvent.var_name.len > MAX_MIDENT_LEN) lvent.var_name.len = MAX_MIDENT_LEN; if (!valid_mname(&lvent.var_name)) { format2zwr((sm_uc_ptr_t)parm->str.addr, parm->str.len, global_names, &n); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWLVN, 2, n, global_names); } } else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWLVN, 2, parm->str.len, parm->str.addr); /* Now look up the name.. */ COMPUTE_HASH_MNAME(&lvent); if ((tabent = lookup_hashtab_mname(&curr_symval->h_symtab, &lvent)) && (NULL != tabent->value)) parmblk->value = (mval *)tabent->value; /* Return lv_val ptr */ else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWLVN, 2, parm->str.len, parm->str.addr); break; default: assertpro(FALSE && vtp_parm); } }
/* Restore given local variable from supplied TP restore entry into given symval. Note lvscan_anchor will only be non-NULL * for the final level we are restoring (but not unwinding). We don't need to restore counters for any vars except the * very last level. * * The return code is only used when unrolling the M stack runs into a trigger base frame which must be unrolled * by gtm_trigger. A non-zero return code signals to tp_unwind() that it needs to rethrow the tprestart error. */ int tp_unwind_restlv(lv_val *curr_lv, lv_val *save_lv, tp_var *restore_ent, lvscan_blk **lvscan_anchor, int *tprestart_rc) { ht_ent_mname *tabent; lv_val *inuse_lv; int elemindx; mv_stent *mvc; lvscan_blk *lvscan, *newlvscan; lvTree *lvt_child; boolean_t var_cloned; assert(curr_lv); assert(LV_IS_BASE_VAR(curr_lv)); assert(curr_lv->tp_var); DBGRFCT((stderr, "\ntp_unwind_restlv: Entered for varname: '%.*s' curr_lv: 0x"lvaddr" save_lv: 0x"lvaddr"\n", restore_ent->key.var_name.len, restore_ent->key.var_name.addr, curr_lv, save_lv)); DBGRFCT((stderr, "tp_unwind_restlv: tp_pointer/current: fp: 0x"lvaddr"/0x"lvaddr" mvc: 0x"lvaddr"/0x"lvaddr " symval: 0x"lvaddr"/0x"lvaddr"\n", tp_pointer->fp, frame_pointer, tp_pointer->mvc, mv_chain, tp_pointer->sym, curr_symval)); /* First get the stack in the position where we can actually process this entry. Need to make sure we are processing * the symbol table we need to be processing so unwind enough stuff to get there. */ if (curr_symval != tp_pointer->sym) { /* Unwind as many stackframes as are necessary up to the max */ while(curr_symval != tp_pointer->sym && frame_pointer < tp_pointer->fp) { # ifdef GTM_TRIGGER if (SFT_TRIGR & frame_pointer->type) { /* We have encountered a trigger base frame. We cannot unroll it because there are C frames * associated with it so we must interrupt this tp_restart and return to gtm_trigger() so * it can unroll the base frame and rethrow the error to properly unroll the C stack. */ *tprestart_rc = ERR_TPRETRY; tprestart_state = TPRESTART_STATE_TPUNW; DBGTRIGR((stderr, "tp_unwind: Encountered trigger base frame during M-stack unwind - " "rethrowing\n")); return -1; } # endif op_unwind(); } if (curr_symval != tp_pointer->sym) { /* Unwind as many mv_stents as are necessary up to the max */ mvc = mv_chain; while(curr_symval != tp_pointer->sym && mvc < tp_pointer->mvc) { unw_mv_ent(mvc); mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc); } mv_chain = mvc; /* Final check */ if (curr_symval != tp_pointer->sym) GTMASSERT; } } var_cloned = curr_lv->tp_var->var_cloned; if (var_cloned) { /* Var/tree has been copied (and modified) -- see about restoring it */ DBGRFCT((stderr, "\ntp_unwind_restlv: curr_lv was modified and cloned -- needs restoration\n")); if (NULL != restore_ent->key.var_name.addr) { /* Restore data into a named variable (hash table entry) * Step 1 -- find its hash table address to see what lv_val is there now. */ tabent = lookup_hashtab_mname(&((tp_pointer->sym)->h_symtab), &restore_ent->key); assert(tabent); /* Step 2 -- If lv_val is NOT the same as it was, then we must replace the lv_val * currently in use. Decrement its use count (which will delete it and the tree if * it is no longer used) and replace with desired previous lv_val whose use count * was incremented when it was saved. */ if (curr_lv != (inuse_lv = (lv_val *)tabent->value)) { if (inuse_lv) DECR_BASE_REF_RQ(tabent, inuse_lv, FALSE); DBGRFCT((stderr, "tp_unwind: hte 0x"lvaddr" being reset from 0x"lvaddr" to 0x"lvaddr"\n", tabent, tabent->value, curr_lv)); tabent->value = (void *)curr_lv; INCR_TREFCNT(curr_lv); /* Back in the hash table, bump its reference */ } } /* Else, if restoring orphaned data, just prune the old var and copy in the saved tree (if one existed) */ /* Step 3 -- We have the correct lv_val in the hash table now but it has the wrong value. * Get rid of its current tree if any. */ if (lvt_child = LV_GET_CHILD(curr_lv)) /* Note assignment */ { DBGRFCT((stderr, "\ntp_unwind_restlv: Killing children of curr_lv 0x"lvaddr"\n", curr_lv)); assert((lvTreeNode *)curr_lv == LVT_PARENT(lvt_child)); LV_CHILD(curr_lv) = NULL; /* prevent recursion due to alias containers */ lv_killarray(lvt_child, FALSE); } /* Step 4: Copy in the needed fields from the saved flavor lv_val back to curr_lv. * Preserve the ref counts of the current var since the copy's ref counts have not been kept up to date. */ DBGRFCT((stderr, "\ntp_unwind_restlv: Restoring value of lv 0x"lvaddr" back into lv 0x"lvaddr"\n", save_lv, curr_lv)); /* The following is optimized to do the initialization of just the needed structure members. For that it assumes a * particular "lv_val" structure layout. The assumed layout is asserted so any changes to the layout will * automatically show an issue here and cause the below initialization to be accordingly reworked. */ assert(0 == OFFSETOF(lv_val, v)); assert(OFFSETOF(lv_val, v) + SIZEOF(curr_lv->v) == OFFSETOF(lv_val, ptrs)); assert(OFFSETOF(lv_val, ptrs) + SIZEOF(curr_lv->ptrs) == OFFSETOF(lv_val, stats)); assert(OFFSETOF(lv_val, stats) + SIZEOF(curr_lv->stats) == OFFSETOF(lv_val, has_aliascont)); assert(OFFSETOF(lv_val, has_aliascont) + SIZEOF(curr_lv->has_aliascont) == OFFSETOF(lv_val, lvmon_mark)); assert(OFFSETOF(lv_val, lvmon_mark) + SIZEOF(curr_lv->lvmon_mark) == OFFSETOF(lv_val, tp_var)); assert(OFFSETOF(lv_val, tp_var) + SIZEOF(curr_lv->tp_var) == SIZEOF(lv_val)); /* save_lv -> curr_lv Copy begin */ curr_lv->v = save_lv->v; curr_lv->ptrs = save_lv->ptrs; assert(0 < curr_lv->stats.trefcnt); /* No need to copy "stats" as curr_lv is more uptodate */ assert(0 < curr_lv->stats.crefcnt); assert(8 == (OFFSETOF(lv_val, tp_var) - OFFSETOF(lv_val, has_aliascont))); /* lv_val->has_aliascont/lvmon_mark all initialized in one shot. * Note: We use "qw_num *" instead of "uint8 *" below because the former works on 32-bit platforms too. * On 64-bit platforms, this is 1 8-byte move instead of 2 4-byte moves (the latter means more instructions). * Even though lvmon_mark is maintained only if DEBUG_ALIAS is #defined, we do the copy here unconditionally * because on 64-bit platforms, a 4-byte copy (of just has_aliascont) is the same as an 8-byte copy * (of has_aliascont and lvmon_mark). On 32-bit platforms, it saves us one copy but we dont worry about it much. */ GTM64_ONLY(assert(IS_PTR_8BYTE_ALIGNED(&curr_lv->has_aliascont));) NON_GTM64_ONLY(assert(IS_PTR_4BYTE_ALIGNED(&curr_lv->has_aliascont));)
void op_indget(mval *dst, mval *target, mval *value) { icode_str indir_src; int rval; ht_ent_mname *tabent; mstr *obj, object; oprtype v; triple *s, *src, *oldchain, tmpchain, *r, *triptr; var_tabent targ_key; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TREF(ind_source_sp) >= TREF(ind_source_top)) || (TREF(ind_result_sp) >= TREF(ind_result_top))) rts_error(VARLSTCNT(1) ERR_INDMAXNEST); /* mdbcondition_handler resets ind_result_sp & ind_source_sp */ MV_FORCE_DEFINED(value); MV_FORCE_STR(target); indir_src.str = target->str; indir_src.code = indir_get; if (NULL == (obj = cache_get(&indir_src))) { obj = &object; if (valid_mname(&target->str)) { targ_key.var_name = target->str; COMPUTE_HASH_MNAME(&targ_key); tabent = lookup_hashtab_mname(&curr_symval->h_symtab, &targ_key); if (!tabent || !LV_IS_VAL_DEFINED(tabent->value)) *dst = *value; else *dst = ((lv_val *)tabent->value)->v; dst->mvtype &= ~MV_ALIASCONT; /* Make sure alias container property does not pass */ return; } comp_init(&target->str); src = newtriple(OC_IGETSRC); switch (TREF(window_token)) { case TK_IDENT: if (EXPR_FAIL != (rval = lvn(&v, OC_SRCHINDX, 0))) /* NOTE assignment */ { s = newtriple(OC_FNGET2); s->operand[0] = v; s->operand[1] = put_tref(src); } break; case TK_CIRCUMFLEX: if (EXPR_FAIL != (rval = gvn())) /* NOTE assignment */ { r = newtriple(OC_FNGVGET1); s = newtriple(OC_FNGVGET2); s->operand[0] = put_tref(r); s->operand[1] = put_tref(src); } break; case TK_ATSIGN: TREF(saw_side_effect) = TREF(shift_side_effects); if (TREF(shift_side_effects) && (GTM_BOOL == TREF(gtm_fullbool))) { dqinit(&tmpchain, exorder); oldchain = setcurtchain(&tmpchain); if (EXPR_FAIL != (rval = indirection(&v))) /* NOTE assignment */ { s = newtriple(OC_INDGET); s->operand[0] = v; s->operand[1] = put_tref(src); newtriple(OC_GVSAVTARG); setcurtchain(oldchain); dqadd(TREF(expr_start), &tmpchain, exorder); TREF(expr_start) = tmpchain.exorder.bl; triptr = newtriple(OC_GVRECTARG); triptr->operand[0] = put_tref(TREF(expr_start)); } else setcurtchain(oldchain); } else { if (EXPR_FAIL != (rval = indirection(&v))) /* NOTE assignment */ { s = newtriple(OC_INDGET); s->operand[0] = v; s->operand[1] = put_tref(src); } } break; default: stx_error(ERR_VAREXPECTED); rval = EXPR_FAIL; break; } v = put_tref(s); if (EXPR_FAIL == comp_fini(rval, obj, OC_IRETMVAL, &v, target->str.len)) return; indir_src.str.addr = target->str.addr; cache_put(&indir_src, obj); /* Fall into code activation below */ } *(TREF(ind_result_sp))++ = dst; *(TREF(ind_source_sp))++ = value; comp_indr(obj); return; }
void gv_bind_name(gd_addr *addr, mstr *targ) { gd_binding *map; ht_ent_mname *tabent; mname_entry gvent; int res; boolean_t added; enum db_acc_method acc_meth; gd_region *reg; gvnh_reg_t *gvnh_reg; int keylen; char format_key[MAX_MIDENT_LEN + 1]; /* max key length + 1 byte for '^' */ gv_namehead *tmp_gvt; sgmnt_addrs *csa; gd_map = addr->maps; gd_map_top = gd_map + addr->n_maps; gvent.var_name.addr = targ->addr; gvent.var_name.len = MIN(targ->len, MAX_MIDENT_LEN); COMPUTE_HASH_MNAME(&gvent); if ((NULL != (tabent = lookup_hashtab_mname((hash_table_mname *)addr->tab_ptr, &gvent))) && (NULL != (gvnh_reg = (gvnh_reg_t *)tabent->value))) { reg = gvnh_reg->gd_reg; if (!reg->open) { gv_init_reg(reg); /* could modify gvnh_reg->gvt if multiple regions map to same db file */ assert(0 == gvnh_reg->gvt->clue.end); } gv_target = gvnh_reg->gvt; gv_cur_region = reg; acc_meth = gv_cur_region->dyn.addr->acc_meth; } else { map = gd_map + 1; /* get past local locks */ for (; (res = memcmp(gvent.var_name.addr, &(map->name[0]), gvent.var_name.len)) >= 0; map++) { assert(map < gd_map_top); if (0 == res && 0 != map->name[gvent.var_name.len]) break; } if (!map->reg.addr->open) gv_init_reg(map->reg.addr); gv_cur_region = map->reg.addr; acc_meth = gv_cur_region->dyn.addr->acc_meth; if ((dba_cm == acc_meth) || (dba_usr == acc_meth)) { tmp_gvt = malloc(SIZEOF(gv_namehead) + gvent.var_name.len); memset(tmp_gvt, 0, SIZEOF(gv_namehead) + gvent.var_name.len); tmp_gvt->gvname.var_name.addr = (char *)tmp_gvt + SIZEOF(gv_namehead); tmp_gvt->nct = 0; tmp_gvt->collseq = NULL; tmp_gvt->regcnt = 1; memcpy(tmp_gvt->gvname.var_name.addr, gvent.var_name.addr, gvent.var_name.len); tmp_gvt->gvname.var_name.len = gvent.var_name.len; tmp_gvt->gvname.hash_code = gvent.hash_code; } else { assert(gv_cur_region->max_key_size <= MAX_KEY_SZ); tmp_gvt = (gv_namehead *)targ_alloc(gv_cur_region->max_key_size, &gvent, gv_cur_region); } gvnh_reg = (gvnh_reg_t *)malloc(SIZEOF(gvnh_reg_t)); gvnh_reg->gvt = tmp_gvt; gvnh_reg->gd_reg = gv_cur_region; if (NULL != tabent) { /* Since the global name was found but gv_target was null and now we created a new gv_target, * the hash table key must point to the newly created gv_target->gvname. */ tabent->key = tmp_gvt->gvname; tabent->value = (char *)gvnh_reg; } else { added = add_hashtab_mname((hash_table_mname *)addr->tab_ptr, &tmp_gvt->gvname, gvnh_reg, &tabent); assert(added); } gv_target = tmp_gvt; /* now that any error possibilities (out-of-memory issues in malloc/add_hashtab_mname) * are all done, it is safe to set gv_target. Setting it before could casue gv_target * and gv_currkey to get out of sync in case of an error condition. */ } if ((keylen = gvent.var_name.len + 2) > gv_cur_region->max_key_size) /* caution: embedded assignment of "keylen" */ { assert(ARRAYSIZE(format_key) >= (1 + gvent.var_name.len)); format_key[0] = '^'; memcpy(&format_key[1], gvent.var_name.addr, gvent.var_name.len); csa = &FILE_INFO(gv_cur_region)->s_addrs; rts_error_csa(CSA_ARG(csa) VARLSTCNT(10) ERR_KEY2BIG, 4, keylen, (int4)gv_cur_region->max_key_size, REG_LEN_STR(gv_cur_region), ERR_GVIS, 2, 1 + gvent.var_name.len, format_key); } memcpy(gv_currkey->base, gvent.var_name.addr, gvent.var_name.len); gv_currkey->base[gvent.var_name.len] = 0; gvent.var_name.len++; gv_currkey->base[gvent.var_name.len] = 0; gv_currkey->end = gvent.var_name.len; gv_currkey->prev = 0; change_reg(); return; }
/* Routine to eliminate the zlinked trigger code for a given trigger about to be deleted. Operations performed * differ depending on platform type (shared binary or not). */ void gtm_trigger_cleanup(gv_trigger_t *trigdsc) { rtn_tabent *rbot, *mid, *rtop; mident *rtnname; rhdtyp *rtnhdr; textElem *telem; int comp, size; stack_frame *fp, *fpprev; mname_entry key; ht_ent_mname *tabent; routine_source *src_tbl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* First thing to do is release trigger source field if it exists */ if (0 < trigdsc->xecute_str.str.len) { free(trigdsc->xecute_str.str.addr); trigdsc->xecute_str.str.len = 0; trigdsc->xecute_str.str.addr = NULL; } /* Next thing to do is find the routine header in the rtn_names list so we can remove it. */ rtnname = &trigdsc->rtn_desc.rt_name; rtnhdr = trigdsc->rtn_desc.rt_adr; rbot = rtn_names; rtop = rtn_names_end; for (;;) { /* See if routine exists in list via a binary search which reverts to serial search when # of items drops below the threshold S_CUTOFF. */ if ((rtop - rbot) < S_CUTOFF) { comp = -1; for (mid = rbot; mid <= rtop ; mid++) { MIDENT_CMP(&mid->rt_name, rtnname, comp); if (0 == comp) break; if (0 < comp) GTMASSERT; /* Routine should be found */ } break; } else { mid = rbot + (rtop - rbot)/2; MIDENT_CMP(&mid->rt_name, rtnname, comp); if (0 == comp) break; else if (0 > comp) { rbot = mid + 1; continue; } else { rtop = mid - 1; continue; } } } # ifdef DEBUG assert(rtnhdr == mid->rt_adr); /* Verify trigger routine we want to remove is not currently active. If it is, we need to GTMASSERT. * Triggers are not like regular routines since they should only ever be referenced from the stack during a * transaction. Likewise, we should only ever load the triggers as the first action in that transaction. */ for (fp = frame_pointer; fp ; fp = fpprev) { fpprev = fp->old_frame_pointer; # ifdef GTM_TRIGGER if (NULL != fpprev && SFT_TRIGR & fpprev->type) fpprev = *(stack_frame **)(fpprev + 1); # endif /* Only one possible version of a trigger routine */ assert(USHBIN_ONLY(NULL) NON_USHBIN_ONLY(fp->rvector) == OLD_RHEAD_ADR(CURRENT_RHEAD_ADR(fp->rvector))); assert(fp->rvector != rtnhdr); } # endif /* Remove break points in this routine before rmv from rtntbl */ zr_remove(rtnhdr, BREAKMSG); /* Release any $TEXT() info this trigger has loaded */ if (NULL != (TREF(rt_name_tbl)).base) { key.var_name = mid->rt_name; COMPUTE_HASH_MNAME(&key); if (NULL != (tabent = lookup_hashtab_mname(TADR(rt_name_tbl), &key))) /* note assignment */ { /* We have a hash entry. Whether it has a value or not, it has a key name (if this is * the first time this trigger is being deleted) that may be pointing into the routine we * are about to remove. Migrate this key name to the stringpool. */ s2pool(&tabent->key.var_name); if (NULL != tabent->value) { /* Has value, release the source. Entries and source are malloc'd in two blocks on UNIX */ src_tbl = (routine_source *)tabent->value; if (NULL != src_tbl->srcbuff) free(src_tbl->srcbuff); free(src_tbl); tabent->value = NULL; } } } /* Remove the routine from the rtn_table */ size = INTCAST((char *)rtn_names_end - (char *)mid); if (0 < size) memmove((char *)mid, (char *)(mid + 1), size); /* Remove this routine name from sorted table */ rtn_names_end--; urx_remove(rtnhdr); /* Remove any unresolved entries */ # ifdef USHBIN_SUPPORTED stp_move((char *)rtnhdr->literal_text_adr, (char *)(rtnhdr->literal_text_adr + rtnhdr->literal_text_len)); GTM_TEXT_FREE(rtnhdr->ptext_adr); /* R/O releasable section */ free(rtnhdr->literal_adr); /* R/W releasable section part 1 */ free(rtnhdr->linkage_adr); /* R/W releasable section part 2 */ free(rtnhdr->labtab_adr); /* Usually non-releasable but triggers don't have labels so * this is just cleaning up a dangling null malloc */ free(rtnhdr); # else # if (!defined(__linux__) && !defined(__CYGWIN__)) || !defined(__i386) || !defined(COMP_GTA) # error Unsupported NON-USHBIN platform # endif /* For a non-shared binary platform we need to get an approximate addr range for stp_move. This is not * done when a routine is replaced on these platforms but in this case we are able to due to the isolated * environment if we only take the precautions of migrating potential literal text which may have been * pointed to by any set environment variables. * In this format, the only platform we support currently is Linux-x86 (i386) which uses GTM_TEXT_ALLOC * to allocate special storage for it to put executable code in. We can access the storage header for * this storage and find out how big it is and use that information to give stp_move a good range since * the literal segment occurs right at the end of allocated storage (for which there is no pointer * in the fileheader). */ telem = (textElem *)((char *)rtnhdr - offsetof(textElem, userStorage)); assert(TextAllocated == telem->state); stp_move((char *)LNRTAB_ADR(rtnhdr) + (rtnhdr->lnrtab_len * SIZEOF(lnr_tabent)), (char *)rtnhdr + telem->realLen); GTM_TEXT_FREE(rtnhdr); # endif }