-
2
- #1
This took two days to track down, so I figured it deserved posting for posterity.
We came upon a situation where the simple solution to our problem was to add an _Access method to an object-type property which would go dynamically retrieve the requested object for us.
In another segment of code we needed to know whether or not the parent object was hanging off of a form class so we could display an error message rather than just log it.
This code used [tt]TYPE()[/tt] as a guard because the expression tested for a Parent reference and, as you all know, [tt]VARTYPE()[/tt] blows up in that scenario.
Don't do all three of these things together!
The offending line of code in the below sample is in [tt]Chatty:Test()[/tt] and evaluates to this: [tt]TYPE("this.ioChild.Parent")[/tt].
This [tt]TYPE()[/tt] invocation causes a dangling object reference to the object returned from the [tt]ioChild_Access()[/tt] method.
The only way, it appears, to get this issue to happen is when all three of: TYPE(), _Access(), and .Parent appear in the statement.
For example, setting a local variable to the object returned from the access method and then using TYPE() to test its Parent reference works fine.
Using an actual object reference which does not have an access method works fine. (See the "Ok" case below)
But, for the love of FoxPro, DO NOT DO ALL THREE TOGETHER.
Sample code:
We came upon a situation where the simple solution to our problem was to add an _Access method to an object-type property which would go dynamically retrieve the requested object for us.
In another segment of code we needed to know whether or not the parent object was hanging off of a form class so we could display an error message rather than just log it.
This code used [tt]TYPE()[/tt] as a guard because the expression tested for a Parent reference and, as you all know, [tt]VARTYPE()[/tt] blows up in that scenario.
Don't do all three of these things together!
The offending line of code in the below sample is in [tt]Chatty:Test()[/tt] and evaluates to this: [tt]TYPE("this.ioChild.Parent")[/tt].
This [tt]TYPE()[/tt] invocation causes a dangling object reference to the object returned from the [tt]ioChild_Access()[/tt] method.
The only way, it appears, to get this issue to happen is when all three of: TYPE(), _Access(), and .Parent appear in the statement.
For example, setting a local variable to the object returned from the access method and then using TYPE() to test its Parent reference works fine.
Using an actual object reference which does not have an access method works fine. (See the "Ok" case below)
But, for the love of FoxPro, DO NOT DO ALL THREE TOGETHER.
Sample code:
Code:
CLEAR
Talk("DoDemo() - Begin")
DoDemo()
Talk("DoDemo() - End")
RETURN
PROCEDURE DoDemo
Talk("Begin")
LOCAL loObj AS Object
loObj = CREATEOBJECT("Chatty")
loObj.Test()
Talk("End")
ENDPROC
PROCEDURE Talk
LPARAMETERS tuObj
LOCAL lcPrefix AS String, lcProc AS String
lcPrefix = REPLICATE(" ", MAX(PROGRAM(-1) - 1, 0))
lcProc = PROGRAM(PROGRAM(-1) - 1) + "()"
DO CASE
CASE VARTYPE(tuObj) == "O"
? lcPrefix + "Object " + tuObj.Tag + " of type " + tuObj.Class + " in " + lcProc
CASE VARTYPE(tuObj) == "C"
? lcPrefix + "Procedure " + lcProc + ": " + tuObj
OTHERWISE
? lcPrefix + "Procedure " + lcProc
ENDCASE
ENDPROC
DEFINE CLASS Chatty AS Custom
ioChild = .NULL.
PROCEDURE Init
this.Tag = SYS(2015)
Talk(this)
this.AddObject("oChild", "nested")
ENDPROC
PROCEDURE Destroy
Talk(this)
ENDPROC
PROCEDURE Test
Talk("Begin")
LOCAL lcType AS String, lcMember AS String
lcMember = "this.ioChild.Parent" && Hang
*lcMember = "this.oChild.Parent" && Ok
lcType = TYPE(lcMember)
Talk("Type of [" + lcMember + "] is: " + lcType)
Talk("End")
ENDPROC
PROCEDURE ioChild_Access
Talk(this)
RETURN this.oChild
ENDPROC
ENDDEFINE
DEFINE CLASS Nested AS Custom
PROCEDURE Init
this.Tag = SYS(2015)
Talk(this)
ENDPROC
PROCEDURE Destroy
Talk(this)
ENDPROC
ENDDEFINE