我想进行一个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>