Publicidad

Creating ASTTs The painful truth

Software Developer en Kaleidos Open Source
10 de Apr de 2016
Publicidad

Más contenido relacionado

Publicidad
Publicidad

Creating ASTTs The painful truth

  1. 1
  2. 2
  3. 3 . 1
  4. 3 . 1
  5. 3 . 1
  6. …​ …​ 3 . 1
  7. 3 . 2
  8. 4 . 1
  9. 4 . 1
  10. 4 . 1
  11. …​ 4 . 1
  12. 4 . 2
  13. 5
  14. 5
  15. 5
  16. 5
  17. 6 . 1
  18. 6 . 2
  19. 6 . 3
  20. 6 . 3
  21. 6 . 3
  22. …​ 6 . 4
  23. 6 . 5
  24. 6 . 6
  25. 6 . 7
  26. ⇒ 1 == 1 6 . 8
  27. ⇒ 1 == 1 6 . 8
  28. ⇒ 1 == 1 6 . 8
  29. ⇒ 1 == 1 6 . 8
  30. ⇒ ⇒ ref.myMethod(3) 6 . 9
  31. ⇒ ⇒ ref.myMethod(3) 6 . 9
  32. ⇒ ⇒ ref.myMethod(3) 6 . 9
  33. ⇒ ⇒ ref.myMethod(3) 6 . 9
  34. 6 . 10
  35. if(booleanExpression) { println "hello" // statement } 6 . 11
  36. if(booleanExpression) { println "hello" // statement } 6 . 11
  37. if(booleanExpression) { println "hello" // statement } 6 . 11
  38. public void main(String[] args) { // block starts // this is inside a block statement } // block ends 6 . 12
  39. public void main(String[] args) { // block starts // this is inside a block statement } // block ends 6 . 12
  40. public void main(String[] args) { // block starts // this is inside a block statement } // block ends 6 . 12
  41. public String greetings() { return "Hello Greach" } 6 . 13
  42. 6 . 14
  43. 6 . 15
  44. 6 . 15
  45. 6 . 15
  46. 6 . 15
  47. 6 . 15
  48. …​ 6 . 15
  49. …​ …​ class A { // ClassNode String greetings // FieldNode String hello() { // MethodNode } } 6 . 16
  50. class A { // ClassNode String hello() // MethodNode { // blockStatement { return "Hello" // returnStatement(constantExpression) } // } } 6 . 17
  51. 7 . 1
  52. 7 . 2
  53. 7 . 3
  54. 7 . 3
  55. 7 . 3
  56. 7 . 4
  57. 7 . 5
  58. …​ 7 . 5
  59. …​ 7 . 5
  60. 7 . 6
  61. 8 . 1
  62. 8 . 2
  63. 8 . 3
  64. 8 . 4
  65. 8 . 5
  66. 8 . 6
  67. 9
  68. 10 . 1
  69. 10 . 2
  70. 10 . 3
  71. 10 . 4
  72. 10 . 5
  73. 10 . 5
  74. 10 . 5
  75. 10 . 5
  76. 11 . 1
  77. package greach.local class A { @WithLogging void doSomething() { // println "Starting doSomething" println "mystuff" // println "Ending doSomething" } } 11 . 2
  78. 11 . 3
  79. …​ 11 . 3
  80. …​ 11 . 3
  81. 11 . 4
  82. 11 . 4
  83. 11 . 4
  84. package greach.local import org.codehaus.groovy.transform.GroovyASTTransformationClass import java.lang.annotation.* (1) @Retention(RetentionPolicy.SOURCE) @Target([ElementType.METHOD]) (2) @GroovyASTTransformationClass( ["greach.local.WithLoggingExplainedTransformation"]) @interface WithLoggingExplained { } 11 . 5
  85. 11 . 6
  86. import org.codehaus.groovy.ast.expr.* import org.codehaus.groovy.ast.stmt.* import org.codehaus.groovy.ast.* import org.codehaus.groovy.transform.* 11 . 7
  87. @GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS) class WithLoggingExplainedTransformation implements ASTTransformation { 11 . 8
  88. @GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS) class WithLoggingExplainedTransformation implements ASTTransformation { 11 . 8
  89. @GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS) class WithLoggingExplainedTransformation implements ASTTransformation { 11 . 8
  90. @GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS) class WithLoggingExplainedTransformation implements ASTTransformation { 11 . 8
  91. @Override void visit(ASTNode[] nodes, SourceUnit sourceUnit) { MethodNode method = (MethodNode) nodes[1] (1) def startMessage = createPrintlnAst("Starting $method.name") def endMessage = createPrintlnAst("Ending $method.name") def existingStatements = ((BlockStatement)method.code).statements (2) existingStatements.add(0, startMessage) existingStatements.add(endMessage) } 11 . 9
  92. 11 . 10
  93. 11 . 10
  94. 11 . 10
  95. private static Statement createPrintlnAst(String message) { new ExpressionStatement( new MethodCallExpression( new VariableExpression("this"), new ConstantExpression("println"), new ArgumentListExpression( new ConstantExpression(message) ) ) ) } 11 . 11
  96. 11 . 12
  97. 11 . 12
  98. 11 . 12
  99. 12 . 1
  100. 12 . 2
  101. 12 . 3
  102. import static org.codehaus.groovy.ast.tools.GeneralUtils.* 12 . 4
  103. 12 . 5
  104. 12 . 5
  105. 12 . 5
  106. private static Statement createPrintlnAst(String message) { new ExpressionStatement( new MethodCallExpression( new VariableExpression("this"), new ConstantExpression("println"), new ArgumentListExpression( new ConstantExpression(message) ) ) ) } 12 . 6
  107. private static Statement createPrintlnAst(String message) { return stmt(callThisX("println", args(constX(message)))) } 12 . 7
  108. private static Statement createPrintlnAst(String message) { new ExpressionStatement( new MethodCallExpression( new VariableExpression("this"), new ConstantExpression("println"), new ArgumentListExpression( new ConstantExpression(message) ) ) ) } private static Statement createPrintlnAst(String message) { return stmt(callThisX("println", args(constX(message)))) } 12 . 8
  109. …​ 12 . 9
  110. 12 . 10
  111. BlockStatement getBlockStmt() { ASTNode[] stmts = new AstBuilder().buildFromCode { return number % 2 == 0 } return stmts.first() as BlockStatement } 12 . 11
  112. BlockStatement getMD5Code(final String propertyName) { def blockStatement = new AstBuilder().buildFromString """ java.security.MessageDigest.getInstance('MD5') .digest(${propertyName}.getBytes('UTF-8')) .encodeHex() .toString() """ return blockStatement.first() as BlockStatement } 12 . 12
  113. 12 . 13
  114. 13 . 1
  115. @GroovyASTTransformation(phase=CompilePhase.INSTRUCTION_SELECTION) class PlayAst extends ExceptionFriendlyAst{ static final PLAY_METHOD_NAME = "play" static final PLAY_METHOD_PARAM_NAME = "params" /* We need to inject a DataFlows instance in a variable called "flow" */ void processNodes(ASTNode[] astNodes,SourceUnit sourceUnit){ /* Checking constraints */ if (!astNodes) return if (!astNodes[0] || !astNodes[1]) return if (!(astNodes[0] instanceof AnnotationNode)) return if (astNodes[0].classNode?.name != Play.class.name) return if (!(astNodes[1] instanceof MethodNode)) return 13 . 2
  116. …​ …​ package greach.builder class Order { @ToMD5 String name @ToMD5 String description } Order order = new Order(name: "john", description: "desc") assert order.nameToMD5() == "527bd5b5d689e2c32ae974c6229ff785" assert order.descriptionToMD5() == "1dee80c7d5ab2c1c90aa8d2f7dd47256" 13 . 3
  117. @CompileStatic @LocalTransformation(A.PHASE_LOCAL.INSTRUCTION_SELECTION) class ToMD5Impl extends LocalTransformationImpl<ToMD5,FieldNode> { @Override void doVisit(AnnotationNode annotation, FieldNode fieldNode, SourceUnit sourceUnit) { MethodNode md5Method = getMD5Method(fieldNode.name) fieldNode.declaringClass.addMethod(md5Method) } 13 . 4
  118. @GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION) class MacroExpandAst extends AbstractASTTransformation { void visit(ASTNode[] nodes, SourceUnit sourceUnit) { sourceUnit.AST.classes.each { ClassNode classNode -> new MacroExpandTransformer().visitClass(classNode) } } } 13 . 5
  119. 13 . 6
  120. 13 . 7
  121. class MacroExpandTransformer extends ClassCodeExpressionTransformer { private SourceUnit sourceUnit MacroExpandTransformer(SourceUnit sourceUnit) { // PASSING SOURCE UNIT this.sourceUnit = sourceUnit } public Expression transform(Expression expression) { // CHECKING AGAIN if (expression instanceof MethodCallExpression && expression.methodAsString == 'let') // CASTINGS MethodCallExpression methodCallExpression = (MethodCallExpression) expression 13 . 8
  122. @GlobalTransformation(A.PHASE_GLOBAL.SEMANTIC) class AddTransformation extends GlobalTransformationImpl { List<Class<Transformer>> getTransformers() { return [AddPropertyTransformer, AddMethodTransformer] } } 13 . 9
  123. class ChangeTripleXToPlusOne extends ExpressionTransformer<MethodCallExpression> { ChangeTripleXToPlusOne(final SourceUnit sourceUnit) { super(sourceUnit, methodCallByNameEq('xxx')) } Expression transformExpression(final MethodCallExpression target) { return A.EXPR.constX(1) } } 13 . 10
  124. class ChangeTripleXToPlusOne extends ExpressionTransformer<MethodCallExpression> { ChangeTripleXToPlusOne(final SourceUnit sourceUnit) { super(sourceUnit, methodCallByNameEq('xxx')) } Expression transformExpression(final MethodCallExpression target) { return A.EXPR.constX(1) } } 13 . 10
  125. class ChangeTripleXToPlusOne extends ExpressionTransformer<MethodCallExpression> { ChangeTripleXToPlusOne(final SourceUnit sourceUnit) { super(sourceUnit, methodCallByNameEq('xxx')) } Expression transformExpression(final MethodCallExpression target) { return A.EXPR.constX(1) } } 13 . 10
  126. 14 . 1
  127. 14 . 2
  128. …​ package greach.builder import org.codehaus.groovy.transform.GroovyASTTransformationClass import java.lang.annotation.ElementType import java.lang.annotation.Retention import java.lang.annotation.RetentionPolicy import java.lang.annotation.Target @Retention(RetentionPolicy.SOURCE) @Target([ElementType.TYPE]) @GroovyASTTransformationClass(["greach.builder.EvenCheckerImpl"]) @interface EvenCheckerJava { } 14 . 3
  129. package greach.builder import asteroid.local.Local @Local(EvenCheckerImpl) @interface EvenChecker { } 14 . 4
  130. package greach.builder import asteroid.local.Local @Local(EvenCheckerImpl) @interface EvenChecker { } 14 . 4
  131. package greach.builder import asteroid.local.Local @Local(EvenCheckerImpl) @interface EvenChecker { } 14 . 4
  132. 14 . 5
  133. …​ 14 . 6
  134. package greach.meta import groovy.transform.ToString import groovy.transform.AnnotationCollector @ToJson @ToString @AnnotationCollector @interface ToEverything { } 14 . 7
  135. package greach.meta @ToEverything class A { String name } A aInstance = new A() assert aInstance.toJson() assert aInstance.toString() 14 . 8
  136. package greach.meta @ToEverything class A { String name } A aInstance = new A() assert aInstance.toJson() assert aInstance.toString() 14 . 8
  137. package greach.meta @ToEverything class A { String name } A aInstance = new A() assert aInstance.toJson() assert aInstance.toString() 14 . 8
  138. 15 . 1
  139. ​ @CompileStatic @LocalTransformation(A.PHASE_LOCAL.INSTRUCTION_SELECTION) class SerializableImpl extends LocalTransformationImpl<Serializable, ClassNode> { @Override void doVisit(AnnotationNode annotation, ClassNode classNode, SourceUnit source) { check: 'package starts with asteroid' classNode.packageName.startsWith('asteroid') check: 'there are at most 2 methods' classNode.methods.size() < 3 then: 'make it implements Serializable and Cloneable' addInterfaces(classNode, java.io.Serializable, Cloneable) } } 15 . 2
  140. ​ ​ 15 . 3
  141. 15 . 4
  142. @ASTTest({ assert node .properties .every { it.type == ClassHelper.make(Integer) } }) @EvenChecker class A { Integer max Integer min String toString() { return "A" } } 15 . 5
  143. 16 . 1
  144. 16 . 2
  145. package greach.builder class ToMD5Test extends GroovyTestCase { 16 . 3
  146. package greach.builder class ToMD5Test extends GroovyTestCase { 16 . 3
  147. package greach.builder class ToMD5Test extends GroovyTestCase { 16 . 3
  148. void testAddingToMD5() { assertScript ''' package greach.builder class Order { @ToMD5 String name @ToMD5 String description } Order order = new Order(name: "john", description: "desc") assert order.nameToMD5() == "527bd5b5d689e2c32ae974c6229ff785" assert order.descriptionToMD5() == "1dee80c7d5ab2c1c90aa8d2f7dd47256" ''' } 16 . 4
  149. void testFailsToUseAnInteger() { shouldFail ''' package greach.builder class Order { @ToMD5 Integer month } ''' } 16 . 5
  150. 16 . 6
  151. 17 . 1
  152. 17 . 2
  153. ​ 17 . 3
  154. 17 . 4
  155. BlockStatement result = macro(true) { println "foo" } 17 . 5
  156. BlockStatement result = macro(true) { println "foo" } // VS def expected = block(stmt(callThisX("println", args(constX("foo"))))) // CHECKED BY AstAssert.assertSyntaxTree([expected], [result]); 17 . 6
  157. BlockStatement result = macro(true) { println "foo" } // VS def expected = block(stmt(callThisX("println", args(constX("foo"))))) // CHECKED BY AstAssert.assertSyntaxTree([expected], [result]); 17 . 6
  158. BlockStatement result = macro(true) { println "foo" } // VS def expected = block(stmt(callThisX("println", args(constX("foo"))))) // CHECKED BY AstAssert.assertSyntaxTree([expected], [result]); 17 . 6
  159. BlockStatement result = macro(true) { println "foo" } // VS def expected = block(stmt(callThisX("println", args(constX("foo"))))) // CHECKED BY AstAssert.assertSyntaxTree([expected], [result]); 17 . 6
  160. …​ 17 . 7
  161. void testClosureExpression() { def ast1 = macro { {-> a } } def ast2 = macro { {-> a } } def ast3 = macro { {-> b } } def ast4 = macro { { a -> a } } def ast5 = macro { { a -> a } } def ast6 = macro { { a,b -> a } } def ast7 = macro { { int a -> a } } assert ASTMatcher.matches(ast1, ast1) assert ASTMatcher.matches(ast1, ast2) assert ASTMatcher.matches(ast2, ast1) assert !ASTMatcher.matches(ast1, ast3) assert !ASTMatcher.matches(ast1, ast4) assert ASTMatcher.matches(ast4, ast5) assert !ASTMatcher.matches(ast5, ast6) assert !ASTMatcher.matches(ast5, ast7) } 17 . 8
  162. 17 . 9
  163. 17 . 10
  164. 17 . 11
  165. 18 . 1
  166. 18 . 2
  167. 18 . 3
  168. 18 . 4
  169. 18 . 5
  170. 18 . 6
Publicidad