use of groovyjarjarasm.asm.MethodVisitor in project groovity by disney.
the class GroovityASTTransformation method visit.
public void visit(ASTNode[] nodes, final SourceUnit sourceUnit) {
ModuleNode mn = sourceUnit.getAST();
try {
if (mn != null) {
ClassNode scriptClassNode = mn.getScriptClassDummy();
LoadFieldVisitor loadFieldVisitor = new LoadFieldVisitor(sourceUnit);
loadFieldVisitor.visitClass(scriptClassNode);
if (mn.getStatementBlock().isEmpty()) {
// System.out.println("Adding dummy statement to force script");
mn.getStatementBlock().addStatement(new ExpressionStatement(new DeclarationExpression(new VariableExpression("___groovy__run__stub___"), Token.newSymbol(Types.EQUAL, 1, 1), new ConstantExpression(null))));
} else {
// check whether script body really does anything, if so add ScriptBody marker API
final AtomicBoolean runnable = new AtomicBoolean(false);
mn.getStatementBlock().visit(new CodeVisitorSupport() {
public void visitExpressionStatement(ExpressionStatement statement) {
Expression expression = statement.getExpression();
if (expression instanceof DeclarationExpression) {
List<AnnotationNode> fa = expression.getAnnotations();
for (AnnotationNode n : fa) {
if ("Field".equals(n.getClassNode().getName()) || Field.class.equals(n.getClassNode().getTypeClass())) {
// short-circuit and ignore the field declaration
return;
}
}
DeclarationExpression de = (DeclarationExpression) expression;
if ((de.getVariableExpression().getModifiers() & ACC_STATIC) != 0) {
// static declarations are implied fields
return;
}
}
runnable.set(true);
}
public void visitReturnStatement(ReturnStatement statement) {
runnable.set(true);
}
public void visitAssertStatement(AssertStatement statement) {
runnable.set(true);
}
});
if (runnable.get()) {
scriptClassNode.addInterface(new ClassNode(ScriptBody.class));
}
}
LoadFinder loadFinder = new LoadFinder(sourceUnit);
loadFinder.visitClass(scriptClassNode);
if (loadFinder.loadMethod != null) {
scriptClassNode.addInterface(new ClassNode(Loadable.class));
}
LineNumberVisitor lineNumberVisitor = null;
if (sourceLineNumbers != null) {
lineNumberVisitor = new LineNumberVisitor(sourceUnit);
}
boolean isLibrary = false;
ArgVisitor argVisitor = new ArgVisitor(sourceUnit);
InitDependencyVisitor initVisitor = new InitDependencyVisitor(sourceUnit);
TagFinder tagFinder = new TagFinder(sourceUnit);
TagCallFinder tagCallFinder = new TagCallFinder(sourceUnit, scriptClassNode, factory.getTaggables());
StaticBindingTransformer staticBindingTransformer = new StaticBindingTransformer(sourceUnit);
StaticFieldVisitor staticFieldVisitor = new StaticFieldVisitor(sourceUnit);
staticFieldVisitor.visitClass(scriptClassNode);
List<MethodNode> methods = mn.getMethods();
if (methods != null) {
for (MethodNode method : methods) {
boolean isFunction = false;
for (AnnotationNode annotation : method.getAnnotations()) {
if ("Function".equals(annotation.getClassNode().getName())) {
// If this script contains at least one Function declaration, mark it as a library
isFunction = true;
break;
}
}
if (isFunction) {
isLibrary = true;
break;
}
}
}
List<ClassNode> cnodes = mn.getClasses();
iterateClassNodes: for (final ClassNode cn : cnodes) {
// remap GSP line numbers so they match up with original source
if (lineNumberVisitor != null) {
lineNumberVisitor.visitClass(cn);
}
tagCallFinder.visitClass(cn);
// add arg annotations to methods to preserve parameter names
argVisitor.visitClass(cn);
if (cn.isInterface()) {
continue;
}
// Skip further processing for Traits as they don't handle static things well yet ...
if (cn.getAnnotations() != null) {
for (AnnotationNode anno : cn.getAnnotations()) {
if (anno.getClassNode().getName().equals("groovy.transform.Trait")) {
// System.out.println("SKIPPING TRAIT "+cn.getName());
continue iterateClassNodes;
}
}
}
staticBindingTransformer.visitClass(cn);
// add statistics gathering to methods and closures
initVisitor.visitClass(cn);
tagFinder.visitClass(cn);
final String internalClassName = BytecodeHelper.getClassInternalName(cn);
// add static missing property support to all classes
BytecodeExpression staticGetPropertyMissingExpression = new BytecodeExpression() {
@Override
public void visit(MethodVisitor mv) {
mv.visitFieldInsn(GETSTATIC, internalClassName, GROOVITY_SCRIPT_HELPER_FIELD, BytecodeHelper.getTypeDescription(ScriptHelper.class));
// BytecodeHelper.visitClassLiteral(mv, cn);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(ScriptHelper.class), "staticPropertyMissing", BytecodeHelper.getMethodDescriptor(Object.class, new Class[] { String.class }), false);
mv.visitInsn(ARETURN);
}
};
MethodNode staticGetPropMethod = new MethodNode("$static_propertyMissing", ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, new ClassNode(Object.class), new Parameter[] { new Parameter(ClassHelper.make(String.class), "propertyName") }, new ClassNode[] {}, new BlockStatement(new Statement[] { new ReturnStatement(staticGetPropertyMissingExpression) }, new VariableScope()));
staticGetPropMethod.setSynthetic(true);
staticGetPropMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, true);
cn.addMethod(staticGetPropMethod);
// add static missing method support to all classes
BytecodeExpression staticGetMethodMissingExpression = new BytecodeExpression() {
@Override
public void visit(MethodVisitor mv) {
mv.visitFieldInsn(GETSTATIC, internalClassName, GROOVITY_SCRIPT_HELPER_FIELD, BytecodeHelper.getTypeDescription(ScriptHelper.class));
// BytecodeHelper.visitClassLiteral(mv, cn);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(ScriptHelper.class), "invokeMethod", BytecodeHelper.getMethodDescriptor(Object.class, new Class[] { String.class, Object.class }), false);
mv.visitInsn(ARETURN);
}
};
MethodNode staticGetMissingMethod = new MethodNode("$static_methodMissing", ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, new ClassNode(Object.class), new Parameter[] { new Parameter(ClassHelper.make(String.class), "methodName"), new Parameter(ClassHelper.make(Object.class), "methodArgs") }, new ClassNode[] {}, new BlockStatement(new Statement[] { new ReturnStatement(staticGetMethodMissingExpression) }, new VariableScope()));
staticGetMissingMethod.setSynthetic(true);
staticGetMissingMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, true);
cn.addMethod(staticGetMissingMethod);
if (!(cn instanceof InnerClassNode) || cn.isStaticClass()) {
FieldNode gsfNode = new FieldNode(GROOVITY_SCRIPT_HELPER_FIELD, ACC_PROTECTED | ACC_STATIC, new ClassNode(ScriptHelper.class), cn, ConstantExpression.NULL);
cn.addField(gsfNode);
// add missing method support to all classes
BytecodeExpression getMethodMissingExpression = new BytecodeExpression() {
@Override
public void visit(MethodVisitor mv) {
mv.visitFieldInsn(GETSTATIC, internalClassName, GROOVITY_SCRIPT_HELPER_FIELD, BytecodeHelper.getTypeDescription(ScriptHelper.class));
// BytecodeHelper.visitClassLiteral(mv, cn);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(ScriptHelper.class), "invokeMethod", BytecodeHelper.getMethodDescriptor(Object.class, new Class[] { String.class, Object.class }), false);
mv.visitInsn(ARETURN);
}
};
MethodNode getMissingMethod = new MethodNode("methodMissing", ACC_PUBLIC | ACC_SYNTHETIC, new ClassNode(Object.class), new Parameter[] { new Parameter(ClassHelper.make(String.class), "methodName"), new Parameter(ClassHelper.make(Object.class), "methodArgs") }, new ClassNode[] { new ClassNode(Exception.class) }, new BlockStatement(new Statement[] { new ReturnStatement(getMethodMissingExpression) }, new VariableScope()));
getMissingMethod.setSynthetic(true);
getMissingMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, true);
cn.addMethod(getMissingMethod);
// add missing property lookup to top-level classes
BytecodeExpression instanceGetPropertyMissingExpression = new BytecodeExpression() {
@Override
public void visit(MethodVisitor mv) {
mv.visitFieldInsn(GETSTATIC, internalClassName, GROOVITY_SCRIPT_HELPER_FIELD, BytecodeHelper.getTypeDescription(ScriptHelper.class));
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(ScriptHelper.class), "getProperty", BytecodeHelper.getMethodDescriptor(Object.class, new Class[] { String.class }), false);
mv.visitInsn(ARETURN);
}
};
MethodNode instanceGetMethod = new MethodNode("propertyMissing", ACC_PUBLIC | ACC_SYNTHETIC, new ClassNode(Object.class), new Parameter[] { new Parameter(ClassHelper.make(String.class), "propertyName") }, new ClassNode[] {}, new BlockStatement(new Statement[] { new ReturnStatement(instanceGetPropertyMissingExpression) }, new VariableScope()));
instanceGetMethod.setSynthetic(true);
instanceGetMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, true);
cn.addMethod(instanceGetMethod);
BytecodeExpression instanceSetPropertyMissingExpression = new BytecodeExpression() {
@Override
public void visit(MethodVisitor mv) {
mv.visitFieldInsn(GETSTATIC, internalClassName, GROOVITY_SCRIPT_HELPER_FIELD, BytecodeHelper.getTypeDescription(ScriptHelper.class));
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(ScriptHelper.class), "setProperty", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class), Type.getType(Object.class)), false);
mv.visitInsn(RETURN);
}
};
MethodNode setMethod = new MethodNode("propertyMissing", ACC_PUBLIC | ACC_SYNTHETIC, ClassHelper.VOID_TYPE, new Parameter[] { new Parameter(ClassHelper.make(String.class), "propertyName"), new Parameter(new ClassNode(Object.class), "newValue") }, new ClassNode[] {}, new BlockStatement(new Statement[] { new ExpressionStatement(instanceSetPropertyMissingExpression) }, new VariableScope()));
setMethod.setSynthetic(true);
setMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, true);
cn.addMethod(setMethod);
if (cn != scriptClassNode) {
// add getBinding to other classes
BytecodeExpression getFactoryCall = new BytecodeExpression() {
@Override
public void visit(MethodVisitor mv) {
mv.visitFieldInsn(GETSTATIC, internalClassName, GROOVITY_SCRIPT_HELPER_FIELD, BytecodeHelper.getTypeDescription(ScriptHelper.class));
mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(ScriptHelper.class), "getBinding", BytecodeHelper.getMethodDescriptor(Binding.class, new Class[] {}), false);
mv.visitInsn(ARETURN);
}
};
MethodNode getMethod = new MethodNode("getBinding", ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, new ClassNode(Binding.class), new Parameter[] {}, new ClassNode[] {}, new BlockStatement(new Statement[] { new ReturnStatement(getFactoryCall) }, new VariableScope()));
getMethod.setSynthetic(true);
getMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, true);
cn.addMethod(getMethod);
}
// add load, run and tag methods to all top level classes
BytecodeExpression loadFactoryCall = new BytecodeExpression() {
@Override
public void visit(MethodVisitor mv) {
mv.visitFieldInsn(GETSTATIC, internalClassName, GROOVITY_SCRIPT_HELPER_FIELD, BytecodeHelper.getTypeDescription(ScriptHelper.class));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(ScriptHelper.class), LOAD, BytecodeHelper.getMethodDescriptor(Script.class, new Class[] { String.class }), false);
mv.visitInsn(ARETURN);
}
};
MethodNode loadMethod = new MethodNode(LOAD, ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, new ClassNode(Script.class), new Parameter[] { new Parameter(new ClassNode(String.class), "path") }, new ClassNode[] { new ClassNode(InstantiationException.class), new ClassNode(IllegalAccessException.class), new ClassNode(ClassNotFoundException.class) }, new BlockStatement(new Statement[] { new ReturnStatement(loadFactoryCall) }, new VariableScope()));
loadMethod.setSynthetic(true);
loadMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, true);
cn.addMethod(loadMethod);
BytecodeExpression runFactoryCall = new BytecodeExpression() {
@Override
public void visit(MethodVisitor mv) {
mv.visitFieldInsn(GETSTATIC, internalClassName, GROOVITY_SCRIPT_HELPER_FIELD, BytecodeHelper.getTypeDescription(ScriptHelper.class));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(ScriptHelper.class), RUN, BytecodeHelper.getMethodDescriptor(Object.class, new Class[] { String.class }), false);
mv.visitInsn(ARETURN);
}
};
MethodNode runMethod = new MethodNode(RUN, ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, new Parameter[] { new Parameter(new ClassNode(String.class), "path") }, new ClassNode[] { new ClassNode(InstantiationException.class), new ClassNode(IllegalAccessException.class), new ClassNode(ClassNotFoundException.class), new ClassNode(IOException.class) }, new BlockStatement(new Statement[] { new ReturnStatement(runFactoryCall) }, new VariableScope()));
runMethod.setSynthetic(true);
runMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, true);
cn.addMethod(runMethod);
BytecodeExpression doStreamCall = new BytecodeExpression() {
@Override
public void visit(MethodVisitor mv) {
mv.visitFieldInsn(GETSTATIC, internalClassName, GROOVITY_SCRIPT_HELPER_FIELD, BytecodeHelper.getTypeDescription(ScriptHelper.class));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(ScriptHelper.class), STREAM, Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Object.class)), false);
mv.visitInsn(RETURN);
}
};
MethodNode doStreamMethod = new MethodNode(STREAM, ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, ClassHelper.VOID_TYPE, new Parameter[] { new Parameter(ClassHelper.make(Object.class), "obj") }, new ClassNode[] {}, new BlockStatement(new Statement[] { new ExpressionStatement(doStreamCall) }, new VariableScope()));
doStreamMethod.setSynthetic(true);
doStreamMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, true);
cn.addMethod(doStreamMethod);
BytecodeExpression doTagFullFactoryCall = new BytecodeExpression() {
@Override
public void visit(MethodVisitor mv) {
mv.visitFieldInsn(GETSTATIC, internalClassName, GROOVITY_SCRIPT_HELPER_FIELD, BytecodeHelper.getTypeDescription(ScriptHelper.class));
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(ScriptHelper.class), TAG, BytecodeHelper.getMethodDescriptor(Object.class, new Class[] { String.class, Map.class, Closure.class }), false);
mv.visitInsn(ARETURN);
}
};
MethodNode doTagFullMethod = new MethodNode(TAG, ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, new Parameter[] { new Parameter(ClassHelper.make(String.class), "tagName"), new Parameter(new ClassNode(Map.class), "attributes"), new Parameter(new ClassNode(Closure.class), "body") }, new ClassNode[] { new ClassNode(Exception.class) }, new BlockStatement(new Statement[] { new ReturnStatement(doTagFullFactoryCall) }, new VariableScope()));
doTagFullMethod.setSynthetic(true);
doTagFullMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, true);
cn.addMethod(doTagFullMethod);
BytecodeExpression doTagShortBodyFactoryCall = new BytecodeExpression() {
@Override
public void visit(MethodVisitor mv) {
mv.visitFieldInsn(GETSTATIC, internalClassName, GROOVITY_SCRIPT_HELPER_FIELD, BytecodeHelper.getTypeDescription(ScriptHelper.class));
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(ScriptHelper.class), TAG, BytecodeHelper.getMethodDescriptor(Object.class, new Class[] { String.class, Closure.class }), false);
mv.visitInsn(ARETURN);
}
};
MethodNode doTagShortBodyMethod = new MethodNode(TAG, ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, new Parameter[] { new Parameter(ClassHelper.make(String.class), "tagName"), new Parameter(new ClassNode(Closure.class), "body") }, new ClassNode[] { new ClassNode(Exception.class) }, new BlockStatement(new Statement[] { new ReturnStatement(doTagShortBodyFactoryCall) }, new VariableScope()));
doTagShortBodyMethod.setSynthetic(true);
doTagShortBodyMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, true);
cn.addMethod(doTagShortBodyMethod);
}
}
// Record whether the class is a library with @Functions in a static final boolean.
final FieldNode isLibraryFieldNode = new FieldNode("isGroovyLibrary", ACC_PUBLIC | ACC_STATIC | ACC_FINAL, new ClassNode(Boolean.class), scriptClassNode, new ConstantExpression(isLibrary));
scriptClassNode.addField(isLibraryFieldNode);
ListExpression dependencyExpression = new ListExpression();
for (String dep : initDependencies) {
dependencyExpression.addExpression(new ConstantExpression(dep));
}
// Store pointers to all dependencies to help control load order
final FieldNode initDependenciesFieldNode = new FieldNode("initDependencies", ACC_PUBLIC | ACC_STATIC | ACC_FINAL, new ClassNode(new ArrayList<String>().getClass()), scriptClassNode, dependencyExpression);
scriptClassNode.addField(initDependenciesFieldNode);
}
} catch (Exception e) {
log.error("Error generating stats AST: ", e);
}
}
Aggregations