第9章、编译期元编程和AST转换

AST- abstract syntax tree ( 抽象的语法树)

1、一个简短故事

1.1、生成字节码而不是源码

  • groovy自动生成getter,setter方法,变成字节码文件

1.2、开发者发挥生成代码的能力

2、使得Groovy更简洁

2.1、代码生成转换

  • @groovy.transform.ToString, 有点类似Lombok

    • /**
       * 使用groovy 的@ToString注解
       * @author liangchen* @date 2020/11/12
       */
      class ToString91_92 extends GroovyTestCase{
      
          void testToStringToGenerating(){
              def sherLock = new Detective(firtName: "Sherlock", lastName: "Holmes")
              assert sherLock.toString() == 'com.jack.groovy.ch9.Detective(Sherlock, Holmes)'
          }
      
          void testToStringParameters(){
              def nancy = new Sleuth(firstName: 'Nancy',lastName: 'Drew')
              assert nancy.toString() == 'com.jack.groovy.ch9.Sleuth(firstName:Nancy, lastName:Drew)'
              // 忽略null值
              nancy.lastName=null
              assert nancy.toString() == 'com.jack.groovy.ch9.Sleuth(firstName:Nancy)'
          }
      }
      @ToString
      class Detective{
          String firtName, lastName
      }
      
      //包括字段名且忽略null的字段
      @ToString(includeNames = true, ignoreNulls =true)
      class Sleuth{
          String firstName, lastName
      }
      
      
  • @EqualsAndHashCode

    • /**
       * @EqualAndHashCode
       * @author liangchen* @date 2020/11/12
       */
      class EqualsAndHashCode93 extends GroovyTestCase {
      
          void testGeneratesEqualAndHashCodeMethods(){
      
              def magneto = new Actor(firstName: "Ian", lastName: "McKellen")
              def gandalf = new Actor(firstName: "Ian", lastName: "McKellen")
              assert  magneto == gandalf
          }
      }
      
      @EqualsAndHashCode
      class Actor{
          String firstName, lastName
      }
      
      
  • @TupleConstructor 构造方法(各种参数的构造方法)

    • /**
       * @TupleConstructor 生成各种参数的构造方法
       * @author liangchen* @date 2020/11/12
       */
      class TupleConstructor94  extends GroovyTestCase{
          void testTupleConstructorToGenerateJava(){
      
              def al = new Athlete('Michael', 'Jordan')
              def a2 = new Athlete('Michael')
              assert al.firstName == a2.firstName
          }
      }
      
      @TupleConstructor
      class Athlete{
      
          String firstName, lastName
      }
      
      
  • @Lazy 延迟加载

    • /**
       * 延迟加载 @Lazy 并没有立即实例化,在使用时候才实例化
       * @author liangchen* @date 2020/11/12
       */
      class Lazy95  extends  GroovyTestCase{
      
          void testLazyDelayProperty(){
              new ResourceMain().with {
                  assert Resource.stats() == '1 alive, 0 used'
                  res2.use()
                  res3.use()
                  res4.use()
                  assert Resource.stats() == '4 alive, 3 used'
                  assert res4 instanceof Resource
                  def expected = 'res4=java.lang.ref.SoftReference'
                  assert  it.dump().contains(expected)
              }
          }
      }
      
      class  Resource{
          private  static alive = 0
          private static used =0
          Resource(){
              alive++
          }
          def use(){
              used++
          }
          static stats(){
              "$alive alive, $used used"
          }
      }
      
      
      class ResourceMain{
          def rea1 = new Resource()
          @Lazy res2 = new Resource()
          @Lazy static res3 = {new Resource()}()
          @Lazy (soft = true) volatile  Resource res4
      }
      
  • @IndexedProperty List生成数组下标

    • /**
       * 类似生成数组的下标
       * @author liangchen* @date 2020/11/12
       */
      class IndexedProperty96 extends GroovyTestCase{
      
          void testUsingIndexedProperty(){
              def books = ['The Mysterious Affair at Styles','The Murder at the Vicarage']
              new Author(name:'Agatha Christie', books:books).with {
                  books[0] = 'Murder on the Orient Express'
                  setBooks(0,'Death on the Nile')
                  assert getBooks(0) == 'Death on the Nile'
                  assert getBooks(1) =='The Murder at the Vicarage'
              }
          }
      }
      
      class Author{
          String name
          @IndexedProperty List<String> books
      }
      
      
  • @InheritConstructors 自动生成继承父类的构造方法

    • import groovy.test.GroovyTestCase
      
      /**
       *
       * @author liangchen* @date 2020/11/12
       */
      class InheritConstructors extends GroovyTestCase{
      
          void testInheritConstructors(){
              def pw1 = new MyPrintWriter(new File('out1.txt'))
              def pw2 = new MyPrintWriter('out2.txt', 'US-ASCII')
              [pw1, pw2].each {
                  //将foo 字符写到 out1.txt和out2.txt文件中
                  it << 'foo'
                  //关闭流
                  it.close()
              }
              //获取文本
              assert new File('out1.txt').text == new File('out2.txt').text
              // 删除文件
              ['out1.txt','out2.txt'].each{new File(it).delete()}
          }
      
      }
      /**
       * 继承父类构造方法
       */
      @groovy.transform.InheritConstructors
      class MyPrintWriter extends PrintWriter{}
      
  • @Sortable (排序)

    • import groovy.test.GroovyTestCase
      import groovy.transform.Sortable
      
      /**
       * @Sortable 注解,有序
       * @author liangchen* @date 2020/11/12
       */
      class Sortable98  extends GroovyTestCase{
      
          void testSortableGenerateComparableMethod() {
              def politicians = [
                      new Politician(first: 'Margaret', initial: 'H', last: 'Thatcher'),
                      new Politician(first: 'George', initial: 'W', last: 'Bush'),
                      new Politician(first: 'Jack', initial: 'O', last: 'Bush')
              ]
              // 执行排序
              def sorted = politicians.toSorted()
              //执行排序之后调用initials
              assert  sorted*.initials() == ['JOB','GWB',"MHT"]
              def byInitial = Politician.comparatorByInitial()
              sorted = politicians.toSorted(byInitial)
              assert sorted*.initials() == ['MHT','JOB', 'GWB']
          }
      }
      
      /**
       * 排序,首先根据last排序,然后按照initial 排序
       */
      @Sortable(includes = 'last,initial')
      class Politician {
          String first
          Character initial
          String last
          String initials(){
              first[0] + initial + last[0]
          }
      }
      
      
  • @Builder注解 ,建造模式创建对象

    • /**
       * 建造模式创建对象, 链式调用
       * @author liangchen* @date 2020/11/12
       */
      class Builder99  extends GroovyTestCase{
      
          void testNormalInstance(){
             def c = new Chemist(first: "jack", last: "mar", born: 1822)
          }
          void testUseBuilderInstance(){
              def builder = Chemist.builder()
              def c = builder.first('jack').last('mar').born(1867).build()
              assert c.first == 'jack'
          }
      }
      
      @Builder
      class Chemist{
          String first, last
          int born
      }
      

2.2、Class设计和设计模式注解

  • @Canonical = @ToString, @EqualsAndHashCode, @TupleConstructor

    • import groovy.test.GroovyTestCase
      import groovy.transform.Canonical
      
      /**
       * 集合注解 @Canonical = @ToString, @EqualsAndHashCode, @TupleConstructor
       * @author liangchen* @date 2020/11/12
       */
      class Canonical910 extends GroovyTestCase{
          void testCanonical(){
              def i1 = new Inventor('Thomas', 'Edison')
              def i2 = new Inventor('Thomas')
              assert i1 != i2
              assert i1.firstName == i2.firstName
              assert i1.toString()=='com.jack.groovy.ch9.Inventor(Thomas, Edison)'
          }
      }
      @Canonical
      class Inventor{
          String firstName, lastName
      }
      
      
      
  • @Immutable (实例化后属性不能变更)

    • /**
       * 一旦实例化属性就不能修改
       * @author liangchen* @date 2020/11/12
       */
      class Immutable911  extends GroovyTestCase{
      
          void testImmutable(){
              def g1 = new Genius(firstName: 'Albert',lastName: 'Einstein')
              assert g1.toString() == 'com.jack.groovy.ch9.Genius(Albert, Einstein)'
      
              def g2 = new Genius('Leonardo', 'da Vinci')
              assert g2.firstName == 'Leonardo'
              assert g1 != g2
              shouldFail (ReadOnlyPropertyException) {
                  g2.lastName = 'DiCaprio'
              }
          }
      }
      
      @Immutable
      class Genius{
         String firstName, lastName
      }
      
      
  • @Delegate 委托能力

    • import groovy.test.GroovyTestCase
      
      /**
       * @Delegate 代理、委托
       * @author liangchen* @date 2020/11/12
       */
      class NoisySet912 extends GroovyTestCase {
      
          void testDelegate(){
              Set ns = new NoisySet();
              ns.add(1)
              ns.addAll([2,3])
              assert ns.size() == 3
          }
      }
      
      /**
       * 如果某个类加了@Delegate,具有委托类能力
       */
      class NoisySet {
      
          @Delegate
          Set delegate = new HashSet()
          
          @Delegate
          Map mapDelegate = new HashMap()
      
          @Override
          boolean add(item) {
              println "adding $item"
              delegate.add(item)
          }
      
          boolean addAll(Collection items) {
              items.each {println "adding $it"}
              delegate.addAll(items)
          }
      
           put(key,value) {
             mapDelegate.put(key, value) 
          }
      
      }
      
      
  • @Singleton 单例

    • package com.jack.groovy.ch9
      
      import groovy.test.GroovyTestCase
      
      /**
       * @author liangchen* @date 2020/11/12
       */
      class Single913 extends GroovyTestCase {
      
          void testSingletonInstance(){
              assert Zeus.instance
              def ex = shouldFail (RuntimeException){
                  new Zeus()
              }
              assert ex.toString()== "Can't instantiate singleton com.jack.groovy.ch9.Zeus. Use com.jack.groovy.ch9.Zeus.instance"
          }
      }
      
      @Singleton
      class Zeus{}
      
      
  • @Memoized 缓存方法结果

    • import groovy.test.GroovyTestCase
      import groovy.transform.Memoized
      
      /**
       * 缓存数据
       * @author liangchen* @date 2020/11/12
       */
      class Memoized914 extends GroovyTestCase {
      
          void testMemoized(){
              new Calc().with {
                  assert  sum(3,4) == 7
                  assert sum(4,4) == 8
                  //查询缓存
                  assert  sum(3,4) == 7
                  assert sum(4,4) == 8
                  assert logs.join(" ") == "3+4 4+4"
              }
          }
      
      }
      
      class Calc {
          def logs = []
      
          @Memoized
          int sum(int a, int b) {
              logs << "$a+$b"
              a + b
          }
      }
      
  • @TailRecursive 尾部递归,避免stack异常

    • import groovy.test.GroovyTestCase
      import groovy.transform.TailRecursive
      
      /**
       * @author liangchen* @date 2020/11/12
       */
      class TailRecursive915 extends GroovyTestCase{
      
          void testTailRecursive(){
              assert ListUtil.reverse(['1','2','3']) == ['3','2','1']
          }
      
      }
      
      class ListUtil{
          static reverse(List list) {
              doReverse(list,[])
          }
      
          @TailRecursive
          private static doReverse(List todo, List done) {
              if(todo.isEmpty()) done
              else doReverse(todo.tail(), [todo.head()] + done)
          }
      }
      
      

2.3、日志提高

  • 日志注解

    • /**
       * @Log注解
       * @author liangchen* @date 2020/11/12
       */
      
      class Log916  extends GroovyTestCase{
      
         
          void testLog(){
              new Logs().search()
          }
      }
      @Log
      class Logs{
          def search(){
              log.fine(runLongDatabaseQuery())
          }
      
          def runLongDatabaseQuery() {
              println 'Calling database'
              return 'query result'
          }
      }
      
      
      
  • 其他注解

    • @Log @Commons @ Log4j @Log4j2 @Slf4j

2.4、声明并发

  • @Synchronized 同步 @WithReadLock 和@WithWriteLock (读锁和写锁)

    • package com.jack.groovy.ch9
      
      import com.beust.jcommander.IValueValidator
      import groovy.test.GroovyTestCase
      import groovy.transform.Synchronized
      import groovy.transform.WithReadLock
      import groovy.transform.WithWriteLock
      import groovy.util.logging.Log
      
      /**
       * @author liangchen* @date 2020/11/12
       */
      class Synchronized917  extends GroovyTestCase{
      
          void testSynchronized(){
              def p1 = new PhoneBook1()
              (0..99).collect{num ->
                  //开启一个线程
                  Thread.start {
                      p1.addNumber('Number' + num, '98765' + num)
                  }
                  //主函数等待
              }*.join()
              assert p1.getNumber('Number43') == '9876543'
          }
      
          void testCustomsSynchronized(){
              def p2 = new PhoneBook2()
              (0..99).collect{num ->
                  Thread.start {
                      p2.addNumber('Number' +num, '98765' + num)
                  }
              }*.join()
              assert p2.getNumber('Number43') == '9876543'
          }
      
          void testReadAndWriteLock(){
              def p3 = new PhoneBook3()
              (3..4).collect{num ->
                  Thread.start {
                      sleep 100*num
                      p3.addNumber('Number' +num, '98765' + num)
                  }
              }
              (2..7).collect{count ->
                  Thread.start {
                      sleep 100 * count
                      p3.getNumber('Number'+ count)
                  }
              }*.join()
          }
      }
      
      class PhoneBook1{
          private final phoneNumbers = [:]
      
          @Synchronized
          def getNumber(key) {
              phoneNumbers[key]
          }
      
          @Synchronized
          void addNumber(key, value) {
              phoneNumbers[key] = value
          }
      }
      
      /**
       * 自定义锁块
       */
      @Log
      class PhoneBook2 {
          private final phoneNumbers = [:]
          private final lock = new Object[0]
      
          @Synchronized('lock')
          def getNumber(key) {
              phoneNumbers[key]
          }
      
          void addNumber(key, value) {
              log.info("Adding phone number $value")
              synchronized (lock) {
                  phoneNumbers[key] = value
              }
          }
      }
      
      
      /**
       * 读写锁
       */
      class PhoneBook3 {
          private final phoneNumbers = dummyNums()
      
          private dummyNums() {
              (1..8).collectEntries{
                  ['Number'+it, '765432' + it]
              }
          }
      
          @WithReadLock
          def getNumber(key) {
              println "Reading started for $key"
              phoneNumbers[key]
              sleep 80
              println "Reading done for $key"
          }
      
          @WithWriteLock
          def addNumber(key,value){
              println "Writing started for $key"
              phoneNumbers[key] = value
              sleep 100
              println "Writing done for $key"
          }
      }
      
      

2.5、容易克隆和扩展

  • @AutoClone

    • import groovy.test.GroovyTestCase
      import groovy.transform.AutoClone
      import groovy.transform.AutoCloneStyle
      import groovy.transform.TupleConstructor
      
      /**
       * @AutoClone 克隆注解
       *
       * @author liangchen* @date 2020/11/13
       */
      class AutoClone720  extends GroovyTestCase{
      
          void testAutoClone(){
              def name = "Heston Blumenthal"
      
              def recipes =["Snail porridge","Bacon & egg ice cream"]
              def born = Date.parse("yyyy-MM-dd", '1999-01-01')
      
              def c1 = new Chef1( name,  recipes,  born)
              def c2 = c1.clone()
              assert  c2.recipes == recipes
              assert c1.name == c2.name
          }
      
          void testAutoCopyConstructor() {
              def name = 'Jamie Oliver'
      
              def recipes = ['Lentil Soup', 'Crispy Duck']
      
              def born = Date.parse('yyyy-MM-dd', '1975-05-27')
      
              def c1 = new Chef2(name, born, recipes)
      
              def c2 = c1.clone()
      
              assert c2.name == name
      
              assert c2.born == born
      
              assert c2.recipes == recipes
          }
      }
      
      /**
       * 提供四种类型, CLONE:不提供深度拷贝,不适合final修饰属性
       * SIMPLE : 只会调用无参构造方法,不支持深度拷贝
       * COPY_CONSTRUCTOR 不支持深度拷贝,支持final修饰属性
       * SERIALIZATION : 支持深度拷贝,不适合final修饰属性拷贝
       */
      @AutoClone
      class Chef1{
      
          String  name
      
          List<String> recipes
      
          Date born
      }
      
      @TupleConstructor
      @AutoClone(style=AutoCloneStyle.COPY_CONSTRUCTOR)
      
      class Person {
      
          final String name
      
          final Date born
      
      }
      
      
      
      @TupleConstructor(includeSuperProperties=true, callSuper=true)
      @AutoClone(style=AutoCloneStyle.COPY_CONSTRUCTOR)
      
      class Chef2 extends Person {
      
          final List<String> recipes
      
      }
      
  • @AutoExternalize

    • import groovy.test.GroovyTestCase
      import groovy.transform.ToString
      
      /**
       * @AutoExternal 序列化
       * @author liangchen* @date 2020/11/13
       */
      class AutoExternalize922 extends GroovyTestCase{
      
          void testAutoExternalize() {
              def c = new Composer(name: 'Wolfgang Amadeus Mozart', born: 1756, married: true)
              //创建输出数组流
              def baos = new ByteArrayOutputStream()
              //写出流到baos中
              baos.withObjectOutputStream {
                  os -> os.writeObject(c)
              }
              // 创建一个输入流
              def bais = new ByteArrayInputStream(baos.toByteArray())
              def loader = getClass().classLoader
              def result
              bais.withObjectInputStream(loader) {
                  result = it.readObject().toString()
              }
              assert result == 'com.jack.groovy.ch9.Composer(Wolfgang Amadeus Mozart, 1756, true)'
      
          }
      
      }
      
      @groovy.transform.AutoExternalize
      @ToString
      class Composer{
          String name
      
          int born
      
          boolean married
      }
      
      

2.6、脚本支持

  • @TimedInterrupt ( 超过时间打断)

    • /**
       * @author liangchen* @date 2020/11/13
       */
      class TimedInterrupt923 extends GroovyTestCase{
      
      
          void testBlastOff1(){
              def b = new BlastOff1()
              Thread.start {
                  try {
                      b.countdown(10)
                  } catch (TimeoutException ignore) {
                      b.logs << 'aborted'
                  }
              }.join()
              assert b.logs.join(' ') == '10 9 8 7 6 aborted'
          }
      }
      
      /**
       * 超过480毫米自动打断方法,触发timeInterrupt
       */
      @TimedInterrupt(value = 480L,unit = TimeUnit.MILLISECONDS)
      class BlastOff1 {
          def logs = []
      
          def countdown(n) {
              sleep 100
              logs << n
              if (n == 0) {
                  logs << 'ignition'
              }else countdown(n -1)
          }
      }
      
      
  • @ThreadInterrupt 线程打断

    • import groovy.test.GroovyTestCase
      import groovy.transform.ThreadInterrupt
      
      /**
       * 线程打断, 这里例子有问题
       * @author liangchen* @date 2020/11/13
       */
      class ThreadInterrupt924 extends GroovyTestCase{
      
          void testThreadInterrupt(){
              def b = new BlastOff2()
              def t1 = Thread.start {
                  try {
                      println 1
                      b.countdown(10)
                      println 2
                  } catch (InterruptedException ignore) {
                      b.logs << 'aborted'
                  }
              }
              sleep 300
              println 3
              t1.interrupt()
              t1.join()
              assert  b.logs.join(' ')=='10 9 8 7 6 5 4 3 2 1 0 ignition'
          }
      }
      
      @ThreadInterrupt()
      class BlastOff2 {
          def logs = []
      
          def countdown(n) {
              sleep 100
              logs << n
              if (n == 0) {
                  logs << 'ignition'
              }else countdown(n -1)
          }
      }
      
      
  • @Field注解 脚本使用

    • import groovy.transform.Field
      
      /**
       * 不需要创建类似class文件结构只要是groovy就行
       */
      @Field
      List awe = [1,2,3]
      def awesum(){
          awe.sum()
      }
      assert awesum() == 6
      
  • @BaseScript

2.7、更多转换

3、探索AST

  • 一般groovy编译和运行过程
    • image-20201113111330225

4、创建ASTs

4.1、亲手创建Ast

  • 利用groovy已有AST的node创建新节点

    • import groovy.test.GroovyTestCase
      import org.codehaus.groovy.ast.ClassHelper
      import org.codehaus.groovy.ast.expr.ArgumentListExpression
      import org.codehaus.groovy.ast.expr.ConstructorCallExpression
      
      import org.codehaus.groovy.ast.stmt.ReturnStatement
      
      import static org.codehaus.groovy.ast.ClassHelper.make
      
      import static org.codehaus.groovy.ast.tools.GeneralUtils.*
      /**
       * @author liangchen* @date 2020/11/13
       */
      class Ast928 extends GroovyTestCase{
      
          void testCreateAst(){
      
             def ast = new ReturnStatement(
                      new ConstructorCallExpression(
                              ClassHelper.make(Date), ArgumentListExpression.EMPTY_ARGUMENTS
                      )
              )
              assert ast instanceof ReturnStatement
      
              //创建语法树返回再抽象
              def asts = returnS(ctorX(make(Date)))
      
              assert asts instanceof ReturnStatement
          }
      
      }
      
      

4.2、AstBuilder.buildFromSpec

  • 利用规范快速构架AST结构

    • /**
       * 利用AstBuilder 快速构建
       * @author liangchen* @date 2020/11/13
       */
      class AstBuilder930  extends GroovyTestCase{
      
          void testAstBuilder(){
              def ast = new AstBuilder().buildFromSpec {
                  returnStatement {
                      constructorCall(Date){
                          argumentList {}
                      }
                  }
              }
              assert ast[0] instanceof  ReturnStatement
          }
      }
      
      

4.3、AstBuilder.buildFromString

  • 利用字符串构建ast node

    • /**
       * 利用字符串直接创建Ast node
       * @author liangchen* @date 2020/11/13
       */
      class AstBuildFromString931 extends GroovyTestCase{
      
          void testAstBuilderFromString(){
              def ast = new AstBuilder().buildFromString('new Date()')
              assert  ast[0] instanceof  BlockStatement
              assert ast[0].statements[0] instanceof  ReturnStatement
          }
      
          void testAstDynamicCode() {
              def approxPI = 3.14G
             // 直接添加方法,采用字符串
              def ast = new AstBuilder().buildFromString(
                      CompilePhase.CLASS_GENERATION, false,
                      'static double getTwoPI() { def pi = ' + approxPI + '; pi * 2 }'
      
              )
      
              assert ast[1] instanceof  ClassNode
              def method =ast[1].methods.find{
                  it.name == 'getTwoPI'
              }
              assert method instanceof MethodNode
              
          }
      }
      
      

4.4、AstBuilder.buildFromCode

  • 利用code构建

    • /**
       * 利用code构建ast node
       * @author liangchen* @date 2020/11/13
       */
      class AstBuildFromCode944  extends GroovyTestCase{
      
          void testAstBuildFromCode(){
              def ast = new AstBuilder().buildFromCode {
                  //直接使用代码
                  new Date()
              }
              assert  ast[0].statements[0]  instanceof  ReturnStatement
          }
      }
      
      

5、本地转换

  • 自定义main注解,自动生成main方法

    • package com.jack.groovy.ch9
      
      
      import org.codehaus.groovy.ast.*
      import org.codehaus.groovy.control.CompilePhase
      import org.codehaus.groovy.control.SourceUnit
      import org.codehaus.groovy.transform.ASTTransformation
      import org.codehaus.groovy.transform.GroovyASTTransformation
      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
      
      /**
       * 定义一下注解Main注解, 增加@GroovyASTransformationClass 表示这个注解处理的类
       * 是MainTransformation
       * RetentionPolicy 注解保持到源码阶段
       */
      @Retention(RetentionPolicy.SOURCE)
      @Target([ElementType.METHOD])
      @GroovyASTTransformationClass(classes = [MainTransformation])
      @interface Main{}
      
      
      import static groovyjarjarasm.asm.Opcodes.ACC_PUBLIC
      import static groovyjarjarasm.asm.Opcodes.ACC_STATIC
      import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE
      import static org.codehaus.groovy.ast.tools.GeneralUtils.*
      
      // 确定那个时期处理,在指令选择处理
      @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)
      class MainTransformation implements ASTTransformation{
          // 定义默认节点
          private NO_EXCEPTIONS = ClassNode.EMPTY_ARRAY
          private STRING_ARRAY = ClassHelper.STRING_TYPE.makeArray()
          @Override
          void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
              // 判断注解是否正确
              if(astNodes?.size() != 2) return
              if(!(astNodes[0] instanceof  AnnotationNode)) return
              if(astNodes[0].classNode.name != Main.name) return
              if(!(astNodes[1] instanceof MethodNode)) return
      
              // 获取目标方法、目标类,目标实例
              def targetMethod = astNodes[1]
              def targetClass = targetMethod.declaringClass
              def targetInstance = ctorX(targetClass)
              def callTarget = callX(targetInstance, targetMethod.name)
              def mainBody = block(stmt(callTarget))
              def visibility = ACC_STATIC | ACC_PUBLIC
              def parameters = params(param(STRING_ARRAY,'args'))
              //目标类增加main方法
              targetClass.addMethod('main', visibility, VOID_TYPE, parameters,NO_EXCEPTIONS, mainBody)
          }
      
      }
      new GroovyShell(getClass().classLoader).evaluate  """
      class Greeter{
              @com.jack.groovy.ch9.Main
              def greet(){
              println "Hello from the greet() method!"
              }
      } 
      """
      

6、全局转换

  • 这个暂时不懂, 有点类似dubbo services, spi

7、测试AST转换

  • GroovyClassLoader 和 GroovyShell

    • package com.jack.groovy.ch9
      
      import groovy.test.GroovyTestCase
      import groovy.transform.WithReadLock
      
      import java.lang.reflect.Modifier
      import java.util.concurrent.locks.ReentrantReadWriteLock
      
      /**
       * ast 测试
       * @author liangchen* @date 2020/11/13
       */
      class WithReadLockTestAst938 extends  GroovyTestCase{
      
          static class MyClass{
              @WithReadLock
              void readerMethod1(){}
          }
          void testLockFieldDefaultsForReadLock(){
              def dee =  MyClass.getDeclaredFields();
              def field = MyClass.getDeclaredField('$reentrantlock')
              assert Modifier.isPrivate(field.modifiers)
      
              assert !Modifier.isTransient(field.modifiers)
      
              assert Modifier.isFinal(field.modifiers)
              assert !Modifier.isStatic(field.modifiers)
              assert field.type == ReentrantReadWriteLock
          }
      
          /**
           * 这是类,不是实例
           */
          void testLockFieldDefaultsForReadLocks(){
              //字符串 直接解析类
              def tester = new GroovyClassLoader().parseClass("""
          class MyClass{
          @groovy.transform.WithReadLock
          public void readerMethod1(){}
          } 
      """)
              def field = tester.getDeclaredField('$reentrantlock')
              assert Modifier.isPrivate(field.modifiers)
      
              assert !Modifier.isTransient(field.modifiers)
      
              assert Modifier.isFinal(field.modifiers)
              assert !Modifier.isStatic(field.modifiers)
              assert field.type == ReentrantReadWriteLock
          }
      
          /**
           * groovyShell evaluate 是一个实例,而不是类
           */
          void testLockFieldDefaultsForReadLockGroovyShell() {
              def tester = new GroovyShell().evaluate("""
              import groovy.transform.WithReadLock
              class MyClass{
                  @WithReadLock
                  public void readerMethod1(){}
              }
              new MyClass()
      """)
              // 这里是getClass()
              def field = tester.getClass().getDeclaredField('$reentrantlock')
              assert Modifier.isPrivate(field.modifiers)
      
              assert !Modifier.isTransient(field.modifiers)
      
              assert Modifier.isFinal(field.modifiers)
              assert !Modifier.isStatic(field.modifiers)
              assert field.type == ReentrantReadWriteLock
          }
      
      }
      
      
  • @ASTTest

    • /**
       * @author liangchen* @date 2020/11/13
       */
      
      @ASTTest(phase = CompilePhase.SEMANTIC_ANALYSIS, value={
          lookup('anchor').each{
              n-> assert n instanceof ForStatement
          }
      })
      void doSomenthing(){
          println "Hello, Groovy"
          anchor:for(int i=0; i<10; i++){println "Iteration $i"}
      }
      

8、限制

10、总结

  • 这一章有蒙圈,后面有需要再更新
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值