• Subject: Re: ILE bug or "feature"?
  • From: Douglas Handy <dhandy1@xxxxxxxxxxxxx>
  • Date: Tue, 06 Feb 2001 19:16:48 -0500

Mark,

>  OK, pop quiz:  What are the values (under debug) of P1x through P5x?

The same pointers will exist on the stack, and since you defined them the same
in TEST2, you'll "see" the same values as TEST1 did.  Had you defined P1x
through P5x as different data types/lengths, what you'd "see" would be based on
the same starting address of the XTEST program variables.  For example, if you
defined P1x as 5 long, it would "see" it as 'ABCDE' and if you defined P4x as 5
long it would "see" it as 'xyz12'.  If you defined one as a packed decimal
number, you'd get a decimal data error if you referenced it.  If you defined P1x
as a 5-digit interger (5I 0), then P1x would equal 49602 (=x'C1C2').

>  Why the surprise?  I expected the program to either bomb (no parms were 
>passed to XTEST2) 

Since the CLLE program XTEST did not prototype the call to TEST2, there is no
parameter checking -- just like in OPM.

Think for a minute what you'd expect if all the programs OPM.  Would you still
be surprised?  That is in essence what you have here since you don't have
prototypes on both sides to validate parameter definitions.

>or the variables should have been cleared.  

I presume what you mean is you think the call stack should have been cleared.

>The pointers 
>from the first call seem to have been retained between calls.

"Retained" may be a little strong -- it is just the "garbage" which remains on
the call stack.

>  Is this a bug, design flaw, feature, other?

Feature.  I wouldn't call it either a bug or a design flaw.

>  To field the obvious question: Why are you calling a module that is 
>supposed to receive parms w/o any?
>1)  It was discovered accidentally.

Which is to say the bug was in the HLL source code, not ILE.  And since CALL is
not a prototyped call structure, the compilers won't tell you they don't match.

>2)  In an ILE environment, where a module is supposed to be designed for 
>reuse, this is an entirely possible scenario.

Prototypes guard against mismatched parameters -- assuming the prototypes are
correct.  If you create a prototype for an external call, say an API program,
but the prototype doesn't match what the program actually expects, then you
still have problems.  Witness the common misconception that an API which wants a
"Binary(4)" value would be (mis)coded in RPG as "4B 0".

Your "entirely possible scenario" would indicate the program/procedure may or
may not need to use all the parameters on each call.  This is accomodated in
prototypes by use of optional and/or omissable parameters.  Which incidentally,
leads to another common mistake.  They are *not* the same thing.  A parameter
may be one or the other, or even both.

An optional parameter does not even need to be listed on the call, and the
called program must use %parms or the PSDS parm count or whatever to determine
how many parameters are on the call stack.  In your example, if TEST2 should
allow calls with P1x to P5x as being optional, then the prototype should list
Options( *NoPass ) for each optional parameter, and check %parms before
referencing P1x to P5x.

If you fail to check %parms before referencing an optional parameter which was
not passed, then the proverbial "unpredictable results" depends on what happens
to be left over on the call stack.  In your example there were valid pointers,
and the pointers pointed to data which was actually compatible with the data
types of P1x to P5x.  Hence the debugger faithfully showed you the contents.

Omissable parameters, OTOH, must be listed on the call and therefore will have
an entry on the call stack.  But it will accept *OMIT which places a null
pointer on the call stack in lieu of a pointer to a valid program variable or
temporary (eg expression).

A program which allows Option( *Omit ) must first check if a valid pointer was
passed or a null pointer.  It can do this via CEETSTA or by comparing the
address to *Null.

If you code it as Options( *NoPass: *Omit ) you must make both tests before
trying to reference it in a RPG program.  First check %parms to make sure a
parameter entry is on the call stack.  Then make sure the stack entry is not a
null pointer.  Only after both of these tests can the program safely reference
the paramter value.

The CEExxx APIs are a classic example of where parameters are defined as
omissable but not optional.  If you define prototypes for them with Options(
*NoPass ) instead of Options( *Omit ), then you have created a time bomb.

If the caller passes every parameter, it works as expected.  If the caller
doesn't list every parameter, then the CEExxx API will check the call stack to
see if it finds a null pointer.  The program may run for a long time seeming to
work fine because the call stack didn't have valid pointers hanging around from
some previous call.  Then one day something else changes in your environment and
all of a sudden there is a (bogus) pointer on the call stack so the CEExxx API
thinks you had passed it, yielding "unpredictable" results.

Doug
+---
| This is the Midrange System Mailing List!
| To submit a new message, send your mail to MIDRANGE-L@midrange.com.
| To subscribe to this list send email to MIDRANGE-L-SUB@midrange.com.
| To unsubscribe from this list send email to MIDRANGE-L-UNSUB@midrange.com.
| Questions should be directed to the list owner/operator: david@midrange.com
+---

As an Amazon Associate we earn from qualifying purchases.

This thread ...

Follow-Ups:
Replies:

Follow On AppleNews
Return to Archive home page | Return to MIDRANGE.COM home page

This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].

Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.