[squeak-dev] what is a transparent proxy?
christoph.thiede at student.hpi.uni-potsdam.de
christoph.thiede at student.hpi.uni-potsdam.de
Sun Jan 16 17:21:10 UTC 2022
my understanding of a "transparent proxy" is that it automatically forwards all requests, thus requiring special metaprogramming logic to reveal an object as a proxy. On the other hand, "intransparent proxies" would define an explicit list of requests to forward. Not sure however whether this is a useful classification ...
> > - ProtoObject is still not yet an empty class. For instance, stuff such as #doOnlyOnce: or #scaledIdentityHash should be implemented on Object only. We should continue work on this in the Trunk. You can mitigate this by redefining these messages in your proxy and forwarding them yourself.
> It would arguably be a more elegant way of organizing code, but not much else.
One central difference it that only an empty ProtoObject allows you for defining a truly transparent proxy. Otherwise, you are required to override a larger number of messages and forward each of them yourself.
> > - Primitive methods do not resolve proxies automatically, i.e., #(1 2 3) at: (MyProxy1 for: 2) would have the primitive failed. My mitigation for this problem was to define an exclusion list for certain classes that should never be wrapped into a proxy, but this is not an ideal solution,
> My mitigation strategy was a simple "yourself" on the end. It instantly gives away to future developers familiar with writing proxy-aware code. It seems like you're willing to go through to avoid that.
Well, this might depend on the use case of your proxies. I have a proxy class that automatically wraps every answer into another proxy to decorate an entire object graph, so an exclusion list for certain types felt like the right approach to me. Building a framework, I have also no control about to which primitives the client sends these answered instances.
> Now you're talkin'! :-) Proxy-aware code!!
I really think this would a small pattern which should be no huge effort to apply to all <primitive:> sends in the Trunk. :-) How could we best design such a pattern? For instance, looking at Object >> #at:
index isInteger ifTrue:
[self class isVariable
ifTrue: [self errorSubscriptBounds: index]
ifFalse: [self errorNotIndexable]].
ifTrue: [^self at: index asInteger]
ifFalse: [self errorNonIntegerIndex]
We could insert this statement at the beginning of the method:
(index isInteger and: [((thisContext objectClass: index) includesBehavior: Integer) not]) ifTrue:
[^ self at: index yourself].
Object>>#perform:withArguments:inSuperclass: could be rewritten like this:
perform: selector withArguments: argArray inSuperclass: lookupClass
+ (selector isSymbol and: [((thisContext objectClass: selector) includesBehavior: Symbol) not])
ifTrue: [^ self perform: selector yourself withArguments: argArray inSuperclass: lookupClass].
ifFalse: [^ self error: 'selector argument must be a Symbol'].
(selector numArgs = argArray size)
ifFalse: [^ self error: 'incorrect number of arguments'].
(self class == lookupClass or: [self class inheritsFrom: lookupClass])
ifFalse: [^ self error: 'lookupClass is not in my inheritance chain'].
And so on for almost all the other primitive users as well.
For #==, it would be harder because our current semantics of identity say that a proxy has its own identity, and it would probably be a bad decision to change this semantics. Maybe we would need a differentiation between "proxy-aware identity" and "effective identity", and #= (like in Symbol >> #=) would be one of many cases where the latter concept would be required. But complexity is increasing right now ...
See also the discussion about the right place for the mirror protocol. 
Any other ideas how we could minify/beautify the above pattern? :-)
Sent from Squeak Inbox Talk
On 2022-01-15T18:51:00-06:00, asqueaker at gmail.com wrote:
> Hi Craig,
> > Indeed, none of our code should be proxy-aware.
> I certainly would agree with the statement, "it would be nice if none
> of our code needed to be proxy-aware," but I don't yet understand how
> it's possible, regardless where the implementation details are handled
> (VM or image). As a VM expert, I can appreciate you preferring to use
> VM-based proxies. The only downside is that those who only understand
> Smalltalk and not the secret innards of the VM cannot realistically
> participate in Squeak anymore if they wish or need to use the classic
> Proxy pattern. We broke the code and also legacy systems for that.
> Which is why I keep bringing up just this one example on Symbol
> and hoping someone can explain it to help me get unstuck. Would you
> follow its code with me, line by line, below? Let's pretend the
> receiver is the symbol #size, and the argument, aSymbol, comes in as
> an instance of MyProxy, which refers to an Integer oid that ultimately
> will reify to the receiver object (#size)).
> It's easy to see why the code no longer works for the traditional Proxy pattern:
> = aSymbol "<------ aSymbol comes in as a MyProxy instance"
> self == aSymbol ifTrue: [^ true]. "<---- false, continue"
> aSymbol isSymbol ifTrue: [^ false]. "<----- true, return false. BUG!"
> "Use String comparison otherwise"
> ^ super = aSymbol
> So what about when aVMProxy instance is passed in? I'm so used to
> normal Smalltalk-think, I'm not even able to think about WHEN or HOW
> the VM would perform it's magic handling of the incoming VMProxy
> instance to make this work. I guess this is a trade-off -- we don't
> have to think about Proxy's, but we have to know about and think about
> VM magic. I'm willing to try, but I don't even know where to begin.
> Would you help?
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Squeak-dev