Most of the tracers, profilers and samplers monitorize the execution of a system. If you want to develop a tracer which actually modifies the system you have to know very well its dynamics to avoid collateral effects.
This is the basic formula for tracers:
- You will wrap anObjectA with Tracer (anObjectB)
- Create a Tracer class with "object" instance variable (most of Tracers subclass from nil or so)
- Implement #initialize in Tracer's class side removing the superclass pointer (superclass := nil)
- Implement #on: anObjectA in Tracer's class side for storing anObjectA in the Tracer's object i.v.
Implement #doesNotUnderstand: aMessage in Tracer's instance side, like this template:
doesNotUnderstand: aMessage
"trace the selector then pass the message on"
| result |
aMessage arguments size = 0
ifTrue:
[result := object perform: aMessage selector]
ifFalse:
[result := object perform: aMessage selector withArguments: aMessage arguments].
^result
in your case before the #perform: send you might access the ObjectA method dictionary and replace the "aMessage" CompiledMethod with another one. To access the method dictionary of a class just sent #methodDictionary or #>> in some Smalltalks.
You may put a new compiled method or even replace the whole dictionary in you know how to do reflection:
| methodDictionary |
methodDictionary := MethodDictionary new.
methodDictionary at: #basicNew put: (Object compilerClass new
compile: 'basicNew ^Point basicNew'
in: Object
notifying: nil
ifFail: []) generate.
Note you don't need a method to reside in a MethodDictionary to be evaluated. See senders of #valueWithReceiver:arguments:
Finally you make sends to your new object
anObjectA := MyTracer on: ObjectA new.
anObjectA example1.
and yes, you could put that in the ObjectA's new method too. You better read some chapters on Reflection in Smalltalk, there are a lot of contents in the web as Smalltalk is the best platform for doing computational reflection.