abcscnuk@csunb.csun.edu (Naoto Kimura (ACM)) (01/31/91)
Right now at work I'm having to deal with some badly written dBASE code.
The original programmer ended up doing just about everything that the
manual specifically warned not to do (like placing EXITs without having
a DO WHILE-ENDDO, placing RETURNs within DO WHILE-ENDDO, inserting ASCII
NULLs into the middle of a parameter string). Anyway, I've tried to
convince everybody at work that they shouldn't suspect my code whenever
anything goes wrong. So far, I've had only limited success in
convincing anybody because of the attitude of: "we've (almost) never had
any of these problems before we hired you." (please note the qualifier
that they keep leaving out) I've had to resort to doing a lot of
rewriting of code the past few months (without telling my boss about it)
to fix most of the bugs, and doing a bit of thinking on the mechanics of
how some of the problems were occurring.
The problem that kept occurring was that parameters weren't being passed
back when a subprogram exited, and this problem only manifested itself
only after compling the code under QuickSilver. The problem never
seemed to appear when run under dBASE or FoxBase. In investigating the
way dBASE handles memory variables and parameter passing, I discovered
that paramter passing is neither pass-by-value nor pass-by-reference.
Variables passed as parameters apparently get changed at the time the
subprogram returns to the caller. Anyway, here is what I think is going
on whenever you've got RETURNs within DO WHILE-ENDDOs:
Suppose we've got the following program files:
yuk.prg:
-------
barf = 'this is a test'
call icky with barf
? barf
icky.prg
--------
parameters wretch
wretch = 1
do while .t.
if wretch>=2
return
endif
wretch = wretch + 1
enddo
here's an example session and how I think parameter passing might be
getting screwed up:
. DO YUK
create new program context
read 'yuk.prg'
YUK.PRG> barf = 'this is a test'
create new PRIVATE variable 'barf'
assign 'barf' the value 'this is a test'
YUK.PRG> call icky with barf
create new program context
ICKY.PRG> parameters wretch
create PRIVATE variable 'wretch' (make note that 'wretch' is a
reference to 'barf' and it needs to be fixed up when we exit.)
assign 'wretch' the value of 'barf' ('this is a test')
ICKY.PRG> wretch = 1
assign 'wretch' the value of 1
ICKY.PRG> do while .t.
create DO WHILE context
evaluate condition, since true enter loop
ICKY.PRG> if wretch=>2
compare wretch to 2
since expression evaulates to false, fall through to endif
ICKY.PRG> return
skip
ICKY.PRG> endif
skip
ICKY.PRG> wretch = wretch + 1
assign 'wretch' the value of 'wretch' plus 1 (2)
ICKY.PRG> enddo
go to beginning of DO WHILE loop
ICKY.PRG> do while .t.
evaluate condition, since true stay in loop context
ICKY.PRG> if wretch=>2
compare wretch to 2
since expression evaluates to true, execute IF clause
ICKY.PRG> return
look at current context (context of DO WHILE):
We don't see anything saying we've got parameters, so we don't
have to do any fixup.
Exit to previous context (the context of ICKY.PRG).
YUK.PRG> ? barf
print value of 'barf', which happens to be 'this is a test'
----------------------------------------------------------------------
Anyway, does anybody have any thoughts about this ?
//-n-\\ Naoto Kimura
_____---=======---_____ (abcscnuk@csuna.csun.edu)
====____\ /.. ..\ /____====
// ---\__O__/--- \\ Enterprise... Surrender or we'll
\_\ /_/ send back your *&^$% tribbles !!awd@dbase.A-T.COM (Alastair Dallas) (02/02/91)
Parameter passing in dBASE is always by reference in the case of simple memvars and always by value in the case of expressions. In your example: a = "this is a test" do icky with a ? a return procedure icky parameter foo foo = 1 return You will find that the top-level code prints 1, at least in dBASE IV. To pass parameters by value (generally better programming practice) you can do as little as enclose them in parentheses: a = "this is a test" do icky with (a) ? a && will print "this is a test" no matter what return Internally, dBASE creates a local alias which references the parameters which are passed by reference when it processes the PARAMETERS command. How you return from the procedure does not affect the operation. Of course, if you're using any random xBASE product ... ;-) /alastair/