From 471db173df94e16aeca5edaa0a5f2948bcf3c50e Mon Sep 17 00:00:00 2001 From: aclement Date: Tue, 21 Oct 2008 22:50:52 +0000 Subject: [PATCH] 246125: c16 --- .../weaver/WeakClassLoaderReference.java | 8 +- .../bcel/BcelWeakClassLoaderReference.java | 49 + .../org/aspectj/weaver/bcel/BcelWorld.java | 5 +- .../weaver/reflect/ReflectionWorld.java | 5 +- .../aspectj/weaver/tools/PointcutParser.java | 942 +++++++++--------- 5 files changed, 519 insertions(+), 490 deletions(-) create mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelWeakClassLoaderReference.java diff --git a/weaver/src/org/aspectj/weaver/WeakClassLoaderReference.java b/weaver/src/org/aspectj/weaver/WeakClassLoaderReference.java index 3f0bdb8bd..bcf650286 100644 --- a/weaver/src/org/aspectj/weaver/WeakClassLoaderReference.java +++ b/weaver/src/org/aspectj/weaver/WeakClassLoaderReference.java @@ -13,8 +13,6 @@ package org.aspectj.weaver; import java.lang.ref.WeakReference; -import org.aspectj.apache.bcel.util.ClassLoaderReference; - /** * Wraps a reference to a classloader inside a WeakReference. This should be used where we do not want the existence of a * classloader reference to prevent garbage collection of that classloader (and possibly an associated weaver instance in the case @@ -34,11 +32,11 @@ import org.aspectj.apache.bcel.util.ClassLoaderReference; * * @author Andy Clement */ -public class WeakClassLoaderReference implements ClassLoaderReference { +public class WeakClassLoaderReference { - private int hashcode; + protected final int hashcode; - private WeakReference loaderRef; + private final WeakReference loaderRef; public WeakClassLoaderReference(ClassLoader loader) { loaderRef = new WeakReference(loader); diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelWeakClassLoaderReference.java b/weaver/src/org/aspectj/weaver/bcel/BcelWeakClassLoaderReference.java new file mode 100644 index 000000000..aac294fa4 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/bcel/BcelWeakClassLoaderReference.java @@ -0,0 +1,49 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement initial implementation + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.util.ClassLoaderReference; +import org.aspectj.weaver.WeakClassLoaderReference; + +/** + * Wraps a reference to a classloader inside a WeakReference. This should be used where we do not want the existence of a + * classloader reference to prevent garbage collection of that classloader (and possibly an associated weaver instance in the case + * of load time weaving). + *

+ * In more detail:
+ * When load time weaving, the class Aj maintains a WeakHashMap from the classloader instance to a weaver instance. The aim is that + * the weaver is around as long as the classloader is and should the classloader be dereferenced then the weaver can also be garbage + * collected. The problem is that if there are many references to the classloader from within the weaver, these are considered hard + * references and cause the classloader to be long lived - even if the user of the classloader has dereferenced it in their code. + * The solution is that the weaver should use instances of WeakClassLoaderReference objects - so that when the users hard reference + * to the classloader goes, nothing in the weaver will cause it to hang around. There is a big assertion here that the + * WeakClassLoaderReference instances will not 'lose' their ClassLoader references until the top level ClassLoader reference is + * null'd. This means there is no need to check for the null case on get() in this WeakReference logic below, because we shouldn't + * be using this weaver if its associated ClassLoader has been collected. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=210470 + * + * + * @author Andy Clement + */ +public class BcelWeakClassLoaderReference extends WeakClassLoaderReference implements ClassLoaderReference { + + public BcelWeakClassLoaderReference(ClassLoader loader) { + super(loader); + } + + public boolean equals(Object obj) { + if (!(obj instanceof BcelWeakClassLoaderReference)) + return false; + BcelWeakClassLoaderReference other = (BcelWeakClassLoaderReference) obj; + return (other.hashcode == hashcode); + } + +} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelWorld.java b/weaver/src/org/aspectj/weaver/bcel/BcelWorld.java index 2453765bc..47f15a22c 100644 --- a/weaver/src/org/aspectj/weaver/bcel/BcelWorld.java +++ b/weaver/src/org/aspectj/weaver/bcel/BcelWorld.java @@ -66,7 +66,6 @@ import org.aspectj.weaver.ResolvedTypeMunger; import org.aspectj.weaver.Shadow; import org.aspectj.weaver.ShadowMunger; import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.WeakClassLoaderReference; import org.aspectj.weaver.World; import org.aspectj.weaver.model.AsmRelationshipProvider; import org.aspectj.weaver.patterns.DeclareAnnotation; @@ -78,7 +77,7 @@ public class BcelWorld extends World implements Repository { private final ClassPathManager classPath; protected Repository delegate; - private WeakClassLoaderReference loaderRef; + private BcelWeakClassLoaderReference loaderRef; private final BcelWeavingSupport bcelWeavingSupport = new BcelWeavingSupport(); private static Trace trace = TraceFactory.getTraceFactory().getTrace(BcelWorld.class); @@ -286,7 +285,7 @@ public class BcelWorld extends World implements Repository { */ public BcelWorld(ClassLoader loader, IMessageHandler handler, ICrossReferenceHandler xrefHandler) { classPath = null; - loaderRef = new WeakClassLoaderReference(loader); + loaderRef = new BcelWeakClassLoaderReference(loader); setMessageHandler(handler); setCrossReferenceHandler(xrefHandler); // Tell BCEL to use us for resolving any classes diff --git a/weaver/src/org/aspectj/weaver/reflect/ReflectionWorld.java b/weaver/src/org/aspectj/weaver/reflect/ReflectionWorld.java index 24b098e3c..4f14303d6 100644 --- a/weaver/src/org/aspectj/weaver/reflect/ReflectionWorld.java +++ b/weaver/src/org/aspectj/weaver/reflect/ReflectionWorld.java @@ -25,9 +25,8 @@ import org.aspectj.weaver.WeakClassLoaderReference; import org.aspectj.weaver.World; /** - * A ReflectionWorld is used solely for purposes of type resolution based on the - * runtime classpath (java.lang.reflect). It does not support weaving operations - * (creation of mungers etc..). + * A ReflectionWorld is used solely for purposes of type resolution based on the runtime classpath (java.lang.reflect). It does not + * support weaving operations (creation of mungers etc..). * */ public class ReflectionWorld extends World implements IReflectionWorld { diff --git a/weaver/src/org/aspectj/weaver/tools/PointcutParser.java b/weaver/src/org/aspectj/weaver/tools/PointcutParser.java index 3e651f0ca..13841b71d 100644 --- a/weaver/src/org/aspectj/weaver/tools/PointcutParser.java +++ b/weaver/src/org/aspectj/weaver/tools/PointcutParser.java @@ -51,494 +51,478 @@ import org.aspectj.weaver.reflect.PointcutParameterImpl; import org.aspectj.weaver.reflect.ReflectionWorld; /** - * A PointcutParser can be used to build PointcutExpressions for a - * user-defined subset of AspectJ's pointcut language + * A PointcutParser can be used to build PointcutExpressions for a user-defined subset of AspectJ's pointcut language */ public class PointcutParser { - + private ReflectionWorld world; private WeakClassLoaderReference classLoaderReference; - private Set supportedPrimitives; - private Set pointcutDesignators = new HashSet(); - - /** - * @return a Set containing every PointcutPrimitive except - * if, cflow, and cflowbelow (useful for passing to - * PointcutParser constructor). - */ - public static Set getAllSupportedPointcutPrimitives() { - Set primitives = new HashSet(); - primitives.add(PointcutPrimitive.ADVICE_EXECUTION); - primitives.add(PointcutPrimitive.ARGS); - primitives.add(PointcutPrimitive.CALL); - primitives.add(PointcutPrimitive.EXECUTION); - primitives.add(PointcutPrimitive.GET); - primitives.add(PointcutPrimitive.HANDLER); - primitives.add(PointcutPrimitive.INITIALIZATION); - primitives.add(PointcutPrimitive.PRE_INITIALIZATION); - primitives.add(PointcutPrimitive.SET); - primitives.add(PointcutPrimitive.STATIC_INITIALIZATION); - primitives.add(PointcutPrimitive.TARGET); - primitives.add(PointcutPrimitive.THIS); - primitives.add(PointcutPrimitive.WITHIN); - primitives.add(PointcutPrimitive.WITHIN_CODE); - primitives.add(PointcutPrimitive.AT_ANNOTATION); - primitives.add(PointcutPrimitive.AT_THIS); - primitives.add(PointcutPrimitive.AT_TARGET); - primitives.add(PointcutPrimitive.AT_ARGS); - primitives.add(PointcutPrimitive.AT_WITHIN); - primitives.add(PointcutPrimitive.AT_WITHINCODE); - primitives.add(PointcutPrimitive.REFERENCE); - - return primitives; - } - - /** - * Returns a pointcut parser that can parse the full AspectJ pointcut - * language with the following exceptions: - *

- *

When resolving types in pointcut expressions, the context classloader is used to find types.

- */ - public static PointcutParser getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution() { - PointcutParser p = new PointcutParser(); - p.setClassLoader(Thread.currentThread().getContextClassLoader()); - return p; - } - - /** - * Returns a pointcut parser that can parse pointcut expressions built - * from a user-defined subset of AspectJ's supported pointcut primitives. - * The following restrictions apply: - * - *

When resolving types in pointcut expressions, the context classloader is used to find types.

- * @param supportedPointcutKinds a set of PointcutPrimitives this parser - * should support - * @throws UnsupportedOperationException if the set contains if, cflow, or - * cflow below - */ - public static PointcutParser getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(Set supportedPointcutKinds) { - PointcutParser p = new PointcutParser(supportedPointcutKinds); - p.setClassLoader(Thread.currentThread().getContextClassLoader()); - return p; - } - - /** - * Returns a pointcut parser that can parse the full AspectJ pointcut - * language with the following exceptions: - * - *

When resolving types in pointcut expressions, the given classloader is used to find types.

- */ - public static PointcutParser getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(ClassLoader classLoader) { - PointcutParser p = new PointcutParser(); + private final Set supportedPrimitives; + private final Set pointcutDesignators = new HashSet(); + + /** + * @return a Set containing every PointcutPrimitive except if, cflow, and cflowbelow (useful for passing to PointcutParser + * constructor). + */ + public static Set getAllSupportedPointcutPrimitives() { + Set primitives = new HashSet(); + primitives.add(PointcutPrimitive.ADVICE_EXECUTION); + primitives.add(PointcutPrimitive.ARGS); + primitives.add(PointcutPrimitive.CALL); + primitives.add(PointcutPrimitive.EXECUTION); + primitives.add(PointcutPrimitive.GET); + primitives.add(PointcutPrimitive.HANDLER); + primitives.add(PointcutPrimitive.INITIALIZATION); + primitives.add(PointcutPrimitive.PRE_INITIALIZATION); + primitives.add(PointcutPrimitive.SET); + primitives.add(PointcutPrimitive.STATIC_INITIALIZATION); + primitives.add(PointcutPrimitive.TARGET); + primitives.add(PointcutPrimitive.THIS); + primitives.add(PointcutPrimitive.WITHIN); + primitives.add(PointcutPrimitive.WITHIN_CODE); + primitives.add(PointcutPrimitive.AT_ANNOTATION); + primitives.add(PointcutPrimitive.AT_THIS); + primitives.add(PointcutPrimitive.AT_TARGET); + primitives.add(PointcutPrimitive.AT_ARGS); + primitives.add(PointcutPrimitive.AT_WITHIN); + primitives.add(PointcutPrimitive.AT_WITHINCODE); + primitives.add(PointcutPrimitive.REFERENCE); + + return primitives; + } + + /** + * Returns a pointcut parser that can parse the full AspectJ pointcut language with the following exceptions: + * + *

+ * When resolving types in pointcut expressions, the context classloader is used to find types. + *

+ */ + public static PointcutParser getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution() { + PointcutParser p = new PointcutParser(); + p.setClassLoader(Thread.currentThread().getContextClassLoader()); + return p; + } + + /** + * Returns a pointcut parser that can parse pointcut expressions built from a user-defined subset of AspectJ's supported + * pointcut primitives. The following restrictions apply: + * + *

+ * When resolving types in pointcut expressions, the context classloader is used to find types. + *

+ * + * @param supportedPointcutKinds a set of PointcutPrimitives this parser should support + * @throws UnsupportedOperationException if the set contains if, cflow, or cflow below + */ + public static PointcutParser getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution( + Set supportedPointcutKinds) { + PointcutParser p = new PointcutParser(supportedPointcutKinds); + p.setClassLoader(Thread.currentThread().getContextClassLoader()); + return p; + } + + /** + * Returns a pointcut parser that can parse the full AspectJ pointcut language with the following exceptions: + * + *

+ * When resolving types in pointcut expressions, the given classloader is used to find types. + *

+ */ + public static PointcutParser getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution( + ClassLoader classLoader) { + PointcutParser p = new PointcutParser(); p.setClassLoader(classLoader); return p; - } - - - /** - * Returns a pointcut parser that can parse pointcut expressions built - * from a user-defined subset of AspectJ's supported pointcut primitives. - * The following restrictions apply: - * - *

When resolving types in pointcut expressions, the given classloader is used to find types.

- * @param supportedPointcutKinds a set of PointcutPrimitives this parser - * should support - * @throws UnsupportedOperationException if the set contains if, cflow, or - * cflow below - */ - public static PointcutParser getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(Set supportedPointcutKinds, ClassLoader classLoader) { - PointcutParser p = new PointcutParser(supportedPointcutKinds); + } + + /** + * Returns a pointcut parser that can parse pointcut expressions built from a user-defined subset of AspectJ's supported + * pointcut primitives. The following restrictions apply: + * + *

+ * When resolving types in pointcut expressions, the given classloader is used to find types. + *

+ * + * @param supportedPointcutKinds a set of PointcutPrimitives this parser should support + * @throws UnsupportedOperationException if the set contains if, cflow, or cflow below + */ + public static PointcutParser getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution( + Set supportedPointcutKinds, ClassLoader classLoader) { + PointcutParser p = new PointcutParser(supportedPointcutKinds); p.setClassLoader(classLoader); - return p; - } - - /** - * Create a pointcut parser that can parse the full AspectJ pointcut - * language with the following exceptions: - * - */ - protected PointcutParser() { - supportedPrimitives = getAllSupportedPointcutPrimitives(); - setClassLoader(PointcutParser.class.getClassLoader()); - } - - /** - * Create a pointcut parser that can parse pointcut expressions built - * from a user-defined subset of AspectJ's supported pointcut primitives. - * The following restrictions apply: - * - * @param supportedPointcutKinds a set of PointcutPrimitives this parser - * should support - * @throws UnsupportedOperationException if the set contains if, cflow, or - * cflow below - */ - private PointcutParser(Set/**/ supportedPointcutKinds) { - supportedPrimitives = supportedPointcutKinds; - for (Iterator iter = supportedPointcutKinds.iterator(); iter.hasNext();) { - PointcutPrimitive element = (PointcutPrimitive) iter.next(); - if ((element == PointcutPrimitive.IF) || - (element == PointcutPrimitive.CFLOW) || - (element == PointcutPrimitive.CFLOW_BELOW)) { - throw new UnsupportedOperationException("Cannot handle if, cflow, and cflowbelow primitives"); - } - } - setClassLoader(PointcutParser.class.getClassLoader()); - } - - protected void setWorld(ReflectionWorld aWorld) { - this.world = aWorld; - } - - /** - * Set the classloader that this parser should use for - * type resolution. - * @param aLoader - */ - protected void setClassLoader(ClassLoader aLoader) { - this.classLoaderReference = new WeakClassLoaderReference(aLoader); - world = new ReflectionWorld(this.classLoaderReference.getClassLoader()); - } - - /** - * Set the lint properties for this parser from the - * given resource on the classpath. - * @param resourcePath path to a file containing aspectj - * lint properties - */ - public void setLintProperties(String resourcePath)throws IOException { - URL url = this.classLoaderReference.getClassLoader().getResource(resourcePath); - InputStream is = url.openStream(); - Properties p = new Properties(); + return p; + } + + /** + * Create a pointcut parser that can parse the full AspectJ pointcut language with the following exceptions: + * + */ + protected PointcutParser() { + supportedPrimitives = getAllSupportedPointcutPrimitives(); + setClassLoader(PointcutParser.class.getClassLoader()); + } + + /** + * Create a pointcut parser that can parse pointcut expressions built from a user-defined subset of AspectJ's supported pointcut + * primitives. The following restrictions apply: + * + * + * @param supportedPointcutKinds a set of PointcutPrimitives this parser should support + * @throws UnsupportedOperationException if the set contains if, cflow, or cflow below + */ + private PointcutParser(Set/* */supportedPointcutKinds) { + supportedPrimitives = supportedPointcutKinds; + for (Iterator iter = supportedPointcutKinds.iterator(); iter.hasNext();) { + PointcutPrimitive element = (PointcutPrimitive) iter.next(); + if ((element == PointcutPrimitive.IF) || (element == PointcutPrimitive.CFLOW) + || (element == PointcutPrimitive.CFLOW_BELOW)) { + throw new UnsupportedOperationException("Cannot handle if, cflow, and cflowbelow primitives"); + } + } + setClassLoader(PointcutParser.class.getClassLoader()); + } + + protected void setWorld(ReflectionWorld aWorld) { + this.world = aWorld; + } + + /** + * Set the classloader that this parser should use for type resolution. + * + * @param aLoader + */ + protected void setClassLoader(ClassLoader aLoader) { + this.classLoaderReference = new WeakClassLoaderReference(aLoader); + world = new ReflectionWorld(this.classLoaderReference.getClassLoader()); + } + + /** + * Set the lint properties for this parser from the given resource on the classpath. + * + * @param resourcePath path to a file containing aspectj lint properties + */ + public void setLintProperties(String resourcePath) throws IOException { + URL url = this.classLoaderReference.getClassLoader().getResource(resourcePath); + InputStream is = url.openStream(); + Properties p = new Properties(); p.load(is); setLintProperties(p); - } - - /** - * Set the lint properties for this parser from the - * given properties set. - * @param properties - */ - public void setLintProperties(Properties properties) { - getWorld().getLint().setFromProperties(properties); - } - - /** - * Register a new pointcut designator handler with this parser. - * This provides an extension mechansim for the integration of - * domain-specific pointcut designators with the AspectJ - * pointcut language. - * @param designatorHandler - */ - public void registerPointcutDesignatorHandler(PointcutDesignatorHandler designatorHandler) { - this.pointcutDesignators.add(designatorHandler); - if (world != null) world.registerPointcutHandler(designatorHandler); - } - - /** - * Create a pointcut parameter of the given name and type. - * @param name - * @param type - * @return - */ - public PointcutParameter createPointcutParameter(String name, Class type) { - return new PointcutParameterImpl(name,type); - } - - /** - * Parse the given pointcut expression. - * A global scope is assumed for resolving any type references, and the pointcut - * must contain no formals (variables to be bound). - * @throws UnsupportedPointcutPrimitiveException if the parser encounters a - * primitive pointcut expression of a kind not supported by this PointcutParser. - * @throws IllegalArgumentException if the expression is not a well-formed - * pointcut expression - */ - public PointcutExpression parsePointcutExpression(String expression) - throws UnsupportedPointcutPrimitiveException, IllegalArgumentException { - return parsePointcutExpression(expression,null,new PointcutParameter[0]); - } - - /** - * Parse the given pointcut expression. - * The pointcut is resolved as if it had been declared inside the inScope class - * (this allows the pointcut to contain unqualified references to other pointcuts - * declared in the same type for example). - * The pointcut may contain zero or more formal parameters to be bound at matched - * join points. - * @throws UnsupportedPointcutPrimitiveException if the parser encounters a - * primitive pointcut expression of a kind not supported by this PointcutParser. - * @throws IllegalArgumentException if the expression is not a well-formed - * pointcut expression - */ - public PointcutExpression parsePointcutExpression( - String expression, - Class inScope, - PointcutParameter[] formalParameters) - throws UnsupportedPointcutPrimitiveException, IllegalArgumentException { - PointcutExpressionImpl pcExpr = null; - try { - Pointcut pc = resolvePointcutExpression(expression,inScope,formalParameters); - pc = concretizePointcutExpression(pc,inScope,formalParameters); - validateAgainstSupportedPrimitives(pc,expression); // again, because we have now followed any ref'd pcuts - pcExpr = new PointcutExpressionImpl(pc,expression,formalParameters,getWorld()); - } catch (ParserException pEx) { - throw new IllegalArgumentException(buildUserMessageFromParserException(expression,pEx)); - } catch (ReflectionWorld.ReflectionWorldException rwEx) { - throw new IllegalArgumentException(rwEx.getMessage()); - } - return pcExpr; - } - - protected Pointcut resolvePointcutExpression( - String expression, - Class inScope, - PointcutParameter[] formalParameters) { - try { - PatternParser parser = new PatternParser(expression); - parser.setPointcutDesignatorHandlers(pointcutDesignators, world); - Pointcut pc = parser.parsePointcut(); - validateAgainstSupportedPrimitives(pc,expression); - IScope resolutionScope = buildResolutionScope((inScope == null ? Object.class : inScope),formalParameters); - pc = pc.resolve(resolutionScope); - return pc; - } catch (ParserException pEx) { - throw new IllegalArgumentException(buildUserMessageFromParserException(expression,pEx)); - } - } - - protected Pointcut concretizePointcutExpression(Pointcut pc, Class inScope, PointcutParameter[] formalParameters) { - ResolvedType declaringTypeForResolution = null; - if (inScope != null) { - declaringTypeForResolution = getWorld().resolve(inScope.getName()); - } else { - declaringTypeForResolution = ResolvedType.OBJECT.resolve(getWorld()); - } - IntMap arity = new IntMap(formalParameters.length); - for (int i = 0; i < formalParameters.length; i++) { - arity.put(i, i); - } - return pc.concretize(declaringTypeForResolution, declaringTypeForResolution, arity); - } - - /** - * Parse the given aspectj type pattern, and return a - * matcher that can be used to match types using it. - * @param typePattern an aspectj type pattern - * @return a type pattern matcher that matches using the given - * pattern - * @throws IllegalArgumentException if the type pattern cannot - * be successfully parsed. - */ - public TypePatternMatcher parseTypePattern(String typePattern) - throws IllegalArgumentException { - try { - TypePattern tp = new PatternParser(typePattern).parseTypePattern(); - tp.resolve(world); - return new TypePatternMatcherImpl(tp,world); - } catch (ParserException pEx) { - throw new IllegalArgumentException(buildUserMessageFromParserException(typePattern,pEx)); - } catch (ReflectionWorld.ReflectionWorldException rwEx) { - throw new IllegalArgumentException(rwEx.getMessage()); - } - } - - private World getWorld() { - return world; - } - - /* for testing */ - Set getSupportedPrimitives() { - return supportedPrimitives; - } - - /* for testing */ - IMessageHandler setCustomMessageHandler(IMessageHandler aHandler) { - IMessageHandler current = getWorld().getMessageHandler(); - getWorld().setMessageHandler(aHandler); - return current; - } - - private IScope buildResolutionScope(Class inScope, PointcutParameter[] formalParameters) { - if (formalParameters == null) formalParameters = new PointcutParameter[0]; - FormalBinding[] formalBindings = new FormalBinding[formalParameters.length]; - for (int i = 0; i < formalBindings.length; i++) { - formalBindings[i] = new FormalBinding(toUnresolvedType(formalParameters[i].getType()),formalParameters[i].getName(),i); + } + + /** + * Set the lint properties for this parser from the given properties set. + * + * @param properties + */ + public void setLintProperties(Properties properties) { + getWorld().getLint().setFromProperties(properties); + } + + /** + * Register a new pointcut designator handler with this parser. This provides an extension mechansim for the integration of + * domain-specific pointcut designators with the AspectJ pointcut language. + * + * @param designatorHandler + */ + public void registerPointcutDesignatorHandler(PointcutDesignatorHandler designatorHandler) { + this.pointcutDesignators.add(designatorHandler); + if (world != null) + world.registerPointcutHandler(designatorHandler); + } + + /** + * Create a pointcut parameter of the given name and type. + * + * @param name + * @param type + * @return + */ + public PointcutParameter createPointcutParameter(String name, Class type) { + return new PointcutParameterImpl(name, type); + } + + /** + * Parse the given pointcut expression. A global scope is assumed for resolving any type references, and the pointcut must + * contain no formals (variables to be bound). + * + * @throws UnsupportedPointcutPrimitiveException if the parser encounters a primitive pointcut expression of a kind not + * supported by this PointcutParser. + * @throws IllegalArgumentException if the expression is not a well-formed pointcut expression + */ + public PointcutExpression parsePointcutExpression(String expression) throws UnsupportedPointcutPrimitiveException, + IllegalArgumentException { + return parsePointcutExpression(expression, null, new PointcutParameter[0]); + } + + /** + * Parse the given pointcut expression. The pointcut is resolved as if it had been declared inside the inScope class (this + * allows the pointcut to contain unqualified references to other pointcuts declared in the same type for example). The pointcut + * may contain zero or more formal parameters to be bound at matched join points. + * + * @throws UnsupportedPointcutPrimitiveException if the parser encounters a primitive pointcut expression of a kind not + * supported by this PointcutParser. + * @throws IllegalArgumentException if the expression is not a well-formed pointcut expression + */ + public PointcutExpression parsePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters) + throws UnsupportedPointcutPrimitiveException, IllegalArgumentException { + PointcutExpressionImpl pcExpr = null; + try { + Pointcut pc = resolvePointcutExpression(expression, inScope, formalParameters); + pc = concretizePointcutExpression(pc, inScope, formalParameters); + validateAgainstSupportedPrimitives(pc, expression); // again, because we have now followed any ref'd pcuts + pcExpr = new PointcutExpressionImpl(pc, expression, formalParameters, getWorld()); + } catch (ParserException pEx) { + throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx)); + } catch (ReflectionWorld.ReflectionWorldException rwEx) { + throw new IllegalArgumentException(rwEx.getMessage()); } - if (inScope == null) { - return new SimpleScope(getWorld(),formalBindings); - } else { - ResolvedType inType = getWorld().resolve(inScope.getName()); - ISourceContext sourceContext = new ISourceContext() { - public ISourceLocation makeSourceLocation(IHasPosition position) { - return new SourceLocation(new File(""),0); - } - public ISourceLocation makeSourceLocation(int line, int offset) { - return new SourceLocation(new File(""),line); - } - public int getOffset() { - return 0; - } - public void tidy() {} - }; - return new BindingScope(inType,sourceContext,formalBindings); - } - } - - private UnresolvedType toUnresolvedType(Class clazz) { - if (clazz.isArray()) { - return UnresolvedType.forSignature(clazz.getName().replace('.','/')); - } else { - return UnresolvedType.forName(clazz.getName()); - } - } - - private void validateAgainstSupportedPrimitives(Pointcut pc, String expression) { - switch(pc.getPointcutKind()) { - case Pointcut.AND: - validateAgainstSupportedPrimitives(((AndPointcut)pc).getLeft(),expression); - validateAgainstSupportedPrimitives(((AndPointcut)pc).getRight(),expression); - break; - case Pointcut.ARGS: - if (!supportedPrimitives.contains(PointcutPrimitive.ARGS)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.ARGS); - break; - case Pointcut.CFLOW: - CflowPointcut cfp = (CflowPointcut) pc; - if (cfp.isCflowBelow()) { - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.CFLOW_BELOW); - } else { - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.CFLOW); - } - case Pointcut.HANDLER: - if (!supportedPrimitives.contains(PointcutPrimitive.HANDLER)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.HANDLER); - break; - case Pointcut.IF: - case Pointcut.IF_FALSE: - case Pointcut.IF_TRUE: - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.IF); - case Pointcut.KINDED: - validateKindedPointcut(((KindedPointcut)pc),expression); - break; - case Pointcut.NOT: - validateAgainstSupportedPrimitives(((NotPointcut)pc).getNegatedPointcut(),expression); - break; - case Pointcut.OR: - validateAgainstSupportedPrimitives(((OrPointcut)pc).getLeft(),expression); - validateAgainstSupportedPrimitives(((OrPointcut)pc).getRight(),expression); - break; - case Pointcut.THIS_OR_TARGET: - boolean isThis = ((ThisOrTargetPointcut)pc).isThis(); - if (isThis && !supportedPrimitives.contains(PointcutPrimitive.THIS)) { - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.THIS); - } else if (!supportedPrimitives.contains(PointcutPrimitive.TARGET)) { - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.TARGET); - } - break; - case Pointcut.WITHIN: - if (!supportedPrimitives.contains(PointcutPrimitive.WITHIN)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.WITHIN); - break; - case Pointcut.WITHINCODE: - if (!supportedPrimitives.contains(PointcutPrimitive.WITHIN_CODE)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.WITHIN_CODE); - break; - case Pointcut.ATTHIS_OR_TARGET: - isThis = ((ThisOrTargetAnnotationPointcut)pc).isThis(); - if (isThis && !supportedPrimitives.contains(PointcutPrimitive.AT_THIS)) { - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_THIS); - } else if (!supportedPrimitives.contains(PointcutPrimitive.AT_TARGET)) { - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_TARGET); - } - break; - case Pointcut.ATARGS: - if (!supportedPrimitives.contains(PointcutPrimitive.AT_ARGS)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_ARGS); - break; - case Pointcut.ANNOTATION: - if (!supportedPrimitives.contains(PointcutPrimitive.AT_ANNOTATION)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_ANNOTATION); - break; - case Pointcut.ATWITHIN: - if (!supportedPrimitives.contains(PointcutPrimitive.AT_WITHIN)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_WITHIN); - break; - case Pointcut.ATWITHINCODE: - if (!supportedPrimitives.contains(PointcutPrimitive.AT_WITHINCODE)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_WITHINCODE); - break; - case Pointcut.REFERENCE: - if (!supportedPrimitives.contains(PointcutPrimitive.REFERENCE)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.REFERENCE); - break; - case Pointcut.USER_EXTENSION: - // always ok... - break; - case Pointcut.NONE: // deliberate fall-through - default: - throw new IllegalArgumentException("Unknown pointcut kind: " + pc.getPointcutKind()); - } - } - - private void validateKindedPointcut(KindedPointcut pc, String expression) { - Shadow.Kind kind = pc.getKind(); - if ((kind == Shadow.MethodCall) || (kind == Shadow.ConstructorCall)) { - if (!supportedPrimitives.contains(PointcutPrimitive.CALL)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.CALL); - } else if ((kind == Shadow.MethodExecution) || (kind == Shadow.ConstructorExecution)) { - if (!supportedPrimitives.contains(PointcutPrimitive.EXECUTION)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.EXECUTION); - } else if (kind == Shadow.AdviceExecution) { - if (!supportedPrimitives.contains(PointcutPrimitive.ADVICE_EXECUTION)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.ADVICE_EXECUTION); - } else if (kind == Shadow.FieldGet) { - if (!supportedPrimitives.contains(PointcutPrimitive.GET)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.GET); - } else if (kind == Shadow.FieldSet) { - if (!supportedPrimitives.contains(PointcutPrimitive.SET)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.SET); - } else if (kind == Shadow.Initialization) { - if (!supportedPrimitives.contains(PointcutPrimitive.INITIALIZATION)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.INITIALIZATION); - } else if (kind == Shadow.PreInitialization) { - if (!supportedPrimitives.contains(PointcutPrimitive.PRE_INITIALIZATION)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.PRE_INITIALIZATION); - } else if (kind == Shadow.StaticInitialization) { - if (!supportedPrimitives.contains(PointcutPrimitive.STATIC_INITIALIZATION)) - throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.STATIC_INITIALIZATION); - } - } - + return pcExpr; + } + + protected Pointcut resolvePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters) { + try { + PatternParser parser = new PatternParser(expression); + parser.setPointcutDesignatorHandlers(pointcutDesignators, world); + Pointcut pc = parser.parsePointcut(); + validateAgainstSupportedPrimitives(pc, expression); + IScope resolutionScope = buildResolutionScope((inScope == null ? Object.class : inScope), formalParameters); + pc = pc.resolve(resolutionScope); + return pc; + } catch (ParserException pEx) { + throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx)); + } + } + + protected Pointcut concretizePointcutExpression(Pointcut pc, Class inScope, PointcutParameter[] formalParameters) { + ResolvedType declaringTypeForResolution = null; + if (inScope != null) { + declaringTypeForResolution = getWorld().resolve(inScope.getName()); + } else { + declaringTypeForResolution = ResolvedType.OBJECT.resolve(getWorld()); + } + IntMap arity = new IntMap(formalParameters.length); + for (int i = 0; i < formalParameters.length; i++) { + arity.put(i, i); + } + return pc.concretize(declaringTypeForResolution, declaringTypeForResolution, arity); + } + + /** + * Parse the given aspectj type pattern, and return a matcher that can be used to match types using it. + * + * @param typePattern an aspectj type pattern + * @return a type pattern matcher that matches using the given pattern + * @throws IllegalArgumentException if the type pattern cannot be successfully parsed. + */ + public TypePatternMatcher parseTypePattern(String typePattern) throws IllegalArgumentException { + try { + TypePattern tp = new PatternParser(typePattern).parseTypePattern(); + tp.resolve(world); + return new TypePatternMatcherImpl(tp, world); + } catch (ParserException pEx) { + throw new IllegalArgumentException(buildUserMessageFromParserException(typePattern, pEx)); + } catch (ReflectionWorld.ReflectionWorldException rwEx) { + throw new IllegalArgumentException(rwEx.getMessage()); + } + } + + private World getWorld() { + return world; + } + + /* for testing */ + Set getSupportedPrimitives() { + return supportedPrimitives; + } + + /* for testing */ + IMessageHandler setCustomMessageHandler(IMessageHandler aHandler) { + IMessageHandler current = getWorld().getMessageHandler(); + getWorld().setMessageHandler(aHandler); + return current; + } + + private IScope buildResolutionScope(Class inScope, PointcutParameter[] formalParameters) { + if (formalParameters == null) + formalParameters = new PointcutParameter[0]; + FormalBinding[] formalBindings = new FormalBinding[formalParameters.length]; + for (int i = 0; i < formalBindings.length; i++) { + formalBindings[i] = new FormalBinding(toUnresolvedType(formalParameters[i].getType()), formalParameters[i].getName(), i); + } + if (inScope == null) { + return new SimpleScope(getWorld(), formalBindings); + } else { + ResolvedType inType = getWorld().resolve(inScope.getName()); + ISourceContext sourceContext = new ISourceContext() { + public ISourceLocation makeSourceLocation(IHasPosition position) { + return new SourceLocation(new File(""), 0); + } + + public ISourceLocation makeSourceLocation(int line, int offset) { + return new SourceLocation(new File(""), line); + } + + public int getOffset() { + return 0; + } + + public void tidy() { + } + }; + return new BindingScope(inType, sourceContext, formalBindings); + } + } + + private UnresolvedType toUnresolvedType(Class clazz) { + if (clazz.isArray()) { + return UnresolvedType.forSignature(clazz.getName().replace('.', '/')); + } else { + return UnresolvedType.forName(clazz.getName()); + } + } + + private void validateAgainstSupportedPrimitives(Pointcut pc, String expression) { + switch (pc.getPointcutKind()) { + case Pointcut.AND: + validateAgainstSupportedPrimitives(((AndPointcut) pc).getLeft(), expression); + validateAgainstSupportedPrimitives(((AndPointcut) pc).getRight(), expression); + break; + case Pointcut.ARGS: + if (!supportedPrimitives.contains(PointcutPrimitive.ARGS)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.ARGS); + break; + case Pointcut.CFLOW: + CflowPointcut cfp = (CflowPointcut) pc; + if (cfp.isCflowBelow()) { + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.CFLOW_BELOW); + } else { + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.CFLOW); + } + case Pointcut.HANDLER: + if (!supportedPrimitives.contains(PointcutPrimitive.HANDLER)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.HANDLER); + break; + case Pointcut.IF: + case Pointcut.IF_FALSE: + case Pointcut.IF_TRUE: + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.IF); + case Pointcut.KINDED: + validateKindedPointcut(((KindedPointcut) pc), expression); + break; + case Pointcut.NOT: + validateAgainstSupportedPrimitives(((NotPointcut) pc).getNegatedPointcut(), expression); + break; + case Pointcut.OR: + validateAgainstSupportedPrimitives(((OrPointcut) pc).getLeft(), expression); + validateAgainstSupportedPrimitives(((OrPointcut) pc).getRight(), expression); + break; + case Pointcut.THIS_OR_TARGET: + boolean isThis = ((ThisOrTargetPointcut) pc).isThis(); + if (isThis && !supportedPrimitives.contains(PointcutPrimitive.THIS)) { + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.THIS); + } else if (!supportedPrimitives.contains(PointcutPrimitive.TARGET)) { + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.TARGET); + } + break; + case Pointcut.WITHIN: + if (!supportedPrimitives.contains(PointcutPrimitive.WITHIN)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.WITHIN); + break; + case Pointcut.WITHINCODE: + if (!supportedPrimitives.contains(PointcutPrimitive.WITHIN_CODE)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.WITHIN_CODE); + break; + case Pointcut.ATTHIS_OR_TARGET: + isThis = ((ThisOrTargetAnnotationPointcut) pc).isThis(); + if (isThis && !supportedPrimitives.contains(PointcutPrimitive.AT_THIS)) { + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_THIS); + } else if (!supportedPrimitives.contains(PointcutPrimitive.AT_TARGET)) { + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_TARGET); + } + break; + case Pointcut.ATARGS: + if (!supportedPrimitives.contains(PointcutPrimitive.AT_ARGS)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_ARGS); + break; + case Pointcut.ANNOTATION: + if (!supportedPrimitives.contains(PointcutPrimitive.AT_ANNOTATION)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_ANNOTATION); + break; + case Pointcut.ATWITHIN: + if (!supportedPrimitives.contains(PointcutPrimitive.AT_WITHIN)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_WITHIN); + break; + case Pointcut.ATWITHINCODE: + if (!supportedPrimitives.contains(PointcutPrimitive.AT_WITHINCODE)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_WITHINCODE); + break; + case Pointcut.REFERENCE: + if (!supportedPrimitives.contains(PointcutPrimitive.REFERENCE)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.REFERENCE); + break; + case Pointcut.USER_EXTENSION: + // always ok... + break; + case Pointcut.NONE: // deliberate fall-through + default: + throw new IllegalArgumentException("Unknown pointcut kind: " + pc.getPointcutKind()); + } + } + + private void validateKindedPointcut(KindedPointcut pc, String expression) { + Shadow.Kind kind = pc.getKind(); + if ((kind == Shadow.MethodCall) || (kind == Shadow.ConstructorCall)) { + if (!supportedPrimitives.contains(PointcutPrimitive.CALL)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.CALL); + } else if ((kind == Shadow.MethodExecution) || (kind == Shadow.ConstructorExecution)) { + if (!supportedPrimitives.contains(PointcutPrimitive.EXECUTION)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.EXECUTION); + } else if (kind == Shadow.AdviceExecution) { + if (!supportedPrimitives.contains(PointcutPrimitive.ADVICE_EXECUTION)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.ADVICE_EXECUTION); + } else if (kind == Shadow.FieldGet) { + if (!supportedPrimitives.contains(PointcutPrimitive.GET)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.GET); + } else if (kind == Shadow.FieldSet) { + if (!supportedPrimitives.contains(PointcutPrimitive.SET)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.SET); + } else if (kind == Shadow.Initialization) { + if (!supportedPrimitives.contains(PointcutPrimitive.INITIALIZATION)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.INITIALIZATION); + } else if (kind == Shadow.PreInitialization) { + if (!supportedPrimitives.contains(PointcutPrimitive.PRE_INITIALIZATION)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.PRE_INITIALIZATION); + } else if (kind == Shadow.StaticInitialization) { + if (!supportedPrimitives.contains(PointcutPrimitive.STATIC_INITIALIZATION)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.STATIC_INITIALIZATION); + } + } + private String buildUserMessageFromParserException(String pc, ParserException ex) { StringBuffer msg = new StringBuffer(); msg.append("Pointcut is not well-formed: expecting '"); @@ -553,7 +537,7 @@ public class PointcutParser { for (int i = 0; i < location.getStart(); i++) { msg.append(" "); } - for (int j=location.getStart(); j <= location.getEnd(); j++) { + for (int j = location.getStart(); j <= location.getEnd(); j++) { msg.append("^"); } msg.append("\n"); -- 2.39.5