---------- Forwarded message ---------- From: Eliot Miranda email@example.com Date: Mon, Sep 7, 2009 at 11:56 AM Subject: Mirror primitives [Was The better debugger support by extending an Object protocol] To: The general-purpose Squeak developers list firstname.lastname@example.org
On Mon, Sep 7, 2009 at 10:29 AM, Igor Stasenko email@example.com wrote:
2009/9/7 Bert Freudenberg firstname.lastname@example.org:
On 07.09.2009, at 18:40, Randal L. Schwartz wrote:
>> "Igor" == Igor Stasenko email@example.com writes:
Igor> Here is the default implementation of
Object> debugPrintOn: aStream Igor> ^ self printOn: aStream
I like it, but I don't like the name. It conjurs up the idea of debugging the #printOn: method itself.
maybe #printForDebuggerOn: ?
It matches the other methods like #longPrintOn: though.
Also that would imply only the debugger invokes it, when it's clearly appropriate for any purpose.
However, apart from this bike shedding ;) do others think it's a good idea in general? IMHO #printOn: is primarily used for debugging anyway so I wouldn't really expect a separate debug print method to be needed. Debugging transparent proxies isn't for the faint-of-heart anyway, so these developers can adapt their tools and proxies I would guess. OTOH there is precedence in e.g. #asExplorerString, which exists solely to get rid of the quotes enclosing strings for aesthetic reasons ...
I agree with you Bert, that we should consider if its really necessary. While #printOn: is used by most code for debugging, some of the code using it to serialize object to textual stream, so the #printOn: usage is much more generic than just debugging.
As for debugging the transparent proxies - yes you can make own tools.. but this means reimplementing/hacking other tools , like debugger, inspector and so on, which will probably end up with similar solution to what i proposing.
For example, there was a point of discrepancy with debugging the code which uses Magma. The magma forwarding proxy reifying the object once the first real message is sent to it. And the problem with such a proxy, that you can't use them in certain situations, like:
self perform: aSelectorProxied with: #foo
because #perform doesn't sends the messages to aSelectorProxied and primitive fails. But then, when you entering the debugger and trying to figure out why its not working - a proxy becomes a real object when it printed out.. and so you can't debug the error using debugger. Which leaves you to wonder, why code works in debugger, but not working at run time :)
I have fixes for this integrated in Qwaq. This is work I did for VisualWorks a while back. The idea is to add a set of mirror primitives, so called because they do reflection, to ContextPart. These primitives implement the basic operations of the object model needed for execution simulation, fetching an object's class, accessing its named and indexed instance variables, and the number of indexed instance variables, sending a message, but they take the object operated on as a parameter and so do function without sending messages to that object. These primitives are then used by the execution simulation machinery to correctly simulate the execution of the virtual machine irrespective of the class and/or messages implemented by the receiver.
For example, (eschewing HTML for Bert's contentment)
ContextPart methods for mirror primitives object: anObject instVarAt: anIndex "Primitive. Answer a fixed variable in an object. The numbering of the variables corresponds to the named instance variables. Fail if the index is not an Integer or is not the index of a fixed variable. Essential for the debugger. See Object documentation whatIsAPrimitive."
<primitive: 73> "Access beyond fixed variables." ^self object: anObject basicAt: anIndex - (self objectClass: anObject) instSize
object: anObject instVarAt: anIndex put: aValue "Primitive. Store a value into a fixed variable in the argument anObject. The numbering of the variables corresponds to the named instance variables. Fail if the index is not an Integer or is not the index of a fixed variable. Answer the value stored as the result. Using this message violates the principle that each object has sovereign control over the storing of values into its instance variables. Essential for the debugger. See Object documentation whatIsAPrimitive."
<primitive: 74> "Access beyond fixed fields" ^self object: anObject basicAt: anIndex - (self objectClass: anObject) instSize put: aValue
ContextPart methods for instruction decoding pushReceiverVariable: offset "Simulate the action of bytecode that pushes the contents of the receiver's instance variable whose index is the argument, index, on the top of the stack."
self push: (self object: self receiver instVarAt: offset + 1)
popIntoReceiverVariable: offset "Simulate the action of bytecode that removes the top of the stack and stores it into an instance variable of my receiver."
self object: self receiver instVarAt: offset + 1 put: self pop
storeIntoReceiverVariable: offset "Simulate the action of bytecode that stores the top of the stack into an instance variable of my receiver."
self object: self receiver instVarAt: offset + 1 put: self top
The attached change set for the image level code is also conflated with changes that allow the debugger to set the primitive error code correctly.
The VM also needs to implement the primitives. If the order of the arguments are the same as those of the corresponding non-reflective primitive (e.g. aContext object: theReceiver instVarAt: index put: value has the same order as theReceiver instVarAt: index put: value) and the primitive pops back the stack using self pop: argumentCount + 1, one can use the same VM primitive implementations as the non-reflective ones and so avoid needing extra primitives.
However, not all primitives are correctly implemented; e.g. at: and at:put: are, but size is not. So before we can use this there is some work to do in the standard VMs. I'm attaching here the implementation in the Stack VM which will require some back porting to the standard VM. None of these changes has performance implications because the special selector impelmentations of at: at:put: size and #== are not affected.
With the above changes one can safely step through code that uses proxies that do not inherit from Object, or even ProtoObject. I still need to implement a PrimitiveObjectInspector, or use the mirror primitives in implementing the Inspector. However, there is one more important thing needed in the debugger which these changes do not address and that is the value of Processor activeProcess within the debugger. e.g. "print it" the following:
| thisProcessOutsideTheDebugger thisProcessInsideTheDebugger | thisProcessOutsideTheDebugger := Processor activeProcess. self halt. thisProcessInsideTheDebugger := Processor activeProcess. 'Send to here (Into) then proceed'. thisProcessOutsideTheDebugger == thisProcessInsideTheDebugger
This will potentially break debugging of servers that maintain process pools. Andreas has proposed a very simple fix for this. Add an instance variable to Process called, e.g. effectiveProcess, which is nil in normal processes. When the debugger runs it sets effectiveProcess to that of the process which it is debugging and Processor activeProcess is implemented as something like
ProcessorScheduler methods for accessing activeProcess ^activeProcess effectiveProcess ifNil: [activeProcess]
I'll try and get round to this soon.
I'm posting this rather than integrating because a) the VMs need to get updated before we can use the image level code and, b) the VM folks need code to test their VMs with. I'm happy to integrate the image-level changes once the VMs have been upgraded.
- Bert -
-- Best regards, Igor Stasenko AKA sig.