Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

PEMSTATUS bug

Status
Not open for further replies.

Olaf Doschke

Programmer
Oct 13, 2004
14,847
DE
I am victim of a bug I just found. Unfortunately of course it happens in a project, where it is important, but take that aside, I can cope with the aftermath...

Code:
o = CREATEOBJECT("nested")
? PEMSTATUS(o,"inner1",5)
? VARTYPE(o.inner1)
? TYPE("o.inner1")
? o.inner1.name

DEFINE CLASS nested as Container
   ADD OBJECT inner1 as innercontainer 
ENDDEFINE    

DEFINE CLASS innercontainer  as Container
   PROCEDURE Init()
      RETURN .F.
ENDDEFINE

The PEMSTATUS says the inner1 object exists, yet it does not due to RETURN .F. from init. In the real world case, this is not that simple of course, there are condition checks, that may or may not lead to a subobject removing itself from a compound class. In my case it's a visual class, but it doesn't matter, the problem also exists in PRG.

I assume this bug is "by design", the inner1 clearly is a member of the class definition of nested, so it is a defined member, it is just not existing at runtime.

To overcome the issue I had to replace PEMSTATUS calls to TRY CATCH statements making use of the probably not existing sub object. As Vartype and Type also tell this unknown element is of type "O" (object), you can't do any precheck as caution about using such an object. Also the locals window will list "inner" as a node of the "o" object, if you start the debugger after creating o. So actually this most probably is no bug of the PEMSTATUS function itself, but of how objects and information about them are managed by VFP.

I wouldn't consider this a serious bug simply by seeing how many VFP software still works, as it may not make much use of such situations, but classes already put together at design time that eventually remove some of their inner components may cause trouble. And this time it's not about the infamous "zombie" objects as dangling object references are sometimes called, that remain if objects are not released correctly. RETURN .F. from init is a correct way of saying you don't want the object to become alive at all. Any objects init finally decides about it's existence with the .T. or .F. return value, .T. is just a default you don't even have to write into the code as RETURN .T.

Bye, Olaf.
 
Weird one Olaf. I use PEMSTATUS for several things, I guess usually I don't remove objects in the middle of object chain, but will try to keep this in mind.

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
It's kinda creepy, but the object inner1 exists, although we can't do anything with it.

Since its initialization failed, none of the properties and methods of its class are going to be instantiated (because the parent wasn't, also). So, asking for o.inner.name will raise an error, but asking for o.inner won't, which will make this all thing at least consistent.

Checking for a living object will require looking into the availability of its properties, but - correct me if I'm wrong - PEMSTATUS() with flags 1 to 6 refers to a class definition of an object, not to the status of an instantiation. That is, it should be used to verify if a property, event, or method was (re)defined for the class of an object, and not to check if the class was instantiated or not as an object.

Instead of PEMSTATUS(o, "inner1", 5), TYPE("o.inner1.name") != "C" would do for the checking. Or, better still, in my view, if the "nested" object operation requires "inner1", then it should be "nested"'s responsibility to check for "inner1"'s availability, and its own Init fail if the contained didn't get instantiated.

Code:
DEFINE CLASS nested as Container
  ADD OBJECT inner1 as innercontainer
  
  FUNCTION Init
  	RETURN TYPE("This.inner1.name") = "C"
ENDDEFINE
 
I'm not sure I'd label this a bug. It's sort of in line with this behavior:

Code:
o = CreateObject("form")
o.Release()
?Type('o')  && "O"
?0  && .NULL.

It's there but it really isn't.
 
I did PEMSTATUS(o,"inner1",5).

It's true, that the encapsulation principle should mean you never address more than the outer level from outside. But then is it really wrong, if a form method acts on a contol inside a container? or pageframe or grid? This inner object is an adapter for a COM server, which in turn manages a smartcard. If the smartcard is not inserted, the initialization finds out and the software has to work in a reduced mode.

The problem appears in several validation calls checking the current situation and that's where it becomes a little blurry whcih class and object instance should really do what to control and validate and check each other.

I think a little refactoring would have meant to only deal with the problem in a single place instead of now - let me lie - about 5 places. Not all that bad. I also thnk about returning .T. in any case and inactivating it with another property quite like Enabled or Visble is doing for controls.

I also get your point, dan, but at least you can get 'X' from Vartype(o) and know you have a certain state.

Bye, Olaf.
 
I kind of agree it's by design of PEMSTATUS, as 5 says Defined property, event, method, or object, and it is defined.

But the problem actually is not about the core PEMSTATUS meaning, but about not being able to find out before erroring. So I think TRY..CATCH is a workaround.

Of these alternative also only TYPE really works:

Code:
? PEMSTATUS(o.inner1,"name",5)
? VARTYPE(o.inner1.name)
? TYPE("o.inner1.name")
The Vartype call errors, too, though a simpler VARTYPE call just like TYPE do not depend on the existence of the given name, eg [tt]? VARTYPE(sdfhjkdshfjkh)[/tt] yields "U", whereas [tt]? sdfhjkdshfjkh[/tt] triggers error "Variable ... not found".

So finally a workable solution is to test for a known property of the subobject via TYPE, if you don't like the TRY..CATCH approach.

Bye, Olaf.
 
Does checking ISNULL(o.inner1) work?

Tamar
 
You can check with the sample repro code: No,that returns .F.

See several results:
PEMSTATUS_vbxu0l.png

The TYPE("o.inner1.name") is the only really helpful value to detect the situation.
The line the debugger points to will error.

The debugger itself, as said, shows the inner1 node as Type O, too, but no [+] expand symbol aside of it, which every real object would show, so the debugger knows more than it shows, it could show "U" as it knows there are no PEMS, and that's impossible with a real object.

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top