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