introduction to groovy runtime metaprogramming and ast transforms
DESCRIPTION
Introduction to Groovy runtime metaprogramming and AST transforms - by Marcin Grzejszczak author of the http://toomuchcoding.blogspot.com blog. The sources can be found here Mercurial Bitbucket - https://bitbucket.org/gregorin1987/too-much-coding/src/e5ab7c69ab7b2796075fd6f087fbf31346aa2d2b/Groovy/ast/?at=default Git Github - https://github.com/marcingrzejszczak/too-much-coding/tree/master/Groovy/astTRANSCRIPT
Introduction to Groovy runtime metaprogramming and AST
transformsby Marcin GrzejszczakTooMuchCoding blog
Question
Integer getTimesTenOf(Integer number) {
return 10 * number;
}
assert 40 == getTimesTenOf(4)
How to calculate ten times a number?
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 Groovypublic class Foo implements groovy.lang.GroovyObject
extends java.lang.Object {
}
GroovyObjectpublic 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)
Expandodef 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
ExpandoMetaClassA 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! - reminderInteger.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