/* ******************************************************************* * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). * All rights reserved. * This program and the accompanying materials are made available * under the terms of the Eclipse Public License v 2.0 * which accompanies this distribution and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt * * Contributors: * PARC initial implementation * ******************************************************************/ package org.aspectj.ajdt.internal.compiler.ast; import java.lang.reflect.Modifier; import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory; import org.aspectj.ajdt.internal.core.builder.EclipseSourceContext; import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation; import org.aspectj.org.eclipse.jdt.internal.compiler.ClassFile; import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TagBits; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.aspectj.org.eclipse.jdt.internal.compiler.parser.Parser; import org.aspectj.weaver.AjAttribute; import org.aspectj.weaver.ResolvedPointcutDefinition; import org.aspectj.weaver.UnresolvedType; import org.aspectj.weaver.patterns.Pointcut; /** * pointcut [declaredModifiers] [declaredName]([arguments]): [pointcutDesignator]; * *

* No method will actually be generated for this node but an attribute will be added to the enclosing class. *

* * @author Jim Hugunin */ public class PointcutDeclaration extends AjMethodDeclaration { public static final char[] mangledPrefix = "ajc$pointcut$".toCharArray(); public PointcutDesignator pointcutDesignator; private int declaredModifiers; private String declaredName; private boolean generateSyntheticPointcutMethod = false; private EclipseFactory world = null; // private boolean mangleSelector = true; private ResolvedPointcutDefinition resolvedPointcutDeclaration = null; public PointcutDeclaration(CompilationResult compilationResult) { super(compilationResult); this.returnType = TypeReference.baseTypeReference(T_void, 0, null); } private Pointcut getPointcut() { if (pointcutDesignator == null) { return Pointcut.makeMatchesNothing(Pointcut.RESOLVED); } else { return pointcutDesignator.getPointcut(); } } public void parseStatements(Parser parser, CompilationUnitDeclaration unit) { // do nothing } public void postParse(TypeDeclaration typeDec) { if (arguments == null) arguments = new Argument[0]; this.declaredModifiers = modifiers; this.declaredName = new String(selector); // amc - if we set mangle selector to false, then the generated bytecode has the // pointcut method name that the user of an @Pointcut would expect. // But then we will unpack it again in the weaver which may cause redundant // error messages to be issued. This seems the better trade-off... // if (mangleSelector) { selector = CharOperation.concat(mangledPrefix, '$', selector, '$', Integer.toHexString(sourceStart).toCharArray()); // } if (Modifier.isAbstract(this.declaredModifiers)) { if ((typeDec instanceof AspectDeclaration) && !Modifier.isAbstract(typeDec.modifiers)) { typeDec.scope.problemReporter().signalError(sourceStart, sourceEnd, "The abstract pointcut " + new String(declaredName) + " can only be defined in an abstract aspect"); ignoreFurtherInvestigation = true; return; } } if (pointcutDesignator != null) { pointcutDesignator.postParse(typeDec, this); } } private boolean isAtAspectJ(TypeDeclaration typeDec) { if (typeDec.annotations == null) return false; for (int i = 0; i < typeDec.annotations.length; i++) { Annotation annotation = typeDec.annotations[i]; if (CharOperation.equals(annotation.resolvedType.signature(),ASPECT_CHARS)) { return true; } } return false; } /** * Called from the AtAspectJVisitor to create the @Pointcut annotation (and corresponding method) for this pointcut * */ public void addAtAspectJAnnotations() { String argNames = buildArgNameRepresentation(); Annotation pcutAnnotation = AtAspectJAnnotationFactory.createPointcutAnnotation(getPointcutText(), argNames, declarationSourceStart); if (annotations == null) { annotations = new Annotation[] { pcutAnnotation }; } else { Annotation[] old = annotations; annotations = new Annotation[old.length + 1]; System.arraycopy(old, 0, annotations, 0, old.length); annotations[old.length] = pcutAnnotation; } generateSyntheticPointcutMethod = true; } public String getPointcutText() { String text = getPointcut().toString(); if (!text.contains("BindingTypePattern")) return text; // has been wrecked by resolution, try to reconstruct from tokens if (pointcutDesignator != null) { text = pointcutDesignator.getPointcutDeclarationText(); } return text; } private String buildArgNameRepresentation() { StringBuilder args = new StringBuilder(); if (this.arguments != null) { for (int i = 0; i < this.arguments.length; i++) { if (i != 0) args.append(","); args.append(new String(this.arguments[i].name)); } } return args.toString(); } // coming from an @Pointcut declaration public void setGenerateSyntheticPointcutMethod() { generateSyntheticPointcutMethod = true; // mangleSelector = false; } private static char[] ASPECT_CHARS = "Lorg/aspectj/lang/annotation/Aspect;".toCharArray(); public void resolve(ClassScope upperScope) { // we attempted to resolve annotations below, but that was too early, so we do it again // now at the 'right' time. if (binding != null) { binding.tagBits -= TagBits.AnnotationResolved; resolveAnnotations(scope, this.annotations, this.binding); TypeDeclaration typeDec = upperScope.referenceContext; if (Modifier.isAbstract(this.declaredModifiers)) { if (!(typeDec instanceof AspectDeclaration)) { if (!isAtAspectJ(typeDec)) { typeDec.scope.problemReporter().signalError(sourceStart, sourceEnd, "The abstract pointcut " + new String(declaredName) + " can only be defined in an aspect"); ignoreFurtherInvestigation = true; return; } } } } // for the rest of the resolution process, this method should do nothing, use the entry point below... } public void resolvePointcut(ClassScope upperScope) { this.world = EclipseFactory.fromScopeLookupEnvironment(upperScope); super.resolve(upperScope); } public void resolveStatements() { if (isAbstract()) { this.modifiers |= ExtraCompilerModifiers.AccSemicolonBody; } if (binding == null || ignoreFurtherInvestigation) return; if (Modifier.isAbstract(this.declaredModifiers) && (pointcutDesignator != null)) { scope.problemReporter().signalError(sourceStart, sourceEnd, "abstract pointcut can't have body"); ignoreFurtherInvestigation = true; return; } if (pointcutDesignator != null) { pointcutDesignator.finishResolveTypes(this, this.binding, arguments.length, scope.enclosingSourceType()); } // System.out.println("resolved: " + getPointcut() + ", " + getPointcut().state); makeResolvedPointcutDefinition(world); resolvedPointcutDeclaration.setPointcut(getPointcut()); super.resolveStatements(); } public ResolvedPointcutDefinition makeResolvedPointcutDefinition(EclipseFactory inWorld) { if (resolvedPointcutDeclaration != null) return resolvedPointcutDeclaration; if (binding == null) { // other errors exist that will be reported separately return null; } // System.out.println("pc: " + getPointcut() + ", " + getPointcut().state); ReferenceBinding declaringClass = binding.declaringClass; TypeBinding[] parameters = binding.parameters; UnresolvedType utDeclaringClass = inWorld.fromBinding(declaringClass); UnresolvedType[] utParameters = inWorld.fromBindings(parameters); resolvedPointcutDeclaration = new ResolvedPointcutDefinition(utDeclaringClass, declaredModifiers, declaredName, utParameters, getPointcut()); // ??? might want to // use null resolvedPointcutDeclaration.setPosition(sourceStart, sourceEnd); resolvedPointcutDeclaration.setSourceContext(new EclipseSourceContext(compilationResult)); return resolvedPointcutDeclaration; } public AjAttribute makeAttribute() { return new AjAttribute.PointcutDeclarationAttribute(makeResolvedPointcutDefinition(world)); } /** * A pointcut declaration exists in a classfile only as an attibute on the class. Unlike advice and inter-type declarations, it * has no corresponding method. */ public void generateCode(ClassScope classScope, ClassFile classFile) { this.world = EclipseFactory.fromScopeLookupEnvironment(classScope); if (ignoreFurtherInvestigation) return; classFile.extraAttributes.add(new EclipseAttributeAdapter(makeAttribute())); addVersionAttributeIfNecessary(classFile); if (generateSyntheticPointcutMethod) { this.binding.modifiers |= ClassFileConstants.AccSynthetic; super.generateCode(classScope, classFile); } return; } /** * Normally, pointcuts occur in aspects - aspects are always tagged with a weaver version attribute, see AspectDeclaration. * However, pointcuts can also occur in regular classes and in this case there is no AspectDeclaration to ensure the attribute * is added. So, this method adds the attribute if someone else hasn't already. */ private void addVersionAttributeIfNecessary(ClassFile classFile) { for (Object o : classFile.extraAttributes) { EclipseAttributeAdapter element = (EclipseAttributeAdapter) o; if (CharOperation.equals(element.getNameChars(), weaverVersionChars)) return; } classFile.extraAttributes.add(new EclipseAttributeAdapter(new AjAttribute.WeaverVersionInfo())); } private static char[] weaverVersionChars = "org.aspectj.weaver.WeaverVersion".toCharArray(); protected int generateInfoAttributes(ClassFile classFile) { return super.generateInfoAttributes(classFile, true); } public StringBuffer printReturnType(int indent, StringBuffer output) { return output.append("pointcut"); } /* * (non-Javadoc) * * @see org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration#printBody(int, java.lang.StringBuffer) */ public StringBuffer printBody(int indent, StringBuffer output) { output.append(": "); output.append(getPointcut()); output.append(";"); return output; } }