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