How to intercept every instance variable write on all objects

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

How to intercept every instance variable write on all objects

Pharo Smalltalk Users mailing list
Hello,

is possible to intercept (before writeú all instance variable writes on every existing objects?

I found a discussion here:
http://forum.world.st/Is-it-possible-to-follow-an-object-through-a-computation-td5092459.html

Known solutions:

1) jugglery with object read only state + ModificationForbidden - not ideal, it's probably slow via exceptions and every nested on:do: stop change detection

2) it's possible to do it via slots - but it does not work on any existing object (only on my specially prepared objects with slots)

3) Metalinks - it's based on intercepting method calls, right? But I need to intercept direct instance variable write, so probably not possible

Is there any way? Thanks! pf



Reply | Threaded
Open this post in threaded view
|

Re: How to intercept instvar write - MetaLinks + anonymous subclass problem

Pharo Smalltalk Users mailing list
> is possible to intercept (before writeú all instance variable writes on every existing objects?
>
> I found a discussion here:
> http://forum.world.st/Is-it-possible-to-follow-an-object-through-a-computation-td5092459.html
>
> Known solutions:
> ....
> 3) Metalinks - it's based on intercepting method calls, right? But I need to intercept direct instance variable write, so probably not possible

Ok, I learned more about MetaLinks and it's perfectly possible to intercept instance variable writes via. installing MetaLink into object instance (cool) - then I can intercept instvar write, but one problem immediately occurs:
when installing MetaLink into object instance, an original object class is exchanged to anonymous subclass of the original class - and because typical #= method is implemented like:

=
        self == anObject
                ifTrue: [ ^ true ].
        self class = anObject class
                ifFalse: [ ^ false ].
        ...

then most of #= comparisons in application logic is broken due to "self class = anObject class" part.

Is there any way to solve this problem?

Thanks! pf

Reply | Threaded
Open this post in threaded view
|

Re: How to intercept instvar write - MetaLinks + anonymous subclass problem

Steven Costiou-2

 

is possible to intercept (before writeú all instance variable writes on every existing objects?


Ok, I learned more about MetaLinks and it's perfectly possible to intercept instance variable writes via. installing MetaLink into object instance (cool) - then I can intercept instvar write, but one problem immediately occurs:
when installing MetaLink into object instance, an original object class is exchanged to anonymous subclass of the original class - and because typical #= method is implemented like:

=
    self == anObject
        ifTrue: [ ^ true ].
    self class = anObject class
        ifFalse: [ ^ false ].
    ...

then most of #= comparisons in application logic is broken due to "self class = anObject class" part.

Is there any way to solve this problem?

Hi,

the anonymous class thing only happen when you install an object-centric metalink, i.e. on only one specific object. I also wanted to hide the anonymous class, or maybe to only make it visible for environment tools. I need to talk about that with Marcus.

Why not installing your metalink on the classes of your objects, so that all instances are affected but in that case there is no anonymous class migration?

If that is not possible, in your case maybe a (temporary) hack would be, in your case, to add a test in the #class method in Object (or wherever it is defined):

class

    |myClass|

    myClass := self class.

    ^myClass isAnonymous ifTrue:[myClass superclass] ifFalse:[myClass]

But i did not test that and i have no idea of the overall impact.

 

Steven.

Reply | Threaded
Open this post in threaded view
|

Re: How to intercept every instance variable write on all objects

Peter Uhnak
In reply to this post by Pharo Smalltalk Users mailing list
Hi,

3) Metalinks - it's based on intercepting method calls, right? But I need to intercept direct instance variable write, so probably not possible

no, you can attach metalink to any AST node, including an Assignment Node.
Some years ago I've made a small utility for this that also auto-reinstalled when the method has changed.
Should work with P6, not sure about the current P7 https://github.com/peteruhnak/metalinks-toolkit .

But of course you can attach the MetaLink directly.

P


On Fri, Feb 1, 2019 at 12:43 AM Petr Fischer via Pharo-users <[hidden email]> wrote:
Hello,

is possible to intercept (before writeú all instance variable writes on every existing objects?

I found a discussion here:
http://forum.world.st/Is-it-possible-to-follow-an-object-through-a-computation-td5092459.html

Known solutions:

1) jugglery with object read only state + ModificationForbidden - not ideal, it's probably slow via exceptions and every nested on:do: stop change detection

2) it's possible to do it via slots - but it does not work on any existing object (only on my specially prepared objects with slots)

3) Metalinks - it's based on intercepting method calls, right? But I need to intercept direct instance variable write, so probably not possible

Is there any way? Thanks! pf



Reply | Threaded
Open this post in threaded view
|

Re: How to intercept instvar write - MetaLinks + anonymous subclass problem

Pharo Smalltalk Users mailing list
In reply to this post by Steven Costiou-2
> > is possible to intercept (before writeú all instance variable writes on every existing objects?
> >
> > Ok, I learned more about MetaLinks and it's perfectly possible to intercept instance variable writes via. installing MetaLink into object instance (cool) - then I can intercept instvar write, but one problem immediately occurs:
> > when installing MetaLink into object instance, an original object class is exchanged to anonymous subclass of the original class - and because typical #= method is implemented like:
> >
> > =
> > self == anObject
> > ifTrue: [ ^ true ].
> > self class = anObject class
> > ifFalse: [ ^ false ].
> > ...
> >
> > then most of #= comparisons in application logic is broken due to "self class = anObject class" part.
> >
> > Is there any way to solve this problem?
>
> Hi,
>
> the anonymous class thing only happen when you install an object-centric
> metalink, i.e. on only one specific object. I also wanted to hide the
> anonymous class, or maybe to only make it visible for environment tools.
> I need to talk about that with Marcus.
>
> Why not installing your metalink on the classes of your objects, so that
> all instances are affected but in that case there is no anonymous class
> migration?
>
> If that is not possible, in your case maybe a (temporary) hack would be,
> in your case, to add a test in the #class method in Object (or wherever
> it is defined):
>
> class
>
>     |myClass|
>
>     myClass := self class.
>
>     ^myClass isAnonymous ifTrue:[myClass superclass] ifFalse:[myClass]
>
> But i did not test that and i have no idea of the overall impact.
>
> Steven.

Thanks for answer! I want to detect instvar writes only for objects (instances), that was previously fetched from external "datastore" (DB like), so I can persist changed objects only back to the datastore at the end of transaction. These objects can be any existing class (Pharo classes, some 3rd party libs classes, everything), so 1) injecting a class is overkill (affects all objects in the image) 2) impossible to rewrite #class method for every existing class.

Injecting object-centric metalinks via anonymous subclass breaks up many things, when I want to treat injected object like any othres (IMHO).

If you will be able to avoid this issue, that would be extra cool :-]

Note: for now, best for my "instvar writes detection" would be probably hack Object>>#attemptToAssign:withIndex: or ProtoObject>>#modificationForbiddenFor:index:value: and make my "datastore fetched objects" read only (via Object>>#setIsReadOnlyObject:).

pf

Reply | Threaded
Open this post in threaded view
|

Re: How to intercept instvar write - MetaLinks + anonymous subclass problem

Steven Costiou-2

Le 2019-02-02 16:26, Petr Fischer via Pharo-users a écrit :

Thanks for answer! I want to detect instvar writes only for objects (instances), that was previously fetched from external "datastore" (DB like), so I can persist changed objects only back to the datastore at the end of transaction. These objects can be any existing class (Pharo classes, some 3rd party libs classes, everything), so 1) injecting a class is overkill (affects all objects in the image) 2) impossible to rewrite #class method for every existing class.

Injecting object-centric metalinks via anonymous subclass breaks up many things, when I want to treat injected object like any othres (IMHO).

If you will be able to avoid this issue, that would be extra cool :-]

Note: for now, best for my "instvar writes detection" would be probably hack Object>>#attemptToAssign:withIndex: or ProtoObject>>#modificationForbiddenFor:index:value: and make my "datastore fetched objects" read only (via Object>>#setIsReadOnlyObject.

pf

I will look into that yes, however object-centric metalinks are not really meant to affect all instnces, only specific ones. That is more the purpose of "standard" metalinks.

Could standard metalinks not work? Since in any case you have to have your hand on those objects, so instead of putting an object-centric metalink you could ask for the class and install a normal metalink on write access there, which would affect all instances of that class. You would also have to keep some kind of record of which class you instrumented, so you do not install the link multiple times.

 

Steven.

Reply | Threaded
Open this post in threaded view
|

Re: How to intercept instvar write - MetaLinks + anonymous subclass problem

Marcus Denker-4
In reply to this post by Pharo Smalltalk Users mailing list

>
> Thanks for answer! I want to detect instvar writes only for objects (instances), that was previously fetched from external "datastore" (DB like), so I can persist changed objects only back to the datastore at the end of transaction. These objects can be any existing class (Pharo classes, some 3rd party libs classes, everything), so 1) injecting a class is overkill (affects all objects in the image) 2) impossible to rewrite #class method for every existing class.
>
> Injecting object-centric metalinks via anonymous subclass breaks up many things, when I want to treat injected object like any othres (IMHO).
>
> If you will be able to avoid this issue, that would be extra cool :-]
>
> Note: for now, best for my "instvar writes detection" would be probably hack Object>>#attemptToAssign:withIndex: or ProtoObject>>#modificationForbiddenFor:index:value: and make my "datastore fetched objects" read only (via Object>>#setIsReadOnlyObject:).
>

The read-only approach has the nice property that it works for collections, too. The meta-link model falls short there (you can not “put a link on offset 4 in this array”, but would need to put it on at:put:)

I started to play with it to see what is needed to create a generalised change detector with read only:
        https://github.com/MarcusDenker/ChangeDetector

        Marcus