🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.postscript
Article #3952

Re: Questions on PS errorhandling

#3952
From: Glenn Reid
Date: Tue, 06 Oct 1992 12:00
183 lines
8691 bytes
Rob Bradlee writes

> I've started maintaining a piece of "system" PostScript code that we  
include 
> with our RIPs.  This code has very careful error handling which has raised
> serveral questions in my mind.
> 
> 	1) Glenn Reid mentions someplace in "Thinking in PS" that 
> 		/routine errordict /typecheck get exec 
> 	   is bad form.  In his example on pg. 139 he uses the form
> 		errordict begin (concatenate) typecheck end
> 
> 	Would Glenn or any one else care to elaborate on the relative 
> 	benefits of "get exec" vs "begin...end".
> 
> 
> 	2) I see in Glenn's above example and in some of the code that 
> 	   I'm checking that the input is validated at the beginning
> 	   and if there is an error then one of the errordict routines
> 	   is called which has the effect of jumping out of the routine.
> 	   Therefore Glenn doesn't bother to put the rest of the code
> 	   in a ifelse clause.  For example:
> 
> 			dup type /arraytype ne {
> 				errordict begin (concatenate) typecheck end
> 				} if
> 			rest of code here
> 
> 	versus:
> 
> 
> 			dup type /arraytype ne {
> 				errordict begin (concatenate) typecheck end
> 				} {
> 				rest of code here
> 				} ifelse
> 
> 	I much prefer the former example because it's much simpler, but
> 	are there any unknown consequences?
> 
> 	3) Finally, one technique of validating a routines input is to
> 		{ dup } stopped
> 	   Is this any better than 
> 		count 1 ge

I couldn't remember writing any of this stuff, so I had to go back and
read it all again :-)  Mind like a sieve.  I thought I'd respond to this
instead of doing all of the things that are so critically important for
me to do right now.  Kind of cathartic to write about PostScript,
I guess.

Lots of thoughts spring to mind.  The main one is that PostScript is
a very wild and woolly language, and error checking is a game of
statistics more than anything.  What I mean by that is that almost
anything can be made to execute differently than it was originally
intended, since it's so easy to redefine the behavior of PostScript
operators.  So you have to choose how "likely" certain things are to
be reliable, and work from there.

To establish this point, I offer the following snippet of PostScript
code:

	/real_ifelse /ifelse load def
	/ifelse {
		exch real_ifelse
	} def

This replaces the "ifelse" operator with one that reverses the TRUE
and FALSE clauses of every conditional in your program.  Just try
to do a good job of preventing THAT from causing weird errors.  Or
redefining "def" or "exch" or something else fundamental that you
always assume work as advertised, even in an error handler.

The good thing is that these kinds of things are statistically
highly unlikely, and may not be worthwhile checking for.
Somewhere in the Green Book I made the plea not to write
"paranoid" code that gets all operators out of systemdict
and otherwise tries to be "robust" at the expense of ever working
correctly.

System code in an interpreter, however, is a different story.  Paranoia
is almost appropriate here.

Let's look at your last question first:

> 	3) Finally, one technique of validating a routines input is to
> 		{ dup } stopped
> 	   Is this any better than 
> 		count 1 ge

Whether to use "stopped" or the more specific "count 1 ge" is highly
contextual.  For an operator like "dup", there is only one possible
failure mode, and it's easy to test for (with "count").  And the likelihood
of somebody redefining "dup" is extremely small.  Note, however, that
if somebody *did* redefine "dup", errors would still be caught by the
"stopped" that would not be caught by the (more specific) "count 1 ge".
While I'm thinking of it, there is one other failure mode of "dup" that
will also be missed by the second test:  stackoverflow.  If the stack is
exactly full to containment, "dup" will fail, but so will "count 1 ge".

In a more general sense, "stopped" catches *any* error, and if you're not
sure what's going to happen, use "stopped".  If you're testing for a
specific condition like the existence of the "colorimage" operator, you
don't need to use "stopped".  For example:

	/colorimage where { } if

is preferable to:

	{ systemdict /colorimage get } stopped { } if

if what you're interested in is knowing if the "colorimage" operator
exists.  If you're actually going to call it, you might want to use
"stopped" so you can recover from any errors that might occur during its
execution.

The other question was about whether to use "get exec" or "begin ... end"
to execute code from a specific dictionary location.  There are issues
in both regards.  Here are some thoughts; for the purpose of discussion,
we'll label the two possibilities (A) and (B) as follows:

A.	systemdict /moveto get exec
B.	systemdict begin moveto end
C.	PasteUp_separation_dict /fill get exec
D.	PasteUp_separation_dict begin fill end

(A) has the distinct disadvantage that it *requires* the moveto operator
to be in a dictionary named "systemdict".  It usually is, of course, and
that's a pretty reliable example, but I have a vague recollection that at
least one PostScript clone used to put all the operators in "userdict",
which works fine as long as you just do name lookup, but would break under
(A) above.  Examples like (C), where you might have redefined "fill" to
do color separation, also have the limitation that you have to get the
dictionary exactly right.  Using "begin ... end" at least will look the
name up in the context of the whole dictionary stack.

The other issue that comes to play is what you have as the current
dictionary (on top of the dictionary stack) when you execute these variants.
(A) is much better than (B) in that it doesn't leave "systemdict" (usually
read-only) as the top-most dictionary on the dictionary stack.  (A) also
doesn't suffer from the possibility that that dictionary could still be
on the stack of the operation failed (important during error handling),
or in the event that somebody redefined "end" to do something silly (it
happens a lot, unfortunately, since "begin" and "end" are such nice names
to use for your own procedures).

It's a tossup.  If you know exactly what the operator does, exactly which
dictionary it's in, and you want to call it directly, circumventing the
dictionary name lookup, then it's better to use the "get exec" technique,
because it most closely preserves the execution environment of the original
operator, and does not require a matching "end".

Occasionally there's the possibility that the location of the name will
vary from one implementation to another (for example, invoking the
"letter" operator, which I think is sometimes in "systemdict",
sometimes in "userdict", sometimes in "statusdict" and sometimes
not present at all).  These situations benefit from the "begin...end"
technique.

There is one other issue.  Some operators (or your own procedures) may
rely on the contents of the dictionary in which they reside.  There
may be other names defined there that could be important.  I think the
reason I used "errordict begin (concatenate) typecheck end" was that
I didn't really know what the heck happens inside the "typecheck"
operator, and thought that it would be safer to put the whole dictionary
on the stack as context, in case there was something in there it needed.
There isn't much written about these error operators, and I was probably
just being superstitious.  Whatever was on the top of the dictionary
stack probably isn't too helpful at a time like this (error condition)
anyway, so it seemed safer to put "errordict" there.

I'm not sure exactly where in my writings I might have advanced the
"begin...end" notation as being superior to "get exec", but I'm pretty
sure I didn't think about it as long then as I am now.  Neither is
perfect, but I'm inclined to lean toward "get exec" for most purposes,
mostly because it doesn't leave a dictionary on top of the dictionary
stack for even a short period of time.

--
 Glenn Reid        		NeXTmail: glenn@rightbrain.com
 RightBrain Software		415-326-2974 (NeXTfax 326-2977)
 Palo Alto, California		Electronic Frontier Foundation, member #054
Technical Details
Message-ID: <902@rtbrain.rightbrain.com>
Path: arc1-ca.pugleaf.net!sparky!uunet!portal!rtbrain!rightbrain.com
References: <10129@cg-atla.UUCP>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!sparky!uunet!portal!rtbrain!rightbrain.com
From: glenn@rightbrain.com (Glenn Reid)
Newsgroups: comp.lang.postscript
Subject: Re: Questions on PS errorhandling
Message-ID: <902@rtbrain.rightbrain.com>
Date: 6 Oct 92 12:00:25 GMT
References: <10129@cg-atla.UUCP>
Sender: glenn@rightbrain.com
Reply-To: glenn@rightbrain.com
Distribution: comp
Lines: 183