Easy way to write a proxy?
Hi! I have a near-to-be naive question. Maybe there is something obvious, but I cannot see it. Consider the following implementation of a proxy: -=-=-=-=-=-=-=-=-=-=-=-= Object subclass: #MyProxy instanceVariableNames: 'object'. MyProxy >> object: anObject object := anObject MyProxy >> doesNotUnderstand: aMessage ^ object perform: aMessage selector withArguments: aMessage arguments Object >> wrap self becomeForward: (MyProxy new object: self; yourself) -=-=-=-=-=-=-=-=-=-=-=-= The problem is with wrap. After the becomeForward:, MyProxy has itself in the object variable. Even self is apparently subject to the #become. It means that the following code loop indefinitely -=-=-=-=-=-=-=-=-=-=-=-= d := Dictionary new. d wrap. d add: #foo -> 10 -=-=-=-=-=-=-=-=-=-=-=-= I know there is Marianoâs library to write proxy. But I still wondering whether this could be done simply. Any idea how to simply create a proxy? Cheers, Alexandre -- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
On 27 Feb 2015, at 03:43, Alexandre Bergel <alexandre.bergel@me.com> wrote:
Hi!
I have a near-to-be naive question. Maybe there is something obvious, but I cannot see it. Consider the following implementation of a proxy:
-=-=-=-=-=-=-=-=-=-=-=-= Object subclass: #MyProxy instanceVariableNames: 'object'.
MyProxy >> object: anObject object := anObject
MyProxy >> doesNotUnderstand: aMessage ^ object perform: aMessage selector withArguments: aMessage arguments
Object >> wrap self becomeForward: (MyProxy new object: self; yourself) -=-=-=-=-=-=-=-=-=-=-=-=
The problem is with wrap. After the becomeForward:, MyProxy has itself in the object variable. Even self is apparently subject to the #become.
It means that the following code loop indefinitely
-=-=-=-=-=-=-=-=-=-=-=-= d := Dictionary new. d wrap. d add: #foo -> 10 -=-=-=-=-=-=-=-=-=-=-=-=
I know there is Marianoâs library to write proxy. But I still wondering whether this could be done simply. Any idea how to simply create a proxy?
Hi Alexandre, You have to store the target and the proxy in different temps, do the become and (re)set the link between both after. Or in your case, something like: Object >> wrap | proxyThenTarget | proxyThenTarget := MyProxy new. self become: proxyThenTarget. âNow self is the proxy" self object: proxyThenTarget. ^ self Note that by subclassing Object many messages wonât be intercepted. With your dictionary example it means that you cannot intercept #at: . Subclassing ProtoObject reduces the number of such messages. Also one can bypass these methods by sending #doesNotUndestand: to the proxy. In the end all depends on your requirements: do you care about uniformity (all messages must be intercepted), composition of proxies, security (no target leaks, no interception bypass), what operations do you want to intercept (just message sends or iv accesses in addition), etc... For most cases Ghost is the way to go. Camille
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
Another way to go is just to do this: -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= ProtoObject subclass: #MyProxy instanceVariableNames: 'object' classVariableNames: '' category: â' MyProxy >> initialize object := self MyProxy >> doesNotUnderstand: aMessage ^ object perform: aMessage selector withArguments: aMessage arguments Object >> wrap self become: MyProxy new Object >> isProxy ^ false MyProxy >> isProxy ^ true -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= So the idea is that the reference to self in MyProxy becomes the wrapped object using the become. The main problem of this kind of implementation is that self is bounded to the proxy, so all the internal calls of self are not going to be intercepted and the return self is going to expose the original object. |obj| obj := Object new. obj isProxy. âfalse" obj wrap. obj isProxy. âtrueâ obj yourself isProxy âfalseâ <ââ This one kind of break the proxy Cheers, Alejandro
On Feb 27, 2015, at 9:35 AM, Camille <camille.teruel@gmail.com> wrote:
On 27 Feb 2015, at 03:43, Alexandre Bergel <alexandre.bergel@me.com> wrote:
Hi!
I have a near-to-be naive question. Maybe there is something obvious, but I cannot see it. Consider the following implementation of a proxy:
-=-=-=-=-=-=-=-=-=-=-=-= Object subclass: #MyProxy instanceVariableNames: 'object'.
MyProxy >> object: anObject object := anObject
MyProxy >> doesNotUnderstand: aMessage ^ object perform: aMessage selector withArguments: aMessage arguments
Object >> wrap self becomeForward: (MyProxy new object: self; yourself) -=-=-=-=-=-=-=-=-=-=-=-=
The problem is with wrap. After the becomeForward:, MyProxy has itself in the object variable. Even self is apparently subject to the #become.
It means that the following code loop indefinitely
-=-=-=-=-=-=-=-=-=-=-=-= d := Dictionary new. d wrap. d add: #foo -> 10 -=-=-=-=-=-=-=-=-=-=-=-=
I know there is Marianoâs library to write proxy. But I still wondering whether this could be done simply. Any idea how to simply create a proxy?
Hi Alexandre,
You have to store the target and the proxy in different temps, do the become and (re)set the link between both after. Or in your case, something like:
Object >> wrap | proxyThenTarget | proxyThenTarget := MyProxy new. self become: proxyThenTarget. âNow self is the proxy" self object: proxyThenTarget. ^ self
Note that by subclassing Object many messages wonât be intercepted. With your dictionary example it means that you cannot intercept #at: . Subclassing ProtoObject reduces the number of such messages. Also one can bypass these methods by sending #doesNotUndestand: to the proxy. In the end all depends on your requirements: do you care about uniformity (all messages must be intercepted), composition of proxies, security (no target leaks, no interception bypass), what operations do you want to intercept (just message sends or iv accesses in addition), etc... For most cases Ghost is the way to go.
Camille
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu <http://www.bergel.eu/> ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
So the idea is that the reference to self in MyProxy becomes the wrapped object using the become.
Yes, for some reason, I did not see this. Well spotted! I am getting old apparently :-)
The main problem of this kind of implementation is that self is bounded to the proxy, so all the internal calls of self are not going to be intercepted and the return self is going to expose the original object.
Yes. Maybe the doesNotUnderstand: can be improved to take this into account. Cheers, Alexandre -- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
Le 27/2/15 17:47, Alexandre Bergel a écrit :
So the idea is that the reference to self in MyProxy becomes the wrapped object using the become. Yes, for some reason, I did not see this. Well spotted! I am getting old apparently :-)
The main problem of this kind of implementation is that self is bounded to the proxy, so all the internal calls of self are not going to be intercepted and the return self is going to expose the original object. Yes. Maybe the doesNotUnderstand: can be improved to take this into account. No it is not possible simply (or you have to rename all your methods and recompile all the class/superclass :).
Cheers, Alexandre
For most cases Ghost is the way to go.
We will add a proxy model (based on Ghost or just ghost as is) to Pharo by default in Pharo5. Then people can just use it. -- Marcus Denker -- denker@acm.org http://www.marcusdenker.de
On Fri, Feb 27, 2015 at 7:10 AM, Marcus Denker <marcus.denker@inria.fr> wrote:
For most cases Ghost is the way to go.
We will add a proxy model (based on Ghost or just ghost as is) to Pharo by default in Pharo5. Then people can just use it.
And with the lazy become of Spur / new Cog that would be a super great combination. -- Mariano http://marianopeck.wordpress.com
For most cases Ghost is the way to go.
We will add a proxy model (based on Ghost or just ghost as is) to Pharo by default in Pharo5. Then people can just use it.
And with the lazy become of Spur / new Cog that would be a super great combination.
Oh yes! We thought about you when we were talking about Spur recently :)
-- Mariano http://marianopeck.wordpress.com
2015-02-27 14:06 GMT+01:00 Mariano Martinez Peck <marianopeck@gmail.com>:
On Fri, Feb 27, 2015 at 7:10 AM, Marcus Denker <marcus.denker@inria.fr> wrote:
For most cases Ghost is the way to go.
We will add a proxy model (based on Ghost or just ghost as is) to Pharo by default in Pharo5. Then people can just use it.
And with the lazy become of Spur / new Cog that would be a super great combination.
I made some benchs for a paper and it's crazy. Current become has
performance proportional to the heap size, and for example it's a 350ms pause for a 500Mb heap on my machine (or 27ms for 35Mb heap). In spur it is 0.2ms independently of the heap size. It goes down to 0.035ms if both objects have the same size in memory.
-- Mariano http://marianopeck.wordpress.com
2015-02-27 20:57 GMT+01:00 Clément Bera <bera.clement@gmail.com>:
2015-02-27 14:06 GMT+01:00 Mariano Martinez Peck <marianopeck@gmail.com>:
On Fri, Feb 27, 2015 at 7:10 AM, Marcus Denker <marcus.denker@inria.fr> wrote:
For most cases Ghost is the way to go.
We will add a proxy model (based on Ghost or just ghost as is) to Pharo by default in Pharo5. Then people can just use it.
And with the lazy become of Spur / new Cog that would be a super great combination.
I made some benchs for a paper and it's crazy. Current become has
performance proportional to the heap size, and for example it's a 350ms pause for a 500Mb heap on my machine (or 27ms for 35Mb heap). In spur it is 0.2ms independently of the heap size. It goes down to 0.035ms if both objects have the same size in memory.
Mariano was dreaming about blazing fast become for Ghost/Fuel/Marea ;-) Luc
-- Mariano http://marianopeck.wordpress.com
Excellent! Alexandre
On Feb 27, 2015, at 7:10 AM, Marcus Denker <marcus.denker@inria.fr> wrote:
For most cases Ghost is the way to go.
We will add a proxy model (based on Ghost or just ghost as is) to Pharo by default in Pharo5. Then people can just use it.
-- Marcus Denker -- denker@acm.org http://www.marcusdenker.de
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
Object >> wrap | proxyThenTarget | proxyThenTarget := MyProxy new. self become: proxyThenTarget. âNow self is the proxy" self object: proxyThenTarget. ^ self
Tshuuâ¦. I did not think about this. Well spotted!!! Alexandre -- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
Alex why ghost is not ok? Because you want to have a portable proxy? Stef Le 27/2/15 03:43, Alexandre Bergel a écrit :
Hi!
I have a near-to-be naive question. Maybe there is something obvious, but I cannot see it. Consider the following implementation of a proxy:
-=-=-=-=-=-=-=-=-=-=-=-= Object subclass: #MyProxy instanceVariableNames: 'object'.
MyProxy >> object: anObject object := anObject
MyProxy >> doesNotUnderstand: aMessage ^ object perform: aMessage selector withArguments: aMessage arguments
Object >> wrap self becomeForward: (MyProxy new object: self; yourself) -=-=-=-=-=-=-=-=-=-=-=-=
The problem is with wrap. After the becomeForward:, MyProxy has itself in the object variable. Even self is apparently subject to the #become.
It means that the following code loop indefinitely
-=-=-=-=-=-=-=-=-=-=-=-= d := Dictionary new. d wrap. d add: #foo -> 10 -=-=-=-=-=-=-=-=-=-=-=-=
I know there is Marianoâs library to write proxy. But I still wondering whether this could be done simply. Any idea how to simply create a proxy?
Cheers, Alexandre
Alex why ghost is not ok?
At that stage, it was more of a mental exercise.
Because you want to have a portable proxy?
If our implementation goes above a simple prototype, yes, it will have to work on VisualWorks. Alexandre
Le 27/2/15 03:43, Alexandre Bergel a écrit :
Hi!
I have a near-to-be naive question. Maybe there is something obvious, but I cannot see it. Consider the following implementation of a proxy:
-=-=-=-=-=-=-=-=-=-=-=-= Object subclass: #MyProxy instanceVariableNames: 'object'.
MyProxy >> object: anObject object := anObject
MyProxy >> doesNotUnderstand: aMessage ^ object perform: aMessage selector withArguments: aMessage arguments
Object >> wrap self becomeForward: (MyProxy new object: self; yourself) -=-=-=-=-=-=-=-=-=-=-=-=
The problem is with wrap. After the becomeForward:, MyProxy has itself in the object variable. Even self is apparently subject to the #become.
It means that the following code loop indefinitely
-=-=-=-=-=-=-=-=-=-=-=-= d := Dictionary new. d wrap. d add: #foo -> 10 -=-=-=-=-=-=-=-=-=-=-=-=
I know there is Marianoâs library to write proxy. But I still wondering whether this could be done simply. Any idea how to simply create a proxy?
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
participants (8)
-
Alejandro Infante -
Alexandre Bergel -
Camille -
Clément Bera -
Luc Fabresse -
Marcus Denker -
Mariano Martinez Peck -
stepharo