|
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 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.