On Tue, Nov 28, 2017 at 10:20 AM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 28 Nov 2017, at 13:52, Mariano Martinez Peck <marianopeck@gmail.com> wrote:
On Tue, Nov 28, 2017 at 9:31 AM, Andreas Brodbeck <dassi@mindclue.ch> wrote: Am 28.11.17 um 12:46 schrieb Mariano Martinez Peck:
Hi Andreas,
Do you know why method contexts are trying to be serialized? Of course, probably because of closures. Do you think / know / are aware of closures as part of your graph? Maybe Sorted Collection? I am asking because if the only thing is SortedCollection then we can use some hook...
I have closures in several places in the object graph. These are objects with some pluggable functionality, which I rely on.
OK. The question is then if those closures are "clean" or not. In other words, what's their scope? Do they refer to variables defined outside the closure (in which case you would need the methods contexts / stack) or are they clean in the sense that you could be able to replace them via a string and then compile them again ? Some time ago we added a #isClean to BlockClosure. You can test a few of your closures to see if this is the case or not.
Btw, I have an application for a client in which we also have closures to define some pluggable behavior. But what I do is:
1) guarantee they are 100% clean (does not go outside of scope) 2) aside from the "block" instVar I also add another instVar which is the "string" version of it. Then I always implement # fuelIgnoredInstanceVariableNames to ignore all those "block" instVars, yet DO NOT ignore the "string" like of those closures. Then, of course, the getter of the block instvar does a lazy compilation if the block instVar is nil...
Ah, that's cheating ;-)
I didn't say it wasn't hahahahahaha
But totally acceptable with clean blocks.
So,
[ :x :y | x < y ] isClean.
=> true
[ :x :y | x < y ] sourceNode formattedCode.
=> '[ :x :y | x < y ]'
But
Compiler evaluate: '[ :x :y | x < y ]'.
=> [ :arg1 :arg2 | arg1 < arg2 ]
So the argument names get lost, which seems like a pity. How are you doing it ? Can it be done differently ?
I really don't care at all how closures look like. The idea is: always keep the string version instVars and sixx/fuel ignore the block version. block version getter compiles from string if nil. I think you might have understood I use the same instVar and that sometimes I set a string and sometimes I set a closure. If that's the case, then yes, I ended up with 2 different instVars... Example: FaAction class >> fuelIgnoredInstanceVariableNames ^ #('actionBlock') FaPersistentObject subclass: #FaAction instanceVariableNames: 'action actionBlock' classVariableNames: '' package: 'FA-Core' FaAction >> actionBlock ^ actionBlock ifNil: [ action isEmptyOrNil ifTrue: [ nil ] ifFalse: [actionBlock := self compile: action ] ] That's is obviously a simplified example and it might have some syntax error but I guess you get the idea, right? So this has worked very well for me with Fuel on Pharo and Sixx on GemStone. There are obvious drawbacks like: it only works for clean closures, having to have 2 instVar per "concept" , have to compile when I only have the string, etc etc.
I know you already have your application written, but I am trying to explain how I was able to use Fuel for this case...
Just for my better understanding, I like to ask: The FLMethodChanged errors will show up for *EVERY* method of a materialized object (Since the bytecodesHash changed for every method) or just those methods which involve fuel-persisted MethodContexts? (Sorry, if the question is stupid, but I am rather new to Fuel internals)
As far as I can remember, the latter.
Cheers, Andreas
-- Andreas Brodbeck www.mindclue.ch
-- Mariano http://marianopeck.wordpress.com
-- Mariano http://marianopeck.wordpress.com