Groovy AST转换:关闭可见性

时间:2017-07-25 14:16:41

标签: java groovy metaprogramming abstract-syntax-tree

我想进行一个AST转换,用一个闭包来包装方法体。我的问题是这个闭包必须访问那个方法参数,但似乎没有可见性。

我的代码可在Github上找到。这是我做的:

注释作为AST转换的切入点:

package wrap;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.codehaus.groovy.transform.GroovyASTTransformationClass;
import org.codehaus.groovy.transform.sc.StaticCompileTransformation;

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
@GroovyASTTransformationClass(classes = { WrapTransformation.class, StaticCompileTransformation.class })
public @interface Wrap {}

AST转换(我尝试过不同的评论代码):

package wrap;

import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.VariableScope;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.transform.AbstractASTTransformation;
import org.codehaus.groovy.transform.GroovyASTTransformation;

@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
public class WrapTransformation extends AbstractASTTransformation {

    @Override
    public void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
        if (astNodes != null) {
            callToHandler((MethodNode) astNodes[1]);
        }
    }

    protected void callToHandler(MethodNode method) {

        method.getParameters()[0].setClosureSharedVariable(true);

        ClosureExpression body = new ClosureExpression( //

                // new Parameter[] { new Parameter(ClassHelper.OBJECT_TYPE, "stringParam") }, //
                Parameter.EMPTY_ARRAY,
                // new Parameter[0],
                // method.getParameters(),
                // null, //

                method.getCode());

        VariableScope scope = new VariableScope(method.getVariableScope());
        // method.getVariableScope().getDeclaredVariables().forEach((k, v) -> {
        // scope.putDeclaredVariable(v);
        // });
        body.setVariableScope(scope);

        MethodCallExpression callExp = new MethodCallExpression( //
                VariableExpression.THIS_EXPRESSION, //
                "handleWrapped", //
                new ArgumentListExpression(body));

        BlockStatement block = new BlockStatement();
        block.addStatement(new ExpressionStatement(callExp));
        method.setCode(block);
    }

}

基类,其方法是将闭包发送到:

package wrap;

import groovy.lang.Closure;

public class BaseClass {

    public Object handleWrapped(Closure<?> closure) {
        return closure.call();
    }
}

JUnit测试用例:

package wrap;

import java.io.File;
import java.util.Arrays;

import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.tools.ast.TransformTestHelper;
import org.junit.Test;

import wrap.WrapTransformation;

public class WrapTransformationTest {

    @Test
    public void wrapped() throws Exception {
        run("src/test/resources/test/MyClass.groovy", "wrapped", "my string");
    }

    @Test
    public void unwrapped() throws Exception {
        run("src/test/resources/test/MyClass.groovy", "unwrapped", "my string");
    }

    protected Object run(String src, String methodName, Object... args) throws Exception {
        TransformTestHelper invoker = new TransformTestHelper(new WrapTransformation(), CompilePhase.SEMANTIC_ANALYSIS);
        File filePath = new File(src);
        Class<?> clazz = invoker.parse(filePath);
        Object instance = clazz.newInstance();
        return clazz.getMethod(methodName, getTypes(args)).invoke(instance, (Object[]) args);
    }

    protected Class<?>[] getTypes(Object[] objs) {
        return Arrays.stream(objs).map(Object::getClass).toArray(Class[]::new);
    }
}

Groovy测试类:

package test

class MyClass extends wrap.BaseClass {

    def unwrapped(String stringParam) {
        handleWrapped({ print stringParam })
    }

    @wrap.Wrap
    def wrapped(String stringParam) {
        print stringParam
    }
}

第二种方法应该等同于第一种方法,但事实并非如此。第一个正确打印“我的字符串”。第二个给出了这个例外:

org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack: No such property: stringParam for class: test.MyClass

使用Eclipse调试后,我发现到达handleWrapped方法的闭包略有不同。第一个包含stringParam字段,而使用AST转换创建的字段不包含。{/ p>

enter image description here

0 个答案:

没有答案