private void verifyCompilePhase(AnnotationNode annotation, Class<?> klass) { GroovyASTTransformation transformationClass = klass.getAnnotation(GroovyASTTransformation.class); if (transformationClass != null) { CompilePhase specifiedCompilePhase = transformationClass.phase(); if (specifiedCompilePhase.getPhaseNumber() < CompilePhase.SEMANTIC_ANALYSIS.getPhaseNumber()) { source .getErrorCollector() .addError( new SimpleMessage( annotation.getClassNode().getName() + " is defined to be run in compile phase " + specifiedCompilePhase + ". Local AST transformations must run in " + CompilePhase.SEMANTIC_ANALYSIS + " or later!", source)); } } else { source .getErrorCollector() .addError( new SimpleMessage( "AST transformation implementation classes must be annotated with " + GroovyASTTransformation.class.getName() + ". " + klass.getName() + " lacks this annotation.", source)); } }
@SuppressWarnings("unused") private List<String> getTransformClassNames( AnnotationNode annotation, Annotation transformClassAnnotation) { List<String> result = new ArrayList<String>(); try { Method valueMethod = transformClassAnnotation.getClass().getMethod("value"); String[] names = (String[]) valueMethod.invoke(transformClassAnnotation); result.addAll(Arrays.asList(names)); Method classesMethod = transformClassAnnotation.getClass().getMethod("classes"); Class[] classes = (Class[]) classesMethod.invoke(transformClassAnnotation); for (Class klass : classes) { result.add(klass.getName()); } if (names.length > 0 && classes.length > 0) { source .getErrorCollector() .addError( new SimpleMessage( "@GroovyASTTransformationClass in " + annotation.getClassNode().getName() + " should specify transforms only by class names or by classes and not by both", source)); } } catch (Exception e) { source.addException(e); } return result; }
/** * Snoops through the declaring class and all parents looking for methods * * <ul> * <li><code>public String getMessage(java.lang.String)</code> * <li><code>public String getMessage(java.lang.String, java.util.Locale)</code> * <li><code>public String getMessage(java.lang.String, java.lang.Object[])</code> * <li><code>public String getMessage(java.lang.String, java.lang.Object[], java.util.Locale) * </code> * <li><code>public String getMessage(java.lang.String, java.util.List)</code> * <li><code>public String getMessage(java.lang.String, java.util.List, java.util.Locale)</code> * <li><code>public String getMessage(java.lang.String, java.util.Map)</code> * <li><code>public String getMessage(java.lang.String, java.util.Map, java.util.Locale)</code> * <li><code>public String getMessage(java.lang.String, java.lang.String)</code> * <li><code>public String getMessage(java.lang.String, java.lang.String, java.util.Locale) * </code> * <li><code>public String getMessage(java.lang.String, java.lang.Object[], java.lang.String) * </code> * <li><code> * public String getMessage(java.lang.String, java.lang.Object[], java.lang.String, java.util.Locale) * </code> * <li><code>public String getMessage(java.lang.String, java.util.List, java.lang.String)</code> * <li><code> * public String getMessage(java.lang.String, java.util.List, java.lang.String, java.util.Locale) * </code> * <li><code>public String getMessage(java.lang.String, java.util.Map, java.lang.String)</code> * <li><code> * public String getMessage(java.lang.String, java.util.Map, java.lang.String, java.util.Locale) * </code> * </ul> * * If any are defined all must be defined or a compilation error results. * * @param declaringClass the class to search * @param sourceUnit the source unit, for error reporting. {@code @NotNull}. * @return true if property change support should be added */ protected static boolean needsMessageSource(ClassNode declaringClass, SourceUnit sourceUnit) { boolean found = false; ClassNode consideredClass = declaringClass; while (consideredClass != null) { for (MethodNode method : consideredClass.getMethods()) { // just check length, MOP will match it up found = method.getName().equals(METHOD_GET_MESSAGE) && method.getParameters().length == 1; found |= method.getName().equals(METHOD_GET_MESSAGE) && method.getParameters().length == 2; found |= method.getName().equals(METHOD_GET_MESSAGE) && method.getParameters().length == 3; found |= method.getName().equals(METHOD_GET_MESSAGE) && method.getParameters().length == 4; if (found) return false; } consideredClass = consideredClass.getSuperClass(); } if (found) { sourceUnit .getErrorCollector() .addErrorAndContinue( new SimpleMessage( "@MessageSourceAware cannot be processed on " + declaringClass.getName() + " because some but not all of variants of getMessage() were declared in the current class or super classes.", sourceUnit)); return false; } return true; }
private void addTransformsToClassNode( AnnotationNode annotation, String[] transformClassNames, Class[] transformClasses) { if (transformClassNames.length == 0 && transformClasses.length == 0) { source .getErrorCollector() .addError( new SimpleMessage( "@GroovyASTTransformationClass in " + annotation.getClassNode().getName() + " does not specify any transform class names/classes", source)); } if (transformClassNames.length > 0 && transformClasses.length > 0) { source .getErrorCollector() .addError( new SimpleMessage( "@GroovyASTTransformationClass in " + annotation.getClassNode().getName() + " should specify transforms only by class names or by classes and not by both", source)); } for (String transformClass : transformClassNames) { try { Class klass = transformLoader.loadClass(transformClass, false, true, false); verifyAndAddTransform(annotation, klass); } catch (ClassNotFoundException e) { source .getErrorCollector() .addErrorAndContinue( new SimpleMessage( "Could not find class for Transformation Processor " + transformClass + " declared by " + annotation.getClassNode().getName(), source)); } } for (Class klass : transformClasses) { verifyAndAddTransform(annotation, klass); } }
private Class[] getTransformClasses(ClassNode classNode) { if (!classNode.hasClass()) { List<AnnotationNode> annotations = classNode.getAnnotations(); AnnotationNode transformAnnotation = null; for (AnnotationNode anno : annotations) { if (anno.getClassNode().getName().equals(GroovyASTTransformationClass.class.getName())) { transformAnnotation = anno; break; } } if (transformAnnotation != null) { Expression expr = (Expression) transformAnnotation.getMember("classes"); if (expr == null) { return NO_CLASSES; } Class<?>[] values = NO_CLASSES; // Will need to extract the classnames if (expr instanceof ListExpression) { List<Class<?>> loadedClasses = new ArrayList<Class<?>>(); ListExpression expression = (ListExpression) expr; List<Expression> expressions = expression.getExpressions(); for (Expression oneExpr : expressions) { String classname = ((ClassExpression) oneExpr).getType().getName(); try { Class<?> clazz = Class.forName(classname, false, transformLoader); loadedClasses.add(clazz); } catch (ClassNotFoundException cnfe) { source .getErrorCollector() .addError( new SimpleMessage( "Ast transform processing, cannot find " + classname, source)); } } if (loadedClasses.size() != 0) { values = loadedClasses.toArray(new Class<?>[loadedClasses.size()]); } return values; } else { } throw new RuntimeException( "nyi implemented in eclipse: need to support: " + expr + " (class=" + expr.getClass() + ")"); } return null; } else { Annotation transformClassAnnotation = getTransformClassAnnotation(classNode); if (transformClassAnnotation == null) { return null; } return getTransformClasses(transformClassAnnotation); } }
private boolean addCollectedAnnotations( List<AnnotationNode> collected, AnnotationNode aliasNode, AnnotatedNode origin) { ClassNode classNode = aliasNode.getClassNode(); boolean ret = false; for (AnnotationNode annotation : classNode.getAnnotations()) { if (annotation.getClassNode().getName().equals(AnnotationCollector.class.getName())) { Expression processorExp = annotation.getMember("processor"); AnnotationCollectorTransform act = null; assertStringConstant(processorExp); if (processorExp != null) { String className = (String) ((ConstantExpression) processorExp).getValue(); Class klass = loadTransformClass(className, aliasNode); if (klass != null) { try { act = (AnnotationCollectorTransform) klass.newInstance(); } catch (InstantiationException e) { source.getErrorCollector().addErrorAndContinue(new ExceptionMessage(e, true, source)); } catch (IllegalAccessException e) { source.getErrorCollector().addErrorAndContinue(new ExceptionMessage(e, true, source)); } } } else { act = new AnnotationCollectorTransform(); } if (act != null) { // GRECLIPSE edit // collected.addAll(act.visit(annotation, aliasNode, origin, source)); // original annotation added to metadata to prevent import organizer from deleting its // import List<AnnotationNode> visitResult = act.visit(annotation, aliasNode, origin, source); for (AnnotationNode annotationNode : visitResult) { Set<AnnotationNode> aliases = annotationNode.getNodeMetaData("AnnotationCollector"); if (aliases == null) annotationNode.setNodeMetaData("AnnotationCollector", (aliases = new HashSet(1))); aliases.add(aliasNode); } collected.addAll(visitResult); // GRECLIPSE end } ret = true; } } return ret; }
/** * Generates a fatal compilation error * * @param sourceUnit the SourceUnit * @param astNode the ASTNode which caused the error * @param message The error message * @param fatal indicates if this is a fatal error */ public static void error( final SourceUnit sourceUnit, final ASTNode astNode, final String message, final boolean fatal) { final SyntaxException syntaxException = new SyntaxException(message, astNode.getLineNumber(), astNode.getColumnNumber()); final SyntaxErrorMessage syntaxErrorMessage = new SyntaxErrorMessage(syntaxException, sourceUnit); sourceUnit.getErrorCollector().addError(syntaxErrorMessage, fatal); }
private void verifyClass(AnnotationNode annotation, Class klass) { if (!ASTTransformation.class.isAssignableFrom(klass)) { source .getErrorCollector() .addError( new SimpleMessage( "Not an ASTTransformation: " + klass.getName() + " declared by " + annotation.getClassNode().getName(), source)); } }
private void assertStringConstant(Expression exp) { if (exp == null) return; if (!(exp instanceof ConstantExpression)) { source .getErrorCollector() .addErrorAndContinue( new SyntaxErrorMessage( new SyntaxException( "Expected a String constant.", exp.getLineNumber(), exp.getColumnNumber()), source)); } ConstantExpression ce = (ConstantExpression) exp; if (!(ce.getValue() instanceof String)) { source .getErrorCollector() .addErrorAndContinue( new SyntaxErrorMessage( new SyntaxException( "Expected a String constant.", exp.getLineNumber(), exp.getColumnNumber()), source)); } }
// GRECLIPSE start protected void addTypeError(String msg, ClassNode expr) { int line = expr.getLineNumber(); int col = expr.getColumnNumber(); SourceUnit source = getSourceUnit(); source .getErrorCollector() .addErrorAndContinue( // GRECLIPSE: start new SyntaxErrorMessage( new PreciseSyntaxException( msg + '\n', line, col, expr.getNameStart(), expr.getNameEnd()), source) // end ); }
private Class loadTransformClass(String transformClass, AnnotationNode annotation) { try { return transformLoader.loadClass(transformClass, false, true, false); } catch (ClassNotFoundException e) { source .getErrorCollector() .addErrorAndContinue( new SimpleMessage( "Could not find class for Transformation Processor " + transformClass + " declared by " + annotation.getClassNode().getName(), source)); } return null; }
public void visitClass(ClassNode node) { ClassNode oldClass = currentClass; currentClass = node; checkImplementsAndExtends(node); if (source != null && !source.getErrorCollector().hasErrors()) { checkClassForIncorrectModifiers(node); checkInterfaceMethodVisibility(node); checkClassForOverwritingFinal(node); checkMethodsForIncorrectModifiers(node); checkMethodsForWeakerAccess(node); checkMethodsForOverridingFinal(node); checkNoAbstractMethodsNonabstractClass(node); checkGenericsUsage(node, node.getUnresolvedInterfaces()); checkGenericsUsage(node, node.getUnresolvedSuperClass()); } super.visitClass(node); currentClass = oldClass; }
protected void addError(String msg, ASTNode expr) { int line = expr.getLineNumber(); int col = expr.getColumnNumber(); // GRECLIPSE int start = expr.getStart(); int end = expr.getEnd() - 1; if (expr instanceof ClassNode) { // assume we have a class declaration ClassNode cn = (ClassNode) expr; if (cn.getNameEnd() > 0) { start = cn.getNameStart(); end = cn.getNameEnd(); } else if (cn.getComponentType() != null) { // avoid extra whitespace after closing ] end--; } } else if (expr instanceof DeclarationExpression) { // assume that we just want to underline the variable declaration DeclarationExpression decl = (DeclarationExpression) expr; Expression lhs = decl.getLeftExpression(); start = lhs.getStart(); // avoid extra space before = if a variable end = lhs instanceof VariableExpression ? start + lhs.getText().length() - 1 : lhs.getEnd() - 1; } // end SourceUnit source = getSourceUnit(); source .getErrorCollector() .addErrorAndContinue( // GRECLIPSE: start new SyntaxErrorMessage( new PreciseSyntaxException(msg + '\n', line, col, start, end), source) // end ); }
protected static boolean needsMybatisContribution( ClassNode declaringClass, SourceUnit sourceUnit) { boolean found1 = false, found2 = false, found3 = false, found4 = false; ClassNode consideredClass = declaringClass; while (consideredClass != null) { for (MethodNode method : consideredClass.getMethods()) { // just check length, MOP will match it up found1 = method.getName().equals(METHOD_WITH_SQL_SESSION) && method.getParameters().length == 1; found2 = method.getName().equals(METHOD_WITH_SQL_SESSION) && method.getParameters().length == 2; found3 = method.getName().equals(METHOD_SET_MYBATIS_PROVIDER) && method.getParameters().length == 1; found4 = method.getName().equals(METHOD_GET_MYBATIS_PROVIDER) && method.getParameters().length == 0; if (found1 && found2 && found3 && found4) { return false; } } consideredClass = consideredClass.getSuperClass(); } if (found1 || found2 || found3 || found4) { sourceUnit .getErrorCollector() .addErrorAndContinue( new SimpleMessage( "@MybatisAware cannot be processed on " + declaringClass.getName() + " because some but not all of methods from " + MybatisContributionHandler.class.getName() + " were declared in the current class or super classes.", sourceUnit)); return false; } return true; }
public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { optimizer.visitClass( classNode, source); // GROOVY-4272: repositioned it here from staticImport if (!classNode.isSynthetic()) { GenericsVisitor genericsVisitor = new GenericsVisitor(source); genericsVisitor.visitClass(classNode); } // // Run the Verifier on the outer class // try { verifier.visitClass(classNode); } catch (GroovyRuntimeException rpe) { ASTNode node = rpe.getNode(); getErrorCollector() .addError( new SyntaxException( rpe.getMessage(), node.getLineNumber(), node.getColumnNumber(), node.getLastLineNumber(), node.getLastColumnNumber()), source); } LabelVerifier lv = new LabelVerifier(source); lv.visitClass(classNode); ClassCompletionVerifier completionVerifier = new ClassCompletionVerifier(source); completionVerifier.visitClass(classNode); ExtendedVerifier xverifier = new ExtendedVerifier(source); xverifier.visitClass(classNode); // because the class may be generated even if a error was found // and that class may have an invalid format we fail here if needed getErrorCollector().failIfErrors(); // // Prep the generator machinery // ClassVisitor visitor = createClassVisitor(); String sourceName = (source == null ? classNode.getModule().getDescription() : source.getName()); // only show the file name and its extension like javac does in its stacktraces rather // than the full path // also takes care of both \ and / depending on the host compiling environment if (sourceName != null) sourceName = sourceName.substring( Math.max(sourceName.lastIndexOf('\\'), sourceName.lastIndexOf('/')) + 1); AsmClassGenerator generator = new AsmClassGenerator(source, context, visitor, sourceName); // // Run the generation and create the class (if required) // // GRECLIPSE: if there are errors, don't generate code. // code gen can fail unexpectedly if there was an earlier error. if (!source.getErrorCollector().hasErrors()) { // end generator.visitClass(classNode); byte[] bytes = ((ClassWriter) visitor).toByteArray(); /// GRECLIPSE: start: added classNode, sourceUnit /*old{ generatedClasses.add(new GroovyClass(classNode.getName(), bytes)); }*/ // newcode generatedClasses.add(new GroovyClass(classNode.getName(), bytes, classNode, source)); // end // // Handle any callback that's been set // if (CompilationUnit.this.classgenCallback != null) { classgenCallback.call(visitor, classNode); } // // Recurse for inner classes // LinkedList innerClasses = generator.getInnerClasses(); while (!innerClasses.isEmpty()) { classgen.call(source, context, (ClassNode) innerClasses.removeFirst()); } // GRECLIPSE: if there are errors, don't generate code } // end }
/** * Main loop entry. * * <p>First, it delegates to the super visitClass so we can collect the relevant annotations in an * AST tree walk. * * <p>Second, it calls the visit method on the transformation for each relevant annotation found. * * @param classNode the class to visit */ public void visitClass(ClassNode classNode) { // only descend if we have annotations to look for Map<Class<? extends ASTTransformation>, Set<ASTNode>> baseTransforms = classNode.getTransforms(phase); if (!baseTransforms.isEmpty()) { final Map<Class<? extends ASTTransformation>, ASTTransformation> transformInstances = new HashMap<Class<? extends ASTTransformation>, ASTTransformation>(); for (Class<? extends ASTTransformation> transformClass : baseTransforms.keySet()) { try { transformInstances.put(transformClass, transformClass.newInstance()); } catch (InstantiationException e) { source .getErrorCollector() .addError( new SimpleMessage( "Could not instantiate Transformation Processor " + transformClass, // + " declared by " + // annotation.getClassNode().getName(), source)); } catch (IllegalAccessException e) { source .getErrorCollector() .addError( new SimpleMessage( "Could not instantiate Transformation Processor " + transformClass, // + " declared by " + // annotation.getClassNode().getName(), source)); } } // invert the map, is now one to many transforms = new HashMap<ASTNode, List<ASTTransformation>>(); for (Map.Entry<Class<? extends ASTTransformation>, Set<ASTNode>> entry : baseTransforms.entrySet()) { for (ASTNode node : entry.getValue()) { List<ASTTransformation> list = transforms.get(node); if (list == null) { list = new ArrayList<ASTTransformation>(); transforms.put(node, list); } list.add(transformInstances.get(entry.getKey())); } } targetNodes = new LinkedList<ASTNode[]>(); // first pass, collect nodes super.visitClass(classNode); // second pass, call visit on all of the collected nodes for (ASTNode[] node : targetNodes) { for (ASTTransformation snt : transforms.get(node[0])) { if (snt instanceof CompilationUnitAware) { ((CompilationUnitAware) snt).setCompilationUnit(context.getCompilationUnit()); } snt.visit(node, source); } } } }