[Bug & design issue] Messages understood or not understood by ProtoObject

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
19 messages Options
Reply | Threaded
Open this post in threaded view
|

[Bug & design issue] Messages understood or not understood by ProtoObject

Christoph Thiede

Hi all! :-)

When we were fiddling around with some proxy implementations today, Marcel and I discovered an interesting set of issues regarding ProtoObjects.

Let's start with a concrete bug:


Code to reproduce <do it>:

ProtoObject new class.


Expected behavior:

DNU exception, because #class is implemented on Object only.


Actual behavior:

Outputs ProtoObject.

Even stranger: If you debug the method instead and simulate the #class send, you get a "simulated message class not understood" error.

If you subclass ProtoObject and override/forward #class, the results will deviate based on whether you are debugging or executing the code.


Some thoughts:

There are two options: Either to implement #class on ProtoObject (by moving the primitive definition up from Object), or to remove it from the specialObjectsArray so that a send to #class is not compiled differently than a send to any other regular message.


This leads us to the rather general question: What special messages should an instance of ProtoObject understand?

We don't answer this question completely consistent at the moment: For example, #instVarAt: is implemented on Object, but #instVarsInclude: is implemented on ProtoObject. Again, it is a bit weird that the implementation of #instVarsInclude: calls #class which is implemented on Object only. And so it goes on:

  • #someObject is implemented on Object, but #nextObject is available in ProtoObject.
  • #withArgs:executeMethod: is implemented on ProtoObject, whereas #perform:withArguments: is on Object.
  • #ifNotNil: is implemented on ProtoObject; however, #ifNotNilDo: is implemented on Object. (btw: does the latter still have any raison d'être?)
  • #flag: exists on ProtoObject, and #yourself exists on Object only. Isn't #yourself rather a syntactical element we would like to behave exactly the same way for every possible message receiver? (btw: Is there a good reason not to speed up #yourself via specialObjectsArray)?
  • (I don't claim for completeness)

And just some other problems regarding to ProtoObject (just collecting them here instead of forgetting them forever):
  • ObjectTracer is broken due to several reasons. Will fix this soon.
  • Inspectors cannot inspect ProtoObjects correctly (for example: Inspector openOn: (ObjectTracer on: Display)). This is because #instVarAt: is implemented on Object only so this message is forwarded.
    Maybe we should replace these critical calls from Inspector by something like #tryPrimitive:173withArgs:? But should we do so in Inspector or rather in a new subclass named ProtoInspector or similar? Hm, then we would need to implement #inspectorClass on ProtoObject, which is bad either ... We will keep investigating this issue.

However, the crucial question is: What special messages should an instance of ProtoObject understand? On the one hand, the idea of ProtoObjects is to work as total proxies with a maximum amount of forwarding potential, which implies a minimum number of methods. On the other hand, certain aspects such as accessing instvars or executing messages are really identity-related. Looking forward to your comments :-)

Best,
Christoph



Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Christoph Thiede

(Separate idea, separate post:)


The main (may I say the only?) use case of ProtoObject is to have a minimum superclass for generic proxies or decorators that are implemented generically using #doesNotUnderstand:.

Besides this mechanism, we also have #run:with:in: (see ObjectsAsMethodsExample).

Would it, hypothetically spoken, be worth a thought to implement some kind of hook on VM side/code simulation that is called before sending any message to a new receiver? Very pseudo:


MyCoolProxy >> #unmanagedPerformMessage: aMessage

    "by implementing this method on a class, you tell the engine to call it before sending any message from a different object to the receiver

    (we could also use something like #flush for this)"

    aMessage = #inspectorClass ifTrue: [

        ^ aMessage sentTo: self "don't forward this message"].

    self log: aMessage. "sending a message to the receiver does not invoke #unmanagedPerformMessage:"

    "Finally ..."

    ^ aMessage sentTo: myTarget


Probably there is at least one very good reason not to implement this, besides confusion and (maybe?) performance. But I would be delighted to hear this reason :-)

Best,
Christoph



Von: Squeak-dev <[hidden email]> im Auftrag von Thiede, Christoph
Gesendet: Freitag, 14. Februar 2020 14:18 Uhr
An: Squeak Dev
Betreff: [squeak-dev] [Bug & design issue] Messages understood or not understood by ProtoObject
 

Hi all! :-)

When we were fiddling around with some proxy implementations today, Marcel and I discovered an interesting set of issues regarding ProtoObjects.

Let's start with a concrete bug:


Code to reproduce <do it>:

ProtoObject new class.


Expected behavior:

DNU exception, because #class is implemented on Object only.


Actual behavior:

Outputs ProtoObject.

Even stranger: If you debug the method instead and simulate the #class send, you get a "simulated message class not understood" error.

If you subclass ProtoObject and override/forward #class, the results will deviate based on whether you are debugging or executing the code.


Some thoughts:

There are two options: Either to implement #class on ProtoObject (by moving the primitive definition up from Object), or to remove it from the specialObjectsArray so that a send to #class is not compiled differently than a send to any other regular message.


This leads us to the rather general question: What special messages should an instance of ProtoObject understand?

We don't answer this question completely consistent at the moment: For example, #instVarAt: is implemented on Object, but #instVarsInclude: is implemented on ProtoObject. Again, it is a bit weird that the implementation of #instVarsInclude: calls #class which is implemented on Object only. And so it goes on:

  • #someObject is implemented on Object, but #nextObject is available in ProtoObject.
  • #withArgs:executeMethod: is implemented on ProtoObject, whereas #perform:withArguments: is on Object.
  • #ifNotNil: is implemented on ProtoObject; however, #ifNotNilDo: is implemented on Object. (btw: does the latter still have any raison d'être?)
  • #flag: exists on ProtoObject, and #yourself exists on Object only. Isn't #yourself rather a syntactical element we would like to behave exactly the same way for every possible message receiver? (btw: Is there a good reason not to speed up #yourself via specialObjectsArray)?
  • (I don't claim for completeness)

And just some other problems regarding to ProtoObject (just collecting them here instead of forgetting them forever):
  • ObjectTracer is broken due to several reasons. Will fix this soon.
  • Inspectors cannot inspect ProtoObjects correctly (for example: Inspector openOn: (ObjectTracer on: Display)). This is because #instVarAt: is implemented on Object only so this message is forwarded.
    Maybe we should replace these critical calls from Inspector by something like #tryPrimitive:173withArgs:? But should we do so in Inspector or rather in a new subclass named ProtoInspector or similar? Hm, then we would need to implement #inspectorClass on ProtoObject, which is bad either ... We will keep investigating this issue.

However, the crucial question is: What special messages should an instance of ProtoObject understand? On the one hand, the idea of ProtoObjects is to work as total proxies with a maximum amount of forwarding potential, which implies a minimum number of methods. On the other hand, certain aspects such as accessing instvars or executing messages are really identity-related. Looking forward to your comments :-)

Best,
Christoph



Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

David T. Lewis
In reply to this post by Christoph Thiede
I think this observation is as much about "expected behavior" as
it is about bugs or implementations :-)

ProtoObject actually does have a class, so without looking at the
implementation it makes perfect sense that it should be able to
answer its class. And given that it does have a class, it would
be unexpected to me if it could not tell me what its class was.

On the other hand, if you poke at it too closely, you can indeed
find some interesting oddities.

Object canUnderstand: #class ==> true
Object new respondsTo: #class ==> true
ProtoObject canUnderstand: #class ==> false
ProtoObject new respondsTo: #class ==> DNU
ProtoObject new class ==> ProtoObject

And if you step into #class in a debugger, you will lock up your
image with infinite debuggers. Arguably, that part is a bug ;-)

I suspect that we are looking at a clever bit of implementation that
addresses the following common use case:

   "Gee, I wonder what the class of ProtoObject is?"

Dave

On Fri, Feb 14, 2020 at 01:18:46PM +0000, Thiede, Christoph wrote:

> Hi all! :-)
>
> When we were fiddling around with some proxy implementations today, Marcel and I discovered an interesting set of issues regarding ProtoObjects.
>
> Let's start with a concrete bug:
>
>
> Code to reproduce <do it>:
>
> ProtoObject new class.
>
>
> Expected behavior:
>
> DNU exception, because #class is implemented on Object only.
>
>
> Actual behavior:
>
> Outputs ProtoObject.
>
> Even stranger: If you debug the method instead and simulate the #class send, you get a "simulated message class not understood" error.
>
> If you subclass ProtoObject and override/forward #class, the results will deviate based on whether you are debugging or executing the code.
>
>
> Some thoughts:
>
> There are two options: Either to implement #class on ProtoObject (by moving the primitive definition up from Object), or to remove it from the specialObjectsArray so that a send to #class is not compiled differently than a send to any other regular message.
>
>
> This leads us to the rather general question: What special messages should an instance of ProtoObject understand?
>
> We don't answer this question completely consistent at the moment: For example, #instVarAt: is implemented on Object, but #instVarsInclude: is implemented on ProtoObject. Again, it is a bit weird that the implementation of #instVarsInclude: calls #class which is implemented on Object only. And so it goes on:
>
>   *   #someObject is implemented on Object, but #nextObject is available in ProtoObject.
>   *   #withArgs:executeMethod: is implemented on ProtoObject, whereas #perform:withArguments: is on Object.
>   *   #ifNotNil: is implemented on ProtoObject; however, #ifNotNilDo: is implemented on Object. (btw: does the latter still have any raison d'?tre?)
>   *   #flag: exists on ProtoObject, and #yourself exists on Object only. Isn't #yourself rather a syntactical element we would like to behave exactly the same way for every possible message receiver? (btw: Is there a good reason not to speed up #yourself via specialObjectsArray)?
>   *   (I don't claim for completeness)
>
> And just some other problems regarding to ProtoObject (just collecting them here instead of forgetting them forever):
>
>   *   ObjectTracer is broken due to several reasons. Will fix this soon.
>   *   Inspectors cannot inspect ProtoObjects correctly (for example: Inspector openOn: (ObjectTracer on: Display)). This is because #instVarAt: is implemented on Object only so this message is forwarded.
> Maybe we should replace these critical calls from Inspector by something like #tryPrimitive:173withArgs:? But should we do so in Inspector or rather in a new subclass named ProtoInspector or similar? Hm, then we would need to implement #inspectorClass on ProtoObject, which is bad either ... We will keep investigating this issue.
>
> However, the crucial question is: What special messages should an instance of ProtoObject understand? On the one hand, the idea of ProtoObjects is to work as total proxies with a maximum amount of forwarding potential, which implies a minimum number of methods. On the other hand, certain aspects such as accessing instvars or executing messages are really identity-related. Looking forward to your comments :-)
>
> Best,
> Christoph

>


Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Eliot Miranda-2
In reply to this post by Christoph Thiede
Hi Christoph,

On Feb 14, 2020, at 5:18 AM, Thiede, Christoph <[hidden email]> wrote:



Hi all! :-)

When we were fiddling around with some proxy implementations today, Marcel and I discovered an interesting set of issues regarding ProtoObjects.

Let's start with a concrete bug:


Code to reproduce <do it>:

ProtoObject new class.


Expected behavior:

DNU exception, because #class is implemented on Object only.


Actual behavior:

Outputs ProtoObject.

Even stranger: If you debug the method instead and simulate the #class send, you get a "simulated message class not understood" error.

If you subclass ProtoObject and override/forward #class, the results will deviate based on whether you are debugging or executing the code.


Some thoughts:

There are two options: Either to implement #class on ProtoObject (by moving the primitive definition up from Object), or to remove it from the specialObjectsArray so that a send to #class is not compiled differently than a send to any other regular message.


Or provide a vm flag which controls whether special selector #class (the bytecode for sending #class) is inlined or not.  This flag can also control #== & #~~.


This leads us to the rather general question: What special messages should an instance of ProtoObject understand?


IMO *only* #doesNotUnderstand:

See below on the mirror protocol

We don't answer this question completely consistent at the moment: For example, #instVarAt: is implemented on Object, but #instVarsInclude: is implemented on ProtoObject. Again, it is a bit weird that the implementation of #instVarsInclude: calls #class which is implemented on Object only. And so it goes on:

  • #someObject is implemented on Object, but #nextObject is available in ProtoObject.
  • #withArgs:executeMethod: is implemented on ProtoObject, whereas #perform:withArguments: is on Object.
  • #ifNotNil: is implemented on ProtoObject; however, #ifNotNilDo: is implemented on Object. (btw: does the latter still have any raison d'être?)
  • #flag: exists on ProtoObject, and #yourself exists on Object only. Isn't #yourself rather a syntactical element we would like to behave exactly the same way for every possible message receiver? (btw: Is there a good reason not to speed up #yourself via specialObjectsArray)?
  • (I don't claim for completeness)

And just some other problems regarding to ProtoObject (just collecting them here instead of forgetting them forever):
  • ObjectTracer is broken due to several reasons. Will fix this soon.
  • Inspectors cannot inspect ProtoObjects correctly (for example: Inspector openOn: (ObjectTracer on: Display)). This is because #instVarAt: is implemented on Object only so this message is forwarded.
    Maybe we should replace these critical calls from Inspector by something like #tryPrimitive:173withArgs:? But should we do so in Inspector or rather in a new subclass named ProtoInspector or similar? Hm, then we would need to implement #inspectorClass on ProtoObject, which is bad either ... We will keep investigating this issue.

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

You see hacks everywhere (the xxxFoo messages in serialization) because we don’t have a well-defined notion of what is in a proxy.  Having one that is as minimal as possible is IMNERHO the clearest definition possible, and the most minimal is just having a #doesNotUnderstand: that prevents recursive does not understand vm exit and displays a stack trace without sending further messages to the proxy (because it uses the mirror primitives).

Note that the mirror primitives are what the execution machinery in Context use to ensure that bytecodes do what they are expected to do.  An I et car access should *not* send instVarAt: to a proxy; it should access the inst var of the proxy, etc.


However, the crucial question is: What special messages should an instance of ProtoObject understand? On the one hand, the idea of ProtoObjects is to work as total proxies with a maximum amount of forwarding potential, which implies a minimum number of methods. On the other hand, certain aspects such as accessing instvars or executing messages are really identity-related. Looking forward to your comments :-)

Hopefully I’ve answered your questions and given you pointers.  I’m interested in collaborating to make this a reality in the next major release.


Best,
Christoph




Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Eliot Miranda-2
In reply to this post by David T. Lewis


> On Feb 14, 2020, at 6:29 AM, David T. Lewis <[hidden email]> wrote:
>
> I think this observation is as much about "expected behavior" as
> it is about bugs or implementations :-)
>
> ProtoObject actually does have a class, so without looking at the
> implementation it makes perfect sense that it should be able to
> answer its class. And given that it does have a class, it would
> be unexpected to me if it could not tell me what its class was.
>
> On the other hand, if you poke at it too closely, you can indeed
> find some interesting oddities.
>
> Object canUnderstand: #class ==> true
> Object new respondsTo: #class ==> true
> ProtoObject canUnderstand: #class ==> false
> ProtoObject new respondsTo: #class ==> DNU
> ProtoObject new class ==> ProtoObject
>
> And if you step into #class in a debugger, you will lock up your
> image with infinite debuggers. Arguably, that part is a bug ;-)
>
> I suspect that we are looking at a clever bit of implementation that
> addresses the following common use case:
>
>   "Gee, I wonder what the class of ProtoObject is?"

To which the answer is “its own business”.  Finding out the real class of a ProtoObject or non-Object subclass of it must be something only privileged parts of the execution simulation, error reporting and inspection systems can do.  Otherwise, building truly transparent proxies is really tricky and full of unexpected edge cases.

Ideally we want to be able to build distributed IDEs for the cloud, etc, where we can have remote tools and distribute processing across many images.  Having very transparent proxies is at the heart of any such thing.

>
> Dave
>
>> On Fri, Feb 14, 2020 at 01:18:46PM +0000, Thiede, Christoph wrote:
>> Hi all! :-)
>>
>> When we were fiddling around with some proxy implementations today, Marcel and I discovered an interesting set of issues regarding ProtoObjects.
>>
>> Let's start with a concrete bug:
>>
>>
>> Code to reproduce <do it>:
>>
>> ProtoObject new class.
>>
>>
>> Expected behavior:
>>
>> DNU exception, because #class is implemented on Object only.
>>
>>
>> Actual behavior:
>>
>> Outputs ProtoObject.
>>
>> Even stranger: If you debug the method instead and simulate the #class send, you get a "simulated message class not understood" error.
>>
>> If you subclass ProtoObject and override/forward #class, the results will deviate based on whether you are debugging or executing the code.
>>
>>
>> Some thoughts:
>>
>> There are two options: Either to implement #class on ProtoObject (by moving the primitive definition up from Object), or to remove it from the specialObjectsArray so that a send to #class is not compiled differently than a send to any other regular message.
>>
>>
>> This leads us to the rather general question: What special messages should an instance of ProtoObject understand?
>>
>> We don't answer this question completely consistent at the moment: For example, #instVarAt: is implemented on Object, but #instVarsInclude: is implemented on ProtoObject. Again, it is a bit weird that the implementation of #instVarsInclude: calls #class which is implemented on Object only. And so it goes on:
>>
>>  *   #someObject is implemented on Object, but #nextObject is available in ProtoObject.
>>  *   #withArgs:executeMethod: is implemented on ProtoObject, whereas #perform:withArguments: is on Object.
>>  *   #ifNotNil: is implemented on ProtoObject; however, #ifNotNilDo: is implemented on Object. (btw: does the latter still have any raison d'?tre?)
>>  *   #flag: exists on ProtoObject, and #yourself exists on Object only. Isn't #yourself rather a syntactical element we would like to behave exactly the same way for every possible message receiver? (btw: Is there a good reason not to speed up #yourself via specialObjectsArray)?
>>  *   (I don't claim for completeness)
>>
>> And just some other problems regarding to ProtoObject (just collecting them here instead of forgetting them forever):
>>
>>  *   ObjectTracer is broken due to several reasons. Will fix this soon.
>>  *   Inspectors cannot inspect ProtoObjects correctly (for example: Inspector openOn: (ObjectTracer on: Display)). This is because #instVarAt: is implemented on Object only so this message is forwarded.
>> Maybe we should replace these critical calls from Inspector by something like #tryPrimitive:173withArgs:? But should we do so in Inspector or rather in a new subclass named ProtoInspector or similar? Hm, then we would need to implement #inspectorClass on ProtoObject, which is bad either ... We will keep investigating this issue.
>>
>> However, the crucial question is: What special messages should an instance of ProtoObject understand? On the one hand, the idea of ProtoObjects is to work as total proxies with a maximum amount of forwarding potential, which implies a minimum number of methods. On the other hand, certain aspects such as accessing instvars or executing messages are really identity-related. Looking forward to your comments :-)
>>
>> Best,
>> Christoph
>
>>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Christoph Thiede
In reply to this post by Eliot Miranda-2

Hi Eliot,


Or provide a vm flag which controls whether special selector #class (the bytecode for sending #class) is inlined or not.  This flag can also control #== & #~~.


Could you give me some (code) pointers about this inlining? :-)

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

I like and do agree to your vision for tidying up ProtoObject!
Maybe we could also consider some "MetaDecorator" that inherits from ProtoObject and implements #class, #instVarAt: etc. for arbitrary targets? This might simplify the access to the mirror protocol, and it would be a bit more object-oriented:

(MetaDecorator on: aMorph) class "--> Morph"
(MetaDecorator on: aProxy) class "--> Proxy"
(MetaDecorator on: aProxy) instVarsInclude: 'target' "--> true"

Hopefully I’ve answered your questions and given you pointers.  I’m interested in collaborating to make this a reality in the next major release.

You did. I'm too.

Best,
Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Eliot Miranda <[hidden email]>
Gesendet: Freitag, 14. Februar 2020 18:27:14
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] [Bug & design issue] Messages understood or not understood by ProtoObject
 
Hi Christoph,

On Feb 14, 2020, at 5:18 AM, Thiede, Christoph <[hidden email]> wrote:



Hi all! :-)

When we were fiddling around with some proxy implementations today, Marcel and I discovered an interesting set of issues regarding ProtoObjects.

Let's start with a concrete bug:


Code to reproduce <do it>:

ProtoObject new class.


Expected behavior:

DNU exception, because #class is implemented on Object only.


Actual behavior:

Outputs ProtoObject.

Even stranger: If you debug the method instead and simulate the #class send, you get a "simulated message class not understood" error.

If you subclass ProtoObject and override/forward #class, the results will deviate based on whether you are debugging or executing the code.


Some thoughts:

There are two options: Either to implement #class on ProtoObject (by moving the primitive definition up from Object), or to remove it from the specialObjectsArray so that a send to #class is not compiled differently than a send to any other regular message.


Or provide a vm flag which controls whether special selector #class (the bytecode for sending #class) is inlined or not.  This flag can also control #== & #~~.


This leads us to the rather general question: What special messages should an instance of ProtoObject understand?


IMO *only* #doesNotUnderstand:

See below on the mirror protocol

We don't answer this question completely consistent at the moment: For example, #instVarAt: is implemented on Object, but #instVarsInclude: is implemented on ProtoObject. Again, it is a bit weird that the implementation of #instVarsInclude: calls #class which is implemented on Object only. And so it goes on:

  • #someObject is implemented on Object, but #nextObject is available in ProtoObject.
  • #withArgs:executeMethod: is implemented on ProtoObject, whereas #perform:withArguments: is on Object.
  • #ifNotNil: is implemented on ProtoObject; however, #ifNotNilDo: is implemented on Object. (btw: does the latter still have any raison d'être?)
  • #flag: exists on ProtoObject, and #yourself exists on Object only. Isn't #yourself rather a syntactical element we would like to behave exactly the same way for every possible message receiver? (btw: Is there a good reason not to speed up #yourself via specialObjectsArray)?
  • (I don't claim for completeness)

And just some other problems regarding to ProtoObject (just collecting them here instead of forgetting them forever):
  • ObjectTracer is broken due to several reasons. Will fix this soon.
  • Inspectors cannot inspect ProtoObjects correctly (for example: Inspector openOn: (ObjectTracer on: Display)). This is because #instVarAt: is implemented on Object only so this message is forwarded.
    Maybe we should replace these critical calls from Inspector by something like #tryPrimitive:173withArgs:? But should we do so in Inspector or rather in a new subclass named ProtoInspector or similar? Hm, then we would need to implement #inspectorClass on ProtoObject, which is bad either ... We will keep investigating this issue.

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

You see hacks everywhere (the xxxFoo messages in serialization) because we don’t have a well-defined notion of what is in a proxy.  Having one that is as minimal as possible is IMNERHO the clearest definition possible, and the most minimal is just having a #doesNotUnderstand: that prevents recursive does not understand vm exit and displays a stack trace without sending further messages to the proxy (because it uses the mirror primitives).

Note that the mirror primitives are what the execution machinery in Context use to ensure that bytecodes do what they are expected to do.  An I et car access should *not* send instVarAt: to a proxy; it should access the inst var of the proxy, etc.


However, the crucial question is: What special messages should an instance of ProtoObject understand? On the one hand, the idea of ProtoObjects is to work as total proxies with a maximum amount of forwarding potential, which implies a minimum number of methods. On the other hand, certain aspects such as accessing instvars or executing messages are really identity-related. Looking forward to your comments :-)

Hopefully I’ve answered your questions and given you pointers.  I’m interested in collaborating to make this a reality in the next major release.


Best,
Christoph




Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Eliot Miranda-2
Hi Christoph,

On Feb 14, 2020, at 9:51 AM, Thiede, Christoph <[hidden email]> wrote:



Hi Eliot,


Or provide a vm flag which controls whether special selector #class (the bytecode for sending #class) is inlined or not.  This flag can also control #== & #~~.


Could you give me some (code) pointers about this inlining? :-)

This is something I first did in VW 3.0 so that people working with proxies could avoid the inlining of #== & #class.  See primitive 537 in VW. The blue book definition for special selector #class & special selector #== has them inlined.  See pp 618 (640 in the online pdf) through 619 in the blue book, and methods specialSelectorPrimitiveResponse and commonSelectorPrimitiveResponse.  In the VM sources see bytecodePrimClass, bytecodePrimIdentical et al in initializeBytecodeTableForSqueakV3PlusClosures, initializeBytecodeTableForSistaV1 and the implementations in StackInterpreter.

The other special selector send bytecodes either apply only to a specific set of classes (e.g. arithmetic is inlined for any combination of SmallInteger and float) or simply function as a space saver (1 byte for the bytecode instead of 1 byte for the send bytecode plus 4 or 8 bytes for the literal selector).  But #class, #== & #~~ are inlined.  They apply to any and all objects.  This is how the system has been defined.  IMO it is a mistake and no longer justified on performance grounds.  But t is the reality on the ground.  And changing it will take some effort.

self systemNavigation browseAllSelect: [:m| (m selector beginsWith: 'bytecodePrim') and: [(m messages includes: #normalSend) not]] localTo: StackInterpreter

(and in the Cogit see implementations of genSpecialSelectorEqualsEquals, genSpecialSelectorClass, genSpecialSelectorNotEqualsEquals).

What I propose is adding a flag bit that persists in the image header, queryable/settable via a vmParameterAt: that turns off this inlining, and turns them into true sends.  I think we'll see slow downs in the 5% to 10% range because of the cost of looking up #== (used heavily in ifNotNil: et al) and we can reduce this by e.g. inlining for nil as a receiver and having the bytecode compiler generate pushNil pushFoo send #== for ifNotNil et al.

Note that Nicolas did something very similar recently to provide control over mixed mode arithmetic.  He added the primitiveDoMixedArithmetic flag, controlled by flag bit 6 in the VM primitive flags (vm parameter 48).  Something like noInlineCommonSelectors would be controlled by bit 7.


There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

I like and do agree to your vision for tidying up ProtoObject!
Maybe we could also consider some "MetaDecorator" that inherits from ProtoObject and implements #class, #instVarAt: etc. for arbitrary targets? This might simplify the access to the mirror protocol, and it would be a bit more object-oriented:

(MetaDecorator on: aMorph) class "--> Morph"
(MetaDecorator on: aProxy) class "--> Proxy"
(MetaDecorator on: aProxy) instVarsInclude: 'target' "--> true"

Sounds good.

Hopefully I’ve answered your questions and given you pointers.  I’m interested in collaborating to make this a reality in the next major release.

You did. I'm too.

Cool. Let's do it :-)  I'm of course busy until next week.  But come Tuesday I should be able to have at it.

Best,
Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Eliot Miranda <[hidden email]>
Gesendet: Freitag, 14. Februar 2020 18:27:14
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] [Bug & design issue] Messages understood or not understood by ProtoObject
 
Hi Christoph,

On Feb 14, 2020, at 5:18 AM, Thiede, Christoph <[hidden email]> wrote:



Hi all! :-)

When we were fiddling around with some proxy implementations today, Marcel and I discovered an interesting set of issues regarding ProtoObjects.

Let's start with a concrete bug:


Code to reproduce <do it>:

ProtoObject new class.


Expected behavior:

DNU exception, because #class is implemented on Object only.


Actual behavior:

Outputs ProtoObject.

Even stranger: If you debug the method instead and simulate the #class send, you get a "simulated message class not understood" error.

If you subclass ProtoObject and override/forward #class, the results will deviate based on whether you are debugging or executing the code.


Some thoughts:

There are two options: Either to implement #class on ProtoObject (by moving the primitive definition up from Object), or to remove it from the specialObjectsArray so that a send to #class is not compiled differently than a send to any other regular message.


Or provide a vm flag which controls whether special selector #class (the bytecode for sending #class) is inlined or not.  This flag can also control #== & #~~.


This leads us to the rather general question: What special messages should an instance of ProtoObject understand?


IMO *only* #doesNotUnderstand:

See below on the mirror protocol

We don't answer this question completely consistent at the moment: For example, #instVarAt: is implemented on Object, but #instVarsInclude: is implemented on ProtoObject. Again, it is a bit weird that the implementation of #instVarsInclude: calls #class which is implemented on Object only. And so it goes on:

  • #someObject is implemented on Object, but #nextObject is available in ProtoObject.
  • #withArgs:executeMethod: is implemented on ProtoObject, whereas #perform:withArguments: is on Object.
  • #ifNotNil: is implemented on ProtoObject; however, #ifNotNilDo: is implemented on Object. (btw: does the latter still have any raison d'être?)
  • #flag: exists on ProtoObject, and #yourself exists on Object only. Isn't #yourself rather a syntactical element we would like to behave exactly the same way for every possible message receiver? (btw: Is there a good reason not to speed up #yourself via specialObjectsArray)?
  • (I don't claim for completeness)

And just some other problems regarding to ProtoObject (just collecting them here instead of forgetting them forever):
  • ObjectTracer is broken due to several reasons. Will fix this soon.
  • Inspectors cannot inspect ProtoObjects correctly (for example: Inspector openOn: (ObjectTracer on: Display)). This is because #instVarAt: is implemented on Object only so this message is forwarded.
    Maybe we should replace these critical calls from Inspector by something like #tryPrimitive:173withArgs:? But should we do so in Inspector or rather in a new subclass named ProtoInspector or similar? Hm, then we would need to implement #inspectorClass on ProtoObject, which is bad either ... We will keep investigating this issue.

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

You see hacks everywhere (the xxxFoo messages in serialization) because we don’t have a well-defined notion of what is in a proxy.  Having one that is as minimal as possible is IMNERHO the clearest definition possible, and the most minimal is just having a #doesNotUnderstand: that prevents recursive does not understand vm exit and displays a stack trace without sending further messages to the proxy (because it uses the mirror primitives).

Note that the mirror primitives are what the execution machinery in Context use to ensure that bytecodes do what they are expected to do.  An I et car access should *not* send instVarAt: to a proxy; it should access the inst var of the proxy, etc.


However, the crucial question is: What special messages should an instance of ProtoObject understand? On the one hand, the idea of ProtoObjects is to work as total proxies with a maximum amount of forwarding potential, which implies a minimum number of methods. On the other hand, certain aspects such as accessing instvars or executing messages are really identity-related. Looking forward to your comments :-)

Hopefully I’ve answered your questions and given you pointers.  I’m interested in collaborating to make this a reality in the next major release.


Best,
Christoph





Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Jakob Reschke-2
Am Fr., 14. Feb. 2020 um 19:42 Uhr schrieb Eliot Miranda <[hidden email]>:
Hi Christoph,

On Feb 14, 2020, at 9:51 AM, Thiede, Christoph <[hidden email]> wrote:

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

I like and do agree to your vision for tidying up ProtoObject!
Maybe we could also consider some "MetaDecorator" that inherits from ProtoObject and implements #class, #instVarAt: etc. for arbitrary targets? This might simplify the access to the mirror protocol, and it would be a bit more object-oriented:

(MetaDecorator on: aMorph) class "--> Morph"
(MetaDecorator on: aProxy) class "--> Proxy"
(MetaDecorator on: aProxy) instVarsInclude: 'target' "--> true"

Sounds good.

If inspectors et al would use this, my decorators might not have to carry the following collection of methods/copies:

asExplorerString basicInspect basicSize class instVarAt: instVarAt:put: perform: perform:withArguments: primitiveFailed printString printStringLimitedTo: respondsTo: xxxClass

If DataStream et al did, add the following:
readDataFrom:size: storeDataOn:

I'm not sure how to generally handle #yourself. I have this in my decorators as well because I don't want to undecorate when the decorator receives it.


Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Eliot Miranda-2
Hi Jakob,

On Fri, Feb 14, 2020 at 1:14 PM Jakob Reschke <[hidden email]> wrote:
Am Fr., 14. Feb. 2020 um 19:42 Uhr schrieb Eliot Miranda <[hidden email]>:
Hi Christoph,

On Feb 14, 2020, at 9:51 AM, Thiede, Christoph <[hidden email]> wrote:

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

I like and do agree to your vision for tidying up ProtoObject!
Maybe we could also consider some "MetaDecorator" that inherits from ProtoObject and implements #class, #instVarAt: etc. for arbitrary targets? This might simplify the access to the mirror protocol, and it would be a bit more object-oriented:

(MetaDecorator on: aMorph) class "--> Morph"
(MetaDecorator on: aProxy) class "--> Proxy"
(MetaDecorator on: aProxy) instVarsInclude: 'target' "--> true"

Sounds good.

If inspectors et al would use this, my decorators might not have to carry the following collection of methods/copies:

asExplorerString basicInspect basicSize class instVarAt: instVarAt:put: perform: perform:withArguments: primitiveFailed printString printStringLimitedTo: respondsTo: xxxClass

If DataStream et al did, add the following:
readDataFrom:size: storeDataOn:

I'm not sure how to generally handle #yourself. I have this in my decorators as well because I don't want to undecorate when the decorator receives it.


typically use of the mirror protocol, at its most basic, looks like

    thisContext object: foo instVarAt: index

so #yourself is handled by ending the cascade early, and answering the object (foo in the above).  Yes that's a tedious refactor, but straightforward, no?  
--
_,,,^..^,,,_
best, Eliot


Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Jakob Reschke-2
Thanks Eliot, I understood that for the inspector use case, but my thought about #yourself was rather the following:

If I pass the decorator around and some domain code (not in Inspector or similar) uses the decorator in a cascade with yourself at the end, I do not want the result to be the undecorated object.

Example:

    anObject := (MyDecorator on: anotherObject)
       doSomething;
       yourself.

I would like that anObject is the decorator and not anotherObject at the end.

Am Fr., 14. Feb. 2020 um 22:39 Uhr schrieb Eliot Miranda <[hidden email]>:
Hi Jakob,

On Fri, Feb 14, 2020 at 1:14 PM Jakob Reschke <[hidden email]> wrote:
Am Fr., 14. Feb. 2020 um 19:42 Uhr schrieb Eliot Miranda <[hidden email]>:
Hi Christoph,

On Feb 14, 2020, at 9:51 AM, Thiede, Christoph <[hidden email]> wrote:

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

I like and do agree to your vision for tidying up ProtoObject!
Maybe we could also consider some "MetaDecorator" that inherits from ProtoObject and implements #class, #instVarAt: etc. for arbitrary targets? This might simplify the access to the mirror protocol, and it would be a bit more object-oriented:

(MetaDecorator on: aMorph) class "--> Morph"
(MetaDecorator on: aProxy) class "--> Proxy"
(MetaDecorator on: aProxy) instVarsInclude: 'target' "--> true"

Sounds good.

If inspectors et al would use this, my decorators might not have to carry the following collection of methods/copies:

asExplorerString basicInspect basicSize class instVarAt: instVarAt:put: perform: perform:withArguments: primitiveFailed printString printStringLimitedTo: respondsTo: xxxClass

If DataStream et al did, add the following:
readDataFrom:size: storeDataOn:

I'm not sure how to generally handle #yourself. I have this in my decorators as well because I don't want to undecorate when the decorator receives it.


typically use of the mirror protocol, at its most basic, looks like

    thisContext object: foo instVarAt: index

so #yourself is handled by ending the cascade early, and answering the object (foo in the above).  Yes that's a tedious refactor, but straightforward, no?  
--
_,,,^..^,,,_
best, Eliot



Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Christoph Thiede

Example:

>    anObject := (MyDecorator on: anotherObject)
>       doSomething;
>       yourself.
> I would like that anObject is the decorator and not anotherObject at the end.

Marcel told me the following tip for this scenario, which sounds quite interesting to me:

doesNotUnderstand: aMessage
| result |
result := aMessage sendTo: target.
^ target == result
ifTrue: [self]
ifFalse: [result]

:-)


Von: Squeak-dev <[hidden email]> im Auftrag von Jakob Reschke <[hidden email]>
Gesendet: Samstag, 15. Februar 2020 09:28:58
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] [Bug & design issue] Messages understood or not understood by ProtoObject
 
Thanks Eliot, I understood that for the inspector use case, but my thought about #yourself was rather the following:

If I pass the decorator around and some domain code (not in Inspector or similar) uses the decorator in a cascade with yourself at the end, I do not want the result to be the undecorated object.

Example:

    anObject := (MyDecorator on: anotherObject)
       doSomething;
       yourself.

I would like that anObject is the decorator and not anotherObject at the end.

Am Fr., 14. Feb. 2020 um 22:39 Uhr schrieb Eliot Miranda <[hidden email]>:
Hi Jakob,

On Fri, Feb 14, 2020 at 1:14 PM Jakob Reschke <[hidden email]> wrote:
Am Fr., 14. Feb. 2020 um 19:42 Uhr schrieb Eliot Miranda <[hidden email]>:
Hi Christoph,

On Feb 14, 2020, at 9:51 AM, Thiede, Christoph <[hidden email]> wrote:

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

I like and do agree to your vision for tidying up ProtoObject!
Maybe we could also consider some "MetaDecorator" that inherits from ProtoObject and implements #class, #instVarAt: etc. for arbitrary targets? This might simplify the access to the mirror protocol, and it would be a bit more object-oriented:

(MetaDecorator on: aMorph) class "--> Morph"
(MetaDecorator on: aProxy) class "--> Proxy"
(MetaDecorator on: aProxy) instVarsInclude: 'target' "--> true"

Sounds good.

If inspectors et al would use this, my decorators might not have to carry the following collection of methods/copies:

asExplorerString basicInspect basicSize class instVarAt: instVarAt:put: perform: perform:withArguments: primitiveFailed printString printStringLimitedTo: respondsTo: xxxClass

If DataStream et al did, add the following:
readDataFrom:size: storeDataOn:

I'm not sure how to generally handle #yourself. I have this in my decorators as well because I don't want to undecorate when the decorator receives it.


typically use of the mirror protocol, at its most basic, looks like

    thisContext object: foo instVarAt: index

so #yourself is handled by ending the cascade early, and answering the object (foo in the above).  Yes that's a tedious refactor, but straightforward, no?  
--
_,,,^..^,,,_
best, Eliot



Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Jakob Reschke-2
Not sendTo: instead of sentTo:? ;-)

Agreed that solves it. And I suppose it is fine to ask the decorator
programmer to implement an #undecorated method to return the inner
object. If it makes sense for the particular decorator.


Am Sa., 15. Feb. 2020 um 15:59 Uhr schrieb Thiede, Christoph
<[hidden email]>:


>
> > Example:
>
> >
> >    anObject := (MyDecorator on: anotherObject)
> >       doSomething;
> >       yourself.
> >
> > I would like that anObject is the decorator and not anotherObject at the end.
>
> Marcel told me the following tip for this scenario, which sounds quite interesting to me:
>
> doesNotUnderstand: aMessage
> | result |
> result := aMessage sendTo: target.
> ^ target == result
> ifTrue: [self]
> ifFalse: [result]
>
> :-)
>
> ________________________________
> Von: Squeak-dev <[hidden email]> im Auftrag von Jakob Reschke <[hidden email]>
> Gesendet: Samstag, 15. Februar 2020 09:28:58
> An: The general-purpose Squeak developers list
> Betreff: Re: [squeak-dev] [Bug & design issue] Messages understood or not understood by ProtoObject
>
> Thanks Eliot, I understood that for the inspector use case, but my thought about #yourself was rather the following:
>
> If I pass the decorator around and some domain code (not in Inspector or similar) uses the decorator in a cascade with yourself at the end, I do not want the result to be the undecorated object.
>
> Example:
>
>     anObject := (MyDecorator on: anotherObject)
>        doSomething;
>        yourself.
>
> I would like that anObject is the decorator and not anotherObject at the end.
>
> Am Fr., 14. Feb. 2020 um 22:39 Uhr schrieb Eliot Miranda <[hidden email]>:
>>
>> Hi Jakob,
>>
>> On Fri, Feb 14, 2020 at 1:14 PM Jakob Reschke <[hidden email]> wrote:
>>>
>>> Am Fr., 14. Feb. 2020 um 19:42 Uhr schrieb Eliot Miranda <[hidden email]>:
>>>>
>>>> Hi Christoph,
>>>>
>>>> On Feb 14, 2020, at 9:51 AM, Thiede, Christoph <[hidden email]> wrote:
>>>>
>>>>
>>>> > There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:
>>>>
>>>> I like and do agree to your vision for tidying up ProtoObject!
>>>> Maybe we could also consider some "MetaDecorator" that inherits from ProtoObject and implements #class, #instVarAt: etc. for arbitrary targets? This might simplify the access to the mirror protocol, and it would be a bit more object-oriented:
>>>>
>>>> (MetaDecorator on: aMorph) class "--> Morph"
>>>> (MetaDecorator on: aProxy) class "--> Proxy"
>>>> (MetaDecorator on: aProxy) instVarsInclude: 'target' "--> true"
>>>>
>>>> Sounds good.
>>>>
>>>>
>>> If inspectors et al would use this, my decorators might not have to carry the following collection of methods/copies:
>>>
>>> asExplorerString basicInspect basicSize class instVarAt: instVarAt:put: perform: perform:withArguments: primitiveFailed printString printStringLimitedTo: respondsTo: xxxClass
>>>
>>> If DataStream et al did, add the following:
>>> readDataFrom:size: storeDataOn:
>>>
>>> I'm not sure how to generally handle #yourself. I have this in my decorators as well because I don't want to undecorate when the decorator receives it.
>>>
>>
>> typically use of the mirror protocol, at its most basic, looks like
>>
>>     thisContext object: foo instVarAt: index
>>
>> so #yourself is handled by ending the cascade early, and answering the object (foo in the above).  Yes that's a tedious refactor, but straightforward, no?
>> --
>> _,,,^..^,,,_
>> best, Eliot
>>
>


Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Christoph Thiede
Not sendTo: instead of sentTo:? ;-)

This was the first mention of #sentTo: in this thread :)

And I suppose it is fine to ask the decorator programmer to implement an #undecorated method to return the inner object.

Sounds like a mirror problem again? Perfect decorators can be nested with arbitrary depth. What should be the return value of (EasterDecorator on: (ChristmasDecorator on: Object new)) undecorated?

Von: Squeak-dev <[hidden email]> im Auftrag von Jakob Reschke <[hidden email]>
Gesendet: Samstag, 15. Februar 2020 16:14:54
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] [Bug & design issue] Messages understood or not understood by ProtoObject
 
Not sendTo: instead of sentTo:? ;-)

Agreed that solves it. And I suppose it is fine to ask the decorator
programmer to implement an #undecorated method to return the inner
object. If it makes sense for the particular decorator.


Am Sa., 15. Feb. 2020 um 15:59 Uhr schrieb Thiede, Christoph
<[hidden email]>:


>
> > Example:
>
> >
> >    anObject := (MyDecorator on: anotherObject)
> >       doSomething;
> >       yourself.
> >
> > I would like that anObject is the decorator and not anotherObject at the end.
>
> Marcel told me the following tip for this scenario, which sounds quite interesting to me:
>
> doesNotUnderstand: aMessage
> | result |
> result := aMessage sendTo: target.
> ^ target == result
> ifTrue: [self]
> ifFalse: [result]
>
> :-)
>
> ________________________________
> Von: Squeak-dev <[hidden email]> im Auftrag von Jakob Reschke <[hidden email]>
> Gesendet: Samstag, 15. Februar 2020 09:28:58
> An: The general-purpose Squeak developers list
> Betreff: Re: [squeak-dev] [Bug & design issue] Messages understood or not understood by ProtoObject
>
> Thanks Eliot, I understood that for the inspector use case, but my thought about #yourself was rather the following:
>
> If I pass the decorator around and some domain code (not in Inspector or similar) uses the decorator in a cascade with yourself at the end, I do not want the result to be the undecorated object.
>
> Example:
>
>     anObject := (MyDecorator on: anotherObject)
>        doSomething;
>        yourself.
>
> I would like that anObject is the decorator and not anotherObject at the end.
>
> Am Fr., 14. Feb. 2020 um 22:39 Uhr schrieb Eliot Miranda <[hidden email]>:
>>
>> Hi Jakob,
>>
>> On Fri, Feb 14, 2020 at 1:14 PM Jakob Reschke <[hidden email]> wrote:
>>>
>>> Am Fr., 14. Feb. 2020 um 19:42 Uhr schrieb Eliot Miranda <[hidden email]>:
>>>>
>>>> Hi Christoph,
>>>>
>>>> On Feb 14, 2020, at 9:51 AM, Thiede, Christoph <[hidden email]> wrote:
>>>>
>>>>
>>>> > There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:
>>>>
>>>> I like and do agree to your vision for tidying up ProtoObject!
>>>> Maybe we could also consider some "MetaDecorator" that inherits from ProtoObject and implements #class, #instVarAt: etc. for arbitrary targets? This might simplify the access to the mirror protocol, and it would be a bit more object-oriented:
>>>>
>>>> (MetaDecorator on: aMorph) class "--> Morph"
>>>> (MetaDecorator on: aProxy) class "--> Proxy"
>>>> (MetaDecorator on: aProxy) instVarsInclude: 'target' "--> true"
>>>>
>>>> Sounds good.
>>>>
>>>>
>>> If inspectors et al would use this, my decorators might not have to carry the following collection of methods/copies:
>>>
>>> asExplorerString basicInspect basicSize class instVarAt: instVarAt:put: perform: perform:withArguments: primitiveFailed printString printStringLimitedTo: respondsTo: xxxClass
>>>
>>> If DataStream et al did, add the following:
>>> readDataFrom:size: storeDataOn:
>>>
>>> I'm not sure how to generally handle #yourself. I have this in my decorators as well because I don't want to undecorate when the decorator receives it.
>>>
>>
>> typically use of the mirror protocol, at its most basic, looks like
>>
>>     thisContext object: foo instVarAt: index
>>
>> so #yourself is handled by ending the cascade early, and answering the object (foo in the above).  Yes that's a tedious refactor, but straightforward, no?
>> --
>> _,,,^..^,,,_
>> best, Eliot
>>
>




Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Jakob Reschke-2
Am Sa., 15. Feb. 2020 um 16:56 Uhr schrieb Thiede, Christoph
<[hidden email]>:
>
> > Not sendTo: instead of sentTo:? ;-)
>
> This was the first mention of #sentTo: in this thread :)

Sorry, I misread. I was referring to the tip you got from Marcel, but
now I see it uses sendTo: already. Lucky for me, we do not also have a
sentDo: ...

> Sounds like a mirror problem again? Perfect decorators can be nested with arbitrary depth. What should be the return value of (EasterDecorator on: (ChristmasDecorator on: Object new)) undecorated?

It depends of course. :-) In my case #undecorated used to strip them
all off, answering the Object at the core. But I think that design was
never perfect anyway. Ideally I would not have had the need to
undecorate anything, right?

Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Christoph Thiede

Ideally I would not have had the need to undecorate anything, right?


At least it seems an interesting vision :)
Also see #xxxUnTrace, an interesting approach.

Von: Squeak-dev <[hidden email]> im Auftrag von Jakob Reschke <[hidden email]>
Gesendet: Samstag, 15. Februar 2020 17:49:27
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] [Bug & design issue] Messages understood or not understood by ProtoObject
 
Am Sa., 15. Feb. 2020 um 16:56 Uhr schrieb Thiede, Christoph
<[hidden email]>:
>
> > Not sendTo: instead of sentTo:? ;-)
>
> This was the first mention of #sentTo: in this thread :)

Sorry, I misread. I was referring to the tip you got from Marcel, but
now I see it uses sendTo: already. Lucky for me, we do not also have a
sentDo: ...

> Sounds like a mirror problem again? Perfect decorators can be nested with arbitrary depth. What should be the return value of (EasterDecorator on: (ChristmasDecorator on: Object new)) undecorated?

It depends of course. :-) In my case #undecorated used to strip them
all off, answering the Object at the core. But I think that design was
never perfect anyway. Ideally I would not have had the need to
undecorate anything, right?



Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Eliot Miranda-2
In reply to this post by Christoph Thiede

On Feb 15, 2020, at 6:59 AM, Thiede, Christoph <[hidden email]> wrote:



Example:

>    anObject := (MyDecorator on: anotherObject)
>       doSomething;
>       yourself.
> I would like that anObject is the decorator and not anotherObject at the end.

Marcel told me the following tip for this scenario, which sounds quite interesting to me:

doesNotUnderstand: aMessage
| result |
result := aMessage sendTo: target.
^ target == result
ifTrue: [self]
ifFalse: [result]

+1.  Everyone writing proxies should be aware of this.  BUT!! !Ot should be written as

^ (thisContext object: target identical: result)
       ifTrue: [self]
       ifFalse: [result]

to avoid sending a message if #== is a true message send (which is what we’re intending to arrange).

P.S. I may have the selector wrong, but there is a mirror identical.  Whatever the selector is, I meant that.

:-)


Von: Squeak-dev <[hidden email]> im Auftrag von Jakob Reschke <[hidden email]>
Gesendet: Samstag, 15. Februar 2020 09:28:58
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] [Bug & design issue] Messages understood or not understood by ProtoObject
 
Thanks Eliot, I understood that for the inspector use case, but my thought about #yourself was rather the following:

If I pass the decorator around and some domain code (not in Inspector or similar) uses the decorator in a cascade with yourself at the end, I do not want the result to be the undecorated object.

Example:

    anObject := (MyDecorator on: anotherObject)
       doSomething;
       yourself.

I would like that anObject is the decorator and not anotherObject at the end.

Am Fr., 14. Feb. 2020 um 22:39 Uhr schrieb Eliot Miranda <[hidden email]>:
Hi Jakob,

On Fri, Feb 14, 2020 at 1:14 PM Jakob Reschke <[hidden email]> wrote:
Am Fr., 14. Feb. 2020 um 19:42 Uhr schrieb Eliot Miranda <[hidden email]>:
Hi Christoph,

On Feb 14, 2020, at 9:51 AM, Thiede, Christoph <[hidden email]> wrote:

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

I like and do agree to your vision for tidying up ProtoObject!
Maybe we could also consider some "MetaDecorator" that inherits from ProtoObject and implements #class, #instVarAt: etc. for arbitrary targets? This might simplify the access to the mirror protocol, and it would be a bit more object-oriented:

(MetaDecorator on: aMorph) class "--> Morph"
(MetaDecorator on: aProxy) class "--> Proxy"
(MetaDecorator on: aProxy) instVarsInclude: 'target' "--> true"

Sounds good.

If inspectors et al would use this, my decorators might not have to carry the following collection of methods/copies:

asExplorerString basicInspect basicSize class instVarAt: instVarAt:put: perform: perform:withArguments: primitiveFailed printString printStringLimitedTo: respondsTo: xxxClass

If DataStream et al did, add the following:
readDataFrom:size: storeDataOn:

I'm not sure how to generally handle #yourself. I have this in my decorators as well because I don't want to undecorate when the decorator receives it.


typically use of the mirror protocol, at its most basic, looks like

    thisContext object: foo instVarAt: index

so #yourself is handled by ending the cascade early, and answering the object (foo in the above).  Yes that's a tedious refactor, but straightforward, no?  
--
_,,,^..^,,,_
best, Eliot




Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

marcel.taeumel
Hi Eliot.

P.S. I may have the selector wrong, but there is a mirror identical.  Whatever the selector is, I meant that.

It is Context >> #object:eqeq:.

Best,
Marcel

Am 16.02.2020 02:59:55 schrieb Eliot Miranda <[hidden email]>:


On Feb 15, 2020, at 6:59 AM, Thiede, Christoph <[hidden email]> wrote:



Example:

>    anObject := (MyDecorator on: anotherObject)
>       doSomething;
>       yourself.
> I would like that anObject is the decorator and not anotherObject at the end.

Marcel told me the following tip for this scenario, which sounds quite interesting to me:

doesNotUnderstand: aMessage
| result |
result := aMessage sendTo: target.
^ target == result
ifTrue: [self]
ifFalse: [result]

+1.  Everyone writing proxies should be aware of this.  BUT!! !Ot should be written as

^ (thisContext object: target identical: result)
       ifTrue: [self]
       ifFalse: [result]

to avoid sending a message if #== is a true message send (which is what we’re intending to arrange).

P.S. I may have the selector wrong, but there is a mirror identical.  Whatever the selector is, I meant that.

:-)


Von: Squeak-dev <[hidden email]> im Auftrag von Jakob Reschke <[hidden email]>
Gesendet: Samstag, 15. Februar 2020 09:28:58
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] [Bug & design issue] Messages understood or not understood by ProtoObject
 
Thanks Eliot, I understood that for the inspector use case, but my thought about #yourself was rather the following:

If I pass the decorator around and some domain code (not in Inspector or similar) uses the decorator in a cascade with yourself at the end, I do not want the result to be the undecorated object.

Example:

    anObject := (MyDecorator on: anotherObject)
       doSomething;
       yourself.

I would like that anObject is the decorator and not anotherObject at the end.

Am Fr., 14. Feb. 2020 um 22:39 Uhr schrieb Eliot Miranda <[hidden email]>:
Hi Jakob,

On Fri, Feb 14, 2020 at 1:14 PM Jakob Reschke <[hidden email]> wrote:
Am Fr., 14. Feb. 2020 um 19:42 Uhr schrieb Eliot Miranda <[hidden email]>:
Hi Christoph,

On Feb 14, 2020, at 9:51 AM, Thiede, Christoph <[hidden email]> wrote:

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

I like and do agree to your vision for tidying up ProtoObject!
Maybe we could also consider some "MetaDecorator" that inherits from ProtoObject and implements #class, #instVarAt: etc. for arbitrary targets? This might simplify the access to the mirror protocol, and it would be a bit more object-oriented:

(MetaDecorator on: aMorph) class "--> Morph"
(MetaDecorator on: aProxy) class "--> Proxy"
(MetaDecorator on: aProxy) instVarsInclude: 'target' "--> true"

Sounds good.

If inspectors et al would use this, my decorators might not have to carry the following collection of methods/copies:

asExplorerString basicInspect basicSize class instVarAt: instVarAt:put: perform: perform:withArguments: primitiveFailed printString printStringLimitedTo: respondsTo: xxxClass

If DataStream et al did, add the following:
readDataFrom:size: storeDataOn:

I'm not sure how to generally handle #yourself. I have this in my decorators as well because I don't want to undecorate when the decorator receives it.


typically use of the mirror protocol, at its most basic, looks like

    thisContext object: foo instVarAt: index

so #yourself is handled by ending the cascade early, and answering the object (foo in the above).  Yes that's a tedious refactor, but straightforward, no?  
--
_,,,^..^,,,_
best, Eliot




Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

marcel.taeumel
In reply to this post by Christoph Thiede
Hi Christoph.

At the byte-code level, one can see the inlining. The #class send is C7, while any other unary message send seems to be D0:



Take look at EncoderV3 class >> #scanBlockOrNilForLiteral: and also explore "Smalltalk specialSelectors".

Best,
Marcel

Am 14.02.2020 18:51:42 schrieb Thiede, Christoph <[hidden email]>:

Hi Eliot,


Or provide a vm flag which controls whether special selector #class (the bytecode for sending #class) is inlined or not.  This flag can also control #== & #~~.


Could you give me some (code) pointers about this inlining? :-)

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

I like and do agree to your vision for tidying up ProtoObject!
Maybe we could also consider some "MetaDecorator" that inherits from ProtoObject and implements #class, #instVarAt: etc. for arbitrary targets? This might simplify the access to the mirror protocol, and it would be a bit more object-oriented:

(MetaDecorator on: aMorph) class "--> Morph"
(MetaDecorator on: aProxy) class "--> Proxy"
(MetaDecorator on: aProxy) instVarsInclude: 'target' "--> true"

Hopefully I’ve answered your questions and given you pointers.  I’m interested in collaborating to make this a reality in the next major release.

You did. I'm too.

Best,
Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Eliot Miranda <[hidden email]>
Gesendet: Freitag, 14. Februar 2020 18:27:14
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] [Bug & design issue] Messages understood or not understood by ProtoObject
 
Hi Christoph,

On Feb 14, 2020, at 5:18 AM, Thiede, Christoph <[hidden email]> wrote:



Hi all! :-)

When we were fiddling around with some proxy implementations today, Marcel and I discovered an interesting set of issues regarding ProtoObjects.

Let's start with a concrete bug:


Code to reproduce <do it>:

ProtoObject new class.


Expected behavior:

DNU exception, because #class is implemented on Object only.


Actual behavior:

Outputs ProtoObject.

Even stranger: If you debug the method instead and simulate the #class send, you get a "simulated message class not understood" error.

If you subclass ProtoObject and override/forward #class, the results will deviate based on whether you are debugging or executing the code.


Some thoughts:

There are two options: Either to implement #class on ProtoObject (by moving the primitive definition up from Object), or to remove it from the specialObjectsArray so that a send to #class is not compiled differently than a send to any other regular message.


Or provide a vm flag which controls whether special selector #class (the bytecode for sending #class) is inlined or not.  This flag can also control #== & #~~.


This leads us to the rather general question: What special messages should an instance of ProtoObject understand?


IMO *only* #doesNotUnderstand:

See below on the mirror protocol

We don't answer this question completely consistent at the moment: For example, #instVarAt: is implemented on Object, but #instVarsInclude: is implemented on ProtoObject. Again, it is a bit weird that the implementation of #instVarsInclude: calls #class which is implemented on Object only. And so it goes on:

  • #someObject is implemented on Object, but #nextObject is available in ProtoObject.
  • #withArgs:executeMethod: is implemented on ProtoObject, whereas #perform:withArguments: is on Object.
  • #ifNotNil: is implemented on ProtoObject; however, #ifNotNilDo: is implemented on Object. (btw: does the latter still have any raison d'être?)
  • #flag: exists on ProtoObject, and #yourself exists on Object only. Isn't #yourself rather a syntactical element we would like to behave exactly the same way for every possible message receiver? (btw: Is there a good reason not to speed up #yourself via specialObjectsArray)?
  • (I don't claim for completeness)

And just some other problems regarding to ProtoObject (just collecting them here instead of forgetting them forever):
  • ObjectTracer is broken due to several reasons. Will fix this soon.
  • Inspectors cannot inspect ProtoObjects correctly (for example: Inspector openOn: (ObjectTracer on: Display)). This is because #instVarAt: is implemented on Object only so this message is forwarded.
    Maybe we should replace these critical calls from Inspector by something like #tryPrimitive:173withArgs:? But should we do so in Inspector or rather in a new subclass named ProtoInspector or similar? Hm, then we would need to implement #inspectorClass on ProtoObject, which is bad either ... We will keep investigating this issue.

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

You see hacks everywhere (the xxxFoo messages in serialization) because we don’t have a well-defined notion of what is in a proxy.  Having one that is as minimal as possible is IMNERHO the clearest definition possible, and the most minimal is just having a #doesNotUnderstand: that prevents recursive does not understand vm exit and displays a stack trace without sending further messages to the proxy (because it uses the mirror primitives).

Note that the mirror primitives are what the execution machinery in Context use to ensure that bytecodes do what they are expected to do.  An I et car access should *not* send instVarAt: to a proxy; it should access the inst var of the proxy, etc.


However, the crucial question is: What special messages should an instance of ProtoObject understand? On the one hand, the idea of ProtoObjects is to work as total proxies with a maximum amount of forwarding potential, which implies a minimum number of methods. On the other hand, certain aspects such as accessing instvars or executing messages are really identity-related. Looking forward to your comments :-)

Hopefully I’ve answered your questions and given you pointers.  I’m interested in collaborating to make this a reality in the next major release.


Best,
Christoph




Reply | Threaded
Open this post in threaded view
|

Re: [Bug & design issue] Messages understood or not understood by ProtoObject

Christoph Thiede

Hi Marcel,


yes, I already discovered both. I thought I introduced this into the initial problem statement :-)


Best,

Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Sonntag, 16. Februar 2020 11:57:33
An: Javier Diaz-Reinoso via Squeak-dev
Betreff: Re: [squeak-dev] [Bug & design issue] Messages understood or not understood by ProtoObject
 
Hi Christoph.

At the byte-code level, one can see the inlining. The #class send is C7, while any other unary message send seems to be D0:



Take look at EncoderV3 class >> #scanBlockOrNilForLiteral: and also explore "Smalltalk specialSelectors".

Best,
Marcel

Am 14.02.2020 18:51:42 schrieb Thiede, Christoph <[hidden email]>:

Hi Eliot,


Or provide a vm flag which controls whether special selector #class (the bytecode for sending #class) is inlined or not.  This flag can also control #== & #~~.


Could you give me some (code) pointers about this inlining? :-)

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

I like and do agree to your vision for tidying up ProtoObject!
Maybe we could also consider some "MetaDecorator" that inherits from ProtoObject and implements #class, #instVarAt: etc. for arbitrary targets? This might simplify the access to the mirror protocol, and it would be a bit more object-oriented:

(MetaDecorator on: aMorph) class "--> Morph"
(MetaDecorator on: aProxy) class "--> Proxy"
(MetaDecorator on: aProxy) instVarsInclude: 'target' "--> true"

Hopefully I’ve answered your questions and given you pointers.  I’m interested in collaborating to make this a reality in the next major release.

You did. I'm too.

Best,
Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Eliot Miranda <[hidden email]>
Gesendet: Freitag, 14. Februar 2020 18:27:14
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] [Bug & design issue] Messages understood or not understood by ProtoObject
 
Hi Christoph,

On Feb 14, 2020, at 5:18 AM, Thiede, Christoph <[hidden email]> wrote:



Hi all! :-)

When we were fiddling around with some proxy implementations today, Marcel and I discovered an interesting set of issues regarding ProtoObjects.

Let's start with a concrete bug:


Code to reproduce <do it>:

ProtoObject new class.


Expected behavior:

DNU exception, because #class is implemented on Object only.


Actual behavior:

Outputs ProtoObject.

Even stranger: If you debug the method instead and simulate the #class send, you get a "simulated message class not understood" error.

If you subclass ProtoObject and override/forward #class, the results will deviate based on whether you are debugging or executing the code.


Some thoughts:

There are two options: Either to implement #class on ProtoObject (by moving the primitive definition up from Object), or to remove it from the specialObjectsArray so that a send to #class is not compiled differently than a send to any other regular message.


Or provide a vm flag which controls whether special selector #class (the bytecode for sending #class) is inlined or not.  This flag can also control #== & #~~.


This leads us to the rather general question: What special messages should an instance of ProtoObject understand?


IMO *only* #doesNotUnderstand:

See below on the mirror protocol

We don't answer this question completely consistent at the moment: For example, #instVarAt: is implemented on Object, but #instVarsInclude: is implemented on ProtoObject. Again, it is a bit weird that the implementation of #instVarsInclude: calls #class which is implemented on Object only. And so it goes on:

  • #someObject is implemented on Object, but #nextObject is available in ProtoObject.
  • #withArgs:executeMethod: is implemented on ProtoObject, whereas #perform:withArguments: is on Object.
  • #ifNotNil: is implemented on ProtoObject; however, #ifNotNilDo: is implemented on Object. (btw: does the latter still have any raison d'être?)
  • #flag: exists on ProtoObject, and #yourself exists on Object only. Isn't #yourself rather a syntactical element we would like to behave exactly the same way for every possible message receiver? (btw: Is there a good reason not to speed up #yourself via specialObjectsArray)?
  • (I don't claim for completeness)

And just some other problems regarding to ProtoObject (just collecting them here instead of forgetting them forever):
  • ObjectTracer is broken due to several reasons. Will fix this soon.
  • Inspectors cannot inspect ProtoObjects correctly (for example: Inspector openOn: (ObjectTracer on: Display)). This is because #instVarAt: is implemented on Object only so this message is forwarded.
    Maybe we should replace these critical calls from Inspector by something like #tryPrimitive:173withArgs:? But should we do so in Inspector or rather in a new subclass named ProtoInspector or similar? Hm, then we would need to implement #inspectorClass on ProtoObject, which is bad either ... We will keep investigating this issue.

There is a good solution to this.  Low-level Inspectors *should not* use instVarAt: et al.  Instead they should access object intervals via “the mirror protocol”, see the methods in Context, #object:instVarAt:[put:] #object:at:[put:] #objectClass:

You see hacks everywhere (the xxxFoo messages in serialization) because we don’t have a well-defined notion of what is in a proxy.  Having one that is as minimal as possible is IMNERHO the clearest definition possible, and the most minimal is just having a #doesNotUnderstand: that prevents recursive does not understand vm exit and displays a stack trace without sending further messages to the proxy (because it uses the mirror primitives).

Note that the mirror primitives are what the execution machinery in Context use to ensure that bytecodes do what they are expected to do.  An I et car access should *not* send instVarAt: to a proxy; it should access the inst var of the proxy, etc.


However, the crucial question is: What special messages should an instance of ProtoObject understand? On the one hand, the idea of ProtoObjects is to work as total proxies with a maximum amount of forwarding potential, which implies a minimum number of methods. On the other hand, certain aspects such as accessing instvars or executing messages are really identity-related. Looking forward to your comments :-)

Hopefully I’ve answered your questions and given you pointers.  I’m interested in collaborating to make this a reality in the next major release.


Best,
Christoph