/src/ghostpdl/psi/zcontrol.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Control operators */ |
18 | | #include "string_.h" |
19 | | #include "ghost.h" |
20 | | #include "stream.h" |
21 | | #include "oper.h" |
22 | | #include "estack.h" |
23 | | #include "files.h" |
24 | | #include "ipacked.h" |
25 | | #include "iutil.h" |
26 | | #include "store.h" |
27 | | #include "interp.h" |
28 | | |
29 | | /* Forward references */ |
30 | | static int check_for_exec(const_os_ptr); |
31 | | static int no_cleanup(i_ctx_t *); |
32 | | static uint count_exec_stack(i_ctx_t *, bool); |
33 | | static uint count_to_stopped(i_ctx_t *, long); |
34 | | static int unmatched_exit(os_ptr, op_proc_t); |
35 | | |
36 | | /* See the comment in opdef.h for an invariant which allows */ |
37 | | /* more efficient implementation of for, loop, and repeat. */ |
38 | | |
39 | | /* <[test0 body0 ...]> .cond - */ |
40 | | static int cond_continue(i_ctx_t *); |
41 | | static int |
42 | | zcond(i_ctx_t *i_ctx_p) |
43 | 0 | { |
44 | 0 | os_ptr op = osp; |
45 | 0 | es_ptr ep = esp; |
46 | |
|
47 | 0 | check_op(1); |
48 | | /* Push the array on the e-stack and call the continuation. */ |
49 | 0 | if (!r_is_array(op)) |
50 | 0 | return_op_typecheck(op); |
51 | 0 | check_execute(*op); |
52 | 0 | if ((r_size(op) & 1) != 0) |
53 | 0 | return_error(gs_error_rangecheck); |
54 | 0 | if (r_size(op) == 0) |
55 | 0 | return zpop(i_ctx_p); |
56 | 0 | check_estack(3); |
57 | 0 | esp = ep += 3; |
58 | 0 | ref_assign(ep - 2, op); /* the cond body */ |
59 | 0 | make_op_estack(ep - 1, cond_continue); |
60 | 0 | array_get(imemory, op, 0L, ep); |
61 | 0 | esfile_check_cache(); |
62 | 0 | pop(1); |
63 | 0 | return o_push_estack; |
64 | 0 | } |
65 | | static int |
66 | | cond_continue(i_ctx_t *i_ctx_p) |
67 | 0 | { |
68 | 0 | os_ptr op = osp; |
69 | 0 | es_ptr ep = esp; |
70 | 0 | int code; |
71 | |
|
72 | 0 | check_op(1); |
73 | | /* The top element of the e-stack is the remaining tail of */ |
74 | | /* the cond body. The top element of the o-stack should be */ |
75 | | /* the (boolean) result of the test that is the first element */ |
76 | | /* of the tail. */ |
77 | 0 | check_type(*op, t_boolean); |
78 | 0 | if (op->value.boolval) { /* true */ |
79 | 0 | array_get(imemory, ep, 1L, ep); |
80 | 0 | esfile_check_cache(); |
81 | 0 | code = o_pop_estack; |
82 | 0 | } else if (r_size(ep) > 2) { /* false */ |
83 | 0 | const ref_packed *elts = ep->value.packed; |
84 | |
|
85 | 0 | check_estack(2); |
86 | 0 | ep = esp; |
87 | 0 | r_dec_size(ep, 2); |
88 | 0 | elts = packed_next(elts); |
89 | 0 | elts = packed_next(elts); |
90 | 0 | ep->value.packed = elts; |
91 | 0 | array_get(imemory, ep, 0L, ep + 2); |
92 | 0 | make_op_estack(ep + 1, cond_continue); |
93 | 0 | esp = ep + 2; |
94 | 0 | esfile_check_cache(); |
95 | 0 | code = o_push_estack; |
96 | 0 | } else { /* fall off end of cond */ |
97 | 0 | esp = ep - 1; |
98 | 0 | code = o_pop_estack; |
99 | 0 | } |
100 | 0 | pop(1); /* get rid of the boolean */ |
101 | 0 | return code; |
102 | 0 | } |
103 | | |
104 | | /* <obj> exec - */ |
105 | | int |
106 | | zexec(i_ctx_t *i_ctx_p) |
107 | 2.06G | { |
108 | 2.06G | os_ptr op = osp; |
109 | 2.06G | int code; |
110 | | |
111 | 2.06G | check_op(1); |
112 | 2.06G | code = check_for_exec(op); |
113 | 2.06G | if (code < 0) { |
114 | 0 | return code; |
115 | 0 | } |
116 | 2.06G | if (!r_has_attr(op, a_executable)) { |
117 | 10.5M | return 0; /* shortcut, literal object just gets pushed back */ |
118 | 10.5M | } |
119 | 2.05G | check_estack(1); |
120 | 2.05G | ++esp; |
121 | 2.05G | ref_assign(esp, op); |
122 | 2.05G | esfile_check_cache(); |
123 | 2.05G | pop(1); |
124 | 2.05G | return o_push_estack; |
125 | 2.05G | } |
126 | | |
127 | | /* <obj1> ... <objn> <n> .execn - */ |
128 | | static int |
129 | | zexecn(i_ctx_t *i_ctx_p) |
130 | 4.80M | { |
131 | 4.80M | os_ptr op = osp; |
132 | 4.80M | uint n, i; |
133 | 4.80M | es_ptr esp_orig; |
134 | | |
135 | 4.80M | check_op(1); |
136 | 4.80M | check_int_leu(*op, max_uint - 1); |
137 | 4.80M | n = (uint) op->value.intval; |
138 | 4.80M | check_op(n + 1); |
139 | 4.80M | check_estack(n); |
140 | 4.80M | esp_orig = esp; |
141 | 18.1M | for (i = 0; i < n; ++i) { |
142 | 13.3M | const ref *rp = ref_stack_index(&o_stack, (long)(i + 1)); |
143 | | |
144 | 13.3M | if (rp == NULL) |
145 | 0 | continue; |
146 | | |
147 | | /* Make sure this object is legal to execute. */ |
148 | 13.3M | if (ref_type_uses_access(r_type(rp))) { |
149 | 6.07M | if (!r_has_attr(rp, a_execute) && |
150 | 6.07M | r_has_attr(rp, a_executable) |
151 | 6.07M | ) { |
152 | 0 | esp = esp_orig; |
153 | 0 | return_error(gs_error_invalidaccess); |
154 | 0 | } |
155 | 6.07M | } |
156 | | /* Executable nulls have a special meaning on the e-stack, */ |
157 | | /* so since they are no-ops, don't push them. */ |
158 | 13.3M | if (!r_has_type_attrs(rp, t_null, a_executable)) { |
159 | 13.3M | ++esp; |
160 | 13.3M | ref_assign(esp, rp); |
161 | 13.3M | } |
162 | 13.3M | } |
163 | 4.80M | esfile_check_cache(); |
164 | 4.80M | pop(n + 1); |
165 | 4.80M | return o_push_estack; |
166 | 4.80M | } |
167 | | |
168 | | /* <array> <executable> .runandhide <obj> */ |
169 | | /* before executing <executable>, <array> is been removed from */ |
170 | | /* the operand stack and placed on the execstack with attributes */ |
171 | | /* changed to 'noaccess'. */ |
172 | | /* After execution, the array will be placed on the top of the */ |
173 | | /* operand stack (on top of any elemetns pushed by <executable> */ |
174 | | /* for both the normal case and for the error case. */ |
175 | | static int end_runandhide(i_ctx_t *); |
176 | | static int err_end_runandhide(i_ctx_t *); |
177 | | static int |
178 | | zrunandhide(i_ctx_t *i_ctx_p) |
179 | 0 | { |
180 | 0 | os_ptr op = osp; |
181 | 0 | es_ptr ep; |
182 | |
|
183 | 0 | check_op(2); |
184 | 0 | if (!r_is_array(op - 1)) |
185 | 0 | return_op_typecheck(op); |
186 | 0 | if (!r_has_attr(op, a_executable)) |
187 | 0 | return 0; /* literal object just gets pushed back */ |
188 | 0 | check_estack(5); |
189 | 0 | ep = esp += 5; |
190 | 0 | make_mark_estack(ep - 4, es_other, err_end_runandhide); /* error case */ |
191 | 0 | make_op_estack(ep - 1, end_runandhide); /* normal case */ |
192 | 0 | ref_assign(ep, op); |
193 | | /* Store the object we are hiding and it's current tas.type_attrs */ |
194 | | /* on the exec stack then change to 'noaccess' */ |
195 | 0 | make_int(ep - 3, (int)op[-1].tas.type_attrs); |
196 | 0 | ref_assign(ep - 2, op - 1); |
197 | 0 | r_clear_attrs(ep - 2, a_all); |
198 | | /* replace the array with a special kind of mark that has a_read access */ |
199 | 0 | esfile_check_cache(); |
200 | 0 | pop(2); |
201 | 0 | return o_push_estack; |
202 | 0 | } |
203 | | static int |
204 | | runandhide_restore_hidden(i_ctx_t *i_ctx_p, ref *obj, ref *attrs) |
205 | 0 | { |
206 | 0 | os_ptr op = osp; |
207 | |
|
208 | 0 | push(1); |
209 | | /* restore the hidden_object and its type_attrs */ |
210 | 0 | ref_assign(op, obj); |
211 | 0 | r_clear_attrs(op, a_all); |
212 | 0 | r_set_attrs(op, attrs->value.intval); |
213 | 0 | return 0; |
214 | 0 | } |
215 | | |
216 | | /* - %end_runandhide hiddenobject */ |
217 | | static int |
218 | | end_runandhide(i_ctx_t *i_ctx_p) |
219 | 0 | { |
220 | 0 | int code; |
221 | |
|
222 | 0 | if ((code = runandhide_restore_hidden(i_ctx_p, esp, esp - 1)) < 0) { |
223 | 0 | esp -= 2; |
224 | 0 | return code; |
225 | 0 | } |
226 | 0 | esp -= 2; /* pop the hidden value and its atributes */ |
227 | 0 | return o_pop_estack; |
228 | 0 | } |
229 | | |
230 | | /* restore hidden object for error returns */ |
231 | | static int |
232 | | err_end_runandhide(i_ctx_t *i_ctx_p) |
233 | 0 | { |
234 | 0 | int code; |
235 | |
|
236 | 0 | if ((code = runandhide_restore_hidden(i_ctx_p, esp + 3, esp + 2)) < 0) |
237 | 0 | return code; |
238 | 0 | return 0; |
239 | 0 | } |
240 | | |
241 | | /* <bool> <proc> if - */ |
242 | | int |
243 | | zif(i_ctx_t *i_ctx_p) |
244 | 0 | { |
245 | 0 | os_ptr op = osp; |
246 | |
|
247 | 0 | check_op(2); |
248 | 0 | check_proc(*op); |
249 | 0 | check_type(op[-1], t_boolean); |
250 | 0 | if (op[-1].value.boolval) { |
251 | 0 | check_estack(1); |
252 | 0 | ++esp; |
253 | 0 | ref_assign(esp, op); |
254 | 0 | esfile_check_cache(); |
255 | 0 | } |
256 | 0 | pop(2); |
257 | 0 | return o_push_estack; |
258 | 0 | } |
259 | | |
260 | | /* <bool> <proc_true> <proc_false> ifelse - */ |
261 | | int |
262 | | zifelse(i_ctx_t *i_ctx_p) |
263 | 0 | { |
264 | 0 | os_ptr op = osp; |
265 | |
|
266 | 0 | check_op(3); |
267 | 0 | check_proc(*op); |
268 | 0 | check_proc(op[-1]); |
269 | 0 | check_type(op[-2], t_boolean); |
270 | 0 | check_estack(1); |
271 | 0 | ++esp; |
272 | 0 | if (op[-2].value.boolval) { |
273 | 0 | ref_assign(esp, op - 1); |
274 | 0 | } else { |
275 | 0 | ref_assign(esp, op); |
276 | 0 | } |
277 | 0 | esfile_check_cache(); |
278 | 0 | pop(3); |
279 | 0 | return o_push_estack; |
280 | 0 | } |
281 | | |
282 | | /* <init> <step> <limit> <proc> for - */ |
283 | | static int |
284 | | for_pos_int_continue(i_ctx_t *), |
285 | | for_neg_int_continue(i_ctx_t *), |
286 | | for_real_continue(i_ctx_t *); |
287 | | int |
288 | | zfor(i_ctx_t *i_ctx_p) |
289 | 37.5M | { |
290 | 37.5M | os_ptr op = osp; |
291 | 37.5M | register es_ptr ep; |
292 | 37.5M | int code; |
293 | 37.5M | float params[3]; |
294 | | |
295 | 37.5M | check_op(4); |
296 | | /* Mostly undocumented, and somewhat bizarre Adobe behavior discovered */ |
297 | | /* with the CET (28-05) and FTS (124-01) is that the proc is not run */ |
298 | | /* if BOTH the initial value and increment are zero. */ |
299 | 37.5M | if ((code = float_params(op - 1, 3, params)) < 0) |
300 | 12 | return code; |
301 | 37.5M | if ( params[0] == 0.0 && params[1] == 0.0 ) { |
302 | 102 | pop(4); /* don't run the proc */ |
303 | 102 | return 0; |
304 | 102 | } |
305 | 37.5M | check_estack(7); |
306 | 37.5M | ep = esp + 6; |
307 | 37.5M | check_proc(*op); |
308 | | /* Push a mark, the control variable set to the initial value, */ |
309 | | /* the increment, the limit, and the procedure, */ |
310 | | /* and invoke the continuation operator. */ |
311 | 37.5M | if (r_has_type(op - 3, t_integer) && |
312 | 37.5M | r_has_type(op - 2, t_integer) |
313 | 37.5M | ) { |
314 | 29.1M | make_int(ep - 4, op[-3].value.intval); |
315 | 29.1M | make_int(ep - 3, op[-2].value.intval); |
316 | 29.1M | switch (r_type(op - 1)) { |
317 | 29.1M | case t_integer: |
318 | 29.1M | make_int(ep - 2, op[-1].value.intval); |
319 | 29.1M | break; |
320 | 16 | case t_real: |
321 | 16 | make_int(ep - 2, (ps_int)op[-1].value.realval); |
322 | 16 | break; |
323 | 0 | default: |
324 | 0 | return_op_typecheck(op - 1); |
325 | 29.1M | } |
326 | 29.1M | if (ep[-3].value.intval >= 0) |
327 | 29.1M | make_op_estack(ep, for_pos_int_continue); |
328 | 21.8M | else |
329 | 29.1M | make_op_estack(ep, for_neg_int_continue); |
330 | 29.1M | } else { |
331 | 8.37M | make_real(ep - 4, params[0]); |
332 | 8.37M | make_real(ep - 3, params[1]); |
333 | 8.37M | make_real(ep - 2, params[2]); |
334 | 8.37M | make_op_estack(ep, for_real_continue); |
335 | 8.37M | } |
336 | 37.5M | make_mark_estack(ep - 5, es_for, no_cleanup); |
337 | 37.5M | ref_assign(ep - 1, op); |
338 | 37.5M | esp = ep; |
339 | 37.5M | pop(4); |
340 | 37.5M | return o_push_estack; |
341 | 37.5M | } |
342 | | /* Continuation operators for for, separate for positive integer, */ |
343 | | /* negative integer, and real. */ |
344 | | /* Execution stack contains mark, control variable, increment, */ |
345 | | /* limit, and procedure (procedure is topmost.) */ |
346 | | /* Continuation operator for positive integers. */ |
347 | | static int |
348 | | for_pos_int_continue(i_ctx_t *i_ctx_p) |
349 | 340M | { |
350 | 340M | os_ptr op = osp; |
351 | 340M | register es_ptr ep = esp; |
352 | 340M | ps_int var = ep[-3].value.intval; |
353 | | |
354 | 340M | if (var > ep[-1].value.intval) { |
355 | 7.26M | esp -= 5; /* pop everything */ |
356 | 7.26M | return o_pop_estack; |
357 | 7.26M | } |
358 | 340M | push(1); |
359 | 333M | make_int(op, var); |
360 | 333M | ep[-3].value.intval = var + ep[-2].value.intval; |
361 | 333M | ref_assign_inline(ep + 2, ep); /* saved proc */ |
362 | 333M | esp = ep + 2; |
363 | 333M | return o_push_estack; |
364 | 333M | } |
365 | | /* Continuation operator for negative integers. */ |
366 | | static int |
367 | | for_neg_int_continue(i_ctx_t *i_ctx_p) |
368 | 178M | { |
369 | 178M | os_ptr op = osp; |
370 | 178M | register es_ptr ep = esp; |
371 | 178M | ps_int var = ep[-3].value.intval; |
372 | | |
373 | 178M | if (var < ep[-1].value.intval) { |
374 | 21.8M | esp -= 5; /* pop everything */ |
375 | 21.8M | return o_pop_estack; |
376 | 21.8M | } |
377 | 178M | push(1); |
378 | 156M | make_int(op, var); |
379 | 156M | ep[-3].value.intval = var + ep[-2].value.intval; |
380 | 156M | ref_assign(ep + 2, ep); /* saved proc */ |
381 | 156M | esp = ep + 2; |
382 | 156M | return o_push_estack; |
383 | 156M | } |
384 | | /* Continuation operator for reals. */ |
385 | | static int |
386 | | for_real_continue(i_ctx_t *i_ctx_p) |
387 | 16.4M | { |
388 | 16.4M | os_ptr op = osp; |
389 | 16.4M | es_ptr ep = esp; |
390 | 16.4M | float var = ep[-3].value.realval; |
391 | 16.4M | float incr = ep[-2].value.realval; |
392 | | |
393 | 16.4M | if (incr >= 0 ? (var > ep[-1].value.realval) : |
394 | 16.4M | (var < ep[-1].value.realval) |
395 | 16.4M | ) { |
396 | 8.37M | esp -= 5; /* pop everything */ |
397 | 8.37M | return o_pop_estack; |
398 | 8.37M | } |
399 | 16.4M | push(1); |
400 | 8.05M | ref_assign(op, ep - 3); |
401 | 8.05M | ep[-3].value.realval = var + incr; |
402 | 8.05M | esp = ep + 2; |
403 | 8.05M | ref_assign(ep + 2, ep); /* saved proc */ |
404 | 8.05M | return o_push_estack; |
405 | 8.05M | } |
406 | | |
407 | | /* |
408 | | * Here we provide an internal variant of 'for' that enumerates the values |
409 | | * A, ((N-1)*A+1*B)/N, ((N-2)*A+2*B)/N, ..., B precisely. The arguments are |
410 | | * A (real), N (integer), and B (real). We need this for loading caches such |
411 | | * as the transfer function cache. |
412 | | * |
413 | | * NOTE: This computation must match the SAMPLE_LOOP_VALUE macro in gscie.h. |
414 | | */ |
415 | | static int for_samples_continue(i_ctx_t *); |
416 | | /* <first> <count> <last> <proc> %for_samples - */ |
417 | | int |
418 | | zfor_samples(i_ctx_t *i_ctx_p) |
419 | 384k | { |
420 | 384k | os_ptr op = osp; |
421 | 384k | es_ptr ep; |
422 | | |
423 | 384k | check_op(4); |
424 | 384k | check_type(op[-3], t_real); |
425 | 384k | check_type(op[-2], t_integer); |
426 | 384k | check_type(op[-1], t_real); |
427 | 384k | check_proc(*op); |
428 | 384k | check_estack(8); |
429 | 384k | ep = esp + 7; |
430 | 384k | make_mark_estack(ep - 6, es_for, no_cleanup); |
431 | 384k | make_int(ep - 5, 0); |
432 | 384k | memcpy(ep - 4, op - 3, 3 * sizeof(ref)); |
433 | 384k | ref_assign(ep - 1, op); |
434 | 384k | make_op_estack(ep, for_samples_continue); |
435 | 384k | esp = ep; |
436 | 384k | pop(4); |
437 | 384k | return o_push_estack; |
438 | 384k | } |
439 | | /* Continuation procedure */ |
440 | | static int |
441 | | for_samples_continue(i_ctx_t *i_ctx_p) |
442 | 98.7M | { |
443 | 98.7M | os_ptr op = osp; |
444 | 98.7M | es_ptr ep = esp; |
445 | 98.7M | int var = ep[-4].value.intval; |
446 | 98.7M | float a = ep[-3].value.realval; |
447 | 98.7M | int n = ep[-2].value.intval; |
448 | 98.7M | float b = ep[-1].value.realval; |
449 | | |
450 | 98.7M | if (var > n) { |
451 | 384k | esp -= 6; /* pop everything */ |
452 | 384k | return o_pop_estack; |
453 | 384k | } |
454 | 98.7M | push(1); |
455 | 98.3M | make_real(op, ((n - var) * a + var * b) / n); |
456 | 98.3M | ep[-4].value.intval = var + 1; |
457 | 98.3M | ref_assign_inline(ep + 2, ep); /* saved proc */ |
458 | 98.3M | esp = ep + 2; |
459 | 98.3M | return o_push_estack; |
460 | 98.3M | } |
461 | | |
462 | | /* <int> <proc> repeat - */ |
463 | | static int repeat_continue(i_ctx_t *); |
464 | | int |
465 | | zrepeat(i_ctx_t *i_ctx_p) |
466 | 11.9M | { |
467 | 11.9M | os_ptr op = osp; |
468 | | |
469 | 11.9M | check_op(2); |
470 | 11.9M | check_proc(*op); |
471 | 11.9M | check_type(op[-1], t_integer); |
472 | 11.9M | if (op[-1].value.intval < 0) |
473 | 0 | return_error(gs_error_rangecheck); |
474 | 11.9M | check_estack(5); |
475 | | /* Push a mark, the count, and the procedure, and invoke */ |
476 | | /* the continuation operator. */ |
477 | 11.9M | push_mark_estack(es_for, no_cleanup); |
478 | 11.9M | *++esp = op[-1]; |
479 | 11.9M | *++esp = *op; |
480 | 11.9M | make_op_estack(esp + 1, repeat_continue); |
481 | 11.9M | pop(2); |
482 | 11.9M | return repeat_continue(i_ctx_p); |
483 | 11.9M | } |
484 | | /* Continuation operator for repeat */ |
485 | | static int |
486 | | repeat_continue(i_ctx_t *i_ctx_p) |
487 | 290M | { |
488 | 290M | es_ptr ep = esp; /* saved proc */ |
489 | | |
490 | 290M | if (--(ep[-1].value.intval) >= 0) { /* continue */ |
491 | 278M | esp += 2; |
492 | 278M | ref_assign(esp, ep); |
493 | 278M | return o_push_estack; |
494 | 278M | } else { /* done */ |
495 | 11.9M | esp -= 3; /* pop mark, count, proc */ |
496 | 11.9M | return o_pop_estack; |
497 | 11.9M | } |
498 | 290M | } |
499 | | |
500 | | /* <proc> loop */ |
501 | | static int loop_continue(i_ctx_t *); |
502 | | static int |
503 | | zloop(i_ctx_t *i_ctx_p) |
504 | 21.8M | { |
505 | 21.8M | os_ptr op = osp; |
506 | | |
507 | 21.8M | check_op(1); |
508 | 21.8M | check_proc(*op); |
509 | 21.8M | check_estack(4); |
510 | | /* Push a mark and the procedure, and invoke */ |
511 | | /* the continuation operator. */ |
512 | 21.8M | push_mark_estack(es_for, no_cleanup); |
513 | 21.8M | *++esp = *op; |
514 | 21.8M | make_op_estack(esp + 1, loop_continue); |
515 | 21.8M | pop(1); |
516 | 21.8M | return loop_continue(i_ctx_p); |
517 | 21.8M | } |
518 | | /* Continuation operator for loop */ |
519 | | static int |
520 | | loop_continue(i_ctx_t *i_ctx_p) |
521 | 190M | { |
522 | 190M | register es_ptr ep = esp; /* saved proc */ |
523 | | |
524 | 190M | ref_assign(ep + 2, ep); |
525 | 190M | esp = ep + 2; |
526 | 190M | return o_push_estack; |
527 | 190M | } |
528 | | |
529 | | /* - exit - */ |
530 | | static int |
531 | | zexit(i_ctx_t *i_ctx_p) |
532 | 22.0M | { |
533 | 22.0M | os_ptr op = osp; |
534 | 22.0M | ref_stack_enum_t rsenum; |
535 | 22.0M | uint scanned = 0; |
536 | | |
537 | 22.0M | ref_stack_enum_begin(&rsenum, &e_stack); |
538 | 22.0M | do { |
539 | 22.0M | uint used = rsenum.size; |
540 | 22.0M | es_ptr ep = rsenum.ptr + used - 1; |
541 | 22.0M | uint count = used; |
542 | | |
543 | 88.0M | for (; count; count--, ep--) |
544 | 88.0M | if (r_is_estack_mark(ep)) |
545 | 22.0M | switch (estack_mark_index(ep)) { |
546 | 22.0M | case es_for: |
547 | 22.0M | pop_estack(i_ctx_p, scanned + (used - count + 1)); |
548 | 22.0M | return o_pop_estack; |
549 | 19 | case es_stopped: |
550 | 19 | return_error(gs_error_invalidexit); /* not a loop */ |
551 | 22.0M | } |
552 | 0 | scanned += used; |
553 | 0 | } while (ref_stack_enum_next(&rsenum)); |
554 | | /* No mark, quit. (per Adobe documentation) */ |
555 | 22.0M | push(2); |
556 | 0 | return unmatched_exit(op, zexit); |
557 | 0 | } |
558 | | |
559 | | /* |
560 | | * .stopped pushes the following on the e-stack: |
561 | | * - A mark with type = es_stopped and procedure = no_cleanup. |
562 | | * - The result to be pushed on a normal return. |
563 | | * - The signal mask for .stop. |
564 | | * - The procedure %stopped_push, to handle the normal return case. |
565 | | */ |
566 | | |
567 | | /* In the normal (no-error) case, pop the mask from the e-stack, */ |
568 | | /* and move the result to the o-stack. */ |
569 | | static int |
570 | | stopped_push(i_ctx_t *i_ctx_p) |
571 | 20.5M | { |
572 | 20.5M | os_ptr op = osp; |
573 | | |
574 | 20.5M | push(1); |
575 | 20.5M | *op = esp[-1]; |
576 | 20.5M | esp -= 3; |
577 | 20.5M | return o_pop_estack; |
578 | 20.5M | } |
579 | | |
580 | | /* - stop - */ |
581 | | /* Equivalent to true 1 .stop. */ |
582 | | /* This is implemented in C because if were a pseudo-operator, */ |
583 | | /* the stacks would get restored in case of an error. */ |
584 | | static int |
585 | | zstop(i_ctx_t *i_ctx_p) |
586 | 110M | { |
587 | 110M | os_ptr op = osp; |
588 | 110M | uint count = count_to_stopped(i_ctx_p, 1L); |
589 | | |
590 | 110M | if (count) { |
591 | | /* |
592 | | * If there are any t_oparrays on the e-stack, they will pop |
593 | | * any new items from the o-stack. Wait to push the 'true' |
594 | | * until we have run all the unwind procedures. |
595 | | */ |
596 | 110M | check_ostack(2); |
597 | 110M | pop_estack(i_ctx_p, count); |
598 | 110M | op = osp; |
599 | 110M | push(1); |
600 | 110M | make_true(op); |
601 | 110M | return o_pop_estack; |
602 | 110M | } |
603 | | /* No mark, quit. (per Adobe documentation) */ |
604 | 110M | push(2); |
605 | 0 | return unmatched_exit(op, zstop); |
606 | 0 | } |
607 | | |
608 | | /* <result> <mask> .stop - */ |
609 | | static int |
610 | | zzstop(i_ctx_t *i_ctx_p) |
611 | 0 | { |
612 | 0 | os_ptr op = osp; |
613 | 0 | uint count; |
614 | |
|
615 | 0 | check_op(2); |
616 | 0 | check_type(*op, t_integer); |
617 | 0 | count = count_to_stopped(i_ctx_p, op->value.intval); |
618 | 0 | if (count) { |
619 | | /* |
620 | | * If there are any t_oparrays on the e-stack, they will pop |
621 | | * any new items from the o-stack. Wait to push the result |
622 | | * until we have run all the unwind procedures. |
623 | | */ |
624 | 0 | ref save_result; |
625 | |
|
626 | 0 | check_op(2); |
627 | 0 | save_result = op[-1]; |
628 | 0 | pop(2); |
629 | 0 | pop_estack(i_ctx_p, count); |
630 | 0 | op = osp; |
631 | 0 | push(1); |
632 | 0 | *op = save_result; |
633 | 0 | return o_pop_estack; |
634 | 0 | } |
635 | | /* No mark, quit. (per Adobe documentation) */ |
636 | 0 | return unmatched_exit(op, zzstop); |
637 | 0 | } |
638 | | |
639 | | /* <obj> stopped <stopped> */ |
640 | | /* Equivalent to false 1 .stopped. */ |
641 | | /* This is implemented in C because if were a pseudo-operator, */ |
642 | | /* the stacks would get restored in case of an error. */ |
643 | | static int |
644 | | zstopped(i_ctx_t *i_ctx_p) |
645 | 9.29M | { |
646 | 9.29M | os_ptr op = osp; |
647 | | |
648 | 9.29M | check_op(1); |
649 | | /* Mark the execution stack, and push the default result */ |
650 | | /* in case control returns normally. */ |
651 | 9.29M | check_estack(5); |
652 | 9.29M | push_mark_estack(es_stopped, no_cleanup); |
653 | 9.29M | ++esp; |
654 | 9.29M | make_false(esp); /* save the result */ |
655 | 9.29M | ++esp; |
656 | 9.29M | make_int(esp, 1); /* save the signal mask */ |
657 | 9.29M | push_op_estack(stopped_push); |
658 | 9.29M | push_op_estack(zexec); /* execute the operand */ |
659 | 9.29M | return o_push_estack; |
660 | 9.29M | } |
661 | | |
662 | | /* <obj> <result> <mask> .stopped <result> */ |
663 | | static int |
664 | | zzstopped(i_ctx_t *i_ctx_p) |
665 | 122M | { |
666 | 122M | os_ptr op = osp; |
667 | 122M | check_type(*op, t_integer); |
668 | 122M | check_op(3); |
669 | | /* Mark the execution stack, and push the default result */ |
670 | | /* in case control returns normally. */ |
671 | 122M | check_estack(5); |
672 | 122M | push_mark_estack(es_stopped, no_cleanup); |
673 | 122M | *++esp = op[-1]; /* save the result */ |
674 | 122M | *++esp = *op; /* save the signal mask */ |
675 | 122M | push_op_estack(stopped_push); |
676 | 122M | push_op_estack(zexec); /* execute the operand */ |
677 | 122M | pop(2); |
678 | 122M | return o_push_estack; |
679 | 122M | } |
680 | | |
681 | | /* <mask> .instopped false */ |
682 | | /* <mask> .instopped <result> true */ |
683 | | static int |
684 | | zinstopped(i_ctx_t *i_ctx_p) |
685 | 111M | { |
686 | 111M | os_ptr op = osp; |
687 | 111M | uint count; |
688 | | |
689 | 111M | check_type(*op, t_integer); |
690 | 111M | count = count_to_stopped(i_ctx_p, op->value.intval); |
691 | 111M | if (count) { |
692 | 111M | push(1); |
693 | 111M | op[-1] = *ref_stack_index(&e_stack, count - 2); /* default result */ |
694 | 111M | make_true(op); |
695 | 111M | } else |
696 | 111M | make_false(op); |
697 | 111M | return 0; |
698 | 111M | } |
699 | | |
700 | | /* <include_marks> .countexecstack <int> */ |
701 | | /* - countexecstack <int> */ |
702 | | /* countexecstack is an operator solely for the sake of the Genoa tests. */ |
703 | | static int |
704 | | zcountexecstack(i_ctx_t *i_ctx_p) |
705 | 345k | { |
706 | 345k | os_ptr op = osp; |
707 | | |
708 | 345k | push(1); |
709 | 345k | make_int(op, count_exec_stack(i_ctx_p, false)); |
710 | 345k | return 0; |
711 | 345k | } |
712 | | static int |
713 | | zcountexecstack1(i_ctx_t *i_ctx_p) |
714 | 13.0k | { |
715 | 13.0k | os_ptr op = osp; |
716 | | |
717 | 13.0k | check_type(*op, t_boolean); |
718 | 13.0k | make_int(op, count_exec_stack(i_ctx_p, op->value.boolval)); |
719 | 13.0k | return 0; |
720 | 13.0k | } |
721 | | |
722 | | /* <array> <include_marks> .execstack <subarray> */ |
723 | | /* <array> execstack <subarray> */ |
724 | | /* execstack is an operator solely for the sake of the Genoa tests. */ |
725 | | static int execstack_continue(i_ctx_t *); |
726 | | static int execstack2_continue(i_ctx_t *); |
727 | | static int |
728 | | push_execstack(i_ctx_t *i_ctx_p, os_ptr op1, bool include_marks, |
729 | | op_proc_t cont) |
730 | 358k | { |
731 | 358k | uint size; |
732 | | /* |
733 | | * We can't do this directly, because the interpreter |
734 | | * might have cached some state. To force the interpreter |
735 | | * to update the stored state, we push a continuation on |
736 | | * the exec stack; the continuation is executed immediately, |
737 | | * and does the actual transfer. |
738 | | */ |
739 | 358k | uint depth; |
740 | | |
741 | 358k | if (!r_is_array(op1)) |
742 | 358k | return_op_typecheck(op1); |
743 | | /* Check the length before the write access per CET 28-03 */ |
744 | 358k | size = r_size(op1); |
745 | 358k | depth = count_exec_stack(i_ctx_p, include_marks); |
746 | 358k | if (depth > size) |
747 | 0 | return_error(gs_error_rangecheck); |
748 | 358k | check_write(*op1); |
749 | 358k | { |
750 | 358k | int code = ref_stack_store_check(&e_stack, op1, size, 0); |
751 | | |
752 | 358k | if (code < 0) |
753 | 0 | return code; |
754 | 358k | } |
755 | 358k | check_estack(1); |
756 | 358k | r_set_size(op1, depth); |
757 | 358k | push_op_estack(cont); |
758 | 358k | return o_push_estack; |
759 | 358k | } |
760 | | static int |
761 | | zexecstack(i_ctx_t *i_ctx_p) |
762 | 345k | { |
763 | 345k | os_ptr op = osp; |
764 | | |
765 | 345k | return push_execstack(i_ctx_p, op, false, execstack_continue); |
766 | 345k | } |
767 | | static int |
768 | | zexecstack2(i_ctx_t *i_ctx_p) |
769 | 13.0k | { |
770 | 13.0k | os_ptr op = osp; |
771 | | |
772 | 13.0k | check_type(*op, t_boolean); |
773 | 13.0k | return push_execstack(i_ctx_p, op - 1, op->value.boolval, execstack2_continue); |
774 | 13.0k | } |
775 | | /* Continuation operator to do the actual transfer. */ |
776 | | /* r_size(op1) was set just above. */ |
777 | | static int |
778 | | do_execstack(i_ctx_t *i_ctx_p, bool include_marks, bool include_oparrays, os_ptr op1) |
779 | 358k | { |
780 | 358k | os_ptr op = osp; |
781 | 358k | ref *arefs = op1->value.refs; |
782 | 358k | uint asize = r_size(op1); |
783 | 358k | uint i; |
784 | 358k | ref *rq; |
785 | | |
786 | | /* |
787 | | * Copy elements from the stack to the array, |
788 | | * optionally skipping executable nulls. |
789 | | * Clear the executable bit in any internal operators, and |
790 | | * convert t_structs and t_astructs (which can only appear |
791 | | * in connection with stack marks, which means that they will |
792 | | * probably be freed when unwinding) to something harmless. |
793 | | */ |
794 | 31.8M | for (i = 0, rq = arefs + asize; rq != arefs; ++i) { |
795 | 31.4M | const ref *rp = ref_stack_index(&e_stack, (long)i); |
796 | | |
797 | 31.4M | if (rp == NULL) |
798 | 0 | continue; |
799 | 31.4M | if (r_has_type_attrs(rp, t_null, a_executable) && !include_marks) |
800 | 5.26M | continue; |
801 | 26.1M | --rq; |
802 | 26.1M | ref_assign_old(op1, rq, rp, "execstack"); |
803 | 26.1M | switch (r_type(rq)) { |
804 | 5.63M | case t_operator: { |
805 | 5.63M | uint opidx = op_index(rq); |
806 | | |
807 | 5.63M | if (opidx == 0 || op_def_is_internal(op_index_def(opidx))) |
808 | 5.63M | r_clear_attrs(rq, a_executable); |
809 | 5.63M | break; |
810 | 0 | } |
811 | 5.90k | case t_struct: |
812 | 5.90k | case t_astruct: { |
813 | 5.90k | const char *tname = rq->value.pstruct ? |
814 | 5.90k | gs_struct_type_name_string( |
815 | 5.90k | gs_object_type(imemory, rq->value.pstruct)) |
816 | 5.90k | : "NULL"; |
817 | | |
818 | 5.90k | make_const_string(rq, a_readonly | avm_foreign, |
819 | 5.90k | strlen(tname), (const byte *)tname); |
820 | 5.90k | break; |
821 | 5.90k | } |
822 | 326k | case t_array: |
823 | 578k | case t_shortarray: |
824 | 6.55M | case t_mixedarray: |
825 | 6.55M | if (!include_oparrays && errorexec_find(i_ctx_p, rq) < 0) |
826 | 6.55M | make_null(rq); |
827 | 6.55M | break; |
828 | 13.9M | default: |
829 | 13.9M | ; |
830 | 26.1M | } |
831 | 26.1M | } |
832 | 358k | pop(op - op1); |
833 | 358k | return 0; |
834 | 358k | } |
835 | | static int |
836 | | execstack_continue(i_ctx_t *i_ctx_p) |
837 | 345k | { |
838 | 345k | os_ptr op = osp; |
839 | | |
840 | 345k | return do_execstack(i_ctx_p, false, false, op); |
841 | 345k | } |
842 | | static int |
843 | | execstack2_continue(i_ctx_t *i_ctx_p) |
844 | 13.0k | { |
845 | 13.0k | os_ptr op = osp; |
846 | | |
847 | 13.0k | return do_execstack(i_ctx_p, op->value.boolval, true, op - 1); |
848 | 13.0k | } |
849 | | |
850 | | /* - .needinput - */ |
851 | | static int |
852 | | zneedinput(i_ctx_t *i_ctx_p) |
853 | 1.78M | { |
854 | 1.78M | return gs_error_NeedInput; /* interpreter will exit to caller */ |
855 | 1.78M | } |
856 | | |
857 | | /* <obj> <int> .quit - */ |
858 | | static int |
859 | | zquit(i_ctx_t *i_ctx_p) |
860 | 694k | { |
861 | 694k | os_ptr op = osp; |
862 | | |
863 | 694k | check_op(2); |
864 | 694k | check_type(*op, t_integer); |
865 | 694k | return_error(gs_error_Quit); /* Interpreter will do the exit */ |
866 | 694k | } |
867 | | |
868 | | /* Get the current file from which the interpreter is reading. */ |
869 | | static ref * |
870 | | zget_current_file(i_ctx_t *i_ctx_p) |
871 | 595k | { |
872 | 595k | ref_stack_enum_t rsenum; |
873 | | |
874 | 595k | ref_stack_enum_begin(&rsenum, &e_stack); |
875 | 595k | do { |
876 | 595k | uint count = rsenum.size; |
877 | 595k | es_ptr ep = rsenum.ptr + count - 1; |
878 | | |
879 | 28.4M | for (; count; count--, ep--) |
880 | 28.4M | if (r_has_type_attrs(ep, t_file, a_executable)) |
881 | 595k | return ep; |
882 | 595k | } while (ref_stack_enum_next(&rsenum)); |
883 | 0 | return 0; |
884 | 595k | } |
885 | | |
886 | | /* - currentfile <file> */ |
887 | | int |
888 | | z_current_file(i_ctx_t *i_ctx_p, ref **s) |
889 | 114M | { |
890 | 114M | ref *fp; |
891 | | /* Check the cache first */ |
892 | 114M | if (esfile != 0) { |
893 | | #ifdef DEBUG |
894 | | /* Check that esfile is valid. */ |
895 | | ref *efp = zget_current_file(i_ctx_p); |
896 | | |
897 | | if (esfile != efp) { |
898 | | lprintf2("currentfile: esfile="PRI_INTPTR", efp="PRI_INTPTR"\n", |
899 | | (intptr_t) esfile, (intptr_t) efp); |
900 | | *s = efp; |
901 | | } else |
902 | | #endif |
903 | 113M | *s = esfile; |
904 | 113M | } else if ((fp = zget_current_file(i_ctx_p)) == 0) { /* Return an invalid file object. */ |
905 | 0 | *s = NULL; |
906 | 595k | } else { |
907 | 595k | *s = fp; |
908 | 595k | esfile_set_cache(fp); |
909 | 595k | } |
910 | 114M | return 0; |
911 | 114M | } |
912 | | static int |
913 | | zcurrentfile(i_ctx_t *i_ctx_p) |
914 | 114M | { |
915 | 114M | os_ptr op = osp; |
916 | 114M | ref *s; |
917 | 114M | int code; |
918 | | |
919 | 114M | push(1); |
920 | | |
921 | 114M | code = z_current_file(i_ctx_p, &s); |
922 | 114M | if (code < 0 || s == NULL) { |
923 | | /* This doesn't make a lot of sense to me, */ |
924 | | /* but it's what the PostScript manual specifies. */ |
925 | 0 | make_invalid_file(i_ctx_p, op); |
926 | 0 | } |
927 | 114M | else { |
928 | 114M | ref_assign(op, s); |
929 | 114M | } |
930 | | /* Make the returned value literal. */ |
931 | 114M | r_clear_attrs(op, a_executable); |
932 | 114M | return code; |
933 | 114M | } |
934 | | /* ------ Initialization procedure ------ */ |
935 | | |
936 | | /* We need to split the table because of the 16-element limit. */ |
937 | | const op_def zcontrol1_op_defs[] = { |
938 | | {"1.cond", zcond}, |
939 | | {"0countexecstack", zcountexecstack}, |
940 | | {"1.countexecstack", zcountexecstack1}, |
941 | | {"0currentfile", zcurrentfile}, |
942 | | {"1exec", zexec}, |
943 | | {"1.execn", zexecn}, |
944 | | {"1execstack", zexecstack}, |
945 | | {"2.execstack", zexecstack2}, |
946 | | {"0exit", zexit}, |
947 | | {"2if", zif}, |
948 | | {"3ifelse", zifelse}, |
949 | | {"0.instopped", zinstopped}, |
950 | | {"0.needinput", zneedinput}, |
951 | | op_def_end(0) |
952 | | }; |
953 | | const op_def zcontrol2_op_defs[] = { |
954 | | {"4for", zfor}, |
955 | | {"1loop", zloop}, |
956 | | {"2.quit", zquit}, |
957 | | {"2repeat", zrepeat}, |
958 | | {"0stop", zstop}, |
959 | | {"1.stop", zzstop}, |
960 | | {"1stopped", zstopped}, |
961 | | {"2.stopped", zzstopped}, |
962 | | op_def_end(0) |
963 | | }; |
964 | | const op_def zcontrol3_op_defs[] = { |
965 | | /* Internal operators */ |
966 | | {"1%cond_continue", cond_continue}, |
967 | | {"1%execstack_continue", execstack_continue}, |
968 | | {"2%execstack2_continue", execstack2_continue}, |
969 | | {"0%for_pos_int_continue", for_pos_int_continue}, |
970 | | {"0%for_neg_int_continue", for_neg_int_continue}, |
971 | | {"0%for_real_continue", for_real_continue}, |
972 | | {"4%for_samples", zfor_samples}, |
973 | | {"0%for_samples_continue", for_samples_continue}, |
974 | | {"0%loop_continue", loop_continue}, |
975 | | {"0%repeat_continue", repeat_continue}, |
976 | | {"0%stopped_push", stopped_push}, |
977 | | {"2.runandhide", zrunandhide}, |
978 | | {"0%end_runandhide", end_runandhide}, |
979 | | op_def_end(0) |
980 | | }; |
981 | | |
982 | | /* ------ Internal routines ------ */ |
983 | | |
984 | | /* |
985 | | * Check the operand of exec or stopped. Return 0 if OK to execute, or a |
986 | | * negative error code. We emulate an apparent bug in Adobe interpreters, |
987 | | * which cause an invalidaccess error when 'exec'ing a noaccess literal |
988 | | * (other than dictionaries). We also match the Adobe interpreters in that |
989 | | * we catch noaccess executable objects here, rather than waiting for the |
990 | | * interpreter to catch them, so that we can signal the error with the |
991 | | * object still on the operand stack. |
992 | | */ |
993 | | static int |
994 | | check_for_exec(const_os_ptr op) |
995 | 2.06G | { |
996 | 2.06G | if (!r_has_attr(op, a_execute) && /* only true if noaccess */ |
997 | 2.06G | ref_type_uses_access(r_type(op)) && |
998 | 2.06G | (r_has_attr(op, a_executable) || !r_has_type(op, t_dictionary)) |
999 | 2.06G | ) { |
1000 | 0 | return_error(gs_error_invalidaccess); |
1001 | 0 | } |
1002 | 2.06G | return 0; |
1003 | 2.06G | } |
1004 | | |
1005 | | /* Vacuous cleanup routine */ |
1006 | | static int |
1007 | | no_cleanup(i_ctx_t *i_ctx_p) |
1008 | 132M | { |
1009 | 132M | return 0; |
1010 | 132M | } |
1011 | | |
1012 | | /* |
1013 | | * Count the number of elements on the exec stack, with or without |
1014 | | * the normally invisible elements (*op is a Boolean that indicates this). |
1015 | | */ |
1016 | | static uint |
1017 | | count_exec_stack(i_ctx_t *i_ctx_p, bool include_marks) |
1018 | 717k | { |
1019 | 717k | uint count = ref_stack_count(&e_stack); |
1020 | | |
1021 | 717k | if (!include_marks) { |
1022 | 717k | uint i; |
1023 | | |
1024 | 63.6M | for (i = count; i--;) { |
1025 | 62.8M | ref *o; |
1026 | 62.8M | o = ref_stack_index(&e_stack, (long)i); |
1027 | 62.8M | if (o == NULL) |
1028 | 0 | continue; |
1029 | 62.8M | if (r_has_type_attrs(o, t_null, a_executable)) |
1030 | 10.5M | --count; |
1031 | 62.8M | } |
1032 | 717k | } |
1033 | 717k | return count; |
1034 | 717k | } |
1035 | | |
1036 | | /* |
1037 | | * Count the number of elements down to and including the first 'stopped' |
1038 | | * mark on the e-stack with a given mask. Return 0 if there is no 'stopped' |
1039 | | * mark. |
1040 | | */ |
1041 | | static uint |
1042 | | count_to_stopped(i_ctx_t *i_ctx_p, long mask) |
1043 | 221M | { |
1044 | 221M | ref_stack_enum_t rsenum; |
1045 | 221M | uint scanned = 0; |
1046 | | |
1047 | 221M | ref_stack_enum_begin(&rsenum, &e_stack); |
1048 | 221M | do { |
1049 | 221M | uint used = rsenum.size; |
1050 | 221M | es_ptr ep = rsenum.ptr + used - 1; |
1051 | 221M | uint count = used; |
1052 | | |
1053 | 2.33G | for (; count; count--, ep--) { |
1054 | 2.33G | if (r_is_estack_mark(ep)) { |
1055 | 433M | if (estack_mark_index(ep) == es_stopped && |
1056 | 433M | (ep[2].value.intval & mask) != 0) |
1057 | 221M | return scanned + (used - count + 1); |
1058 | 433M | } |
1059 | 2.33G | } |
1060 | 46 | scanned += used; |
1061 | 46 | } while (ref_stack_enum_next(&rsenum)); |
1062 | 46 | return 0; |
1063 | 221M | } |
1064 | | |
1065 | | /* |
1066 | | * Pop the e-stack, executing cleanup procedures as needed. |
1067 | | * We could make this more efficient using ref_stack_enum_*, |
1068 | | * but it isn't used enough to make this worthwhile. |
1069 | | */ |
1070 | | void |
1071 | | pop_estack(i_ctx_t *i_ctx_p, uint count) |
1072 | 133M | { |
1073 | 133M | uint idx = 0; |
1074 | 133M | uint popped = 0; |
1075 | | |
1076 | 133M | esfile_clear_cache(); |
1077 | 1.38G | for (; idx < count; idx++) { |
1078 | 1.25G | ref *ep = ref_stack_index(&e_stack, idx - popped); |
1079 | | |
1080 | 1.25G | if (ep == NULL) |
1081 | 0 | continue; |
1082 | | |
1083 | 1.25G | if (r_is_estack_mark(ep)) { |
1084 | | /* This exec stack juggling is to cope with hitting |
1085 | | exactly the bottom of a stack block. It is possible |
1086 | | to end up with the book keeping at the bottom of |
1087 | | one block, and the opproc at the top of the previous |
1088 | | block. If we pop everything in one go, the book keeping |
1089 | | entries disappear, so we pop to the start of the book |
1090 | | keeping values, call the cleanup, then pop the final |
1091 | | entry. |
1092 | | */ |
1093 | 238M | op_proc_t opproc = real_opproc(ep); |
1094 | 238M | ref_stack_pop(&e_stack, idx - popped); |
1095 | 238M | esp--; |
1096 | 238M | (*opproc) (i_ctx_p); |
1097 | 238M | esp++; |
1098 | 238M | ref_stack_pop(&e_stack, 1); |
1099 | 238M | popped = idx + 1; |
1100 | 238M | } |
1101 | 1.25G | } |
1102 | 133M | ref_stack_pop(&e_stack, count - popped); |
1103 | 133M | } |
1104 | | |
1105 | | /* |
1106 | | * Execute a quit in the case of an exit or stop with no appropriate |
1107 | | * enclosing control scope (loop or stopped). The caller has already |
1108 | | * ensured two free slots on the top of the o-stack. |
1109 | | */ |
1110 | | static int |
1111 | | unmatched_exit(os_ptr op, op_proc_t opproc) |
1112 | 0 | { |
1113 | 0 | make_oper(op - 1, 0, opproc); |
1114 | 0 | make_int(op, gs_error_invalidexit); |
1115 | 0 | return_error(gs_error_Quit); |
1116 | 0 | } |