Compiling and performing code

We show several ways to programmatically create classes or methods, or evaluate code.

You can compile and evaluate a valid Smalltalk string like this:

Smalltalk compiler evaluate: '3 + 4'.
  

This can be useful if you need to construct and evaluate Smalltalk expressions from data (that you trust).

In Smalltalk, you can classically create a new class programmatically by sending a suitable message to the superclass of the class to be created. Here, for example, we create HellowWorld as a subclass of Object ProtoObject << #Object slots: {}; tag: 'Objects'; package: 'Kernel' .

Object subclass: #HelloWorld
  instanceVariableNames: ''
  classVariableNames: ''
  category: 'HelloWorld'.
  

As you might imagine, the method Class>>#subclass:instanceVariableNames:classVariableNames:category: subclass: aSubclassSymbol instanceVariableNames: instVarNameList classVariableNames: classVarNames category: aCategorySymbol self deprecated: 'This method uses the old way of creating classes. The new way is to use the ShiftClassInstaller.' transformWith: '`@rcv subclass: `@name instanceVariableNames: `@instVar classVariableNames: `@classVar category: `@package' -> '`@rcv classInstaller make: [ :builder | builder superclass: `@rcv; name: `@name; slotsFromString: `@instVar; sharedVariablesFromString: `@classVar; category: `@package; environment: `@rcv environment ]'. ^ self classInstaller make: [ :builder | builder superclass: self; name: aSubclassSymbol; slotsFromString: instVarNameList; sharedVariablesFromString: classVarNames; category: aCategorySymbol; environment: self environment ] is defined in Class ClassDescription << #Class slots: { #subclasses . #name . #classPool . #sharedPools . #environment . #commentSourcePointer . #packageTag }; sharedVariables: { #ObsoleteClasses }; tag: 'Classes'; package: 'Kernel-CodeModel' . (See Understanding Smalltalk classes and metaclasses, which explains that all metaclasses, such as Object ProtoObject << #Object slots: {}; tag: 'Objects'; package: 'Kernel' , inherit from Class ClassDescription << #Class slots: { #subclasses . #name . #classPool . #sharedPools . #environment . #commentSourcePointer . #packageTag }; sharedVariables: { #ObsoleteClasses }; tag: 'Classes'; package: 'Kernel-CodeModel' ).

If you browse the method above, you will see that it has a large number of subclass:* siblings, with various paramters for creating subclasses.

Class methods select: [:m | m selector beginsWith: 'subclass:'].
  

Caveat: Actually, in Pharo the heavy lifting of creating classes is now done by a dedicated builder class, ShiftClassBuilder Object << #ShiftClassBuilder slots: { #buildEnvironment . #installingEnvironment . #superclassName . #name . #layoutDefinition . #comment . #commentStamp . #superclass . #newMetaclass . #newClass . #oldClass . #oldMetaclass . #builderEnhancer . #extensibleProperties . #changeComparers . #changes . #metaSuperclass . #superclassResolver . #inRemake . #package . #tag }; sharedVariables: { #BuilderEnhancer }; tag: 'Builder'; package: 'Shift-ClassBuilder' . Just browse any of the subclass:* methods and follow the delegation chain till you find where the work is actually done.

Once you have a class, you can add methods by asking the class to compile the source code of the new method:

(Smalltalk at: #HelloWorld) compile: 'hello ^ ''hello'''.
  

This is the same as:

#HelloWorld asClass compile: 'hello ^ ''hello'''.
  

We can then inspect the newly compiled method:

#HelloWorld asClass >> #hello.
  

Which is the same as:

#HelloWorld asClass methodDict at:  #hello.
  

If the classor method to perform are only known at run time, there are two ways to evaluate a method programmatically. You can either send #perform: to the object, or you can send #valueWithReceiver:arguments: to the method.

Here we send perform: twice, first to create a new instance of Hello World and then to send then instance #hello.

((Smalltalk at: #HelloWorld) perform: #new) perform: #hello.
  

Without reflection this is the same as: HelloWorld new hello.

As you might guess, there are multiple variants of perform:* that take different numbers of arguments.

3 perform: #+ with: 4.
  
3 perform: #+ withArguments: {4}.
  

Note that with perform:, the method to be evaluated will still be dynamically looked up in the superclass chain.

You can also directly execute a method, explicitly passing in the receiver and any arguments. Here we look up the hello method we compiled earlier in the HelloWorld class. Then we directly execute the method ( i.e., without any further lookup) with a Hello World instance as the receover and an empty argument array:

method := #HelloWorld asClass>>#hello.
method valueWithReceiver: #HelloWorld asClass new arguments: #().
  

And here is 3+4 evaluated explicitly:

SmallInteger >> #+ valueWithReceiver: 3 arguments: #(4).
  

Here we query an instance of the GtTour Object << #GtTour traits: {TGtSlideShow}; slots: {}; tag: 'Slideshows'; package: 'GToolkit-Demo-MoldableDevelopment' slideshow for its <gtSlide> methods, select one at random, and evaluate it.

aSlideshow := GtTour new.
(aSlideshow class methods 
	select: [ :m | m hasPragmaNamed: 'gtSlide' ]) atRandom
		valueWithReceiver: aSlideshow
		arguments: {GtProtoLiveSlide new}.
  

Of course we have to know that a slide can be instantiated by passing it an instance of GtProtoLiveSlide BrStencil << #GtProtoLiveSlide slots: { #priority . #definingMethod . #notes }; package: 'GToolkit-Presenter' .