Introduction to Groovy runtime
metaprogramming and AST
transforms
by Marcin Grzejszczak
TooMuchCoding blog
Question
How to calculate ten times a number?

Integer getTimesTenOf(Integer number) {
return 10 * number;
}
assert 40 == getTimesTenOf(4)
Let’s invert the concept!
assert 40 == 4.tens
It can’t be done… Can it?
public final class Integer ... {
// ...
}
Add that method!
Integer.metaClass.getTimesTens = {
return 10 * delegate
}
assert 40 == 4.timesTen
Monkey patch
● Extend or modify the run-time code
● Do not alter the original source code
● Also known as duck punching and shaking
the bag
Classes with Groovy
public class Foo implements groovy.lang.GroovyObject
extends java.lang.Object {

}
GroovyObject
public interface GroovyObject {

Object invokeMethod(String name, Object args);

Object getProperty(String propertyName);

void setProperty(String propertyName, Object newValue);

MetaClass getMetaClass();

void setMetaClass(MetaClass metaClass);
}
Meta Object Protocol

●
●
●
●

MetaObject is an object that manipulates, creates, describes, or
implements other objects (including itself).
Stored info includes base object's type, interface, class, methods etc.
MetaObjects are examples of reflection concept
The Meta-Object-Protocol (MOP) is the collection of rules of how a request
for a method call is handled by the Groovy runtime system
MetaClass

MetaClass defines the behaviour of any given Groovy or Java class ●

●
●

The MetaClass interface has two parts:
○ Client API - via the extend MetaObjectProtocol interface
○ Contract with the Groovy runtime system.
In general the compiler and Groovy runtime engine interact with methods
on MetaClass class
In general the MetaClass clients interact with the method defined by the
MetaObjectProtocol interface
Expando
●
●

●

Kind of a dynamic bean.
It will memorize
○ added properties
○ added methods (from closures)
Useful for
○ mocking
○ when you don’t want to create a new class (you just want to record
behaviour)
Expando
def player = new Expando(name: 'Robert')
assert 'Robert' == player.name

player.surname = 'Lewandowski'
assert 'Lewandowski' == player.surname

player.presentYourself = {
return "Name: $name, Surname: $surname"
}

String result = player
.presentYourself()
assert 'Name: Robert, Surname: Lewandowski' == result
ExpandoMetaClass
A special implementation of a MetaClass that allows you to
(using closures):
● dynamically add methods,
● constructors,
● properties and static methods
ExpandoMetaClass
●

ExpandoMetaClass - Borrowing Methods — Borrowing methods from other classes

●

ExpandoMetaClass - Constructors — Adding or overriding constructors

●

ExpandoMetaClass Domain-Specific Language

●

ExpandoMetaClass - Dynamic Method Names — Dynamically creating method names

●

ExpandoMetaClass - GroovyObject Methods — Overriding invokeMethod, getProperty and setProperty

●

ExpandoMetaClass - Interfaces — Adding methods on interfaces

●

ExpandoMetaClass - Methods — Adding or overriding instance methods

●

ExpandoMetaClass - Overriding static invokeMethod — Overriding invokeMethod for static methods

●

ExpandoMetaClass - Properties — Adding or overriding properties

●

ExpandoMetaClass - Runtime Discovery — Overriding invokeMethod for static methods

●

ExpandoMetaClass - Static Methods — Adding or overriding static methods
Add that method! - reminder
Integer.metaClass.getTimesTen = {
return 10 * delegate
}
assert 40 == 4.timesTen
Add that method! - watch out!
Integer.metaClass.getTimesTens = {
return 10 * delegate
}
Remember that :
● You are mixing here a method to a class not to an object
● In the same JVM all objects of that class will have that method added
to MetaClass! (You can image the downsides of this)
● It’s safer to use categories - only a block of code will have those
methods mixed in
MOP - find that missing method!

Taken from Venkat's
Programming Groovy 2
Metaprogramming in Groovy
● Runtime
○ Categories
○ Expando / MetaClass / ExpandoMetaClass
● Compile Time
○ AST Transformations
○ Extension Module
Runtime - drawbacks
● efficiency - finding if method exists
● method / property missing - people start
asking questions how can that even compile
● no IDE support
Abstract Syntax Tree
● Representation of the abstract syntactic structure of
source code
● Each node of the tree denotes a construct occurring in
the source code.
● The syntax is "abstract" in not representing every detail
appearing in the real syntax.
An abstract syntax tree for the following
code for the Euclidean algorithm:

while b ≠ 0
if a > b
a=a−b
else
b=b−a
return a
AST Transformation
● Compile-time metaprogramming
● Bytecode manipulation
● Either global or local
○ Global in any compiler phase
○ Local in a semantic analysis phase
or later
Did you know that…?
Groovy compiler has 9 phases?
● Initialization
●

Parsing

●

Conversion

●

Semantic Analysis (important for Local AST transforms)

●

Canonicalization

●

Instruction Selection

●

Class Generation

●

Output

●

Finalization
AST Transformation annotations
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●

@AnnotationCollector
@Externalize
@BaseScript
@Canonical
@CompileStatic
@TypeChecked
@ConditionalInterrupt
@EqualsAndHashcode
@Mixin
@Category
@Immutable
@Field
@IndexedProperty
@InheritConstructors
@Memoized
@PackageScope
@Synchronized

●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●

@ThreadInterrupt
@TimedInterrupt
@ToString
@TupleConstructor
@WithReadLock
@WithWriteLock
@Delegate
@DelegatesTo
@Lazy
@Newify
@Singleton
@ASTTest
@AutoClone
@Commons
@Log
@Slf4j
@NotYetImplemented
@Grab
@GrabResolver
TooMuchCoding Git Github repository
TooMuchCoding Mercurial Bitbucket repository
Local AST transforms for dummies
●
●
●
●

Check the Guidebook
Define an annotation
Create an AST transform for the annotation
Build the transform
AST transforms - not that easy :(
● Creating implementation is not trivial
○ Verify node
○ Verify preconditions
○ Make the implementation

● Use tools
○ AstBuilder from string
○ AstBuilder from code
○ AstBuilder from spec

● The code might get really messy
Sources and recommended readings
●
●
●
●
●

Wikipedia (MonkeyPatch, MOP)
http://groovy.codehaus.org/
Groovy AST Demisitified
Groovy AST tutorials
Project Lombok for Java

Introduction to Groovy runtime metaprogramming and AST transforms