当前位置: 首页 > 知识库问答 >
问题:

在Groovy中使用AST转换修改方法

闾丘才哲
2023-03-14

在我可以进一步回答之前,原始问题已被删除,因此我将问题与答案重新发布如下:

我无法使用AST转换修改我的方法,因为我无法在修改后执行前面的方法语句。我从方法中提取语句,将其保存在某个临时变量中,但在修改之后,当我尝试执行它时,得到MissingPropertyException:No such property:code for class:Calculator,就像我试图使用类中的属性,而不是方法中的前一个代码块一样。你知道我做错了什么吗?

//annotation
@Retention(RetentionPolicy.SOURCE)
@Target([ElementType.TYPE])
@GroovyASTTransformationClass("CreatedAtTransformation")
public @interface CreatedAt {
    String name() default "";
}

//AST Transformation
@GroovyASTTransformation(phase = SEMANTIC_ANALYSIS)
public class CreatedAtTransformation implements ASTTransformation {

    public void visit(ASTNode[] astNodes, SourceUnit source) {

        //private final long field creation
        ClassNode myClass = (ClassNode) astNodes[1]
        ClassNode longClass = new ClassNode(Long.class)
        FieldNode field = new FieldNode("timeOfInstantiation", FieldNode.ACC_PRIVATE | FieldNode.ACC_FINAL, longClass, myClass, new ConstantExpression(System.currentTimeMillis()))
        myClass.addField(field)

        //statement
        AstBuilder ab = new AstBuilder()
        List<ASTNode> statement = ab.buildFromCode {
            timeOfInstantiation
        }

        //value of the annotation expression(name of the method)
        def annotationExpression = astNodes[0].members.name
        String annotationValueString = annotationExpression.value

        //public final method creation
        myClass.addMethod(annotationValueString, Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, ClassHelper.Long_TYPE,[] as Parameter[], [] as ClassNode[], statement[0])

        //modification of method "add"
        def addMethods = myClass.getMethods("add")
        for(m in addMethods){
            def code = m.getCode().statements

            //statement
            AstBuilder abc = new AstBuilder()
            List<ASTNode> statement1 = abc.buildFromCode {
                timeOfInstantiation = System.currentTimeMillis()
                for(c in code){
                    c.expression
                }
            }
            m.setCode(statement1[0])
        }

        //modification of method "subtract"
        def subtractMethods = myClass.getMethods("subtract")
        for(m in subtractMethods){
            def code = m.getCode().statements

            //statement
            AstBuilder abc = new AstBuilder()
            List<ASTNode> statement1 = abc.buildFromCode {
                timeOfInstantiation = System.currentTimeMillis()
                for(c in code){
                    c.expression
                }
            }
            m.setCode(statement1[0])
        }
    }
}

//class
final calculator = new GroovyShell(this.class.getClassLoader()).evaluate('''
@CreatedAt(name = "timestamp")
class Calculator {
    int sum = 0

    def add(int value) {
        int v = sum + value
        sum = v
    }

    def subtract(int value) {
        sum -= value
    }
}

new Calculator()
''')

//test
assert System.currentTimeMillis() >= calculator.timestamp()
assert calculator.timestamp() == calculator.timestamp()
def oldTimeStamp = calculator.timestamp()

sleep(1000)
calculator.add(10)
assert oldTimeStamp < calculator.timestamp()
assert calculator.timestamp() == calculator.timestamp()
oldTimeStamp = calculator.timestamp()

sleep(1000)
calculator.subtract(1)
assert oldTimeStamp < calculator.timestamp()
assert calculator.timestamp() == calculator.timestamp()

println 'well done'

这个问题实际需要的代码比实际需要的要多。重要的部分是方法的修改。提前感谢。

从我的角度来看,我不知道AST的代码是否真的有用,或者只是为了获取和示例工作,学习AST转换...

共有1个答案

邹杰
2023-03-14

所以我最初的答案是:

当涉及到AST转换时,很难获得帮助。虽然只是猜测,但我认为这可能与变量范围有关。有一个VariableScopeVisitor在AST进程的早期运行,它设置变量的范围,但是从AST的描述来看,您添加的是稍后要访问的代码。因此,您可能需要再次运行VariableScopeVisitor来修复它,以便现有代码可以访问您的注入代码。

今年我在GR8Conf.US做了一个介绍AST的演讲,里面有很多资源:

https://docs.google.com/presentation/d/1D4B0YQd0_0HYxK2FOt3xILM9XIymh-G-jh1TbQldbVA/edit?usp=sharing

我将看一看这篇讨论变量范围的文章:

然而真正的答案是

AST 转换可能很困难,并且使用 AstBuilder 虽然可以方便,但可能会带来问题,因此我经常直接使用 API。一旦我学习了宏和宏方法,Groovy 2.5中的新功能,我可能不必使用API,但在此之前,我使用API重写了部分代码,如下所示:

        //modification of method "add"
        def addMethods = myClass.getMethods("add")
        for(m in addMethods){
            def code = m.getCode().statements

            //statement
            //AstBuilder abc = new AstBuilder()
            Statement s1 = new ExpressionStatement(
                    new BinaryExpression(
                            new VariableExpression('timeOfInstantiation'),
                            Token.newSymbol(org.codehaus.groovy.syntax.Types.EQUAL,0,0),
                            new MethodCallExpression(
                                    new ClassExpression(new ClassNode(java.lang.System)),
                                    'currentTimeMillis',
                                    ArgumentListExpression.EMPTY_ARGUMENTS
                            )
                    )

            )
//            List<ASTNode> statement1 = abc.buildFromString('timeOfInstantiation = System.currentTimeMillis()')
//            List<ASTNode> statement1 = abc.buildFromCode {
//                timeOfInstantiation = System.currentTimeMillis()
//                for(c in code){
//                    c.expression
//                }
//            }

            code.add(0,s1)
            //m.setCode(statement1[0])
        }

这段代码可以清理一点,但它应该可以工作。我还必须将timeOfInstantiation更改为私有,而不是最终,这样分配代码就可以这样工作:

FieldNode field = new FieldNode("timeOfInstantiation", FieldNode.ACC_PRIVATE, longClass, myClass, new ConstantExpression(System.currentTimeMillis()))

我还会在我的演示文稿中查看测试应用程序参考,因为它允许调试AST转换并使用Groovy控制台查看转换在做什么。

 类似资料:
  • 我正在寻找Groovy AST转换,它可以在我的类中生成构建器模式代码。 我知道有一些类似于< code>@Canonnical或< code>@ToString或< code>@EqualsAndHashCode的增强器可以自动生成有用的方法,希望有@GenerateBuilder。我想这样使用它:

  • 我正在尝试使用AST转换替换类的方法。 我首先检查该方法是否存在,然后将其删除(基于此)。 我看到集合上的大小发生了变化,但该方法仍然可以在类节点上访问。 删除该方法后,我想添加一个同名的新方法: 但是,这会导致以下错误: 正确的方法是什么? 更新:由于我打算做的就是替换该方法的现有功能,因此我创建了一个新的块语句,并使用 在现有方法上设置了它。

  • 我写了我自己的AST转换,它应该生成getter和setter方法(在这里创建getter方法)。但是他们不工作,不能理解原因。 使用属性创建注释 我的AST转换代码应该为注释字段创建getter方法 注释检查 在哪个地方我犯了错误? 运行结果: 线程“main”groovy中出现异常。lang.MissingMethodException:没有方法ua的签名。家插科打诨。用法实例getMyCou

  • ASTParser.setSource有不同输入类型的多态方法。 但是,当我使用作为setSource方法的输入时, 我得到一个错误,说我没有使用char[]作为输入参数。 为什么会出现这个错误? 我使用日食 jdt/ast 作为独立 Java 程序的一部分。在此过程中,我不会使用日食项目/资源,而是将 java 源代码作为 setSource() 的参数加载到 char[] 中。我不确定,但ec

  • 这与我的问题有关,即拦截给定类中对字段的所有访问,而不仅仅是以与Groovy“属性”样式访问一致的方式进行的访问。您可以在这里查看:在groovy中拦截LOCAL属性访问。 我发现的一种肯定会解决我的问题的方法是在编译时使用AST重写任何具有属性访问的非属性访问。例如,如果一个类如下所示: 这些断言语句将会工作,因为getter方法直接访问x,而foo.x通过getProperty("x ")在返

  • 我写了一个Groovy AST变换,它只在Grails自动重新加载要应用的类时为我运行。如果我清理项目并使用运行应用程序启动应用程序,则 AST 转换不会运行。触摸类以便圣杯自动重新加载会导致转换运行。 注释和ASTTransformation实现是groovy类,位于Grails应用程序的src/groovy目录中。注释用于域类,用groovy编写在域目录中。 这是否可能是由groovy文件编译