We speak of message passing when the objects as defined in the interpreter implement the send operation whose interface looks as follows:
object = { private: ... public: send(m:message,arguments:objects) }
Hence, the send operation only takes a message and a list of arguments, which are also objects. If the object does self-sends, it uses itself at the implementation level. It is impossible to 'change' the self of an object.
The difference with delegation is that with delegation, the send operation is parametrised by an extra parameter, which represents the 'self' the object will use:
object = { private: ... public: delegate(m:message,arguments:objects,self:object) }
When objects (at the implementation level) define the delegate message, an external client can invoke a message on the object, thereby transferring 'another' self to the object. And this is what is fundamental about delegation: the ability to invoke a message on an object with the 'self' of the inheriting (or delegating) object.
The fact that objects only implement the send is the whole idea behind Agora. Notice that even the interpreter cannot do more with object than message sending. Hence, if we look at objects seen through the eyes of the interpreter (which is exactly what reflection allows us to do), we cannot break the protocol of an object. With delegation, it is relatively easy to breach the encapsulation of objects. This is the major message of our ECOOP'95 paper.