/* Copy refs from one place to another. */ int refcpy_to_old(ref * aref, uint index, const ref * from, uint size, gs_dual_memory_t *idmemory, client_name_t cname) { ref *to = aref->value.refs + index; int code = refs_check_space(from, size, r_space(aref)); if (code < 0) return code; /* We have to worry about aliasing.... */ if (to <= from || from + size <= to) while (size--) ref_assign_old(aref, to, from, cname), to++, from++; else for (from += size, to += size; size--;) from--, to--, ref_assign_old(aref, to, from, cname); return 0; }
/* <bool> setpacking - */ static int zsetpacking(i_ctx_t *i_ctx_p) { os_ptr op = osp; ref cont; check_type(*op, t_boolean); make_struct(&cont, avm_local, ref_array_packing_container); ref_assign_old(&cont, &ref_array_packing, op, "setpacking"); pop(1); return 0; }
/* <names> .installsystemnames - */ static int zinstallsystemnames(i_ctx_t *i_ctx_p) { os_ptr op = osp; if (r_space(op) != avm_global || imemory_save_level(iimemory_global) != 0) return_error(gs_error_invalidaccess); check_read_type(*op, t_shortarray); ref_assign_old(NULL, system_names_p, op, ".installsystemnames"); pop(1); return 0; }
/* <string> <index> <int> put - */ static int zput(i_ctx_t *i_ctx_p) { os_ptr op = osp; os_ptr op1 = op - 1; os_ptr op2 = op1 - 1; byte *sdata; uint ssize; switch (r_type(op2)) { case t_dictionary: if (i_ctx_p->in_superexec == 0) check_dict_write(*op2); { int code = idict_put(op2, op1, op); if (code < 0) return code; /* error */ } break; case t_array: check_write(*op2); check_int_ltu(*op1, r_size(op2)); store_check_dest(op2, op); { ref *eltp = op2->value.refs + (uint) op1->value.intval; ref_assign_old(op2, eltp, op, "put"); } break; case t_mixedarray: /* packed arrays are read-only */ case t_shortarray: return_error(e_invalidaccess); case t_string: sdata = op2->value.bytes; ssize = r_size(op2); str: check_write(*op2); check_int_ltu(*op1, ssize); check_int_leu(*op, 0xff); sdata[(uint)op1->value.intval] = (byte)op->value.intval; break; case t_astruct: if (gs_object_type(imemory, op2->value.pstruct) != &st_bytes) return_error(e_typecheck); sdata = r_ptr(op2, byte); ssize = gs_object_size(imemory, op2->value.pstruct); goto str; default: return_op_typecheck(op2); } pop(3); return 0; }
/* r_size(op1) was set just above. */ static int do_execstack(i_ctx_t *i_ctx_p, bool include_marks, os_ptr op1) { os_ptr op = osp; ref *arefs = op1->value.refs; uint asize = r_size(op1); uint i; ref *rq; /* * Copy elements from the stack to the array, * optionally skipping executable nulls. * Clear the executable bit in any internal operators, and * convert t_structs and t_astructs (which can only appear * in connection with stack marks, which means that they will * probably be freed when unwinding) to something harmless. */ for (i = 0, rq = arefs + asize; rq != arefs; ++i) { const ref *rp = ref_stack_index(&e_stack, (long)i); if (r_has_type_attrs(rp, t_null, a_executable) && !include_marks) continue; --rq; ref_assign_old(op1, rq, rp, "execstack"); switch (r_type(rq)) { case t_operator: { uint opidx = op_index(rq); if (opidx == 0 || op_def_is_internal(op_index_def(opidx))) r_clear_attrs(rq, a_executable); break; } case t_struct: case t_astruct: { const char *tname = rq->value.pstruct ? gs_struct_type_name_string( gs_object_type(imemory, rq->value.pstruct)) : "NULL"; make_const_string(rq, a_readonly | avm_foreign, strlen(tname), (const byte *)tname); break; } default: ; } } pop(op - op1); return 0; }
/* <int> setobjectformat - */ static int zsetobjectformat(i_ctx_t *i_ctx_p) { os_ptr op = osp; ref cont; check_type(*op, t_integer); if (op->value.intval < 0 || op->value.intval > 4) return_error(gs_error_rangecheck); make_struct(&cont, avm_local, ref_binary_object_format_container); ref_assign_old(&cont, &ref_binary_object_format, op, "setobjectformat"); pop(1); return 0; }
/* its length; nothing else has been checked. */ static int copy_interval(i_ctx_t *i_ctx_p /* for ref_assign_old */, os_ptr prto, uint index, os_ptr prfrom, client_name_t cname) { int fromtype = r_type(prfrom); uint fromsize = r_size(prfrom); if (!(fromtype == r_type(prto) || ((fromtype == t_shortarray || fromtype == t_mixedarray) && r_type(prto) == t_array)) ) return_op_typecheck(prfrom); check_read(*prfrom); check_write(*prto); if (fromsize > r_size(prto) - index) return_error(e_rangecheck); switch (fromtype) { case t_array: { /* We have to worry about aliasing, */ /* but refcpy_to_old takes care of it for us. */ return refcpy_to_old(prto, index, prfrom->value.refs, fromsize, idmemory, cname); } case t_string: { /* memmove takes care of aliasing. */ memmove(prto->value.bytes + index, prfrom->value.bytes, fromsize); } break; case t_mixedarray: case t_shortarray: { /* We don't have to worry about aliasing, because */ /* packed arrays are read-only and hence the destination */ /* can't be a packed array. */ uint i; const ref_packed *packed = prfrom->value.packed; ref *pdest = prto->value.refs + index; ref elt; for (i = 0; i < fromsize; i++, pdest++) { packed_get(imemory, packed, &elt); ref_assign_old(prto, pdest, &elt, cname); packed = packed_next(packed); } } break; } return 0; }
/* <name> <proc> .makeoperator <oper> */ static int zmakeoperator(i_ctx_t *i_ctx_p) { os_ptr op = osp; op_array_table *opt; uint count; ref *tab; check_type(op[-1], t_name); check_proc(*op); switch (r_space(op)) { case avm_global: opt = &i_ctx_p->op_array_table_global; break; case avm_local: opt = &i_ctx_p->op_array_table_local; break; default: return_error(e_invalidaccess); } count = opt->count; tab = opt->table.value.refs; /* * restore doesn't reset op_array_table.count, but it does * remove entries from op_array_table.table. Since we fill * the table in order, we can detect that a restore has occurred * by checking whether what should be the most recent entry * is occupied. If not, we scan backwards over the vacated entries * to find the true end of the table. */ while (count > 0 && r_has_type(&tab[count - 1], t_null)) --count; if (count == r_size(&opt->table)) return_error(e_limitcheck); ref_assign_old(&opt->table, &tab[count], op, "makeoperator"); opt->nx_table[count] = name_index(imemory, op - 1); op_index_ref(imemory, opt->base_index + count, op - 1); opt->count = count + 1; pop(1); return 0; }
/* * This forces a "put" even if the object is not writable, and (if the * object is systemdict or the save level is 0) even if the value is in * local VM. It is meant to be used only for replacing the value of * FontDirectory in systemdict when switching between local and global VM, * and a few similar applications. After initialization, this operator * should no longer be accessible by name. */ static int zforceput(i_ctx_t *i_ctx_p) { os_ptr op = osp; os_ptr op1 = op - 1; os_ptr op2 = op - 2; int code; switch (r_type(op2)) { case t_array: check_int_ltu(*op1, r_size(op2)); if (r_space(op2) > r_space(op)) { if (imemory_save_level(iimemory)) return_error(e_invalidaccess); } { ref *eltp = op2->value.refs + (uint) op1->value.intval; ref_assign_old(op2, eltp, op, "put"); } break; case t_dictionary: if (op2->value.pdict == systemdict->value.pdict || !imemory_save_level(iimemory) ) { uint space = r_space(op2); r_set_space(op2, avm_local); code = idict_put(op2, op1, op); r_set_space(op2, space); } else code = idict_put(op2, op1, op); if (code < 0) return code; break; default: return_error(e_typecheck); } pop(3); return 0; }
static int zbind(i_ctx_t *i_ctx_p) { os_ptr op = osp; uint depth = 1; ref defn; register os_ptr bsp; switch (r_type(op)) { case t_array: if (!r_has_attr(op, a_write)) { return 0; /* per PLRM3 */ } case t_mixedarray: case t_shortarray: defn = *op; break; case t_oparray: defn = *op->value.const_refs; break; default: return_op_typecheck(op); } push(1); *op = defn; bsp = op; /* * We must not make the top-level procedure read-only, * but we must bind it even if it is read-only already. * * Here are the invariants for the following loop: * `depth' elements have been pushed on the ostack; * For i < depth, p = ref_stack_index(&o_stack, i): * *p is an array (or packedarray) ref. */ while (depth) { while (r_size(bsp)) { ref_packed *const tpp = (ref_packed *)bsp->value.packed; /* break const */ r_dec_size(bsp, 1); if (r_is_packed(tpp)) { /* Check for a packed executable name */ ushort elt = *tpp; if (r_packed_is_exec_name(&elt)) { ref nref; ref *pvalue; name_index_ref(imemory, packed_name_index(&elt), &nref); if ((pvalue = dict_find_name(&nref)) != 0 && r_is_ex_oper(pvalue) ) { store_check_dest(bsp, pvalue); /* * Always save the change, since this can only * happen once. */ ref_do_save(bsp, tpp, "bind"); *tpp = pt_tag(pt_executable_operator) + op_index(pvalue); } } bsp->value.packed = tpp + 1; } else { ref *const tp = bsp->value.refs++; switch (r_type(tp)) { case t_name: /* bind the name if an operator */ if (r_has_attr(tp, a_executable)) { ref *pvalue; if ((pvalue = dict_find_name(tp)) != 0 && r_is_ex_oper(pvalue) ) { store_check_dest(bsp, pvalue); ref_assign_old(bsp, tp, pvalue, "bind"); } } break; case t_array: /* push into array if writable */ if (!r_has_attr(tp, a_write)) break; case t_mixedarray: case t_shortarray: if (r_has_attr(tp, a_executable)) { /* Make reference read-only */ r_clear_attrs(tp, a_write); if (bsp >= ostop) { /* Push a new stack block. */ ref temp; int code; temp = *tp; osp = bsp; code = ref_stack_push(&o_stack, 1); if (code < 0) { ref_stack_pop(&o_stack, depth); return_error(code); } bsp = osp; *bsp = temp; } else *++bsp = *tp; depth++; } } } } bsp--; depth--; if (bsp < osbot) { /* Pop back to the previous stack block. */ osp = bsp; ref_stack_pop_block(&o_stack); bsp = osp; } } osp = bsp; return 0; }