Creating ASTTs The painful truth
-
Upload
mario-garcia -
Category
Software
-
view
777 -
download
0
Transcript of Creating ASTTs The painful truth
1
2
3 . 1
3 . 1
3 . 1
… …
3 . 1
3 . 2
4 . 1
4 . 1
4 . 1
…
4 . 1
4 . 2
5
5
5
5
6 . 1
6 . 2
6 . 3
6 . 3
6 . 3
…
6 . 4
6 . 5
6 . 6
6 . 7
⇒1==1
6 . 8
⇒1==1
6 . 8
⇒1==1
6 . 8
⇒1==1
6 . 8
⇒ ⇒ref.myMethod(3)
6 . 9
⇒ ⇒ref.myMethod(3)
6 . 9
⇒ ⇒ref.myMethod(3)
6 . 9
⇒ ⇒ref.myMethod(3)
6 . 9
6 . 10
if(booleanExpression){println"hello"//statement}
6 . 11
if(booleanExpression){println"hello"//statement}
6 . 11
if(booleanExpression){println"hello"//statement}
6 . 11
publicvoidmain(String[]args){//blockstarts//thisisinsideablockstatement}//blockends
6 . 12
publicvoidmain(String[]args){//blockstarts//thisisinsideablockstatement}//blockends
6 . 12
publicvoidmain(String[]args){//blockstarts//thisisinsideablockstatement}//blockends
6 . 12
publicStringgreetings(){return"HelloGreach"}
6 . 13
6 . 14
6 . 15
6 . 15
6 . 15
6 . 15
6 . 15
…
6 . 15
…
…
classA{//ClassNodeStringgreetings//FieldNode
Stringhello(){//MethodNode
}}
6 . 16
classA{//ClassNode
Stringhello()//MethodNode{//blockStatement{
return"Hello"//returnStatement(constantExpression)
}//}}
6 . 17
7 . 1
7 . 2
7 . 3
7 . 3
7 . 3
7 . 5
…
7 . 5
…
7 . 5
7 . 6
8 . 1
8 . 2
8 . 3
8 . 4
8 . 5
8 . 6
9
10 . 1
10 . 2
10 . 3
10 . 5
10 . 5
10 . 5
10 . 5
11 . 1
packagegreach.local
classA{@WithLoggingvoiddoSomething(){//println"StartingdoSomething"
println"mystuff"
//println"EndingdoSomething"}}
11 . 2
11 . 3
…
11 . 3
…
11 . 3
11 . 4
11 . 4
11 . 4
packagegreach.local
importorg.codehaus.groovy.transform.GroovyASTTransformationClassimportjava.lang.annotation.*
(1)@Retention(RetentionPolicy.SOURCE)@Target([ElementType.METHOD])(2)@GroovyASTTransformationClass(["greach.local.WithLoggingExplainedTransformation"])@interfaceWithLoggingExplained{}
11 . 5
11 . 6
importorg.codehaus.groovy.ast.expr.*
importorg.codehaus.groovy.ast.stmt.*
importorg.codehaus.groovy.ast.*
importorg.codehaus.groovy.transform.*
11 . 7
@GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)classWithLoggingExplainedTransformationimplementsASTTransformation{
11 . 8
@GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)classWithLoggingExplainedTransformationimplementsASTTransformation{
11 . 8
@GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)classWithLoggingExplainedTransformationimplementsASTTransformation{
11 . 8
@GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)classWithLoggingExplainedTransformationimplementsASTTransformation{
11 . 8
@Overridevoidvisit(ASTNode[]nodes,SourceUnitsourceUnit){MethodNodemethod=(MethodNode)nodes[1](1)
defstartMessage=createPrintlnAst("Starting$method.name")defendMessage=createPrintlnAst("Ending$method.name")
defexistingStatements=((BlockStatement)method.code).statements(2)existingStatements.add(0,startMessage)existingStatements.add(endMessage)
}
11 . 9
11 . 10
11 . 10
11 . 10
privatestaticStatementcreatePrintlnAst(Stringmessage){newExpressionStatement(newMethodCallExpression(newVariableExpression("this"),newConstantExpression("println"),newArgumentListExpression(newConstantExpression(message))))}
11 . 11
11 . 12
11 . 12
11 . 12
12 . 1
12 . 2
12 . 3
importstaticorg.codehaus.groovy.ast.tools.GeneralUtils.*
12 . 4
12 . 5
12 . 5
12 . 5
privatestaticStatementcreatePrintlnAst(Stringmessage){newExpressionStatement(newMethodCallExpression(newVariableExpression("this"),newConstantExpression("println"),newArgumentListExpression(newConstantExpression(message))))}
12 . 6
privatestaticStatementcreatePrintlnAst(Stringmessage){returnstmt(callThisX("println",args(constX(message))))}
12 . 7
privatestaticStatementcreatePrintlnAst(Stringmessage){newExpressionStatement(newMethodCallExpression(newVariableExpression("this"),newConstantExpression("println"),newArgumentListExpression(newConstantExpression(message))))}
privatestaticStatementcreatePrintlnAst(Stringmessage){returnstmt(callThisX("println",args(constX(message))))}
12 . 8
…
12 . 9
12 . 10
BlockStatementgetBlockStmt(){ASTNode[]stmts=newAstBuilder().buildFromCode{returnnumber%2==0}
returnstmts.first()asBlockStatement}
12 . 11
BlockStatementgetMD5Code(finalStringpropertyName){defblockStatement=newAstBuilder().buildFromString"""java.security.MessageDigest.getInstance('MD5').digest(${propertyName}.getBytes('UTF-8')).encodeHex().toString()"""
returnblockStatement.first()asBlockStatement}
12 . 12
12 . 13
13 . 1
@GroovyASTTransformation(phase=CompilePhase.INSTRUCTION_SELECTION)classPlayAstextendsExceptionFriendlyAst{
staticfinalPLAY_METHOD_NAME="play"staticfinalPLAY_METHOD_PARAM_NAME="params"
/*WeneedtoinjectaDataFlowsinstanceinavariablecalled"flow"*/voidprocessNodes(ASTNode[]astNodes,SourceUnitsourceUnit){/*Checkingconstraints*/if(!astNodes)returnif(!astNodes[0]||!astNodes[1])returnif(!(astNodes[0]instanceofAnnotationNode))returnif(astNodes[0].classNode?.name!=Play.class.name)returnif(!(astNodes[1]instanceofMethodNode))return
13 . 2
……
packagegreach.builder
classOrder{@ToMD5Stringname@ToMD5Stringdescription}
Orderorder=newOrder(name:"john",description:"desc")
assertorder.nameToMD5()=="527bd5b5d689e2c32ae974c6229ff785"assertorder.descriptionToMD5()=="1dee80c7d5ab2c1c90aa8d2f7dd47256"
13 . 3
@CompileStatic@LocalTransformation(A.PHASE_LOCAL.INSTRUCTION_SELECTION)classToMD5ImplextendsLocalTransformationImpl<ToMD5,FieldNode>{
@OverridevoiddoVisit(AnnotationNodeannotation,FieldNodefieldNode,SourceUnitsourceUnit){MethodNodemd5Method=getMD5Method(fieldNode.name)
fieldNode.declaringClass.addMethod(md5Method)}
13 . 4
@GroovyASTTransformation(phase=CompilePhase.CANONICALIZATION)classMacroExpandAstextendsAbstractASTTransformation{
voidvisit(ASTNode[]nodes,SourceUnitsourceUnit){sourceUnit.AST.classes.each{ClassNodeclassNode->newMacroExpandTransformer().visitClass(classNode)}}
}
13 . 5
13 . 6
13 . 7
classMacroExpandTransformerextendsClassCodeExpressionTransformer{
privateSourceUnitsourceUnit
MacroExpandTransformer(SourceUnitsourceUnit){//PASSINGSOURCEUNITthis.sourceUnit=sourceUnit}
publicExpressiontransform(Expressionexpression){//CHECKINGAGAINif(expressioninstanceofMethodCallExpression&&expression.methodAsString=='let'){//CASTINGSMethodCallExpressionmethodCallExpression=(MethodCallExpression)expression
13 . 8
@GlobalTransformation(A.PHASE_GLOBAL.SEMANTIC)classAddTransformationextendsGlobalTransformationImpl{
List<Class<Transformer>>getTransformers(){return[AddPropertyTransformer,AddMethodTransformer]}}
13 . 9
classChangeTripleXToPlusOneextendsExpressionTransformer<MethodCallExpression>{
ChangeTripleXToPlusOne(finalSourceUnitsourceUnit){super(sourceUnit,methodCallByNameEq('xxx'))}
ExpressiontransformExpression(finalMethodCallExpressiontarget){returnA.EXPR.constX(1)}}
13 . 10
classChangeTripleXToPlusOneextendsExpressionTransformer<MethodCallExpression>{
ChangeTripleXToPlusOne(finalSourceUnitsourceUnit){super(sourceUnit,methodCallByNameEq('xxx'))}
ExpressiontransformExpression(finalMethodCallExpressiontarget){returnA.EXPR.constX(1)}}
13 . 10
classChangeTripleXToPlusOneextendsExpressionTransformer<MethodCallExpression>{
ChangeTripleXToPlusOne(finalSourceUnitsourceUnit){super(sourceUnit,methodCallByNameEq('xxx'))}
ExpressiontransformExpression(finalMethodCallExpressiontarget){returnA.EXPR.constX(1)}}
13 . 10
14 . 1
14 . 2
…
packagegreach.builder
importorg.codehaus.groovy.transform.GroovyASTTransformationClass
importjava.lang.annotation.ElementTypeimportjava.lang.annotation.Retentionimportjava.lang.annotation.RetentionPolicyimportjava.lang.annotation.Target
@Retention(RetentionPolicy.SOURCE)@Target([ElementType.TYPE])@GroovyASTTransformationClass(["greach.builder.EvenCheckerImpl"])@interfaceEvenCheckerJava{}
14 . 3
packagegreach.builder
importasteroid.local.Local
@Local(EvenCheckerImpl)@interfaceEvenChecker{}
14 . 4
packagegreach.builder
importasteroid.local.Local
@Local(EvenCheckerImpl)@interfaceEvenChecker{}
14 . 4
packagegreach.builder
importasteroid.local.Local
@Local(EvenCheckerImpl)@interfaceEvenChecker{}
14 . 4
14 . 5
…
14 . 6
packagegreach.meta
importgroovy.transform.ToStringimportgroovy.transform.AnnotationCollector
@ToJson@ToString@AnnotationCollector@interfaceToEverything{}
14 . 7
packagegreach.meta
@ToEverythingclassA{Stringname}
AaInstance=newA()
assertaInstance.toJson()assertaInstance.toString()
14 . 8
packagegreach.meta
@ToEverythingclassA{Stringname}
AaInstance=newA()
assertaInstance.toJson()assertaInstance.toString()
14 . 8
packagegreach.meta
@ToEverythingclassA{Stringname}
AaInstance=newA()
assertaInstance.toJson()assertaInstance.toString()
14 . 8
15 . 1
@CompileStatic@LocalTransformation(A.PHASE_LOCAL.INSTRUCTION_SELECTION)classSerializableImplextendsLocalTransformationImpl<Serializable,ClassNode>{@OverridevoiddoVisit(AnnotationNodeannotation,ClassNodeclassNode,SourceUnitsource){check:'packagestartswithasteroid'classNode.packageName.startsWith('asteroid')
check:'thereareatmost2methods'classNode.methods.size()<3
then:'makeitimplementsSerializableandCloneable'addInterfaces(classNode,java.io.Serializable,Cloneable)}}
15 . 2
15 . 3
15 . 4
@ASTTest({assertnode.properties.every{it.type==ClassHelper.make(Integer)}})@EvenCheckerclassA{IntegermaxIntegermin
StringtoString(){return"A"}}
15 . 5
16 . 1
16 . 2
packagegreach.builder
classToMD5TestextendsGroovyTestCase{
16 . 3
packagegreach.builder
classToMD5TestextendsGroovyTestCase{
16 . 3
packagegreach.builder
classToMD5TestextendsGroovyTestCase{
16 . 3
voidtestAddingToMD5(){assertScript'''packagegreach.builder
classOrder{@ToMD5Stringname@ToMD5Stringdescription}
Orderorder=newOrder(name:"john",description:"desc")
assertorder.nameToMD5()=="527bd5b5d689e2c32ae974c6229ff785"assertorder.descriptionToMD5()=="1dee80c7d5ab2c1c90aa8d2f7dd47256"'''}
16 . 4
voidtestFailsToUseAnInteger(){shouldFail'''packagegreach.builder
classOrder{@ToMD5Integermonth}'''}
16 . 5
16 . 6
17 . 1
17 . 2
17 . 3
17 . 4
BlockStatementresult=macro(true){println"foo"}
17 . 5
BlockStatementresult=macro(true){println"foo"}
//VS
defexpected=block(stmt(callThisX("println",args(constX("foo")))))
//CHECKEDBYAstAssert.assertSyntaxTree([expected],[result]);
17 . 6
BlockStatementresult=macro(true){println"foo"}
//VS
defexpected=block(stmt(callThisX("println",args(constX("foo")))))
//CHECKEDBYAstAssert.assertSyntaxTree([expected],[result]);
17 . 6
BlockStatementresult=macro(true){println"foo"}
//VS
defexpected=block(stmt(callThisX("println",args(constX("foo")))))
//CHECKEDBYAstAssert.assertSyntaxTree([expected],[result]);
17 . 6
BlockStatementresult=macro(true){println"foo"}
//VS
defexpected=block(stmt(callThisX("println",args(constX("foo")))))
//CHECKEDBYAstAssert.assertSyntaxTree([expected],[result]);
17 . 6
…
17 . 7
voidtestClosureExpression(){defast1=macro{{->a}}defast2=macro{{->a}}defast3=macro{{->b}}defast4=macro{{a->a}}defast5=macro{{a->a}}defast6=macro{{a,b->a}}defast7=macro{{inta->a}}assertASTMatcher.matches(ast1,ast1)assertASTMatcher.matches(ast1,ast2)assertASTMatcher.matches(ast2,ast1)assert!ASTMatcher.matches(ast1,ast3)assert!ASTMatcher.matches(ast1,ast4)assertASTMatcher.matches(ast4,ast5)assert!ASTMatcher.matches(ast5,ast6)assert!ASTMatcher.matches(ast5,ast7)}
17 . 8
17 . 9
17 . 10
17 . 11
18 . 1