Re: [Pharo-users] Modifying the AST of a method

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

Re: [Pharo-users] Modifying the AST of a method

Marcus Denker-4



> On 08 Sep 2015, at 03:02, Sean P. DeNigris <[hidden email]> wrote:
>
> From Günter Khyo:
>
> Günter Khyo wrote
>> Hello!
>>
>> I want to replace particular nodes of an AST (such as Assignments or Self
>> sends) with my own nodes.
>> Is there an easy (high-level) way to do this without being familiar with
>> the entire compiler infrastructure?
>>
>> The problem I am having is that AST nodes maintain internal state such as
>> parent links and, in the case of RBVariables, links to scopes and
>> bindings. There might be many other dependencies I am missing and missing
>> one of them can trigger exceptions with stack traces buried deep in the IR
>> Code-Generator and visitor calls.
>>
>> For example, if I want to replace an assignment such as
>>
>> Morph>>position: aPoint
>>
>> "box := self fullBounds."
>>
>> with something like
>>
>> "self assignAndNotifiyDependents: self fullBounds to: 'box' "
>>
>> Btw: I know there is a rewrite tool, but I want to do it programmatically.

Hello,

With Pharo5 there are two mechanism that can help you.

1) MetaLinks (aka reflectivity).
=======================

They allow you to reflectively define that a meta object to be called before/after or instead of an AST node.

as you want to do something in addition here, you can add the notification as a #before link:

link := MetaLink new
        metaObject: #object;
        selector: #assignAndNotifiyDependents:;
        arguments: #(newValue).
       
this is a link that when installed on an AssignmentNode, will call #assignAndNotifiyDependents on the object with
the new value (the to be assigned value) as the argument.

To install this on all ivar assignments, do:

MyClass assignmentNodes do: [ :each | each variable isInstance ifTrue: [each link: link ]].

to get rid of the link:

link uninstall

But MetaLinks are very hidden: you do not see them in your design. So they should be used with care.

2) First class Instance variable (aka Slots).
=================================

In your example, what you really want is a “special” kind of variable that always notifies depends. Wouldn’t it be nice
if you could define such a special kind of Variable and, in the class definition of you class, specify that the ivar ‘box” is that
special kind?

In Pharo5, you can do just that.

1) define your special variable.

IndexedSlot subclass: #NotifyDependsSlot
        slots: { }
        classVariables: {  }
        category: 'Demo’

change what it means to write these kind of variables by implementing:

write: aValue to: anObject
        super write: aValue to: anObject.
        anObject assignAndNotifiyDependents: aValue.


Now change the class definition of your class to make ‘box’ into this special kind of variable:

Object subclass: #MyClass
        slots: { #box => NotifyDependsSlot }
        classVariables: {  }
        category: ‘Demo'


DONE.

Ah, but reflective slot write is slow, so if you use this a lot, add these two method:

emitValue: methodBuilder

        methodBuilder pushInstVar: index.

emitStore: methodBuilder
        | tempName |
        tempName := Object new.
        methodBuilder
                addTemp: tempName;
                storeTemp: tempName;
                pushReceiver;
                pushTemp: tempName;
                send: #assignAndNotifiyDependents:;
                popTop;
                storeInstVar: index.


These generate byte code inline for the read and write instead of calling the #write:to: method.

(I attach a fillout of MyClass that i used to test this)






MyClass.st (778 bytes) Download Attachment