summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Deutschmann <whissi@gentoo.org>2019-10-15 12:24:12 +0200
committerThomas Deutschmann <whissi@gentoo.org>2020-08-13 11:26:55 +0200
commite088156d5b620e5e639580dacf85c6dc13823c74 (patch)
tree57f5c025e203279944da512166c20bc0521d8ccd /psi/interp.c
downloadghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.gz
ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.bz2
ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.zip
Import Ghostscript 9.50ghostscript-9.50
Signed-off-by: Thomas Deutschmann <whissi@gentoo.org>
Diffstat (limited to 'psi/interp.c')
-rw-r--r--psi/interp.c2054
1 files changed, 2054 insertions, 0 deletions
diff --git a/psi/interp.c b/psi/interp.c
new file mode 100644
index 00000000..3ac5c70c
--- /dev/null
+++ b/psi/interp.c
@@ -0,0 +1,2054 @@
+/* Copyright (C) 2001-2019 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
+ CA 94945, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Ghostscript language interpreter */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gsstruct.h" /* for iastruct.h */
+#include "gserrors.h" /* for gpcheck.h */
+#include "stream.h"
+#include "ierrors.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "iastruct.h"
+#include "icontext.h"
+#include "icremap.h"
+#include "idebug.h"
+#include "igstate.h" /* for handling gs_error_Remap_Color */
+#include "inamedef.h"
+#include "iname.h" /* for the_name_table */
+#include "interp.h"
+#include "ipacked.h"
+#include "ostack.h" /* must precede iscan.h */
+#include "strimpl.h" /* for sfilter.h */
+#include "sfilter.h" /* for iscan.h */
+#include "iscan.h"
+#include "iddict.h"
+#include "isave.h"
+#include "istack.h"
+#include "itoken.h"
+#include "iutil.h" /* for array_get */
+#include "ivmspace.h"
+#include "iinit.h"
+#include "dstack.h"
+#include "files.h" /* for file_check_read */
+#include "oper.h"
+#include "store.h"
+#include "gpcheck.h"
+
+/*
+ * We may or may not optimize the handling of the special fast operators
+ * in packed arrays. If we do this, they run much faster when packed, but
+ * slightly slower when not packed.
+ */
+#define PACKED_SPECIAL_OPS 1
+
+/*
+ * Pseudo-operators (procedures of type t_oparray) record
+ * the operand and dictionary stack pointers, and restore them if an error
+ * occurs during the execution of the procedure and if the procedure hasn't
+ * (net) decreased the depth of the stack. While this obviously doesn't
+ * do all the work of restoring the state if a pseudo-operator gets an
+ * error, it's a big help. The only downside is that pseudo-operators run
+ * a little slower.
+ */
+
+/* GC descriptors for stacks */
+extern_st(st_ref_stack);
+public_st_dict_stack();
+public_st_exec_stack();
+public_st_op_stack();
+
+/*
+ * Apply an operator. When debugging, we route all operator calls
+ * through a procedure.
+ */
+#if defined(DEBUG_TRACE_PS_OPERATORS) || defined(DEBUG)
+#define call_operator(proc, p) (*call_operator_fn)(proc, p)
+static int
+do_call_operator(op_proc_t op_proc, i_ctx_t *i_ctx_p)
+{
+ int code;
+ code = op_proc(i_ctx_p);
+ if (gs_debug_c(gs_debug_flag_validate_clumps))
+ ivalidate_clean_spaces(i_ctx_p);
+ return code; /* A good place for a conditional breakpoint. */
+}
+static int
+do_call_operator_verbose(op_proc_t op_proc, i_ctx_t *i_ctx_p)
+{
+ int code;
+
+#ifndef SHOW_STACK_DEPTHS
+ if_debug1m('!', imemory, "[!]operator %s\n", op_get_name_string(op_proc));
+#else
+ if_debug3m('!', imemory, "[!][es=%d os=%d]operator %s\n",
+ esp-i_ctx_p->exec_stack.stack.bot,
+ osp-i_ctx_p->op_stack.stack.bot,
+ op_get_name_string(op_proc));
+#endif
+ code = do_call_operator(op_proc, i_ctx_p);
+#if defined(SHOW_STACK_DEPTHS)
+ if_debug2m('!', imemory, "[!][es=%d os=%d]\n",
+ esp-i_ctx_p->exec_stack.stack.bot,
+ osp-i_ctx_p->op_stack.stack.bot);
+#endif
+ if (gs_debug_c(gs_debug_flag_validate_clumps))
+ ivalidate_clean_spaces(i_ctx_p);
+ return code; /* A good place for a conditional breakpoint. */
+}
+#else
+# define call_operator(proc, p) ((*(proc))(p))
+#endif
+
+/* Define debugging statistics (not threadsafe as uses globals) */
+#if defined(DEBUG) && !defined(GS_THREADSAFE)
+struct stats_interp_s {
+ long top;
+ long lit, lit_array, exec_array, exec_operator, exec_name;
+ long x_add, x_def, x_dup, x_exch, x_if, x_ifelse,
+ x_index, x_pop, x_roll, x_sub;
+ long find_name, name_lit, name_proc, name_oparray, name_operator;
+ long p_full, p_exec_operator, p_exec_oparray, p_exec_non_x_operator,
+ p_integer, p_lit_name, p_exec_name;
+ long p_find_name, p_name_lit, p_name_proc;
+} stats_interp;
+# define INCR(v) (++(stats_interp.v))
+#else
+# define INCR(v) DO_NOTHING
+#endif
+
+/* Forward references */
+static int estack_underflow(i_ctx_t *);
+static int interp(i_ctx_t **, const ref *, ref *);
+static int interp_exit(i_ctx_t *);
+static int zforceinterp_exit(i_ctx_t *i_ctx_p);
+static void set_gc_signal(i_ctx_t *, int);
+static int copy_stack(i_ctx_t *, const ref_stack_t *, int skip, ref *);
+static int oparray_pop(i_ctx_t *);
+static int oparray_cleanup(i_ctx_t *);
+static int zerrorexec(i_ctx_t *);
+static int zfinderrorobject(i_ctx_t *);
+static int errorexec_pop(i_ctx_t *);
+static int errorexec_cleanup(i_ctx_t *);
+static int zsetstackprotect(i_ctx_t *);
+static int zcurrentstackprotect(i_ctx_t *);
+static int zactonuel(i_ctx_t *);
+
+/* Stack sizes */
+
+/* The maximum stack sizes may all be set in the makefile. */
+
+/*
+ * Define the initial maximum size of the operand stack (MaxOpStack
+ * user parameter).
+ */
+#ifndef MAX_OSTACK
+# define MAX_OSTACK 800
+#endif
+/*
+ * The minimum block size for extending the operand stack is the larger of:
+ * - the maximum number of parameters to an operator
+ * (currently setcolorscreen, with 12 parameters);
+ * - the maximum number of values pushed by an operator
+ * (currently setcolortransfer, which calls zcolor_remap_one 4 times
+ * and therefore pushes 16 values).
+ */
+#define MIN_BLOCK_OSTACK 16
+const int gs_interp_max_op_num_args = MIN_BLOCK_OSTACK; /* for iinit.c */
+
+/*
+ * Define the initial maximum size of the execution stack (MaxExecStack
+ * user parameter).
+ */
+#ifndef MAX_ESTACK
+# define MAX_ESTACK 5000
+#endif
+/*
+ * The minimum block size for extending the execution stack is the largest
+ * size of a contiguous block surrounding an e-stack mark. (At least,
+ * that's what the minimum value would be if we supported multi-block
+ * estacks, which we currently don't.) Currently, the largest such block is
+ * the one created for text processing, which is 8 (snumpush) slots.
+ */
+#define MIN_BLOCK_ESTACK 8
+/*
+ * If we get an e-stack overflow, we need to cut it back far enough to
+ * have some headroom for executing the error procedure.
+ */
+#define ES_HEADROOM 20
+
+/*
+ * Define the initial maximum size of the dictionary stack (MaxDictStack
+ * user parameter). Again, this is also currently the block size for
+ * extending the d-stack.
+ */
+#ifndef MAX_DSTACK
+# define MAX_DSTACK 20
+#endif
+/*
+ * The minimum block size for extending the dictionary stack is the number
+ * of permanent entries on the dictionary stack, currently 3.
+ */
+#define MIN_BLOCK_DSTACK 3
+
+/* See estack.h for a description of the execution stack. */
+
+/* The logic for managing icount and iref below assumes that */
+/* there are no control operators which pop and then push */
+/* information on the execution stack. */
+
+/* Stacks */
+extern_st(st_ref_stack);
+#define OS_GUARD_UNDER 10
+#define OS_GUARD_OVER 10
+#define OS_REFS_SIZE(body_size)\
+ (stack_block_refs + OS_GUARD_UNDER + (body_size) + OS_GUARD_OVER)
+
+#define ES_GUARD_UNDER 1
+#define ES_GUARD_OVER 10
+#define ES_REFS_SIZE(body_size)\
+ (stack_block_refs + ES_GUARD_UNDER + (body_size) + ES_GUARD_OVER)
+
+#define DS_REFS_SIZE(body_size)\
+ (stack_block_refs + (body_size))
+
+/* Extended types. The interpreter may replace the type of operators */
+/* in procedures with these, to speed up the interpretation loop. */
+/****** NOTE: If you add or change entries in this list, */
+/****** you must change the three dispatches in the interpreter loop. */
+/* The operator procedures are declared in opextern.h. */
+#define tx_op t_next_index
+typedef enum {
+ tx_op_add = tx_op,
+ tx_op_def,
+ tx_op_dup,
+ tx_op_exch,
+ tx_op_if,
+ tx_op_ifelse,
+ tx_op_index,
+ tx_op_pop,
+ tx_op_roll,
+ tx_op_sub,
+ tx_next_op
+} special_op_types;
+
+#define num_special_ops ((int)tx_next_op - tx_op)
+const int gs_interp_num_special_ops = num_special_ops; /* for iinit.c */
+const int tx_next_index = tx_next_op;
+
+/*
+ * NOTE: if the size of either table below ever exceeds 15 real entries, it
+ * will have to be split.
+ */
+/* Define the extended-type operators per the list above. */
+const op_def interp1_op_defs[] = {
+ /*
+ * The very first entry, which corresponds to operator index 0,
+ * must not contain an actual operator.
+ */
+ op_def_begin_dict("systemdict"),
+ {"2add", zadd},
+ {"2def", zdef},
+ {"1dup", zdup},
+ {"2exch", zexch},
+ {"2if", zif},
+ {"3ifelse", zifelse},
+ {"1index", zindex},
+ {"1pop", zpop},
+ {"2roll", zroll},
+ {"2sub", zsub},
+ op_def_end(0)
+};
+/* Define the internal interpreter operators. */
+const op_def interp2_op_defs[] = {
+ {"0.currentstackprotect", zcurrentstackprotect},
+ {"1.setstackprotect", zsetstackprotect},
+ {"2.errorexec", zerrorexec},
+ {"0.finderrorobject", zfinderrorobject},
+ {"0%interp_exit", interp_exit},
+ {"0.forceinterp_exit", zforceinterp_exit},
+ {"0%oparray_pop", oparray_pop},
+ {"0%errorexec_pop", errorexec_pop},
+ {"0.actonuel", zactonuel},
+ op_def_end(0)
+};
+
+#define make_null_proc(pref)\
+ make_empty_const_array(pref, a_executable + a_readonly)
+
+/* Initialize the interpreter. */
+int
+gs_interp_init(i_ctx_t **pi_ctx_p, const ref *psystem_dict,
+ gs_dual_memory_t *dmem)
+{
+ /* Create and initialize a context state. */
+ gs_context_state_t *pcst = 0;
+ int code = context_state_alloc(&pcst, psystem_dict, dmem);
+ if (code >= 0) {
+ code = context_state_load(pcst);
+ if (code < 0) {
+ context_state_free(pcst);
+ pcst = NULL;
+ }
+ }
+
+ if (code < 0)
+ lprintf1("Fatal error %d in gs_interp_init!\n", code);
+ *pi_ctx_p = pcst;
+
+ return code;
+}
+/*
+ * Create initial stacks for the interpreter.
+ * We export this for creating new contexts.
+ */
+int
+gs_interp_alloc_stacks(gs_ref_memory_t *mem, gs_context_state_t * pcst)
+{
+ int code;
+ gs_ref_memory_t *smem =
+ (gs_ref_memory_t *)gs_memory_stable((gs_memory_t *)mem);
+ ref stk;
+
+#define REFS_SIZE_OSTACK OS_REFS_SIZE(MAX_OSTACK)
+#define REFS_SIZE_ESTACK ES_REFS_SIZE(MAX_ESTACK)
+#define REFS_SIZE_DSTACK DS_REFS_SIZE(MAX_DSTACK)
+ code = gs_alloc_ref_array(smem, &stk, 0,
+ REFS_SIZE_OSTACK + REFS_SIZE_ESTACK +
+ REFS_SIZE_DSTACK, "gs_interp_alloc_stacks");
+ if (code < 0)
+ return code;
+
+ {
+ ref_stack_t *pos = &pcst->op_stack.stack;
+
+ r_set_size(&stk, REFS_SIZE_OSTACK);
+ code = ref_stack_init(pos, &stk, OS_GUARD_UNDER, OS_GUARD_OVER, NULL,
+ smem, NULL);
+ if (code < 0)
+ return code;
+ ref_stack_set_error_codes(pos, gs_error_stackunderflow, gs_error_stackoverflow);
+ ref_stack_set_max_count(pos, MAX_OSTACK);
+ stk.value.refs += REFS_SIZE_OSTACK;
+ }
+
+ {
+ ref_stack_t *pes = &pcst->exec_stack.stack;
+ ref euop;
+
+ r_set_size(&stk, REFS_SIZE_ESTACK);
+ make_oper(&euop, 0, estack_underflow);
+ code = ref_stack_init(pes, &stk, ES_GUARD_UNDER, ES_GUARD_OVER, &euop,
+ smem, NULL);
+ if (code < 0)
+ return code;
+ ref_stack_set_error_codes(pes, gs_error_ExecStackUnderflow,
+ gs_error_execstackoverflow);
+ /**************** E-STACK EXPANSION IS NYI. ****************/
+ ref_stack_allow_expansion(pes, false);
+ ref_stack_set_max_count(pes, MAX_ESTACK);
+ stk.value.refs += REFS_SIZE_ESTACK;
+ }
+
+ {
+ ref_stack_t *pds = &pcst->dict_stack.stack;
+
+ r_set_size(&stk, REFS_SIZE_DSTACK);
+ code = ref_stack_init(pds, &stk, 0, 0, NULL, smem, NULL);
+ if (code < 0)
+ return code;
+ ref_stack_set_error_codes(pds, gs_error_dictstackunderflow,
+ gs_error_dictstackoverflow);
+ ref_stack_set_max_count(pds, MAX_DSTACK);
+ }
+
+#undef REFS_SIZE_OSTACK
+#undef REFS_SIZE_ESTACK
+#undef REFS_SIZE_DSTACK
+ return 0;
+}
+/*
+ * Free the stacks when destroying a context. This is the inverse of
+ * create_stacks.
+ */
+void
+gs_interp_free_stacks(gs_ref_memory_t * smem, gs_context_state_t * pcst)
+{
+ /* Free the stacks in inverse order of allocation. */
+ ref_stack_release(&pcst->dict_stack.stack);
+ ref_stack_release(&pcst->exec_stack.stack);
+ ref_stack_release(&pcst->op_stack.stack);
+}
+void
+gs_interp_reset(i_ctx_t *i_ctx_p)
+{ /* Reset the stacks. */
+ ref_stack_clear(&o_stack);
+ ref_stack_clear(&e_stack);
+ esp++;
+ make_oper(esp, 0, interp_exit);
+ ref_stack_pop_to(&d_stack, min_dstack_size);
+ dict_set_top();
+}
+/* Report an e-stack block underflow. The bottom guard slots of */
+/* e-stack blocks contain a pointer to this procedure. */
+static int
+estack_underflow(i_ctx_t *i_ctx_p)
+{
+ return gs_error_ExecStackUnderflow;
+}
+
+/*
+ * Create an operator during initialization.
+ * If operator is hard-coded into the interpreter,
+ * assign it a special type and index.
+ */
+void
+gs_interp_make_oper(ref * opref, op_proc_t proc, int idx)
+{
+ int i;
+
+ for (i = num_special_ops; i > 0 && proc != interp1_op_defs[i].proc; --i)
+ DO_NOTHING;
+ if (i > 0)
+ make_tasv(opref, tx_op + (i - 1), a_executable, i, opproc, proc);
+ else
+ make_tasv(opref, t_operator, a_executable, idx, opproc, proc);
+}
+
+/*
+ * Call the garbage collector, updating the context pointer properly.
+ */
+int
+interp_reclaim(i_ctx_t **pi_ctx_p, int space)
+{
+ i_ctx_t *i_ctx_p = *pi_ctx_p;
+ gs_gc_root_t ctx_root, *r = &ctx_root;
+ int code;
+
+#ifdef DEBUG
+ if (gs_debug_c(gs_debug_flag_gc_disable))
+ return 0;
+#endif
+
+ gs_register_struct_root(imemory_system, &r,
+ (void **)pi_ctx_p, "interp_reclaim(pi_ctx_p)");
+ code = (*idmemory->reclaim)(idmemory, space);
+ i_ctx_p = *pi_ctx_p; /* may have moved */
+ gs_unregister_root(imemory_system, r, "interp_reclaim(pi_ctx_p)");
+ return code;
+}
+
+/*
+ * Invoke the interpreter. If execution completes normally, return 0.
+ * If an error occurs, the action depends on user_errors as follows:
+ * user_errors < 0: always return an error code.
+ * user_errors >= 0: let the PostScript machinery handle all errors.
+ * (This will eventually result in a fatal error if no 'stopped'
+ * is active.)
+ * In case of a quit or a fatal error, also store the exit code.
+ * Set *perror_object to null or the error object.
+ */
+static int gs_call_interp(i_ctx_t **, ref *, int, int *, ref *);
+int
+gs_interpret(i_ctx_t **pi_ctx_p, ref * pref, int user_errors, int *pexit_code,
+ ref * perror_object)
+{
+ i_ctx_t *i_ctx_p = *pi_ctx_p;
+ gs_gc_root_t error_root, *r = &error_root;
+ int code;
+
+ gs_register_ref_root(imemory_system, &r,
+ (void **)&perror_object, "gs_interpret");
+ code = gs_call_interp(pi_ctx_p, pref, user_errors, pexit_code,
+ perror_object);
+ i_ctx_p = *pi_ctx_p;
+ gs_unregister_root(imemory_system, &error_root, "gs_interpret");
+ /* Avoid a dangling reference to the lib context GC signal. */
+ set_gc_signal(i_ctx_p, 0);
+ return code;
+}
+static int
+gs_call_interp(i_ctx_t **pi_ctx_p, ref * pref, int user_errors,
+ int *pexit_code, ref * perror_object)
+{
+ ref *epref = pref;
+ ref doref;
+ ref *perrordict;
+ ref error_name;
+ int code, ccode;
+ ref saref;
+ i_ctx_t *i_ctx_p = *pi_ctx_p;
+ int *gc_signal = &imemory_system->gs_lib_ctx->gcsignal;
+
+ *pexit_code = 0;
+ *gc_signal = 0;
+ ialloc_reset_requested(idmemory);
+again:
+ /* Avoid a dangling error object that might get traced by a future GC. */
+ make_null(perror_object);
+ o_stack.requested = e_stack.requested = d_stack.requested = 0;
+ while (*gc_signal) { /* Some routine below triggered a GC. */
+ gs_gc_root_t epref_root, *r = &epref_root;
+
+ *gc_signal = 0;
+ /* Make sure that doref will get relocated properly if */
+ /* a garbage collection happens with epref == &doref. */
+ gs_register_ref_root(imemory_system, &r,
+ (void **)&epref, "gs_call_interp(epref)");
+ code = interp_reclaim(pi_ctx_p, -1);
+ i_ctx_p = *pi_ctx_p;
+ gs_unregister_root(imemory_system, &epref_root,
+ "gs_call_interp(epref)");
+ if (code < 0)
+ return code;
+ }
+ code = interp(pi_ctx_p, epref, perror_object);
+ i_ctx_p = *pi_ctx_p;
+ if (!r_has_type(&i_ctx_p->error_object, t__invalid)) {
+ *perror_object = i_ctx_p->error_object;
+ make_t(&i_ctx_p->error_object, t__invalid);
+ }
+ /* Prevent a dangling reference to the GC signal in ticks_left */
+ /* in the frame of interp, but be prepared to do a GC if */
+ /* an allocation in this routine asks for it. */
+ *gc_signal = 0;
+ set_gc_signal(i_ctx_p, 1);
+ if (esp < esbot) /* popped guard entry */
+ esp = esbot;
+ switch (code) {
+ case gs_error_Fatal:
+ *pexit_code = 255;
+ return code;
+ case gs_error_Quit:
+ *perror_object = osp[-1];
+ *pexit_code = code = osp->value.intval;
+ osp -= 2;
+ return
+ (code == 0 ? gs_error_Quit :
+ code < 0 && code > -100 ? code : gs_error_Fatal);
+ case gs_error_InterpreterExit:
+ return 0;
+ case gs_error_ExecStackUnderflow:
+/****** WRONG -- must keep mark blocks intact ******/
+ ref_stack_pop_block(&e_stack);
+ doref = *perror_object;
+ epref = &doref;
+ goto again;
+ case gs_error_VMreclaim:
+ /* Do the GC and continue. */
+ /* We ignore the return value here, if it fails here
+ * we'll call it again having jumped to the "again" label.
+ * Where, assuming it fails again, we'll handle the error.
+ */
+ (void)interp_reclaim(pi_ctx_p,
+ (osp->value.intval == 2 ?
+ avm_global : avm_local));
+ i_ctx_p = *pi_ctx_p;
+ make_oper(&doref, 0, zpop);
+ epref = &doref;
+ goto again;
+ case gs_error_NeedInput:
+ case gs_error_interrupt:
+ return code;
+ }
+ /* Adjust osp in case of operand stack underflow */
+ if (osp < osbot - 1)
+ osp = osbot - 1;
+ /* We have to handle stack over/underflow specially, because */
+ /* we might be able to recover by adding or removing a block. */
+ switch (code) {
+ case gs_error_dictstackoverflow:
+ /* We don't have to handle this specially: */
+ /* The only places that could generate it */
+ /* use check_dstack, which does a ref_stack_extend, */
+ /* so if` we get this error, it's a real one. */
+ if (osp >= ostop) {
+ if ((ccode = ref_stack_extend(&o_stack, 1)) < 0)
+ return ccode;
+ }
+ /* Skip system dictionaries for CET 20-02-02 */
+ ccode = copy_stack(i_ctx_p, &d_stack, min_dstack_size, &saref);
+ if (ccode < 0)
+ return ccode;
+ ref_stack_pop_to(&d_stack, min_dstack_size);
+ dict_set_top();
+ *++osp = saref;
+ break;
+ case gs_error_dictstackunderflow:
+ if (ref_stack_pop_block(&d_stack) >= 0) {
+ dict_set_top();
+ doref = *perror_object;
+ epref = &doref;
+ goto again;
+ }
+ break;
+ case gs_error_execstackoverflow:
+ /* We don't have to handle this specially: */
+ /* The only places that could generate it */
+ /* use check_estack, which does a ref_stack_extend, */
+ /* so if we get this error, it's a real one. */
+ if (osp >= ostop) {
+ if ((ccode = ref_stack_extend(&o_stack, 1)) < 0)
+ return ccode;
+ }
+ ccode = copy_stack(i_ctx_p, &e_stack, 0, &saref);
+ if (ccode < 0)
+ return ccode;
+ {
+ uint count = ref_stack_count(&e_stack);
+ uint limit = ref_stack_max_count(&e_stack) - ES_HEADROOM;
+
+ if (count > limit) {
+ /*
+ * If there is an e-stack mark within MIN_BLOCK_ESTACK of
+ * the new top, cut the stack back to remove the mark.
+ */
+ int skip = count - limit;
+ int i;
+
+ for (i = skip; i < skip + MIN_BLOCK_ESTACK; ++i) {
+ const ref *ep = ref_stack_index(&e_stack, i);
+
+ if (r_has_type_attrs(ep, t_null, a_executable)) {
+ skip = i + 1;
+ break;
+ }
+ }
+ pop_estack(i_ctx_p, skip);
+ }
+ }
+ *++osp = saref;
+ break;
+ case gs_error_stackoverflow:
+ if (ref_stack_extend(&o_stack, o_stack.requested) >= 0) { /* We can't just re-execute the object, because */
+ /* it might be a procedure being pushed as a */
+ /* literal. We check for this case specially. */
+ doref = *perror_object;
+ if (r_is_proc(&doref)) {
+ *++osp = doref;
+ make_null_proc(&doref);
+ }
+ epref = &doref;
+ goto again;
+ }
+ ccode = copy_stack(i_ctx_p, &o_stack, 0, &saref);
+ if (ccode < 0)
+ return ccode;
+ ref_stack_clear(&o_stack);
+ *++osp = saref;
+ break;
+ case gs_error_stackunderflow:
+ if (ref_stack_pop_block(&o_stack) >= 0) {
+ doref = *perror_object;
+ epref = &doref;
+ goto again;
+ }
+ break;
+ }
+ if (user_errors < 0)
+ return code;
+ if (gs_errorname(i_ctx_p, code, &error_name) < 0)
+ return code; /* out-of-range error code! */
+
+ /* We refer to gserrordict first, which is not accessible to Postcript jobs
+ * If we're running with SAFERERRORS all the handlers are copied to gserrordict
+ * so we'll always find the default one. If not SAFERERRORS, only gs specific
+ * errors are in gserrordict.
+ */
+ if (dict_find_string(systemdict, "gserrordict", &perrordict) <= 0 ||
+ (dict_find(perrordict, &error_name, &epref) <= 0 &&
+ (dict_find_string(systemdict, "errordict", &perrordict) <= 0 ||
+ dict_find(perrordict, &error_name, &epref) <= 0))
+ )
+ return code; /* error name not in errordict??? */
+
+ doref = *epref;
+ epref = &doref;
+ /* Push the error object on the operand stack if appropriate. */
+ if (!GS_ERROR_IS_INTERRUPT(code)) {
+ byte buf[260], *bufptr;
+ uint rlen;
+ /* Replace the error object if within an oparray or .errorexec. */
+ osp++;
+ if (osp >= ostop) {
+ *pexit_code = gs_error_Fatal;
+ return_error(gs_error_Fatal);
+ }
+ *osp = *perror_object;
+ errorexec_find(i_ctx_p, osp);
+
+ if (!r_has_type(osp, t_string) && !r_has_type(osp, t_name)) {
+ code = obj_cvs(imemory, osp, buf + 2, 256, &rlen, (const byte **)&bufptr);
+ if (code < 0) {
+ const char *unknownstr = "--unknown--";
+ rlen = strlen(unknownstr);
+ memcpy(buf, unknownstr, rlen);
+ bufptr = buf;
+ }
+ else {
+ ref *tobj;
+ bufptr[rlen] = '\0';
+ /* Only pass a name object if the operator doesn't exist in systemdict
+ * i.e. it's an internal operator we have hidden
+ */
+ code = dict_find_string(systemdict, (const char *)bufptr, &tobj);
+ if (code <= 0) {
+ buf[0] = buf[1] = buf[rlen + 2] = buf[rlen + 3] = '-';
+ rlen += 4;
+ bufptr = buf;
+ }
+ else {
+ bufptr = NULL;
+ }
+ }
+ if (bufptr) {
+ code = name_ref(imemory, buf, rlen, osp, 1);
+ if (code < 0)
+ make_null(osp);
+ }
+ }
+ }
+ goto again;
+}
+static int
+interp_exit(i_ctx_t *i_ctx_p)
+{
+ return gs_error_InterpreterExit;
+}
+
+/* Only used (currently) with language switching:
+ * allows the PS interpreter to co-exist with the
+ * PJL interpreter.
+ */
+static int
+zforceinterp_exit(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+
+ check_file(s, op);
+ i_ctx_p->uel_position = stell(s)-1;
+ /* resetfile */
+ if (file_is_valid(s, op))
+ sreset(s);
+
+ if (!gs_lib_ctx_get_act_on_uel((gs_memory_t *)(i_ctx_p->memory.current)))
+ return 0;
+
+ gs_interp_reset(i_ctx_p);
+ /* gs_interp_reset() actually leaves the op stack one entry below
+ * the bottom of the stack, and that can cause problems depending
+ * on the interpreter state at the end of the job.
+ * So push a null object, and the return code before continuing.
+ */
+ push(2);
+ op = osp;
+ make_null(op - 1);
+ make_int(op, gs_error_InterpreterExit);
+ return_error(gs_error_Quit);
+}
+
+/* Set the GC signal for all VMs. */
+static void
+set_gc_signal(i_ctx_t *i_ctx_p, int value)
+{
+ gs_memory_gc_status_t stat;
+ int i;
+
+ for (i = 0; i < countof(idmemory->spaces_indexed); i++) {
+ gs_ref_memory_t *mem = idmemory->spaces_indexed[i];
+ gs_ref_memory_t *mem_stable;
+
+ if (mem == 0)
+ continue;
+ for (;; mem = mem_stable) {
+ mem_stable = (gs_ref_memory_t *)
+ gs_memory_stable((gs_memory_t *)mem);
+ gs_memory_gc_status(mem, &stat);
+ stat.signal_value = value;
+ gs_memory_set_gc_status(mem, &stat);
+ if (mem_stable == mem)
+ break;
+ }
+ }
+}
+
+/* Copy top elements of an overflowed stack into a (local) array. */
+/* Adobe copies only 500 top elements, we copy up to 65535 top elements */
+/* for better debugging, PLRM compliance, and backward compatibility. */
+static int
+copy_stack(i_ctx_t *i_ctx_p, const ref_stack_t * pstack, int skip, ref * arr)
+{
+ uint size = ref_stack_count(pstack) - skip;
+ uint save_space = ialloc_space(idmemory);
+ int code, i;
+ ref *safety, *safe;
+
+ if (size > 65535)
+ size = 65535;
+ ialloc_set_space(idmemory, avm_local);
+ code = ialloc_ref_array(arr, a_all, size, "copy_stack");
+ if (code >= 0)
+ code = ref_stack_store(pstack, arr, size, 0, 1, true, idmemory,
+ "copy_stack");
+ /* If we are copying the exec stack, try to replace any oparrays with
+ * with the operator than references them
+ */
+ if (pstack == &e_stack) {
+ for (i = 0; i < size; i++) {
+ if (errorexec_find(i_ctx_p, &arr->value.refs[i]) < 0)
+ make_null(&arr->value.refs[i]);
+ }
+ }
+ if (pstack == &o_stack && dict_find_string(systemdict, "SAFETY", &safety) > 0 &&
+ dict_find_string(safety, "safe", &safe) > 0 && r_has_type(safe, t_boolean) &&
+ safe->value.boolval == true) {
+ code = ref_stack_array_sanitize(i_ctx_p, arr, arr);
+ if (code < 0)
+ return code;
+ }
+ ialloc_set_space(idmemory, save_space);
+ return code;
+}
+
+/* Get the name corresponding to an error number. */
+int
+gs_errorname(i_ctx_t *i_ctx_p, int code, ref * perror_name)
+{
+ ref *perrordict, *pErrorNames;
+
+ if (dict_find_string(systemdict, "errordict", &perrordict) <= 0 ||
+ dict_find_string(systemdict, "ErrorNames", &pErrorNames) <= 0
+ )
+ return_error(gs_error_undefined); /* errordict or ErrorNames not found?! */
+ return array_get(imemory, pErrorNames, (long)(-code - 1), perror_name);
+}
+
+/* Store an error string in $error.errorinfo. */
+/* This routine is here because of the proximity to the error handler. */
+int
+gs_errorinfo_put_string(i_ctx_t *i_ctx_p, const char *str)
+{
+ ref rstr;
+ ref *pderror;
+ int code = string_to_ref(str, &rstr, iimemory, "gs_errorinfo_put_string");
+
+ if (code < 0)
+ return code;
+ if (dict_find_string(systemdict, "$error", &pderror) <= 0 ||
+ !r_has_type(pderror, t_dictionary) ||
+ idict_put_string(pderror, "errorinfo", &rstr) < 0
+ )
+ return_error(gs_error_Fatal);
+ return 0;
+}
+
+/* Main interpreter. */
+/* If execution terminates normally, return gs_error_InterpreterExit. */
+/* If an error occurs, leave the current object in *perror_object */
+/* and return a (negative) error code. */
+static int
+interp(i_ctx_t **pi_ctx_p /* context for execution, updated if resched */,
+ const ref * pref /* object to interpret */,
+ ref * perror_object)
+{
+ i_ctx_t *i_ctx_p = *pi_ctx_p;
+ /*
+ * Note that iref may actually be either a ref * or a ref_packed *.
+ * Certain DEC compilers assume that a ref * is ref-aligned even if it
+ * is cast to a short *, and generate code on this assumption, leading
+ * to "unaligned access" errors. For this reason, we declare
+ * iref_packed, and use a macro to cast it to the more aligned type
+ * where necessary (which is almost everywhere it is used). This may
+ * lead to compiler warnings about "cast increases alignment
+ * requirements", but this is less harmful than expensive traps at run
+ * time.
+ */
+ register const ref_packed *iref_packed = (const ref_packed *)pref;
+ /*
+ * To make matters worse, some versions of gcc/egcs have a bug that
+ * leads them to assume that if iref_packed is EVER cast to a ref *,
+ * it is ALWAYS ref-aligned. We detect this in stdpre.h and provide
+ * the following workaround:
+ */
+#ifdef ALIGNMENT_ALIASING_BUG
+ const ref *iref_temp;
+# define IREF (iref_temp = (const ref *)iref_packed, iref_temp)
+#else
+# define IREF ((const ref *)iref_packed)
+#endif
+#define SET_IREF(rp) (iref_packed = (const ref_packed *)(rp))
+ register int icount = 0; /* # of consecutive tokens at iref */
+ register os_ptr iosp = osp; /* private copy of osp */
+ register es_ptr iesp = esp; /* private copy of esp */
+ int code;
+ ref token; /* token read from file or string, */
+ /* must be declared in this scope */
+ ref *pvalue;
+ ref refnull;
+ uint opindex; /* needed for oparrays */
+ os_ptr whichp;
+
+ /*
+ * We have to make the error information into a struct;
+ * otherwise, the Watcom compiler will assign it to registers
+ * strictly on the basis of textual frequency.
+ * We also have to use ref_assign_inline everywhere, and
+ * avoid direct assignments of refs, so that esi and edi
+ * will remain available on Intel processors.
+ */
+ struct interp_error_s {
+ int code;
+ int line;
+ const ref *obj;
+ ref full;
+ } ierror;
+
+ /*
+ * Get a pointer to the name table so that we can use the
+ * inline version of name_index_ref.
+ */
+ const name_table *const int_nt = imemory->gs_lib_ctx->gs_name_table;
+
+#define set_error(ecode)\
+ { ierror.code = ecode; ierror.line = __LINE__; }
+#define return_with_error(ecode, objp)\
+ { set_error(ecode); ierror.obj = objp; goto rwe; }
+#define return_with_error_iref(ecode)\
+ { set_error(ecode); goto rwei; }
+#define return_with_code_iref()\
+ { ierror.line = __LINE__; goto rweci; }
+#define return_with_stackoverflow(objp)\
+ { o_stack.requested = 1; return_with_error(gs_error_stackoverflow, objp); }
+#define return_with_stackoverflow_iref()\
+ { o_stack.requested = 1; return_with_error_iref(gs_error_stackoverflow); }
+/*
+ * If control reaches the special operators (x_add, etc.) as a result of
+ * interpreting an executable name, iref points to the name, not the
+ * operator, so the name rather than the operator becomes the error object,
+ * which is wrong. We detect and handle this case explicitly when an error
+ * occurs, so as not to slow down the non-error case.
+ */
+#define return_with_error_tx_op(err_code)\
+ { if (r_has_type(IREF, t_name)) {\
+ return_with_error(err_code, pvalue);\
+ } else {\
+ return_with_error_iref(err_code);\
+ }\
+ }
+
+ int *ticks_left = &imemory_system->gs_lib_ctx->gcsignal;
+
+#if defined(DEBUG_TRACE_PS_OPERATORS) || defined(DEBUG)
+ int (*call_operator_fn)(op_proc_t, i_ctx_t *) = do_call_operator;
+
+ if (gs_debug_c('!'))
+ call_operator_fn = do_call_operator_verbose;
+#endif
+
+ *ticks_left = i_ctx_p->time_slice_ticks;
+
+ make_null(&ierror.full);
+ ierror.obj = &ierror.full;
+ make_null(&refnull);
+ pvalue = &refnull;
+
+ /*
+ * If we exceed the VMThreshold, set *ticks_left to -100
+ * to alert the interpreter that we need to garbage collect.
+ */
+ set_gc_signal(i_ctx_p, -100);
+
+ esfile_clear_cache();
+ /*
+ * From here on, if icount > 0, iref and icount correspond
+ * to the top entry on the execution stack: icount is the count
+ * of sequential entries remaining AFTER the current one.
+ */
+#define IREF_NEXT(ip)\
+ ((const ref_packed *)((const ref *)(ip) + 1))
+#define IREF_NEXT_EITHER(ip)\
+ ( r_is_packed(ip) ? (ip) + 1 : IREF_NEXT(ip) )
+#define store_state(ep)\
+ ( icount > 0 ? (ep->value.const_refs = IREF + 1, r_set_size(ep, icount)) : 0 )
+#define store_state_short(ep)\
+ ( icount > 0 ? (ep->value.packed = iref_packed + 1, r_set_size(ep, icount)) : 0 )
+#define store_state_either(ep)\
+ ( icount > 0 ? (ep->value.packed = IREF_NEXT_EITHER(iref_packed), r_set_size(ep, icount)) : 0 )
+#define next()\
+ if ( --icount > 0 ) { iref_packed = IREF_NEXT(iref_packed); goto top; } else goto out
+#define next_short()\
+ if ( --icount <= 0 ) { if ( icount < 0 ) goto up; iesp--; }\
+ ++iref_packed; goto top
+#define next_either()\
+ if ( --icount <= 0 ) { if ( icount < 0 ) goto up; iesp--; }\
+ iref_packed = IREF_NEXT_EITHER(iref_packed); goto top
+
+#if !PACKED_SPECIAL_OPS
+# undef next_either
+# define next_either() next()
+# undef store_state_either
+# define store_state_either(ep) store_state(ep)
+#endif
+
+ /* We want to recognize executable arrays here, */
+ /* so we push the argument on the estack and enter */
+ /* the loop at the bottom. */
+ if (iesp >= estop)
+ return_with_error(gs_error_execstackoverflow, pref);
+ ++iesp;
+ ref_assign_inline(iesp, pref);
+ goto bot;
+ top:
+ /*
+ * This is the top of the interpreter loop.
+ * iref points to the ref being interpreted.
+ * Note that this might be an element of a packed array,
+ * not a real ref: we carefully arranged the first 16 bits of
+ * a ref and of a packed array element so they could be distinguished
+ * from each other. (See ghost.h and packed.h for more detail.)
+ */
+ INCR(top);
+#ifdef DEBUG
+ /* Do a little validation on the top o-stack entry. */
+ if (iosp >= osbot &&
+ (r_type(iosp) == t__invalid || r_type(iosp) >= tx_next_op)
+ ) {
+ mlprintf(imemory, "Invalid value on o-stack!\n");
+ return_with_error_iref(gs_error_Fatal);
+ }
+ if (gs_debug['I'] ||
+ (gs_debug['i'] &&
+ (r_is_packed(iref_packed) ?
+ r_packed_is_name(iref_packed) :
+ r_has_type(IREF, t_name)))
+ ) {
+ os_ptr save_osp = osp; /* avoid side-effects */
+ es_ptr save_esp = esp;
+
+ osp = iosp;
+ esp = iesp;
+ dmlprintf5(imemory, "d%u,e%u<%u>0x%lx(%d): ",
+ ref_stack_count(&d_stack), ref_stack_count(&e_stack),
+ ref_stack_count(&o_stack), (ulong)IREF, icount);
+ debug_print_ref(imemory, IREF);
+ if (iosp >= osbot) {
+ dmputs(imemory, " // ");
+ debug_print_ref(imemory, iosp);
+ }
+ dmputc(imemory, '\n');
+ osp = save_osp;
+ esp = save_esp;
+ dmflush(imemory);
+ }
+#endif
+/* Objects that have attributes (arrays, dictionaries, files, and strings) */
+/* use lit and exec; other objects use plain and plain_exec. */
+#define lit(t) type_xe_value(t, a_execute)
+#define exec(t) type_xe_value(t, a_execute + a_executable)
+#define nox(t) type_xe_value(t, 0)
+#define nox_exec(t) type_xe_value(t, a_executable)
+#define plain(t) type_xe_value(t, 0)
+#define plain_exec(t) type_xe_value(t, a_executable)
+ /*
+ * We have to populate enough cases of the switch statement to force
+ * some compilers to use a dispatch rather than a testing loop.
+ * What a nuisance!
+ */
+ switch (r_type_xe(iref_packed)) {
+ /* Access errors. */
+#define cases_invalid()\
+ case plain(t__invalid): case plain_exec(t__invalid)
+ cases_invalid():
+ return_with_error_iref(gs_error_Fatal);
+#define cases_nox()\
+ case nox_exec(t_array): case nox_exec(t_dictionary):\
+ case nox_exec(t_file): case nox_exec(t_string):\
+ case nox_exec(t_mixedarray): case nox_exec(t_shortarray)
+ cases_nox():
+ return_with_error_iref(gs_error_invalidaccess);
+ /*
+ * Literal objects. We have to enumerate all the types.
+ * In fact, we have to include some extra plain_exec entries
+ * just to populate the switch. We break them up into groups
+ * to avoid overflowing some preprocessors.
+ */
+#define cases_lit_1()\
+ case lit(t_array): case nox(t_array):\
+ case plain(t_boolean): case plain_exec(t_boolean):\
+ case lit(t_dictionary): case nox(t_dictionary)
+#define cases_lit_2()\
+ case lit(t_file): case nox(t_file):\
+ case plain(t_fontID): case plain_exec(t_fontID):\
+ case plain(t_integer): case plain_exec(t_integer):\
+ case plain(t_mark): case plain_exec(t_mark)
+#define cases_lit_3()\
+ case plain(t_name):\
+ case plain(t_null):\
+ case plain(t_oparray):\
+ case plain(t_operator)
+#define cases_lit_4()\
+ case plain(t_real): case plain_exec(t_real):\
+ case plain(t_save): case plain_exec(t_save):\
+ case lit(t_string): case nox(t_string)
+#define cases_lit_5()\
+ case lit(t_mixedarray): case nox(t_mixedarray):\
+ case lit(t_shortarray): case nox(t_shortarray):\
+ case plain(t_device): case plain_exec(t_device):\
+ case plain(t_struct): case plain_exec(t_struct):\
+ case plain(t_astruct): case plain_exec(t_astruct)
+ /* Executable arrays are treated as literals in direct execution. */
+#define cases_lit_array()\
+ case exec(t_array): case exec(t_mixedarray): case exec(t_shortarray)
+ cases_lit_1():
+ cases_lit_2():
+ cases_lit_3():
+ cases_lit_4():
+ cases_lit_5():
+ INCR(lit);
+ break;
+ cases_lit_array():
+ INCR(lit_array);
+ break;
+ /* Special operators. */
+ case plain_exec(tx_op_add):
+x_add: INCR(x_add);
+ osp = iosp; /* sync o_stack */
+ if ((code = zop_add(i_ctx_p)) < 0)
+ return_with_error_tx_op(code);
+ iosp--;
+ next_either();
+ case plain_exec(tx_op_def):
+x_def: INCR(x_def);
+ osp = iosp; /* sync o_stack */
+ if ((code = zop_def(i_ctx_p)) < 0)
+ return_with_error_tx_op(code);
+ iosp -= 2;
+ next_either();
+ case plain_exec(tx_op_dup):
+x_dup: INCR(x_dup);
+ if (iosp < osbot)
+ return_with_error_tx_op(gs_error_stackunderflow);
+ if (iosp >= ostop) {
+ o_stack.requested = 1;
+ return_with_error_tx_op(gs_error_stackoverflow);
+ }
+ iosp++;
+ ref_assign_inline(iosp, iosp - 1);
+ next_either();
+ case plain_exec(tx_op_exch):
+x_exch: INCR(x_exch);
+ if (iosp <= osbot)
+ return_with_error_tx_op(gs_error_stackunderflow);
+ ref_assign_inline(&token, iosp);
+ ref_assign_inline(iosp, iosp - 1);
+ ref_assign_inline(iosp - 1, &token);
+ next_either();
+ case plain_exec(tx_op_if):
+x_if: INCR(x_if);
+ if (!r_is_proc(iosp))
+ return_with_error_tx_op(check_proc_failed(iosp));
+ if (!r_has_type(iosp - 1, t_boolean))
+ return_with_error_tx_op((iosp <= osbot ?
+ gs_error_stackunderflow : gs_error_typecheck));
+ if (!iosp[-1].value.boolval) {
+ iosp -= 2;
+ next_either();
+ }
+ if (iesp >= estop)
+ return_with_error_tx_op(gs_error_execstackoverflow);
+ store_state_either(iesp);
+ whichp = iosp;
+ iosp -= 2;
+ goto ifup;
+ case plain_exec(tx_op_ifelse):
+x_ifelse: INCR(x_ifelse);
+ if (!r_is_proc(iosp))
+ return_with_error_tx_op(check_proc_failed(iosp));
+ if (!r_is_proc(iosp - 1))
+ return_with_error_tx_op(check_proc_failed(iosp - 1));
+ if (!r_has_type(iosp - 2, t_boolean))
+ return_with_error_tx_op((iosp < osbot + 2 ?
+ gs_error_stackunderflow : gs_error_typecheck));
+ if (iesp >= estop)
+ return_with_error_tx_op(gs_error_execstackoverflow);
+ store_state_either(iesp);
+ whichp = (iosp[-2].value.boolval ? iosp - 1 : iosp);
+ iosp -= 3;
+ /* Open code "up" for the array case(s) */
+ ifup:if ((icount = r_size(whichp) - 1) <= 0) {
+ if (icount < 0)
+ goto up; /* 0-element proc */
+ SET_IREF(whichp->value.refs); /* 1-element proc */
+ if (--(*ticks_left) > 0)
+ goto top;
+ }
+ ++iesp;
+ /* Do a ref_assign, but also set iref. */
+ iesp->tas = whichp->tas;
+ SET_IREF(iesp->value.refs = whichp->value.refs);
+ if (--(*ticks_left) > 0)
+ goto top;
+ goto slice;
+ case plain_exec(tx_op_index):
+x_index: INCR(x_index);
+ osp = iosp; /* zindex references o_stack */
+ if ((code = zindex(i_ctx_p)) < 0)
+ return_with_error_tx_op(code);
+ next_either();
+ case plain_exec(tx_op_pop):
+x_pop: INCR(x_pop);
+ if (iosp < osbot)
+ return_with_error_tx_op(gs_error_stackunderflow);
+ iosp--;
+ next_either();
+ case plain_exec(tx_op_roll):
+x_roll: INCR(x_roll);
+ osp = iosp; /* zroll references o_stack */
+ if ((code = zroll(i_ctx_p)) < 0)
+ return_with_error_tx_op(code);
+ iosp -= 2;
+ next_either();
+ case plain_exec(tx_op_sub):
+x_sub: INCR(x_sub);
+ osp = iosp; /* sync o_stack */
+ if ((code = zop_sub(i_ctx_p)) < 0)
+ return_with_error_tx_op(code);
+ iosp--;
+ next_either();
+ /* Executable types. */
+ case plain_exec(t_null):
+ goto bot;
+ case plain_exec(t_oparray):
+ /* Replace with the definition and go again. */
+ INCR(exec_array);
+ opindex = op_index(IREF);
+ pvalue = (ref *)IREF->value.const_refs;
+ opst: /* Prepare to call a t_oparray procedure in *pvalue. */
+ store_state(iesp);
+ oppr: /* Record the stack depths in case of failure. */
+ if (iesp >= estop - 4)
+ return_with_error_iref(gs_error_execstackoverflow);
+ iesp += 5;
+ osp = iosp; /* ref_stack_count_inline needs this */
+ make_mark_estack(iesp - 4, es_other, oparray_cleanup);
+ make_int(iesp - 3, opindex); /* for .errorexec effect */
+ make_int(iesp - 2, ref_stack_count_inline(&o_stack));
+ make_int(iesp - 1, ref_stack_count_inline(&d_stack));
+ make_op_estack(iesp, oparray_pop);
+ goto pr;
+ prst: /* Prepare to call the procedure (array) in *pvalue. */
+ store_state(iesp);
+ pr: /* Call the array in *pvalue. State has been stored. */
+ /* We want to do this check before assigning icount so icount is correct
+ * in the event of a gs_error_execstackoverflow
+ */
+ if (iesp >= estop) {
+ return_with_error_iref(gs_error_execstackoverflow);
+ }
+ if ((icount = r_size(pvalue) - 1) <= 0) {
+ if (icount < 0)
+ goto up; /* 0-element proc */
+ SET_IREF(pvalue->value.refs); /* 1-element proc */
+ if (--(*ticks_left) > 0)
+ goto top;
+ }
+ ++iesp;
+ /* Do a ref_assign, but also set iref. */
+ iesp->tas = pvalue->tas;
+ SET_IREF(iesp->value.refs = pvalue->value.refs);
+ if (--(*ticks_left) > 0)
+ goto top;
+ goto slice;
+ case plain_exec(t_operator):
+ INCR(exec_operator);
+ if (--(*ticks_left) <= 0) { /* The following doesn't work, */
+ /* and I can't figure out why. */
+/****** goto sst; ******/
+ }
+ esp = iesp; /* save for operator */
+ osp = iosp; /* ditto */
+ /* Operator routines take osp as an argument. */
+ /* This is just a convenience, since they adjust */
+ /* osp themselves to reflect the results. */
+ /* Operators that (net) push information on the */
+ /* operand stack must check for overflow: */
+ /* this normally happens automatically through */
+ /* the push macro (in oper.h). */
+ /* Operators that do not typecheck their operands, */
+ /* or take a variable number of arguments, */
+ /* must check explicitly for stack underflow. */
+ /* (See oper.h for more detail.) */
+ /* Note that each case must set iosp = osp: */
+ /* this is so we can switch on code without having to */
+ /* store it and reload it (for dumb compilers). */
+ switch (code = call_operator(real_opproc(IREF), i_ctx_p)) {
+ case 0: /* normal case */
+ case 1: /* alternative success case */
+ iosp = osp;
+ next();
+ case o_push_estack: /* store the state and go to up */
+ store_state(iesp);
+ opush:iosp = osp;
+ iesp = esp;
+ if (--(*ticks_left) > 0)
+ goto up;
+ goto slice;
+ case o_pop_estack: /* just go to up */
+ opop:iosp = osp;
+ if (esp == iesp)
+ goto bot;
+ iesp = esp;
+ goto up;
+ case gs_error_Remap_Color:
+oe_remap: store_state(iesp);
+remap: if (iesp + 2 >= estop) {
+ esp = iesp;
+ code = ref_stack_extend(&e_stack, 2);
+ if (code < 0)
+ return_with_error_iref(code);
+ iesp = esp;
+ }
+ packed_get(imemory, iref_packed, iesp + 1);
+ make_oper(iesp + 2, 0,
+ r_ptr(&istate->remap_color_info,
+ int_remap_color_info_t)->proc);
+ iesp += 2;
+ goto up;
+ }
+ iosp = osp;
+ iesp = esp;
+ return_with_code_iref();
+ case plain_exec(t_name):
+ INCR(exec_name);
+ pvalue = IREF->value.pname->pvalue;
+ if (!pv_valid(pvalue)) {
+ uint nidx = names_index(int_nt, IREF);
+ uint htemp = 0;
+
+ INCR(find_name);
+ if ((pvalue = dict_find_name_by_index_inline(nidx, htemp)) == 0)
+ return_with_error_iref(gs_error_undefined);
+ }
+ /* Dispatch on the type of the value. */
+ /* Again, we have to over-populate the switch. */
+ switch (r_type_xe(pvalue)) {
+ cases_invalid():
+ return_with_error_iref(gs_error_Fatal);
+ cases_nox(): /* access errors */
+ return_with_error_iref(gs_error_invalidaccess);
+ cases_lit_1():
+ cases_lit_2():
+ cases_lit_3():
+ cases_lit_4():
+ cases_lit_5():
+ INCR(name_lit);
+ /* Just push the value */
+ if (iosp >= ostop)
+ return_with_stackoverflow(pvalue);
+ ++iosp;
+ ref_assign_inline(iosp, pvalue);
+ next();
+ case exec(t_array):
+ case exec(t_mixedarray):
+ case exec(t_shortarray):
+ INCR(name_proc);
+ /* This is an executable procedure, execute it. */
+ goto prst;
+ case plain_exec(tx_op_add):
+ goto x_add;
+ case plain_exec(tx_op_def):
+ goto x_def;
+ case plain_exec(tx_op_dup):
+ goto x_dup;
+ case plain_exec(tx_op_exch):
+ goto x_exch;
+ case plain_exec(tx_op_if):
+ goto x_if;
+ case plain_exec(tx_op_ifelse):
+ goto x_ifelse;
+ case plain_exec(tx_op_index):
+ goto x_index;
+ case plain_exec(tx_op_pop):
+ goto x_pop;
+ case plain_exec(tx_op_roll):
+ goto x_roll;
+ case plain_exec(tx_op_sub):
+ goto x_sub;
+ case plain_exec(t_null):
+ goto bot;
+ case plain_exec(t_oparray):
+ INCR(name_oparray);
+ opindex = op_index(pvalue);
+ pvalue = (ref *)pvalue->value.const_refs;
+ goto opst;
+ case plain_exec(t_operator):
+ INCR(name_operator);
+ { /* Shortcut for operators. */
+ /* See above for the logic. */
+ if (--(*ticks_left) <= 0) { /* The following doesn't work, */
+ /* and I can't figure out why. */
+/****** goto sst; ******/
+ }
+ esp = iesp;
+ osp = iosp;
+ switch (code = call_operator(real_opproc(pvalue),
+ i_ctx_p)
+ ) {
+ case 0: /* normal case */
+ case 1: /* alternative success case */
+ iosp = osp;
+ next();
+ case o_push_estack:
+ store_state(iesp);
+ goto opush;
+ case o_pop_estack:
+ goto opop;
+ case gs_error_Remap_Color:
+ goto oe_remap;
+ }
+ iosp = osp;
+ iesp = esp;
+ return_with_error(code, pvalue);
+ }
+ case plain_exec(t_name):
+ case exec(t_file):
+ case exec(t_string):
+ default:
+ /* Not a procedure, reinterpret it. */
+ store_state(iesp);
+ icount = 0;
+ SET_IREF(pvalue);
+ goto top;
+ }
+ case exec(t_file):
+ { /* Executable file. Read the next token and interpret it. */
+ stream *s;
+ scanner_state sstate;
+
+ check_read_known_file(i_ctx_p, s, IREF, return_with_error_iref);
+ rt:
+ if (iosp >= ostop) /* check early */
+ return_with_stackoverflow_iref();
+ osp = iosp; /* gs_scan_token uses ostack */
+ gs_scanner_init_options(&sstate, IREF, i_ctx_p->scanner_options);
+ again:
+ code = gs_scan_token(i_ctx_p, &token, &sstate);
+ iosp = osp; /* ditto */
+ switch (code) {
+ case 0: /* read a token */
+ /* It's worth checking for literals, which make up */
+ /* the majority of input tokens, before storing the */
+ /* state on the e-stack. Note that because of //, */
+ /* the token may have *any* type and attributes. */
+ /* Note also that executable arrays aren't executed */
+ /* at the top level -- they're treated as literals. */
+ if (!r_has_attr(&token, a_executable) ||
+ r_is_array(&token)
+ ) { /* If gs_scan_token used the o-stack, */
+ /* we know we can do a push now; if not, */
+ /* the pre-check is still valid. */
+ iosp++;
+ ref_assign_inline(iosp, &token);
+ goto rt;
+ }
+ store_state(iesp);
+ /* Push the file on the e-stack */
+ if (iesp >= estop)
+ return_with_error_iref(gs_error_execstackoverflow);
+ esfile_set_cache(++iesp);
+ ref_assign_inline(iesp, IREF);
+ SET_IREF(&token);
+ icount = 0;
+ goto top;
+ case gs_error_undefined: /* //name undefined */
+ gs_scanner_error_object(i_ctx_p, &sstate, &token);
+ return_with_error(code, &token);
+ case scan_EOF: /* end of file */
+ esfile_clear_cache();
+ goto bot;
+ case scan_BOS:
+ /* Binary object sequences */
+ /* ARE executed at the top level. */
+ store_state(iesp);
+ /* Push the file on the e-stack */
+ if (iesp >= estop)
+ return_with_error_iref(gs_error_execstackoverflow);
+ esfile_set_cache(++iesp);
+ ref_assign_inline(iesp, IREF);
+ pvalue = &token;
+ goto pr;
+ case scan_Refill:
+ store_state(iesp);
+ /* iref may point into the exec stack; */
+ /* save its referent now. */
+ ref_assign_inline(&token, IREF);
+ /* Push the file on the e-stack */
+ if (iesp >= estop)
+ return_with_error_iref(gs_error_execstackoverflow);
+ ++iesp;
+ ref_assign_inline(iesp, &token);
+ esp = iesp;
+ osp = iosp;
+ code = gs_scan_handle_refill(i_ctx_p, &sstate, true,
+ ztokenexec_continue);
+ scan_cont:
+ iosp = osp;
+ iesp = esp;
+ switch (code) {
+ case 0:
+ iesp--; /* don't push the file */
+ goto again; /* stacks are unchanged */
+ case o_push_estack:
+ esfile_clear_cache();
+ if (--(*ticks_left) > 0)
+ goto up;
+ goto slice;
+ }
+ /* must be an error */
+ iesp--; /* don't push the file */
+ return_with_code_iref();
+ case scan_Comment:
+ case scan_DSC_Comment: {
+ /* See scan_Refill above for comments. */
+ ref file_token;
+
+ store_state(iesp);
+ ref_assign_inline(&file_token, IREF);
+ if (iesp >= estop)
+ return_with_error_iref(gs_error_execstackoverflow);
+ ++iesp;
+ ref_assign_inline(iesp, &file_token);
+ esp = iesp;
+ osp = iosp;
+ code = ztoken_handle_comment(i_ctx_p,
+ &sstate, &token,
+ code, true, true,
+ ztokenexec_continue);
+ }
+ goto scan_cont;
+ default: /* error */
+ ref_assign_inline(&token, IREF);
+ gs_scanner_error_object(i_ctx_p, &sstate, &token);
+ return_with_error(code, &token);
+ }
+ }
+ case exec(t_string):
+ { /* Executable string. Read a token and interpret it. */
+ stream ss;
+ scanner_state sstate;
+
+ s_init(&ss, NULL);
+ sread_string(&ss, IREF->value.bytes, r_size(IREF));
+ gs_scanner_init_stream_options(&sstate, &ss, SCAN_FROM_STRING);
+ osp = iosp; /* gs_scan_token uses ostack */
+ code = gs_scan_token(i_ctx_p, &token, &sstate);
+ iosp = osp; /* ditto */
+ switch (code) {
+ case 0: /* read a token */
+ case scan_BOS: /* binary object sequence */
+ store_state(iesp);
+ /* If the updated string isn't empty, push it back */
+ /* on the e-stack. */
+ {
+ /* This is just the available buffer size, so
+ a signed int is plenty big
+ */
+ int size = sbufavailable(&ss);
+
+ if (size > 0) {
+ if (iesp >= estop)
+ return_with_error_iref(gs_error_execstackoverflow);
+ ++iesp;
+ iesp->tas.type_attrs = IREF->tas.type_attrs;
+ iesp->value.const_bytes = sbufptr(&ss);
+ r_set_size(iesp, size);
+ }
+ }
+ if (code == 0) {
+ SET_IREF(&token);
+ icount = 0;
+ goto top;
+ }
+ /* Handle BOS specially */
+ pvalue = &token;
+ goto pr;
+ case scan_EOF: /* end of string */
+ goto bot;
+ case scan_Refill: /* error */
+ code = gs_note_error(gs_error_syntaxerror);
+ /* fall through */
+ default: /* error */
+ ref_assign_inline(&token, IREF);
+ gs_scanner_error_object(i_ctx_p, &sstate, &token);
+ return_with_error(code, &token);
+ }
+ }
+ /* Handle packed arrays here by re-dispatching. */
+ /* This also picks up some anomalous cases of non-packed arrays. */
+ default:
+ {
+ uint index;
+
+ switch (*iref_packed >> r_packed_type_shift) {
+ case pt_full_ref:
+ case pt_full_ref + 1:
+ INCR(p_full);
+ if (iosp >= ostop)
+ return_with_stackoverflow_iref();
+ /* We know this can't be an executable object */
+ /* requiring special handling, so we just push it. */
+ ++iosp;
+ /* We know that refs are properly aligned: */
+ /* see packed.h for details. */
+ ref_assign_inline(iosp, IREF);
+ next();
+ case pt_executable_operator:
+ index = *iref_packed & packed_value_mask;
+ if (--(*ticks_left) <= 0) { /* The following doesn't work, */
+ /* and I can't figure out why. */
+/****** goto sst_short; ******/
+ }
+ if (!op_index_is_operator(index)) {
+ INCR(p_exec_oparray);
+ store_state_short(iesp);
+ opindex = index;
+ /* Call the operator procedure. */
+ index -= op_def_count;
+ pvalue = (ref *)
+ (index < r_size(&i_ctx_p->op_array_table_global.table) ?
+ i_ctx_p->op_array_table_global.table.value.const_refs +
+ index :
+ i_ctx_p->op_array_table_local.table.value.const_refs +
+ (index - r_size(&i_ctx_p->op_array_table_global.table)));
+ goto oppr;
+ }
+ INCR(p_exec_operator);
+ /* See the main plain_exec(t_operator) case */
+ /* for details of what happens here. */
+#if PACKED_SPECIAL_OPS
+ /*
+ * We arranged in iinit.c that the special ops
+ * have operator indices starting at 1.
+ *
+ * The (int) cast in the next line is required
+ * because some compilers don't allow arithmetic
+ * involving two different enumerated types.
+ */
+# define case_xop(xop) case xop - (int)tx_op + 1
+ switch (index) {
+ case_xop(tx_op_add):goto x_add;
+ case_xop(tx_op_def):goto x_def;
+ case_xop(tx_op_dup):goto x_dup;
+ case_xop(tx_op_exch):goto x_exch;
+ case_xop(tx_op_if):goto x_if;
+ case_xop(tx_op_ifelse):goto x_ifelse;
+ case_xop(tx_op_index):goto x_index;
+ case_xop(tx_op_pop):goto x_pop;
+ case_xop(tx_op_roll):goto x_roll;
+ case_xop(tx_op_sub):goto x_sub;
+ case 0: /* for dumb compilers */
+ default:
+ ;
+ }
+# undef case_xop
+#endif
+ INCR(p_exec_non_x_operator);
+ esp = iesp;
+ osp = iosp;
+ switch (code = call_operator(op_index_proc(index), i_ctx_p)) {
+ case 0:
+ case 1:
+ iosp = osp;
+ next_short();
+ case o_push_estack:
+ store_state_short(iesp);
+ goto opush;
+ case o_pop_estack:
+ iosp = osp;
+ if (esp == iesp) {
+ next_short();
+ }
+ iesp = esp;
+ goto up;
+ case gs_error_Remap_Color:
+ store_state_short(iesp);
+ goto remap;
+ }
+ iosp = osp;
+ iesp = esp;
+ return_with_code_iref();
+ case pt_integer:
+ INCR(p_integer);
+ if (iosp >= ostop)
+ return_with_stackoverflow_iref();
+ ++iosp;
+ make_int(iosp,
+ ((int)*iref_packed & packed_int_mask) +
+ packed_min_intval);
+ next_short();
+ case pt_literal_name:
+ INCR(p_lit_name);
+ {
+ uint nidx = *iref_packed & packed_value_mask;
+
+ if (iosp >= ostop)
+ return_with_stackoverflow_iref();
+ ++iosp;
+ name_index_ref_inline(int_nt, nidx, iosp);
+ next_short();
+ }
+ case pt_executable_name:
+ INCR(p_exec_name);
+ {
+ uint nidx = *iref_packed & packed_value_mask;
+
+ pvalue = name_index_ptr_inline(int_nt, nidx)->pvalue;
+ if (!pv_valid(pvalue)) {
+ uint htemp = 0;
+
+ INCR(p_find_name);
+ if ((pvalue = dict_find_name_by_index_inline(nidx, htemp)) == 0) {
+ names_index_ref(int_nt, nidx, &token);
+ return_with_error(gs_error_undefined, &token);
+ }
+ }
+ if (r_has_masked_attrs(pvalue, a_execute, a_execute + a_executable)) { /* Literal, push it. */
+ INCR(p_name_lit);
+ if (iosp >= ostop)
+ return_with_stackoverflow_iref();
+ ++iosp;
+ ref_assign_inline(iosp, pvalue);
+ next_short();
+ }
+ if (r_is_proc(pvalue)) { /* This is an executable procedure, */
+ /* execute it. */
+ INCR(p_name_proc);
+ store_state_short(iesp);
+ goto pr;
+ }
+ /* Not a literal or procedure, reinterpret it. */
+ store_state_short(iesp);
+ icount = 0;
+ SET_IREF(pvalue);
+ goto top;
+ }
+ /* default can't happen here */
+ }
+ }
+ }
+ /* Literal type, just push it. */
+ if (iosp >= ostop)
+ return_with_stackoverflow_iref();
+ ++iosp;
+ ref_assign_inline(iosp, IREF);
+ bot:next();
+ out: /* At most 1 more token in the current procedure. */
+ /* (We already decremented icount.) */
+ if (!icount) {
+ /* Pop the execution stack for tail recursion. */
+ iesp--;
+ iref_packed = IREF_NEXT(iref_packed);
+ goto top;
+ }
+ up:if (--(*ticks_left) < 0)
+ goto slice;
+ /* See if there is anything left on the execution stack. */
+ if (!r_is_proc(iesp)) {
+ SET_IREF(iesp--);
+ icount = 0;
+ goto top;
+ }
+ SET_IREF(iesp->value.refs); /* next element of array */
+ icount = r_size(iesp) - 1;
+ if (icount <= 0) { /* <= 1 more elements */
+ iesp--; /* pop, or tail recursion */
+ if (icount < 0)
+ goto up;
+ }
+ goto top;
+ sched: /* We've just called a scheduling procedure. */
+ /* The interpreter state is in memory; iref is not current. */
+ if (code < 0) {
+ set_error(code);
+ /*
+ * We need a real object to return as the error object.
+ * (It only has to last long enough to store in
+ * *perror_object.)
+ */
+ make_null_proc(&ierror.full);
+ SET_IREF(ierror.obj = &ierror.full);
+ goto error_exit;
+ }
+ /* Reload state information from memory. */
+ iosp = osp;
+ iesp = esp;
+ goto up;
+#if 0 /****** ****** ***** */
+ sst: /* Time-slice, but push the current object first. */
+ store_state(iesp);
+ if (iesp >= estop)
+ return_with_error_iref(gs_error_execstackoverflow);
+ iesp++;
+ ref_assign_inline(iesp, iref);
+#endif /****** ****** ***** */
+ slice: /* It's time to time-slice or garbage collect. */
+ /* iref is not live, so we don't need to do a store_state. */
+ osp = iosp;
+ esp = iesp;
+ /* If *ticks_left <= -100, we need to GC now. */
+ if ((*ticks_left) <= -100) { /* We need to garbage collect now. */
+ *pi_ctx_p = i_ctx_p;
+ code = interp_reclaim(pi_ctx_p, -1);
+ i_ctx_p = *pi_ctx_p;
+ } else
+ code = 0;
+ *ticks_left = i_ctx_p->time_slice_ticks;
+ set_code_on_interrupt(imemory, &code);
+ goto sched;
+
+ /* Error exits. */
+
+ rweci:
+ ierror.code = code;
+ rwei:
+ ierror.obj = IREF;
+ rwe:
+ if (!r_is_packed(iref_packed))
+ store_state(iesp);
+ else {
+ /*
+ * We need a real object to return as the error object.
+ * (It only has to last long enough to store in *perror_object.)
+ */
+ packed_get(imemory, (const ref_packed *)ierror.obj, &ierror.full);
+ store_state_short(iesp);
+ if (IREF == ierror.obj)
+ SET_IREF(&ierror.full);
+ ierror.obj = &ierror.full;
+ }
+ error_exit:
+ if (GS_ERROR_IS_INTERRUPT(ierror.code)) { /* We must push the current object being interpreted */
+ /* back on the e-stack so it will be re-executed. */
+ /* Currently, this is always an executable operator, */
+ /* but it might be something else someday if we check */
+ /* for interrupts in the interpreter loop itself. */
+ if (iesp >= estop)
+ ierror.code = gs_error_execstackoverflow;
+ else {
+ iesp++;
+ ref_assign_inline(iesp, IREF);
+ }
+ }
+ esp = iesp;
+ osp = iosp;
+ ref_assign_inline(perror_object, ierror.obj);
+#ifdef DEBUG
+ if (ierror.code == gs_error_InterpreterExit) {
+ /* Do not call gs_log_error to reduce the noise. */
+ return gs_error_InterpreterExit;
+ }
+#endif
+ return gs_log_error(ierror.code, __FILE__, ierror.line);
+}
+
+/* Pop the bookkeeping information for a normal exit from a t_oparray. */
+static int
+oparray_pop(i_ctx_t *i_ctx_p)
+{
+ esp -= 4;
+ return o_pop_estack;
+}
+
+/* Restore the stack pointers after an error inside a t_oparray procedure. */
+/* This procedure is called only from pop_estack. */
+static int
+oparray_cleanup(i_ctx_t *i_ctx_p)
+{ /* esp points just below the cleanup procedure. */
+ es_ptr ep = esp;
+ uint ocount_old = (uint) ep[3].value.intval;
+ uint dcount_old = (uint) ep[4].value.intval;
+ uint ocount = ref_stack_count(&o_stack);
+ uint dcount = ref_stack_count(&d_stack);
+
+ if (ocount > ocount_old)
+ ref_stack_pop(&o_stack, ocount - ocount_old);
+ if (dcount > dcount_old) {
+ ref_stack_pop(&d_stack, dcount - dcount_old);
+ dict_set_top();
+ }
+ return 0;
+}
+
+/* Don't restore the stack pointers. */
+static int
+oparray_no_cleanup(i_ctx_t *i_ctx_p)
+{
+ return 0;
+}
+
+/* Find the innermost oparray. */
+static ref *
+oparray_find(i_ctx_t *i_ctx_p)
+{
+ long i;
+ ref *ep;
+
+ for (i = 0; (ep = ref_stack_index(&e_stack, i)) != 0; ++i) {
+ if (r_is_estack_mark(ep) &&
+ (ep->value.opproc == oparray_cleanup ||
+ ep->value.opproc == oparray_no_cleanup)
+ )
+ return ep;
+ }
+ return 0;
+}
+
+/* <errorobj> <obj> .errorexec ... */
+/* Execute an object, substituting errorobj for the 'command' if an error */
+/* occurs during the execution. Cf .execfile (in zfile.c). */
+static int
+zerrorexec(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_op(2);
+ check_estack(4); /* mark/cleanup, errobj, pop, obj */
+ push_mark_estack(es_other, errorexec_cleanup);
+ *++esp = op[-1];
+ push_op_estack(errorexec_pop);
+ code = zexec(i_ctx_p);
+ if (code >= 0)
+ pop(1);
+ else
+ esp -= 3; /* undo our additions to estack */
+ return code;
+}
+
+/* - .finderrorobject <errorobj> true */
+/* - .finderrorobject false */
+/* If we are within an .errorexec or oparray, return the error object */
+/* and true, otherwise return false. */
+static int
+zfinderrorobject(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref errobj;
+
+ if (errorexec_find(i_ctx_p, &errobj)) {
+ push(2);
+ op[-1] = errobj;
+ make_true(op);
+ } else {
+ push(1);
+ make_false(op);
+ }
+ return 0;
+}
+
+/*
+ * Find the innermost .errorexec or oparray. If there is an oparray, or a
+ * .errorexec with errobj != null, store it in *perror_object and return 1,
+ * otherwise return 0;
+ */
+int
+errorexec_find(i_ctx_t *i_ctx_p, ref *perror_object)
+{
+ long i;
+ const ref *ep;
+
+ for (i = 0; (ep = ref_stack_index(&e_stack, i)) != 0; ++i) {
+ if (r_is_estack_mark(ep)) {
+ if (ep->value.opproc == oparray_cleanup) {
+ /* See oppr: above. */
+ uint opindex = (uint)ep[1].value.intval;
+ if (opindex == 0) /* internal operator, ignore */
+ continue;
+ op_index_ref(imemory, opindex, perror_object);
+ return 1;
+ }
+ if (ep->value.opproc == oparray_no_cleanup)
+ return 0; /* protection disabled */
+ if (ep->value.opproc == errorexec_cleanup) {
+ if (r_has_type(ep + 1, t_null))
+ return 0;
+ *perror_object = ep[1]; /* see .errorexec above */
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Pop the bookkeeping information on a normal exit from .errorexec. */
+static int
+errorexec_pop(i_ctx_t *i_ctx_p)
+{
+ esp -= 2;
+ return o_pop_estack;
+}
+
+/* Clean up when unwinding the stack on an error. (No action needed.) */
+static int
+errorexec_cleanup(i_ctx_t *i_ctx_p)
+{
+ return 0;
+}
+
+/* <bool> .setstackprotect - */
+/* Set whether to protect the stack for the innermost oparray. */
+static int
+zsetstackprotect(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *ep = oparray_find(i_ctx_p);
+
+ check_type(*op, t_boolean);
+ if (ep == 0)
+ return_error(gs_error_rangecheck);
+ ep->value.opproc =
+ (op->value.boolval ? oparray_cleanup : oparray_no_cleanup);
+ pop(1);
+ return 0;
+}
+
+/* - .currentstackprotect <bool> */
+/* Return the stack protection status. */
+static int
+zcurrentstackprotect(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *ep = oparray_find(i_ctx_p);
+
+ if (ep == 0)
+ return_error(gs_error_rangecheck);
+ push(1);
+ make_bool(op, ep->value.opproc == oparray_cleanup);
+ return 0;
+}
+
+static int
+zactonuel(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_bool(op, !!gs_lib_ctx_get_act_on_uel((gs_memory_t *)(i_ctx_p->memory.current)));
+ return 0;
+}