If you can't read please download the document
Upload
hendersk
View
2.760
Download
0
Embed Size (px)
Citation preview
Groovy AST Transformations
What is Groovy?
A dynamic programming language that runs on the JVM
Language is essentially a superset of Java, in fact grammar to parse Groovy is constructed from Java grammar
Groovy source code is translated into Java bytecode by the Groovy compiler for execution on the JVM
Where is Groovy?
Groovy as a scripting language
Frameworks for application developmentGrails Web framework
Griffon Swing applications
Gaelyk Google App Engine
TestingEasyb Behavior Driven Development
Spock BDD and mocking
Gmock - Mocking
Grails MVC framework with controllers and service classes in GroovyGriffon Grails-like application framework for developing rich desktop applicationsGaelyk lightweight Groovy toolkit for building and deploying applications on Google App EngineEasyb test specifications written in Groovy
Where is Groovy? (cont...)
Building projectsGradle
Gant
Gant tool for scripting Ant tasks using Groovy instead of XML to specify logicGradle enterprise-grade build system- Groovy build scripts- Dependency management- Used by hibernate, Grails, Groovy
How does Groovy code become bytcode?
What is an Abstract Syntax Tree?
Rooted tree of nodes
Composed of nodes that correspond to Groovy language constructs
We are interested in Groovy's AST syntax tree
Composed of ASTNodes from the org.codehaus.groovy.ast package and subpackages
Tree structure lends itself to processing using Visitor design pattern
This AST is a rooted tree made up of nodes that describes the various constructs within source code in a form that can be easily processed using the Visitor design pattern (http://en.wikipedia.org/wiki/Visitor_pattern). The Visitor design pattern essentially constructs a visitor object that traverses the tree and performs some action on each node in the tree.
What is an AST Transformation?
Compiler hook Groovy provides into compilation process
Means of extending language without grammar changes
Allows manipulation of AST during compilation prior to bytecode generation
Two typesLocal
Global
Local AST Transformations
More common
Applied to specific declarations whose AST is to be modified by the transformation
Annotation indicates AST transformation should be applied to declaration
AST is walked and AST transformation applied to nodes that are annotated with transformation annotation (Visitor design pattern)
Many supplied with Groovy distribution
Global AST Transformations
Less common
Applied to every source unit in compilation
Uses jar file service provider mechanism to identify global AST transformations
Jar file added to classpath of compiler that contains service locator file identifying name of class that implements AST transformation
Groovy's Built-in AST Transformations
Code generation
Design pattern implementation
Simplified logging
Concurrency support
Cloning and externalization
JavaBeans support
Script safety
Static typing
Miscellaneous
Code Generation
@ToString
@EqualsAndHashCode
@TupleConstructor
@Canonical
@Lazy
@InheritConstructors
Focus on automating repetative task of writing common methods likequals, hashCode and constructors
Example - @ToString
@groovy.transform.ToStringclass Person { String first, last}def person = new Person(first:"Hamlet", last:"D'Arcy")println "${person.toString()}"
Result with @ToString transformation: Person(Hamlet, D'Arcy)
Result without @ToString transformation:Person@175078b
Design Pattern Implementation
@Delgate
@Singleton
@Immutable
@Mixin
@Category
Example - @Delegate
class Delegate1Class { public void method1() {} public void method2(String p) {}}
public class OwnerClass { @Delegate Delegate1Class delegate1 = new Delegate1Class()}
The @Delegate AST transformation implements delegation by adding all of the public methods from the delegate class to the owner class.
Verify using javap on OwnerClass
Simplified Logging
@Log
@Log4j
@Slf4j
@Commons
Concurrency Support
@Synchronized
@WithReadLock
@WithWriteLock
Cloning and Externalization
@AutoClone
@AutoExternalize
JavaBeans Support
@Bindable
@Vetoable
@ListenerList
Scripting Safety
@TimedInterrupt
@ThreadInterrupt
@ConditionalInterrupt
Static Typing
@TypeChecked
@CompileStatic
Example - @TypeChecked
@groovy.transform.TypeCheckedNumber test() { // Cannot find matching method MyMethod() // Variable is undelcared println myField // Cannot assign String to int int object = "myString" // Cannot return value of type String on method returning type Number return "myString"}
Miscellaneous
@Field
@PackageScope
@Newify
Location of Built-in AST Transformations
Annotation definition usually found in groovy.transform or groovy.lang
Implementation class usually found in org.codehaus.groovy.transform
Custom AST Transformations
Defined in exactly same manner as built-in AST transformations
Steps
Create AST transformation implementation class that implements the ASTTransformation interface
Create AST transformation annotation declaration and link it to the implementation class with the @GroovyASTTransformationClass annotation
The Implementation Class
Implements the ASTTransformation interfaceSingle methodvoid visit(ASTNode nodes[], SourceUnit source)
Compiler invokes this method on AST of annotated element
nodes array contains AnnotationNode for AST transformation annotation and AnnotatedNode corresponding to annotated declaration
HelloWorldASTTransformation
@GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)public class HelloWorldASTTransformation implements ASTTransformation {
public void visit(ASTNode[] nodes, SourceUnit source) { MethodNode methodNode = (MethodNode)nodes[1] Statement methodCode = methodNode.getCode()
// // Add greeting to beginning of code block. // methodCode.getStatements().add(0, createPrintlnStatement()) }
The Annotation Type Declaration
Indicate declaration types to which AST transformation is applicable with @Target annotation
Indicate implementation class with @GroovyASTTransformationClass annotation
HelloWorld
@Target([ElementType.METHOD])@GroovyASTTransformationClass("HelloWorldASTTransformation")public @interface HelloWorld {}
HelloWorldExample
@HelloWorldvoid myMethod() {}myMethod()
The Hard Part Creating AST objects
Tools to helpAST Browser
ASTBuilder
Ways to create AST objectsManually using ASTNode subclass constructors (leveraging AST Browser)
Using ASTBuilder.buildFromSpec
Using ASTBuilder.buildFromString
Using ASTBuilder.buildFromCode
Implementing createPrintlnStatement Manually
private Statement createPrintlnStatement() { Statement printlnStatement = new ExpressionStatement( new MethodCallExpression( new VariableExpression("this"), new ConstantExpression("println"), new ArgumentListExpression( new ConstantExpression("Hello World!!!!")) )) return printlnStatement }
Implementing createPrintlnStatement using buildFromSpec
private Statement createPrintlnStatement() { List results = new AstBuilder().buildFromSpec { expression { methodCall { variable "this" constant "println" argumentList { constant "Hello World!!!!" } } } } return results[0] }
- Shorthand notation for every ASTNode type- API simplified- Helps eliminates some verbosity and complexity- Returns script class node as well- desired AST in first entry
Implementing createPrintlnStatement using buildFromString
private Statement createPrintlnStatement() { List result = new AstBuilder().buildFromString("println 'Hello World!!!!'; return") return result[0] }
Implementing createPrintlnStatement using buildFromCode
private Statement createPrintlnStatement() {
List result = new AstBuilder().buildFromCode { println "Hello World!!!!" return } return result[0] }
Resources
Groovy code itself provides excellent examples
AST Browser is invaluable for seeing what code is generated by a transformation
Groovy in Action (2nd edition) in MEAP Chapter 9 written by Hamlet D'Arcy
Unit tests for ASTBuilder
Shameless plug: Groovy Under the Hood in GroovyMag