Модифицировать метод с использованием AST Transformation в Groovy - PullRequest
0 голосов
/ 28 октября 2018

Исходный вопрос был удален, прежде чем я смог ответить на него, поэтому я отправляю вопрос с ответом:

Я не могу изменить свой метод, используя Преобразование AST, поскольку яне могу понять, как выполнить предыдущие инструкции метода после моей модификации.Я извлекаю операторы из метода, сохраняю их в какой-то временной переменной, но позже, после моей модификации, когда я пытаюсь их выполнить, я получаю MissingPropertyException: Нет такого свойства: код для класса: Калькулятор, как будто я пытаюсь использовать свойствоиз моего класса, а не предыдущий блок кода из моего метода.Есть идеи, что я делаю не так?

//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 Transforms ...

1 Ответ

0 голосов
/ 28 октября 2018

Итак, мой первоначальный ответ был:

Может быть трудно получить помощь, когда дело доходит до преобразований AST.Хотя это предположение, я думаю, что это может иметь отношение к переменной области.Существует VariableScopeVisitor, который запускается на ранних этапах процесса AST и задает область видимости переменных, однако из описания вашего AST вы добавляете код, к которому вы хотите обращаться позже.Поэтому вам, возможно, придется снова запустить VariableScopeVisitor, чтобы исправить его так, чтобы существующий код имел доступ к введенному вами коду.

В этом году я провел вступительный доклад AST на GR8Conf.US, в котором много ресурсов.:

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 на private, а не final, чтобы код назначения работал следующим образом:

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

Я бы также посмотрел ссылку на тестовое приложение в своей презентации, так как это будетразрешить отладку преобразований AST и использование консоли Groovy, чтобы увидеть, что делают преобразования.

...