Search code examples
reflectionmetaprogrammingsmalltalkpharo

Refactoring methods and creating copies with a different name in Pharo Smalltalk?


I am experimenting with refactoring and some custom functionality. Is there a way to copy a method to the same class as the original but under a new name? (essentially creating a really shallow copy) You can do this by hand by editing the method source, but can it be done programatically?

For example:

doMethodName: anArgument
^ anObject doThat with: anArgument

becomes:

doNewMethodName: anArgument
^ anObject doThat with: anArgument

Solution

  • You can compile methods by sending a compile: message to the target class.

    1. retrieve the method
      • e.g. method := Float>>#cos or method := Float methodNamed: #cos
    2. retrieve the source code

      • method sourceCode will return the method code as a string
      • method ast (or method parseTree) will return the code as parsed tree representation
    3. Compile code into a class (optionally with protocol)

      • TargetClass compile: sourceCode, or
      • TargetClass compile: sourceCode classified: protocol

    So if you have

    Something>>doMethodName: anArgument
        ^ anObject doThat with: anArgument
    

    You can do

    code := (Something>>#doMethodName:) sourceCode.
    "replace all matches"
    newCode := code copyReplaceAll: 'doMethodName:' with: 'doNewMethodName:'.
    "or just the first"
    newCode := code copyWithRegex: '^doMethodName\:' matchesReplacedWith: 'doNewMethodName:'.
    Something compile: newCode.
    

    Using AST

    sourceCode returns the code as string, which isn't the nicest to manipulate. If you just want to change the method name, you can rename it in the AST, e.g.

    tree := (Something>>#doMethodName:) parseTree.
    tree selector: 'doNewerMethodName:'.
    Something compile: tree newSource.