在我可以进一步回答之前,原始问题已被删除,因此我将问题与答案重新发布如下:
我无法使用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转换...
所以我最初的答案是:
当涉及到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文件编译