diff options
author | Andy Clement <aclement@pivotal.io> | 2019-01-23 18:53:51 -0800 |
---|---|---|
committer | Andy Clement <aclement@pivotal.io> | 2019-01-23 18:53:51 -0800 |
commit | afaa961b294eca20fa9d54359c53a1de2d3c41fd (patch) | |
tree | 4b756f0f0dc4764cfcaeb200756de79cd4cdf56a /org.aspectj.matcher/src/main/java/org/aspectj | |
parent | 74dcae875f1c89b7e3fa2ffa6b524a3c187a597e (diff) | |
download | aspectj-afaa961b294eca20fa9d54359c53a1de2d3c41fd.tar.gz aspectj-afaa961b294eca20fa9d54359c53a1de2d3c41fd.zip |
mavenized org.aspectj.matcher module - wip
Diffstat (limited to 'org.aspectj.matcher/src/main/java/org/aspectj')
258 files changed, 52802 insertions, 0 deletions
diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AbstractAnnotationAJ.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AbstractAnnotationAJ.java new file mode 100644 index 000000000..503d6b3b5 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AbstractAnnotationAJ.java @@ -0,0 +1,158 @@ +/* ******************************************************************* + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + +public abstract class AbstractAnnotationAJ implements AnnotationAJ { + + protected final ResolvedType type; + + private Set<String> supportedTargets = null; // @target meta annotation + + public AbstractAnnotationAJ(ResolvedType type) { + this.type = type; + } + + /** + * {@inheritDoc} + */ + public final ResolvedType getType() { + return type; + } + + /** + * {@inheritDoc} + */ + public final String getTypeSignature() { + return type.getSignature(); + } + + /** + * {@inheritDoc} + */ + public final String getTypeName() { + return type.getName(); + } + + /** + * {@inheritDoc} + */ + public final boolean allowedOnAnnotationType() { + ensureAtTargetInitialized(); + if (supportedTargets.isEmpty()) { + return true; + } + return supportedTargets.contains("ANNOTATION_TYPE"); + } + + /** + * {@inheritDoc} + */ + public final boolean allowedOnField() { + ensureAtTargetInitialized(); + if (supportedTargets.isEmpty()) { + return true; + } + return supportedTargets.contains("FIELD"); + } + + /** + * {@inheritDoc} + */ + public final boolean allowedOnRegularType() { + ensureAtTargetInitialized(); + if (supportedTargets.isEmpty()) { + return true; + } + return supportedTargets.contains("TYPE"); + } + + /** + * {@inheritDoc} + */ + public final void ensureAtTargetInitialized() { + if (supportedTargets == null) { + AnnotationAJ atTargetAnnotation = retrieveAnnotationOnAnnotation(UnresolvedType.AT_TARGET); + if (atTargetAnnotation == null) { + supportedTargets = Collections.emptySet(); + } else { + supportedTargets = atTargetAnnotation.getTargets(); + } + } + } + + /** + * {@inheritDoc} + */ + public final String getValidTargets() { + StringBuffer sb = new StringBuffer(); + sb.append("{"); + for (Iterator<String> iter = supportedTargets.iterator(); iter.hasNext();) { + String evalue = iter.next(); + sb.append(evalue); + if (iter.hasNext()) { + sb.append(","); + } + } + sb.append("}"); + return sb.toString(); + } + + /** + * {@inheritDoc} + */ + public final boolean specifiesTarget() { + ensureAtTargetInitialized(); + return !supportedTargets.isEmpty(); + } + + /** + * Helper method to retrieve an annotation on an annotation e.g. retrieveAnnotationOnAnnotation(UnresolvedType.AT_TARGET) + */ + private final AnnotationAJ retrieveAnnotationOnAnnotation(UnresolvedType requiredAnnotationSignature) { + AnnotationAJ[] annos = type.getAnnotations(); + for (int i = 0; i < annos.length; i++) { + AnnotationAJ a = annos[i]; + if (a.getTypeSignature().equals(requiredAnnotationSignature.getSignature())) { + return annos[i]; + } + } + return null; + } + + /** + * {@inheritDoc} + */ + public abstract boolean isRuntimeVisible(); + + /** + * {@inheritDoc} + */ + public abstract Set<String> getTargets(); + + /** + * {@inheritDoc} + */ + public abstract boolean hasNameValuePair(String name, String value); + + /** + * {@inheritDoc} + */ + public abstract boolean hasNamedValue(String name); + + /** + * {@inheritDoc} + */ + public abstract String stringify(); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AbstractReferenceTypeDelegate.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AbstractReferenceTypeDelegate.java new file mode 100644 index 000000000..7f3fb60ee --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AbstractReferenceTypeDelegate.java @@ -0,0 +1,158 @@ +/* ******************************************************************* + * Copyright (c) 2002 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: + * PARC initial implementation + * Andy Clement - June 2005 - separated out from ResolvedType + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.ArrayList; +import java.util.List; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.util.GenericSignature; +import org.aspectj.util.GenericSignature.ClassSignature; +import org.aspectj.util.GenericSignatureParser; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; + +public abstract class AbstractReferenceTypeDelegate implements ReferenceTypeDelegate { + + private String sourcefilename = UNKNOWN_SOURCE_FILE; + private ISourceContext sourceContext = SourceContextImpl.UNKNOWN_SOURCE_CONTEXT; + + protected boolean exposedToWeaver; + protected ReferenceType resolvedTypeX; + protected ClassSignature cachedGenericClassTypeSignature; + + // Happens to match Bcel javaClass default of '<Unknown>' + public final static String UNKNOWN_SOURCE_FILE = "<Unknown>"; + + public AbstractReferenceTypeDelegate(ReferenceType resolvedTypeX, boolean exposedToWeaver) { + this.resolvedTypeX = resolvedTypeX; + this.exposedToWeaver = exposedToWeaver; + } + + public final boolean isClass() { + return !isAspect() && !isInterface(); + } + + public boolean isCacheable() { + return false; + } + + /** + * Designed to be overriden by EclipseType to disable collection of shadow mungers during pre-weave compilation phase + */ + public boolean doesNotExposeShadowMungers() { + return false; + } + + public boolean isExposedToWeaver() { + return exposedToWeaver; + } + + public ReferenceType getResolvedTypeX() { + return resolvedTypeX; + } + + public final String getSourcefilename() { + return sourcefilename; + } + + public final void setSourcefilename(String sourceFileName) { + sourcefilename = sourceFileName; + if (sourceFileName != null && sourceFileName.equals(AbstractReferenceTypeDelegate.UNKNOWN_SOURCE_FILE)) { + sourcefilename = "Type '" + getResolvedTypeX().getName() + "' (no debug info available)"; + } else { + String pname = getResolvedTypeX().getPackageName(); + if (pname != null) { + sourcefilename = pname.replace('.', '/') + '/' + sourceFileName; + } + } + if (sourcefilename != null && sourceContext instanceof SourceContextImpl) { + ((SourceContextImpl) sourceContext).setSourceFileName(sourcefilename); + } + } + + public ISourceLocation getSourceLocation() { + return getSourceContext().makeSourceLocation(0, 0); + } + + public ISourceContext getSourceContext() { + return sourceContext; + } + + public void setSourceContext(ISourceContext isc) { + sourceContext = isc; + } + + public GenericSignature.ClassSignature getGenericClassTypeSignature() { + if (cachedGenericClassTypeSignature == null) { + String sig = getDeclaredGenericSignature(); + if (sig != null) { + GenericSignatureParser parser = new GenericSignatureParser(); + cachedGenericClassTypeSignature = parser.parseAsClassSignature(sig); + } + } + return cachedGenericClassTypeSignature; + } + + protected GenericSignature.FormalTypeParameter[] getFormalTypeParametersFromOuterClass() { + List<GenericSignature.FormalTypeParameter> typeParameters = new ArrayList<GenericSignature.FormalTypeParameter>(); + ResolvedType outerClassType = getOuterClass(); + if (!(outerClassType instanceof ReferenceType)) { + if (outerClassType == null) { + return GenericSignature.FormalTypeParameter.NONE; + } else { + throw new BCException("Whilst processing type '" + this.resolvedTypeX.getSignature() + + "' - cannot cast the outer type to a reference type. Signature=" + outerClassType.getSignature() + + " toString()=" + outerClassType.toString()+" class=" + outerClassType.getClassName()); + } + } + ReferenceType outer = (ReferenceType) outerClassType; + ReferenceTypeDelegate outerDelegate = outer.getDelegate(); + AbstractReferenceTypeDelegate outerObjectType = (AbstractReferenceTypeDelegate) outerDelegate; + if (outerObjectType.isNested()) { + GenericSignature.FormalTypeParameter[] parentParams = outerObjectType.getFormalTypeParametersFromOuterClass(); + for (int i = 0; i < parentParams.length; i++) { + typeParameters.add(parentParams[i]); + } + } + GenericSignature.ClassSignature outerSig = outerObjectType.getGenericClassTypeSignature(); + if (outerSig != null) { + for (int i = 0; i < outerSig.formalTypeParameters.length; i++) { + typeParameters.add(outerSig.formalTypeParameters[i]); + } + } + + GenericSignature.FormalTypeParameter[] ret = new GenericSignature.FormalTypeParameter[typeParameters.size()]; + typeParameters.toArray(ret); + return ret; + } + + public boolean copySourceContext() { + return true; + } + + public int getCompilerVersion() { + return WeaverVersionInfo.getCurrentWeaverMajorVersion(); + } + + public void ensureConsistent() { + + } + + public boolean isWeavable() { + return false; + } + + public boolean hasBeenWoven() { + return false; + } +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Advice.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Advice.java new file mode 100644 index 000000000..d6c8ea87f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Advice.java @@ -0,0 +1,515 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.util.Collections; +import java.util.List; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.patterns.AndPointcut; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.TypePattern; + +public abstract class Advice extends ShadowMunger { + + protected AjAttribute.AdviceAttribute attribute; + protected transient AdviceKind kind; // alias for attribute.getKind() + protected Member signature; + private boolean isAnnotationStyle; + + // not necessarily declaring aspect, this is a semantics change from 1.0 + protected ResolvedType concreteAspect; // null until after concretize + + // Just for Cflow*entry kinds + protected List<ShadowMunger> innerCflowEntries = Collections.emptyList(); + protected int nFreeVars; + + protected TypePattern exceptionType; // just for Softener kind + + // if we are parameterized, these type may be different to the advice + // signature types + protected UnresolvedType[] bindingParameterTypes; + + protected boolean hasMatchedAtLeastOnce = false; + + // based on annotations on this advice + protected List<Lint.Kind> suppressedLintKinds = null; + + public ISourceLocation lastReportedMonitorExitJoinpointLocation = null; + + public static Advice makeCflowEntry(World world, Pointcut entry, boolean isBelow, Member stackField, int nFreeVars, + List<ShadowMunger> innerCflowEntries, ResolvedType inAspect) { + Advice ret = world.createAdviceMunger(isBelow ? AdviceKind.CflowBelowEntry : AdviceKind.CflowEntry, entry, stackField, 0, + entry, inAspect); + ret.innerCflowEntries = innerCflowEntries; + ret.nFreeVars = nFreeVars; + ret.setDeclaringType(inAspect); // correct? + return ret; + } + + public static Advice makePerCflowEntry(World world, Pointcut entry, boolean isBelow, Member stackField, ResolvedType inAspect, + List<ShadowMunger> innerCflowEntries) { + Advice ret = world.createAdviceMunger(isBelow ? AdviceKind.PerCflowBelowEntry : AdviceKind.PerCflowEntry, entry, + stackField, 0, entry, inAspect); + ret.innerCflowEntries = innerCflowEntries; + ret.concreteAspect = inAspect; + return ret; + } + + public static Advice makePerObjectEntry(World world, Pointcut entry, boolean isThis, ResolvedType inAspect) { + Advice ret = world.createAdviceMunger(isThis ? AdviceKind.PerThisEntry : AdviceKind.PerTargetEntry, entry, null, 0, entry, + inAspect); + + ret.concreteAspect = inAspect; + return ret; + } + + // PTWIMPL per type within entry advice is what initializes the aspect + // instance in the matched type + public static Advice makePerTypeWithinEntry(World world, Pointcut p, ResolvedType inAspect) { + Advice ret = world.createAdviceMunger(AdviceKind.PerTypeWithinEntry, p, null, 0, p, inAspect); + ret.concreteAspect = inAspect; + return ret; + } + + public static Advice makeSoftener(World world, Pointcut entry, TypePattern exceptionType, ResolvedType inAspect, + IHasSourceLocation loc) { + Advice ret = world.createAdviceMunger(AdviceKind.Softener, entry, null, 0, loc, inAspect); + ret.exceptionType = exceptionType; + return ret; + } + + public Advice(AjAttribute.AdviceAttribute attribute, Pointcut pointcut, Member signature) { + super(pointcut, attribute.getStart(), attribute.getEnd(), attribute.getSourceContext(), ShadowMungerAdvice); + this.attribute = attribute; + this.isAnnotationStyle = signature != null && !signature.getName().startsWith("ajc$"); + this.kind = attribute.getKind(); // alias + this.signature = signature; + if (signature != null) { + bindingParameterTypes = signature.getParameterTypes(); + } else { + bindingParameterTypes = new UnresolvedType[0]; + } + } + + @Override + public boolean match(Shadow shadow, World world) { + if (super.match(shadow, world)) { + if (shadow.getKind() == Shadow.ExceptionHandler) { + if (kind.isAfter() || kind == AdviceKind.Around) { + world.showMessage(IMessage.WARNING, WeaverMessages.format(WeaverMessages.ONLY_BEFORE_ON_HANDLER), + getSourceLocation(), shadow.getSourceLocation()); + return false; + } + } + if (shadow.getKind() == Shadow.SynchronizationLock || shadow.getKind() == Shadow.SynchronizationUnlock) { + if (kind == AdviceKind.Around + // Don't work, see comments in SynchronizationTests + // && attribute.getProceedCallSignatures()!=null + // && attribute.getProceedCallSignatures().length!=0 + ) { + world.showMessage(IMessage.WARNING, WeaverMessages.format(WeaverMessages.NO_AROUND_ON_SYNCHRONIZATION), + getSourceLocation(), shadow.getSourceLocation()); + return false; + } + } + + if (hasExtraParameter() && kind == AdviceKind.AfterReturning) { + ResolvedType resolvedExtraParameterType = getExtraParameterType().resolve(world); + ResolvedType shadowReturnType = shadow.getReturnType().resolve(world); + boolean matches = (resolvedExtraParameterType.isConvertableFrom(shadowReturnType) && shadow.getKind() + .hasReturnValue()); + if (matches && resolvedExtraParameterType.isParameterizedType()) { + maybeIssueUncheckedMatchWarning(resolvedExtraParameterType, shadowReturnType, shadow, world); + } + return matches; + } else if (hasExtraParameter() && kind == AdviceKind.AfterThrowing) { // pr119749 + ResolvedType exceptionType = getExtraParameterType().resolve(world); + if (!exceptionType.isCheckedException() || exceptionType.getName().equals("java.lang.Exception")) { // pr292239 + return true; + } + UnresolvedType[] shadowThrows = shadow.getSignature().getExceptions(world); + boolean matches = false; + for (int i = 0; i < shadowThrows.length && !matches; i++) { + ResolvedType type = shadowThrows[i].resolve(world); + if (exceptionType.isAssignableFrom(type)) { + matches = true; + } + } + return matches; + } else if (kind == AdviceKind.PerTargetEntry) { + return shadow.hasTarget(); + } else if (kind == AdviceKind.PerThisEntry) { + // Groovy Constructors have a strange switch statement in them - this switch statement can leave us in places where + // the + // instance is not initialized (a super ctor hasn't been called yet). + // In these situations it isn't safe to do a perObjectBind, the instance is not initialized and cannot be passed + // over. + if (shadow.getEnclosingCodeSignature().getName().equals("<init>")) { + if (world.resolve(shadow.getEnclosingType()).isGroovyObject()) { + return false; + } + } + return shadow.hasThis(); + } else if (kind == AdviceKind.Around) { + if (shadow.getKind() == Shadow.PreInitialization) { + world.showMessage(IMessage.WARNING, WeaverMessages.format(WeaverMessages.AROUND_ON_PREINIT), + getSourceLocation(), shadow.getSourceLocation()); + return false; + } else if (shadow.getKind() == Shadow.Initialization) { + world.showMessage(IMessage.WARNING, WeaverMessages.format(WeaverMessages.AROUND_ON_INIT), getSourceLocation(), + shadow.getSourceLocation()); + return false; + } else if (shadow.getKind() == Shadow.StaticInitialization + && shadow.getEnclosingType().resolve(world).isInterface()) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.AROUND_ON_INTERFACE_STATICINIT, shadow + .getEnclosingType().getName()), getSourceLocation(), shadow.getSourceLocation()); + return false; + } else { + // System.err.println(getSignature().getReturnType() + + // " from " + shadow.getReturnType()); + if (getSignature().getReturnType().equals(UnresolvedType.VOID)) { + if (!shadow.getReturnType().equals(UnresolvedType.VOID)) { + String s = shadow.toString(); + String s2 = WeaverMessages.format(WeaverMessages.NON_VOID_RETURN, s); + world.showMessage(IMessage.ERROR, s2, getSourceLocation(), shadow.getSourceLocation()); + return false; + } + } else if (getSignature().getReturnType().equals(UnresolvedType.OBJECT)) { + return true; + } else { + ResolvedType shadowReturnType = shadow.getReturnType().resolve(world); + ResolvedType adviceReturnType = getSignature().getGenericReturnType().resolve(world); + + if (shadowReturnType.isParameterizedType() && adviceReturnType.isRawType()) { // Set + // < + // Integer + // > + // and + // Set + ResolvedType shadowReturnGenericType = shadowReturnType.getGenericType(); // Set + ResolvedType adviceReturnGenericType = adviceReturnType.getGenericType(); // Set + if (shadowReturnGenericType.isAssignableFrom(adviceReturnGenericType) + && world.getLint().uncheckedAdviceConversion.isEnabled()) { + world.getLint().uncheckedAdviceConversion.signal( + new String[] { shadow.toString(), shadowReturnType.getName(), adviceReturnType.getName() }, + shadow.getSourceLocation(), new ISourceLocation[] { getSourceLocation() }); + } + } else if (!shadowReturnType.isAssignableFrom(adviceReturnType)) { + // System.err.println(this + ", " + sourceContext + + // ", " + start); + world.showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.INCOMPATIBLE_RETURN_TYPE, shadow), getSourceLocation(), + shadow.getSourceLocation()); + return false; + } + } + } + } + return true; + } else { + return false; + } + } + + /** + * In after returning advice if we are binding the extra parameter to a parameterized type we may not be able to do a type-safe + * conversion. + * + * @param resolvedExtraParameterType the type in the after returning declaration + * @param shadowReturnType the type at the shadow + * @param world + */ + private void maybeIssueUncheckedMatchWarning(ResolvedType afterReturningType, ResolvedType shadowReturnType, Shadow shadow, + World world) { + boolean inDoubt = !afterReturningType.isAssignableFrom(shadowReturnType); + if (inDoubt && world.getLint().uncheckedArgument.isEnabled()) { + String uncheckedMatchWith = afterReturningType.getSimpleBaseName(); + if (shadowReturnType.isParameterizedType() && (shadowReturnType.getRawType() == afterReturningType.getRawType())) { + uncheckedMatchWith = shadowReturnType.getSimpleName(); + } + if (!Utils.isSuppressing(getSignature().getAnnotations(), "uncheckedArgument")) { + world.getLint().uncheckedArgument.signal(new String[] { afterReturningType.getSimpleName(), uncheckedMatchWith, + afterReturningType.getSimpleBaseName(), shadow.toResolvedString(world) }, getSourceLocation(), + new ISourceLocation[] { shadow.getSourceLocation() }); + } + } + } + + // ---- + + public AdviceKind getKind() { + return kind; + } + + public Member getSignature() { + return signature; + } + + public boolean hasExtraParameter() { + return (getExtraParameterFlags() & ExtraArgument) != 0; + } + + protected int getExtraParameterFlags() { + return attribute.getExtraParameterFlags(); + } + + protected int getExtraParameterCount() { + return countOnes(getExtraParameterFlags() & ParameterMask); + } + + public UnresolvedType[] getBindingParameterTypes() { + return bindingParameterTypes; + } + + public void setBindingParameterTypes(UnresolvedType[] types) { + bindingParameterTypes = types; + } + + public static int countOnes(int bits) { + int ret = 0; + while (bits != 0) { + if ((bits & 1) != 0) { + ret += 1; + } + bits = bits >> 1; + } + return ret; + } + + public int getBaseParameterCount() { + return getSignature().getParameterTypes().length - getExtraParameterCount(); + } + + public String[] getBaseParameterNames(World world) { + String[] allNames = getSignature().getParameterNames(world); + int extras = getExtraParameterCount(); + if (extras == 0) { + return allNames; + } + String[] result = new String[getBaseParameterCount()]; + for (int i = 0; i < result.length; i++) { + result[i] = allNames[i]; + } + return result; + } + + /** + * Return the type of the 'extra argument'. For either after returning or after throwing advice, the extra argument will be the + * returned value or the thrown exception respectively. With annotation style the user may declare the parameters in any order, + * whereas for code style they are in a well defined order. So there is some extra complexity in here for annotation style that + * looks up the correct parameter in the advice signature by name, based on the name specified in the annotation. If this fails + * then we 'fallback' to guessing at positions, where the extra argument is presumed to come at the end. + * + * @return the type of the extraParameter + */ + public UnresolvedType getExtraParameterType() { + if (!hasExtraParameter()) { + return ResolvedType.MISSING; + } + if (signature instanceof ResolvedMember) { + ResolvedMember method = (ResolvedMember) signature; + UnresolvedType[] parameterTypes = method.getGenericParameterTypes(); + if (getConcreteAspect().isAnnotationStyleAspect()) { + + // Examine the annotation to determine the parameter name then look it up in the parameters for the method + String[] pnames = method.getParameterNames(); + if (pnames != null) { + // It is worth attempting to look up the correct parameter + AnnotationAJ[] annos = getSignature().getAnnotations(); + String parameterToLookup = null; + if (annos != null && (getKind() == AdviceKind.AfterThrowing || getKind() == AdviceKind.AfterReturning)) { + for (int i = 0; i < annos.length && parameterToLookup == null; i++) { + AnnotationAJ anno = annos[i]; + String annosig = anno.getType().getSignature(); + if (annosig.equals("Lorg/aspectj/lang/annotation/AfterThrowing;")) { + // the 'throwing' value in the annotation will name the parameter to bind to + parameterToLookup = anno.getStringFormOfValue("throwing"); + } else if (annosig.equals("Lorg/aspectj/lang/annotation/AfterReturning;")) { + // the 'returning' value in the annotation will name the parameter to bind to + parameterToLookup = anno.getStringFormOfValue("returning"); + } + } + } + if (parameterToLookup != null) { + for (int i = 0; i < pnames.length; i++) { + if (pnames[i].equals(parameterToLookup)) { + return parameterTypes[i]; + } + } + } + } + + // Don't think this code works so well... why isnt it getBaseParameterCount()-1 ? + + int baseParmCnt = getBaseParameterCount(); + + // bug 122742 - if we're an annotation style aspect then one + // of the extra parameters could be JoinPoint which we want + // to ignore + while ((baseParmCnt + 1 < parameterTypes.length) + && (parameterTypes[baseParmCnt].equals(AjcMemberMaker.TYPEX_JOINPOINT) + || parameterTypes[baseParmCnt].equals(AjcMemberMaker.TYPEX_STATICJOINPOINT) || parameterTypes[baseParmCnt] + .equals(AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT))) { + baseParmCnt++; + } + return parameterTypes[baseParmCnt]; + } else { + return parameterTypes[getBaseParameterCount()]; + } + } else { + return signature.getParameterTypes()[getBaseParameterCount()]; + } + } + + public UnresolvedType getDeclaringAspect() { + return getOriginalSignature().getDeclaringType(); + } + + protected Member getOriginalSignature() { + return signature; + } + + protected String extraParametersToString() { + if (getExtraParameterFlags() == 0) { + return ""; + } else { + return "(extraFlags: " + getExtraParameterFlags() + ")"; + } + } + + @Override + public Pointcut getPointcut() { + return pointcut; + } + + // ---- + + /** + * @param fromType is guaranteed to be a non-abstract aspect + * @param clause has been concretized at a higher level + */ + @Override + public ShadowMunger concretize(ResolvedType fromType, World world, PerClause clause) { + // assert !fromType.isAbstract(); + Pointcut p = pointcut.concretize(fromType, getDeclaringType(), signature.getArity(), this); + if (clause != null) { + Pointcut oldP = p; + p = new AndPointcut(clause, p); + p.copyLocationFrom(oldP); + p.state = Pointcut.CONCRETE; + + // FIXME ? ATAJ copy unbound bindings to ignore + p.m_ignoreUnboundBindingForNames = oldP.m_ignoreUnboundBindingForNames; + } + + Advice munger = world.getWeavingSupport().createAdviceMunger(attribute, p, signature, fromType); + munger.bindingParameterTypes = bindingParameterTypes; + munger.setDeclaringType(getDeclaringType()); + // System.err.println("concretizing here " + p + " with clause " + + // clause); + return munger; + } + + // ---- from object + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("(").append(getKind()).append(extraParametersToString()); + sb.append(": ").append(pointcut).append("->").append(signature).append(")"); + return sb.toString(); + // return "(" + // + getKind() + // + extraParametersToString() + // + ": " + // + pointcut + // + "->" + // + signature + // + ")"; + } + + // XXX this perhaps ought to take account of the other fields in advice ... + @Override + public boolean equals(Object other) { + if (!(other instanceof Advice)) { + return false; + } + Advice o = (Advice) other; + return o.kind.equals(kind) && ((o.pointcut == null) ? (pointcut == null) : o.pointcut.equals(pointcut)) + && ((o.signature == null) ? (signature == null) : o.signature.equals(signature)); + // && (AsmManager.getDefault().getHandleProvider().dependsOnLocation() ? ((o.getSourceLocation() == null) ? + // (getSourceLocation() == null) + // : o.getSourceLocation().equals(getSourceLocation())) + // : true) // pr134471 - remove when handles are improved + // // to be independent of location + // ; + + } + + private volatile int hashCode = 0; + + @Override + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37 * result + kind.hashCode(); + result = 37 * result + ((pointcut == null) ? 0 : pointcut.hashCode()); + result = 37 * result + ((signature == null) ? 0 : signature.hashCode()); + hashCode = result; + } + return hashCode; + } + + // ---- fields + + public static final int ExtraArgument = 0x01; + public static final int ThisJoinPoint = 0x02; + public static final int ThisJoinPointStaticPart = 0x04; + public static final int ThisEnclosingJoinPointStaticPart = 0x08; + public static final int ParameterMask = 0x0f; + // For an if pointcut, this indicates it is hard wired to access a constant of either true or false + public static final int ConstantReference = 0x10; + // When the above flag is set, this indicates whether it is true or false + public static final int ConstantValue = 0x20; + // public static final int CanInline = 0x40; // didnt appear to be getting used + public static final int ThisAspectInstance = 0x40; + + // cant use 0x80 ! the value is written out as a byte and -1 has special meaning (-1 is 0x80...) + + // for testing only + public void setLexicalPosition(int lexicalPosition) { + start = lexicalPosition; + } + + public boolean isAnnotationStyle() { + return isAnnotationStyle; + } + + public ResolvedType getConcreteAspect() { + return concreteAspect; + } + + public boolean hasMatchedSomething() { + return hasMatchedAtLeastOnce; + } + + public void setHasMatchedSomething(boolean hasMatchedSomething) { + hasMatchedAtLeastOnce = hasMatchedSomething; + } + + public abstract boolean hasDynamicTests(); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AdviceKind.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AdviceKind.java new file mode 100644 index 000000000..af0646935 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AdviceKind.java @@ -0,0 +1,135 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.IOException; + +import org.aspectj.util.TypeSafeEnum; + +/** + * The five kinds of advice in AspectJ. + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public class AdviceKind extends TypeSafeEnum { + private int precedence; + private boolean isAfter; + private boolean isCflow; + + public AdviceKind(String name, int key, int precedence, boolean isAfter, boolean isCflow) { + super(name, key); + this.precedence = precedence; + this.isAfter = isAfter; + this.isCflow = isCflow; + } + + public static AdviceKind read(VersionedDataInputStream s) throws IOException { + int key = s.readByte(); + switch (key) { + case 1: + return Before; + case 2: + return After; + case 3: + return AfterThrowing; + case 4: + return AfterReturning; + case 5: + return Around; + case 6: + return CflowEntry; + case 7: + return CflowBelowEntry; + + case 8: + return InterInitializer; + + case 9: + return PerCflowEntry; + case 10: + return PerCflowBelowEntry; + case 11: + return PerThisEntry; + case 12: + return PerTargetEntry; + + case 13: + return Softener; + + case 14: + return PerTypeWithinEntry; + } + throw new RuntimeException("unimplemented kind: " + key); + } + + public static final AdviceKind Before = new AdviceKind("before", 1, 0, false, false); + public static final AdviceKind After = new AdviceKind("after", 2, 0, true, false); + public static final AdviceKind AfterThrowing = new AdviceKind("afterThrowing", 3, 0, true, false); + public static final AdviceKind AfterReturning = new AdviceKind("afterReturning", 4, 0, true, false); + public static final AdviceKind Around = new AdviceKind("around", 5, 0, false, false); + + // these kinds can't be declared, but are used by the weaver + public static final AdviceKind CflowEntry = new AdviceKind("cflowEntry", 6, 1, false, true); + public static final AdviceKind CflowBelowEntry = new AdviceKind("cflowBelowEntry", 7, -1, false, true); // XXX resolve + // precednece with the + // below + public static final AdviceKind InterInitializer = new AdviceKind("interInitializer", 8, -2, false, false); + + public static final AdviceKind PerCflowEntry = new AdviceKind("perCflowEntry", 9, 1, false, true); + public static final AdviceKind PerCflowBelowEntry = new AdviceKind("perCflowBelowEntry", 10, -1, false, true); + + public static final AdviceKind PerThisEntry = new AdviceKind("perThisEntry", 11, 1, false, false); + public static final AdviceKind PerTargetEntry = new AdviceKind("perTargetEntry", 12, 1, false, false); + + public static final AdviceKind Softener = new AdviceKind("softener", 13, 1, false, false); + + // PTWIMPL Advice representing when aspect should be initialized + public static final AdviceKind PerTypeWithinEntry = new AdviceKind("perTypeWithinEntry", 14, 1, false, false); + + public static AdviceKind stringToKind(String s) { + if (s.equals(Before.getName())) + return Before; + if (s.equals(After.getName())) + return After; + if (s.equals(AfterThrowing.getName())) + return AfterThrowing; + if (s.equals(AfterReturning.getName())) + return AfterReturning; + if (s.equals(Around.getName())) + return Around; + throw new IllegalArgumentException("unknown kind: " + "\"" + s + "\""); + } + + public boolean isAfter() { + return this.isAfter; + } + + public boolean isCflow() { + return this.isCflow; + } + + public int getPrecedence() { + return precedence; + } + + public boolean isPerEntry() { + return this == PerCflowEntry || this == PerCflowBelowEntry || this == PerThisEntry || this == PerTargetEntry + || this == PerTypeWithinEntry; // PTWIMPL Allow for PTW case + } + + public boolean isPerObjectEntry() { + return this == PerThisEntry || this == PerTargetEntry; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AjAttribute.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AjAttribute.java new file mode 100644 index 000000000..781227141 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AjAttribute.java @@ -0,0 +1,757 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.bridge.Version; +import org.aspectj.util.FileUtil; +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.IScope; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; + +/** + * These attributes are written to and read from .class files (see the JVM spec). + * + * <p> + * Each member or type can have a number of AjAttributes. Each such attribute is in 1-1 correspondence with an Unknown bcel + * attribute. Creating one of these does NOTHING to the underlying thing, so if you really want to add an attribute to a particular + * thing, well, you'd better actually do that. + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public abstract class AjAttribute { + + public static final String AttributePrefix = "org.aspectj.weaver"; + + protected abstract void write(CompressingDataOutputStream s) throws IOException; + + public abstract String getNameString(); + + public char[] getNameChars() { + return getNameString().toCharArray(); + } + + /** + * Just writes the contents + */ + public byte[] getBytes(ConstantPoolWriter compressor) { + try { + ByteArrayOutputStream b0 = new ByteArrayOutputStream(); + CompressingDataOutputStream s0 = new CompressingDataOutputStream(b0, compressor); + write(s0); + s0.close(); + return b0.toByteArray(); + } catch (IOException e) { + // shouldn't happen with ByteArrayOutputStreams + throw new RuntimeException("sanity check"); + } + } + + /** + * Writes the full attribute, i.e. name_index, length, and contents + * + * @param constantPool + */ + public byte[] getAllBytes(short nameIndex, ConstantPoolWriter dataCompressor) { + try { + byte[] bytes = getBytes(dataCompressor); + + ByteArrayOutputStream b0 = new ByteArrayOutputStream(); + DataOutputStream s0 = new DataOutputStream(b0); + + s0.writeShort(nameIndex); + s0.writeInt(bytes.length); + s0.write(bytes); + s0.close(); + return b0.toByteArray(); + } catch (IOException e) { + // shouldn't happen with ByteArrayOutputStreams + throw new RuntimeException("sanity check"); + } + } + + public static AjAttribute read(AjAttribute.WeaverVersionInfo v, String name, byte[] bytes, ISourceContext context, World w, + ConstantPoolReader dataDecompressor) { + try { + if (bytes == null) { + bytes = new byte[0]; + } + + VersionedDataInputStream s = new VersionedDataInputStream(new ByteArrayInputStream(bytes), dataDecompressor); + s.setVersion(v); + if (name.equals(Aspect.AttributeName)) { + return new Aspect(PerClause.readPerClause(s, context)); + } else if (name.equals(MethodDeclarationLineNumberAttribute.AttributeName)) { + return MethodDeclarationLineNumberAttribute.read(s); + } else if (name.equals(WeaverState.AttributeName)) { + return new WeaverState(WeaverStateInfo.read(s, context)); + } else if (name.equals(WeaverVersionInfo.AttributeName)) { + return WeaverVersionInfo.read(s); + } else if (name.equals(AdviceAttribute.AttributeName)) { + AdviceAttribute aa = AdviceAttribute.read(s, context); + aa.getPointcut().check(context, w); + return aa; + } else if (name.equals(PointcutDeclarationAttribute.AttributeName)) { + PointcutDeclarationAttribute pda = new PointcutDeclarationAttribute(ResolvedPointcutDefinition.read(s, context)); + pda.pointcutDef.getPointcut().check(context, w); + return pda; + } else if (name.equals(TypeMunger.AttributeName)) { + return new TypeMunger(ResolvedTypeMunger.read(s, context)); + } else if (name.equals(AjSynthetic.AttributeName)) { + return new AjSynthetic(); + } else if (name.equals(DeclareAttribute.AttributeName)) { + return new DeclareAttribute(Declare.read(s, context)); + } else if (name.equals(PrivilegedAttribute.AttributeName)) { + return PrivilegedAttribute.read(s, context); + } else if (name.equals(SourceContextAttribute.AttributeName)) { + return SourceContextAttribute.read(s); + } else if (name.equals(EffectiveSignatureAttribute.AttributeName)) { + return EffectiveSignatureAttribute.read(s, context); + } else { + // We have to tell the user about this... + if (w == null || w.getMessageHandler() == null) { + throw new BCException("unknown attribute" + name); + } + w.getMessageHandler().handleMessage(MessageUtil.warn("unknown attribute encountered " + name)); + return null; + } + } catch (BCException e) { + throw new BCException("malformed " + name + " attribute (length:" + bytes.length + ")" + e); + } catch (IOException e) { + throw new BCException("malformed " + name + " attribute (length:" + bytes.length + ")" + e); + } + } + + // ---- + + /** + * Synthetic members should have NO advice put on them or on their contents. This attribute is currently unused as we consider + * all members starting with NameMangler.PREFIX to automatically be synthetic. As we use this we might find that we want + * multiple kinds of synthetic. In particular, if we want to treat the call to a synthetic getter (say, of an introduced field) + * as a field reference itself, then a method might want a particular kind of AjSynthetic attribute that also includes a + * signature of what it stands for. + */ + public static class AjSynthetic extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.AjSynthetic"; + + @Override + public String getNameString() { + return AttributeName; + } + + // private ResolvedTypeMunger munger; + public AjSynthetic() { + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + } + } + + public static class TypeMunger extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.TypeMunger"; + + @Override + public String getNameString() { + return AttributeName; + } + + private final ResolvedTypeMunger munger; + + public TypeMunger(ResolvedTypeMunger munger) { + this.munger = munger; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + munger.write(s); + } + + public ConcreteTypeMunger reify(World world, ResolvedType aspectType) { + return world.getWeavingSupport().concreteTypeMunger(munger, aspectType); + } + } + + public static class WeaverState extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.WeaverState"; + + @Override + public String getNameString() { + return AttributeName; + } + + private final WeaverStateInfo kind; + + public WeaverState(WeaverStateInfo kind) { + this.kind = kind; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + kind.write(s); + } + + public WeaverStateInfo reify() { + return kind; + } + } + + public static class WeaverVersionInfo extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.WeaverVersion"; + + // If you change the format of an AspectJ class file, you have two + // options: + // - changing the minor version means you have not added anything that + // prevents + // previous versions of the weaver from operating (e.g. + // MethodDeclarationLineNumber attribute) + // - changing the major version means you have added something that + // prevents previous + // versions of the weaver from operating correctly. + // + // The user will get a warning for any org.aspectj.weaver attributes the + // weaver does + // not recognize. + + // When we don't know ... (i.e. pre 1.2.1) + public final static short WEAVER_VERSION_MAJOR_UNKNOWN = 0; + public final static short WEAVER_VERSION_MINOR_UNKNOWN = 0; + + // These are the weaver major/minor numbers for AspectJ 1.2.1 + public final static short WEAVER_VERSION_MAJOR_AJ121 = 1; + public final static short WEAVER_VERSION_MINOR_AJ121 = 0; + + // These are the weaver major/minor numbers for AspectJ 1.5.0 + public final static short WEAVER_VERSION_MAJOR_AJ150M4 = 3; + public final static short WEAVER_VERSION_MAJOR_AJ150 = 2; + public final static short WEAVER_VERSION_MINOR_AJ150 = 0; + + // These are the weaver major/minor numbers for AspectJ 1.6.0 + public final static short WEAVER_VERSION_MAJOR_AJ160M2 = 5; + public final static short WEAVER_VERSION_MAJOR_AJ160 = 4; + public final static short WEAVER_VERSION_MINOR_AJ160 = 0; + + // These are the weaver major/minor numbers for AspectJ 1.6.1 + // added annotation value binding + public final static short WEAVER_VERSION_MAJOR_AJ161 = 6; + public final static short WEAVER_VERSION_MINOR_AJ161 = 0; + + // 1.6.9 adds new style ITDs. This is used to see what version of AJ was used to + // build the ITDs so we know id the generated get/set dispatchers are using old + // or new style (new style will be get/setters for private ITD fields) + public final static short WEAVER_VERSION_AJ169 = 7; + + // These are the weaver major/minor versions for *this* weaver + private final static short CURRENT_VERSION_MAJOR = WEAVER_VERSION_AJ169; + private final static short CURRENT_VERSION_MINOR = 0; + + public final static WeaverVersionInfo UNKNOWN = new WeaverVersionInfo(WEAVER_VERSION_MAJOR_UNKNOWN, + WEAVER_VERSION_MINOR_UNKNOWN); + public final static WeaverVersionInfo CURRENT = new WeaverVersionInfo(CURRENT_VERSION_MAJOR, CURRENT_VERSION_MINOR); + + // These are the versions read in from a particular class file. + private final short major_version; + private final short minor_version; + + private long buildstamp = Version.NOTIME; + + @Override + public String getNameString() { + return AttributeName; + } + + // Default ctor uses the current version numbers + public WeaverVersionInfo() { + major_version = CURRENT_VERSION_MAJOR; + minor_version = CURRENT_VERSION_MINOR; + } + + public WeaverVersionInfo(short major, short minor) { + major_version = major; + minor_version = minor; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeShort(CURRENT_VERSION_MAJOR); + s.writeShort(CURRENT_VERSION_MINOR); + s.writeLong(Version.getTime()); // build used to construct the + // class... + } + + public static WeaverVersionInfo read(VersionedDataInputStream s) throws IOException { + short major = s.readShort(); + short minor = s.readShort(); + WeaverVersionInfo wvi = new WeaverVersionInfo(major, minor); + if (s.getMajorVersion() >= WEAVER_VERSION_MAJOR_AJ150M4) { + long stamp = 0; + try { + stamp = s.readLong(); + wvi.setBuildstamp(stamp); + } catch (EOFException eof) { + // didnt find that build stamp - its not the end of the + // world + } + } + return wvi; + } + + public short getMajorVersion() { + return major_version; + } + + public short getMinorVersion() { + return minor_version; + } + + public static short getCurrentWeaverMajorVersion() { + return CURRENT_VERSION_MAJOR; + } + + public static short getCurrentWeaverMinorVersion() { + return CURRENT_VERSION_MINOR; + } + + public void setBuildstamp(long stamp) { + buildstamp = stamp; + } + + public long getBuildstamp() { + return buildstamp; + } + + @Override + public String toString() { + return major_version + "." + minor_version; + } + + public static String toCurrentVersionString() { + return CURRENT_VERSION_MAJOR + "." + CURRENT_VERSION_MINOR; + } + + } + + public static class SourceContextAttribute extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.SourceContext"; + + @Override + public String getNameString() { + return AttributeName; + } + + private final String sourceFileName; + private final int[] lineBreaks; + + public SourceContextAttribute(String sourceFileName, int[] lineBreaks) { + this.sourceFileName = sourceFileName; + this.lineBreaks = lineBreaks; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + if (s.canCompress()) { + s.writeCompressedPath(sourceFileName); + } else { + s.writeUTF(sourceFileName); + } + s.writeInt(lineBreaks.length); + int previous = 0; + for (int i = 0, max = lineBreaks.length; i < max; i++) { + s.writeShort(lineBreaks[i] - previous); + previous = lineBreaks[i]; + } + } + + public static SourceContextAttribute read(VersionedDataInputStream s) throws IOException { + String sourceFileName = s.isAtLeast169() ? s.readPath() : s.readUTF(); + int lineBreaks = s.readInt(); + int[] lines = new int[lineBreaks]; + int previous = 0; + for (int i = 0; i < lineBreaks; i++) { + if (s.isAtLeast169()) { + lines[i] = s.readShort() + previous; + previous = lines[i]; + } else { + lines[i] = s.readInt(); + } + } + return new SourceContextAttribute(sourceFileName, lines); + } + + public int[] getLineBreaks() { + return lineBreaks; + } + + public String getSourceFileName() { + return sourceFileName; + } + } + + public static class MethodDeclarationLineNumberAttribute extends AjAttribute { + + public static final String AttributeName = "org.aspectj.weaver.MethodDeclarationLineNumber"; + + @Override + public String getNameString() { + return AttributeName; + } + + private final int lineNumber; + + // AV: added in 1.5 M3 thus handling cases where we don't have that + // information + private final int offset; + + public MethodDeclarationLineNumberAttribute(int line, int offset) { + lineNumber = line; + this.offset = offset; + } + + public int getLineNumber() { + return lineNumber; + } + + public int getOffset() { + return offset; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeInt(lineNumber); + s.writeInt(offset); + } + + public static MethodDeclarationLineNumberAttribute read(VersionedDataInputStream s) throws IOException { + int line = s.readInt(); + int offset = 0; + if (s.available() > 0) { + offset = s.readInt(); + } + return new MethodDeclarationLineNumberAttribute(line, offset); + } + + @Override + public String toString() { + return AttributeName + ": " + lineNumber + ":" + offset; + } + } + + public static class PointcutDeclarationAttribute extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.PointcutDeclaration"; + + @Override + public String getNameString() { + return AttributeName; + } + + private final ResolvedPointcutDefinition pointcutDef; + + public PointcutDeclarationAttribute(ResolvedPointcutDefinition pointcutDef) { + this.pointcutDef = pointcutDef; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + pointcutDef.write(s); + } + + public ResolvedPointcutDefinition reify() { + return pointcutDef; + } + } + + public static class DeclareAttribute extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.Declare"; + + @Override + public String getNameString() { + return AttributeName; + } + + private final Declare declare; + + public DeclareAttribute(Declare declare) { + this.declare = declare; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + declare.write(s); + } + + public Declare getDeclare() { + return declare; + } + } + + public static class AdviceAttribute extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.Advice"; + + @Override + public String getNameString() { + return AttributeName; + } + + private final AdviceKind kind; + private final Pointcut pointcut; + private final int extraParameterFlags; + private final int start; + private final int end; + private final ISourceContext sourceContext; + + // these are only used by around advice + private boolean proceedInInners; + private ResolvedMember[] proceedCallSignatures; // size == # of proceed + // calls in body + private boolean[] formalsUnchangedToProceed; // size == formals.size + private UnresolvedType[] declaredExceptions; + + /** + * @param lexicalPosition must be greater than the lexicalPosition of any advice declared before this one in an aspect, + * otherwise, it can be any value. + */ + public AdviceAttribute(AdviceKind kind, Pointcut pointcut, int extraArgumentFlags, int start, int end, + ISourceContext sourceContext) { + this.kind = kind; + this.pointcut = pointcut; + extraParameterFlags = extraArgumentFlags; + this.start = start; + this.end = end; + this.sourceContext = sourceContext; + + // XXX put this back when testing works better (or fails better) + // if (kind == AdviceKind.Around) throw new + // IllegalArgumentException("not for around"); + } + + public AdviceAttribute(AdviceKind kind, Pointcut pointcut, int extraArgumentFlags, int start, int end, + ISourceContext sourceContext, boolean proceedInInners, ResolvedMember[] proceedCallSignatures, + boolean[] formalsUnchangedToProceed, UnresolvedType[] declaredExceptions) { + this.kind = kind; + this.pointcut = pointcut; + extraParameterFlags = extraArgumentFlags; + this.start = start; + this.end = end; + this.sourceContext = sourceContext; + + if (kind != AdviceKind.Around) { + throw new IllegalArgumentException("only for around"); + } + + this.proceedInInners = proceedInInners; + this.proceedCallSignatures = proceedCallSignatures; + this.formalsUnchangedToProceed = formalsUnchangedToProceed; + this.declaredExceptions = declaredExceptions; + } + + public static AdviceAttribute read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AdviceKind kind = AdviceKind.read(s); + if (kind == AdviceKind.Around) { + return new AdviceAttribute(kind, Pointcut.read(s, context), s.readByte(), s.readInt(), s.readInt(), context, + s.readBoolean(), ResolvedMemberImpl.readResolvedMemberArray(s, context), FileUtil.readBooleanArray(s), + UnresolvedType.readArray(s)); + } else { + return new AdviceAttribute(kind, Pointcut.read(s, context), s.readByte(), s.readInt(), s.readInt(), context); + } + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + kind.write(s); + pointcut.write(s); + s.writeByte(extraParameterFlags); + s.writeInt(start); + s.writeInt(end); + + if (kind == AdviceKind.Around) { + s.writeBoolean(proceedInInners); + ResolvedMemberImpl.writeArray(proceedCallSignatures, s); + FileUtil.writeBooleanArray(formalsUnchangedToProceed, s); + UnresolvedType.writeArray(declaredExceptions, s); + } + } + + public Advice reify(Member signature, World world, ResolvedType concreteAspect) { + return world.getWeavingSupport().createAdviceMunger(this, pointcut, signature, concreteAspect); + } + + @Override + public String toString() { + return "AdviceAttribute(" + kind + ", " + pointcut + ", " + extraParameterFlags + ", " + start + ")"; + } + + public int getExtraParameterFlags() { + return extraParameterFlags; + } + + public AdviceKind getKind() { + return kind; + } + + public Pointcut getPointcut() { + return pointcut; + } + + public UnresolvedType[] getDeclaredExceptions() { + return declaredExceptions; + } + + public boolean[] getFormalsUnchangedToProceed() { + return formalsUnchangedToProceed; + } + + public ResolvedMember[] getProceedCallSignatures() { + return proceedCallSignatures; + } + + public boolean isProceedInInners() { + return proceedInInners; + } + + public int getEnd() { + return end; + } + + public ISourceContext getSourceContext() { + return sourceContext; + } + + public int getStart() { + return start; + } + + } + + public static class Aspect extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.Aspect"; + + @Override + public String getNameString() { + return AttributeName; + } + + private final PerClause perClause; + private IScope resolutionScope; + + public Aspect(PerClause perClause) { + this.perClause = perClause; + } + + public PerClause reify(ResolvedType inAspect) { + // XXXperClause.concretize(inAspect); + return perClause; + } + + public PerClause reifyFromAtAspectJ(ResolvedType inAspect) { + perClause.resolve(resolutionScope); + return perClause; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + perClause.write(s); + } + + public void setResolutionScope(IScope binding) { + resolutionScope = binding; + } + } + + public static class PrivilegedAttribute extends AjAttribute { + + public static final String AttributeName = "org.aspectj.weaver.Privileged"; + + private final ResolvedMember[] accessedMembers; + + public PrivilegedAttribute(ResolvedMember[] accessedMembers) { + this.accessedMembers = accessedMembers; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + ResolvedMemberImpl.writeArray(accessedMembers, s); + } + + public ResolvedMember[] getAccessedMembers() { + return accessedMembers; + } + + public static PrivilegedAttribute read(VersionedDataInputStream stream, ISourceContext context) throws IOException { + PrivilegedAttribute pa = new PrivilegedAttribute(ResolvedMemberImpl.readResolvedMemberArray(stream, context)); + return pa; + } + + @Override + public String getNameString() { + return AttributeName; + } + } + + public static class EffectiveSignatureAttribute extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.EffectiveSignature"; + + @Override + public String getNameString() { + return AttributeName; + } + + private final ResolvedMember effectiveSignature; + private final Shadow.Kind shadowKind; + private final boolean weaveBody; + + public EffectiveSignatureAttribute(ResolvedMember effectiveSignature, Shadow.Kind shadowKind, boolean weaveBody) { + this.effectiveSignature = effectiveSignature; + this.shadowKind = shadowKind; + this.weaveBody = weaveBody; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + effectiveSignature.write(s); + shadowKind.write(s); + s.writeBoolean(weaveBody); + } + + public static EffectiveSignatureAttribute read(VersionedDataInputStream s, ISourceContext context) throws IOException { + ResolvedMember member = ResolvedMemberImpl.readResolvedMember(s, context); + return new EffectiveSignatureAttribute(member, Shadow.Kind.read(s), s.readBoolean()); + } + + public ResolvedMember getEffectiveSignature() { + return effectiveSignature; + } + + @Override + public String toString() { + return "EffectiveSignatureAttribute(" + effectiveSignature + ", " + shadowKind + ")"; + } + + public Shadow.Kind getShadowKind() { + return shadowKind; + } + + public boolean isWeaveBody() { + return weaveBody; + } + + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AjcMemberMaker.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AjcMemberMaker.java new file mode 100644 index 000000000..1ba711baa --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AjcMemberMaker.java @@ -0,0 +1,698 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import java.lang.reflect.Modifier; + +/** + * The AjcMemberMaker is responsible for creating the representations of methods/fields/etc that are placed in both aspects and + * affected target types. It uses the NameMangler class to create the actual names that will be used. + */ +public class AjcMemberMaker { + + private static final int PUBLIC_STATIC_FINAL = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; + + private static final int PRIVATE_STATIC = Modifier.PRIVATE | Modifier.STATIC; + + private static final int PUBLIC_STATIC = Modifier.PUBLIC | Modifier.STATIC; + + private static final int BRIDGE = 0x0040; + + private static final int VISIBILITY = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED; + + public static final UnresolvedType CFLOW_STACK_TYPE = UnresolvedType.forName(NameMangler.CFLOW_STACK_TYPE); + + public static final UnresolvedType AROUND_CLOSURE_TYPE = UnresolvedType + .forSignature("Lorg/aspectj/runtime/internal/AroundClosure;"); + + public static final UnresolvedType CONVERSIONS_TYPE = UnresolvedType.forSignature("Lorg/aspectj/runtime/internal/Conversions;"); + + public static final UnresolvedType NO_ASPECT_BOUND_EXCEPTION = UnresolvedType + .forSignature("Lorg/aspectj/lang/NoAspectBoundException;"); + + public static ResolvedMember ajcPreClinitMethod(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PRIVATE_STATIC, NameMangler.AJC_PRE_CLINIT_NAME, "()V"); + } + + public static ResolvedMember ajcPostClinitMethod(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PRIVATE_STATIC, NameMangler.AJC_POST_CLINIT_NAME, "()V"); + } + + public static Member noAspectBoundExceptionInit() { + return new ResolvedMemberImpl(Member.METHOD, NO_ASPECT_BOUND_EXCEPTION, Modifier.PUBLIC, "<init>", "()V"); + } + + public static Member noAspectBoundExceptionInit2() { + return new ResolvedMemberImpl(Member.METHOD, NO_ASPECT_BOUND_EXCEPTION, Modifier.PUBLIC, "<init>", + "(Ljava/lang/String;Ljava/lang/Throwable;)V"); + } + + public static Member noAspectBoundExceptionInitWithCause() { + return new ResolvedMemberImpl(Member.METHOD, NO_ASPECT_BOUND_EXCEPTION, Modifier.PUBLIC, "<init>", + "(Ljava/lang/String;Ljava/lang/Throwable;)V"); + } + + public static ResolvedMember perCflowPush(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, NameMangler.PERCFLOW_PUSH_METHOD, "()V"); + } + + public static ResolvedMember perCflowField(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.FIELD, declaringType, PUBLIC_STATIC, NameMangler.PERCFLOW_FIELD_NAME, + CFLOW_STACK_TYPE.getSignature()); + } + + public static ResolvedMember perSingletonField(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.FIELD, declaringType, PUBLIC_STATIC, NameMangler.PERSINGLETON_FIELD_NAME, + declaringType.getSignature()); + } + + public static ResolvedMember initFailureCauseField(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.FIELD, declaringType, PRIVATE_STATIC, NameMangler.INITFAILURECAUSE_FIELD_NAME, + UnresolvedType.THROWABLE.getSignature()); + } + + public static ResolvedMember perObjectField(UnresolvedType declaringType, ResolvedType aspectType) { + int modifiers = Modifier.PRIVATE; + if (!UnresolvedType.SERIALIZABLE.resolve(aspectType.getWorld()).isAssignableFrom(aspectType)) { + modifiers |= Modifier.TRANSIENT; + } + return new ResolvedMemberImpl(Member.FIELD, declaringType, modifiers, aspectType, + NameMangler.perObjectInterfaceField(aspectType), UnresolvedType.NONE); + } + + // PTWIMPL ResolvedMember for aspect instance field, declared in matched type + public static ResolvedMember perTypeWithinField(UnresolvedType declaringType, ResolvedType aspectType) { + int modifiers = Modifier.PRIVATE | Modifier.STATIC; + if (!isSerializableAspect(aspectType)) { + modifiers |= Modifier.TRANSIENT; + } + return new ResolvedMemberImpl(Member.FIELD, declaringType, modifiers, aspectType, + NameMangler.perTypeWithinFieldForTarget(aspectType), UnresolvedType.NONE); + } + + // PTWIMPL ResolvedMember for type instance field, declared in aspect + // (holds typename for which aspect instance exists) + public static ResolvedMember perTypeWithinWithinTypeField(UnresolvedType declaringType, ResolvedType aspectType) { + int modifiers = Modifier.PRIVATE; + if (!isSerializableAspect(aspectType)) { + modifiers |= Modifier.TRANSIENT; + } + return new ResolvedMemberImpl(Member.FIELD, declaringType, modifiers, UnresolvedType.JL_STRING, + NameMangler.PERTYPEWITHIN_WITHINTYPEFIELD, UnresolvedType.NONE); + } + + private static boolean isSerializableAspect(ResolvedType aspectType) { + return UnresolvedType.SERIALIZABLE.resolve(aspectType.getWorld()).isAssignableFrom(aspectType); + } + + public static ResolvedMember perObjectBind(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC | Modifier.SYNCHRONIZED, NameMangler.PEROBJECT_BIND_METHOD, + "(Ljava/lang/Object;)V"); + } + + // PTWIMPL ResolvedMember for getInstance() method, declared in aspect + public static ResolvedMember perTypeWithinGetInstance(UnresolvedType declaringType) { + // private static a.X ajc$getInstance(java.lang.Class) + ResolvedMemberImpl rm = new ResolvedMemberImpl(Member.METHOD, declaringType, PRIVATE_STATIC, declaringType, // return value + NameMangler.PERTYPEWITHIN_GETINSTANCE_METHOD, new UnresolvedType[] { UnresolvedType.JL_CLASS }); + return rm; + } + + // PTWIMPL ResolvedMember for getWithinTypeName() method + public static ResolvedMember perTypeWithinGetWithinTypeNameMethod(UnresolvedType declaringType, boolean inJava5Mode) { + // public String getWithinTypeName() + ResolvedMemberImpl rm = new ResolvedMemberImpl(Member.METHOD, declaringType, Modifier.PUBLIC, UnresolvedType.JL_STRING, // return + // value + NameMangler.PERTYPEWITHIN_GETWITHINTYPENAME_METHOD, UnresolvedType.NONE); + return rm; + } + + public static ResolvedMember perTypeWithinCreateAspectInstance(UnresolvedType declaringType) { + // public static a.X ajc$createAspectInstance(java.lang.String) + ResolvedMemberImpl rm = new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, declaringType, // return value + NameMangler.PERTYPEWITHIN_CREATEASPECTINSTANCE_METHOD, + new UnresolvedType[] { UnresolvedType.forSignature("Ljava/lang/String;") }, new UnresolvedType[] {}); + return rm; + } + + public static UnresolvedType perObjectInterfaceType(UnresolvedType aspectType) { + return UnresolvedType.forName(aspectType.getName() + "$ajcMightHaveAspect"); + } + + public static ResolvedMember perObjectInterfaceGet(UnresolvedType aspectType) { + return new ResolvedMemberImpl(Member.METHOD, perObjectInterfaceType(aspectType), Modifier.PUBLIC | Modifier.ABSTRACT, + NameMangler.perObjectInterfaceGet(aspectType), "()" + aspectType.getSignature()); + } + + public static ResolvedMember perObjectInterfaceSet(UnresolvedType aspectType) { + return new ResolvedMemberImpl(Member.METHOD, perObjectInterfaceType(aspectType), Modifier.PUBLIC | Modifier.ABSTRACT, + NameMangler.perObjectInterfaceSet(aspectType), "(" + aspectType.getSignature() + ")V"); + } + + // PTWIMPL ResolvedMember for localAspectOf() method, declared in matched type + public static ResolvedMember perTypeWithinLocalAspectOf(UnresolvedType shadowType, UnresolvedType aspectType) { + return new ResolvedMemberImpl(Member.METHOD, shadowType,// perTypeWithinInterfaceType(aspectType), + Modifier.PUBLIC | Modifier.STATIC, NameMangler.perTypeWithinLocalAspectOf(aspectType), "()" + + aspectType.getSignature()); + } + + public static ResolvedMember perSingletonAspectOfMethod(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, "aspectOf", "()" + declaringType.getSignature()); + } + + public static ResolvedMember perSingletonHasAspectMethod(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, "hasAspect", "()Z"); + } + + public static ResolvedMember perCflowAspectOfMethod(UnresolvedType declaringType) { + return perSingletonAspectOfMethod(declaringType); + } + + public static ResolvedMember perCflowHasAspectMethod(UnresolvedType declaringType) { + return perSingletonHasAspectMethod(declaringType); + } + + public static ResolvedMember perObjectAspectOfMethod(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, "aspectOf", "(Ljava/lang/Object;)" + + declaringType.getSignature()); + } + + public static ResolvedMember perObjectHasAspectMethod(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, "hasAspect", "(Ljava/lang/Object;)Z"); + } + + // PTWIMPL ResolvedMember for aspectOf(), declared in aspect + public static ResolvedMember perTypeWithinAspectOfMethod(UnresolvedType declaringType, boolean inJava5Mode) { + UnresolvedType parameterType = null; + if (inJava5Mode) { + parameterType = UnresolvedType.forRawTypeName("java.lang.Class"); + } else { + parameterType = UnresolvedType.forSignature("Ljava/lang/Class;"); + } + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, declaringType, "aspectOf", + new UnresolvedType[] { parameterType }); + // return new ResolvedMemberImpl(Member.METHOD, + // declaringType, PUBLIC_STATIC, "aspectOf", + // "(Ljava/lang/Class;)" + declaringType.getSignature()); + } + + /* + * public static ResolvedMember perTypeWithinGetWithinTypeMethod(UnresolvedType declaringType, boolean inJava5Mode) { + * UnresolvedType returnType = null; if (inJava5Mode) { returnType = UnresolvedType.forRawTypeName("java.lang.Class"); } else { + * returnType = UnresolvedType.forSignature("Ljava/lang/Class;"); } return new + * ResolvedMemberImpl(Member.METHOD,declaringType,Modifier.PUBLIC,ResolvedType.JAVA_LANG_STRING,"getWithinType",new + * UnresolvedType[]{}); } + */ + + // PTWIMPL ResolvedMember for hasAspect(), declared in aspect + public static ResolvedMember perTypeWithinHasAspectMethod(UnresolvedType declaringType, boolean inJava5Mode) { + UnresolvedType parameterType = null; + if (inJava5Mode) { + parameterType = UnresolvedType.forRawTypeName("java.lang.Class"); + } else { + parameterType = UnresolvedType.forSignature("Ljava/lang/Class;"); + } + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, UnresolvedType.BOOLEAN, "hasAspect", + new UnresolvedType[] { parameterType }); + // return new ResolvedMemberImpl(Member.METHOD, + // declaringType, PUBLIC_STATIC, "hasAspect", + // "(Ljava/lang/Class;)Z"); + } + + // -- privileged accessors + + public static ResolvedMember privilegedAccessMethodForMethod(UnresolvedType aspectType, ResolvedMember method) { + return new ResolvedMemberImpl(Member.METHOD, method.getDeclaringType(), Modifier.PUBLIC + | (Modifier.isStatic(method.getModifiers()) ? Modifier.STATIC : 0), method.getReturnType(), + NameMangler.privilegedAccessMethodForMethod(method.getName(), method.getDeclaringType(), aspectType), + method.getParameterTypes(), method.getExceptions()); + } + + /** + * Return a resolvedmember representing the synthetic getter for the field. The old style (<1.6.9) is a heavyweight static + * method with a long name. The new style (1.6.9 and later) is short, and reusable across aspects. + * + * @param aspectType the aspect attempting the access + * @param field the field to be accessed + * @param shortSyntax is the old (long) or new (short) style format being used + * @return a resolvedmember representing the synthetic getter + */ + public static ResolvedMember privilegedAccessMethodForFieldGet(UnresolvedType aspectType, Member field, boolean shortSyntax) { + UnresolvedType fieldDeclaringType = field.getDeclaringType(); + if (shortSyntax) { + UnresolvedType[] args = null; + if (Modifier.isStatic(field.getModifiers())) { + args = ResolvedType.NONE; + } else { + args = new UnresolvedType[] { fieldDeclaringType }; + } + StringBuffer name = new StringBuffer("ajc$get$"); + name.append(field.getName()); + return new ResolvedMemberImpl(Member.METHOD, fieldDeclaringType, PUBLIC_STATIC, field.getReturnType(), name.toString(), + args); + } else { + String getterName = NameMangler.privilegedAccessMethodForFieldGet(field.getName(), fieldDeclaringType, aspectType); + String sig; + if (Modifier.isStatic(field.getModifiers())) { + sig = "()" + field.getReturnType().getSignature(); + } else { + sig = "(" + fieldDeclaringType.getSignature() + ")" + field.getReturnType().getSignature(); + } + return new ResolvedMemberImpl(Member.METHOD, fieldDeclaringType, PUBLIC_STATIC, getterName, sig); + } + } + + /** + * Return a resolvedmember representing the synthetic setter for the field. The old style (<1.6.9) is a heavyweight static + * method with a long name. The new style (1.6.9 and later) is short, not always static, and reusable across aspects. + * + * @param aspectType the aspect attempting the access + * @param field the field to be accessed + * @param shortSyntax is the old or new style format being used + * @return a resolvedmember representing the synthetic setter + */ + public static ResolvedMember privilegedAccessMethodForFieldSet(UnresolvedType aspectType, Member field, boolean shortSyntax) { + UnresolvedType fieldDeclaringType = field.getDeclaringType(); + if (shortSyntax) { + UnresolvedType[] args = null; + if (Modifier.isStatic(field.getModifiers())) { + args = new UnresolvedType[] { field.getType() }; + } else { + args = new UnresolvedType[] { fieldDeclaringType, field.getType() }; + } + StringBuffer name = new StringBuffer("ajc$set$"); + name.append(field.getName()); + return new ResolvedMemberImpl(Member.METHOD, fieldDeclaringType, PUBLIC_STATIC, UnresolvedType.VOID, name.toString(), + args); + } else { + String setterName = NameMangler.privilegedAccessMethodForFieldSet(field.getName(), fieldDeclaringType, aspectType); + String sig; + if (Modifier.isStatic(field.getModifiers())) { + sig = "(" + field.getReturnType().getSignature() + ")V"; + } else { + sig = "(" + fieldDeclaringType.getSignature() + field.getReturnType().getSignature() + ")V"; + } + return new ResolvedMemberImpl(Member.METHOD, fieldDeclaringType, PUBLIC_STATIC, setterName, sig); + } + } + + // --- inline accessors + // ??? can eclipse handle a transform this weird without putting synthetics into the mix + public static ResolvedMember superAccessMethod(UnresolvedType baseType, ResolvedMember method) { + UnresolvedType[] paramTypes = method.getParameterTypes(); + // if (!method.isStatic()) { + // paramTypes = UnresolvedType.insert(method.getDeclaringType(), paramTypes); + // } + return new ResolvedMemberImpl(Member.METHOD, baseType, Modifier.PUBLIC, method.getReturnType(), + NameMangler.superDispatchMethod(baseType, method.getName()), paramTypes, method.getExceptions()); + } + + public static ResolvedMember inlineAccessMethodForMethod(UnresolvedType aspectType, ResolvedMember method) { + UnresolvedType[] paramTypes = method.getParameterTypes(); + if (!Modifier.isStatic(method.getModifiers())) { + paramTypes = UnresolvedType.insert(method.getDeclaringType(), paramTypes); + } + return new ResolvedMemberImpl(Member.METHOD, aspectType, + PUBLIC_STATIC, // ??? what about privileged and super access + // ???Modifier.PUBLIC | (method.isStatic() ? Modifier.STATIC : 0), + method.getReturnType(), + + NameMangler.inlineAccessMethodForMethod(method.getName(), method.getDeclaringType(), aspectType), paramTypes, + method.getExceptions()); + } + + public static ResolvedMember inlineAccessMethodForFieldGet(UnresolvedType aspectType, Member field) { + String sig; + if (Modifier.isStatic(field.getModifiers())) { + sig = "()" + field.getReturnType().getSignature(); + } else { + sig = "(" + field.getDeclaringType().getSignature() + ")" + field.getReturnType().getSignature(); + } + + return new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC, // Modifier.PUBLIC | (field.isStatic() ? + // Modifier.STATIC : 0), + NameMangler.inlineAccessMethodForFieldGet(field.getName(), field.getDeclaringType(), aspectType), sig); + } + + public static ResolvedMember inlineAccessMethodForFieldSet(UnresolvedType aspectType, Member field) { + String sig; + if (Modifier.isStatic(field.getModifiers())) { + sig = "(" + field.getReturnType().getSignature() + ")V"; + } else { + sig = "(" + field.getDeclaringType().getSignature() + field.getReturnType().getSignature() + ")V"; + } + + return new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC, // Modifier.PUBLIC | (field.isStatic() ? + // Modifier.STATIC : 0), + NameMangler.inlineAccessMethodForFieldSet(field.getName(), field.getDeclaringType(), aspectType), sig); + } + + // --- runtimeLibrary api stuff + + public static Member cflowStackPeekInstance() { + return new MemberImpl(Member.METHOD, CFLOW_STACK_TYPE, 0, "peekInstance", "()Ljava/lang/Object;"); + } + + public static Member cflowStackPushInstance() { + return new MemberImpl(Member.METHOD, CFLOW_STACK_TYPE, 0, "pushInstance", "(Ljava/lang/Object;)V"); + } + + public static Member cflowStackIsValid() { + return new MemberImpl(Member.METHOD, CFLOW_STACK_TYPE, 0, "isValid", "()Z"); + } + + public static Member cflowStackInit() { + return new MemberImpl(Member.CONSTRUCTOR, CFLOW_STACK_TYPE, 0, "<init>", "()V"); + } + + public static Member aroundClosurePreInitializationField() { + return new MemberImpl(Member.FIELD, AROUND_CLOSURE_TYPE, 0, "preInitializationState", "[Ljava/lang/Object;"); + } + + public static Member aroundClosurePreInitializationGetter() { + return new MemberImpl(Member.METHOD, AROUND_CLOSURE_TYPE, 0, "getPreInitializationState", "()[Ljava/lang/Object;"); + } + + public static ResolvedMember preIntroducedConstructor(UnresolvedType aspectType, UnresolvedType targetType, + UnresolvedType[] paramTypes) { + return new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC_FINAL, UnresolvedType.OBJECTARRAY, + NameMangler.preIntroducedConstructor(aspectType, targetType), paramTypes); + } + + public static ResolvedMember postIntroducedConstructor(UnresolvedType aspectType, UnresolvedType targetType, + UnresolvedType[] paramTypes) { + return new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC_FINAL, UnresolvedType.VOID, + NameMangler.postIntroducedConstructor(aspectType, targetType), UnresolvedType.insert(targetType, paramTypes)); + } + + public static ResolvedMember itdAtDeclareParentsField(ResolvedType targetType, UnresolvedType itdType, UnresolvedType aspectType) { + return new ResolvedMemberImpl(Member.FIELD, targetType, Modifier.PRIVATE, itdType, NameMangler.itdAtDeclareParentsField( + aspectType, itdType), ResolvedType.NONE); + } + + public static ResolvedMember interConstructor(ResolvedType targetType, ResolvedMember constructor, UnresolvedType aspectType) { + // + // ResolvedType targetType, + // UnresolvedType[] argTypes, + // int modifiers) + // { + ResolvedMember ret = new ResolvedMemberImpl(Member.CONSTRUCTOR, targetType, Modifier.PUBLIC, UnresolvedType.VOID, "<init>", + constructor.getParameterTypes(), constructor.getExceptions()); + // System.out.println("ret: " + ret + " mods: " + Modifier.toString(modifiers)); + if (Modifier.isPublic(constructor.getModifiers())) { + return ret; + } + while (true) { + ret = addCookieTo(ret, aspectType); + if (targetType.lookupMemberNoSupers(ret) == null) { + return ret; + } + } + } + + public static ResolvedMember interFieldInitializer(ResolvedMember field, UnresolvedType aspectType) { + return new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC, NameMangler.interFieldInitializer(aspectType, + field.getDeclaringType(), field.getName()), Modifier.isStatic(field.getModifiers()) ? "()V" : "(" + + field.getDeclaringType().getSignature() + ")V"); + } + + private static int makePublicNonFinal(int modifiers) { + return (modifiers & ~VISIBILITY & ~Modifier.FINAL) | Modifier.PUBLIC; + } + + private static int makeNonFinal(int modifiers) { + return (modifiers & ~Modifier.FINAL); + } + + /** + * This static method goes on the aspect that declares the inter-type field + */ + public static ResolvedMember interFieldSetDispatcher(ResolvedMember field, UnresolvedType aspectType) { + ResolvedMember rm = new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC, UnresolvedType.VOID, + NameMangler.interFieldSetDispatcher(aspectType, field.getDeclaringType(), field.getName()), Modifier.isStatic(field + .getModifiers()) ? new UnresolvedType[] { field.getReturnType() } : new UnresolvedType[] { + field.getDeclaringType(), field.getReturnType() }); + rm.setTypeVariables(field.getTypeVariables()); + return rm; + } + + /** + * This static method goes on the aspect that declares the inter-type field + */ + public static ResolvedMember interFieldGetDispatcher(ResolvedMember field, UnresolvedType aspectType) { + ResolvedMember rm = new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC, field.getReturnType(), + NameMangler.interFieldGetDispatcher(aspectType, field.getDeclaringType(), field.getName()), Modifier.isStatic(field + .getModifiers()) ? UnresolvedType.NONE : new UnresolvedType[] { field.getDeclaringType() }, + UnresolvedType.NONE); + rm.setTypeVariables(field.getTypeVariables()); + return rm; + } + + // private static int makeFieldModifiers(int declaredModifiers) { + // int ret = Modifier.PUBLIC; + // if (Modifier.isTransient(declaredModifiers)) ret |= Modifier.TRANSIENT; + // if (Modifier.isVolatile(declaredModifiers)) ret |= Modifier.VOLATILE; + // return ret; + // } + + /** + * This field goes on the class the field is declared onto. Field names for ITDs onto interfaces are handled below. + */ + public static ResolvedMember interFieldClassField(ResolvedMember field, UnresolvedType aspectType, boolean newStyle) { + int modifiers = (newStyle ? makeNonFinal(field.getModifiers()) : makePublicNonFinal(field.getModifiers())); + String name = null; + if (newStyle) { + name = field.getName(); + } else { + name = NameMangler.interFieldClassField(field.getModifiers(), aspectType, field.getDeclaringType(), field.getName()); + } + return new ResolvedMemberImpl(Member.FIELD, field.getDeclaringType(), modifiers, field.getReturnType(), name, + UnresolvedType.NONE, UnresolvedType.NONE); + } + + /** + * This field goes on top-most implementers of the interface the field is declared onto + */ + public static ResolvedMember interFieldInterfaceField(ResolvedMember field, UnresolvedType onClass, UnresolvedType aspectType, boolean newStyle) { + String name = null; + if (newStyle) { + name = field.getName(); + } else { + name = NameMangler.interFieldInterfaceField(aspectType, field.getDeclaringType(), field.getName()); + } + return new ResolvedMemberImpl(Member.FIELD, onClass, makePublicNonFinal(field.getModifiers()), field.getReturnType(), + name, UnresolvedType.NONE, UnresolvedType.NONE); + } + + /** + * This instance method goes on the interface the field is declared onto as well as its top-most implementors + */ + public static ResolvedMember interFieldInterfaceSetter(ResolvedMember field, ResolvedType onType, UnresolvedType aspectType) { + int modifiers = Modifier.PUBLIC; + if (onType.isInterface()) { + modifiers |= Modifier.ABSTRACT; + } + ResolvedMember rm = new ResolvedMemberImpl(Member.METHOD, onType, modifiers, UnresolvedType.VOID, + NameMangler.interFieldInterfaceSetter(aspectType, field.getDeclaringType(), field.getName()), + new UnresolvedType[] { field.getReturnType() }, UnresolvedType.NONE); + rm.setTypeVariables(field.getTypeVariables()); + return rm; + } + + /** + * This instance method goes on the interface the field is declared onto as well as its top-most implementors + */ + public static ResolvedMember interFieldInterfaceGetter(ResolvedMember field, ResolvedType onType, UnresolvedType aspectType) { + int modifiers = Modifier.PUBLIC; + if (onType.isInterface()) { + modifiers |= Modifier.ABSTRACT; + } + ResolvedMember rm = new ResolvedMemberImpl(Member.METHOD, onType, modifiers, field.getReturnType(), + NameMangler.interFieldInterfaceGetter(aspectType, field.getDeclaringType(), field.getName()), UnresolvedType.NONE, + UnresolvedType.NONE); + rm.setTypeVariables(field.getTypeVariables()); + return rm; + } + + /** + * This method goes on the target type of the inter-type method. (and possibly the topmost-implementors, if the target type is + * an interface). The implementation will call the interMethodDispatch method on the aspect. + */ + public static ResolvedMember interMethod(ResolvedMember meth, UnresolvedType aspectType, boolean onInterface) { + if (Modifier.isPublic(meth.getModifiers()) && !onInterface) { + return meth; + } + + int modifiers = makePublicNonFinal(meth.getModifiers()); + if (onInterface) { + modifiers |= Modifier.ABSTRACT; + } + + ResolvedMemberImpl rmi = new ResolvedMemberImpl(Member.METHOD, meth.getDeclaringType(), modifiers, meth.getReturnType(), + NameMangler.interMethod(meth.getModifiers(), aspectType, meth.getDeclaringType(), meth.getName()), + meth.getParameterTypes(), meth.getExceptions()); + rmi.setParameterNames(meth.getParameterNames()); + rmi.setTypeVariables(meth.getTypeVariables()); + return rmi; + } + + /** + * This method goes on the target type of the inter-type method. (and possibly the topmost-implementors, if the target type is + * an interface). The implementation will call the interMethodDispatch method on the aspect. + */ + public static ResolvedMember interMethodBridger(ResolvedMember meth, UnresolvedType aspectType, boolean onInterface) { + // if (Modifier.isPublic(meth.getModifiers()) && !onInterface) + // return meth; + + int modifiers = makePublicNonFinal(meth.getModifiers()) | BRIDGE; + if (onInterface) { + modifiers |= Modifier.ABSTRACT; + } + + ResolvedMemberImpl rmi = new ResolvedMemberImpl(Member.METHOD, meth.getDeclaringType(), modifiers, meth.getReturnType(), + NameMangler.interMethod(meth.getModifiers(), aspectType, meth.getDeclaringType(), meth.getName()), + meth.getParameterTypes(), meth.getExceptions()); + rmi.setTypeVariables(meth.getTypeVariables()); + return rmi; + } + + /** + * Sometimes the intertyped method requires a bridge method alongside it. For example if the method 'N SomeI<N>.m()' is put onto + * an interface 'interface I<N extends Number>' and then a concrete implementation is 'class C implements I<Float>' then the ITD + * on the interface will be 'Number m()', whereas the ITD on the 'topmostimplementor' will be 'Float m()'. A bridge method needs + * to be created in the topmostimplementor 'Number m()' that delegates to 'Float m()' + */ + public static ResolvedMember bridgerToInterMethod(ResolvedMember meth, UnresolvedType aspectType) { + + int modifiers = makePublicNonFinal(meth.getModifiers()); + + ResolvedMemberImpl rmi = new ResolvedMemberImpl(Member.METHOD, aspectType, modifiers, meth.getReturnType(), + NameMangler.interMethod(meth.getModifiers(), aspectType, meth.getDeclaringType(), meth.getName()), + meth.getParameterTypes(), meth.getExceptions()); + rmi.setTypeVariables(meth.getTypeVariables()); + return rmi; + } + + /** + * This static method goes on the declaring aspect of the inter-type method. The implementation calls the interMethodBody() + * method on the aspect. + */ + public static ResolvedMember interMethodDispatcher(ResolvedMember meth, UnresolvedType aspectType) { + UnresolvedType[] paramTypes = meth.getParameterTypes(); + if (!Modifier.isStatic(meth.getModifiers())) { + paramTypes = UnresolvedType.insert(meth.getDeclaringType(), paramTypes); + } + + ResolvedMemberImpl rmi = new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC, meth.getReturnType(), + NameMangler.interMethodDispatcher(aspectType, meth.getDeclaringType(), meth.getName()), paramTypes, + meth.getExceptions()); + rmi.setParameterNames(meth.getParameterNames()); + rmi.setTypeVariables(meth.getTypeVariables()); + + return rmi; + } + + /** + * This method goes on the declaring aspect of the inter-type method. It contains the real body of the ITD method. + */ + public static ResolvedMember interMethodBody(ResolvedMember meth, UnresolvedType aspectType) { + UnresolvedType[] paramTypes = meth.getParameterTypes(); + if (!Modifier.isStatic(meth.getModifiers())) { + paramTypes = UnresolvedType.insert(meth.getDeclaringType(), paramTypes); + } + + int modifiers = PUBLIC_STATIC; + if (Modifier.isStrict(meth.getModifiers())) { + modifiers |= Modifier.STRICT; + } + + ResolvedMemberImpl rmi = new ResolvedMemberImpl(Member.METHOD, aspectType, modifiers, meth.getReturnType(), + NameMangler.interMethodBody(aspectType, meth.getDeclaringType(), meth.getName()), paramTypes, meth.getExceptions()); + rmi.setParameterNames(meth.getParameterNames()); + rmi.setTypeVariables(meth.getTypeVariables()); + return rmi; + } + + private static ResolvedMember addCookieTo(ResolvedMember ret, UnresolvedType aspectType) { + UnresolvedType[] params = ret.getParameterTypes(); + + UnresolvedType[] freshParams = UnresolvedType.add(params, aspectType); + return new ResolvedMemberImpl(ret.getKind(), ret.getDeclaringType(), ret.getModifiers(), ret.getReturnType(), + ret.getName(), freshParams, ret.getExceptions()); + } + + public static ResolvedMember toObjectConversionMethod(UnresolvedType fromType) { + if (fromType.isPrimitiveType()) { + String name = fromType.toString() + "Object"; + return new ResolvedMemberImpl(Member.METHOD, CONVERSIONS_TYPE, PUBLIC_STATIC, UnresolvedType.OBJECT, name, + new UnresolvedType[] { fromType }, UnresolvedType.NONE); + } else { + return null; + } + } + + public static Member interfaceConstructor(ResolvedType resolvedTypeX) { + // AMC next two lines should not be needed when sig for generic type is changed + ResolvedType declaringType = resolvedTypeX; + if (declaringType.isRawType()) { + declaringType = declaringType.getGenericType(); + } + return new ResolvedMemberImpl(Member.CONSTRUCTOR, declaringType, Modifier.PUBLIC, "<init>", "()V"); + } + + // -- common types we use. Note: Java 5 dependand types are refered to as String + public final static UnresolvedType ASPECT_ANNOTATION = UnresolvedType.forSignature("Lorg/aspectj/lang/annotation/Aspect;"); + + public final static UnresolvedType BEFORE_ANNOTATION = UnresolvedType.forSignature("Lorg/aspectj/lang/annotation/Before;"); + + public final static UnresolvedType AROUND_ANNOTATION = UnresolvedType.forSignature("Lorg/aspectj/lang/annotation/Around;"); + + public final static UnresolvedType AFTERRETURNING_ANNOTATION = UnresolvedType + .forSignature("Lorg/aspectj/lang/annotation/AfterReturning;"); + + public final static UnresolvedType AFTERTHROWING_ANNOTATION = UnresolvedType + .forSignature("Lorg/aspectj/lang/annotation/AfterThrowing;"); + + public final static UnresolvedType AFTER_ANNOTATION = UnresolvedType.forSignature("Lorg/aspectj/lang/annotation/After;"); + + public final static UnresolvedType POINTCUT_ANNOTATION = UnresolvedType.forSignature("Lorg/aspectj/lang/annotation/Pointcut;"); + + public final static UnresolvedType DECLAREERROR_ANNOTATION = UnresolvedType + .forSignature("Lorg/aspectj/lang/annotation/DeclareError;"); + + public final static UnresolvedType DECLAREWARNING_ANNOTATION = UnresolvedType + .forSignature("Lorg/aspectj/lang/annotation/DeclareWarning;"); + + public final static UnresolvedType DECLAREPRECEDENCE_ANNOTATION = UnresolvedType + .forSignature("Lorg/aspectj/lang/annotation/DeclarePrecedence;"); + + // public final static UnresolvedType DECLAREIMPLEMENTS_ANNOTATION = + // UnresolvedType.forSignature("Lorg/aspectj/lang/annotation/DeclareImplements;"); + + public final static UnresolvedType DECLAREPARENTS_ANNOTATION = UnresolvedType + .forSignature("Lorg/aspectj/lang/annotation/DeclareParents;"); + + public final static UnresolvedType DECLAREMIXIN_ANNOTATION = UnresolvedType + .forSignature("Lorg/aspectj/lang/annotation/DeclareMixin;"); + + public final static UnresolvedType TYPEX_JOINPOINT = UnresolvedType.forSignature("Lorg/aspectj/lang/JoinPoint;"); + + public final static UnresolvedType TYPEX_PROCEEDINGJOINPOINT = UnresolvedType + .forSignature("Lorg/aspectj/lang/ProceedingJoinPoint;"); + + public final static UnresolvedType TYPEX_STATICJOINPOINT = UnresolvedType + .forSignature("Lorg/aspectj/lang/JoinPoint$StaticPart;"); + + public final static UnresolvedType TYPEX_ENCLOSINGSTATICJOINPOINT = UnresolvedType + .forSignature("Lorg/aspectj/lang/JoinPoint$EnclosingStaticPart;"); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotatedElement.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotatedElement.java new file mode 100644 index 000000000..86297b85b --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotatedElement.java @@ -0,0 +1,21 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver; + +/** + * Represents any element that may have annotations + */ +public interface AnnotatedElement { + boolean hasAnnotation(UnresolvedType ofType); + + ResolvedType[] getAnnotationTypes(); + + AnnotationAJ getAnnotationOfType(UnresolvedType ofType); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationAJ.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationAJ.java new file mode 100644 index 000000000..311b12e1f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationAJ.java @@ -0,0 +1,102 @@ +/* ******************************************************************* + * Copyright (c) 2006-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 + * + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.Set; + +/** + * Simple representation of an annotation that the weaver can work with. + * + * @author AndyClement + */ +public interface AnnotationAJ { + + public static final AnnotationAJ[] EMPTY_ARRAY = new AnnotationAJ[0]; + + /** + * @return the signature for the annotation type, eg. Lcom/foo/MyAnno; + */ + public String getTypeSignature(); + + /** + * @return the type name for the annotation, eg. com.foo.MyAnno + */ + public String getTypeName(); + + /** + * @return the type of the annotation + */ + public ResolvedType getType(); + + /** + * return true if this annotation can target an annotation type + */ + public boolean allowedOnAnnotationType(); + + /** + * @return true if this annotation can be put on a field + */ + public boolean allowedOnField(); + + /** + * @return true if this annotation can target a 'regular' type. A 'regular' type is enum/class/interface - it is *not* + * annotation. + */ + public boolean allowedOnRegularType(); + + /** + * @return for the @target annotation, this will return a set of the element-types it can be applied to. For other annotations , + * it returns the empty set. + */ + public Set<String> getTargets(); + + /** + * @param name the name of the value + * @return true if there is a value with that name + */ + public boolean hasNamedValue(String name); + + /** + * @param name the name of the annotation field + * @param value the value of the annotation field + * @return true if there is a value with the specified name and value + */ + public boolean hasNameValuePair(String name, String value); + + /** + * @return String representation of the valid targets for this annotation, eg. "{TYPE,FIELD}" + */ + public String getValidTargets(); + + /** + * @return String form of the annotation and any values, eg. @Foo(a=b,c=d) + */ + public String stringify(); + + /** + * @return true if this annotation is marked with @target + */ + public boolean specifiesTarget(); + + /** + * @return true if the annotation is marked for runtime visibility + */ + public boolean isRuntimeVisible(); + + /** + * Determine the string representation of the value of a field. For example in @SuppressAjWarnings({"adviceDidNotMatch"}) the + * return value for getStringFormOfValue("value") would be "[adviceDidNotMatch]". + * + * @param name the name of the annotation field being looked up + * @return string representation of the value of that field, may be null if no such field set + */ + public String getStringFormOfValue(String name); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationAnnotationValue.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationAnnotationValue.java new file mode 100644 index 000000000..c25c33cd2 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationAnnotationValue.java @@ -0,0 +1,35 @@ +/* ******************************************************************* + * Copyright (c) 2006 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 IBM initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +public class AnnotationAnnotationValue extends AnnotationValue { + + private AnnotationAJ value; + + public AnnotationAnnotationValue(AnnotationAJ value) { + super(AnnotationValue.ANNOTATION); + this.value = value; + } + + public AnnotationAJ getAnnotation() { + return value; + } + + public String stringify() { + return value.stringify(); + } + + public String toString() { + return value.toString(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationNameValuePair.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationNameValuePair.java new file mode 100644 index 000000000..17824ac69 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationNameValuePair.java @@ -0,0 +1,47 @@ +/* ******************************************************************* + * Copyright (c) 2006 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 IBM initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +public class AnnotationNameValuePair { + + private String name; + + private AnnotationValue val; + + public AnnotationNameValuePair(String name, AnnotationValue val) { + this.name = name; + this.val = val; + } + + public String getName() { + return name; + } + + public AnnotationValue getValue() { + return val; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(name + "=" + val.toString()); + return sb.toString(); + } + + public String stringify() { + StringBuffer sb = new StringBuffer(); + if (!name.equals("value")) { + sb.append(name + "="); + } + sb.append(val.stringify()); + return sb.toString(); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationOnTypeMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationOnTypeMunger.java new file mode 100644 index 000000000..6390ed311 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationOnTypeMunger.java @@ -0,0 +1,56 @@ +/* ******************************************************************* + * Copyright (c) 2005 IBM + * 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; + +import java.io.IOException; + +/** + * Represents adding an annotation to a type + */ +public class AnnotationOnTypeMunger extends ResolvedTypeMunger { + AnnotationAJ newAnnotation; + + public AnnotationOnTypeMunger(AnnotationAJ anno) { + super(AnnotationOnType, null); + newAnnotation = anno; + } + + public void write(CompressingDataOutputStream s) throws IOException { + throw new RuntimeException("unimplemented"); + } + + public AnnotationAJ getNewAnnotation() { + return newAnnotation; + } + + public boolean equals(Object other) { + if (!(other instanceof AnnotationOnTypeMunger)) { + return false; + } + AnnotationOnTypeMunger o = (AnnotationOnTypeMunger) other; + // TODO does not check equality of annotation values + return newAnnotation.getTypeSignature().equals(o.newAnnotation.getTypeSignature()); + } + + private volatile int hashCode = 0; + + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37 * result + newAnnotation.getTypeSignature().hashCode(); + hashCode = result; + } + return hashCode; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationTargetKind.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationTargetKind.java new file mode 100644 index 000000000..fe2104484 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationTargetKind.java @@ -0,0 +1,59 @@ +/******************************************************************** + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Helen Hawkins - Initial implementation + *******************************************************************/ +package org.aspectj.weaver; + +import java.io.DataInputStream; +import java.io.IOException; + +import org.aspectj.util.TypeSafeEnum; + +/** + * A TypeSafeEnum similar to the Java5 ElementType Enum + */ +public class AnnotationTargetKind extends TypeSafeEnum { + + public AnnotationTargetKind(String name, int key) { + super(name, key); + } + + public static AnnotationTargetKind read(DataInputStream s) throws IOException { + int key = s.readByte(); + switch (key) { + case 1: + return ANNOTATION_TYPE; + case 2: + return CONSTRUCTOR; + case 3: + return FIELD; + case 4: + return LOCAL_VARIABLE; + case 5: + return METHOD; + case 6: + return PACKAGE; + case 7: + return PARAMETER; + case 8: + return TYPE; + } + throw new BCException("weird annotation target kind " + key); + } + + public static final AnnotationTargetKind ANNOTATION_TYPE = new AnnotationTargetKind("ANNOTATION_TYPE", 1); + public static final AnnotationTargetKind CONSTRUCTOR = new AnnotationTargetKind("CONSTRUCTOR", 2); + public static final AnnotationTargetKind FIELD = new AnnotationTargetKind("FIELD", 3); + public static final AnnotationTargetKind LOCAL_VARIABLE = new AnnotationTargetKind("LOCAL_VARIABLE", 4); + public static final AnnotationTargetKind METHOD = new AnnotationTargetKind("METHOD", 5); + public static final AnnotationTargetKind PACKAGE = new AnnotationTargetKind("PACKAGE", 6); + public static final AnnotationTargetKind PARAMETER = new AnnotationTargetKind("PARAMETER", 7); + public static final AnnotationTargetKind TYPE = new AnnotationTargetKind("TYPE", 8); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationValue.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationValue.java new file mode 100644 index 000000000..32f366774 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/AnnotationValue.java @@ -0,0 +1,71 @@ +/* ******************************************************************* + * Copyright (c) 2006 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 IBM initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +public abstract class AnnotationValue { + + protected int valueKind; + + public static final int STRING = 's'; + public static final int ENUM_CONSTANT = 'e'; + public static final int CLASS = 'c'; + public static final int ANNOTATION = '@'; + public static final int ARRAY = '['; + + public static final int PRIMITIVE_INT = 'I'; + public static final int PRIMITIVE_BYTE = 'B'; + public static final int PRIMITIVE_CHAR = 'C'; + public static final int PRIMITIVE_DOUBLE = 'D'; + public static final int PRIMITIVE_FLOAT = 'F'; + public static final int PRIMITIVE_LONG = 'J'; + public static final int PRIMITIVE_SHORT = 'S'; + public static final int PRIMITIVE_BOOLEAN = 'Z'; + + public abstract String stringify(); + + public AnnotationValue(int kind) { + valueKind = kind; + } + + public static String whatKindIsThis(int kind) { + switch (kind) { + case PRIMITIVE_BYTE: // byte + return "byte"; + case PRIMITIVE_CHAR: // char + return "char"; + case PRIMITIVE_DOUBLE: // double + return "double"; + case PRIMITIVE_FLOAT: // float + return "float"; + case PRIMITIVE_INT: // int + return "int"; + case PRIMITIVE_LONG: // long + return "long"; + case PRIMITIVE_SHORT: // short + return "short"; + case PRIMITIVE_BOOLEAN: // boolean + return "boolean"; + case 's': // String + return "string"; + case 'e': // Enum constant + return "enum"; + case 'c': // Class + return "class"; + case '@': // Annotation + return "annotation"; + case '[': // Array + return "array"; + default: + throw new RuntimeException("Dont know what this is : " + kind); + } + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ArrayAnnotationValue.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ArrayAnnotationValue.java new file mode 100644 index 000000000..f4ef760bb --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ArrayAnnotationValue.java @@ -0,0 +1,59 @@ +/* ******************************************************************* + * Copyright (c) 2006 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 IBM initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +public class ArrayAnnotationValue extends AnnotationValue { + + private AnnotationValue[] values; + + public ArrayAnnotationValue() { + super(AnnotationValue.ARRAY); + } + + public void setValues(AnnotationValue[] values) { + this.values = values; + } + + public ArrayAnnotationValue(AnnotationValue[] values) { + super(AnnotationValue.ARRAY); + this.values = values; + } + + public AnnotationValue[] getValues() { + return values; + } + + public String stringify() { + StringBuffer sb = new StringBuffer(); + sb.append("["); + for (int i = 0; i < values.length; i++) { + sb.append(values[i].stringify()); + if (i + 1 < values.length) + sb.append(","); + } + sb.append("]"); + return sb.toString(); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("{"); + for (int i = 0; i < values.length; i++) { + sb.append(values[i].toString()); + if ((i + 1) < values.length) + sb.append(","); + } + sb.append("}"); + return sb.toString(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ArrayReferenceType.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ArrayReferenceType.java new file mode 100644 index 000000000..aa4fa55c0 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ArrayReferenceType.java @@ -0,0 +1,211 @@ +/* ******************************************************************* + * 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; + +import java.lang.reflect.Modifier; + +/** + * Represents a resolved array type + * + * @author Andy Clement + */ +public class ArrayReferenceType extends ReferenceType { + + private final ResolvedType componentType; + + public ArrayReferenceType(String sig, String erasureSig, World world, ResolvedType componentType) { + super(sig, erasureSig, world); + this.componentType = componentType; + } + + // These methods are from the original implementation when Array was a ResolvedType and not a ReferenceType + + public final ResolvedMember[] getDeclaredFields() { + return ResolvedMember.NONE; + } + + public final ResolvedMember[] getDeclaredMethods() { + // ??? should this return clone? Probably not... + // If it ever does, here is the code: + // ResolvedMember cloneMethod = + // new ResolvedMember(Member.METHOD,this,Modifier.PUBLIC,UnresolvedType.OBJECT,"clone",new UnresolvedType[]{}); + // return new ResolvedMember[]{cloneMethod}; + return ResolvedMember.NONE; + } + + public final ResolvedType[] getDeclaredInterfaces() { + return new ResolvedType[] { world.getCoreType(CLONEABLE), world.getCoreType(SERIALIZABLE) }; + } + + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + return null; + } + + public AnnotationAJ[] getAnnotations() { + return AnnotationAJ.EMPTY_ARRAY; + } + + public ResolvedType[] getAnnotationTypes() { + return ResolvedType.NONE; + } + + public final ResolvedMember[] getDeclaredPointcuts() { + return ResolvedMember.NONE; + } + + public boolean hasAnnotation(UnresolvedType ofType) { + return false; + } + + public final ResolvedType getSuperclass() { + return world.getCoreType(OBJECT); + } + + public final boolean isAssignableFrom(ResolvedType o) { + if (!o.isArray()) + return false; + if (o.getComponentType().isPrimitiveType()) { + return o.equals(this); + } else { + return getComponentType().resolve(world).isAssignableFrom(o.getComponentType().resolve(world)); + } + } + + public boolean isAssignableFrom(ResolvedType o, boolean allowMissing) { + return isAssignableFrom(o); + } + + public final boolean isCoerceableFrom(ResolvedType o) { + if (o.equals(UnresolvedType.OBJECT) || o.equals(UnresolvedType.SERIALIZABLE) || o.equals(UnresolvedType.CLONEABLE)) { + return true; + } + if (!o.isArray()) + return false; + if (o.getComponentType().isPrimitiveType()) { + return o.equals(this); + } else { + return getComponentType().resolve(world).isCoerceableFrom(o.getComponentType().resolve(world)); + } + } + + public final int getModifiers() { + int mask = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED; + return (componentType.getModifiers() & mask) | Modifier.FINAL; + } + + public UnresolvedType getComponentType() { + return componentType; + } + + public ResolvedType getResolvedComponentType() { + return componentType; + } + + public ISourceContext getSourceContext() { + return getResolvedComponentType().getSourceContext(); + } + + // Methods overridden from ReferenceType follow + + public TypeVariable[] getTypeVariables() { + if (this.typeVariables == null && componentType.getTypeVariables() != null) { + this.typeVariables = componentType.getTypeVariables(); + for (int i = 0; i < this.typeVariables.length; i++) { + this.typeVariables[i].resolve(world); + } + } + return this.typeVariables; + } + + public boolean isAnnotation() { + return false; + } + + public boolean isAnonymous() { + return false; + } + + public boolean isAnnotationStyleAspect() { + return false; + } + + public boolean isAspect() { + return false; + } + + public boolean isPrimitiveType() { + return typeKind == TypeKind.PRIMITIVE; + } + + public boolean isSimpleType() { + return typeKind == TypeKind.SIMPLE; + } + + public boolean isRawType() { + return typeKind == TypeKind.RAW; + } + + public boolean isGenericType() { + return typeKind == TypeKind.GENERIC; + } + + public boolean isParameterizedType() { + return typeKind == TypeKind.PARAMETERIZED; + } + + public boolean isTypeVariableReference() { + return typeKind == TypeKind.TYPE_VARIABLE; + } + + public boolean isGenericWildcard() { + return typeKind == TypeKind.WILDCARD; + } + + public boolean isEnum() { + return false; + } + + public boolean isNested() { + return false; + } + + public boolean isClass() { + return false; + } + + @Override + public boolean isExposedToWeaver() { + return false; + } + + public boolean canAnnotationTargetType() { + return false; + } + + public AnnotationTargetKind[] getAnnotationTargetKinds() { + return null; + } + + public boolean isAnnotationWithRuntimeRetention() { + return false; + } + + public boolean isPrimitiveArray() { + if (componentType.isPrimitiveType()) { + return true; + } else if (componentType.isArray()) { + return componentType.isPrimitiveArray(); + } else { + return false; + } + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/BCException.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/BCException.java new file mode 100644 index 000000000..81bf607ed --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/BCException.java @@ -0,0 +1,63 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.PrintStream; +import java.io.PrintWriter; + +import org.aspectj.bridge.context.CompilationAndWeavingContext; + +/** + * Exception to use inside the bcweaver. + */ +@SuppressWarnings("serial") +public class BCException extends RuntimeException { + Throwable thrown; + + public BCException() { + super(); + } + + public BCException(String s) { + super(s + "\n" + CompilationAndWeavingContext.getCurrentContext()); + } + + public BCException(String s, Throwable thrown) { + this(s); + this.thrown = thrown; + } + + public void printStackTrace() { + printStackTrace(System.err); + } + + public void printStackTrace(PrintStream s) { + printStackTrace(new PrintWriter(s)); + } + + public void printStackTrace(PrintWriter s) { + super.printStackTrace(s); + if (null != thrown) { + s.print("Caused by: "); + s.print(thrown.getClass().getName()); + String message = thrown.getMessage(); + if (null != message) { + s.print(": "); + s.print(message); + } + s.println(); + thrown.printStackTrace(s); + } + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/BindingScope.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/BindingScope.java new file mode 100644 index 000000000..d97fb8fb6 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/BindingScope.java @@ -0,0 +1,68 @@ +/* ******************************************************************* + * Copyright (c) 2006-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 + * + * ******************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.patterns.FormalBinding; +import org.aspectj.weaver.patterns.SimpleScope; + +/** + * BindingScope that knows the enclosingType, which is needed for pointcut reference resolution + * + * @author Alexandre Vasseur + * @author Andy Clement + */ +public class BindingScope extends SimpleScope { + private final ResolvedType enclosingType; + private final ISourceContext sourceContext; + private boolean importsUpdated = false; + + public BindingScope(ResolvedType type, ISourceContext sourceContext, FormalBinding[] bindings) { + super(type.getWorld(), bindings); + this.enclosingType = type; + this.sourceContext = sourceContext; + } + + public ResolvedType getEnclosingType() { + return enclosingType; + } + + public ISourceLocation makeSourceLocation(IHasPosition location) { + return sourceContext.makeSourceLocation(location); + } + + public UnresolvedType lookupType(String name, IHasPosition location) { + // bug 126560 + if (enclosingType != null && !importsUpdated) { + // add the package we're in to the list of imported + // prefixes so that we can find types in the same package + String pkgName = enclosingType.getPackageName(); + if (pkgName != null && !pkgName.equals("")) { + String[] existingImports = getImportedPrefixes(); + String pkgNameWithDot = pkgName.concat("."); + boolean found = false; + for (String existingImport : existingImports) { + if (existingImport.equals(pkgNameWithDot)) { + found = true; + break; + } + } + if (!found) { + String[] newImports = new String[existingImports.length + 1]; + System.arraycopy(existingImports, 0, newImports, 0, existingImports.length); + newImports[existingImports.length] = pkgNameWithDot; + setImportedPrefixes(newImports); + } + } + importsUpdated = true; + } + return super.lookupType(name, location); + } +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/BoundedReferenceType.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/BoundedReferenceType.java new file mode 100644 index 000000000..62bd4d23a --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/BoundedReferenceType.java @@ -0,0 +1,236 @@ +/* ******************************************************************* + * Copyright (c) 2010 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://eclipse.org/legal/epl-v10.html + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.Map; + +/** + * A BoundedReferenceType is the result of a generics wildcard expression ? extends String, ? super Foo etc.. + * + * The "signature" for a bounded reference type follows the generic signature specification in section 4.4 of JVM spec: *,+,- plus + * signature strings. + * + * The bound may be a type variable (e.g. ? super T) + * + * @author Adrian Colyer + * @author Andy Clement + */ +public class BoundedReferenceType extends ReferenceType { + + // possible kinds of BoundedReferenceType + public static final int UNBOUND = 0; + public static final int EXTENDS = 1; + public static final int SUPER = 2; + + public int kind; + + private ResolvedType lowerBound; + + private ResolvedType upperBound; + + protected ReferenceType[] additionalInterfaceBounds = ReferenceType.EMPTY_ARRAY; + + public BoundedReferenceType(ReferenceType aBound, boolean isExtends, World world) { + super((isExtends ? "+" : "-") + aBound.signature, aBound.signatureErasure, world); + if (isExtends) { + this.kind = EXTENDS; + } else { + this.kind = SUPER; + } + if (isExtends) { + upperBound = aBound; + } else { + lowerBound = aBound; + upperBound = world.resolve(UnresolvedType.OBJECT); + } + setDelegate(new BoundedReferenceTypeDelegate((ReferenceType) getUpperBound())); + } + + public BoundedReferenceType(ReferenceType aBound, boolean isExtends, World world, ReferenceType[] additionalInterfaces) { + this(aBound, isExtends, world); + this.additionalInterfaceBounds = additionalInterfaces; + } + + /** + * only for use when resolving GenericsWildcardTypeX or a TypeVariableReferenceType + */ + protected BoundedReferenceType(String signature, String erasedSignature, World world) { + super(signature, erasedSignature, world); + if (signature.equals("*")) { + // pure wildcard + this.kind = UNBOUND; + upperBound = world.resolve(UnresolvedType.OBJECT); + } else { + upperBound = world.resolve(forSignature(erasedSignature)); + } + setDelegate(new BoundedReferenceTypeDelegate((ReferenceType) upperBound)); + } + + /** + * Constructs the BoundedReferenceType representing an unbounded wildcard '?'. In this situation the signature is '*' and the + * erased signature is Ljava/lang/Object; + */ + public BoundedReferenceType(World world) { + super("*", "Ljava/lang/Object;", world); + this.kind = UNBOUND; + upperBound = world.resolve(UnresolvedType.OBJECT); + setDelegate(new BoundedReferenceTypeDelegate((ReferenceType) upperBound)); + } + + public UnresolvedType getUpperBound() { + return upperBound; + } + + public UnresolvedType getLowerBound() { + return lowerBound; + } + + public ReferenceType[] getAdditionalBounds() { + return additionalInterfaceBounds; + } + + @Override + public UnresolvedType parameterize(Map<String, UnresolvedType> typeBindings) { + if (this.kind == UNBOUND) { + return this; + } + ReferenceType[] parameterizedAdditionalInterfaces = new ReferenceType[additionalInterfaceBounds == null ? 0 + : additionalInterfaceBounds.length]; + for (int i = 0; i < parameterizedAdditionalInterfaces.length; i++) { + parameterizedAdditionalInterfaces[i] = (ReferenceType) additionalInterfaceBounds[i].parameterize(typeBindings); + } + if (this.kind == EXTENDS) { + return new BoundedReferenceType((ReferenceType) getUpperBound().parameterize(typeBindings), true, world, + parameterizedAdditionalInterfaces); + } else { + // (this.kind == SUPER) + UnresolvedType parameterizedLowerBound = getLowerBound().parameterize(typeBindings); + if (!(parameterizedLowerBound instanceof ReferenceType)) { + throw new IllegalStateException("PR543023: Unexpectedly found a non reference type: "+ + parameterizedLowerBound.getClass().getName()+" with signature "+parameterizedLowerBound.getSignature()); + } + return new BoundedReferenceType((ReferenceType)parameterizedLowerBound , false, world, + parameterizedAdditionalInterfaces); + } + } + + @Override + public String getSignatureForAttribute() { + StringBuilder ret = new StringBuilder(); + if (kind==SUPER){ + ret.append("-"); + ret.append(lowerBound.getSignatureForAttribute()); + for (int i=0;i<additionalInterfaceBounds.length;i++) { + ret.append(additionalInterfaceBounds[i].getSignatureForAttribute()); + } + } else if (kind==EXTENDS) { + ret.append("+"); + ret.append(upperBound.getSignatureForAttribute()); + for (int i=0;i<additionalInterfaceBounds.length;i++) { + ret.append(additionalInterfaceBounds[i].getSignatureForAttribute()); + } + } else if (kind==UNBOUND) { + ret.append("*"); + } + return ret.toString(); + } + + + public boolean hasLowerBound() { + return lowerBound != null; + } + + public boolean isExtends() { + return this.kind == EXTENDS; + } + + public boolean isSuper() { + return this.kind == SUPER; + } + + public boolean isUnbound() { + return this.kind == UNBOUND; + } + + public boolean alwaysMatches(ResolvedType aCandidateType) { + if (isExtends()) { + // aCandidateType must be a subtype of upperBound + return ((ReferenceType) getUpperBound()).isAssignableFrom(aCandidateType); + } else if (isSuper()) { + // aCandidateType must be a supertype of lowerBound + return aCandidateType.isAssignableFrom((ReferenceType) getLowerBound()); + } else { + return true; // straight '?' + } + } + + // this "maybe matches" that + public boolean canBeCoercedTo(ResolvedType aCandidateType) { + if (alwaysMatches(aCandidateType)) { + return true; + } + if (aCandidateType.isGenericWildcard()) { + BoundedReferenceType boundedRT = (BoundedReferenceType) aCandidateType; + ResolvedType myUpperBound = (ResolvedType) getUpperBound(); + ResolvedType myLowerBound = (ResolvedType) getLowerBound(); + if (isExtends()) { + if (boundedRT.isExtends()) { + return myUpperBound.isAssignableFrom((ResolvedType) boundedRT.getUpperBound()); + } else if (boundedRT.isSuper()) { + return myUpperBound == boundedRT.getLowerBound(); + } else { + return true; // it's '?' + } + } else if (isSuper()) { + if (boundedRT.isSuper()) { + return ((ResolvedType) boundedRT.getLowerBound()).isAssignableFrom(myLowerBound); + } else if (boundedRT.isExtends()) { + return myLowerBound == boundedRT.getUpperBound(); + } else { + return true; + } + } else { + return true; + } + } else { + return false; + } + } + + @Override + public String getSimpleName() { + if (!isExtends() && !isSuper()) { + return "?"; + } + if (isExtends()) { + return ("? extends " + getUpperBound().getSimpleName()); + } else { + return ("? super " + getLowerBound().getSimpleName()); + } + } + + // override to include additional interface bounds... + @Override + public ResolvedType[] getDeclaredInterfaces() { + ResolvedType[] interfaces = super.getDeclaredInterfaces(); + if (additionalInterfaceBounds.length > 0) { + ResolvedType[] allInterfaces = new ResolvedType[interfaces.length + additionalInterfaceBounds.length]; + System.arraycopy(interfaces, 0, allInterfaces, 0, interfaces.length); + System.arraycopy(additionalInterfaceBounds, 0, allInterfaces, interfaces.length, additionalInterfaceBounds.length); + return allInterfaces; + } else { + return interfaces; + } + } + + @Override + public boolean isGenericWildcard() { + return true; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/BoundedReferenceTypeDelegate.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/BoundedReferenceTypeDelegate.java new file mode 100644 index 000000000..f928b729b --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/BoundedReferenceTypeDelegate.java @@ -0,0 +1,138 @@ +/** + * + */ +package org.aspectj.weaver; + +import java.util.Collection; +import java.util.Collections; + +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.PerClause; + +class BoundedReferenceTypeDelegate extends AbstractReferenceTypeDelegate { + + public BoundedReferenceTypeDelegate(ReferenceType backing) { + super(backing, false); + } + + public boolean isAspect() { + return resolvedTypeX.isAspect(); + } + + public boolean isAnnotationStyleAspect() { + return resolvedTypeX.isAnnotationStyleAspect(); + } + + public boolean isInterface() { + return resolvedTypeX.isInterface(); + } + + public boolean isEnum() { + return resolvedTypeX.isEnum(); + } + + public boolean isAnnotation() { + return resolvedTypeX.isAnnotation(); + } + + public boolean isAnnotationWithRuntimeRetention() { + return resolvedTypeX.isAnnotationWithRuntimeRetention(); + } + + public boolean isAnonymous() { + return resolvedTypeX.isAnonymous(); + } + + public boolean isNested() { + return resolvedTypeX.isNested(); + } + + public ResolvedType getOuterClass() { + return resolvedTypeX.getOuterClass(); + } + + public String getRetentionPolicy() { + return resolvedTypeX.getRetentionPolicy(); + } + + public boolean canAnnotationTargetType() { + return resolvedTypeX.canAnnotationTargetType(); + } + + public AnnotationTargetKind[] getAnnotationTargetKinds() { + return resolvedTypeX.getAnnotationTargetKinds(); + } + + public boolean isGeneric() { + return resolvedTypeX.isGenericType(); + } + + public String getDeclaredGenericSignature() { + return resolvedTypeX.getDeclaredGenericSignature(); + } + + public boolean hasAnnotation(UnresolvedType ofType) { + return resolvedTypeX.hasAnnotation(ofType); + } + + public AnnotationAJ[] getAnnotations() { + return resolvedTypeX.getAnnotations(); + } + + public boolean hasAnnotations() { + return resolvedTypeX.hasAnnotations(); + } + + public ResolvedType[] getAnnotationTypes() { + return resolvedTypeX.getAnnotationTypes(); + } + + public ResolvedMember[] getDeclaredFields() { + return resolvedTypeX.getDeclaredFields(); + } + + public ResolvedType[] getDeclaredInterfaces() { + return resolvedTypeX.getDeclaredInterfaces(); + } + + public ResolvedMember[] getDeclaredMethods() { + return resolvedTypeX.getDeclaredMethods(); + } + + public ResolvedMember[] getDeclaredPointcuts() { + return resolvedTypeX.getDeclaredPointcuts(); + } + + public PerClause getPerClause() { + return resolvedTypeX.getPerClause(); + } + + public Collection<Declare> getDeclares() { + return resolvedTypeX.getDeclares(); + } + + public Collection<ConcreteTypeMunger> getTypeMungers() { + return resolvedTypeX.getTypeMungers(); + } + + public Collection<ResolvedMember> getPrivilegedAccesses() { + return Collections.emptyList(); + } + + public int getModifiers() { + return resolvedTypeX.getModifiers(); + } + + public ResolvedType getSuperclass() { + return resolvedTypeX.getSuperclass(); + } + + public WeaverStateInfo getWeaverState() { + return null; + } + + public TypeVariable[] getTypeVariables() { + return resolvedTypeX.getTypeVariables(); + } + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Checker.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Checker.java new file mode 100644 index 000000000..4a765a9e5 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Checker.java @@ -0,0 +1,281 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.patterns.DeclareErrorOrWarning; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; + +/** + * Representation of a shadow munger for a declare error or warning declaration. + * + * @author Andy Clement + */ +public class Checker extends ShadowMunger { + + private boolean isError; // if not error then it is a warning + private String message; + private volatile int hashCode = -1; + + @SuppressWarnings("unused") + private Checker() { + } + + /** + * Create a Checker for a declare error or declare warning. + * + * @param deow the declare error or declare warning for which to create the checker munger + */ + public Checker(DeclareErrorOrWarning deow) { + super(deow.getPointcut(), deow.getStart(), deow.getEnd(), deow.getSourceContext(), ShadowMungerDeow); + this.message = deow.getMessage(); + this.isError = deow.isError(); + } + + /** + * Only used when filling in a parameterized Checker + */ + private Checker(Pointcut pointcut, int start, int end, ISourceContext context, String message, boolean isError) { + super(pointcut, start, end, context, ShadowMungerDeow); + this.message = message; + this.isError = isError; + } + + public boolean isError() { + return isError; + } + + public String getMessage(Shadow shadow) { + return format(this.message, shadow); + } + + @Override + public void specializeOn(Shadow shadow) { + throw new IllegalStateException("Cannot call specializeOn(...) for a Checker"); + } + + @Override + public boolean implementOn(Shadow shadow) { + throw new IllegalStateException("Cannot call implementOn(...) for a Checker"); + } + + /** + * Determine if the Checker matches at a shadow. If it does then we can immediately report the message. Currently, there can + * never be a non-statically determinable match. + * + * @param shadow the shadow which to match against + * @param world the world through which to access message handlers + */ + @Override + public boolean match(Shadow shadow, World world) { + if (super.match(shadow, world)) { + world.reportCheckerMatch(this, shadow); + } + return false; + } + + // implementation for PartialOrder.PartialComparable + public int compareTo(Object other) { + return 0; + } + + @Override + public boolean mustCheckExceptions() { + return true; + } + + @Override + public Collection<ResolvedType> getThrownExceptions() { + return Collections.emptyList(); + } + + // FIXME this perhaps ought to take account of the other fields in advice (use super.equals?) + @Override + public boolean equals(Object other) { + if (!(other instanceof Checker)) { + return false; + } + Checker o = (Checker) other; + return o.isError == isError && ((o.pointcut == null) ? (pointcut == null) : o.pointcut.equals(pointcut)); + } + + @Override + public int hashCode() { + if (hashCode == -1) { + int result = 17; + result = 37 * result + (isError ? 1 : 0); + result = 37 * result + ((pointcut == null) ? 0 : pointcut.hashCode()); + hashCode = result; + } + return hashCode; + } + + /** + * Parameterize the Checker by parameterizing the pointcut + */ + @Override + public ShadowMunger parameterizeWith(ResolvedType declaringType, Map<String, UnresolvedType> typeVariableMap) { + Checker ret = new Checker(this.pointcut.parameterizeWith(typeVariableMap, declaringType.getWorld()), this.start, this.end, + this.sourceContext, this.message, this.isError); + return ret; + } + + /** + * Concretize this Checker by concretizing the pointcut + */ + @Override + public ShadowMunger concretize(ResolvedType theAspect, World world, PerClause clause) { + this.pointcut = this.pointcut.concretize(theAspect, getDeclaringType(), 0, this); + this.hashCode = -1; + return this; + } + + @Override + public ResolvedType getConcreteAspect() { + return getDeclaringType(); + } + + // public void write(DataOutputStream stream) throws IOException { + // super.write(stream); + // stream.writeBoolean(isError); + // stream.writeUTF(message); + // } + // + // public static Checker read(DataInputStream stream, World world) throws IOException { + // Checker checker = new Checker(); + // checker.isError = stream.readBoolean(); + // checker.message = stream.readUTF(); + // return checker; + // } + + // Return the next non-escaped (with a '\') open curly + private int nextCurly(String string, int pos) { + do { + int curlyIndex = string.indexOf('{', pos); + if (curlyIndex == -1) { + return -1; + } + if (curlyIndex == 0) { + return 0; + } + if (string.charAt(curlyIndex - 1) != '\\') { + return curlyIndex; + } + pos = curlyIndex + 1; + } while (pos < string.length()); + return -1; + } + + private String format(String msg, Shadow shadow) { + int pos = 0; + int curlyIndex = nextCurly(msg, 0); + if (curlyIndex == -1) { + // was there an escaped one? + if (msg.indexOf('{') != -1) { + return msg.replace("\\{", "{"); + } else { + return msg; + } + } + StringBuffer ret = new StringBuffer(); + while (curlyIndex >= 0) { + if (curlyIndex > 0) { + ret.append(msg.substring(pos, curlyIndex).replace("\\{", "{")); + } + int endCurly = msg.indexOf('}', curlyIndex); + if (endCurly == -1) { + // wasn't closed properly - ignore it + ret.append('{'); + pos = curlyIndex + 1; + } else { + ret.append(getValue(msg.substring(curlyIndex + 1, endCurly), shadow)); + } + pos = endCurly + 1; + curlyIndex = nextCurly(msg, pos); + } + ret.append(msg.substring(pos, msg.length())); + return ret.toString(); + } + + /** + * @param buf the buffer in which to insert the substitution + * @param shadow shadow from which to draw context info + * @param c the substitution character + */ + private String getValue(String key, Shadow shadow) { + if (key.equalsIgnoreCase("joinpoint")) { + return shadow.toString(); + } else if (key.equalsIgnoreCase("joinpoint.kind")) { + return shadow.getKind().getName(); + } else if (key.equalsIgnoreCase("joinpoint.enclosingclass")) { + return shadow.getEnclosingType().getName(); + } else if (key.equalsIgnoreCase("joinpoint.enclosingmember.name")) { + Member member = shadow.getEnclosingCodeSignature(); + if (member==null) { + return ""; + } else { + return member.getName(); + } + } else if (key.equalsIgnoreCase("joinpoint.enclosingmember")) { + Member member = shadow.getEnclosingCodeSignature(); + if (member==null) { + return ""; + } else { + return member.toString(); + } + } else if (key.equalsIgnoreCase("joinpoint.signature")) { + return shadow.getSignature().toString(); + } else if (key.equalsIgnoreCase("joinpoint.signature.declaringtype")) { + return shadow.getSignature().getDeclaringType().toString(); + } else if (key.equalsIgnoreCase("joinpoint.signature.name")) { + return shadow.getSignature().getName(); + } else if (key.equalsIgnoreCase("joinpoint.sourcelocation.sourcefile")) { + ISourceLocation loc = shadow.getSourceLocation(); + if ((loc != null) && (loc.getSourceFile() != null)) { + return loc.getSourceFile().toString(); + } else { + return "UNKNOWN"; + } + } else if (key.equalsIgnoreCase("joinpoint.sourcelocation.line")) { + ISourceLocation loc = shadow.getSourceLocation(); + if (loc != null) { + return Integer.toString(loc.getLine()); + } else { + return "-1"; + } + } else if (key.equalsIgnoreCase("advice.aspecttype")) { + return getDeclaringType().getName(); + } else if (key.equalsIgnoreCase("advice.sourcelocation.line")) { + ISourceLocation loc = getSourceLocation(); + if ((loc != null) && (loc.getSourceFile() != null)) { + return Integer.toString(loc.getLine()); + } else { + return "-1"; + } + } else if (key.equalsIgnoreCase("advice.sourcelocation.sourcefile")) { + ISourceLocation loc = getSourceLocation(); + if ((loc != null) && (loc.getSourceFile() != null)) { + return loc.getSourceFile().toString(); + } else { + return "UNKNOWN"; + } + } else { + return "UNKNOWN_KEY{" + key + "}"; + } + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ClassAnnotationValue.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ClassAnnotationValue.java new file mode 100644 index 000000000..cb7476c32 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ClassAnnotationValue.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * Copyright (c) 2006 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 IBM initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +public class ClassAnnotationValue extends AnnotationValue { + + private String signature; + + public ClassAnnotationValue(String sig) { + super(AnnotationValue.CLASS); + this.signature = sig; + } + + public String stringify() { + return signature; + } + + public String toString() { + return signature; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/CompressingDataOutputStream.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/CompressingDataOutputStream.java new file mode 100644 index 000000000..43a66ff4f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/CompressingDataOutputStream.java @@ -0,0 +1,98 @@ +/* ******************************************************************* + * Copyright (c) 2010 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 (SpringSource) + * ******************************************************************/ +package org.aspectj.weaver; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * A variation of a DataOutputStream that is linked to a constant pool writer. The linked constant pool can be used to compress + * objects into to simple index references into the constant pool. The corresponding decompression is done in the + * VersionedDataInputStream. + * + * @author Andy Clement + */ +public class CompressingDataOutputStream extends DataOutputStream { + + private ConstantPoolWriter constantPoolWriter; + public boolean compressionEnabled = true; + + public CompressingDataOutputStream(ByteArrayOutputStream baos, ConstantPoolWriter constantPoolWriter) { + super(baos); + this.constantPoolWriter = constantPoolWriter; + } + + public CompressingDataOutputStream(FileOutputStream fos) { + super(fos); + } + + public boolean canCompress() { + return constantPoolWriter != null && compressionEnabled; + } + + /** + * @param signature of the form 'La/b/c/d;' + * @return the constant pool index + */ + public int compressSignature(String signature) { + if (constantPoolWriter == null) { + throw new IllegalStateException(); + } + return constantPoolWriter.writeUtf8(signature); + } + + /** + * @param filepath a file system path 'c:\a\b\c.txt' or '/a/b/c.txt' + * @return the constant pool index + */ + public int compressFilepath(String filepath) { + if (constantPoolWriter == null) { + throw new IllegalStateException(); + } + return constantPoolWriter.writeUtf8(filepath); + } + + /** + * @param name a simple name (for example a method or field name) + * @return the constant pool index + */ + public int compressName(String name) { + if (constantPoolWriter == null) { + throw new IllegalStateException(); + } + return constantPoolWriter.writeUtf8(name); + } + + /** + * @param name a simple name (for example a method or field name) + */ + public void writeCompressedName(String name) throws IOException { + writeShort(compressName(name)); + } + + /** + * @param signature of the form 'La/b/c/d;' + */ + public void writeCompressedSignature(String signature) throws IOException { + writeShort(compressSignature(signature)); + } + + /** + * @param path a file system path 'c:\a\b\c.txt' or '/a/b/c.txt' + */ + public void writeCompressedPath(String path) throws IOException { + writeShort(compressFilepath(path)); + } + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ConcreteTypeMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ConcreteTypeMunger.java new file mode 100644 index 000000000..81cf351a9 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ConcreteTypeMunger.java @@ -0,0 +1,164 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.util.PartialOrder; + +public abstract class ConcreteTypeMunger implements PartialOrder.PartialComparable { + protected ResolvedTypeMunger munger; + protected ResolvedType aspectType; + + public ConcreteTypeMunger(ResolvedTypeMunger munger, ResolvedType aspectType) { + this.munger = munger; + this.aspectType = aspectType; + } + + /** + * Equivalence can be true for an EclipseTypeMunger and a BcelTypeMunger that represent the same transformation (just at + * different points in the pipeline). + */ + public boolean equivalentTo(Object other) { + if (!(other instanceof ConcreteTypeMunger)) { + return false; + } + ConcreteTypeMunger o = (ConcreteTypeMunger) other; + ResolvedTypeMunger otherTypeMunger = o.getMunger(); + ResolvedTypeMunger thisTypeMunger = getMunger(); + if (thisTypeMunger instanceof NewConstructorTypeMunger && otherTypeMunger instanceof NewConstructorTypeMunger) { + return (((NewConstructorTypeMunger) otherTypeMunger).equivalentTo(thisTypeMunger)) + && ((o.getAspectType() == null) ? (getAspectType() == null) : o.getAspectType().equals(getAspectType())); + } else { + return ((otherTypeMunger == null) ? (thisTypeMunger == null) : otherTypeMunger.equals(thisTypeMunger)) + && ((o.getAspectType() == null) ? (getAspectType() == null) : o.getAspectType().equals(getAspectType())); + } + } + + // public abstract boolean munge(LazyClassGen gen); + + /** + * returns null for mungers that are used internally, but were not part of a declared thing in source code. + */ + public ResolvedTypeMunger getMunger() { + return munger; + } + + public ResolvedType getAspectType() { + return aspectType; + } + + public ResolvedMember getSignature() { + return munger.getSignature(); + } + + public World getWorld() { + return aspectType.getWorld(); + } + + public ISourceLocation getSourceLocation() { + if (munger == null) { + return null; + } + return munger.getSourceLocation(); // XXX + } + + public boolean matches(ResolvedType onType) { + if (munger == null) { + throw new RuntimeException("huh: " + this); + } + return munger.matches(onType, aspectType); + } + + public ResolvedMember getMatchingSyntheticMember(Member member) { + return munger.getMatchingSyntheticMember(member, aspectType); + } + + public int compareTo(Object other) { + ConcreteTypeMunger o = (ConcreteTypeMunger) other; + + ResolvedType otherAspect = o.aspectType; + + if (aspectType.equals(otherAspect)) { + return getSignature().getStart() < o.getSignature().getStart() ? -1 : +1; + } else if (aspectType.isAssignableFrom(o.aspectType)) { + return +1; + } else if (o.aspectType.isAssignableFrom(aspectType)) { + return -1; + } else { + return 0; + } + } + + public int fallbackCompareTo(Object other) { + // ConcreteTypeMunger o = (ConcreteTypeMunger) other; + return 0; + } + + /** + * returns true if the ITD target type used type variables, for example I<T>. When they are specified like this, the ITDs + * 'share' type variables with the generic type. Usually this method is called because we need to know whether to tailor the + * munger for addition to a particular type. For example: <code> + * interface I<T> {} + * + * aspect X implements I<String> { + * List<T> I<T>.foo { return null; } + * } + * </code> In this case the munger matches X but it matches with the form <code> + * List<String> foo() { return null; } + * </code> + */ + public boolean isTargetTypeParameterized() { + if (munger == null) { + return false; + } + return munger.sharesTypeVariablesWithGenericType(); + } + + /** + * For an ITD made on a generic type that shares type variables with that target type, this method will tailor the ITD for a + * particular usage of the generic type - either in its raw or parameterized form. + */ + public abstract ConcreteTypeMunger parameterizedFor(ResolvedType targetType); + + public boolean isLateMunger() { + if (munger == null) { + return false; + } + return munger.isLateMunger(); + } + + public abstract ConcreteTypeMunger parameterizeWith(Map<String, UnresolvedType> parameterizationMap, World world); + + /** + * Some type mungers are created purely to help with the implementation of shadow mungers. For example to support the cflow() + * pointcut we create a new cflow field in the aspect, and that is added via a BcelCflowCounterFieldAdder. + * + * During compilation we need to compare sets of type mungers, and if some only come into existence after the 'shadowy' type + * things have been processed, we need to ignore them during the comparison. + * + * Returning true from this method indicates the type munger exists to support 'shadowy' stuff - and so can be ignored in some + * comparison. + */ + public boolean existsToSupportShadowMunging() { + if (munger != null) { + return munger.existsToSupportShadowMunging(); + } + return false; + } + + public boolean shouldOverwrite() { + return true; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ConstantPoolReader.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ConstantPoolReader.java new file mode 100644 index 000000000..1345e7e09 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ConstantPoolReader.java @@ -0,0 +1,23 @@ +/* ******************************************************************* + * Copyright (c) 2010 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 (SpringSource) + * ******************************************************************/ +package org.aspectj.weaver; + +/** + * Used during attribute reading to decode constant pool references. + * + * @author Andy Clement + */ +public interface ConstantPoolReader { + + String readUtf8(int constantPoolIndex); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ConstantPoolWriter.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ConstantPoolWriter.java new file mode 100644 index 000000000..2b145a1de --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ConstantPoolWriter.java @@ -0,0 +1,23 @@ +/* ******************************************************************* + * Copyright (c) 2010 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 (SpringSource) + * ******************************************************************/ +package org.aspectj.weaver; + +/** + * Used during attribute writing to encode common strings/etc as constant pool references. + * + * @author Andy Clement + */ +public interface ConstantPoolWriter { + + int writeUtf8(String string); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Constants.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Constants.java new file mode 100644 index 000000000..bdbde3853 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Constants.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM + * 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 API and implementation + *******************************************************************************/ +package org.aspectj.weaver; + +/** + * Some useful weaver constants. + * + * Current uses: 1. Holds values that are necessary for working with 1.5 code but which don't exist in a 1.4 world. + */ +public interface Constants { + + public final static int ACC_BRIDGE = 0x0040; + public final static int ACC_VARARGS = 0x0080; + + public final static String RUNTIME_LEVEL_12 = "1.2"; + public final static String RUNTIME_LEVEL_15 = "1.5"; + public final static String RUNTIME_LEVEL_19 = "1.9"; + + // Default for 1.5.0 + public final static String RUNTIME_LEVEL_DEFAULT = RUNTIME_LEVEL_15; +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/CrosscuttingMembers.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/CrosscuttingMembers.java new file mode 100644 index 000000000..8e41c0a82 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/CrosscuttingMembers.java @@ -0,0 +1,591 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.DeclareAnnotation; +import org.aspectj.weaver.patterns.DeclareErrorOrWarning; +import org.aspectj.weaver.patterns.DeclareParents; +import org.aspectj.weaver.patterns.DeclarePrecedence; +import org.aspectj.weaver.patterns.DeclareSoft; +import org.aspectj.weaver.patterns.DeclareTypeErrorOrWarning; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.PointcutRewriter; + +/** + * This holds on to all members that have an invasive effect outside of there own compilation unit. These members need to be all + * gathered up and in a world before any weaving can take place. + * + * They are also important in the compilation process and need to be gathered up before the inter-type declaration weaving stage + * (unsurprisingly). + * + * All members are concrete. + * + * @author Jim Hugunin + */ +public class CrosscuttingMembers { + private final ResolvedType inAspect; + private final World world; + + private PerClause perClause; + + private List<ShadowMunger> shadowMungers = new ArrayList<ShadowMunger>(4); + private List<ConcreteTypeMunger> typeMungers = new ArrayList<ConcreteTypeMunger>(4); + private List<ConcreteTypeMunger> lateTypeMungers = new ArrayList<ConcreteTypeMunger>(0); + + private Set<DeclareParents> declareParents = new HashSet<DeclareParents>(); + private Set<DeclareSoft> declareSofts = new HashSet<DeclareSoft>(); + private List<Declare> declareDominates = new ArrayList<Declare>(4); + + // These are like declare parents type mungers + private Set<DeclareAnnotation> declareAnnotationsOnType = new LinkedHashSet<DeclareAnnotation>(); + private Set<DeclareAnnotation> declareAnnotationsOnField = new LinkedHashSet<DeclareAnnotation>(); + private Set<DeclareAnnotation> declareAnnotationsOnMethods = new LinkedHashSet<DeclareAnnotation>(); + // declareAnnotationsOnMethods includes constructors too + + private Set<DeclareTypeErrorOrWarning> declareTypeEow = new HashSet<DeclareTypeErrorOrWarning>(); + + private boolean shouldConcretizeIfNeeded = true; + + public CrosscuttingMembers(ResolvedType inAspect, boolean shouldConcretizeIfNeeded) { + this.inAspect = inAspect; + world = inAspect.getWorld(); + this.shouldConcretizeIfNeeded = shouldConcretizeIfNeeded; + } + + private final Hashtable<String, Object> cflowFields = new Hashtable<String, Object>(); + private final Hashtable<String, Object> cflowBelowFields = new Hashtable<String, Object>(); + + // public void addConcreteShadowMungers(Collection c) { + // shadowMungers.addAll(c); + // } + + public void addConcreteShadowMunger(ShadowMunger m) { + // assert m is concrete + shadowMungers.add(m); + } + + public void addShadowMungers(Collection<ShadowMunger> c) { + for (ShadowMunger munger : c) { + addShadowMunger(munger); + } + } + + private void addShadowMunger(ShadowMunger m) { + if (inAspect.isAbstract()) { + return; // mungers for abstract aspects are not added + } + addConcreteShadowMunger(m.concretize(inAspect, world, perClause)); + } + + public void addTypeMungers(Collection<ConcreteTypeMunger> c) { + typeMungers.addAll(c); + } + + public void addTypeMunger(ConcreteTypeMunger m) { + if (m == null) { + throw new Error("FIXME AV - should not happen or what ?");// return; + } + typeMungers.add(m); + } + + public void addLateTypeMungers(Collection<ConcreteTypeMunger> c) { + lateTypeMungers.addAll(c); + } + + public void addLateTypeMunger(ConcreteTypeMunger m) { + lateTypeMungers.add(m); + } + + public void addDeclares(Collection<Declare> declares) { + for (Declare declare : declares) { + addDeclare(declare); + } + } + + public void addDeclare(Declare declare) { + // this is not extensible, oh well + if (declare instanceof DeclareErrorOrWarning) { + ShadowMunger m = new Checker((DeclareErrorOrWarning) declare); + m.setDeclaringType(declare.getDeclaringType()); + addShadowMunger(m); + } else if (declare instanceof DeclarePrecedence) { + declareDominates.add(declare); + } else if (declare instanceof DeclareParents) { + DeclareParents dp = (DeclareParents) declare; + exposeTypes(dp.getParents().getExactTypes()); + declareParents.add(dp); + } else if (declare instanceof DeclareSoft) { + DeclareSoft d = (DeclareSoft) declare; + // Ordered so that during concretization we can check the related + // munger + ShadowMunger m = Advice.makeSoftener(world, d.getPointcut(), d.getException(), inAspect, d); + m.setDeclaringType(d.getDeclaringType()); + Pointcut concretePointcut = d.getPointcut().concretize(inAspect, d.getDeclaringType(), 0, m); + m.pointcut = concretePointcut; + declareSofts.add(new DeclareSoft(d.getException(), concretePointcut)); + addConcreteShadowMunger(m); + } else if (declare instanceof DeclareAnnotation) { + // FIXME asc perf Possible Improvement. Investigate why this is + // called twice in a weave ? + DeclareAnnotation da = (DeclareAnnotation) declare; + if (da.getAspect() == null) { + da.setAspect(inAspect); + } + if (da.isDeclareAtType()) { + declareAnnotationsOnType.add(da); + } else if (da.isDeclareAtField()) { + declareAnnotationsOnField.add(da); + } else if (da.isDeclareAtMethod() || da.isDeclareAtConstuctor()) { + declareAnnotationsOnMethods.add(da); + } + } else if (declare instanceof DeclareTypeErrorOrWarning) { + declareTypeEow.add((DeclareTypeErrorOrWarning) declare); + } else { + throw new RuntimeException("unimplemented"); + } + } + + public void exposeTypes(List<UnresolvedType> typesToExpose) { + for (UnresolvedType typeToExpose : typesToExpose) { + exposeType(typeToExpose); + } + } + + public void exposeType(UnresolvedType typeToExpose) { + if (ResolvedType.isMissing(typeToExpose)) { + return; + } + if (typeToExpose.isParameterizedType() || typeToExpose.isRawType()) { + if (typeToExpose instanceof ResolvedType) { + typeToExpose = ((ResolvedType) typeToExpose).getGenericType(); + } else { + typeToExpose = UnresolvedType.forSignature(typeToExpose.getErasureSignature()); + } + } + // Check we haven't already got a munger for this: + String signatureToLookFor = typeToExpose.getSignature(); + for (Iterator<ConcreteTypeMunger> iterator = typeMungers.iterator(); iterator.hasNext();) { + ConcreteTypeMunger cTM = iterator.next(); + ResolvedTypeMunger rTM = cTM.getMunger(); + if (rTM != null && rTM instanceof ExposeTypeMunger) { + String exposedType = ((ExposeTypeMunger) rTM).getExposedTypeSignature(); + if (exposedType.equals(signatureToLookFor)) { + return; // dont need to bother + } + } + } + addTypeMunger(world.getWeavingSupport().concreteTypeMunger(new ExposeTypeMunger(typeToExpose), inAspect)); + // ResolvedMember member = new ResolvedMemberImpl( + // Member.STATIC_INITIALIZATION, typeToExpose, 0, UnresolvedType.VOID, + // "<clinit>", UnresolvedType.NONE); + // addTypeMunger(world.concreteTypeMunger( + // new PrivilegedAccessMunger(member), inAspect)); + } + + public void addPrivilegedAccesses(Collection<ResolvedMember> accessedMembers) { + int version = inAspect.getCompilerVersion(); + for (ResolvedMember member : accessedMembers) { + // Looking it up ensures we get the annotations - the accessedMembers are just retrieved from the attribute and + // don't have that information + ResolvedMember resolvedMember = world.resolve(member); + + // pr333469 + // If the member is for an ITD (e.g. serialVersionUID) then during resolution we may resolve it on + // a supertype because it doesn't yet exist on the target. + // For example: MyList extends ArrayList<String> and the ITD is on MyList - after resolution it may be: + // ArrayList<String>.serialVersionUID, we need to avoid that happening + + if (resolvedMember == null) { + // can happen for ITDs - are there many privileged access ITDs?? + resolvedMember = member; + if (resolvedMember.hasBackingGenericMember()) { + resolvedMember = resolvedMember.getBackingGenericMember(); + } + } else { + UnresolvedType unresolvedDeclaringType = member.getDeclaringType().getRawType(); + UnresolvedType resolvedDeclaringType = resolvedMember.getDeclaringType().getRawType(); + if (!unresolvedDeclaringType.equals(resolvedDeclaringType)) { + resolvedMember = member; + } + } + PrivilegedAccessMunger privilegedAccessMunger = new PrivilegedAccessMunger(resolvedMember, + version >= WeaverVersionInfo.WEAVER_VERSION_AJ169); + ConcreteTypeMunger concreteTypeMunger = world.getWeavingSupport().concreteTypeMunger(privilegedAccessMunger, inAspect); + addTypeMunger(concreteTypeMunger); + } + } + + public Collection<ShadowMunger> getCflowEntries() { + List<ShadowMunger> ret = new ArrayList<ShadowMunger>(); + for (ShadowMunger m : shadowMungers) { + if (m instanceof Advice) { + Advice a = (Advice) m; + if (a.getKind().isCflow()) { + ret.add(a); + } + } + } + return ret; + } + + /** + * Updates the records if something has changed. This is called at most twice, firstly whilst collecting ITDs and declares. At + * this point the CrosscuttingMembers we're comparing ourselves with doesn't know about shadowmungers. Therefore a straight + * comparison with the existing list of shadowmungers would return that something has changed even though it might not have, so + * in this first round we ignore the shadowMungers. The second time this is called is whilst we're preparing to weave. At this + * point we know everything in the system and so we're able to compare the shadowMunger list. (see bug 129163) + * + * @param other + * @param careAboutShadowMungers + * @return true if something has changed since the last time this method was called, false otherwise + */ + public boolean replaceWith(CrosscuttingMembers other, boolean careAboutShadowMungers) { + boolean changed = false; + + if (careAboutShadowMungers) { + if (perClause == null || !perClause.equals(other.perClause)) { + changed = true; + perClause = other.perClause; + } + } + + // XXX all of the below should be set equality rather than list equality + // System.err.println("old: " + shadowMungers + " new: " + + // other.shadowMungers); + + if (careAboutShadowMungers) { + // bug 129163: use set equality rather than list equality + Set<ShadowMunger> theseShadowMungers = new HashSet<ShadowMunger>(); + Set<ShadowMunger> theseInlinedAroundMungers = new HashSet<ShadowMunger>(); + for (ShadowMunger munger : shadowMungers) { + if (munger instanceof Advice) { + Advice adviceMunger = (Advice) munger; + // bug 154054: if we're around advice that has been inlined + // then we need to do more checking than existing equals + // methods allow + if (!world.isXnoInline() && adviceMunger.getKind().equals(AdviceKind.Around)) { + theseInlinedAroundMungers.add(adviceMunger); + } else { + theseShadowMungers.add(adviceMunger); + } + } else { + theseShadowMungers.add(munger); + } + } + Set<ShadowMunger> tempSet = new HashSet<ShadowMunger>(); + tempSet.addAll(other.shadowMungers); + Set<ShadowMunger> otherShadowMungers = new HashSet<ShadowMunger>(); + Set<ShadowMunger> otherInlinedAroundMungers = new HashSet<ShadowMunger>(); + for (ShadowMunger munger : tempSet) { + if (munger instanceof Advice) { + Advice adviceMunger = (Advice) munger; + // bug 154054: if we're around advice that has been inlined + // then we need to do more checking than existing equals + // methods allow + if (!world.isXnoInline() && adviceMunger.getKind().equals(AdviceKind.Around)) { + otherInlinedAroundMungers.add(rewritePointcutInMunger(adviceMunger)); + } else { + otherShadowMungers.add(rewritePointcutInMunger(adviceMunger)); + } + } else { + otherShadowMungers.add(rewritePointcutInMunger(munger)); + } + } + if (!theseShadowMungers.equals(otherShadowMungers)) { + changed = true; + } + if (!equivalent(theseInlinedAroundMungers, otherInlinedAroundMungers)) { + changed = true; + } + + // bug 158573 - if there are no changes then preserve whether + // or not a particular shadowMunger has matched something. + if (!changed) { + for (ShadowMunger munger : shadowMungers) { + int i = other.shadowMungers.indexOf(munger); + ShadowMunger otherMunger = other.shadowMungers.get(i); + if (munger instanceof Advice) { + ((Advice) otherMunger).setHasMatchedSomething(((Advice) munger).hasMatchedSomething()); + } + } + } + // replace the existing list of shadowmungers with the + // new ones in case anything like the sourcelocation has + // changed, however, don't want this flagged as a change + // which will force a full build - bug 134541 + shadowMungers = other.shadowMungers; + } + + // bug 129163: use set equality rather than list equality and + // if we dont care about shadow mungers then ignore those + // typeMungers which are created to help with the implementation + // of shadowMungers + Set<Object> theseTypeMungers = new HashSet<Object>(); + Set<Object> otherTypeMungers = new HashSet<Object>(); + if (!careAboutShadowMungers) { + for (Iterator<ConcreteTypeMunger> iter = typeMungers.iterator(); iter.hasNext();) { + Object o = iter.next(); + if (o instanceof ConcreteTypeMunger) { + ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) o; + if (!typeMunger.existsToSupportShadowMunging()) { + theseTypeMungers.add(typeMunger); + } + } else { + theseTypeMungers.add(o); + } + } + + for (Iterator<ConcreteTypeMunger> iter = other.typeMungers.iterator(); iter.hasNext();) { + Object o = iter.next(); + if (o instanceof ConcreteTypeMunger) { + ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) o; + if (!typeMunger.existsToSupportShadowMunging()) { + otherTypeMungers.add(typeMunger); + } + } else { + otherTypeMungers.add(o); + } + } + } else { + theseTypeMungers.addAll(typeMungers); + otherTypeMungers.addAll(other.typeMungers); + } + + // initial go at equivalence logic rather than set compare (see + // pr133532) + if (theseTypeMungers.size() != otherTypeMungers.size()) { + changed = true; + typeMungers = other.typeMungers; + } else { + boolean shouldOverwriteThis = false; + boolean foundInequality = false; + for (Iterator<Object> iter = theseTypeMungers.iterator(); iter.hasNext() && !foundInequality;) { + Object thisOne = iter.next(); + boolean foundInOtherSet = false; + for (Object otherOne : otherTypeMungers) { + if (thisOne instanceof ConcreteTypeMunger) { + if (((ConcreteTypeMunger) thisOne).shouldOverwrite()) { + shouldOverwriteThis = true; + } + } + if (thisOne instanceof ConcreteTypeMunger && otherOne instanceof ConcreteTypeMunger) { + if (((ConcreteTypeMunger) thisOne).equivalentTo(otherOne)) { + foundInOtherSet = true; + } else if (thisOne.equals(otherOne)) { + foundInOtherSet = true; + } + } else { + if (thisOne.equals(otherOne)) { + foundInOtherSet = true; + } + } + } + if (!foundInOtherSet) { + foundInequality = true; + } + } + if (foundInequality) { + // System.out.println("type munger change"); + changed = true; + } + if (shouldOverwriteThis) { + typeMungers = other.typeMungers; + } + } + // if (!theseTypeMungers.equals(otherTypeMungers)) { + // changed = true; + // typeMungers = other.typeMungers; + // } + + if (!lateTypeMungers.equals(other.lateTypeMungers)) { + changed = true; + lateTypeMungers = other.lateTypeMungers; + } + + if (!declareDominates.equals(other.declareDominates)) { + changed = true; + declareDominates = other.declareDominates; + } + + if (!declareParents.equals(other.declareParents)) { + // Are the differences just because of a mixin? These are not created until weave time so should be gotten rid of for + // the up front comparison + if (!careAboutShadowMungers) { + // this means we are in front end compilation and if the differences are purely mixin parents, we can continue OK + Set<DeclareParents> trimmedThis = new HashSet<DeclareParents>(); + for (Iterator<DeclareParents> iterator = declareParents.iterator(); iterator.hasNext();) { + DeclareParents decp = iterator.next(); + if (!decp.isMixin()) { + trimmedThis.add(decp); + } + } + Set<DeclareParents> trimmedOther = new HashSet<DeclareParents>(); + for (Iterator<DeclareParents> iterator = other.declareParents.iterator(); iterator.hasNext();) { + DeclareParents decp = iterator.next(); + if (!decp.isMixin()) { + trimmedOther.add(decp); + } + } + if (!trimmedThis.equals(trimmedOther)) { + changed = true; + declareParents = other.declareParents; + } + } else { + changed = true; + declareParents = other.declareParents; + } + } + + if (!declareSofts.equals(other.declareSofts)) { + changed = true; + declareSofts = other.declareSofts; + } + + // DECAT for when attempting to replace an aspect + if (!declareAnnotationsOnType.equals(other.declareAnnotationsOnType)) { + changed = true; + declareAnnotationsOnType = other.declareAnnotationsOnType; + } + + if (!declareAnnotationsOnField.equals(other.declareAnnotationsOnField)) { + changed = true; + declareAnnotationsOnField = other.declareAnnotationsOnField; + } + + if (!declareAnnotationsOnMethods.equals(other.declareAnnotationsOnMethods)) { + changed = true; + declareAnnotationsOnMethods = other.declareAnnotationsOnMethods; + } + if (!declareTypeEow.equals(other.declareTypeEow)) { + changed = true; + declareTypeEow = other.declareTypeEow; + } + + return changed; + } + + private boolean equivalent(Set<ShadowMunger> theseInlinedAroundMungers, Set<ShadowMunger> otherInlinedAroundMungers) { + if (theseInlinedAroundMungers.size() != otherInlinedAroundMungers.size()) { + return false; + } + for (Iterator<ShadowMunger> iter = theseInlinedAroundMungers.iterator(); iter.hasNext();) { + Advice thisAdvice = (Advice) iter.next(); + boolean foundIt = false; + for (Iterator<ShadowMunger> iterator = otherInlinedAroundMungers.iterator(); iterator.hasNext();) { + Advice otherAdvice = (Advice) iterator.next(); + if (thisAdvice.equals(otherAdvice)) { + if (thisAdvice.getSignature() instanceof ResolvedMemberImpl) { + if (((ResolvedMemberImpl) thisAdvice.getSignature()).isEquivalentTo(otherAdvice.getSignature())) { + foundIt = true; + continue; + } + } + return false; + } + } + if (!foundIt) { + return false; + } + } + return true; + } + + private ShadowMunger rewritePointcutInMunger(ShadowMunger munger) { + PointcutRewriter pr = new PointcutRewriter(); + Pointcut p = munger.getPointcut(); + Pointcut newP = pr.rewrite(p); + if (p.m_ignoreUnboundBindingForNames.length != 0) { + // *sigh* dirty fix for dirty hacky implementation pr149305 + newP.m_ignoreUnboundBindingForNames = p.m_ignoreUnboundBindingForNames; + } + munger.setPointcut(newP); + return munger; + } + + public void setPerClause(PerClause perClause) { + if (shouldConcretizeIfNeeded) { + this.perClause = perClause.concretize(inAspect); + } else { + this.perClause = perClause; + } + } + + public List<Declare> getDeclareDominates() { + return declareDominates; + } + + public Collection<DeclareParents> getDeclareParents() { + return declareParents; + } + + public Collection<DeclareSoft> getDeclareSofts() { + return declareSofts; + } + + public List<ShadowMunger> getShadowMungers() { + return shadowMungers; + } + + public List<ConcreteTypeMunger> getTypeMungers() { + return typeMungers; + } + + public List<ConcreteTypeMunger> getLateTypeMungers() { + return lateTypeMungers; + } + + public Collection<DeclareAnnotation> getDeclareAnnotationOnTypes() { + return declareAnnotationsOnType; + } + + public Collection<DeclareAnnotation> getDeclareAnnotationOnFields() { + return declareAnnotationsOnField; + } + + /** + * includes declare @method and @constructor + */ + public Collection<DeclareAnnotation> getDeclareAnnotationOnMethods() { + return declareAnnotationsOnMethods; + } + + public Collection<DeclareTypeErrorOrWarning> getDeclareTypeErrorOrWarning() { + return declareTypeEow; + } + + public Map<String, Object> getCflowBelowFields() { + return cflowBelowFields; + } + + public Map<String, Object> getCflowFields() { + return cflowFields; + } + + public void clearCaches() { + cflowFields.clear(); + cflowBelowFields.clear(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/CrosscuttingMembersSet.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/CrosscuttingMembersSet.java new file mode 100644 index 000000000..969f96ece --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/CrosscuttingMembersSet.java @@ -0,0 +1,459 @@ +/* ******************************************************************* + * Copyright (c) 2002-2009 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: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.DeclareAnnotation; +import org.aspectj.weaver.patterns.DeclareParents; +import org.aspectj.weaver.patterns.DeclareSoft; +import org.aspectj.weaver.patterns.DeclareTypeErrorOrWarning; +import org.aspectj.weaver.patterns.IVerificationRequired; + +/** + * This holds on to all CrosscuttingMembers for a world. It handles management of change. + * + * @author Jim Hugunin + * @author Andy Clement + */ +public class CrosscuttingMembersSet { + + private transient World world; + + // FIXME AV - ? we may need a sequencedHashMap there to ensure source based precedence for @AJ advice + private final Map<ResolvedType, CrosscuttingMembers> members = new HashMap<ResolvedType, CrosscuttingMembers>(); + + // List of things to be verified once the type system is 'complete' + private transient List<IVerificationRequired> verificationList = null; + + private List<ShadowMunger> shadowMungers = null; + private List<ConcreteTypeMunger> typeMungers = null; + private List<ConcreteTypeMunger> lateTypeMungers = null; + private List<DeclareSoft> declareSofts = null; + private List<DeclareParents> declareParents = null; + private List<DeclareAnnotation> declareAnnotationOnTypes = null; + private List<DeclareAnnotation> declareAnnotationOnFields = null; + private List<DeclareAnnotation> declareAnnotationOnMethods = null; // includes constructors + private List<DeclareTypeErrorOrWarning> declareTypeEows = null; + private List<Declare> declareDominates = null; + private boolean changedSinceLastReset = false; + + public CrosscuttingMembersSet(World world) { + this.world = world; + } + + public boolean addOrReplaceAspect(ResolvedType aspectType) { + return addOrReplaceAspect(aspectType, true); + } + + /** + * Check if any parent aspects of the supplied aspect have unresolved dependencies (and so + * should cause this aspect to be turned off). + * @param aspectType the aspect whose parents should be checked + * @return true if this aspect should be excluded because of a parents' missing dependencies + */ + private boolean excludeDueToParentAspectHavingUnresolvedDependency(ResolvedType aspectType) { + ResolvedType parent = aspectType.getSuperclass(); + boolean excludeDueToParent = false; + while (parent != null) { + if (parent.isAspect() && parent.isAbstract() && world.hasUnsatisfiedDependency(parent)) { + if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) { + world.getMessageHandler().handleMessage( + MessageUtil.info("deactivating aspect '" + aspectType.getName() + "' as the parent aspect '"+parent.getName()+ + "' has unsatisfied dependencies")); + } + excludeDueToParent = true; + } + parent = parent.getSuperclass(); + } + return excludeDueToParent; + } + + /** + * @return whether or not that was a change to the global signature XXX for efficiency we will need a richer representation than + * this + */ + public boolean addOrReplaceAspect(ResolvedType aspectType, boolean inWeavingPhase) { + if (!world.isAspectIncluded(aspectType)) { + return false; + } + if (world.hasUnsatisfiedDependency(aspectType)) { + return false; + } + // Abstract super aspects might have unsatisfied dependencies + if (excludeDueToParentAspectHavingUnresolvedDependency(aspectType)) { + return false; + } + + boolean change = false; + CrosscuttingMembers xcut = members.get(aspectType); + if (xcut == null) { + members.put(aspectType, aspectType.collectCrosscuttingMembers(inWeavingPhase)); + clearCaches(); + change = true; + } else { + if (xcut.replaceWith(aspectType.collectCrosscuttingMembers(inWeavingPhase), inWeavingPhase)) { + clearCaches(); + change = true; + } else { + if (inWeavingPhase) { + // bug 134541 - even though we haven't changed we may have updated the + // sourcelocation for the shadowMunger which we need to pick up + shadowMungers = null; + } + change = false; + } + } + if (aspectType.isAbstract()) { + // we might have sub-aspects that need to re-collect their crosscutting members from us + boolean ancestorChange = addOrReplaceDescendantsOf(aspectType, inWeavingPhase); + change = change || ancestorChange; + } + changedSinceLastReset = changedSinceLastReset || change; + + return change; + } + + private boolean addOrReplaceDescendantsOf(ResolvedType aspectType, boolean inWeavePhase) { + // System.err.println("Looking at descendants of "+aspectType.getName()); + Set<ResolvedType> knownAspects = members.keySet(); + Set<ResolvedType> toBeReplaced = new HashSet<ResolvedType>(); + for (Iterator<ResolvedType> it = knownAspects.iterator(); it.hasNext();) { + ResolvedType candidateDescendant = it.next(); + // allowMissing = true - if something is missing, it really probably is not a descendant + if ((candidateDescendant != aspectType) && (aspectType.isAssignableFrom(candidateDescendant, true))) { + toBeReplaced.add(candidateDescendant); + } + } + boolean change = false; + for (Iterator<ResolvedType> it = toBeReplaced.iterator(); it.hasNext();) { + ResolvedType next = it.next(); + boolean thisChange = addOrReplaceAspect(next, inWeavePhase); + change = change || thisChange; + } + return change; + } + + public void addAdviceLikeDeclares(ResolvedType aspectType) { + if (!members.containsKey(aspectType)) { + return; + } + CrosscuttingMembers xcut = members.get(aspectType); + xcut.addDeclares(aspectType.collectDeclares(true)); + } + + public boolean deleteAspect(UnresolvedType aspectType) { + boolean isAspect = members.remove(aspectType) != null; + clearCaches(); + return isAspect; + } + + public boolean containsAspect(UnresolvedType aspectType) { + return members.containsKey(aspectType); + } + + // XXX only for testing + public void addFixedCrosscuttingMembers(ResolvedType aspectType) { + members.put(aspectType, aspectType.crosscuttingMembers); + clearCaches(); + } + + private void clearCaches() { + shadowMungers = null; + typeMungers = null; + lateTypeMungers = null; + declareSofts = null; + declareParents = null; + declareAnnotationOnFields = null; + declareAnnotationOnMethods = null; + declareAnnotationOnTypes = null; + declareDominates = null; + } + + public List<ShadowMunger> getShadowMungers() { + if (shadowMungers == null) { + List<ShadowMunger> ret = new ArrayList<ShadowMunger>(); + for (Iterator<CrosscuttingMembers> i = members.values().iterator(); i.hasNext();) { + ret.addAll(i.next().getShadowMungers()); + } + shadowMungers = ret; + } + return shadowMungers; + } + + public List<ConcreteTypeMunger> getTypeMungers() { + if (typeMungers == null) { + List<ConcreteTypeMunger> ret = new ArrayList<ConcreteTypeMunger>(); + for (CrosscuttingMembers xmembers : members.values()) { + // With 1.6.9 there is a change that enables use of more optimal accessors (accessors for private fields). + // Here is where we determine if two aspects are asking for access to the same field. If they are + // and + // In the new style multiple aspects can share the same privileged accessors, so here we check if + // two aspects are asking for access to the same field. If they are then we don't add a duplicate + // accessor. + for (ConcreteTypeMunger mungerToAdd : xmembers.getTypeMungers()) { + ResolvedTypeMunger resolvedMungerToAdd = mungerToAdd.getMunger(); + if (isNewStylePrivilegedAccessMunger(resolvedMungerToAdd)) { + String newFieldName = resolvedMungerToAdd.getSignature().getName(); + boolean alreadyExists = false; + for (ConcreteTypeMunger existingMunger : ret) { + ResolvedTypeMunger existing = existingMunger.getMunger(); + if (isNewStylePrivilegedAccessMunger(existing)) { + String existingFieldName = existing.getSignature().getName(); + if (existingFieldName.equals(newFieldName) + && existing.getSignature().getDeclaringType().equals( + resolvedMungerToAdd.getSignature().getDeclaringType())) { + alreadyExists = true; + break; + } + } + } + if (!alreadyExists) { + ret.add(mungerToAdd); + } + } else { + ret.add(mungerToAdd); + } + } + } + typeMungers = ret; + } + return typeMungers; + } + + /** + * Retrieve a subset of all known mungers, those of a specific kind. + * + * @param kind the kind of munger requested + * @return a list of those mungers (list is empty if none found) + */ + public List<ConcreteTypeMunger> getTypeMungersOfKind(ResolvedTypeMunger.Kind kind) { + List<ConcreteTypeMunger> collected = null; + for (ConcreteTypeMunger typeMunger : typeMungers) { + if (typeMunger.getMunger() != null && typeMunger.getMunger().getKind() == kind) { + if (collected == null) { + collected = new ArrayList<ConcreteTypeMunger>(); + } + collected.add(typeMunger); + } + } + if (collected == null) { + return Collections.emptyList(); + } else { + return collected; + } + } + + /** + * Determine if the type munger is: (1) for privileged access (2) for a normally non visible field (3) is from an aspect wanting + * 'old style' (ie. long) accessor names + */ + private boolean isNewStylePrivilegedAccessMunger(ResolvedTypeMunger typeMunger) { + boolean b = (typeMunger != null && typeMunger.getKind() == ResolvedTypeMunger.PrivilegedAccess && typeMunger.getSignature() + .getKind() == Member.FIELD); + if (!b) { + return b; + } + PrivilegedAccessMunger privAccessMunger = (PrivilegedAccessMunger) typeMunger; + return privAccessMunger.shortSyntax; + } + + public List<ConcreteTypeMunger> getLateTypeMungers() { + if (lateTypeMungers == null) { + List<ConcreteTypeMunger> ret = new ArrayList<ConcreteTypeMunger>(); + for (Iterator<CrosscuttingMembers> i = members.values().iterator(); i.hasNext();) { + ret.addAll(i.next().getLateTypeMungers()); + } + lateTypeMungers = ret; + } + return lateTypeMungers; + } + + public List<DeclareSoft> getDeclareSofts() { + if (declareSofts == null) { + Set<DeclareSoft> ret = new HashSet<DeclareSoft>(); + for (Iterator<CrosscuttingMembers> i = members.values().iterator(); i.hasNext();) { + ret.addAll(i.next().getDeclareSofts()); + } + declareSofts = new ArrayList<DeclareSoft>(); + declareSofts.addAll(ret); + } + return declareSofts; + } + + public List<DeclareParents> getDeclareParents() { + if (declareParents == null) { + Set<DeclareParents> ret = new HashSet<DeclareParents>(); + for (Iterator<CrosscuttingMembers> i = members.values().iterator(); i.hasNext();) { + ret.addAll(i.next().getDeclareParents()); + } + declareParents = new ArrayList<DeclareParents>(); + declareParents.addAll(ret); + } + return declareParents; + } + + /** + * @return an amalgamation of the declare @type statements. + */ + public List<DeclareAnnotation> getDeclareAnnotationOnTypes() { + if (declareAnnotationOnTypes == null) { + Set<DeclareAnnotation> ret = new LinkedHashSet<DeclareAnnotation>(); + for (Iterator<CrosscuttingMembers> i = members.values().iterator(); i.hasNext();) { + ret.addAll(i.next().getDeclareAnnotationOnTypes()); + } + declareAnnotationOnTypes = new ArrayList<DeclareAnnotation>(); + declareAnnotationOnTypes.addAll(ret); + } + return declareAnnotationOnTypes; + } + + /** + * @return an amalgamation of the declare @field statements. + */ + public List<DeclareAnnotation> getDeclareAnnotationOnFields() { + if (declareAnnotationOnFields == null) { + Set<DeclareAnnotation> ret = new LinkedHashSet<DeclareAnnotation>(); + for (Iterator<CrosscuttingMembers> i = members.values().iterator(); i.hasNext();) { + ret.addAll(i.next().getDeclareAnnotationOnFields()); + } + declareAnnotationOnFields = new ArrayList<DeclareAnnotation>(); + declareAnnotationOnFields.addAll(ret); + } + return declareAnnotationOnFields; + } + + /** + * @return an amalgamation of the declare @method/@constructor statements. + */ + public List<DeclareAnnotation> getDeclareAnnotationOnMethods() { + if (declareAnnotationOnMethods == null) { + Set<DeclareAnnotation> ret = new LinkedHashSet<DeclareAnnotation>(); + for (Iterator<CrosscuttingMembers> i = members.values().iterator(); i.hasNext();) { + ret.addAll(i.next().getDeclareAnnotationOnMethods()); + } + declareAnnotationOnMethods = new ArrayList<DeclareAnnotation>(); + declareAnnotationOnMethods.addAll(ret); + // world.sortDeclareAnnotations(declareAnnotationOnMethods); + } + return declareAnnotationOnMethods; + } + + /** + * Return an amalgamation of the declare type eow statements + */ + public List<DeclareTypeErrorOrWarning> getDeclareTypeEows() { + if (declareTypeEows == null) { + Set<DeclareTypeErrorOrWarning> ret = new HashSet<DeclareTypeErrorOrWarning>(); + for (Iterator<CrosscuttingMembers> i = members.values().iterator(); i.hasNext();) { + ret.addAll(i.next().getDeclareTypeErrorOrWarning()); + } + declareTypeEows = new ArrayList<DeclareTypeErrorOrWarning>(); + declareTypeEows.addAll(ret); + } + return declareTypeEows; + } + + public List<Declare> getDeclareDominates() { + if (declareDominates == null) { + List<Declare> ret = new ArrayList<Declare>(); + for (Iterator<CrosscuttingMembers> i = members.values().iterator(); i.hasNext();) { + ret.addAll(i.next().getDeclareDominates()); + } + declareDominates = ret; + } + return declareDominates; + } + + public ResolvedType findAspectDeclaringParents(DeclareParents p) { + Set<ResolvedType> keys = this.members.keySet(); + for (Iterator<ResolvedType> iter = keys.iterator(); iter.hasNext();) { + ResolvedType element = iter.next(); + for (Iterator i = members.get(element).getDeclareParents().iterator(); i.hasNext();) { + DeclareParents dp = (DeclareParents) i.next(); + if (dp.equals(p)) { + return element; + } + } + } + return null; + } + + public void reset() { + verificationList = null; + changedSinceLastReset = false; + } + + public boolean hasChangedSinceLastReset() { + return changedSinceLastReset; + } + + /** + * Record something that needs verifying when we believe the type system is complete. Used for things that can't be verified as + * we go along - for example some recursive type variable references (pr133307) + */ + public void recordNecessaryCheck(IVerificationRequired verification) { + if (verificationList == null) { + verificationList = new ArrayList<IVerificationRequired>(); + } + verificationList.add(verification); + } + + /** + * Called when type bindings are complete - calls all registered verification objects in turn. + */ + public void verify() { + if (verificationList == null) { + return; + } + for (Iterator<IVerificationRequired> iter = verificationList.iterator(); iter.hasNext();) { + IVerificationRequired element = iter.next(); + element.verify(); + } + verificationList = null; + } + + public int serializationVersion = 1; + + public void write(CompressingDataOutputStream stream) throws IOException { + // stream.writeInt(serializationVersion); + stream.writeInt(shadowMungers.size()); + for (Iterator iterator = shadowMungers.iterator(); iterator.hasNext();) { + ShadowMunger shadowMunger = (ShadowMunger) iterator.next(); + shadowMunger.write(stream); + } + // // private List /* ShadowMunger */shadowMungers = null; + // // private List typeMungers = null; + // // private List lateTypeMungers = null; + // // private List declareSofts = null; + // // private List declareParents = null; + // // private List declareAnnotationOnTypes = null; + // // private List declareAnnotationOnFields = null; + // // private List declareAnnotationOnMethods = null; // includes constructors + // // private List declareDominates = null; + // // private boolean changedSinceLastReset = false; + // + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/CustomMungerFactory.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/CustomMungerFactory.java new file mode 100644 index 000000000..91bcbdf6b --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/CustomMungerFactory.java @@ -0,0 +1,52 @@ +/* ******************************************************************* + * Copyright (c) 2007 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: + * Linton Ye https://bugs.eclipse.org/bugs/show_bug.cgi?id=193065 + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.util.Collection; + +/** + * <p> + * This interface is introduced to support tools like PointcutDoctor. + * </p> + * <p> + * A CustomMungerFactory is used to create ShadowMungers and/or ConcreteTypeMungers so that an extender can extract extra + * information during the weaving process. + * </p> + * <p> + * A CustomMungerFactory is assigned to a weaver through its AjCompiler in extenders' code, and gets invoked by the weaver right + * before the weaving starts. The custom shadow/type mungers being created will be added into the shadow/type munger list in the + * weaver and participate the weaving process. For example, the match method of each custom shadow munger will be called against + * each shadow. + * </p> + * + * @author lintonye + * + */ +public interface CustomMungerFactory { + + /** + * @param aspectType + * @return a Collection<ShadowMunger> of custom shadow mungers for the given aspect + */ + public Collection<ShadowMunger> createCustomShadowMungers(ResolvedType aspectType); + + /** + * @param aspectType + * @return a Collection<ConcreteTypeMunger> of custom type mungers for the given aspect + */ + public Collection<ConcreteTypeMunger> createCustomTypeMungers(ResolvedType aspectType); + + public Collection<ShadowMunger> getAllCreatedCustomShadowMungers(); + + public Collection<ConcreteTypeMunger> getAllCreatedCustomTypeMungers(); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Dump.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Dump.java new file mode 100644 index 000000000..103e9fec2 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Dump.java @@ -0,0 +1,523 @@ +/* ******************************************************************* + * Copyright (c) 2004,2010 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: + * Matthew Webster, IBM + * ******************************************************************/ +package org.aspectj.weaver; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHolder; +import org.aspectj.bridge.Version; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; +import org.aspectj.weaver.tools.Traceable; + +/** + * @author Matthew Webster + */ +public class Dump { + + public final static String DUMP_CONDITION_PROPERTY = "org.aspectj.weaver.Dump.condition"; + public final static String DUMP_DIRECTORY_PROPERTY = "org.aspectj.dump.directory"; + + /* Format for unique filename based on date & time */ + private static final String FILENAME_PREFIX = "ajcore"; + // private static final DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); + // private static final DateFormat timeFormat = new SimpleDateFormat("HHmmss.SSS"); + private static final String FILENAME_SUFFIX = "txt"; + + public static final String UNKNOWN_FILENAME = "Unknown"; + public static final String DUMP_EXCLUDED = "Excluded"; + public static final String NULL_OR_EMPTY = "Empty"; + + private static Class<?> exceptionClass; + private static IMessage.Kind conditionKind = IMessage.ABORT; + private static File directory = new File("."); + + private String reason; + private String fileName; + private PrintStream print; + + private static String[] savedCommandLine; + private static List<String> savedFullClasspath; + private static IMessageHolder savedMessageHolder; + + // private static Map<INode, WeakReference<INode>> nodes = Collections + // .synchronizedMap(new WeakHashMap<INode, WeakReference<INode>>()); + private static String lastDumpFileName = UNKNOWN_FILENAME; + + private static boolean preserveOnNextReset = false; + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(Dump.class); + + /** + * for testing only, so that we can verify dump contents after compilation has completely finished + */ + public static void preserveOnNextReset() { + preserveOnNextReset = true; + } + + public static void reset() { + if (preserveOnNextReset) { + preserveOnNextReset = false; + return; + } else { + // nodes.clear(); + savedMessageHolder = null; + } + } + + /* + * Dump methods + */ + public static String dump(String reason) { + String fileName = UNKNOWN_FILENAME; + Dump dump = null; + try { + dump = new Dump(reason); + fileName = dump.getFileName(); + dump.dumpDefault(); + } finally { + if (dump != null) { + dump.close(); + } + } + return fileName; + } + + public static String dumpWithException(Throwable th) { + return dumpWithException(savedMessageHolder, th); + } + + public static String dumpWithException(IMessageHolder messageHolder, Throwable th) { + if (!getDumpOnException()) { + return null; + } + if (trace.isTraceEnabled()) { + trace.enter("dumpWithException", null, new Object[] { messageHolder, th }); + } + + String fileName = UNKNOWN_FILENAME; + Dump dump = null; + try { + dump = new Dump(th.getClass().getName()); + fileName = dump.getFileName(); + dump.dumpException(messageHolder, th); + } finally { + if (dump != null) { + dump.close(); + } + } + + if (trace.isTraceEnabled()) { + trace.exit("dumpWithException", fileName); + } + return fileName; + } + + public static String dumpOnExit() { + return dumpOnExit(savedMessageHolder, false); + } + + public static String dumpOnExit(IMessageHolder messageHolder, boolean reset) { + if (!getDumpOnException()) { + return null; + } + if (trace.isTraceEnabled()) { + trace.enter("dumpOnExit", null, messageHolder); + } + String fileName = UNKNOWN_FILENAME; + + if (!shouldDumpOnExit(messageHolder)) { + fileName = DUMP_EXCLUDED; + } else { + Dump dump = null; + try { + dump = new Dump(conditionKind.toString()); + fileName = dump.getFileName(); + dump.dumpDefault(messageHolder); + } finally { + if (dump != null) { + dump.close(); + } + } + } + + if (reset) { + messageHolder.clearMessages(); + } + + if (trace.isTraceEnabled()) { + trace.exit("dumpOnExit", fileName); + } + return fileName; + } + + private static boolean shouldDumpOnExit(IMessageHolder messageHolder) { + if (trace.isTraceEnabled()) { + trace.enter("shouldDumpOnExit", null, messageHolder); + } + if (trace.isTraceEnabled()) { + trace.event("shouldDumpOnExit", null, conditionKind); + } + boolean result = (messageHolder == null) || messageHolder.hasAnyMessage(conditionKind, true); + + if (trace.isTraceEnabled()) { + trace.exit("shouldDumpOnExit", result); + } + return result; + } + + /* + * Dump configuration + */ + public static void setDumpOnException(boolean b) { + if (b) { + exceptionClass = java.lang.Throwable.class; + } else { + exceptionClass = null; + } + } + + public static boolean setDumpDirectory(String directoryName) { + if (trace.isTraceEnabled()) { + trace.enter("setDumpDirectory", null, directoryName); + } + boolean success = false; + + File newDirectory = new File(directoryName); + if (newDirectory.exists()) { + directory = newDirectory; + success = true; + } + + if (trace.isTraceEnabled()) { + trace.exit("setDumpDirectory", success); + } + return success; + + } + + public static boolean getDumpOnException() { + return (exceptionClass != null); + } + + public static boolean setDumpOnExit(IMessage.Kind condition) { + if (trace.isTraceEnabled()) { + trace.event("setDumpOnExit", null, condition); + } + + conditionKind = condition; + return true; + } + + public static boolean setDumpOnExit(String condition) { + for (IMessage.Kind kind : IMessage.KINDS) { + if (kind.toString().equals(condition)) { + return setDumpOnExit(kind); + } + } + return false; + } + + public static IMessage.Kind getDumpOnExit() { + return conditionKind; + } + + public static String getLastDumpFileName() { + return lastDumpFileName; + } + + public static void saveCommandLine(String[] args) { + savedCommandLine = new String[args.length]; + System.arraycopy(args, 0, savedCommandLine, 0, args.length); + } + + public static void saveFullClasspath(List<String> list) { + savedFullClasspath = list; + } + + public static void saveMessageHolder(IMessageHolder holder) { + savedMessageHolder = holder; + } + + // public static void registerNode(Class<?> module, INode newNode) { + // if (trace.isTraceEnabled()) { + // trace.enter("registerNode", null, new Object[] { module, newNode }); + // } + // + // // TODO surely this should preserve the module???? it never has.... + // nodes.put(newNode, new WeakReference<INode>(newNode)); + // + // if (trace.isTraceEnabled()) { + // trace.exit("registerNode", nodes.size()); + // } + // } + + private Dump(String reason) { + if (trace.isTraceEnabled()) { + trace.enter("<init>", this, reason); + } + + this.reason = reason; + + openDump(); + dumpAspectJProperties(); + dumpDumpConfiguration(); + + if (trace.isTraceEnabled()) { + trace.exit("<init>", this); + } + } + + public String getFileName() { + return fileName; + } + + private void dumpDefault() { + dumpDefault(savedMessageHolder); + } + + private void dumpDefault(IMessageHolder holder) { + dumpSytemProperties(); + dumpCommandLine(); + dumpFullClasspath(); + dumpCompilerMessages(holder); + + // dumpNodes(); + } + + // private void dumpNodes() { + // + // IVisitor dumpVisitor = new IVisitor() { + // + // public void visitObject(Object obj) { + // println(formatObj(obj)); + // } + // + // public void visitList(List list) { + // println(list); + // } + // }; + // + // Set<INode> keys = nodes.keySet(); + // for (INode dumpNode : keys) { + // println("---- " + formatObj(dumpNode) + " ----"); + // try { + // dumpNode.accept(dumpVisitor); + // } catch (Exception ex) { + // trace.error(formatObj(dumpNode).toString(), ex); + // } + // } + // } + + private void dumpException(IMessageHolder messageHolder, Throwable th) { + println("---- Exception Information ---"); + println(th); + dumpDefault(messageHolder); + } + + private void dumpAspectJProperties() { + println("---- AspectJ Properties ---"); + println("AspectJ Compiler " + Version.text + " built on " + Version.time_text); + } + + private void dumpDumpConfiguration() { + println("---- Dump Properties ---"); + println("Dump file: " + fileName); + println("Dump reason: " + reason); + println("Dump on exception: " + (exceptionClass != null)); + println("Dump at exit condition: " + conditionKind); + } + + private void dumpFullClasspath() { + println("---- Full Classpath ---"); + if (savedFullClasspath != null && savedFullClasspath.size() > 0) { + for (String fileName : savedFullClasspath) { + File file = new File(fileName); + println(file); + } + } else { + println(NULL_OR_EMPTY); + } + } + + private void dumpSytemProperties() { + println("---- System Properties ---"); + Properties props = System.getProperties(); + println(props); + } + + private void dumpCommandLine() { + println("---- Command Line ---"); + println(savedCommandLine); + } + + private void dumpCompilerMessages(IMessageHolder messageHolder) { + println("---- Compiler Messages ---"); + if (messageHolder != null) { + for (Iterator<IMessage> i = messageHolder.getUnmodifiableListView().iterator(); i.hasNext();) { + IMessage message = i.next(); + println(message.toString()); + } + } else { + println(NULL_OR_EMPTY); + } + } + + /* + * Dump output + */ + private void openDump() { + if (print != null) { + return; + } + + Date now = new Date(); + fileName = FILENAME_PREFIX + "." + new SimpleDateFormat("yyyyMMdd").format(now) + "." + + new SimpleDateFormat("HHmmss.SSS").format(now) + "." + FILENAME_SUFFIX; + try { + File file = new File(directory, fileName); + print = new PrintStream(new FileOutputStream(file), true); + trace.info("Dumping to " + file.getAbsolutePath()); + } catch (Exception ex) { + print = System.err; + trace.info("Dumping to stderr"); + fileName = UNKNOWN_FILENAME; + } + + lastDumpFileName = fileName; + } + + public void close() { + print.close(); + } + + private void println(Object obj) { + print.println(obj); + } + + private void println(Object[] array) { + if (array == null) { + println(NULL_OR_EMPTY); + return; + } + + for (int i = 0; i < array.length; i++) { + print.println(array[i]); + } + } + + private void println(Properties props) { + Iterator iter = props.keySet().iterator(); + while (iter.hasNext()) { + String key = (String) iter.next(); + String value = props.getProperty(key); + print.println(key + "=" + value); + } + } + + private void println(Throwable th) { + th.printStackTrace(print); + } + + private void println(File file) { + print.print(file.getAbsolutePath()); + if (!file.exists()) { + println("(missing)"); + } else if (file.isDirectory()) { + int count = file.listFiles().length; + println("(" + count + " entries)"); + } else { + println("(" + file.length() + " bytes)"); + } + } + + @SuppressWarnings("rawtypes") + private void println(List list) { + if (list == null || list.isEmpty()) { + println(NULL_OR_EMPTY); + } else { + for (Iterator i = list.iterator(); i.hasNext();) { + Object o = i.next(); + if (o instanceof Exception) { + println((Exception) o); + } else { + println(o.toString()); + } + } + } + } + + private static Object formatObj(Object obj) { + + /* These classes have a safe implementation of toString() */ + if (obj == null || obj instanceof String || obj instanceof Number || obj instanceof Boolean || obj instanceof Exception + || obj instanceof Character || obj instanceof Class || obj instanceof File || obj instanceof StringBuffer + || obj instanceof URL) { + return obj; + } else { + try { + + /* Classes can provide an alternative implementation of toString() */ + if (obj instanceof Traceable) { + Traceable t = (Traceable) obj; + return t.toTraceString(); + } else { + return obj.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(obj)); + } + + /* Object.hashCode() can be override and may thow an exception */ + } catch (Exception ex) { + return obj.getClass().getName() + "@FFFFFFFF"; + } + } + } + + static { + String exceptionName = System.getProperty("org.aspectj.weaver.Dump.exception", "true"); + if (!exceptionName.equals("false")) { + setDumpOnException(true); + } + + String conditionName = System.getProperty(DUMP_CONDITION_PROPERTY); + if (conditionName != null) { + setDumpOnExit(conditionName); + } + + String directoryName = System.getProperty(DUMP_DIRECTORY_PROPERTY); + if (directoryName != null) { + setDumpDirectory(directoryName); + } + } + + public interface INode { + + public void accept(IVisitor visior); + + } + + public interface IVisitor { + + public void visitObject(Object s); + + public void visitList(List list); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/EnumAnnotationValue.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/EnumAnnotationValue.java new file mode 100644 index 000000000..f15968b8d --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/EnumAnnotationValue.java @@ -0,0 +1,41 @@ +/* ******************************************************************* + * Copyright (c) 2006 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 IBM initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +public class EnumAnnotationValue extends AnnotationValue { + + private String typeSignature; + private String value; + + public EnumAnnotationValue(String typeSignature, String value) { + super(AnnotationValue.ENUM_CONSTANT); + this.typeSignature = typeSignature; + this.value = value; + } + + public String getType() { + return typeSignature; + } + + public String stringify() { + return typeSignature+value; + } + + public String getValue() { + return value; + } + + public String toString() { + return "E(" + typeSignature + " " + value + ")"; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ExposeTypeMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ExposeTypeMunger.java new file mode 100644 index 000000000..f5b842011 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ExposeTypeMunger.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * Copyright (c) 2007 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, IBM initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +/** + * Special kind of privileged access munger which exposes a type to be public. + */ +public class ExposeTypeMunger extends PrivilegedAccessMunger { + + public ExposeTypeMunger(UnresolvedType typeToExpose) { + super(new ResolvedMemberImpl(Member.STATIC_INITIALIZATION, typeToExpose, 0, UnresolvedType.VOID, "<clinit>", + UnresolvedType.NONE), false); + } + + public String toString() { + return "ExposeTypeMunger(" + getSignature().getDeclaringType().getName() + ")"; + } + + public String getExposedTypeSignature() { + return getSignature().getDeclaringType().getSignature(); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/GeneratedReferenceTypeDelegate.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/GeneratedReferenceTypeDelegate.java new file mode 100644 index 000000000..5d08467d0 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/GeneratedReferenceTypeDelegate.java @@ -0,0 +1,155 @@ +/* ******************************************************************* + * 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://eclipse.org/legal/epl-v10.html + * + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.Collection; + +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.PerClause; + +/** + * A delegate that can sit in the ReferenceType instance created for an aspect generated from aop.xml. Only answers the minimal set + * of information required as the type is processed. + * + * @author Andy Clement + */ +public class GeneratedReferenceTypeDelegate extends AbstractReferenceTypeDelegate { + + private ResolvedType superclass; + + public GeneratedReferenceTypeDelegate(ReferenceType backing) { + super(backing, false); + } + + public boolean isAspect() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isAnnotationStyleAspect() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isInterface() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isEnum() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isAnnotation() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isAnnotationWithRuntimeRetention() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isAnonymous() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isNested() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public ResolvedType getOuterClass() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public String getRetentionPolicy() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean canAnnotationTargetType() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public AnnotationTargetKind[] getAnnotationTargetKinds() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isGeneric() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public String getDeclaredGenericSignature() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean hasAnnotation(UnresolvedType ofType) { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public AnnotationAJ[] getAnnotations() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean hasAnnotations() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public ResolvedType[] getAnnotationTypes() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public ResolvedMember[] getDeclaredFields() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public ResolvedType[] getDeclaredInterfaces() { + return ResolvedType.NONE; + } + + public ResolvedMember[] getDeclaredMethods() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public ResolvedMember[] getDeclaredPointcuts() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public PerClause getPerClause() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public Collection<Declare> getDeclares() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public Collection<ConcreteTypeMunger> getTypeMungers() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public Collection<ResolvedMember> getPrivilegedAccesses() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public int getModifiers() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public void setSuperclass(ResolvedType superclass) { + this.superclass = superclass; + } + + public ResolvedType getSuperclass() { + return this.superclass; + } + + public WeaverStateInfo getWeaverState() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public TypeVariable[] getTypeVariables() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IClassWeaver.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IClassWeaver.java new file mode 100644 index 000000000..ad65eedf9 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IClassWeaver.java @@ -0,0 +1,28 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +/** + * An IClassWeaver is initialized with a class (a type, really, but let's ignore that for now) and a world, and has one method that + * actually weaves the contents of the world into the class implementation. + */ + +public interface IClassWeaver { + + /** + * perform the weaving. + * + * @return <code>true</code> if the class is changed by the weaving, <code>false</code> otherwise. + */ + boolean weave(); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ICrossReferenceHandler.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ICrossReferenceHandler.java new file mode 100644 index 000000000..e38338ae1 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ICrossReferenceHandler.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2000, 2003 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.bridge.ISourceLocation; + +/** + * Clients can pass a single cross-reference handler to the weaver on construction of a BcelWorld. Any cross-references detected + * during munging will be notified to the handler. + */ +public interface ICrossReferenceHandler { + + void addCrossReference(ISourceLocation from, ISourceLocation to, String kind, boolean runtimeTest); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IEclipseSourceContext.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IEclipseSourceContext.java new file mode 100644 index 000000000..d691c503d --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IEclipseSourceContext.java @@ -0,0 +1,15 @@ +/******************************************************************** + * Copyright (c) 2006 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://eclipse.org/legal/epl-v10.html + * + * Contributors: IBM Corporation - initial API and implementation + * Helen Hawkins - iniital version + *******************************************************************/ +package org.aspectj.weaver; + +public interface IEclipseSourceContext extends ISourceContext { + public void removeUnnecessaryProblems(Member method, int problemLineNumber); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IHasPosition.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IHasPosition.java new file mode 100644 index 000000000..8efb55828 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IHasPosition.java @@ -0,0 +1,32 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +public interface IHasPosition { + /** + * The starting index of this location in the character stream. + */ + int getStart(); + + /** + * The ending index of this location in the character stream + * + * This points to the last character in this token. + * + * If a location truly had no contents, then start == end + 1. We don't recommend this. + */ + int getEnd(); + // + // String getFileName(); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IHasSourceLocation.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IHasSourceLocation.java new file mode 100644 index 000000000..5b011f246 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IHasSourceLocation.java @@ -0,0 +1,21 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import org.aspectj.bridge.ISourceLocation; + +public interface IHasSourceLocation extends IHasPosition { + ISourceContext getSourceContext(); + + ISourceLocation getSourceLocation(); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ISourceContext.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ISourceContext.java new file mode 100644 index 000000000..0f2f0aba1 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ISourceContext.java @@ -0,0 +1,25 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import org.aspectj.bridge.ISourceLocation; + +public interface ISourceContext { + public ISourceLocation makeSourceLocation(IHasPosition position); + + public ISourceLocation makeSourceLocation(int line, int offset); + + public int getOffset(); + + public void tidy(); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IUnwovenClassFile.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IUnwovenClassFile.java new file mode 100644 index 000000000..85ab7a16d --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IUnwovenClassFile.java @@ -0,0 +1,27 @@ +/* ******************************************************************* + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver; + +/** + * History: 246125 + * + * @author Andy Clement + */ +public interface IUnwovenClassFile { + + String getFilename(); + + String getClassName(); + + byte[] getBytes(); + + char[] getClassNameAsChars(); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IWeaveRequestor.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IWeaveRequestor.java new file mode 100644 index 000000000..60037c2e6 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IWeaveRequestor.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.aspectj.weaver; + +/** + * @author colyer + * + * This interface is implemented by clients driving weaving through the IClassFileProvider interface. It is used by the + * weaver to return woven class file results back to the client. The client can correlate weave results with inputs since it + * knows the last UnwovenClassFile returned by its iterator. + */ +public interface IWeaveRequestor { + + /* + * A class file resulting from a weave (yes, even though the type name says "unwoven"...). + */ + void acceptResult(IUnwovenClassFile result); + + // various notifications to the requestor about our progress... + void processingReweavableState(); + + void addingTypeMungers(); + + void weavingAspects(); + + void weavingClasses(); + + void weaveCompleted(); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IWeavingSupport.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IWeavingSupport.java new file mode 100644 index 000000000..3f259c66c --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IWeavingSupport.java @@ -0,0 +1,45 @@ +/* ******************************************************************* + * 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; + +import org.aspectj.weaver.ast.Var; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; + +/** + * Encapsulates operations that a world will need to support if it is actually going to modify bytecode rather than just match + * against it. {@see BcelWeavingSupport} + * + * @author Andy Clement + */ +public interface IWeavingSupport { + + public Advice createAdviceMunger(AjAttribute.AdviceAttribute attribute, Pointcut pointcut, Member signature, + ResolvedType concreteAspect); + + public abstract ConcreteTypeMunger makeCflowStackFieldAdder(ResolvedMember cflowField); + + public abstract ConcreteTypeMunger makeCflowCounterFieldAdder(ResolvedMember cflowField); + + /** + * Register a munger for perclause @AJ aspect so that we add aspectOf(..) to them as needed + * + * @see org.aspectj.weaver.bcel.BcelWorld#makePerClauseAspect(ResolvedType, org.aspectj.weaver.patterns.PerClause.Kind) + */ + public abstract ConcreteTypeMunger makePerClauseAspect(ResolvedType aspect, PerClause.Kind kind); + + public abstract ConcreteTypeMunger concreteTypeMunger(ResolvedTypeMunger munger, ResolvedType aspectType); + + public ConcreteTypeMunger createAccessForInlineMunger(ResolvedType inAspect); + + public Var makeCflowAccessVar(ResolvedType formalType, Member cflowField, int arrayIndex); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IntMap.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IntMap.java new file mode 100644 index 000000000..c0af738ba --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/IntMap.java @@ -0,0 +1,150 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.util.ArrayList; +import java.util.List; + +public class IntMap { + // public static final IntMap EMPTY = new IntMap(0) { + // public boolean directlyInAdvice() { return true; } + // public ShadowMunger getEnclosingAdvice() { return null; } //XXX possible + // }; + + // XXX begin hack to avoid a signature refactoring in Pointcut + private ResolvedType concreteAspect; + private ShadowMunger enclosingAdvice; + private List<ResolvedPointcutDefinition> enclosingDefinition = new ArrayList<ResolvedPointcutDefinition>(); + + public void pushEnclosingDefinition(ResolvedPointcutDefinition def) { + enclosingDefinition.add(def); + } + + public void popEnclosingDefinitition() { + enclosingDefinition.remove(enclosingDefinition.size() - 1); + } + + public ResolvedPointcutDefinition peekEnclosingDefinition() { + if (enclosingDefinition.size() == 0) { + return null; + } + return enclosingDefinition.get(enclosingDefinition.size() - 1); + } + + public boolean directlyInAdvice() { + return enclosingDefinition.isEmpty(); + } + + public ShadowMunger getEnclosingAdvice() { + return enclosingAdvice; + } + + public void setEnclosingAdvice(ShadowMunger advice) { + this.enclosingAdvice = advice; + } + + public Member getAdviceSignature() { + if (enclosingAdvice instanceof Advice) { + return ((Advice) enclosingAdvice).getSignature(); + } else { + return null; + } + } + + public ResolvedType getConcreteAspect() { + return concreteAspect; + } + + public void setConcreteAspect(ResolvedType concreteAspect) { + this.concreteAspect = concreteAspect; + } + + public void copyContext(IntMap bindings) { + this.enclosingAdvice = bindings.enclosingAdvice; + this.enclosingDefinition = bindings.enclosingDefinition; + this.concreteAspect = bindings.concreteAspect; + } + + // XXX end hack to avoid a signature refactoring in Pointcut + + private static final int MISSING = -1; + + private int[] map; + + private IntMap(int[] map) { + this.map = map; + } + + public IntMap() { + map = new int[0]; + } + + public IntMap(int initialCapacity) { + map = new int[initialCapacity]; + for (int i = 0; i < initialCapacity; i++) { + map[i] = MISSING; + } + } + + public void put(int key, int val) { + /* assert (val >= 0 && key >= 0) */ + if (key >= map.length) { + int[] tmp = new int[key * 2 + 1]; // ??? better expansion function + System.arraycopy(map, 0, tmp, 0, map.length); + for (int i = map.length, len = tmp.length; i < len; i++) { + tmp[i] = MISSING; + } + map = tmp; + } + map[key] = val; + } + + public int get(int key) { + return map[key]; + } + + public boolean hasKey(int key) { + return (key < map.length && map[key] != MISSING); + } + + // ---- factory methods + + public static IntMap idMap(int size) { + int[] map = new int[size]; + for (int i = 0; i < size; i++) { + map[i] = i; + } + return new IntMap(map); + } + + // ---- from object + + public String toString() { + StringBuffer buf = new StringBuffer("["); + boolean seenFirst = false; + for (int i = 0, len = map.length; i < len; i++) { + if (map[i] != MISSING) { + if (seenFirst) { + buf.append(", "); + } + seenFirst = true; + buf.append(i); + buf.append(" -> "); + buf.append(map[i]); + } + } + buf.append("]"); + return buf.toString(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Iterators.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Iterators.java new file mode 100644 index 000000000..1a2e82006 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Iterators.java @@ -0,0 +1,359 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; + +public final class Iterators { + + /** + * Private constructor, nobody should ever make one of these + */ + private Iterators() { + } + + /** + * A getter represents a mapping function from Object to Iterator + */ + public interface Getter<A, B> { + Iterator<B> get(A target); + } + + /** + * A filter represents a mapping function from Iterator to Iterator + */ + public interface Filter<T> { + Iterator<T> filter(Iterator<T> in); + } + + /** + * Create a new filter F that, when wrapped around another iterator I, creates a new iterator I' that will return only those + * values of I that have not yet been returned by I', discarding duplicates. + */ + public static <T> Filter<T> dupFilter() { + return new Filter<T>() { + final Set<T> seen = new HashSet<T>(); // should have weak ptrs? + + public Iterator<T> filter(final Iterator<T> in) { + return new Iterator<T>() { + boolean fresh = false; + T peek; + + public boolean hasNext() { + if (fresh) { + return true; + } + while (true) { + if (!in.hasNext()) { + return false; + } + peek = in.next(); + if (!seen.contains(peek)) { + fresh = true; + return true; + } else { + peek = null; // garbage collection + } + } + } + + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + T ret = peek; + seen.add(peek); + peek = null; + fresh = false; + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + /** + * Creates an iterator that will return the elements of a specified array, in order. Like Arrays.asList(o).iterator(), without + * all that pesky safety. + */ + + public static <T> Iterator<T> array(final T[] o) { + return new Iterator<T>() { + int i = 0; + int len = (o == null) ? 0 : o.length; + + public boolean hasNext() { + return i < len; + } + + public T next() { + if (i < len) { + return o[i++]; + } else { + throw new NoSuchElementException(); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + public static class ResolvedTypeArrayIterator implements Iterator<ResolvedType> { + private ResolvedType[] array; + private int index; + private int len; + private boolean wantGenerics; + private List<String> alreadySeen; // type signatures + + public ResolvedTypeArrayIterator(ResolvedType[] array, List<String> alreadySeen, boolean wantGenerics) { + assert array != null; + this.array = array; + this.wantGenerics = wantGenerics; + this.len = array.length; + this.index = 0; + this.alreadySeen = alreadySeen; + moveToNextNewOne(); + } + + private void moveToNextNewOne() { + while (index < len) { + ResolvedType interfaceType = array[index]; + if (!wantGenerics && interfaceType.isParameterizedOrGenericType()) { + interfaceType = interfaceType.getRawType(); + } + String signature = interfaceType.getSignature(); + if (!alreadySeen.contains(signature)) { + break; + } + index++; + } + } + + public boolean hasNext() { + return index < len; + } + + public ResolvedType next() { + if (index < len) { + ResolvedType oo = array[index++]; + if (!wantGenerics && (oo.isParameterizedType() || oo.isGenericType())) { + oo = oo.getRawType(); + } + alreadySeen.add(oo.getSignature()); + moveToNextNewOne(); + return oo; + } else { + throw new NoSuchElementException(); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } + + public static Iterator<ResolvedType> array(final ResolvedType[] o, final boolean genericsAware) { + return new Iterator<ResolvedType>() { + int i = 0; + int len = (o == null) ? 0 : o.length; + + public boolean hasNext() { + return i < len; + } + + public ResolvedType next() { + if (i < len) { + ResolvedType oo = o[i++]; + if (!genericsAware && (oo.isParameterizedType() || oo.isGenericType())) { + return oo.getRawType(); + } + return oo; + } else { + throw new NoSuchElementException(); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * creates an iterator I based on a base iterator A and a getter G. I returns, in order, forall (i in A), G(i). + */ + public static <A, B> Iterator<B> mapOver(final Iterator<A> a, final Getter<A, B> g) { + return new Iterator<B>() { + Iterator<B> delegate = new Iterator<B>() { + public boolean hasNext() { + if (!a.hasNext()) { + return false; + } + A o = a.next(); + delegate = append1(g.get(o), this); + return delegate.hasNext(); + } + + public B next() { + if (!hasNext()) { + throw new UnsupportedOperationException(); + } + return delegate.next(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + + public boolean hasNext() { + return delegate.hasNext(); + } + + public B next() { + return delegate.next(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * creates an iterator I based on a base iterator A and a getter G. I returns, in order, forall (i in I) i :: forall (i' in + * g(i)) recur(i', g) + */ + public static <A> Iterator<A> recur(final A a, final Getter<A, A> g) { + return new Iterator<A>() { + Iterator<A> delegate = one(a); + + public boolean hasNext() { + return delegate.hasNext(); + } + + public A next() { + A next = delegate.next(); + delegate = append(g.get(next), delegate); + return next; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * creates an iterator I based on base iterators A and B. Returns the elements returned by A followed by those returned by B. If + * B is empty, simply returns A, and if A is empty, simply returns B. Do NOT USE if b.hasNext() is not idempotent. + */ + public static <T> Iterator<T> append(final Iterator<T> a, final Iterator<T> b) { + if (!b.hasNext()) { + return a; + } + return append1(a, b); + } + + /** + * creates an iterator I based on base iterators A and B. Returns the elements returned by A followed by those returned by B. If + * A is empty, simply returns B. Guaranteed not to call B.hasNext() until A is empty. + */ + public static <T> Iterator<T> append1(final Iterator<T> a, final Iterator<T> b) { + if (!a.hasNext()) { + return b; + } + return new Iterator<T>() { + public boolean hasNext() { + return a.hasNext() || b.hasNext(); + } + + public T next() { + if (a.hasNext()) { + return a.next(); + } + if (b.hasNext()) { + return b.next(); + } + throw new NoSuchElementException(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * creates an iterator I based on a base iterator A and an object O. Returns the elements returned by A, followed by O. + */ + public static <T> Iterator<T> snoc(final Iterator<T> first, final T last) { + return new Iterator<T>() { + T last1 = last; + + public boolean hasNext() { + return first.hasNext() || last1 != null; + } + + public T next() { + if (first.hasNext()) { + return first.next(); + } else if (last1 == null) { + throw new NoSuchElementException(); + } + T ret = last1; + last1 = null; + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * creates an iterator I based on an object O. Returns O, once. + */ + public static <T> Iterator<T> one(final T it) { + return new Iterator<T>() { + boolean avail = true; + + public boolean hasNext() { + return avail; + } + + public T next() { + if (!avail) { + throw new NoSuchElementException(); + } + avail = false; + return it; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/JoinPointSignature.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/JoinPointSignature.java new file mode 100644 index 000000000..57171dd13 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/JoinPointSignature.java @@ -0,0 +1,378 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.AjAttribute.EffectiveSignatureAttribute; + +/** + * @author colyer Instances of this class are created by ResolvedMember.getSignatures() when collating all of the signatures for a + * member. We need to create entries in the set for the "gaps" in the hierarchy. For example: + * + * class A { void foo(); } + * + * class B extends A {} + * + * Join Point : call(* B.foo()) + * + * has signatures: + * + * B.foo() AND A.foo() B.foo() will be created as a ResolvedMemberWithSubstituteDeclaringType + * + * Oh for a JDK 1.4 dynamic proxy.... we have to run on 1.3 :( + */ +public class JoinPointSignature implements ResolvedMember { + + public static final JoinPointSignature[] EMPTY_ARRAY = new JoinPointSignature[0]; + + private ResolvedMember realMember; + private ResolvedType substituteDeclaringType; + + public JoinPointSignature(ResolvedMember backing, ResolvedType aType) { + this.realMember = backing; + this.substituteDeclaringType = aType; + } + + public UnresolvedType getDeclaringType() { + return substituteDeclaringType; + } + + public int getModifiers(World world) { + return realMember.getModifiers(world); + } + + public int getModifiers() { + return realMember.getModifiers(); + } + + public UnresolvedType[] getExceptions(World world) { + return realMember.getExceptions(world); + } + + public UnresolvedType[] getExceptions() { + return realMember.getExceptions(); + } + + public ShadowMunger getAssociatedShadowMunger() { + return realMember.getAssociatedShadowMunger(); + } + + public boolean isAjSynthetic() { + return realMember.isAjSynthetic(); + } + + public boolean hasAnnotation(UnresolvedType ofType) { + return realMember.hasAnnotation(ofType); + } + + public ResolvedType[] getAnnotationTypes() { + return realMember.getAnnotationTypes(); + } + + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + return realMember.getAnnotationOfType(ofType); + } + + public void setAnnotationTypes(ResolvedType[] annotationtypes) { + realMember.setAnnotationTypes(annotationtypes); + } + + public void setAnnotations(AnnotationAJ[] annotations) { + realMember.setAnnotations(annotations); + } + + public void addAnnotation(AnnotationAJ annotation) { + realMember.addAnnotation(annotation); + } + + public boolean isBridgeMethod() { + return realMember.isBridgeMethod(); + } + + public boolean isVarargsMethod() { + return realMember.isVarargsMethod(); + } + + public boolean isSynthetic() { + return realMember.isSynthetic(); + } + + public void write(CompressingDataOutputStream s) throws IOException { + realMember.write(s); + } + + public ISourceContext getSourceContext(World world) { + return realMember.getSourceContext(world); + } + + public String[] getParameterNames() { + return realMember.getParameterNames(); + } + + public void setParameterNames(String[] names) { + realMember.setParameterNames(names); + } + + public String[] getParameterNames(World world) { + return realMember.getParameterNames(world); + } + + public EffectiveSignatureAttribute getEffectiveSignature() { + return realMember.getEffectiveSignature(); + } + + public ISourceLocation getSourceLocation() { + return realMember.getSourceLocation(); + } + + public int getEnd() { + return realMember.getEnd(); + } + + public ISourceContext getSourceContext() { + return realMember.getSourceContext(); + } + + public int getStart() { + return realMember.getStart(); + } + + public void setPosition(int sourceStart, int sourceEnd) { + realMember.setPosition(sourceStart, sourceEnd); + } + + public void setSourceContext(ISourceContext sourceContext) { + realMember.setSourceContext(sourceContext); + } + + public boolean isAbstract() { + return realMember.isAbstract(); + } + + public boolean isPublic() { + return realMember.isPublic(); + } + + public boolean isDefault() { + return realMember.isDefault(); + } + + public boolean isVisible(ResolvedType fromType) { + return realMember.isVisible(fromType); + } + + public void setCheckedExceptions(UnresolvedType[] checkedExceptions) { + realMember.setCheckedExceptions(checkedExceptions); + } + + public void setAnnotatedElsewhere(boolean b) { + realMember.setAnnotatedElsewhere(b); + } + + public boolean isAnnotatedElsewhere() { + return realMember.isAnnotatedElsewhere(); + } + + public UnresolvedType getGenericReturnType() { + return realMember.getGenericReturnType(); + } + + public UnresolvedType[] getGenericParameterTypes() { + return realMember.getGenericParameterTypes(); + } + + public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType, + boolean isParameterized) { + return realMember.parameterizedWith(typeParameters, newDeclaringType, isParameterized); + } + + public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType, + boolean isParameterized, List<String> aliases) { + return realMember.parameterizedWith(typeParameters, newDeclaringType, isParameterized, aliases); + } + + public void setTypeVariables(TypeVariable[] types) { + realMember.setTypeVariables(types); + } + + public TypeVariable[] getTypeVariables() { + return realMember.getTypeVariables(); + } + + public TypeVariable getTypeVariableNamed(String name) { + return realMember.getTypeVariableNamed(name); + } + + public boolean matches(ResolvedMember aCandidateMatch, boolean ignoreGenerics) { + return realMember.matches(aCandidateMatch, ignoreGenerics); + } + + public ResolvedMember resolve(World world) { + return realMember.resolve(world); + } + + public int compareTo(Member other) { + return realMember.compareTo(other); + } + + public MemberKind getKind() { + return realMember.getKind(); + } + + public UnresolvedType getReturnType() { + return realMember.getReturnType(); + } + + public UnresolvedType getType() { + return realMember.getType(); + } + + public String getName() { + return realMember.getName(); + } + + public UnresolvedType[] getParameterTypes() { + return realMember.getParameterTypes(); + } + + public AnnotationAJ[][] getParameterAnnotations() { + return realMember.getParameterAnnotations(); + } + + public ResolvedType[][] getParameterAnnotationTypes() { + return realMember.getParameterAnnotationTypes(); + } + + public String getSignature() { + return realMember.getSignature(); + } + + public int getArity() { + return realMember.getArity(); + } + + public String getParameterSignature() { + return realMember.getParameterSignature(); + } + + public boolean isCompatibleWith(Member am) { + return realMember.isCompatibleWith(am); + } + + public boolean canBeParameterized() { + return realMember.canBeParameterized(); + } + + public AnnotationAJ[] getAnnotations() { + return realMember.getAnnotations(); + } + + public Collection<ResolvedType> getDeclaringTypes(World world) { + throw new UnsupportedOperationException("Adrian doesn't think you should be calling this..."); + } + + public JoinPointSignatureIterator getJoinPointSignatures(World world) { + return realMember.getJoinPointSignatures(world); + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getReturnType().getName()); + buf.append(' '); + buf.append(getDeclaringType().getName()); + buf.append('.'); + buf.append(getName()); + if (getKind() != FIELD) { + buf.append("("); + UnresolvedType[] parameterTypes = getParameterTypes(); + if (parameterTypes.length != 0) { + buf.append(parameterTypes[0]); + for (int i = 1, len = parameterTypes.length; i < len; i++) { + buf.append(", "); + buf.append(parameterTypes[i].getName()); + } + } + buf.append(")"); + } + return buf.toString(); + } + + public String toGenericString() { + return realMember.toGenericString(); + } + + public String toDebugString() { + return realMember.toDebugString(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof JoinPointSignature)) { + return false; + } + JoinPointSignature other = (JoinPointSignature) obj; + if (!realMember.equals(other.realMember)) { + return false; + } + if (!substituteDeclaringType.equals(other.substituteDeclaringType)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return 17 + (37 * realMember.hashCode()) + (37 * substituteDeclaringType.hashCode()); + } + + public boolean hasBackingGenericMember() { + return realMember.hasBackingGenericMember(); + } + + public ResolvedMember getBackingGenericMember() { + return realMember.getBackingGenericMember(); + } + + public void evictWeavingState() { + realMember.evictWeavingState(); + } + + public ResolvedMember parameterizedWith(Map m, World w) { + return realMember.parameterizedWith(m, w); + } + + public String getAnnotationDefaultValue() { + return realMember.getAnnotationDefaultValue(); + } + + public String getParameterSignatureErased() { + return realMember.getParameterSignatureErased(); + } + + public String getSignatureErased() { + return realMember.getSignatureErased(); + } + + public boolean isDefaultConstructor() { + return realMember.isDefaultConstructor(); + } + + public boolean equalsApartFromDeclaringType(Object other) { + return realMember.equalsApartFromDeclaringType(other); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/JoinPointSignatureIterator.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/JoinPointSignatureIterator.java new file mode 100644 index 000000000..50332c1c0 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/JoinPointSignatureIterator.java @@ -0,0 +1,281 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * Iterates over the signatures of a join point, calculating new signatures lazily to minimize processing and to avoid unneccessary + * "can't find type" errors. Iterator can be cached and reused by calling the "reset" method between iterations. + */ +public class JoinPointSignatureIterator implements Iterator<JoinPointSignature> { + + ResolvedType firstDefiningType; + + private Member signaturesOfMember; + private ResolvedMember firstDefiningMember; + private World world; + private List<JoinPointSignature> discoveredSignatures = new ArrayList<JoinPointSignature>(); + private List<JoinPointSignature> additionalSignatures = Collections.emptyList(); + private Iterator<JoinPointSignature> discoveredSignaturesIterator = null; + private Iterator<ResolvedType> superTypeIterator = null; + private boolean isProxy = false; + private Set<ResolvedType> visitedSuperTypes = new HashSet<ResolvedType>(); + private List<SearchPair> yetToBeProcessedSuperMembers = null; + + private boolean iteratingOverDiscoveredSignatures = true; + private boolean couldBeFurtherAsYetUndiscoveredSignatures = true; + private final static UnresolvedType jlrProxy = UnresolvedType.forSignature("Ljava/lang/reflect/Proxy;"); + + public JoinPointSignatureIterator(Member joinPointSignature, World world) { + this.signaturesOfMember = joinPointSignature; + this.world = world; + addSignaturesUpToFirstDefiningMember(); + if (!shouldWalkUpHierarchy()) { + couldBeFurtherAsYetUndiscoveredSignatures = false; + } + } + + public void reset() { + discoveredSignaturesIterator = discoveredSignatures.iterator(); + additionalSignatures.clear(); + iteratingOverDiscoveredSignatures = true; + } + + public boolean hasNext() { + if (iteratingOverDiscoveredSignatures && discoveredSignaturesIterator.hasNext()) { + return true; + } else if (couldBeFurtherAsYetUndiscoveredSignatures) { + if (additionalSignatures.size() > 0) { + return true; + } else { + return findSignaturesFromSupertypes(); + } + } else { + return false; + } + } + + public JoinPointSignature next() { + if (iteratingOverDiscoveredSignatures && discoveredSignaturesIterator.hasNext()) { + return discoveredSignaturesIterator.next(); + } else { + if (additionalSignatures.size() > 0) { + return additionalSignatures.remove(0); + } + } + throw new NoSuchElementException(); + } + + public void remove() { + throw new UnsupportedOperationException("can't remove from JoinPointSignatureIterator"); + } + + /** + * Walk up the hierarchy creating one member for each type up to and including the first defining type. + */ + private void addSignaturesUpToFirstDefiningMember() { + ResolvedType originalDeclaringType = signaturesOfMember.getDeclaringType().resolve(world); + ResolvedType superType = originalDeclaringType.getSuperclass(); + if (superType != null && superType.equals(jlrProxy)) { + // Proxy types are generated without any regard to generics (pr268419) and so the member walking + // should also ignore them + isProxy = true; + } + + // is it the array constructor join point? + if (world.isJoinpointArrayConstructionEnabled() && originalDeclaringType.isArray()) { + Member m = signaturesOfMember; + ResolvedMember rm = new ResolvedMemberImpl(m.getKind(), m.getDeclaringType(), m.getModifiers(), m.getReturnType(), m + .getName(), m.getParameterTypes()); + discoveredSignatures.add(new JoinPointSignature(rm, originalDeclaringType)); + couldBeFurtherAsYetUndiscoveredSignatures = false; + return; + } + + firstDefiningMember = (signaturesOfMember instanceof ResolvedMember ? + (ResolvedMember) signaturesOfMember: signaturesOfMember.resolve(world)); + + if (firstDefiningMember == null) { + couldBeFurtherAsYetUndiscoveredSignatures = false; + return; + } + + // declaringType can be unresolved if we matched a synthetic member generated by Aj... + // should be fixed elsewhere but add this resolve call on the end for now so that we can + // focus on one problem at a time... + firstDefiningType = firstDefiningMember.getDeclaringType().resolve(world); + if (firstDefiningType != originalDeclaringType) { + if (signaturesOfMember.getKind() == Member.CONSTRUCTOR) { + return; + } + } + + if (originalDeclaringType == firstDefiningType) { + // a common case + discoveredSignatures.add(new JoinPointSignature(firstDefiningMember, originalDeclaringType)); + } else { + List<ResolvedType> declaringTypes = new ArrayList<ResolvedType>(); + accumulateTypesInBetween(originalDeclaringType, firstDefiningType, declaringTypes); + for (ResolvedType declaringType : declaringTypes) { + discoveredSignatures.add(new JoinPointSignature(firstDefiningMember, declaringType)); + } + } + } + + /** + * Build a list containing every type between subtype and supertype, inclusively. + */ + private void accumulateTypesInBetween(ResolvedType subType, ResolvedType superType, List<ResolvedType> types) { + types.add(subType); + if (subType == superType) { + return; + } else { + for (Iterator<ResolvedType> iter = subType.getDirectSupertypes(); iter.hasNext();) { + ResolvedType parent = iter.next(); + if (superType.isAssignableFrom(parent, true)) { + accumulateTypesInBetween(parent, superType, types); + } + } + } + } + + private boolean shouldWalkUpHierarchy() { + if (signaturesOfMember.getKind() == Member.CONSTRUCTOR) { + return false; + } + if (signaturesOfMember.getKind() == Member.FIELD) { + return false; + } + if (Modifier.isStatic(signaturesOfMember.getModifiers())) { + return false; + } + return true; + } + + private boolean findSignaturesFromSupertypes() { + iteratingOverDiscoveredSignatures = false; + if (superTypeIterator == null) { + superTypeIterator = firstDefiningType.getDirectSupertypes(); + } + if (superTypeIterator.hasNext()) { + ResolvedType superType = superTypeIterator.next(); + if (isProxy && (superType.isGenericType() || superType.isParameterizedType())) { + superType = superType.getRawType(); + } + if (visitedSuperTypes.contains(superType)) { + return findSignaturesFromSupertypes(); + } else { + // we haven't looked in this type yet + visitedSuperTypes.add(superType); + if (superType.isMissing()) { + // issue a warning, stop looking for join point signatures in this line + warnOnMissingType(superType); + return findSignaturesFromSupertypes(); + } + ResolvedMemberImpl foundMember = (ResolvedMemberImpl) superType.lookupResolvedMember(firstDefiningMember, true, + isProxy); + if (foundMember != null && isVisibleTo(firstDefiningMember, foundMember)) { + List<ResolvedType> declaringTypes = new ArrayList<ResolvedType>(); + // declaring type can be unresolved if the member can from an ITD... + ResolvedType resolvedDeclaringType = foundMember.getDeclaringType().resolve(world); + accumulateTypesInBetween(superType, resolvedDeclaringType, declaringTypes); + for (ResolvedType declaringType : declaringTypes) { + JoinPointSignature member = null; + if (isProxy) { + if (declaringType.isGenericType() || declaringType.isParameterizedType()) { + declaringType = declaringType.getRawType(); + } + } + member = new JoinPointSignature(foundMember, declaringType); + discoveredSignatures.add(member); // for next time we are reset + if (additionalSignatures == Collections.EMPTY_LIST) { + additionalSignatures = new ArrayList<JoinPointSignature>(); + } + additionalSignatures.add(member); // for this time + } + // if this was a parameterized type, look in the generic type that backs it too + if (!isProxy && superType.isParameterizedType() && (foundMember.backingGenericMember != null)) { + JoinPointSignature member = new JoinPointSignature(foundMember.backingGenericMember, + foundMember.declaringType.resolve(world)); + discoveredSignatures.add(member); // for next time we are reset + if (additionalSignatures == Collections.EMPTY_LIST) { + additionalSignatures = new ArrayList<JoinPointSignature>(); + } + additionalSignatures.add(member); // for this time + } + if (yetToBeProcessedSuperMembers == null) { + yetToBeProcessedSuperMembers = new ArrayList<SearchPair>(); + } + yetToBeProcessedSuperMembers.add(new SearchPair(foundMember, superType)); + return true; + } else { + return findSignaturesFromSupertypes(); + } + } + } + if (yetToBeProcessedSuperMembers != null && !yetToBeProcessedSuperMembers.isEmpty()) { + SearchPair nextUp = yetToBeProcessedSuperMembers.remove(0); + firstDefiningType = nextUp.type; + firstDefiningMember = nextUp.member; + superTypeIterator = null; + return findSignaturesFromSupertypes(); + } + couldBeFurtherAsYetUndiscoveredSignatures = false; + return false; + } + + /** + * Returns true if the parent member is visible to the child member In the same declaring type this is always true, otherwise if + * parent is private it is false. + * + * @param childMember + * @param parentMember + * @return + */ + private boolean isVisibleTo(ResolvedMember childMember, ResolvedMember parentMember) { + if (childMember.getDeclaringType().equals(parentMember.getDeclaringType())) { + return true; + } + if (Modifier.isPrivate(parentMember.getModifiers())) { + return false; + } else { + return true; + } + } + + private void warnOnMissingType(ResolvedType missing) { + if (missing instanceof MissingResolvedTypeWithKnownSignature) { + // which it should be... + MissingResolvedTypeWithKnownSignature mrt = (MissingResolvedTypeWithKnownSignature) missing; + mrt.raiseWarningOnJoinPointSignature(signaturesOfMember.toString()); + } + } + + private static class SearchPair { + public ResolvedMember member; + public ResolvedType type; + + public SearchPair(ResolvedMember member, ResolvedType type) { + this.member = member; + this.type = type; + } + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Lint.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Lint.java new file mode 100644 index 000000000..aab34b8f6 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Lint.java @@ -0,0 +1,355 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.MessageFormat; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +public class Lint { + Map<String, Lint.Kind> kinds = new HashMap<String, Lint.Kind>(); + /* private */World world; + + public final Kind invalidAbsoluteTypeName = new Kind("invalidAbsoluteTypeName", "no match for this type name: {0}"); + + public final Kind invalidWildcardTypeName = new Kind("invalidWildcardTypeName", "no match for this type pattern: {0}"); + + public final Kind unresolvableMember = new Kind("unresolvableMember", "can not resolve this member: {0}"); + + public final Kind typeNotExposedToWeaver = new Kind("typeNotExposedToWeaver", + "this affected type is not exposed to the weaver: {0}"); + + public final Kind shadowNotInStructure = new Kind("shadowNotInStructure", + "the shadow for this join point is not exposed in the structure model: {0}"); + + public final Kind unmatchedSuperTypeInCall = new Kind("unmatchedSuperTypeInCall", + "does not match because declaring type is {0}, if match desired use target({1})"); + + public final Kind unmatchedTargetKind = new Kind("unmatchedTargetKind", "does not match because annotation {0} has @Target{1}"); + + public final Kind canNotImplementLazyTjp = new Kind("canNotImplementLazyTjp", + "can not implement lazyTjp on this joinpoint {0} because around advice is used"); + + public final Kind multipleAdviceStoppingLazyTjp = new Kind("multipleAdviceStoppingLazyTjp", + "can not implement lazyTjp at joinpoint {0} because of advice conflicts, see secondary locations to find conflicting advice"); + + public final Kind needsSerialVersionUIDField = new Kind("needsSerialVersionUIDField", + "serialVersionUID of type {0} needs to be set because of {1}"); + + public final Kind serialVersionUIDBroken = new Kind("brokeSerialVersionCompatibility", + "serialVersionUID of type {0} is broken because of added field {1}"); + + public final Kind noInterfaceCtorJoinpoint = new Kind("noInterfaceCtorJoinpoint", + "no interface constructor-execution join point - use {0}+ for implementing classes"); + + public final Kind noJoinpointsForBridgeMethods = new Kind( + "noJoinpointsForBridgeMethods", + "pointcut did not match on the method call to a bridge method. Bridge methods are generated by the compiler and have no join points"); + + public final Kind enumAsTargetForDecpIgnored = new Kind("enumAsTargetForDecpIgnored", + "enum type {0} matches a declare parents type pattern but is being ignored"); + + public final Kind annotationAsTargetForDecpIgnored = new Kind("annotationAsTargetForDecpIgnored", + "annotation type {0} matches a declare parents type pattern but is being ignored"); + + public final Kind cantMatchArrayTypeOnVarargs = new Kind("cantMatchArrayTypeOnVarargs", + "an array type as the last parameter in a signature does not match on the varargs declared method: {0}"); + + public final Kind adviceDidNotMatch = new Kind("adviceDidNotMatch", "advice defined in {0} has not been applied"); + + public final Kind invalidTargetForAnnotation = new Kind("invalidTargetForAnnotation", + "{0} is not a valid target for annotation {1}, this annotation can only be applied to {2}"); + + public final Kind elementAlreadyAnnotated = new Kind("elementAlreadyAnnotated", + "{0} - already has an annotation of type {1}, cannot add a second instance"); + + public final Kind runtimeExceptionNotSoftened = new Kind("runtimeExceptionNotSoftened", + "{0} will not be softened as it is already a RuntimeException"); + + public final Kind uncheckedArgument = new Kind("uncheckedArgument", + "unchecked match of {0} with {1} when argument is an instance of {2} at join point {3}"); + + public final Kind uncheckedAdviceConversion = new Kind("uncheckedAdviceConversion", + "unchecked conversion when advice applied at shadow {0}, expected {1} but advice uses {2}"); + + public final Kind noGuardForLazyTjp = new Kind("noGuardForLazyTjp", + "can not build thisJoinPoint lazily for this advice since it has no suitable guard"); + + public final Kind noExplicitConstructorCall = new Kind("noExplicitConstructorCall", + "inter-type constructor does not contain explicit constructor call: field initializers in the target type will not be executed"); + + public final Kind aspectExcludedByConfiguration = new Kind("aspectExcludedByConfiguration", + "aspect {0} exluded for class loader {1}"); + + public final Kind unorderedAdviceAtShadow = new Kind("unorderedAdviceAtShadow", + "at this shadow {0} no precedence is specified between advice applying from aspect {1} and aspect {2}"); + + public final Kind swallowedExceptionInCatchBlock = new Kind("swallowedExceptionInCatchBlock", + "exception swallowed in catch block"); + + public final Kind calculatingSerialVersionUID = new Kind("calculatingSerialVersionUID", + "calculated SerialVersionUID for type {0} to be {1}"); + + public final Kind nonReweavableTypeEncountered = new Kind("nonReweavableTypeEncountered", + "class {0} is already woven and has not been built in reweavable mode"); + + // there are a lot of messages in the cant find type family - I'm defining an umbrella lint warning that + // allows a user to control their severity (for e.g. ltw or binary weaving) + public final Kind cantFindType = new Kind("cantFindType", "{0}"); + + public final Kind cantFindTypeAffectingJoinPointMatch = new Kind("cantFindTypeAffectingJPMatch", "{0}"); + + public final Kind advisingSynchronizedMethods = new Kind("advisingSynchronizedMethods", + "advice matching the synchronized method shadow ''{0}'' will be executed outside the lock rather than inside (compiler limitation)"); + + public final Kind mustWeaveXmlDefinedAspects = new Kind( + "mustWeaveXmlDefinedAspects", + "XML Defined aspects must be woven in cases where cflow pointcuts are involved. Currently the include/exclude patterns exclude ''{0}''"); + + public final Kind cannotAdviseJoinpointInInterfaceWithAroundAdvice = new Kind( + "cannotAdviseJoinpointInInterfaceWithAroundAdvice", + "The joinpoint ''{0}'' cannot be advised and is being skipped as the compiler implementation will lead to creation of methods with bodies in an interface (compiler limitation)"); + + /** + * Indicates an aspect could not be found when attempting reweaving. + */ + public final Kind missingAspectForReweaving = new Kind("missingAspectForReweaving", + "aspect {0} cannot be found when reweaving {1}"); + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(Lint.class); + + public Lint(World world) { + if (trace.isTraceEnabled()) { + trace.enter("<init>", this, world); + } + this.world = world; + if (trace.isTraceEnabled()) { + trace.exit("<init>"); + } + } + + public void setAll(String messageKind) { + if (trace.isTraceEnabled()) { + trace.enter("setAll", this, messageKind); + } + setAll(getMessageKind(messageKind)); + if (trace.isTraceEnabled()) { + trace.exit("setAll"); + } + } + + private void setAll(IMessage.Kind messageKind) { + for (Kind kind : kinds.values()) { + kind.setKind(messageKind); + } + } + + public void setFromMap(Map<String,String> lintOptionsMap) { + for (String key: lintOptionsMap.keySet()) { + String value = lintOptionsMap.get(key); + Kind kind = kinds.get(key); + if (kind == null) { + MessageUtil.error(world.getMessageHandler(), WeaverMessages.format(WeaverMessages.XLINT_KEY_ERROR, key)); + } else { + kind.setKind(getMessageKind(value)); + } + } + } + + public void setFromProperties(File file) { + if (trace.isTraceEnabled()) { + trace.enter("setFromProperties", this, file); + } + InputStream s = null; + try { + s = new FileInputStream(file); + setFromProperties(s); + } catch (IOException ioe) { + MessageUtil.error(world.getMessageHandler(), + WeaverMessages.format(WeaverMessages.XLINT_LOAD_ERROR, file.getPath(), ioe.getMessage())); + } finally { + if (s != null) { + try { + s.close(); + } catch (IOException e) { + // ignore + } + } + } + + if (trace.isTraceEnabled()) { + trace.exit("setFromProperties"); + } + } + + public void loadDefaultProperties() { + InputStream s = getClass().getResourceAsStream("XlintDefault.properties"); + if (s == null) { + MessageUtil.warn(world.getMessageHandler(), WeaverMessages.format(WeaverMessages.XLINTDEFAULT_LOAD_ERROR)); + return; + } + try { + setFromProperties(s); + } catch (IOException ioe) { + MessageUtil.error(world.getMessageHandler(), + WeaverMessages.format(WeaverMessages.XLINTDEFAULT_LOAD_PROBLEM, ioe.getMessage())); + } finally { + try { + s.close(); + } catch (IOException ioe) { + // ignore + } + } + + } + + private void setFromProperties(InputStream s) throws IOException { + Properties p = new Properties(); + p.load(s); + setFromProperties(p); + } + + @SuppressWarnings("rawtypes") + public void setFromProperties(Properties properties) { + for (Iterator i = properties.entrySet().iterator(); i.hasNext();) { + Map.Entry entry = (Map.Entry) i.next(); + Kind kind = kinds.get(entry.getKey()); + if (kind == null) { + MessageUtil.error(world.getMessageHandler(), WeaverMessages.format(WeaverMessages.XLINT_KEY_ERROR, entry.getKey())); + } else { + kind.setKind(getMessageKind((String) entry.getValue())); + } + } + } + + public Collection<Kind> allKinds() { + return kinds.values(); + } + + public Kind getLintKind(String name) { + return kinds.get(name); + } + + // temporarily suppress the given lint messages + public void suppressKinds(Collection<Kind> lintKind) { + if (lintKind.isEmpty()) { + return; + } + for (Kind k : lintKind) { + k.setSuppressed(true); + } + } + + // remove any suppression of lint warnings in place + public void clearAllSuppressions() { + for (Kind k : kinds.values()) { + k.setSuppressed(false); + } + } + + public void clearSuppressions(Collection<Lint.Kind> lintKinds) { + for (Kind k : lintKinds) { + k.setSuppressed(false); + } + } + + private IMessage.Kind getMessageKind(String v) { + if (v.equals("ignore")) { + return null; + } else if (v.equals("warning")) { + return IMessage.WARNING; + } else if (v.equals("error")) { + return IMessage.ERROR; + } + + MessageUtil.error(world.getMessageHandler(), WeaverMessages.format(WeaverMessages.XLINT_VALUE_ERROR, v)); + return null; + } + + public Kind fromKey(String lintkey) { + return kinds.get(lintkey); + } + + public class Kind { + private final String name; + private final String message; + private IMessage.Kind kind = IMessage.WARNING; + private boolean isSupressed = false; // by SuppressAjWarnings + + public Kind(String name, String message) { + this.name = name; + this.message = message; + kinds.put(this.name, this); + } + + public void setSuppressed(boolean shouldBeSuppressed) { + this.isSupressed = shouldBeSuppressed; + } + + public boolean isEnabled() { + return (kind != null) && !isSupressed(); + } + + private boolean isSupressed() { + // can't suppress errors! + return isSupressed && (kind != IMessage.ERROR); + } + + public String getName() { + return name; + } + + public IMessage.Kind getKind() { + return kind; + } + + public void setKind(IMessage.Kind kind) { + this.kind = kind; + } + + public void signal(String info, ISourceLocation location) { + if (kind == null) { + return; + } + + String text = MessageFormat.format(message, new Object[] { info }); + text += " [Xlint:" + name + "]"; + world.getMessageHandler().handleMessage(new LintMessage(text, kind, location, null, getLintKind(name))); + } + + public void signal(String[] infos, ISourceLocation location, ISourceLocation[] extraLocations) { + if (kind == null) { + return; + } + + String text = MessageFormat.format(message, (Object[]) infos); + text += " [Xlint:" + name + "]"; + world.getMessageHandler().handleMessage(new LintMessage(text, kind, location, extraLocations, getLintKind(name))); + } + + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/LintMessage.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/LintMessage.java new file mode 100644 index 000000000..f54e20e7d --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/LintMessage.java @@ -0,0 +1,44 @@ +/* ******************************************************************* + * Copyright (c) 2002-2006 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: + * PARC initial implementation + * AndyClement extracted as self contained type from Lint type (4-Aug-06) + * ******************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; + +public class LintMessage extends Message { + + // private Lint.Kind lintKind; + private String lintKind; + + public LintMessage(String message, IMessage.Kind messageKind, ISourceLocation location, ISourceLocation[] extraLocations, + Lint.Kind lintKind) { + super(message, "", messageKind, location, null, extraLocations); + this.lintKind = lintKind.getName(); + } + + public LintMessage(String message, String extraDetails, org.aspectj.weaver.Lint.Kind kind2, Kind kind, + ISourceLocation sourceLocation, Throwable object, ISourceLocation[] seeAlsoLocations, boolean declared, int id, + int sourceStart, int sourceEnd) { + super(message, extraDetails, kind, sourceLocation, object, seeAlsoLocations, declared, id, sourceStart, sourceEnd); + this.lintKind = kind2.getName(); + } + + /** + * @return Returns the Lint kind of this message + */ + public String getLintKind() { + return lintKind; + } + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Member.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Member.java new file mode 100644 index 000000000..8134e8df2 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Member.java @@ -0,0 +1,93 @@ +/* ******************************************************************* + * Copyright (c) 2002-2010 + * 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 + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.Collection; + +/** + * Abstract representation of a member (field/constructor/method) within a type. + * + * @author PARC + * @author Adrian Colyer + * @author Andy Clement + */ +public interface Member extends Comparable<Member> { + + public static final Member[] NONE = new Member[0]; + + public static final MemberKind METHOD = new MemberKind("METHOD", 1); + public static final MemberKind FIELD = new MemberKind("FIELD", 2); + public static final MemberKind CONSTRUCTOR = new MemberKind("CONSTRUCTOR", 3); + public static final MemberKind STATIC_INITIALIZATION = new MemberKind("STATIC_INITIALIZATION", 4); + public static final MemberKind POINTCUT = new MemberKind("POINTCUT", 5); + public static final MemberKind ADVICE = new MemberKind("ADVICE", 6); + public static final MemberKind HANDLER = new MemberKind("HANDLER", 7); + public static final MemberKind MONITORENTER = new MemberKind("MONITORENTER", 8); + public static final MemberKind MONITOREXIT = new MemberKind("MONITOREXIT", 9); + + public static final AnnotationAJ[][] NO_PARAMETER_ANNOTATIONXS = new AnnotationAJ[][] {}; + public static final ResolvedType[][] NO_PARAMETER_ANNOTATION_TYPES = new ResolvedType[][] {}; + + /** + * @return the kind of member from those listed as MemberKind instances + */ + public MemberKind getKind(); + + public String getName(); + + public UnresolvedType getDeclaringType(); + + public UnresolvedType[] getParameterTypes(); + + public UnresolvedType[] getGenericParameterTypes(); + + public UnresolvedType getType(); + + public UnresolvedType getReturnType(); + + public UnresolvedType getGenericReturnType(); + + /** + * Return full signature, including return type, e.g. "()LFastCar;". For a signature without the return type, use + * getParameterSignature() - it is important to choose the right one in the face of covariance. + */ + public String getSignature(); + + public JoinPointSignatureIterator getJoinPointSignatures(World world); + + public int getArity(); + + /** + * Return signature without return type, e.g. "()" for a signature *with* the return type, use getSignature() - it is important + * to choose the right one in the face of covariance. + */ + public String getParameterSignature(); + + public int getModifiers(World world); + + public int getModifiers(); + + /** + * Returns true iff the member is generic (NOT parameterized) + */ + public boolean canBeParameterized(); + + public AnnotationAJ[] getAnnotations(); + + public Collection<ResolvedType> getDeclaringTypes(World world); + + public String[] getParameterNames(World world); + + public UnresolvedType[] getExceptions(World world); + + public ResolvedMember resolve(World world); + + public int compareTo(Member other); + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MemberImpl.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MemberImpl.java new file mode 100644 index 000000000..fbf497ed2 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MemberImpl.java @@ -0,0 +1,542 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +public class MemberImpl implements Member { + + protected MemberKind kind; + protected int modifiers; + protected String name; + protected UnresolvedType declaringType; + protected UnresolvedType returnType; + protected UnresolvedType[] parameterTypes; + private final String erasedSignature; // eg. (Ljava/util/Set;V)Ljava/lang/String; + private String paramSignature; // eg. (Ljava/util/Set<Ljava/lang/String;>;V) // no return type + + // OPTIMIZE move out of the member! + private boolean reportedCantFindDeclaringType = false; + private boolean reportedUnresolvableMember = false; + + /** + * All the signatures that a join point with this member as its signature has. + */ + private JoinPointSignatureIterator joinPointSignatures = null; + + /** + * Construct a MemberImpl using an erased signature for the parameters and return type (member method/ctor) or type (member + * field) + */ + public MemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, String name, String erasedSignature) { + this.kind = kind; + this.declaringType = declaringType; + this.modifiers = modifiers; + this.name = name; + this.erasedSignature = erasedSignature; + if (kind == FIELD) { + this.returnType = UnresolvedType.forSignature(erasedSignature); + this.parameterTypes = UnresolvedType.NONE; + } else { + Object[] returnAndParams = signatureToTypes(erasedSignature); + this.returnType = (UnresolvedType) returnAndParams[0]; + this.parameterTypes = (UnresolvedType[]) returnAndParams[1]; + } + } + + /** + * Construct a MemberImpl using real type information for the parameters and return type (member method/ctor) or type (member + * field) + */ + public MemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, UnresolvedType returnType, String name, + UnresolvedType[] parameterTypes) { + this.kind = kind; + this.declaringType = declaringType; + this.modifiers = modifiers; + this.returnType = returnType; + this.name = name; + this.parameterTypes = parameterTypes; + if (kind == FIELD) { + this.erasedSignature = returnType.getErasureSignature(); + } else { + this.erasedSignature = typesToSignature(returnType, parameterTypes, true); + + // Check parameter recovery by collapsing types to the string then rebuilding them from that + // this will check we are capable of having WeakRefs to the parameter types + // String nonErasedSignature = getParameterSignature()+getReturnType().getSignature(); + // Object[] returnAndParams = signatureToTypes(nonErasedSignature); + // UnresolvedType[] recoveredParams = (UnresolvedType[]) returnAndParams[1]; + // for (int jj=0;jj<parameterTypes.length;jj++) { + // if (!parameterTypes[jj].getSignature().equals(recoveredParams[jj].getSignature())) { + // throw new + // RuntimeException(parameterTypes[jj].getSignature()+" != "+recoveredParams[jj].getSignature()+" "+paramSignature); + // } + // } + } + + } + + public ResolvedMember resolve(World world) { + return world.resolve(this); + } + + // ---- utility methods + + /** + * Build a signature based on the return type and parameter types. For example: "(Ljava/util/Set<Ljava/lang/String;>;)V" or + * "(Ljava/util/Set;)V". The latter form shows what happens when the generics are erased + */ + public static String typesToSignature(UnresolvedType returnType, UnresolvedType[] paramTypes, boolean eraseGenerics) { + StringBuilder buf = new StringBuilder(); + buf.append("("); + for (UnresolvedType paramType : paramTypes) { + if (eraseGenerics) { + buf.append(paramType.getErasureSignature()); + } else { + buf.append(paramType.getSignature()); + } + } + buf.append(")"); + if (eraseGenerics) { + buf.append(returnType.getErasureSignature()); + } else { + buf.append(returnType.getSignature()); + } + return buf.toString(); + } + + /** + * Returns "(<signaturesOfParamTypes>,...)" - unlike the other typesToSignature that also includes the return type, this one + * just deals with the parameter types. + */ + public static String typesToSignature(UnresolvedType[] paramTypes) { + StringBuffer buf = new StringBuffer(); + buf.append("("); + for (int i = 0; i < paramTypes.length; i++) { + buf.append(paramTypes[i].getSignature()); + } + buf.append(")"); + return buf.toString(); + } + + /** + * returns an Object[] pair of UnresolvedType, UnresolvedType[] representing return type, argument types parsed from the JVM + * bytecode signature of a method. Yes, this should actually return a nice statically-typed pair object, but we don't have one + * of those. + * + * <blockquote> + * + * <pre> + * UnresolvedType.signatureToTypes("()[Z")[0].equals(Type.forSignature("[Z")) + * UnresolvedType.signatureToTypes("(JJ)I")[1] + * .equals(UnresolvedType.forSignatures(new String[] {"J", "J"})) + * </pre> + * + * </blockquote> + * + * @param erasedSignature the JVM bytecode method signature string we want to break apart + * @return a pair of UnresolvedType, UnresolvedType[] representing the return types and parameter types. + */ + private static Object[] signatureToTypes(String sig) { + boolean hasParameters = sig.charAt(1) != ')'; + if (hasParameters) { + List<UnresolvedType> l = new ArrayList<UnresolvedType>(); + int i = 1; + boolean hasAnyAnglies = sig.indexOf('<') != -1; + while (true) { + char c = sig.charAt(i); + if (c == ')') { + break; // break out when the hit the ')' + } + int start = i; + while (c == '[') { + c = sig.charAt(++i); + } + if (c == 'L' || c == 'P') { + int nextSemicolon = sig.indexOf(';', start); + int firstAngly = (hasAnyAnglies ? sig.indexOf('<', start) : -1); + if (!hasAnyAnglies || firstAngly == -1 || firstAngly > nextSemicolon) { + i = nextSemicolon + 1; + l.add(UnresolvedType.forSignature(sig.substring(start, i))); + } else { + // generics generics generics + // Have to skip to the *correct* ';' + boolean endOfSigReached = false; + int posn = firstAngly; + int genericDepth = 0; + while (!endOfSigReached) { + switch (sig.charAt(posn)) { + case '<': + genericDepth++; + break; + case '>': + genericDepth--; + break; + case ';': + if (genericDepth == 0) { + endOfSigReached = true; + } + break; + default: + } + posn++; + } + // posn now points to the correct nextSemicolon :) + i = posn; + l.add(UnresolvedType.forSignature(sig.substring(start, i))); + } + } else if (c == 'T') { // assumed 'reference' to a type + // variable, so just "Tname;" + int nextSemicolon = sig.indexOf(';', start); + String nextbit = sig.substring(start, nextSemicolon + 1); + l.add(UnresolvedType.forSignature(nextbit)); + i = nextSemicolon + 1; + } else { + i++; + l.add(UnresolvedType.forSignature(sig.substring(start, i))); + } + } + UnresolvedType[] paramTypes = l.toArray(new UnresolvedType[l.size()]); + UnresolvedType returnType = UnresolvedType.forSignature(sig.substring(i + 1, sig.length())); + return new Object[] { returnType, paramTypes }; + } else { + UnresolvedType returnType = UnresolvedType.forSignature(sig.substring(2)); + return new Object[] { returnType, UnresolvedType.NONE }; + } + } + + // ---- factory methods + public static MemberImpl field(String declaring, int mods, String name, String signature) { + return field(declaring, mods, UnresolvedType.forSignature(signature), name); + } + + // OPTIMIZE do we need to call this? unless necessary the signatureToTypes() + // call smacks of laziness on the behalf of the caller of this method + public static MemberImpl method(UnresolvedType declaring, int mods, String name, String signature) { + Object[] pair = signatureToTypes(signature); + return method(declaring, mods, (UnresolvedType) pair[0], name, (UnresolvedType[]) pair[1]); + } + + public static MemberImpl monitorEnter() { + return new MemberImpl(MONITORENTER, UnresolvedType.OBJECT, Modifier.STATIC, UnresolvedType.VOID, "<lock>", + UnresolvedType.ARRAY_WITH_JUST_OBJECT); + } + + public static MemberImpl monitorExit() { + return new MemberImpl(MONITOREXIT, UnresolvedType.OBJECT, Modifier.STATIC, UnresolvedType.VOID, "<unlock>", + UnresolvedType.ARRAY_WITH_JUST_OBJECT); + } + + public static Member pointcut(UnresolvedType declaring, String name, String signature) { + Object[] pair = signatureToTypes(signature); + return pointcut(declaring, 0, (UnresolvedType) pair[0], name, (UnresolvedType[]) pair[1]); + } + + private static MemberImpl field(String declaring, int mods, UnresolvedType ty, String name) { + return new MemberImpl(FIELD, UnresolvedType.forName(declaring), mods, ty, name, UnresolvedType.NONE); + } + + public static MemberImpl method(UnresolvedType declTy, int mods, UnresolvedType rTy, String name, UnresolvedType[] paramTys) { + return new MemberImpl( + // ??? this calls <clinit> a method + name.equals("<init>") ? CONSTRUCTOR : METHOD, declTy, mods, rTy, name, paramTys); + } + + private static Member pointcut(UnresolvedType declTy, int mods, UnresolvedType rTy, String name, UnresolvedType[] paramTys) { + return new MemberImpl(POINTCUT, declTy, mods, rTy, name, paramTys); + } + + public static ResolvedMemberImpl makeExceptionHandlerSignature(UnresolvedType inType, UnresolvedType catchType) { + return new ResolvedMemberImpl(HANDLER, inType, Modifier.STATIC, "<catch>", "(" + catchType.getSignature() + ")V"); + } + + @Override + public final boolean equals(Object other) { + if (!(other instanceof Member)) { + return false; + } + Member o = (Member) other; + return (getKind() == o.getKind() && getName().equals(o.getName()) && getSignature().equals(o.getSignature()) && getDeclaringType() + .equals(o.getDeclaringType())); + } + + /** + * @return true if this member equals the one supplied in every respect other than the declaring type + */ + public final boolean equalsApartFromDeclaringType(Object other) { + if (!(other instanceof Member)) { + return false; + } + Member o = (Member) other; + return (getKind() == o.getKind() && getName().equals(o.getName()) && getSignature().equals(o.getSignature())); + } + + /** + * Equality is checked based on the underlying signature, so the hash code of a member is based on its kind, name, signature, + * and declaring type. The algorithm for this was taken from page 38 of effective java. + */ + private volatile int hashCode = 0; + + @Override + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37 * result + getKind().hashCode(); + result = 37 * result + getName().hashCode(); + result = 37 * result + getSignature().hashCode(); + result = 37 * result + getDeclaringType().hashCode(); + hashCode = result; + } + return hashCode; + } + + public int compareTo(Member other) { + Member o = other; + int i = getName().compareTo(o.getName()); + if (i != 0) { + return i; + } + return getSignature().compareTo(o.getSignature()); + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(returnType.getName()); + buf.append(' '); + if (declaringType == null) { + buf.append("<NULL>"); + } else { + buf.append(declaringType.getName()); + } + buf.append('.'); + buf.append(name); + if (kind != FIELD) { + buf.append("("); + if (parameterTypes.length != 0) { + buf.append(parameterTypes[0]); + for (int i = 1, len = parameterTypes.length; i < len; i++) { + buf.append(", "); + buf.append(parameterTypes[i].getName()); + } + } + buf.append(")"); + } + return buf.toString(); + } + + public MemberKind getKind() { + return kind; + } + + public UnresolvedType getDeclaringType() { + return declaringType; + } + + public UnresolvedType getReturnType() { + return returnType; + } + + public UnresolvedType getGenericReturnType() { + return getReturnType(); + } + + public UnresolvedType[] getGenericParameterTypes() { + return getParameterTypes(); + } + + public final UnresolvedType getType() { + return returnType; + } + + public String getName() { + return name; + } + + public UnresolvedType[] getParameterTypes() { + return parameterTypes; + } + + public String getSignature() { + return erasedSignature; + } + + public int getArity() { + return parameterTypes.length; + } + + public String getParameterSignature() { + if (paramSignature == null) { + StringBuilder sb = new StringBuilder("("); + for (UnresolvedType parameterType : parameterTypes) { + sb.append(parameterType.getSignature()); + } + paramSignature = sb.append(")").toString(); + } + return paramSignature; + } + + // OPTIMIZE see next line. Why the hell are they in here if we only know it + // once resolution has occurred... + // ---- things we know only with resolution + + public int getModifiers(World world) { + ResolvedMember resolved = resolve(world); + if (resolved == null) { + reportDidntFindMember(world); + return 0; + } + return resolved.getModifiers(); + } + + public UnresolvedType[] getExceptions(World world) { + ResolvedMember resolved = resolve(world); + if (resolved == null) { + reportDidntFindMember(world); + return UnresolvedType.NONE; + } + return resolved.getExceptions(); + } + + public final boolean isStatic() { + return Modifier.isStatic(modifiers); + } + + public final boolean isInterface() { + return Modifier.isInterface(modifiers); + } + + public final boolean isPrivate() { + return Modifier.isPrivate(modifiers); + } + + public boolean canBeParameterized() { + return false; + } + + public int getModifiers() { + return modifiers; + } + + public AnnotationAJ[] getAnnotations() { + throw new UnsupportedOperationException("You should resolve this member '" + this + + "' and call getAnnotations() on the result..."); + } + + // ---- fields 'n' stuff + + public Collection<ResolvedType> getDeclaringTypes(World world) { + ResolvedType myType = getDeclaringType().resolve(world); + Collection<ResolvedType> ret = new HashSet<ResolvedType>(); + if (kind == CONSTRUCTOR) { + // this is wrong if the member doesn't exist, but that doesn't + // matter + ret.add(myType); + } else if (Modifier.isStatic(modifiers) || kind == FIELD) { + walkUpStatic(ret, myType); + } else { + walkUp(ret, myType); + } + + return ret; + } + + private boolean walkUp(Collection<ResolvedType> acc, ResolvedType curr) { + if (acc.contains(curr)) { + return true; + } + + boolean b = false; + for (Iterator<ResolvedType> i = curr.getDirectSupertypes(); i.hasNext();) { + b |= walkUp(acc, i.next()); + } + + if (!b && curr.isParameterizedType()) { + b = walkUp(acc, curr.getGenericType()); + } + + if (!b) { + b = curr.lookupMemberNoSupers(this) != null; + } + if (b) { + acc.add(curr); + } + return b; + } + + private boolean walkUpStatic(Collection<ResolvedType> acc, ResolvedType curr) { + if (curr.lookupMemberNoSupers(this) != null) { + acc.add(curr); + return true; + } else { + boolean b = false; + for (Iterator<ResolvedType> i = curr.getDirectSupertypes(); i.hasNext();) { + b |= walkUpStatic(acc, i.next()); + } + if (!b && curr.isParameterizedType()) { + b = walkUpStatic(acc, curr.getGenericType()); + } + if (b) { + acc.add(curr); + } + return b; + } + } + + public String[] getParameterNames(World world) { + ResolvedMember resolved = resolve(world); + if (resolved == null) { + reportDidntFindMember(world); + return null; + } + return resolved.getParameterNames(); + } + + /** + * All the signatures that a join point with this member as its signature has. + */ + public JoinPointSignatureIterator getJoinPointSignatures(World inAWorld) { + if (joinPointSignatures == null) { + joinPointSignatures = new JoinPointSignatureIterator(this, inAWorld); + } + joinPointSignatures.reset(); + return joinPointSignatures; + } + + /** + * Raises an [Xlint:cantFindType] message if the declaring type cannot be found or an [Xlint:unresolvableMember] message if the + * type can be found (bug 149908) + */ + private void reportDidntFindMember(World world) { + if (reportedCantFindDeclaringType || reportedUnresolvableMember) { + return; + } + ResolvedType rType = getDeclaringType().resolve(world); + if (rType.isMissing()) { + world.getLint().cantFindType.signal(WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE, rType.getName()), null); + reportedCantFindDeclaringType = true; + } else { + world.getLint().unresolvableMember.signal(getName(), null); + reportedUnresolvableMember = true; + } + } + + public void wipeJoinpointSignatures() { + joinPointSignatures = null; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MemberKind.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MemberKind.java new file mode 100644 index 000000000..f1d5dfa0f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MemberKind.java @@ -0,0 +1,48 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import java.io.DataInputStream; +import java.io.IOException; + +import org.aspectj.util.TypeSafeEnum; + +public class MemberKind extends TypeSafeEnum { + public MemberKind(String name, int key) { + super(name, key); + } + + public static MemberKind read(DataInputStream s) throws IOException { + int key = s.readByte(); + switch (key) { + case 1: + return Member.METHOD; + case 2: + return Member.FIELD; + case 3: + return Member.CONSTRUCTOR; + case 4: + return Member.STATIC_INITIALIZATION; + case 5: + return Member.POINTCUT; + case 6: + return Member.ADVICE; + case 7: + return Member.HANDLER; + case 8: + return Member.MONITORENTER; + case 9: + return Member.MONITOREXIT; + } + throw new BCException("Unexpected memberkind, should be (1-9) but was " + key); + } +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MemberUtils.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MemberUtils.java new file mode 100644 index 000000000..201eb758f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MemberUtils.java @@ -0,0 +1,25 @@ +/* ******************************************************************* + * Copyright (c) 2010 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, SpringSource + * ******************************************************************/ +package org.aspectj.weaver; + +/** + * Common utility methods for members. + * + * @author Andy Clement + */ +public class MemberUtils { + + public static boolean isConstructor(ResolvedMember member) { + return member.getName().equals("<init>"); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MethodDelegateTypeMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MethodDelegateTypeMunger.java new file mode 100644 index 000000000..79eab7d4d --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MethodDelegateTypeMunger.java @@ -0,0 +1,298 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.IOException; + +import org.aspectj.weaver.patterns.TypePattern; + +/** + * Type munger for annotation style ITD declare parents. with an interface AND an implementation. Given the aspect that has a field + * public static Interface fieldI = ... // impl. we will weave in the Interface' methods and delegate to the aspect public static + * field fieldI + * + * Note: this munger DOES NOT handles the interface addition to the target classes - a regular Parent kinded munger must be added in + * coordination. + */ +public class MethodDelegateTypeMunger extends ResolvedTypeMunger { + + private final UnresolvedType aspect; + + private UnresolvedType fieldType; + + /** + * The mixin implementation (which should have a no-argument constructor) + */ + private final String implClassName; + + /** + * Type pattern this munger applies to + */ + private final TypePattern typePattern; + + /** + * When created to represent a mixed in method for @DeclareMixin, these hold the signature of the factory method + */ + private String factoryMethodName; + private String factoryMethodSignature; + + private int bitflags; + private static final int REPLACING_EXISTING_METHOD = 0x001; + + /** + * Construct a new type munger for @AspectJ ITD + * + * @param signature + * @param aspect + * @param implClassName + * @param typePattern + */ + public MethodDelegateTypeMunger(ResolvedMember signature, UnresolvedType aspect, String implClassName, TypePattern typePattern) { + super(MethodDelegate2, signature); + this.aspect = aspect; + this.typePattern = typePattern; + this.implClassName = implClassName; + factoryMethodName = ""; + factoryMethodSignature = ""; + } + + public MethodDelegateTypeMunger(ResolvedMember signature, UnresolvedType aspect, String implClassName, TypePattern typePattern, + String factoryMethodName, String factoryMethodSignature) { + super(MethodDelegate2, signature); + this.aspect = aspect; + this.typePattern = typePattern; + this.implClassName = implClassName; + this.factoryMethodName = factoryMethodName; + this.factoryMethodSignature = factoryMethodSignature; + } + + public boolean equals(Object other) { + if (!(other instanceof MethodDelegateTypeMunger)) { + return false; + } + MethodDelegateTypeMunger o = (MethodDelegateTypeMunger) other; + return ((o.aspect == null) ? (aspect == null) : aspect.equals(o.aspect)) + && ((o.typePattern == null) ? (typePattern == null) : typePattern.equals(o.typePattern)) + && ((o.implClassName == null) ? (implClassName == null) : implClassName.equals(o.implClassName)) + && ((o.fieldType == null ? (fieldType == null) : fieldType.equals(o.fieldType))) + && ((o.factoryMethodName == null) ? (factoryMethodName == null) : factoryMethodName.equals(o.factoryMethodName)) + && ((o.factoryMethodSignature == null) ? (factoryMethodSignature == null) : factoryMethodSignature + .equals(o.factoryMethodSignature)) && o.bitflags == bitflags; + } + + private volatile int hashCode = 0; + + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37 * result + ((aspect == null) ? 0 : aspect.hashCode()); + result = 37 * result + ((typePattern == null) ? 0 : typePattern.hashCode()); + result = 37 * result + ((implClassName == null) ? 0 : implClassName.hashCode()); + result = 37 * result + ((fieldType == null) ? 0 : fieldType.hashCode()); + result = 37 * result + ((factoryMethodName == null) ? 0 : factoryMethodName.hashCode()); + result = 37 * result + ((factoryMethodSignature == null) ? 0 : factoryMethodSignature.hashCode()); + result = 37 * result + bitflags; + hashCode = result; + } + return hashCode; + } + + public ResolvedMember getDelegate(ResolvedType targetType) { + return AjcMemberMaker.itdAtDeclareParentsField(targetType, fieldType, aspect); + } + + public ResolvedMember getDelegateFactoryMethod(World w) { + ResolvedType aspectType = w.resolve(aspect); + ResolvedMember[] methods = aspectType.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + ResolvedMember rm = methods[i]; + if (rm.getName().equals(factoryMethodName) && rm.getSignature().equals(factoryMethodSignature)) { + return rm; + } + } + return null; + } + + public String getImplClassName() { + return implClassName; + } + + public void write(CompressingDataOutputStream s) throws IOException { + kind.write(s); + signature.write(s); + aspect.write(s); + s.writeUTF(implClassName); + typePattern.write(s); + fieldType.write(s); + s.writeUTF(factoryMethodName); + s.writeUTF(factoryMethodSignature); + s.writeInt(bitflags); + } + + public static ResolvedTypeMunger readMethod(VersionedDataInputStream s, ISourceContext context, boolean isEnhanced) + throws IOException { + ResolvedMemberImpl signature = ResolvedMemberImpl.readResolvedMember(s, context); + UnresolvedType aspect = UnresolvedType.read(s); + String implClassName = s.readUTF(); + TypePattern tp = TypePattern.read(s, context); + MethodDelegateTypeMunger typeMunger = new MethodDelegateTypeMunger(signature, aspect, implClassName, tp); + UnresolvedType fieldType = null; + if (isEnhanced) { + fieldType = UnresolvedType.read(s); + } else { + // a guess... that will work in a lot of cases + fieldType = signature.getDeclaringType(); + } + typeMunger.setFieldType(fieldType); + if (isEnhanced) { + typeMunger.factoryMethodName = s.readUTF(); + typeMunger.factoryMethodSignature = s.readUTF(); + typeMunger.bitflags = s.readInt(); + } + return typeMunger; + } + + /** + * Match based on given type pattern, only classes can be matched + * + * @param matchType + * @param aspectType + * @return true if match + */ + public boolean matches(ResolvedType matchType, ResolvedType aspectType) { + // match only on class + if (matchType.isEnum() || matchType.isInterface() || matchType.isAnnotation()) { + return false; + } + + return typePattern.matchesStatically(matchType); + } + + /** + * Needed for reweavable + * + * @return true + */ + public boolean changesPublicSignature() { + return true; + } + + public static class FieldHostTypeMunger extends ResolvedTypeMunger { + + private final UnresolvedType aspect; + + /** + * Type pattern this munger applies to + */ + private final TypePattern typePattern; + + /** + * Construct a new type munger for @AspectJ ITD + * + * @param field + * @param aspect + * @param typePattern + */ + public FieldHostTypeMunger(ResolvedMember field, UnresolvedType aspect, TypePattern typePattern) { + super(FieldHost, field); + this.aspect = aspect; + this.typePattern = typePattern; + } + + public boolean equals(Object other) { + if (!(other instanceof FieldHostTypeMunger)) { + return false; + } + FieldHostTypeMunger o = (FieldHostTypeMunger) other; + return ((o.aspect == null) ? (aspect == null) : aspect.equals(o.aspect)) + && ((o.typePattern == null) ? (typePattern == null) : typePattern.equals(o.typePattern)); + } + + public int hashCode() { + int result = 17; + result = 37 * result + ((aspect == null) ? 0 : aspect.hashCode()); + result = 37 * result + ((typePattern == null) ? 0 : typePattern.hashCode()); + return result; + } + + public void write(CompressingDataOutputStream s) throws IOException { + kind.write(s); + signature.write(s); + aspect.write(s); + typePattern.write(s); + } + + public static ResolvedTypeMunger readFieldHost(VersionedDataInputStream s, ISourceContext context) throws IOException { + ResolvedMemberImpl signature = ResolvedMemberImpl.readResolvedMember(s, context); + UnresolvedType aspect = UnresolvedType.read(s); + TypePattern tp = TypePattern.read(s, context); + return new FieldHostTypeMunger(signature, aspect, tp); + } + + /** + * Match based on given type pattern, only classes can be matched + * + * @param matchType + * @param aspectType + * @return true if match + */ + public boolean matches(ResolvedType matchType, ResolvedType aspectType) { + // match only on class + if (matchType.isEnum() || matchType.isInterface() || matchType.isAnnotation()) { + return false; + } + + return typePattern.matchesStatically(matchType); + } + + public boolean changesPublicSignature() { + return false; + } + + public boolean existsToSupportShadowMunging() { + return true; + } + } + + public void setFieldType(UnresolvedType fieldType) { + this.fieldType = fieldType; + } + + public boolean specifiesDelegateFactoryMethod() { + return factoryMethodName != null && factoryMethodName.length() != 0; + } + + public String getFactoryMethodName() { + return factoryMethodName; + } + + public String getFactoryMethodSignature() { + return factoryMethodSignature; + } + + public UnresolvedType getAspect() { + return aspect; + } + + public boolean existsToSupportShadowMunging() { + return true; + } + + public void tagAsReplacingExistingMethod() { + bitflags |= REPLACING_EXISTING_METHOD; + } + + public boolean isReplacingExistingMethod() { + return (bitflags & REPLACING_EXISTING_METHOD) != 0; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MissingResolvedTypeWithKnownSignature.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MissingResolvedTypeWithKnownSignature.java new file mode 100644 index 000000000..da61ffd8b --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/MissingResolvedTypeWithKnownSignature.java @@ -0,0 +1,253 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.Collections; +import java.util.List; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.context.CompilationAndWeavingContext; + +/** + * When we try to resolve a type in the world that we require to be present, and then fail to find it, we return an instance of this + * class. This class defers the production of the "can't find type error" until the first time that someone asks a question that + * can't be answered solely from the signature. This enables the weaver to be more tolerant of missing types. + * + */ +public class MissingResolvedTypeWithKnownSignature extends ResolvedType { + + private static ResolvedMember[] NO_MEMBERS = new ResolvedMember[0]; + private static ResolvedType[] NO_TYPES = new ResolvedType[0]; + private boolean issuedCantFindTypeError = false; + private boolean issuedJoinPointWarning = false; + private boolean issuedMissingInterfaceWarning = false; + + /** + * @param signature + * @param world + */ + public MissingResolvedTypeWithKnownSignature(String signature, World world) { + super(signature, world); + } + + @Override + public boolean isMissing() { + return true; + } + + /** + * @param signature + * @param signatureErasure + * @param world + */ + public MissingResolvedTypeWithKnownSignature(String signature, String signatureErasure, World world) { + super(signature, signatureErasure, world); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ResolvedType#getDeclaredFields() + */ + @Override + public ResolvedMember[] getDeclaredFields() { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_FIELDS); + return NO_MEMBERS; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ResolvedType#getDeclaredMethods() + */ + @Override + public ResolvedMember[] getDeclaredMethods() { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_METHODS); + return NO_MEMBERS; + } + + @Override + public AnnotationAJ[] getAnnotations() { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_ANNOTATION); + return AnnotationAJ.EMPTY_ARRAY; + } + + @Override + public ResolvedType[] getDeclaredInterfaces() { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_INTERFACES); + return NO_TYPES; + } + + @Override + public ResolvedMember[] getDeclaredPointcuts() { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_POINTCUTS); + return NO_MEMBERS; + } + + @Override + public ResolvedType getSuperclass() { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_SUPERCLASS); + return ResolvedType.MISSING; + } + + @Override + public int getModifiers() { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_MODIFIERS); + return 0; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ResolvedType#getSourceContext() + */ + @Override + public ISourceContext getSourceContext() { + return new ISourceContext() { + + @Override + public ISourceLocation makeSourceLocation(IHasPosition position) { + return null; + } + + @Override + public ISourceLocation makeSourceLocation(int line, int offset) { + return null; + } + + @Override + public int getOffset() { + return 0; + } + + @Override + public void tidy() { + } + + }; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ResolvedType#isAssignableFrom(org.aspectj.weaver.ResolvedType) + */ + @Override + public boolean isAssignableFrom(ResolvedType other) { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_ASSIGNABLE, other.getName()); + return false; + } + + @Override + public boolean isAssignableFrom(ResolvedType other, boolean allowMissing) { + if (allowMissing) { + return false; + } else { + return isAssignableFrom(other); + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ResolvedType#isCoerceableFrom(org.aspectj.weaver.ResolvedType) + */ + @Override + public boolean isCoerceableFrom(ResolvedType other) { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_COERCEABLE, other.getName()); + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.AnnotatedElement#hasAnnotation(org.aspectj.weaver.UnresolvedType) + */ + @Override + public boolean hasAnnotation(UnresolvedType ofType) { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_ANNOTATION); + return false; + } + + @Override + public List getInterTypeMungers() { + return Collections.EMPTY_LIST; + } + + @Override + public List getInterTypeMungersIncludingSupers() { + return Collections.EMPTY_LIST; + } + + @Override + public List getInterTypeParentMungers() { + return Collections.EMPTY_LIST; + } + + @Override + public List getInterTypeParentMungersIncludingSupers() { + return Collections.EMPTY_LIST; + } + + @Override + protected void collectInterTypeMungers(List collector) { + return; + } + + public void raiseWarningOnJoinPointSignature(String signature) { + if (issuedJoinPointWarning) { + return; + } + String message = WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_JOINPOINT, getName(), signature); + message += "\n" + CompilationAndWeavingContext.getCurrentContext(); + world.getLint().cantFindTypeAffectingJoinPointMatch.signal(message, null); + // MessageUtil.warn(world.getMessageHandler(),message); + issuedJoinPointWarning = true; + } + + public void raiseWarningOnMissingInterfaceWhilstFindingMethods() { + if (issuedMissingInterfaceWarning) { + return; + } + String message = WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_INTERFACE_METHODS, getName(), signature); + message += "\n" + CompilationAndWeavingContext.getCurrentContext(); + world.getLint().cantFindTypeAffectingJoinPointMatch.signal(message, null); + // MessageUtil.warn(world.getMessageHandler(),message); + issuedMissingInterfaceWarning = true; + } + + private void raiseCantFindType(String key) { + if (!world.getLint().cantFindType.isEnabled()) { + return; + } + if (issuedCantFindTypeError) { + return; + } + String message = WeaverMessages.format(key, getName()); + message += "\n" + CompilationAndWeavingContext.getCurrentContext(); + world.getLint().cantFindType.signal(message, null); + // MessageUtil.error(world.getMessageHandler(),message); + issuedCantFindTypeError = true; + } + + private void raiseCantFindType(String key, String insert) { + if (issuedCantFindTypeError) { + return; + } + String message = WeaverMessages.format(key, getName(), insert); + message += "\n" + CompilationAndWeavingContext.getCurrentContext(); + world.getLint().cantFindType.signal(message, null); + // MessageUtil.error(world.getMessageHandler(),message); + issuedCantFindTypeError = true; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NameMangler.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NameMangler.java new file mode 100644 index 000000000..f9162684a --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NameMangler.java @@ -0,0 +1,357 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.lang.reflect.Modifier; + +public class NameMangler { + + // public static final char[] AJC_DOLLAR_PREFIX = { 'a', 'j', 'c', '$' }; + // public static final char[] CLINIT = { '<', 'c', 'l', 'i', 'n', 'i', 't', '>' }; + public static final String PREFIX = "ajc$"; + public static final char[] PREFIX_CHARS = "ajc$".toCharArray(); + // public static final char[] INIT = { '<', 'i', 'n', 'i', 't', '>' }; + public static final String ITD_PREFIX = PREFIX + "interType$"; + // public static final char[] METHOD_ASPECTOF = {'a', 's', 'p','e','c','t','O','f'}; + // public static final char[] METHOD_HASASPECT = { 'h', 'a', 's', 'A', 's', 'p', 'e', 'c', 't' }; + // public static final char[] STATIC_INITIALIZER = { '<', 'c', 'l', 'i', 'n', 'i', 't', '>' }; + + public static final String CFLOW_STACK_TYPE = "org.aspectj.runtime.internal.CFlowStack"; + public static final String CFLOW_COUNTER_TYPE = "org.aspectj.runtime.internal.CFlowCounter"; + + public static final UnresolvedType CFLOW_STACK_UNRESOLVEDTYPE = UnresolvedType + .forSignature("Lorg/aspectj/runtime/internal/CFlowStack;"); + + public static final UnresolvedType CFLOW_COUNTER_UNRESOLVEDTYPE = UnresolvedType + .forSignature("Lorg/aspectj/runtime/internal/CFlowCounter;"); + + public static final String SOFT_EXCEPTION_TYPE = "org.aspectj.lang.SoftException"; + + public static final String PERSINGLETON_FIELD_NAME = PREFIX + "perSingletonInstance"; + public static final String PERCFLOW_FIELD_NAME = PREFIX + "perCflowStack"; + // public static final String PERTHIS_FIELD_NAME = PREFIX + "perSingletonInstance"; + + // ----- + public static final String PERCFLOW_PUSH_METHOD = PREFIX + "perCflowPush"; + + public static final String PEROBJECT_BIND_METHOD = PREFIX + "perObjectBind"; + + // PTWIMPL Method and field names + public static final String PERTYPEWITHIN_GETINSTANCE_METHOD = PREFIX + "getInstance"; + public static final String PERTYPEWITHIN_CREATEASPECTINSTANCE_METHOD = PREFIX + "createAspectInstance"; + public static final String PERTYPEWITHIN_WITHINTYPEFIELD = PREFIX + "withinType"; + public static final String PERTYPEWITHIN_GETWITHINTYPENAME_METHOD = "getWithinTypeName"; + + public static final String AJC_PRE_CLINIT_NAME = PREFIX + "preClinit"; + + public static final String AJC_POST_CLINIT_NAME = PREFIX + "postClinit"; + + public static final String INITFAILURECAUSE_FIELD_NAME = PREFIX + "initFailureCause"; + + public static final String ANNOTATION_CACHE_FIELD_NAME = PREFIX + "anno$"; + + public static boolean isSyntheticMethod(String methodName, boolean declaredInAspect) { + if (methodName.startsWith(PREFIX)) { + // it's synthetic unless it is an advice method + if (methodName.startsWith("ajc$before") || methodName.startsWith("ajc$after")) { + return false; + } else if (methodName.startsWith("ajc$around")) { + // around advice method is not synthetic, but generated proceed is... + return (methodName.endsWith("proceed")); + } else if (methodName.startsWith("ajc$interMethod$")) { + return false; // body of an itd-m + } + return true; + } else if (methodName.indexOf("_aroundBody") != -1) { + return true; + } + // these aren't the droids you're looking for...move along...... pr148727 + // else if (declaredInAspect) { + // if (methodName.equals("aspectOf") || methodName.equals("hasAspect")) { + // return true; + // } + // } + return false; + } + + public static String perObjectInterfaceGet(UnresolvedType aspectType) { + return makeName(aspectType.getNameAsIdentifier(), "perObjectGet"); + } + + public static String perObjectInterfaceSet(UnresolvedType aspectType) { + return makeName(aspectType.getNameAsIdentifier(), "perObjectSet"); + } + + public static String perObjectInterfaceField(UnresolvedType aspectType) { + return makeName(aspectType.getNameAsIdentifier(), "perObjectField"); + } + + // PTWIMPL method names that must include aspect type + public static String perTypeWithinFieldForTarget(UnresolvedType aspectType) { + return makeName(aspectType.getNameAsIdentifier(), "ptwAspectInstance"); + } + + public static String perTypeWithinLocalAspectOf(UnresolvedType aspectType) { + return makeName(aspectType.getNameAsIdentifier(), "localAspectOf"); + } + + public static String itdAtDeclareParentsField(UnresolvedType aspectType, UnresolvedType itdType) { + return makeName("instance", aspectType.getNameAsIdentifier(), itdType.getNameAsIdentifier()); + } + + public static String privilegedAccessMethodForMethod(String name, UnresolvedType objectType, UnresolvedType aspectType) { + return makeName("privMethod", aspectType.getNameAsIdentifier(), objectType.getNameAsIdentifier(), name); + } + + /** + * Create the old style (<1.6.9) format getter name which includes the aspect requesting access and the type containing the + * field in the name of the type. At 1.6.9 and above the name is simply ajc$get$<fieldname> + */ + public static String privilegedAccessMethodForFieldGet(String name, UnresolvedType objectType, UnresolvedType aspectType) { + StringBuilder nameBuilder = new StringBuilder(); + nameBuilder.append(makeName("privFieldGet", aspectType.getNameAsIdentifier(), objectType.getNameAsIdentifier(), name)); + return nameBuilder.toString(); + } + + /** + * Create the old style (<1.6.9) format setter name which includes the aspect requesting access and the type containing the + * field in the name of the type. At 1.6.9 and above the name is simply ajc$set$<fieldname> + */ + public static String privilegedAccessMethodForFieldSet(String name, UnresolvedType objectType, UnresolvedType aspectType) { + return makeName("privFieldSet", aspectType.getNameAsIdentifier(), objectType.getNameAsIdentifier(), name); + } + + public static String inlineAccessMethodForMethod(String name, UnresolvedType objectType, UnresolvedType aspectType) { + return makeName("inlineAccessMethod", aspectType.getNameAsIdentifier(), objectType.getNameAsIdentifier(), name); + } + + public static String inlineAccessMethodForFieldGet(String name, UnresolvedType objectType, UnresolvedType aspectType) { + return makeName("inlineAccessFieldGet", aspectType.getNameAsIdentifier(), objectType.getNameAsIdentifier(), name); + } + + public static String inlineAccessMethodForFieldSet(String name, UnresolvedType objectType, UnresolvedType aspectType) { + return makeName("inlineAccessFieldSet", aspectType.getNameAsIdentifier(), objectType.getNameAsIdentifier(), name); + } + + /** + * The name of methods corresponding to advice declarations Of the form: + * "ajc$[AdviceKind]$[AspectName]$[NumberOfAdviceInAspect]$[PointcutHash]" + */ + public static String adviceName(String nameAsIdentifier, AdviceKind kind, int adviceSeqNumber, int pcdHash) { + String newname = makeName(kind.getName(), nameAsIdentifier, Integer.toString(adviceSeqNumber), Integer.toHexString(pcdHash)); + return newname; + } + + /** + * This field goes on top-most implementers of the interface the field is declared onto + */ + public static String interFieldInterfaceField(UnresolvedType aspectType, UnresolvedType interfaceType, String name) { + return makeName("interField", aspectType.getNameAsIdentifier(), interfaceType.getNameAsIdentifier(), name); + } + + /** + * This instance method goes on the interface the field is declared onto as well as its top-most implementors + */ + public static String interFieldInterfaceSetter(UnresolvedType aspectType, UnresolvedType interfaceType, String name) { + return makeName("interFieldSet", aspectType.getNameAsIdentifier(), interfaceType.getNameAsIdentifier(), name); + } + + /** + * This instance method goes on the interface the field is declared onto as well as its top-most implementors + */ + public static String interFieldInterfaceGetter(UnresolvedType aspectType, UnresolvedType interfaceType, String name) { + return makeName("interFieldGet", aspectType.getNameAsIdentifier(), interfaceType.getNameAsIdentifier(), name); + } + + /** + * This static method goes on the aspect that declares the inter-type field + */ + public static String interFieldSetDispatcher(UnresolvedType aspectType, UnresolvedType onType, String name) { + return makeName("interFieldSetDispatch", aspectType.getNameAsIdentifier(), onType.getNameAsIdentifier(), name); + } + + /** + * This static method goes on the aspect that declares the inter-type field + */ + public static String interFieldGetDispatcher(UnresolvedType aspectType, UnresolvedType onType, String name) { + return makeName("interFieldGetDispatch", aspectType.getNameAsIdentifier(), onType.getNameAsIdentifier(), name); + } + + /** + * This field goes on the class the field is declared onto + */ + public static String interFieldClassField(int modifiers, UnresolvedType aspectType, UnresolvedType classType, String name) { + // return name; + if (Modifier.isPublic(modifiers)) { + return name; + } + // // ??? might want to handle case where aspect and class are in same package similar to public + return makeName("interField", makeVisibilityName(modifiers, aspectType), name); + } + + // /** + // * This static method goes on the aspect that declares the inter-type field + // */ + // public static String classFieldSetDispatcher(UnresolvedType aspectType, UnresolvedType classType, String name) { + // return makeName("interFieldSetDispatch", aspectType.getNameAsIdentifier(), + // classType.getNameAsIdentifier(), name); + // } + // + // /** + // * This static method goes on the aspect that declares the inter-type field + // */ + // public static String classFieldGetDispatcher(UnresolvedType aspectType, UnresolvedType classType, String name) + // { + // return makeName( + // "interFieldGetDispatch", + // aspectType.getNameAsIdentifier(), + // classType.getNameAsIdentifier(), + // name); + // } + + /** + * This static void method goes on the aspect that declares the inter-type field and is called from the appropriate place + * (target's initializer, or clinit, or topmost implementer's inits), to initialize the field; + */ + + public static String interFieldInitializer(UnresolvedType aspectType, UnresolvedType classType, String name) { + return makeName("interFieldInit", aspectType.getNameAsIdentifier(), classType.getNameAsIdentifier(), name); + } + + // ---- + + /** + * This method goes on the target type of the inter-type method. (and possibly the topmost-implemeters, if the target type is an + * interface) + */ + public static String interMethod(int modifiers, UnresolvedType aspectType, UnresolvedType classType, String name) { + if (Modifier.isPublic(modifiers)) { + return name; + } + // ??? might want to handle case where aspect and class are in same package similar to public + return makeName("interMethodDispatch2", makeVisibilityName(modifiers, aspectType), name); + } + + /** + * This static method goes on the declaring aspect of the inter-type method. + */ + public static String interMethodDispatcher(UnresolvedType aspectType, UnresolvedType classType, String name) { + return makeName("interMethodDispatch1", aspectType.getNameAsIdentifier(), classType.getNameAsIdentifier(), name); + } + + /** + * This static method goes on the declaring aspect of the inter-type method. + */ + public static String interMethodBody(UnresolvedType aspectType, UnresolvedType classType, String name) { + return makeName("interMethod", aspectType.getNameAsIdentifier(), classType.getNameAsIdentifier(), name); + } + + // ---- + + /** + * This static method goes on the declaring aspect of the inter-type constructor. + */ + public static String preIntroducedConstructor(UnresolvedType aspectType, UnresolvedType targetType) { + return makeName("preInterConstructor", aspectType.getNameAsIdentifier(), targetType.getNameAsIdentifier()); + } + + /** + * This static method goes on the declaring aspect of the inter-type constructor. + */ + public static String postIntroducedConstructor(UnresolvedType aspectType, UnresolvedType targetType) { + return makeName("postInterConstructor", aspectType.getNameAsIdentifier(), targetType.getNameAsIdentifier()); + } + + // ---- + + /** + * This static method goes on the target class of the inter-type method. + */ + public static String superDispatchMethod(UnresolvedType classType, String name) { + return makeName("superDispatch", classType.getNameAsIdentifier(), name); + } + + /** + * This static method goes on the target class of the inter-type method. + */ + public static String protectedDispatchMethod(UnresolvedType classType, String name) { + return makeName("protectedDispatch", classType.getNameAsIdentifier(), name); + } + + // ---- + + private static String makeVisibilityName(int modifiers, UnresolvedType aspectType) { + if (Modifier.isPrivate(modifiers)) { + return aspectType.getOutermostType().getNameAsIdentifier(); + } else if (Modifier.isProtected(modifiers)) { + throw new RuntimeException("protected inter-types not allowed"); + } else if (Modifier.isPublic(modifiers)) { + return ""; + } else { + return aspectType.getPackageNameAsIdentifier(); + } + } + + private static String makeName(String s1, String s2) { + return "ajc$" + s1 + "$" + s2; + } + + public static String makeName(String s1, String s2, String s3) { + return "ajc$" + s1 + "$" + s2 + "$" + s3; + } + + public static String makeName(String s1, String s2, String s3, String s4) { + return "ajc$" + s1 + "$" + s2 + "$" + s3 + "$" + s4; + } + + public static String cflowStack(CrosscuttingMembers xcut) { + return makeName("cflowStack", Integer.toHexString(xcut.getCflowEntries().size())); + } + + public static String cflowCounter(CrosscuttingMembers xcut) { + return makeName("cflowCounter", Integer.toHexString(xcut.getCflowEntries().size())); + } + + public static String makeClosureClassName(UnresolvedType enclosingType, String suffix) { + return enclosingType.getName() + "$AjcClosure" + suffix; + } + + public static String aroundShadowMethodName(Member shadowSig, String suffixTag) { + StringBuffer ret = new StringBuffer(); + ret.append(getExtractableName(shadowSig)).append("_aroundBody").append(suffixTag); + return ret.toString(); + } + + public static String aroundAdviceMethodName(Member shadowSig, String suffixTag) { + StringBuffer ret = new StringBuffer(); + ret.append(getExtractableName(shadowSig)).append("_aroundBody").append(suffixTag).append("$advice"); + return ret.toString(); + } + + public static String getExtractableName(Member shadowSignature) { + String name = shadowSignature.getName(); + MemberKind kind = shadowSignature.getKind(); + if (kind == Member.CONSTRUCTOR) { + return "init$"; + } else if (kind == Member.STATIC_INITIALIZATION) { + return "clinit$"; + } else { + return name; + } + } + + public static String proceedMethodName(String adviceMethodName) { + return adviceMethodName + "proceed"; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewConstructorTypeMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewConstructorTypeMunger.java new file mode 100644 index 000000000..d7d1ba18d --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewConstructorTypeMunger.java @@ -0,0 +1,163 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.IOException; +import java.util.List; +import java.util.Set; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; + +public class NewConstructorTypeMunger extends ResolvedTypeMunger { + private ResolvedMember syntheticConstructor; + private ResolvedMember explicitConstructor; + + public NewConstructorTypeMunger(ResolvedMember signature, ResolvedMember syntheticConstructor, + ResolvedMember explicitConstructor, Set superMethodsCalled, List typeVariableAliases) { + super(Constructor, signature); + this.syntheticConstructor = syntheticConstructor; + this.typeVariableAliases = typeVariableAliases; + this.explicitConstructor = explicitConstructor; + this.setSuperMethodsCalled(superMethodsCalled); + + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof NewConstructorTypeMunger)) { + return false; + } + NewConstructorTypeMunger o = (NewConstructorTypeMunger) other; + return ((syntheticConstructor == null) ? (o.syntheticConstructor == null) : syntheticConstructor + .equals(o.syntheticConstructor)) + & ((explicitConstructor == null) ? (o.explicitConstructor == null) : explicitConstructor + .equals(o.explicitConstructor)); + } + + // pr262218 - equivalence ignores the explicit constructor since that won't have yet been set for an EclipseTypeMunger + public boolean equivalentTo(Object other) { + if (!(other instanceof NewConstructorTypeMunger)) { + return false; + } + NewConstructorTypeMunger o = (NewConstructorTypeMunger) other; + return ((syntheticConstructor == null) ? (o.syntheticConstructor == null) : syntheticConstructor + .equals(o.syntheticConstructor)); + } + + private volatile int hashCode = 0; + + @Override + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37 * result + ((syntheticConstructor == null) ? 0 : syntheticConstructor.hashCode()); + result = 37 * result + ((explicitConstructor == null) ? 0 : explicitConstructor.hashCode()); + hashCode = result; + } + return hashCode; + } + + // doesnt seem required.... + // public ResolvedMember getDispatchMethod(UnresolvedType aspectType) { + // return AjcMemberMaker.interMethodBody(signature, aspectType); + // } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + kind.write(s); + signature.write(s); + syntheticConstructor.write(s); + explicitConstructor.write(s); + writeSuperMethodsCalled(s); + writeSourceLocation(s); + writeOutTypeAliases(s); + } + + public static ResolvedTypeMunger readConstructor(VersionedDataInputStream s, ISourceContext context) throws IOException { + ISourceLocation sloc = null; + ResolvedMember sig = ResolvedMemberImpl.readResolvedMember(s, context); + ResolvedMember syntheticCtor = ResolvedMemberImpl.readResolvedMember(s, context); + ResolvedMember explicitCtor = ResolvedMemberImpl.readResolvedMember(s, context); + Set superMethodsCalled = readSuperMethodsCalled(s); + sloc = readSourceLocation(s); + List typeVarAliases = readInTypeAliases(s); + ResolvedTypeMunger munger = new NewConstructorTypeMunger(sig, syntheticCtor, explicitCtor, superMethodsCalled, + typeVarAliases); + if (sloc != null) { + munger.setSourceLocation(sloc); + } + return munger; + } + + public ResolvedMember getExplicitConstructor() { + return explicitConstructor; + } + + public ResolvedMember getSyntheticConstructor() { + return syntheticConstructor; + } + + public void setExplicitConstructor(ResolvedMember explicitConstructor) { + this.explicitConstructor = explicitConstructor; + // reset hashCode so that its recalculated with new value + hashCode = 0; + } + + @Override + public ResolvedMember getMatchingSyntheticMember(Member member, ResolvedType aspectType) { + ResolvedMember ret = getSyntheticConstructor(); + if (ResolvedType.matches(ret, member)) { + return getSignature(); + } + return super.getMatchingSyntheticMember(member, aspectType); + } + + public void check(World world) { + if (getSignature().getDeclaringType().resolve(world).isAspect()) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ITD_CONS_ON_ASPECT), getSignature() + .getSourceLocation(), null); + } + } + + /** + * see ResolvedTypeMunger.parameterizedFor(ResolvedType) + */ + @Override + public ResolvedTypeMunger parameterizedFor(ResolvedType target) { + ResolvedType genericType = target; + if (target.isRawType() || target.isParameterizedType()) { + genericType = genericType.getGenericType(); + } + ResolvedMember parameterizedSignature = null; + // If we are parameterizing it for a generic type, we just need to 'swap the letters' from the ones used + // in the original ITD declaration to the ones used in the actual target type declaration. + if (target.isGenericType()) { + TypeVariable vars[] = target.getTypeVariables(); + UnresolvedTypeVariableReferenceType[] varRefs = new UnresolvedTypeVariableReferenceType[vars.length]; + for (int i = 0; i < vars.length; i++) { + varRefs[i] = new UnresolvedTypeVariableReferenceType(vars[i]); + } + parameterizedSignature = getSignature().parameterizedWith(varRefs, genericType, true, typeVariableAliases); + } else { + // For raw and 'normal' parameterized targets (e.g. Interface, Interface<String>) + parameterizedSignature = getSignature().parameterizedWith(target.getTypeParameters(), genericType, + target.isParameterizedType(), typeVariableAliases); + } + NewConstructorTypeMunger nctm = new NewConstructorTypeMunger(parameterizedSignature, syntheticConstructor, + explicitConstructor, getSuperMethodsCalled(), typeVariableAliases); + nctm.setSourceLocation(getSourceLocation()); + return nctm; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewFieldTypeMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewFieldTypeMunger.java new file mode 100644 index 000000000..ed8afc1e0 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewFieldTypeMunger.java @@ -0,0 +1,160 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; + +/** + * Code that created version one style ITD type mungers will be using direct field access from the dispatchers + * + * @author Andy + * + */ +public class NewFieldTypeMunger extends ResolvedTypeMunger { + + public static final int VersionOne = 1; + public static final int VersionTwo = 2; // new style ITDs + + public int version = VersionOne; + + public NewFieldTypeMunger(ResolvedMember signature, Set superMethodsCalled, List typeVariableAliases) { + super(Field, signature); + this.version = VersionTwo; + this.typeVariableAliases = typeVariableAliases; + signature.setAnnotatedElsewhere(true); + this.setSuperMethodsCalled(superMethodsCalled); + } + + public ResolvedMember getInitMethod(UnresolvedType aspectType) { + return AjcMemberMaker.interFieldInitializer(signature, aspectType); + } + + public void write(CompressingDataOutputStream s) throws IOException { + kind.write(s); + signature.write(s); + writeSuperMethodsCalled(s); + writeSourceLocation(s); + writeOutTypeAliases(s); + s.writeInt(version); + } + + public static ResolvedTypeMunger readField(VersionedDataInputStream s, ISourceContext context) throws IOException { + ISourceLocation sloc = null; + ResolvedMember fieldSignature = ResolvedMemberImpl.readResolvedMember(s, context); + Set superMethodsCalled = readSuperMethodsCalled(s); + sloc = readSourceLocation(s); + List aliases = readInTypeAliases(s); + NewFieldTypeMunger munger = new NewFieldTypeMunger(fieldSignature, superMethodsCalled, aliases); + if (sloc != null) { + munger.setSourceLocation(sloc); + } + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_AJ169) { + // there is a version int + munger.version = s.readInt(); + } else { + munger.version = VersionOne; + } + return munger; + } + + public ResolvedMember getMatchingSyntheticMember(Member member, ResolvedType aspectType) { + // ??? might give a field where a method is expected + ResolvedType onType = aspectType.getWorld().resolve(getSignature().getDeclaringType()); + if (onType.isRawType()) { + onType = onType.getGenericType(); + } + + ResolvedMember ret = AjcMemberMaker.interFieldGetDispatcher(getSignature(), aspectType); + if (ResolvedType.matches(ret, member)) { + return getSignature(); + } + ret = AjcMemberMaker.interFieldSetDispatcher(getSignature(), aspectType); + if (ResolvedType.matches(ret, member)) { + return getSignature(); + } + ret = AjcMemberMaker.interFieldInterfaceGetter(getSignature(), onType, aspectType); + if (ResolvedType.matches(ret, member)) { + return getSignature(); + } + ret = AjcMemberMaker.interFieldInterfaceSetter(getSignature(), onType, aspectType); + if (ResolvedType.matches(ret, member)) { + return getSignature(); + } + return super.getMatchingSyntheticMember(member, aspectType); + } + + /** + * see ResolvedTypeMunger.parameterizedFor(ResolvedType) + */ + public ResolvedTypeMunger parameterizedFor(ResolvedType target) { + ResolvedType genericType = target; + if (target.isRawType() || target.isParameterizedType()) { + genericType = genericType.getGenericType(); + } + ResolvedMember parameterizedSignature = null; + // If we are parameterizing it for a generic type, we just need to 'swap the letters' from the ones used + // in the original ITD declaration to the ones used in the actual target type declaration. + if (target.isGenericType()) { + TypeVariable vars[] = target.getTypeVariables(); + UnresolvedTypeVariableReferenceType[] varRefs = new UnresolvedTypeVariableReferenceType[vars.length]; + for (int i = 0; i < vars.length; i++) { + varRefs[i] = new UnresolvedTypeVariableReferenceType(vars[i]); + } + parameterizedSignature = getSignature().parameterizedWith(varRefs, genericType, true, typeVariableAliases); + } else { + // For raw and 'normal' parameterized targets (e.g. Interface, Interface<String>) + parameterizedSignature = getSignature().parameterizedWith(target.getTypeParameters(), genericType, + target.isParameterizedType(), typeVariableAliases); + } + NewFieldTypeMunger nftm = new NewFieldTypeMunger(parameterizedSignature, getSuperMethodsCalled(), typeVariableAliases); + nftm.setDeclaredSignature(getSignature()); + nftm.setSourceLocation(getSourceLocation()); + return nftm; + } + + public ResolvedTypeMunger parameterizeWith(Map<String, UnresolvedType> m, World w) { + ResolvedMember parameterizedSignature = getSignature().parameterizedWith(m, w); + NewFieldTypeMunger nftm = new NewFieldTypeMunger(parameterizedSignature, getSuperMethodsCalled(), typeVariableAliases); + nftm.setDeclaredSignature(getSignature()); + nftm.setSourceLocation(getSourceLocation()); + return nftm; + } + + public boolean equals(Object other) { + if (!(other instanceof NewFieldTypeMunger)) { + return false; + } + NewFieldTypeMunger o = (NewFieldTypeMunger) other; + return ((kind == null) ? (o.kind == null) : kind.equals(o.kind)) + && ((signature == null) ? (o.signature == null) : signature.equals(o.signature)) + && ((declaredSignature == null) ? (o.declaredSignature == null) : declaredSignature.equals(o.declaredSignature)) + && ((typeVariableAliases == null) ? (o.typeVariableAliases == null) : typeVariableAliases + .equals(o.typeVariableAliases)); + } + + public int hashCode() { + int result = 17; + result = 37 * result + kind.hashCode(); + result = 37 * result + ((signature == null) ? 0 : signature.hashCode()); + result = 37 * result + ((declaredSignature == null) ? 0 : declaredSignature.hashCode()); + result = 37 * result + ((typeVariableAliases == null) ? 0 : typeVariableAliases.hashCode()); + return result; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewMemberClassTypeMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewMemberClassTypeMunger.java new file mode 100644 index 000000000..c086c2c86 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewMemberClassTypeMunger.java @@ -0,0 +1,90 @@ +/* ******************************************************************* + * Copyright (c) 2010 SpringSource, 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 + * ******************************************************************/ +package org.aspectj.weaver; + +import java.io.IOException; +import java.util.List; + +import org.aspectj.bridge.ISourceLocation; + +/** + * Weaver representation of an intertype declared member class. The munger captures the name of the type being declared and the + * target. + * + * @author Andy Clement + * @since 1.6.9 + */ +public class NewMemberClassTypeMunger extends ResolvedTypeMunger { + + private UnresolvedType targetType; + private String memberTypeName; // short (last part of) name + private int version = 1; // 1.6.9m2 + + public NewMemberClassTypeMunger(UnresolvedType targetType, String memberTypeName) { + super(ResolvedTypeMunger.InnerClass, null); + this.targetType = targetType; + this.memberTypeName = memberTypeName; + } + + @Override + public void write(CompressingDataOutputStream stream) throws IOException { + kind.write(stream); + stream.writeInt(version); + targetType.write(stream); + stream.writeUTF(memberTypeName); + writeSourceLocation(stream); + writeOutTypeAliases(stream); + } + + public static ResolvedTypeMunger readInnerClass(VersionedDataInputStream stream, ISourceContext context) throws IOException { + /* int version = */stream.readInt(); + UnresolvedType targetType = UnresolvedType.read(stream); + String memberTypeName = stream.readUTF(); + ISourceLocation sourceLocation = readSourceLocation(stream); + List<String> typeVarAliases = readInTypeAliases(stream); + + NewMemberClassTypeMunger newInstance = new NewMemberClassTypeMunger(targetType, memberTypeName); + newInstance.setTypeVariableAliases(typeVarAliases); + newInstance.setSourceLocation(sourceLocation); + return newInstance; + } + + public UnresolvedType getTargetType() { + return targetType; + } + + public UnresolvedType getDeclaringType() { + return targetType; + } + + public String getMemberTypeName() { + return memberTypeName; + } + + public int hashCode() { + int result = 17; + result = 37 * result + kind.hashCode(); + result = 37 * result + memberTypeName.hashCode(); + result = 37 * result + targetType.hashCode(); + result = 37 * result + ((typeVariableAliases == null) ? 0 : typeVariableAliases.hashCode()); + return result; + } + + public boolean equals(Object other) { + if (!(other instanceof NewMemberClassTypeMunger)) { + return false; + } + NewMemberClassTypeMunger o = (NewMemberClassTypeMunger) other; + return ((kind == null) ? (o.kind == null) : kind.equals(o.kind)) + && memberTypeName.equals(o.memberTypeName) + && targetType.equals(o.targetType) + && ((typeVariableAliases == null) ? (o.typeVariableAliases == null) : typeVariableAliases + .equals(o.typeVariableAliases)); + } +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewMethodTypeMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewMethodTypeMunger.java new file mode 100644 index 000000000..a6e0ffce7 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewMethodTypeMunger.java @@ -0,0 +1,149 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.ISourceLocation; + +public class NewMethodTypeMunger extends ResolvedTypeMunger { + + public NewMethodTypeMunger(ResolvedMember signature, Set superMethodsCalled, List typeVariableAliases) { + super(Method, signature); + this.typeVariableAliases = typeVariableAliases; + this.setSuperMethodsCalled(superMethodsCalled); + } + + public ResolvedMember getInterMethodBody(UnresolvedType aspectType) { + return AjcMemberMaker.interMethodBody(signature, aspectType); + } + + /** + * If the munger has a declared signature + */ + public ResolvedMember getDeclaredInterMethodBody(UnresolvedType aspectType, World w) { + if (declaredSignature != null) { + ResolvedMember rm = declaredSignature.parameterizedWith(null, signature.getDeclaringType().resolve(w), false, + getTypeVariableAliases()); + return AjcMemberMaker.interMethodBody(rm, aspectType); + } else { + return AjcMemberMaker.interMethodBody(signature, aspectType); + } + } + + // public ResolvedMember getInterMethodDispatcher(UnresolvedType aspectType) { + // return AjcMemberMaker.interMethodDispatcher(signature, aspectType); + // } + + public ResolvedMember getDeclaredInterMethodDispatcher(UnresolvedType aspectType, World w) { + if (declaredSignature != null) { + ResolvedMember rm = declaredSignature.parameterizedWith(null, signature.getDeclaringType().resolve(w), false, + getTypeVariableAliases()); + return AjcMemberMaker.interMethodDispatcher(rm, aspectType); + } else { + return AjcMemberMaker.interMethodDispatcher(signature, aspectType); + } + } + + public void write(CompressingDataOutputStream s) throws IOException { + kind.write(s); + signature.write(s); + writeSuperMethodsCalled(s); + writeSourceLocation(s); + writeOutTypeAliases(s); + } + + public static ResolvedTypeMunger readMethod(VersionedDataInputStream s, ISourceContext context) throws IOException { + ISourceLocation sloc = null; + ResolvedMemberImpl rmImpl = ResolvedMemberImpl.readResolvedMember(s, context); + Set<ResolvedMember> superMethodsCalled = readSuperMethodsCalled(s); + sloc = readSourceLocation(s); + List<String> typeVarAliases = readInTypeAliases(s); + + ResolvedTypeMunger munger = new NewMethodTypeMunger(rmImpl, superMethodsCalled, typeVarAliases); + if (sloc != null) { + munger.setSourceLocation(sloc); + } + return munger; + } + + public ResolvedMember getMatchingSyntheticMember(Member member, ResolvedType aspectType) { + ResolvedMember ret = AjcMemberMaker.interMethodDispatcher(getSignature(), aspectType); + if (ResolvedType.matches(ret, member)) { + return getSignature(); + } + return super.getMatchingSyntheticMember(member, aspectType); + } + + /** + * see ResolvedTypeMunger.parameterizedFor(ResolvedType) + */ + public ResolvedTypeMunger parameterizedFor(ResolvedType target) { + ResolvedType genericType = target; + if (target.isRawType() || target.isParameterizedType()) { + genericType = genericType.getGenericType(); + } + ResolvedMember parameterizedSignature = null; + // If we are parameterizing it for a generic type, we just need to 'swap the letters' from the ones used + // in the original ITD declaration to the ones used in the actual target type declaration. + if (target.isGenericType()) { + TypeVariable vars[] = target.getTypeVariables(); + UnresolvedTypeVariableReferenceType[] varRefs = new UnresolvedTypeVariableReferenceType[vars.length]; + for (int i = 0; i < vars.length; i++) { + varRefs[i] = new UnresolvedTypeVariableReferenceType(vars[i]); + } + parameterizedSignature = getSignature().parameterizedWith(varRefs, genericType, true, typeVariableAliases); + } else { + // For raw and 'normal' parameterized targets (e.g. Interface, Interface<String>) + parameterizedSignature = getSignature().parameterizedWith(target.getTypeParameters(), genericType, + target.isParameterizedType(), typeVariableAliases); + } + NewMethodTypeMunger nmtm = new NewMethodTypeMunger(parameterizedSignature, getSuperMethodsCalled(), typeVariableAliases); + nmtm.setDeclaredSignature(getSignature()); + nmtm.setSourceLocation(getSourceLocation()); + return nmtm; + } + + public boolean equals(Object other) { + if (!(other instanceof NewMethodTypeMunger)) { + return false; + } + NewMethodTypeMunger o = (NewMethodTypeMunger) other; + return ((kind == null) ? (o.kind == null) : kind.equals(o.kind)) + && ((signature == null) ? (o.signature == null) : signature.equals(o.signature)) + && ((declaredSignature == null) ? (o.declaredSignature == null) : declaredSignature.equals(o.declaredSignature)) + && ((typeVariableAliases == null) ? (o.typeVariableAliases == null) : typeVariableAliases + .equals(o.typeVariableAliases)); + } + + public int hashCode() { + int result = 17; + result = 37 * result + kind.hashCode(); + result = 37 * result + ((signature == null) ? 0 : signature.hashCode()); + result = 37 * result + ((declaredSignature == null) ? 0 : declaredSignature.hashCode()); + result = 37 * result + ((typeVariableAliases == null) ? 0 : typeVariableAliases.hashCode()); + return result; + } + + public ResolvedTypeMunger parameterizeWith(Map<String, UnresolvedType> m, World w) { + ResolvedMember parameterizedSignature = getSignature().parameterizedWith(m, w); + NewMethodTypeMunger nmtm = new NewMethodTypeMunger(parameterizedSignature, getSuperMethodsCalled(), typeVariableAliases); + nmtm.setDeclaredSignature(getSignature()); + nmtm.setSourceLocation(getSourceLocation()); + return nmtm; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewParentTypeMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewParentTypeMunger.java new file mode 100644 index 000000000..cb3110073 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/NewParentTypeMunger.java @@ -0,0 +1,68 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.IOException; + +public class NewParentTypeMunger extends ResolvedTypeMunger { + ResolvedType newParent; + ResolvedType declaringType; + private boolean isMixin; + + public NewParentTypeMunger(ResolvedType newParent, ResolvedType declaringType) { + super(Parent, null); + this.newParent = newParent; + this.declaringType = declaringType; + this.isMixin = false; + } + + public void write(CompressingDataOutputStream s) throws IOException { + throw new RuntimeException("unimplemented"); + } + + public ResolvedType getNewParent() { + return newParent; + } + + public boolean equals(Object other) { + if (!(other instanceof NewParentTypeMunger)) { + return false; + } + NewParentTypeMunger o = (NewParentTypeMunger) other; + return newParent.equals(o.newParent) && isMixin == o.isMixin; + } + + private volatile int hashCode = 0; + + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37 * result + newParent.hashCode(); + result = 37 * result + (isMixin ? 0 : 1); + hashCode = result; + } + return hashCode; + } + + public ResolvedType getDeclaringType() { + return declaringType; + } + + public void setIsMixin(boolean b) { + isMixin = true; + } + + public boolean isMixin() { + return isMixin; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PerObjectInterfaceTypeMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PerObjectInterfaceTypeMunger.java new file mode 100644 index 000000000..ce4348241 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PerObjectInterfaceTypeMunger.java @@ -0,0 +1,96 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * Alexandre Vasseur rearchitected for #75442 finer grained matching + * ******************************************************************/ +package org.aspectj.weaver; + +import java.io.IOException; + +import org.aspectj.weaver.patterns.PerFromSuper; +import org.aspectj.weaver.patterns.PerObject; +import org.aspectj.weaver.patterns.PerThisOrTargetPointcutVisitor; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.TypePattern; + +public class PerObjectInterfaceTypeMunger extends ResolvedTypeMunger { + + private final UnresolvedType interfaceType; + private final Pointcut testPointcut; + private TypePattern lazyTestTypePattern; + + public boolean equals(Object other) { + if (other == null || !(other instanceof PerObjectInterfaceTypeMunger)) { + return false; + } + PerObjectInterfaceTypeMunger o = (PerObjectInterfaceTypeMunger) other; + return ((testPointcut == null) ? (o.testPointcut == null) : testPointcut.equals(o.testPointcut)) + && ((lazyTestTypePattern == null) ? (o.lazyTestTypePattern == null) : lazyTestTypePattern + .equals(o.lazyTestTypePattern)); + } + + private volatile int hashCode = 0; + + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37 * result + ((testPointcut == null) ? 0 : testPointcut.hashCode()); + result = 37 * result + ((lazyTestTypePattern == null) ? 0 : lazyTestTypePattern.hashCode()); + hashCode = result; + } + return hashCode; + } + + public PerObjectInterfaceTypeMunger(UnresolvedType aspectType, Pointcut testPointcut) { + super(PerObjectInterface, null); + this.testPointcut = testPointcut; + this.interfaceType = AjcMemberMaker.perObjectInterfaceType(aspectType); + } + + private TypePattern getTestTypePattern(ResolvedType aspectType) { + if (lazyTestTypePattern == null) { + final boolean isPerThis; + if (aspectType.getPerClause() instanceof PerFromSuper) { + PerFromSuper ps = (PerFromSuper) aspectType.getPerClause(); + isPerThis = ((PerObject) ps.lookupConcretePerClause(aspectType)).isThis(); + } else { + isPerThis = ((PerObject) aspectType.getPerClause()).isThis(); + } + PerThisOrTargetPointcutVisitor v = new PerThisOrTargetPointcutVisitor(!isPerThis, aspectType); + lazyTestTypePattern = v.getPerTypePointcut(testPointcut); + // reset hashCode so that its recalculated with the new lazyTestTypePattern + hashCode = 0; + } + return lazyTestTypePattern; + } + + public void write(CompressingDataOutputStream s) throws IOException { + throw new RuntimeException("shouldn't be serialized"); + } + + public UnresolvedType getInterfaceType() { + return interfaceType; + } + + public Pointcut getTestPointcut() { + return testPointcut; + } + + public boolean matches(ResolvedType matchType, ResolvedType aspectType) { + if (matchType.isInterface()) { + return false; + } + return getTestTypePattern(aspectType).matchesStatically(matchType); + } + + public boolean isLateMunger() { + return true; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PerTypeWithinTargetTypeMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PerTypeWithinTargetTypeMunger.java new file mode 100644 index 000000000..ad549417a --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PerTypeWithinTargetTypeMunger.java @@ -0,0 +1,86 @@ +/* ******************************************************************* + * Copyright (c) 2005 IBM, 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; + +import java.io.IOException; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.patterns.PerTypeWithin; +import org.aspectj.weaver.patterns.Pointcut; + +// PTWIMPL Target type munger adds the localAspectOf() method +public class PerTypeWithinTargetTypeMunger extends ResolvedTypeMunger { + private UnresolvedType aspectType; + private PerTypeWithin testPointcut; + + public PerTypeWithinTargetTypeMunger(UnresolvedType aspectType, PerTypeWithin testPointcut) { + super(PerTypeWithinInterface, null); + this.aspectType = aspectType; + this.testPointcut = testPointcut; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PerTypeWithinTargetTypeMunger)) { + return false; + } + PerTypeWithinTargetTypeMunger o = (PerTypeWithinTargetTypeMunger) other; + return ((o.testPointcut == null) ? (testPointcut == null) : testPointcut.equals(o.testPointcut)) + && ((o.aspectType == null) ? (aspectType == null) : aspectType.equals(o.aspectType)); + } + + private volatile int hashCode = 0; + + @Override + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37 * result + ((testPointcut == null) ? 0 : testPointcut.hashCode()); + result = 37 * result + ((aspectType == null) ? 0 : aspectType.hashCode()); + hashCode = result; + } + return hashCode; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + throw new RuntimeException("shouldn't be serialized"); + } + + public UnresolvedType getAspectType() { + return aspectType; + } + + public Pointcut getTestPointcut() { + return testPointcut; + } + + // This is a lexical within() so if you say PerTypeWithin(Test) and matchType is an + // inner type (e.g. Test$NestedType) then it should match successfully + // Does not match if the target is an interface + @Override + public boolean matches(ResolvedType matchType, ResolvedType aspectType) { + return isWithinType(matchType).alwaysTrue() && !matchType.isInterface() && (matchType.canBeSeenBy(aspectType) || aspectType.isPrivilegedAspect()); + } + + private FuzzyBoolean isWithinType(ResolvedType type) { + while (type != null) { + if (testPointcut.getTypePattern().matchesStatically(type)) { + return FuzzyBoolean.YES; + } + type = type.getDeclaringType(); + } + return FuzzyBoolean.NO; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PersistenceSupport.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PersistenceSupport.java new file mode 100644 index 000000000..d98c7678d --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PersistenceSupport.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * Copyright (c) 2009 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 + * + * ******************************************************************/ +package org.aspectj.weaver; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * @author Andy Clement + */ +public class PersistenceSupport { + + public static void write(CompressingDataOutputStream stream, ISourceContext sourceContext) throws IOException { + throw new IllegalStateException(); + } + + public static void write(CompressingDataOutputStream stream, Serializable serializableObject) throws IOException { + ObjectOutputStream oos = new ObjectOutputStream(stream); + oos.writeObject(serializableObject); + oos.flush(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PoliceExtensionUse.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PoliceExtensionUse.java new file mode 100644 index 000000000..e35a9b8a3 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PoliceExtensionUse.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM + * 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 API and implementation + *******************************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor; +import org.aspectj.weaver.patterns.AndPointcut; +import org.aspectj.weaver.patterns.KindedPointcut; +import org.aspectj.weaver.patterns.NotPointcut; +import org.aspectj.weaver.patterns.OrPointcut; +import org.aspectj.weaver.patterns.Pointcut; + +/** + * Walks a pointcut and determines if the synchronization related designators have been used: lock() or unlock() + */ +public class PoliceExtensionUse extends AbstractPatternNodeVisitor { + + private boolean synchronizationDesignatorEncountered; + private World world; + private Pointcut p; + + public PoliceExtensionUse(World w, Pointcut p) { + this.world = w; + this.p = p; + this.synchronizationDesignatorEncountered = false; + } + + public boolean synchronizationDesignatorEncountered() { + return synchronizationDesignatorEncountered; + } + + public Object visit(KindedPointcut node, Object data) { + if (world == null) + return super.visit(node, data); // error scenario can sometimes lead to this LazyClassGen.toLongString() + if (node.getKind() == Shadow.SynchronizationLock || node.getKind() == Shadow.SynchronizationUnlock) + synchronizationDesignatorEncountered = true; + // Check it! + if (!world.isJoinpointSynchronizationEnabled()) { + if (node.getKind() == Shadow.SynchronizationLock) { + IMessage m = MessageUtil.warn( + "lock() pointcut designator cannot be used without the option -Xjoinpoints:synchronization", p + .getSourceLocation()); + world.getMessageHandler().handleMessage(m); + } else if (node.getKind() == Shadow.SynchronizationUnlock) { + IMessage m = MessageUtil.warn( + "unlock() pointcut designator cannot be used without the option -Xjoinpoints:synchronization", p + .getSourceLocation()); + world.getMessageHandler().handleMessage(m); + } + } + return super.visit(node, data); + } + + public Object visit(AndPointcut node, Object data) { + node.getLeft().accept(this, data); + node.getRight().accept(this, data); + return node; + } + + public Object visit(NotPointcut node, Object data) { + node.getNegatedPointcut().accept(this, data); + return node; + } + + public Object visit(OrPointcut node, Object data) { + node.getLeft().accept(this, data); + node.getRight().accept(this, data); + return node; + } + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Position.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Position.java new file mode 100644 index 000000000..af9f38328 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Position.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +public class Position implements IHasPosition { + private int start, end; + + public Position(int start, int end) { + this.start = start; + this.end = end; + } + + public int getEnd() { + return end; + } + + public int getStart() { + return start; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PrivilegedAccessMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PrivilegedAccessMunger.java new file mode 100644 index 000000000..3acbf9b4b --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/PrivilegedAccessMunger.java @@ -0,0 +1,94 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.IOException; + +/** + * A privileged access munger is for handling privileged access to a member. It determines the names of the getter/setter that will + * be used to access a private field in some type, or the special method that provides access to a private method. + * + * There are two syntax styles for field access, the older style was in use up to AspectJ 1.6.9 and involves long named getters and + * setters which include the requesting aspect and the target type. The short style syntax is use from AspectJ 1.6.9 onwards is + * simply 'ajc$get$<fieldname>' and 'ajc$set$<fieldname>' - as the requesting aspect isn't included in the name they can be shared + * across aspects. + */ +public class PrivilegedAccessMunger extends ResolvedTypeMunger { + + public boolean shortSyntax = false; + + public PrivilegedAccessMunger(ResolvedMember member, boolean shortSyntax) { + super(PrivilegedAccess, member); + this.shortSyntax = shortSyntax; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + throw new RuntimeException("should not be serialized"); + } + + public ResolvedMember getMember() { + return getSignature(); + } + + @Override + public ResolvedMember getMatchingSyntheticMember(Member member, ResolvedType aspectType) { + ResolvedMember ret; + // assert if shortSyntax then aspectType.getCompilerVersion()>=169 + if (getSignature().getKind() == Member.FIELD) { + ret = AjcMemberMaker.privilegedAccessMethodForFieldGet(aspectType, getSignature(), shortSyntax); + if (ResolvedType.matches(ret, member)) { + return getSignature(); + } + ret = AjcMemberMaker.privilegedAccessMethodForFieldSet(aspectType, getSignature(), shortSyntax); + if (ResolvedType.matches(ret, member)) { + return getSignature(); + } + } else { + // System.err.println("sig: " + getSignature()); + ret = AjcMemberMaker.privilegedAccessMethodForMethod(aspectType, getSignature()); + if (ResolvedType.matches(ret, member)) { + return getSignature(); + } + } + return null; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PrivilegedAccessMunger)) { + return false; + } + PrivilegedAccessMunger o = (PrivilegedAccessMunger) other; + return kind.equals(o.kind) + && ((o.signature == null) ? (signature == null) : signature.equals(o.signature)) + && ((o.declaredSignature == null) ? (declaredSignature == null) : declaredSignature.equals(o.declaredSignature)) + && ((o.typeVariableAliases == null) ? (typeVariableAliases == null) : typeVariableAliases + .equals(o.typeVariableAliases)); + } + + @Override + public int hashCode() { + int result = 17; + result = 37 * result + kind.hashCode(); + result = 37 * result + ((signature == null) ? 0 : signature.hashCode()); + result = 37 * result + ((declaredSignature == null) ? 0 : declaredSignature.hashCode()); + result = 37 * result + ((typeVariableAliases == null) ? 0 : typeVariableAliases.hashCode()); + return result; + } + + @Override + public boolean existsToSupportShadowMunging() { + return true; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ReferenceType.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ReferenceType.java new file mode 100644 index 000000000..7dc162a16 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ReferenceType.java @@ -0,0 +1,1280 @@ +/* ******************************************************************* + * Copyright (c) 2002 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: + * PARC initial implementation + * Andy Clement - June 2005 - separated out from ResolvedType + * ******************************************************************/ +package org.aspectj.weaver; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.World.TypeMap; +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.PerClause; + +/** + * A reference type represents some 'real' type, not a primitive, not an array - + * but a real type, for example java.util.List. Each ReferenceType has a + * delegate that is the underlying artifact - either an eclipse artifact or a + * bcel artifact. If the type represents a raw type (i.e. there is a generic + * form) then the genericType field is set to point to the generic type. If it + * is for a parameterized type then the generic type is also set to point to the + * generic form. + */ +public class ReferenceType extends ResolvedType { + + public static final ReferenceType[] EMPTY_ARRAY = new ReferenceType[0]; + + /** + * For generic types, this list holds references to all the derived raw and + * parameterized versions. We need this so that if the generic delegate is + * swapped during incremental compilation, the delegate of the derivatives + * is swapped also. + */ + private final List<WeakReference<ReferenceType>> derivativeTypes = new ArrayList<WeakReference<ReferenceType>>(); + + /** + * For parameterized types (or the raw type) - this field points to the + * actual reference type from which they are derived. + */ + ReferenceType genericType = null; + + ReferenceType rawType = null; // generic types have a pointer back to their + // raw variant (prevents GC of the raw from + // the typemap!) + + ReferenceTypeDelegate delegate = null; + int startPos = 0; + int endPos = 0; + + // cached values for members + ResolvedMember[] parameterizedMethods = null; + ResolvedMember[] parameterizedFields = null; + ResolvedMember[] parameterizedPointcuts = null; + WeakReference<ResolvedType[]> parameterizedInterfaces = new WeakReference<ResolvedType[]>( + null); + Collection<Declare> parameterizedDeclares = null; + // Collection parameterizedTypeMungers = null; + + // During matching it can be necessary to temporary mark types as annotated. + // For example + // a declare @type may trigger a separate declare parents to match, and so + // the annotation + // is temporarily held against the referencetype, the annotation will be + // properly + // added to the class during weaving. + private ResolvedType[] annotationTypes = null; + private AnnotationAJ[] annotations = null; + + // Similarly these are temporary replacements and additions for the + // superclass and + // superinterfaces + private ResolvedType newSuperclass; + private ResolvedType[] newInterfaces; + + public ReferenceType(String signature, World world) { + super(signature, world); + } + + public ReferenceType(String signature, String signatureErasure, World world) { + super(signature, signatureErasure, world); + } + + public static ReferenceType fromTypeX(UnresolvedType tx, World world) { + ReferenceType rt = new ReferenceType(tx.getErasureSignature(), world); + rt.typeKind = tx.typeKind; + return rt; + } + + /** + * Constructor used when creating a parameterized type. + */ + public ReferenceType(ResolvedType theGenericType, + ResolvedType[] theParameters, World aWorld) { + super(makeParameterizedSignature(theGenericType, theParameters), + theGenericType.signatureErasure, aWorld); + ReferenceType genericReferenceType = (ReferenceType) theGenericType; + this.typeParameters = theParameters; + this.genericType = genericReferenceType; + this.typeKind = TypeKind.PARAMETERIZED; + this.delegate = genericReferenceType.getDelegate(); + genericReferenceType.addDependentType(this); + } + + synchronized void addDependentType(ReferenceType dependent) { + // checkDuplicates(dependent); + synchronized (derivativeTypes) { + this.derivativeTypes + .add(new WeakReference<ReferenceType>(dependent)); + } + } + + public void checkDuplicates(ReferenceType newRt) { + synchronized (derivativeTypes) { + List<WeakReference<ReferenceType>> forRemoval = new ArrayList<WeakReference<ReferenceType>>(); + for (WeakReference<ReferenceType> derivativeTypeReference : derivativeTypes) { + ReferenceType derivativeType = derivativeTypeReference.get(); + if (derivativeType == null) { + forRemoval.add(derivativeTypeReference); + } else { + if (derivativeType.getTypekind() != newRt.getTypekind()) { + continue; // cannot be this one + } + if (equal2(newRt.getTypeParameters(), + derivativeType.getTypeParameters())) { + if (TypeMap.useExpendableMap) { + throw new IllegalStateException(); + } + } + } + } + derivativeTypes.removeAll(forRemoval); + } + } + + private boolean equal2(UnresolvedType[] typeParameters, + UnresolvedType[] resolvedParameters) { + if (typeParameters.length != resolvedParameters.length) { + return false; + } + int len = typeParameters.length; + for (int p = 0; p < len; p++) { + if (!typeParameters[p].equals(resolvedParameters[p])) { + return false; + } + } + return true; + } + + @Override + public String getSignatureForAttribute() { + if (genericType == null || typeParameters == null) { + return getSignature(); + } + return makeDeclaredSignature(genericType, typeParameters); + } + + /** + * Create a reference type for a generic type + */ + public ReferenceType(UnresolvedType genericType, World world) { + super(genericType.getSignature(), world); + typeKind = TypeKind.GENERIC; + this.typeVariables = genericType.typeVariables; + } + + @Override + public boolean isClass() { + return getDelegate().isClass(); + } + + @Override + public int getCompilerVersion() { + return getDelegate().getCompilerVersion(); + } + + @Override + public boolean isGenericType() { + return !isParameterizedType() && !isRawType() + && getDelegate().isGeneric(); + } + + public String getGenericSignature() { + String sig = getDelegate().getDeclaredGenericSignature(); + return (sig == null) ? "" : sig; + } + + @Override + public AnnotationAJ[] getAnnotations() { + return getDelegate().getAnnotations(); + } + + @Override + public boolean hasAnnotations() { + return getDelegate().hasAnnotations(); + } + + @Override + public void addAnnotation(AnnotationAJ annotationX) { + if (annotations == null) { + annotations = new AnnotationAJ[] { annotationX }; + } else { + AnnotationAJ[] newAnnotations = new AnnotationAJ[annotations.length + 1]; + System.arraycopy(annotations, 0, newAnnotations, 1, + annotations.length); + newAnnotations[0] = annotationX; + annotations = newAnnotations; + } + addAnnotationType(annotationX.getType()); + } + + public boolean hasAnnotation(UnresolvedType ofType) { + boolean onDelegate = getDelegate().hasAnnotation(ofType); + if (onDelegate) { + return true; + } + if (annotationTypes != null) { + for (int i = 0; i < annotationTypes.length; i++) { + if (annotationTypes[i].equals(ofType)) { + return true; + } + } + } + return false; + } + + private void addAnnotationType(ResolvedType ofType) { + if (annotationTypes == null) { + annotationTypes = new ResolvedType[1]; + annotationTypes[0] = ofType; + } else { + ResolvedType[] newAnnotationTypes = new ResolvedType[annotationTypes.length + 1]; + System.arraycopy(annotationTypes, 0, newAnnotationTypes, 1, + annotationTypes.length); + newAnnotationTypes[0] = ofType; + annotationTypes = newAnnotationTypes; + } + } + + @Override + public ResolvedType[] getAnnotationTypes() { + if (getDelegate() == null) { + throw new BCException("Unexpected null delegate for type " + + this.getName()); + } + if (annotationTypes == null) { + // there are no extras: + return getDelegate().getAnnotationTypes(); + } else { + ResolvedType[] delegateAnnotationTypes = getDelegate() + .getAnnotationTypes(); + ResolvedType[] result = new ResolvedType[annotationTypes.length + + delegateAnnotationTypes.length]; + System.arraycopy(delegateAnnotationTypes, 0, result, 0, + delegateAnnotationTypes.length); + System.arraycopy(annotationTypes, 0, result, + delegateAnnotationTypes.length, annotationTypes.length); + return result; + } + } + + @Override + public String getNameAsIdentifier() { + return getRawName().replace('.', '_'); + } + + @Override + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + AnnotationAJ[] axs = getDelegate().getAnnotations(); + if (axs != null) { + for (int i = 0; i < axs.length; i++) { + if (axs[i].getTypeSignature().equals(ofType.getSignature())) { + return axs[i]; + } + } + } + if (annotations != null) { + String searchSig = ofType.getSignature(); + for (int i = 0; i < annotations.length; i++) { + if (annotations[i].getTypeSignature().equals(searchSig)) { + return annotations[i]; + } + } + } + return null; + } + + @Override + public boolean isAspect() { + return getDelegate().isAspect(); + } + + @Override + public boolean isAnnotationStyleAspect() { + return getDelegate().isAnnotationStyleAspect(); + } + + @Override + public boolean isEnum() { + return getDelegate().isEnum(); + } + + @Override + public boolean isAnnotation() { + return getDelegate().isAnnotation(); + } + + @Override + public boolean isAnonymous() { + return getDelegate().isAnonymous(); + } + + @Override + public boolean isNested() { + return getDelegate().isNested(); + } + + public ResolvedType getOuterClass() { + return getDelegate().getOuterClass(); + } + + public String getRetentionPolicy() { + return getDelegate().getRetentionPolicy(); + } + + @Override + public boolean isAnnotationWithRuntimeRetention() { + return getDelegate().isAnnotationWithRuntimeRetention(); + } + + @Override + public boolean canAnnotationTargetType() { + return getDelegate().canAnnotationTargetType(); + } + + @Override + public AnnotationTargetKind[] getAnnotationTargetKinds() { + return getDelegate().getAnnotationTargetKinds(); + } + + // true iff the statement "this = (ThisType) other" would compile + @Override + public boolean isCoerceableFrom(ResolvedType o) { + ResolvedType other = o.resolve(world); + + if (this.isAssignableFrom(other) || other.isAssignableFrom(this)) { + return true; + } + + if (this.isParameterizedType() && other.isParameterizedType()) { + return isCoerceableFromParameterizedType(other); + } + + if (this.isParameterizedType() && other.isRawType()) { + return ((ReferenceType) this.getRawType()).isCoerceableFrom(other + .getGenericType()); + } + + if (this.isRawType() && other.isParameterizedType()) { + return this.getGenericType().isCoerceableFrom((other.getRawType())); + } + + if (!this.isInterface() && !other.isInterface()) { + return false; + } + if (this.isFinal() || other.isFinal()) { + return false; + } + + // 20170927: What is the block of code for? It mentions jls5.5 which isn't on this topic (old version of jls?) + // Some possible references: http://docs.oracle.com/javase/specs/jls/se9/jls9.pdf 5.1.6 (narrowing reference conversion) + // On Java 9 the test GenericsTests.testAfterReturningWithWildcardVar will fail because this code below + // used to find Set and List were the same, but now finds they are not. (so it doesn't put out the unchecked + // conversion message). However the code "List l = (List)someSet;" still compiles on 9 - so is this code bogus? + + // ??? needs to be Methods, not just declared methods? JLS 5.5 unclear + ResolvedMember[] a = getDeclaredMethods(); + ResolvedMember[] b = other.getDeclaredMethods(); + for (int ai = 0, alen = a.length; ai < alen; ai++) { + for (int bi = 0, blen = b.length; bi < blen; bi++) { + if (!b[bi].isCompatibleWith(a[ai])) { + return false; + } + } + } + return true; + } + + private final boolean isCoerceableFromParameterizedType(ResolvedType other) { + if (!other.isParameterizedType()) { + return false; + } + ResolvedType myRawType = getRawType(); + ResolvedType theirRawType = other.getRawType(); + if (myRawType == theirRawType + || myRawType.isCoerceableFrom(theirRawType)) { + if (getTypeParameters().length == other.getTypeParameters().length) { + // there's a chance it can be done + ResolvedType[] myTypeParameters = getResolvedTypeParameters(); + ResolvedType[] theirTypeParameters = other + .getResolvedTypeParameters(); + for (int i = 0; i < myTypeParameters.length; i++) { + if (myTypeParameters[i] != theirTypeParameters[i]) { + // thin ice now... but List<String> may still be + // coerceable from e.g. List<T> + if (myTypeParameters[i].isGenericWildcard()) { + BoundedReferenceType wildcard = (BoundedReferenceType) myTypeParameters[i]; + if (!wildcard + .canBeCoercedTo(theirTypeParameters[i])) { + return false; + } + } else if (myTypeParameters[i] + .isTypeVariableReference()) { + TypeVariableReferenceType tvrt = (TypeVariableReferenceType) myTypeParameters[i]; + TypeVariable tv = tvrt.getTypeVariable(); + tv.resolve(world); + if (!tv.canBeBoundTo(theirTypeParameters[i])) { + return false; + } + } else if (theirTypeParameters[i] + .isTypeVariableReference()) { + TypeVariableReferenceType tvrt = (TypeVariableReferenceType) theirTypeParameters[i]; + TypeVariable tv = tvrt.getTypeVariable(); + tv.resolve(world); + if (!tv.canBeBoundTo(myTypeParameters[i])) { + return false; + } + } else if (theirTypeParameters[i].isGenericWildcard()) { + BoundedReferenceType wildcard = (BoundedReferenceType) theirTypeParameters[i]; + if (!wildcard.canBeCoercedTo(myTypeParameters[i])) { + return false; + } + } else { + return false; + } + } + } + return true; + } + // } else { + // // we do this walk for situations like the following: + // // Base<T>, Sub<S,T> extends Base<S> + // // is Sub<Y,Z> coerceable from Base<X> ??? + // for (Iterator i = getDirectSupertypes(); i.hasNext();) { + // ReferenceType parent = (ReferenceType) i.next(); + // if (parent.isCoerceableFromParameterizedType(other)) + // return true; + // } + } + return false; + } + + @Override + public boolean isAssignableFrom(ResolvedType other) { + return isAssignableFrom(other, false); + } + + // TODO rewrite this method - it is a terrible mess + + // true iff the statement "this = other" would compile. + @Override + public boolean isAssignableFrom(ResolvedType other, boolean allowMissing) { + if (other.isPrimitiveType()) { + if (!world.isInJava5Mode()) { + return false; + } + if (ResolvedType.validBoxing.contains(this.getSignature() + + other.getSignature())) { + return true; + } + } + if (this == other) { + return true; + } + + if (this.getSignature().equals("Ljava/lang/Object;")) { + return true; + } + + if (!isTypeVariableReference() + && other.getSignature().equals("Ljava/lang/Object;")) { + return false; + } + + boolean thisRaw = this.isRawType(); + if (thisRaw && other.isParameterizedOrGenericType()) { + return isAssignableFrom(other.getRawType()); + } + + boolean thisGeneric = this.isGenericType(); + if (thisGeneric && other.isParameterizedOrRawType()) { + return isAssignableFrom(other.getGenericType()); + } + + if (this.isParameterizedType()) { + // look at wildcards... + if (((ReferenceType) this.getRawType()).isAssignableFrom(other)) { + boolean wildcardsAllTheWay = true; + ResolvedType[] myParameters = this.getResolvedTypeParameters(); + for (int i = 0; i < myParameters.length; i++) { + if (!myParameters[i].isGenericWildcard()) { + wildcardsAllTheWay = false; + } else { + BoundedReferenceType boundedRT = (BoundedReferenceType) myParameters[i]; + if (boundedRT.isExtends() || boundedRT.isSuper()) { + wildcardsAllTheWay = false; + } + } + } + if (wildcardsAllTheWay && !other.isParameterizedType()) { + return true; + } + // we have to match by parameters one at a time + ResolvedType[] theirParameters = other + .getResolvedTypeParameters(); + boolean parametersAssignable = true; + if (myParameters.length == theirParameters.length) { + for (int i = 0; i < myParameters.length + && parametersAssignable; i++) { + if (myParameters[i] == theirParameters[i]) { + continue; + } + // dont do this: pr253109 + // if + // (myParameters[i].isAssignableFrom(theirParameters[i], + // allowMissing)) { + // continue; + // } + ResolvedType mp = myParameters[i]; + ResolvedType tp = theirParameters[i]; + if (mp.isParameterizedType() + && tp.isParameterizedType()) { + if (mp.getGenericType().equals(tp.getGenericType())) { + UnresolvedType[] mtps = mp.getTypeParameters(); + UnresolvedType[] ttps = tp.getTypeParameters(); + for (int ii = 0; ii < mtps.length; ii++) { + if (mtps[ii].isTypeVariableReference() + && ttps[ii] + .isTypeVariableReference()) { + TypeVariable mtv = ((TypeVariableReferenceType) mtps[ii]) + .getTypeVariable(); + boolean b = mtv + .canBeBoundTo((ResolvedType) ttps[ii]); + if (!b) {// TODO incomplete testing here + // I think + parametersAssignable = false; + break; + } + } else { + parametersAssignable = false; + break; + } + } + continue; + } else { + parametersAssignable = false; + break; + } + } + if (myParameters[i].isTypeVariableReference() + && theirParameters[i].isTypeVariableReference()) { + TypeVariable myTV = ((TypeVariableReferenceType) myParameters[i]) + .getTypeVariable(); + // TypeVariable theirTV = + // ((TypeVariableReferenceType) + // theirParameters[i]).getTypeVariable(); + boolean b = myTV.canBeBoundTo(theirParameters[i]); + if (!b) {// TODO incomplete testing here I think + parametersAssignable = false; + break; + } else { + continue; + } + } + if (!myParameters[i].isGenericWildcard()) { + parametersAssignable = false; + break; + } else { + BoundedReferenceType wildcardType = (BoundedReferenceType) myParameters[i]; + if (!wildcardType.alwaysMatches(theirParameters[i])) { + parametersAssignable = false; + break; + } + } + } + } else { + parametersAssignable = false; + } + if (parametersAssignable) { + return true; + } + } + } + + // eg this=T other=Ljava/lang/Object; + if (isTypeVariableReference() && !other.isTypeVariableReference()) { + TypeVariable aVar = ((TypeVariableReference) this) + .getTypeVariable(); + return aVar.resolve(world).canBeBoundTo(other); + } + + if (other.isTypeVariableReference()) { + TypeVariableReferenceType otherType = (TypeVariableReferenceType) other; + if (this instanceof TypeVariableReference) { + return ((TypeVariableReference) this) + .getTypeVariable() + .resolve(world) + .canBeBoundTo( + otherType.getTypeVariable().getFirstBound() + .resolve(world));// pr171952 + // return + // ((TypeVariableReference)this).getTypeVariable()==otherType + // .getTypeVariable(); + } else { + // FIXME asc should this say canBeBoundTo?? + return this.isAssignableFrom(otherType.getTypeVariable() + .getFirstBound().resolve(world)); + } + } + + if (allowMissing && other.isMissing()) { + return false; + } + + ResolvedType[] interfaces = other.getDeclaredInterfaces(); + for (ResolvedType intface : interfaces) { + boolean b; + if (thisRaw && intface.isParameterizedOrGenericType()) { + b = this.isAssignableFrom(intface.getRawType(), allowMissing); + } else { + b = this.isAssignableFrom(intface, allowMissing); + } + if (b) { + return true; + } + } + ResolvedType superclass = other.getSuperclass(); + if (superclass != null) { + boolean b; + if (thisRaw && superclass.isParameterizedOrGenericType()) { + b = this.isAssignableFrom(superclass.getRawType(), allowMissing); + } else { + b = this.isAssignableFrom(superclass, allowMissing); + } + if (b) { + return true; + } + } + return false; + } + + @Override + public ISourceContext getSourceContext() { + return getDelegate().getSourceContext(); + } + + @Override + public ISourceLocation getSourceLocation() { + ISourceContext isc = getDelegate().getSourceContext(); + return isc.makeSourceLocation(new Position(startPos, endPos)); + } + + @Override + public boolean isExposedToWeaver() { + return (getDelegate() == null) || delegate.isExposedToWeaver(); + } + + @Override + public WeaverStateInfo getWeaverState() { + return getDelegate().getWeaverState(); + } + + @Override + public ResolvedMember[] getDeclaredFields() { + if (parameterizedFields != null) { + return parameterizedFields; + } + if (isParameterizedType() || isRawType()) { + ResolvedMember[] delegateFields = getDelegate().getDeclaredFields(); + parameterizedFields = new ResolvedMember[delegateFields.length]; + for (int i = 0; i < delegateFields.length; i++) { + parameterizedFields[i] = delegateFields[i].parameterizedWith( + getTypesForMemberParameterization(), this, + isParameterizedType()); + } + return parameterizedFields; + } else { + return getDelegate().getDeclaredFields(); + } + } + + /** + * Find out from the generic signature the true signature of any interfaces + * I implement. If I am parameterized, these may then need to be + * parameterized before returning. + */ + @Override + public ResolvedType[] getDeclaredInterfaces() { + ResolvedType[] interfaces = parameterizedInterfaces.get(); + if (interfaces != null) { + return interfaces; + } + ResolvedType[] delegateInterfaces = getDelegate() + .getDeclaredInterfaces(); + if (isRawType()) { + if (newInterfaces != null) {// debug 375777 + throw new IllegalStateException( + "The raw type should never be accumulating new interfaces, they should be on the generic type. Type is " + + this.getName()); + } + ResolvedType[] newInterfacesFromGenericType = genericType.newInterfaces; + if (newInterfacesFromGenericType != null) { + ResolvedType[] extraInterfaces = new ResolvedType[delegateInterfaces.length + + newInterfacesFromGenericType.length]; + System.arraycopy(delegateInterfaces, 0, extraInterfaces, 0, + delegateInterfaces.length); + System.arraycopy(newInterfacesFromGenericType, 0, + extraInterfaces, delegateInterfaces.length, + newInterfacesFromGenericType.length); + delegateInterfaces = extraInterfaces; + } + } else if (newInterfaces != null) { + // OPTIMIZE does this part of the method trigger often? + ResolvedType[] extraInterfaces = new ResolvedType[delegateInterfaces.length + + newInterfaces.length]; + System.arraycopy(delegateInterfaces, 0, extraInterfaces, 0, + delegateInterfaces.length); + System.arraycopy(newInterfaces, 0, extraInterfaces, + delegateInterfaces.length, newInterfaces.length); + + delegateInterfaces = extraInterfaces; + } + if (isParameterizedType()) { + // UnresolvedType[] paramTypes = + // getTypesForMemberParameterization(); + interfaces = new ResolvedType[delegateInterfaces.length]; + for (int i = 0; i < delegateInterfaces.length; i++) { + // We may have to sub/super set the set of parametertypes if the + // implemented interface + // needs more or less than this type does. (pr124803/pr125080) + + if (delegateInterfaces[i].isParameterizedType()) { + interfaces[i] = delegateInterfaces[i].parameterize( + getMemberParameterizationMap()).resolve(world); + } else { + interfaces[i] = delegateInterfaces[i]; + } + } + parameterizedInterfaces = new WeakReference<ResolvedType[]>( + interfaces); + return interfaces; + } else if (isRawType()) { + UnresolvedType[] paramTypes = getTypesForMemberParameterization(); + interfaces = new ResolvedType[delegateInterfaces.length]; + for (int i = 0, max = interfaces.length; i < max; i++) { + interfaces[i] = delegateInterfaces[i]; + if (interfaces[i].isGenericType()) { + // a generic supertype of a raw type is replaced by its raw + // equivalent + interfaces[i] = interfaces[i].getRawType().resolve( + getWorld()); + } else if (interfaces[i].isParameterizedType()) { + // a parameterized supertype collapses any type vars to + // their upper bounds + UnresolvedType[] toUseForParameterization = determineThoseTypesToUse( + interfaces[i], paramTypes); + interfaces[i] = interfaces[i] + .parameterizedWith(toUseForParameterization); + } + } + parameterizedInterfaces = new WeakReference<ResolvedType[]>( + interfaces); + return interfaces; + } + if (getDelegate().isCacheable()) { + parameterizedInterfaces = new WeakReference<ResolvedType[]>( + delegateInterfaces); + } + return delegateInterfaces; + } + + /** + * It is possible this type has multiple type variables but the interface we + * are about to parameterize only uses a subset - this method determines the + * subset to use by looking at the type variable names used. For example: + * <code> + * class Foo<T extends String,E extends Number> implements SuperInterface<T> {} + * </code> where <code> + * interface SuperInterface<Z> {} + * </code> In that example, a use of the 'Foo' raw type should know that it + * implements the SuperInterface<String>. + */ + private UnresolvedType[] determineThoseTypesToUse( + ResolvedType parameterizedInterface, UnresolvedType[] paramTypes) { + // What are the type parameters for the supertype? + UnresolvedType[] tParms = parameterizedInterface.getTypeParameters(); + UnresolvedType[] retVal = new UnresolvedType[tParms.length]; + + // Go through the supertypes type parameters, if any of them is a type + // variable, use the + // real type variable on the declaring type. + + // it is possibly overkill to look up the type variable - ideally the + // entry in the type parameter list for the + // interface should be the a ref to the type variable in the current + // type ... but I'm not 100% confident right now. + for (int i = 0; i < tParms.length; i++) { + UnresolvedType tParm = tParms[i]; + if (tParm.isTypeVariableReference()) { + TypeVariableReference tvrt = (TypeVariableReference) tParm; + TypeVariable tv = tvrt.getTypeVariable(); + int rank = getRank(tv.getName()); + // -1 probably means it is a reference to a type variable on the + // outer generic type (see pr129566) + if (rank != -1) { + retVal[i] = paramTypes[rank]; + } else { + retVal[i] = tParms[i]; + } + } else { + retVal[i] = tParms[i]; + } + + } + return retVal; + } + + /** + * Returns the position within the set of type variables for this type for + * the specified type variable name. Returns -1 if there is no type variable + * with the specified name. + */ + private int getRank(String tvname) { + TypeVariable[] thisTypesTVars = getGenericType().getTypeVariables(); + for (int i = 0; i < thisTypesTVars.length; i++) { + TypeVariable tv = thisTypesTVars[i]; + if (tv.getName().equals(tvname)) { + return i; + } + } + return -1; + } + + @Override + public ResolvedMember[] getDeclaredMethods() { + if (parameterizedMethods != null) { + return parameterizedMethods; + } + if (isParameterizedType() || isRawType()) { + ResolvedMember[] delegateMethods = getDelegate() + .getDeclaredMethods(); + UnresolvedType[] parameters = getTypesForMemberParameterization(); + parameterizedMethods = new ResolvedMember[delegateMethods.length]; + for (int i = 0; i < delegateMethods.length; i++) { + parameterizedMethods[i] = delegateMethods[i].parameterizedWith( + parameters, this, isParameterizedType()); + } + return parameterizedMethods; + } else { + return getDelegate().getDeclaredMethods(); + } + } + + @Override + public ResolvedMember[] getDeclaredPointcuts() { + if (parameterizedPointcuts != null) { + return parameterizedPointcuts; + } + if (isParameterizedType()) { + ResolvedMember[] delegatePointcuts = getDelegate() + .getDeclaredPointcuts(); + parameterizedPointcuts = new ResolvedMember[delegatePointcuts.length]; + for (int i = 0; i < delegatePointcuts.length; i++) { + parameterizedPointcuts[i] = delegatePointcuts[i] + .parameterizedWith(getTypesForMemberParameterization(), + this, isParameterizedType()); + } + return parameterizedPointcuts; + } else { + return getDelegate().getDeclaredPointcuts(); + } + } + + private UnresolvedType[] getTypesForMemberParameterization() { + UnresolvedType[] parameters = null; + if (isParameterizedType()) { + parameters = getTypeParameters(); + } else if (isRawType()) { + // raw type, use upper bounds of type variables on generic type + TypeVariable[] tvs = getGenericType().getTypeVariables(); + parameters = new UnresolvedType[tvs.length]; + for (int i = 0; i < tvs.length; i++) { + parameters[i] = tvs[i].getFirstBound(); + } + } + return parameters; + } + + @Override + public TypeVariable[] getTypeVariables() { + if (typeVariables == null) { + typeVariables = getDelegate().getTypeVariables(); + for (int i = 0; i < this.typeVariables.length; i++) { + typeVariables[i].resolve(world); + } + } + return typeVariables; + } + + @Override + public PerClause getPerClause() { + PerClause pclause = getDelegate().getPerClause(); + if (pclause != null && isParameterizedType()) { // could cache the + // result here... + Map<String, UnresolvedType> parameterizationMap = getAjMemberParameterizationMap(); + pclause = (PerClause) pclause.parameterizeWith(parameterizationMap, + world); + } + return pclause; + } + + @Override + public Collection<Declare> getDeclares() { + if (parameterizedDeclares != null) { + return parameterizedDeclares; + } + Collection<Declare> declares = null; + if (ajMembersNeedParameterization()) { + Collection<Declare> genericDeclares = getDelegate().getDeclares(); + parameterizedDeclares = new ArrayList<Declare>(); + Map<String, UnresolvedType> parameterizationMap = getAjMemberParameterizationMap(); + for (Declare declareStatement : genericDeclares) { + parameterizedDeclares.add(declareStatement.parameterizeWith( + parameterizationMap, world)); + } + declares = parameterizedDeclares; + } else { + declares = getDelegate().getDeclares(); + } + for (Declare d : declares) { + d.setDeclaringType(this); + } + return declares; + } + + @Override + public Collection<ConcreteTypeMunger> getTypeMungers() { + return getDelegate().getTypeMungers(); + } + + @Override + public Collection<ResolvedMember> getPrivilegedAccesses() { + return getDelegate().getPrivilegedAccesses(); + } + + @Override + public int getModifiers() { + return getDelegate().getModifiers(); + } + + WeakReference<ResolvedType> superclassReference = new WeakReference<ResolvedType>( + null); + + @Override + public ResolvedType getSuperclass() { + ResolvedType ret = null;// superclassReference.get(); + // if (ret != null) { + // return ret; + // } + if (newSuperclass != null) { + if (this.isParameterizedType() + && newSuperclass.isParameterizedType()) { + return newSuperclass.parameterize( + getMemberParameterizationMap()).resolve(getWorld()); + } + if (getDelegate().isCacheable()) { + superclassReference = new WeakReference<ResolvedType>(ret); + } + return newSuperclass; + } + try { + world.setTypeVariableLookupScope(this); + ret = getDelegate().getSuperclass(); + } finally { + world.setTypeVariableLookupScope(null); + } + if (this.isParameterizedType() && ret.isParameterizedType()) { + ret = ret.parameterize(getMemberParameterizationMap()).resolve( + getWorld()); + } + if (getDelegate().isCacheable()) { + superclassReference = new WeakReference<ResolvedType>(ret); + } + return ret; + } + + public ReferenceTypeDelegate getDelegate() { + return delegate; + } + + public void setDelegate(ReferenceTypeDelegate delegate) { + // Don't copy from BcelObjectType to EclipseSourceType - the context may + // be tidied (result null'd) after previous weaving + if (this.delegate != null + && this.delegate.copySourceContext() + && this.delegate.getSourceContext() != SourceContextImpl.UNKNOWN_SOURCE_CONTEXT) { + ((AbstractReferenceTypeDelegate) delegate) + .setSourceContext(this.delegate.getSourceContext()); + } + this.delegate = delegate; + synchronized (derivativeTypes) { + List<WeakReference<ReferenceType>> forRemoval = new ArrayList<WeakReference<ReferenceType>>(); + for (WeakReference<ReferenceType> derivativeRef : derivativeTypes) { + ReferenceType derivative = derivativeRef.get(); + if (derivative != null) { + derivative.setDelegate(delegate); + } else { + forRemoval.add(derivativeRef); + } + } + derivativeTypes.removeAll(forRemoval); + } + + // If we are raw, we have a generic type - we should ensure it uses the + // same delegate + if (isRawType() && getGenericType() != null) { + ReferenceType genType = (ReferenceType) getGenericType(); + if (genType.getDelegate() != delegate) { // avoids circular updates + genType.setDelegate(delegate); + } + } + clearParameterizationCaches(); + ensureConsistent(); + } + + private void clearParameterizationCaches() { + parameterizedFields = null; + parameterizedInterfaces.clear(); + parameterizedMethods = null; + parameterizedPointcuts = null; + superclassReference = new WeakReference<ResolvedType>(null); + } + + public int getEndPos() { + return endPos; + } + + public int getStartPos() { + return startPos; + } + + public void setEndPos(int endPos) { + this.endPos = endPos; + } + + public void setStartPos(int startPos) { + this.startPos = startPos; + } + + @Override + public boolean doesNotExposeShadowMungers() { + return getDelegate().doesNotExposeShadowMungers(); + } + + public String getDeclaredGenericSignature() { + return getDelegate().getDeclaredGenericSignature(); + } + + public void setGenericType(ReferenceType rt) { + genericType = rt; + // Should we 'promote' this reference type from simple to raw? + // makes sense if someone is specifying that it has a generic form + if (typeKind == TypeKind.SIMPLE) { + typeKind = TypeKind.RAW; + signatureErasure = signature; + if (newInterfaces != null) { // debug 375777 + throw new IllegalStateException( + "Simple type promoted to raw, but simple type had new interfaces/superclass. Type is " + + this.getName()); + } + } + if (typeKind == TypeKind.RAW) { + genericType.addDependentType(this); + } + if (isRawType()) { + genericType.rawType = this; + } + if (this.isRawType() && rt.isRawType()) { + new RuntimeException( + "PR341926 diagnostics: Incorrect setup for a generic type, raw type should not point to raw: " + + this.getName()).printStackTrace(); + } + } + + public void demoteToSimpleType() { + genericType = null; + typeKind = TypeKind.SIMPLE; + signatureErasure = null; + } + + @Override + public ReferenceType getGenericType() { + if (isGenericType()) { + return this; + } + return genericType; + } + + /** + * a parameterized signature starts with a "P" in place of the "L", see the + * comment on signatures in UnresolvedType. + * + * @param aGenericType + * @param someParameters + * @return + */ + private static String makeParameterizedSignature(ResolvedType aGenericType, + ResolvedType[] someParameters) { + String rawSignature = aGenericType.getErasureSignature(); + StringBuffer ret = new StringBuffer(); + ret.append(PARAMETERIZED_TYPE_IDENTIFIER); + ret.append(rawSignature.substring(1, rawSignature.length() - 1)); + ret.append("<"); + for (int i = 0; i < someParameters.length; i++) { + ret.append(someParameters[i].getSignature()); + } + ret.append(">;"); + return ret.toString(); + } + + private static String makeDeclaredSignature(ResolvedType aGenericType, + UnresolvedType[] someParameters) { + StringBuffer ret = new StringBuffer(); + String rawSig = aGenericType.getErasureSignature(); + ret.append(rawSig.substring(0, rawSig.length() - 1)); + ret.append("<"); + for (int i = 0; i < someParameters.length; i++) { + if (someParameters[i] instanceof ReferenceType) { + ret.append(((ReferenceType) someParameters[i]) + .getSignatureForAttribute()); + } else if (someParameters[i] instanceof Primitive) { + ret.append(((Primitive) someParameters[i]) + .getSignatureForAttribute()); + } else { + throw new IllegalStateException( + "DebugFor325731: expected a ReferenceType or Primitive but was " + + someParameters[i] + " of type " + + someParameters[i].getClass().getName()); + } + } + ret.append(">;"); + return ret.toString(); + } + + @Override + public void ensureConsistent() { + annotations = null; + annotationTypes = null; + newSuperclass = null; + bits = 0; // clears the hierarchy complete tag (amongst other things) + newInterfaces = null; + typeVariables = null; + parameterizedInterfaces.clear(); + superclassReference = new WeakReference<ResolvedType>(null); + if (getDelegate() != null) { + delegate.ensureConsistent(); + } + if (isParameterizedOrRawType()) { + ReferenceType genericType = getGenericType(); + if (genericType != null) { + genericType.ensureConsistent(); + } + } + } + + @Override + public void addParent(ResolvedType newParent) { + if (this.isRawType()) { + throw new IllegalStateException( + "The raw type should never be accumulating new interfaces, they should be on the generic type. Type is " + + this.getName()); + } + if (newParent.isClass()) { + newSuperclass = newParent; + superclassReference = new WeakReference<ResolvedType>(null); + } else { + if (newInterfaces == null) { + newInterfaces = new ResolvedType[1]; + newInterfaces[0] = newParent; + } else { + ResolvedType[] existing = getDelegate().getDeclaredInterfaces(); + if (existing != null) { + for (int i = 0; i < existing.length; i++) { + if (existing[i].equals(newParent)) { + return; // already has this interface + } + } + } + ResolvedType[] newNewInterfaces = new ResolvedType[newInterfaces.length + 1]; + System.arraycopy(newInterfaces, 0, newNewInterfaces, 1, + newInterfaces.length); + newNewInterfaces[0] = newParent; + newInterfaces = newNewInterfaces; + } + if (this.isGenericType()) { + synchronized (derivativeTypes) { + for (WeakReference<ReferenceType> derivativeTypeRef : derivativeTypes) { + ReferenceType derivativeType = derivativeTypeRef.get(); + if (derivativeType != null) { + derivativeType.parameterizedInterfaces.clear(); + } + } + } + } + parameterizedInterfaces.clear(); + } + } + + private boolean equal(UnresolvedType[] typeParameters, + ResolvedType[] resolvedParameters) { + if (typeParameters.length != resolvedParameters.length) { + return false; + } + int len = typeParameters.length; + for (int p = 0; p < len; p++) { + if (!typeParameters[p].equals(resolvedParameters[p])) { + return false; + } + } + return true; + } + + /** + * Look for a derivative type with the specified type parameters. This can + * avoid creating an unnecessary new (duplicate) with the same information + * in it. This method also cleans up any reference entries that have been + * null'd by a GC. + * + * @param typeParameters + * the type parameters to use when searching for the derivative + * type. + * @return an existing derivative type or null if there isn't one + */ + public ReferenceType findDerivativeType(ResolvedType[] typeParameters) { + synchronized (derivativeTypes) { + List<WeakReference<ReferenceType>> forRemoval = new ArrayList<WeakReference<ReferenceType>>(); + for (WeakReference<ReferenceType> derivativeTypeRef : derivativeTypes) { + ReferenceType derivativeType = derivativeTypeRef.get(); + if (derivativeType == null) { + forRemoval.add(derivativeTypeRef); + } else { + if (derivativeType.isRawType()) { + continue; + } + if (equal(derivativeType.typeParameters, typeParameters)) { + return derivativeType; // this escape route wont remove + // the empty refs + } + } + } + derivativeTypes.removeAll(forRemoval); + } + return null; + } + + public boolean hasNewInterfaces() { + return newInterfaces != null; + } + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ReferenceTypeDelegate.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ReferenceTypeDelegate.java new file mode 100644 index 000000000..e07c032e6 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ReferenceTypeDelegate.java @@ -0,0 +1,148 @@ +/* ******************************************************************* + * Copyright (c) 2002 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: + * PARC initial implementation + * Andy Clement - June 2005 - separated out from ResolvedType + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.Collection; + +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.PerClause; + +/** + * Abstraction over a type - a reference type is Object or a descendant of Object, other types (int/etc) are considered primitive + * types. Abstract implementation provided by AbstractReferenceTypeDelegate. + */ + +public interface ReferenceTypeDelegate { + + public boolean isAspect(); + + /** + * @return true if the type is an annotation style aspect (a type marked @Aspect) + */ + public boolean isAnnotationStyleAspect(); + + public boolean isInterface(); + + public boolean isEnum(); + + public boolean isAnnotation(); + + public String getRetentionPolicy(); + + /** + * @return true if this annotation type can be on a regular type (ie. it doesn't specify anything or it specifies TYPE) + */ + public boolean canAnnotationTargetType(); + + /** + * @return all the possible targets that this annotation can be placed upon + */ + public AnnotationTargetKind[] getAnnotationTargetKinds(); + + /** + * @return true if this annotation type has a retention policy of RUNTIME + */ + public boolean isAnnotationWithRuntimeRetention(); + + public boolean isClass(); + + public boolean isGeneric(); + + public boolean isAnonymous(); + + /** + * @return true if this class is nested (this includes: member classes, local classes, anonymous classes) + */ + public boolean isNested(); + + public boolean hasAnnotation(UnresolvedType ofType); + + public AnnotationAJ[] getAnnotations(); + + public ResolvedType[] getAnnotationTypes(); + + public ResolvedMember[] getDeclaredFields(); + + public ResolvedType[] getDeclaredInterfaces(); + + public ResolvedMember[] getDeclaredMethods(); + + public ResolvedMember[] getDeclaredPointcuts(); + + public TypeVariable[] getTypeVariables(); + + public int getModifiers(); + + // aspect declaration related members + /** + * @return for an aspect declaration, return the + */ + public PerClause getPerClause(); + + public Collection<Declare> getDeclares(); + + public Collection<ConcreteTypeMunger> getTypeMungers(); + + public Collection<ResolvedMember> getPrivilegedAccesses(); + + // end of aspect declaration related members + + public ResolvedType getSuperclass(); + + public WeaverStateInfo getWeaverState(); + + public ReferenceType getResolvedTypeX(); + + // needs renaming isWeavable or removing from here + public boolean isExposedToWeaver(); + + public boolean doesNotExposeShadowMungers(); + + public ISourceContext getSourceContext(); + + public String getSourcefilename(); + + public String getDeclaredGenericSignature(); + + public ResolvedType getOuterClass(); + + public boolean copySourceContext(); + + /** + * TODO Caching of methods besides getDeclaredInterfaces() may also be dependent on this flag - which? + * + * @return true if something the result of getDeclaredInterfaces() can be cached by the caller + */ + public boolean isCacheable(); + + /** + * If known, return the compiler/weaver version used to build this delegate. Default is the most recent level as specified in + * {@link WeaverVersionInfo}. + * + * @return the major version + */ + public int getCompilerVersion(); + + /** + * Implementations need to clear state + */ + public void ensureConsistent(); + + public boolean isWeavable(); + + public boolean hasBeenWoven(); + + public boolean hasAnnotations(); + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvableTypeList.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvableTypeList.java new file mode 100644 index 000000000..34e17b44a --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvableTypeList.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * Copyright (c) 2009 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; + +/** + * Carries an array of unresolved types - will resolve them on demand. Can be used where currently the entire array gets resolved + * and a ResolvedType array is passed on. Depending on the situation there may not be a need to resolve all the entries so this can + * perform better. Note: the array elements are resolved in place, so the caller should not be surprised if elements and resolved + * after the type list has been used. + * + * @author Andy Clement + */ +public class ResolvableTypeList { + + public int length; + private World world; + private UnresolvedType[] types; + + public ResolvableTypeList(World world, UnresolvedType[] unresolvedTypes) { + length = unresolvedTypes.length; + types = unresolvedTypes; + this.world = world; + } + + public ResolvedType getResolved(int nameIndex) { + UnresolvedType ut = types[nameIndex]; + if (!(ut instanceof ResolvedType)) { + types[nameIndex] = world.resolve(ut); + return (ResolvedType) types[nameIndex]; + } else { + return (ResolvedType) ut; + } + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedMember.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedMember.java new file mode 100644 index 000000000..0b5def29a --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedMember.java @@ -0,0 +1,158 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * 2005 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: + * PARC initial implementation + * AMC extracted as interface + * ******************************************************************/ +package org.aspectj.weaver; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; + +public interface ResolvedMember extends Member, AnnotatedElement, TypeVariableDeclaringElement { + + public static final ResolvedMember[] NONE = new ResolvedMember[0]; + + public int getModifiers(World world); + + public int getModifiers(); + + public UnresolvedType[] getExceptions(World world); + + public UnresolvedType[] getExceptions(); + + public ShadowMunger getAssociatedShadowMunger(); + + public boolean isAjSynthetic(); + + public boolean isCompatibleWith(Member am); + + public boolean hasAnnotation(UnresolvedType ofType); + + public AnnotationAJ[] getAnnotations(); + + public ResolvedType[] getAnnotationTypes(); + + public void setAnnotationTypes(ResolvedType[] annotationtypes); + + public void addAnnotation(AnnotationAJ annotation); + + public boolean isBridgeMethod(); + + public boolean isVarargsMethod(); + + public boolean isSynthetic(); + + public void write(CompressingDataOutputStream s) throws IOException; + + public ISourceContext getSourceContext(World world); + + public String[] getParameterNames(); + + public void setParameterNames(String[] names); + + public AnnotationAJ[][] getParameterAnnotations(); + + public ResolvedType[][] getParameterAnnotationTypes(); + + public String getAnnotationDefaultValue(); + + public String getParameterSignatureErased(); + + public String getSignatureErased(); + + public String[] getParameterNames(World world); + + public AjAttribute.EffectiveSignatureAttribute getEffectiveSignature(); + + public ISourceLocation getSourceLocation(); + + public int getStart(); + + public int getEnd(); + + public ISourceContext getSourceContext(); + + public void setPosition(int sourceStart, int sourceEnd); + + public void setSourceContext(ISourceContext sourceContext); + + public boolean isAbstract(); + + public boolean isPublic(); + + public boolean isDefault(); + + public boolean isVisible(ResolvedType fromType); + + public void setCheckedExceptions(UnresolvedType[] checkedExceptions); + + public void setAnnotatedElsewhere(boolean b); + + public boolean isAnnotatedElsewhere(); + + // like toString but include generic signature info + public String toGenericString(); + + public String toDebugString(); + + public boolean hasBackingGenericMember(); + + public ResolvedMember getBackingGenericMember(); + + /** + * Get the UnresolvedType for the return type, taking generic signature into account + */ + public UnresolvedType getGenericReturnType(); + + /** + * Get the TypeXs of the parameter types, taking generic signature into account + */ + public UnresolvedType[] getGenericParameterTypes(); + + public boolean equalsApartFromDeclaringType(Object other); + + // return a resolved member in which all type variables in the signature of + // this member have been replaced with the given bindings. the isParameterized flag tells us whether we are creating a raw type + // version or not + // if isParameterized List<T> will turn into List<String> (for example), + // but if !isParameterized List<T> will turn into List. + public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType, + boolean isParameterized); + + // this variant allows for aliases for type variables (i.e. allowing them to + // have another name) + // this is used for processing ITDs that share type variables with their + // target generic type + public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType, + boolean isParameterized, List<String> aliases); + + public void setTypeVariables(TypeVariable[] types); + + public TypeVariable[] getTypeVariables(); + + /** + * Returns true if this member matches the other. The matching takes into account name and parameter types only. When comparing + * parameter types, we allow any type variable to match any other type variable regardless of bounds. + */ + public boolean matches(ResolvedMember aCandidateMatch, boolean ignoreGenerics); + + public void evictWeavingState(); + + public ResolvedMember parameterizedWith(Map<String, UnresolvedType> m, World w); + + public boolean isDefaultConstructor(); + + public void setAnnotations(AnnotationAJ[] annotations); + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedMemberImpl.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedMemberImpl.java new file mode 100644 index 000000000..cfd0508c4 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedMemberImpl.java @@ -0,0 +1,1266 @@ +/* ******************************************************************* + * Copyright (c) 2002-2010 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 + * ******************************************************************/ +package org.aspectj.weaver; + +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.ISourceLocation; + +/** + * Represent a resolved member. Components of it are expected to exist. This member will correspond to a real member *unless* it is + * being used to represent the effect of an ITD. + * + * @author PARC + * @author Andy Clement + */ +public class ResolvedMemberImpl extends MemberImpl implements IHasPosition, ResolvedMember { + + private String[] parameterNames = null; + private boolean isResolved = false; + protected UnresolvedType[] checkedExceptions = UnresolvedType.NONE; + + /** + * if this member is a parameterized version of a member in a generic type, then this field holds a reference to the member we + * parameterize. + */ + protected ResolvedMember backingGenericMember = null; + + protected AnnotationAJ[] annotations = null; + protected ResolvedType[] annotationTypes = null; + protected AnnotationAJ[][] parameterAnnotations = null; + protected ResolvedType[][] parameterAnnotationTypes = null; + + // Some members are 'created' to represent other things (for example ITDs). + // These members have their annotations stored elsewhere, and this flag indicates + // that is the case. It is up to the caller to work out where that is! + // Once determined the caller may choose to stash the annotations in this + // member... + private boolean isAnnotatedElsewhere = false; + private boolean isAjSynthetic = false; + + // generic methods have type variables + protected TypeVariable[] typeVariables; + + // these three fields hold the source location of this member + protected int start, end; + protected ISourceContext sourceContext = null; + + // XXX deprecate this in favor of the constructor below + public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, UnresolvedType returnType, String name, + UnresolvedType[] parameterTypes) { + super(kind, declaringType, modifiers, returnType, name, parameterTypes); + } + + public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, UnresolvedType returnType, String name, + UnresolvedType[] parameterTypes, UnresolvedType[] checkedExceptions) { + super(kind, declaringType, modifiers, returnType, name, parameterTypes); + this.checkedExceptions = checkedExceptions; + } + + public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, UnresolvedType returnType, String name, + UnresolvedType[] parameterTypes, UnresolvedType[] checkedExceptions, ResolvedMember backingGenericMember) { + this(kind, declaringType, modifiers, returnType, name, parameterTypes, checkedExceptions); + this.backingGenericMember = backingGenericMember; + this.isAjSynthetic = backingGenericMember.isAjSynthetic(); + } + + public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, String name, String signature) { + super(kind, declaringType, modifiers, name, signature); + } + + /** + * Compute the full set of signatures for a member. This walks up the hierarchy giving the ResolvedMember in each defining type + * in the hierarchy. A shadowMember can be created with a target type (declaring type) that does not actually define the member. + * This is ok as long as the member is inherited in the declaring type. Each declaring type in the line to the actual declaring + * type is added as an additional signature. For example: + * + * class A { void foo(); } class B extends A {} + * + * shadowMember : void B.foo() + * + * gives { void B.foo(), void A.foo() } + * + * @param joinPointSignature + * @param inAWorld + */ + public static JoinPointSignature[] getJoinPointSignatures(Member joinPointSignature, World inAWorld) { + + // Walk up hierarchy creating one member for each type up to and + // including the + // first defining type + ResolvedType originalDeclaringType = joinPointSignature.getDeclaringType().resolve(inAWorld); + ResolvedMemberImpl firstDefiningMember = (ResolvedMemberImpl) joinPointSignature.resolve(inAWorld); + if (firstDefiningMember == null) { + return JoinPointSignature.EMPTY_ARRAY; + } + // declaringType can be unresolved if we matched a synthetic member + // generated by Aj... + // should be fixed elsewhere but add this resolve call on the end for + // now so that we can + // focus on one problem at a time... + ResolvedType firstDefiningType = firstDefiningMember.getDeclaringType().resolve(inAWorld); + if (firstDefiningType != originalDeclaringType) { + if (joinPointSignature.getKind() == Member.CONSTRUCTOR) { + return JoinPointSignature.EMPTY_ARRAY; + } + // else if (shadowMember.isStatic()) { + // return new ResolvedMember[] {firstDefiningMember}; + // } + } + + List<ResolvedType> declaringTypes = new ArrayList<ResolvedType>(); + accumulateTypesInBetween(originalDeclaringType, firstDefiningType, declaringTypes); + Set<ResolvedMember> memberSignatures = new HashSet<ResolvedMember>(); + for (ResolvedType declaringType : declaringTypes) { + memberSignatures.add(new JoinPointSignature(firstDefiningMember, declaringType)); + } + + if (shouldWalkUpHierarchyFor(firstDefiningMember)) { + // now walk up the hierarchy from the firstDefiningMember and + // include the signature for + // every type between the firstDefiningMember and the root defining + // member. + Iterator<ResolvedType> superTypeIterator = firstDefiningType.getDirectSupertypes(); + List<ResolvedType> typesAlreadyVisited = new ArrayList<ResolvedType>(); + accumulateMembersMatching(firstDefiningMember, superTypeIterator, typesAlreadyVisited, memberSignatures, false); + } + + JoinPointSignature[] ret = new JoinPointSignature[memberSignatures.size()]; + memberSignatures.toArray(ret); + return ret; + } + + private static boolean shouldWalkUpHierarchyFor(Member aMember) { + if (aMember.getKind() == Member.CONSTRUCTOR) { + return false; + } + if (aMember.getKind() == Member.FIELD) { + return false; + } + if (Modifier.isStatic(aMember.getModifiers())) { + return false; + } + return true; + } + + /** + * Build a list containing every type between subtype and supertype, inclusively. + */ + private static void accumulateTypesInBetween(ResolvedType subType, ResolvedType superType, List<ResolvedType> types) { + types.add(subType); + if (subType == superType) { + return; + } else { + for (Iterator<ResolvedType> iter = subType.getDirectSupertypes(); iter.hasNext();) { + ResolvedType parent = iter.next(); + if (superType.isAssignableFrom(parent)) { + accumulateTypesInBetween(parent, superType, types); + } + } + } + } + + /** + * We have a resolved member, possibly with type parameter references as parameters or return type. We need to find all its + * ancestor members. When doing this, a type parameter matches regardless of bounds (bounds can be narrowed down the hierarchy). + */ + private static void accumulateMembersMatching(ResolvedMemberImpl memberToMatch, Iterator<ResolvedType> typesToLookIn, + List<ResolvedType> typesAlreadyVisited, Set<ResolvedMember> foundMembers, boolean ignoreGenerics) { + while (typesToLookIn.hasNext()) { + ResolvedType toLookIn = typesToLookIn.next(); + if (!typesAlreadyVisited.contains(toLookIn)) { + typesAlreadyVisited.add(toLookIn); + ResolvedMemberImpl foundMember = (ResolvedMemberImpl) toLookIn.lookupResolvedMember(memberToMatch, true, + ignoreGenerics); + if (foundMember != null && isVisibleTo(memberToMatch, foundMember)) { + List<ResolvedType> declaringTypes = new ArrayList<ResolvedType>(); + // declaring type can be unresolved if the member can from + // an ITD... + ResolvedType resolvedDeclaringType = foundMember.getDeclaringType().resolve(toLookIn.getWorld()); + accumulateTypesInBetween(toLookIn, resolvedDeclaringType, declaringTypes); + for (ResolvedType declaringType : declaringTypes) { + // typesAlreadyVisited.add(declaringType); + foundMembers.add(new JoinPointSignature(foundMember, declaringType)); + } + if (!ignoreGenerics && toLookIn.isParameterizedType() && (foundMember.backingGenericMember != null)) { + foundMembers.add(new JoinPointSignature(foundMember.backingGenericMember, foundMember.declaringType + .resolve(toLookIn.getWorld()))); + } + accumulateMembersMatching(foundMember, toLookIn.getDirectSupertypes(), typesAlreadyVisited, foundMembers, + ignoreGenerics); + // if this was a parameterized type, look in the generic + // type that backs it too + } + } + } + } + + /** + * Returns true if the parent member is visible to the child member In the same declaring type this is always true, otherwise if + * parent is private it is false. + * + * @param childMember + * @param parentMember + * @return + */ + private static boolean isVisibleTo(ResolvedMember childMember, ResolvedMember parentMember) { + if (childMember.getDeclaringType().equals(parentMember.getDeclaringType())) { + return true; + } + if (Modifier.isPrivate(parentMember.getModifiers())) { + return false; + } else { + return true; + } + } + + // ---- + + @Override + public final int getModifiers(World world) { + return modifiers; + } + + @Override + public final int getModifiers() { + return modifiers; + } + + // ---- + + @Override + public final UnresolvedType[] getExceptions(World world) { + return getExceptions(); + } + + public UnresolvedType[] getExceptions() { + return checkedExceptions; + } + + public ShadowMunger getAssociatedShadowMunger() { + return null; + } + + // ??? true or false? + public boolean isAjSynthetic() { + return isAjSynthetic; + } + + protected void setAjSynthetic(boolean b) { + isAjSynthetic = b; + } + + public boolean hasAnnotations() { + return (annotationTypes != null); + } + + /** + * Check if this member has an annotation of the specified type. If the member has a backing generic member then this member + * represents a parameterization of a member in a generic type and the annotations available on the backing generic member + * should be used. + * + * @param ofType the type of the annotation being searched for + * @return true if the annotation is found on this member or its backing generic member + */ + public boolean hasAnnotation(UnresolvedType ofType) { + // The ctors don't allow annotations to be specified ... yet - but + // that doesn't mean it is an error to call this method. + // Normally the weaver will be working with subtypes of + // this type - BcelField/BcelMethod + if (backingGenericMember != null) { + if (annotationTypes != null) { + throw new BCException("Unexpectedly found a backing generic member and a local set of annotations"); + } + return backingGenericMember.hasAnnotation(ofType); + } + if (annotationTypes != null) { + for (int i = 0, max = annotationTypes.length; i < max; i++) { + if (annotationTypes[i].equals(ofType)) { + return true; + } + } + } + return false; + } + + public ResolvedType[] getAnnotationTypes() { + // The ctors don't allow annotations to be specified ... yet - but + // that doesn't mean it is an error to call this method. + // Normally the weaver will be working with subtypes of + // this type - BcelField/BcelMethod + if (backingGenericMember != null) { + if (annotationTypes != null) { + throw new BCException("Unexpectedly found a backing generic member and a local set of annotations"); + } + return backingGenericMember.getAnnotationTypes(); + } + return annotationTypes; + } + + public String getAnnotationDefaultValue() { + throw new UnsupportedOperationException( + "You should resolve this member and call getAnnotationDefaultValue() on the result..."); + } + + @Override + public AnnotationAJ[] getAnnotations() { + if (backingGenericMember != null) { + return backingGenericMember.getAnnotations(); + } + if (annotations!=null) { + return annotations; + } + return super.getAnnotations(); + } + + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + if (annotations!=null) { + // this means they have been set (we are likely a placeholder for an ITD, so a fake member) + for (AnnotationAJ annotation: annotations) { + if (annotation.getType().equals(ofType)) { + return annotation; + } + } + return null; + } + throw new UnsupportedOperationException("You should resolve this member and call getAnnotationOfType() on the result..."); + } + + public void setAnnotations(AnnotationAJ[] annotations) { + this.annotations = annotations; + } + + public void setAnnotationTypes(ResolvedType[] annotationTypes) { + this.annotationTypes = annotationTypes; + } + + public ResolvedType[][] getParameterAnnotationTypes() { + return parameterAnnotationTypes; + } + + public AnnotationAJ[][] getParameterAnnotations() { + if (backingGenericMember != null) { + return backingGenericMember.getParameterAnnotations(); + } + throw new BCException("Cannot return parameter annotations for a " + this.getClass().getName() + " member"); + // return super.getParameterAnnotations(); + } + + public void addAnnotation(AnnotationAJ annotation) { + if (annotationTypes == null) { + annotationTypes = new ResolvedType[1]; + annotationTypes[0] = annotation.getType(); + annotations = new AnnotationAJ[1]; + annotations[0] = annotation; + } else { + int len = annotations.length; + AnnotationAJ[] ret = new AnnotationAJ[len + 1]; + System.arraycopy(annotations, 0, ret, 0, len); + ret[len] = annotation; + annotations = ret; + + ResolvedType[] newAnnotationTypes = new ResolvedType[len + 1]; + System.arraycopy(annotationTypes, 0, newAnnotationTypes, 0, len); + newAnnotationTypes[len] = annotation.getType(); + annotationTypes = newAnnotationTypes; + } + } + + public boolean isBridgeMethod() { + return (modifiers & Constants.ACC_BRIDGE) != 0 && getKind().equals(METHOD); + } + + public boolean isVarargsMethod() { + return (modifiers & Constants.ACC_VARARGS) != 0; + } + + public void setVarargsMethod() { + modifiers = modifiers | Constants.ACC_VARARGS; + } + + public boolean isSynthetic() { + // See Bcelmethod.isSynthetic() which takes account of preJava5 + // Synthetic modifier + return (modifiers & 4096) != 0; // do we know better? + } + + public void write(CompressingDataOutputStream s) throws IOException { + getKind().write(s); + s.writeBoolean(s.canCompress()); // boolean indicates if parts of this are compressed references + + // write out the signature of the declaring type of this member + if (s.canCompress()) { + s.writeCompressedSignature(getDeclaringType().getSignature()); + } else { + getDeclaringType().write(s); + } + + // write out the modifiers + s.writeInt(modifiers); + + // write out the name and the signature of this member + if (s.canCompress()) { + s.writeCompressedName(getName()); + s.writeCompressedSignature(getSignature()); + } else { + s.writeUTF(getName()); + s.writeUTF(getSignature()); + } + + // write out the array clauses + UnresolvedType.writeArray(getExceptions(), s); + + s.writeInt(getStart()); + s.writeInt(getEnd()); + s.writeBoolean(isVarargsMethod()); + + // Write out any type variables... + if (typeVariables == null) { + s.writeByte(0); + } else { + s.writeByte(typeVariables.length); + for (int i = 0; i < typeVariables.length; i++) { + typeVariables[i].write(s); + } + } + String gsig = getGenericSignature(); + + // change this to a byte: 255=false 0>254 means true and encodes the number of parameters + if (getSignature().equals(gsig)) { + s.writeByte(0xff); + } else { + s.writeByte(parameterTypes.length); + for (int i = 0; i < parameterTypes.length; i++) { + if (s.canCompress()) { + s.writeCompressedSignature(parameterTypes[i].getSignature()); + } else { + UnresolvedType array_element = parameterTypes[i]; + array_element.write(s); + } + } + if (s.canCompress()) { + s.writeCompressedSignature(returnType.getSignature()); + } else { + returnType.write(s); + } + } + } + + /** + * Return the member generic signature that would be suitable for inclusion in a class file Signature attribute. For: <T> + * List<String> getThem(T t) {} we would create: <T:Ljava/lang/Object;>(TT;)Ljava/util/List<Ljava/lang/String;>;; + * + * @return the generic signature for the member that could be inserted into a class file + */ + public String getSignatureForAttribute() { + StringBuffer sb = new StringBuffer(); + if (typeVariables != null) { + sb.append("<"); + for (int i = 0; i < typeVariables.length; i++) { + sb.append(typeVariables[i].getSignatureForAttribute()); // need + // a + // 'getSignatureForAttribute()' + } + sb.append(">"); + } + sb.append("("); + for (int i = 0; i < parameterTypes.length; i++) { + ResolvedType ptype = (ResolvedType) parameterTypes[i]; + sb.append(ptype.getSignatureForAttribute()); + } + sb.append(")"); + sb.append(((ResolvedType) returnType).getSignatureForAttribute()); + return sb.toString(); + } + + public String getGenericSignature() { + StringBuffer sb = new StringBuffer(); + if (typeVariables != null) { + sb.append("<"); + for (int i = 0; i < typeVariables.length; i++) { + sb.append(typeVariables[i].getSignature()); + } + sb.append(">"); + } + sb.append("("); + for (int i = 0; i < parameterTypes.length; i++) { + UnresolvedType ptype = parameterTypes[i]; + sb.append(ptype.getSignature()); + } + sb.append(")"); + sb.append(returnType.getSignature()); + return sb.toString(); + } + + public static void writeArray(ResolvedMember[] members, CompressingDataOutputStream s) throws IOException { + s.writeInt(members.length); + for (int i = 0, len = members.length; i < len; i++) { + members[i].write(s); + } + } + + public static ResolvedMemberImpl readResolvedMember(VersionedDataInputStream s, ISourceContext sourceContext) + throws IOException { + + MemberKind mk = MemberKind.read(s); + boolean compressed = (s.isAtLeast169() ? s.readBoolean() : false); + UnresolvedType declaringType = compressed ? UnresolvedType.forSignature(s.readUtf8(s.readShort())) : UnresolvedType.read(s); + int modifiers = s.readInt(); + String name = compressed ? s.readUtf8(s.readShort()) : s.readUTF(); + String signature = compressed ? s.readUtf8(s.readShort()) : s.readUTF(); + ResolvedMemberImpl m = new ResolvedMemberImpl(mk, declaringType, modifiers, name, signature); + m.checkedExceptions = UnresolvedType.readArray(s); + + m.start = s.readInt(); + m.end = s.readInt(); + m.sourceContext = sourceContext; + + if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + + if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150M4) { + boolean isvarargs = s.readBoolean(); + if (isvarargs) { + m.setVarargsMethod(); + } + } + + int tvcount = s.isAtLeast169() ? s.readByte() : s.readInt(); + if (tvcount != 0) { + m.typeVariables = new TypeVariable[tvcount]; + for (int i = 0; i < tvcount; i++) { + m.typeVariables[i] = TypeVariable.read(s); + m.typeVariables[i].setDeclaringElement(m); + m.typeVariables[i].setRank(i); + } + } + if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150M4) { + int pcount = -1; + boolean hasAGenericSignature = false; + if (s.isAtLeast169()) { + pcount = s.readByte(); + hasAGenericSignature = (pcount >= 0 && pcount < 255); + } else { + hasAGenericSignature = s.readBoolean(); + } + if (hasAGenericSignature) { + int ps = (s.isAtLeast169() ? pcount : s.readInt()); + UnresolvedType[] params = new UnresolvedType[ps]; + for (int i = 0; i < params.length; i++) { + if (compressed) { + params[i] = TypeFactory.createTypeFromSignature(s.readSignature()); + } else { + params[i] = TypeFactory.createTypeFromSignature(s.readUTF()); + } + } + UnresolvedType rt = compressed ? TypeFactory.createTypeFromSignature(s.readSignature()) : TypeFactory + .createTypeFromSignature(s.readUTF()); + m.parameterTypes = params; + m.returnType = rt; + } + } + } + return m; + } + + public static ResolvedMember[] readResolvedMemberArray(VersionedDataInputStream s, ISourceContext context) throws IOException { + int len = s.readInt(); + ResolvedMember[] members = new ResolvedMember[len]; + for (int i = 0; i < len; i++) { + members[i] = ResolvedMemberImpl.readResolvedMember(s, context); + } + return members; + } + + // OPTIMIZE dont like how resolve(world) on ResolvedMemberImpl does + // something different to world.resolve(member) + @Override + public ResolvedMember resolve(World world) { + if (isResolved) { + return this; + } + // make sure all the pieces of a resolvedmember really are resolved + try { + if (typeVariables != null && typeVariables.length > 0) { + for (int i = 0; i < typeVariables.length; i++) { + typeVariables[i] = typeVariables[i].resolve(world); + } + } + world.setTypeVariableLookupScope(this); + // if (annotationTypes != null) { + // Set<ResolvedType> r = new HashSet<ResolvedType>(); + // for (UnresolvedType element : annotationTypes) { + // // for (Iterator iter = annotationTypes.iterator(); iter.hasNext();) { + // // UnresolvedType element = (UnresolvedType) iter.next(); + // r.add(world.resolve(element)); + // } + // annotationTypes = r; + // } + declaringType = declaringType.resolve(world); + if (declaringType.isRawType()) { + declaringType = ((ReferenceType) declaringType).getGenericType(); + } + + if (parameterTypes != null && parameterTypes.length > 0) { + for (int i = 0; i < parameterTypes.length; i++) { + parameterTypes[i] = parameterTypes[i].resolve(world); + } + } + + returnType = returnType.resolve(world); + + } finally { + world.setTypeVariableLookupScope(null); + } + isResolved = true; + return this; + } + + public ISourceContext getSourceContext(World world) { + return getDeclaringType().resolve(world).getSourceContext(); + } + + public String[] getParameterNames() { + return parameterNames; + } + + public final void setParameterNames(String[] pnames) { + parameterNames = pnames; + } + + @Override + public final String[] getParameterNames(World world) { + return getParameterNames(); + } + + public AjAttribute.EffectiveSignatureAttribute getEffectiveSignature() { + return null; + } + + public ISourceLocation getSourceLocation() { + // System.out.println("get context: " + this + " is " + sourceContext); + if (getSourceContext() == null) { + // System.err.println("no context: " + this); + return null; + } + return getSourceContext().makeSourceLocation(this); + } + + public int getEnd() { + return end; + } + + public ISourceContext getSourceContext() { + return sourceContext; + } + + public int getStart() { + return start; + } + + public void setPosition(int sourceStart, int sourceEnd) { + this.start = sourceStart; + this.end = sourceEnd; + } + + public void setDeclaringType(ReferenceType rt) { + declaringType = rt; + } + + public void setSourceContext(ISourceContext sourceContext) { + this.sourceContext = sourceContext; + } + + public boolean isAbstract() { + return Modifier.isAbstract(modifiers); + } + + public boolean isPublic() { + return Modifier.isPublic(modifiers); + } + + public boolean isDefault() { + int mods = getModifiers(); + return !(Modifier.isPublic(mods) || Modifier.isProtected(mods) || Modifier.isPrivate(mods)); + } + + public boolean isVisible(ResolvedType fromType) { + UnresolvedType declaringType = getDeclaringType(); + ResolvedType type = null; + if (fromType.equals(declaringType)) { + type = fromType; + } else { + World world = fromType.getWorld(); + type = declaringType.resolve(world); + } + return ResolvedType.isVisible(getModifiers(), type, fromType); + } + + public void setCheckedExceptions(UnresolvedType[] checkedExceptions) { + this.checkedExceptions = checkedExceptions; + } + + public void setAnnotatedElsewhere(boolean b) { + isAnnotatedElsewhere = b; + } + + public boolean isAnnotatedElsewhere() { + return isAnnotatedElsewhere; + } + + /** + * Get the UnresolvedType for the return type, taking generic signature into account + */ + @Override + public UnresolvedType getGenericReturnType() { + return getReturnType(); + } + + /** + * Get the TypeXs of the parameter types, taking generic signature into account + */ + @Override + public UnresolvedType[] getGenericParameterTypes() { + return getParameterTypes(); + } + + public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType, + boolean isParameterized) { + return parameterizedWith(typeParameters, newDeclaringType, isParameterized, null); + } + + /** + * Return a resolvedmember in which all the type variables in the signature have been replaced with the given bindings. The + * 'isParameterized' flag tells us whether we are creating a raw type version or not. if (isParameterized) then List<T> will + * turn into List<String> (for example) - if (!isParameterized) then List<T> will turn into List. + */ + public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType, + boolean isParameterized, List<String> aliases) { + // PR308773 + // this check had problems for the inner type of a generic type because the inner type can be represented + // by a 'simple type' if it is only sharing type variables with the outer and has none of its own. To avoid the + // check going bang in this case we check for $ (crap...) - we can't check the outer because the declaring type + // is considered unresolved... + if (// isParameterized && <-- might need this bit... + !getDeclaringType().isGenericType() && getDeclaringType().getName().indexOf("$") == -1) { + throw new IllegalStateException("Can't ask to parameterize a member of non-generic type: " + getDeclaringType() + + " kind(" + getDeclaringType().typeKind + ")"); + } + TypeVariable[] typeVariables = getDeclaringType().getTypeVariables(); + if (isParameterized && (typeVariables.length != typeParameters.length)) { + throw new IllegalStateException("Wrong number of type parameters supplied"); + } + Map<String, UnresolvedType> typeMap = new HashMap<String, UnresolvedType>(); + boolean typeParametersSupplied = typeParameters != null && typeParameters.length > 0; + if (typeVariables != null) { + // If no 'replacements' were supplied in the typeParameters array + // then collapse + // type variables to their first bound. + for (int i = 0; i < typeVariables.length; i++) { + UnresolvedType ut = (!typeParametersSupplied ? typeVariables[i].getFirstBound() : typeParameters[i]); + typeMap.put(typeVariables[i].getName(), ut); + } + } + // For ITDs on generic types that use type variables from the target type, the aliases + // record the alternative names used throughout the ITD expression that must map to + // the same value as the type variables real name. + if (aliases != null) { + int posn = 0; + for (String typeVariableAlias : aliases) { + typeMap.put(typeVariableAlias, (!typeParametersSupplied ? typeVariables[posn].getFirstBound() + : typeParameters[posn])); + posn++; + } + } + + UnresolvedType parameterizedReturnType = parameterize(getGenericReturnType(), typeMap, isParameterized, + newDeclaringType.getWorld()); + UnresolvedType[] parameterizedParameterTypes = new UnresolvedType[getGenericParameterTypes().length]; + UnresolvedType[] genericParameterTypes = getGenericParameterTypes(); + for (int i = 0; i < parameterizedParameterTypes.length; i++) { + parameterizedParameterTypes[i] = parameterize(genericParameterTypes[i], typeMap, isParameterized, + newDeclaringType.getWorld()); + } + ResolvedMemberImpl ret = new ResolvedMemberImpl(getKind(), newDeclaringType, getModifiers(), parameterizedReturnType, + getName(), parameterizedParameterTypes, getExceptions(), this); + ret.setTypeVariables(getTypeVariables()); + ret.setSourceContext(getSourceContext()); + ret.setPosition(getStart(), getEnd()); + ret.setParameterNames(getParameterNames()); + return ret; + } + + /** + * Replace occurrences of type variables in the signature with values contained in the map. The map is of the form + * A=String,B=Integer and so a signature List<A> Foo.m(B i) {} would become List<String> Foo.m(Integer i) {} + */ + public ResolvedMember parameterizedWith(Map<String, UnresolvedType> m, World w) { + // if (//isParameterized && <-- might need this bit... + // !getDeclaringType().isGenericType()) { + // throw new IllegalStateException( + // "Can't ask to parameterize a member of non-generic type: " + // +getDeclaringType()+" kind("+ + // getDeclaringType().typeKind+")"); + // } + declaringType = declaringType.resolve(w); + if (declaringType.isRawType()) { + declaringType = ((ResolvedType) declaringType).getGenericType(); + // TypeVariable[] typeVariables = getDeclaringType().getTypeVariables(); + // if (isParameterized && (typeVariables.length != + // typeParameters.length)) { + // throw new + // IllegalStateException("Wrong number of type parameters supplied"); + // } + // Map typeMap = new HashMap(); + // boolean typeParametersSupplied = typeParameters!=null && + // typeParameters.length>0; + // if (typeVariables!=null) { + // // If no 'replacements' were supplied in the typeParameters array + // then collapse + // // type variables to their first bound. + // for (int i = 0; i < typeVariables.length; i++) { + // UnresolvedType ut = + // (!typeParametersSupplied?typeVariables[i].getFirstBound + // ():typeParameters[i]); + // typeMap.put(typeVariables[i].getName(),ut); + // } + // } + // // For ITDs on generic types that use type variables from the target + // type, the aliases + // // record the alternative names used throughout the ITD expression + // that must map to + // // the same value as the type variables real name. + // if (aliases!=null) { + // int posn = 0; + // for (Iterator iter = aliases.iterator(); iter.hasNext();) { + // String typeVariableAlias = (String) iter.next(); + // typeMap.put(typeVariableAlias,(!typeParametersSupplied?typeVariables[ + // posn].getFirstBound():typeParameters[posn])); + // posn++; + // } + // } + } + + UnresolvedType parameterizedReturnType = parameterize(getGenericReturnType(), m, true, w); + UnresolvedType[] parameterizedParameterTypes = new UnresolvedType[getGenericParameterTypes().length]; + UnresolvedType[] genericParameterTypes = getGenericParameterTypes(); + for (int i = 0; i < parameterizedParameterTypes.length; i++) { + parameterizedParameterTypes[i] = parameterize(genericParameterTypes[i], m, true, w); + } + ResolvedMemberImpl ret = new ResolvedMemberImpl(getKind(), declaringType, getModifiers(), parameterizedReturnType, + getName(), parameterizedParameterTypes, getExceptions(), this); + ret.setTypeVariables(getTypeVariables()); + ret.setSourceContext(getSourceContext()); + ret.setPosition(getStart(), getEnd()); + ret.setParameterNames(getParameterNames()); + return ret; + } + + public void setTypeVariables(TypeVariable[] tvars) { + typeVariables = tvars; + } + + public TypeVariable[] getTypeVariables() { + return typeVariables; + } + + protected UnresolvedType parameterize(UnresolvedType aType, Map<String, UnresolvedType> typeVariableMap, + boolean inParameterizedType, World w) { + if (aType instanceof TypeVariableReference) { + String variableName = ((TypeVariableReference) aType).getTypeVariable().getName(); + if (!typeVariableMap.containsKey(variableName)) { + return aType; // if the type variable comes from the method (and + // not the type) thats OK + } + return typeVariableMap.get(variableName); + } else if (aType.isParameterizedType()) { + if (inParameterizedType) { + if (w != null) { + aType = aType.resolve(w); + } else { + UnresolvedType dType = getDeclaringType(); + aType = aType.resolve(((ResolvedType) dType).getWorld()); + } + return aType.parameterize(typeVariableMap); + } else { + return aType.getRawType(); + } + } else if (aType.isArray()) { + // The component type might be a type variable (pr150095) + int dims = 1; + String sig = aType.getSignature(); + // while (sig.charAt(dims) == '[') + // dims++; + UnresolvedType arrayType = null; + UnresolvedType componentSig = UnresolvedType.forSignature(sig.substring(dims)); + UnresolvedType parameterizedComponentSig = parameterize(componentSig, typeVariableMap, inParameterizedType, w); + if (parameterizedComponentSig.isTypeVariableReference() + && parameterizedComponentSig instanceof UnresolvedTypeVariableReferenceType + && typeVariableMap.containsKey(((UnresolvedTypeVariableReferenceType) parameterizedComponentSig) + .getTypeVariable().getName())) { // pr250632 + // TODO ASC bah, this code is rubbish - i should fix it properly + StringBuffer newsig = new StringBuffer(); + newsig.append("[T"); + newsig.append(((UnresolvedTypeVariableReferenceType) parameterizedComponentSig).getTypeVariable().getName()); + newsig.append(";"); + arrayType = UnresolvedType.forSignature(newsig.toString()); + } else { + arrayType = ResolvedType.makeArray(parameterizedComponentSig, dims); + } + return arrayType; + } + return aType; + } + + /** + * If this member is defined by a parameterized super-type, return the erasure of that member. For example: interface I<T> { T + * foo(T aTea); } class C implements I<String> { String foo(String aString) { return "something"; } } The resolved member for + * C.foo has signature String foo(String). The erasure of that member is Object foo(Object) -- use upper bound of type variable. + * A type is a supertype of itself. + */ + // public ResolvedMember getErasure() { + // if (calculatedMyErasure) return myErasure; + // calculatedMyErasure = true; + // ResolvedType resolvedDeclaringType = (ResolvedType) getDeclaringType(); + // // this next test is fast, and the result is cached. + // if (!resolvedDeclaringType.hasParameterizedSuperType()) { + // return null; + // } else { + // // we have one or more parameterized super types. + // // this member may be defined by one of them... we need to find out. + // Collection declaringTypes = + // this.getDeclaringTypes(resolvedDeclaringType.getWorld()); + // for (Iterator iter = declaringTypes.iterator(); iter.hasNext();) { + // ResolvedType aDeclaringType = (ResolvedType) iter.next(); + // if (aDeclaringType.isParameterizedType()) { + // // we've found the (a?) parameterized type that defines this member. + // // now get the erasure of it + // ResolvedMemberImpl matchingMember = (ResolvedMemberImpl) + // aDeclaringType.lookupMemberNoSupers(this); + // if (matchingMember != null && matchingMember.backingGenericMember != + // null) { + // myErasure = matchingMember.backingGenericMember; + // return myErasure; + // } + // } + // } + // } + // return null; + // } + // + // private ResolvedMember myErasure = null; + // private boolean calculatedMyErasure = false; + public boolean hasBackingGenericMember() { + return backingGenericMember != null; + } + + public ResolvedMember getBackingGenericMember() { + return backingGenericMember; + } + + /** + * For ITDs, we use the default factory methods to build a resolved member, then alter a couple of characteristics using this + * method - this is safe. + */ + public void resetName(String newName) { + this.name = newName; + } + + public void resetKind(MemberKind newKind) { + this.kind = newKind; + } + + public void resetModifiers(int newModifiers) { + this.modifiers = newModifiers; + } + + public void resetReturnTypeToObjectArray() { + returnType = UnresolvedType.OBJECTARRAY; + } + + /** + * Returns true if this member matches the other. The matching takes into account name and parameter types only. When comparing + * parameter types, we allow any type variable to match any other type variable regardless of bounds. + */ + public boolean matches(ResolvedMember aCandidateMatch, boolean ignoreGenerics) { + ResolvedMemberImpl candidateMatchImpl = (ResolvedMemberImpl) aCandidateMatch; + if (!getName().equals(aCandidateMatch.getName())) { + return false; + } + UnresolvedType[] parameterTypes = getGenericParameterTypes(); + UnresolvedType[] candidateParameterTypes = aCandidateMatch.getGenericParameterTypes(); + if (parameterTypes.length != candidateParameterTypes.length) { + return false; + } + boolean b = false; + /* + * if (ignoreGenerics) { String myParameterSignature = getParameterSigWithBoundsRemoved(); String + * candidateParameterSignature = candidateMatchImpl.getParameterSigWithBoundsRemoved(); if + * (myParameterSignature.equals(candidateParameterSignature)) { b = true; } else { myParameterSignature = + * (hasBackingGenericMember() ? backingGenericMember.getParameterSignatureErased() : getParameterSignatureErased()); + * candidateParameterSignature = (candidateMatchImpl.hasBackingGenericMember() ? candidateMatchImpl.backingGenericMember + * .getParameterSignatureErased() : candidateMatchImpl.getParameterSignatureErased()); // System.out.println("my psig = " + + * myParameterSignature); // System.out.println("can psig = " + candidateParameterSignature); b = + * myParameterSignature.equals(candidateParameterSignature); } } else { + */ + String myParameterSignature = getParameterSigWithBoundsRemoved(); + String candidateParameterSignature = candidateMatchImpl.getParameterSigWithBoundsRemoved(); + if (myParameterSignature.equals(candidateParameterSignature)) { + b = true; + } else { + // try erasure + myParameterSignature = getParameterSignatureErased(); + candidateParameterSignature = candidateMatchImpl.getParameterSignatureErased(); + // myParameterSignature = (hasBackingGenericMember() ? backingGenericMember.getParameterSignatureErased() + // : getParameterSignatureErased()); + // candidateParameterSignature = (candidateMatchImpl.hasBackingGenericMember() ? + // candidateMatchImpl.backingGenericMember + // .getParameterSignatureErased() : candidateMatchImpl.getParameterSignatureErased()); + // System.out.println("my psig = " + myParameterSignature); + // System.out.println("can psig = " + candidateParameterSignature); + b = myParameterSignature.equals(candidateParameterSignature); + // } + } + // System.out.println("Checking param signatures: " + b); + return b; + } + + /** + * converts e.g. <T extends Number>.... List<T> to just Ljava/util/List<T;>; whereas the full signature would be + * Ljava/util/List<T:Ljava/lang/Number;>; + */ + private String myParameterSignatureWithBoundsRemoved = null; + /** + * converts e.g. <T extends Number>.... List<T> to just Ljava/util/List; + */ + private String myParameterSignatureErasure = null; + + // does NOT produce a meaningful java signature, but does give a unique + // string suitable for + // comparison. + private String getParameterSigWithBoundsRemoved() { + if (myParameterSignatureWithBoundsRemoved != null) { + return myParameterSignatureWithBoundsRemoved; + } + StringBuffer sig = new StringBuffer(); + UnresolvedType[] myParameterTypes = getGenericParameterTypes(); + for (int i = 0; i < myParameterTypes.length; i++) { + appendSigWithTypeVarBoundsRemoved(myParameterTypes[i], sig, new HashSet<UnresolvedType>()); + } + myParameterSignatureWithBoundsRemoved = sig.toString(); + return myParameterSignatureWithBoundsRemoved; + } + + /** + * Return the erased form of the signature with bounds collapsed for type variables, etc. Does not include the return type, @see + * getParam + */ + public String getParameterSignatureErased() { + if (myParameterSignatureErasure == null) { + StringBuilder sig = new StringBuilder(); + for (UnresolvedType parameter : getParameterTypes()) { + sig.append(parameter.getErasureSignature()); + } + myParameterSignatureErasure = sig.toString(); + } + return myParameterSignatureErasure; + } + + public String getSignatureErased() { + StringBuffer sb = new StringBuffer(); + sb.append("("); + sb.append(getParameterSignatureErased()); + sb.append(")"); + sb.append(getReturnType().getErasureSignature()); + return sb.toString(); + } + + // does NOT produce a meaningful java signature, but does give a unique + // string suitable for + // comparison. + public static void appendSigWithTypeVarBoundsRemoved(UnresolvedType aType, StringBuffer toBuffer, + Set<UnresolvedType> alreadyUsedTypeVars) { + if (aType.isTypeVariableReference()) { + TypeVariableReferenceType typeVariableRT = (TypeVariableReferenceType) aType; + // pr204505 + if (alreadyUsedTypeVars.contains(aType)) { + toBuffer.append("..."); + } else { + alreadyUsedTypeVars.add(aType); + appendSigWithTypeVarBoundsRemoved(typeVariableRT.getTypeVariable().getFirstBound(), toBuffer, alreadyUsedTypeVars); + } + // toBuffer.append("T;"); + } else if (aType.isParameterizedType()) { + toBuffer.append(aType.getRawType().getSignature()); + toBuffer.append("<"); + for (int i = 0; i < aType.getTypeParameters().length; i++) { + appendSigWithTypeVarBoundsRemoved(aType.getTypeParameters()[i], toBuffer, alreadyUsedTypeVars); + } + toBuffer.append(">;"); + } else { + toBuffer.append(aType.getSignature()); + } + } + + /** + * Useful for writing tests, returns *everything* we know about this member. + */ + public String toDebugString() { + StringBuffer r = new StringBuffer(); + + // modifiers + int mods = modifiers; + if ((mods & 4096) > 0) { + mods = mods - 4096; // remove synthetic (added in the ASM case but + } + // not in the BCEL case...) + if ((mods & 512) > 0) { + mods = mods - 512; // remove interface (added in the BCEL case but + } + // not in the ASM case...) + if ((mods & 131072) > 0) { + mods = mods - 131072; // remove deprecated (added in the ASM case + } + // but not in the BCEL case...) + String modsStr = Modifier.toString(mods); + if (modsStr.length() != 0) { + r.append(modsStr).append("(" + mods + ")").append(" "); + } + + // type variables + if (typeVariables != null && typeVariables.length > 0) { + r.append("<"); + for (int i = 0; i < typeVariables.length; i++) { + if (i > 0) { + r.append(","); + } + TypeVariable t = typeVariables[i]; + r.append(t.toDebugString()); + } + r.append("> "); + } + + // 'declaring' type + r.append(getGenericReturnType().toDebugString()); + r.append(' '); + + // name + r.append(declaringType.getName()); + r.append('.'); + r.append(name); + + // parameter signature if a method + if (kind != FIELD) { + r.append("("); + UnresolvedType[] params = getGenericParameterTypes(); + boolean parameterNamesExist = showParameterNames && parameterNames != null && parameterNames.length == params.length; + if (params.length != 0) { + for (int i = 0, len = params.length; i < len; i++) { + if (i > 0) { + r.append(", "); + } + r.append(params[i].toDebugString()); + if (parameterNamesExist) { + r.append(" ").append(parameterNames[i]); + } + } + } + r.append(")"); + } + return r.toString(); + } + + // SECRETAPI - controlling whether parameter names come out in the debug + // string (for testing purposes) + public static boolean showParameterNames = true; + + public String toGenericString() { + StringBuffer buf = new StringBuffer(); + buf.append(getGenericReturnType().getSimpleName()); + buf.append(' '); + buf.append(declaringType.getName()); + buf.append('.'); + buf.append(name); + if (kind != FIELD) { + buf.append("("); + UnresolvedType[] params = getGenericParameterTypes(); + if (params.length != 0) { + buf.append(params[0].getSimpleName()); + for (int i = 1, len = params.length; i < len; i++) { + buf.append(", "); + buf.append(params[i].getSimpleName()); + } + } + buf.append(")"); + } + return buf.toString(); + } + + public boolean isCompatibleWith(Member am) { + if (kind != METHOD || am.getKind() != METHOD) { + return true; + } + if (!name.equals(am.getName())) { + return true; + } + if (!equalTypes(getParameterTypes(), am.getParameterTypes())) { + return true; + } + return getReturnType().equals(am.getReturnType()); + } + + private static boolean equalTypes(UnresolvedType[] a, UnresolvedType[] b) { + int len = a.length; + if (len != b.length) { + return false; + } + for (int i = 0; i < len; i++) { + if (!a[i].equals(b[i])) { + return false; + } + } + return true; + } + + public TypeVariable getTypeVariableNamed(String name) { + // Check locally... + if (typeVariables != null) { + for (int i = 0; i < typeVariables.length; i++) { + if (typeVariables[i].getName().equals(name)) { + return typeVariables[i]; + } + } + } + // check the declaring type! + return declaringType.getTypeVariableNamed(name); + + // Do generic aspects with ITDs that share type variables with the + // aspect and the target type and have their own tvars cause + // this to be messier? + } + + public void evictWeavingState() { + } + + + public boolean isEquivalentTo(Object other) { + return this.equals(other); + } + + public boolean isDefaultConstructor() { + return false; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedPointcutDefinition.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedPointcutDefinition.java new file mode 100644 index 000000000..500b30cd0 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedPointcutDefinition.java @@ -0,0 +1,142 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.weaver.patterns.Pointcut; + +public class ResolvedPointcutDefinition extends ResolvedMemberImpl { + private Pointcut pointcut; + + public ResolvedPointcutDefinition(UnresolvedType declaringType, int modifiers, String name, UnresolvedType[] parameterTypes, + Pointcut pointcut) { + this(declaringType, modifiers, name, parameterTypes, UnresolvedType.VOID, pointcut); + } + + /** + * An instance which can be given a specific returnType, used f.e. in if() pointcut for @AJ + * + * @param declaringType + * @param modifiers + * @param name + * @param parameterTypes + * @param returnType + * @param pointcut + */ + public ResolvedPointcutDefinition(UnresolvedType declaringType, int modifiers, String name, UnresolvedType[] parameterTypes, + UnresolvedType returnType, Pointcut pointcut) { + super(POINTCUT, declaringType, modifiers, returnType, name, parameterTypes); + this.pointcut = pointcut; + // XXXpointcut.assertState(Pointcut.RESOLVED); + checkedExceptions = UnresolvedType.NONE; + } + + // ---- + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + getDeclaringType().write(s); + s.writeInt(getModifiers()); + s.writeUTF(getName()); + UnresolvedType.writeArray(getParameterTypes(), s); + pointcut.write(s); + } + + public static ResolvedPointcutDefinition read(VersionedDataInputStream s, ISourceContext context) throws IOException { + ResolvedPointcutDefinition rpd = new ResolvedPointcutDefinition(UnresolvedType.read(s), s.readInt(), s.readUTF(), + UnresolvedType.readArray(s), Pointcut.read(s, context)); + rpd.setSourceContext(context); // whilst we have a source context, let's remember it + return rpd; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("pointcut "); + buf.append((getDeclaringType() == null ? "<nullDeclaringType>" : getDeclaringType().getName())); + buf.append("."); + buf.append(getName()); + buf.append("("); + for (int i = 0; i < getParameterTypes().length; i++) { + if (i > 0) { + buf.append(", "); + } + buf.append(getParameterTypes()[i].toString()); + } + buf.append(")"); + // buf.append(pointcut); + + return buf.toString(); + } + + public Pointcut getPointcut() { + return pointcut; + } + + @Override + public boolean isAjSynthetic() { + return true; + } + + /** + * Called when asking a parameterized super-aspect for its pointcuts. + */ + @Override + public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType, + boolean isParameterized) { + TypeVariable[] typeVariables = getDeclaringType().resolve(newDeclaringType.getWorld()).getTypeVariables(); + if (isParameterized && (typeVariables.length != typeParameters.length)) { + throw new IllegalStateException("Wrong number of type parameters supplied"); + } + Map typeMap = new HashMap(); + boolean typeParametersSupplied = typeParameters != null && typeParameters.length > 0; + if (typeVariables != null) { + // If no 'replacements' were supplied in the typeParameters array then collapse + // type variables to their first bound. + for (int i = 0; i < typeVariables.length; i++) { + UnresolvedType ut = (!typeParametersSupplied ? typeVariables[i].getFirstBound() : typeParameters[i]); + typeMap.put(typeVariables[i].getName(), ut); + } + } + UnresolvedType parameterizedReturnType = parameterize(getGenericReturnType(), typeMap, isParameterized, + newDeclaringType.getWorld()); + UnresolvedType[] parameterizedParameterTypes = new UnresolvedType[getGenericParameterTypes().length]; + for (int i = 0; i < parameterizedParameterTypes.length; i++) { + parameterizedParameterTypes[i] = parameterize(getGenericParameterTypes()[i], typeMap, isParameterized, + newDeclaringType.getWorld()); + } + ResolvedPointcutDefinition ret = new ResolvedPointcutDefinition(newDeclaringType, getModifiers(), getName(), + parameterizedParameterTypes, parameterizedReturnType, pointcut.parameterizeWith(typeMap, + newDeclaringType.getWorld())); + ret.setTypeVariables(getTypeVariables()); + ret.setSourceContext(getSourceContext()); + ret.setPosition(getStart(), getEnd()); + ret.setParameterNames(getParameterNames()); + return ret; + // return this; + } + + // for testing + public static final ResolvedPointcutDefinition DUMMY = new ResolvedPointcutDefinition(UnresolvedType.OBJECT, 0, "missing", + UnresolvedType.NONE, Pointcut.makeMatchesNothing(Pointcut.RESOLVED)); + + public static final ResolvedPointcutDefinition[] NO_POINTCUTS = new ResolvedPointcutDefinition[] {}; + + public void setPointcut(Pointcut pointcut) { + this.pointcut = pointcut; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedType.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedType.java new file mode 100644 index 000000000..98400ebdc --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedType.java @@ -0,0 +1,2933 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * Alexandre Vasseur @AspectJ ITDs + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.Iterators.Getter; +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.PerClause; + +public abstract class ResolvedType extends UnresolvedType implements AnnotatedElement { + + public static final ResolvedType[] EMPTY_RESOLVED_TYPE_ARRAY = new ResolvedType[0]; + public static final String PARAMETERIZED_TYPE_IDENTIFIER = "P"; + + // Set temporarily during a type pattern match call - this currently used to hold the + // annotations that may be attached to a type when it used as a parameter + public ResolvedType[] temporaryAnnotationTypes; + private ResolvedType[] resolvedTypeParams; + private String binaryPath; + + protected World world; + + protected int bits; + + private static int AnnotationBitsInitialized = 0x0001; + private static int AnnotationMarkedInherited = 0x0002; + private static int MungersAnalyzed = 0x0004; + private static int HasParentMunger = 0x0008; + private static int TypeHierarchyCompleteBit = 0x0010; + private static int GroovyObjectInitialized = 0x0020; + private static int IsGroovyObject = 0x0040; + private static int IsPrivilegedBitInitialized = 0x0080; + private static int IsPrivilegedAspect = 0x0100; + + protected ResolvedType(String signature, World world) { + super(signature); + this.world = world; + } + + protected ResolvedType(String signature, String signatureErasure, World world) { + super(signature, signatureErasure); + this.world = world; + } + + @Override + public int getSize() { + return 1; + } + + /** + * Returns an iterator through ResolvedType objects representing all the direct supertypes of this type. That is, through the + * superclass, if any, and all declared interfaces. + */ + public final Iterator<ResolvedType> getDirectSupertypes() { + Iterator<ResolvedType> interfacesIterator = Iterators.array(getDeclaredInterfaces()); + ResolvedType superclass = getSuperclass(); + if (superclass == null) { + return interfacesIterator; + } else { + return Iterators.snoc(interfacesIterator, superclass); + } + } + + public abstract ResolvedMember[] getDeclaredFields(); + + public abstract ResolvedMember[] getDeclaredMethods(); + + public abstract ResolvedType[] getDeclaredInterfaces(); + + public abstract ResolvedMember[] getDeclaredPointcuts(); + + public boolean isCacheable() { + return true; + } + + /** + * @return the superclass of this type, or null (if this represents a jlObject, primitive, or void) + */ + public abstract ResolvedType getSuperclass(); + + public abstract int getModifiers(); + + public boolean canBeSeenBy(ResolvedType from) { + int targetMods = getModifiers(); + if (Modifier.isPublic(targetMods)) { + return true; + } + if (Modifier.isPrivate(targetMods)) { + return false; + } + // isProtected() or isDefault() + return getPackageName().equals(from.getPackageName()); + } + + // return true if this resolved type couldn't be found (but we know it's name maybe) + public boolean isMissing() { + return false; + } + + // FIXME asc I wonder if in some circumstances MissingWithKnownSignature + // should not be considered + // 'really' missing as some code can continue based solely on the signature + public static boolean isMissing(UnresolvedType unresolved) { + if (unresolved instanceof ResolvedType) { + ResolvedType resolved = (ResolvedType) unresolved; + return resolved.isMissing(); + } else { + return (unresolved == MISSING); + } + } + + @Override + public ResolvedType[] getAnnotationTypes() { + return EMPTY_RESOLVED_TYPE_ARRAY; + } + + @Override + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + return null; + } + + // public final UnresolvedType getSuperclass(World world) { + // return getSuperclass(); + // } + + // This set contains pairs of types whose signatures are concatenated + // together, this means with a fast lookup we can tell if two types + // are equivalent. + protected static Set<String> validBoxing = new HashSet<String>(); + + static { + validBoxing.add("Ljava/lang/Byte;B"); + validBoxing.add("Ljava/lang/Character;C"); + validBoxing.add("Ljava/lang/Double;D"); + validBoxing.add("Ljava/lang/Float;F"); + validBoxing.add("Ljava/lang/Integer;I"); + validBoxing.add("Ljava/lang/Long;J"); + validBoxing.add("Ljava/lang/Short;S"); + validBoxing.add("Ljava/lang/Boolean;Z"); + validBoxing.add("BLjava/lang/Byte;"); + validBoxing.add("CLjava/lang/Character;"); + validBoxing.add("DLjava/lang/Double;"); + validBoxing.add("FLjava/lang/Float;"); + validBoxing.add("ILjava/lang/Integer;"); + validBoxing.add("JLjava/lang/Long;"); + validBoxing.add("SLjava/lang/Short;"); + validBoxing.add("ZLjava/lang/Boolean;"); + } + + // utilities + public ResolvedType getResolvedComponentType() { + return null; + } + + public World getWorld() { + return world; + } + + // ---- things from object + + @Override + public boolean equals(Object other) { + if (other instanceof ResolvedType) { + return this == other; + } else { + return super.equals(other); + } + } + + // ---- difficult things + + /** + * returns an iterator through all of the fields of this type, in order for checking from JVM spec 2ed 5.4.3.2. This means that + * the order is + * <p/> + * <ul> + * <li>fields from current class</li> + * <li>recur into direct superinterfaces</li> + * <li>recur into superclass</li> + * </ul> + * <p/> + * We keep a hashSet of interfaces that we've visited so we don't spiral out into 2^n land. + */ + public Iterator<ResolvedMember> getFields() { + final Iterators.Filter<ResolvedType> dupFilter = Iterators.dupFilter(); + Iterators.Getter<ResolvedType, ResolvedType> typeGetter = new Iterators.Getter<ResolvedType, ResolvedType>() { + @Override + public Iterator<ResolvedType> get(ResolvedType o) { + return dupFilter.filter(o.getDirectSupertypes()); + } + }; + return Iterators.mapOver(Iterators.recur(this, typeGetter), FieldGetterInstance); + } + + /** + * returns an iterator through all of the methods of this type, in order for checking from JVM spec 2ed 5.4.3.3. This means that + * the order is + * <p/> + * <ul> + * <li>methods from current class</li> + * <li>recur into superclass, all the way up, not touching interfaces</li> + * <li>recur into all superinterfaces, in some unspecified order (but those 'closest' to this type are first)</li> + * </ul> + * <p/> + * + * @param wantGenerics is true if the caller would like all generics information, otherwise those methods are collapsed to their + * erasure + */ + public Iterator<ResolvedMember> getMethods(boolean wantGenerics, boolean wantDeclaredParents) { + return Iterators.mapOver(getHierarchy(wantGenerics, wantDeclaredParents), MethodGetterInstance); + } + + public Iterator<ResolvedMember> getMethodsIncludingIntertypeDeclarations(boolean wantGenerics, boolean wantDeclaredParents) { + return Iterators.mapOver(getHierarchy(wantGenerics, wantDeclaredParents), MethodGetterWithItdsInstance); + } + + /** + * An Iterators.Getter that returns an iterator over all methods declared on some resolved type. + */ + private static class MethodGetter implements Iterators.Getter<ResolvedType, ResolvedMember> { + @Override + public Iterator<ResolvedMember> get(ResolvedType type) { + return Iterators.array(type.getDeclaredMethods()); + } + } + + /** + * An Iterators.Getter that returns an iterator over all pointcuts declared on some resolved type. + */ + private static class PointcutGetter implements Iterators.Getter<ResolvedType, ResolvedMember> { + @Override + public Iterator<ResolvedMember> get(ResolvedType o) { + return Iterators.array(o.getDeclaredPointcuts()); + } + } + + // OPTIMIZE could cache the result of discovering ITDs + + // Getter that returns all declared methods for a type through an iterator - including intertype declarations + private static class MethodGetterIncludingItds implements Iterators.Getter<ResolvedType, ResolvedMember> { + @Override + public Iterator<ResolvedMember> get(ResolvedType type) { + ResolvedMember[] methods = type.getDeclaredMethods(); + if (type.interTypeMungers != null) { + int additional = 0; + for (ConcreteTypeMunger typeTransformer : type.interTypeMungers) { + ResolvedMember rm = typeTransformer.getSignature(); + // BUG won't this include fields? When we are looking for methods + if (rm != null) { // new parent type munger can have null signature + additional++; + } + } + if (additional > 0) { + ResolvedMember[] methods2 = new ResolvedMember[methods.length + additional]; + System.arraycopy(methods, 0, methods2, 0, methods.length); + additional = methods.length; + for (ConcreteTypeMunger typeTransformer : type.interTypeMungers) { + ResolvedMember rm = typeTransformer.getSignature(); + if (rm != null) { // new parent type munger can have null signature + methods2[additional++] = typeTransformer.getSignature(); + } + } + methods = methods2; + } + } + return Iterators.array(methods); + } + } + + /** + * An Iterators.Getter that returns an iterator over all fields declared on some resolved type. + */ + private static class FieldGetter implements Iterators.Getter<ResolvedType, ResolvedMember> { + @Override + public Iterator<ResolvedMember> get(ResolvedType type) { + return Iterators.array(type.getDeclaredFields()); + } + } + + private final static MethodGetter MethodGetterInstance = new MethodGetter(); + private final static MethodGetterIncludingItds MethodGetterWithItdsInstance = new MethodGetterIncludingItds(); + private final static PointcutGetter PointcutGetterInstance = new PointcutGetter(); + private final static FieldGetter FieldGetterInstance = new FieldGetter(); + + /** + * Return an iterator over the types in this types hierarchy - starting with this type first, then all superclasses up to Object + * and then all interfaces (starting with those 'nearest' this type). + * + * @param wantGenerics true if the caller wants full generic information + * @param wantDeclaredParents true if the caller even wants those parents introduced via declare parents + * @return an iterator over all types in the hierarchy of this type + */ + public Iterator<ResolvedType> getHierarchy() { + return getHierarchy(false, false); + } + + public Iterator<ResolvedType> getHierarchy(final boolean wantGenerics, final boolean wantDeclaredParents) { + + final Iterators.Getter<ResolvedType, ResolvedType> interfaceGetter = new Iterators.Getter<ResolvedType, ResolvedType>() { + List<String> alreadySeen = new ArrayList<String>(); // Strings are signatures (ResolvedType.getSignature()) + + @Override + public Iterator<ResolvedType> get(ResolvedType type) { + ResolvedType[] interfaces = type.getDeclaredInterfaces(); + + // remove interfaces introduced by declare parents + // relatively expensive but hopefully uncommon + if (!wantDeclaredParents && type.hasNewParentMungers()) { + // Throw away interfaces from that array if they were decp'd onto here + List<Integer> forRemoval = new ArrayList<Integer>(); + for (ConcreteTypeMunger munger : type.interTypeMungers) { + if (munger.getMunger() != null) { + ResolvedTypeMunger m = munger.getMunger(); + if (m.getKind() == ResolvedTypeMunger.Parent) { + ResolvedType newType = ((NewParentTypeMunger) m).getNewParent(); + if (!wantGenerics && newType.isParameterizedOrGenericType()) { + newType = newType.getRawType(); + } + for (int ii = 0; ii < interfaces.length; ii++) { + ResolvedType iface = interfaces[ii]; + if (!wantGenerics && iface.isParameterizedOrGenericType()) { + iface = iface.getRawType(); + } + if (newType.getSignature().equals(iface.getSignature())) { // pr171953 + forRemoval.add(ii); + } + } + } + } + } + // Found some to remove from those we are going to iterate over + if (forRemoval.size() > 0) { + ResolvedType[] interfaces2 = new ResolvedType[interfaces.length - forRemoval.size()]; + int p = 0; + for (int ii = 0; ii < interfaces.length; ii++) { + if (!forRemoval.contains(ii)) { + interfaces2[p++] = interfaces[ii]; + } + } + interfaces = interfaces2; + } + } + return new Iterators.ResolvedTypeArrayIterator(interfaces, alreadySeen, wantGenerics); + } + }; + + // If this type is an interface, there are only interfaces to walk + if (this.isInterface()) { + return new SuperInterfaceWalker(interfaceGetter, this); + } else { + SuperInterfaceWalker superInterfaceWalker = new SuperInterfaceWalker(interfaceGetter); + Iterator<ResolvedType> superClassesIterator = new SuperClassWalker(this, superInterfaceWalker, wantGenerics); + // append() will check if the second iterator is empty before appending - but the types which the superInterfaceWalker + // needs to visit are only accumulated whilst the superClassesIterator is in progress + return Iterators.append1(superClassesIterator, superInterfaceWalker); + } + } + + /** + * Return a list of methods, first those declared on this class, then those declared on the superclass (recurse) and then those + * declared on the superinterfaces. This is expensive - use the getMethods() method if you can! + */ + public List<ResolvedMember> getMethodsWithoutIterator(boolean includeITDs, boolean allowMissing, boolean genericsAware) { + List<ResolvedMember> methods = new ArrayList<ResolvedMember>(); + Set<String> knowninterfaces = new HashSet<String>(); + addAndRecurse(knowninterfaces, methods, this, includeITDs, allowMissing, genericsAware); + return methods; + } + + /** + * Return a list of the types in the hierarchy of this type, starting with this type. The order in the list is the superclasses + * followed by the super interfaces. + * + * @param genericsAware should the list include parameterized/generic types (if not, they will be collapsed to raw)? + * @return list of resolvedtypes in this types hierarchy, including this type first + */ + public List<ResolvedType> getHierarchyWithoutIterator(boolean includeITDs, boolean allowMissing, boolean genericsAware) { + List<ResolvedType> types = new ArrayList<ResolvedType>(); + Set<String> visited = new HashSet<String>(); + recurseHierarchy(visited, types, this, includeITDs, allowMissing, genericsAware); + return types; + } + + private void addAndRecurse(Set<String> knowninterfaces, List<ResolvedMember> collector, ResolvedType resolvedType, + boolean includeITDs, boolean allowMissing, boolean genericsAware) { + // Add the methods declared on this type + collector.addAll(Arrays.asList(resolvedType.getDeclaredMethods())); + // now add all the inter-typed members too + if (includeITDs && resolvedType.interTypeMungers != null) { + for (ConcreteTypeMunger typeTransformer : interTypeMungers) { + ResolvedMember rm = typeTransformer.getSignature(); + if (rm != null) { // new parent type munger can have null signature + collector.add(typeTransformer.getSignature()); + } + } + } + // BUG? interface type superclass is Object - is that correct? + if (!resolvedType.isInterface() && !resolvedType.equals(ResolvedType.OBJECT)) { + ResolvedType superType = resolvedType.getSuperclass(); + if (superType != null && !superType.isMissing()) { + if (!genericsAware && superType.isParameterizedOrGenericType()) { + superType = superType.getRawType(); + } + // Recurse if we are not at the top + addAndRecurse(knowninterfaces, collector, superType, includeITDs, allowMissing, genericsAware); + } + } + // Go through the interfaces on the way back down + ResolvedType[] interfaces = resolvedType.getDeclaredInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + ResolvedType iface = interfaces[i]; + if (!genericsAware && iface.isParameterizedOrGenericType()) { + iface = iface.getRawType(); + } + // we need to know if it is an interface from Parent kind munger + // as those are used for @AJ ITD and we precisely want to skip those + boolean shouldSkip = false; + for (int j = 0; j < resolvedType.interTypeMungers.size(); j++) { + ConcreteTypeMunger munger = resolvedType.interTypeMungers.get(j); + if (munger.getMunger() != null && munger.getMunger().getKind() == ResolvedTypeMunger.Parent + && ((NewParentTypeMunger) munger.getMunger()).getNewParent().equals(iface) // pr171953 + ) { + shouldSkip = true; + break; + } + } + + // Do not do interfaces more than once + if (!shouldSkip && !knowninterfaces.contains(iface.getSignature())) { + knowninterfaces.add(iface.getSignature()); + if (allowMissing && iface.isMissing()) { + if (iface instanceof MissingResolvedTypeWithKnownSignature) { + ((MissingResolvedTypeWithKnownSignature) iface).raiseWarningOnMissingInterfaceWhilstFindingMethods(); + } + } else { + addAndRecurse(knowninterfaces, collector, iface, includeITDs, allowMissing, genericsAware); + } + } + } + } + + /** + * Recurse up a type hierarchy, first the superclasses then the super interfaces. + */ + private void recurseHierarchy(Set<String> knowninterfaces, List<ResolvedType> collector, ResolvedType resolvedType, + boolean includeITDs, boolean allowMissing, boolean genericsAware) { + collector.add(resolvedType); + if (!resolvedType.isInterface() && !resolvedType.equals(ResolvedType.OBJECT)) { + ResolvedType superType = resolvedType.getSuperclass(); + if (superType != null && !superType.isMissing()) { + if (!genericsAware && (superType.isParameterizedType() || superType.isGenericType())) { + superType = superType.getRawType(); + } + // Recurse if we are not at the top + recurseHierarchy(knowninterfaces, collector, superType, includeITDs, allowMissing, genericsAware); + } + } + // Go through the interfaces on the way back down + ResolvedType[] interfaces = resolvedType.getDeclaredInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + ResolvedType iface = interfaces[i]; + if (!genericsAware && (iface.isParameterizedType() || iface.isGenericType())) { + iface = iface.getRawType(); + } + // we need to know if it is an interface from Parent kind munger + // as those are used for @AJ ITD and we precisely want to skip those + boolean shouldSkip = false; + for (int j = 0; j < resolvedType.interTypeMungers.size(); j++) { + ConcreteTypeMunger munger = resolvedType.interTypeMungers.get(j); + if (munger.getMunger() != null && munger.getMunger().getKind() == ResolvedTypeMunger.Parent + && ((NewParentTypeMunger) munger.getMunger()).getNewParent().equals(iface) // pr171953 + ) { + shouldSkip = true; + break; + } + } + + // Do not do interfaces more than once + if (!shouldSkip && !knowninterfaces.contains(iface.getSignature())) { + knowninterfaces.add(iface.getSignature()); + if (allowMissing && iface.isMissing()) { + if (iface instanceof MissingResolvedTypeWithKnownSignature) { + ((MissingResolvedTypeWithKnownSignature) iface).raiseWarningOnMissingInterfaceWhilstFindingMethods(); + } + } else { + recurseHierarchy(knowninterfaces, collector, iface, includeITDs, allowMissing, genericsAware); + } + } + } + } + + public ResolvedType[] getResolvedTypeParameters() { + if (resolvedTypeParams == null) { + resolvedTypeParams = world.resolve(typeParameters); + } + return resolvedTypeParams; + } + + /** + * described in JVM spec 2ed 5.4.3.2 + */ + public ResolvedMember lookupField(Member field) { + Iterator<ResolvedMember> i = getFields(); + while (i.hasNext()) { + ResolvedMember resolvedMember = i.next(); + if (matches(resolvedMember, field)) { + return resolvedMember; + } + if (resolvedMember.hasBackingGenericMember() && field.getName().equals(resolvedMember.getName())) { + // might be worth checking the member behind the parameterized member (see pr137496) + if (matches(resolvedMember.getBackingGenericMember(), field)) { + return resolvedMember; + } + } + } + return null; + } + + /** + * described in JVM spec 2ed 5.4.3.3. Doesnt check ITDs. + * + * <p> + * Check the current type for the method. If it is not found, check the super class and any super interfaces. Taking care not to + * process interfaces multiple times. + */ + public ResolvedMember lookupMethod(Member m) { + List<ResolvedType> typesTolookat = new ArrayList<ResolvedType>(); + typesTolookat.add(this); + int pos = 0; + while (pos < typesTolookat.size()) { + ResolvedType type = typesTolookat.get(pos++); + if (!type.isMissing()) { + ResolvedMember[] methods = type.getDeclaredMethods(); + if (methods != null) { + for (int i = 0; i < methods.length; i++) { + ResolvedMember method = methods[i]; + if (matches(method, m)) { + return method; + } + // might be worth checking the method behind the parameterized method (137496) + if (method.hasBackingGenericMember() && m.getName().equals(method.getName())) { + if (matches(method.getBackingGenericMember(), m)) { + return method; + } + } + } + } + } + // Queue the superclass: + ResolvedType superclass = type.getSuperclass(); + if (superclass != null) { + typesTolookat.add(superclass); + } + // Queue any interfaces not already checked: + ResolvedType[] superinterfaces = type.getDeclaredInterfaces(); + if (superinterfaces != null) { + for (int i = 0; i < superinterfaces.length; i++) { + ResolvedType interf = superinterfaces[i]; + if (!typesTolookat.contains(interf)) { + typesTolookat.add(interf); + } + } + } + } + return null; + } + + /** + * @param member the member to lookup in intertype declarations affecting this type + * @return the real signature defined by any matching intertype declaration, otherwise null + */ + public ResolvedMember lookupMethodInITDs(Member member) { + for (ConcreteTypeMunger typeTransformer : interTypeMungers) { + if (matches(typeTransformer.getSignature(), member)) { + return typeTransformer.getSignature(); + } + } + return null; + } + + /** + * return null if not found + */ + private ResolvedMember lookupMember(Member m, ResolvedMember[] a) { + for (int i = 0; i < a.length; i++) { + ResolvedMember f = a[i]; + if (matches(f, m)) { + return f; + } + } + return null; + } + + // Bug (1) Do callers expect ITDs to be involved in the lookup? or do they do their own walk over ITDs? + /** + * Looks for the first member in the hierarchy matching aMember. This method differs from lookupMember(Member) in that it takes + * into account parameters which are type variables - which clearly an unresolved Member cannot do since it does not know + * anything about type variables. + */ + public ResolvedMember lookupResolvedMember(ResolvedMember aMember, boolean allowMissing, boolean eraseGenerics) { + Iterator<ResolvedMember> toSearch = null; + ResolvedMember found = null; + if ((aMember.getKind() == Member.METHOD) || (aMember.getKind() == Member.CONSTRUCTOR)) { + // toSearch = getMethodsWithoutIterator(true, allowMissing, !eraseGenerics).iterator(); + toSearch = getMethodsIncludingIntertypeDeclarations(!eraseGenerics, true); + } else if (aMember.getKind()==Member.ADVICE) { + return null; + } else { + assert aMember.getKind() == Member.FIELD; + toSearch = getFields(); + } + while (toSearch.hasNext()) { + ResolvedMember candidate = toSearch.next(); + if (eraseGenerics) { + if (candidate.hasBackingGenericMember()) { + candidate = candidate.getBackingGenericMember(); + } + } + // OPTIMIZE speed up matches? optimize order of checks + if (candidate.matches(aMember, eraseGenerics)) { + found = candidate; + break; + } + } + + return found; + } + + public static boolean matches(Member m1, Member m2) { + if (m1 == null) { + return m2 == null; + } + if (m2 == null) { + return false; + } + + // Check the names + boolean equalNames = m1.getName().equals(m2.getName()); + if (!equalNames) { + return false; + } + + // Check the signatures + boolean equalSignatures = m1.getSignature().equals(m2.getSignature()); + if (equalSignatures) { + return true; + } + + // If they aren't the same, we need to allow for covariance ... where + // one sig might be ()LCar; and + // the subsig might be ()LFastCar; - where FastCar is a subclass of Car + boolean equalCovariantSignatures = m1.getParameterSignature().equals(m2.getParameterSignature()); + if (equalCovariantSignatures) { + return true; + } + + return false; + } + public static boolean conflictingSignature(Member m1, Member m2) { + return conflictingSignature(m1,m2,true); + } + + /** + * Do the two members conflict? Due to the change in 1.7.1, field itds on interfaces now act like 'default' fields - so types implementing + * those fields get the field if they don't have it already, otherwise they keep what they have. The conflict detection below had to be + * altered. Previously (<1.7.1) it is not a conflict if the declaring types are different. With v2itds it may still be a conflict if the + * declaring types are different. + */ + public static boolean conflictingSignature(Member m1, Member m2, boolean v2itds) { + if (m1 == null || m2 == null) { + return false; + } + if (!m1.getName().equals(m2.getName())) { + return false; + } + if (m1.getKind() != m2.getKind()) { + return false; + } + if (m1.getKind() == Member.FIELD) { + if (v2itds) { + if (m1.getDeclaringType().equals(m2.getDeclaringType())) { + return true; + } + } else { + return m1.getDeclaringType().equals(m2.getDeclaringType()); + } + } else if (m1.getKind() == Member.POINTCUT) { + return true; + } + + UnresolvedType[] p1 = m1.getGenericParameterTypes(); + UnresolvedType[] p2 = m2.getGenericParameterTypes(); + if (p1 == null) { + p1 = m1.getParameterTypes(); + } + if (p2 == null) { + p2 = m2.getParameterTypes(); + } + int n = p1.length; + if (n != p2.length) { + return false; + } + + for (int i = 0; i < n; i++) { + if (!p1[i].equals(p2[i])) { + return false; + } + } + return true; + } + + /** + * returns an iterator through all of the pointcuts of this type, in order for checking from JVM spec 2ed 5.4.3.2 (as for + * fields). This means that the order is + * <p/> + * <ul> + * <li>pointcuts from current class</li> + * <li>recur into direct superinterfaces</li> + * <li>recur into superclass</li> + * </ul> + * <p/> + * We keep a hashSet of interfaces that we've visited so we don't spiral out into 2^n land. + */ + public Iterator<ResolvedMember> getPointcuts() { + final Iterators.Filter<ResolvedType> dupFilter = Iterators.dupFilter(); + // same order as fields + Iterators.Getter<ResolvedType, ResolvedType> typeGetter = new Iterators.Getter<ResolvedType, ResolvedType>() { + @Override + public Iterator<ResolvedType> get(ResolvedType o) { + return dupFilter.filter(o.getDirectSupertypes()); + } + }; + return Iterators.mapOver(Iterators.recur(this, typeGetter), PointcutGetterInstance); + } + + public ResolvedPointcutDefinition findPointcut(String name) { + for (Iterator<ResolvedMember> i = getPointcuts(); i.hasNext();) { + ResolvedPointcutDefinition f = (ResolvedPointcutDefinition) i.next(); + // the ResolvedPointcutDefinition can be null if there are other problems that prevented its resolution + if (f != null && name.equals(f.getName())) { + return f; + } + } + // pr120521 + if (!getOutermostType().equals(this)) { + ResolvedType outerType = getOutermostType().resolve(world); + ResolvedPointcutDefinition rpd = outerType.findPointcut(name); + return rpd; + } + return null; // should we throw an exception here? + } + + // all about collecting CrosscuttingMembers + + // ??? collecting data-structure, shouldn't really be a field + public CrosscuttingMembers crosscuttingMembers; + + public CrosscuttingMembers collectCrosscuttingMembers(boolean shouldConcretizeIfNeeded) { + crosscuttingMembers = new CrosscuttingMembers(this, shouldConcretizeIfNeeded); + if (getPerClause() == null) { + return crosscuttingMembers; + } + crosscuttingMembers.setPerClause(getPerClause()); + crosscuttingMembers.addShadowMungers(collectShadowMungers()); + // GENERICITDFIX + // crosscuttingMembers.addTypeMungers(collectTypeMungers()); + crosscuttingMembers.addTypeMungers(getTypeMungers()); + // FIXME AV - skip but needed ?? or ?? + // crosscuttingMembers.addLateTypeMungers(getLateTypeMungers()); + crosscuttingMembers.addDeclares(collectDeclares(!this.doesNotExposeShadowMungers())); + crosscuttingMembers.addPrivilegedAccesses(getPrivilegedAccesses()); + + // System.err.println("collected cc members: " + this + ", " + + // collectDeclares()); + return crosscuttingMembers; + } + + public final List<Declare> collectDeclares(boolean includeAdviceLike) { + if (!this.isAspect()) { + return Collections.emptyList(); + } + + List<Declare> ret = new ArrayList<Declare>(); + // if (this.isAbstract()) { + // for (Iterator i = getDeclares().iterator(); i.hasNext();) { + // Declare dec = (Declare) i.next(); + // if (!dec.isAdviceLike()) ret.add(dec); + // } + // + // if (!includeAdviceLike) return ret; + + if (!this.isAbstract()) { + // ret.addAll(getDeclares()); + final Iterators.Filter<ResolvedType> dupFilter = Iterators.dupFilter(); + Iterators.Getter<ResolvedType, ResolvedType> typeGetter = new Iterators.Getter<ResolvedType, ResolvedType>() { + @Override + public Iterator<ResolvedType> get(ResolvedType o) { + return dupFilter.filter((o).getDirectSupertypes()); + } + }; + Iterator<ResolvedType> typeIterator = Iterators.recur(this, typeGetter); + + while (typeIterator.hasNext()) { + ResolvedType ty = typeIterator.next(); + // System.out.println("super: " + ty + ", " + ); + for (Iterator<Declare> i = ty.getDeclares().iterator(); i.hasNext();) { + Declare dec = i.next(); + if (dec.isAdviceLike()) { + if (includeAdviceLike) { + ret.add(dec); + } + } else { + ret.add(dec); + } + } + } + } + + return ret; + } + + private final List<ShadowMunger> collectShadowMungers() { + if (!this.isAspect() || this.isAbstract() || this.doesNotExposeShadowMungers()) { + return Collections.emptyList(); + } + + List<ShadowMunger> acc = new ArrayList<ShadowMunger>(); + final Iterators.Filter<ResolvedType> dupFilter = Iterators.dupFilter(); + Iterators.Getter<ResolvedType, ResolvedType> typeGetter = new Iterators.Getter<ResolvedType, ResolvedType>() { + @Override + public Iterator<ResolvedType> get(ResolvedType o) { + return dupFilter.filter((o).getDirectSupertypes()); + } + }; + Iterator<ResolvedType> typeIterator = Iterators.recur(this, typeGetter); + + while (typeIterator.hasNext()) { + ResolvedType ty = typeIterator.next(); + acc.addAll(ty.getDeclaredShadowMungers()); + } + + return acc; + } + + public void addParent(ResolvedType newParent) { + // Nothing to do for anything except a ReferenceType + } + + protected boolean doesNotExposeShadowMungers() { + return false; + } + + public PerClause getPerClause() { + return null; + } + + public Collection<Declare> getDeclares() { + return Collections.emptyList(); + } + + public Collection<ConcreteTypeMunger> getTypeMungers() { + return Collections.emptyList(); + } + + public Collection<ResolvedMember> getPrivilegedAccesses() { + return Collections.emptyList(); + } + + // ---- useful things + + public final boolean isInterface() { + return Modifier.isInterface(getModifiers()); + } + + public final boolean isAbstract() { + return Modifier.isAbstract(getModifiers()); + } + + public boolean isClass() { + return false; + } + + public boolean isAspect() { + return false; + } + + public boolean isAnnotationStyleAspect() { + return false; + } + + /** + * Note: Only overridden by Name subtype. + */ + public boolean isEnum() { + return false; + } + + /** + * Note: Only overridden by Name subtype. + */ + public boolean isAnnotation() { + return false; + } + + public boolean isAnonymous() { + return false; + } + + public boolean isNested() { + return false; + } + + public ResolvedType getOuterClass() { + return null; + } + + public void addAnnotation(AnnotationAJ annotationX) { + throw new RuntimeException("ResolvedType.addAnnotation() should never be called"); + } + + public AnnotationAJ[] getAnnotations() { + throw new RuntimeException("ResolvedType.getAnnotations() should never be called"); + } + + public boolean hasAnnotations() { + throw new RuntimeException("ResolvedType.getAnnotations() should never be called"); + } + + + /** + * Note: Only overridden by ReferenceType subtype + */ + public boolean canAnnotationTargetType() { + return false; + } + + /** + * Note: Only overridden by ReferenceType subtype + */ + public AnnotationTargetKind[] getAnnotationTargetKinds() { + return null; + } + + /** + * Note: Only overridden by Name subtype. + */ + public boolean isAnnotationWithRuntimeRetention() { + return false; + } + + public boolean isSynthetic() { + return signature.indexOf("$ajc") != -1; + } + + public final boolean isFinal() { + return Modifier.isFinal(getModifiers()); + } + + protected Map<String, UnresolvedType> getMemberParameterizationMap() { + if (!isParameterizedType()) { + return Collections.emptyMap(); + } + TypeVariable[] tvs = getGenericType().getTypeVariables(); + Map<String, UnresolvedType> parameterizationMap = new HashMap<String, UnresolvedType>(); + if (tvs.length != typeParameters.length) { + world.getMessageHandler() + .handleMessage( + new Message("Mismatch when building parameterization map. For type '" + this.signature + + "' expecting "+tvs.length+":["+toString(tvs)+"] type parameters but found "+typeParameters.length+ + ":["+toString(typeParameters)+"]", "", + IMessage.ERROR, getSourceLocation(), null, + new ISourceLocation[] { getSourceLocation() })); + } else { + for (int i = 0; i < tvs.length; i++) { + parameterizationMap.put(tvs[i].getName(), typeParameters[i]); + } + } + return parameterizationMap; + } + + private String toString(UnresolvedType[] typeParameters) { + StringBuilder s = new StringBuilder(); + for (UnresolvedType tv: typeParameters) { + s.append(tv.getSignature()).append(" "); + } + return s.toString().trim(); + } + + private String toString(TypeVariable[] tvs) { + StringBuilder s = new StringBuilder(); + for (TypeVariable tv: tvs) { + s.append(tv.getName()).append(" "); + } + return s.toString().trim(); + } + + public List<ShadowMunger> getDeclaredAdvice() { + List<ShadowMunger> l = new ArrayList<ShadowMunger>(); + ResolvedMember[] methods = getDeclaredMethods(); + if (isParameterizedType()) { + methods = getGenericType().getDeclaredMethods(); + } + Map<String, UnresolvedType> typeVariableMap = getAjMemberParameterizationMap(); + for (int i = 0, len = methods.length; i < len; i++) { + ShadowMunger munger = methods[i].getAssociatedShadowMunger(); + if (munger != null) { + if (ajMembersNeedParameterization()) { + // munger.setPointcut(munger.getPointcut().parameterizeWith( + // typeVariableMap)); + munger = munger.parameterizeWith(this, typeVariableMap); + if (munger instanceof Advice) { + Advice advice = (Advice) munger; + // update to use the parameterized signature... + UnresolvedType[] ptypes = methods[i].getGenericParameterTypes(); + UnresolvedType[] newPTypes = new UnresolvedType[ptypes.length]; + for (int j = 0; j < ptypes.length; j++) { + if (ptypes[j] instanceof TypeVariableReferenceType) { + TypeVariableReferenceType tvrt = (TypeVariableReferenceType) ptypes[j]; + if (typeVariableMap.containsKey(tvrt.getTypeVariable().getName())) { + newPTypes[j] = typeVariableMap.get(tvrt.getTypeVariable().getName()); + } else { + newPTypes[j] = ptypes[j]; + } + } else { + newPTypes[j] = ptypes[j]; + } + } + advice.setBindingParameterTypes(newPTypes); + } + } + munger.setDeclaringType(this); + l.add(munger); + } + } + return l; + } + + public List<ShadowMunger> getDeclaredShadowMungers() { + return getDeclaredAdvice(); + } + + // ---- only for testing! + + public ResolvedMember[] getDeclaredJavaFields() { + return filterInJavaVisible(getDeclaredFields()); + } + + public ResolvedMember[] getDeclaredJavaMethods() { + return filterInJavaVisible(getDeclaredMethods()); + } + + private ResolvedMember[] filterInJavaVisible(ResolvedMember[] ms) { + List<ResolvedMember> l = new ArrayList<ResolvedMember>(); + for (int i = 0, len = ms.length; i < len; i++) { + if (!ms[i].isAjSynthetic() && ms[i].getAssociatedShadowMunger() == null) { + l.add(ms[i]); + } + } + return l.toArray(new ResolvedMember[l.size()]); + } + + public abstract ISourceContext getSourceContext(); + + // ---- fields + + public static final ResolvedType[] NONE = new ResolvedType[0]; + public static final ResolvedType[] EMPTY_ARRAY = NONE; + + public static final Missing MISSING = new Missing(); + + // ---- types + public static ResolvedType makeArray(ResolvedType type, int dim) { + if (dim == 0) { + return type; + } + ResolvedType array = new ArrayReferenceType("[" + type.getSignature(), "[" + type.getErasureSignature(), type.getWorld(), + type); + return makeArray(array, dim - 1); + } + + static class Primitive extends ResolvedType { + private final int size; + private final int index; + + Primitive(String signature, int size, int index) { + super(signature, null); + this.size = size; + this.index = index; + this.typeKind = TypeKind.PRIMITIVE; + } + + @Override + public final int getSize() { + return size; + } + + @Override + public final int getModifiers() { + return Modifier.PUBLIC | Modifier.FINAL; + } + + @Override + public final boolean isPrimitiveType() { + return true; + } + + @Override + public boolean hasAnnotation(UnresolvedType ofType) { + return false; + } + + @Override + public final boolean isAssignableFrom(ResolvedType other) { + if (!other.isPrimitiveType()) { + if (!world.isInJava5Mode()) { + return false; + } + return validBoxing.contains(this.getSignature() + other.getSignature()); + } + return assignTable[((Primitive) other).index][index]; + } + + @Override + public final boolean isAssignableFrom(ResolvedType other, boolean allowMissing) { + return isAssignableFrom(other); + } + + @Override + public final boolean isCoerceableFrom(ResolvedType other) { + if (this == other) { + return true; + } + if (!other.isPrimitiveType()) { + return false; + } + if (index > 6 || ((Primitive) other).index > 6) { + return false; + } + return true; + } + + @Override + public ResolvedType resolve(World world) { + if (this.world != world) { + throw new IllegalStateException(); + } + this.world = world; + return super.resolve(world); + } + + @Override + public final boolean needsNoConversionFrom(ResolvedType other) { + if (!other.isPrimitiveType()) { + return false; + } + return noConvertTable[((Primitive) other).index][index]; + } + + private static final boolean[][] assignTable = {// to: B C D F I J S V Z + // from + { true, true, true, true, true, true, true, false, false }, // B + { false, true, true, true, true, true, false, false, false }, // C + { false, false, true, false, false, false, false, false, false }, // D + { false, false, true, true, false, false, false, false, false }, // F + { false, false, true, true, true, true, false, false, false }, // I + { false, false, true, true, false, true, false, false, false }, // J + { false, false, true, true, true, true, true, false, false }, // S + { false, false, false, false, false, false, false, true, false }, // V + { false, false, false, false, false, false, false, false, true }, // Z + }; + private static final boolean[][] noConvertTable = {// to: B C D F I J S + // V Z from + { true, true, false, false, true, false, true, false, false }, // B + { false, true, false, false, true, false, false, false, false }, // C + { false, false, true, false, false, false, false, false, false }, // D + { false, false, false, true, false, false, false, false, false }, // F + { false, false, false, false, true, false, false, false, false }, // I + { false, false, false, false, false, true, false, false, false }, // J + { false, false, false, false, true, false, true, false, false }, // S + { false, false, false, false, false, false, false, true, false }, // V + { false, false, false, false, false, false, false, false, true }, // Z + }; + + // ---- + + @Override + public final ResolvedMember[] getDeclaredFields() { + return ResolvedMember.NONE; + } + + @Override + public final ResolvedMember[] getDeclaredMethods() { + return ResolvedMember.NONE; + } + + @Override + public final ResolvedType[] getDeclaredInterfaces() { + return ResolvedType.NONE; + } + + @Override + public final ResolvedMember[] getDeclaredPointcuts() { + return ResolvedMember.NONE; + } + + @Override + public final ResolvedType getSuperclass() { + return null; + } + + @Override + public ISourceContext getSourceContext() { + return null; + } + + } + + static class Missing extends ResolvedType { + Missing() { + super(MISSING_NAME, null); + } + + // public final String toString() { + // return "<missing>"; + // } + @Override + public final String getName() { + return MISSING_NAME; + } + + @Override + public final boolean isMissing() { + return true; + } + + @Override + public boolean hasAnnotation(UnresolvedType ofType) { + return false; + } + + @Override + public final ResolvedMember[] getDeclaredFields() { + return ResolvedMember.NONE; + } + + @Override + public final ResolvedMember[] getDeclaredMethods() { + return ResolvedMember.NONE; + } + + @Override + public final ResolvedType[] getDeclaredInterfaces() { + return ResolvedType.NONE; + } + + @Override + public final ResolvedMember[] getDeclaredPointcuts() { + return ResolvedMember.NONE; + } + + @Override + public final ResolvedType getSuperclass() { + return null; + } + + @Override + public final int getModifiers() { + return 0; + } + + @Override + public final boolean isAssignableFrom(ResolvedType other) { + return false; + } + + @Override + public final boolean isAssignableFrom(ResolvedType other, boolean allowMissing) { + return false; + } + + @Override + public final boolean isCoerceableFrom(ResolvedType other) { + return false; + } + + @Override + public boolean needsNoConversionFrom(ResolvedType other) { + return false; + } + + @Override + public ISourceContext getSourceContext() { + return null; + } + + } + + /** + * Look up a member, takes into account any ITDs on this type. return null if not found + */ + public ResolvedMember lookupMemberNoSupers(Member member) { + ResolvedMember ret = lookupDirectlyDeclaredMemberNoSupers(member); + if (ret == null && interTypeMungers != null) { + for (ConcreteTypeMunger tm : interTypeMungers) { + if (matches(tm.getSignature(), member)) { + return tm.getSignature(); + } + } + } + return ret; + } + + public ResolvedMember lookupMemberWithSupersAndITDs(Member member) { + ResolvedMember ret = lookupMemberNoSupers(member); + if (ret != null) { + return ret; + } + + ResolvedType supert = getSuperclass(); + while (ret == null && supert != null) { + ret = supert.lookupMemberNoSupers(member); + if (ret == null) { + supert = supert.getSuperclass(); + } + } + + return ret; + } + + /** + * as lookupMemberNoSupers, but does not include ITDs + * + * @param member + * @return + */ + public ResolvedMember lookupDirectlyDeclaredMemberNoSupers(Member member) { + ResolvedMember ret; + if (member.getKind() == Member.FIELD) { + ret = lookupMember(member, getDeclaredFields()); + } else { + // assert member.getKind() == Member.METHOD || member.getKind() == + // Member.CONSTRUCTOR + ret = lookupMember(member, getDeclaredMethods()); + } + return ret; + } + + /** + * This lookup has specialized behaviour - a null result tells the EclipseTypeMunger that it should make a default + * implementation of a method on this type. + * + * @param member + * @return + */ + public ResolvedMember lookupMemberIncludingITDsOnInterfaces(Member member) { + return lookupMemberIncludingITDsOnInterfaces(member, this); + } + + private ResolvedMember lookupMemberIncludingITDsOnInterfaces(Member member, ResolvedType onType) { + ResolvedMember ret = onType.lookupMemberNoSupers(member); + if (ret != null) { + return ret; + } else { + ResolvedType superType = onType.getSuperclass(); + if (superType != null) { + ret = lookupMemberIncludingITDsOnInterfaces(member, superType); + } + if (ret == null) { + // try interfaces then, but only ITDs now... + ResolvedType[] superInterfaces = onType.getDeclaredInterfaces(); + for (int i = 0; i < superInterfaces.length; i++) { + ret = superInterfaces[i].lookupMethodInITDs(member); + if (ret != null) { + return ret; + } + } + } + } + return ret; + } + + protected List<ConcreteTypeMunger> interTypeMungers = new ArrayList<ConcreteTypeMunger>(); + + public List<ConcreteTypeMunger> getInterTypeMungers() { + return interTypeMungers; + } + + public List<ConcreteTypeMunger> getInterTypeParentMungers() { + List<ConcreteTypeMunger> l = new ArrayList<ConcreteTypeMunger>(); + for (ConcreteTypeMunger element : interTypeMungers) { + if (element.getMunger() instanceof NewParentTypeMunger) { + l.add(element); + } + } + return l; + } + + /** + * ??? This method is O(N*M) where N = number of methods and M is number of inter-type declarations in my super + */ + public List<ConcreteTypeMunger> getInterTypeMungersIncludingSupers() { + ArrayList<ConcreteTypeMunger> ret = new ArrayList<ConcreteTypeMunger>(); + collectInterTypeMungers(ret); + return ret; + } + + public List<ConcreteTypeMunger> getInterTypeParentMungersIncludingSupers() { + ArrayList<ConcreteTypeMunger> ret = new ArrayList<ConcreteTypeMunger>(); + collectInterTypeParentMungers(ret); + return ret; + } + + private void collectInterTypeParentMungers(List<ConcreteTypeMunger> collector) { + for (Iterator<ResolvedType> iter = getDirectSupertypes(); iter.hasNext();) { + ResolvedType superType = iter.next(); + superType.collectInterTypeParentMungers(collector); + } + collector.addAll(getInterTypeParentMungers()); + } + + protected void collectInterTypeMungers(List<ConcreteTypeMunger> collector) { + for (Iterator<ResolvedType> iter = getDirectSupertypes(); iter.hasNext();) { + ResolvedType superType = iter.next(); + if (superType == null) { + throw new BCException("UnexpectedProblem: a supertype in the hierarchy for " + this.getName() + " is null"); + } + superType.collectInterTypeMungers(collector); + } + + outer: for (Iterator<ConcreteTypeMunger> iter1 = collector.iterator(); iter1.hasNext();) { + ConcreteTypeMunger superMunger = iter1.next(); + if (superMunger.getSignature() == null) { + continue; + } + + if (!superMunger.getSignature().isAbstract()) { + continue; + } + + for (ConcreteTypeMunger myMunger : getInterTypeMungers()) { + if (conflictingSignature(myMunger.getSignature(), superMunger.getSignature())) { + iter1.remove(); + continue outer; + } + } + + if (!superMunger.getSignature().isPublic()) { + continue; + } + + for (Iterator<ResolvedMember> iter = getMethods(true, true); iter.hasNext();) { + ResolvedMember method = iter.next(); + if (conflictingSignature(method, superMunger.getSignature())) { + iter1.remove(); + continue outer; + } + } + } + + collector.addAll(getInterTypeMungers()); + } + + /** + * Check: 1) That we don't have any abstract type mungers unless this type is abstract. 2) That an abstract ITDM on an interface + * is declared public. (Compiler limitation) (PR70794) + */ + public void checkInterTypeMungers() { + if (isAbstract()) { + return; + } + + boolean itdProblem = false; + + for (ConcreteTypeMunger munger : getInterTypeMungersIncludingSupers()) { + itdProblem = checkAbstractDeclaration(munger) || itdProblem; // Rule 2 + } + + if (itdProblem) { + return; // If the rules above are broken, return right now + } + + for (ConcreteTypeMunger munger : getInterTypeMungersIncludingSupers()) { + if (munger.getSignature() != null && munger.getSignature().isAbstract() && munger.getMunger().getKind()!=ResolvedTypeMunger.PrivilegedAccess) { // Rule 1 + if (munger.getMunger().getKind() == ResolvedTypeMunger.MethodDelegate2) { + // ignore for @AJ ITD as munger.getSignature() is the + // interface method hence abstract + } else { + world.getMessageHandler() + .handleMessage( + new Message("must implement abstract inter-type declaration: " + munger.getSignature(), "", + IMessage.ERROR, getSourceLocation(), null, + new ISourceLocation[] { getMungerLocation(munger) })); + } + } + } + } + + /** + * See PR70794. This method checks that if an abstract inter-type method declaration is made on an interface then it must also + * be public. This is a compiler limitation that could be made to work in the future (if someone provides a worthwhile usecase) + * + * @return indicates if the munger failed the check + */ + private boolean checkAbstractDeclaration(ConcreteTypeMunger munger) { + if (munger.getMunger() != null && (munger.getMunger() instanceof NewMethodTypeMunger)) { + ResolvedMember itdMember = munger.getSignature(); + ResolvedType onType = itdMember.getDeclaringType().resolve(world); + if (onType.isInterface() && itdMember.isAbstract() && !itdMember.isPublic()) { + world.getMessageHandler().handleMessage( + new Message(WeaverMessages.format(WeaverMessages.ITD_ABSTRACT_MUST_BE_PUBLIC_ON_INTERFACE, + munger.getSignature(), onType), "", Message.ERROR, getSourceLocation(), null, + new ISourceLocation[] { getMungerLocation(munger) })); + return true; + } + } + return false; + } + + /** + * Get a source location for the munger. Until intertype mungers remember where they came from, the source location for the + * munger itself is null. In these cases use the source location for the aspect containing the ITD. + */ + private ISourceLocation getMungerLocation(ConcreteTypeMunger munger) { + ISourceLocation sloc = munger.getSourceLocation(); + if (sloc == null) { + sloc = munger.getAspectType().getSourceLocation(); + } + return sloc; + } + + /** + * Returns a ResolvedType object representing the declaring type of this type, or null if this type does not represent a + * non-package-level-type. + * <p/> + * <strong>Warning</strong>: This is guaranteed to work for all member types. For anonymous/local types, the only guarantee is + * given in JLS 13.1, where it guarantees that if you call getDeclaringType() repeatedly, you will eventually get the top-level + * class, but it does not say anything about classes in between. + * + * @return the declaring type, or null if it is not an nested type. + */ + public ResolvedType getDeclaringType() { + if (isArray()) { + return null; + } + if (isNested() || isAnonymous()) { + return getOuterClass(); + } + return null; + } + + public static boolean isVisible(int modifiers, ResolvedType targetType, ResolvedType fromType) { + // System.err.println("mod: " + modifiers + ", " + targetType + " and " + // + fromType); + + if (Modifier.isPublic(modifiers)) { + return true; + } else if (Modifier.isPrivate(modifiers)) { + return targetType.getOutermostType().equals(fromType.getOutermostType()); + } else if (Modifier.isProtected(modifiers)) { + return samePackage(targetType, fromType) || targetType.isAssignableFrom(fromType); + } else { // package-visible + return samePackage(targetType, fromType); + } + } + + private static boolean samePackage(ResolvedType targetType, ResolvedType fromType) { + String p1 = targetType.getPackageName(); + String p2 = fromType.getPackageName(); + if (p1 == null) { + return p2 == null; + } + if (p2 == null) { + return false; + } + return p1.equals(p2); + } + + /** + * Checks if the generic type for 'this' and the generic type for 'other' are the same - it can be passed raw or parameterized + * versions and will just compare the underlying generic type. + */ + private boolean genericTypeEquals(ResolvedType other) { + ResolvedType rt = other; + if (rt.isParameterizedType() || rt.isRawType()) { + rt.getGenericType(); + } + if (((isParameterizedType() || isRawType()) && getGenericType().equals(rt)) || (this.equals(other))) { + return true; + } + return false; + } + + /** + * Look up the actual occurence of a particular type in the hierarchy for 'this' type. The input is going to be a generic type, + * and the caller wants to know if it was used in its RAW or a PARAMETERIZED form in this hierarchy. + * + * returns null if it can't be found. + */ + public ResolvedType discoverActualOccurrenceOfTypeInHierarchy(ResolvedType lookingFor) { + if (!lookingFor.isGenericType()) { + throw new BCException("assertion failed: method should only be called with generic type, but " + lookingFor + " is " + + lookingFor.typeKind); + } + + if (this.equals(ResolvedType.OBJECT)) { + return null; + } + + if (genericTypeEquals(lookingFor)) { + return this; + } + + ResolvedType superT = getSuperclass(); + if (superT.genericTypeEquals(lookingFor)) { + return superT; + } + + ResolvedType[] superIs = getDeclaredInterfaces(); + for (int i = 0; i < superIs.length; i++) { + ResolvedType superI = superIs[i]; + if (superI.genericTypeEquals(lookingFor)) { + return superI; + } + ResolvedType checkTheSuperI = superI.discoverActualOccurrenceOfTypeInHierarchy(lookingFor); + if (checkTheSuperI != null) { + return checkTheSuperI; + } + } + return superT.discoverActualOccurrenceOfTypeInHierarchy(lookingFor); + } + + /** + * Called for all type mungers but only does something if they share type variables with a generic type which they target. When + * this happens this routine will check for the target type in the target hierarchy and 'bind' any type parameters as + * appropriate. For example, for the ITD "List<T> I<T>.x" against a type like this: "class A implements I<String>" this routine + * will return a parameterized form of the ITD "List<String> I.x" + */ + public ConcreteTypeMunger fillInAnyTypeParameters(ConcreteTypeMunger munger) { + boolean debug = false; + ResolvedMember member = munger.getSignature(); + if (munger.isTargetTypeParameterized()) { + if (debug) { + System.err.println("Processing attempted parameterization of " + munger + " targetting type " + this); + } + if (debug) { + System.err.println(" This type is " + this + " (" + typeKind + ")"); + } + // need to tailor this munger instance for the particular target... + if (debug) { + System.err.println(" Signature that needs parameterizing: " + member); + } + // Retrieve the generic type + ResolvedType onTypeResolved = world.resolve(member.getDeclaringType()); + ResolvedType onType = onTypeResolved.getGenericType(); + if (onType == null) { + // The target is not generic + getWorld().getMessageHandler().handleMessage( + MessageUtil.error("The target type for the intertype declaration is not generic", + munger.getSourceLocation())); + return munger; + } + member.resolve(world); // Ensure all parts of the member are resolved + if (debug) { + System.err.println(" Actual target ontype: " + onType + " (" + onType.typeKind + ")"); + } + // quickly find the targettype in the type hierarchy for this type + // (it will be either RAW or PARAMETERIZED) + ResolvedType actualTarget = discoverActualOccurrenceOfTypeInHierarchy(onType); + if (actualTarget == null) { + throw new BCException("assertion failed: asked " + this + " for occurrence of " + onType + " in its hierarchy??"); + } + + // only bind the tvars if its a parameterized type or the raw type + // (in which case they collapse to bounds) - don't do it + // for generic types ;) + if (!actualTarget.isGenericType()) { + if (debug) { + System.err.println("Occurrence in " + this + " is actually " + actualTarget + " (" + actualTarget.typeKind + + ")"); + // parameterize the signature + // ResolvedMember newOne = + // member.parameterizedWith(actualTarget.getTypeParameters(), + // onType,actualTarget.isParameterizedType()); + } + } + // if (!actualTarget.isRawType()) + munger = munger.parameterizedFor(actualTarget); + if (debug) { + System.err.println("New sig: " + munger.getSignature()); + } + + if (debug) { + System.err.println("====================================="); + } + } + return munger; + } + + /** + * Add an intertype munger to this type. isDuringCompilation tells us if we should be checking for an error scenario where two + * ITD fields are trying to use the same name. When this happens during compilation one of them is altered to get mangled name + * but when it happens during weaving it is too late and we need to put out an error asking them to recompile. + */ + public void addInterTypeMunger(ConcreteTypeMunger munger, boolean isDuringCompilation) { + ResolvedMember sig = munger.getSignature(); + bits = (bits & ~MungersAnalyzed); // clear the bit - as the mungers have changed + if (sig == null || munger.getMunger() == null || munger.getMunger().getKind() == ResolvedTypeMunger.PrivilegedAccess) { + interTypeMungers.add(munger); + return; + } + + // ConcreteTypeMunger originalMunger = munger; + // we will use the 'parameterized' ITD for all the comparisons but we + // say the original + // one passed in actually matched as it will be added to the intertype + // member finder + // for the target type. It is possible we only want to do this if a + // generic type + // is discovered and the tvar is collapsed to a bound? + munger = fillInAnyTypeParameters(munger); + sig = munger.getSignature(); // possibly changed when type parms filled in + + if (sig.getKind() == Member.METHOD) { + // OPTIMIZE can this be sped up? + if (clashesWithExistingMember(munger, getMethods(true, false))) { // ITDs checked below + return; + } + if (this.isInterface()) { + // OPTIMIZE this set of methods are always the same - must we keep creating them as a list? + if (clashesWithExistingMember(munger, Arrays.asList(world.getCoreType(OBJECT).getDeclaredMethods()).iterator())) { + return; + } + } + } else if (sig.getKind() == Member.FIELD) { + if (clashesWithExistingMember(munger, Arrays.asList(getDeclaredFields()).iterator())) { + return; + } + // Cannot cope with two version '2' style mungers for the same field on the same type + // Must error and request the user recompile at least one aspect with the + // -Xset:itdStyle=1 option + if (!isDuringCompilation) { + ResolvedTypeMunger thisRealMunger = munger.getMunger(); + if (thisRealMunger instanceof NewFieldTypeMunger) { + NewFieldTypeMunger newFieldTypeMunger = (NewFieldTypeMunger) thisRealMunger; + if (newFieldTypeMunger.version == NewFieldTypeMunger.VersionTwo) { + String thisRealMungerSignatureName = newFieldTypeMunger.getSignature().getName(); + for (ConcreteTypeMunger typeMunger : interTypeMungers) { + if (typeMunger.getMunger() instanceof NewFieldTypeMunger) { + if (typeMunger.getSignature().getKind() == Member.FIELD) { + NewFieldTypeMunger existing = (NewFieldTypeMunger) typeMunger.getMunger(); + if (existing.getSignature().getName().equals(thisRealMungerSignatureName) + && existing.version == NewFieldTypeMunger.VersionTwo + // this check ensures no problem for a clash with an ITD on an interface + && existing.getSignature().getDeclaringType() + .equals(newFieldTypeMunger.getSignature().getDeclaringType())) { + + // report error on the aspect + StringBuffer sb = new StringBuffer(); + sb.append("Cannot handle two aspects both attempting to use new style ITDs for the same named field "); + sb.append("on the same target type. Please recompile at least one aspect with '-Xset:itdVersion=1'."); + sb.append(" Aspects involved: " + munger.getAspectType().getName() + " and " + + typeMunger.getAspectType().getName() + "."); + sb.append(" Field is named '" + existing.getSignature().getName() + "'"); + getWorld().getMessageHandler().handleMessage( + new Message(sb.toString(), getSourceLocation(), true)); + return; + } + } + } + } + } + } + } + } else { + if (clashesWithExistingMember(munger, Arrays.asList(getDeclaredMethods()).iterator())) { + return; + } + } + + boolean needsAdding =true; + boolean needsToBeAddedEarlier =false; + // now compare to existingMungers + for (Iterator<ConcreteTypeMunger> i = interTypeMungers.iterator(); i.hasNext();) { + ConcreteTypeMunger existingMunger = i.next(); + boolean v2itds = munger.getSignature().getKind()== Member.FIELD && (munger.getMunger() instanceof NewFieldTypeMunger) && ((NewFieldTypeMunger)munger.getMunger()).version==NewFieldTypeMunger.VersionTwo; + + if (conflictingSignature(existingMunger.getSignature(), munger.getSignature(),v2itds)) { + // System.err.println("match " + munger + " with " + existingMunger); + if (isVisible(munger.getSignature().getModifiers(), munger.getAspectType(), existingMunger.getAspectType())) { + // System.err.println(" is visible"); + int c = compareMemberPrecedence(sig, existingMunger.getSignature()); + if (c == 0) { + c = getWorld().compareByPrecedenceAndHierarchy(munger.getAspectType(), existingMunger.getAspectType()); + } + // System.err.println(" compare: " + c); + if (c < 0) { + // the existing munger dominates the new munger + checkLegalOverride(munger.getSignature(), existingMunger.getSignature(), 0x11, null); + needsAdding = false; + if (munger.getSignature().getKind()== Member.FIELD && munger.getSignature().getDeclaringType().resolve(world).isInterface() && ((NewFieldTypeMunger)munger.getMunger()).version==NewFieldTypeMunger.VersionTwo) { + // still need to add it + needsAdding=true; + } + break; + } else if (c > 0) { + // the new munger dominates the existing one + checkLegalOverride(existingMunger.getSignature(), munger.getSignature(), 0x11, null); +// i.remove(); + if (existingMunger.getSignature().getKind()==Member.FIELD && + existingMunger.getSignature().getDeclaringType().resolve(world).isInterface() + && ((NewFieldTypeMunger)existingMunger.getMunger()).version==NewFieldTypeMunger.VersionTwo) { + needsToBeAddedEarlier=true; + } else { + i.remove(); + } + break; + } else { + interTypeConflictError(munger, existingMunger); + interTypeConflictError(existingMunger, munger); + return; + } + } + } + } + // System.err.println("adding: " + munger + " to " + this); + // we are adding the parameterized form of the ITD to the list of + // mungers. Within it, the munger knows the original declared + // signature for the ITD so it can be retrieved. + if (needsAdding) { + if (!needsToBeAddedEarlier) { + interTypeMungers.add(munger); + } else { + interTypeMungers.add(0,munger); + } + } + } + + /** + * Compare the type transformer with the existing members. A clash may not be an error (the ITD may be the 'default + * implementation') so returning false is not always a sign of an error. + * + * @return true if there is a clash + */ + private boolean clashesWithExistingMember(ConcreteTypeMunger typeTransformer, Iterator<ResolvedMember> existingMembers) { + ResolvedMember typeTransformerSignature = typeTransformer.getSignature(); + + // ResolvedType declaringAspectType = munger.getAspectType(); + // if (declaringAspectType.isRawType()) declaringAspectType = + // declaringAspectType.getGenericType(); + // if (declaringAspectType.isGenericType()) { + // + // ResolvedType genericOnType = + // getWorld().resolve(sig.getDeclaringType()).getGenericType(); + // ConcreteTypeMunger ctm = + // munger.parameterizedFor(discoverActualOccurrenceOfTypeInHierarchy + // (genericOnType)); + // sig = ctm.getSignature(); // possible sig change when type + // } + // if (munger.getMunger().hasTypeVariableAliases()) { + // ResolvedType genericOnType = + // getWorld().resolve(sig.getDeclaringType()).getGenericType(); + // ConcreteTypeMunger ctm = + // munger.parameterizedFor(discoverActualOccurrenceOfTypeInHierarchy( + // genericOnType)); + // sig = ctm.getSignature(); // possible sig change when type parameters + // filled in + // } + ResolvedTypeMunger rtm = typeTransformer.getMunger(); + boolean v2itds = true; + if (rtm instanceof NewFieldTypeMunger && ((NewFieldTypeMunger)rtm).version==NewFieldTypeMunger.VersionOne) { + v2itds = false; + } + while (existingMembers.hasNext()) { + ResolvedMember existingMember = existingMembers.next(); + // don't worry about clashing with bridge methods + if (existingMember.isBridgeMethod()) { + continue; + } + if (conflictingSignature(existingMember, typeTransformerSignature,v2itds)) { + // System.err.println("conflict: existingMember=" + + // existingMember + " typeMunger=" + munger); + // System.err.println(munger.getSourceLocation() + ", " + + // munger.getSignature() + ", " + + // munger.getSignature().getSourceLocation()); + + if (isVisible(existingMember.getModifiers(), this, typeTransformer.getAspectType())) { + int c = compareMemberPrecedence(typeTransformerSignature, existingMember); + // System.err.println(" c: " + c); + if (c < 0) { + ResolvedType typeTransformerTargetType = typeTransformerSignature.getDeclaringType().resolve(world); + if (typeTransformerTargetType.isInterface()) { + ResolvedType existingMemberType = existingMember.getDeclaringType().resolve(world); + if ((rtm instanceof NewMethodTypeMunger) && !typeTransformerTargetType.equals(existingMemberType)) { + // Might be pr404601. ITD is on an interface with a different visibility to the real member + if (Modifier.isPrivate(typeTransformerSignature.getModifiers()) && + Modifier.isPublic(existingMember.getModifiers())) { + world.getMessageHandler().handleMessage(new Message("private intertype declaration '"+typeTransformerSignature.toString()+"' clashes with public member '"+existingMember.toString()+"'",existingMember.getSourceLocation(),true)); + } + } + } + // existingMember dominates munger + checkLegalOverride(typeTransformerSignature, existingMember, 0x10, typeTransformer.getAspectType()); + return true; + } else if (c > 0) { + // munger dominates existingMember + checkLegalOverride(existingMember, typeTransformerSignature, 0x01, typeTransformer.getAspectType()); + // interTypeMungers.add(munger); + // ??? might need list of these overridden abstracts + continue; + } else { + // bridge methods can differ solely in return type. + // FIXME this whole method seems very hokey - unaware of covariance/varargs/bridging - it + // could do with a rewrite ! + boolean sameReturnTypes = (existingMember.getReturnType().equals(typeTransformerSignature.getReturnType())); + if (sameReturnTypes) { + // pr206732 - if the existingMember is due to a + // previous application of this same ITD (which can + // happen if this is a binary type being brought in + // from the aspectpath). The 'better' fix is + // to recognize it is from the aspectpath at a + // higher level and dont do this, but that is rather + // more work. + boolean isDuplicateOfPreviousITD = false; + ResolvedType declaringRt = existingMember.getDeclaringType().resolve(world); + WeaverStateInfo wsi = declaringRt.getWeaverState(); + if (wsi != null) { + List<ConcreteTypeMunger> mungersAffectingThisType = wsi.getTypeMungers(declaringRt); + if (mungersAffectingThisType != null) { + for (Iterator<ConcreteTypeMunger> iterator = mungersAffectingThisType.iterator(); iterator + .hasNext() && !isDuplicateOfPreviousITD;) { + ConcreteTypeMunger ctMunger = iterator.next(); + // relatively crude check - is the ITD + // for the same as the existingmember + // and does it come + // from the same aspect + if (ctMunger.getSignature().equals(existingMember) + && ctMunger.aspectType.equals(typeTransformer.getAspectType())) { + isDuplicateOfPreviousITD = true; + } + } + } + } + if (!isDuplicateOfPreviousITD) { + // b275032 - this is OK if it is the default ctor and that default ctor was generated + // at compile time, otherwise we cannot overwrite it + if (!(typeTransformerSignature.getName().equals("<init>") && existingMember.isDefaultConstructor())) { + String aspectName = typeTransformer.getAspectType().getName(); + ISourceLocation typeTransformerLocation = typeTransformer.getSourceLocation(); + ISourceLocation existingMemberLocation = existingMember.getSourceLocation(); + String msg = WeaverMessages.format(WeaverMessages.ITD_MEMBER_CONFLICT, aspectName, + existingMember); + + // this isn't quite right really... as I think the errors should only be recorded against + // what is currently being processed or they may get lost or reported twice + + // report error on the aspect + getWorld().getMessageHandler().handleMessage(new Message(msg, typeTransformerLocation, true)); + + // report error on the affected type, if we can + if (existingMemberLocation != null) { + getWorld().getMessageHandler() + .handleMessage(new Message(msg, existingMemberLocation, true)); + } + return true; // clash - so ignore this itd + } + } + } + } + } else if (isDuplicateMemberWithinTargetType(existingMember, this, typeTransformerSignature)) { + getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.ITD_MEMBER_CONFLICT, typeTransformer + .getAspectType().getName(), existingMember), typeTransformer.getSourceLocation())); + return true; + } + } + } + return false; + } + + // we know that the member signature matches, but that the member in the + // target type is not visible to the aspect. + // this may still be disallowed if it would result in two members within the + // same declaring type with the same + // signature AND more than one of them is concrete AND they are both visible + // within the target type. + private boolean isDuplicateMemberWithinTargetType(ResolvedMember existingMember, ResolvedType targetType, + ResolvedMember itdMember) { + if ((existingMember.isAbstract() || itdMember.isAbstract())) { + return false; + } + UnresolvedType declaringType = existingMember.getDeclaringType(); + if (!targetType.equals(declaringType)) { + return false; + } + // now have to test that itdMember is visible from targetType + if (Modifier.isPrivate(itdMember.getModifiers())) { + return false; + } + if (itdMember.isPublic()) { + return true; + } + // must be in same package to be visible then... + if (!targetType.getPackageName().equals(itdMember.getDeclaringType().getPackageName())) { + return false; + } + + // trying to put two members with the same signature into the exact same + // type..., and both visible in that type. + return true; + } + + /** + * @param transformerPosition which parameter is the type transformer (0x10 for first, 0x01 for second, 0x11 for both, 0x00 for + * neither) + * @param aspectType the declaring type of aspect defining the *first* type transformer + * @return true if the override is legal note: calling showMessage with two locations issues TWO messages, not ONE message with + * an additional source location. + */ + public boolean checkLegalOverride(ResolvedMember parent, ResolvedMember child, int transformerPosition, ResolvedType aspectType) { + // System.err.println("check: " + child.getDeclaringType() + " overrides " + parent.getDeclaringType()); + if (Modifier.isFinal(parent.getModifiers())) { + // If the ITD matching is occurring due to pulling in a BinaryTypeBinding then this check can incorrectly + // signal an error because the ITD transformer being examined here will exactly match the member it added + // during the first round of compilation. This situation can only occur if the ITD is on an interface whilst + // the class is the top most implementor. If the ITD is on the same type that received it during compilation, + // this method won't be called as the previous check for precedence level will return 0. + + if (transformerPosition == 0x10 && aspectType != null) { + ResolvedType nonItdDeclaringType = child.getDeclaringType().resolve(world); + WeaverStateInfo wsi = nonItdDeclaringType.getWeaverState(); + if (wsi != null) { + List<ConcreteTypeMunger> transformersOnThisType = wsi.getTypeMungers(nonItdDeclaringType); + if (transformersOnThisType != null) { + for (ConcreteTypeMunger transformer : transformersOnThisType) { + // relatively crude check - is the ITD for the same as the existingmember + // and does it come from the same aspect + if (transformer.aspectType.equals(aspectType)) { + if (parent.equalsApartFromDeclaringType(transformer.getSignature())) { + return true; + } + } + } + } + } + } + + world.showMessage(Message.ERROR, WeaverMessages.format(WeaverMessages.CANT_OVERRIDE_FINAL_MEMBER, parent), + child.getSourceLocation(), null); + return false; + } + + boolean incompatibleReturnTypes = false; + // In 1.5 mode, allow for covariance on return type + if (world.isInJava5Mode() && parent.getKind() == Member.METHOD) { + + // Look at the generic types when doing this comparison + ResolvedType rtParentReturnType = parent.resolve(world).getGenericReturnType().resolve(world); + ResolvedType rtChildReturnType = child.resolve(world).getGenericReturnType().resolve(world); + incompatibleReturnTypes = !rtParentReturnType.isAssignableFrom(rtChildReturnType); + // For debug, uncomment this bit and we'll repeat the check - stick + // a breakpoint on the call + // if (incompatibleReturnTypes) { + // incompatibleReturnTypes = + // !rtParentReturnType.isAssignableFrom(rtChildReturnType); + // } + } else { + ResolvedType rtParentReturnType = parent.resolve(world).getGenericReturnType().resolve(world); + ResolvedType rtChildReturnType = child.resolve(world).getGenericReturnType().resolve(world); + + incompatibleReturnTypes = !rtParentReturnType.equals(rtChildReturnType); + } + + if (incompatibleReturnTypes) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ITD_RETURN_TYPE_MISMATCH, parent, child), + child.getSourceLocation(), parent.getSourceLocation()); + return false; + } + if (parent.getKind() == Member.POINTCUT) { + UnresolvedType[] pTypes = parent.getParameterTypes(); + UnresolvedType[] cTypes = child.getParameterTypes(); + if (!Arrays.equals(pTypes, cTypes)) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ITD_PARAM_TYPE_MISMATCH, parent, child), + child.getSourceLocation(), parent.getSourceLocation()); + return false; + } + } + // System.err.println("check: " + child.getModifiers() + + // " more visible " + parent.getModifiers()); + if (isMoreVisible(parent.getModifiers(), child.getModifiers())) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ITD_VISIBILITY_REDUCTION, parent, child), + child.getSourceLocation(), parent.getSourceLocation()); + return false; + } + + // check declared exceptions + ResolvedType[] childExceptions = world.resolve(child.getExceptions()); + ResolvedType[] parentExceptions = world.resolve(parent.getExceptions()); + ResolvedType runtimeException = world.resolve("java.lang.RuntimeException"); + ResolvedType error = world.resolve("java.lang.Error"); + + outer: for (int i = 0, leni = childExceptions.length; i < leni; i++) { + // System.err.println("checking: " + childExceptions[i]); + if (runtimeException.isAssignableFrom(childExceptions[i])) { + continue; + } + if (error.isAssignableFrom(childExceptions[i])) { + continue; + } + + for (int j = 0, lenj = parentExceptions.length; j < lenj; j++) { + if (parentExceptions[j].isAssignableFrom(childExceptions[i])) { + continue outer; + } + } + + // this message is now better handled my MethodVerifier in JDT core. + // world.showMessage(IMessage.ERROR, + // WeaverMessages.format(WeaverMessages.ITD_DOESNT_THROW, + // childExceptions[i].getName()), + // child.getSourceLocation(), null); + + return false; + } + boolean parentStatic = Modifier.isStatic(parent.getModifiers()); + boolean childStatic = Modifier.isStatic(child.getModifiers()); + if (parentStatic && !childStatic) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ITD_OVERRIDDEN_STATIC, child, parent), + child.getSourceLocation(), null); + return false; + } else if (childStatic && !parentStatic) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ITD_OVERIDDING_STATIC, child, parent), + child.getSourceLocation(), null); + return false; + } + return true; + + } + + private int compareMemberPrecedence(ResolvedMember m1, ResolvedMember m2) { + // if (!m1.getReturnType().equals(m2.getReturnType())) return 0; + + // need to allow for the special case of 'clone' - which is like + // abstract but is + // not marked abstract. The code below this next line seems to make + // assumptions + // about what will have gotten through the compiler based on the normal + // java rules. clone goes against these... + if (Modifier.isProtected(m2.getModifiers()) && m2.getName().charAt(0) == 'c') { + UnresolvedType declaring = m2.getDeclaringType(); + if (declaring != null) { + if (declaring.getName().equals("java.lang.Object") && m2.getName().equals("clone")) { + return +1; + } + } + } + + if (Modifier.isAbstract(m1.getModifiers())) { + return -1; + } + if (Modifier.isAbstract(m2.getModifiers())) { + return +1; + } + + if (m1.getDeclaringType().equals(m2.getDeclaringType())) { + return 0; + } + + ResolvedType t1 = m1.getDeclaringType().resolve(world); + ResolvedType t2 = m2.getDeclaringType().resolve(world); + if (t1.isAssignableFrom(t2)) { + return -1; + } + if (t2.isAssignableFrom(t1)) { + return +1; + } + return 0; + } + + public static boolean isMoreVisible(int m1, int m2) { + if (Modifier.isPrivate(m1)) { + return false; + } + if (isPackage(m1)) { + return Modifier.isPrivate(m2); + } + if (Modifier.isProtected(m1)) { + return /* private package */(Modifier.isPrivate(m2) || isPackage(m2)); + } + if (Modifier.isPublic(m1)) { + return /* private package protected */!Modifier.isPublic(m2); + } + throw new RuntimeException("bad modifier: " + m1); + } + + private static boolean isPackage(int i) { + return (0 == (i & (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED))); + } + + private void interTypeConflictError(ConcreteTypeMunger m1, ConcreteTypeMunger m2) { + // XXX this works only if we ignore separate compilation issues + // XXX dual errors possible if (this instanceof BcelObjectType) return; + /* + * if (m1.getMunger().getKind() == ResolvedTypeMunger.Field && m2.getMunger().getKind() == ResolvedTypeMunger.Field) { // if + * *exactly* the same, it's ok return true; } + */ + // System.err.println("conflict at " + m2.getSourceLocation()); + getWorld().showMessage( + IMessage.ERROR, + WeaverMessages.format(WeaverMessages.ITD_CONFLICT, m1.getAspectType().getName(), m2.getSignature(), m2 + .getAspectType().getName()), m2.getSourceLocation(), getSourceLocation()); + // return false; + } + + public ResolvedMember lookupSyntheticMember(Member member) { + // ??? horribly inefficient + // for (Iterator i = + // System.err.println("lookup " + member + " in " + interTypeMungers); + for (ConcreteTypeMunger m : interTypeMungers) { + ResolvedMember ret = m.getMatchingSyntheticMember(member); + if (ret != null) { + // System.err.println(" found: " + ret); + return ret; + } + } + + // Handling members for the new array join point + if (world.isJoinpointArrayConstructionEnabled() && this.isArray()) { + if (member.getKind() == Member.CONSTRUCTOR) { + ResolvedMemberImpl ret = new ResolvedMemberImpl(Member.CONSTRUCTOR, this, Modifier.PUBLIC, UnresolvedType.VOID, + "<init>", world.resolve(member.getParameterTypes())); + // Give the parameters names - they are going to be the dimensions uses to build the array (dim0 > dimN) + int count = ret.getParameterTypes().length; + String[] paramNames = new String[count]; + for (int i = 0; i < count; i++) { + paramNames[i] = new StringBuffer("dim").append(i).toString(); + } + ret.setParameterNames(paramNames); + return ret; + } + } + + // if (this.getSuperclass() != ResolvedType.OBJECT && + // this.getSuperclass() != null) { + // return getSuperclass().lookupSyntheticMember(member); + // } + + return null; + } + + static class SuperClassWalker implements Iterator<ResolvedType> { + + private ResolvedType curr; + private SuperInterfaceWalker iwalker; + private boolean wantGenerics; + + public SuperClassWalker(ResolvedType type, SuperInterfaceWalker iwalker, boolean genericsAware) { + this.curr = type; + this.iwalker = iwalker; + this.wantGenerics = genericsAware; + } + + @Override + public boolean hasNext() { + return curr != null; + } + + @Override + public ResolvedType next() { + ResolvedType ret = curr; + if (!wantGenerics && ret.isParameterizedOrGenericType()) { + ret = ret.getRawType(); + } + iwalker.push(ret); // tell the interface walker about another class whose interfaces need visiting + curr = curr.getSuperclass(); + return ret; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + static class SuperInterfaceWalker implements Iterator<ResolvedType> { + + private Getter<ResolvedType, ResolvedType> ifaceGetter; + Iterator<ResolvedType> delegate = null; + public Queue<ResolvedType> toPersue = new LinkedList<ResolvedType>(); + public Set<ResolvedType> visited = new HashSet<ResolvedType>(); + + SuperInterfaceWalker(Iterators.Getter<ResolvedType, ResolvedType> ifaceGetter) { + this.ifaceGetter = ifaceGetter; + } + + SuperInterfaceWalker(Iterators.Getter<ResolvedType, ResolvedType> ifaceGetter, ResolvedType interfaceType) { + this.ifaceGetter = ifaceGetter; + this.delegate = Iterators.one(interfaceType); + } + + @Override + public boolean hasNext() { + if (delegate == null || !delegate.hasNext()) { + // either we set it up or we have run out, is there anything else to look at? + if (toPersue.isEmpty()) { + return false; + } + do { + ResolvedType next = toPersue.remove(); + visited.add(next); + delegate = ifaceGetter.get(next); // retrieve interfaces from a class or another interface + } while (!delegate.hasNext() && !toPersue.isEmpty()); + } + return delegate.hasNext(); + } + + public void push(ResolvedType ret) { + toPersue.add(ret); + } + + @Override + public ResolvedType next() { + ResolvedType next = delegate.next(); + // BUG should check for generics and erase? + // if (!visited.contains(next)) { + // visited.add(next); + if (visited.add(next)) { + toPersue.add(next); // pushes on interfaces already visited? + } + return next; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + public void clearInterTypeMungers() { + if (isRawType()) { + ResolvedType genericType = getGenericType(); + if (genericType.isRawType()) { // ERROR SITUATION: PR341926 + // For some reason the raw type is pointing to another raw form (possibly itself) + System.err.println("DebugFor341926: Type " + this.getName() + " has an incorrect generic form"); + } else { + genericType.clearInterTypeMungers(); + } + } + // interTypeMungers.clear(); + // BUG? Why can't this be clear() instead: 293620 c6 + interTypeMungers = new ArrayList<ConcreteTypeMunger>(); + } + + public boolean isTopmostImplementor(ResolvedType interfaceType) { + boolean b = true; + if (isInterface()) { + b = false; + } else if (!interfaceType.isAssignableFrom(this, true)) { + b = false; + } else { + ResolvedType superclass = this.getSuperclass(); + if (superclass.isMissing()) { + b = true; // we don't know anything about supertype, and it can't be exposed to weaver + } else if (interfaceType.isAssignableFrom(superclass, true)) { // check that I'm truly the topmost implementor + b = false; + } + } + // System.out.println("is " + getName() + " topmostimplementor of " + interfaceType + "? " + b); + return b; + } + + public ResolvedType getTopmostImplementor(ResolvedType interfaceType) { + if (isInterface()) { + return null; + } + if (!interfaceType.isAssignableFrom(this)) { + return null; + } + // Check if my super class is an implementor? + ResolvedType higherType = this.getSuperclass().getTopmostImplementor(interfaceType); + if (higherType != null) { + return higherType; + } + return this; + } + + public List<ResolvedMember> getExposedPointcuts() { + List<ResolvedMember> ret = new ArrayList<ResolvedMember>(); + if (getSuperclass() != null) { + ret.addAll(getSuperclass().getExposedPointcuts()); + } + + for (ResolvedType type : getDeclaredInterfaces()) { + addPointcutsResolvingConflicts(ret, Arrays.asList(type.getDeclaredPointcuts()), false); + } + + addPointcutsResolvingConflicts(ret, Arrays.asList(getDeclaredPointcuts()), true); + + for (ResolvedMember member : ret) { + ResolvedPointcutDefinition inherited = (ResolvedPointcutDefinition) member; + if (inherited != null && inherited.isAbstract()) { + if (!this.isAbstract()) { + getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.POINCUT_NOT_CONCRETE, inherited, this.getName()), + inherited.getSourceLocation(), this.getSourceLocation()); + } + } + } + return ret; + } + + private void addPointcutsResolvingConflicts(List<ResolvedMember> acc, List<ResolvedMember> added, boolean isOverriding) { + for (Iterator<ResolvedMember> i = added.iterator(); i.hasNext();) { + ResolvedPointcutDefinition toAdd = (ResolvedPointcutDefinition) i.next(); + for (Iterator<ResolvedMember> j = acc.iterator(); j.hasNext();) { + ResolvedPointcutDefinition existing = (ResolvedPointcutDefinition) j.next(); + if (toAdd == null || existing == null || existing == toAdd) { + continue; + } + UnresolvedType pointcutDeclaringTypeUT = existing.getDeclaringType(); + if (pointcutDeclaringTypeUT != null) { + ResolvedType pointcutDeclaringType = pointcutDeclaringTypeUT.resolve(getWorld()); + if (!isVisible(existing.getModifiers(), pointcutDeclaringType, this)) { + // if they intended to override it but it is not visible, + // give them a nicer message + if (existing.isAbstract() && conflictingSignature(existing, toAdd)) { + getWorld().showMessage( + IMessage.ERROR, + WeaverMessages.format(WeaverMessages.POINTCUT_NOT_VISIBLE, existing.getDeclaringType() + .getName() + "." + existing.getName() + "()", this.getName()), + toAdd.getSourceLocation(), null); + j.remove(); + } + continue; + } + } + if (conflictingSignature(existing, toAdd)) { + if (isOverriding) { + checkLegalOverride(existing, toAdd, 0x00, null); + j.remove(); + } else { + getWorld().showMessage( + IMessage.ERROR, + WeaverMessages.format(WeaverMessages.CONFLICTING_INHERITED_POINTCUTS, + this.getName() + toAdd.getSignature()), existing.getSourceLocation(), + toAdd.getSourceLocation()); + j.remove(); + } + } + } + acc.add(toAdd); + } + } + + public ISourceLocation getSourceLocation() { + return null; + } + + public boolean isExposedToWeaver() { + return false; + } + + public WeaverStateInfo getWeaverState() { + return null; + } + + /** + * Overridden by ReferenceType to return a sensible answer for parameterized and raw types. + * + * @return + */ + public ReferenceType getGenericType() { + // if (!(isParameterizedType() || isRawType())) + // throw new BCException("The type " + getBaseName() + " is not parameterized or raw - it has no generic type"); + return null; + } + + @Override + public ResolvedType getRawType() { + return super.getRawType().resolve(world); + } + + public ResolvedType parameterizedWith(UnresolvedType[] typeParameters) { + if (!(isGenericType() || isParameterizedType())) { + return this; + } + return TypeFactory.createParameterizedType(this.getGenericType(), typeParameters, getWorld()); + } + + /** + * Iff I am a parameterized type, and any of my parameters are type variable references (or nested parameterized types), + * return a version with those type parameters replaced in accordance with the passed bindings. + */ + @Override + public UnresolvedType parameterize(Map<String, UnresolvedType> typeBindings) { + if (!isParameterizedType()) { + // throw new IllegalStateException("Can't parameterize a type that is not a parameterized type"); + return this; + } + boolean workToDo = false; + for (int i = 0; i < typeParameters.length; i++) { + if (typeParameters[i].isTypeVariableReference() || (typeParameters[i] instanceof BoundedReferenceType) || typeParameters[i].isParameterizedType()) { + workToDo = true; + } + } + if (!workToDo) { + return this; + } else { + UnresolvedType[] newTypeParams = new UnresolvedType[typeParameters.length]; + for (int i = 0; i < newTypeParams.length; i++) { + newTypeParams[i] = typeParameters[i]; + if (newTypeParams[i].isTypeVariableReference()) { + TypeVariableReferenceType tvrt = (TypeVariableReferenceType) newTypeParams[i]; + UnresolvedType binding = typeBindings.get(tvrt.getTypeVariable().getName()); + if (binding != null) { + newTypeParams[i] = binding; + } + } else if (newTypeParams[i] instanceof BoundedReferenceType) { + BoundedReferenceType brType = (BoundedReferenceType) newTypeParams[i]; + newTypeParams[i] = brType.parameterize(typeBindings); + // brType.parameterize(typeBindings) + } else if (newTypeParams[i].isParameterizedType()) { + newTypeParams[i] = newTypeParams[i].parameterize(typeBindings); + } + } + return TypeFactory.createParameterizedType(getGenericType(), newTypeParams, getWorld()); + } + } + + // public boolean hasParameterizedSuperType() { + // getParameterizedSuperTypes(); + // return parameterizedSuperTypes.length > 0; + // } + + // public boolean hasGenericSuperType() { + // ResolvedType[] superTypes = getDeclaredInterfaces(); + // for (int i = 0; i < superTypes.length; i++) { + // if (superTypes[i].isGenericType()) + // return true; + // } + // return false; + // } + + // private ResolvedType[] parameterizedSuperTypes = null; + + /** + * Similar to the above method, but accumulates the super types + * + * @return + */ + // public ResolvedType[] getParameterizedSuperTypes() { + // if (parameterizedSuperTypes != null) + // return parameterizedSuperTypes; + // List accumulatedTypes = new ArrayList(); + // accumulateParameterizedSuperTypes(this, accumulatedTypes); + // ResolvedType[] ret = new ResolvedType[accumulatedTypes.size()]; + // parameterizedSuperTypes = (ResolvedType[]) accumulatedTypes.toArray(ret); + // return parameterizedSuperTypes; + // } + // private void accumulateParameterizedSuperTypes(ResolvedType forType, List + // parameterizedTypeList) { + // if (forType.isParameterizedType()) { + // parameterizedTypeList.add(forType); + // } + // if (forType.getSuperclass() != null) { + // accumulateParameterizedSuperTypes(forType.getSuperclass(), + // parameterizedTypeList); + // } + // ResolvedType[] interfaces = forType.getDeclaredInterfaces(); + // for (int i = 0; i < interfaces.length; i++) { + // accumulateParameterizedSuperTypes(interfaces[i], parameterizedTypeList); + // } + // } + /** + * @return true if assignable to java.lang.Exception + */ + public boolean isException() { + return (world.getCoreType(UnresolvedType.JL_EXCEPTION).isAssignableFrom(this)); + } + + /** + * @return true if it is an exception and it is a checked one, false otherwise. + */ + public boolean isCheckedException() { + if (!isException()) { + return false; + } + if (world.getCoreType(UnresolvedType.RUNTIME_EXCEPTION).isAssignableFrom(this)) { + return false; + } + return true; + } + + /** + * Determines if variables of this type could be assigned values of another with lots of help. java.lang.Object is convertable + * from all types. A primitive type is convertable from X iff it's assignable from X. A reference type is convertable from X iff + * it's coerceable from X. In other words, X isConvertableFrom Y iff the compiler thinks that _some_ value of Y could be + * assignable to a variable of type X without loss of precision. + * + * @param other the other type + * @param world the {@link World} in which the possible assignment should be checked. + * @return true iff variables of this type could be assigned values of other with possible conversion + */ + public final boolean isConvertableFrom(ResolvedType other) { + + // // version from TypeX + // if (this.equals(OBJECT)) return true; + // if (this.isPrimitiveType() || other.isPrimitiveType()) return + // this.isAssignableFrom(other); + // return this.isCoerceableFrom(other); + // + + // version from ResolvedTypeX + if (this.equals(OBJECT)) { + return true; + } + if (world.isInJava5Mode()) { + if (this.isPrimitiveType() ^ other.isPrimitiveType()) { // If one is + // primitive + // and the + // other + // isnt + if (validBoxing.contains(this.getSignature() + other.getSignature())) { + return true; + } + } + } + if (this.isPrimitiveType() || other.isPrimitiveType()) { + return this.isAssignableFrom(other); + } + return this.isCoerceableFrom(other); + } + + /** + * Determines if the variables of this type could be assigned values of another type without casting. This still allows for + * assignment conversion as per JLS 2ed 5.2. For object types, this means supertypeOrEqual(THIS, OTHER). + * + * @param other the other type + * @param world the {@link World} in which the possible assignment should be checked. + * @return true iff variables of this type could be assigned values of other without casting + * @throws NullPointerException if other is null + */ + public abstract boolean isAssignableFrom(ResolvedType other); + + public abstract boolean isAssignableFrom(ResolvedType other, boolean allowMissing); + + /** + * Determines if values of another type could possibly be cast to this type. The rules followed are from JLS 2ed 5.5, + * "Casting Conversion". + * <p/> + * <p> + * This method should be commutative, i.e., for all UnresolvedType a, b and all World w: + * <p/> + * <blockquote> + * + * <pre> + * a.isCoerceableFrom(b, w) == b.isCoerceableFrom(a, w) + * </pre> + * + * </blockquote> + * + * @param other the other type + * @param world the {@link World} in which the possible coersion should be checked. + * @return true iff values of other could possibly be cast to this type. + * @throws NullPointerException if other is null. + */ + public abstract boolean isCoerceableFrom(ResolvedType other); + + public boolean needsNoConversionFrom(ResolvedType o) { + return isAssignableFrom(o); + } + + public String getSignatureForAttribute() { + return signature; // Assume if this is being called that it is for a + // simple type (eg. void, int, etc) + } + + private FuzzyBoolean parameterizedWithTypeVariable = FuzzyBoolean.MAYBE; + + /** + * return true if the parameterization of this type includes a member type variable. Member type variables occur in generic + * methods/ctors. + */ + public boolean isParameterizedWithTypeVariable() { + // MAYBE means we haven't worked it out yet... + if (parameterizedWithTypeVariable == FuzzyBoolean.MAYBE) { + + // if there are no type parameters then we cant be... + if (typeParameters == null || typeParameters.length == 0) { + parameterizedWithTypeVariable = FuzzyBoolean.NO; + return false; + } + + for (int i = 0; i < typeParameters.length; i++) { + ResolvedType aType = (ResolvedType) typeParameters[i]; + if (aType.isTypeVariableReference() + // Changed according to the problems covered in bug 222648 + // Don't care what kind of type variable - the fact that there + // is one + // at all means we can't risk caching it against we get confused + // later + // by another variation of the parameterization that just + // happens to + // use the same type variable name + + // assume the worst - if its definetly not a type declared one, + // it could be anything + // && ((TypeVariableReference)aType).getTypeVariable(). + // getDeclaringElementKind()!=TypeVariable.TYPE + ) { + parameterizedWithTypeVariable = FuzzyBoolean.YES; + return true; + } + if (aType.isParameterizedType()) { + boolean b = aType.isParameterizedWithTypeVariable(); + if (b) { + parameterizedWithTypeVariable = FuzzyBoolean.YES; + return true; + } + } + if (aType.isGenericWildcard()) { + BoundedReferenceType boundedRT = (BoundedReferenceType) aType; + if (boundedRT.isExtends()) { + boolean b = false; + UnresolvedType upperBound = boundedRT.getUpperBound(); + if (upperBound.isParameterizedType()) { + b = ((ResolvedType) upperBound).isParameterizedWithTypeVariable(); + } else if (upperBound.isTypeVariableReference() + && ((TypeVariableReference) upperBound).getTypeVariable().getDeclaringElementKind() == TypeVariable.METHOD) { + b = true; + } + if (b) { + parameterizedWithTypeVariable = FuzzyBoolean.YES; + return true; + } + // FIXME asc need to check additional interface bounds + } + if (boundedRT.isSuper()) { + boolean b = false; + UnresolvedType lowerBound = boundedRT.getLowerBound(); + if (lowerBound.isParameterizedType()) { + b = ((ResolvedType) lowerBound).isParameterizedWithTypeVariable(); + } else if (lowerBound.isTypeVariableReference() + && ((TypeVariableReference) lowerBound).getTypeVariable().getDeclaringElementKind() == TypeVariable.METHOD) { + b = true; + } + if (b) { + parameterizedWithTypeVariable = FuzzyBoolean.YES; + return true; + } + } + } + } + parameterizedWithTypeVariable = FuzzyBoolean.NO; + } + return parameterizedWithTypeVariable.alwaysTrue(); + } + + protected boolean ajMembersNeedParameterization() { + if (isParameterizedType()) { + return true; + } + ResolvedType superclass = getSuperclass(); + if (superclass != null && !superclass.isMissing()) { + return superclass.ajMembersNeedParameterization(); + } + return false; + } + + protected Map<String, UnresolvedType> getAjMemberParameterizationMap() { + Map<String, UnresolvedType> myMap = getMemberParameterizationMap(); + if (myMap.isEmpty()) { + // might extend a parameterized aspect that we also need to + // consider... + if (getSuperclass() != null) { + return getSuperclass().getAjMemberParameterizationMap(); + } + } + return myMap; + } + + public void setBinaryPath(String binaryPath) { + this.binaryPath = binaryPath; + } + + /** + * Returns the path to the jar or class file from which this binary aspect came or null if not a binary aspect + */ + public String getBinaryPath() { + return binaryPath; + } + + /** + * Undo any temporary modifications to the type (for example it may be holding annotations temporarily whilst some matching is + * occurring - These annotations will be added properly during weaving but sometimes for type completion they need to be held + * here for a while). + */ + public void ensureConsistent() { + // Nothing to do for anything except a ReferenceType + } + + /** + * For an annotation type, this will return if it is marked with @Inherited + */ + public boolean isInheritedAnnotation() { + ensureAnnotationBitsInitialized(); + return (bits & AnnotationMarkedInherited) != 0; + } + + /* + * Setup the bitflags if they have not already been done. + */ + private void ensureAnnotationBitsInitialized() { + if ((bits & AnnotationBitsInitialized) == 0) { + bits |= AnnotationBitsInitialized; + // Is it marked @Inherited? + if (hasAnnotation(UnresolvedType.AT_INHERITED)) { + bits |= AnnotationMarkedInherited; + } + } + } + + private boolean hasNewParentMungers() { + if ((bits & MungersAnalyzed) == 0) { + bits |= MungersAnalyzed; + for (ConcreteTypeMunger munger : interTypeMungers) { + ResolvedTypeMunger resolvedTypeMunger = munger.getMunger(); + if (resolvedTypeMunger != null && resolvedTypeMunger.getKind() == ResolvedTypeMunger.Parent) { + bits |= HasParentMunger; + } + } + } + return (bits & HasParentMunger) != 0; + } + + public void tagAsTypeHierarchyComplete() { + if (isParameterizedOrRawType()) { + ReferenceType genericType = this.getGenericType(); + genericType.tagAsTypeHierarchyComplete(); + return; + } + bits |= TypeHierarchyCompleteBit; + } + + public boolean isTypeHierarchyComplete() { + if (isParameterizedOrRawType()) { + return this.getGenericType().isTypeHierarchyComplete(); + } + return (bits & TypeHierarchyCompleteBit) != 0; + } + + /** + * return the weaver version used to build this type - defaults to the most recent version unless discovered otherwise. + * + * @return the (major) version, {@link WeaverVersionInfo} + */ + public int getCompilerVersion() { + return WeaverVersionInfo.getCurrentWeaverMajorVersion(); + } + + public boolean isPrimitiveArray() { + return false; + } + + public boolean isGroovyObject() { + if ((bits & GroovyObjectInitialized) == 0) { + ResolvedType[] intfaces = getDeclaredInterfaces(); + boolean done = false; + // TODO do we need to walk more of these? (i.e. the interfaces interfaces and supertypes supertype). Check what groovy + // does in the case where a hierarchy is involved and there are types in between GroovyObject/GroovyObjectSupport and + // the type + if (intfaces != null) { + for (ResolvedType intface : intfaces) { + if (intface.getName().equals("groovy.lang.GroovyObject")) { + bits |= IsGroovyObject; + done = true; + break; + } + } + } + if (!done) { + // take a look at the supertype + if (getSuperclass().getName().equals("groovy.lang.GroovyObjectSupport")) { + bits |= IsGroovyObject; + } + } + bits |= GroovyObjectInitialized; + } + return (bits & IsGroovyObject) != 0; + } + + public boolean isPrivilegedAspect() { + if ((bits & IsPrivilegedBitInitialized) == 0) { + AnnotationAJ privilegedAnnotation = getAnnotationOfType(UnresolvedType.AJC_PRIVILEGED); + if (privilegedAnnotation != null) { + bits |= IsPrivilegedAspect; + } + // TODO do we need to reset this bit if the annotations are set again ? + bits |= IsPrivilegedBitInitialized; + } + return (bits & IsPrivilegedAspect) != 0; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedTypeMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedTypeMunger.java new file mode 100644 index 000000000..2ba3cbc36 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ResolvedTypeMunger.java @@ -0,0 +1,499 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * Alexandre Vasseur @AspectJ ITDs + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.SourceLocation; +import org.aspectj.util.TypeSafeEnum; + +/** + * This is an abstraction over method/field introduction. It might not have the chops to handle other inter-type declarations. This + * is the thing that is used on the eclipse side and serialized into a ConcreteTypeMunger. + */ +public abstract class ResolvedTypeMunger { + + protected Kind kind; + protected ResolvedMember signature; + + /** + * The declared signature is filled in when a type munger is parameterized for application to a particular type. It represents + * the signature originally declared in the source file. + */ + protected ResolvedMember declaredSignature; + + // This list records the occurences (in order) of any names specified in the <> + // for a target type for the ITD. So for example, for List<C,B,A> this list + // will be C,B,A - the list is used later to map other occurrences of C,B,A + // across the intertype declaration to the right type variables in the generic + // type upon which the itd is being made. + // might need serializing the class file for binary weaving. + protected List<String> typeVariableAliases; + + private Set<ResolvedMember> superMethodsCalled = Collections.emptySet(); + + private ISourceLocation location; + + private ResolvedType onType = null; + + public ResolvedTypeMunger(Kind kind, ResolvedMember signature) { + this.kind = kind; + this.signature = signature; + UnresolvedType declaringType = signature != null ? signature.getDeclaringType() : null; + if (declaringType != null) { + if (declaringType.isRawType()) { + throw new IllegalStateException("Use generic type, not raw type"); + } + if (declaringType.isParameterizedType()) { + throw new IllegalStateException("Use generic type, not parameterized type"); + } + } + // boolean aChangeOccurred = false; + // + // UnresolvedType rt = signature.getReturnType(); + // if (rt.isParameterizedType() || rt.isGenericType()) {rt = rt.getRawType();aChangeOccurred=true;} + // UnresolvedType[] pt = signature.getParameterTypes(); + // for (int i = 0; i < pt.length; i++) { + // if (pt[i].isParameterizedType() || pt[i].isGenericType()) { pt[i] = pt[i].getRawType();aChangeOccurred=true;} + // } + // if (aChangeOccurred) { + // this.signature = new + // ResolvedMemberImpl(signature.getKind(),signature.getDeclaringType(),signature.getModifiers(),rt,signature + // .getName(),pt,signature.getExceptions()); + // } + } + + public void setSourceLocation(ISourceLocation isl) { + location = isl; + } + + public ISourceLocation getSourceLocation() { + return location; + } + + // ---- + + // fromType is guaranteed to be a non-abstract aspect + // public ConcreteTypeMunger concretize(World world, ResolvedType aspectType) { + // + // ConcreteTypeMunger munger = world.concreteTypeMunger(this, aspectType); + // return munger; + // } + + public boolean matches(ResolvedType matchType, ResolvedType aspectType) { + if (onType == null) { + onType = matchType.getWorld().resolve(getDeclaringType()); + if (onType.isRawType()) { + onType = onType.getGenericType(); + } + } + // System.err.println("matching: " + this + " to " + matchType + " onType = " + onType); + if (matchType.equals(onType)) { + if (!onType.isExposedToWeaver()) { + // if the onType is an interface, and it already has the member we are about + // to munge, then this is ok... + boolean ok = (onType.isInterface() && (onType.lookupMemberWithSupersAndITDs(getSignature()) != null)); + + if (!ok && onType.getWeaverState() == null) { + if (matchType.getWorld().getLint().typeNotExposedToWeaver.isEnabled()) { + matchType.getWorld().getLint().typeNotExposedToWeaver.signal(matchType.getName(), signature + .getSourceLocation()); + } + } + } + return true; + } + // System.err.println("NO MATCH DIRECT"); + + if (onType.isInterface()) { + return matchType.isTopmostImplementor(onType); + } else { + return false; + } + } + + // ---- + + @Override + public String toString() { + return "ResolvedTypeMunger(" + getKind() + ", " + getSignature() + ")"; + // .superMethodsCalled + ")"; + } + + // ---- + + public static ResolvedTypeMunger read(VersionedDataInputStream s, ISourceContext context) throws IOException { + Kind kind = Kind.read(s); + if (kind == Field) { + return NewFieldTypeMunger.readField(s, context); + } else if (kind == Method) { + return NewMethodTypeMunger.readMethod(s, context); + } else if (kind == Constructor) { + return NewConstructorTypeMunger.readConstructor(s, context); + } else if (kind == MethodDelegate) { + return MethodDelegateTypeMunger.readMethod(s, context, false); + } else if (kind == FieldHost) { + return MethodDelegateTypeMunger.FieldHostTypeMunger.readFieldHost(s, context); + } else if (kind == MethodDelegate2) { + return MethodDelegateTypeMunger.readMethod(s, context, true); + } else if (kind == InnerClass) { + return NewMemberClassTypeMunger.readInnerClass(s, context); + } else { + throw new RuntimeException("unimplemented"); + } + } + + protected static Set<ResolvedMember> readSuperMethodsCalled(VersionedDataInputStream s) throws IOException { + Set<ResolvedMember> ret = new HashSet<ResolvedMember>(); + int n = -1; + if (s.isAtLeast169()) { + n = s.readByte(); + } else { + n = s.readInt(); + } + if (n < 0) { + throw new BCException("Problem deserializing type munger"); + } + for (int i = 0; i < n; i++) { + ret.add(ResolvedMemberImpl.readResolvedMember(s, null)); + } + return ret; + } + + protected final void writeSuperMethodsCalled(CompressingDataOutputStream s) throws IOException { + if (superMethodsCalled == null || superMethodsCalled.size() == 0) { + s.writeByte(0); + return; + } + List<ResolvedMember> ret = new ArrayList<ResolvedMember>(superMethodsCalled); + Collections.sort(ret); + int n = ret.size(); + s.writeByte(n); + for (ResolvedMember m : ret) { + m.write(s); + } + } + + protected static ISourceLocation readSourceLocation(VersionedDataInputStream s) throws IOException { + // Location persistence for type mungers was added after 1.2.1 was shipped... + if (s.getMajorVersion() < AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + return null; + } + SourceLocation ret = null; + ObjectInputStream ois = null; + try { + // This logic copes with the location missing from the attribute - an EOFException will + // occur on the next line and we ignore it. + byte b = 0; + // if we aren't on 1.6.9 or we are on 1.6.9 but not compressed, then read as object stream + if (!s.isAtLeast169() || (b = s.readByte()) == 0) { + ois = new ObjectInputStream(s); + boolean validLocation = (Boolean) ois.readObject(); + if (validLocation) { + File f = (File) ois.readObject(); + Integer ii = (Integer) ois.readObject(); + Integer offset = (Integer) ois.readObject(); + ret = new SourceLocation(f, ii.intValue()); + ret.setOffset(offset.intValue()); + } + } else { + boolean validLocation = b == 2; + if (validLocation) { + String path = s.readUtf8(s.readShort()); + File f = new File(path); + ret = new SourceLocation(f, s.readInt()); + int offset = s.readInt(); + ret.setOffset(offset); + } + } + + } catch (EOFException eof) { + return null; // This exception occurs if processing an 'old style' file where the + // type munger attributes don't include the source location. + } catch (IOException ioe) { + // Something went wrong, maybe this is an 'old style' file that doesnt attach locations to mungers? + // (but I thought that was just an EOFException?) + ioe.printStackTrace(); + return null; + } catch (ClassNotFoundException e) { + } finally { + if (ois != null) { + ois.close(); + } + } + return ret; + } + + protected final void writeSourceLocation(CompressingDataOutputStream s) throws IOException { + if (s.canCompress()) { + s.writeByte(1 + (location == null ? 0 : 1)); // 1==compressed no location 2==compressed with location + if (location != null) { + s.writeCompressedPath(location.getSourceFile().getPath()); + s.writeInt(location.getLine()); + s.writeInt(location.getOffset()); + } + } else { + s.writeByte(0); + ObjectOutputStream oos = new ObjectOutputStream(s); + oos.writeObject(new Boolean(location != null)); + if (location != null) { + oos.writeObject(location.getSourceFile()); + oos.writeObject(new Integer(location.getLine())); + oos.writeObject(new Integer(location.getOffset())); + } + oos.flush(); + oos.close(); + } + } + + public abstract void write(CompressingDataOutputStream s) throws IOException; + + public Kind getKind() { + return kind; + } + + public static class Kind extends TypeSafeEnum { + /* private */Kind(String name, int key) { + super(name, key); + } + + public static Kind read(DataInputStream s) throws IOException { + int key = s.readByte(); + switch (key) { + case 1: + return Field; + case 2: + return Method; + case 5: + return Constructor; + case 9: + return MethodDelegate; + case 10: + return FieldHost; + case 11: + return MethodDelegate2; + case 12: + return InnerClass; + } + throw new BCException("bad kind: " + key); + } + + @Override + public String toString() { + // we want MethodDelegate to appear as Method in WeaveInfo messages + // TODO we may want something for fieldhost ? + if (getName().startsWith(MethodDelegate.getName())) {// startsWith will cover MethodDelegate2 as well + return Method.toString(); + } else { + return super.toString(); + } + } + } + + // ---- fields + + public static final Kind Field = new Kind("Field", 1); + public static final Kind Method = new Kind("Method", 2); + public static final Kind Constructor = new Kind("Constructor", 5); + // not serialized, only created during concretization of aspects + public static final Kind PerObjectInterface = new Kind("PerObjectInterface", 3); + public static final Kind PrivilegedAccess = new Kind("PrivilegedAccess", 4); + public static final Kind Parent = new Kind("Parent", 6); + // PTWIMPL not serialized, used during concretization of aspects + public static final Kind PerTypeWithinInterface = new Kind("PerTypeWithinInterface", 7); + public static final Kind AnnotationOnType = new Kind("AnnotationOnType", 8); // not serialized + public static final Kind MethodDelegate = new Kind("MethodDelegate", 9);// serialized, @AJ ITDs + public static final Kind FieldHost = new Kind("FieldHost", 10);// serialized, @AJ ITDs + public static final Kind MethodDelegate2 = new Kind("MethodDelegate2", 11);// serialized, @AJ ITDs + public static final Kind InnerClass = new Kind("InnerClass", 12); + + public static final String SUPER_DISPATCH_NAME = "superDispatch"; + + public void setSuperMethodsCalled(Set<ResolvedMember> c) { + this.superMethodsCalled = c; + } + + public Set<ResolvedMember> getSuperMethodsCalled() { + return superMethodsCalled; + } + + public ResolvedMember getSignature() { + return signature; + } + + // ---- + + public ResolvedMember getMatchingSyntheticMember(Member member, ResolvedType aspectType) { + if ((getSignature() != null) && getSignature().isPublic() && member.equals(getSignature())) { + return getSignature(); + } + + return null; + } + + public boolean changesPublicSignature() { + return kind == Field || kind == Method || kind == Constructor; + } + + public boolean needsAccessToTopmostImplementor() { + if (kind == Field) { + return true; + } else if (kind == Method) { + return !signature.isAbstract(); + } else { + return false; + } + } + + protected static List<String> readInTypeAliases(VersionedDataInputStream s) throws IOException { + if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + int count = -1; + if (s.isAtLeast169()) { + count = s.readByte(); + } else { + count = s.readInt(); + } + if (count != 0) { + List<String> aliases = new ArrayList<String>(); + for (int i = 0; i < count; i++) { + aliases.add(s.readUTF()); + } + return aliases; + } + } + return null; + } + + protected final void writeOutTypeAliases(DataOutputStream s) throws IOException { + // Write any type variable aliases + if (typeVariableAliases == null || typeVariableAliases.size() == 0) { + s.writeByte(0); + } else { + s.writeByte(typeVariableAliases.size()); + for (String element : typeVariableAliases) { + s.writeUTF(element); + } + } + } + + public List<String> getTypeVariableAliases() { + return typeVariableAliases; + } + + protected void setTypeVariableAliases(List<String> typeVariableAliases) { + this.typeVariableAliases = typeVariableAliases; + } + + public boolean hasTypeVariableAliases() { + return (typeVariableAliases != null && typeVariableAliases.size() > 0); + } + + /** + * return true if type variables are specified with the target type for this ITD. e.g. this would return true: + * "int I<A,B>.m() { return 42; }" + */ + public boolean sharesTypeVariablesWithGenericType() { + return (typeVariableAliases != null && typeVariableAliases.size() > 0); + } + + /** + * Parameterizes a resolved type munger for a particular usage of its target type (this is used when the target type is generic + * and the ITD shares type variables with the target) see ConcreteTypeMunger.parameterizedFor + */ + public ResolvedTypeMunger parameterizedFor(ResolvedType target) { + throw new BCException("Dont call parameterizedFor on a type munger of this kind: " + this.getClass()); + } + + // ResolvedType genericType = target; + // if (target.isRawType() || target.isParameterizedType()) genericType = genericType.getGenericType(); + // ResolvedMember parameterizedSignature = null; + // // If we are parameterizing it for a generic type, we just need to 'swap the letters' from the ones used + // // in the original ITD declaration to the ones used in the actual target type declaration. + // if (target.isGenericType()) { + // TypeVariable vars[] = target.getTypeVariables(); + // UnresolvedTypeVariableReferenceType[] varRefs = new UnresolvedTypeVariableReferenceType[vars.length]; + // for (int i = 0; i < vars.length; i++) { + // varRefs[i] = new UnresolvedTypeVariableReferenceType(vars[i]); + // } + // parameterizedSignature = getSignature().parameterizedWith(varRefs,genericType,true,typeVariableAliases); + // } else { + // // For raw and 'normal' parameterized targets (e.g. Interface, Interface<String>) + // parameterizedSignature = + // getSignature().parameterizedWith(target.getTypeParameters(),genericType,target.isParameterizedType(),typeVariableAliases); + // } + // return new NewMethodTypeMunger(parameterizedSignature,getSuperMethodsCalled(),typeVariableAliases); + // } + // /** + // * see ResolvedTypeMunger.parameterizedFor(ResolvedType) + // */ + // public ResolvedTypeMunger parameterizedFor(ResolvedType target) { + // ResolvedType genericType = target; + // if (target.isRawType() || target.isParameterizedType()) genericType = genericType.getGenericType(); + // ResolvedMember parameterizedSignature = + // getSignature().parameterizedWith(target.getTypeParameters(),genericType,target.isParameterizedType(),typeVariableAliases); + // return new NewFieldTypeMunger(parameterizedSignature,getSuperMethodsCalled(),typeVariableAliases); + // } + + public void setDeclaredSignature(ResolvedMember rm) { + declaredSignature = rm; + } + + public ResolvedMember getDeclaredSignature() { + return declaredSignature; + } + + /** + * A late munger has to be done after shadow munging since which shadows are matched can affect the operation of the late + * munger. e.g. perobjectinterfacemunger + */ + public boolean isLateMunger() { + return false; + } + + /** + * Some type mungers are created purely to help with the implementation of shadow mungers. For example to support the cflow() + * pointcut we create a new cflow field in the aspect, and that is added via a BcelCflowCounterFieldAdder. + * + * During compilation we need to compare sets of type mungers, and if some only come into existence after the 'shadowy' type + * things have been processed, we need to ignore them during the comparison. + * + * Returning true from this method indicates the type munger exists to support 'shadowy' stuff - and so can be ignored in some + * comparison. + */ + public boolean existsToSupportShadowMunging() { + return false; + } + + public ResolvedTypeMunger parameterizeWith(Map<String, UnresolvedType> m, World w) { + throw new BCException("Dont call parameterizeWith() on a type munger of this kind: " + this.getClass()); + } + + public UnresolvedType getDeclaringType() { + return getSignature().getDeclaringType(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/RuntimeVersion.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/RuntimeVersion.java new file mode 100644 index 000000000..0cf90a9a2 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/RuntimeVersion.java @@ -0,0 +1,47 @@ +/* ******************************************************************* + * Copyright (c) 2018 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 + * ******************************************************************/ +package org.aspectj.weaver; + +/** + * Captures important runtime versions. Typically new versions are added here if something + * changes in the runtime and the code generation may be able to do something different + * (more optimal) for a later runtime. + * + * @author Andy Clement + */ +public enum RuntimeVersion { + + V1_2("1.2"), V1_5("1.5"), V1_6_10("1.6.10"), V1_9("1.9"); + + private String[] aliases = null; + + RuntimeVersion(String... aliases) { + this.aliases = aliases; + } + + public static RuntimeVersion getVersionFor(String version) { + for (RuntimeVersion candidateVersion: values()) { + if (candidateVersion.name().equals(version)) { + return candidateVersion; + } + if (candidateVersion.aliases != null) { + for (String alias: candidateVersion.aliases) { + if (alias.equals(version)) { + return candidateVersion; + } + } + } + } + return null; + } + + public boolean isThisVersionOrLater(RuntimeVersion version) { + return this.compareTo(version) >= 0; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Shadow.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Shadow.java new file mode 100644 index 000000000..587d19c15 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Shadow.java @@ -0,0 +1,687 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.DataInputStream; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.PartialOrder; +import org.aspectj.util.TypeSafeEnum; +import org.aspectj.weaver.ast.Var; + +/* + * The superclass of anything representing a the shadow of a join point. A shadow represents + * some bit of code, and encompasses both entry and exit from that code. All shadows have a kind + * and a signature. + */ + +public abstract class Shadow { + + // every Shadow has a unique id, doesn't matter if it wraps... + private static int nextShadowID = 100; // easier to spot than zero. // OPTIMIZE is this a bug? static? + + private final Kind kind; + private final Member signature; + private Member matchingSignature; + private ResolvedMember resolvedSignature; + protected final Shadow enclosingShadow; + protected List<ShadowMunger> mungers = Collections.emptyList(); + + public int shadowId = nextShadowID++; // every time we build a shadow, it gets a new id + + // ---- + protected Shadow(Kind kind, Member signature, Shadow enclosingShadow) { + this.kind = kind; + this.signature = signature; + this.enclosingShadow = enclosingShadow; + } + + // ---- + + public abstract World getIWorld(); + + public List<ShadowMunger> getMungers() { + return mungers; + } + + /** + * could this(*) pcd ever match + */ + public final boolean hasThis() { + if (getKind().neverHasThis()) { + return false; + } else if (getKind().isEnclosingKind()) { + return !Modifier.isStatic(getSignature().getModifiers()); + } else if (enclosingShadow == null) { + return false; + } else { + return enclosingShadow.hasThis(); + } + } + + /** + * the type of the this object here + * + * @throws IllegalStateException if there is no this here + */ + public final UnresolvedType getThisType() { + if (!hasThis()) { + throw new IllegalStateException("no this"); + } + if (getKind().isEnclosingKind()) { + return getSignature().getDeclaringType(); + } else { + return enclosingShadow.getThisType(); + } + } + + /** + * a var referencing this + * + * @throws IllegalStateException if there is no target here + */ + public abstract Var getThisVar(); + + /** + * could target(*) pcd ever match + */ + public final boolean hasTarget() { + if (getKind().neverHasTarget()) { + return false; + } else if (getKind().isTargetSameAsThis()) { + return hasThis(); + } else { + return !Modifier.isStatic(getSignature().getModifiers()); + } + } + + /** + * the type of the target object here + * + * @throws IllegalStateException if there is no target here + */ + public final UnresolvedType getTargetType() { + if (!hasTarget()) { + throw new IllegalStateException("no target"); + } + return getSignature().getDeclaringType(); + } + + /** + * a var referencing the target + * + * @throws IllegalStateException if there is no target here + */ + public abstract Var getTargetVar(); + + public UnresolvedType[] getArgTypes() { + if (getKind() == FieldSet) { + return new UnresolvedType[] { getSignature().getReturnType() }; + } + return getSignature().getParameterTypes(); + } + + public boolean isShadowForArrayConstructionJoinpoint() { + return (getKind() == ConstructorCall && signature.getDeclaringType().isArray()); + } + + public boolean isShadowForMonitor() { + return (getKind() == SynchronizationLock || getKind() == SynchronizationUnlock); + } + + // will return the right length array of ints depending on how many dimensions the array has + public ResolvedType[] getArgumentTypesForArrayConstructionShadow() { + String s = signature.getDeclaringType().getSignature(); + int pos = s.indexOf("["); + int dims = 1; + while (pos < s.length()) { + pos++; + if (pos < s.length()) { + dims += (s.charAt(pos) == '[' ? 1 : 0); + } + } + ResolvedType intType = UnresolvedType.INT.resolve(this.getIWorld()); + if (dims == 1) { + return new ResolvedType[] { intType }; + } + ResolvedType[] someInts = new ResolvedType[dims]; + for (int i = 0; i < dims; i++) { + someInts[i] = intType; + } + return someInts; + } + + public UnresolvedType[] getGenericArgTypes() { + if (isShadowForArrayConstructionJoinpoint()) { + return getArgumentTypesForArrayConstructionShadow(); + } + if (isShadowForMonitor()) { + return UnresolvedType.ARRAY_WITH_JUST_OBJECT; + } + if (getKind() == FieldSet) { + return new UnresolvedType[] { getResolvedSignature().getGenericReturnType() }; + } + return getResolvedSignature().getGenericParameterTypes(); + } + + public UnresolvedType getArgType(int arg) { + if (getKind() == FieldSet) { + return getSignature().getReturnType(); + } + return getSignature().getParameterTypes()[arg]; + } + + public int getArgCount() { + if (getKind() == FieldSet) { + return 1; + } + return getSignature().getParameterTypes().length; + } + + // /** + // * Return name of the argument at position 'i' at this shadow. This does not make sense for all shadows - but can be useful in + // * the case of, for example, method-execution. + // * + // * @return null if it cannot be determined + // */ + // public String getArgName(int i, World w) { + // String[] names = getSignature().getParameterNames(w); + // if (names == null || i >= names.length) + // return null; + // return names[i]; + // } + + public abstract UnresolvedType getEnclosingType(); + + public abstract Var getArgVar(int i); + + public abstract Var getThisJoinPointVar(); + + public abstract Var getThisJoinPointStaticPartVar(); + + public abstract Var getThisEnclosingJoinPointStaticPartVar(); + + public abstract Var getThisAspectInstanceVar(ResolvedType aspectType); + + // annotation variables + public abstract Var getKindedAnnotationVar(UnresolvedType forAnnotationType); + + public abstract Var getWithinAnnotationVar(UnresolvedType forAnnotationType); + + public abstract Var getWithinCodeAnnotationVar(UnresolvedType forAnnotationType); + + public abstract Var getThisAnnotationVar(UnresolvedType forAnnotationType); + + public abstract Var getTargetAnnotationVar(UnresolvedType forAnnotationType); + + public abstract Var getArgAnnotationVar(int i, UnresolvedType forAnnotationType); + + public abstract Member getEnclosingCodeSignature(); + + /** + * returns the kind of shadow this is, representing what happens under this shadow + */ + public Kind getKind() { + return kind; + } + + /** + * returns the signature of the thing under this shadow + */ + public Member getSignature() { + return signature; + } + + /** + * returns the signature of the thing under this shadow, with any synthetic arguments removed + */ + public Member getMatchingSignature() { + return matchingSignature != null ? matchingSignature : signature; + } + + public void setMatchingSignature(Member member) { + this.matchingSignature = member; + } + + /** + * returns the resolved signature of the thing under this shadow + * + */ + public ResolvedMember getResolvedSignature() { + if (resolvedSignature == null) { + resolvedSignature = signature.resolve(getIWorld()); + } + return resolvedSignature; + } + + public UnresolvedType getReturnType() { + if (kind == ConstructorCall) { + return getSignature().getDeclaringType(); + } else if (kind == FieldSet) { + return UnresolvedType.VOID; + } else if (kind == SynchronizationLock || kind == SynchronizationUnlock) { + return UnresolvedType.VOID; + } + return getResolvedSignature().getGenericReturnType(); + } + + public static String METHOD_EXECUTION = "method-execution"; + public static String METHOD_CALL = "method-call"; + public static String CONSTRUCTOR_EXECUTION = "constructor-execution"; + public static String CONSTRUCTOR_CALL = "constructor-call"; + public static String FIELD_GET = "field-get"; + public static String FIELD_SET = "field-set"; + public static String STATICINITIALIZATION = "staticinitialization"; + public static String PREINITIALIZATION = "preinitialization"; + public static String INITIALIZATION = "initialization"; + public static String EXCEPTION_HANDLER = "exception-handler"; + public static String SYNCHRONIZATION_LOCK = "lock"; + public static String SYNCHRONIZATION_UNLOCK = "unlock"; + public static String ADVICE_EXECUTION = "adviceexecution"; + + /** + * These names are the ones that will be returned by thisJoinPoint.getKind() Those need to be documented somewhere + */ + public static final Kind MethodCall = new Kind(METHOD_CALL, 1, true); + public static final Kind ConstructorCall = new Kind(CONSTRUCTOR_CALL, 2, true); + public static final Kind MethodExecution = new Kind(METHOD_EXECUTION, 3, false); + public static final Kind ConstructorExecution = new Kind(CONSTRUCTOR_EXECUTION, 4, false); + public static final Kind FieldGet = new Kind(FIELD_GET, 5, true); + public static final Kind FieldSet = new Kind(FIELD_SET, 6, true); + public static final Kind StaticInitialization = new Kind(STATICINITIALIZATION, 7, false); + public static final Kind PreInitialization = new Kind(PREINITIALIZATION, 8, false); + public static final Kind AdviceExecution = new Kind(ADVICE_EXECUTION, 9, false); + public static final Kind Initialization = new Kind(INITIALIZATION, 10, false); + public static final Kind ExceptionHandler = new Kind(EXCEPTION_HANDLER, 11, true); + public static final Kind SynchronizationLock = new Kind(SYNCHRONIZATION_LOCK, 12, true); + public static final Kind SynchronizationUnlock = new Kind(SYNCHRONIZATION_UNLOCK, 13, true); + + // Bits here are 1<<(Kind.getKey()) - and unfortunately keys didn't start at zero so bits here start at 2 + public static final int MethodCallBit = 0x002; + public static final int ConstructorCallBit = 0x004; + public static final int MethodExecutionBit = 0x008; + public static final int ConstructorExecutionBit = 0x010; + public static final int FieldGetBit = 0x020; + public static final int FieldSetBit = 0x040; + public static final int StaticInitializationBit = 0x080; + public static final int PreInitializationBit = 0x100; + public static final int AdviceExecutionBit = 0x200; + public static final int InitializationBit = 0x400; + public static final int ExceptionHandlerBit = 0x800; + public static final int SynchronizationLockBit = 0x1000; + public static final int SynchronizationUnlockBit = 0x2000; + + public static final int MAX_SHADOW_KIND = 13; + public static final Kind[] SHADOW_KINDS = new Kind[] { MethodCall, ConstructorCall, MethodExecution, ConstructorExecution, + FieldGet, FieldSet, StaticInitialization, PreInitialization, AdviceExecution, Initialization, ExceptionHandler, + SynchronizationLock, SynchronizationUnlock }; + + public static final int ALL_SHADOW_KINDS_BITS; + public static final int NO_SHADOW_KINDS_BITS; + + static { + ALL_SHADOW_KINDS_BITS = 0x3ffe; + NO_SHADOW_KINDS_BITS = 0x0000; + } + + /** + * Return count of how many bits set in the supplied parameter. + */ + public static int howMany(int i) { + int count = 0; + for (int j = 0; j < SHADOW_KINDS.length; j++) { + if ((i & SHADOW_KINDS[j].bit) != 0) { + count++; + } + } + return count; + } + + /** + * A type-safe enum representing the kind of shadows + */ + public static final class Kind extends TypeSafeEnum { + // private boolean argsOnStack; //XXX unused + + public int bit; + + public Kind(String name, int key, boolean argsOnStack) { + super(name, key); + bit = 1 << key; + // this.argsOnStack = argsOnStack; + } + + public String toLegalJavaIdentifier() { + return getName().replace('-', '_'); + } + + public boolean argsOnStack() { + return !isTargetSameAsThis(); + } + + // false for handlers + public boolean allowsExtraction() { + return true; + } + + public boolean isSet(int i) { + return (i & bit) != 0; + } + + // XXX revisit along with removal of priorities + public boolean hasHighPriorityExceptions() { + return !isTargetSameAsThis(); + } + + private final static int hasReturnValueFlag = MethodCallBit | ConstructorCallBit | MethodExecutionBit | FieldGetBit + | AdviceExecutionBit; + + /** + * These shadow kinds have return values that can be bound in after returning(Dooberry doo) advice. + * + * @return + */ + public boolean hasReturnValue() { + return (bit & hasReturnValueFlag) != 0; + } + + private final static int isEnclosingKindFlag = MethodExecutionBit | ConstructorExecutionBit | AdviceExecutionBit + | StaticInitializationBit | InitializationBit; + + /** + * These are all the shadows that contains other shadows within them and are often directly associated with methods. + */ + public boolean isEnclosingKind() { + return (bit & isEnclosingKindFlag) != 0; + } + + private final static int isTargetSameAsThisFlag = MethodExecutionBit | ConstructorExecutionBit | StaticInitializationBit + | PreInitializationBit | AdviceExecutionBit | InitializationBit; + + public boolean isTargetSameAsThis() { + return (bit & isTargetSameAsThisFlag) != 0; + } + + private final static int neverHasTargetFlag = ConstructorCallBit | ExceptionHandlerBit | PreInitializationBit + | StaticInitializationBit | SynchronizationLockBit | SynchronizationUnlockBit; + + public boolean neverHasTarget() { + return (bit & neverHasTargetFlag) != 0; + } + + private final static int neverHasThisFlag = PreInitializationBit | StaticInitializationBit; + + public boolean neverHasThis() { + return (bit & neverHasThisFlag) != 0; + } + + public String getSimpleName() { + int dash = getName().lastIndexOf('-'); + if (dash == -1) { + return getName(); + } else { + return getName().substring(dash + 1); + } + } + + public static Kind read(DataInputStream s) throws IOException { + int key = s.readByte(); + switch (key) { + case 1: + return MethodCall; + case 2: + return ConstructorCall; + case 3: + return MethodExecution; + case 4: + return ConstructorExecution; + case 5: + return FieldGet; + case 6: + return FieldSet; + case 7: + return StaticInitialization; + case 8: + return PreInitialization; + case 9: + return AdviceExecution; + case 10: + return Initialization; + case 11: + return ExceptionHandler; + case 12: + return SynchronizationLock; + case 13: + return SynchronizationUnlock; + } + throw new BCException("unknown kind: " + key); + } + } + + /** + * Only does the check if the munger requires it (@AJ aspects don't) + * + * @param munger + * @return + */ + protected boolean checkMunger(ShadowMunger munger) { + if (munger.mustCheckExceptions()) { + for (Iterator<ResolvedType> i = munger.getThrownExceptions().iterator(); i.hasNext();) { + if (!checkCanThrow(munger, i.next())) { + return false; + } + } + } + return true; + } + + protected boolean checkCanThrow(ShadowMunger munger, ResolvedType resolvedTypeX) { + if (getKind() == ExceptionHandler) { + // XXX much too lenient rules here, need to walk up exception handlers + return true; + } + + if (!isDeclaredException(resolvedTypeX, getSignature())) { + getIWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_THROW_CHECKED, resolvedTypeX, this), // from + // advice + // in + // \ + // '" + // + + // munger + // . + // + + // "\'" + // , + getSourceLocation(), munger.getSourceLocation()); + } + + return true; + } + + private boolean isDeclaredException(ResolvedType resolvedTypeX, Member member) { + ResolvedType[] excs = getIWorld().resolve(member.getExceptions(getIWorld())); + for (int i = 0, len = excs.length; i < len; i++) { + if (excs[i].isAssignableFrom(resolvedTypeX)) { + return true; + } + } + return false; + } + + public void addMunger(ShadowMunger munger) { + if (checkMunger(munger)) { + if (mungers == Collections.EMPTY_LIST) { + mungers = new ArrayList<ShadowMunger>(); + } + this.mungers.add(munger); + } + } + + public final void implement() { + sortMungers(); + if (mungers == null) { + return; + } + prepareForMungers(); + implementMungers(); + } + + private void sortMungers() { + + List sorted = PartialOrder.sort(mungers); + + // Bunch of code to work out whether to report xlints for advice that isn't ordered at this Joinpoint + possiblyReportUnorderedAdvice(sorted); + + if (sorted == null) { + // this means that we have circular dependencies + for (ShadowMunger m : mungers) { + getIWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.CIRCULAR_DEPENDENCY, this), m.getSourceLocation())); + } + } + mungers = sorted; + } + + // not quite optimal... but the xlint is ignore by default + private void possiblyReportUnorderedAdvice(List sorted) { + if (sorted != null && getIWorld().getLint().unorderedAdviceAtShadow.isEnabled() && mungers.size() > 1) { + + // Stores a set of strings of the form 'aspect1:aspect2' which indicates there is no + // precedence specified between the two aspects at this shadow. + Set<String> clashingAspects = new HashSet<String>(); + int max = mungers.size(); + + // Compare every pair of advice mungers + for (int i = max - 1; i >= 0; i--) { + for (int j = 0; j < i; j++) { + Object a = mungers.get(i); + Object b = mungers.get(j); + + // Make sure they are the right type + if (a instanceof Advice && b instanceof Advice) { + Advice adviceA = (Advice) a; + Advice adviceB = (Advice) b; + if (!adviceA.concreteAspect.equals(adviceB.concreteAspect)) { + AdviceKind adviceKindA = adviceA.getKind(); + AdviceKind adviceKindB = adviceB.getKind(); + + // make sure they are the nice ones (<6) and not any synthetic advice ones we + // create to support other features of the language. + if (adviceKindA.getKey() < (byte) 6 && adviceKindB.getKey() < (byte) 6 + && adviceKindA.getPrecedence() == adviceKindB.getPrecedence()) { + + // Ask the world if it knows about precedence between these + Integer order = getIWorld().getPrecedenceIfAny(adviceA.concreteAspect, adviceB.concreteAspect); + + if (order != null && order.equals(new Integer(0))) { + String key = adviceA.getDeclaringAspect() + ":" + adviceB.getDeclaringAspect(); + String possibleExistingKey = adviceB.getDeclaringAspect() + ":" + adviceA.getDeclaringAspect(); + if (!clashingAspects.contains(possibleExistingKey)) { + clashingAspects.add(key); + } + } + } + } + } + } + } + for (Iterator<String> iter = clashingAspects.iterator(); iter.hasNext();) { + String element = iter.next(); + String aspect1 = element.substring(0, element.indexOf(":")); + String aspect2 = element.substring(element.indexOf(":") + 1); + getIWorld().getLint().unorderedAdviceAtShadow.signal(new String[] { this.toString(), aspect1, aspect2 }, + this.getSourceLocation(), null); + } + } + } + + /** + * Prepare the shadow for implementation. After this is done, the shadow should be in such a position that each munger simply + * needs to be implemented. + */ + protected void prepareForMungers() { + throw new RuntimeException("Generic shadows cannot be prepared"); + } + + /** Actually implement the (non-empty) mungers associated with this shadow */ + private void implementMungers() { + World world = getIWorld(); + for (ShadowMunger munger : mungers) { + if (munger.implementOn(this)) { + world.reportMatch(munger, this); + } + } + } + + public abstract ISourceLocation getSourceLocation(); + + // ---- utility + + public String toString() { + return getKind() + "(" + getSignature() + ")"; // + getSourceLines(); + } + + public String toResolvedString(World world) { + StringBuffer sb = new StringBuffer(); + sb.append(getKind()); + sb.append("("); + Member m = getSignature(); + if (m == null) { + sb.append("<<missing signature>>"); + } else { + ResolvedMember rm = world.resolve(m); + if (rm == null) { + sb.append("<<unresolvableMember:").append(m).append(">>"); + } else { + String genString = rm.toGenericString(); + if (genString == null) { + sb.append("<<unableToGetGenericStringFor:").append(rm).append(">>"); + } else { + sb.append(genString); + } + + } + } + sb.append(")"); + return sb.toString(); + // was: return getKind() + "(" + world.resolve(getSignature()).toGenericString() + ")"; + } + + /** + * Convert a bit array for the shadow kinds into a set of them... should only be used for testing - mainline code should do bit + * manipulation! + */ + public static Set<Kind> toSet(int i) { + Set<Kind> results = new HashSet<Kind>(); + for (int j = 0; j < Shadow.SHADOW_KINDS.length; j++) { + Kind k = Shadow.SHADOW_KINDS[j]; + if (k.isSet(i)) { + results.add(k); + } + } + return results; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ShadowMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ShadowMunger.java new file mode 100644 index 000000000..c07d7e8e7 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ShadowMunger.java @@ -0,0 +1,307 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.bridge.SourceLocation; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.util.PartialOrder; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.TypePattern; + +/** + * For every shadow munger, nothing can be done with it until it is concretized. Then... + * + * (Then we call fast match.) + * + * For every shadow munger, for every shadow, first match is called, then (if match returned true) the shadow munger is specialized + * for the shadow, which may modify state. Then implement is called. + */ +public abstract class ShadowMunger implements PartialOrder.PartialComparable, IHasPosition { + + public static final ShadowMunger[] NONE = new ShadowMunger[0]; + + private static int VERSION_1 = 1; // ShadowMunger version for serialization + + protected static final int ShadowMungerAdvice = 1; + protected static final int ShadowMungerDeow = 2; + + public String handle = null; + + private int shadowMungerKind; + + protected int start, end; + protected ISourceContext sourceContext; + private ISourceLocation sourceLocation; + private ISourceLocation binarySourceLocation; + private File binaryFile; + private ResolvedType declaringType; + private boolean isBinary; + private boolean checkedIsBinary; + + protected Pointcut pointcut; + + protected ShadowMunger() { + } + + public ShadowMunger(Pointcut pointcut, int start, int end, ISourceContext sourceContext, int shadowMungerKind) { + this.shadowMungerKind = shadowMungerKind; + this.pointcut = pointcut; + this.start = start; + this.end = end; + this.sourceContext = sourceContext; + } + + /** + * All overriding methods should call super + */ + public boolean match(Shadow shadow, World world) { + if (world.isXmlConfigured() && world.isAspectIncluded(declaringType)) { + TypePattern scoped = world.getAspectScope(declaringType); + if (scoped != null) { + // Check the 'cached' exclusion map + Set<ResolvedType> excludedTypes = world.getExclusionMap().get(declaringType); + ResolvedType type = shadow.getEnclosingType().resolve(world); + if (excludedTypes != null && excludedTypes.contains(type)) { + return false; + } + boolean b = scoped.matches(type, TypePattern.STATIC).alwaysTrue(); + if (!b) { + if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) { + world.getMessageHandler().handleMessage( + MessageUtil.info("Type '" + type.getName() + "' not woven by aspect '" + declaringType.getName() + + "' due to scope exclusion in XML definition")); + } + if (excludedTypes == null) { + excludedTypes = new HashSet<ResolvedType>(); + excludedTypes.add(type); + world.getExclusionMap().put(declaringType, excludedTypes); + } else { + excludedTypes.add(type); + } + return false; + } + } + } + if (world.areInfoMessagesEnabled() && world.isTimingEnabled()) { + long starttime = System.nanoTime(); + FuzzyBoolean isMatch = pointcut.match(shadow); + long endtime = System.nanoTime(); + world.record(pointcut, endtime - starttime); + return isMatch.maybeTrue(); + } else { + FuzzyBoolean isMatch = pointcut.match(shadow); + return isMatch.maybeTrue(); + } + } + + public int fallbackCompareTo(Object other) { + return toString().compareTo(toString()); + } + + public int getEnd() { + return end; + } + + public int getStart() { + return start; + } + + public ISourceLocation getSourceLocation() { + if (sourceLocation == null) { + if (sourceContext != null) { + sourceLocation = sourceContext.makeSourceLocation(this); + } + } + if (isBinary()) { + if (binarySourceLocation == null) { + binarySourceLocation = getBinarySourceLocation(sourceLocation); + } + return binarySourceLocation; + } + return sourceLocation; + } + + public Pointcut getPointcut() { + return pointcut; + } + + // pointcut may be updated during rewriting... + public void setPointcut(Pointcut pointcut) { + this.pointcut = pointcut; + } + + /** + * Invoked when the shadow munger of a resolved type are processed. + * + * @param aType + */ + public void setDeclaringType(ResolvedType aType) { + declaringType = aType; + } + + public ResolvedType getDeclaringType() { + return declaringType; + } + + public abstract ResolvedType getConcreteAspect(); + + /** + * Returns the binarySourceLocation for the given sourcelocation. This isn't cached because it's used when faulting in the + * binary nodes and is called with ISourceLocations for all advice, pointcuts and deows contained within the + * resolvedDeclaringAspect. + */ + public ISourceLocation getBinarySourceLocation(ISourceLocation sl) { + if (sl == null) { + return null; + } + String sourceFileName = null; + if (getDeclaringType() instanceof ReferenceType) { + String s = ((ReferenceType) getDeclaringType()).getDelegate().getSourcefilename(); + int i = s.lastIndexOf('/'); + if (i != -1) { + sourceFileName = s.substring(i + 1); + } else { + sourceFileName = s; + } + } + ISourceLocation sLoc = new SourceLocation(getBinaryFile(), sl.getLine(), sl.getEndLine(), + ((sl.getColumn() == 0) ? ISourceLocation.NO_COLUMN : sl.getColumn()), sl.getContext(), sourceFileName); + return sLoc; + } + + /** + * Returns the File with pathname to the class file, for example either:<br> + * C:\temp \ajcSandbox\workspace\ajcTest16957.tmp\simple.jar!pkg\BinaryAspect.class if the class file is in a jar file, or <br> + * C:\temp\ajcSandbox\workspace\ajcTest16957.tmp!pkg\BinaryAspect.class if the class file is in a directory + */ + private File getBinaryFile() { + if (binaryFile == null) { + String binaryPath = getDeclaringType().getBinaryPath(); + if (binaryPath == null) { + // Looks like an aspect that has been picked up from the classpath (likely an abstract one + // being extended). As it didn't come in via inpath or aspectpath the binarypath has not + // yet been constructed. + + // We can't discover where the file came from now, that info has been lost. So just + // use "classpath" for now - until we discover we need to get this right. + + binaryPath = "classpath"; + getDeclaringType().setBinaryPath(binaryPath); + // ReferenceTypeDelegate delegate = ((ReferenceType) getDeclaringType()).getDelegate(); + // if (delegate instanceof BcelObjectType) { + // grab javaclass... but it doesnt know the originating file + // } + } + if (binaryPath.indexOf("!") == -1) { + File f = getDeclaringType().getSourceLocation().getSourceFile(); + // Replace the source file suffix with .class + int i = f.getPath().lastIndexOf('.'); + String path = null; + if (i != -1) { + path = f.getPath().substring(0, i) + ".class"; + } else { + path = f.getPath() + ".class"; + } + binaryFile = new File(binaryPath + "!" + path); + } else { + binaryFile = new File(binaryPath); + } + } + return binaryFile; + } + + /** + * Returns whether or not this shadow munger came from a binary aspect - keep a record of whether or not we've checked if we're + * binary otherwise we keep calculating the same thing many times + */ + public boolean isBinary() { + if (!checkedIsBinary) { + ResolvedType rt = getDeclaringType(); + if (rt != null) { + isBinary = ((rt.getBinaryPath() == null) ? false : true); + } + checkedIsBinary = true; + } + return isBinary; + } + + public abstract ShadowMunger concretize(ResolvedType fromType, World world, PerClause clause); + + public abstract void specializeOn(Shadow shadow); + + /** + * Implement this munger at the specified shadow, returning a boolean to indicate success. + * + * @param shadow the shadow where this munger should be applied + * @return true if the implement was successful + */ + public abstract boolean implementOn(Shadow shadow); + + public abstract ShadowMunger parameterizeWith(ResolvedType declaringType, Map<String, UnresolvedType> typeVariableMap); + + /** + * @return a Collection of ResolvedTypes for all checked exceptions that might be thrown by this munger + */ + public abstract Collection<ResolvedType> getThrownExceptions(); + + /** + * Does the munger have to check that its exception are accepted by the shadow ? It is not the case for annotation style around + * advice, for example: that can throw Throwable, even if the advised method does not throw any exceptions. + * + * @return true if munger has to check that its exceptions can be thrown based on the shadow + */ + public abstract boolean mustCheckExceptions(); + + public void write(CompressingDataOutputStream stream) throws IOException { + stream.writeInt(VERSION_1); + stream.writeInt(shadowMungerKind); // determines real subclass + stream.writeInt(start); + stream.writeInt(end); + PersistenceSupport.write(stream, sourceContext); + PersistenceSupport.write(stream, sourceLocation); + PersistenceSupport.write(stream, binarySourceLocation); + PersistenceSupport.write(stream, binaryFile); + declaringType.write(stream); + stream.writeBoolean(isBinary); + stream.writeBoolean(checkedIsBinary); + pointcut.write(stream); + } + + // + // public static ShadowMunger read(VersionedDataInputStream stream, World world) throws IOException { + // stream.readInt(); + // int kind = stream.readInt(); + // ShadowMunger newShadowMunger = null; + // switch (kind) { + // case ShadowMungerAdvice: + // // world.getWeavingSupport().createAdviceMunger(attribute, pointcut, signature) + // case ShadowMungerDeow: + // newShadowMunger = Checker.read(stream, world); + // default: + // throw new IllegalStateException("Unexpected type of shadow munger found on deserialization: " + kind); + // } + // newShadowMunger.binaryFile = null; + // } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/SignatureUtils.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/SignatureUtils.java new file mode 100644 index 000000000..d32f7d99f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/SignatureUtils.java @@ -0,0 +1,240 @@ +/* ******************************************************************* + * 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 - refactored out of MemberImpl + * ******************************************************************/ +package org.aspectj.weaver; + +import java.lang.reflect.Modifier; + +public class SignatureUtils { + + public static String getSignatureString(Member m, World world) { + MemberKind kind = m.getKind(); + if (kind == Member.METHOD) { + return getMethodSignatureString(m, world); + } else if (kind == Member.CONSTRUCTOR) { + return getConstructorSignatureString(m, world); + } else if (kind == Member.FIELD) { + return getFieldSignatureString(m, world); + } else if (kind == Member.HANDLER) { + return getHandlerSignatureString(m, world); + } else if (kind == Member.STATIC_INITIALIZATION) { + return getStaticInitializationSignatureString(m, world); + } else if (kind == Member.ADVICE) { + return getAdviceSignatureString(m, world); + } else if (kind == Member.MONITORENTER || kind == Member.MONITOREXIT) { + return getMonitorSignatureString(m, world); + } else { + throw new BCException("Do not know the signature string for MemberKind " + kind); + } + } + + public static String getSignatureMakerName(Member m) { + MemberKind kind = m.getKind(); + if (kind == Member.METHOD) { + return "makeMethodSig"; + } else if (kind == Member.CONSTRUCTOR) { + return "makeConstructorSig"; + } else if (kind == Member.FIELD) { + return "makeFieldSig"; + } else if (kind == Member.HANDLER) { + return "makeCatchClauseSig"; + } else if (kind == Member.STATIC_INITIALIZATION) { + return "makeInitializerSig"; + } else if (kind == Member.ADVICE) { + return "makeAdviceSig"; + } else if (kind == Member.MONITORENTER) { + return "makeLockSig"; + } else if (kind == Member.MONITOREXIT) { + return "makeUnlockSig"; + } else { + throw new BCException("Do not know the signature maker name for MemberKind " + kind); + } + } + + public static String getSignatureType(Member m) { + MemberKind kind = m.getKind(); + if (m.getName().equals("<clinit>") && kind != Member.STATIC_INITIALIZATION) + throw new BCException(); + // if (m.getName().equals("<clinit>")) return "org.aspectj.lang.reflect.InitializerSignature"; + + if (kind == Member.METHOD) { + return "org.aspectj.lang.reflect.MethodSignature"; + } else if (kind == Member.CONSTRUCTOR) { + return "org.aspectj.lang.reflect.ConstructorSignature"; + } else if (kind == Member.FIELD) { + return "org.aspectj.lang.reflect.FieldSignature"; + } else if (kind == Member.HANDLER) { + return "org.aspectj.lang.reflect.CatchClauseSignature"; + } else if (kind == Member.STATIC_INITIALIZATION) { + return "org.aspectj.lang.reflect.InitializerSignature"; + } else if (kind == Member.ADVICE) { + return "org.aspectj.lang.reflect.AdviceSignature"; + } else if (kind == Member.MONITORENTER) { + return "org.aspectj.lang.reflect.LockSignature"; + } else if (kind == Member.MONITOREXIT) { + return "org.aspectj.lang.reflect.UnlockSignature"; + } else { + throw new BCException("Do not know the signature type for MemberKind " + kind); + } + } + + // --- + + private static String getHandlerSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(0)); + buf.append('-'); + // buf.append(getName()); + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); + buf.append('-'); + buf.append(makeString(m.getParameterTypes()[0])); + buf.append('-'); + String pName = "<missing>"; + String[] names = m.getParameterNames(world); + if (names != null) + pName = names[0]; + buf.append(pName); + buf.append('-'); + return buf.toString(); + } + + private static String getStaticInitializationSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(m.getModifiers(world))); + buf.append('-'); + // buf.append(getName()); + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); + buf.append('-'); + return buf.toString(); + } + + protected static String getAdviceSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(m.getModifiers(world))); + buf.append('-'); + buf.append(m.getName()); + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); + buf.append('-'); + buf.append(makeString(m.getParameterTypes())); + buf.append('-'); + buf.append(makeString(m.getParameterNames(world))); + buf.append('-'); + buf.append(makeString(m.getExceptions(world))); + buf.append('-'); + buf.append(makeString(m.getReturnType())); + buf.append('-'); + return buf.toString(); + } + + protected static String getMethodSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(m.getModifiers(world))); + buf.append('-'); + buf.append(m.getName()); + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); + buf.append('-'); + buf.append(makeString(m.getParameterTypes())); + buf.append('-'); + buf.append(makeString(m.getParameterNames(world))); + buf.append('-'); + buf.append(makeString(m.getExceptions(world))); + buf.append('-'); + buf.append(makeString(m.getReturnType())); + buf.append('-'); + return buf.toString(); + } + + protected static String getMonitorSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(Modifier.STATIC)); // modifiers + buf.append('-'); + buf.append(m.getName()); // name + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); // Declaring Type + buf.append('-'); + buf.append(makeString(m.getParameterTypes()[0])); // Parameter Types + buf.append('-'); + buf.append(""); // Parameter names + buf.append('-'); + return buf.toString(); + } + + protected static String getConstructorSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(m.getModifiers(world))); + buf.append('-'); + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); + buf.append('-'); + buf.append(makeString(m.getParameterTypes())); + buf.append('-'); + buf.append(makeString(m.getParameterNames(world))); + buf.append('-'); + buf.append(makeString(m.getExceptions(world))); + buf.append('-'); + return buf.toString(); + } + + protected static String getFieldSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(m.getModifiers(world))); + buf.append('-'); + buf.append(m.getName()); + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); + buf.append('-'); + buf.append(makeString(m.getReturnType())); + buf.append('-'); + return buf.toString(); + } + + protected static String makeString(int i) { + return Integer.toString(i, 16); + } + + protected static String makeString(UnresolvedType t) { + // this is the inverse of the odd behavior for Class.forName w/ arrays + if (t.isArray()) { + // this behavior matches the string used by the eclipse compiler for Foo.class literals + return t.getSignature().replace('/', '.'); + } else { + return t.getName(); + } + } + + protected static String makeString(UnresolvedType[] types) { + if (types == null) + return ""; + StringBuffer buf = new StringBuffer(); + for (int i = 0, len = types.length; i < len; i++) { + buf.append(makeString(types[i])); + buf.append(':'); + } + return buf.toString(); + } + + protected static String makeString(String[] names) { + if (names == null) + return ""; + StringBuffer buf = new StringBuffer(); + for (int i = 0, len = names.length; i < len; i++) { + buf.append(names[i]); + buf.append(':'); + } + return buf.toString(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/SimpleAnnotationValue.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/SimpleAnnotationValue.java new file mode 100644 index 000000000..312f7726b --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/SimpleAnnotationValue.java @@ -0,0 +1,110 @@ +/* ******************************************************************* + * Copyright (c) 2006 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 IBM initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +public class SimpleAnnotationValue extends AnnotationValue { + + public SimpleAnnotationValue(int kind) { + super(kind); + } + + public SimpleAnnotationValue(int kind, Object value) { + super(kind); + switch (kind) { + case AnnotationValue.PRIMITIVE_BYTE: + theByte = ((Byte) value).byteValue(); + break; + case AnnotationValue.PRIMITIVE_CHAR: + theChar = ((Character) value).charValue(); + break; + case AnnotationValue.PRIMITIVE_INT: + theInt = ((Integer) value).intValue(); + break; + case AnnotationValue.STRING: + theString = (String) value; + break; + case AnnotationValue.PRIMITIVE_DOUBLE: + theDouble = ((Double) value).doubleValue(); + break; + case AnnotationValue.PRIMITIVE_FLOAT: + theFloat = ((Float) value).floatValue(); + break; + case AnnotationValue.PRIMITIVE_LONG: + theLong = ((Long) value).longValue(); + break; + case AnnotationValue.PRIMITIVE_SHORT: + theShort = ((Short) value).shortValue(); + break; + case AnnotationValue.PRIMITIVE_BOOLEAN: + theBoolean = ((Boolean) value).booleanValue(); + break; + default: + throw new BCException("Not implemented for this kind: " + whatKindIsThis(kind)); + } + } + + private byte theByte; + private char theChar; + private int theInt; + private String theString; + private double theDouble; + private float theFloat; + private long theLong; + private short theShort; + private boolean theBoolean; + + public void setValueString(String s) { + theString = s; + } + + public void setValueByte(byte b) { + theByte = b; + } + + public void setValueChar(char c) { + theChar = c; + } + + public void setValueInt(int i) { + theInt = i; + } + + public String stringify() { + switch (valueKind) { + case 'B': // byte + return Byte.toString(theByte); + case 'C': // char + return new Character(theChar).toString(); + case 'D': // double + return Double.toString(theDouble); + case 'F': // float + return Float.toString(theFloat); + case 'I': // int + return Integer.toString(theInt); + case 'J': // long + return Long.toString(theLong); + case 'S': // short + return Short.toString(theShort); + case 'Z': // boolean + return new Boolean(theBoolean).toString(); + case 's': // String + return theString; + default: + throw new BCException("Do not understand this kind: " + valueKind); + } + } + + public String toString() { + return stringify(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/SourceContextImpl.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/SourceContextImpl.java new file mode 100644 index 000000000..2ae91c649 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/SourceContextImpl.java @@ -0,0 +1,98 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.File; +import java.util.Arrays; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.SourceLocation; + +public class SourceContextImpl implements ISourceContext { + + private int[] lineBreaks; + String sourceFilename; + + public SourceContextImpl(AbstractReferenceTypeDelegate delegate) { + sourceFilename = delegate.getSourcefilename(); + } + + public void configureFromAttribute(String name, int[] linebreaks) { + this.sourceFilename = name; + this.lineBreaks = linebreaks; + } + + public void setSourceFileName(String name) { + sourceFilename = name; + } + + private File getSourceFile() { + return new File(sourceFilename); + } + + public void tidy() { + } + + public int getOffset() { + return 0; + } + + public ISourceLocation makeSourceLocation(IHasPosition position) { + if (lineBreaks != null) { + int line = Arrays.binarySearch(lineBreaks, position.getStart()); + if (line < 0) { + line = -line; + } + return new SourceLocation(getSourceFile(), line); // ??? have more info + } else { + return new SourceLocation(getSourceFile(), 0); + } + } + + public ISourceLocation makeSourceLocation(int line, int offset) { + if (line < 0) { + line = 0; + } + SourceLocation sl = new SourceLocation(getSourceFile(), line); + if (offset > 0) { + sl.setOffset(offset); + } else { + if (lineBreaks != null) { + int likelyOffset = 0; + if (line > 0 && line < lineBreaks.length) { + // 1st char of given line is next char after previous end of line + likelyOffset = lineBreaks[line - 1] + 1; + } + sl.setOffset(likelyOffset); + } + } + return sl; + } + + public final static ISourceContext UNKNOWN_SOURCE_CONTEXT = new ISourceContext() { + public ISourceLocation makeSourceLocation(IHasPosition position) { + return null; + } + + public ISourceLocation makeSourceLocation(int line, int offset) { + return null; + } + + public int getOffset() { + return 0; + } + + public void tidy() { + } + }; +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/StandardAnnotation.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/StandardAnnotation.java new file mode 100644 index 000000000..475b0977f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/StandardAnnotation.java @@ -0,0 +1,157 @@ +/* ******************************************************************* + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * This type represents the weavers abstraction of an annotation - it is not tied to any underlying BCI toolkit. The weaver actualy + * handles these through AnnotationX wrapper objects - until we start transforming the BCEL annotations into this form (expensive) + * or offer a clever visitor mechanism over the BCEL annotation stuff that builds these annotation types directly. + * + * @author AndyClement + */ +public class StandardAnnotation extends AbstractAnnotationAJ { + + private final boolean isRuntimeVisible; + + private List<AnnotationNameValuePair> nvPairs = null; + + public StandardAnnotation(ResolvedType type, boolean isRuntimeVisible) { + super(type); + this.isRuntimeVisible = isRuntimeVisible; + } + + /** + * {@inheritDoc} + */ + public boolean isRuntimeVisible() { + return isRuntimeVisible; + } + + /** + * {@inheritDoc} + */ + public String stringify() { + StringBuffer sb = new StringBuffer(); + sb.append("@").append(type.getClassName()); + if (hasNameValuePairs()) { + sb.append("("); + for (AnnotationNameValuePair nvPair : nvPairs) { + sb.append(nvPair.stringify()); + } + sb.append(")"); + } + return sb.toString(); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("Anno[" + getTypeSignature() + " " + (isRuntimeVisible ? "rVis" : "rInvis")); + if (nvPairs != null) { + sb.append(" "); + for (Iterator<AnnotationNameValuePair> iter = nvPairs.iterator(); iter.hasNext();) { + AnnotationNameValuePair element = iter.next(); + sb.append(element.toString()); + if (iter.hasNext()) { + sb.append(","); + } + } + } + sb.append("]"); + return sb.toString(); + } + + /** + * {@inheritDoc} + */ + public boolean hasNamedValue(String n) { + if (nvPairs == null) { + return false; + } + for (int i = 0; i < nvPairs.size(); i++) { + AnnotationNameValuePair pair = nvPairs.get(i); + if (pair.getName().equals(n)) { + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + public boolean hasNameValuePair(String n, String v) { + if (nvPairs == null) { + return false; + } + for (int i = 0; i < nvPairs.size(); i++) { + AnnotationNameValuePair pair = nvPairs.get(i); + if (pair.getName().equals(n)) { + if (pair.getValue().stringify().equals(v)) { + return true; + } + } + } + return false; + } + + /** + * {@inheritDoc} + */ + public Set<String> getTargets() { + if (!type.equals(UnresolvedType.AT_TARGET)) { + return Collections.emptySet(); + } + AnnotationNameValuePair nvp = nvPairs.get(0); + ArrayAnnotationValue aav = (ArrayAnnotationValue) nvp.getValue(); + AnnotationValue[] avs = aav.getValues(); + Set<String> targets = new HashSet<String>(); + for (int i = 0; i < avs.length; i++) { + EnumAnnotationValue value = (EnumAnnotationValue)avs[i]; + targets.add(value.getValue()); + } + return targets; + } + + public List<AnnotationNameValuePair> getNameValuePairs() { + return nvPairs; + } + + public boolean hasNameValuePairs() { + return nvPairs != null && nvPairs.size() != 0; + } + + public void addNameValuePair(AnnotationNameValuePair pair) { + if (nvPairs == null) { + nvPairs = new ArrayList<AnnotationNameValuePair>(); + } + nvPairs.add(pair); + } + + /** + * {@inheritDoc} + */ + public String getStringFormOfValue(String name) { + if (hasNameValuePairs()) { + for (AnnotationNameValuePair nvPair : nvPairs) { + if (nvPair.getName().equals(name)) { + return nvPair.getValue().stringify(); + } + } + } + return null; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/StaticJoinPointFactory.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/StaticJoinPointFactory.java new file mode 100644 index 000000000..591e6c470 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/StaticJoinPointFactory.java @@ -0,0 +1,54 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +public class StaticJoinPointFactory { + // int usedKeys; + // + // List/*String*/ strings = new ArrayList(); + // Map/*String,Integer*/ keysForStrings = new HashMap(); + // + // public StaticJoinPointFactory() { + // super(); + // } + // + // static char[] encoding = new char[] { + // '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',//10 + // 'a', 'b', 'z', //36 + // 'A', 'B', 'Z', //62 + // '%', '$', //64 + // }; + // + // static int TWO_WORDS = 64*64-1; + // static int WORD_MASK = 63; + // + // public void write(String s, StringBuffer result) { + // int i = getIndex(s); + // encode(i, result); + // } + // + // void encode(int i, StringBuffer result) { + // if (i > TWO_WORDS) { + // throw new RuntimeException("unimplemented"); + // } else { + // result.append( encoding[(i >> 6) & WORD_MASK] ); + // result.append( encoding[i & WORD_MASK] ); + // } + // } + // + // public String read(StringReader reader) { + // int i = reader.read(); + // + // } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TemporaryTypeMunger.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TemporaryTypeMunger.java new file mode 100644 index 000000000..0e0a89fce --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TemporaryTypeMunger.java @@ -0,0 +1,37 @@ +/* ******************************************************************* + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.Map; + +/** + * Some methods need a temporary type munger (because ConcreteTypeMunger is abstract - dont ask...). + * + * TODO ought to remove the need for this or at least sort out the two methods that are in it, they look weird... + * + * @author AndyClement + */ +public class TemporaryTypeMunger extends ConcreteTypeMunger { + + public TemporaryTypeMunger(ResolvedTypeMunger munger, ResolvedType aspectType) { + super(munger, aspectType); + } + + @Override + public ConcreteTypeMunger parameterizeWith(Map parameterizationMap, World world) { + throw new UnsupportedOperationException("Cannot be called on a TemporaryTypeMunger"); + } + + @Override + public ConcreteTypeMunger parameterizedFor(ResolvedType targetType) { + throw new UnsupportedOperationException("Cannot be called on a TemporaryTypeMunger"); + } + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeFactory.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeFactory.java new file mode 100644 index 000000000..e565e5bc8 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeFactory.java @@ -0,0 +1,377 @@ +/* ******************************************************************* + * Copyright (c) 2005-2010 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://eclipse.org/legal/epl-v10.html + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Adrian Colyer + * @author Andy Clement + */ +public class TypeFactory { + + /** + * Create a parameterized version of a generic type. + * + * @param aGenericType + * @param someTypeParameters note, in the case of an inner type of a parameterized type, this parameter may legitimately be null + * @param inAWorld + * @return + */ + public static ReferenceType createParameterizedType(ResolvedType aBaseType, UnresolvedType[] someTypeParameters, World inAWorld) { + ResolvedType baseType = aBaseType; + if (!aBaseType.isGenericType()) { + if (someTypeParameters != null && someTypeParameters.length > 0) { + if (!aBaseType.isRawType()) { + throw new IllegalStateException("Expecting raw type, but " + aBaseType+" is of type "+aBaseType.getTypekind()); + } + baseType = baseType.getGenericType(); + if (baseType == null) { + throw new IllegalStateException("Raw type does not have generic type set"); + } + } // else if someTypeParameters is null, then the base type is allowed to be non-generic, it's an inner + } + ResolvedType[] resolvedParameters = inAWorld.resolve(someTypeParameters); + + ReferenceType existingType = ((ReferenceType)baseType).findDerivativeType(resolvedParameters); + + ReferenceType pType = null; + + if (existingType!=null) { + pType = existingType; + } else { + pType =new ReferenceType(baseType, resolvedParameters, inAWorld); + } + // pType.setSourceContext(aBaseType.getSourceContext()); + return (ReferenceType) pType.resolve(inAWorld); + } + + /** + * Create an *unresolved* parameterized version of a generic type. + */ + public static UnresolvedType createUnresolvedParameterizedType(String sig, String erasuresig, UnresolvedType[] arguments) { + return new UnresolvedType(sig, erasuresig, arguments); + } + + // public static ReferenceType createRawType( + // ResolvedType aBaseType, + // World inAWorld + // ) { + // if (aBaseType.isRawType()) return (ReferenceType) aBaseType; + // if (!aBaseType.isGenericType()) { + // if (!aBaseType.isRawType()) throw new IllegalStateException("Expecting generic type"); + // } + // ReferenceType rType = new ReferenceType(aBaseType,inAWorld); + // //rType.setSourceContext(aBaseType.getSourceContext()); + // return (ReferenceType) rType.resolve(inAWorld); + // } + + /** + * Creates a sensible unresolvedtype from some signature, for example: signature = LIGuard<TT;>; bound = toString=IGuard<T> + * sig=PIGuard<TT;>; sigErasure=LIGuard; kind=parameterized + */ + static UnresolvedType convertSigToType(String aSignature) { + UnresolvedType bound = null; + int startOfParams = aSignature.indexOf('<'); + if (startOfParams == -1) { + bound = UnresolvedType.forSignature(aSignature); + } else { + int endOfParams = aSignature.lastIndexOf('>'); + String signatureErasure = "L" + aSignature.substring(1, startOfParams) + ";"; + UnresolvedType[] typeParams = createTypeParams(aSignature.substring(startOfParams + 1, endOfParams)); + bound = new UnresolvedType("P" + aSignature.substring(1), signatureErasure, typeParams); + } + return bound; + } + + /** + * Used by UnresolvedType.read, creates a type from a full signature. + */ + public static UnresolvedType createTypeFromSignature(String signature) { + // if (signature.equals(ResolvedType.MISSING_NAME)) { + // return ResolvedType.MISSING; + // } + + char firstChar = signature.charAt(0); + if (firstChar == 'P') { + // parameterized type, calculate signature erasure and type parameters + // (see pr122458) It is possible for a parameterized type to have *no* type parameters visible in its signature. + // This happens for an inner type of a parameterized type which simply inherits the type parameters + // of its parent. In this case it is parameterized but theres no < in the signature. + int startOfParams = signature.indexOf('<'); + + if (startOfParams == -1) { + // Should be an inner type of a parameterized type - could assert there is a '$' in the signature.... + String signatureErasure = "L" + signature.substring(1); + return new UnresolvedType(signature, signatureErasure, UnresolvedType.NONE); + } else { + int endOfParams = locateMatchingEndAngleBracket(signature, startOfParams); + StringBuffer erasureSig = new StringBuffer(signature); + erasureSig.setCharAt(0, 'L'); + while (startOfParams != -1) { + erasureSig.delete(startOfParams, endOfParams + 1); + startOfParams = locateFirstBracket(erasureSig); + if (startOfParams != -1) { + endOfParams = locateMatchingEndAngleBracket(erasureSig, startOfParams); + } + } + + String signatureErasure = erasureSig.toString();// "L" + erasureSig.substring(1); + + // the type parameters of interest are only those that apply to the 'last type' in the signature + // if the signature is 'PMyInterface<String>$MyOtherType;' then there are none... + String lastType = null; + int nestedTypePosition = signature.indexOf("$", endOfParams); // don't look for $ INSIDE the parameters + if (nestedTypePosition != -1) { + lastType = signature.substring(nestedTypePosition + 1); + } else { + lastType = new String(signature); + } + startOfParams = lastType.indexOf("<"); + UnresolvedType[] typeParams = UnresolvedType.NONE; + if (startOfParams != -1) { + endOfParams = locateMatchingEndAngleBracket(lastType, startOfParams); + typeParams = createTypeParams(lastType.substring(startOfParams + 1, endOfParams)); + } + StringBuilder s = new StringBuilder(); + int firstAngleBracket = signature.indexOf('<'); + s.append("P").append(signature.substring(1, firstAngleBracket)); + s.append('<'); + for (UnresolvedType typeParameter : typeParams) { + s.append(typeParameter.getSignature()); + } + s.append(">;"); + signature = s.toString();// 'P' + signature.substring(1); + return new UnresolvedType(signature, signatureErasure, typeParams); + } + // can't replace above with convertSigToType - leads to stackoverflow + } else if ((firstChar == '?' || firstChar == '*') && signature.length()==1) { + return WildcardedUnresolvedType.QUESTIONMARK; + } else if (firstChar == '+') { + // ? extends ... + UnresolvedType upperBound = convertSigToType(signature.substring(1)); + WildcardedUnresolvedType wildcardedUT = new WildcardedUnresolvedType(signature, upperBound, null); + return wildcardedUT; + } else if (firstChar == '-') { + // ? super ... + UnresolvedType lowerBound = convertSigToType(signature.substring(1)); + WildcardedUnresolvedType wildcardedUT = new WildcardedUnresolvedType(signature, null, lowerBound); + return wildcardedUT; + } else if (firstChar == 'T') { + String typeVariableName = signature.substring(1); + if (typeVariableName.endsWith(";")) { + typeVariableName = typeVariableName.substring(0, typeVariableName.length() - 1); + } + return new UnresolvedTypeVariableReferenceType(new TypeVariable(typeVariableName)); + } else if (firstChar == '[') { + int dims = 0; + while (signature.charAt(dims) == '[') { + dims++; + } + UnresolvedType componentType = createTypeFromSignature(signature.substring(dims)); + return new UnresolvedType(signature, signature.substring(0, dims) + componentType.getErasureSignature()); + } else if (signature.length() == 1) { // could be a primitive + switch (firstChar) { + + case 'V': + return UnresolvedType.VOID; + case 'Z': + return UnresolvedType.BOOLEAN; + case 'B': + return UnresolvedType.BYTE; + case 'C': + return UnresolvedType.CHAR; + case 'D': + return UnresolvedType.DOUBLE; + case 'F': + return UnresolvedType.FLOAT; + case 'I': + return UnresolvedType.INT; + case 'J': + return UnresolvedType.LONG; + case 'S': + return UnresolvedType.SHORT; + } + } else if (firstChar == '@') { + // missing type + return ResolvedType.MISSING; + } else if (firstChar == 'L') { + // only an issue if there is also an angle bracket + int leftAngleBracket = signature.indexOf('<'); + + if (leftAngleBracket == -1) { + return new UnresolvedType(signature); + } else { + int endOfParams = locateMatchingEndAngleBracket(signature, leftAngleBracket); + StringBuffer erasureSig = new StringBuffer(signature); + erasureSig.setCharAt(0, 'L'); + while (leftAngleBracket != -1) { + erasureSig.delete(leftAngleBracket, endOfParams + 1); + leftAngleBracket = locateFirstBracket(erasureSig); + if (leftAngleBracket != -1) { + endOfParams = locateMatchingEndAngleBracket(erasureSig, leftAngleBracket); + } + } + + String signatureErasure = erasureSig.toString(); + + // TODO should consider all the intermediate parameterizations as well! + // the type parameters of interest are only those that apply to the 'last type' in the signature + // if the signature is 'PMyInterface<String>$MyOtherType;' then there are none... + String lastType = null; + int nestedTypePosition = signature.indexOf("$", endOfParams); // don't look for $ INSIDE the parameters + if (nestedTypePosition != -1) { + lastType = signature.substring(nestedTypePosition + 1); + } else { + lastType = new String(signature); + } + leftAngleBracket = lastType.indexOf("<"); + UnresolvedType[] typeParams = UnresolvedType.NONE; + if (leftAngleBracket != -1) { + endOfParams = locateMatchingEndAngleBracket(lastType, leftAngleBracket); + typeParams = createTypeParams(lastType.substring(leftAngleBracket + 1, endOfParams)); + } + StringBuilder s = new StringBuilder(); + int firstAngleBracket = signature.indexOf('<'); + s.append("P").append(signature.substring(1, firstAngleBracket)); + s.append('<'); + for (UnresolvedType typeParameter : typeParams) { + s.append(typeParameter.getSignature()); + } + s.append(">;"); + signature = s.toString();// 'P' + signature.substring(1); + return new UnresolvedType(signature, signatureErasure, typeParams); + } + + } + return new UnresolvedType(signature); + } + + private static int locateMatchingEndAngleBracket(CharSequence signature, int startOfParams) { + if (startOfParams == -1) { + return -1; + } + int count = 1; + int idx = startOfParams; + int max = signature.length(); + while (idx < max) { + char ch = signature.charAt(++idx); + if (ch == '<') { + count++; + } else if (ch == '>') { + if (count == 1) { + break; + } + count--; + } + } + return idx; + } + + private static int locateFirstBracket(StringBuffer signature) { + int idx = 0; + int max = signature.length(); + while (idx < max) { + if (signature.charAt(idx) == '<') { + return idx; + } + idx++; + } + return -1; + } + + private static UnresolvedType[] createTypeParams(String typeParameterSpecification) { + String remainingToProcess = typeParameterSpecification; + List<UnresolvedType> types = new ArrayList<UnresolvedType>(); + while (remainingToProcess.length() != 0) { + int endOfSig = 0; + int anglies = 0; + boolean hadAnglies = false; + boolean sigFound = false; // OPTIMIZE can this be done better? + for (endOfSig = 0; (endOfSig < remainingToProcess.length()) && !sigFound; endOfSig++) { + char thisChar = remainingToProcess.charAt(endOfSig); + switch (thisChar) { + case '<': + anglies++; + hadAnglies = true; + break; + case '>': + anglies--; + break; + case '*': + if (anglies==0) { + int nextCharPos = endOfSig+1; + if (nextCharPos>=remainingToProcess.length()) { + sigFound=true; + } else { + char nextChar = remainingToProcess.charAt(nextCharPos); + if (!(nextChar=='+' || nextChar=='-')) { + // dont need to set endOfSig as the loop will increment + // it to the right place before it exits + sigFound=true; + } + } + } + break; + case '[': + if (anglies == 0) { + // the next char might be a [ or a primitive type ref (BCDFIJSZ) + int nextChar = endOfSig + 1; + while (remainingToProcess.charAt(nextChar) == '[') { + nextChar++; + } + if ("BCDFIJSZ".indexOf(remainingToProcess.charAt(nextChar)) != -1) { + // it is something like [I or [[S + sigFound = true; + endOfSig = nextChar; + break; + } + } + break; + case ';': + if (anglies == 0) { + sigFound = true; + break; + } + } + } + String forProcessing = remainingToProcess.substring(0, endOfSig); + if (hadAnglies && forProcessing.charAt(0) == 'L') { + forProcessing = "P" + forProcessing.substring(1); + } + types.add(createTypeFromSignature(forProcessing)); + remainingToProcess = remainingToProcess.substring(endOfSig); + } + UnresolvedType[] typeParams = new UnresolvedType[types.size()]; + types.toArray(typeParams); + return typeParams; + } + + // OPTIMIZE improve all this signature processing stuff, use char arrays, etc + + /** + * Create a signature then delegate to the other factory method. Same input/output: baseTypeSignature="LSomeType;" arguments[0]= + * something with sig "Pcom/Foo<Ljava/lang/String;>;" signature created = "PSomeType<Pcom/Foo<Ljava/lang/String;>;>;" + */ + public static UnresolvedType createUnresolvedParameterizedType(String baseTypeSignature, UnresolvedType[] arguments) { + StringBuffer parameterizedSig = new StringBuffer(); + parameterizedSig.append(ResolvedType.PARAMETERIZED_TYPE_IDENTIFIER); + parameterizedSig.append(baseTypeSignature.substring(1, baseTypeSignature.length() - 1)); + if (arguments.length > 0) { + parameterizedSig.append("<"); + for (int i = 0; i < arguments.length; i++) { + parameterizedSig.append(arguments[i].getSignature()); + } + parameterizedSig.append(">"); + } + parameterizedSig.append(";"); + return createUnresolvedParameterizedType(parameterizedSig.toString(), baseTypeSignature, arguments); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeVariable.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeVariable.java new file mode 100644 index 000000000..d67bbd8dc --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeVariable.java @@ -0,0 +1,371 @@ +/* ******************************************************************* + * Copyright (c) 2005-2010 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://eclipse.org/legal/epl-v10.html + * ******************************************************************/ +package org.aspectj.weaver; + +import java.io.IOException; + +/** + * Represents a type variable with possible bounds. + * + * @author Adrian Colyer + * @author Andy Clement + */ +public class TypeVariable { + + public static final TypeVariable[] NONE = new TypeVariable[0]; + + // the name of the type variable as recorded in the generic signature + private String name; + // index + private int rank; + // computed as required: either ==superclass or ==superInterfaces[0] or is OBJECT + private UnresolvedType firstbound; + // the upper bound of the type variable. From the extends clause, eg. T extends Number + private UnresolvedType superclass; + // any additional upper (interface) bounds. from the extends clause, e.g. T extends Number & Comparable + private UnresolvedType[] superInterfaces = UnresolvedType.NONE; + // It would be nice to push this field onto the TypeVariableDeclaringElement + // interface (a getKind()) but at the moment we don't always guarantee + // to set the declaring element (eclipse seems to utilise the knowledge of + // what declared the type variable, but we dont yet...) + public static final int UNKNOWN = -1; + public static final int METHOD = 1; + public static final int TYPE = 2; + // What kind of element declared this type variable? + private int declaringElementKind = UNKNOWN; + private TypeVariableDeclaringElement declaringElement; + // whether or not the bounds of this type variable have been resolved + public boolean isResolved = false; + // Is this type variable in the process of being resolved (allows for something self-referential like Enum) + private boolean beingResolved = false; + + /** + * Constructor for an unbound type variable, eg. 'T' + */ + public TypeVariable(String name) { + this.name = name; + } + + public TypeVariable(String name, UnresolvedType anUpperBound) { + this(name); + this.superclass = anUpperBound; + } + + public TypeVariable(String name, UnresolvedType anUpperBound, UnresolvedType[] superInterfaces) { + this(name, anUpperBound); + this.superInterfaces = superInterfaces; + } + + /** + * @return the first bound, either the superclass or if non is specified the first interface or if non are specified then OBJECT + */ + public UnresolvedType getFirstBound() { + if (firstbound != null) { + return firstbound; + } + if (superclass == null || superclass.getSignature().equals("Ljava/lang/Object;")) { + if (superInterfaces.length > 0) { + firstbound = superInterfaces[0]; + } else { + firstbound = UnresolvedType.OBJECT; + } + } else { + firstbound = superclass; + } + return firstbound; + } + + public UnresolvedType getUpperBound() { + return superclass; + } + + public UnresolvedType[] getSuperInterfaces() { + return superInterfaces; + } + + public String getName() { + return name; + } + + /** + * resolve all the bounds of this type variable + */ + public TypeVariable resolve(World world) { + if (isResolved) { + return this; + } + if (beingResolved) { + return this; + } + beingResolved = true; + + TypeVariable resolvedTVar = null; + + if (declaringElement != null) { + // resolve by finding the real type var that we refer to... + if (declaringElementKind == TYPE) { + UnresolvedType declaring = (UnresolvedType) declaringElement; + ReferenceType rd = (ReferenceType) declaring.resolve(world); + TypeVariable[] tVars = rd.getTypeVariables(); + for (int i = 0; i < tVars.length; i++) { + if (tVars[i].getName().equals(getName())) { + resolvedTVar = tVars[i]; + break; + } + } + } else { + // look for type variable on method... + ResolvedMember declaring = (ResolvedMember) declaringElement; + TypeVariable[] tvrts = declaring.getTypeVariables(); + for (int i = 0; i < tvrts.length; i++) { + if (tvrts[i].getName().equals(getName())) { + resolvedTVar = tvrts[i]; + // if (tvrts[i].isTypeVariableReference()) { + // TypeVariableReferenceType tvrt = (TypeVariableReferenceType) tvrts[i].resolve(inSomeWorld); + // TypeVariable tv = tvrt.getTypeVariable(); + // if (tv.getName().equals(getName())) resolvedTVar = tv; + // } + } + } + } + + if (resolvedTVar == null) { + throw new IllegalStateException(); + // well, this is bad... we didn't find the type variable on the member + // could be a separate compilation issue... + // should issue message, this is a workaround to get us going... + // resolvedTVar = this; + } + } else { + resolvedTVar = this; + } + + superclass = resolvedTVar.superclass; + superInterfaces = resolvedTVar.superInterfaces; + + if (superclass != null) { + ResolvedType rt = superclass.resolve(world); +// if (!superclass.isTypeVariableReference() && rt.isInterface()) { +// throw new IllegalStateException("Why is the type an interface? " + rt); +// } + superclass = rt; + } + firstbound = getFirstBound().resolve(world); + + for (int i = 0; i < superInterfaces.length; i++) { + superInterfaces[i] = superInterfaces[i].resolve(world); + } + isResolved = true; + beingResolved = false; + return this; + } + + /** + * answer true if the given type satisfies all of the bound constraints of this type variable. If type variable has not been + * resolved then throws IllegalStateException + */ + public boolean canBeBoundTo(ResolvedType candidate) { + if (!isResolved) { + throw new IllegalStateException("Can't answer binding questions prior to resolving"); + } + + // wildcard can accept any binding + if (candidate.isGenericWildcard()) { + return true; + } + + // otherwise can be bound iff... + + // candidate is a subtype of upperBound + if (superclass != null && !isASubtypeOf(superclass, candidate)) { + return false; + } + // candidate is a subtype of all superInterfaces + for (int i = 0; i < superInterfaces.length; i++) { + if (!isASubtypeOf(superInterfaces[i], candidate)) { + return false; + } + } + return true; + } + + private boolean isASubtypeOf(UnresolvedType candidateSuperType, UnresolvedType candidateSubType) { + ResolvedType superType = (ResolvedType) candidateSuperType; + ResolvedType subType = (ResolvedType) candidateSubType; + return superType.isAssignableFrom(subType); + } + + // only used when resolving + public void setUpperBound(UnresolvedType superclass) { + // if (isResolved) { + // throw new IllegalStateException("Why set this late?"); + // } + this.firstbound = null; + this.superclass = superclass; + } + + // only used when resolving + public void setAdditionalInterfaceBounds(UnresolvedType[] superInterfaces) { + // if (isResolved) { + // throw new IllegalStateException("Why set this late?"); + // } + this.firstbound = null; + this.superInterfaces = superInterfaces; + } + + public String toDebugString() { + return getDisplayName(); + } + + public String getDisplayName() { + StringBuffer ret = new StringBuffer(); + ret.append(name); + if (!getFirstBound().getName().equals("java.lang.Object")) { + ret.append(" extends "); + ret.append(getFirstBound().getName()); + if (superInterfaces != null) { + for (int i = 0; i < superInterfaces.length; i++) { + if (!getFirstBound().equals(superInterfaces[i])) { + ret.append(" & "); + ret.append(superInterfaces[i].getName()); + } + } + } + } + return ret.toString(); + } + + @Override + public String toString() { + return "TypeVar " + getDisplayName(); + } + + /** + * Return complete signature, e.g. "T extends Number" would return "T:Ljava/lang/Number;" note: MAY INCLUDE P types if bounds + * are parameterized types + */ + public String getSignature() { + StringBuffer sb = new StringBuffer(); + sb.append(name); + sb.append(":"); + if (superInterfaces.length == 0 || !superclass.getSignature().equals(UnresolvedType.OBJECT.getSignature())) { + sb.append(superclass.getSignature()); + } + if (superInterfaces.length != 0) { + for (int i = 0; i < superInterfaces.length; i++) { + sb.append(":"); + UnresolvedType iBound = superInterfaces[i]; + sb.append(iBound.getSignature()); + } + } + return sb.toString(); + } + + /** + * @return signature for inclusion in an attribute, there must be no 'P' in it signatures + */ + public String getSignatureForAttribute() { + StringBuffer sb = new StringBuffer(); + sb.append(name); + sb.append(":"); + if (superInterfaces.length == 0 || !superclass.getSignature().equals(UnresolvedType.OBJECT.getSignature())) { + sb.append(((ReferenceType)superclass).getSignatureForAttribute()); + } + if (superInterfaces.length != 0) { + for (int i = 0; i < superInterfaces.length; i++) { + sb.append(":"); + ResolvedType iBound = (ResolvedType) superInterfaces[i]; + sb.append(iBound.getSignatureForAttribute()); + } + } + return sb.toString(); + } + + public void setRank(int rank) { + this.rank = rank; + } + + public int getRank() { + return rank; + } + + public void setDeclaringElement(TypeVariableDeclaringElement element) { + this.declaringElement = element; + if (element instanceof UnresolvedType) { + this.declaringElementKind = TYPE; + } else { + this.declaringElementKind = METHOD; + } + } + + public TypeVariableDeclaringElement getDeclaringElement() { + return declaringElement; + } + + public void setDeclaringElementKind(int kind) { + this.declaringElementKind = kind; + } + + public int getDeclaringElementKind() { + // if (declaringElementKind==UNKNOWN) throw new RuntimeException("Dont know declarer of this tvar : "+this); + return declaringElementKind; + } + + public void write(CompressingDataOutputStream s) throws IOException { + // name, upperbound, additionalInterfaceBounds, lowerbound + s.writeUTF(name); + superclass.write(s); + if (superInterfaces.length == 0) { + s.writeInt(0); + } else { + s.writeInt(superInterfaces.length); + for (int i = 0; i < superInterfaces.length; i++) { + UnresolvedType ibound = superInterfaces[i]; + ibound.write(s); + } + } + } + + public static TypeVariable read(VersionedDataInputStream s) throws IOException { + + // if (s.getMajorVersion()>=AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + + String name = s.readUTF(); + UnresolvedType ubound = UnresolvedType.read(s); + int iboundcount = s.readInt(); + UnresolvedType[] ibounds = UnresolvedType.NONE; + if (iboundcount > 0) { + ibounds = new UnresolvedType[iboundcount]; + for (int i = 0; i < iboundcount; i++) { + ibounds[i] = UnresolvedType.read(s); + } + } + + TypeVariable newVariable = new TypeVariable(name, ubound, ibounds); + return newVariable; + } + + public String getGenericSignature() { + return "T" + name + ";"; + } + + public String getErasureSignature() { + return getFirstBound().getErasureSignature(); + } + + public UnresolvedType getSuperclass() { + return superclass; + } + + public void setSuperclass(UnresolvedType superclass) { + this.firstbound = null; + this.superclass = superclass; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeVariableDeclaringElement.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeVariableDeclaringElement.java new file mode 100644 index 000000000..ae41fd09c --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeVariableDeclaringElement.java @@ -0,0 +1,21 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement Initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +/** + * Tag interface - methods and types can be declaring elements for type variables. See the TypeVariable class which holds onto the + * declaring element + */ +public interface TypeVariableDeclaringElement { + public TypeVariable getTypeVariableNamed(String name); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeVariableReference.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeVariableReference.java new file mode 100644 index 000000000..138fbced7 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeVariableReference.java @@ -0,0 +1,22 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +/** + * Implemented by Types that represent references to type variables + * + */ +public interface TypeVariableReference { + + TypeVariable getTypeVariable(); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeVariableReferenceType.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeVariableReferenceType.java new file mode 100644 index 000000000..1e867ab5e --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/TypeVariableReferenceType.java @@ -0,0 +1,154 @@ +/* ******************************************************************* + * Copyright (c) 2005-2012 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://eclipse.org/legal/epl-v10.html + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.Map; + +/** + * ReferenceType pointing to a type variable. The delegate for this reference type is the upperbound on the type variable (so + * Object if not otherwise specified). + * + * @author Adrian Colyer + * @author Andy Clement + */ +public class TypeVariableReferenceType extends ReferenceType implements TypeVariableReference { + + private TypeVariable typeVariable; + + public TypeVariableReferenceType(TypeVariable typeVariable, World world) { + super(typeVariable.getGenericSignature(), typeVariable.getErasureSignature(), world); + this.typeVariable = typeVariable; + } + + @Override + public boolean equals(Object other) { + if (other instanceof TypeVariableReferenceType) { + return typeVariable==((TypeVariableReferenceType)other).typeVariable; + } + return false; + } + + @Override + public int hashCode() { + return typeVariable.hashCode(); + } + + /** + * For a TypeVariableReferenceType the delegate is the delegate for the first bound. + */ + @Override + public ReferenceTypeDelegate getDelegate() { + if (this.delegate == null) { + ResolvedType resolvedFirstBound = typeVariable.getFirstBound().resolve(world); + BoundedReferenceTypeDelegate brtd = null; + if (resolvedFirstBound.isMissing()) { + brtd = new BoundedReferenceTypeDelegate((ReferenceType) world.resolve(UnresolvedType.OBJECT)); + setDelegate(brtd); // set now because getSourceLocation() below will cause a recursive step to discover the delegate + world.getLint().cantFindType.signal( + "Unable to find type for generic bound. Missing type is " + resolvedFirstBound.getName(), + getSourceLocation()); + } else { + brtd = new BoundedReferenceTypeDelegate((ReferenceType) resolvedFirstBound); + setDelegate(brtd); + } + + } + return this.delegate; + } + + @Override + public UnresolvedType parameterize(Map<String, UnresolvedType> typeBindings) { + UnresolvedType ut = typeBindings.get(getName()); + if (ut != null) { + return world.resolve(ut); + } + return this; + } + + public TypeVariable getTypeVariable() { + return typeVariable; + } + + @Override + public boolean isTypeVariableReference() { + return true; + } + + @Override + public String toString() { + return typeVariable.getName(); + } + + @Override + public boolean isGenericWildcard() { + return false; + } + + @Override + public boolean isAnnotation() { + ReferenceType upper = (ReferenceType) typeVariable.getUpperBound(); + if (upper.isAnnotation()) { + return true; + } + World world = upper.getWorld(); + typeVariable.resolve(world); + ResolvedType annotationType = ResolvedType.ANNOTATION.resolve(world); + UnresolvedType[] ifBounds = typeVariable.getSuperInterfaces();// AdditionalBounds(); + for (int i = 0; i < ifBounds.length; i++) { + if (((ReferenceType) ifBounds[i]).isAnnotation()) { + return true; + } + if (ifBounds[i].equals(annotationType)) { + return true; // annotation itself does not have the annotation flag set in Java! + } + } + return false; + } + + /** + * return the signature for a *REFERENCE* to a type variable, which is simply: Tname; there is no bounds info included, that is + * in the signature of the type variable itself + */ + @Override + public String getSignature() { + StringBuffer sb = new StringBuffer(); + sb.append("T"); + sb.append(typeVariable.getName()); + sb.append(";"); + return sb.toString(); + } + + /** + * @return the name of the type variable + */ + public String getTypeVariableName() { + return typeVariable.getName(); + } + + public ReferenceType getUpperBound() { + return (ReferenceType) typeVariable.resolve(world).getUpperBound(); + } + + /** + * resolve the type variable we are managing and then return this object. 'this' is already a ResolvedType but the type variable + * may transition from a not-resolved to a resolved state. + */ + public ResolvedType resolve(World world) { + typeVariable.resolve(world); + return this; + } + + /** + * @return true if the type variable this reference is managing is resolved + */ + public boolean isTypeVariableResolved() { + return typeVariable.isResolved; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/UnresolvedType.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/UnresolvedType.java new file mode 100644 index 000000000..d8d46176d --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/UnresolvedType.java @@ -0,0 +1,968 @@ +/* ******************************************************************* + * Copyright (c) 2002,2005 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: + * PARC initial implementation + * Andy Clement start of generics upgrade... + * Adrian Colyer - overhaul + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.GenericSignature; +import org.aspectj.util.GenericSignature.ClassSignature; +import org.aspectj.util.GenericSignatureParser; +import org.aspectj.weaver.tools.Traceable; + +/** + * A UnresolvedType represents a type to the weaver. UnresolvedTypes are resolved in some World (a type repository). When a + * UnresolvedType is resolved it turns into a ResolvedType which may be a primitive type, or a ReferenceType. ReferenceTypes may + * refer to simple, generic, parameterized or type-variable based reference types. A ReferenceType is backed by a delegate that + * provides information about the type based on some repository (for example an Eclipse based delegate, a bytecode based delegate or + * a reflection based delegate). + * <p> + * Every UnresolvedType has a signature, the unique key for the type in the world. + */ +public class UnresolvedType implements Traceable, TypeVariableDeclaringElement { + + // common type structures + public static final UnresolvedType[] NONE = new UnresolvedType[0]; + public static final UnresolvedType OBJECT = forSignature("Ljava/lang/Object;"); + public static final UnresolvedType OBJECTARRAY = forSignature("[Ljava/lang/Object;"); + public static final UnresolvedType CLONEABLE = forSignature("Ljava/lang/Cloneable;"); + public static final UnresolvedType SERIALIZABLE = forSignature("Ljava/io/Serializable;"); + public static final UnresolvedType THROWABLE = forSignature("Ljava/lang/Throwable;"); + public static final UnresolvedType RUNTIME_EXCEPTION = forSignature("Ljava/lang/RuntimeException;"); + public static final UnresolvedType ERROR = forSignature("Ljava/lang/Error;"); + public static final UnresolvedType AT_INHERITED = forSignature("Ljava/lang/annotation/Inherited;"); + public static final UnresolvedType AT_RETENTION = forSignature("Ljava/lang/annotation/Retention;"); + public static final UnresolvedType ENUM = forSignature("Ljava/lang/Enum;"); + public static final UnresolvedType ANNOTATION = forSignature("Ljava/lang/annotation/Annotation;"); + public static final UnresolvedType JL_CLASS = forSignature("Ljava/lang/Class;"); + public static final UnresolvedType JAVA_LANG_CLASS_ARRAY = forSignature("[Ljava/lang/Class;"); + public static final UnresolvedType JL_STRING = forSignature("Ljava/lang/String;"); + public static final UnresolvedType JL_EXCEPTION = forSignature("Ljava/lang/Exception;"); + public static final UnresolvedType JAVA_LANG_REFLECT_METHOD = forSignature("Ljava/lang/reflect/Method;"); + public static final UnresolvedType JAVA_LANG_REFLECT_FIELD = forSignature("Ljava/lang/reflect/Field;"); + public static final UnresolvedType JAVA_LANG_REFLECT_CONSTRUCTOR = forSignature("Ljava/lang/reflect/Constructor;"); + public static final UnresolvedType JAVA_LANG_ANNOTATION = forSignature("Ljava/lang/annotation/Annotation;"); + public static final UnresolvedType SUPPRESS_AJ_WARNINGS = forSignature("Lorg/aspectj/lang/annotation/SuppressAjWarnings;"); + public static final UnresolvedType AT_TARGET = forSignature("Ljava/lang/annotation/Target;"); + public static final UnresolvedType SOMETHING = new UnresolvedType("?"); + public static final UnresolvedType[] ARRAY_WITH_JUST_OBJECT = new UnresolvedType[] { OBJECT }; + public static final UnresolvedType JOINPOINT_STATICPART = forSignature("Lorg/aspectj/lang/JoinPoint$StaticPart;"); + public static final UnresolvedType JOINPOINT_ENCLOSINGSTATICPART = forSignature("Lorg/aspectj/lang/JoinPoint$EnclosingStaticPart;"); + public static final UnresolvedType AJC_PRIVILEGED = forSignature("Lorg/aspectj/internal/lang/annotation/ajcPrivileged;"); + + public static final UnresolvedType BOOLEAN = forPrimitiveType("Z"); + public static final UnresolvedType BYTE = forPrimitiveType("B"); + public static final UnresolvedType CHAR = forPrimitiveType("C"); + public static final UnresolvedType DOUBLE = forPrimitiveType("D"); + public static final UnresolvedType FLOAT = forPrimitiveType("F"); + public static final UnresolvedType INT = forPrimitiveType("I"); + public static final UnresolvedType LONG = forPrimitiveType("J"); + public static final UnresolvedType SHORT = forPrimitiveType("S"); + public static final UnresolvedType VOID = forPrimitiveType("V"); + + // A type is considered missing if we have a signature for it but cannot find the delegate + public static final String MISSING_NAME = "@missing@"; + + // OPTIMIZE I dont think you can ask something unresolved what kind of type it is, how can it always know? Push down into + // resolvedtype that will force references to resolvedtypes to be correct rather than relying on unresolvedtypes to answer + // questions + protected TypeKind typeKind = TypeKind.SIMPLE; // what kind of type am I? + + protected String signature; + + /** + * The erasure of the signature. Contains only the Java signature of the type with all supertype, superinterface, type variable, + * and parameter information removed. + */ + protected String signatureErasure; + + /** + * Calculated on first request - the package name (java.lang for type java.lang.String) + */ + private String packageName; + + /** + * Calculated on first request - the class name (String for type java.lang.String) + */ + private String className; + + /** + * Iff isParameterized(), then these are the type parameters + */ + protected UnresolvedType[] typeParameters; + + /** + * Iff isGeneric(), then these are the type variables declared on the type Iff isParameterized(), then these are the type + * variables bound as parameters in the type + */ + // OPTIMIZE should be no state in here that will damage whether equals() is correct... + protected TypeVariable[] typeVariables; + + public boolean isPrimitiveType() { + return typeKind == TypeKind.PRIMITIVE; + } + + public boolean isVoid() { + // OPTIMIZE promote to bitflag? + return signature.equals("V"); + } + + public boolean isSimpleType() { + return typeKind == TypeKind.SIMPLE; + } + + public boolean isRawType() { + return typeKind == TypeKind.RAW; + } + + public boolean isGenericType() { + return typeKind == TypeKind.GENERIC; + } + + public boolean isParameterizedType() { + return typeKind == TypeKind.PARAMETERIZED; + } + + public boolean isParameterizedOrGenericType() { + return typeKind == TypeKind.GENERIC || typeKind == TypeKind.PARAMETERIZED; + } + + public boolean isParameterizedOrRawType() { + return typeKind == TypeKind.PARAMETERIZED || typeKind == TypeKind.RAW; + } + + public boolean isTypeVariableReference() { + return typeKind == TypeKind.TYPE_VARIABLE; + } + + public boolean isGenericWildcard() { + return typeKind == TypeKind.WILDCARD; + } + + public TypeKind getTypekind() { + return typeKind; + } + + // for any reference type, we can get some extra information... + public final boolean isArray() { + return signature.length() > 0 && signature.charAt(0) == '['; + } + + /** + * Equality is checked based on the underlying signature. + */ + @Override + public boolean equals(Object other) { + if (!(other instanceof UnresolvedType)) { + return false; + } + return signature.equals(((UnresolvedType) other).signature); + } + + /** + * Equality is checked based on the underlying signature, so the hash code of a particular type is the hash code of its + * signature string. + */ + @Override + public int hashCode() { + return signature.hashCode(); + } + + protected UnresolvedType(String signature) { + this.signature = signature; + this.signatureErasure = signature; + } + + protected UnresolvedType(String signature, String signatureErasure) { + this.signature = signature; + this.signatureErasure = signatureErasure; + } + + // called from TypeFactory + public UnresolvedType(String signature, String signatureErasure, UnresolvedType[] typeParams) { + this.signature = signature; + this.signatureErasure = signatureErasure; + this.typeParameters = typeParams; + if (typeParams != null) { + this.typeKind = TypeKind.PARAMETERIZED; + } + } + + // The operations supported by an UnresolvedType are those that do not require a world + + /** + * This is the size of this type as used in JVM. + */ + public int getSize() { + return size; + } + + private int size = 1; + + /** + * NOTE: Use forSignature() if you can, it'll be cheaper ! Constructs a UnresolvedType for a java language type name. For + * example: + * + * <blockquote> + * + * <pre> + * UnresolvedType.forName("java.lang.Thread[]") + * UnresolvedType.forName("int") + * </pre> + * + * </blockquote> + * + * Types may equivalently be produced by this or by {@link #forSignature(String)}. + * + * <blockquote> + * + * <pre> + * UnresolvedType.forName("java.lang.Thread[]").equals(Type.forSignature("[Ljava/lang/Thread;") + * UnresolvedType.forName("int").equals(Type.forSignature("I")) + * </pre> + * + * </blockquote> + * + * @param name the java language type name in question. + * @return a type object representing that java language type. + */ + // OPTIMIZE change users of this to use forSignature, especially for simple cases + public static UnresolvedType forName(String name) { + return forSignature(nameToSignature(name)); + } + + /** + * Constructs a UnresolvedType for each java language type name in an incoming array. + * + * @param names an array of java language type names. + * @return an array of UnresolvedType objects. + * @see #forName(String) + */ + public static UnresolvedType[] forNames(String[] names) { + UnresolvedType[] ret = new UnresolvedType[names.length]; + for (int i = 0, len = names.length; i < len; i++) { + ret[i] = UnresolvedType.forName(names[i]); + } + return ret; + } + + public static UnresolvedType forGenericType(String name, TypeVariable[] tvbs, String genericSig) { + String sig = nameToSignature(name); + UnresolvedType ret = UnresolvedType.forSignature(sig); + ret.typeKind = TypeKind.GENERIC; + ret.typeVariables = tvbs; + ret.signatureErasure = sig; + return ret; + } + + public static UnresolvedType forGenericTypeSignature(String sig, String declaredGenericSig) { + UnresolvedType ret = UnresolvedType.forSignature(sig); + ret.typeKind = TypeKind.GENERIC; + + ClassSignature csig = new GenericSignatureParser().parseAsClassSignature(declaredGenericSig); + + GenericSignature.FormalTypeParameter[] ftps = csig.formalTypeParameters; + ret.typeVariables = new TypeVariable[ftps.length]; + for (int i = 0; i < ftps.length; i++) { + GenericSignature.FormalTypeParameter parameter = ftps[i]; + if (parameter.classBound instanceof GenericSignature.ClassTypeSignature) { + GenericSignature.ClassTypeSignature cts = (GenericSignature.ClassTypeSignature) parameter.classBound; + ret.typeVariables[i] = new TypeVariable(ftps[i].identifier, UnresolvedType.forSignature(cts.outerType.identifier + + ";")); + } else if (parameter.classBound instanceof GenericSignature.TypeVariableSignature) { + GenericSignature.TypeVariableSignature tvs = (GenericSignature.TypeVariableSignature) parameter.classBound; + UnresolvedTypeVariableReferenceType utvrt = new UnresolvedTypeVariableReferenceType(new TypeVariable( + tvs.typeVariableName)); + ret.typeVariables[i] = new TypeVariable(ftps[i].identifier, utvrt); + } else { + throw new BCException( + "UnresolvedType.forGenericTypeSignature(): Do not know how to process type variable bound of type '" + + parameter.classBound.getClass() + "'. Full signature is '" + sig + "'"); + } + } + ret.signatureErasure = sig; + ret.signature = ret.signatureErasure; + return ret; + } + + public static UnresolvedType forGenericTypeVariables(String sig, TypeVariable[] tVars) { + UnresolvedType ret = UnresolvedType.forSignature(sig); + ret.typeKind = TypeKind.GENERIC; + ret.typeVariables = tVars; + ret.signatureErasure = sig; + ret.signature = ret.signatureErasure; + return ret; + } + + public static UnresolvedType forRawTypeName(String name) { + UnresolvedType ret = UnresolvedType.forName(name); + ret.typeKind = TypeKind.RAW; + return ret; + } + + public static UnresolvedType forPrimitiveType(String signature) { + UnresolvedType ret = new UnresolvedType(signature); + ret.typeKind = TypeKind.PRIMITIVE; + if (signature.equals("J") || signature.equals("D")) { + ret.size = 2; + } else if (signature.equals("V")) { + ret.size = 0; + } + return ret; + } + + /** + * Creates a new type array with a fresh type appended to the end. + * + * @param types the left hand side of the new array + * @param end the right hand side of the new array + */ + public static UnresolvedType[] add(UnresolvedType[] types, UnresolvedType end) { + int len = types.length; + UnresolvedType[] ret = new UnresolvedType[len + 1]; + System.arraycopy(types, 0, ret, 0, len); + ret[len] = end; + return ret; + } + + /** + * Creates a new type array with a fresh type inserted at the beginning. + * + * + * @param start the left hand side of the new array + * @param types the right hand side of the new array + */ + public static UnresolvedType[] insert(UnresolvedType start, UnresolvedType[] types) { + int len = types.length; + UnresolvedType[] ret = new UnresolvedType[len + 1]; + ret[0] = start; + System.arraycopy(types, 0, ret, 1, len); + return ret; + } + + /** + * Constructs a Type for a JVM bytecode signature string. For example: + * + * <blockquote> + * + * <pre> + * UnresolvedType.forSignature("[Ljava/lang/Thread;") + * UnresolvedType.forSignature("I"); + * </pre> + * + * </blockquote> + * + * Types may equivalently be produced by this or by {@link #forName(String)}. This method should not be passed P signatures. + * + * <blockquote> + * + * <pre> + * UnresolvedType.forName("java.lang.Thread[]").equals(Type.forSignature("[Ljava/lang/Thread;") + * UnresolvedType.forName("int").equals(Type.forSignature("I")) + * </pre> + * + * </blockquote> + * + * @param signature the JVM bytecode signature string for the desired type. + * @return a type object represnting that JVM bytecode signature. + */ + public static UnresolvedType forSignature(String signature) { + assert !(signature.startsWith("L") && signature.indexOf("<") != -1); + switch (signature.charAt(0)) { + case 'B': + return UnresolvedType.BYTE; + case 'C': + return UnresolvedType.CHAR; + case 'D': + return UnresolvedType.DOUBLE; + case 'F': + return UnresolvedType.FLOAT; + case 'I': + return UnresolvedType.INT; + case 'J': + return UnresolvedType.LONG; + case 'L': + return TypeFactory.createTypeFromSignature(signature); + case 'P': + return TypeFactory.createTypeFromSignature(signature); + case 'S': + return UnresolvedType.SHORT; + case 'V': + return UnresolvedType.VOID; + case 'Z': + return UnresolvedType.BOOLEAN; + case '[': + return TypeFactory.createTypeFromSignature(signature); + case '+': + return TypeFactory.createTypeFromSignature(signature); + case '-': + return TypeFactory.createTypeFromSignature(signature); + case '?': + return TypeFactory.createTypeFromSignature(signature); + case 'T': + return TypeFactory.createTypeFromSignature(signature); + default: + throw new BCException("Bad type signature " + signature); + } + } + + /** + * Constructs a UnresolvedType for each JVM bytecode type signature in an incoming array. + * + * @param names an array of JVM bytecode type signatures + * @return an array of UnresolvedType objects. + * @see #forSignature(String) + */ + public static UnresolvedType[] forSignatures(String[] sigs) { + UnresolvedType[] ret = new UnresolvedType[sigs.length]; + for (int i = 0, len = sigs.length; i < len; i++) { + ret[i] = UnresolvedType.forSignature(sigs[i]); + } + return ret; + } + + /** + * Returns the name of this type in java language form (e.g. java.lang.Thread or boolean[]). This produces a more aesthetically + * pleasing string than {@link java.lang.Class#getName()}. + * + * @return the java language name of this type. + */ + public String getName() { + return signatureToName(signature); + } + + public String getSimpleName() { + String name = getRawName(); + int lastDot = name.lastIndexOf('.'); + if (lastDot != -1) { + name = name.substring(lastDot + 1); + } + if (isParameterizedType()) { + StringBuffer sb = new StringBuffer(name); + sb.append("<"); + for (int i = 0; i < (typeParameters.length - 1); i++) { + sb.append(typeParameters[i].getSimpleName()); + sb.append(","); + } + sb.append(typeParameters[typeParameters.length - 1].getSimpleName()); + sb.append(">"); + name = sb.toString(); + } + return name; + } + + public String getRawName() { + return signatureToName((signatureErasure == null ? signature : signatureErasure)); + } + + public String getBaseName() { + String name = getName(); + if (isParameterizedType() || isGenericType()) { + if (typeParameters == null) { + return name; + } else { + return name.substring(0, name.indexOf("<")); + } + } else { + return name; + } + } + + public String getSimpleBaseName() { + String name = getBaseName(); + int lastDot = name.lastIndexOf('.'); + if (lastDot != -1) { + name = name.substring(lastDot + 1); + } + return name; + } + + /** + * Returns an array of strings representing the java langauge names of an array of types. + * + * @param types an array of UnresolvedType objects + * @return an array of Strings fo the java language names of types. + * @see #getName() + */ + public static String[] getNames(UnresolvedType[] types) { + String[] ret = new String[types.length]; + for (int i = 0, len = types.length; i < len; i++) { + ret[i] = types[i].getName(); + } + return ret; + } + + /** + * Returns the name of this type in JVM signature form. For all UnresolvedType t: + * + * <blockquote> + * + * <pre> + * UnresolvedType.forSignature(t.getSignature()).equals(t) + * </pre> + * + * </blockquote> + * + * and for all String s where s is a lexically valid JVM type signature string: + * + * <blockquote> + * + * <pre> + * UnresolvedType.forSignature(s).getSignature().equals(s) + * </pre> + * + * </blockquote> + * + * @return the java JVM signature string for this type. + */ + public String getSignature() { + return signature; + } + + /** + * For parameterized types, return the signature for the raw type + */ + public String getErasureSignature() { + if (signatureErasure == null) { + return signature; + } + return signatureErasure; + } + + private boolean needsModifiableDelegate = false; + + public boolean needsModifiableDelegate() { + return needsModifiableDelegate; + } + + public void setNeedsModifiableDelegate(boolean b) { + this.needsModifiableDelegate = b; + } + + public UnresolvedType getRawType() { + return UnresolvedType.forSignature(getErasureSignature()); + } + + /** + * Returns a UnresolvedType object representing the effective outermost enclosing type for a name type. For all other types, + * this will return the type itself. + * + * The only guarantee is given in JLS 13.1 where code generated according to those rules will have type names that can be split + * apart in this way. + * + * @return the outermost enclosing UnresolvedType object or this. + */ + public UnresolvedType getOutermostType() { + if (isArray() || isPrimitiveType()) { + return this; + } + String sig = getErasureSignature(); + int dollar = sig.indexOf('$'); + if (dollar != -1) { + return UnresolvedType.forSignature(sig.substring(0, dollar) + ';'); + } else { + return this; + } + } + + /** + * Returns a UnresolvedType object representing the component type of this array, or null if this type does not represent an + * array type. + * + * @return the component UnresolvedType object, or null. + */ + public UnresolvedType getComponentType() { + if (isArray()) { + return forSignature(signature.substring(1)); + } else { + return null; + } + } + + /** + * Returns a java language string representation of this type. + */ + @Override + public String toString() { + return getName(); // + " - " + getKind(); + } + + public String toDebugString() { + return getName(); + } + + // ---- requires worlds + + /** + * Returns a resolved version of this type according to a particular world. + * + * @param world the {@link World} within which to resolve. + * @return a resolved type representing this type in the appropriate world. + */ + public ResolvedType resolve(World world) { + return world.resolve(this); + } + + // ---- helpers + + private static String signatureToName(String signature) { + switch (signature.charAt(0)) { + case 'B': + return "byte"; + case 'C': + return "char"; + case 'D': + return "double"; + case 'F': + return "float"; + case 'I': + return "int"; + case 'J': + return "long"; + case 'L': + String name = signature.substring(1, signature.length() - 1).replace('/', '.'); + return name; + case 'T': + StringBuffer nameBuff2 = new StringBuffer(); + int colon = signature.indexOf(";"); + String tvarName = signature.substring(1, colon); + nameBuff2.append(tvarName); + return nameBuff2.toString(); + case 'P': // it's one of our parameterized type sigs + StringBuffer nameBuff = new StringBuffer(); + // signature for parameterized types is e.g. + // List<String> -> Ljava/util/List<Ljava/lang/String;>; + // Map<String,List<Integer>> -> Ljava/util/Map<java/lang/String;Ljava/util/List<Ljava/lang/Integer;>;>; + int paramNestLevel = 0; + for (int i = 1; i < signature.length(); i++) { + char c = signature.charAt(i); + switch (c) { + case '/': + nameBuff.append('.'); + break; + case '<': + nameBuff.append("<"); + paramNestLevel++; + StringBuffer innerBuff = new StringBuffer(); + while (paramNestLevel > 0) { + c = signature.charAt(++i); + if (c == '<') { + paramNestLevel++; + } + if (c == '>') { + paramNestLevel--; + } + if (paramNestLevel > 0) { + innerBuff.append(c); + } + if (c == ';' && paramNestLevel == 1) { + nameBuff.append(signatureToName(innerBuff.toString())); + if (signature.charAt(i + 1) != '>') { + nameBuff.append(','); + } + innerBuff = new StringBuffer(); + } + } + nameBuff.append(">"); + break; + case ';': + break; + default: + nameBuff.append(c); + } + } + return nameBuff.toString(); + case 'S': + return "short"; + case 'V': + return "void"; + case 'Z': + return "boolean"; + case '[': + return signatureToName(signature.substring(1, signature.length())) + "[]"; + // case '<': + // // its a generic! + // if (signature.charAt(1)=='>') return signatureToName(signature.substring(2)); + case '+': + return "? extends " + signatureToName(signature.substring(1, signature.length())); + case '-': + return "? super " + signatureToName(signature.substring(1, signature.length())); + case '*': + return "?"; + default: + throw new BCException("Bad type signature: " + signature); + } + } + + private static String nameToSignature(String name) { + int len = name.length(); + if (len < 8) { + if (name.equals("int")) { + return "I"; + } + if (name.equals("void")) { + return "V"; + } + if (name.equals("long")) { + return "J"; + } + if (name.equals("boolean")) { + return "Z"; + } + if (name.equals("double")) { + return "D"; + } + if (name.equals("float")) { + return "F"; + } + if (name.equals("byte")) { + return "B"; + } + if (name.equals("short")) { + return "S"; + } + if (name.equals("char")) { + return "C"; + } + if (name.equals("?")) { + return name; + } + } + if (len == 0) { + throw new BCException("Bad type name: " + name); + } + if (name.endsWith("[]")) { + return "[" + nameToSignature(name.substring(0, name.length() - 2)); + } + + // Sometimes the 'name' for an array is of the form: [Ljava.lang.String; + if (name.charAt(0)=='[') { + return name.replace('.','/'); + } + + if (name.indexOf("<") == -1) { + // not parameterized + return new StringBuilder("L").append(name.replace('.', '/')).append(';').toString(); + } else { + StringBuffer nameBuff = new StringBuffer(); + int nestLevel = 0; + nameBuff.append("P"); + for (int i = 0; i < len; i++) { + char c = name.charAt(i); + switch (c) { + case '.': + nameBuff.append('/'); + break; + case '<': + nameBuff.append("<"); + nestLevel++; + StringBuffer innerBuff = new StringBuffer(); + while (nestLevel > 0) { + c = name.charAt(++i); + if (c == '<') { + nestLevel++; + } else if (c == '>') { + nestLevel--; + } + if (c == ',' && nestLevel == 1) { + nameBuff.append(nameToSignature(innerBuff.toString())); + innerBuff = new StringBuffer(); + } else { + if (nestLevel > 0) { + innerBuff.append(c); + } + } + } + nameBuff.append(nameToSignature(innerBuff.toString())); + nameBuff.append('>'); + break; +// case '>': +// throw new IllegalStateException("Should by matched by <"); +// case ',': +// throw new IllegalStateException("Should only happen inside <...>"); + default: + nameBuff.append(c); + } + } + nameBuff.append(";"); + return nameBuff.toString(); + } + } + + /** + * Write out an UnresolvedType - the signature should be enough. + */ + public final void write(CompressingDataOutputStream s) throws IOException { + s.writeUTF(getSignature()); + } + + /** + * Read in an UnresolvedType - just read the signature and rebuild the UnresolvedType. + */ + public static UnresolvedType read(DataInputStream s) throws IOException { + String sig = s.readUTF(); + if (sig.equals(MISSING_NAME)) { + return ResolvedType.MISSING; + } else { + // TODO isn't it a shame to build these (this method is expensive) and then chuck them away on resolution? + // TODO review callers and see if they are immediately resolving it, maybe we can do something more optimal if they are + return UnresolvedType.forSignature(sig); + } + } + + public String getNameAsIdentifier() { + return getName().replace('.', '_'); + } + + public String getPackageNameAsIdentifier() { + String name = getName(); + int index = name.lastIndexOf('.'); + if (index == -1) { + return ""; + } else { + return name.substring(0, index).replace('.', '_'); + } + } + + public UnresolvedType[] getTypeParameters() { + return typeParameters == null ? UnresolvedType.NONE : typeParameters; + } + + public TypeVariable[] getTypeVariables() { + return typeVariables; + } + + public static class TypeKind { + // Note: It is not sufficient to say that a parameterized type with no type parameters in fact + // represents a raw type - a parameterized type with no type parameters can represent + // an inner type of a parameterized type that specifies no type parameters of its own. + public final static TypeKind PRIMITIVE = new TypeKind("primitive"); + public final static TypeKind SIMPLE = new TypeKind("simple"); // a type with NO type parameters/vars + public final static TypeKind RAW = new TypeKind("raw"); // the erasure of a generic type + public final static TypeKind GENERIC = new TypeKind("generic"); // a generic type + public final static TypeKind PARAMETERIZED = new TypeKind("parameterized"); // a parameterized type + public final static TypeKind TYPE_VARIABLE = new TypeKind("type_variable"); // a type variable + public final static TypeKind WILDCARD = new TypeKind("wildcard"); // a generic wildcard type + + @Override + public String toString() { + return type; + } + + private TypeKind(String type) { + this.type = type; + } + + private final String type; + } + + @Override + public TypeVariable getTypeVariableNamed(String name) { + TypeVariable[] vars = getTypeVariables(); + if (vars == null || vars.length == 0) { + return null; + } + for (int i = 0; i < vars.length; i++) { + TypeVariable aVar = vars[i]; + if (aVar.getName().equals(name)) { + return aVar; + } + } + return null; + } + + @Override + public String toTraceString() { + return getClass().getName() + "[" + getName() + "]"; + } + + /** + * Return a version of this parameterized type in which any type parameters that are type variable references are replaced by + * their matching type variable binding. + */ + // OPTIMIZE methods like this just allow callers to be lazy and not ensure they are working with the right (resolved) subtype + public UnresolvedType parameterize(Map<String, UnresolvedType> typeBindings) { + throw new UnsupportedOperationException("unable to parameterize unresolved type: " + signature); + } + + /** + * @return the class name (does not include the package name) + */ + public String getClassName() { + if (className == null) { + String name = getName(); + if (name.indexOf("<") != -1) { + name = name.substring(0, name.indexOf("<")); + } + int index = name.lastIndexOf('.'); + if (index == -1) { + className = name; + } else { + className = name.substring(index + 1); + } + } + return className; + } + + /** + * @return the package name (no class name included) + */ + public String getPackageName() { + if (packageName == null) { + String name = getName(); + int angly = name.indexOf('<'); + if (angly != -1) { + name = name.substring(0, angly); + } + int index = name.lastIndexOf('.'); + if (index == -1) { + packageName = ""; + } else { + packageName = name.substring(0, index); + } + } + return packageName; + } + + public static void writeArray(UnresolvedType[] types, CompressingDataOutputStream stream) throws IOException { + int len = types.length; + stream.writeShort(len); + for (UnresolvedType type : types) { + type.write(stream); + } + } + + public static UnresolvedType[] readArray(DataInputStream s) throws IOException { + int len = s.readShort(); + if (len == 0) { + return UnresolvedType.NONE; + } + UnresolvedType[] types = new UnresolvedType[len]; + for (int i = 0; i < len; i++) { + types[i] = UnresolvedType.read(s); + } + return types; + } + + public static UnresolvedType makeArray(UnresolvedType base, int dims) { + StringBuffer sig = new StringBuffer(); + for (int i = 0; i < dims; i++) { + sig.append("["); + } + sig.append(base.getSignature()); + return UnresolvedType.forSignature(sig.toString()); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/UnresolvedTypeVariableReferenceType.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/UnresolvedTypeVariableReferenceType.java new file mode 100644 index 000000000..fbeb47164 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/UnresolvedTypeVariableReferenceType.java @@ -0,0 +1,90 @@ +/* ******************************************************************* + * Copyright (c) 2005-2010 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://eclipse.org/legal/epl-v10.html + * ******************************************************************/ +package org.aspectj.weaver; + +/** + * @author Adrian Colyer + * @author Andy Clement + */ +public class UnresolvedTypeVariableReferenceType extends UnresolvedType implements TypeVariableReference { + + private TypeVariable typeVariable; + + // constructor used as place-holder when dealing with circular refs such as Enum + public UnresolvedTypeVariableReferenceType() { + super("Ljava/lang/Object;"); + } + + public UnresolvedTypeVariableReferenceType(TypeVariable aTypeVariable) { + super("T" + aTypeVariable.getName() + ";", aTypeVariable.getFirstBound().getErasureSignature());//aTypeVariable.getFirstBound().getSignature()); + this.typeVariable = aTypeVariable; + } + + // only used when resolving circular refs... + public void setTypeVariable(TypeVariable aTypeVariable) { + this.signature = "T" + aTypeVariable.getName() + ";"; // aTypeVariable.getUpperBound().getSignature(); + this.signatureErasure = aTypeVariable.getFirstBound().getErasureSignature(); + this.typeVariable = aTypeVariable; + this.typeKind = TypeKind.TYPE_VARIABLE; + } + + @Override + public ResolvedType resolve(World world) { + TypeVariableDeclaringElement typeVariableScope = world.getTypeVariableLookupScope(); + TypeVariable resolvedTypeVariable = null; + TypeVariableReferenceType tvrt = null; + if (typeVariableScope == null) { + // throw new BCException("There is no scope in which to lookup type variables!"); + // FIXME asc correct thing to do is go bang, but to limp along, lets cope with the scope missing + resolvedTypeVariable = typeVariable.resolve(world); + tvrt = new TypeVariableReferenceType(resolvedTypeVariable, world); + } else { + boolean foundOK = false; + resolvedTypeVariable = typeVariableScope.getTypeVariableNamed(typeVariable.getName()); + // FIXME asc remove this when the shared type var stuff is sorted + if (resolvedTypeVariable == null) { + resolvedTypeVariable = typeVariable.resolve(world); + } else { + foundOK = true; + } + tvrt = new TypeVariableReferenceType(resolvedTypeVariable, world); + } + + return tvrt; + } + + @Override + public boolean isTypeVariableReference() { + return true; + } + + public TypeVariable getTypeVariable() { + return typeVariable; + } + + @Override + public String toString() { + if (typeVariable == null) { + return "<type variable not set!>"; + } else { + return "T" + typeVariable.getName() + ";"; + } + } + + @Override + public String toDebugString() { + return typeVariable.getName(); + } + + @Override + public String getErasureSignature() { + return typeVariable.getFirstBound().getSignature(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Utils.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Utils.java new file mode 100644 index 000000000..8053af0c0 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/Utils.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * 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://eclipse.org/legal/epl-v10.html + * + * ******************************************************************/ +package org.aspectj.weaver; + +/** + * + * @author Andy Clement + */ +public class Utils { + + /** + * Check if the annotations contain a SuppressAjWarnings annotation and if that annotation specifies that the given lint message + * (identified by its key) should be ignored. + * + */ + public static boolean isSuppressing(AnnotationAJ[] anns, String lintkey) { + if (anns == null) { + return false; + } + // Go through the annotation types on the advice + for (int i = 0; i < anns.length; i++) { + if (UnresolvedType.SUPPRESS_AJ_WARNINGS.getSignature().equals(anns[i].getTypeSignature())) { + // Two possibilities: + // 1. there are no values specified (i.e. @SuppressAjWarnings) + // 2. there are values specified (i.e. @SuppressAjWarnings("A") or @SuppressAjWarnings({"A","B"}) + String value = anns[i].getStringFormOfValue("value"); + // Slightly lazy, just doing a string indexof + if (value == null || value.indexOf(lintkey) != -1) { + return true; + } + } + } + return false; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/VersionedDataInputStream.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/VersionedDataInputStream.java new file mode 100644 index 000000000..38b2081b0 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/VersionedDataInputStream.java @@ -0,0 +1,87 @@ +/* ******************************************************************* + * Copyright (c) 2005-2010 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 (IBM, SpringSource) + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; + +/** + * Lightweight subclass of DataInputStream that knows what version of the weaver was used to construct the data in it. The input + * stream has a constant pool reader attached which enables it to decode constant pool references found within the data being read. + * + * @author Andy Clement + */ +public class VersionedDataInputStream extends DataInputStream { + + private WeaverVersionInfo version = new WeaverVersionInfo();// assume we are the latest unless something tells us otherwise... + + private ConstantPoolReader constantPoolReader; + + public VersionedDataInputStream(InputStream is, ConstantPoolReader constantPoolReader) { + super(is); + this.constantPoolReader = constantPoolReader; + } + + public int getMajorVersion() { + return version.getMajorVersion(); + } + + public int getMinorVersion() { + return version.getMinorVersion(); + } + + public long getBuildstamp() { + return version.getBuildstamp(); + } + + public void setVersion(WeaverVersionInfo version) { + this.version = version; + } + + public String readUtf8(int cpIndex) { + if (constantPoolReader == null) { + throw new IllegalStateException(); + } + if (cpIndex < 0) { + throw new IllegalStateException(cpIndex + ""); + } + return constantPoolReader.readUtf8(cpIndex); + } + + public boolean canDecompress() { + return constantPoolReader != null; + } + + public boolean isAtLeast169() { + return getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_AJ169; + } + + public String readPath() throws IOException { + return readUtf8(readShort()); + } + + public String readSignature() throws IOException { + return readUtf8(readShort()); + } + + public UnresolvedType readSignatureAsUnresolvedType() throws IOException { + return UnresolvedType.forSignature(readUtf8(readShort())); + } + + public String toString() { + return "VersionedDataInputStream: version=" + version + " constantPoolReader?" + (constantPoolReader != null); + } +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/WeakClassLoaderReference.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/WeakClassLoaderReference.java new file mode 100644 index 000000000..ddca93202 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/WeakClassLoaderReference.java @@ -0,0 +1,69 @@ +/* ******************************************************************* + * 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; + +import java.lang.ref.WeakReference; + +/** + * 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). + * <p> + * In more detail:<br> + * 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, Abraham Nevado + */ +public class WeakClassLoaderReference{ + + protected final int hashcode; + + private final WeakReference loaderRef; + + public WeakClassLoaderReference(ClassLoader loader) { + loaderRef = new WeakReference(loader); + if(loader == null){ + // Bug: 363962 + // Check that ClassLoader is not null, for instance when loaded from BootStrapClassLoader + hashcode = System.identityHashCode(this); + }else{ + hashcode = loader.hashCode() * 37; + } + } + + public ClassLoader getClassLoader() { + ClassLoader instance = (ClassLoader) loaderRef.get(); + // Assert instance!=null + return instance; + } + + public boolean equals(Object obj) { + if (!(obj instanceof WeakClassLoaderReference)) + return false; + WeakClassLoaderReference other = (WeakClassLoaderReference) obj; + return (other.hashcode == hashcode); + } + + public int hashCode() { + return hashcode; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/WeaverMessages.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/WeaverMessages.java new file mode 100644 index 000000000..5a64a77e8 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/WeaverMessages.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (c) 2004-2019 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 + *******************************************************************************/ +package org.aspectj.weaver; + +import java.text.MessageFormat; +import java.util.ResourceBundle; + +/** + * @author Andy Clement + * @author IBM + */ +public class WeaverMessages { + + private static ResourceBundle bundle = ResourceBundle.getBundle("org.aspectj.weaver.weaver-messages"); + + public static final String ARGS_IN_DECLARE = "argsInDeclare"; + public static final String CFLOW_IN_DECLARE = "cflowInDeclare"; + public static final String IF_IN_DECLARE = "ifInDeclare"; + public static final String THIS_OR_TARGET_IN_DECLARE = "thisOrTargetInDeclare"; + public static final String ABSTRACT_POINTCUT = "abstractPointcut"; + public static final String POINCUT_NOT_CONCRETE = "abstractPointcutNotMadeConcrete"; + public static final String POINTCUT_NOT_VISIBLE = "pointcutNotVisible"; + public static final String CONFLICTING_INHERITED_POINTCUTS = "conflictingInheritedPointcuts"; + public static final String CIRCULAR_POINTCUT = "circularPointcutDeclaration"; + public static final String CANT_FIND_POINTCUT = "cantFindPointcut"; + public static final String EXACT_TYPE_PATTERN_REQD = "exactTypePatternRequired"; + public static final String CANT_BIND_TYPE = "cantBindType"; + public static final String WILDCARD_NOT_ALLOWED = "wildcardTypePatternNotAllowed"; + public static final String FIELDS_CANT_HAVE_VOID_TYPE = "fieldCantBeVoid"; + public static final String NO_NEWARRAY_JOINPOINTS_BY_DEFAULT = "noNewArrayJoinpointsByDefault"; + public static final String UNSUPPORTED_POINTCUT_PRIMITIVE = "unsupportedPointcutPrimitive"; + public static final String MISSING_TYPE_PREVENTS_MATCH = "missingTypePreventsMatch"; + + public static final String DECP_OBJECT = "decpObject"; + public static final String CANT_EXTEND_SELF = "cantExtendSelf"; + public static final String INTERFACE_CANT_EXTEND_CLASS = "interfaceExtendClass"; + public static final String DECP_HIERARCHY_ERROR = "decpHierarchy"; + + public static final String MULTIPLE_MATCHES_IN_PRECEDENCE = "multipleMatchesInPrecedence"; + public static final String TWO_STARS_IN_PRECEDENCE = "circularityInPrecedenceStar"; + public static final String CLASSES_IN_PRECEDENCE = "nonAspectTypesInPrecedence"; + public static final String TWO_PATTERN_MATCHES_IN_PRECEDENCE = "circularityInPrecedenceTwo"; + + public static final String NOT_THROWABLE = "notThrowable"; + + public static final String ITD_CONS_ON_ASPECT = "itdConsOnAspect"; + public static final String ITD_RETURN_TYPE_MISMATCH = "returnTypeMismatch"; + public static final String ITD_PARAM_TYPE_MISMATCH = "paramTypeMismatch"; + public static final String ITD_VISIBILITY_REDUCTION = "visibilityReduction"; + public static final String ITD_DOESNT_THROW = "doesntThrow"; + public static final String ITD_OVERRIDDEN_STATIC = "overriddenStatic"; + public static final String ITD_OVERIDDING_STATIC = "overridingStatic"; + public static final String ITD_CONFLICT = "itdConflict"; + public static final String ITD_MEMBER_CONFLICT = "itdMemberConflict"; + public static final String ITD_NON_EXPOSED_IMPLEMENTOR = "itdNonExposedImplementor"; + public static final String ITD_ABSTRACT_MUST_BE_PUBLIC_ON_INTERFACE = "itdAbstractMustBePublicOnInterface"; + public static final String CANT_OVERRIDE_FINAL_MEMBER = "cantOverrideFinalMember"; + + public static final String NON_VOID_RETURN = "nonVoidReturn"; + public static final String INCOMPATIBLE_RETURN_TYPE = "incompatibleReturnType"; + public static final String CANT_THROW_CHECKED = "cantThrowChecked"; + public static final String CIRCULAR_DEPENDENCY = "circularDependency"; + + public static final String MISSING_PER_CLAUSE = "missingPerClause"; + public static final String WRONG_PER_CLAUSE = "wrongPerClause"; + + public static final String ALREADY_WOVEN = "alreadyWoven"; + public static final String REWEAVABLE_MODE = "reweavableMode"; + public static final String PROCESSING_REWEAVABLE = "processingReweavable"; + public static final String MISSING_REWEAVABLE_TYPE = "missingReweavableType"; + public static final String VERIFIED_REWEAVABLE_TYPE = "verifiedReweavableType"; + public static final String ASPECT_NEEDED = "aspectNeeded"; + public static final String REWEAVABLE_ASPECT_NOT_REGISTERED = "reweavableAspectNotRegistered"; + + public static final String CANT_FIND_TYPE = "cantFindType"; + public static final String CANT_FIND_CORE_TYPE = "cantFindCoreType"; + public static final String CANT_FIND_TYPE_WITHINPCD = "cantFindTypeWithinpcd"; + public static final String CANT_FIND_TYPE_DURING_AROUND_WEAVE = "cftDuringAroundWeave"; + public static final String CANT_FIND_TYPE_DURING_AROUND_WEAVE_PREINIT = "cftDuringAroundWeavePreinit"; + public static final String CANT_FIND_TYPE_EXCEPTION_TYPE = "cftExceptionType"; + public static final String CANT_FIND_TYPE_ARG_TYPE = "cftArgType"; + public static final String CANT_FIND_PARENT_TYPE = "cantFindParentType"; + public static final String CANT_FIND_PARENT_TYPE_NO_SUB = "cantFindParentTypeNoSub"; + public static final String CANT_FIND_TYPE_FIELDS = "cantFindTypeFields"; + public static final String CANT_FIND_TYPE_SUPERCLASS = "cantFindTypeSuperclass"; + public static final String CANT_FIND_TYPE_INTERFACES = "cantFindTypeInterfaces"; + public static final String CANT_FIND_TYPE_METHODS = "cantFindTypeMethods"; + public static final String CANT_FIND_TYPE_POINTCUTS = "cantFindTypePointcuts"; + public static final String CANT_FIND_TYPE_MODIFIERS = "cantFindTypeModifiers"; + public static final String CANT_FIND_TYPE_ANNOTATION = "cantFindTypeAnnotation"; + public static final String CANT_FIND_TYPE_ASSIGNABLE = "cantFindTypeAssignable"; + public static final String CANT_FIND_TYPE_COERCEABLE = "cantFindTypeCoerceable"; + public static final String CANT_FIND_TYPE_JOINPOINT = "cantFindTypeJoinPoint"; + public static final String CANT_FIND_TYPE_INTERFACE_METHODS = "cantFindTypeInterfaceMethods"; + + public static final String DECP_BINARY_LIMITATION = "decpBinaryLimitation"; + public static final String OVERWRITE_JSR45 = "overwriteJSR45"; + public static final String IF_IN_PERCLAUSE = "ifInPerClause"; + public static final String IF_LEXICALLY_IN_CFLOW = "ifLexicallyInCflow"; + public static final String ONLY_BEFORE_ON_HANDLER = "onlyBeforeOnHandler"; + public static final String NO_AROUND_ON_SYNCHRONIZATION = "noAroundOnSynchronization"; + public static final String AROUND_ON_PREINIT = "aroundOnPreInit"; + public static final String AROUND_ON_INIT = "aroundOnInit"; + public static final String AROUND_ON_INTERFACE_STATICINIT = "aroundOnInterfaceStaticInit"; + + public static final String PROBLEM_GENERATING_METHOD = "problemGeneratingMethod"; + public static final String CLASS_TOO_BIG = "classTooBig"; + + public static final String ZIPFILE_ENTRY_MISSING = "zipfileEntryMissing"; + public static final String ZIPFILE_ENTRY_INVALID = "zipfileEntryInvalid"; + public static final String DIRECTORY_ENTRY_MISSING = "directoryEntryMissing"; + public static final String OUTJAR_IN_INPUT_PATH = "outjarInInputPath"; + + public static final String XLINT_LOAD_ERROR = "problemLoadingXLint"; + public static final String XLINTDEFAULT_LOAD_ERROR = "unableToLoadXLintDefault"; + public static final String XLINTDEFAULT_LOAD_PROBLEM = "errorLoadingXLintDefault"; + public static final String XLINT_KEY_ERROR = "invalidXLintKey"; + public static final String XLINT_VALUE_ERROR = "invalidXLintMessageKind"; + + public static final String UNBOUND_FORMAL = "unboundFormalInPC"; + public static final String AMBIGUOUS_BINDING = "ambiguousBindingInPC"; + public static final String AMBIGUOUS_BINDING_IN_OR = "ambiguousBindingInOrPC"; + public static final String NEGATION_DOESNT_ALLOW_BINDING = "negationDoesntAllowBinding"; + + // Java5 messages + public static final String ITDC_ON_ENUM_NOT_ALLOWED = "itdcOnEnumNotAllowed"; + public static final String ITDM_ON_ENUM_NOT_ALLOWED = "itdmOnEnumNotAllowed"; + public static final String ITDF_ON_ENUM_NOT_ALLOWED = "itdfOnEnumNotAllowed"; + public static final String CANT_DECP_ON_ENUM_TO_IMPL_INTERFACE = "cantDecpOnEnumToImplInterface"; + public static final String CANT_DECP_ON_ENUM_TO_EXTEND_CLASS = "cantDecpOnEnumToExtendClass"; + public static final String CANT_DECP_TO_MAKE_ENUM_SUPERTYPE = "cantDecpToMakeEnumSupertype"; + public static final String ITDC_ON_ANNOTATION_NOT_ALLOWED = "itdcOnAnnotationNotAllowed"; + public static final String ITDM_ON_ANNOTATION_NOT_ALLOWED = "itdmOnAnnotationNotAllowed"; + public static final String ITDF_ON_ANNOTATION_NOT_ALLOWED = "itdfOnAnnotationNotAllowed"; + public static final String CANT_DECP_ON_ANNOTATION_TO_IMPL_INTERFACE = "cantDecpOnAnnotationToImplInterface"; + public static final String CANT_DECP_ON_ANNOTATION_TO_EXTEND_CLASS = "cantDecpOnAnnotationToExtendClass"; + public static final String CANT_DECP_TO_MAKE_ANNOTATION_SUPERTYPE = "cantDecpToMakeAnnotationSupertype"; + public static final String REFERENCE_TO_NON_ANNOTATION_TYPE = "referenceToNonAnnotationType"; + public static final String BINDING_NON_RUNTIME_RETENTION_ANNOTATION = "bindingNonRuntimeRetentionAnnotation"; + + public static final String UNSUPPORTED_ANNOTATION_VALUE_TYPE = "unsupportedAnnotationValueType"; + + public static final String INCORRECT_TARGET_FOR_DECLARE_ANNOTATION = "incorrectTargetForDeclareAnnotation"; + public static final String NO_MATCH_BECAUSE_SOURCE_RETENTION = "noMatchBecauseSourceRetention"; + + // Annotation Value messages + public static final String INVALID_ANNOTATION_VALUE = "invalidAnnotationValue"; + public static final String UNKNOWN_ANNOTATION_VALUE = "unknownAnnotationValue"; + + // < Java5 messages + public static final String ATANNOTATION_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "atannotationNeedsJava5"; + public static final String ATWITHIN_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "atwithinNeedsJava5"; + public static final String ATWITHINCODE_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "atwithincodeNeedsJava5"; + public static final String ATTHIS_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "atthisNeedsJava5"; + public static final String ATTARGET_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "attargetNeedsJava5"; + public static final String ATARGS_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "atargsNeedsJava5"; + public static final String DECLARE_ATTYPE_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "declareAtTypeNeedsJava5"; + public static final String DECLARE_ATMETHOD_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "declareAtMethodNeedsJava5"; + public static final String DECLARE_ATFIELD_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "declareAtFieldNeedsJava5"; + public static final String DECLARE_ATCONS_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "declareAtConsNeedsJava5"; + public static final String ANNOTATIONS_NEED_JAVA5 = "annotationsRequireJava5"; + + // Generics + public static final String CANT_DECP_MULTIPLE_PARAMETERIZATIONS = "cantDecpMultipleParameterizations"; + public static final String HANDLER_PCD_DOESNT_SUPPORT_PARAMETERS = "noParameterizedTypePatternInHandler"; + public static final String INCORRECT_NUMBER_OF_TYPE_ARGUMENTS = "incorrectNumberOfTypeArguments"; + public static final String VIOLATES_TYPE_VARIABLE_BOUNDS = "violatesTypeVariableBounds"; + public static final String NO_STATIC_INIT_JPS_FOR_PARAMETERIZED_TYPES = "noStaticInitJPsForParameterizedTypes"; + public static final String NOT_A_GENERIC_TYPE = "notAGenericType"; + public static final String WITHIN_PCD_DOESNT_SUPPORT_PARAMETERS = "noParameterizedTypePatternInWithin"; + public static final String THIS_AND_TARGET_DONT_SUPPORT_PARAMETERS = "noParameterizedTypesInThisAndTarget"; + public static final String GET_AND_SET_DONT_SUPPORT_DEC_TYPE_PARAMETERS = "noParameterizedTypesInGetAndSet"; + public static final String NO_INIT_JPS_FOR_PARAMETERIZED_TYPES = "noInitJPsForParameterizedTypes"; + public static final String NO_GENERIC_THROWABLES = "noGenericThrowables"; + public static final String WITHINCODE_DOESNT_SUPPORT_PARAMETERIZED_DECLARING_TYPES = "noParameterizedDeclaringTypesWithinCode"; + public static final String EXECUTION_DOESNT_SUPPORT_PARAMETERIZED_DECLARING_TYPES = "noParameterizedDeclaringTypesInExecution"; + public static final String CALL_DOESNT_SUPPORT_PARAMETERIZED_DECLARING_TYPES = "noParameterizedDeclaringTypesInCall"; + public static final String CANT_REFERENCE_POINTCUT_IN_RAW_TYPE = "noRawTypePointcutReferences"; + + public static final String HAS_MEMBER_NOT_ENABLED = "hasMemberNotEnabled"; + + public static final String MUST_KEEP_OVERWEAVING_ONCE_START = "mustKeepOverweavingOnceStart"; + + // @AspectJ + public static final String RETURNING_FORMAL_NOT_DECLARED_IN_ADVICE = "returningFormalNotDeclaredInAdvice"; + public static final String THROWN_FORMAL_NOT_DECLARED_IN_ADVICE = "thrownFormalNotDeclaredInAdvice"; + + public static String format(String key) { + return bundle.getString(key); + } + + public static String format(String key, Object insert) { + return MessageFormat.format(bundle.getString(key), new Object[] { insert }); + } + + public static String format(String key, Object insert1, Object insert2) { + return MessageFormat.format(bundle.getString(key), new Object[] { insert1, insert2 }); + } + + public static String format(String key, Object insert1, Object insert2, Object insert3) { + return MessageFormat.format(bundle.getString(key), new Object[] { insert1, insert2, insert3 }); + } + + public static String format(String key, Object insert1, Object insert2, Object insert3, Object insert4) { + return MessageFormat.format(bundle.getString(key), new Object[] { insert1, insert2, insert3, insert4 }); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/WeaverStateInfo.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/WeaverStateInfo.java new file mode 100644 index 000000000..f0dcbf270 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/WeaverStateInfo.java @@ -0,0 +1,581 @@ +/* ******************************************************************* + * Copyright (c) 2002-2019 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.aspectj.bridge.IMessage; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; + +/** + * WeaverStateInfo represents how a type was processed. It is used by the weaver to determine how a type was previously treated and + * whether reweaving is allowed. The format in the data stream is: + * + * Byte: Kind. UNTOUCHED|WOVEN|EXTENDED - If extended it can have two extra bits set 'REWEAVABLE' and 'REWEAVABLE_COMPRESSION_BIT' + * Short: typeMungerCount - how many type mungers have affected this type <UnresolvedType & ResolvedTypeMunger>: The type mungers + * themselves If we are reweavable then we also have: Short: Number of aspects that touched this type in some way when it was + * previously woven <String> The fully qualified name of each type Int: Length of class file data (i.e. the unwovenclassfile) + * Byte[]: The class file data, compressed if REWEAVABLE_COMPRESSION_BIT set. + * + * @author Andy Clement + */ +public class WeaverStateInfo { + private List<Entry> typeMungers; + private boolean oldStyle; + + private boolean reweavable; + private boolean reweavableCompressedMode; // If true, unwovenClassFile is uncompressed on read + private boolean reweavableDiffMode; // if true, unwovenClassFile is written and read as a diff + + // These must exist in the world for reweaving to be valid. + // It is a set of signatures 'La/b/c/D;' + private Set<String> aspectsAffectingType; + + private byte[] unwovenClassFile; // Original 'untouched' class file + private static boolean reweavableDefault = true; // ajh02: changed from false; + private static boolean reweavableCompressedModeDefault = false; + private static boolean reweavableDiffModeDefault = true; + + // when serializing the WeaverStateInfo we come to adding the reweavable data, + // we'd like to add a diff of the unwovenClassFile and the wovenClassFile, + // but we don't have the wovenClassFile yet as we're still in the process of making it. + // so we put this key there instead as a stub. + // Then when the wovenClassFile has been made, replaceKeyWithDiff is called. + private static byte[] key = { -51, 34, 105, 56, -34, 65, 45, 78, -26, 125, 114, 97, 98, 1, -1, -42 }; + private boolean unwovenClassFileIsADiff = false; + + int compressionEnabled = 0; // 0=dont know, 1=no, 2=yes + + private void checkCompressionEnabled() { + if (compressionEnabled == 0) { + // work it out! + compressionEnabled = 1; + try { + String value = System.getProperty("aspectj.compression.weaverstateinfo", "false"); + if (value.equalsIgnoreCase("true")) { + System.out.println("ASPECTJ: aspectj.compression.weaverstateinfo=true: compressing weaverstateinfo"); + compressionEnabled = 2; + } + } catch (Throwable t) { + // nop + } + } + } + + private WeaverStateInfo() { + // this(new ArrayList(), false,reweavableDefault,reweavableCompressedModeDefault,reweavableDiffModeDefault); + } + + public WeaverStateInfo(boolean reweavable) { + this(new ArrayList<Entry>(), false, reweavable, reweavableCompressedModeDefault, reweavableDiffModeDefault); + } + + private WeaverStateInfo(List<Entry> typeMungers, boolean oldStyle, boolean reweavableMode, boolean reweavableCompressedMode, + boolean reweavableDiffMode) { + this.typeMungers = typeMungers; + this.oldStyle = oldStyle; + this.reweavable = reweavableMode; + this.reweavableCompressedMode = reweavableCompressedMode; + this.reweavableDiffMode = reweavableMode ? reweavableDiffMode : false; + this.aspectsAffectingType = new HashSet<String>(); + this.unwovenClassFile = null; + } + + public static void setReweavableModeDefaults(boolean mode, boolean compress, boolean diff) { + reweavableDefault = mode; + reweavableCompressedModeDefault = compress; + reweavableDiffModeDefault = diff; + } + + private static final int UNTOUCHED = 0, WOVEN = 2, EXTENDED = 3; + + // Use 'bits' for these capabilities - only valid in EXTENDED mode + private static final byte REWEAVABLE_BIT = 1 << 4; + private static final byte REWEAVABLE_COMPRESSION_BIT = 1 << 5; + private static final byte REWEAVABLE_DIFF_BIT = 1 << 6; + + /** See comments on write() */ + public static final WeaverStateInfo read(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte b = s.readByte(); + + boolean isReweavable = ((b & REWEAVABLE_BIT) != 0); + if (isReweavable) { + b = (byte) (b - REWEAVABLE_BIT); + } + + boolean isReweavableCompressed = ((b & REWEAVABLE_COMPRESSION_BIT) != 0); + if (isReweavableCompressed) { + b = (byte) (b - REWEAVABLE_COMPRESSION_BIT); + } + + boolean isReweavableDiff = ((b & REWEAVABLE_DIFF_BIT) != 0); + if (isReweavableDiff) { + b = (byte) (b - REWEAVABLE_DIFF_BIT); + } + + switch (b) { + case UNTOUCHED: + throw new RuntimeException("unexpected UNWOVEN"); + case WOVEN: + return new WeaverStateInfo(Collections.<Entry>emptyList(), true, isReweavable, isReweavableCompressed, isReweavableDiff); + case EXTENDED: + boolean isCompressed = false; + if (s.isAtLeast169()) { + isCompressed = s.readBoolean(); + } + + int n = s.readShort(); + List<Entry> l = new ArrayList<Entry>(); + for (int i = 0; i < n; i++) { + // conditional on version + UnresolvedType aspectType = null; + if (isCompressed) { + int cpIndex = s.readShort(); + String signature = s.readUtf8(cpIndex); + if (signature.charAt(0) == '@') { // '@missing@' + aspectType = ResolvedType.MISSING; + } else { + aspectType = UnresolvedType.forSignature(signature); + } + } else { + aspectType = UnresolvedType.read(s); + } + ResolvedTypeMunger typeMunger = ResolvedTypeMunger.read(s, context); + l.add(new Entry(aspectType, typeMunger)); + } + WeaverStateInfo wsi = new WeaverStateInfo(l, false, isReweavable, isReweavableCompressed, isReweavableDiff); + readAnyReweavableData(wsi, s, isCompressed); + return wsi; + } + throw new RuntimeException("bad WeaverState.Kind: " + b + ". File was :" + + (context == null ? "unknown" : context.makeSourceLocation(0, 0).toString())); + } + + private static class Entry { + public UnresolvedType aspectType; + public ResolvedTypeMunger typeMunger; + + public Entry(UnresolvedType aspectType, ResolvedTypeMunger typeMunger) { + this.aspectType = aspectType; + this.typeMunger = typeMunger; + } + + public String toString() { + return "<" + aspectType + ", " + typeMunger + ">"; + } + } + + /** + * Serialize the WeaverStateInfo. Various bits are set within the 'kind' flag to indicate the structure of the attribute. In + * reweavable diff mode a 'marker' is inserted at the start of the attribute to indicate where the final calculated diff should + * be inserted. When the key is replaced with the diff, the 'kind' byte moves to the front of the attribute - thats why in the + * read logic you'll see it expecting the kind as the first byte. + */ + public void write(CompressingDataOutputStream s) throws IOException { + checkCompressionEnabled(); + if (oldStyle || reweavableCompressedMode) { + throw new RuntimeException("shouldn't be writing this"); + } + + byte weaverStateInfoKind = EXTENDED; + if (reweavable) { + weaverStateInfoKind |= REWEAVABLE_BIT; + } + + if (reweavableDiffMode) { + s.write(key); // put key in so we can replace it with the diff later + weaverStateInfoKind |= REWEAVABLE_DIFF_BIT; + } + + s.writeByte(weaverStateInfoKind); + + // Tag whether the remainder of the data is subject to cp compression + try { + s.compressionEnabled = compressionEnabled == 2; + s.writeBoolean(s.canCompress()); + + int n = typeMungers.size(); + s.writeShort(n); + for (Entry e : typeMungers) { + if (s.canCompress()) { + s.writeCompressedSignature(e.aspectType.getSignature()); + } else { + e.aspectType.write(s); + } + e.typeMunger.write(s); + } + writeAnyReweavableData(this, s, s.canCompress()); + } finally { + s.compressionEnabled = true; + } + } + + private final static byte[] NO_BYTES = new byte[0]; + + /** + * If the weaver is ever invoked in over weaving mode, we should + * not include the key when writing out, it won't be replaced later. + * If we turn off the reweaving flag that unfortunately removes + * the 'what aspects have been woven into this type' list which we + * want to keep as it helps overweaving avoid weaving an aspect in + * twice. + */ + public void markOverweavingInUse() { + reweavableDiffMode = false; + unwovenClassFile = NO_BYTES; + } + + public void addConcreteMunger(ConcreteTypeMunger munger) { + typeMungers.add(new Entry(munger.getAspectType(), munger.getMunger())); + } + + public String toString() { + return "WeaverStateInfo(aspectsAffectingType=" + aspectsAffectingType + "," + typeMungers + ", " + oldStyle + ")"; + } + + public List<ConcreteTypeMunger> getTypeMungers(ResolvedType onType) { + World world = onType.getWorld(); + List<ConcreteTypeMunger> ret = new ArrayList<ConcreteTypeMunger>(); + for (Entry entry : typeMungers) { + ResolvedType aspectType = world.resolve(entry.aspectType, true); + if (aspectType.isMissing()) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ASPECT_NEEDED, entry.aspectType, onType), + onType.getSourceLocation(), null); + continue; + } + + ret.add(new TemporaryTypeMunger(entry.typeMunger, aspectType)); + } + return ret; + } + + public boolean isOldStyle() { + return oldStyle; + } + + public byte[] getUnwovenClassFileData() { + return unwovenClassFile; + } + + public byte[] getUnwovenClassFileData(byte wovenClassFile[]) { + if (unwovenClassFileIsADiff) { + unwovenClassFile = applyDiff(wovenClassFile, unwovenClassFile); + unwovenClassFileIsADiff = false; + } + return unwovenClassFile; + } + + public void setUnwovenClassFileData(byte[] data) { + unwovenClassFile = data; + } + + public boolean isReweavable() { + return reweavable; + } + + public void setReweavable(boolean rw) { + reweavable = rw; + } + + public void addAspectsAffectingType(Collection<String> aspects) { + aspectsAffectingType.addAll(aspects); + } + + public void addAspectAffectingType(String aspectSignature) { + aspectsAffectingType.add(aspectSignature); + } + + public Set<String> getAspectsAffectingType() { + return this.aspectsAffectingType; + } + + private static void readAnyReweavableData(WeaverStateInfo wsi, VersionedDataInputStream s, boolean compressed) + throws IOException { + if (wsi.isReweavable()) { + // Load list of aspects that need to exist in the world for reweaving to be 'legal' + int numberAspectsAffectingType = s.readShort(); + for (int i = 0; i < numberAspectsAffectingType; i++) { + String str = null; + if (compressed) { + str = s.readSignature(); + } else { + str = s.readUTF(); + // Prior to 1.6.9 we were writing out names (com.foo.Bar) rather than signatures (Lcom/foo/Bar;) + // From 1.6.9 onwards we write out signatures (pr319431) + if (s.getMajorVersion() < WeaverVersionInfo.WEAVER_VERSION_AJ169) { + // It is a name, make it a signature + StringBuilder sb = new StringBuilder(); + sb.append("L").append(str.replace('.', '/')).append(";"); + str = sb.toString(); + } + } + wsi.addAspectAffectingType(str); + } + + int unwovenClassFileSize = s.readInt(); + byte[] classData = null; + // the unwovenClassFile may have been compressed: + if (wsi.reweavableCompressedMode) { + classData = new byte[unwovenClassFileSize]; + ZipInputStream zis = new ZipInputStream(s); + ZipEntry zen = zis.getNextEntry(); + int current = 0; + int bytesToGo = unwovenClassFileSize; + while (bytesToGo > 0) { + int amount = zis.read(classData, current, bytesToGo); + current += amount; + bytesToGo -= amount; + } + zis.closeEntry(); + if (bytesToGo != 0) { + throw new IOException("ERROR whilst reading compressed reweavable data, expected " + unwovenClassFileSize + + " bytes, only found " + current); + } + } else { + classData = new byte[unwovenClassFileSize]; + if (unwovenClassFileSize != 0) { + int bytesread = s.read(classData); + if (bytesread != unwovenClassFileSize) { + throw new IOException("ERROR whilst reading reweavable data, expected " + unwovenClassFileSize + + " bytes, only found " + bytesread); + } + } + } + + // if it was diffMode we'll have to remember to apply the diff if someone + // asks for the unwovenClassFile + wsi.unwovenClassFileIsADiff = wsi.reweavableDiffMode; + wsi.setUnwovenClassFileData(classData); + } + } + + /** + * Here is the cleverness for reweavable diff mode. The class file on disk contains, inside the weaverstateinfo attribute, a + * diff that can be applied to 'itself' to recover the original class - which can then be rewoven. + */ + public byte[] replaceKeyWithDiff(byte wovenClassFile[]) { + // we couldn't have made the diff earlier + // as we didn't have the wovenClassFile + // so we left a key there as a marker to come back to + + if (reweavableDiffMode) { + ByteArrayOutputStream arrayStream = new ByteArrayOutputStream(); + DataOutputStream s = new DataOutputStream(arrayStream); + + int endOfKey = findEndOfKey(wovenClassFile); + int startOfKey = endOfKey - key.length; + // the length of the wsi attribute is written infront of it in the classFile, + // swapping the diff for the key will probably change the length of the wsi, + // so we'll have to fiddle with the four 'int length' bytes + int oldLengthLocation = startOfKey - 4; + int oldLength = readInt(wovenClassFile, oldLengthLocation); + wovenClassFile = deleteInArray(wovenClassFile, startOfKey, endOfKey); // delete the key + + byte[] wovenClassFileUpToWSI = new byte[oldLengthLocation]; + System.arraycopy(wovenClassFile, 0, wovenClassFileUpToWSI, 0, oldLengthLocation); + + byte[] diff = generateDiff(wovenClassFileUpToWSI, unwovenClassFile); + try { // put the length of the diff infront of the diff + s.writeInt(diff.length); + s.write(diff); + } catch (IOException e) { + } + diff = arrayStream.toByteArray(); + // we have to swap the oldLength for the new one, + // and add the diff, using the oldLength to work out where it should go :) + + int newLength = oldLength - key.length + diff.length; + byte newLengthBytes[] = serializeInt(newLength); + + // swap in the serialized newLength for the oldOne: + wovenClassFile[oldLengthLocation] = newLengthBytes[0]; + wovenClassFile[oldLengthLocation + 1] = newLengthBytes[1]; + wovenClassFile[oldLengthLocation + 2] = newLengthBytes[2]; + wovenClassFile[oldLengthLocation + 3] = newLengthBytes[3]; + + // add the diff + wovenClassFile = insertArray(diff, wovenClassFile, oldLengthLocation + 4 + oldLength - key.length); + } + return wovenClassFile; + } + + private static final int findEndOfKey(byte[] wovenClassFile) { + // looks through the classfile backwards (as the attributes are all near the end) + for (int i = wovenClassFile.length - 1; i > 0; i--) { + if (endOfKeyHere(wovenClassFile, i)) { + return i + 1; + } + } + throw new RuntimeException("key not found in wovenClassFile"); // should never happen + } + + private static final boolean endOfKeyHere(byte lookIn[], int i) { + for (int j = 0; j < key.length; j++) { + if (key[key.length - 1 - j] != lookIn[i - j]) { + return false; + } + } + return true; + } + + private static final byte[] insertArray(byte toInsert[], byte original[], int offset) { + byte result[] = new byte[original.length + toInsert.length]; + System.arraycopy(original, 0, result, 0, offset); + System.arraycopy(toInsert, 0, result, offset, toInsert.length); + System.arraycopy(original, offset, result, offset + toInsert.length, original.length - offset); + return result; + } + + private static final int readInt(byte[] a, int offset) { + ByteArrayInputStream b = new ByteArrayInputStream(a, offset, 4); + DataInputStream d = new DataInputStream(b); + int length = -1; + try { + length = d.readInt(); + } catch (IOException e) { + throw (new RuntimeException("readInt called with a bad array or offset")); // should never happen + } + return length; + } + + private static final byte[] deleteInArray(byte a[], int start, int end) { + int lengthToDelete = end - start; + byte result[] = new byte[a.length - lengthToDelete]; // make a new array + System.arraycopy(a, 0, result, 0, start); // copy in the bit before the deleted bit + System.arraycopy(a, end, result, start, a.length - end); // copy in the bit after the deleted bit + return result; + } + + // ajh02: a quick note about the diff format... + // + // classfiles consist of: + // 8 bytes: magic number and minor and major versions, + // 2 bytes: its constant pool count + // n bytes: the rest of the class file + // + // weaving a classfile never changes the classfile's first 8 bytes, + // and after the constant pool count there's usually a run of bytes that weaving didn't change + // hereafter referred to as the run + // + // so the diff consists of: + // 2 bytes: its constant pool count + // 4 bytes: length of the run + // n bytes: the rest of the unwovenClassFile + + byte[] generateDiff(byte[] wovenClassFile, byte[] unWovenClassFile) { + + // find how long the run is + int lookingAt = 10; + int shorterLength = (wovenClassFile.length < unWovenClassFile.length) ? wovenClassFile.length : unWovenClassFile.length; + while (lookingAt < shorterLength && (wovenClassFile[lookingAt] == unWovenClassFile[lookingAt])) { + lookingAt++; + } + int lengthInCommon = lookingAt - 10; + byte[] diff = new byte[unWovenClassFile.length - 4 - lengthInCommon]; + + // first 2 bytes of the diff are the constant pool count + diff[0] = unWovenClassFile[8]; + diff[1] = unWovenClassFile[9]; + + // then 4 bytes saying how long the run is + byte[] lengthInCommonBytes = serializeInt(lengthInCommon); + diff[2] = lengthInCommonBytes[0]; + diff[3] = lengthInCommonBytes[1]; + diff[4] = lengthInCommonBytes[2]; + diff[5] = lengthInCommonBytes[3]; + + // then we just dump the rest of the unWovenClassFile verbatim + System.arraycopy(unWovenClassFile, 10 + lengthInCommon, diff, 6, diff.length - 6); + + return diff; + } + + byte[] applyDiff(byte[] wovenClassFile, byte[] diff) { + + int lengthInCommon = readInt(diff, 2); + byte[] unWovenClassFile = new byte[4 + diff.length + lengthInCommon]; + + // copy the first 8 bytes from the wovenClassFile + System.arraycopy(wovenClassFile, 0, unWovenClassFile, 0, 8); + + // copy the constant pool count from the diff + unWovenClassFile[8] = diff[0]; + unWovenClassFile[9] = diff[1]; + + // copy the run from the wovenClassFile + System.arraycopy(wovenClassFile, 10, unWovenClassFile, 10, lengthInCommon); + + // copy the stuff after the run from the diff + System.arraycopy(diff, 6, unWovenClassFile, 10 + lengthInCommon, diff.length - 6); + + return unWovenClassFile; + } + + private byte[] serializeInt(int i) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(4); + DataOutputStream dos = new DataOutputStream(bos); + try { + dos.writeInt(i); + } catch (IOException e) { + } + return bos.toByteArray(); + } + + private static void writeAnyReweavableData(WeaverStateInfo wsi, CompressingDataOutputStream s, boolean compress) + throws IOException { + if (wsi.isReweavable()) { + // Write out list of aspects that must exist next time we try and weave this class + s.writeShort(wsi.aspectsAffectingType.size()); + for (String type : wsi.aspectsAffectingType) { + if (compress) { + s.writeCompressedSignature(type); + } else { + s.writeUTF(type); + } + } + byte[] data = wsi.unwovenClassFile; + // if we're not in diffMode, write the unwovenClassFile now, + // otherwise we'll insert it as a diff later + if (!wsi.reweavableDiffMode) { + s.writeInt(data.length); + s.write(wsi.unwovenClassFile); + } + } + } + + /** + * @return true if the supplied aspect is already in the list of those affecting this type + */ + public boolean isAspectAlreadyApplied(ResolvedType someAspect) { + String someAspectSignature = someAspect.getSignature(); + for (String aspectSignature : aspectsAffectingType) { + if (aspectSignature.equals(someAspectSignature)) { + return true; + } + } + return false; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/WildcardedUnresolvedType.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/WildcardedUnresolvedType.java new file mode 100644 index 000000000..d3e609e7f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/WildcardedUnresolvedType.java @@ -0,0 +1,70 @@ +/* ******************************************************************* + * 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; + +/** + * Represents a wildcarded bound for a generic type, this can be unbounded '?' or bounded via extends '? extends Foo' or super '? + * super Foo'. The signature for a ? is in fact "*" and the erasure signature is the upper bound which defaults to java.lang.Object + * if nothing is specified. On resolution, this becomes a BoundedReferenceType + * + * @author Andy Clement + */ +public class WildcardedUnresolvedType extends UnresolvedType { + + // TODO does not cope with extra bounds '? extends A & B & C' + + public static final int UNBOUND = 0; + public static final int EXTENDS = 1; + public static final int SUPER = 2; + + public static final WildcardedUnresolvedType QUESTIONMARK = new WildcardedUnresolvedType("*", UnresolvedType.OBJECT, null); + + private int boundKind = UNBOUND; // UNBOUND, EXTENDS, SUPER + + private UnresolvedType lowerBound; + + private UnresolvedType upperBound; + + public WildcardedUnresolvedType(String signature, UnresolvedType upperBound, UnresolvedType lowerBound) { + super(signature, (upperBound == null ? UnresolvedType.OBJECT.signature : upperBound.signatureErasure)); + this.typeKind = TypeKind.WILDCARD; + this.upperBound = upperBound; + this.lowerBound = lowerBound; + if (signature.charAt(0) == '-') { + boundKind = SUPER; + } + if (signature.charAt(0) == '+') { + boundKind = EXTENDS; + } + } + + public UnresolvedType getUpperBound() { + return upperBound; + } + + public UnresolvedType getLowerBound() { + return lowerBound; + } + + public boolean isExtends() { + return boundKind == EXTENDS; + } + + public boolean isSuper() { + return boundKind == SUPER; + } + + public boolean isUnbound() { + return boundKind == UNBOUND; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/World.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/World.java new file mode 100644 index 000000000..644f232ac --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/World.java @@ -0,0 +1,2029 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * 2005 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: + * PARC initial implementation + * Adrian Colyer, Andy Clement, overhaul for generics, Abraham Nevado + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.WeakHashMap; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessage.Kind; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.bridge.context.PinpointingMessageHandler; +import org.aspectj.util.IStructureModel; +import org.aspectj.weaver.ResolvedType.Primitive; +import org.aspectj.weaver.UnresolvedType.TypeKind; +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.DeclareAnnotation; +import org.aspectj.weaver.patterns.DeclareParents; +import org.aspectj.weaver.patterns.DeclarePrecedence; +import org.aspectj.weaver.patterns.DeclareSoft; +import org.aspectj.weaver.patterns.DeclareTypeErrorOrWarning; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.TypePattern; +import org.aspectj.weaver.tools.PointcutDesignatorHandler; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +/** + * A World is a collection of known types and crosscutting members. + */ +public abstract class World implements Dump.INode { + + /** handler for any messages produced during resolution etc. */ + private IMessageHandler messageHandler = IMessageHandler.SYSTEM_ERR; + + /** handler for cross-reference information produced during the weaving process */ + private ICrossReferenceHandler xrefHandler = null; + + /** Currently 'active' scope in which to lookup (resolve) typevariable references */ + private TypeVariableDeclaringElement typeVariableLookupScope; + + /** The heart of the world, a map from type signatures to resolved types */ + protected TypeMap typeMap = new TypeMap(this); + + /** New pointcut designators this world supports */ + private Set<PointcutDesignatorHandler> pointcutDesignators; + + // see pr145963 + /** Should we create the hierarchy for binary classes and aspects */ + public static boolean createInjarHierarchy = true; + + /** Calculator for working out aspect precedence */ + private final AspectPrecedenceCalculator precedenceCalculator; + + /** All of the type and shadow mungers known to us */ + private final CrosscuttingMembersSet crosscuttingMembersSet = new CrosscuttingMembersSet(this); + + /** The structure model for the compilation */ + private IStructureModel model = null; + + /** for processing Xlint messages */ + private Lint lint = new Lint(this); + + /** XnoInline option setting passed down to weaver */ + private boolean XnoInline; + + /** XlazyTjp option setting passed down to weaver */ + private boolean XlazyTjp; + + /** XhasMember option setting passed down to weaver */ + private boolean XhasMember = false; + + /** Xpinpoint controls whether we put out developer info showing the source of messages */ + private boolean Xpinpoint = false; + + /** When behaving in a Java 5 way autoboxing is considered */ + private boolean behaveInJava5Way = false; + + /** Should timing information be reported (as info messages)? */ + private boolean timing = false; + private boolean timingPeriodically = true; + + /** Determines if this world could be used for multiple compiles */ + private boolean incrementalCompileCouldFollow = false; + + /** The level of the aspectjrt.jar the code we generate needs to run on */ + public static final RuntimeVersion RUNTIME_LEVEL_DEFAULT = RuntimeVersion.V1_5; + private RuntimeVersion targetAspectjRuntimeLevel = RUNTIME_LEVEL_DEFAULT; + + /** Flags for the new joinpoints that are 'optional': -Xjoinpoints:arrayconstruction -Xjoinpoints:synchronization */ + private boolean optionalJoinpoint_ArrayConstruction = false; + private boolean optionalJoinpoint_Synchronization = false; + + private boolean addSerialVerUID = false; + + private Properties extraConfiguration = null; + private boolean checkedAdvancedConfiguration = false; + private boolean synchronizationPointcutsInUse = false; + // Xset'table options + private boolean runMinimalMemory = false; + private boolean transientTjpFields = false; + private boolean runMinimalMemorySet = false; + private boolean shouldPipelineCompilation = true; + private boolean shouldGenerateStackMaps = false; + protected boolean bcelRepositoryCaching = xsetBCEL_REPOSITORY_CACHING_DEFAULT.equalsIgnoreCase("true"); + private boolean fastMethodPacking = false; + private int itdVersion = 2; // defaults to 2nd generation itds + + // Minimal Model controls whether model entities that are not involved in relationships are deleted post-build + private boolean minimalModel = true; + private boolean useFinal = true; + private boolean targettingRuntime1_6_10 = false; + + private boolean completeBinaryTypes = false; + private boolean overWeaving = false; + private static boolean systemPropertyOverWeaving = false; + public boolean forDEBUG_structuralChangesCode = false; + public boolean forDEBUG_bridgingCode = false; + public boolean optimizedMatching = true; + public boolean generateNewLvts = true; + protected long timersPerJoinpoint = 25000; + protected long timersPerType = 250; + + public int infoMessagesEnabled = 0; // 0=uninitialized, 1=no, 2=yes + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(World.class); + + private boolean errorThreshold; + private boolean warningThreshold; + + /** + * A list of RuntimeExceptions containing full stack information for every type we couldn't find. + */ + private List<RuntimeException> dumpState_cantFindTypeExceptions = null; + + static { + try { + String value = System.getProperty("aspectj.overweaving", "false"); + if (value.equalsIgnoreCase("true")) { + System.out.println("ASPECTJ: aspectj.overweaving=true: overweaving switched ON"); + systemPropertyOverWeaving = true; + } + } catch (Throwable t) { + System.err.println("ASPECTJ: Unable to read system properties"); + t.printStackTrace(); + } + } + + public final Primitive BYTE = new Primitive("B", 1, 0); + public final Primitive CHAR = new Primitive("C", 1, 1); + public final Primitive DOUBLE = new Primitive("D", 2, 2); + public final Primitive FLOAT = new Primitive("F", 1, 3); + public final Primitive INT = new Primitive("I", 1, 4); + public final Primitive LONG = new Primitive("J", 2, 5); + public final Primitive SHORT = new Primitive("S", 1, 6); + public final Primitive BOOLEAN = new Primitive("Z", 1, 7); + public final Primitive VOID = new Primitive("V", 0, 8); + + /** + * Insert the primitives + */ + protected World() { + super(); + // Dump.registerNode(this.getClass(), this); + typeMap.put("B", BYTE); + typeMap.put("S", SHORT); + typeMap.put("I", INT); + typeMap.put("J", LONG); + typeMap.put("F", FLOAT); + typeMap.put("D", DOUBLE); + typeMap.put("C", CHAR); + typeMap.put("Z", BOOLEAN); + typeMap.put("V", VOID); + precedenceCalculator = new AspectPrecedenceCalculator(this); + } + + /** + * Dump processing when a fatal error occurs + */ + @Override + public void accept(Dump.IVisitor visitor) { + // visitor.visitObject("Extra configuration:"); + // visitor.visitList(extraConfiguration.); + visitor.visitObject("Shadow mungers:"); + visitor.visitList(crosscuttingMembersSet.getShadowMungers()); + visitor.visitObject("Type mungers:"); + visitor.visitList(crosscuttingMembersSet.getTypeMungers()); + visitor.visitObject("Late Type mungers:"); + visitor.visitList(crosscuttingMembersSet.getLateTypeMungers()); + if (dumpState_cantFindTypeExceptions != null) { + visitor.visitObject("Cant find type problems:"); + visitor.visitList(dumpState_cantFindTypeExceptions); + dumpState_cantFindTypeExceptions = null; + } + } + + // ========================================================================== + // T Y P E R E S O L U T I O N + // ========================================================================== + + /** + * Resolve a type that we require to be present in the world + */ + public ResolvedType resolve(UnresolvedType ty) { + return resolve(ty, false); + } + + /** + * Attempt to resolve a type - the source location gives you some context in which resolution is taking place. In the case of an + * error where we can't find the type - we can then at least report why (source location) we were trying to resolve it. + */ + public ResolvedType resolve(UnresolvedType ty, ISourceLocation isl) { + ResolvedType ret = resolve(ty, true); + if (ResolvedType.isMissing(ty)) { + // IMessage msg = null; + getLint().cantFindType.signal(WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE, ty.getName()), isl); + // if (isl!=null) { + // msg = MessageUtil.error(WeaverMessages.format(WeaverMessages. + // CANT_FIND_TYPE,ty.getName()),isl); + // } else { + // msg = MessageUtil.error(WeaverMessages.format(WeaverMessages. + // CANT_FIND_TYPE,ty.getName())); + // } + // messageHandler.handleMessage(msg); + } + return ret; + } + + /** + * Convenience method for resolving an array of unresolved types in one hit. Useful for e.g. resolving type parameters in + * signatures. + */ + public ResolvedType[] resolve(UnresolvedType[] types) { + if (types == null) { + return ResolvedType.NONE; + } + + ResolvedType[] ret = new ResolvedType[types.length]; + for (int i = 0; i < types.length; i++) { + ret[i] = resolve(types[i]); + } + return ret; + } + + /** + * Resolve a type. This the hub of type resolution. The resolved type is added to the type map by signature. + */ + public ResolvedType resolve(UnresolvedType ty, boolean allowMissing) { + + // special resolution processing for already resolved types. + if (ty instanceof ResolvedType) { + ResolvedType rty = (ResolvedType) ty; + rty = resolve(rty); + // A TypeVariableReferenceType may look like it is resolved (it extends ResolvedType) but the internal + // type variable may not yet have been resolved + if (!rty.isTypeVariableReference() || ((TypeVariableReferenceType) rty).isTypeVariableResolved()) { + return rty; + } + } + + // dispatch back to the type variable reference to resolve its + // constituent parts don't do this for other unresolved types otherwise + // you'll end up in a + // loop + if (ty.isTypeVariableReference()) { + return ty.resolve(this); + } + + // if we've already got a resolved type for the signature, just return + // it + // after updating the world + String signature = ty.getSignature(); + ResolvedType ret = typeMap.get(signature); + if (ret != null) { + ret.world = this; // Set the world for the RTX + return ret; + } else if (signature.equals("?") || signature.equals("*")) { + // might be a problem here, not sure '?' should make it to here as a + // signature, the + // proper signature for wildcard '?' is '*' + // fault in generic wildcard, can't be done earlier because of init + // issues + // TODO ought to be shared single instance representing this + ResolvedType something = getWildcard(); + typeMap.put("?", something); + return something; + } + + // no existing resolved type, create one + synchronized (buildingTypeLock) { + if (ty.isArray()) { + ResolvedType componentType = resolve(ty.getComponentType(), allowMissing); + ret = new ArrayReferenceType(signature, "[" + componentType.getErasureSignature(), this, componentType); + } else { + ret = resolveToReferenceType(ty, allowMissing); + if (!allowMissing && ret.isMissing()) { + ret = handleRequiredMissingTypeDuringResolution(ty); + } + if (completeBinaryTypes) { + completeBinaryType(ret); + } + } + } + + // Pulling in the type may have already put the right entry in the map + ResolvedType result = typeMap.get(signature); + if (result == null && !ret.isMissing()) { + ret = ensureRawTypeIfNecessary(ret); + typeMap.put(signature, ret); + return ret; + } + if (result == null) { + return ret; + } else { + return result; + } + } + + private Object buildingTypeLock = new Object(); + + // Only need one representation of '?' in a world - can be shared + private BoundedReferenceType wildcard; + + private BoundedReferenceType getWildcard() { + if (wildcard == null) { + wildcard = new BoundedReferenceType(this); + } + return wildcard; + } + + /** + * Called when a type is resolved - enables its type hierarchy to be finished off before we proceed + */ + protected void completeBinaryType(ResolvedType ret) { + } + + /** + * Return true if the classloader relating to this world is definetly the one that will define the specified class. Return false + * otherwise or we don't know for certain. + */ + public boolean isLocallyDefined(String classname) { + return false; + } + + /** + * We tried to resolve a type and couldn't find it... + */ + private ResolvedType handleRequiredMissingTypeDuringResolution(UnresolvedType ty) { + // defer the message until someone asks a question of the type that we + // can't answer + // just from the signature. + // MessageUtil.error(messageHandler, + // WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE,ty.getName())); + if (dumpState_cantFindTypeExceptions == null) { + dumpState_cantFindTypeExceptions = new ArrayList<RuntimeException>(); + } + if (dumpState_cantFindTypeExceptions.size() < 100) { // limit growth + dumpState_cantFindTypeExceptions.add(new RuntimeException("Can't find type " + ty.getName())); + } + return new MissingResolvedTypeWithKnownSignature(ty.getSignature(), this); + } + + /** + * Some TypeFactory operations create resolved types directly, but these won't be in the typeMap - this resolution process puts + * them there. Resolved types are also told their world which is needed for the special autoboxing resolved types. + */ + public ResolvedType resolve(ResolvedType ty) { + if (ty.isTypeVariableReference()) { + return ty; // until type variables have proper sigs... + } + ResolvedType resolved = typeMap.get(ty.getSignature()); + if (resolved == null) { + resolved = ensureRawTypeIfNecessary(ty); + typeMap.put(ty.getSignature(), resolved); + resolved = ty; + } + resolved.world = this; + return resolved; + } + + /** + * When the world is operating in 1.5 mode, the TypeMap should only contain RAW types and never directly generic types. The RAW + * type will contain a reference to the generic type. + * + * @param type a possibly generic type for which the raw needs creating as it is not currently in the world + * @return a type suitable for putting into the world + */ + private ResolvedType ensureRawTypeIfNecessary(ResolvedType type) { + if (!isInJava5Mode() || type.isRawType()) { + return type; + } + // Key requirement here is if it is generic, create a RAW entry to be put in the map that points to it + if (type instanceof ReferenceType && ((ReferenceType) type).getDelegate() != null && type.isGenericType()) { + ReferenceType rawType = new ReferenceType(type.getSignature(), this); + rawType.typeKind = UnresolvedType.TypeKind.RAW; + ReferenceTypeDelegate delegate = ((ReferenceType) type).getDelegate(); + rawType.setDelegate(delegate); + rawType.setGenericType((ReferenceType) type); + return rawType; + } + // probably parameterized... + return type; + } + + /** + * Convenience method for finding a type by name and resolving it in one step. + */ + public ResolvedType resolve(String name) { + // trace.enter("resolve", this, new Object[] {name}); + ResolvedType ret = resolve(UnresolvedType.forName(name)); + // trace.exit("resolve", ret); + return ret; + } + + public ReferenceType resolveToReferenceType(String name) { + return (ReferenceType) resolve(name); + } + + public ResolvedType resolve(String name, boolean allowMissing) { + return resolve(UnresolvedType.forName(name), allowMissing); + } + + /** + * Resolve to a ReferenceType - simple, raw, parameterized, or generic. Raw, parameterized, and generic versions of a type share + * a delegate. + */ + private final ResolvedType resolveToReferenceType(UnresolvedType ty, boolean allowMissing) { + if (ty.isParameterizedType()) { + // ======= parameterized types ================ + ResolvedType rt = resolveGenericTypeFor(ty, allowMissing); + if (rt.isMissing()) { + return rt; + } + ReferenceType genericType = (ReferenceType) rt; + ReferenceType parameterizedType = TypeFactory.createParameterizedType(genericType, ty.typeParameters, this); + return parameterizedType; + + } else if (ty.isGenericType()) { + // ======= generic types ====================== + ResolvedType rt = resolveGenericTypeFor(ty, false); + if (rt.isMissing()) { + return rt; + } + ReferenceType genericType = (ReferenceType) rt; + if (rt.isMissing()) { + return rt; + } + return genericType; + + } else if (ty.isGenericWildcard()) { + // ======= generic wildcard types ============= + return resolveGenericWildcardFor((WildcardedUnresolvedType) ty); + } else { + // ======= simple and raw types =============== + String erasedSignature = ty.getErasureSignature(); + ReferenceType simpleOrRawType = new ReferenceType(erasedSignature, this); + if (ty.needsModifiableDelegate()) { + simpleOrRawType.setNeedsModifiableDelegate(true); + } + ReferenceTypeDelegate delegate = resolveDelegate(simpleOrRawType); + + if (delegate == null) { + return new MissingResolvedTypeWithKnownSignature(ty.getSignature(), erasedSignature, this); + } + + if (delegate.isGeneric() && behaveInJava5Way) { + // ======== raw type =========== + simpleOrRawType.typeKind = TypeKind.RAW; + if (simpleOrRawType.hasNewInterfaces()) { // debug 375777 + throw new IllegalStateException( + "Simple type promoted forced to raw, but it had new interfaces/superclass. Type is " + + simpleOrRawType.getName()); + } + ReferenceType genericType = makeGenericTypeFrom(delegate, simpleOrRawType); + simpleOrRawType.setDelegate(delegate); + genericType.setDelegate(delegate); + simpleOrRawType.setGenericType(genericType); + return simpleOrRawType; + } else { + // ======== simple type ========= + simpleOrRawType.setDelegate(delegate); + return simpleOrRawType; + } + } + } + + /** + * Attempt to resolve a type that should be a generic type. + */ + public ResolvedType resolveGenericTypeFor(UnresolvedType anUnresolvedType, boolean allowMissing) { + // Look up the raw type by signature + String rawSignature = anUnresolvedType.getRawType().getSignature(); + ResolvedType rawType = typeMap.get(rawSignature); + if (rawType == null) { + rawType = resolve(UnresolvedType.forSignature(rawSignature), allowMissing); + typeMap.put(rawSignature, rawType); + } + if (rawType.isMissing()) { + return rawType; + } + + // Does the raw type know its generic form? (It will if we created the + // raw type from a source type, it won't if its been created just + // through + // being referenced, e.g. java.util.List + ResolvedType genericType = rawType.getGenericType(); + + // There is a special case to consider here (testGenericsBang_pr95993 + // highlights it) + // You may have an unresolvedType for a parameterized type but it + // is backed by a simple type rather than a generic type. This occurs + // for + // inner types of generic types that inherit their enclosing types + // type variables. + if (rawType.isSimpleType() && (anUnresolvedType.typeParameters == null || anUnresolvedType.typeParameters.length == 0)) { + rawType.world = this; + return rawType; + } + + if (genericType != null) { + genericType.world = this; + return genericType; + } else { + // Fault in the generic that underpins the raw type ;) + ReferenceTypeDelegate delegate = resolveDelegate((ReferenceType) rawType); + ReferenceType genericRefType = makeGenericTypeFrom(delegate, ((ReferenceType) rawType)); + ((ReferenceType) rawType).setGenericType(genericRefType); + genericRefType.setDelegate(delegate); + ((ReferenceType) rawType).setDelegate(delegate); + return genericRefType; + } + } + + private ReferenceType makeGenericTypeFrom(ReferenceTypeDelegate delegate, ReferenceType rawType) { + String genericSig = delegate.getDeclaredGenericSignature(); + if (genericSig != null) { + return new ReferenceType(UnresolvedType.forGenericTypeSignature(rawType.getSignature(), + delegate.getDeclaredGenericSignature()), this); + } else { + return new ReferenceType(UnresolvedType.forGenericTypeVariables(rawType.getSignature(), delegate.getTypeVariables()), + this); + } + } + + /** + * Go from an unresolved generic wildcard (represented by UnresolvedType) to a resolved version (BoundedReferenceType). + */ + private ReferenceType resolveGenericWildcardFor(WildcardedUnresolvedType aType) { + BoundedReferenceType ret = null; + // FIXME asc doesnt take account of additional interface bounds (e.g. ? super R & Serializable - can you do that?) + if (aType.isExtends()) { + ResolvedType resolvedUpperBound = resolve(aType.getUpperBound()); + if (resolvedUpperBound.isMissing()) { + return getWildcard(); + } + ret = new BoundedReferenceType((ReferenceType)resolvedUpperBound, true, this); + } else if (aType.isSuper()) { + ResolvedType resolvedLowerBound = resolve(aType.getLowerBound()); + if (resolvedLowerBound.isMissing()) { + return getWildcard(); + } + ret = new BoundedReferenceType((ReferenceType)resolvedLowerBound, false, this); + } else { + // must be ? on its own! + ret = getWildcard(); + } + return ret; + } + + /** + * Find the ReferenceTypeDelegate behind this reference type so that it can fulfill its contract. + */ + protected abstract ReferenceTypeDelegate resolveDelegate(ReferenceType ty); + + /** + * Special resolution for "core" types like OBJECT. These are resolved just like any other type, but if they are not found it is + * more serious and we issue an error message immediately. + */ + // OPTIMIZE streamline path for core types? They are just simple types, + // could look straight in the typemap? + public ResolvedType getCoreType(UnresolvedType tx) { + ResolvedType coreTy = resolve(tx, true); + if (coreTy.isMissing()) { + MessageUtil.error(messageHandler, WeaverMessages.format(WeaverMessages.CANT_FIND_CORE_TYPE, tx.getName())); + } + return coreTy; + } + + /** + * Lookup a type by signature, if not found then build one and put it in the map. + */ + public ReferenceType lookupOrCreateName(UnresolvedType ty) { + String signature = ty.getSignature(); + ReferenceType ret = lookupBySignature(signature); + if (ret == null) { + ret = ReferenceType.fromTypeX(ty, this); + typeMap.put(signature, ret); + } + return ret; + } + + /** + * Lookup a reference type in the world by its signature. Returns null if not found. + */ + public ReferenceType lookupBySignature(String signature) { + return (ReferenceType) typeMap.get(signature); + } + + // ========================================================================== + // === + // T Y P E R E S O L U T I O N -- E N D + // ========================================================================== + // === + + /** + * Member resolution is achieved by resolving the declaring type and then looking up the member in the resolved declaring type. + */ + public ResolvedMember resolve(Member member) { + ResolvedType declaring = member.getDeclaringType().resolve(this); + if (declaring.isRawType()) { + declaring = declaring.getGenericType(); + } + ResolvedMember ret; + if (member.getKind() == Member.FIELD) { + ret = declaring.lookupField(member); + } else { + ret = declaring.lookupMethod(member); + } + + if (ret != null) { + return ret; + } + + return declaring.lookupSyntheticMember(member); + } + + private boolean allLintIgnored = false; + + public void setAllLintIgnored() { + allLintIgnored = true; + } + + public boolean areAllLintIgnored() { + return allLintIgnored; + } + + public abstract IWeavingSupport getWeavingSupport(); + + /** + * Create an advice shadow munger from the given advice attribute + */ + // public abstract Advice createAdviceMunger(AjAttribute.AdviceAttribute + // attribute, Pointcut pointcut, Member signature); + /** + * Create an advice shadow munger for the given advice kind + */ + public final Advice createAdviceMunger(AdviceKind kind, Pointcut p, Member signature, int extraParameterFlags, + IHasSourceLocation loc, ResolvedType declaringAspect) { + AjAttribute.AdviceAttribute attribute = new AjAttribute.AdviceAttribute(kind, p, extraParameterFlags, loc.getStart(), + loc.getEnd(), loc.getSourceContext()); + return getWeavingSupport().createAdviceMunger(attribute, p, signature, declaringAspect); + } + + /** + * Same signature as org.aspectj.util.PartialOrder.PartialComparable.compareTo + */ + public int compareByPrecedence(ResolvedType aspect1, ResolvedType aspect2) { + return precedenceCalculator.compareByPrecedence(aspect1, aspect2); + } + + public Integer getPrecedenceIfAny(ResolvedType aspect1, ResolvedType aspect2) { + return precedenceCalculator.getPrecedenceIfAny(aspect1, aspect2); + } + + /** + * compares by precedence with the additional rule that a super-aspect is sorted before its sub-aspects + */ + public int compareByPrecedenceAndHierarchy(ResolvedType aspect1, ResolvedType aspect2) { + return precedenceCalculator.compareByPrecedenceAndHierarchy(aspect1, aspect2); + } + + // simple property getter and setters + // =========================================================== + + /** + * Nobody should hold onto a copy of this message handler, or setMessageHandler won't work right. + */ + public IMessageHandler getMessageHandler() { + return messageHandler; + } + + public void setMessageHandler(IMessageHandler messageHandler) { + if (this.isInPinpointMode()) { + this.messageHandler = new PinpointingMessageHandler(messageHandler); + } else { + this.messageHandler = messageHandler; + } + } + + /** + * convenenience method for creating and issuing messages via the message handler - if you supply two locations you will get two + * messages. + */ + public void showMessage(Kind kind, String message, ISourceLocation loc1, ISourceLocation loc2) { + if (loc1 != null) { + messageHandler.handleMessage(new Message(message, kind, null, loc1)); + if (loc2 != null) { + messageHandler.handleMessage(new Message(message, kind, null, loc2)); + } + } else { + messageHandler.handleMessage(new Message(message, kind, null, loc2)); + } + } + + public void setCrossReferenceHandler(ICrossReferenceHandler xrefHandler) { + this.xrefHandler = xrefHandler; + } + + /** + * Get the cross-reference handler for the world, may be null. + */ + public ICrossReferenceHandler getCrossReferenceHandler() { + return xrefHandler; + } + + public void setTypeVariableLookupScope(TypeVariableDeclaringElement scope) { + typeVariableLookupScope = scope; + } + + public TypeVariableDeclaringElement getTypeVariableLookupScope() { + return typeVariableLookupScope; + } + + public List<DeclareParents> getDeclareParents() { + return crosscuttingMembersSet.getDeclareParents(); + } + + public List<DeclareAnnotation> getDeclareAnnotationOnTypes() { + return crosscuttingMembersSet.getDeclareAnnotationOnTypes(); + } + + public List<DeclareAnnotation> getDeclareAnnotationOnFields() { + return crosscuttingMembersSet.getDeclareAnnotationOnFields(); + } + + public List<DeclareAnnotation> getDeclareAnnotationOnMethods() { + return crosscuttingMembersSet.getDeclareAnnotationOnMethods(); + } + + public List<DeclareTypeErrorOrWarning> getDeclareTypeEows() { + return crosscuttingMembersSet.getDeclareTypeEows(); + } + + public List<DeclareSoft> getDeclareSoft() { + return crosscuttingMembersSet.getDeclareSofts(); + } + + public CrosscuttingMembersSet getCrosscuttingMembersSet() { + return crosscuttingMembersSet; + } + + public IStructureModel getModel() { + return model; + } + + public void setModel(IStructureModel model) { + this.model = model; + } + + public Lint getLint() { + return lint; + } + + public void setLint(Lint lint) { + this.lint = lint; + } + + public boolean isXnoInline() { + return XnoInline; + } + + public void setXnoInline(boolean xnoInline) { + XnoInline = xnoInline; + } + + public boolean isXlazyTjp() { + return XlazyTjp; + } + + public void setXlazyTjp(boolean b) { + XlazyTjp = b; + } + + public boolean isHasMemberSupportEnabled() { + return XhasMember; + } + + public void setXHasMemberSupportEnabled(boolean b) { + XhasMember = b; + } + + public boolean isInPinpointMode() { + return Xpinpoint; + } + + public void setPinpointMode(boolean b) { + Xpinpoint = b; + } + + public boolean useFinal() { + return useFinal; + } + + public boolean isMinimalModel() { + ensureAdvancedConfigurationProcessed(); + return minimalModel; + } + + public boolean isTargettingRuntime1_6_10() { + ensureAdvancedConfigurationProcessed(); + return targettingRuntime1_6_10; + } + + public void setBehaveInJava5Way(boolean b) { + behaveInJava5Way = b; + } + + /** + * Set the timing option (whether to collect timing info), this will also need INFO messages turned on for the message handler + * being used. The reportPeriodically flag should be set to false under AJDT so numbers just come out at the end. + */ + public void setTiming(boolean timersOn, boolean reportPeriodically) { + timing = timersOn; + timingPeriodically = reportPeriodically; + } + + /** + * Set the error and warning threashold which can be taken from CompilerOptions (see bug 129282) + * + * @param errorThreshold + * @param warningThreshold + */ + public void setErrorAndWarningThreshold(boolean errorThreshold, boolean warningThreshold) { + this.errorThreshold = errorThreshold; + this.warningThreshold = warningThreshold; + } + + /** + * @return true if ignoring the UnusedDeclaredThrownException and false if this compiler option is set to error or warning + */ + public boolean isIgnoringUnusedDeclaredThrownException() { + // the 0x800000 is CompilerOptions.UnusedDeclaredThrownException + // which is ASTNode.bit24 + return errorThreshold||warningThreshold; +// if ((errorThreshold & 0x800000) != 0 || (warningThreshold & 0x800000) != 0) { +// return false; +// } +// return true; + } + + public void performExtraConfiguration(String config) { + if (config == null) { + return; + } + // Bunch of name value pairs to split + extraConfiguration = new Properties(); + int pos = -1; + while ((pos = config.indexOf(",")) != -1) { + String nvpair = config.substring(0, pos); + int pos2 = nvpair.indexOf("="); + if (pos2 != -1) { + String n = nvpair.substring(0, pos2); + String v = nvpair.substring(pos2 + 1); + extraConfiguration.setProperty(n, v); + } + config = config.substring(pos + 1); + } + if (config.length() > 0) { + int pos2 = config.indexOf("="); + if (pos2 != -1) { + String n = config.substring(0, pos2); + String v = config.substring(pos2 + 1); + extraConfiguration.setProperty(n, v); + } + } + ensureAdvancedConfigurationProcessed(); + } + + public boolean areInfoMessagesEnabled() { + if (infoMessagesEnabled == 0) { + infoMessagesEnabled = (messageHandler.isIgnoring(IMessage.INFO) ? 1 : 2); + } + return infoMessagesEnabled == 2; + } + + /** + * may return null + */ + public Properties getExtraConfiguration() { + return extraConfiguration; + } + + public final static String xsetAVOID_FINAL = "avoidFinal"; // default true + + public final static String xsetWEAVE_JAVA_PACKAGES = "weaveJavaPackages"; // default + // false + // - + // controls + // LTW + public final static String xsetWEAVE_JAVAX_PACKAGES = "weaveJavaxPackages"; // default + // false + // - + // controls + // LTW + public final static String xsetCAPTURE_ALL_CONTEXT = "captureAllContext"; // default + // false + public final static String xsetRUN_MINIMAL_MEMORY = "runMinimalMemory"; // default + // true + public final static String xsetDEBUG_STRUCTURAL_CHANGES_CODE = "debugStructuralChangesCode"; // default + // false + public final static String xsetDEBUG_BRIDGING = "debugBridging"; // default + // false + public final static String xsetTRANSIENT_TJP_FIELDS = "makeTjpFieldsTransient"; // default false + public final static String xsetBCEL_REPOSITORY_CACHING = "bcelRepositoryCaching"; + public final static String xsetPIPELINE_COMPILATION = "pipelineCompilation"; + public final static String xsetGENERATE_STACKMAPS = "generateStackMaps"; + public final static String xsetPIPELINE_COMPILATION_DEFAULT = "true"; + public final static String xsetCOMPLETE_BINARY_TYPES = "completeBinaryTypes"; + public final static String xsetCOMPLETE_BINARY_TYPES_DEFAULT = "false"; + public final static String xsetTYPE_DEMOTION = "typeDemotion"; + public final static String xsetTYPE_DEMOTION_DEBUG = "typeDemotionDebug"; + public final static String xsetTYPE_REFS = "useWeakTypeRefs"; + public final static String xsetBCEL_REPOSITORY_CACHING_DEFAULT = "true"; + public final static String xsetFAST_PACK_METHODS = "fastPackMethods"; // default true + public final static String xsetOVERWEAVING = "overWeaving"; + public final static String xsetOPTIMIZED_MATCHING = "optimizedMatching"; + public final static String xsetTIMERS_PER_JOINPOINT = "timersPerJoinpoint"; + public final static String xsetTIMERS_PER_FASTMATCH_CALL = "timersPerFastMatchCall"; + public final static String xsetITD_VERSION = "itdVersion"; + public final static String xsetITD_VERSION_ORIGINAL = "1"; + public final static String xsetITD_VERSION_2NDGEN = "2"; + public final static String xsetITD_VERSION_DEFAULT = xsetITD_VERSION_2NDGEN; + public final static String xsetMINIMAL_MODEL = "minimalModel"; + public final static String xsetTARGETING_RUNTIME_1610 = "targetRuntime1_6_10"; + + // This option allows you to prevent AspectJ adding local variable tables - some tools (e.g. dex) may + // not like what gets created because even though it is valid, the bytecode they are processing has + // unexpected quirks that mean the table entries are violated in the code. See issue: + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=470658 + public final static String xsetGENERATE_NEW_LVTS="generateNewLocalVariableTables"; + + public boolean isInJava5Mode() { + return behaveInJava5Way; + } + + public boolean isTimingEnabled() { + return timing; + } + + public void setTargetAspectjRuntimeLevel(String s) { + targetAspectjRuntimeLevel = RuntimeVersion.getVersionFor(s); + } + + public void setOptionalJoinpoints(String jps) { + if (jps == null) { + return; + } + if (jps.indexOf("arrayconstruction") != -1) { + optionalJoinpoint_ArrayConstruction = true; + } + if (jps.indexOf("synchronization") != -1) { + optionalJoinpoint_Synchronization = true; + } + } + + public boolean isJoinpointArrayConstructionEnabled() { + return optionalJoinpoint_ArrayConstruction; + } + + public boolean isJoinpointSynchronizationEnabled() { + return optionalJoinpoint_Synchronization; + } + + public RuntimeVersion getTargetAspectjRuntimeLevel() { + return targetAspectjRuntimeLevel; + } + + // OPTIMIZE are users falling foul of not supplying -1.5 and so targetting the old runtime? + public boolean isTargettingAspectJRuntime12() { + boolean b = false; // pr116679 + if (!isInJava5Mode()) { + b = true; + } else { + b = (getTargetAspectjRuntimeLevel() == RuntimeVersion.V1_2); + } + return b; + } + + /* + * Map of types in the world, can have 'references' to expendable ones which can be garbage collected to recover memory. An + * expendable type is a reference type that is not exposed to the weaver (ie just pulled in for type resolution purposes). + * Generic types have their raw form added to the map, which has a pointer to the underlying generic. + */ + public static class TypeMap { + + // Strategy for entries in the expendable map + public final static int DONT_USE_REFS = 0; // Hang around forever + public final static int USE_WEAK_REFS = 1; // Collected asap + public final static int USE_SOFT_REFS = 2; // Collected when short on memory + + public List<String> addedSinceLastDemote; + public List<String> writtenClasses; + + private static boolean debug = false; + public static boolean useExpendableMap = true; // configurable for reliable testing + private boolean demotionSystemActive; + private boolean debugDemotion = false; + + public int policy = USE_WEAK_REFS; + + // Map of types that never get thrown away + final Map<String, ResolvedType> tMap = new HashMap<String, ResolvedType>(); + + // Map of types that may be ejected from the cache if we need space + final Map<String, Reference<ResolvedType>> expendableMap = Collections + .synchronizedMap(new WeakHashMap<String, Reference<ResolvedType>>()); + + private final World w; + + // profiling tools... + private boolean memoryProfiling = false; + private int maxExpendableMapSize = -1; + private int collectedTypes = 0; + private final ReferenceQueue<ResolvedType> rq = new ReferenceQueue<ResolvedType>(); + + TypeMap(World w) { + // Demotion activated when switched on and loadtime weaving or in AJDT + demotionSystemActive = w.isDemotionActive() && (w.isLoadtimeWeaving() || w.couldIncrementalCompileFollow()); + addedSinceLastDemote = new ArrayList<String>(); + writtenClasses = new ArrayList<String>(); + this.w = w; + memoryProfiling = false;// !w.getMessageHandler().isIgnoring(Message. + // INFO); + } + + // For testing + public Map<String, Reference<ResolvedType>> getExpendableMap() { + return expendableMap; + } + + // For testing + public Map<String, ResolvedType> getMainMap() { + return tMap; + } + + public int demote() { + return demote(false); + } + + /** + * Go through any types added during the previous file weave. If any are suitable for demotion, then put them in the + * expendable map where GC can claim them at some point later. Demotion means: the type is not an aspect, the type is not + * java.lang.Object, the type is not primitive and the type is not affected by type mungers in any way. Further refinements + * of these conditions may allow for more demotions. + * + * @return number of types demoted + */ + public int demote(boolean atEndOfCompile) { + if (!demotionSystemActive) { + return 0; + } + if (debugDemotion) { + System.out.println("Demotion running " + addedSinceLastDemote); + } + boolean isLtw = w.isLoadtimeWeaving(); + int demotionCounter = 0; + if (isLtw) { + // Loadtime weaving demotion strategy + for (String key : addedSinceLastDemote) { + ResolvedType type = tMap.get(key); + if (type != null && !type.isAspect() && !type.equals(UnresolvedType.OBJECT) && !type.isPrimitiveType()) { + List<ConcreteTypeMunger> typeMungers = type.getInterTypeMungers(); + if (typeMungers == null || typeMungers.size() == 0) { + tMap.remove(key); + insertInExpendableMap(key, type); + demotionCounter++; + } + } + } + addedSinceLastDemote.clear(); + } else { + // Compile time demotion strategy + List<String> forRemoval = new ArrayList<String>(); + for (String key : addedSinceLastDemote) { + ResolvedType type = tMap.get(key); + if (type == null) { + // TODO not 100% sure why it is not there, where did it go? + forRemoval.add(key); + continue; + } + if (!writtenClasses.contains(type.getName())) { // COSTLY + continue; + } + if (type != null && !type.isAspect() && !type.equals(UnresolvedType.OBJECT) && !type.isPrimitiveType()) { + List<ConcreteTypeMunger> typeMungers = type.getInterTypeMungers(); + if (typeMungers == null || typeMungers.size() == 0) { + /* + * if (type.isNested()) { try { ReferenceType rt = (ReferenceType) w.resolve(type.getOutermostType()); + * if (!rt.isMissing()) { ReferenceTypeDelegate delegate = ((ReferenceType) type).getDelegate(); boolean + * isWeavable = delegate == null ? false : delegate.isExposedToWeaver(); boolean hasBeenWoven = delegate + * == null ? false : delegate.hasBeenWoven(); if (isWeavable && !hasBeenWoven) { // skip demotion of + * this inner type for now continue; } } } catch (ClassCastException cce) { cce.printStackTrace(); + * System.out.println("outer of " + key + " is not a reftype? " + type.getOutermostType()); // throw new + * IllegalStateException(cce); } } + */ + ReferenceTypeDelegate delegate = ((ReferenceType) type).getDelegate(); + boolean isWeavable = delegate == null ? false : delegate.isExposedToWeaver(); + boolean hasBeenWoven = delegate == null ? false : delegate.hasBeenWoven(); + if (!isWeavable || hasBeenWoven) { + if (debugDemotion) { + System.out.println("Demoting " + key); + } + forRemoval.add(key); + tMap.remove(key); + insertInExpendableMap(key, type); + demotionCounter++; + } + } else { + // no need to try this again, it will never be demoted + writtenClasses.remove(type.getName()); + forRemoval.add(key); + } + } else { + writtenClasses.remove(type.getName()); + // no need to try this again, it will never be demoted + forRemoval.add(key); + } + } + addedSinceLastDemote.removeAll(forRemoval); + } + if (debugDemotion) { + System.out.println("Demoted " + demotionCounter + " types. Types remaining in fixed set #" + tMap.keySet().size() + + ". addedSinceLastDemote size is " + addedSinceLastDemote.size()); + System.out.println("writtenClasses.size() = " + writtenClasses.size() + ": " + writtenClasses); + } + if (atEndOfCompile) { + if (debugDemotion) { + System.out.println("Clearing writtenClasses"); + } + writtenClasses.clear(); + } + return demotionCounter; + } + + private void insertInExpendableMap(String key, ResolvedType type) { + if (useExpendableMap) { + if (!expendableMap.containsKey(key)) { + if (policy == USE_SOFT_REFS) { + expendableMap.put(key, new SoftReference<ResolvedType>(type)); + } else { + expendableMap.put(key, new WeakReference<ResolvedType>(type)); + } + } + } + } + + /** + * Add a new type into the map, the key is the type signature. Some types do *not* go in the map, these are ones involving + * *member* type variables. The reason is that when all you have is the signature which gives you a type variable name, you + * cannot guarantee you are using the type variable in the same way as someone previously working with a similarly named + * type variable. So, these do not go into the map: - TypeVariableReferenceType. - ParameterizedType where a member type + * variable is involved. - BoundedReferenceType when one of the bounds is a type variable. + * + * definition: "member type variables" - a tvar declared on a generic method/ctor as opposed to those you see declared on a + * generic type. + */ + public ResolvedType put(String key, ResolvedType type) { + if (!type.isCacheable()) { + return type; + } + if (type.isParameterizedType() && type.isParameterizedWithTypeVariable()) { + if (debug) { + System.err + .println("Not putting a parameterized type that utilises member declared type variables into the typemap: key=" + + key + " type=" + type); + } + return type; + } + if (type.isTypeVariableReference()) { + if (debug) { + System.err.println("Not putting a type variable reference type into the typemap: key=" + key + " type=" + type); + } + return type; + } + // this test should be improved - only avoid putting them in if one + // of the + // bounds is a member type variable + if (type instanceof BoundedReferenceType) { + if (debug) { + System.err.println("Not putting a bounded reference type into the typemap: key=" + key + " type=" + type); + } + return type; + } + if (type instanceof MissingResolvedTypeWithKnownSignature) { + if (debug) { + System.err.println("Not putting a missing type into the typemap: key=" + key + " type=" + type); + } + return type; + } + + if ((type instanceof ReferenceType) && (((ReferenceType) type).getDelegate() == null) && w.isExpendable(type)) { + if (debug) { + System.err.println("Not putting expendable ref type with null delegate into typemap: key=" + key + " type=" + + type); + } + return type; + } + + // TODO should this be in as a permanent assertion? + + if ((type instanceof ReferenceType) && type.getWorld().isInJava5Mode() + && (((ReferenceType) type).getDelegate() != null) && type.isGenericType()) { + throw new BCException("Attempt to add generic type to typemap " + type.toString() + " (should be raw)"); + } + + + if (w.isExpendable(type)) { + if (useExpendableMap) { + // Dont use reference queue for tracking if not profiling... + if (policy == USE_WEAK_REFS) { + if (memoryProfiling) { + expendableMap.put(key, new WeakReference<ResolvedType>(type, rq)); + } else { + expendableMap.put(key, new WeakReference<ResolvedType>(type)); + } + } else if (policy == USE_SOFT_REFS) { + if (memoryProfiling) { + expendableMap.put(key, new SoftReference<ResolvedType>(type, rq)); + } else { + expendableMap.put(key, new SoftReference<ResolvedType>(type)); + } + // } else { + // expendableMap.put(key, type); + } + } + if (memoryProfiling && expendableMap.size() > maxExpendableMapSize) { + maxExpendableMapSize = expendableMap.size(); + } + return type; + } else { + if (demotionSystemActive) { + // System.out.println("Added since last demote " + key); + addedSinceLastDemote.add(key); + } + + return tMap.put(key, type); + } + } + + public void report() { + if (!memoryProfiling) { + return; + } + checkq(); + w.getMessageHandler().handleMessage( + MessageUtil.info("MEMORY: world expendable type map reached maximum size of #" + maxExpendableMapSize + + " entries")); + w.getMessageHandler().handleMessage( + MessageUtil.info("MEMORY: types collected through garbage collection #" + collectedTypes + " entries")); + } + + public void checkq() { + if (!memoryProfiling) { + return; + } + Reference<? extends ResolvedType> r = null; + while ((r=rq.poll()) != null) { + collectedTypes++; + } + } + + /** + * Lookup a type by its signature, always look in the real map before the expendable map + */ + public ResolvedType get(String key) { + checkq(); + ResolvedType ret = tMap.get(key); + if (ret == null) { + if (policy == USE_WEAK_REFS) { + WeakReference<ResolvedType> ref = (WeakReference<ResolvedType>) expendableMap.get(key); + if (ref != null) { + ret = ref.get(); +// if (ret==null) { +// expendableMap.remove(key); +// } + } + } else if (policy == USE_SOFT_REFS) { + SoftReference<ResolvedType> ref = (SoftReference<ResolvedType>) expendableMap.get(key); + if (ref != null) { + ret = ref.get(); +// if (ret==null) { +// expendableMap.remove(key); +// } + } + // } else { + // return (ResolvedType) expendableMap.get(key); + } + } + return ret; + } + + /** Remove a type from the map */ + public ResolvedType remove(String key) { + ResolvedType ret = tMap.remove(key); + if (ret == null) { + if (policy == USE_WEAK_REFS) { + WeakReference<ResolvedType> wref = (WeakReference<ResolvedType>) expendableMap.remove(key); + if (wref != null) { + ret = wref.get(); + } + } else if (policy == USE_SOFT_REFS) { + SoftReference<ResolvedType> wref = (SoftReference<ResolvedType>) expendableMap.remove(key); + if (wref != null) { + ret = wref.get(); + } + // } else { + // ret = (ResolvedType) expendableMap.remove(key); + } + } + return ret; + } + + public void classWriteEvent(String classname) { + // that is a name com.Foo and not a signature Lcom/Foo; boooooooooo! + if (demotionSystemActive) { + writtenClasses.add(classname); + } + if (debugDemotion) { + System.out.println("Class write event for " + classname); + } + } + + public void demote(ResolvedType type) { + String key = type.getSignature(); + if (debugDemotion) { + addedSinceLastDemote.remove(key); + } + tMap.remove(key); + insertInExpendableMap(key, type); + } + + // public ResolvedType[] getAllTypes() { + // List/* ResolvedType */results = new ArrayList(); + // + // collectTypes(expendableMap, results); + // collectTypes(tMap, results); + // return (ResolvedType[]) results.toArray(new + // ResolvedType[results.size()]); + // } + // + // private void collectTypes(Map map, List/* ResolvedType */results) { + // for (Iterator iterator = map.keySet().iterator(); + // iterator.hasNext();) { + // String key = (String) iterator.next(); + // ResolvedType type = get(key); + // if (type != null) + // results.add(type); + // else + // System.err.println("null!:" + key); + // } + // } + + } + + /** + * This class is used to compute and store precedence relationships between aspects. + */ + private static class AspectPrecedenceCalculator { + + private final World world; + private final Map<PrecedenceCacheKey, Integer> cachedResults; + + public AspectPrecedenceCalculator(World forSomeWorld) { + world = forSomeWorld; + cachedResults = new HashMap<PrecedenceCacheKey, Integer>(); + } + + /** + * Ask every declare precedence in the world to order the two aspects. If more than one declare precedence gives an + * ordering, and the orderings conflict, then that's an error. + */ + public int compareByPrecedence(ResolvedType firstAspect, ResolvedType secondAspect) { + PrecedenceCacheKey key = new PrecedenceCacheKey(firstAspect, secondAspect); + if (cachedResults.containsKey(key)) { + return (cachedResults.get(key)).intValue(); + } else { + int order = 0; + DeclarePrecedence orderer = null; // Records the declare + // precedence statement that + // gives the first ordering + for (Iterator<Declare> i = world.getCrosscuttingMembersSet().getDeclareDominates().iterator(); i.hasNext();) { + DeclarePrecedence d = (DeclarePrecedence) i.next(); + int thisOrder = d.compare(firstAspect, secondAspect); + if (thisOrder != 0) { + if (orderer == null) { + orderer = d; + } + if (order != 0 && order != thisOrder) { + ISourceLocation[] isls = new ISourceLocation[2]; + isls[0] = orderer.getSourceLocation(); + isls[1] = d.getSourceLocation(); + Message m = new Message("conflicting declare precedence orderings for aspects: " + + firstAspect.getName() + " and " + secondAspect.getName(), null, true, isls); + world.getMessageHandler().handleMessage(m); + } else { + order = thisOrder; + } + } + } + cachedResults.put(key, new Integer(order)); + return order; + } + } + + public Integer getPrecedenceIfAny(ResolvedType aspect1, ResolvedType aspect2) { + return cachedResults.get(new PrecedenceCacheKey(aspect1, aspect2)); + } + + public int compareByPrecedenceAndHierarchy(ResolvedType firstAspect, ResolvedType secondAspect) { + if (firstAspect.equals(secondAspect)) { + return 0; + } + + int ret = compareByPrecedence(firstAspect, secondAspect); + if (ret != 0) { + return ret; + } + + if (firstAspect.isAssignableFrom(secondAspect)) { + return -1; + } else if (secondAspect.isAssignableFrom(firstAspect)) { + return +1; + } + + return 0; + } + + private static class PrecedenceCacheKey { + public ResolvedType aspect1; + public ResolvedType aspect2; + + public PrecedenceCacheKey(ResolvedType a1, ResolvedType a2) { + aspect1 = a1; + aspect2 = a2; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PrecedenceCacheKey)) { + return false; + } + PrecedenceCacheKey other = (PrecedenceCacheKey) obj; + return (aspect1 == other.aspect1 && aspect2 == other.aspect2); + } + + @Override + public int hashCode() { + return aspect1.hashCode() + aspect2.hashCode(); + } + } + } + + public void validateType(UnresolvedType type) { + } + + // --- with java5 we can get into a recursive mess if we aren't careful when + // resolving types (*cough* java.lang.Enum) --- + + public boolean isDemotionActive() { + return true; + } + + // --- this first map is for java15 delegates which may try and recursively + // access the same type variables. + // --- I would rather stash this against a reference type - but we don't + // guarantee referencetypes are unique for + // so we can't :( + private final Map<Class<?>, TypeVariable[]> workInProgress1 = new HashMap<Class<?>, TypeVariable[]>(); + + public TypeVariable[] getTypeVariablesCurrentlyBeingProcessed(Class<?> baseClass) { + return workInProgress1.get(baseClass); + } + + public void recordTypeVariablesCurrentlyBeingProcessed(Class<?> baseClass, TypeVariable[] typeVariables) { + workInProgress1.put(baseClass, typeVariables); + } + + public void forgetTypeVariablesCurrentlyBeingProcessed(Class<?> baseClass) { + workInProgress1.remove(baseClass); + } + + public void setAddSerialVerUID(boolean b) { + addSerialVerUID = b; + } + + public boolean isAddSerialVerUID() { + return addSerialVerUID; + } + + /** be careful calling this - pr152257 */ + public void flush() { + typeMap.expendableMap.clear(); + } + + public void ensureAdvancedConfigurationProcessed() { + + // Check *once* whether the user has switched asm support off + if (!checkedAdvancedConfiguration) { + Properties p = getExtraConfiguration(); + if (p != null) { + + String s = p.getProperty(xsetBCEL_REPOSITORY_CACHING, xsetBCEL_REPOSITORY_CACHING_DEFAULT); + bcelRepositoryCaching = s.equalsIgnoreCase("true"); + if (!bcelRepositoryCaching) { + getMessageHandler().handleMessage( + MessageUtil + .info("[bcelRepositoryCaching=false] AspectJ will not use a bcel cache for class information")); + } + + // ITD Versions + // 1 is the first version in use up to AspectJ 1.6.8 + // 2 is from 1.6.9 onwards + s = p.getProperty(xsetITD_VERSION, xsetITD_VERSION_DEFAULT); + if (s.equals(xsetITD_VERSION_ORIGINAL)) { + itdVersion = 1; + } + + s = p.getProperty(xsetAVOID_FINAL, "false"); + if (s.equalsIgnoreCase("true")) { + useFinal = false; // if avoidFinal=true, then set useFinal to false + } + + s = p.getProperty(xsetMINIMAL_MODEL, "true"); + if (s.equalsIgnoreCase("false")) { + minimalModel = false; + } + + s = p.getProperty(xsetTARGETING_RUNTIME_1610, "false"); + if (s.equalsIgnoreCase("true")) { + targettingRuntime1_6_10 = true; + } + + s = p.getProperty(xsetFAST_PACK_METHODS, "true"); + fastMethodPacking = s.equalsIgnoreCase("true"); + + s = p.getProperty(xsetPIPELINE_COMPILATION, xsetPIPELINE_COMPILATION_DEFAULT); + shouldPipelineCompilation = s.equalsIgnoreCase("true"); + + s = p.getProperty(xsetGENERATE_STACKMAPS, "false"); + shouldGenerateStackMaps = s.equalsIgnoreCase("true"); + + s = p.getProperty(xsetCOMPLETE_BINARY_TYPES, xsetCOMPLETE_BINARY_TYPES_DEFAULT); + completeBinaryTypes = s.equalsIgnoreCase("true"); + if (completeBinaryTypes) { + getMessageHandler().handleMessage( + MessageUtil.info("[completeBinaryTypes=true] Completion of binary types activated")); + } + + s = p.getProperty(xsetTYPE_DEMOTION); // default is: ON + if (s != null) { + boolean b = typeMap.demotionSystemActive; + if (b && s.equalsIgnoreCase("false")) { + System.out.println("typeDemotion=false: type demotion switched OFF"); + typeMap.demotionSystemActive = false; + } else if (!b && s.equalsIgnoreCase("true")) { + System.out.println("typeDemotion=true: type demotion switched ON"); + typeMap.demotionSystemActive = true; + } + } + + s = p.getProperty(xsetOVERWEAVING, "false"); + if (s.equalsIgnoreCase("true")) { + overWeaving = true; + } + + s = p.getProperty(xsetTYPE_DEMOTION_DEBUG, "false"); + if (s.equalsIgnoreCase("true")) { + typeMap.debugDemotion = true; + } + s = p.getProperty(xsetTYPE_REFS, "true"); + if (s.equalsIgnoreCase("false")) { + typeMap.policy = TypeMap.USE_SOFT_REFS; + } + + runMinimalMemorySet = p.getProperty(xsetRUN_MINIMAL_MEMORY) != null; + s = p.getProperty(xsetRUN_MINIMAL_MEMORY, "false"); + runMinimalMemory = s.equalsIgnoreCase("true"); + // if (runMinimalMemory) + // getMessageHandler().handleMessage(MessageUtil.info( + // "[runMinimalMemory=true] Optimizing bcel processing (and cost of performance) to use less memory" + // )); + + s = p.getProperty(xsetDEBUG_STRUCTURAL_CHANGES_CODE, "false"); + forDEBUG_structuralChangesCode = s.equalsIgnoreCase("true"); + + s = p.getProperty(xsetTRANSIENT_TJP_FIELDS,"false"); + transientTjpFields = s.equalsIgnoreCase("true"); + + s = p.getProperty(xsetDEBUG_BRIDGING, "false"); + forDEBUG_bridgingCode = s.equalsIgnoreCase("true"); + + s = p.getProperty(xsetGENERATE_NEW_LVTS,"true"); + generateNewLvts = s.equalsIgnoreCase("true"); + if (!generateNewLvts) { + getMessageHandler().handleMessage(MessageUtil.info("[generateNewLvts=false] for methods without an incoming local variable table, do not generate one")); + } + + s = p.getProperty(xsetOPTIMIZED_MATCHING, "true"); + optimizedMatching = s.equalsIgnoreCase("true"); + if (!optimizedMatching) { + getMessageHandler().handleMessage(MessageUtil.info("[optimizedMatching=false] optimized matching turned off")); + } + + s = p.getProperty(xsetTIMERS_PER_JOINPOINT, "25000"); + try { + timersPerJoinpoint = Integer.parseInt(s); + } catch (Exception e) { + getMessageHandler().handleMessage(MessageUtil.error("unable to process timersPerJoinpoint value of " + s)); + timersPerJoinpoint = 25000; + } + + s = p.getProperty(xsetTIMERS_PER_FASTMATCH_CALL, "250"); + try { + timersPerType = Integer.parseInt(s); + } catch (Exception e) { + getMessageHandler().handleMessage(MessageUtil.error("unable to process timersPerType value of " + s)); + timersPerType = 250; + } + + } + try { + if (systemPropertyOverWeaving) { + overWeaving = true; + } + String value = null; + value = System.getProperty("aspectj.typeDemotion", "false"); + if (value.equalsIgnoreCase("true")) { + System.out.println("ASPECTJ: aspectj.typeDemotion=true: type demotion switched ON"); + typeMap.demotionSystemActive = true; + } + value = System.getProperty("aspectj.minimalModel", "false"); + if (value.equalsIgnoreCase("true")) { + System.out.println("ASPECTJ: aspectj.minimalModel=true: minimal model switched ON"); + minimalModel = true; + } + } catch (Throwable t) { + System.err.println("ASPECTJ: Unable to read system properties"); + t.printStackTrace(); + } + checkedAdvancedConfiguration = true; + } + } + + public boolean isRunMinimalMemory() { + ensureAdvancedConfigurationProcessed(); + return runMinimalMemory; + } + + public boolean isTransientTjpFields() { + ensureAdvancedConfigurationProcessed(); + return transientTjpFields; + } + + public boolean isRunMinimalMemorySet() { + ensureAdvancedConfigurationProcessed(); + return runMinimalMemorySet; + } + + public boolean shouldFastPackMethods() { + ensureAdvancedConfigurationProcessed(); + return fastMethodPacking; + } + + public boolean shouldPipelineCompilation() { + ensureAdvancedConfigurationProcessed(); + return shouldPipelineCompilation; + } + + public boolean shouldGenerateStackMaps() { + ensureAdvancedConfigurationProcessed(); + return shouldGenerateStackMaps; + } + + public void setIncrementalCompileCouldFollow(boolean b) { + incrementalCompileCouldFollow = b; + } + + public boolean couldIncrementalCompileFollow() { + return incrementalCompileCouldFollow; + } + + public void setSynchronizationPointcutsInUse() { + if (trace.isTraceEnabled()) { + trace.enter("setSynchronizationPointcutsInUse", this); + } + synchronizationPointcutsInUse = true; + if (trace.isTraceEnabled()) { + trace.exit("setSynchronizationPointcutsInUse"); + } + } + + public boolean areSynchronizationPointcutsInUse() { + return synchronizationPointcutsInUse; + } + + /** + * Register a new pointcut designator handler with the world - this can be used by any pointcut parsers attached to the world. + * + * @param designatorHandler handler for the new pointcut + */ + public void registerPointcutHandler(PointcutDesignatorHandler designatorHandler) { + if (pointcutDesignators == null) { + pointcutDesignators = new HashSet<PointcutDesignatorHandler>(); + } + pointcutDesignators.add(designatorHandler); + } + + public Set<PointcutDesignatorHandler> getRegisteredPointcutHandlers() { + if (pointcutDesignators == null) { + return Collections.emptySet(); + } + return pointcutDesignators; + } + + public void reportMatch(ShadowMunger munger, Shadow shadow) { + + } + + public boolean isOverWeaving() { + return overWeaving; + } + + public void reportCheckerMatch(Checker checker, Shadow shadow) { + } + + /** + * @return true if this world has the activation and scope of application of the aspects controlled via aop.xml files + */ + public boolean isXmlConfigured() { + return false; + } + + public boolean isAspectIncluded(ResolvedType aspectType) { + return true; + } + + /** + * Determine if the named aspect requires a particular type around in order to be useful. The type is named in the aop.xml file + * against the aspect. + * + * @return true if there is a type missing that this aspect really needed around + */ + public boolean hasUnsatisfiedDependency(ResolvedType aspectType) { + return false; + } + + public TypePattern getAspectScope(ResolvedType declaringType) { + return null; + } + + public Map<String, ResolvedType> getFixed() { + return typeMap.tMap; + } + + public Map<String, Reference<ResolvedType>> getExpendable() { + return typeMap.expendableMap; + } + + /** + * Ask the type map to demote any types it can - we don't want them anchored forever. + */ + public void demote() { + typeMap.demote(); + } + + // protected boolean isExpendable(ResolvedType type) { + // if (type.equals(UnresolvedType.OBJECT)) + // return false; + // if (type == null) + // return false; + // boolean isExposed = type.isExposedToWeaver(); + // boolean nullDele = (type instanceof ReferenceType) ? ((ReferenceType) type).getDelegate() != null : true; + // if (isExposed || !isExposed && nullDele) + // return false; + // return !type.isPrimitiveType(); + // } + + /** + * Reference types we don't intend to weave may be ejected from the cache if we need the space. + */ + protected boolean isExpendable(ResolvedType type) { + return !type.equals(UnresolvedType.OBJECT) && !type.isExposedToWeaver() && !type.isPrimitiveType() + && !type.isPrimitiveArray(); + } + + // map from aspect > excluded types + // memory issue here? + private Map<ResolvedType, Set<ResolvedType>> exclusionMap = new HashMap<ResolvedType, Set<ResolvedType>>(); + + public Map<ResolvedType, Set<ResolvedType>> getExclusionMap() { + return exclusionMap; + } + + private TimeCollector timeCollector = null; + + /** + * Record the time spent matching a pointcut - this will accumulate over the lifetime of this world/weaver and be reported every + * 25000 join points. + */ + public void record(Pointcut pointcut, long timetaken) { + if (timeCollector == null) { + ensureAdvancedConfigurationProcessed(); + timeCollector = new TimeCollector(this); + } + timeCollector.record(pointcut, timetaken); + } + + /** + * Record the time spent fastmatching a pointcut - this will accumulate over the lifetime of this world/weaver and be reported + * every 250 types. + */ + public void recordFastMatch(Pointcut pointcut, long timetaken) { + if (timeCollector == null) { + ensureAdvancedConfigurationProcessed(); + timeCollector = new TimeCollector(this); + } + timeCollector.recordFastMatch(pointcut, timetaken); + } + + public void reportTimers() { + if (timeCollector != null && !timingPeriodically) { + timeCollector.report(); + timeCollector = new TimeCollector(this); + } + } + + private static class TimeCollector { + private World world; + long joinpointCount; + long typeCount; + long perJoinpointCount; + long perTypes; + Map<String, Long> joinpointsPerPointcut = new HashMap<String, Long>(); + Map<String, Long> timePerPointcut = new HashMap<String, Long>(); + Map<String, Long> fastMatchTimesPerPointcut = new HashMap<String, Long>(); + Map<String, Long> fastMatchTypesPerPointcut = new HashMap<String, Long>(); + + TimeCollector(World world) { + this.perJoinpointCount = world.timersPerJoinpoint; + this.perTypes = world.timersPerType; + this.world = world; + this.joinpointCount = 0; + this.typeCount = 0; + this.joinpointsPerPointcut = new HashMap<String, Long>(); + this.timePerPointcut = new HashMap<String, Long>(); + } + + public void report() { + long totalTime = 0L; + for (String p : joinpointsPerPointcut.keySet()) { + totalTime += timePerPointcut.get(p); + } + world.getMessageHandler().handleMessage( + MessageUtil.info("Pointcut matching cost (total=" + (totalTime / 1000000) + "ms for " + joinpointCount + + " joinpoint match calls):")); + for (String p : joinpointsPerPointcut.keySet()) { + StringBuffer sb = new StringBuffer(); + sb.append("Time:" + (timePerPointcut.get(p) / 1000000) + "ms (jps:#" + joinpointsPerPointcut.get(p) + + ") matching against " + p); + world.getMessageHandler().handleMessage(MessageUtil.info(sb.toString())); + } + world.getMessageHandler().handleMessage(MessageUtil.info("---")); + + totalTime = 0L; + for (String p : fastMatchTimesPerPointcut.keySet()) { + totalTime += fastMatchTimesPerPointcut.get(p); + } + world.getMessageHandler().handleMessage( + MessageUtil.info("Pointcut fast matching cost (total=" + (totalTime / 1000000) + "ms for " + typeCount + + " fast match calls):")); + for (String p : fastMatchTimesPerPointcut.keySet()) { + StringBuffer sb = new StringBuffer(); + sb.append("Time:" + (fastMatchTimesPerPointcut.get(p) / 1000000) + "ms (types:#" + fastMatchTypesPerPointcut.get(p) + + ") fast matching against " + p); + world.getMessageHandler().handleMessage(MessageUtil.info(sb.toString())); + } + world.getMessageHandler().handleMessage(MessageUtil.info("---")); + + } + + void record(Pointcut pointcut, long timetakenInNs) { + joinpointCount++; + String pointcutText = pointcut.toString(); + Long jpcounter = joinpointsPerPointcut.get(pointcutText); + if (jpcounter == null) { + jpcounter = 1L; + } else { + jpcounter++; + } + joinpointsPerPointcut.put(pointcutText, jpcounter); + + Long time = timePerPointcut.get(pointcutText); + if (time == null) { + time = timetakenInNs; + } else { + time += timetakenInNs; + } + timePerPointcut.put(pointcutText, time); + if (world.timingPeriodically) { + if ((joinpointCount % perJoinpointCount) == 0) { + long totalTime = 0L; + for (String p : joinpointsPerPointcut.keySet()) { + totalTime += timePerPointcut.get(p); + } + world.getMessageHandler().handleMessage( + MessageUtil.info("Pointcut matching cost (total=" + (totalTime / 1000000) + "ms for " + joinpointCount + + " joinpoint match calls):")); + for (String p : joinpointsPerPointcut.keySet()) { + StringBuffer sb = new StringBuffer(); + sb.append("Time:" + (timePerPointcut.get(p) / 1000000) + "ms (jps:#" + joinpointsPerPointcut.get(p) + + ") matching against " + p); + world.getMessageHandler().handleMessage(MessageUtil.info(sb.toString())); + } + world.getMessageHandler().handleMessage(MessageUtil.info("---")); + } + } + } + + void recordFastMatch(Pointcut pointcut, long timetakenInNs) { + typeCount++; + String pointcutText = pointcut.toString(); + Long typecounter = fastMatchTypesPerPointcut.get(pointcutText); + if (typecounter == null) { + typecounter = 1L; + } else { + typecounter++; + } + fastMatchTypesPerPointcut.put(pointcutText, typecounter); + + Long time = fastMatchTimesPerPointcut.get(pointcutText); + if (time == null) { + time = timetakenInNs; + } else { + time += timetakenInNs; + } + fastMatchTimesPerPointcut.put(pointcutText, time); + if (world.timingPeriodically) { + if ((typeCount % perTypes) == 0) { + long totalTime = 0L; + for (String p : fastMatchTimesPerPointcut.keySet()) { + totalTime += fastMatchTimesPerPointcut.get(p); + } + world.getMessageHandler().handleMessage( + MessageUtil.info("Pointcut fast matching cost (total=" + (totalTime / 1000000) + "ms for " + typeCount + + " fast match calls):")); + for (String p : fastMatchTimesPerPointcut.keySet()) { + StringBuffer sb = new StringBuffer(); + sb.append("Time:" + (fastMatchTimesPerPointcut.get(p) / 1000000) + "ms (types:#" + + fastMatchTypesPerPointcut.get(p) + ") fast matching against " + p); + world.getMessageHandler().handleMessage(MessageUtil.info(sb.toString())); + } + world.getMessageHandler().handleMessage(MessageUtil.info("---")); + } + } + } + } + + public TypeMap getTypeMap() { + return typeMap; + } + + public static void reset() { + // ResolvedType.resetPrimitives(); + } + + /** + * Returns the version of ITD that this world wants to create. The default is the new style (2) but in some cases where there + * might be a clash, the old style can be used. It is set through the option -Xset:itdVersion=1 + * + * @return the ITD version this world wants to create - 1=oldstyle 2=new, transparent style + */ + public int getItdVersion() { + return itdVersion; + } + + // if not loadtime weaving then we are compile time weaving or post-compile time weaving + public abstract boolean isLoadtimeWeaving(); + + public void classWriteEvent(char[][] compoundName) { + // override if interested in write events + } + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/XlintDefault.properties b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/XlintDefault.properties new file mode 100644 index 000000000..416fc2d55 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/XlintDefault.properties @@ -0,0 +1,50 @@ +invalidAbsoluteTypeName = warning +invalidWildcardTypeName = ignore + +unresolvableMember = warning + +typeNotExposedToWeaver = warning + +shadowNotInStructure = ignore + +unmatchedSuperTypeInCall = warning + +canNotImplementLazyTjp = ignore +multipleAdviceStoppingLazyTjp=ignore +noGuardForLazyTjp=ignore + +uncheckedAdviceConversion = warning + +needsSerialVersionUIDField = ignore +brokeSerialVersionCompatibility = ignore + +noInterfaceCtorJoinpoint = warning + +noJoinpointsForBridgeMethods = warning +cantMatchArrayTypeOnVarargs = ignore +enumAsTargetForDecpIgnored = warning +annotationAsTargetForDecpIgnored = warning +adviceDidNotMatch = warning +invalidTargetForAnnotation = warning +elementAlreadyAnnotated = warning +runtimeExceptionNotSoftened = warning +uncheckedArgument = warning +noExplicitConstructorCall = warning + +aspectExcludedByConfiguration = ignore + +unmatchedTargetKind = warning + +cantFindType = error +cantFindTypeAffectingJPMatch = warning + +unorderedAdviceAtShadow=ignore +swallowedExceptionInCatchBlock=ignore +calculatingSerialVersionUID=ignore +advisingSynchronizedMethods=warning +mustWeaveXmlDefinedAspects=warning + +missingAspectForReweaving=error +cannotAdviseJoinpointInInterfaceWithAroundAdvice=warning + +nonReweavableTypeEncountered=error
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/ASTNode.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/ASTNode.java new file mode 100644 index 000000000..7e8b08347 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/ASTNode.java @@ -0,0 +1,22 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.ast; + +public abstract class ASTNode { + + public ASTNode() { + super(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/And.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/And.java new file mode 100644 index 000000000..8679d48d0 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/And.java @@ -0,0 +1,51 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.ast; + + +public class And extends Test { + Test left, right; + + public And(Test left, Test right) { + super(); + this.left = left; + this.right = right; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public String toString() { + return "(" + left + " && " + right + ")"; + } + + public boolean equals(Object other) { + if (other instanceof And) { + And o = (And) other; + return o.left.equals(left) && o.right.equals(right); + } else { + return false; + } + } + + public Test getLeft() { + return left; + } + + public Test getRight() { + return right; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Call.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Call.java new file mode 100644 index 000000000..1a1b52abe --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Call.java @@ -0,0 +1,41 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.ast; + +import org.aspectj.weaver.Member; + +public class Call extends Test { + // assert m.return value is boolean + private final Member method; + private final Expr[] args; + + public Call(Member m, Expr[] args) { + super(); + this.method = m; + this.args = args; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public Expr[] getArgs() { + return args; + } + + public Member getMethod() { + return method; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/CallExpr.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/CallExpr.java new file mode 100644 index 000000000..e8191a42e --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/CallExpr.java @@ -0,0 +1,48 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.ast; + +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; + +public class CallExpr extends Expr { + // assert m.return value is boolean + private final Member method; + private final Expr[] args; + private final ResolvedType returnType; // yes, stored in method as well, but that one isn't resolved + + public CallExpr(Member m, Expr[] args, ResolvedType returnType) { + super(); + this.method = m; + this.args = args; + this.returnType = returnType; + } + + public void accept(IExprVisitor v) { + v.visit(this); + } + + public Expr[] getArgs() { + return args; + } + + public Member getMethod() { + return method; + } + + public ResolvedType getType() { + return returnType; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Expr.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Expr.java new file mode 100644 index 000000000..1b22c8f42 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Expr.java @@ -0,0 +1,34 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.ast; + +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; + +public abstract class Expr extends ASTNode { + + public Expr() { + super(); + } + + public static final Expr[] NONE = new Expr[0]; + + public abstract void accept(IExprVisitor v); + + public abstract ResolvedType getType(); + + public static CallExpr makeCallExpr(Member member, Expr[] exprs, ResolvedType returnType) { + return new CallExpr(member, exprs, returnType); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/FieldGet.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/FieldGet.java new file mode 100644 index 000000000..2e145e3c2 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/FieldGet.java @@ -0,0 +1,44 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.ast; + +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; + +public class FieldGet extends Expr { + Member field; + ResolvedType resolvedType; + + public FieldGet(Member field, ResolvedType resolvedType) { + super(); + this.field = field; + this.resolvedType = resolvedType; + } + + public ResolvedType getType() { + return resolvedType; + } + + public String toString() { + return "(FieldGet " + field + ")"; + } + + public void accept(IExprVisitor v) { + v.visit(this); + } + + public Member getField() { + return field; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/FieldGetCall.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/FieldGetCall.java new file mode 100644 index 000000000..64aaf4b1a --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/FieldGetCall.java @@ -0,0 +1,47 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.ast; + +import org.aspectj.weaver.Member; + +public class FieldGetCall extends Test { + // assert m.return value is boolean + private final Member field; + private final Member method; + private final Expr[] args; + + public FieldGetCall(Member f, Member m, Expr[] args) { + super(); + this.field = f; + this.method = m; + this.args = args; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public Expr[] getArgs() { + return args; + } + + public Member getMethod() { + return method; + } + + public Member getField() { + return field; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/HasAnnotation.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/HasAnnotation.java new file mode 100644 index 000000000..885e9d083 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/HasAnnotation.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.aspectj.weaver.ast; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; + +public class HasAnnotation extends Test { + + private Var v; + private ResolvedType annType; + + public HasAnnotation(Var v, ResolvedType annType) { + super(); + this.v = v; + this.annType = annType; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.ast.Test#accept(org.aspectj.weaver.ast.ITestVisitor) + */ + public void accept(ITestVisitor v) { + v.visit(this); + } + + public String toString() { + return "(" + v + " has annotation @" + annType + ")"; + } + + public boolean equals(Object other) { + if (other instanceof HasAnnotation) { + HasAnnotation o = (HasAnnotation) other; + return o.v.equals(v) && o.annType.equals(annType); + } else { + return false; + } + } + + public int hashCode() { + return v.hashCode()*37+annType.hashCode(); + } + + public Var getVar() { + return v; + } + + public UnresolvedType getAnnotationType() { + return annType; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/IExprVisitor.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/IExprVisitor.java new file mode 100644 index 000000000..89e0b3d74 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/IExprVisitor.java @@ -0,0 +1,23 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.ast; + +public interface IExprVisitor { + + void visit(Var i); + + void visit(FieldGet fieldGet); + + void visit(CallExpr callExpr); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/ITestVisitor.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/ITestVisitor.java new file mode 100644 index 000000000..fc99272af --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/ITestVisitor.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.ast; + +import org.aspectj.weaver.internal.tools.MatchingContextBasedTest; + + +public interface ITestVisitor { + + void visit(And e); + void visit(Instanceof i); + void visit(Not not); + void visit(Or or); + void visit(Literal literal); + void visit(Call call); + void visit(FieldGetCall fieldGetCall); + void visit(HasAnnotation hasAnnotation); + void visit(MatchingContextBasedTest matchingContextTest); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Instanceof.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Instanceof.java new file mode 100644 index 000000000..feec6a2be --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Instanceof.java @@ -0,0 +1,56 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.ast; + +import org.aspectj.weaver.UnresolvedType; + +public class Instanceof extends Test { + Var var; + UnresolvedType type; + + public Instanceof(Var left, UnresolvedType right) { + super(); + this.var = left; + this.type = right; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public String toString() { + return "(" + var + " instanceof " + type + ")"; + } + + public boolean equals(Object other) { + if (other instanceof Instanceof) { + Instanceof o = (Instanceof) other; + return o.var.equals(var) && o.type.equals(type); + } else { + return false; + } + } + + public int hashCode() { + return var.hashCode()*37+type.hashCode(); + } + + public Var getVar() { + return var; + } + + public UnresolvedType getType() { + return type; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Literal.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Literal.java new file mode 100644 index 000000000..bb7968eb5 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Literal.java @@ -0,0 +1,39 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.ast; + + +public final class Literal extends Test { + + boolean noTest; + boolean val; + + private Literal(boolean val, boolean noTest) { + super(); + this.val = val; + this.noTest = noTest; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public static final Literal TRUE = new Literal(true, false); + public static final Literal FALSE = new Literal(false, false); +// public static final Literal NO_TEST = new Literal(false, true); + + public String toString() { + return noTest ? "NO_TEST" : val ? "TRUE" : "FALSE"; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Not.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Not.java new file mode 100644 index 000000000..366a193a4 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Not.java @@ -0,0 +1,49 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.ast; + + +public class Not extends Test { + Test test; + + public Not(Test test) { + super(); + this.test = test; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public Test getBody() { + return test; + } + + public String toString() { + return "!" + test; + } + + public boolean equals(Object other) { + if (other instanceof Not) { + Not o = (Not) other; + return o.test.equals(test); + } else { + return false; + } + } + + public int hashCode() { + return test.hashCode(); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Or.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Or.java new file mode 100644 index 000000000..b3143df73 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Or.java @@ -0,0 +1,56 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.ast; + +public class Or extends Test { + Test left, right; + + public Or(Test left, Test right) { + super(); + this.left = left; + this.right = right; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public String toString() { + return "(" + left + " || " + right + ")"; + } + + public boolean equals(Object other) { + if (other instanceof Or) { + Or o = (Or) other; + return o.left.equals(left) && o.right.equals(right); + } else { + return false; + } + } + + public int hashCode() { + int result = 19; + result = 37*result + left.hashCode(); + result = 37*result + right.hashCode(); + return result; + } + + public Test getLeft() { + return left; + } + + public Test getRight() { + return right; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Test.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Test.java new file mode 100644 index 000000000..8da7694e6 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Test.java @@ -0,0 +1,95 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.ast; + +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; + +public abstract class Test extends ASTNode { + + public Test() { + super(); + } + + public abstract void accept(ITestVisitor v); + + public static Test makeAnd(Test a, Test b) { +// if (a == Literal.NO_TEST) return b; +// if (b == Literal.NO_TEST) return a; + if (a == Literal.TRUE) { + if (b == Literal.TRUE) { + return a; + } else { + return b; + } + } else if (b == Literal.TRUE) { + return a; + } else if (a == Literal.FALSE || b == Literal.FALSE) { + return Literal.FALSE; + } else { + return new And(a, b); + } + } + + public static Test makeOr(Test a, Test b) { +// if (a == Literal.NO_TEST) return a; +// if (b == Literal.NO_TEST) return b; + if (a == Literal.FALSE) { + return b; + } else if (b == Literal.FALSE) { + return a; + } else if (a == Literal.TRUE || b == Literal.TRUE) { + return Literal.TRUE; + } else { + return new Or(a, b); + } + } + + public static Test makeNot(Test a) { + if (a instanceof Not) { + return ((Not) a).getBody(); + } else if (a == Literal.TRUE) { + return Literal.FALSE; + } else if (a == Literal.FALSE) { + return Literal.TRUE; +// } else if (a == Literal.NO_TEST) { +// return a; + } else { + return new Not(a); + } + } + + // uses our special rules that anything matches object + public static Test makeInstanceof(Var v, ResolvedType ty) { + if (ty.equals(ResolvedType.OBJECT)) return Literal.TRUE; + + Test e; + if (ty.isAssignableFrom(v.getType())) e = Literal.TRUE; + else if (! ty.isCoerceableFrom(v.getType())) e = Literal.FALSE; + else e = new Instanceof(v, ty); + return e; + } + + public static Test makeHasAnnotation(Var v, ResolvedType annTy) { + return new HasAnnotation(v,annTy); + } + + public static Test makeCall(Member m, Expr[] args) { + return new Call(m, args); + } + public static Test makeFieldGetCall(Member f, Member m, Expr[] args) { + return new FieldGetCall(f, m, args); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Var.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Var.java new file mode 100644 index 000000000..77fddb557 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ast/Var.java @@ -0,0 +1,49 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.ast; + +import org.aspectj.weaver.ResolvedType; + +public class Var extends Expr { + public static final Var[] NONE = new Var[0]; + ResolvedType variableType; + + public Var(ResolvedType variableType) { + super(); + this.variableType = variableType; + } + + public ResolvedType getType() { + return variableType; + } + + public String toString() { + return "(Var " + variableType + ")"; + } + + public void accept(IExprVisitor v) { + v.visit(this); + } + + /** + * For an annotation this will return a variable that can access a specific field of the annotation (of the specified type) TODO + * what kind of behaviour happens for two annotation fields of the same type? + * + * @param formalType + * @param formalName + * @return + */ + public Var getAccessorForValue(ResolvedType formalType, String formalName) { + throw new IllegalStateException("Only makes sense for annotation variables"); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/MatchingContextBasedTest.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/MatchingContextBasedTest.java new file mode 100644 index 000000000..b5a78e0e6 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/MatchingContextBasedTest.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.internal.tools; + +import org.aspectj.weaver.ast.ITestVisitor; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.tools.MatchingContext; +import org.aspectj.weaver.tools.ContextBasedMatcher; + +/** + * Test that uses MatchingContext to match (or not) + * + */ +public class MatchingContextBasedTest extends Test { + + private final ContextBasedMatcher matcher; + + public MatchingContextBasedTest(ContextBasedMatcher pc) { + this.matcher = pc; + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.ast.Test#accept(org.aspectj.weaver.ast.ITestVisitor) + */ + public void accept(ITestVisitor v) { + v.visit(this); + } + + public boolean matches(MatchingContext context) { + return this.matcher.matchesDynamically(context); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/PointcutDesignatorHandlerBasedPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/PointcutDesignatorHandlerBasedPointcut.java new file mode 100644 index 000000000..e5672dede --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/PointcutDesignatorHandlerBasedPointcut.java @@ -0,0 +1,187 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.internal.tools; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ReferenceTypeDelegate; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.patterns.Bindings; +import org.aspectj.weaver.patterns.ExposedState; +import org.aspectj.weaver.patterns.FastMatchInfo; +import org.aspectj.weaver.patterns.IScope; +import org.aspectj.weaver.patterns.PatternNodeVisitor; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.reflect.ReflectionBasedReferenceTypeDelegate; +import org.aspectj.weaver.reflect.ReflectionFastMatchInfo; +import org.aspectj.weaver.reflect.ReflectionShadow; +import org.aspectj.weaver.reflect.ReflectionWorld; +import org.aspectj.weaver.tools.ContextBasedMatcher; +import org.aspectj.weaver.tools.MatchingContext; + +/** + * Implementation of Pointcut that is backed by a user-extension pointcut designator handler. + * + */ +public class PointcutDesignatorHandlerBasedPointcut extends Pointcut { + + private final ContextBasedMatcher matcher; + private final World world; + + public PointcutDesignatorHandlerBasedPointcut(ContextBasedMatcher expr, World world) { + this.matcher = expr; + this.world = world; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#getPointcutKind() + */ + public byte getPointcutKind() { + return Pointcut.USER_EXTENSION; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) + */ + public FuzzyBoolean fastMatch(FastMatchInfo info) { + if (info instanceof ReflectionFastMatchInfo) { + // Really need a reflectionworld here... + if (!(world instanceof ReflectionWorld)) { + throw new IllegalStateException("Can only match user-extension pcds with a ReflectionWorld"); + } + Class<?> clazz = null; + try { + clazz = Class.forName(info.getType().getName(), false, ((ReflectionWorld) world).getClassLoader()); + } catch (ClassNotFoundException cnfe) { + if (info.getType() instanceof ReferenceType) { + ReferenceTypeDelegate rtd = ((ReferenceType)info.getType()).getDelegate(); + if (rtd instanceof ReflectionBasedReferenceTypeDelegate) { + clazz = ((ReflectionBasedReferenceTypeDelegate)rtd).getClazz(); + } + } + } + if (clazz == null) { + return FuzzyBoolean.MAYBE; + } + return FuzzyBoolean.fromBoolean(this.matcher.couldMatchJoinPointsInType(clazz, ((ReflectionFastMatchInfo) info).getMatchingContext())); + } + throw new IllegalStateException("Can only match user-extension pcds against Reflection FastMatchInfo objects"); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#couldMatchKinds() + */ + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#matchInternal(org.aspectj.weaver.Shadow) + */ + protected FuzzyBoolean matchInternal(Shadow shadow) { + if (shadow instanceof ReflectionShadow) { + MatchingContext context = ((ReflectionShadow) shadow).getMatchingContext(); + org.aspectj.weaver.tools.FuzzyBoolean match = this.matcher.matchesStatically(context); + if (match == org.aspectj.weaver.tools.FuzzyBoolean.MAYBE) { + return FuzzyBoolean.MAYBE; + } else if (match == org.aspectj.weaver.tools.FuzzyBoolean.YES) { + return FuzzyBoolean.YES; + } else if (match == org.aspectj.weaver.tools.FuzzyBoolean.NO) { + return FuzzyBoolean.NO; + } + } + throw new IllegalStateException("Can only match user-extension pcds against Reflection shadows (not BCEL)"); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(org.aspectj.weaver.patterns.IScope, + * org.aspectj.weaver.patterns.Bindings) + */ + protected void resolveBindings(IScope scope, Bindings bindings) { + // no-op + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#concretize1(org.aspectj.weaver.ResolvedType, org.aspectj.weaver.ResolvedType, + * org.aspectj.weaver.IntMap) + */ + protected Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + return this; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#findResidueInternal(org.aspectj.weaver.Shadow, + * org.aspectj.weaver.patterns.ExposedState) + */ + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + if (!this.matcher.mayNeedDynamicTest()) { + return Literal.TRUE; + } else { + // could be more efficient here! + matchInternal(shadow); + return new MatchingContextBasedTest(this.matcher); + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#parameterizeWith(java.util.Map) + */ + public Pointcut parameterizeWith(Map typeVariableMap, World w) { + return this; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + public void write(CompressingDataOutputStream s) throws IOException { + throw new UnsupportedOperationException("can't write custom pointcut designator expressions to stream"); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PatternNode#accept(org.aspectj.weaver.patterns.PatternNodeVisitor, java.lang.Object) + */ + public Object accept(PatternNodeVisitor visitor, Object data) { + // visitor.visit(this); + // no-op? + return data; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/PointcutExpressionImpl.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/PointcutExpressionImpl.java new file mode 100644 index 000000000..822c78d9a --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/PointcutExpressionImpl.java @@ -0,0 +1,442 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.internal.tools; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor; +import org.aspectj.weaver.patterns.AnnotationPointcut; +import org.aspectj.weaver.patterns.ArgsAnnotationPointcut; +import org.aspectj.weaver.patterns.ArgsPointcut; +import org.aspectj.weaver.patterns.CflowPointcut; +import org.aspectj.weaver.patterns.ExposedState; +import org.aspectj.weaver.patterns.IfPointcut; +import org.aspectj.weaver.patterns.NotAnnotationTypePattern; +import org.aspectj.weaver.patterns.NotPointcut; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.ThisOrTargetAnnotationPointcut; +import org.aspectj.weaver.patterns.ThisOrTargetPointcut; +import org.aspectj.weaver.patterns.WithinAnnotationPointcut; +import org.aspectj.weaver.patterns.WithinCodeAnnotationPointcut; +import org.aspectj.weaver.reflect.ReflectionFastMatchInfo; +import org.aspectj.weaver.reflect.ReflectionShadow; +import org.aspectj.weaver.reflect.ReflectionWorld; +import org.aspectj.weaver.reflect.ShadowMatchImpl; +import org.aspectj.weaver.tools.DefaultMatchingContext; +import org.aspectj.weaver.tools.MatchingContext; +import org.aspectj.weaver.tools.PointcutExpression; +import org.aspectj.weaver.tools.PointcutParameter; +import org.aspectj.weaver.tools.ShadowMatch; + +/** + * Map from weaver.tools interface to internal Pointcut implementation... + */ +public class PointcutExpressionImpl implements PointcutExpression { + + private final static boolean MATCH_INFO = false; + + private World world; + private Pointcut pointcut; + private String expression; + private PointcutParameter[] parameters; + private MatchingContext matchContext = new DefaultMatchingContext(); + + public PointcutExpressionImpl(Pointcut pointcut, String expression, PointcutParameter[] params, World inWorld) { + this.pointcut = pointcut; + this.expression = expression; + this.world = inWorld; + this.parameters = params; + if (this.parameters == null) { + this.parameters = new PointcutParameter[0]; + } + } + + public Pointcut getUnderlyingPointcut() { + return this.pointcut; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.tools.PointcutExpression#setMatchingContext(org.aspectj.weaver.tools.MatchingContext) + */ + public void setMatchingContext(MatchingContext aMatchContext) { + this.matchContext = aMatchContext; + } + + public boolean couldMatchJoinPointsInType(Class aClass) { + ResolvedType matchType = world.resolve(aClass.getName()); + if (matchType.isMissing() && (world instanceof ReflectionWorld)) { + // Class is a generated class that cannot be 'looked up' via getResource. + // For example a proxy or lambda. + // Use the class itself in this case + matchType = ((ReflectionWorld)world).resolveUsingClass(aClass); + } + ReflectionFastMatchInfo info = new ReflectionFastMatchInfo(matchType, null, this.matchContext, world); + boolean couldMatch = pointcut.fastMatch(info).maybeTrue(); + if (MATCH_INFO) { + System.out.println("MATCHINFO: fast match for '" + this.expression + "' against '" + aClass.getName() + "': " + + couldMatch); + } + return couldMatch; + } + + public boolean mayNeedDynamicTest() { + HasPossibleDynamicContentVisitor visitor = new HasPossibleDynamicContentVisitor(); + pointcut.traverse(visitor, null); + return visitor.hasDynamicContent(); + } + + private ExposedState getExposedState() { + return new ExposedState(parameters.length); + } + + public ShadowMatch matchesMethodExecution(Method aMethod) { + ShadowMatch match = matchesExecution(aMethod); + if (MATCH_INFO && match.maybeMatches()) { + System.out.println("MATCHINFO: method execution match on '" + aMethod + "' for '" + this.expression + "': " + + (match.alwaysMatches() ? "YES" : "MAYBE")); + } + return match; + } + + public ShadowMatch matchesConstructorExecution(Constructor aConstructor) { + ShadowMatch match = matchesExecution(aConstructor); + if (MATCH_INFO && match.maybeMatches()) { + System.out.println("MATCHINFO: constructor execution match on '" + aConstructor + "' for '" + this.expression + "': " + + (match.alwaysMatches() ? "YES" : "MAYBE")); + } + return match; + } + + private ShadowMatch matchesExecution(Member aMember) { + Shadow s = ReflectionShadow.makeExecutionShadow(world, aMember, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aMember); + sm.setWithinCode(null); + sm.setWithinType(aMember.getDeclaringClass()); + return sm; + } + + public ShadowMatch matchesStaticInitialization(Class aClass) { + Shadow s = ReflectionShadow.makeStaticInitializationShadow(world, aClass, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(null); + sm.setWithinCode(null); + sm.setWithinType(aClass); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: static initialization match on '" + aClass.getName() + "' for '" + this.expression + + "': " + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + public ShadowMatch matchesAdviceExecution(Method aMethod) { + Shadow s = ReflectionShadow.makeAdviceExecutionShadow(world, aMethod, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aMethod); + sm.setWithinCode(null); + sm.setWithinType(aMethod.getDeclaringClass()); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: advice execution match on '" + aMethod + "' for '" + this.expression + "': " + + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + public ShadowMatch matchesInitialization(Constructor aConstructor) { + Shadow s = ReflectionShadow.makeInitializationShadow(world, aConstructor, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aConstructor); + sm.setWithinCode(null); + sm.setWithinType(aConstructor.getDeclaringClass()); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: initialization match on '" + aConstructor + "' for '" + this.expression + "': " + + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + public ShadowMatch matchesPreInitialization(Constructor aConstructor) { + Shadow s = ReflectionShadow.makePreInitializationShadow(world, aConstructor, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aConstructor); + sm.setWithinCode(null); + sm.setWithinType(aConstructor.getDeclaringClass()); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: preinitialization match on '" + aConstructor + "' for '" + this.expression + "': " + + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + public ShadowMatch matchesMethodCall(Method aMethod, Member withinCode) { + Shadow s = ReflectionShadow.makeCallShadow(world, aMethod, withinCode, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aMethod); + sm.setWithinCode(withinCode); + sm.setWithinType(withinCode.getDeclaringClass()); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: method call match on '" + aMethod + "' withinCode='" + withinCode + "' for '" + + this.expression + "': " + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + public ShadowMatch matchesMethodCall(Method aMethod, Class callerType) { + Shadow s = ReflectionShadow.makeCallShadow(world, aMethod, callerType, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aMethod); + sm.setWithinCode(null); + sm.setWithinType(callerType); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: method call match on '" + aMethod + "' callerType='" + callerType.getName() + "' for '" + + this.expression + "': " + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + public ShadowMatch matchesConstructorCall(Constructor aConstructor, Class callerType) { + Shadow s = ReflectionShadow.makeCallShadow(world, aConstructor, callerType, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aConstructor); + sm.setWithinCode(null); + sm.setWithinType(callerType); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: constructor call match on '" + aConstructor + "' callerType='" + callerType.getName() + + "' for '" + this.expression + "': " + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + public ShadowMatch matchesConstructorCall(Constructor aConstructor, Member withinCode) { + Shadow s = ReflectionShadow.makeCallShadow(world, aConstructor, withinCode, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aConstructor); + sm.setWithinCode(withinCode); + sm.setWithinType(withinCode.getDeclaringClass()); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: constructor call match on '" + aConstructor + "' withinCode='" + withinCode + "' for '" + + this.expression + "': " + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + public ShadowMatch matchesHandler(Class exceptionType, Class handlingType) { + Shadow s = ReflectionShadow.makeHandlerShadow(world, exceptionType, handlingType, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(null); + sm.setWithinCode(null); + sm.setWithinType(handlingType); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: handler match on '" + exceptionType.getName() + "' handlingType='" + handlingType + + "' for '" + this.expression + "': " + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + public ShadowMatch matchesHandler(Class exceptionType, Member withinCode) { + Shadow s = ReflectionShadow.makeHandlerShadow(world, exceptionType, withinCode, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(null); + sm.setWithinCode(withinCode); + sm.setWithinType(withinCode.getDeclaringClass()); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: handler match on '" + exceptionType.getName() + "' withinCode='" + withinCode + + "' for '" + this.expression + "': " + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + public ShadowMatch matchesFieldGet(Field aField, Class withinType) { + Shadow s = ReflectionShadow.makeFieldGetShadow(world, aField, withinType, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aField); + sm.setWithinCode(null); + sm.setWithinType(withinType); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: field get match on '" + aField + "' withinType='" + withinType.getName() + "' for '" + + this.expression + "': " + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + public ShadowMatch matchesFieldGet(Field aField, Member withinCode) { + Shadow s = ReflectionShadow.makeFieldGetShadow(world, aField, withinCode, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aField); + sm.setWithinCode(withinCode); + sm.setWithinType(withinCode.getDeclaringClass()); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: field get match on '" + aField + "' withinCode='" + withinCode + "' for '" + + this.expression + "': " + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + public ShadowMatch matchesFieldSet(Field aField, Class withinType) { + Shadow s = ReflectionShadow.makeFieldSetShadow(world, aField, withinType, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aField); + sm.setWithinCode(null); + sm.setWithinType(withinType); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: field set match on '" + aField + "' withinType='" + withinType.getName() + "' for '" + + this.expression + "': " + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + public ShadowMatch matchesFieldSet(Field aField, Member withinCode) { + Shadow s = ReflectionShadow.makeFieldSetShadow(world, aField, withinCode, this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aField); + sm.setWithinCode(withinCode); + sm.setWithinType(withinCode.getDeclaringClass()); + if (MATCH_INFO && sm.maybeMatches()) { + System.out.println("MATCHINFO: field set match on '" + aField + "' withinCode='" + withinCode + "' for '" + + this.expression + "': " + (sm.alwaysMatches() ? "YES" : "MAYBE")); + } + return sm; + } + + private ShadowMatchImpl getShadowMatch(Shadow forShadow) { + org.aspectj.util.FuzzyBoolean match = pointcut.match(forShadow); + Test residueTest = Literal.TRUE; + ExposedState state = getExposedState(); + if (match.maybeTrue()) { + residueTest = pointcut.findResidue(forShadow, state); + } + ShadowMatchImpl sm = new ShadowMatchImpl(match, residueTest, state, parameters); + sm.setMatchingContext(this.matchContext); + return sm; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.tools.PointcutExpression#getPointcutExpression() + */ + public String getPointcutExpression() { + return expression; + } + + private static class HasPossibleDynamicContentVisitor extends AbstractPatternNodeVisitor { + private boolean hasDynamicContent = false; + + public boolean hasDynamicContent() { + return hasDynamicContent; + } + + @Override + public Object visit(WithinAnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(WithinCodeAnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(AnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(ArgsAnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(ArgsPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(CflowPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(IfPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(NotAnnotationTypePattern node, Object data) { + return node.getNegatedPattern().accept(this, data); + } + + @Override + public Object visit(NotPointcut node, Object data) { + return node.getNegatedPointcut().accept(this, data); + } + + @Override + public Object visit(ThisOrTargetAnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(ThisOrTargetPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + } + + public static class Handler implements Member { + + private Class decClass; + private Class exType; + + public Handler(Class decClass, Class exType) { + this.decClass = decClass; + this.exType = exType; + } + + public int getModifiers() { + return 0; + } + + public Class getDeclaringClass() { + return decClass; + } + + public String getName() { + return null; + } + + public Class getHandledExceptionType() { + return exType; + } + + public boolean isSynthetic() { + return false; + } + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/StandardPointcutExpressionImpl.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/StandardPointcutExpressionImpl.java new file mode 100644 index 000000000..a767ed18b --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/StandardPointcutExpressionImpl.java @@ -0,0 +1,385 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.internal.tools; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Member; + +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor; +import org.aspectj.weaver.patterns.AnnotationPointcut; +import org.aspectj.weaver.patterns.ArgsAnnotationPointcut; +import org.aspectj.weaver.patterns.ArgsPointcut; +import org.aspectj.weaver.patterns.CflowPointcut; +import org.aspectj.weaver.patterns.ExposedState; +import org.aspectj.weaver.patterns.IfPointcut; +import org.aspectj.weaver.patterns.NotAnnotationTypePattern; +import org.aspectj.weaver.patterns.NotPointcut; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.ThisOrTargetAnnotationPointcut; +import org.aspectj.weaver.patterns.ThisOrTargetPointcut; +import org.aspectj.weaver.patterns.WithinAnnotationPointcut; +import org.aspectj.weaver.patterns.WithinCodeAnnotationPointcut; +import org.aspectj.weaver.reflect.ReflectionFastMatchInfo; +import org.aspectj.weaver.reflect.StandardShadow; +import org.aspectj.weaver.reflect.StandardShadowMatchImpl; +import org.aspectj.weaver.tools.DefaultMatchingContext; +import org.aspectj.weaver.tools.MatchingContext; +import org.aspectj.weaver.tools.PointcutParameter; +import org.aspectj.weaver.tools.ShadowMatch; +import org.aspectj.weaver.tools.StandardPointcutExpression; + +/** + * Map from weaver.tools interface to internal Pointcut implementation... + */ +public class StandardPointcutExpressionImpl implements StandardPointcutExpression { + + private World world; + private Pointcut pointcut; + private String expression; + private PointcutParameter[] parameters; + private MatchingContext matchContext = new DefaultMatchingContext(); + + public StandardPointcutExpressionImpl(Pointcut pointcut, String expression, PointcutParameter[] params, World inWorld) { + this.pointcut = pointcut; + this.expression = expression; + this.world = inWorld; + this.parameters = params; + if (this.parameters == null) { + this.parameters = new PointcutParameter[0]; + } + } + + public Pointcut getUnderlyingPointcut() { + return this.pointcut; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.tools.PointcutExpression#setMatchingContext(org.aspectj.weaver.tools.MatchingContext) + */ + public void setMatchingContext(MatchingContext aMatchContext) { + this.matchContext = aMatchContext; + } + + public boolean couldMatchJoinPointsInType(Class aClass) { + ResolvedType matchType = world.resolve(aClass.getName()); + ReflectionFastMatchInfo info = new ReflectionFastMatchInfo(matchType, null, this.matchContext, world); + return pointcut.fastMatch(info).maybeTrue(); + } + + public boolean mayNeedDynamicTest() { + HasPossibleDynamicContentVisitor visitor = new HasPossibleDynamicContentVisitor(); + pointcut.traverse(visitor, null); + return visitor.hasDynamicContent(); + } + + private ExposedState getExposedState() { + return new ExposedState(parameters.length); + } + + // public ShadowMatch matchesMethodExecution(Method aMethod) { + // return matchesExecution(aMethod); + // } + + public ShadowMatch matchesMethodExecution(ResolvedMember aMethod) { + return matchesExecution(aMethod); + } + + public ShadowMatch matchesConstructorExecution(Constructor aConstructor) { + return null; + // return matchesExecution(aConstructor); + } + + // private ShadowMatch matchesExecution(Member aMember) { + // Shadow s = ReflectionShadow.makeExecutionShadow(world, aMember, this.matchContext); + // ShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(aMember); + // sm.setWithinCode(null); + // sm.setWithinType(aMember.getDeclaringClass()); + // return sm; + // } + + private ShadowMatch matchesExecution(ResolvedMember aMember) { + Shadow s = StandardShadow.makeExecutionShadow(world, aMember, this.matchContext); + StandardShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aMember); + sm.setWithinCode(null); + sm.setWithinType((ResolvedType) aMember.getDeclaringType()); + return sm; + } + + // public ShadowMatch matchesStaticInitialization(Class aClass) { + // Shadow s = ReflectionShadow.makeStaticInitializationShadow(world, aClass, this.matchContext); + // StandardShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(null); + // sm.setWithinCode(null); + // sm.setWithinType(aClass); + // return sm; + // } + + public ShadowMatch matchesStaticInitialization(ResolvedType aType) { + Shadow s = StandardShadow.makeStaticInitializationShadow(world, aType, this.matchContext); + StandardShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(null); + sm.setWithinCode(null); + sm.setWithinType(aType); + return sm; + } + + // public ShadowMatch matchesAdviceExecution(Method aMethod) { + // Shadow s = ReflectionShadow.makeAdviceExecutionShadow(world, aMethod, this.matchContext); + // StandardShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(aMethod); + // sm.setWithinCode(null); + // sm.setWithinType(aMethod.getDeclaringClass()); + // return sm; + // } + // + // public ShadowMatch matchesInitialization(Constructor aConstructor) { + // Shadow s = ReflectionShadow.makeInitializationShadow(world, aConstructor, this.matchContext); + // StandardShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(aConstructor); + // sm.setWithinCode(null); + // sm.setWithinType(aConstructor.getDeclaringClass()); + // return sm; + // } + // + // public ShadowMatch matchesPreInitialization(Constructor aConstructor) { + // Shadow s = ReflectionShadow.makePreInitializationShadow(world, aConstructor, this.matchContext); + // StandardShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(aConstructor); + // sm.setWithinCode(null); + // sm.setWithinType(aConstructor.getDeclaringClass()); + // return sm; + // } + // + public ShadowMatch matchesMethodCall(ResolvedMember aMethod, ResolvedMember withinCode) { + Shadow s = StandardShadow.makeCallShadow(world, aMethod, withinCode, this.matchContext); + StandardShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aMethod); + sm.setWithinCode(withinCode); + sm.setWithinType((ResolvedType) withinCode.getDeclaringType()); + return sm; + } + + // + // public ShadowMatch matchesMethodCall(Method aMethod, Class callerType) { + // Shadow s = ReflectionShadow.makeCallShadow(world, aMethod, callerType, this.matchContext); + // ShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(aMethod); + // sm.setWithinCode(null); + // sm.setWithinType(callerType); + // return sm; + // } + // + // public ShadowMatch matchesConstructorCall(Constructor aConstructor, Class callerType) { + // Shadow s = ReflectionShadow.makeCallShadow(world, aConstructor, callerType, this.matchContext); + // ShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(aConstructor); + // sm.setWithinCode(null); + // sm.setWithinType(callerType); + // return sm; + // } + // + // public ShadowMatch matchesConstructorCall(Constructor aConstructor, Member withinCode) { + // Shadow s = ReflectionShadow.makeCallShadow(world, aConstructor, withinCode, this.matchContext); + // ShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(aConstructor); + // sm.setWithinCode(withinCode); + // sm.setWithinType(withinCode.getDeclaringClass()); + // return sm; + // } + // + // public ShadowMatch matchesHandler(Class exceptionType, Class handlingType) { + // Shadow s = ReflectionShadow.makeHandlerShadow(world, exceptionType, handlingType, this.matchContext); + // ShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(null); + // sm.setWithinCode(null); + // sm.setWithinType(handlingType); + // return sm; + // } + // + // public ShadowMatch matchesHandler(Class exceptionType, Member withinCode) { + // Shadow s = ReflectionShadow.makeHandlerShadow(world, exceptionType, withinCode, this.matchContext); + // ShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(null); + // sm.setWithinCode(withinCode); + // sm.setWithinType(withinCode.getDeclaringClass()); + // return sm; + // } + // + // public ShadowMatch matchesFieldGet(Field aField, Class withinType) { + // Shadow s = ReflectionShadow.makeFieldGetShadow(world, aField, withinType, this.matchContext); + // ShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(aField); + // sm.setWithinCode(null); + // sm.setWithinType(withinType); + // return sm; + // } + // + // public ShadowMatch matchesFieldGet(Field aField, Member withinCode) { + // Shadow s = ReflectionShadow.makeFieldGetShadow(world, aField, withinCode, this.matchContext); + // ShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(aField); + // sm.setWithinCode(withinCode); + // sm.setWithinType(withinCode.getDeclaringClass()); + // return sm; + // } + // + // public ShadowMatch matchesFieldSet(Field aField, Class withinType) { + // Shadow s = ReflectionShadow.makeFieldSetShadow(world, aField, withinType, this.matchContext); + // ShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(aField); + // sm.setWithinCode(null); + // sm.setWithinType(withinType); + // return sm; + // } + // + // public ShadowMatch matchesFieldSet(Field aField, Member withinCode) { + // Shadow s = ReflectionShadow.makeFieldSetShadow(world, aField, withinCode, this.matchContext); + // StandardShadowMatchImpl sm = getShadowMatch(s); + // sm.setSubject(aField); + // sm.setWithinCode(withinCode); + // sm.setWithinType(withinCode.getDeclaringClass()); + // return sm; + // } + + private StandardShadowMatchImpl getShadowMatch(Shadow forShadow) { + org.aspectj.util.FuzzyBoolean match = pointcut.match(forShadow); + Test residueTest = Literal.TRUE; + ExposedState state = getExposedState(); + if (match.maybeTrue()) { + residueTest = pointcut.findResidue(forShadow, state); + } + StandardShadowMatchImpl sm = new StandardShadowMatchImpl(match, residueTest, state, parameters); + sm.setMatchingContext(this.matchContext); + return sm; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.tools.PointcutExpression#getPointcutExpression() + */ + public String getPointcutExpression() { + return expression; + } + + private static class HasPossibleDynamicContentVisitor extends AbstractPatternNodeVisitor { + private boolean hasDynamicContent = false; + + public boolean hasDynamicContent() { + return hasDynamicContent; + } + + @Override + public Object visit(WithinAnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(WithinCodeAnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(AnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(ArgsAnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(ArgsPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(CflowPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(IfPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(NotAnnotationTypePattern node, Object data) { + return node.getNegatedPattern().accept(this, data); + } + + @Override + public Object visit(NotPointcut node, Object data) { + return node.getNegatedPointcut().accept(this, data); + } + + @Override + public Object visit(ThisOrTargetAnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + @Override + public Object visit(ThisOrTargetPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + } + + public static class Handler implements Member { + + private Class decClass; + private Class exType; + + public Handler(Class decClass, Class exType) { + this.decClass = decClass; + this.exType = exType; + } + + public int getModifiers() { + return 0; + } + + public Class getDeclaringClass() { + return decClass; + } + + public String getName() { + return null; + } + + public Class getHandledExceptionType() { + return exType; + } + + public boolean isSynthetic() { + return false; + } + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/TypePatternMatcherImpl.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/TypePatternMatcherImpl.java new file mode 100644 index 000000000..8ce4bceea --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/internal/tools/TypePatternMatcherImpl.java @@ -0,0 +1,34 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.internal.tools; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.patterns.TypePattern; +import org.aspectj.weaver.reflect.ReflectionBasedReferenceTypeDelegateFactory; +import org.aspectj.weaver.tools.TypePatternMatcher; + +public class TypePatternMatcherImpl implements TypePatternMatcher { + + private final TypePattern pattern; + private final World world; + + public TypePatternMatcherImpl(TypePattern pattern, World world) { + this.pattern = pattern; + this.world = world; + } + + public boolean matches(Class aClass) { + ResolvedType rt = + ReflectionBasedReferenceTypeDelegateFactory.resolveTypeInWorld(aClass,world); + return pattern.matchesStatically(rt); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AbstractPatternNodeVisitor.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AbstractPatternNodeVisitor.java new file mode 100644 index 000000000..34efee3ee --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AbstractPatternNodeVisitor.java @@ -0,0 +1,253 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import org.aspectj.weaver.patterns.Pointcut.MatchesNothingPointcut; + +/** + * @author colyer + * + */ +public abstract class AbstractPatternNodeVisitor implements PatternNodeVisitor { + + public Object visit(AnyTypePattern node, Object data) { + return node; + } + + public Object visit(NoTypePattern node, Object data) { + return node; + } + + public Object visit(EllipsisTypePattern node, Object data) { + return node; + } + + public Object visit(AnyWithAnnotationTypePattern node, Object data) { + return node; + } + + public Object visit(AnyAnnotationTypePattern node, Object data) { + return node; + } + + public Object visit(EllipsisAnnotationTypePattern node, Object data) { + return node; + } + + public Object visit(AndAnnotationTypePattern node, Object data) { + return node; + } + + public Object visit(AndPointcut node, Object data) { + return node; + } + + public Object visit(AndTypePattern node, Object data) { + return node; + } + + public Object visit(AnnotationPatternList node, Object data) { + return node; + } + + public Object visit(AnnotationPointcut node, Object data) { + return node; + } + + public Object visit(ArgsAnnotationPointcut node, Object data) { + return node; + } + + public Object visit(ArgsPointcut node, Object data) { + return node; + } + + public Object visit(BindingAnnotationTypePattern node, Object data) { + return node; + } + + public Object visit(BindingTypePattern node, Object data) { + return node; + } + + public Object visit(CflowPointcut node, Object data) { + return node; + } + + public Object visit(ConcreteCflowPointcut node, Object data) { + return node; + } + + public Object visit(DeclareAnnotation node, Object data) { + return node; + } + + public Object visit(DeclareErrorOrWarning node, Object data) { + return node; + } + + public Object visit(DeclareParents node, Object data) { + return node; + } + + public Object visit(DeclarePrecedence node, Object data) { + return node; + } + + public Object visit(DeclareSoft node, Object data) { + return node; + } + + public Object visit(ExactAnnotationTypePattern node, Object data) { + return node; + } + + public Object visit(ExactTypePattern node, Object data) { + return node; + } + + public Object visit(HandlerPointcut node, Object data) { + return node; + } + + public Object visit(IfPointcut node, Object data) { + return node; + } + + public Object visit(KindedPointcut node, Object data) { + return node; + } + + public Object visit(ModifiersPattern node, Object data) { + return node; + } + + public Object visit(NamePattern node, Object data) { + return node; + } + + public Object visit(NotAnnotationTypePattern node, Object data) { + return node; + } + + public Object visit(NotPointcut node, Object data) { + return node; + } + + public Object visit(NotTypePattern node, Object data) { + return node; + } + + public Object visit(OrAnnotationTypePattern node, Object data) { + return node; + } + + public Object visit(OrPointcut node, Object data) { + return node; + } + + public Object visit(OrTypePattern node, Object data) { + return node; + } + + public Object visit(PerCflow node, Object data) { + return node; + } + + public Object visit(PerFromSuper node, Object data) { + return node; + } + + public Object visit(PerObject node, Object data) { + return node; + } + + public Object visit(PerSingleton node, Object data) { + return node; + } + + public Object visit(PerTypeWithin node, Object data) { + return node; + } + + public Object visit(PatternNode node, Object data) { + return node; + } + + public Object visit(ReferencePointcut node, Object data) { + return node; + } + + public Object visit(SignaturePattern node, Object data) { + return node; + } + + public Object visit(ThisOrTargetAnnotationPointcut node, Object data) { + return node; + } + + public Object visit(ThisOrTargetPointcut node, Object data) { + return node; + } + + public Object visit(ThrowsPattern node, Object data) { + return node; + } + + public Object visit(TypePatternList node, Object data) { + return node; + } + + public Object visit(WildAnnotationTypePattern node, Object data) { + return node; + } + + public Object visit(WildTypePattern node, Object data) { + return node; + } + + public Object visit(WithinAnnotationPointcut node, Object data) { + return node; + } + + public Object visit(WithinCodeAnnotationPointcut node, Object data) { + return node; + } + + public Object visit(WithinPointcut node, Object data) { + return node; + } + + public Object visit(WithincodePointcut node, Object data) { + return node; + } + + public Object visit(MatchesNothingPointcut node, Object data) { + return node; + } + + public Object visit(TypeVariablePattern node, Object data) { + return node; + } + + public Object visit(TypeVariablePatternList node, Object data) { + return node; + } + + public Object visit(HasMemberTypePattern node, Object data) { + return node; + } + + public Object visit(TypeCategoryTypePattern node, Object data) { + return node; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AbstractSignaturePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AbstractSignaturePattern.java new file mode 100644 index 000000000..cb87ee431 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AbstractSignaturePattern.java @@ -0,0 +1,79 @@ +/* ******************************************************************* + * Copyright (c) 2010 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 - SpringSource + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; + +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.VersionedDataInputStream; + +/** + * Implements common functions to be used across ISignaturePatterns. + * + * @author Andy Clement + * @since 1.6.9 + */ +public abstract class AbstractSignaturePattern implements ISignaturePattern { + + protected void writePlaceholderLocation(CompressingDataOutputStream s) throws IOException { + s.writeInt(0); + s.writeInt(0); + } + + public static ISignaturePattern readCompoundSignaturePattern(VersionedDataInputStream s, ISourceContext context) + throws IOException { + byte key = s.readByte(); + switch (key) { + case PATTERN: + return SignaturePattern.read(s, context); + case AND: + return AndSignaturePattern.readAndSignaturePattern(s, context); + case OR: + return OrSignaturePattern.readOrSignaturePattern(s, context); + case NOT: + return NotSignaturePattern.readNotSignaturePattern(s, context); + default: + throw new BCException("unknown SignatureTypePattern kind: " + key); + } + } + + public static void writeCompoundSignaturePattern(CompressingDataOutputStream s, ISignaturePattern sigPattern) + throws IOException { + if (sigPattern instanceof SignaturePattern) { + s.writeByte(PATTERN); + ((SignaturePattern) sigPattern).write(s); + } else if (sigPattern instanceof AndSignaturePattern) { + AndSignaturePattern andSignaturePattern = (AndSignaturePattern) sigPattern; + s.writeByte(AND); + writeCompoundSignaturePattern(s, andSignaturePattern.getLeft()); + writeCompoundSignaturePattern(s, andSignaturePattern.getRight()); + s.writeInt(0); + s.writeInt(0); // TODO positions not yet set properly + } else if (sigPattern instanceof OrSignaturePattern) { + OrSignaturePattern orSignaturePattern = (OrSignaturePattern) sigPattern; + s.writeByte(OR); + writeCompoundSignaturePattern(s, orSignaturePattern.getLeft()); + writeCompoundSignaturePattern(s, orSignaturePattern.getRight()); + s.writeInt(0); + s.writeInt(0); // TODO positions not yet set properly + } else { + // negated + NotSignaturePattern notSignaturePattern = (NotSignaturePattern) sigPattern; + s.writeByte(NOT); + writeCompoundSignaturePattern(s, notSignaturePattern.getNegated()); + s.writeInt(0); + s.writeInt(0); // TODO positions not yet set properly + } + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AndAnnotationTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AndAnnotationTypePattern.java new file mode 100644 index 000000000..c14f36025 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AndAnnotationTypePattern.java @@ -0,0 +1,142 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; + +/** + * @author colyer + * + * TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style - Code + * Templates + */ +public class AndAnnotationTypePattern extends AnnotationTypePattern { + + private AnnotationTypePattern left; + private AnnotationTypePattern right; + + public AndAnnotationTypePattern(AnnotationTypePattern left, AnnotationTypePattern right) { + this.left = left; + this.right = right; + setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); + } + + public FuzzyBoolean matches(AnnotatedElement annotated) { + return left.matches(annotated).and(right.matches(annotated)); + } + + public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) { + return left.matches(annotated, parameterAnnotations).and(right.matches(annotated, parameterAnnotations)); + } + + public void resolve(World world) { + left.resolve(world); + right.resolve(world); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolveBindings(org.aspectj.weaver.patterns.IScope, + * org.aspectj.weaver.patterns.Bindings, boolean) + */ + public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) { + left = left.resolveBindings(scope, bindings, allowBinding); + right = right.resolveBindings(scope, bindings, allowBinding); + return this; + } + + public AnnotationTypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + AnnotationTypePattern newLeft = left.parameterizeWith(typeVariableMap, w); + AnnotationTypePattern newRight = right.parameterizeWith(typeVariableMap, w); + AndAnnotationTypePattern ret = new AndAnnotationTypePattern(newLeft, newRight); + ret.copyLocationFrom(this); + if (this.isForParameterAnnotationMatch()) { + ret.setForParameterAnnotationMatch(); + } + return ret; + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern p = new AndAnnotationTypePattern(AnnotationTypePattern.read(s, context), AnnotationTypePattern.read( + s, context)); + p.readLocation(context, s); + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160) { + if (s.readBoolean()) { + p.setForParameterAnnotationMatch(); + } + } + return p; + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.AND); + left.write(s); + right.write(s); + writeLocation(s); + s.writeBoolean(isForParameterAnnotationMatch()); + } + + public boolean equals(Object obj) { + if (!(obj instanceof AndAnnotationTypePattern)) { + return false; + } + AndAnnotationTypePattern other = (AndAnnotationTypePattern) obj; + return (left.equals(other.left) && right.equals(other.right) && left.isForParameterAnnotationMatch() == right + .isForParameterAnnotationMatch()); + } + + public int hashCode() { + int result = 17; + result = result * 37 + left.hashCode(); + result = result * 37 + right.hashCode(); + result = result * 37 + (isForParameterAnnotationMatch() ? 0 : 1); + return result; + } + + public String toString() { + return left.toString() + " " + right.toString(); + } + + public AnnotationTypePattern getLeft() { + return left; + } + + public AnnotationTypePattern getRight() { + return right; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor, data); + left.traverse(visitor, ret); + right.traverse(visitor, ret); + return ret; + } + + public void setForParameterAnnotationMatch() { + left.setForParameterAnnotationMatch(); + right.setForParameterAnnotationMatch(); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AndPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AndPointcut.java new file mode 100644 index 000000000..20fc74734 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AndPointcut.java @@ -0,0 +1,138 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; + +public class AndPointcut extends Pointcut { + Pointcut left, right; // exposed for testing + + private int couldMatchKinds; + + public AndPointcut(Pointcut left, Pointcut right) { + super(); + this.left = left; + this.right = right; + this.pointcutKind = AND; + setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); + couldMatchKinds = left.couldMatchKinds() & right.couldMatchKinds(); + } + + public int couldMatchKinds() { + return couldMatchKinds; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + FuzzyBoolean leftMatch = left.fastMatch(type); + if (leftMatch.alwaysFalse()) { + return leftMatch; + } + return leftMatch.and(right.fastMatch(type)); + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + FuzzyBoolean leftMatch = left.match(shadow); + if (leftMatch.alwaysFalse()) { + return leftMatch; + } + return leftMatch.and(right.match(shadow)); + } + + public String toString() { + return "(" + left.toString() + " && " + right.toString() + ")"; + } + + public boolean equals(Object other) { + if (!(other instanceof AndPointcut)) { + return false; + } + AndPointcut o = (AndPointcut) other; + return o.left.equals(left) && o.right.equals(right); + } + + public int hashCode() { + int result = 19; + result = 37 * result + left.hashCode(); + result = 37 * result + right.hashCode(); + return result; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + left.resolveBindings(scope, bindings); + right.resolveBindings(scope, bindings); + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.AND); + left.write(s); + right.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AndPointcut ret = new AndPointcut(Pointcut.read(s, context), Pointcut.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return Test.makeAnd(left.findResidue(shadow, state), right.findResidue(shadow, state)); + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + AndPointcut ret = new AndPointcut(left.concretize(inAspect, declaringType, bindings), right.concretize(inAspect, + declaringType, bindings)); + ret.copyLocationFrom(this); + ret.m_ignoreUnboundBindingForNames = m_ignoreUnboundBindingForNames; + return ret; + } + + @Override + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + AndPointcut ret = new AndPointcut(left.parameterizeWith(typeVariableMap, w), right.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + ret.m_ignoreUnboundBindingForNames = m_ignoreUnboundBindingForNames; + return ret; + } + + public Pointcut getLeft() { + return left; + } + + public Pointcut getRight() { + return right; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor, data); + left.traverse(visitor, ret); + right.traverse(visitor, ret); + return ret; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AndSignaturePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AndSignaturePattern.java new file mode 100644 index 000000000..c05995377 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AndSignaturePattern.java @@ -0,0 +1,104 @@ +/* ******************************************************************* + * Copyright (c) 2010 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 - SpringSource + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * Represents the AND of two other signature patterns. + * + * @author Andy Clement + * @since 1.6.9 + */ +public class AndSignaturePattern extends AbstractSignaturePattern { + + private ISignaturePattern leftSp; + private ISignaturePattern rightSp; + private List<ExactTypePattern> exactDeclaringTypes; + + public AndSignaturePattern(ISignaturePattern leftSp, ISignaturePattern rightSp) { + this.leftSp = leftSp; + this.rightSp = rightSp; + } + + public boolean couldEverMatch(ResolvedType type) { + return leftSp.couldEverMatch(type) || rightSp.couldEverMatch(type); + } + + public List<ExactTypePattern> getExactDeclaringTypes() { + if (exactDeclaringTypes == null) { + exactDeclaringTypes = new ArrayList<ExactTypePattern>(); + exactDeclaringTypes.addAll(leftSp.getExactDeclaringTypes()); + exactDeclaringTypes.addAll(rightSp.getExactDeclaringTypes()); + } + return exactDeclaringTypes; + } + + public boolean isMatchOnAnyName() { + return leftSp.isMatchOnAnyName() || rightSp.isMatchOnAnyName(); + } + + public boolean isStarAnnotation() { + return leftSp.isStarAnnotation() || rightSp.isStarAnnotation(); + } + + public boolean matches(Member member, World world, boolean b) { + return (leftSp.matches(member, world, b)) && (rightSp.matches(member, world, b)); + } + + public ISignaturePattern parameterizeWith(Map<String, UnresolvedType> typeVariableBindingMap, World world) { + return new AndSignaturePattern(leftSp.parameterizeWith(typeVariableBindingMap, world), rightSp.parameterizeWith( + typeVariableBindingMap, world)); + } + + public ISignaturePattern resolveBindings(IScope scope, Bindings bindings) { + // Whilst the real SignaturePattern returns 'this' we are safe to return 'this' here rather than build a new + // AndSignaturePattern + leftSp.resolveBindings(scope, bindings); + rightSp.resolveBindings(scope, bindings); + return this; + } + + public static ISignaturePattern readAndSignaturePattern(VersionedDataInputStream s, ISourceContext context) throws IOException { + AndSignaturePattern ret = new AndSignaturePattern(readCompoundSignaturePattern(s, context), readCompoundSignaturePattern(s, + context)); + s.readInt(); + s.readInt(); + // ret.readLocation(context, s); // TODO output position currently useless so dont need to do this + return ret; + } + + public ISignaturePattern getLeft() { + return leftSp; + } + + public ISignaturePattern getRight() { + return rightSp; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(leftSp.toString()).append(" && ").append(rightSp.toString()); + return sb.toString(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AndTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AndTypePattern.java new file mode 100644 index 000000000..781a5881a --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AndTypePattern.java @@ -0,0 +1,198 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * left && right + * + * <p> + * any binding to formals is explicitly forbidden for any composite by the language + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public class AndTypePattern extends TypePattern { + private TypePattern left, right; + + public AndTypePattern(TypePattern left, TypePattern right) { + super(false, false); // ?? we override all methods that care about includeSubtypes + this.left = left; + this.right = right; + setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); + } + + @Override + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return true; // don't dive into ands yet.... + } + + @Override + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + return left.matchesInstanceof(type).and(right.matchesInstanceof(type)); + } + + @Override + protected boolean matchesExactly(ResolvedType type) { + // ??? if these had side-effects, this sort-circuit could be a mistake + return left.matchesExactly(type) && right.matchesExactly(type); + } + + @Override + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + return left.matchesExactly(type, annotatedType) && right.matchesExactly(type, annotatedType); + } + + @Override + public boolean matchesStatically(ResolvedType type) { + return left.matchesStatically(type) && right.matchesStatically(type); + } + + @Override + public void setIsVarArgs(boolean isVarArgs) { + this.isVarArgs = isVarArgs; + left.setIsVarArgs(isVarArgs); + right.setIsVarArgs(isVarArgs); + } + + @Override + public void setAnnotationTypePattern(AnnotationTypePattern annPatt) { + if (annPatt == AnnotationTypePattern.ANY) { + return; + } + if (left.annotationPattern == AnnotationTypePattern.ANY) { + left.setAnnotationTypePattern(annPatt); + } else { + left.setAnnotationTypePattern(new AndAnnotationTypePattern(left.annotationPattern, annPatt)); + } + if (right.annotationPattern == AnnotationTypePattern.ANY) { + right.setAnnotationTypePattern(annPatt); + } else { + right.setAnnotationTypePattern(new AndAnnotationTypePattern(right.annotationPattern, annPatt)); + } + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(TypePattern.AND); + left.write(s); + right.write(s); + writeLocation(s); + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AndTypePattern ret = new AndTypePattern(TypePattern.read(s, context), TypePattern.read(s, context)); + ret.readLocation(context, s); + if (ret.left.isVarArgs && ret.right.isVarArgs) { + ret.isVarArgs = true; + } + return ret; + } + + @Override + public TypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding, boolean requireExactType) { + if (requireExactType) { + return notExactType(scope); + } + left = left.resolveBindings(scope, bindings, false, false); + right = right.resolveBindings(scope, bindings, false, false); + return this; + } + + @Override + public TypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + TypePattern newLeft = left.parameterizeWith(typeVariableMap, w); + TypePattern newRight = right.parameterizeWith(typeVariableMap, w); + AndTypePattern ret = new AndTypePattern(newLeft, newRight); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public String toString() { + StringBuffer buff = new StringBuffer(); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append('('); + buff.append(annotationPattern.toString()); + buff.append(' '); + } + buff.append('('); + buff.append(left.toString()); + buff.append(" && "); + buff.append(right.toString()); + buff.append(')'); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append(')'); + } + return buff.toString(); + } + + public TypePattern getLeft() { + return left; + } + + public TypePattern getRight() { + return right; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AndTypePattern)) { + return false; + } + AndTypePattern atp = (AndTypePattern) obj; + return left.equals(atp.left) && right.equals(atp.right); + } + + @Override + public boolean isStarAnnotation() { + return left.isStarAnnotation() && right.isStarAnnotation(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int ret = 17; + ret = ret + 37 * left.hashCode(); + ret = ret + 37 * right.hashCode(); + return ret; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor, data); + left.traverse(visitor, ret); + right.traverse(visitor, ret); + return ret; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnnotationPatternList.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnnotationPatternList.java new file mode 100644 index 000000000..0b93b48d1 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnnotationPatternList.java @@ -0,0 +1,215 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * @author Adrian Colyer + */ +public class AnnotationPatternList extends PatternNode { + + private AnnotationTypePattern[] typePatterns; + int ellipsisCount = 0; + + public static final AnnotationPatternList EMPTY = new AnnotationPatternList(new AnnotationTypePattern[] {}); + + public static final AnnotationPatternList ANY = new AnnotationPatternList( + new AnnotationTypePattern[] { AnnotationTypePattern.ELLIPSIS }); + + public AnnotationPatternList() { + typePatterns = new AnnotationTypePattern[0]; + ellipsisCount = 0; + } + + public AnnotationPatternList(AnnotationTypePattern[] arguments) { + this.typePatterns = arguments; + for (int i = 0; i < arguments.length; i++) { + if (arguments[i] == AnnotationTypePattern.ELLIPSIS) { + ellipsisCount++; + } + } + } + + public AnnotationPatternList(List<AnnotationTypePattern> l) { + this((AnnotationTypePattern[]) l.toArray(new AnnotationTypePattern[l.size()])); + } + + protected AnnotationTypePattern[] getAnnotationPatterns() { + return typePatterns; + } + + public AnnotationPatternList parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + AnnotationTypePattern[] parameterizedPatterns = new AnnotationTypePattern[this.typePatterns.length]; + for (int i = 0; i < parameterizedPatterns.length; i++) { + parameterizedPatterns[i] = this.typePatterns[i].parameterizeWith(typeVariableMap, w); + } + AnnotationPatternList ret = new AnnotationPatternList(parameterizedPatterns); + ret.copyLocationFrom(this); + return ret; + } + + public void resolve(World inWorld) { + for (int i = 0; i < typePatterns.length; i++) { + typePatterns[i].resolve(inWorld); + } + } + + public FuzzyBoolean matches(ResolvedType[] someArgs) { + // do some quick length tests first + int numArgsMatchedByEllipsis = (someArgs.length + ellipsisCount) - typePatterns.length; + if (numArgsMatchedByEllipsis < 0) { + return FuzzyBoolean.NO; + } + if ((numArgsMatchedByEllipsis > 0) && (ellipsisCount == 0)) { + return FuzzyBoolean.NO; + } + // now work through the args and the patterns, skipping at ellipsis + FuzzyBoolean ret = FuzzyBoolean.YES; + int argsIndex = 0; + for (int i = 0; i < typePatterns.length; i++) { + if (typePatterns[i] == AnnotationTypePattern.ELLIPSIS) { + // match ellipsisMatchCount args + argsIndex += numArgsMatchedByEllipsis; + } else if (typePatterns[i] == AnnotationTypePattern.ANY) { + argsIndex++; + } else { + // match the argument type at argsIndex with the ExactAnnotationTypePattern + // we know it is exact because nothing else is allowed in args + if (someArgs[argsIndex].isPrimitiveType()) { + return FuzzyBoolean.NO; // can never match + } + ExactAnnotationTypePattern ap = (ExactAnnotationTypePattern) typePatterns[i]; + FuzzyBoolean matches = ap.matchesRuntimeType(someArgs[argsIndex]); + if (matches == FuzzyBoolean.NO) { + return FuzzyBoolean.MAYBE; // could still match at runtime + } else { + argsIndex++; + ret = ret.and(matches); + } + } + } + return ret; + } + + public int size() { + return typePatterns.length; + } + + public AnnotationTypePattern get(int index) { + return typePatterns[index]; + } + + public AnnotationPatternList resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) { + for (int i = 0; i < typePatterns.length; i++) { + AnnotationTypePattern p = typePatterns[i]; + if (p != null) { + typePatterns[i] = typePatterns[i].resolveBindings(scope, bindings, allowBinding); + } + } + return this; + } + + public AnnotationPatternList resolveReferences(IntMap bindings) { + int len = typePatterns.length; + AnnotationTypePattern[] ret = new AnnotationTypePattern[len]; + for (int i = 0; i < len; i++) { + ret[i] = typePatterns[i].remapAdviceFormals(bindings); + } + return new AnnotationPatternList(ret); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("("); + for (int i = 0, len = typePatterns.length; i < len; i++) { + AnnotationTypePattern type = typePatterns[i]; + if (i > 0) { + buf.append(", "); + } + if (type == AnnotationTypePattern.ELLIPSIS) { + buf.append(".."); + } else { + String annPatt = type.toString(); + buf.append(annPatt.startsWith("@") ? annPatt.substring(1) : annPatt); + } + } + buf.append(")"); + return buf.toString(); + } + + public boolean equals(Object other) { + if (!(other instanceof AnnotationPatternList)) { + return false; + } + AnnotationPatternList o = (AnnotationPatternList) other; + int len = o.typePatterns.length; + if (len != this.typePatterns.length) { + return false; + } + for (int i = 0; i < len; i++) { + if (!this.typePatterns[i].equals(o.typePatterns[i])) { + return false; + } + } + return true; + } + + public int hashCode() { + int result = 41; + for (int i = 0, len = typePatterns.length; i < len; i++) { + result = 37 * result + typePatterns[i].hashCode(); + } + return result; + } + + public static AnnotationPatternList read(VersionedDataInputStream s, ISourceContext context) throws IOException { + short len = s.readShort(); + AnnotationTypePattern[] arguments = new AnnotationTypePattern[len]; + for (int i = 0; i < len; i++) { + arguments[i] = AnnotationTypePattern.read(s, context); + } + AnnotationPatternList ret = new AnnotationPatternList(arguments); + ret.readLocation(context, s); + return ret; + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeShort(typePatterns.length); + for (int i = 0; i < typePatterns.length; i++) { + typePatterns[i].write(s); + } + writeLocation(s); + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor, data); + for (int i = 0; i < typePatterns.length; i++) { + typePatterns[i].traverse(visitor, ret); + } + return ret; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnnotationPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnnotationPointcut.java new file mode 100644 index 000000000..e829d3772 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnnotationPointcut.java @@ -0,0 +1,339 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ConcreteTypeMunger; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.NewFieldTypeMunger; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; + +/** + * (at)Annotation((at)Foo) or (at)Annotation(foo)<br> + * <p> + * Matches any join point where the subject of the join point has an annotation matching the annotationTypePattern: + * + * <br> + * Join Point Kind - Subject <br> + * ================================ <br> + * method call - the target method <br> + * method execution - the method <br> + * constructor call - the constructor <br> + * constructor execution - the constructor <br> + * get - the target field <br> + * set - the target field <br> + * adviceexecution - the advice <br> + * initialization - the constructor <br> + * preinitialization - the constructor <br> + * staticinitialization - the type being initialized <br> + * handler - the declared type of the handled exception <br> + */ +public class AnnotationPointcut extends NameBindingPointcut { + + private ExactAnnotationTypePattern annotationTypePattern; + private String declarationText; + + public AnnotationPointcut(ExactAnnotationTypePattern type) { + super(); + this.annotationTypePattern = type; + this.pointcutKind = Pointcut.ANNOTATION; + buildDeclarationText(); + } + + public AnnotationPointcut(ExactAnnotationTypePattern type, ShadowMunger munger) { + this(type); + buildDeclarationText(); + } + + public ExactAnnotationTypePattern getAnnotationTypePattern() { + return annotationTypePattern; + } + + @Override + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + @Override + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + AnnotationPointcut ret = new AnnotationPointcut((ExactAnnotationTypePattern) annotationTypePattern.parameterizeWith( + typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) + */ + @Override + public FuzzyBoolean fastMatch(FastMatchInfo info) { + if (info.getKind() == Shadow.StaticInitialization) { + return annotationTypePattern.fastMatches(info.getType()); + } else { + return FuzzyBoolean.MAYBE; + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) + */ + @Override + protected FuzzyBoolean matchInternal(Shadow shadow) { + AnnotatedElement toMatchAgainst = null; + Member member = shadow.getSignature(); + ResolvedMember rMember = member.resolve(shadow.getIWorld()); + + if (rMember == null) { + if (member.getName().startsWith(NameMangler.PREFIX)) { + return FuzzyBoolean.NO; + } + shadow.getIWorld().getLint().unresolvableMember.signal(member.toString(), getSourceLocation()); + return FuzzyBoolean.NO; + } + + Shadow.Kind kind = shadow.getKind(); + if (kind == Shadow.StaticInitialization) { + toMatchAgainst = rMember.getDeclaringType().resolve(shadow.getIWorld()); + } else if ((kind == Shadow.ExceptionHandler)) { + toMatchAgainst = rMember.getParameterTypes()[0].resolve(shadow.getIWorld()); + } else { + toMatchAgainst = rMember; + // FIXME asc I'd like to get rid of this bit of logic altogether, shame ITD fields don't have an effective sig attribute + // FIXME asc perf cache the result of discovering the member that contains the real annotations + if (rMember.isAnnotatedElsewhere()) { + if (kind == Shadow.FieldGet || kind == Shadow.FieldSet) { + // FIXME asc should include supers with getInterTypeMungersIncludingSupers ? + List mungers = rMember.getDeclaringType().resolve(shadow.getIWorld()).getInterTypeMungers(); + for (Iterator iter = mungers.iterator(); iter.hasNext();) { + ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) iter.next(); + if (typeMunger.getMunger() instanceof NewFieldTypeMunger) { + ResolvedMember fakerm = typeMunger.getSignature(); + if (fakerm.equals(member)) { + ResolvedMember ajcMethod = AjcMemberMaker.interFieldInitializer(fakerm, typeMunger.getAspectType()); + ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod); + toMatchAgainst = rmm; + } + } + } + } + } + } + + annotationTypePattern.resolve(shadow.getIWorld()); + return annotationTypePattern.matches(toMatchAgainst); + } + + private ResolvedMember findMethod(ResolvedType aspectType, ResolvedMember ajcMethod) { + ResolvedMember decMethods[] = aspectType.getDeclaredMethods(); + for (int i = 0; i < decMethods.length; i++) { + ResolvedMember member = decMethods[i]; + if (member.equals(ajcMethod)) { + return member; + } + } + return null; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(org.aspectj.weaver.patterns.IScope, + * org.aspectj.weaver.patterns.Bindings) + */ + @Override + protected void resolveBindings(IScope scope, Bindings bindings) { + if (!scope.getWorld().isInJava5Mode()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ATANNOTATION_ONLY_SUPPORTED_AT_JAVA5_LEVEL), + getSourceLocation())); + return; + } + annotationTypePattern = (ExactAnnotationTypePattern) annotationTypePattern.resolveBindings(scope, bindings, true); + // must be either a Var, or an annotation type pattern + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#concretize1(org.aspectj.weaver.ResolvedType, org.aspectj.weaver.IntMap) + */ + @Override + protected Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + ExactAnnotationTypePattern newType = (ExactAnnotationTypePattern) annotationTypePattern.remapAdviceFormals(bindings); + Pointcut ret = new AnnotationPointcut(newType, bindings.getEnclosingAdvice()); + ret.copyLocationFrom(this); + return ret; + } + + @Override + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + if (annotationTypePattern instanceof BindingAnnotationFieldTypePattern) { + if (shadow.getKind() != Shadow.MethodExecution) { + shadow.getIWorld() + .getMessageHandler() + .handleMessage( + MessageUtil + .error("Annotation field binding is only supported at method-execution join points (compiler limitation)", + getSourceLocation())); + return Literal.TRUE; // exit quickly, error will prevent weaving + } + BindingAnnotationFieldTypePattern btp = (BindingAnnotationFieldTypePattern) annotationTypePattern; + ResolvedType formalType = btp.getFormalType().resolve(shadow.getIWorld()); + UnresolvedType annoType = btp.getAnnotationType(); + // TODO 2 need to sort out appropriate creation of the AnnotationAccessFieldVar - what happens for + // reflective (ReflectionShadow) access to types? + Var var = shadow.getKindedAnnotationVar(annoType); + if (var == null) { + throw new BCException("Unexpected problem locating annotation at join point '" + shadow + "'"); + } + state.set(btp.getFormalIndex(), var.getAccessorForValue(formalType, btp.formalName)); + } else if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern) annotationTypePattern; + UnresolvedType annotationType = btp.getAnnotationType(); + Var var = shadow.getKindedAnnotationVar(annotationType); + + // At this point, var *could* be null. The only reason this could happen (if we aren't failing...) + // is if another binding annotation designator elsewhere in the pointcut is going to expose the annotation + // eg. (execution(* a*(..)) && @annotation(foo)) || (execution(* b*(..)) && @this(foo)) + // where sometimes @annotation will be providing the value, and sometimes + // @this will be providing the value (see pr138223) + + // If we are here for other indecipherable reasons (it's not the case above...) then + // you might want to uncomment this next bit of code to collect the diagnostics + // if (var == null) throw new BCException("Impossible! annotation=["+annotationType+ + // "] shadow=["+shadow+" at "+shadow.getSourceLocation()+ + // "] pointcut is at ["+getSourceLocation()+"]"); + if (var == null) { + if (matchInternal(shadow).alwaysTrue()) { + return Literal.TRUE; + } else { + return Literal.FALSE; + } + } + state.set(btp.getFormalIndex(), var); + } + + if (matchInternal(shadow).alwaysTrue()) { + return Literal.TRUE; + } else { + return Literal.FALSE; + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + @Override + public List<BindingPattern> getBindingAnnotationTypePatterns() { + if (annotationTypePattern instanceof BindingPattern) { // BindingAnnotationTypePattern) { + List<BindingPattern> l = new ArrayList<BindingPattern>(); + l.add((BindingPattern)annotationTypePattern); + return l; + } else { + return Collections.emptyList(); + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + @Override + public List<BindingTypePattern> getBindingTypePatterns() { + return Collections.emptyList(); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.ANNOTATION); + annotationTypePattern.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern type = AnnotationTypePattern.read(s, context); + AnnotationPointcut ret = new AnnotationPointcut((ExactAnnotationTypePattern) type); + ret.readLocation(context, s); + return ret; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof AnnotationPointcut)) { + return false; + } + AnnotationPointcut o = (AnnotationPointcut) other; + return o.annotationTypePattern.equals(this.annotationTypePattern); + } + + @Override + public int hashCode() { + int result = 17; + result = 37 * result + annotationTypePattern.hashCode(); + return result; + } + + public void buildDeclarationText() { + StringBuffer buf = new StringBuffer(); + buf.append("@annotation("); + String annPatt = annotationTypePattern.toString(); + buf.append(annPatt.startsWith("@") ? annPatt.substring(1) : annPatt); + buf.append(")"); + this.declarationText = buf.toString(); + } + + @Override + public String toString() { + return this.declarationText; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnnotationTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnnotationTypePattern.java new file mode 100644 index 000000000..82fb33854 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnnotationTypePattern.java @@ -0,0 +1,158 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public abstract class AnnotationTypePattern extends PatternNode { + + public static final AnnotationTypePattern ANY = new AnyAnnotationTypePattern(); + public static final AnnotationTypePattern ELLIPSIS = new EllipsisAnnotationTypePattern(); + public static final AnnotationTypePattern[] NONE = new AnnotationTypePattern[0]; + private boolean isForParameterAnnotationMatch; + + /** + * TODO: write, read, equals & hashCode both in annotation hierarchy and in altered TypePattern hierarchy + */ + protected AnnotationTypePattern() { + super(); + } + + public abstract FuzzyBoolean matches(AnnotatedElement annotated); + + public abstract FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations); + + public FuzzyBoolean fastMatches(AnnotatedElement annotated) { + return FuzzyBoolean.MAYBE; + } + + public AnnotationTypePattern remapAdviceFormals(IntMap bindings) { + return this; + } + + public abstract void resolve(World world); + + public abstract AnnotationTypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w); + + public boolean isAny() { + return false; + } + + /** + * This can modify in place, or return a new TypePattern if the type changes. + */ + public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) { + return this; + } + + public static final byte EXACT = 1; + public static final byte BINDING = 2; + public static final byte NOT = 3; + public static final byte OR = 4; + public static final byte AND = 5; + public static final byte ELLIPSIS_KEY = 6; + public static final byte ANY_KEY = 7; + public static final byte WILD = 8; + public static final byte EXACTFIELD = 9; + public static final byte BINDINGFIELD = 10; + public static final byte BINDINGFIELD2 = 11; + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte key = s.readByte(); + switch (key) { + case EXACT: + return ExactAnnotationTypePattern.read(s, context); + case BINDING: + return BindingAnnotationTypePattern.read(s, context); + case NOT: + return NotAnnotationTypePattern.read(s, context); + case OR: + return OrAnnotationTypePattern.read(s, context); + case AND: + return AndAnnotationTypePattern.read(s, context); + case WILD: + return WildAnnotationTypePattern.read(s, context); + case EXACTFIELD: + return ExactAnnotationFieldTypePattern.read(s, context); + case BINDINGFIELD: + return BindingAnnotationFieldTypePattern.read(s, context); + case BINDINGFIELD2: + return BindingAnnotationFieldTypePattern.read2(s, context); + case ELLIPSIS_KEY: + return ELLIPSIS; + case ANY_KEY: + return ANY; + } + throw new BCException("unknown TypePattern kind: " + key); + } + + public void setForParameterAnnotationMatch() { + isForParameterAnnotationMatch = true; + } + + public boolean isForParameterAnnotationMatch() { + return isForParameterAnnotationMatch; + } + +} + +class EllipsisAnnotationTypePattern extends AnnotationTypePattern { + + @Override + public FuzzyBoolean matches(AnnotatedElement annotated) { + return FuzzyBoolean.NO; + } + + @Override + public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) { + return FuzzyBoolean.NO; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.ELLIPSIS_KEY); + } + + @Override + public void resolve(World world) { + } + + @Override + public String toString() { + return ".."; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public AnnotationTypePattern parameterizeWith(Map arg0, World w) { + return this; + } + + @Override + public void setForParameterAnnotationMatch() { + } + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnyAnnotationTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnyAnnotationTypePattern.java new file mode 100644 index 000000000..769424311 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnyAnnotationTypePattern.java @@ -0,0 +1,74 @@ +/* ******************************************************************* + * 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 - extracted from AnnotationTypePattern + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +public class AnyAnnotationTypePattern extends AnnotationTypePattern { + + @Override + public FuzzyBoolean fastMatches(AnnotatedElement annotated) { + return FuzzyBoolean.YES; + } + + @Override + public FuzzyBoolean matches(AnnotatedElement annotated) { + return FuzzyBoolean.YES; + } + + @Override + public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) { + return FuzzyBoolean.YES; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.ANY_KEY); + } + + @Override + public void resolve(World world) { + } + + @Override + public String toString() { + return "@ANY"; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public boolean isAny() { + return true; + } + + @Override + public AnnotationTypePattern parameterizeWith(Map<String,UnresolvedType> arg0, World w) { + return this; + } + + @Override + public void setForParameterAnnotationMatch() { + + } +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnyTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnyTypePattern.java new file mode 100644 index 000000000..db9ba5130 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnyTypePattern.java @@ -0,0 +1,113 @@ +/* ******************************************************************* + * Copyright (c) 2002, 2010 Palo Alto Research Center, Incorporated (PARC) and others. + * 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: + * PARC initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +public class AnyTypePattern extends TypePattern { + + /** + * Constructor for EllipsisTypePattern. + * + * @param includeSubtypes + */ + public AnyTypePattern() { + super(false, false, new TypePatternList()); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern) + */ + @Override + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return true; + } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesExactly(IType) + */ + @Override + protected boolean matchesExactly(ResolvedType type) { + return true; + } + + @Override + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + return true; + } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesInstanceof(IType) + */ + @Override + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + return FuzzyBoolean.YES; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(ANY_KEY); + } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matches(IType, MatchKind) + */ + // public FuzzyBoolean matches(IType type, MatchKind kind) { + // return FuzzyBoolean.YES; + // } + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesSubtypes(IType) + */ + @Override + protected boolean matchesSubtypes(ResolvedType type) { + return true; + } + + @Override + public boolean isStar() { + return true; + } + + @Override + public String toString() { + return "*"; + } + + @Override + public boolean equals(Object obj) { + return (obj instanceof AnyTypePattern); + } + + @Override + public int hashCode() { + return 37; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public TypePattern parameterizeWith(Map<String,UnresolvedType> arg0, World w) { + return this; + } +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnyWithAnnotationTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnyWithAnnotationTypePattern.java new file mode 100644 index 000000000..8306ff6d0 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/AnyWithAnnotationTypePattern.java @@ -0,0 +1,143 @@ +/* ******************************************************************* + * Copyright (c) 2002, 2010 Palo Alto Research Center, Incorporated (PARC) and others. + * 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: + * PARC initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + + +/** + * This type represents a type pattern of '*' but with an annotation specified, e.g. '@Color *' + */ +public class AnyWithAnnotationTypePattern extends TypePattern { + + public AnyWithAnnotationTypePattern(AnnotationTypePattern atp) { + super(false, false); + annotationPattern = atp; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return true; + } + + @Override + protected boolean matchesExactly(ResolvedType type) { + annotationPattern.resolve(type.getWorld()); + boolean b = false; + if (type.temporaryAnnotationTypes != null) { + b = annotationPattern.matches(type, type.temporaryAnnotationTypes).alwaysTrue(); + } else { + b = annotationPattern.matches(type).alwaysTrue(); + } + return b; + } + + @Override + public TypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding, boolean requireExactType) { + if (requireExactType) { + scope.getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.WILDCARD_NOT_ALLOWED), getSourceLocation())); + return NO; + } + return super.resolveBindings(scope, bindings, allowBinding, requireExactType); + } + + @Override + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + annotationPattern.resolve(type.getWorld()); + return annotationPattern.matches(annotatedType).alwaysTrue(); + } + + @Override + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + if (Modifier.isFinal(type.getModifiers())) { + return FuzzyBoolean.fromBoolean(matchesExactly(type)); + } + return FuzzyBoolean.MAYBE; + } + + @Override + public TypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + AnyWithAnnotationTypePattern ret = new AnyWithAnnotationTypePattern(this.annotationPattern.parameterizeWith( + typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(TypePattern.ANY_WITH_ANNO); + annotationPattern.write(s); + writeLocation(s); + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext c) throws IOException { + AnnotationTypePattern annPatt = AnnotationTypePattern.read(s, c); + AnyWithAnnotationTypePattern ret = new AnyWithAnnotationTypePattern(annPatt); + ret.readLocation(c, s); + return ret; + } + + // public FuzzyBoolean matches(IType type, MatchKind kind) { + // return FuzzyBoolean.YES; + // } + + @Override + protected boolean matchesSubtypes(ResolvedType type) { + return true; + } + + @Override + public boolean isStar() { + return false; + } + + @Override + public String toString() { + return "(" + annotationPattern + " *)"; + } + + public AnnotationTypePattern getAnnotationTypePattern() { + return annotationPattern; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AnyWithAnnotationTypePattern)) { + return false; + } + AnyWithAnnotationTypePattern awatp = (AnyWithAnnotationTypePattern) obj; + return (annotationPattern.equals(awatp.annotationPattern)); + } + + @Override + public int hashCode() { + return annotationPattern.hashCode(); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ArgsAnnotationPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ArgsAnnotationPointcut.java new file mode 100644 index 000000000..db612b8cd --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ArgsAnnotationPointcut.java @@ -0,0 +1,250 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; + +/** + * @author colyer + * + * TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style - Code + * Templates + */ +public class ArgsAnnotationPointcut extends NameBindingPointcut { + + private AnnotationPatternList arguments; + private String declarationText; + + /** + * + */ + public ArgsAnnotationPointcut(AnnotationPatternList arguments) { + super(); + this.arguments = arguments; + this.pointcutKind = ATARGS; + buildDeclarationText(); + } + + public AnnotationPatternList getArguments() { + return arguments; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; // empty args() matches jps with no args + } + + public Pointcut parameterizeWith(Map typeVariableMap, World w) { + ArgsAnnotationPointcut ret = new ArgsAnnotationPointcut(arguments.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) + */ + public FuzzyBoolean fastMatch(FastMatchInfo info) { + return FuzzyBoolean.MAYBE; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) + */ + protected FuzzyBoolean matchInternal(Shadow shadow) { + arguments.resolve(shadow.getIWorld()); + FuzzyBoolean ret = arguments.matches(shadow.getIWorld().resolve(shadow.getArgTypes())); + return ret; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(org.aspectj.weaver.patterns.IScope, + * org.aspectj.weaver.patterns.Bindings) + */ + protected void resolveBindings(IScope scope, Bindings bindings) { + if (!scope.getWorld().isInJava5Mode()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ATARGS_ONLY_SUPPORTED_AT_JAVA5_LEVEL), + getSourceLocation())); + return; + } + arguments.resolveBindings(scope, bindings, true); + if (arguments.ellipsisCount > 1) { + scope.message(IMessage.ERROR, this, "uses more than one .. in args (compiler limitation)"); + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#concretize1(org.aspectj.weaver.ResolvedType, org.aspectj.weaver.IntMap) + */ + protected Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + if (isDeclare(bindings.getEnclosingAdvice())) { + // Enforce rule about which designators are supported in declare + inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ARGS_IN_DECLARE), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + AnnotationPatternList list = arguments.resolveReferences(bindings); + Pointcut ret = new ArgsAnnotationPointcut(list); + ret.copyLocationFrom(this); + return ret; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#findResidue(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) + */ + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + int len = shadow.getArgCount(); + + // do some quick length tests first + int numArgsMatchedByEllipsis = (len + arguments.ellipsisCount) - arguments.size(); + if (numArgsMatchedByEllipsis < 0) { + return Literal.FALSE; // should never happen + } + if ((numArgsMatchedByEllipsis > 0) && (arguments.ellipsisCount == 0)) { + return Literal.FALSE; // should never happen + } + // now work through the args and the patterns, skipping at ellipsis + Test ret = Literal.TRUE; + int argsIndex = 0; + for (int i = 0; i < arguments.size(); i++) { + if (arguments.get(i) == AnnotationTypePattern.ELLIPSIS) { + // match ellipsisMatchCount args + argsIndex += numArgsMatchedByEllipsis; + } else if (arguments.get(i) == AnnotationTypePattern.ANY) { + argsIndex++; + } else { + // match the argument type at argsIndex with the ExactAnnotationTypePattern + // we know it is exact because nothing else is allowed in args + ExactAnnotationTypePattern ap = (ExactAnnotationTypePattern) arguments.get(i); + UnresolvedType argType = shadow.getArgType(argsIndex); + ResolvedType rArgType = argType.resolve(shadow.getIWorld()); + if (rArgType.isMissing()) { + shadow.getIWorld().getLint().cantFindType.signal(new String[] { WeaverMessages.format( + WeaverMessages.CANT_FIND_TYPE_ARG_TYPE, argType.getName()) }, shadow.getSourceLocation(), + new ISourceLocation[] { getSourceLocation() }); + // IMessage msg = new Message( + // WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_ARG_TYPE,argType.getName()), + // "",IMessage.ERROR,shadow.getSourceLocation(),null,new ISourceLocation[]{getSourceLocation()}); + } + + ResolvedType rAnnType = ap.getAnnotationType().resolve(shadow.getIWorld()); + if (ap instanceof BindingAnnotationTypePattern) { + BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern) ap; + Var annvar = shadow.getArgAnnotationVar(argsIndex, rAnnType); + state.set(btp.getFormalIndex(), annvar); + } + if (!ap.matches(rArgType).alwaysTrue()) { + // we need a test... + ret = Test.makeAnd(ret, Test.makeHasAnnotation(shadow.getArgVar(argsIndex), rAnnType)); + } + argsIndex++; + } + } + return ret; + } + + public List<BindingPattern> getBindingAnnotationTypePatterns() { + List<BindingPattern> l = new ArrayList<BindingPattern>(); + AnnotationTypePattern[] pats = arguments.getAnnotationPatterns(); + for (int i = 0; i < pats.length; i++) { + if (pats[i] instanceof BindingAnnotationTypePattern) { + l.add((BindingPattern)pats[i]); + } + } + return l; + } + + public List<BindingTypePattern> getBindingTypePatterns() { + return Collections.emptyList(); + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.ATARGS); + arguments.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationPatternList annotationPatternList = AnnotationPatternList.read(s, context); + ArgsAnnotationPointcut ret = new ArgsAnnotationPointcut(annotationPatternList); + ret.readLocation(context, s); + return ret; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (!(obj instanceof ArgsAnnotationPointcut)) { + return false; + } + ArgsAnnotationPointcut other = (ArgsAnnotationPointcut) obj; + return other.arguments.equals(arguments); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return 17 + 37 * arguments.hashCode(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + private void buildDeclarationText() { + StringBuffer buf = new StringBuffer("@args"); + buf.append(arguments.toString()); + this.declarationText = buf.toString(); + } + + public String toString() { + return this.declarationText; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ArgsPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ArgsPointcut.java new file mode 100644 index 000000000..56b1a4dc5 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ArgsPointcut.java @@ -0,0 +1,288 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +/** + * args(arguments) + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public class ArgsPointcut extends NameBindingPointcut { + private static final String ASPECTJ_JP_SIGNATURE_PREFIX = "Lorg/aspectj/lang/JoinPoint"; + private static final String ASPECTJ_SYNTHETIC_SIGNATURE_PREFIX = "Lorg/aspectj/runtime/internal/"; + + private TypePatternList arguments; + private String stringRepresentation; + + public ArgsPointcut(TypePatternList arguments) { + this.arguments = arguments; + this.pointcutKind = ARGS; + this.stringRepresentation = "args" + arguments.toString() + ""; + } + + public TypePatternList getArguments() { + return arguments; + } + + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + ArgsPointcut ret = new ArgsPointcut(this.arguments.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; // empty args() matches jps with no args + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + ResolvedType[] argumentsToMatchAgainst = getArgumentsToMatchAgainst(shadow); + FuzzyBoolean ret = arguments.matches(argumentsToMatchAgainst, TypePattern.DYNAMIC); + return ret; + } + + private ResolvedType[] getArgumentsToMatchAgainst(Shadow shadow) { + + if (shadow.isShadowForArrayConstructionJoinpoint()) { + return shadow.getArgumentTypesForArrayConstructionShadow(); + } + + ResolvedType[] argumentsToMatchAgainst = shadow.getIWorld().resolve(shadow.getGenericArgTypes()); + + // special treatment for adviceexecution which may have synthetic arguments we + // want to ignore. + if (shadow.getKind() == Shadow.AdviceExecution) { + int numExtraArgs = 0; + for (int i = 0; i < argumentsToMatchAgainst.length; i++) { + String argumentSignature = argumentsToMatchAgainst[i].getSignature(); + if (argumentSignature.startsWith(ASPECTJ_JP_SIGNATURE_PREFIX) + || argumentSignature.startsWith(ASPECTJ_SYNTHETIC_SIGNATURE_PREFIX)) { + numExtraArgs++; + } else { + // normal arg after AJ type means earlier arg was NOT synthetic + numExtraArgs = 0; + } + } + if (numExtraArgs > 0) { + int newArgLength = argumentsToMatchAgainst.length - numExtraArgs; + ResolvedType[] argsSubset = new ResolvedType[newArgLength]; + System.arraycopy(argumentsToMatchAgainst, 0, argsSubset, 0, newArgLength); + argumentsToMatchAgainst = argsSubset; + } + } else if (shadow.getKind() == Shadow.ConstructorExecution) { + if (shadow.getMatchingSignature().getParameterTypes().length < argumentsToMatchAgainst.length) { + // there are one or more synthetic args on the end, caused by non-public itd constructor + int newArgLength = shadow.getMatchingSignature().getParameterTypes().length; + ResolvedType[] argsSubset = new ResolvedType[newArgLength]; + System.arraycopy(argumentsToMatchAgainst, 0, argsSubset, 0, newArgLength); + argumentsToMatchAgainst = argsSubset; + } + } + return argumentsToMatchAgainst; + } + + public List<BindingPattern> getBindingAnnotationTypePatterns() { + return Collections.emptyList(); + } + + public List<BindingTypePattern> getBindingTypePatterns() { + List<BindingTypePattern> l = new ArrayList<BindingTypePattern>(); + TypePattern[] pats = arguments.getTypePatterns(); + for (int i = 0; i < pats.length; i++) { + if (pats[i] instanceof BindingTypePattern) { + l.add((BindingTypePattern)pats[i]); + } + } + return l; + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.ARGS); + arguments.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + ArgsPointcut ret = new ArgsPointcut(TypePatternList.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + public boolean equals(Object other) { + if (!(other instanceof ArgsPointcut)) { + return false; + } + ArgsPointcut o = (ArgsPointcut) other; + return o.arguments.equals(this.arguments); + } + + public int hashCode() { + return arguments.hashCode(); + } + + public void resolveBindings(IScope scope, Bindings bindings) { + arguments.resolveBindings(scope, bindings, true, true); + if (arguments.ellipsisCount > 1) { + scope.message(IMessage.ERROR, this, "uses more than one .. in args (compiler limitation)"); + } + } + + public void postRead(ResolvedType enclosingType) { + arguments.postRead(enclosingType); + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + if (isDeclare(bindings.getEnclosingAdvice())) { + // Enforce rule about which designators are supported in declare + inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ARGS_IN_DECLARE), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + TypePatternList args = arguments.resolveReferences(bindings); + if (inAspect.crosscuttingMembers != null) { + inAspect.crosscuttingMembers.exposeTypes(args.getExactTypes()); + } + Pointcut ret = new ArgsPointcut(args); + ret.copyLocationFrom(this); + return ret; + } + + private Test findResidueNoEllipsis(Shadow shadow, ExposedState state, TypePattern[] patterns) { + ResolvedType[] argumentsToMatchAgainst = getArgumentsToMatchAgainst(shadow); + int len = argumentsToMatchAgainst.length; + // System.err.println("boudn to : " + len + ", " + patterns.length); + if (patterns.length != len) { + return Literal.FALSE; + } + + Test ret = Literal.TRUE; + + for (int i = 0; i < len; i++) { + UnresolvedType argType = shadow.getGenericArgTypes()[i]; + TypePattern type = patterns[i]; + ResolvedType argRTX = shadow.getIWorld().resolve(argType, true); + if (!(type instanceof BindingTypePattern)) { + if (argRTX.isMissing()) { + shadow.getIWorld().getLint().cantFindType.signal(new String[] { WeaverMessages.format( + WeaverMessages.CANT_FIND_TYPE_ARG_TYPE, argType.getName()) }, shadow.getSourceLocation(), + new ISourceLocation[] { getSourceLocation() }); + // IMessage msg = new Message( + // WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_ARG_TYPE,argType.getName()), + // "",IMessage.ERROR,shadow.getSourceLocation(),null,new ISourceLocation[]{getSourceLocation()}); + // shadow.getIWorld().getMessageHandler().handleMessage(msg); + } + if (type.matchesInstanceof(argRTX).alwaysTrue()) { + continue; + } + } + + World world = shadow.getIWorld(); + ResolvedType typeToExpose = type.getExactType().resolve(world); + if (typeToExpose.isParameterizedType()) { + boolean inDoubt = (type.matchesInstanceof(argRTX) == FuzzyBoolean.MAYBE); + if (inDoubt && world.getLint().uncheckedArgument.isEnabled()) { + String uncheckedMatchWith = typeToExpose.getSimpleBaseName(); + if (argRTX.isParameterizedType() && (argRTX.getRawType() == typeToExpose.getRawType())) { + uncheckedMatchWith = argRTX.getSimpleName(); + } + if (!isUncheckedArgumentWarningSuppressed()) { + world.getLint().uncheckedArgument.signal(new String[] { typeToExpose.getSimpleName(), uncheckedMatchWith, + typeToExpose.getSimpleBaseName(), shadow.toResolvedString(world) }, getSourceLocation(), + new ISourceLocation[] { shadow.getSourceLocation() }); + } + } + } + + ret = Test.makeAnd(ret, exposeStateForVar(shadow.getArgVar(i), type, state, shadow.getIWorld())); + } + + return ret; + } + + /** + * We need to find out if someone has put the @SuppressAjWarnings{"uncheckedArgument"} annotation somewhere. That somewhere is + * going to be an a piece of advice that uses this pointcut. But how do we find it??? + * + * @return + */ + private boolean isUncheckedArgumentWarningSuppressed() { + return false; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + ResolvedType[] argsToMatch = getArgumentsToMatchAgainst(shadow); + if (arguments.matches(argsToMatch, TypePattern.DYNAMIC).alwaysFalse()) { + return Literal.FALSE; + } + int ellipsisCount = arguments.ellipsisCount; + if (ellipsisCount == 0) { + return findResidueNoEllipsis(shadow, state, arguments.getTypePatterns()); + } else if (ellipsisCount == 1) { + TypePattern[] patternsWithEllipsis = arguments.getTypePatterns(); + TypePattern[] patternsWithoutEllipsis = new TypePattern[argsToMatch.length]; + int lenWithEllipsis = patternsWithEllipsis.length; + int lenWithoutEllipsis = patternsWithoutEllipsis.length; + // l1+1 >= l0 + int indexWithEllipsis = 0; + int indexWithoutEllipsis = 0; + while (indexWithoutEllipsis < lenWithoutEllipsis) { + TypePattern p = patternsWithEllipsis[indexWithEllipsis++]; + if (p == TypePattern.ELLIPSIS) { + int newLenWithoutEllipsis = lenWithoutEllipsis - (lenWithEllipsis - indexWithEllipsis); + while (indexWithoutEllipsis < newLenWithoutEllipsis) { + patternsWithoutEllipsis[indexWithoutEllipsis++] = TypePattern.ANY; + } + } else { + patternsWithoutEllipsis[indexWithoutEllipsis++] = p; + } + } + return findResidueNoEllipsis(shadow, state, patternsWithoutEllipsis); + } else { + throw new BCException("unimplemented"); + } + } + + public String toString() { + return this.stringRepresentation; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BasicToken.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BasicToken.java new file mode 100644 index 000000000..e6e201daf --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BasicToken.java @@ -0,0 +1,75 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.patterns; + + +public final class BasicToken implements IToken { + private String value; + private boolean isIdentifier; + private String literalKind; + + private int start; + private int end; + + public static BasicToken makeOperator(String value, int start, int end) { + return new BasicToken(value.intern(), false, null, start, end); + } + + public static BasicToken makeIdentifier(String value, int start, int end) { + return new BasicToken(value, true, null, start, end); + } + + public static BasicToken makeLiteral(String value, String kind, int start, int end) { + return new BasicToken(value, false, kind.intern(), start, end); + } + + + private BasicToken(String value, boolean isIdentifier, String literalKind, int start, int end) { + this.value = value; + this.isIdentifier = isIdentifier; + this.literalKind = literalKind; + this.start = start; + this.end = end; + } + + public int getStart() { return start; } + public int getEnd() { return end; } + public String getFileName() { return "unknown"; } + + public String getString() { + return value; + } + + public boolean isIdentifier() { + return isIdentifier; + } + + public Pointcut maybeGetParsedPointcut() { + return null; + } + + + + public String toString() { + String s; + if (isIdentifier) s = value; + else s = "'" + value + "'"; + + return s + "@" + start + ":" + end; + } + public String getLiteralKind() { + return literalKind; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BasicTokenSource.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BasicTokenSource.java new file mode 100644 index 000000000..0a099529b --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BasicTokenSource.java @@ -0,0 +1,188 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.patterns; + +import java.util.ArrayList; +import java.util.List; + +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; + + +public class BasicTokenSource implements ITokenSource { + private int index = 0; + private IToken[] tokens; + private ISourceContext sourceContext; + + public BasicTokenSource(IToken[] tokens, ISourceContext sourceContext) { + this.tokens = tokens; + this.sourceContext = sourceContext; + } + + public int getIndex() { + return index; + } + + public void setIndex(int newIndex) { + this.index = newIndex; + } + + public IToken next() { + try { + return tokens[index++]; + } catch (ArrayIndexOutOfBoundsException e) { + return IToken.EOF; + } + } + + public IToken peek() { + try { + return tokens[index]; + } catch (ArrayIndexOutOfBoundsException e) { + return IToken.EOF; + } + } + + public IToken peek(int offset) { + try { + return tokens[index+offset]; + } catch (ArrayIndexOutOfBoundsException e) { + return IToken.EOF; + } + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("["); + for (int i = 0; i < tokens.length; i++) { + IToken t = tokens[i]; + if (t == null) + break; + if (i > 0) + buf.append(", "); + buf.append(t.toString()); + } + buf.append("]"); + return buf.toString(); + } + + + ////////////////////////////////////////////////////// + // Convenience, maybe just for testing + public static ITokenSource makeTokenSource(String input, ISourceContext context) { + char[] chars = input.toCharArray(); + + int i = 0; + List<BasicToken> tokens = new ArrayList<BasicToken>(); + + while (i < chars.length) { + char ch = chars[i++]; + switch(ch) { + case ' ': + case '\t': + case '\n': + case '\r': + continue; + case '*': + case '(': + case ')': + case '+': + case '[': + case ']': + case ',': + case '!': + case ':': + case '@': + case '<': + case '>': + case '=': + case '?': + tokens.add(BasicToken.makeOperator(makeString(ch), i-1, i-1)); + continue; + case '.': + if ((i+2)<=chars.length) { + // could be '...' + char nextChar1 = chars[i]; + char nextChar2 = chars[i+1]; + if (ch==nextChar1 && ch==nextChar2) { + // '...' + tokens.add(BasicToken.makeIdentifier("...",i-1,i+1)); + i=i+2; + } else { + tokens.add(BasicToken.makeOperator(makeString(ch), i-1, i-1)); + } + } else { + tokens.add(BasicToken.makeOperator(makeString(ch), i-1, i-1)); + } + continue; + case '&': + if ((i+1) <= chars.length && chars[i] != '&') { + tokens.add(BasicToken.makeOperator(makeString(ch),i-1,i-1)); + continue; + } + // fall-through + case '|': + if (i == chars.length) { + throw new BCException("bad " + ch); + } + char nextChar = chars[i++]; + if (nextChar == ch) { + tokens.add(BasicToken.makeOperator(makeString(ch, 2), i-2, i-1)); + } else { + throw new RuntimeException("bad " + ch); + } + continue; + + case '\"': + int start0 = i-1; + while (i < chars.length && !(chars[i]=='\"')) i++; + i += 1; + tokens.add(BasicToken.makeLiteral(new String(chars, start0+1, i-start0-2), "string", start0, i-1)); + continue; + default: + int start = i-1; + while (i < chars.length && Character.isJavaIdentifierPart(chars[i])) { i++; } + tokens.add(BasicToken.makeIdentifier(new String(chars, start, i-start), start, i-1)); + + } + } + + //System.out.println(tokens); + + return new BasicTokenSource((IToken[])tokens.toArray(new IToken[tokens.size()]), context); + } + + private static String makeString(char ch) { + return Character.toString(ch); + } + + private static String makeString(char ch, int count) { + // slightly inefficient ;-) + char[] chars = new char[count]; + for (int i=0; i<count; i++) { chars[i] = ch; } + return new String(chars); + } + public ISourceContext getSourceContext() { + return sourceContext; + } + public void setSourceContext(ISourceContext context) { + this.sourceContext = context; + } + + @Override + public boolean hasMoreTokens() { + return index < tokens.length; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BindingAnnotationFieldTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BindingAnnotationFieldTypePattern.java new file mode 100644 index 000000000..707f1e79e --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BindingAnnotationFieldTypePattern.java @@ -0,0 +1,198 @@ +/* ******************************************************************* + * 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.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +/** + * Represents an attempt to bind the field of an annotation within a pointcut. For example:<br> + * <code><pre> + * before(Level lev): execution(* *(..)) && @annotation(TraceAnnotation(lev)) + * </pre></code><br> + * This binding annotation type pattern will be for 'lev'. + */ +public class BindingAnnotationFieldTypePattern extends ExactAnnotationTypePattern implements BindingPattern { + + protected int formalIndex; + UnresolvedType formalType; // In this construct the formal type differs from the annotation type + + public BindingAnnotationFieldTypePattern(UnresolvedType formalType, int formalIndex, UnresolvedType theAnnotationType) { + super(theAnnotationType, null); + this.formalIndex = formalIndex; + this.formalType = formalType; + } + + public void resolveBinding(World world) { + if (resolved) { + return; + } + resolved = true; + formalType = world.resolve(formalType); + annotationType = world.resolve(annotationType); + ResolvedType annoType = (ResolvedType) annotationType; + if (!annoType.isAnnotation()) { + IMessage m = MessageUtil + .error(WeaverMessages.format(WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE, annoType.getName()), + getSourceLocation()); + world.getMessageHandler().handleMessage(m); + resolved = false; + } + } + + @Override + public AnnotationTypePattern parameterizeWith(Map typeVariableMap, World w) { + throw new BCException("Parameterization not implemented for annotation field binding construct (compiler limitation)"); + // UnresolvedType newAnnotationType = annotationType; + // if (annotationType.isTypeVariableReference()) { + // TypeVariableReference t = (TypeVariableReference) annotationType; + // String key = t.getTypeVariable().getName(); + // if (typeVariableMap.containsKey(key)) { + // newAnnotationType = (UnresolvedType) typeVariableMap.get(key); + // } + // } else if (annotationType.isParameterizedType()) { + // newAnnotationType = annotationType.parameterize(typeVariableMap); + // } + // BindingAnnotationTypePattern ret = new BindingAnnotationTypePattern(newAnnotationType, this.formalIndex); + // if (newAnnotationType instanceof ResolvedType) { + // ResolvedType rat = (ResolvedType) newAnnotationType; + // verifyRuntimeRetention(rat.getWorld(), rat); + // } + // ret.copyLocationFrom(this); + // return ret; + } + + @Override + public int getFormalIndex() { + return formalIndex; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BindingAnnotationFieldTypePattern)) { + return false; + } + BindingAnnotationFieldTypePattern btp = (BindingAnnotationFieldTypePattern) obj; + return (btp.formalIndex == formalIndex) && (annotationType.equals(btp.annotationType)) + && (formalType.equals(btp.formalType)); + } + + @Override + public int hashCode() { + return (annotationType.hashCode() * 37 + formalIndex * 37) + formalType.hashCode(); + } + + @Override + public AnnotationTypePattern remapAdviceFormals(IntMap bindings) { + if (!bindings.hasKey(formalIndex)) { + throw new BCException("Annotation field binding reference must be bound (compiler limitation)"); + // must be something like returning the unbound form: return new ExactAnnotationTypePattern(annotationType, + // null); + } else { + int newFormalIndex = bindings.get(formalIndex); + BindingAnnotationFieldTypePattern baftp = new BindingAnnotationFieldTypePattern(formalType, newFormalIndex, + annotationType); + baftp.formalName = formalName; + return baftp; + } + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.BINDINGFIELD2); + formalType.write(s); // the type of the field within the annotation + s.writeShort((short) formalIndex); + annotationType.write(s); // the annotation type + s.writeUTF(formalName); + writeLocation(s); + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern ret = new BindingAnnotationFieldTypePattern(UnresolvedType.read(s), s.readShort(), + UnresolvedType.read(s)); + ret.readLocation(context, s); + return ret; + } + + public static AnnotationTypePattern read2(VersionedDataInputStream s, ISourceContext context) throws IOException { + BindingAnnotationFieldTypePattern ret = new BindingAnnotationFieldTypePattern(UnresolvedType.read(s), s.readShort(), + UnresolvedType.read(s)); + ret.formalName = s.readUTF(); + ret.readLocation(context, s); + return ret; + } + + @Override + public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) { + // Inheritance irrelevant because @annotation(Anno(x)) only supported at method execution join points (compiler limitation) + // boolean checkSupers = false; + // if (getResolvedAnnotationType().hasAnnotation(UnresolvedType.AT_INHERITED)) { + // if (annotated instanceof ResolvedType) { + // checkSupers = true; + // } + // } + // + if (annotated.hasAnnotation(annotationType)) { + if (annotationType instanceof ReferenceType) { + ReferenceType rt = (ReferenceType) annotationType; + if (rt.getRetentionPolicy() != null && rt.getRetentionPolicy().equals("SOURCE")) { + rt.getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.warn(WeaverMessages.format(WeaverMessages.NO_MATCH_BECAUSE_SOURCE_RETENTION, + annotationType, annotated), getSourceLocation())); + return FuzzyBoolean.NO; + } + ResolvedMember[] methods = rt.getDeclaredMethods(); + boolean found = false; + for (int i = 0; i < methods.length && !found; i++) { + if (methods[i].getReturnType().equals(formalType)) { + found = true; + } + } + return (found ? FuzzyBoolean.YES : FuzzyBoolean.NO); + } + } + // else if (checkSupers) { + // ResolvedType toMatchAgainst = ((ResolvedType) annotated).getSuperclass(); + // while (toMatchAgainst != null) { + // if (toMatchAgainst.hasAnnotation(annotationType)) { + // return FuzzyBoolean.YES; + // } + // toMatchAgainst = toMatchAgainst.getSuperclass(); + // } + // } + // + return FuzzyBoolean.NO; + } + + public UnresolvedType getFormalType() { + return formalType; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BindingAnnotationTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BindingAnnotationTypePattern.java new file mode 100644 index 000000000..142f6155e --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BindingAnnotationTypePattern.java @@ -0,0 +1,138 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeVariableReference; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +public class BindingAnnotationTypePattern extends ExactAnnotationTypePattern implements BindingPattern { + + protected int formalIndex; + + /** + * @param annotationType + */ + public BindingAnnotationTypePattern(UnresolvedType annotationType, int index) { + super(annotationType, null); + this.formalIndex = index; + } + + public BindingAnnotationTypePattern(FormalBinding binding) { + this(binding.getType(), binding.getIndex()); + } + + public void resolveBinding(World world) { + if (resolved) { + return; + } + resolved = true; + annotationType = annotationType.resolve(world); + ResolvedType resolvedAnnotationType = (ResolvedType) annotationType; + if (!resolvedAnnotationType.isAnnotation()) { + IMessage m = MessageUtil.error(WeaverMessages.format(WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE, annotationType + .getName()), getSourceLocation()); + world.getMessageHandler().handleMessage(m); + resolved = false; + } + if (annotationType.isTypeVariableReference()) { + return; // we'll deal with this next check when the type var is actually bound... + } + verifyRuntimeRetention(world, resolvedAnnotationType); + } + + private void verifyRuntimeRetention(World world, ResolvedType resolvedAnnotationType) { + if (!resolvedAnnotationType.isAnnotationWithRuntimeRetention()) { // default is class visibility + // default is class visibility + IMessage m = MessageUtil.error(WeaverMessages.format(WeaverMessages.BINDING_NON_RUNTIME_RETENTION_ANNOTATION, + annotationType.getName()), getSourceLocation()); + world.getMessageHandler().handleMessage(m); + resolved = false; + } + } + + public AnnotationTypePattern parameterizeWith(Map typeVariableMap, World w) { + UnresolvedType newAnnotationType = annotationType; + if (annotationType.isTypeVariableReference()) { + TypeVariableReference t = (TypeVariableReference) annotationType; + String key = t.getTypeVariable().getName(); + if (typeVariableMap.containsKey(key)) { + newAnnotationType = (UnresolvedType) typeVariableMap.get(key); + } + } else if (annotationType.isParameterizedType()) { + newAnnotationType = annotationType.parameterize(typeVariableMap); + } + BindingAnnotationTypePattern ret = new BindingAnnotationTypePattern(newAnnotationType, this.formalIndex); + if (newAnnotationType instanceof ResolvedType) { + ResolvedType rat = (ResolvedType) newAnnotationType; + verifyRuntimeRetention(rat.getWorld(), rat); + } + ret.copyLocationFrom(this); + return ret; + } + + public int getFormalIndex() { + return formalIndex; + } + + public boolean equals(Object obj) { + if (!(obj instanceof BindingAnnotationTypePattern)) { + return false; + } + BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern) obj; + return (super.equals(btp) && (btp.formalIndex == formalIndex)); + } + + public int hashCode() { + return super.hashCode() * 37 + formalIndex; + } + + public AnnotationTypePattern remapAdviceFormals(IntMap bindings) { + if (!bindings.hasKey(formalIndex)) { + return new ExactAnnotationTypePattern(annotationType, null); + } else { + int newFormalIndex = bindings.get(formalIndex); + return new BindingAnnotationTypePattern(annotationType, newFormalIndex); + } + } + + private static final byte VERSION = 1; // rev if serialised form changed + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.BINDING); + s.writeByte(VERSION); + annotationType.write(s); + s.writeShort((short) formalIndex); + writeLocation(s); + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte version = s.readByte(); + if (version > VERSION) { + throw new BCException("BindingAnnotationTypePattern was written by a more recent version of AspectJ"); + } + AnnotationTypePattern ret = new BindingAnnotationTypePattern(UnresolvedType.read(s), s.readShort()); + ret.readLocation(context, s); + return ret; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BindingPattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BindingPattern.java new file mode 100644 index 000000000..4d909ede5 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BindingPattern.java @@ -0,0 +1,19 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import org.aspectj.weaver.IHasPosition; + +/** + * Marker interface for BindingTypePattern and BindingAnnotationTypePattern + */ +public interface BindingPattern extends IHasPosition { + int getFormalIndex(); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BindingTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BindingTypePattern.java new file mode 100644 index 000000000..900285038 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/BindingTypePattern.java @@ -0,0 +1,110 @@ +/* ******************************************************************* + * Copyright (c) 2002, 2010 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * Nieraj Singh + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public class BindingTypePattern extends ExactTypePattern implements BindingPattern { + private int formalIndex; + private String bindingName; + + public BindingTypePattern(UnresolvedType type, int index, boolean isVarArgs) { + super(type, false, isVarArgs); + this.formalIndex = index; + } + + public BindingTypePattern(FormalBinding binding, boolean isVarArgs) { + this(binding.getType(), binding.getIndex(), isVarArgs); + this.bindingName = binding.getName(); + } + + public int getFormalIndex() { + return formalIndex; + } + + public String getBindingName() { + return bindingName; + } + + public boolean equals(Object other) { + if (!(other instanceof BindingTypePattern)) { + return false; + } + BindingTypePattern o = (BindingTypePattern) other; + if (includeSubtypes != o.includeSubtypes) { + return false; + } + if (isVarArgs != o.isVarArgs) { + return false; + } + return o.type.equals(this.type) && o.formalIndex == this.formalIndex; + } + + public int hashCode() { + int result = 17; + result = 37 * result + super.hashCode(); + result = 37 * result + formalIndex; + return result; + } + + public void write(CompressingDataOutputStream out) throws IOException { + out.writeByte(TypePattern.BINDING); + type.write(out); + out.writeShort((short) formalIndex); + out.writeBoolean(isVarArgs); + writeLocation(out); + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + UnresolvedType type = UnresolvedType.read(s); + int index = s.readShort(); + boolean isVarargs = false; + if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + isVarargs = s.readBoolean(); + } + TypePattern ret = new BindingTypePattern(type, index, isVarargs); + ret.readLocation(context, s); + return ret; + } + + public TypePattern remapAdviceFormals(IntMap bindings) { + if (!bindings.hasKey(formalIndex)) { + return new ExactTypePattern(type, false, isVarArgs); + } else { + int newFormalIndex = bindings.get(formalIndex); + return new BindingTypePattern(type, newFormalIndex, isVarArgs); + } + } + + public TypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + ExactTypePattern superParameterized = (ExactTypePattern) super.parameterizeWith(typeVariableMap, w); + BindingTypePattern ret = new BindingTypePattern(superParameterized.getExactType(), this.formalIndex, this.isVarArgs); + ret.copyLocationFrom(this); + return ret; + } + + public String toString() { + // Thread.currentThread().dumpStack(); + return "BindingTypePattern(" + super.toString() + ", " + formalIndex + ")"; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/Bindings.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/Bindings.java new file mode 100644 index 000000000..4853dd049 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/Bindings.java @@ -0,0 +1,136 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import org.aspectj.bridge.IMessage; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.UnresolvedType; + +public class Bindings { + public static final Bindings NONE = new Bindings(0); + + private BindingPattern[] bindings; + + public Bindings(BindingPattern[] bindings) { + this.bindings = bindings; + } + + public Bindings(int count) { + this(new BindingPattern[count]); + } + + public void register(BindingPattern binding, IScope scope) { + int index = binding.getFormalIndex(); + BindingPattern existingBinding = bindings[index]; + if (existingBinding != null) { + scope.message(IMessage.ERROR, existingBinding, binding, "multiple bindings" + index + ", " + binding); + } + bindings[index] = binding; + } + + public void mergeIn(Bindings other, IScope scope) { + for (int i = 0, len = other.bindings.length; i < len; i++) { + if (other.bindings[i] != null) { + register(other.bindings[i], scope); + } + } + } + + /** + * signals an error if one has a binding and other doesn't + */ + public void checkEquals(Bindings other, IScope scope) { + BindingPattern[] b1 = this.bindings; + BindingPattern[] b2 = other.bindings; + int len = b1.length; + if (len != b2.length) { + throw new BCException("INSANE"); + } + + for (int i = 0; i < len; i++) { + if (b1[i] == null && b2[i] != null) { + scope.message(IMessage.ERROR, b2[i], "inconsistent binding"); + b1[i] = b2[i]; // done just to produce fewer error messages + } else if (b2[i] == null && b1[i] != null) { + scope.message(IMessage.ERROR, b1[i], "inconsistent binding"); + b2[i] = b1[i]; // done just to produce fewer error messages + } + } + } + + public String toString() { + StringBuffer buf = new StringBuffer("Bindings("); + for (int i = 0, len = bindings.length; i < len; i++) { + if (i > 0) + buf.append(", "); + buf.append(bindings[i]); + } + buf.append(")"); + return buf.toString(); + } + + public int[] getUsedFormals() { + // System.out.println("used: " + this); + int[] ret = new int[bindings.length]; + int index = 0; + for (int i = 0, len = bindings.length; i < len; i++) { + if (bindings[i] != null) { + ret[index++] = i; + } + } + int[] newRet = new int[index]; + System.arraycopy(ret, 0, newRet, 0, index); + // System.out.println("ret: " + index); + return newRet; + } + + public UnresolvedType[] getUsedFormalTypes() { + UnresolvedType[] ret = new UnresolvedType[bindings.length]; + int index = 0; + for (int i = 0, len = bindings.length; i < len; i++) { + if (bindings[i] != null) { + ret[index++] = ((BindingTypePattern) bindings[i]).getExactType(); + } + } + UnresolvedType[] newRet = new UnresolvedType[index]; + System.arraycopy(ret, 0, newRet, 0, index); + // System.out.println("ret: " + index); + return newRet; + } + + public Bindings copy() { + // int len = bindings.length; + // boolean[] a = new boolean[len]; + // System.arraycopy(bindings, 0, a, 0, len); + return new Bindings((BindingPattern[]) bindings.clone()); + } + + public void checkAllBound(IScope scope) { + for (int i = 0, len = bindings.length; i < len; i++) { + if (bindings[i] == null) { + // ATAJ: avoid warnings for implicit bindings + if (scope.getFormal(i) instanceof FormalBinding.ImplicitFormalBinding) { + bindings[i] = new BindingTypePattern(scope.getFormal(i), false); + } else { + scope.message(IMessage.ERROR, scope.getFormal(i), "formal unbound in pointcut "); + } + } + } + + } + + public int size() { + return bindings.length; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/CflowPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/CflowPointcut.java new file mode 100644 index 000000000..a83236e5c --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/CflowPointcut.java @@ -0,0 +1,355 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.util.FileUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.CrosscuttingMembers; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.patterns.ConcreteCflowPointcut.Slot; + +public class CflowPointcut extends Pointcut { + private final Pointcut entry; // The pointcut inside the cflow() that + // represents the 'entry' point + boolean isBelow;// Is this cflowbelow? + private int[] freeVars; + + /** + * Used to indicate that we're in the context of a cflow when concretizing if's + * + * Will be removed or replaced with something better when we handle this as a non-error + */ + public static final ResolvedPointcutDefinition CFLOW_MARKER = new ResolvedPointcutDefinition(null, 0, null, + UnresolvedType.NONE, Pointcut.makeMatchesNothing(Pointcut.RESOLVED)); + + public CflowPointcut(Pointcut entry, boolean isBelow, int[] freeVars) { + // System.err.println("Building cflow pointcut "+entry.toString()); + this.entry = entry; + this.isBelow = isBelow; + this.freeVars = freeVars; + pointcutKind = CFLOW; + } + + public boolean isCflowBelow() { + return isBelow; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + // enh 76055 + public Pointcut getEntry() { + return entry; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + // ??? this is not maximally efficient + return FuzzyBoolean.MAYBE; + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.CFLOW); + entry.write(s); + s.writeBoolean(isBelow); + FileUtil.writeIntArray(freeVars, s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + + CflowPointcut ret = new CflowPointcut(Pointcut.read(s, context), s.readBoolean(), FileUtil.readIntArray(s)); + ret.readLocation(context, s); + return ret; + } + + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + CflowPointcut ret = new CflowPointcut(entry.parameterizeWith(typeVariableMap, w), isBelow, freeVars); + ret.copyLocationFrom(this); + return ret; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + if (bindings == null) { + entry.resolveBindings(scope, null); + entry.state = RESOLVED; + freeVars = new int[0]; + } else { + // ??? for if's sake we might need to be more careful here + Bindings entryBindings = new Bindings(bindings.size()); + + entry.resolveBindings(scope, entryBindings); + entry.state = RESOLVED; + + freeVars = entryBindings.getUsedFormals(); + + bindings.mergeIn(entryBindings, scope); + } + } + + public boolean equals(Object other) { + if (!(other instanceof CflowPointcut)) { + return false; + } + CflowPointcut o = (CflowPointcut) other; + return o.entry.equals(entry) && o.isBelow == isBelow; + } + + public int hashCode() { + int result = 17; + result = 37 * result + entry.hashCode(); + result = 37 * result + (isBelow ? 0 : 1); + return result; + } + + public String toString() { + return "cflow" + (isBelow ? "below" : "") + "(" + entry + ")"; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + throw new RuntimeException("unimplemented - did concretization fail?"); + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + + // the pointcut is marked as CONCRETE after returning from this + // call - so we can't skip concretization + // if (this.entry.state == Pointcut.SYMBOLIC) { + // // too early to concretize, return unchanged + // return this; + // } + + // Enforce rule about which designators are supported in declare + if (isDeclare(bindings.getEnclosingAdvice())) { + inAspect.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.CFLOW_IN_DECLARE, isBelow ? "below" : ""), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + // make this remap from formal positions to arrayIndices + IntMap entryBindings = new IntMap(); + if (freeVars != null) { + for (int i = 0, len = freeVars.length; i < len; i++) { + int freeVar = freeVars[i]; + // int formalIndex = bindings.get(freeVar); + entryBindings.put(freeVar, i); + } + } + entryBindings.copyContext(bindings); + // System.out.println(this + " bindings: " + entryBindings); + + World world = inAspect.getWorld(); + + Pointcut concreteEntry; + + ResolvedType concreteAspect = bindings.getConcreteAspect(); + + CrosscuttingMembers xcut = concreteAspect.crosscuttingMembers; + Collection<ShadowMunger> previousCflowEntries = xcut.getCflowEntries(); + + entryBindings.pushEnclosingDefinition(CFLOW_MARKER); + // This block concretizes the pointcut within the cflow pointcut + try { + concreteEntry = entry.concretize(inAspect, declaringType, entryBindings); + } finally { + entryBindings.popEnclosingDefinitition(); + } + + List<ShadowMunger> innerCflowEntries = new ArrayList<ShadowMunger>(xcut.getCflowEntries()); + innerCflowEntries.removeAll(previousCflowEntries); + + // Four routes of interest through this code (did I hear someone say + // refactor??) + // 1) no state in the cflow - we can use a counter *and* we have seen + // this pointcut + // before - so use the same counter as before. + // 2) no state in the cflow - we can use a counter, but this is the + // first time + // we have seen this pointcut, so build the infrastructure. + // 3) state in the cflow - we need to use a stack *and* we have seen + // this pointcut + // before - so share the stack. + // 4) state in the cflow - we need to use a stack, but this is the first + // time + // we have seen this pointcut, so build the infrastructure. + + if (freeVars == null || freeVars.length == 0) { // No state, so don't + // use a stack, use a + // counter. + ResolvedMember localCflowField = null; + + Object field = getCflowfield(xcut, concreteEntry, concreteAspect, "counter"); + + // Check if we have already got a counter for this cflow pointcut + if (field != null) { + localCflowField = (ResolvedMember) field; // Use the one we + // already have + + } else { + + // Create a counter field in the aspect + localCflowField = new ResolvedMemberImpl(Member.FIELD, concreteAspect, Modifier.STATIC | Modifier.PUBLIC, + NameMangler.cflowCounter(xcut), UnresolvedType.forName(NameMangler.CFLOW_COUNTER_TYPE) + .getSignature()); + + // Create type munger to add field to the aspect + concreteAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport().makeCflowCounterFieldAdder( + localCflowField)); + + // Create shadow munger to push stuff onto the stack + concreteAspect.crosscuttingMembers.addConcreteShadowMunger(Advice.makeCflowEntry(world, concreteEntry, isBelow, + localCflowField, freeVars == null ? 0 : freeVars.length, innerCflowEntries, inAspect)); + + putCflowfield(xcut, concreteEntry, concreteAspect, localCflowField, "counter"); // Remember + // it + } + + Pointcut ret = new ConcreteCflowPointcut(concreteAspect, localCflowField, null, true); + ret.copyLocationFrom(this); + return ret; + } else { + + List<Slot> slots = new ArrayList<Slot>(); + + for (int i = 0, len = freeVars.length; i < len; i++) { + int freeVar = freeVars[i]; + + // we don't need to keep state that isn't actually exposed to + // advice + // ??? this means that we will store some state that we won't + // actually use, optimize this later + if (!bindings.hasKey(freeVar)) { + continue; + } + + int formalIndex = bindings.get(freeVar); + + // We need to look in the right place for the type of the + // formal. Suppose the advice looks like this: + // before(String s): somePointcut(*,s) + // where the first argument in somePointcut is of type Number + // for free variable 0 we want to ask the pointcut for the type + // of its first argument, if we only + // ask the advice for the type of its first argument then we'll + // get the wrong type (pr86903) + + ResolvedPointcutDefinition enclosingDef = bindings.peekEnclosingDefinition(); + ResolvedType formalType = null; + + // Is there a useful enclosing pointcut? + if (enclosingDef != null && enclosingDef.getParameterTypes().length > 0) { + formalType = enclosingDef.getParameterTypes()[freeVar].resolve(world); + } else { + formalType = bindings.getAdviceSignature().getParameterTypes()[formalIndex].resolve(world); + } + + ConcreteCflowPointcut.Slot slot = new ConcreteCflowPointcut.Slot(formalIndex, formalType, i); + slots.add(slot); + } + ResolvedMember localCflowField = null; + Object field = getCflowfield(xcut, concreteEntry, concreteAspect, "stack"); + if (field != null) { + localCflowField = (ResolvedMember) field; + } else { + + localCflowField = new ResolvedMemberImpl(Member.FIELD, concreteAspect, Modifier.STATIC | Modifier.PUBLIC, + NameMangler.cflowStack(xcut), UnresolvedType.forName(NameMangler.CFLOW_STACK_TYPE) + .getSignature()); + // System.out.println("adding field to: " + inAspect + " field " + // + cflowField); + + // add field and initializer to inAspect + // XXX and then that info above needs to be mapped down here to + // help with + // XXX getting the exposed state right + concreteAspect.crosscuttingMembers.addConcreteShadowMunger(Advice.makeCflowEntry(world, concreteEntry, isBelow, + localCflowField, freeVars.length, innerCflowEntries, inAspect)); + + concreteAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport() + .makeCflowStackFieldAdder(localCflowField)); + putCflowfield(xcut, concreteEntry, concreteAspect, localCflowField, "stack"); + } + Pointcut ret = new ConcreteCflowPointcut(concreteAspect, localCflowField, slots, false); + ret.copyLocationFrom(this); + return ret; + } + + } + + private String getKey(Pointcut p, ResolvedType a, String stackOrCounter) { + StringBuffer sb = new StringBuffer(); + sb.append(a.getName()); + sb.append("::"); + sb.append(p.toString()); + sb.append("::"); + sb.append(stackOrCounter); + return sb.toString(); + } + + private Object getCflowfield(CrosscuttingMembers xcut, Pointcut pcutkey, ResolvedType concreteAspect, String stackOrCounter) { + String key = getKey(pcutkey, concreteAspect, stackOrCounter); + Object o = null; + if (isBelow) { + o = xcut.getCflowBelowFields().get(key); + } else { + o = xcut.getCflowFields().get(key); + } + // System.err.println("Retrieving for key "+key+" returning "+o); + return o; + } + + private void putCflowfield(CrosscuttingMembers xcut, Pointcut pcutkey, ResolvedType concreteAspect, Object o, + String stackOrCounter) { + String key = getKey(pcutkey, concreteAspect, stackOrCounter); + // System.err.println("Storing cflow field for key"+key); + if (isBelow) { + xcut.getCflowBelowFields().put(key, o); + } else { + xcut.getCflowFields().put(key, o); + } + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ConcreteCflowPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ConcreteCflowPointcut.java new file mode 100644 index 000000000..09208f9c2 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ConcreteCflowPointcut.java @@ -0,0 +1,184 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberImpl; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Test; + +public class ConcreteCflowPointcut extends Pointcut { + private final Member cflowField; + List<Slot> slots; // exposed for testing + boolean usesCounter; + ResolvedType aspect; + + // Can either use a counter or a stack to implement cflow. + public ConcreteCflowPointcut(ResolvedType aspect, Member cflowField, List<Slot> slots, boolean usesCounter) { + this.aspect = aspect; + this.cflowField = cflowField; + this.slots = slots; + this.usesCounter = usesCounter; + this.pointcutKind = CFLOW; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + // ??? this is not maximally efficient + // Check we'll be able to do the residue! + + // this bit is for pr145693 - we cannot match at all if one of the types is missing, we will be unable + // to create the residue + if (slots != null) { + for (Slot slot: slots) { + ResolvedType rt = slot.formalType; + if (rt.isMissing()) { + ISourceLocation[] locs = new ISourceLocation[] { getSourceLocation() }; + Message m = new Message(WeaverMessages.format(WeaverMessages.MISSING_TYPE_PREVENTS_MATCH, rt.getName()), "", + Message.WARNING, shadow.getSourceLocation(), null, locs); + rt.getWorld().getMessageHandler().handleMessage(m); + return FuzzyBoolean.NO; + } + } + } + return FuzzyBoolean.MAYBE; + } + + // used by weaver when validating bindings + public int[] getUsedFormalSlots() { + if (slots == null) { + return new int[0]; + } + int[] indices = new int[slots.size()]; + for (int i = 0; i < indices.length; i++) { + indices[i] = ((Slot) slots.get(i)).formalIndex; + } + return indices; + } + + public void write(CompressingDataOutputStream s) throws IOException { + throw new RuntimeException("unimplemented"); + } + + public void resolveBindings(IScope scope, Bindings bindings) { + throw new RuntimeException("unimplemented"); + } + + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + throw new RuntimeException("unimplemented"); + } + + public boolean equals(Object other) { + if (!(other instanceof ConcreteCflowPointcut)) { + return false; + } + ConcreteCflowPointcut o = (ConcreteCflowPointcut) other; + return o.cflowField.equals(this.cflowField); + } + + public int hashCode() { + int result = 17; + result = 37 * result + cflowField.hashCode(); + return result; + } + + public String toString() { + return "concretecflow(" + cflowField + ")"; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + // System.out.println("find residue: " + this); + if (usesCounter) { + return Test.makeFieldGetCall(cflowField, cflowCounterIsValidMethod, Expr.NONE); + } else { + if (slots != null) { // null for cflows managed by counters + for (Slot slot: slots) { + // System.out.println("slot: " + slot.formalIndex); + state.set(slot.formalIndex, + aspect.getWorld().getWeavingSupport().makeCflowAccessVar(slot.formalType, cflowField, slot.arrayIndex)); + } + } + return Test.makeFieldGetCall(cflowField, cflowStackIsValidMethod, Expr.NONE); + } + } + + private static final Member cflowStackIsValidMethod = MemberImpl.method(NameMangler.CFLOW_STACK_UNRESOLVEDTYPE, 0, + UnresolvedType.BOOLEAN, "isValid", UnresolvedType.NONE); + + private static final Member cflowCounterIsValidMethod = MemberImpl.method(NameMangler.CFLOW_COUNTER_UNRESOLVEDTYPE, 0, + UnresolvedType.BOOLEAN, "isValid", UnresolvedType.NONE); + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + throw new RuntimeException("unimplemented"); + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public static class Slot { + int formalIndex; + ResolvedType formalType; + int arrayIndex; + + public Slot(int formalIndex, ResolvedType formalType, int arrayIndex) { + this.formalIndex = formalIndex; + this.formalType = formalType; + this.arrayIndex = arrayIndex; + } + + public boolean equals(Object other) { + if (!(other instanceof Slot)) { + return false; + } + + Slot o = (Slot) other; + return o.formalIndex == this.formalIndex && o.arrayIndex == this.arrayIndex && o.formalType.equals(this.formalType); + } + + public int hashCode() { + int result = 19; + result = 37 * result + formalIndex; + result = 37 * result + arrayIndex; + result = 37 * result + formalType.hashCode(); + return result; + } + + public String toString() { + return "Slot(" + formalIndex + ", " + formalType + ", " + arrayIndex + ")"; + } + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/Declare.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/Declare.java new file mode 100644 index 000000000..48769fcb6 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/Declare.java @@ -0,0 +1,89 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public abstract class Declare extends PatternNode { + public static final byte ERROR_OR_WARNING = 1; + public static final byte PARENTS = 2; + public static final byte SOFT = 3; + public static final byte DOMINATES = 4; + public static final byte ANNOTATION = 5; + public static final byte PARENTSMIXIN = 6; + public static final byte TYPE_ERROR_OR_WARNING = 7; + + // set when reading declare from aspect + private ResolvedType declaringType; + + public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte kind = s.readByte(); + switch (kind) { + case ERROR_OR_WARNING: + return DeclareErrorOrWarning.read(s, context); + case DOMINATES: + return DeclarePrecedence.read(s, context); + case PARENTS: + return DeclareParents.read(s, context); + case SOFT: + return DeclareSoft.read(s, context); + case ANNOTATION: + return DeclareAnnotation.read(s, context); + case PARENTSMIXIN: + return DeclareParentsMixin.read(s, context); + case TYPE_ERROR_OR_WARNING: + return DeclareTypeErrorOrWarning.read(s, context); + default: + throw new RuntimeException("unimplemented"); + } + } + + /** + * Returns this declare mutated + */ + public abstract void resolve(IScope scope); + + /** + * Returns a version of this declare element in which all references to type variables are replaced with their bindings given in + * the map. + */ + public abstract Declare parameterizeWith(Map<String, UnresolvedType> typeVariableBindingMap, World w); + + /** + * Indicates if this declare should be treated like advice. If true, the declare will have no effect in an abstract aspect. It + * will be inherited by any concrete aspects and will have an effect for each concrete aspect it is ultimately inherited by. + */ + public abstract boolean isAdviceLike(); + + /** + * Declares have methods in the .class file against which info can be stored (for example, the annotation in the case of declare + * annotation). The name is of the form ajc$declare_XXX_NNN where XXX can optionally be set in this 'getNameSuffix()' method - + * depending on whether, at weave time, we want to easily differentiate between the declare methods. + */ + public abstract String getNameSuffix(); + + public void setDeclaringType(ResolvedType aType) { + this.declaringType = aType; + } + + public ResolvedType getDeclaringType() { + return declaringType; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareAnnotation.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareAnnotation.java new file mode 100644 index 000000000..ba1d29415 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareAnnotation.java @@ -0,0 +1,537 @@ +/* ******************************************************************* + * Copyright (c) 2005 IBM Corporation + * 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: + * Adrian Colyer initial implementation + * Andy Clement got it working + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +/** + * Represents a declare annotation statement, one of atField, atMethod, atConstructor or atType. + * + * @author Andy Clement + */ +public class DeclareAnnotation extends Declare { + + public static final Kind AT_TYPE = new Kind(1, "type"); + public static final Kind AT_FIELD = new Kind(2, "field"); + public static final Kind AT_METHOD = new Kind(3, "method"); + public static final Kind AT_CONSTRUCTOR = new Kind(4, "constructor"); + public static final Kind AT_REMOVE_FROM_FIELD = new Kind(5, "removeFromField"); + + private Kind kind; + // for declare @type + private TypePattern typePattern; + // for declare @field,@method,@constructor + private ISignaturePattern signaturePattern; + private ResolvedType containingAspect; + private List<String> annotationMethods; + private List<String> annotationStrings; + private AnnotationAJ annotation; // discovered when required + private ResolvedType annotationType; // discovered when required + + // not serialized: + private int annotationStart; + private int annotationEnd; + + /** + * Constructor for declare atType. + */ + public DeclareAnnotation(Kind kind, TypePattern typePattern) { + this.typePattern = typePattern; + this.kind = kind; + init(); + } + + /** + * Constructor for declare atMethod/atField/atConstructor. + */ + public DeclareAnnotation(Kind kind, ISignaturePattern sigPattern) { + this.signaturePattern = sigPattern; + this.kind = kind; + init(); + } + + private void init() { + this.annotationMethods = new ArrayList<String>(); + annotationMethods.add("unknown"); + this.annotationStrings = new ArrayList<String>(); + annotationStrings.add("@<annotation>"); + } + + /** + * Returns the string, useful before the real annotation has been resolved + */ + public String getAnnotationString() { + return annotationStrings.get(0); + } + + public boolean isExactPattern() { + return typePattern instanceof ExactTypePattern; + } + + public String getAnnotationMethod() { + return annotationMethods.get(0); + } + + @Override + public String toString() { + StringBuilder ret = new StringBuilder(); + ret.append("declare @"); + ret.append(kind); + ret.append(" : "); + ret.append(typePattern != null ? typePattern.toString() : signaturePattern.toString()); + ret.append(" : "); + ret.append(annotationStrings.get(0)); + return ret.toString(); + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public void resolve(IScope scope) { + if (!scope.getWorld().isInJava5Mode()) { + String msg = null; + if (kind == AT_TYPE) { + msg = WeaverMessages.DECLARE_ATTYPE_ONLY_SUPPORTED_AT_JAVA5_LEVEL; + } else if (kind == AT_METHOD) { + msg = WeaverMessages.DECLARE_ATMETHOD_ONLY_SUPPORTED_AT_JAVA5_LEVEL; + } else if (kind == AT_FIELD) { + msg = WeaverMessages.DECLARE_ATFIELD_ONLY_SUPPORTED_AT_JAVA5_LEVEL; + } else if (kind == AT_CONSTRUCTOR) { + msg = WeaverMessages.DECLARE_ATCONS_ONLY_SUPPORTED_AT_JAVA5_LEVEL; + } + scope.message(MessageUtil.error(WeaverMessages.format(msg), getSourceLocation())); + return; + } + if (typePattern != null) { + typePattern = typePattern.resolveBindings(scope, Bindings.NONE, false, false); + } + if (signaturePattern != null) { + signaturePattern = signaturePattern.resolveBindings(scope, Bindings.NONE); + } + this.containingAspect = scope.getEnclosingType(); + } + + @Override + public Declare parameterizeWith(Map<String, UnresolvedType> typeVariableBindingMap, World w) { + DeclareAnnotation ret; + if (this.kind == AT_TYPE) { + ret = new DeclareAnnotation(kind, this.typePattern.parameterizeWith(typeVariableBindingMap, w)); + } else { + ret = new DeclareAnnotation(kind, this.signaturePattern.parameterizeWith(typeVariableBindingMap, w)); + } + ret.annotationMethods = this.annotationMethods; + ret.annotationStrings = this.annotationStrings; + ret.annotation = this.annotation; + ret.containingAspect = this.containingAspect; + ret.copyLocationFrom(this); + return ret; + } + + @Override + public boolean isAdviceLike() { + return false; + } + + public void setAnnotationString(String annotationString) { + this.annotationStrings.set(0, annotationString); + } + + public void setAnnotationLocation(int start, int end) { + this.annotationStart = start; + this.annotationEnd = end; + } + + public int getAnnotationSourceStart() { + return annotationStart; + } + + public int getAnnotationSourceEnd() { + return annotationEnd; + } + + public void setAnnotationMethod(String methodName) { + this.annotationMethods.set(0, methodName); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DeclareAnnotation)) { + return false; + } + DeclareAnnotation other = (DeclareAnnotation) obj; + if (!this.kind.equals(other.kind)) { + return false; + } + if (!this.annotationStrings.get(0).equals(other.annotationStrings.get(0))) { + return false; + } + if (!this.annotationMethods.get(0).equals(other.annotationMethods.get(0))) { + return false; + } + if (this.typePattern != null) { + if (!typePattern.equals(other.typePattern)) { + return false; + } + } + if (this.signaturePattern != null) { + if (!signaturePattern.equals(other.signaturePattern)) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + int result = 19; + result = 37 * result + kind.hashCode(); + result = 37 * result + annotationStrings.get(0).hashCode(); + result = 37 * result + annotationMethods.get(0).hashCode(); + if (typePattern != null) { + result = 37 * result + typePattern.hashCode(); + } + if (signaturePattern != null) { + result = 37 * result + signaturePattern.hashCode(); + } + return result; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Declare.ANNOTATION); + if (kind.id == AT_FIELD.id && isRemover) { + s.writeInt(AT_REMOVE_FROM_FIELD.id); + } else { + s.writeInt(kind.id); + } + int max = 0; + s.writeByte(max = annotationStrings.size()); + for (int i = 0; i < max; i++) { + s.writeUTF(annotationStrings.get(i)); + } + s.writeByte(max = annotationMethods.size()); + for (int i = 0; i < max; i++) { + s.writeUTF(annotationMethods.get(i)); + } + if (typePattern != null) { + typePattern.write(s); + } + if (signaturePattern != null) { + AbstractSignaturePattern.writeCompoundSignaturePattern(s, signaturePattern); + } + writeLocation(s); + } + + public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { + DeclareAnnotation ret = null; + boolean isRemover = false; + int kind = s.readInt(); + if (kind == AT_REMOVE_FROM_FIELD.id) { + kind = AT_FIELD.id; + isRemover = true; + } + // old format was just a single string and method + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_AJ169) { + // int numAnnotationStrings = + s.readByte(); + } + String annotationString = s.readUTF(); + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_AJ169) { + // int numAnnotationMethods = + s.readByte(); + } + String annotationMethod = s.readUTF(); + TypePattern tp = null; + SignaturePattern sp = null; + switch (kind) { + case 1: + tp = TypePattern.read(s, context); + ret = new DeclareAnnotation(AT_TYPE, tp); + break; + case 2: + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_AJ169) { + ret = new DeclareAnnotation(AT_FIELD, AbstractSignaturePattern.readCompoundSignaturePattern(s, context)); + } else { + sp = SignaturePattern.read(s, context); + ret = new DeclareAnnotation(AT_FIELD, sp); + } + if (isRemover) { + ret.setRemover(true); + } + break; + case 3: + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_AJ169) { + ret = new DeclareAnnotation(AT_METHOD, AbstractSignaturePattern.readCompoundSignaturePattern(s, context)); + } else { + sp = SignaturePattern.read(s, context); + ret = new DeclareAnnotation(AT_METHOD, sp); + } + break; + case 4: + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_AJ169) { + ret = new DeclareAnnotation(AT_CONSTRUCTOR, AbstractSignaturePattern.readCompoundSignaturePattern(s, context)); + } else { + sp = SignaturePattern.read(s, context); + ret = new DeclareAnnotation(AT_CONSTRUCTOR, sp); + } + break; + + } + ret.setAnnotationString(annotationString); + ret.setAnnotationMethod(annotationMethod); + ret.readLocation(context, s); + return ret; + } + + /** + * For declare atConstructor, atMethod, atField + */ + public boolean matches(ResolvedMember resolvedmember, World world) { + if (kind == AT_METHOD || kind == AT_CONSTRUCTOR) { + if (resolvedmember != null && resolvedmember.getName().charAt(0) == '<') { + // <clinit> or <init> + if (kind == AT_METHOD) { + return false; + } + } + } + return signaturePattern.matches(resolvedmember, world, false); + } + + /** + * For declare atType. + */ + public boolean matches(ResolvedType type) { + if (!typePattern.matchesStatically(type)) { + return false; + } + if (type.getWorld().getLint().typeNotExposedToWeaver.isEnabled() && !type.isExposedToWeaver()) { + type.getWorld().getLint().typeNotExposedToWeaver.signal(type.getName(), getSourceLocation()); + } + return true; + } + + public void setAspect(ResolvedType typeX) { + containingAspect = typeX; + } + + public UnresolvedType getAspect() { + return containingAspect; + } + + public void copyAnnotationTo(ResolvedType onType) { + ensureAnnotationDiscovered(); + if (!onType.hasAnnotation(annotation.getType())) { + onType.addAnnotation(annotation); + } + } + + public AnnotationAJ getAnnotation() { + ensureAnnotationDiscovered(); + return annotation; + } + + /** + * The annotation specified in the declare @type is stored against a simple method of the form "ajc$declare_<NN>", this method + * finds that method and retrieves the annotation + */ + private void ensureAnnotationDiscovered() { + if (annotation != null) { + return; + } + String annotationMethod = annotationMethods.get(0); + for (Iterator<ResolvedMember> iter = containingAspect.getMethods(true, true); iter.hasNext();) { + ResolvedMember member = iter.next(); + if (member.getName().equals(annotationMethod)) { + AnnotationAJ[] annos = member.getAnnotations(); + if (annos == null) { + // if weaving broken code, this can happen + return; + } + int idx = 0; + if (annos.length > 0 + && annos[0].getType().getSignature().equals("Lorg/aspectj/internal/lang/annotation/ajcDeclareAnnotation;")) { + idx = 1; + } + annotation = annos[idx]; + break; + } + } + } + + public TypePattern getTypePattern() { + return typePattern; + } + + public ISignaturePattern getSignaturePattern() { + return signaturePattern; + } + + public boolean isStarredAnnotationPattern() { + if (typePattern != null) { + return typePattern.isStarAnnotation(); + } else { + return signaturePattern.isStarAnnotation(); + } + } + + public Kind getKind() { + return kind; + } + + public boolean isDeclareAtConstuctor() { + return kind.equals(AT_CONSTRUCTOR); + } + + public boolean isDeclareAtMethod() { + return kind.equals(AT_METHOD); + } + + public boolean isDeclareAtType() { + return kind.equals(AT_TYPE); + } + + public boolean isDeclareAtField() { + return kind.equals(AT_FIELD); + } + + /** + * @return the type of the annotation + */ + public ResolvedType getAnnotationType() { + if (annotationType == null) { + String annotationMethod = annotationMethods.get(0); + for (Iterator<ResolvedMember> iter = containingAspect.getMethods(true, true); iter.hasNext();) { + ResolvedMember member = iter.next(); + if (member.getName().equals(annotationMethod)) { + ResolvedType[] annoTypes = member.getAnnotationTypes(); + if (annoTypes == null) { + // if weaving broken code, this can happen + return null; + } + int idx = 0; + if (annoTypes[0].getSignature().equals("Lorg/aspectj/internal/lang/annotation/ajcDeclareAnnotation;")) { + idx = 1; + } + annotationType = annoTypes[idx]; + break; + } + } + } + return annotationType; + } + + /** + * @return true if the annotation specified is allowed on a field + */ + public boolean isAnnotationAllowedOnField() { + ensureAnnotationDiscovered(); + return annotation.allowedOnField(); + } + + public String getPatternAsString() { + if (signaturePattern != null) { + return signaturePattern.toString(); + } + if (typePattern != null) { + return typePattern.toString(); + } + return "DONT KNOW"; + } + + /** + * Return true if this declare annotation could ever match something in the specified type - only really able to make + * intelligent decision if a type was specified in the sig/type pattern signature. + */ + public boolean couldEverMatch(ResolvedType type) { + // Haven't implemented variant for typePattern (doesn't seem worth it!) + // BUGWARNING This test might not be sufficient for funny cases relating + // to interfaces and the use of '+' - but it seems really important to + // do something here so we don't iterate over all fields and all methods + // in all types exposed to the weaver! So look out for bugs here and + // we can update the test as appropriate. + if (signaturePattern != null) { + return signaturePattern.couldEverMatch(type); + } + return true; + } + + /** + * Provide a name suffix so that we can tell the different declare annotations forms apart in the AjProblemReporter + */ + @Override + public String getNameSuffix() { + return getKind().toString(); + } + + /** + * Captures type of declare annotation (method/type/field/constructor) + */ + public static class Kind { + private final int id; + private String s; + + private Kind(int n, String name) { + id = n; + s = name; + } + + @Override + public int hashCode() { + return (19 + 37 * id); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Kind)) { + return false; + } + Kind other = (Kind) obj; + return other.id == id; + } + + @Override + public String toString() { + return "at_" + s; + } + } + + boolean isRemover = false; + + public void setRemover(boolean b) { + isRemover = b; + } + + public boolean isRemover() { + return isRemover; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareErrorOrWarning.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareErrorOrWarning.java new file mode 100644 index 000000000..0a46fa1e9 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareErrorOrWarning.java @@ -0,0 +1,130 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public class DeclareErrorOrWarning extends Declare { + private boolean isError; + private Pointcut pointcut; + private String message; + + public DeclareErrorOrWarning(boolean isError, Pointcut pointcut, String message) { + this.isError = isError; + this.pointcut = pointcut; + this.message = message; + } + + /** + * returns "declare warning: <message>" or "declare error: <message>" + */ + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("declare "); + if (isError) { + buf.append("error: "); + } else { + buf.append("warning: "); + } + buf.append(pointcut); + buf.append(": "); + buf.append("\""); + buf.append(message); + buf.append("\";"); + return buf.toString(); + } + + public boolean equals(Object other) { + if (!(other instanceof DeclareErrorOrWarning)) { + return false; + } + DeclareErrorOrWarning o = (DeclareErrorOrWarning) other; + return (o.isError == isError) && o.pointcut.equals(pointcut) && o.message.equals(message); + } + + public int hashCode() { + int result = isError ? 19 : 23; + result = 37 * result + pointcut.hashCode(); + result = 37 * result + message.hashCode(); + return result; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Declare.ERROR_OR_WARNING); + s.writeBoolean(isError); + pointcut.write(s); + s.writeUTF(message); + writeLocation(s); + } + + public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { + Declare ret = new DeclareErrorOrWarning(s.readBoolean(), Pointcut.read(s, context), s.readUTF()); + ret.readLocation(context, s); + return ret; + } + + public boolean isError() { + return isError; + } + + public String getMessage() { + return message; + } + + public Pointcut getPointcut() { + return pointcut; + } + + public void resolve(IScope scope) { + pointcut = pointcut.resolve(scope); + } + + public Declare parameterizeWith(Map<String,UnresolvedType> typeVariableBindingMap, World w) { + Declare ret = new DeclareErrorOrWarning(isError, pointcut.parameterizeWith(typeVariableBindingMap, w), message); + ret.copyLocationFrom(this); + return ret; + } + + public boolean isAdviceLike() { + return true; + } + + public String getNameSuffix() { + return "eow"; + } + + /** + * returns "declare warning" or "declare error" + */ + public String getName() { + StringBuffer buf = new StringBuffer(); + buf.append("declare "); + if (isError) { + buf.append("error"); + } else { + buf.append("warning"); + } + return buf.toString(); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareParents.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareParents.java new file mode 100644 index 000000000..53553afb3 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareParents.java @@ -0,0 +1,364 @@ +/* ******************************************************************* + * Copyright (c) 2002-2019 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 + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +/** + * @author Thomas Kiviaho + * @author Andy Clement + * @author PARC + */ +public class DeclareParents extends Declare { + protected TypePattern child; + protected TypePatternList parents; + private boolean isWildChild = false; + protected boolean isExtends = true; + + public DeclareParents(TypePattern child, List<TypePattern> parents, boolean isExtends) { + this(child, new TypePatternList(parents), isExtends); + } + + protected DeclareParents(TypePattern child, TypePatternList parents, boolean isExtends) { + this.child = child; + this.parents = parents; + this.isExtends = isExtends; + WildChildFinder wildChildFinder = new WildChildFinder(); + child.accept(wildChildFinder, null); + isWildChild = wildChildFinder.containedWildChild(); + } + + public boolean match(ResolvedType typeX) { + if (!child.matchesStatically(typeX)) { + return false; + } + if (typeX.getWorld().getLint().typeNotExposedToWeaver.isEnabled() && !typeX.isExposedToWeaver()) { + typeX.getWorld().getLint().typeNotExposedToWeaver.signal(typeX.getName(), getSourceLocation()); + } + + return true; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public Declare parameterizeWith(Map<String,UnresolvedType> typeVariableBindingMap, World w) { + DeclareParents ret = new DeclareParents(child.parameterizeWith(typeVariableBindingMap, w), parents.parameterizeWith( + typeVariableBindingMap, w), isExtends); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("declare parents: "); + buf.append(child); + buf.append(isExtends ? " extends " : " implements "); // extends and implements are treated equivalently + buf.append(parents); + buf.append(";"); + return buf.toString(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof DeclareParents)) { + return false; + } + DeclareParents o = (DeclareParents) other; + return o.child.equals(child) && o.parents.equals(parents); + } + + // ??? cache this + @Override + public int hashCode() { + int result = 23; + result = 37 * result + child.hashCode(); + result = 37 * result + parents.hashCode(); + return result; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Declare.PARENTS); + child.write(s); + parents.write(s); + writeLocation(s); + } + + public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { + DeclareParents ret = new DeclareParents(TypePattern.read(s, context), TypePatternList.read(s, context), true); + ret.readLocation(context, s); + return ret; + } + + public boolean parentsIncludeInterface(World w) { + for (int i = 0; i < parents.size(); i++) { + if (parents.get(i).getExactType().resolve(w).isInterface()) { + return true; + } + } + return false; + } + + public boolean parentsIncludeClass(World w) { + for (int i = 0; i < parents.size(); i++) { + if (parents.get(i).getExactType().resolve(w).isClass()) { + return true; + } + } + return false; + } + + @Override + public void resolve(IScope scope) { + TypePattern resolvedChild = child.resolveBindings(scope, Bindings.NONE, false, false); + if (!resolvedChild.equals(child)) { + WildChildFinder wildChildFinder = new WildChildFinder(); + resolvedChild.accept(wildChildFinder, null); + isWildChild = wildChildFinder.containedWildChild(); + this.child = resolvedChild; + } + parents = parents.resolveBindings(scope, Bindings.NONE, false, true); + // Could assert this ... + // for (int i=0; i < parents.size(); i++) { + // parents.get(i).assertExactType(scope.getMessageHandler()); + // } + } + + public TypePatternList getParents() { + return parents; + } + + public TypePattern getChild() { + return child; + } + + // note - will always return true after deserialization, this doesn't affect weaver + public boolean isExtends() { + return this.isExtends; + } + + @Override + public boolean isAdviceLike() { + return false; + } + + private ResolvedType maybeGetNewParent(ResolvedType targetType, TypePattern typePattern, World world, boolean reportErrors) { + if (typePattern == TypePattern.NO) { + return null; // already had an error here + } + + // isWildChild = (child instanceof WildTypePattern); + UnresolvedType iType = typePattern.getExactType(); + ResolvedType parentType = iType.resolve(world); + + if (targetType.equals(world.getCoreType(UnresolvedType.OBJECT))) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.DECP_OBJECT), this.getSourceLocation(), null); + return null; + } + + // Ensure the target doesn't already have an + // alternate parameterization of the generic type on it + if (parentType.isParameterizedType() || parentType.isRawType()) { + // Let's take a look at the parents we already have + boolean isOK = verifyNoInheritedAlternateParameterization(targetType, parentType, world); + if (!isOK) { + return null; + } + } + + if (parentType.isAssignableFrom(targetType)) { + return null; // already a parent + } + + // Enum types that are targetted for decp through a wild type pattern get linted + if (reportErrors && isWildChild && targetType.isEnum()) { + world.getLint().enumAsTargetForDecpIgnored.signal(targetType.toString(), getSourceLocation()); + } + + // Annotation types that are targetted for decp through a wild type pattern get linted + if (reportErrors && isWildChild && targetType.isAnnotation()) { + world.getLint().annotationAsTargetForDecpIgnored.signal(targetType.toString(), getSourceLocation()); + } + + // 1. Can't use decp to make an enum/annotation type implement an interface + if (targetType.isEnum() && parentType.isInterface()) { + if (reportErrors && !isWildChild) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ENUM_TO_IMPL_INTERFACE, + targetType), getSourceLocation(), null); + } + return null; + } + if (targetType.isAnnotation() && parentType.isInterface()) { + if (reportErrors && !isWildChild) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ANNOTATION_TO_IMPL_INTERFACE, + targetType), getSourceLocation(), null); + } + return null; + } + + // 2. Can't use decp to change supertype of an enum/annotation + if (targetType.isEnum() && parentType.isClass()) { + if (reportErrors && !isWildChild) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ENUM_TO_EXTEND_CLASS, + targetType), getSourceLocation(), null); + } + return null; + } + if (targetType.isAnnotation() && parentType.isClass()) { + if (reportErrors && !isWildChild) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ANNOTATION_TO_EXTEND_CLASS, + targetType), getSourceLocation(), null); + } + return null; + } + + // 3. Can't use decp to declare java.lang.Enum/java.lang.annotation.Annotation as the parent of a type + if (parentType.getSignature().equals(UnresolvedType.ENUM.getSignature())) { + if (reportErrors && !isWildChild) { + world.showMessage(IMessage.ERROR, WeaverMessages + .format(WeaverMessages.CANT_DECP_TO_MAKE_ENUM_SUPERTYPE, targetType), getSourceLocation(), null); + } + return null; + } + if (parentType.getSignature().equals(UnresolvedType.ANNOTATION.getSignature())) { + if (reportErrors && !isWildChild) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_TO_MAKE_ANNOTATION_SUPERTYPE, + targetType), getSourceLocation(), null); + } + return null; + } + + if (parentType.isAssignableFrom(targetType)) { + return null; // already a parent + } + + if (targetType.isAssignableFrom(parentType)) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_EXTEND_SELF, targetType.getName()), this + .getSourceLocation(), null); + return null; + } + + if (parentType.isClass()) { + if (targetType.isInterface()) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.INTERFACE_CANT_EXTEND_CLASS), this + .getSourceLocation(), null); + return null; + // how to handle xcutting errors??? + } + + if (!targetType.getSuperclass().isAssignableFrom(parentType)) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.DECP_HIERARCHY_ERROR, iType.getName(), + targetType.getSuperclass().getName()), this.getSourceLocation(), null); + return null; + } else { + return parentType; + } + } else { + return parentType; + } + } + + /** + * This method looks through the type hierarchy for some target type - it is attempting to find an existing parameterization + * that clashes with the new parent that the user wants to apply to the type. If it finds an existing parameterization that + * matches the new one, it silently completes, if it finds one that clashes (e.g. a type already has A<String> when the user + * wants to add A<Number>) then it will produce an error. + * + * It uses recursion and exits recursion on hitting 'jlObject' + * + * Related bugzilla entries: pr110788 + */ + private boolean verifyNoInheritedAlternateParameterization(ResolvedType typeToVerify, ResolvedType newParent, World world) { + + if (typeToVerify.equals(ResolvedType.OBJECT)) { + return true; + } + + ResolvedType newParentGenericType = newParent.getGenericType(); + Iterator<ResolvedType> iter = typeToVerify.getDirectSupertypes(); + while (iter.hasNext()) { + ResolvedType supertype = iter.next(); + if (((supertype.isRawType() && newParent.isParameterizedType()) || (supertype.isParameterizedType() && newParent + .isRawType())) + && newParentGenericType.equals(supertype.getGenericType())) { + // new parent is a parameterized type, but this is a raw type + world.getMessageHandler().handleMessage( + new Message(WeaverMessages.format(WeaverMessages.CANT_DECP_MULTIPLE_PARAMETERIZATIONS, newParent.getName(), + typeToVerify.getName(), supertype.getName()), getSourceLocation(), true, + new ISourceLocation[] { typeToVerify.getSourceLocation() })); + return false; + } + if (supertype.isParameterizedType()) { + ResolvedType generictype = supertype.getGenericType(); + + // If the generic types are compatible but the parameterizations aren't then we have a problem + if (generictype.isAssignableFrom(newParentGenericType) && !supertype.isAssignableFrom(newParent)) { + world.getMessageHandler().handleMessage( + new Message(WeaverMessages.format(WeaverMessages.CANT_DECP_MULTIPLE_PARAMETERIZATIONS, newParent + .getName(), typeToVerify.getName(), supertype.getName()), getSourceLocation(), true, + new ISourceLocation[] { typeToVerify.getSourceLocation() })); + return false; + } + } + if (!verifyNoInheritedAlternateParameterization(supertype, newParent, world)) { + return false; + } + } + return true; + } + + public List<ResolvedType> findMatchingNewParents(ResolvedType onType, boolean reportErrors) { + if (onType.isRawType()) { + onType = onType.getGenericType(); + } + if (!match(onType)) { + return Collections.emptyList(); + } + + List<ResolvedType> ret = new ArrayList<ResolvedType>(); + for (int i = 0; i < parents.size(); i++) { + ResolvedType t = maybeGetNewParent(onType, parents.get(i), onType.getWorld(), reportErrors); + if (t != null) { + ret.add(t); + } + } + + return ret; + } + + @Override + public String getNameSuffix() { + return "parents"; + } + + public boolean isMixin() { + return false; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareParentsMixin.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareParentsMixin.java new file mode 100644 index 000000000..9c2db1156 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareParentsMixin.java @@ -0,0 +1,83 @@ +/* ******************************************************************* + * Copyright (c) 2009 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: + * initial implementation Andy Clement + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.List; + +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.VersionedDataInputStream; + +/** + * Constructed based on an @DeclareMixin being found in an aspect. + * + * @author Andy Clement + */ +public class DeclareParentsMixin extends DeclareParents { + private int bitflags = 0x0000; // for future expansion + + public DeclareParentsMixin(TypePattern child, List parents) { + super(child, parents, true); + } + + public DeclareParentsMixin(TypePattern child, TypePatternList parents) { + super(child, parents, true); + } + + public boolean equals(Object other) { + if (!(other instanceof DeclareParentsMixin)) { + return false; + } + DeclareParentsMixin o = (DeclareParentsMixin) other; + return o.child.equals(child) && o.parents.equals(parents) && o.bitflags == bitflags; + } + + public int hashCode() { + int result = 23; + result = 37 * result + child.hashCode(); + result = 37 * result + parents.hashCode(); + result = 37 * result + bitflags; + return result; + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Declare.PARENTSMIXIN); + child.write(s); + parents.write(s); + writeLocation(s); + s.writeInt(bitflags); + } + + public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { + DeclareParentsMixin ret = new DeclareParentsMixin(TypePattern.read(s, context), TypePatternList.read(s, context)); + ret.readLocation(context, s); + ret.bitflags = s.readInt(); + return ret; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("declare parents mixin: "); + buf.append(child); + buf.append(" implements "); + buf.append(parents); + buf.append(";"); + buf.append("bits=0x").append(Integer.toHexString(bitflags)); + return buf.toString(); + } + + public boolean isMixin() { + return true; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclarePrecedence.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclarePrecedence.java new file mode 100644 index 000000000..678ea89e6 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclarePrecedence.java @@ -0,0 +1,197 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +public class DeclarePrecedence extends Declare { + private TypePatternList patterns; + private IScope scope = null; // non-null means it has not yet been resolved (used by annotation style lazy resolution) + + public DeclarePrecedence(List patterns) { + this(new TypePatternList(patterns)); + } + + private DeclarePrecedence(TypePatternList patterns) { + this.patterns = patterns; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Declare parameterizeWith(Map typeVariableBindingMap, World w) { + DeclarePrecedence ret = new DeclarePrecedence(this.patterns.parameterizeWith(typeVariableBindingMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("declare precedence: "); + buf.append(patterns); + buf.append(";"); + return buf.toString(); + } + + public boolean equals(Object other) { + if (!(other instanceof DeclarePrecedence)) { + return false; + } + DeclarePrecedence o = (DeclarePrecedence) other; + return o.patterns.equals(patterns); + } + + public int hashCode() { + return patterns.hashCode(); + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Declare.DOMINATES); + patterns.write(s); + writeLocation(s); + } + + public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { + Declare ret = new DeclarePrecedence(TypePatternList.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + public void setScopeForResolution(IScope scope) { + this.scope = scope; + } + + public void ensureResolved() { // Lazy resolution - due to pr256779 + if (scope != null) { + try { + resolve(scope); + } finally { + scope = null; + } + } + } + + public void resolve(IScope scope) { + patterns = patterns.resolveBindings(scope, Bindings.NONE, false, false); + boolean seenStar = false; + + for (int i = 0; i < patterns.size(); i++) { + TypePattern pi = patterns.get(i); + if (pi.isStar()) { + if (seenStar) { + scope.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.TWO_STARS_IN_PRECEDENCE), + pi.getSourceLocation(), null); + } + seenStar = true; + continue; + } + ResolvedType exactType = pi.getExactType().resolve(scope.getWorld()); + if (exactType.isMissing()) { + continue; + } + + // Cannot do a dec prec specifying a non-aspect types unless suffixed with a '+' + if (!exactType.isAspect() && !exactType.isAnnotationStyleAspect() && !pi.isIncludeSubtypes() + && !exactType.isTypeVariableReference()) { + scope.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.CLASSES_IN_PRECEDENCE, exactType.getName()), pi.getSourceLocation(), + null); + } + + for (int j = 0; j < patterns.size(); j++) { + if (j == i) { + continue; + } + TypePattern pj = patterns.get(j); + if (pj.isStar()) { + continue; + } + if (pj.matchesStatically(exactType)) { + scope.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.TWO_PATTERN_MATCHES_IN_PRECEDENCE, exactType.getName()), + pi.getSourceLocation(), pj.getSourceLocation()); + } + } + } + } + + public TypePatternList getPatterns() { + ensureResolved(); + return patterns; + } + + private int matchingIndex(ResolvedType a) { + ensureResolved(); + int knownMatch = -1; + int starMatch = -1; + for (int i = 0, len = patterns.size(); i < len; i++) { + TypePattern p = patterns.get(i); + if (p.isStar()) { + starMatch = i; + } else if (p.matchesStatically(a)) { + if (knownMatch != -1) { + a.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.MULTIPLE_MATCHES_IN_PRECEDENCE, a, patterns.get(knownMatch), p), + patterns.get(knownMatch).getSourceLocation(), p.getSourceLocation()); + return -1; + } else { + knownMatch = i; + } + } + } + if (knownMatch == -1) { + return starMatch; + } else { + return knownMatch; + } + } + + public int compare(ResolvedType aspect1, ResolvedType aspect2) { + ensureResolved(); + int index1 = matchingIndex(aspect1); + int index2 = matchingIndex(aspect2); + + // System.out.println("a1: " + aspect1 + ", " + aspect2 + " = " + index1 + ", " + index2); + + if (index1 == -1 || index2 == -1) { + return 0; + } + + if (index1 == index2) { + return 0; + } else if (index1 > index2) { + return -1; + } else { + return +1; + } + } + + public boolean isAdviceLike() { + return false; + } + + public String getNameSuffix() { + return "precedence"; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareSoft.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareSoft.java new file mode 100644 index 000000000..3714d5fdc --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareSoft.java @@ -0,0 +1,139 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeVariableReferenceType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +public class DeclareSoft extends Declare { + private TypePattern exception; + private Pointcut pointcut; + + public DeclareSoft(TypePattern exception, Pointcut pointcut) { + this.exception = exception; + this.pointcut = pointcut; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public Declare parameterizeWith(Map typeVariableBindingMap, World w) { + DeclareSoft ret = new DeclareSoft(exception.parameterizeWith(typeVariableBindingMap, w), pointcut.parameterizeWith( + typeVariableBindingMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("declare soft: "); + buf.append(exception); + buf.append(": "); + buf.append(pointcut); + buf.append(";"); + return buf.toString(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof DeclareSoft)) { + return false; + } + DeclareSoft o = (DeclareSoft) other; + return o.pointcut.equals(pointcut) && o.exception.equals(exception); + } + + @Override + public int hashCode() { + int result = 19; + result = 37 * result + pointcut.hashCode(); + result = 37 * result + exception.hashCode(); + return result; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Declare.SOFT); + exception.write(s); + pointcut.write(s); + writeLocation(s); + } + + public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { + Declare ret = new DeclareSoft(TypePattern.read(s, context), Pointcut.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + public Pointcut getPointcut() { + return pointcut; + } + + public TypePattern getException() { + return exception; + } + + @Override + public void resolve(IScope scope) { + exception = exception.resolveBindings(scope, null, false, true); + ResolvedType excType = exception.getExactType().resolve(scope.getWorld()); + if (!excType.isMissing()) { + if (excType.isTypeVariableReference()) { + TypeVariableReferenceType typeVariableRT = (TypeVariableReferenceType) excType; + // a declare soft in a generic abstract aspect, we need to check the upper bound + // WIBBLE + excType = typeVariableRT.getTypeVariable().getFirstBound().resolve(scope.getWorld()); + } + if (!scope.getWorld().getCoreType(UnresolvedType.THROWABLE).isAssignableFrom(excType)) { + scope.getWorld() + .showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.NOT_THROWABLE, excType.getName()), + exception.getSourceLocation(), null); + pointcut = Pointcut.makeMatchesNothing(Pointcut.RESOLVED); + return; + } + // ENH 42743 suggests that we don't soften runtime exceptions. + if (scope.getWorld().getCoreType(UnresolvedType.RUNTIME_EXCEPTION).isAssignableFrom(excType)) { + scope.getWorld().getLint().runtimeExceptionNotSoftened.signal(new String[] { excType.getName() }, exception + .getSourceLocation(), null); + pointcut = Pointcut.makeMatchesNothing(Pointcut.RESOLVED); + return; + } + } + + pointcut = pointcut.resolve(scope); + } + + @Override + public boolean isAdviceLike() { + return false; + } + + @Override + public String getNameSuffix() { + return "soft"; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareTypeErrorOrWarning.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareTypeErrorOrWarning.java new file mode 100644 index 000000000..daaf84496 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/DeclareTypeErrorOrWarning.java @@ -0,0 +1,135 @@ +/* ******************************************************************* + * Copyright (c) 2010 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.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * For a declare error/warning that specified a type pattern rather than a pointcut. + * + * @author Andy Clement + * @since 1.6.9 + */ +public class DeclareTypeErrorOrWarning extends Declare { + private boolean isError; + private TypePattern typePattern; + private String message; + + public DeclareTypeErrorOrWarning(boolean isError, TypePattern typePattern, String message) { + this.isError = isError; + this.typePattern = typePattern; + this.message = message; + } + + /** + * returns "declare warning: <typepattern>: <message>" or "declare error: <typepattern>: <message>" + */ + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("declare "); + if (isError) { + buf.append("error: "); + } else { + buf.append("warning: "); + } + buf.append(typePattern); + buf.append(": "); + buf.append("\""); + buf.append(message); + buf.append("\";"); + return buf.toString(); + } + + public boolean equals(Object other) { + if (!(other instanceof DeclareTypeErrorOrWarning)) { + return false; + } + DeclareTypeErrorOrWarning o = (DeclareTypeErrorOrWarning) other; + return (o.isError == isError) && o.typePattern.equals(typePattern) && o.message.equals(message); + } + + public int hashCode() { + int result = isError ? 19 : 23; + result = 37 * result + typePattern.hashCode(); + result = 37 * result + message.hashCode(); + return result; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Declare.TYPE_ERROR_OR_WARNING); + s.writeBoolean(isError); + typePattern.write(s); + s.writeUTF(message); + writeLocation(s); + } + + public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { + Declare ret = new DeclareTypeErrorOrWarning(s.readBoolean(), TypePattern.read(s, context), s.readUTF()); + ret.readLocation(context, s); + return ret; + } + + public boolean isError() { + return isError; + } + + public String getMessage() { + return message; + } + + public TypePattern getTypePattern() { + return typePattern; + } + + public void resolve(IScope scope) { + typePattern.resolve(scope.getWorld()); + } + + public Declare parameterizeWith(Map typeVariableBindingMap, World w) { + Declare ret = new DeclareTypeErrorOrWarning(isError, typePattern.parameterizeWith(typeVariableBindingMap, w), message); + ret.copyLocationFrom(this); + return ret; + } + + public boolean isAdviceLike() { + return false; + } + + public String getNameSuffix() { + return "teow"; + } + + /** + * returns "declare type warning" or "declare type error" + */ + public String getName() { + StringBuffer buf = new StringBuffer(); + buf.append("declare type "); + if (isError) { + buf.append("error"); + } else { + buf.append("warning"); + } + return buf.toString(); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/EllipsisTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/EllipsisTypePattern.java new file mode 100644 index 000000000..2abc51327 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/EllipsisTypePattern.java @@ -0,0 +1,109 @@ +/* ******************************************************************* + * Copyright (c) 2002, 2010 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: + * PARC initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.World; + +public class EllipsisTypePattern extends TypePattern { + + /** + * Constructor for EllipsisTypePattern. + * + * @param includeSubtypes + */ + public EllipsisTypePattern() { + super(false, false, new TypePatternList()); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern) + */ + @Override + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return true; + } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesExactly(IType) + */ + @Override + protected boolean matchesExactly(ResolvedType type) { + return false; + } + + @Override + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + return false; + } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesInstanceof(IType) + */ + @Override + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + return FuzzyBoolean.NO; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(ELLIPSIS_KEY); + } + + @Override + public boolean isEllipsis() { + return true; + } + + @Override + public String toString() { + return ".."; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + return (obj instanceof EllipsisTypePattern); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return 17 * 37; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public TypePattern parameterizeWith(Map typeVariableMap, World w) { + return this; + } + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ExactAnnotationFieldTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ExactAnnotationFieldTypePattern.java new file mode 100644 index 000000000..627b622eb --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ExactAnnotationFieldTypePattern.java @@ -0,0 +1,253 @@ +/* ******************************************************************* + * 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.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * Represents an attempt to bind the field of an annotation within a pointcut. For example:<br> + * <code><pre> + * before(Level lev): execution(* *(..)) && @annotation(TraceAnnotation(lev)) + * </pre></code><br> + * This binding annotation type pattern will be for 'lev'. + */ +public class ExactAnnotationFieldTypePattern extends ExactAnnotationTypePattern { + + UnresolvedType annotationType; + private ResolvedMember field; + + public ExactAnnotationFieldTypePattern(ExactAnnotationTypePattern p, String formalName) { + super(formalName); + this.annotationType = p.annotationType; + this.copyLocationFrom(p); + } + + public ExactAnnotationFieldTypePattern(UnresolvedType annotationType, String formalName) { + super(formalName); + this.annotationType = annotationType; + } + + /** + * resolve one of these funky things. Need to: <br> + * (a) Check the formal is bound <br> + * (b) Check the annotation type is valid + */ + @Override + public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) { + if (resolved) { + return this; + } + resolved = true; + FormalBinding formalBinding = scope.lookupFormal(formalName); + if (formalBinding == null) { + scope.message(IMessage.ERROR, this, + "When using @annotation(<annotationType>(<annotationField>)), <annotationField> must be bound"); + return this; + } + + annotationType = scope.getWorld().resolve(annotationType, true); + + // May not be directly found if in a package, so go looking if that is the case: + if (ResolvedType.isMissing(annotationType)) { + String cleanname = annotationType.getName(); + UnresolvedType type = null; + while (ResolvedType.isMissing(type = scope.lookupType(cleanname, this))) { + int lastDot = cleanname.lastIndexOf('.'); + if (lastDot == -1) { + break; + } + cleanname = cleanname.substring(0, lastDot) + "$" + cleanname.substring(lastDot + 1); + } + annotationType = scope.getWorld().resolve(type, true); + if (ResolvedType.isMissing(annotationType)) { + // there are likely to be other errors around that have led to us being unable to + // resolve the annotation type, let's quit now + return this; + } + } + + verifyIsAnnotationType((ResolvedType) annotationType, scope); + + ResolvedType formalBindingType = formalBinding.getType().resolve(scope.getWorld()); + + String bindingTypeSignature = formalBindingType.getSignature(); + if (!(formalBindingType.isEnum() || bindingTypeSignature.equals("Ljava/lang/String;") || bindingTypeSignature.equals("I"))) { + scope.message(IMessage.ERROR, this, + "The field within the annotation must be an enum, string or int. '" + formalBinding.getType() + + "' is not (compiler limitation)"); + } + bindingPattern = true; + + // Check that the formal is bound to a type that is represented by one field in the annotation type + ReferenceType theAnnotationType = (ReferenceType) annotationType; + ResolvedMember[] annotationFields = theAnnotationType.getDeclaredMethods(); + field = null; + boolean looksAmbiguous = false; + for (int i = 0; i < annotationFields.length; i++) { + ResolvedMember resolvedMember = annotationFields[i]; + if (resolvedMember.getReturnType().equals(formalBinding.getType())) { + if (field != null) { + boolean haveProblem = true; + // use the name to differentiate + if (field.getName().equals(formalName)) { + // don't use this new field + haveProblem = false; + } else if (resolvedMember.getName().equals(formalName)) { + // ok, let's use this one + field = resolvedMember; + haveProblem = false; + } + if (haveProblem) { + looksAmbiguous = true; + } + } else { + field = resolvedMember; + } + } + } + if (looksAmbiguous) { + // did we find something that does match by name? + if (field == null || !field.getName().equals(formalName)) { + scope.message(IMessage.ERROR, this, "The field type '" + formalBinding.getType() + + "' is ambiguous for annotation type '" + theAnnotationType.getName() + "'"); + } + } + if (field == null) { + scope.message(IMessage.ERROR, this, "No field of type '" + formalBinding.getType() + "' exists on annotation type '" + + theAnnotationType.getName() + "'"); + } + + BindingAnnotationFieldTypePattern binding = new BindingAnnotationFieldTypePattern(formalBinding.getType(), + formalBinding.getIndex(), theAnnotationType); + binding.copyLocationFrom(this); + binding.formalName = this.formalName; + bindings.register(binding, scope); + binding.resolveBinding(scope.getWorld()); + return binding; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.EXACTFIELD); + s.writeUTF(formalName); + annotationType.write(s); + writeLocation(s); + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + ExactAnnotationFieldTypePattern ret; + String formalName = s.readUTF(); + UnresolvedType annotationType = UnresolvedType.read(s); + ret = new ExactAnnotationFieldTypePattern(annotationType, formalName); + ret.readLocation(context, s); + return ret; + } + + // --- + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ExactAnnotationFieldTypePattern)) { + return false; + } + ExactAnnotationFieldTypePattern other = (ExactAnnotationFieldTypePattern) obj; + return (other.annotationType.equals(annotationType)) && (other.field.equals(field)) + && (other.formalName.equals(this.formalName)); + } + + @Override + public int hashCode() { + int hashcode = annotationType.hashCode(); + hashcode = hashcode * 37 + field.hashCode(); + hashcode = hashcode * 37 + formalName.hashCode(); + return hashcode; + } + + // TODO these are currently unimplemented as I believe it resolves to a Binding form *always* and so they don't get + // called + + @Override + public FuzzyBoolean fastMatches(AnnotatedElement annotated) { + throw new BCException("unimplemented"); + } + + @Override + public UnresolvedType getAnnotationType() { + throw new BCException("unimplemented"); + } + + @Override + public Map getAnnotationValues() { + throw new BCException("unimplemented"); + } + + @Override + public ResolvedType getResolvedAnnotationType() { + throw new BCException("unimplemented"); + } + + @Override + public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) { + throw new BCException("unimplemented"); + } + + @Override + public FuzzyBoolean matches(AnnotatedElement annotated) { + throw new BCException("unimplemented"); + } + + @Override + public FuzzyBoolean matchesRuntimeType(AnnotatedElement annotated) { + throw new BCException("unimplemented"); + } + + @Override + public AnnotationTypePattern parameterizeWith(Map typeVariableMap, World w) { + throw new BCException("unimplemented"); + } + + @Override + public void resolve(World world) { + throw new BCException("unimplemented"); + } + + @Override + public String toString() { + if (!resolved && formalName != null) { + return formalName; + } + StringBuffer ret = new StringBuffer(); + ret.append("@").append(annotationType.toString()); + ret.append("(").append(formalName).append(")"); + return ret.toString(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ExactAnnotationTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ExactAnnotationTypePattern.java new file mode 100644 index 000000000..b51d72df9 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ExactAnnotationTypePattern.java @@ -0,0 +1,470 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeVariableReference; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +/** + * Matches an annotation of a given type + */ +public class ExactAnnotationTypePattern extends AnnotationTypePattern { + + protected UnresolvedType annotationType; + protected String formalName; + protected boolean resolved = false; + protected boolean bindingPattern = false; + private Map<String, String> annotationValues; + + // OPTIMIZE is annotationtype really unresolved???? surely it is resolved by + // now... + public ExactAnnotationTypePattern(UnresolvedType annotationType, Map<String, String> annotationValues) { + this.annotationType = annotationType; + this.annotationValues = annotationValues; + this.resolved = (annotationType instanceof ResolvedType); + } + + // Used when deserializing, values will be added + private ExactAnnotationTypePattern(UnresolvedType annotationType) { + this.annotationType = annotationType; + this.resolved = (annotationType instanceof ResolvedType); + } + + protected ExactAnnotationTypePattern(String formalName) { + this.formalName = formalName; + this.resolved = false; + this.bindingPattern = true; + // will be turned into BindingAnnotationTypePattern during resolution + } + + public ResolvedType getResolvedAnnotationType() { + if (!resolved) { + throw new IllegalStateException("I need to be resolved first!"); + } + return (ResolvedType) annotationType; + } + + public UnresolvedType getAnnotationType() { + return annotationType; + } + + public Map<String, String> getAnnotationValues() { + return annotationValues; + } + + @Override + public FuzzyBoolean fastMatches(AnnotatedElement annotated) { + if (annotated.hasAnnotation(annotationType) && annotationValues == null) { + return FuzzyBoolean.YES; + } else { + // could be inherited, but we don't know that until we are + // resolved, and we're not yet... + return FuzzyBoolean.MAYBE; + } + } + + @Override + public FuzzyBoolean matches(AnnotatedElement annotated) { + return matches(annotated, null); + } + + @Override + public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) { + if (!isForParameterAnnotationMatch()) { + boolean checkSupers = false; + if (getResolvedAnnotationType().isInheritedAnnotation()) { + if (annotated instanceof ResolvedType) { + checkSupers = true; + } + } + + if (annotated.hasAnnotation(annotationType)) { + if (annotationType instanceof ReferenceType) { + ReferenceType rt = (ReferenceType) annotationType; + if (rt.getRetentionPolicy() != null && rt.getRetentionPolicy().equals("SOURCE")) { + rt.getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.warn(WeaverMessages.format(WeaverMessages.NO_MATCH_BECAUSE_SOURCE_RETENTION, + annotationType, annotated), getSourceLocation())); + return FuzzyBoolean.NO; + } + } + + // Are we also matching annotation values? + if (annotationValues != null) { + AnnotationAJ theAnnotation = annotated.getAnnotationOfType(annotationType); + + // Check each one + Set<String> keys = annotationValues.keySet(); + for (String k : keys) { + boolean notEqual = false; + String v = annotationValues.get(k); + // if the key has a trailing '!' then it means the source expressed k!=v - so we are looking for + // something other than the value specified + if (k.endsWith("!")) { + notEqual = true; + k = k.substring(0, k.length() - 1); + } + if (theAnnotation.hasNamedValue(k)) { + // Simple case, value is 'name=value' and the + // annotation specified the same thing + if (notEqual) { + if (theAnnotation.hasNameValuePair(k, v)) { + return FuzzyBoolean.NO; + } + } else { + if (!theAnnotation.hasNameValuePair(k, v)) { + return FuzzyBoolean.NO; + } + } + } else { + // Complex case, look at the default value + ResolvedMember[] ms = ((ResolvedType) annotationType).getDeclaredMethods(); + boolean foundMatch = false; + for (int i = 0; i < ms.length && !foundMatch; i++) { + if (ms[i].isAbstract() && ms[i].getParameterTypes().length == 0 && ms[i].getName().equals(k)) { + // we might be onto something + String s = ms[i].getAnnotationDefaultValue(); + if (s != null && s.equals(v)) { + foundMatch = true; + } + } + } + if (notEqual) { + if (foundMatch) { + return FuzzyBoolean.NO; + } + } else { + if (!foundMatch) { + return FuzzyBoolean.NO; + } + } + } + } + } + return FuzzyBoolean.YES; + } else if (checkSupers) { + ResolvedType toMatchAgainst = ((ResolvedType) annotated).getSuperclass(); + while (toMatchAgainst != null) { + if (toMatchAgainst.hasAnnotation(annotationType)) { + // Are we also matching annotation values? + if (annotationValues != null) { + AnnotationAJ theAnnotation = toMatchAgainst.getAnnotationOfType(annotationType); + + // Check each one + Set<String> keys = annotationValues.keySet(); + for (String k : keys) { + String v = annotationValues.get(k); + if (theAnnotation.hasNamedValue(k)) { + // Simple case, value is 'name=value' and + // the annotation specified the same thing + if (!theAnnotation.hasNameValuePair(k, v)) { + return FuzzyBoolean.NO; + } + } else { + // Complex case, look at the default value + ResolvedMember[] ms = ((ResolvedType) annotationType).getDeclaredMethods(); + boolean foundMatch = false; + for (int i = 0; i < ms.length && !foundMatch; i++) { + if (ms[i].isAbstract() && ms[i].getParameterTypes().length == 0 + && ms[i].getName().equals(k)) { + // we might be onto something + String s = ms[i].getAnnotationDefaultValue(); + if (s != null && s.equals(v)) { + foundMatch = true; + } + } + } + if (!foundMatch) { + return FuzzyBoolean.NO; + } + } + } + } + return FuzzyBoolean.YES; + } + toMatchAgainst = toMatchAgainst.getSuperclass(); + } + } + } else { + // check parameter annotations + if (parameterAnnotations == null) { + return FuzzyBoolean.NO; + } + for (int i = 0; i < parameterAnnotations.length; i++) { + if (annotationType.equals(parameterAnnotations[i])) { + // Are we also matching annotation values? + if (annotationValues != null) { + parameterAnnotations[i] + .getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil + .error("Compiler limitation: annotation value matching for parameter annotations not yet supported")); + return FuzzyBoolean.NO; + } + return FuzzyBoolean.YES; + } + } + } + + return FuzzyBoolean.NO; + } + + // this version should be called for @this, @target, @args + public FuzzyBoolean matchesRuntimeType(AnnotatedElement annotated) { + if (getResolvedAnnotationType().isInheritedAnnotation()) { + // a static match is good enough + if (matches(annotated).alwaysTrue()) { + return FuzzyBoolean.YES; + } + } + // a subtype could match at runtime + return FuzzyBoolean.MAYBE; + } + + @Override + public void resolve(World world) { + if (!resolved) { + annotationType = annotationType.resolve(world); + resolved = true; + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolveBindings(org .aspectj.weaver.patterns.IScope, + * org.aspectj.weaver.patterns.Bindings, boolean) + */ + @Override + public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) { + if (resolved) { + return this; + } + resolved = true; + String simpleName = maybeGetSimpleName(); + if (simpleName != null) { + FormalBinding formalBinding = scope.lookupFormal(simpleName); + if (formalBinding != null) { + if (bindings == null) { + scope.message(IMessage.ERROR, this, "negation doesn't allow binding"); + return this; + } + if (!allowBinding) { + scope.message(IMessage.ERROR, this, "name binding only allowed in @pcds, args, this, and target"); + return this; + } + formalName = simpleName; + bindingPattern = true; + verifyIsAnnotationType(formalBinding.getType().resolve(scope.getWorld()), scope); + BindingAnnotationTypePattern binding = new BindingAnnotationTypePattern(formalBinding); + binding.copyLocationFrom(this); + bindings.register(binding, scope); + binding.resolveBinding(scope.getWorld()); + if (isForParameterAnnotationMatch()) { + binding.setForParameterAnnotationMatch(); + } + + return binding; + } + } + + // Non binding case + String cleanname = annotationType.getName(); + annotationType = scope.getWorld().resolve(annotationType, true); + + // We may not have found it if it is in a package, lets look it up... + if (ResolvedType.isMissing(annotationType)) { + UnresolvedType type = null; + while (ResolvedType.isMissing(type = scope.lookupType(cleanname, this))) { + int lastDot = cleanname.lastIndexOf('.'); + if (lastDot == -1) { + break; + } + cleanname = cleanname.substring(0, lastDot) + "$" + cleanname.substring(lastDot + 1); + } + annotationType = scope.getWorld().resolve(type, true); + } + + verifyIsAnnotationType((ResolvedType) annotationType, scope); + return this; + } + + @Override + public AnnotationTypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + UnresolvedType newAnnotationType = annotationType; + if (annotationType.isTypeVariableReference()) { + TypeVariableReference t = (TypeVariableReference) annotationType; + String key = t.getTypeVariable().getName(); + if (typeVariableMap.containsKey(key)) { + newAnnotationType = typeVariableMap.get(key); + } + } else if (annotationType.isParameterizedType()) { + newAnnotationType = annotationType.parameterize(typeVariableMap); + } + ExactAnnotationTypePattern ret = new ExactAnnotationTypePattern(newAnnotationType, annotationValues); + ret.formalName = formalName; + ret.bindingPattern = bindingPattern; + ret.copyLocationFrom(this); + if (isForParameterAnnotationMatch()) { + ret.setForParameterAnnotationMatch(); + } + return ret; + } + + protected String maybeGetSimpleName() { + if (formalName != null) { + return formalName; + } + String ret = annotationType.getName(); + return (ret.indexOf('.') == -1) ? ret : null; + } + + protected void verifyIsAnnotationType(ResolvedType type, IScope scope) { + if (!type.isAnnotation()) { + IMessage m = MessageUtil.error(WeaverMessages.format(WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE, type.getName()), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + resolved = false; + } + } + + private static byte VERSION = 1; // rev if serialisation form changes + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.EXACT); + s.writeByte(VERSION); + s.writeBoolean(bindingPattern); + if (bindingPattern) { + s.writeUTF(formalName); + } else { + annotationType.write(s); + } + writeLocation(s); + s.writeBoolean(isForParameterAnnotationMatch()); + if (annotationValues == null) { + s.writeInt(0); + } else { + s.writeInt(annotationValues.size()); + Set<String> key = annotationValues.keySet(); + for (Iterator<String> keys = key.iterator(); keys.hasNext();) { + String k = keys.next(); + s.writeUTF(k); + s.writeUTF(annotationValues.get(k)); + } + } + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + ExactAnnotationTypePattern ret; + byte version = s.readByte(); + if (version > VERSION) { + throw new BCException("ExactAnnotationTypePattern was written by a newer version of AspectJ"); + } + boolean isBindingPattern = s.readBoolean(); + if (isBindingPattern) { + ret = new ExactAnnotationTypePattern(s.readUTF()); + } else { + ret = new ExactAnnotationTypePattern(UnresolvedType.read(s)); + } + ret.readLocation(context, s); + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160) { + if (s.readBoolean()) { + ret.setForParameterAnnotationMatch(); + } + } + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160M2) { + int annotationValueCount = s.readInt(); + if (annotationValueCount > 0) { + Map<String, String> aValues = new HashMap<String, String>(); + for (int i = 0; i < annotationValueCount; i++) { + String key = s.readUTF(); + String val = s.readUTF(); + aValues.put(key, val); + } + ret.annotationValues = aValues; + } + } + return ret; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ExactAnnotationTypePattern)) { + return false; + } + ExactAnnotationTypePattern other = (ExactAnnotationTypePattern) obj; + return (other.annotationType.equals(annotationType)) + && isForParameterAnnotationMatch() == other.isForParameterAnnotationMatch() + && (annotationValues == null ? other.annotationValues == null : annotationValues.equals(other.annotationValues)); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return (((annotationType.hashCode()) * 37 + (isForParameterAnnotationMatch() ? 0 : 1)) * 37) + + (annotationValues == null ? 0 : annotationValues.hashCode()); + } + + @Override + public String toString() { + if (!resolved && formalName != null) { + return formalName; + } + String ret = "@" + annotationType.toString(); + if (formalName != null) { + ret = ret + " " + formalName; + } + return ret; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ExactTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ExactTypePattern.java new file mode 100644 index 000000000..68353422f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ExactTypePattern.java @@ -0,0 +1,341 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeVariableReference; +import org.aspectj.weaver.TypeVariableReferenceType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public class ExactTypePattern extends TypePattern { + protected UnresolvedType type; + protected transient ResolvedType resolvedType; + public boolean checked = false; + public boolean isVoid = false; + + public static final Map<String, Class<?>> primitiveTypesMap; + public static final Map<String, Class<?>> boxedPrimitivesMap; + private static final Map<String, Class<?>> boxedTypesMap; + + static { + primitiveTypesMap = new HashMap<String, Class<?>>(); + primitiveTypesMap.put("int", int.class); + primitiveTypesMap.put("short", short.class); + primitiveTypesMap.put("long", long.class); + primitiveTypesMap.put("byte", byte.class); + primitiveTypesMap.put("char", char.class); + primitiveTypesMap.put("float", float.class); + primitiveTypesMap.put("double", double.class); + + boxedPrimitivesMap = new HashMap<String, Class<?>>(); + boxedPrimitivesMap.put("java.lang.Integer", Integer.class); + boxedPrimitivesMap.put("java.lang.Short", Short.class); + boxedPrimitivesMap.put("java.lang.Long", Long.class); + boxedPrimitivesMap.put("java.lang.Byte", Byte.class); + boxedPrimitivesMap.put("java.lang.Character", Character.class); + boxedPrimitivesMap.put("java.lang.Float", Float.class); + boxedPrimitivesMap.put("java.lang.Double", Double.class); + + boxedTypesMap = new HashMap<String, Class<?>>(); + boxedTypesMap.put("int", Integer.class); + boxedTypesMap.put("short", Short.class); + boxedTypesMap.put("long", Long.class); + boxedTypesMap.put("byte", Byte.class); + boxedTypesMap.put("char", Character.class); + boxedTypesMap.put("float", Float.class); + boxedTypesMap.put("double", Double.class); + + } + + @Override + protected boolean matchesSubtypes(ResolvedType type) { + boolean match = super.matchesSubtypes(type); + if (match) { + return match; + } + // are we dealing with array funkyness - pattern might be jlObject[]+ and type jlString[] + if (type.isArray() && this.type.isArray()) { + ResolvedType componentType = type.getComponentType().resolve(type.getWorld()); + UnresolvedType newPatternType = this.type.getComponentType(); + ExactTypePattern etp = new ExactTypePattern(newPatternType, includeSubtypes, false); + return etp.matchesSubtypes(componentType, type); + } + return match; + } + + public ExactTypePattern(UnresolvedType type, boolean includeSubtypes, boolean isVarArgs) { + super(includeSubtypes, isVarArgs); + this.type = type; + } + + @Override + public boolean isArray() { + return type.isArray(); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern) + */ + @Override + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + if (super.couldEverMatchSameTypesAs(other)) { + return true; + } + // false is necessary but not sufficient + UnresolvedType otherType = other.getExactType(); + if (!ResolvedType.isMissing(otherType)) { + return type.equals(otherType); + } + if (other instanceof WildTypePattern) { + WildTypePattern owtp = (WildTypePattern) other; + String yourSimpleNamePrefix = owtp.getNamePatterns()[0].maybeGetSimpleName(); + if (yourSimpleNamePrefix != null) { + return (type.getName().startsWith(yourSimpleNamePrefix)); + } + } + return true; + } + + @Override + protected boolean matchesExactly(ResolvedType matchType) { + boolean typeMatch = this.type.equals(matchType); + if (!typeMatch && (matchType.isParameterizedType() || matchType.isGenericType())) { + typeMatch = this.type.equals(matchType.getRawType()); + } + if (!typeMatch && matchType.isTypeVariableReference()) { + typeMatch = matchesTypeVariable((TypeVariableReferenceType) matchType); + } + if (!typeMatch) { + return false; + } + annotationPattern.resolve(matchType.getWorld()); + boolean annMatch = false; + if (matchType.temporaryAnnotationTypes != null) { + annMatch = annotationPattern.matches(matchType, matchType.temporaryAnnotationTypes).alwaysTrue(); + } else { + annMatch = annotationPattern.matches(matchType).alwaysTrue(); + } + return (typeMatch && annMatch); + } + + private boolean matchesTypeVariable(TypeVariableReferenceType matchType) { + // was this method previously coded to return false *on purpose* ?? pr124808 + return this.type.equals(((TypeVariableReference) matchType).getTypeVariable().getFirstBound()); + // return false; + } + + @Override + protected boolean matchesExactly(ResolvedType matchType, ResolvedType annotatedType) { + boolean typeMatch = this.type.equals(matchType); + if (!typeMatch && (matchType.isParameterizedType() || matchType.isGenericType())) { + typeMatch = this.type.equals(matchType.getRawType()); + } + if (!typeMatch && matchType.isTypeVariableReference()) { + typeMatch = matchesTypeVariable((TypeVariableReferenceType) matchType); + } + annotationPattern.resolve(matchType.getWorld()); + boolean annMatch = false; + if (annotatedType.temporaryAnnotationTypes != null) { + annMatch = annotationPattern.matches(annotatedType, annotatedType.temporaryAnnotationTypes).alwaysTrue(); + } else { + annMatch = annotationPattern.matches(annotatedType).alwaysTrue(); + } + return (typeMatch && annMatch); + } + + public UnresolvedType getType() { + return type; + } + + public ResolvedType getResolvedExactType(World world) { + if (resolvedType == null) { + resolvedType = world.resolve(type); + } + return resolvedType; + } + + @Override + public boolean isVoid() { + if (!checked) { + isVoid = this.type.getSignature().equals("V"); + checked = true; + } + return isVoid; + } + + // true if (matchType instanceof this.type) + @Override + public FuzzyBoolean matchesInstanceof(ResolvedType matchType) { + // in our world, Object is assignable from anything + annotationPattern.resolve(matchType.getWorld()); + if (type.equals(ResolvedType.OBJECT)) { + return FuzzyBoolean.YES.and(annotationPattern.matches(matchType)); + } + + ResolvedType resolvedType = type.resolve(matchType.getWorld()); + if (resolvedType.isAssignableFrom(matchType)) { + return FuzzyBoolean.YES.and(annotationPattern.matches(matchType)); + } + + // fix for PR 64262 - shouldn't try to coerce primitives + if (type.isPrimitiveType()) { + return FuzzyBoolean.NO; + } else { + return matchType.isCoerceableFrom(type.resolve(matchType.getWorld())) ? FuzzyBoolean.MAYBE : FuzzyBoolean.NO; + } + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ExactTypePattern)) { + return false; + } + if (other instanceof BindingTypePattern) { + return false; + } + ExactTypePattern o = (ExactTypePattern) other; + if (includeSubtypes != o.includeSubtypes) { + return false; + } + if (isVarArgs != o.isVarArgs) { + return false; + } + if (!typeParameters.equals(o.typeParameters)) { + return false; + } + return (o.type.equals(this.type) && o.annotationPattern.equals(this.annotationPattern)); + } + + @Override + public int hashCode() { + int result = 17; + result = 37 * result + type.hashCode(); + result = 37 * result + new Boolean(includeSubtypes).hashCode(); + result = 37 * result + new Boolean(isVarArgs).hashCode(); + result = 37 * result + typeParameters.hashCode(); + result = 37 * result + annotationPattern.hashCode(); + return result; + } + + private static final byte EXACT_VERSION = 1; // rev if changed + + @Override + public void write(CompressingDataOutputStream out) throws IOException { + out.writeByte(TypePattern.EXACT); + out.writeByte(EXACT_VERSION); + out.writeCompressedSignature(type.getSignature()); + out.writeBoolean(includeSubtypes); + out.writeBoolean(isVarArgs); + annotationPattern.write(out); + typeParameters.write(out); + writeLocation(out); + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + return readTypePattern150(s, context); + } else { + return readTypePatternOldStyle(s, context); + } + } + + public static TypePattern readTypePattern150(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte version = s.readByte(); + if (version > EXACT_VERSION) { + throw new BCException("ExactTypePattern was written by a more recent version of AspectJ"); + } + TypePattern ret = new ExactTypePattern(s.isAtLeast169() ? s.readSignatureAsUnresolvedType() : UnresolvedType.read(s), s + .readBoolean(), s.readBoolean()); + ret.setAnnotationTypePattern(AnnotationTypePattern.read(s, context)); + ret.setTypeParameters(TypePatternList.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + public static TypePattern readTypePatternOldStyle(DataInputStream s, ISourceContext context) throws IOException { + TypePattern ret = new ExactTypePattern(UnresolvedType.read(s), s.readBoolean(), false); + ret.readLocation(context, s); + return ret; + } + + @Override + public String toString() { + StringBuffer buff = new StringBuffer(); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append('('); + buff.append(annotationPattern.toString()); + buff.append(' '); + } + String typeString = type.toString(); + if (isVarArgs) { + typeString = typeString.substring(0, typeString.lastIndexOf('[')); + } + buff.append(typeString); + if (includeSubtypes) { + buff.append('+'); + } + if (isVarArgs) { + buff.append("..."); + } + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append(')'); + } + return buff.toString(); + } + + @Override + public TypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding, boolean requireExactType) { + throw new BCException("trying to re-resolve"); + } + + /** + * return a version of this type pattern with all type variables references replaced by the corresponding entry in the map. + */ + @Override + public TypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + UnresolvedType newType = type; + if (type.isTypeVariableReference()) { + TypeVariableReference t = (TypeVariableReference) type; + String key = t.getTypeVariable().getName(); + if (typeVariableMap.containsKey(key)) { + newType = (UnresolvedType) typeVariableMap.get(key); + } + } else if (type.isParameterizedType()) { + newType = w.resolve(type).parameterize(typeVariableMap); + } + ExactTypePattern ret = new ExactTypePattern(newType, includeSubtypes, isVarArgs); + ret.annotationPattern = annotationPattern.parameterizeWith(typeVariableMap, w); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ExposedState.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ExposedState.java new file mode 100644 index 000000000..2a9807118 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ExposedState.java @@ -0,0 +1,117 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.util.Arrays; + +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Var; + +public class ExposedState { + public static final boolean[] NO_ERRONEOUS_VARS = new boolean[0]; + public Var[] vars; + private boolean[] erroneousVars; + private Expr aspectInstance; + private UnresolvedType[] expectedVarTypes; // enables us to check that binding is occurring with the *right* types + private ResolvedType concreteAspect; + + public ExposedState(int size) { + super(); + if (size == 0) { + vars = Var.NONE; + erroneousVars = NO_ERRONEOUS_VARS; + } else { + vars = new Var[size]; + erroneousVars = new boolean[size]; + + } + } + + public ExposedState(Member signature) { + // XXX there maybe something about target for non-static sigs + this(signature.getParameterTypes().length); + expectedVarTypes = new UnresolvedType[signature.getParameterTypes().length]; + if (expectedVarTypes.length > 0) { + for (int i = 0; i < signature.getParameterTypes().length; i++) { + expectedVarTypes[i] = signature.getParameterTypes()[i]; + } + } + + } + + public boolean isFullySetUp() { + for (int i = 0; i < vars.length; i++) { + if (vars[i] == null) + return false; + } + return true; + } + + public void set(int i, Var var) { + // check the type is OK if we can... these are the same rules as in matchesInstanceOf() processing + if (expectedVarTypes != null) { + ResolvedType expected = expectedVarTypes[i].resolve(var.getType().getWorld()); + if (!expected.equals(ResolvedType.OBJECT)) { + if (!expected.isAssignableFrom(var.getType())) { + if (!var.getType().isCoerceableFrom(expected)) { + // throw new + // BCException("Expected type "+expectedVarTypes[i]+" in slot "+i+" but attempt to put "+var.getType()+" into it"); + return; + } + } + } + } + vars[i] = var; + } + + public Var get(int i) { + return vars[i]; + } + + public int size() { + return vars.length; + } + + public Expr getAspectInstance() { + return aspectInstance; + } + + public void setAspectInstance(Expr aspectInstance) { + this.aspectInstance = aspectInstance; + } + + public String toString() { + return "ExposedState(#Vars=" + vars.length + ",Vars=" + Arrays.asList(vars) + ",AspectInstance=" + aspectInstance + ")"; + } + + // Set to true if we have reported an error message against it, + // prevents us blowing up in later code gen. + public void setErroneousVar(int formalIndex) { + erroneousVars[formalIndex] = true; + } + + public boolean isErroneousVar(int formalIndex) { + return erroneousVars[formalIndex]; + } + + public void setConcreteAspect(ResolvedType concreteAspect) { + this.concreteAspect = concreteAspect; + } + + public ResolvedType getConcreteAspect() { + return this.concreteAspect; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/FastMatchInfo.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/FastMatchInfo.java new file mode 100644 index 000000000..a18830832 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/FastMatchInfo.java @@ -0,0 +1,52 @@ +/* ******************************************************************* + * Copyright (c) 2004 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: + * Jim Hugunin initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.World; +import org.aspectj.weaver.Shadow.Kind; + +/** + * Represents a type that pointcuts may match. Fast match for a pointcut should quickly say NO if it can. + */ +public class FastMatchInfo { + private Kind kind; + private ResolvedType type; + public World world; + + public FastMatchInfo(ResolvedType type, Shadow.Kind kind, World world) { + this.type = type; + this.kind = kind; + this.world = world; + } + + /** + * kind can be null to indicate that all kinds should be considered. This is usually done as a first pass + * + * @return + */ + public Kind getKind() { + return kind; + } + + public ResolvedType getType() { + return type; + } + + @Override + public String toString() { + return "FastMatchInfo [type=" + type.getName() + "] [" + (kind == null ? "AllKinds" : "Kind=" + kind) + "]"; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/FormalBinding.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/FormalBinding.java new file mode 100644 index 000000000..73693f07c --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/FormalBinding.java @@ -0,0 +1,82 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import org.aspectj.weaver.IHasPosition; +import org.aspectj.weaver.UnresolvedType; + +public class FormalBinding implements IHasPosition { + private final UnresolvedType type; + private final String name; + private final int index; + private final int start, end; + + public FormalBinding(UnresolvedType type, String name, int index, int start, int end) { + this.type = type; + this.name = name; + this.index = index; + this.start = start; + this.end = end; + } + + public FormalBinding(UnresolvedType type, int index) { + this(type, "unknown", index, 0, 0); + } + + public FormalBinding(UnresolvedType type, String name, int index) { + this(type, name, index, 0, 0); + } + + // ---- + + public String toString() { + return type.toString() + ":" + index; + } + + public int getEnd() { + return end; + } + + public int getStart() { + return start; + } + + public int getIndex() { + return index; + } + + public String getName() { + return name; + } + + public UnresolvedType getType() { + return type; + } + + // ---- + + public static final FormalBinding[] NONE = new FormalBinding[0]; + + /** + * A marker class for bindings for which we want to ignore unbound issue and consider them as implicit binding - f.e. to handle + * JoinPoint in @AJ advices + * + * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> + */ + public static class ImplicitFormalBinding extends FormalBinding { + public ImplicitFormalBinding(UnresolvedType type, String name, int index) { + super(type, name, index); + } + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HandlerPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HandlerPointcut.java new file mode 100644 index 000000000..3f92ab04a --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HandlerPointcut.java @@ -0,0 +1,141 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +/** + * This is a kind of KindedPointcut. This belongs either in a hierarchy with it or in a new place to share code with other potential + * future statement-level pointcuts like synchronized and throws + */ +public class HandlerPointcut extends Pointcut { + TypePattern exceptionType; + + private static final int MATCH_KINDS = Shadow.ExceptionHandler.bit; + + public HandlerPointcut(TypePattern exceptionType) { + this.exceptionType = exceptionType; + this.pointcutKind = HANDLER; + } + + public int couldMatchKinds() { + return MATCH_KINDS; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + // ??? should be able to do better by finding all referenced types in type + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + if (shadow.getKind() != Shadow.ExceptionHandler) { + return FuzzyBoolean.NO; + } + + exceptionType.resolve(shadow.getIWorld()); + + // we know we have exactly one parameter since we're checking an exception handler + return exceptionType.matches(shadow.getSignature().getParameterTypes()[0].resolve(shadow.getIWorld()), TypePattern.STATIC); + } + + public Pointcut parameterizeWith(Map typeVariableMap, World w) { + HandlerPointcut ret = new HandlerPointcut(exceptionType.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + public boolean equals(Object other) { + if (!(other instanceof HandlerPointcut)) { + return false; + } + HandlerPointcut o = (HandlerPointcut) other; + return o.exceptionType.equals(this.exceptionType); + } + + public int hashCode() { + int result = 17; + result = 37 * result + exceptionType.hashCode(); + return result; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("handler("); + buf.append(exceptionType.toString()); + buf.append(")"); + return buf.toString(); + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.HANDLER); + exceptionType.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + HandlerPointcut ret = new HandlerPointcut(TypePattern.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + // XXX note: there is no namebinding in any kinded pointcut. + // still might want to do something for better error messages + // We want to do something here to make sure we don't sidestep the parameter + // list in capturing type identifiers. + public void resolveBindings(IScope scope, Bindings bindings) { + exceptionType = exceptionType.resolveBindings(scope, bindings, false, false); + boolean invalidParameterization = false; + if (exceptionType.getTypeParameters().size() > 0) { + invalidParameterization = true; + } + UnresolvedType exactType = exceptionType.getExactType(); + if (exactType != null && exactType.isParameterizedType()) { + invalidParameterization = true; + } + if (invalidParameterization) { + // no parameterized or generic types for handler + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.HANDLER_PCD_DOESNT_SUPPORT_PARAMETERS), + getSourceLocation())); + } + // XXX add error if exact binding and not an exception + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + Pointcut ret = new HandlerPointcut(exceptionType); + ret.copyLocationFrom(this); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HasMemberTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HasMemberTypePattern.java new file mode 100644 index 000000000..2b1f28fd4 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HasMemberTypePattern.java @@ -0,0 +1,196 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * Nieraj Singh + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ConcreteTypeMunger; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +/** + * @author colyer Matches types that have a certain method / constructor / field Currently only allowed within declare parents and + * declare @type + */ +public class HasMemberTypePattern extends TypePattern { + + private SignaturePattern signaturePattern; + + public HasMemberTypePattern(SignaturePattern aSignaturePattern) { + super(false, false); + this.signaturePattern = aSignaturePattern; + } + + @Override + protected boolean matchesExactly(ResolvedType type) { + if (signaturePattern.getKind() == Member.FIELD) { + return hasField(type); + } else { + return hasMethod(type); + } + } + + public ISignaturePattern getSignaturePattern() { + return signaturePattern; + } + + private final static String declareAtPrefix = "ajc$declare_at"; + + private boolean hasField(ResolvedType type) { + // TODO what about ITDs + World world = type.getWorld(); + for (Iterator iter = type.getFields(); iter.hasNext();) { + Member field = (Member) iter.next(); + if (field.getName().startsWith(declareAtPrefix)) { + continue; + } + if (signaturePattern.matches(field, type.getWorld(), false)) { + if (field.getDeclaringType().resolve(world) != type) { + if (Modifier.isPrivate(field.getModifiers())) { + continue; + } + } + return true; + } + } + return false; + } + + protected boolean hasMethod(ResolvedType type) { + // TODO what about ITDs + World world = type.getWorld(); + for (Iterator<ResolvedMember> iter = type.getMethods(true, true); iter.hasNext();) { + Member method = (Member) iter.next(); + if (method.getName().startsWith(declareAtPrefix)) { + continue; + } + if (signaturePattern.matches(method, type.getWorld(), false)) { + ResolvedType declaringType = method.getDeclaringType().resolve(world); + if (declaringType != type) { + if (Modifier.isPrivate(method.getModifiers())) { + continue; + } + } + // J9: Object.finalize() is marked Deprecated it seems... triggers unhelpful messages + if (method.getName().equals("finalize") && declaringType.equals(ResolvedType.OBJECT) + && (signaturePattern.getAnnotationPattern() instanceof ExactAnnotationTypePattern) + && ((ExactAnnotationTypePattern)signaturePattern.getAnnotationPattern()).getAnnotationType().getSignature().equals("Ljava/lang/Deprecated;")) { + continue; + } + return true; + } + } + // try itds before we give up (this doesnt find annotations - the signature returned may not include them) + List<ConcreteTypeMunger> mungers = type.getInterTypeMungersIncludingSupers(); + for (Iterator<ConcreteTypeMunger> iter = mungers.iterator(); iter.hasNext();) { + ConcreteTypeMunger munger = iter.next(); + Member member = munger.getSignature(); + if (signaturePattern.matches(member, type.getWorld(), false)) { + if (!Modifier.isPublic(member.getModifiers())) { + continue; + } + return true; + } + } + return false; + } + + @Override + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + return matchesExactly(type); + } + + @Override + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + throw new UnsupportedOperationException("hasmethod/field do not support instanceof matching"); + } + + @Override + public TypePattern parameterizeWith(Map typeVariableMap, World w) { + HasMemberTypePattern ret = new HasMemberTypePattern(signaturePattern.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public TypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding, boolean requireExactType) { + // check that hasmember type patterns are allowed! + if (!scope.getWorld().isHasMemberSupportEnabled()) { + String msg = WeaverMessages.format(WeaverMessages.HAS_MEMBER_NOT_ENABLED, this.toString()); + scope.message(IMessage.ERROR, this, msg); + } + signaturePattern.resolveBindings(scope, bindings); + return this; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof HasMemberTypePattern)) { + return false; + } + if (this == obj) { + return true; + } + return signaturePattern.equals(((HasMemberTypePattern) obj).signaturePattern); + } + + @Override + public int hashCode() { + return signaturePattern.hashCode(); + } + + @Override + public String toString() { + StringBuffer buff = new StringBuffer(); + if (signaturePattern.getKind() == Member.FIELD) { + buff.append("hasfield("); + } else { + buff.append("hasmethod("); + } + buff.append(signaturePattern.toString()); + buff.append(")"); + return buff.toString(); + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(TypePattern.HAS_MEMBER); + signaturePattern.write(s); + writeLocation(s); + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + SignaturePattern sp = SignaturePattern.read(s, context); + HasMemberTypePattern ret = new HasMemberTypePattern(sp); + ret.readLocation(context, s); + return ret; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HasMemberTypePatternFinder.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HasMemberTypePatternFinder.java new file mode 100644 index 000000000..5fb59503f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HasMemberTypePatternFinder.java @@ -0,0 +1,33 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +/** + * @author colyer + * usage : new HasMemberTypePatternFinder(pattern).hasMemberTypePattern() + */ +public class HasMemberTypePatternFinder extends AbstractPatternNodeVisitor { + + private boolean hasMemberTypePattern = false; + + public HasMemberTypePatternFinder(TypePattern aPattern) { + aPattern.traverse(this, null); + } + + public Object visit(HasMemberTypePattern node, Object data) { + hasMemberTypePattern = true; + return null; + } + + public boolean hasMemberTypePattern() { return hasMemberTypePattern; } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HasMemberTypePatternForPerThisMatching.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HasMemberTypePatternForPerThisMatching.java new file mode 100644 index 000000000..128338f8e --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HasMemberTypePatternForPerThisMatching.java @@ -0,0 +1,65 @@ +/* ******************************************************************* + * Copyright (c) 2011 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.List; + +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ConcreteTypeMunger; +import org.aspectj.weaver.ResolvedType; + +/** + * pr354470. This is a special subtype of HasMemberTypePattern. In order to optimize this situation: <br> + * <code><pre> + * aspect X perthis(transactional()) {<br> + * pointcut transactional: execution(@Foo * *(..));<br> + * </pre></code> + * <p> + * When this occurs we obviously only want an aspect instance when there is a method annotated with @Foo. For a regular execution + * pointcut we couldn't really do this due to the multiple joinpoint signatures for each joinpoint (and so lots of types get the + * ajcMightHaveAspect interface). However, for an execution pointcut involving an annotation we can do something clever. Annotations + * must match against the first primary joinpoint signature - so when computing the type pattern to use for matching when processing + * the perthis() clause above, we can use the HasMemberTypePattern - because that effectively does what we want. We want an aspect + * instance if the type hasmethod(...) with the appropriate annotation. This would be great... but breaks in the face of ITDs. If + * the method that hasmethod() would match is introduced via an ITD we come unstuck, the code in HasMemberTypePattern.hasMethod() + * does look at ITDs but it won't see annotations, they aren't visible (at least through EclipseResolvedMember objects). And so this + * subclass is created to say 'if the supertype thinks it is a match, great, but if it doesnt then if there are ITDs on the target, + * they might match so just say 'true''. Note that returning true is just confirming whether the 'mightHaveAspect' interface (and + * friends) are getting added. + * + * @author Andy Clement + */ +public class HasMemberTypePatternForPerThisMatching extends HasMemberTypePattern { + + public HasMemberTypePatternForPerThisMatching(SignaturePattern aSignaturePattern) { + super(aSignaturePattern); + } + + protected boolean hasMethod(ResolvedType type) { + boolean b = super.hasMethod(type); + if (b) { + return true; + } + // If there are ITDs, have to be safe and just assume one of them might match + List<ConcreteTypeMunger> mungers = type.getInterTypeMungersIncludingSupers(); + if (mungers.size() != 0) { + return true; + } + return false; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + throw new IllegalAccessError("Should never be called, these are transient and don't get serialized"); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor.java new file mode 100644 index 000000000..0b2b9b5fc --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor.java @@ -0,0 +1,50 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import org.aspectj.weaver.UnresolvedType; + +/** + * @author colyer + * + */ +public class HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor + extends AbstractPatternNodeVisitor { + + boolean ohYesItHas = false; + + /** + * Is the Exact type parameterized? + * Generic is ok as that just means we resolved a simple type pattern to a generic type + */ + public Object visit(ExactTypePattern node, Object data) { + UnresolvedType theExactType = node.getExactType(); + if (theExactType.isParameterizedType()) ohYesItHas = true; + //if (theExactType.isGenericType()) ohYesItHas = true; + return data; + } + + /** + * Any type bounds are bad. + * Type parameters are right out. + */ + public Object visit(WildTypePattern node, Object data) { + if (node.getUpperBound() != null) ohYesItHas = true; + if (node.getLowerBound() != null) ohYesItHas = true; + if (node.getTypeParameters().size() != 0) ohYesItHas = true; + return data; + } + + public boolean wellHasItThen/*?*/() { + return ohYesItHas; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/IScope.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/IScope.java new file mode 100644 index 000000000..69ba19686 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/IScope.java @@ -0,0 +1,59 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.weaver.IHasPosition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +public interface IScope { + + /** + * @return the type corresponding to the name in this scope, or ResolvedType.MISSING if no such type exists + */ + UnresolvedType lookupType(String name, IHasPosition location); + + World getWorld(); + + ResolvedType getEnclosingType(); + + // these next three are used to create {@link BindingTypePattern} objects. + IMessageHandler getMessageHandler(); + + /** + * @return the formal associated with the name, or null if no such formal exists + */ + FormalBinding lookupFormal(String name); + + /** + * @return the formal with the index. Throws ArrayOutOfBounds exception if out of bounds + */ + FormalBinding getFormal(int i); + + int getFormalCount(); + + String[] getImportedPrefixes(); + + String[] getImportedNames(); + + void message(IMessage.Kind kind, IHasPosition location, String message); + + void message(IMessage.Kind kind, IHasPosition location1, IHasPosition location2, String message); + + void message(IMessage aMessage); + + // ISourceLocation makeSourceLocation(ILocation location); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ISignaturePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ISignaturePattern.java new file mode 100644 index 000000000..99ad99377 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ISignaturePattern.java @@ -0,0 +1,32 @@ +package org.aspectj.weaver.patterns; + +import java.util.List; +import java.util.Map; + +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +public interface ISignaturePattern { + + byte PATTERN = 1; + byte NOT = 2; + byte OR = 3; + byte AND = 4; + + boolean matches(Member member, World world, boolean b); + + ISignaturePattern parameterizeWith(Map<String, UnresolvedType> typeVariableBindingMap, World world); + + ISignaturePattern resolveBindings(IScope scope, Bindings none); + + List<ExactTypePattern> getExactDeclaringTypes(); + + boolean isMatchOnAnyName(); + + boolean couldEverMatch(ResolvedType type); + + boolean isStarAnnotation(); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/IToken.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/IToken.java new file mode 100644 index 000000000..aa925fd69 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/IToken.java @@ -0,0 +1,53 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.patterns; + +import org.aspectj.weaver.IHasPosition; + +public interface IToken extends IHasPosition { + public static final IToken EOF = BasicToken.makeOperator("<eof>", 0, 0); + + /** + * Returns the string value of this token. + * + * If isIdentifier is false, then this string must be intern'd + * so that == matching can be used. + * + * If isIdentifier is true, interning is not required. + */ + public String getString(); + + /** + * Whether this should be treated as a token or a generic identifier + */ + public boolean isIdentifier(); + + /** + * Whether this should be treated as a literal value + * + * Kinds == "string", ??? + * + * returns null if this isn't a literal + */ + public String getLiteralKind(); + + + /** + * If this token represents a pre-parsed Pointcut, then return it; + * otherwise returns null. + * + * Needed for the implementation of 'if' + */ + public Pointcut maybeGetParsedPointcut(); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ITokenSource.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ITokenSource.java new file mode 100644 index 000000000..4649b046f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ITokenSource.java @@ -0,0 +1,27 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.patterns; + +import org.aspectj.weaver.ISourceContext; + + +public interface ITokenSource { + public IToken next(); + public IToken peek(); + public IToken peek(int offset); + public int getIndex(); + public void setIndex(int newIndex); + public ISourceContext getSourceContext(); + public boolean hasMoreTokens(); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/IVerificationRequired.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/IVerificationRequired.java new file mode 100644 index 000000000..f1ce19eff --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/IVerificationRequired.java @@ -0,0 +1,22 @@ +/* ******************************************************************* + * Copyright (c) 2006 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 IBM initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + + +/** + * Implementors provide a 'verify()' method that is invoked at the end of type + * binding completion. + * @see WildTypePattern.VerifyBoundsForTypePattern + */ +public interface IVerificationRequired { + public void verify(); +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/IfPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/IfPointcut.java new file mode 100644 index 000000000..a845153f9 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/IfPointcut.java @@ -0,0 +1,614 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * Alexandre Vasseur if() implementation for @AJ style + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; + +public class IfPointcut extends Pointcut { + public ResolvedMember testMethod; + public int extraParameterFlags; + + /** + * A token source dump that looks like a pointcut (but is NOT parseable at all - just here to help debugging etc) + */ + private final String enclosingPointcutHint; + + public Pointcut residueSource; + int baseArgsCount; + + // XXX some way to compute args + + public IfPointcut(ResolvedMember testMethod, int extraParameterFlags) { + this.testMethod = testMethod; + this.extraParameterFlags = extraParameterFlags; + this.pointcutKind = IF; + this.enclosingPointcutHint = null; + } + + /** + * No-arg constructor for @AJ style, where the if() body is actually the @Pointcut annotated method + */ + public IfPointcut(String enclosingPointcutHint) { + this.pointcutKind = IF; + this.enclosingPointcutHint = enclosingPointcutHint; + this.testMethod = null;// resolved during concretize + this.extraParameterFlags = -1;// allows to keep track of the @Aj style + } + + @Override + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + @Override + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + @Override + protected FuzzyBoolean matchInternal(Shadow shadow) { + if ((extraParameterFlags & Advice.ConstantReference) != 0) { + if ((extraParameterFlags & Advice.ConstantValue) != 0) { + return FuzzyBoolean.YES; + } else { + return FuzzyBoolean.NO; + } + } + // ??? this is not maximally efficient + return FuzzyBoolean.MAYBE; + } + + public boolean alwaysFalse() { + return false; + } + + public boolean alwaysTrue() { + return false; + } + + // enh 76055 + public Pointcut getResidueSource() { + return residueSource; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.IF); + s.writeBoolean(testMethod != null); // do we have a test method? + if (testMethod != null) { + testMethod.write(s); + } + s.writeByte(extraParameterFlags); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + boolean hasTestMethod = s.readBoolean(); + ResolvedMember resolvedTestMethod = null; + if (hasTestMethod) { // should always have a test method unless @AJ style + resolvedTestMethod = ResolvedMemberImpl.readResolvedMember(s, context); + } + IfPointcut ret = new IfPointcut(resolvedTestMethod, s.readByte()); + ret.readLocation(context, s); + return ret; + } + + @Override + public void resolveBindings(IScope scope, Bindings bindings) { + // ??? all we need is good error messages in here in cflow contexts + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof IfPointcut)) { + return false; + } + IfPointcut o = (IfPointcut) other; + if (o.testMethod == null) { + return (this.testMethod == null); + } + return o.testMethod.equals(this.testMethod); + } + + @Override + public int hashCode() { + int result = 17; + result = 37 * result + testMethod.hashCode(); + return result; + } + + @Override + public String toString() { + if (extraParameterFlags < 0) { + // @AJ style + return "if()"; + } else { + return "if(" + testMethod + ")";// FIXME AV - bad, this makes it unparsable. Perhaps we can use if() for code style + // behind the scene! + } + } + + // ??? The implementation of name binding and type checking in if PCDs is very convoluted + // There has to be a better way... + private boolean findingResidue = false; + + // Similar to lastMatchedShadowId - but only for if PCDs. + private int ifLastMatchedShadowId; + private Test ifLastMatchedShadowResidue; + + /** + * At each shadow that matched, the residue can be different. + */ + @Override + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + if (findingResidue) { + return Literal.TRUE; + } + findingResidue = true; + try { + + // Have we already been asked this question? + if (shadow.shadowId == ifLastMatchedShadowId) { + return ifLastMatchedShadowResidue; + } + + Test ret = Literal.TRUE; + List<Var> args = new ArrayList<Var>(); + + // code style + if (extraParameterFlags >= 0) { + if ((extraParameterFlags & Advice.ConstantReference) != 0) { + // it is either always true or always false, no need for test + if ((extraParameterFlags & Advice.ConstantValue) != 0) { + ret = Literal.TRUE; + ifLastMatchedShadowId = shadow.shadowId; + ifLastMatchedShadowResidue = ret; + return ret; + } else { + // Dont think we should be in here as the match cannot have succeeded... + ret = Literal.FALSE; + ifLastMatchedShadowId = shadow.shadowId; + ifLastMatchedShadowResidue = ret; + return ret; + } + } + + // If there are no args to sort out, don't bother with the recursive call + if (baseArgsCount > 0) { + ExposedState myState = new ExposedState(baseArgsCount); + myState.setConcreteAspect(state.getConcreteAspect()); + // ??? we throw out the test that comes from this walk. All we want here + // is bindings for the arguments + residueSource.findResidue(shadow, myState); + + UnresolvedType[] pTypes = (testMethod == null ? null : testMethod.getParameterTypes()); + if (pTypes != null && baseArgsCount > pTypes.length) { // pr155347 + throw new BCException("Unexpected problem with testMethod " + testMethod + ": expecting " + baseArgsCount + + " arguments"); + } + // pr118149 + // It is possible for vars in myState (which would normally be set + // in the call to residueSource.findResidue) to not be set (be null) + // in an Or pointcut with if expressions in both branches, and where + // one branch is known statically to not match. In this situation we + // simply return Test. + for (int i = 0; i < baseArgsCount; i++) { + Var v = myState.get(i); + if (v == null) { + continue; // pr118149 + } + args.add(v); + ret = Test.makeAnd(ret, Test.makeInstanceof(v, pTypes[i].resolve(shadow.getIWorld()))); + } + } + + // handle thisJoinPoint parameters + if ((extraParameterFlags & Advice.ThisJoinPoint) != 0) { + args.add(shadow.getThisJoinPointVar()); + } + + if ((extraParameterFlags & Advice.ThisJoinPointStaticPart) != 0) { + args.add(shadow.getThisJoinPointStaticPartVar()); + } + + if ((extraParameterFlags & Advice.ThisEnclosingJoinPointStaticPart) != 0) { + args.add(shadow.getThisEnclosingJoinPointStaticPartVar()); + } + + if ((extraParameterFlags & Advice.ThisAspectInstance) != 0) { + args.add(shadow.getThisAspectInstanceVar(state.getConcreteAspect())); + } + } else { + // @style is slightly different + int currentStateIndex = 0; + // FIXME AV - "args(jp)" test(jp, thejp) will fail here + for (int i = 0; i < testMethod.getParameterTypes().length; i++) { + String argSignature = testMethod.getParameterTypes()[i].getSignature(); + if (AjcMemberMaker.TYPEX_JOINPOINT.getSignature().equals(argSignature)) { + args.add(shadow.getThisJoinPointVar()); + } else if (AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.getSignature().equals(argSignature)) { + args.add(shadow.getThisJoinPointVar()); + } else if (AjcMemberMaker.TYPEX_STATICJOINPOINT.getSignature().equals(argSignature)) { + args.add(shadow.getThisJoinPointStaticPartVar()); + } else if (AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.getSignature().equals(argSignature)) { + args.add(shadow.getThisEnclosingJoinPointStaticPartVar()); + } else { + if (state.size() == 0 || currentStateIndex > state.size()) { + String[] paramNames = testMethod.getParameterNames(); + StringBuffer errorParameter = new StringBuffer(); + if (paramNames != null) { + errorParameter.append(testMethod.getParameterTypes()[i].getName()).append(" "); + errorParameter.append(paramNames[i]); + shadow.getIWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.error("Missing binding for if() pointcut method. Parameter " + (i + 1) + + "(" + errorParameter.toString() + + ") must be bound - even in reference pointcuts (compiler limitation)", + testMethod.getSourceLocation())); + } else { + shadow.getIWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.error("Missing binding for if() pointcut method. Parameter " + (i + 1) + + " must be bound - even in reference pointcuts (compiler limitation)", + testMethod.getSourceLocation())); + } + return Literal.TRUE; // exit quickly + } + // we don't use i as JoinPoint.* can be anywhere in the signature in @style + Var v = state.get(currentStateIndex++); + + while (v == null && currentStateIndex < state.size()) { // pr162135 + v = state.get(currentStateIndex++); + } + args.add(v); + + ret = Test.makeAnd(ret, + Test.makeInstanceof(v, testMethod.getParameterTypes()[i].resolve(shadow.getIWorld()))); + } + } + } + + ret = Test.makeAnd(ret, Test.makeCall(testMethod, (Expr[]) args.toArray(new Expr[args.size()]))); + + // Remember... + ifLastMatchedShadowId = shadow.shadowId; + ifLastMatchedShadowResidue = ret; + return ret; + } finally { + findingResidue = false; + } + } + + // amc - the only reason this override seems to be here is to stop the copy, but + // that can be prevented by overriding shouldCopyLocationForConcretization, + // allowing me to make the method final in Pointcut. + // public Pointcut concretize(ResolvedType inAspect, IntMap bindings) { + // return this.concretize1(inAspect, bindings); + // } + + @Override + protected boolean shouldCopyLocationForConcretize() { + return false; + } + + private IfPointcut partiallyConcretized = null; + + @Override + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + // System.err.println("concretize: " + this + " already: " + partiallyConcretized); + + if (isDeclare(bindings.getEnclosingAdvice())) { + // Enforce rule about which designators are supported in declare + inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_DECLARE), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + if (partiallyConcretized != null) { + return partiallyConcretized; + } + + final IfPointcut ret; + if (extraParameterFlags < 0 && testMethod == null) { + // @AJ style, we need to find the testMethod in the aspect defining the "if()" enclosing pointcut + ResolvedPointcutDefinition def = bindings.peekEnclosingDefinition(); + if (def != null) { + ResolvedType aspect = inAspect.getWorld().resolve(def.getDeclaringType()); + for (Iterator memberIter = aspect.getMethods(true, true); memberIter.hasNext();) { + ResolvedMember method = (ResolvedMember) memberIter.next(); + if (def.getName().equals(method.getName()) + && def.getParameterTypes().length == method.getParameterTypes().length) { + boolean sameSig = true; + for (int j = 0; j < method.getParameterTypes().length; j++) { + UnresolvedType argJ = method.getParameterTypes()[j]; + if (!argJ.equals(def.getParameterTypes()[j])) { + sameSig = false; + break; + } + } + if (sameSig) { + testMethod = method; + break; + } + } + } + if (testMethod == null) { + inAspect.getWorld().showMessage(IMessage.ERROR, + "Cannot find if() body from '" + def.toString() + "' for '" + enclosingPointcutHint + "'", + this.getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + } else { + testMethod = inAspect.getWorld().resolve(bindings.getAdviceSignature()); + } + ret = new IfPointcut(enclosingPointcutHint); + ret.testMethod = testMethod; + } else { + ret = new IfPointcut(testMethod, extraParameterFlags); + } + ret.copyLocationFrom(this); + partiallyConcretized = ret; + + // It is possible to directly code your pointcut expression in a per clause + // rather than defining a pointcut declaration and referencing it in your + // per clause. If you do this, we have problems (bug #62458). For now, + // let's police that you are trying to code a pointcut in a per clause and + // put out a compiler error. + if (bindings.directlyInAdvice() && bindings.getEnclosingAdvice() == null) { + // Assumption: if() is in a per clause if we say we are directly in advice + // but we have no enclosing advice. + inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_PERCLAUSE), + this.getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + if (bindings.directlyInAdvice()) { + ShadowMunger advice = bindings.getEnclosingAdvice(); + if (advice instanceof Advice) { + ret.baseArgsCount = ((Advice) advice).getBaseParameterCount(); + } else { + ret.baseArgsCount = 0; + } + ret.residueSource = advice.getPointcut().concretize(inAspect, inAspect, ret.baseArgsCount, advice); + } else { + ResolvedPointcutDefinition def = bindings.peekEnclosingDefinition(); + if (def == CflowPointcut.CFLOW_MARKER) { + inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_LEXICALLY_IN_CFLOW), + getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + ret.baseArgsCount = def.getParameterTypes().length; + + // for @style, we have implicit binding for JoinPoint.* things + // FIXME AV - will lead to failure for "args(jp)" test(jp, thejp) / see args() implementation + if (ret.extraParameterFlags < 0) { + ret.baseArgsCount = 0; + for (int i = 0; i < testMethod.getParameterTypes().length; i++) { + String argSignature = testMethod.getParameterTypes()[i].getSignature(); + if (AjcMemberMaker.TYPEX_JOINPOINT.getSignature().equals(argSignature) + || AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.getSignature().equals(argSignature) + || AjcMemberMaker.TYPEX_STATICJOINPOINT.getSignature().equals(argSignature) + || AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.getSignature().equals(argSignature)) { + + } else { + ret.baseArgsCount++; + } + } + } + + IntMap newBindings = IntMap.idMap(ret.baseArgsCount); + newBindings.copyContext(bindings); + ret.residueSource = def.getPointcut().concretize(inAspect, declaringType, newBindings); + } + + return ret; + } + + // we can't touch "if" methods + @Override + public Pointcut parameterizeWith(Map typeVariableMap, World w) { + return this; + } + + // public static Pointcut MatchesNothing = new MatchesNothingPointcut(); + // ??? there could possibly be some good optimizations to be done at this point + public static IfPointcut makeIfFalsePointcut(State state) { + IfPointcut ret = new IfFalsePointcut(); + ret.state = state; + return ret; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public static class IfFalsePointcut extends IfPointcut { + + public IfFalsePointcut() { + super(null, 0); + this.pointcutKind = Pointcut.IF_FALSE; + } + + @Override + public int couldMatchKinds() { + return Shadow.NO_SHADOW_KINDS_BITS; + } + + @Override + public boolean alwaysFalse() { + return true; + } + + @Override + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return Literal.FALSE; // can only get here if an earlier error occurred + } + + @Override + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.NO; + } + + @Override + protected FuzzyBoolean matchInternal(Shadow shadow) { + return FuzzyBoolean.NO; + } + + @Override + public void resolveBindings(IScope scope, Bindings bindings) { + } + + @Override + public void postRead(ResolvedType enclosingType) { + } + + @Override + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + if (isDeclare(bindings.getEnclosingAdvice())) { + // Enforce rule about which designators are supported in declare + inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_DECLARE), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + return makeIfFalsePointcut(state); + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.IF_FALSE); + } + + @Override + public int hashCode() { + int result = 17; + return result; + } + + @Override + public String toString() { + return "if(false)"; + } + } + + public static IfPointcut makeIfTruePointcut(State state) { + IfPointcut ret = new IfTruePointcut(); + ret.state = state; + return ret; + } + + public static class IfTruePointcut extends IfPointcut { + + public IfTruePointcut() { + super(null, 0); + this.pointcutKind = Pointcut.IF_TRUE; + } + + @Override + public boolean alwaysTrue() { + return true; + } + + @Override + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return Literal.TRUE; // can only get here if an earlier error occurred + } + + @Override + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.YES; + } + + @Override + protected FuzzyBoolean matchInternal(Shadow shadow) { + return FuzzyBoolean.YES; + } + + @Override + public void resolveBindings(IScope scope, Bindings bindings) { + } + + @Override + public void postRead(ResolvedType enclosingType) { + } + + @Override + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + if (isDeclare(bindings.getEnclosingAdvice())) { + // Enforce rule about which designators are supported in declare + inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_DECLARE), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + return makeIfTruePointcut(state); + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(IF_TRUE); + } + + @Override + public int hashCode() { + int result = 37; + return result; + } + + @Override + public String toString() { + return "if(true)"; + } + } + + /** + * Called when it is determined that the pointcut refers to a constant value of TRUE or FALSE - enabling exact matching and no + * unnecessary calls to the method representing the if body. + */ + public void setAlways(boolean matches) { + extraParameterFlags |= Advice.ConstantReference; + if (matches) { + extraParameterFlags |= Advice.ConstantValue; + } + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/KindedPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/KindedPointcut.java new file mode 100644 index 000000000..56a45892d --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/KindedPointcut.java @@ -0,0 +1,478 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import static org.aspectj.util.FuzzyBoolean.MAYBE; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Checker; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +public class KindedPointcut extends Pointcut { + Shadow.Kind kind; + private SignaturePattern signature; + private int matchKinds; + + private ShadowMunger munger = null; // only set after concretization + + public KindedPointcut(Shadow.Kind kind, SignaturePattern signature) { + this.kind = kind; + this.signature = signature; + this.pointcutKind = KINDED; + this.matchKinds = kind.bit; + } + + public KindedPointcut(Shadow.Kind kind, SignaturePattern signature, ShadowMunger munger) { + this(kind, signature); + this.munger = munger; + } + + public SignaturePattern getSignature() { + return signature; + } + + @Override + public int couldMatchKinds() { + return matchKinds; + } + + public boolean couldEverMatchSameJoinPointsAs(KindedPointcut other) { + if (this.kind != other.kind) { + return false; + } + String myName = signature.getName().maybeGetSimpleName(); + String yourName = other.signature.getName().maybeGetSimpleName(); + if (myName != null && yourName != null) { + if (!myName.equals(yourName)) { + return false; + } + } + if (signature.getParameterTypes().ellipsisCount == 0) { + if (other.signature.getParameterTypes().ellipsisCount == 0) { + if (signature.getParameterTypes().getTypePatterns().length != other.signature.getParameterTypes().getTypePatterns().length) { + return false; + } + } + } + return true; + } + + @Override + public FuzzyBoolean fastMatch(FastMatchInfo info) { + // info.getKind()==null means all kinds + if (info.getKind() != null) { + if (info.getKind() != kind) { + return FuzzyBoolean.NO; + } + } + + // KindedPointcut represents these join points: + // method-execution/ctor-execution/method-call/ctor-call/field-get/field-set/advice-execution/static-initialization + // initialization/pre-initialization + + // Check if the global fastmatch flag is on - the flag can be removed (and this made default) once it proves stable! + if (info.world.optimizedMatching) { + + // For now, just consider MethodExecution and Initialization + if ((kind == Shadow.MethodExecution || kind == Shadow.Initialization) && info.getKind() == null) { + boolean fastMatchingOnAspect = info.getType().isAspect(); + // an Aspect may define ITDs, and although our signature declaring type pattern may not match on the + // aspect, the ITDs may have a different signature as we iterate through the members of the aspect. Let's not + // try and work through that here and just say MAYBE + if (fastMatchingOnAspect) { + return MAYBE; + } + // Aim here is to do the same test as is done for signature pattern declaring type pattern matching + if (this.getSignature().isExactDeclaringTypePattern()) { + ExactTypePattern typePattern = (ExactTypePattern) this.getSignature().getDeclaringType(); + // Interface checks are more expensive, they could be anywhere... + ResolvedType patternExactType = typePattern.getResolvedExactType(info.world); + if (patternExactType.isInterface()) { + ResolvedType curr = info.getType(); + Iterator<ResolvedType> hierarchyWalker = curr.getHierarchy(true, true); + boolean found = false; + while (hierarchyWalker.hasNext()) { + curr = hierarchyWalker.next(); + if (typePattern.matchesStatically(curr)) { + found = true; + break; + } + } + if (!found) { + return FuzzyBoolean.NO; + } + } else if (patternExactType.isClass()) { + ResolvedType curr = info.getType(); + do { + if (typePattern.matchesStatically(curr)) { + break; + } + curr = curr.getSuperclass(); + } while (curr != null); + if (curr == null) { + return FuzzyBoolean.NO; + } + } + } else if (this.getSignature().getDeclaringType() instanceof AnyWithAnnotationTypePattern) { + // aim here is to say NO if the annotation is not possible in the hierarchy here + ResolvedType type = info.getType(); + AnnotationTypePattern annotationTypePattern = ((AnyWithAnnotationTypePattern) getSignature().getDeclaringType()) + .getAnnotationPattern(); + if (annotationTypePattern instanceof ExactAnnotationTypePattern) { + ExactAnnotationTypePattern exactAnnotationTypePattern = (ExactAnnotationTypePattern) annotationTypePattern; + if (exactAnnotationTypePattern.getAnnotationValues() == null + || exactAnnotationTypePattern.getAnnotationValues().size() == 0) { + ResolvedType annotationType = exactAnnotationTypePattern.getAnnotationType().resolve(info.world); + if (type.hasAnnotation(annotationType)) { + return FuzzyBoolean.MAYBE; + } + if (annotationType.isInheritedAnnotation()) { + // ok - we may be picking it up from further up the hierarchy (but only a super*class*) + ResolvedType toMatchAgainst = type.getSuperclass(); + boolean found = false; + while (toMatchAgainst != null) { + if (toMatchAgainst.hasAnnotation(annotationType)) { + found = true; + break; + } + toMatchAgainst = toMatchAgainst.getSuperclass(); + } + if (!found) { + return FuzzyBoolean.NO; + } + } else { + return FuzzyBoolean.NO; + } + } + } + } else if (this.getSignature().getDeclaringType() instanceof WildTypePattern) { + final WildTypePattern pattern = (WildTypePattern) this.getSignature().getDeclaringType(); + final ResolvedType type = info.getType(); + return pattern.matches(type, TypePattern.STATIC); + } + } + } + + return FuzzyBoolean.MAYBE; + } + + @Override + protected FuzzyBoolean matchInternal(Shadow shadow) { + if (shadow.getKind() != kind) { + return FuzzyBoolean.NO; + } + + if (shadow.getKind() == Shadow.SynchronizationLock && kind == Shadow.SynchronizationLock) { + return FuzzyBoolean.YES; + } + if (shadow.getKind() == Shadow.SynchronizationUnlock && kind == Shadow.SynchronizationUnlock) { + return FuzzyBoolean.YES; + } + + if (!signature.matches(shadow.getMatchingSignature(), shadow.getIWorld(), this.kind == Shadow.MethodCall)) { + + if (kind == Shadow.MethodCall) { + warnOnConfusingSig(shadow); + // warnOnBridgeMethod(shadow); + } + return FuzzyBoolean.NO; + } + + return FuzzyBoolean.YES; + } + + // private void warnOnBridgeMethod(Shadow shadow) { + // if (shadow.getIWorld().getLint().noJoinpointsForBridgeMethods.isEnabled()) { + // ResolvedMember rm = shadow.getSignature().resolve(shadow.getIWorld()); + // if (rm!=null) { + // int shadowModifiers = rm.getModifiers(); //shadow.getSignature().getModifiers(shadow.getIWorld()); + // if (ResolvedType.hasBridgeModifier(shadowModifiers)) { + // shadow.getIWorld().getLint().noJoinpointsForBridgeMethods.signal(new String[]{},getSourceLocation(), + // new ISourceLocation[]{shadow.getSourceLocation()}); + // } + // } + // } + // } + + private void warnOnConfusingSig(Shadow shadow) { + // Don't do all this processing if we don't need to ! + if (!shadow.getIWorld().getLint().unmatchedSuperTypeInCall.isEnabled()) { + return; + } + + // no warnings for declare error/warning + if (munger instanceof Checker) { + return; + } + + World world = shadow.getIWorld(); + + // warning never needed if the declaring type is any + UnresolvedType exactDeclaringType = signature.getDeclaringType().getExactType(); + + ResolvedType shadowDeclaringType = shadow.getSignature().getDeclaringType().resolve(world); + + if (signature.getDeclaringType().isStar() || ResolvedType.isMissing(exactDeclaringType) + || exactDeclaringType.resolve(world).isMissing()) { + return; + } + + // warning not needed if match type couldn't ever be the declaring type + if (!shadowDeclaringType.isAssignableFrom(exactDeclaringType.resolve(world))) { + return; + } + + // if the method in the declaring type is *not* visible to the + // exact declaring type then warning not needed. + ResolvedMember rm = shadow.getSignature().resolve(world); + // rm can be null in the case where we are binary weaving, and looking at a class with a call to a method in another class, + // but because of class incompatibilities, the method does not exist on the target class anymore. + // this will be reported elsewhere. + if (rm == null) { + return; + } + + int shadowModifiers = rm.getModifiers(); + if (!ResolvedType.isVisible(shadowModifiers, shadowDeclaringType, exactDeclaringType.resolve(world))) { + return; + } + + if (!signature.getReturnType().matchesStatically(shadow.getSignature().getReturnType().resolve(world))) { + // Covariance issue... + // The reason we didn't match is that the type pattern for the pointcut (Car) doesn't match the + // return type for the specific declaration at the shadow. (FastCar Sub.getCar()) + // XXX Put out another XLINT in this case? + return; + } + // PR60015 - Don't report the warning if the declaring type is object and 'this' is an interface + if (exactDeclaringType.resolve(world).isInterface() && shadowDeclaringType.equals(world.resolve("java.lang.Object"))) { + return; + } + + SignaturePattern nonConfusingPattern = new SignaturePattern(signature.getKind(), signature.getModifiers(), + signature.getReturnType(), TypePattern.ANY, signature.getName(), signature.getParameterTypes(), + signature.getThrowsPattern(), signature.getAnnotationPattern()); + + if (nonConfusingPattern.matches(shadow.getSignature(), shadow.getIWorld(), true)) { + shadow.getIWorld().getLint().unmatchedSuperTypeInCall.signal(new String[] { + shadow.getSignature().getDeclaringType().toString(), signature.getDeclaringType().toString() }, + this.getSourceLocation(), new ISourceLocation[] { shadow.getSourceLocation() }); + } + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof KindedPointcut)) { + return false; + } + KindedPointcut o = (KindedPointcut) other; + return o.kind == this.kind && o.signature.equals(this.signature); + } + + @Override + public int hashCode() { + int result = 17; + result = 37 * result + kind.hashCode(); + result = 37 * result + signature.hashCode(); + return result; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(kind.getSimpleName()); + buf.append("("); + buf.append(signature.toString()); + buf.append(")"); + return buf.toString(); + } + + @Override + public void postRead(ResolvedType enclosingType) { + signature.postRead(enclosingType); + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.KINDED); + kind.write(s); + signature.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + Shadow.Kind kind = Shadow.Kind.read(s); + SignaturePattern sig = SignaturePattern.read(s, context); + KindedPointcut ret = new KindedPointcut(kind, sig); + ret.readLocation(context, s); + return ret; + } + + // XXX note: there is no namebinding in any kinded pointcut. + // still might want to do something for better error messages + // We want to do something here to make sure we don't sidestep the parameter + // list in capturing type identifiers. + @Override + public void resolveBindings(IScope scope, Bindings bindings) { + if (kind == Shadow.Initialization) { + // scope.getMessageHandler().handleMessage( + // MessageUtil.error( + // "initialization unimplemented in 1.1beta1", + // this.getSourceLocation())); + } + signature = signature.resolveBindings(scope, bindings); + + if (kind == Shadow.ConstructorExecution) { // Bug fix 60936 + if (signature.getDeclaringType() != null) { + World world = scope.getWorld(); + UnresolvedType exactType = signature.getDeclaringType().getExactType(); + if (signature.getKind() == Member.CONSTRUCTOR && !ResolvedType.isMissing(exactType) + && exactType.resolve(world).isInterface() && !signature.getDeclaringType().isIncludeSubtypes()) { + world.getLint().noInterfaceCtorJoinpoint.signal(exactType.toString(), getSourceLocation()); + } + } + } + + // no parameterized types + if (kind == Shadow.StaticInitialization) { + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getDeclaringType().traverse(visitor, null); + if (visitor.wellHasItThen/* ? */()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.NO_STATIC_INIT_JPS_FOR_PARAMETERIZED_TYPES), + getSourceLocation())); + } + } + + // no parameterized types in declaring type position + if ((kind == Shadow.FieldGet) || (kind == Shadow.FieldSet)) { + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getDeclaringType().traverse(visitor, null); + if (visitor.wellHasItThen/* ? */()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.GET_AND_SET_DONT_SUPPORT_DEC_TYPE_PARAMETERS), + getSourceLocation())); + } + + // fields can't have a void type! + UnresolvedType returnType = signature.getReturnType().getExactType(); + if (returnType.equals(UnresolvedType.VOID)) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.FIELDS_CANT_HAVE_VOID_TYPE), + getSourceLocation())); + } + } + + // no join points for initialization and preinitialization of parameterized types + // no throwable parameterized types + if ((kind == Shadow.Initialization) || (kind == Shadow.PreInitialization)) { + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getDeclaringType().traverse(visitor, null); + if (visitor.wellHasItThen/* ? */()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.NO_INIT_JPS_FOR_PARAMETERIZED_TYPES), + getSourceLocation())); + } + + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getThrowsPattern().traverse(visitor, null); + if (visitor.wellHasItThen/* ? */()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.NO_GENERIC_THROWABLES), getSourceLocation())); + } + } + + // no parameterized types in declaring type position + // no throwable parameterized types + if ((kind == Shadow.MethodExecution) || (kind == Shadow.ConstructorExecution)) { + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getDeclaringType().traverse(visitor, null); + if (visitor.wellHasItThen/* ? */()) { + scope.message(MessageUtil.error( + WeaverMessages.format(WeaverMessages.EXECUTION_DOESNT_SUPPORT_PARAMETERIZED_DECLARING_TYPES), + getSourceLocation())); + } + + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getThrowsPattern().traverse(visitor, null); + if (visitor.wellHasItThen/* ? */()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.NO_GENERIC_THROWABLES), getSourceLocation())); + } + } + + // no parameterized types in declaring type position + // no throwable parameterized types + if ((kind == Shadow.MethodCall) || (kind == Shadow.ConstructorCall)) { + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getDeclaringType().traverse(visitor, null); + if (visitor.wellHasItThen/* ? */()) { + scope.message(MessageUtil.error( + WeaverMessages.format(WeaverMessages.CALL_DOESNT_SUPPORT_PARAMETERIZED_DECLARING_TYPES), + getSourceLocation())); + } + + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getThrowsPattern().traverse(visitor, null); + if (visitor.wellHasItThen/* ? */()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.NO_GENERIC_THROWABLES), getSourceLocation())); + } + if (!scope.getWorld().isJoinpointArrayConstructionEnabled() && kind == Shadow.ConstructorCall + && signature.getDeclaringType().isArray()) { + scope.message(MessageUtil.warn(WeaverMessages.format(WeaverMessages.NO_NEWARRAY_JOINPOINTS_BY_DEFAULT), + getSourceLocation())); + } + } + } + + @Override + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; + } + + @Override + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + Pointcut ret = new KindedPointcut(kind, signature, bindings.getEnclosingAdvice()); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + Pointcut ret = new KindedPointcut(kind, signature.parameterizeWith(typeVariableMap, w), munger); + ret.copyLocationFrom(this); + return ret; + } + + public Shadow.Kind getKind() { + return kind; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ModifiersPattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ModifiersPattern.java new file mode 100644 index 000000000..6c1d0557e --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ModifiersPattern.java @@ -0,0 +1,105 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.VersionedDataInputStream; + +public class ModifiersPattern extends PatternNode { + private int requiredModifiers; + private int forbiddenModifiers; + + public static final ModifiersPattern ANY = new ModifiersPattern(0, 0); + + private static Map<String, Integer> modifierFlags = null; + + static { + modifierFlags = new HashMap<String, Integer>(); + int flag = 1; + while (flag <= Modifier.STRICT) { + String flagName = Modifier.toString(flag); + modifierFlags.put(flagName, new Integer(flag)); + flag = flag << 1; + } + modifierFlags.put("synthetic", new Integer(0x1000 /* Modifier.SYNTHETIC */)); + } + + public ModifiersPattern(int requiredModifiers, int forbiddenModifiers) { + this.requiredModifiers = requiredModifiers; + this.forbiddenModifiers = forbiddenModifiers; + } + + public String toString() { + if (this == ANY) { + return ""; + } + + String ret = Modifier.toString(requiredModifiers); + if (forbiddenModifiers == 0) { + return ret; + } else { + return ret + " !" + Modifier.toString(forbiddenModifiers); + } + } + + public boolean equals(Object other) { + if (!(other instanceof ModifiersPattern)) { + return false; + } + ModifiersPattern o = (ModifiersPattern) other; + return o.requiredModifiers == this.requiredModifiers && o.forbiddenModifiers == this.forbiddenModifiers; + } + + public int hashCode() { + int result = 17; + result = 37 * result + requiredModifiers; + result = 37 * result + forbiddenModifiers; + return result; + } + + public boolean matches(int modifiers) { + return ((modifiers & requiredModifiers) == requiredModifiers) && ((modifiers & forbiddenModifiers) == 0); + } + + public static ModifiersPattern read(VersionedDataInputStream s) throws IOException { + int requiredModifiers = s.readShort(); + int forbiddenModifiers = s.readShort(); + if (requiredModifiers == 0 && forbiddenModifiers == 0) { + return ANY; + } + return new ModifiersPattern(requiredModifiers, forbiddenModifiers); + } + + public void write(CompressingDataOutputStream s) throws IOException { + // s.writeByte(MODIFIERS_PATTERN); + s.writeShort(requiredModifiers); + s.writeShort(forbiddenModifiers); + } + + public static int getModifierFlag(String name) { + Integer flag = modifierFlags.get(name); + if (flag == null) { + return -1; + } + return flag.intValue(); + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NameBindingPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NameBindingPointcut.java new file mode 100644 index 000000000..34b25e207 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NameBindingPointcut.java @@ -0,0 +1,52 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.patterns; + +import java.util.List; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; + +/** + * Common super type for Pointcuts that can bind formal parameters. + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public abstract class NameBindingPointcut extends Pointcut { + + public NameBindingPointcut() { + super(); + } + + protected Test exposeStateForVar(Var var,TypePattern type, ExposedState state, World world) { + if (type instanceof BindingTypePattern) { + BindingTypePattern b = (BindingTypePattern)type; + state.set(b.getFormalIndex(), var); + } + ResolvedType myType = type.getExactType().resolve(world); + if (myType.isParameterizedType()) { + // unchecked warning already issued... + myType = (ResolvedType) myType.getRawType(); + } + return Test.makeInstanceof(var, myType.resolve(world)); + } + + public abstract List<BindingTypePattern> getBindingTypePatterns(); + public abstract List<BindingPattern> getBindingAnnotationTypePatterns(); + + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NamePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NamePattern.java new file mode 100644 index 000000000..561b99197 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NamePattern.java @@ -0,0 +1,208 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; + +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.VersionedDataInputStream; + +public class NamePattern extends PatternNode { + char[] pattern; + int starCount = 0; + private int hashcode = -1; + + public static final NamePattern ELLIPSIS = new NamePattern(""); + public static final NamePattern ANY = new NamePattern("*"); + + public NamePattern(String name) { + this(name.toCharArray()); + } + + public NamePattern(char[] pattern) { + this.pattern = pattern; + + for (int i = 0, len = pattern.length; i < len; i++) { + if (pattern[i] == '*') { + starCount++; + } + } + hashcode = new String(pattern).hashCode(); + } + + public boolean matches(char[] a2) { + char[] a1 = pattern; + int len1 = a1.length; + int len2 = a2.length; + if (starCount == 0) { + if (len1 != len2) { + return false; + } + for (int i = 0; i < len1; i++) { + if (a1[i] != a2[i]) { + return false; + } + } + return true; + } else if (starCount == 1) { + // just '*' matches anything + if (len1 == 1) { + return true; + } + if (len1 > len2 + 1) { + return false; + } + int i2 = 0; + for (int i1 = 0; i1 < len1; i1++) { + char c1 = a1[i1]; + if (c1 == '*') { + i2 = len2 - (len1 - (i1 + 1)); + } else if (c1 != a2[i2++]) { + return false; + } + } + return true; + } else { + // System.err.print("match(\"" + pattern + "\", \"" + target + "\") -> "); + boolean b = outOfStar(a1, a2, 0, 0, len1 - starCount, len2, starCount); + // System.err.println(b); + return b; + } + } + + private static boolean outOfStar(final char[] pattern, final char[] target, int pi, int ti, int pLeft, int tLeft, + final int starsLeft) { + if (pLeft > tLeft) { + return false; + } + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length && pi < pattern.length) + if (tLeft == 0) { + return true; + } + if (pLeft == 0) { + return (starsLeft > 0); + } + if (pattern[pi] == '*') { + return inStar(pattern, target, pi + 1, ti, pLeft, tLeft, starsLeft - 1); + } + if (target[ti] != pattern[pi]) { + return false; + } + pi++; + ti++; + pLeft--; + tLeft--; + } + } + + private static boolean inStar(final char[] pattern, final char[] target, int pi, int ti, final int pLeft, int tLeft, + int starsLeft) { + // invariant: pLeft > 0, so we know we'll run out of stars and find a real char in pattern + char patternChar = pattern[pi]; + while (patternChar == '*') { + starsLeft--; + patternChar = pattern[++pi]; + } + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length) + if (pLeft > tLeft) { + return false; + } + if (target[ti] == patternChar) { + if (outOfStar(pattern, target, pi + 1, ti + 1, pLeft - 1, tLeft - 1, starsLeft)) { + return true; + } + } + ti++; + tLeft--; + } + } + + public boolean matches(String other) { + if (starCount == 1 && pattern.length == 1) { + // optimize for wildcard + return true; + } + return matches(other.toCharArray()); + } + + @Override + public String toString() { + return new String(pattern); + } + + @Override + public boolean equals(Object other) { + if (other instanceof NamePattern) { + NamePattern otherPat = (NamePattern) other; + if (otherPat.starCount != this.starCount) { + return false; + } + if (otherPat.pattern.length != this.pattern.length) { + return false; + } + for (int i = 0; i < this.pattern.length; i++) { + if (this.pattern[i] != otherPat.pattern[i]) { + return false; + } + } + return true; + } + return false; + } + + @Override + public int hashCode() { + return hashcode; + } + + @Override + public void write(CompressingDataOutputStream out) throws IOException { + out.writeUTF(new String(pattern)); + } + + public static NamePattern read(VersionedDataInputStream in) throws IOException { + String s = in.readUTF(); + if (s.length() == 0) { + return ELLIPSIS; + } + return new NamePattern(s); + } + + /** + * Method maybeGetSimpleName. + * + * @return String + */ + public String maybeGetSimpleName() { + if (starCount == 0 && pattern.length > 0) { + return new String(pattern); + } + return null; + } + + /** + * Method isAny. + * + * @return boolean + */ + public boolean isAny() { + return starCount == 1 && pattern.length == 1; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NoTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NoTypePattern.java new file mode 100644 index 000000000..e5be0a969 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NoTypePattern.java @@ -0,0 +1,118 @@ +/* ******************************************************************* + * Copyright (c) 2002, 2010 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: + * PARC initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +public class NoTypePattern extends TypePattern { + + public NoTypePattern() { + super(false, false, new TypePatternList()); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern) + */ + @Override + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return false; + } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesExactly(IType) + */ + @Override + protected boolean matchesExactly(ResolvedType type) { + return false; + } + + @Override + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + return false; + } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesInstanceof(IType) + */ + @Override + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + return FuzzyBoolean.NO; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(NO_KEY); + } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matches(IType, MatchKind) + */ + // public FuzzyBoolean matches(IType type, MatchKind kind) { + // return FuzzyBoolean.YES; + // } + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesSubtypes(IType) + */ + @Override + protected boolean matchesSubtypes(ResolvedType type) { + return false; + } + + @Override + public boolean isStar() { + return false; + } + + @Override + public String toString() { + return "<nothing>"; + }// FIXME AV - bad! toString() cannot be parsed back (not idempotent) + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + return (obj instanceof NoTypePattern); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return 17 * 37 * 37; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public TypePattern parameterizeWith(Map<String,UnresolvedType> arg0, World w) { + return this; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NotAnnotationTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NotAnnotationTypePattern.java new file mode 100644 index 000000000..0317b85a5 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NotAnnotationTypePattern.java @@ -0,0 +1,136 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; + +public class NotAnnotationTypePattern extends AnnotationTypePattern { + + AnnotationTypePattern negatedPattern; + + public NotAnnotationTypePattern(AnnotationTypePattern pattern) { + this.negatedPattern = pattern; + setLocation(pattern.getSourceContext(), pattern.getStart(), pattern.getEnd()); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#matches(org.aspectj.weaver.AnnotatedElement) + */ + public FuzzyBoolean matches(AnnotatedElement annotated) { + return negatedPattern.matches(annotated).not(); + } + + public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) { + return negatedPattern.matches(annotated, parameterAnnotations).not(); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolve(org.aspectj.weaver.World) + */ + public void resolve(World world) { + negatedPattern.resolve(world); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolveBindings(org.aspectj.weaver.patterns.IScope, + * org.aspectj.weaver.patterns.Bindings, boolean) + */ + public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) { + negatedPattern = negatedPattern.resolveBindings(scope, bindings, allowBinding); + return this; + } + + public AnnotationTypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + AnnotationTypePattern newNegatedPattern = negatedPattern.parameterizeWith(typeVariableMap, w); + NotAnnotationTypePattern ret = new NotAnnotationTypePattern(newNegatedPattern); + ret.copyLocationFrom(this); + if (this.isForParameterAnnotationMatch()) { + ret.setForParameterAnnotationMatch(); + } + return ret; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.NOT); + negatedPattern.write(s); + writeLocation(s); + s.writeBoolean(isForParameterAnnotationMatch()); + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern ret = new NotAnnotationTypePattern(AnnotationTypePattern.read(s, context)); + ret.readLocation(context, s); + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160) { + if (s.readBoolean()) { + ret.setForParameterAnnotationMatch(); + } + } + return ret; + } + + public boolean equals(Object obj) { + if (!(obj instanceof NotAnnotationTypePattern)) { + return false; + } + NotAnnotationTypePattern other = (NotAnnotationTypePattern) obj; + return other.negatedPattern.equals(negatedPattern) + && other.isForParameterAnnotationMatch() == isForParameterAnnotationMatch(); + } + + public int hashCode() { + int result = 17 + 37 * negatedPattern.hashCode(); + result = 37 * result + (isForParameterAnnotationMatch() ? 0 : 1); + return result; + } + + public String toString() { + return "!" + negatedPattern.toString(); + } + + public AnnotationTypePattern getNegatedPattern() { + return negatedPattern; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor, data); + negatedPattern.traverse(visitor, ret); + return ret; + } + + public void setForParameterAnnotationMatch() { + negatedPattern.setForParameterAnnotationMatch(); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NotPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NotPointcut.java new file mode 100644 index 000000000..85d2cdb7a --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NotPointcut.java @@ -0,0 +1,140 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; + +public class NotPointcut extends Pointcut { + private Pointcut body; + + public NotPointcut(Pointcut negated) { + super(); + this.body = negated; + this.pointcutKind = NOT; + setLocation(negated.getSourceContext(), negated.getStart(), negated.getEnd()); // should that be at least start-1? + } + + public NotPointcut(Pointcut pointcut, int startPos) { + this(pointcut); + setLocation(pointcut.getSourceContext(), startPos, pointcut.getEnd()); + } + + @Override + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public Pointcut getNegatedPointcut() { + return body; + } + + @Override + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return body.fastMatch(type).not(); + } + + @Override + protected FuzzyBoolean matchInternal(Shadow shadow) { + return body.match(shadow).not(); + } + + @Override + public String toString() { + return "!" + body.toString(); + + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof NotPointcut)) { + return false; + } + NotPointcut o = (NotPointcut) other; + return o.body.equals(body); + } + + @Override + public int hashCode() { + return 37 * 23 + body.hashCode(); + } + + @Override + public void resolveBindings(IScope scope, Bindings bindings) { + // Bindings old = bindings.copy(); + + // Bindings newBindings = new Bindings(bindings.size()); + + body.resolveBindings(scope, null); + + // newBindings.checkEmpty(scope, "negation does not allow binding"); + // bindings.checkEquals(old, scope); + + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.NOT); + body.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + NotPointcut ret = new NotPointcut(Pointcut.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + @Override + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return Test.makeNot(body.findResidue(shadow, state)); + } + + @Override + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + Pointcut ret = new NotPointcut(body.concretize(inAspect, declaringType, bindings)); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + Pointcut ret = new NotPointcut(body.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor, data); + this.body.traverse(visitor, ret); + return ret; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NotSignaturePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NotSignaturePattern.java new file mode 100644 index 000000000..b6140ece1 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NotSignaturePattern.java @@ -0,0 +1,91 @@ +/* ******************************************************************* + * Copyright (c) 2010 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 - SpringSource + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * Represents the NOT of a signature pattern + * + * @author Andy Clement + * @since 1.6.9 + */ +public class NotSignaturePattern extends AbstractSignaturePattern { + + private ISignaturePattern negatedSp; + + public NotSignaturePattern(ISignaturePattern negatedSp) { + this.negatedSp = negatedSp; + } + + public boolean couldEverMatch(ResolvedType type) { + if (negatedSp.getExactDeclaringTypes().size() == 0) { + return true; + } + return !negatedSp.couldEverMatch(type); + } + + public List<ExactTypePattern> getExactDeclaringTypes() { + return negatedSp.getExactDeclaringTypes(); + } + + public boolean isMatchOnAnyName() { + return negatedSp.isMatchOnAnyName(); + } + + public boolean isStarAnnotation() { + return negatedSp.isStarAnnotation(); + } + + public boolean matches(Member member, World world, boolean b) { + return !negatedSp.matches(member, world, b); + } + + public ISignaturePattern parameterizeWith(Map<String, UnresolvedType> typeVariableBindingMap, World world) { + return new NotSignaturePattern(negatedSp.parameterizeWith(typeVariableBindingMap, world)); + } + + public ISignaturePattern resolveBindings(IScope scope, Bindings bindings) { + // Whilst the real SignaturePattern returns 'this' we are safe to return 'this' here rather than build a new + // AndSignaturePattern + negatedSp.resolveBindings(scope, bindings); + return this; + } + + public static ISignaturePattern readNotSignaturePattern(VersionedDataInputStream s, ISourceContext context) throws IOException { + NotSignaturePattern ret = new NotSignaturePattern(readCompoundSignaturePattern(s, context)); + // ret.readLocation(context, s); // TODO output position currently useless so dont need to do this + s.readInt(); + s.readInt(); + return ret; + } + + public ISignaturePattern getNegated() { + return negatedSp; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("!").append(negatedSp.toString()); + return sb.toString(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NotTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NotTypePattern.java new file mode 100644 index 000000000..0f8f82c21 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/NotTypePattern.java @@ -0,0 +1,180 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * !TypePattern + * + * <p> + * any binding to formals is explicitly forbidden for any composite, ! is just the most obviously wrong case. + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public class NotTypePattern extends TypePattern { + private TypePattern negatedPattern; + private boolean isBangVoid = false; // is this '!void' + private boolean checked = false; + + public NotTypePattern(TypePattern pattern) { + super(false, false); // ??? we override all methods that care about includeSubtypes + this.negatedPattern = pattern; + setLocation(pattern.getSourceContext(), pattern.getStart(), pattern.getEnd()); + } + + public TypePattern getNegatedPattern() { + return negatedPattern; + } + + @Override + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return true; + } + + @Override + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + return negatedPattern.matchesInstanceof(type).not(); + } + + @Override + protected boolean matchesExactly(ResolvedType type) { + return (!negatedPattern.matchesExactly(type) && annotationPattern.matches(type).alwaysTrue()); + } + + @Override + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + return (!negatedPattern.matchesExactly(type, annotatedType) && annotationPattern.matches(annotatedType).alwaysTrue()); + } + + @Override + public boolean matchesStatically(ResolvedType type) { + return !negatedPattern.matchesStatically(type); + } + + @Override + public void setAnnotationTypePattern(AnnotationTypePattern annPatt) { + super.setAnnotationTypePattern(annPatt); + } + + @Override + public void setIsVarArgs(boolean isVarArgs) { + negatedPattern.setIsVarArgs(isVarArgs); + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(TypePattern.NOT); + negatedPattern.write(s); + annotationPattern.write(s); + writeLocation(s); + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + TypePattern ret = new NotTypePattern(TypePattern.read(s, context)); + if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + ret.annotationPattern = AnnotationTypePattern.read(s, context); + } + ret.readLocation(context, s); + return ret; + } + + @Override + public TypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding, boolean requireExactType) { + if (requireExactType) { + return notExactType(scope); + } + negatedPattern = negatedPattern.resolveBindings(scope, bindings, false, false); + return this; + } + + @Override + public boolean isBangVoid() { + if (!checked) { + isBangVoid = negatedPattern.getExactType().isVoid(); + checked = true; + } + return isBangVoid; + } + + @Override + public TypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + TypePattern newNegatedPattern = negatedPattern.parameterizeWith(typeVariableMap, w); + NotTypePattern ret = new NotTypePattern(newNegatedPattern); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public String toString() { + StringBuffer buff = new StringBuffer(); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append('('); + buff.append(annotationPattern.toString()); + buff.append(' '); + } + buff.append('!'); + buff.append(negatedPattern); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append(')'); + } + return buff.toString(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof NotTypePattern)) { + return false; + } + return (negatedPattern.equals(((NotTypePattern) obj).negatedPattern)); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return 17 + 37 * negatedPattern.hashCode(); + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor, data); + negatedPattern.traverse(visitor, ret); + return ret; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/OrAnnotationTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/OrAnnotationTypePattern.java new file mode 100644 index 000000000..3cfe1de8b --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/OrAnnotationTypePattern.java @@ -0,0 +1,131 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; + +public class OrAnnotationTypePattern extends AnnotationTypePattern { + + private AnnotationTypePattern left; + private AnnotationTypePattern right; + + public OrAnnotationTypePattern(AnnotationTypePattern left, AnnotationTypePattern right) { + this.left = left; + this.right = right; + setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); + } + + public FuzzyBoolean matches(AnnotatedElement annotated) { + return left.matches(annotated).or(right.matches(annotated)); + } + + public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) { + return left.matches(annotated, parameterAnnotations).or(right.matches(annotated, parameterAnnotations)); + } + + public void resolve(World world) { + left.resolve(world); + right.resolve(world); + } + + public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) { + left = left.resolveBindings(scope, bindings, allowBinding); + right = right.resolveBindings(scope, bindings, allowBinding); + return this; + } + + public AnnotationTypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + AnnotationTypePattern newLeft = left.parameterizeWith(typeVariableMap, w); + AnnotationTypePattern newRight = right.parameterizeWith(typeVariableMap, w); + OrAnnotationTypePattern ret = new OrAnnotationTypePattern(newLeft, newRight); + ret.copyLocationFrom(this); + if (isForParameterAnnotationMatch()) { + ret.setForParameterAnnotationMatch(); + } + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor, data); + left.traverse(visitor, ret); + right.traverse(visitor, ret); + return ret; + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern p = new OrAnnotationTypePattern(AnnotationTypePattern.read(s, context), AnnotationTypePattern.read(s, + context)); + p.readLocation(context, s); + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160) { + if (s.readBoolean()) { + p.setForParameterAnnotationMatch(); + } + } + return p; + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.OR); + left.write(s); + right.write(s); + writeLocation(s); + s.writeBoolean(isForParameterAnnotationMatch()); + } + + public boolean equals(Object obj) { + if (!(obj instanceof OrAnnotationTypePattern)) { + return false; + } + OrAnnotationTypePattern other = (OrAnnotationTypePattern) obj; + return (left.equals(other.left) && right.equals(other.right)) + && isForParameterAnnotationMatch() == other.isForParameterAnnotationMatch(); + } + + public int hashCode() { + int result = 17; + result = result * 37 + left.hashCode(); + result = result * 37 + right.hashCode(); + result = result * 37 + (isForParameterAnnotationMatch() ? 0 : 1); + return result; + } + + public String toString() { + return "(" + left.toString() + " || " + right.toString() + ")"; + } + + public AnnotationTypePattern getLeft() { + return left; + } + + public AnnotationTypePattern getRight() { + return right; + } + + public void setForParameterAnnotationMatch() { + left.setForParameterAnnotationMatch(); + right.setForParameterAnnotationMatch(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/OrPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/OrPointcut.java new file mode 100644 index 000000000..dde02f726 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/OrPointcut.java @@ -0,0 +1,146 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.UnresolvedType; + + +public class OrPointcut extends Pointcut { + Pointcut left, right; + private int couldMatchKinds; + + public OrPointcut(Pointcut left, Pointcut right) { + super(); + this.left = left; + this.right = right; + setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); + this.pointcutKind = OR; + this.couldMatchKinds = left.couldMatchKinds() | right.couldMatchKinds(); + } + + public int couldMatchKinds() { + return couldMatchKinds; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + FuzzyBoolean leftMatch = left.fastMatch(type); + if (leftMatch.alwaysTrue()) { + return leftMatch; + } + return leftMatch.or(right.fastMatch(type)); + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + FuzzyBoolean leftMatch = left.match(shadow); + if (leftMatch.alwaysTrue()) { + return leftMatch; + } + return leftMatch.or(right.match(shadow)); + } + + public String toString() { + return "(" + left.toString() + " || " + right.toString() + ")"; + } + + public boolean equals(Object other) { + if (!(other instanceof OrPointcut)) { + return false; + } + OrPointcut o = (OrPointcut) other; + return o.left.equals(left) && o.right.equals(right); + } + + public int hashCode() { + int result = 31; + result = 37 * result + left.hashCode(); + result = 37 * result + right.hashCode(); + return result; + } + + /** + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(IScope, Bindings) + */ + public void resolveBindings(IScope scope, Bindings bindings) { + Bindings old = bindings == null ? null : bindings.copy(); + + left.resolveBindings(scope, bindings); + right.resolveBindings(scope, old); + if (bindings != null) { + bindings.checkEquals(old, scope); + } + + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.OR); + left.write(s); + right.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + OrPointcut ret = new OrPointcut(Pointcut.read(s, context), Pointcut.read(s, context)); + ret.readLocation(context, s); + return ret; + + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return Test.makeOr(left.findResidue(shadow, state), right.findResidue(shadow, state)); + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + Pointcut ret = new OrPointcut(left.concretize(inAspect, declaringType, bindings), right.concretize(inAspect, declaringType, + bindings)); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + Pointcut ret = new OrPointcut(left.parameterizeWith(typeVariableMap, w), right.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + public Pointcut getLeft() { + return left; + } + + public Pointcut getRight() { + return right; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor, data); + left.traverse(visitor, ret); + right.traverse(visitor, ret); + return ret; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/OrSignaturePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/OrSignaturePattern.java new file mode 100644 index 000000000..67346f444 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/OrSignaturePattern.java @@ -0,0 +1,104 @@ +/* ******************************************************************* + * Copyright (c) 2010 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 - SpringSource + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * Represents the OR of two other signature patterns. + * + * @author Andy Clement + * @since 1.6.9 + */ +public class OrSignaturePattern extends AbstractSignaturePattern { + + private ISignaturePattern leftSp; + private ISignaturePattern rightSp; + private List<ExactTypePattern> exactDeclaringTypes; + + public OrSignaturePattern(ISignaturePattern leftSp, ISignaturePattern rightSp) { + this.leftSp = leftSp; + this.rightSp = rightSp; + } + + public boolean couldEverMatch(ResolvedType type) { + return leftSp.couldEverMatch(type) || rightSp.couldEverMatch(type); + } + + public List<ExactTypePattern> getExactDeclaringTypes() { + if (exactDeclaringTypes == null) { + exactDeclaringTypes = new ArrayList<ExactTypePattern>(); + exactDeclaringTypes.addAll(leftSp.getExactDeclaringTypes()); + exactDeclaringTypes.addAll(rightSp.getExactDeclaringTypes()); + } + return exactDeclaringTypes; + } + + public boolean isMatchOnAnyName() { + return leftSp.isMatchOnAnyName() || rightSp.isMatchOnAnyName(); + } + + public boolean isStarAnnotation() { + return leftSp.isStarAnnotation() || rightSp.isStarAnnotation(); + } + + public boolean matches(Member member, World world, boolean b) { + return (leftSp.matches(member, world, b)) || (rightSp.matches(member, world, b)); + } + + public ISignaturePattern parameterizeWith(Map<String, UnresolvedType> typeVariableBindingMap, World world) { + return new OrSignaturePattern(leftSp.parameterizeWith(typeVariableBindingMap, world), rightSp.parameterizeWith( + typeVariableBindingMap, world)); + } + + public ISignaturePattern resolveBindings(IScope scope, Bindings bindings) { + // Whilst the real SignaturePattern returns 'this' we are safe to return 'this' here rather than build a new + // AndSignaturePattern + leftSp.resolveBindings(scope, bindings); + rightSp.resolveBindings(scope, bindings); + return this; + } + + public static ISignaturePattern readOrSignaturePattern(VersionedDataInputStream s, ISourceContext context) throws IOException { + OrSignaturePattern ret = new OrSignaturePattern(readCompoundSignaturePattern(s, context), readCompoundSignaturePattern(s, + context)); + s.readInt(); + s.readInt(); + // ret.readLocation(context, s); // TODO output position currently useless so dont need to do this + return ret; + } + + public ISignaturePattern getLeft() { + return leftSp; + } + + public ISignaturePattern getRight() { + return rightSp; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(leftSp.toString()).append(" || ").append(rightSp.toString()); + return sb.toString(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/OrTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/OrTypePattern.java new file mode 100644 index 000000000..b31a4e9ad --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/OrTypePattern.java @@ -0,0 +1,193 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * left || right + * + * <p> + * any binding to formals is explicitly forbidden for any composite by the language + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public class OrTypePattern extends TypePattern { + private TypePattern left, right; + + public OrTypePattern(TypePattern left, TypePattern right) { + super(false, false); // ??? we override all methods that care about includeSubtypes + this.left = left; + this.right = right; + setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); + } + + public TypePattern getRight() { + return right; + } + + public TypePattern getLeft() { + return left; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern) + */ + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return true; // don't dive at the moment... + } + + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + return left.matchesInstanceof(type).or(right.matchesInstanceof(type)); + } + + protected boolean matchesExactly(ResolvedType type) { + // ??? if these had side-effects, this sort-circuit could be a mistake + return left.matchesExactly(type) || right.matchesExactly(type); + } + + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + // ??? if these had side-effects, this sort-circuit could be a mistake + return left.matchesExactly(type, annotatedType) || right.matchesExactly(type, annotatedType); + } + + public boolean matchesStatically(ResolvedType type) { + return left.matchesStatically(type) || right.matchesStatically(type); + } + + public void setIsVarArgs(boolean isVarArgs) { + this.isVarArgs = isVarArgs; + left.setIsVarArgs(isVarArgs); + right.setIsVarArgs(isVarArgs); + } + + public void setAnnotationTypePattern(AnnotationTypePattern annPatt) { + if (annPatt == AnnotationTypePattern.ANY) { + return; + } + if (left.annotationPattern == AnnotationTypePattern.ANY) { + left.setAnnotationTypePattern(annPatt); + } else { + left.setAnnotationTypePattern(new AndAnnotationTypePattern(left.annotationPattern, annPatt)); + } + if (right.annotationPattern == AnnotationTypePattern.ANY) { + right.setAnnotationTypePattern(annPatt); + } else { + right.setAnnotationTypePattern(new AndAnnotationTypePattern(right.annotationPattern, annPatt)); + } + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(TypePattern.OR); + left.write(s); + right.write(s); + writeLocation(s); + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + OrTypePattern ret = new OrTypePattern(TypePattern.read(s, context), TypePattern.read(s, context)); + ret.readLocation(context, s); + if (ret.left.isVarArgs && ret.right.isVarArgs) { + ret.isVarArgs = true; + } + return ret; + } + + public TypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding, boolean requireExactType) { + if (requireExactType) { + return notExactType(scope); + } + left = left.resolveBindings(scope, bindings, false, false); + right = right.resolveBindings(scope, bindings, false, false); + return this; + } + + public TypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + TypePattern newLeft = left.parameterizeWith(typeVariableMap, w); + TypePattern newRight = right.parameterizeWith(typeVariableMap, w); + OrTypePattern ret = new OrTypePattern(newLeft, newRight); + ret.copyLocationFrom(this); + return ret; + } + + public String toString() { + StringBuffer buff = new StringBuffer(); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append('('); + buff.append(annotationPattern.toString()); + buff.append(' '); + } + buff.append('('); + buff.append(left.toString()); + buff.append(" || "); + buff.append(right.toString()); + buff.append(')'); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append(')'); + } + return buff.toString(); + } + + public boolean isStarAnnotation() { + return left.isStarAnnotation() || right.isStarAnnotation(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (!(obj instanceof OrTypePattern)) { + return false; + } + OrTypePattern other = (OrTypePattern) obj; + return left.equals(other.left) && right.equals(other.right); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + int ret = 17; + ret = ret + 37 * left.hashCode(); + ret = ret + 37 * right.hashCode(); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor, data); + left.traverse(visitor, ret); + right.traverse(visitor, ret); + return ret; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ParserException.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ParserException.java new file mode 100644 index 000000000..036061f15 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ParserException.java @@ -0,0 +1,28 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import org.aspectj.weaver.IHasPosition; + +public class ParserException extends RuntimeException { + private IHasPosition token; + + public ParserException(String message, IHasPosition token) { + super(message); + this.token = token; + } + + public IHasPosition getLocation() { + return token; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PatternNode.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PatternNode.java new file mode 100644 index 000000000..8a7aae097 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PatternNode.java @@ -0,0 +1,88 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.IHasSourceLocation; +import org.aspectj.weaver.ISourceContext; + +public abstract class PatternNode implements IHasSourceLocation { + protected int start, end; + protected ISourceContext sourceContext; + + public PatternNode() { + super(); + start = end = -1; + } + + public int getStart() { + return start + (sourceContext != null ? sourceContext.getOffset() : 0); + } + + public int getEnd() { + return end + (sourceContext != null ? sourceContext.getOffset() : 0); + } + + public ISourceContext getSourceContext() { + return sourceContext; + } + + public String getFileName() { + return "unknown"; + } + + public void setLocation(ISourceContext sourceContext, int start, int end) { + this.sourceContext = sourceContext; + this.start = start; + this.end = end; + } + + public void copyLocationFrom(PatternNode other) { + this.start = other.start; + this.end = other.end; + this.sourceContext = other.sourceContext; + } + + public ISourceLocation getSourceLocation() { + // System.out.println("get context: " + this + " is " + sourceContext); + if (sourceContext == null) { + // System.err.println("no context: " + this); + return null; + } + return sourceContext.makeSourceLocation(this); + } + + public abstract void write(CompressingDataOutputStream s) throws IOException; + + public void writeLocation(DataOutputStream s) throws IOException { + s.writeInt(start); + s.writeInt(end); + } + + public void readLocation(ISourceContext context, DataInputStream s) throws IOException { + start = s.readInt(); + end = s.readInt(); + this.sourceContext = context; + } + + public abstract Object accept(PatternNodeVisitor visitor, Object data); + + public Object traverse(PatternNodeVisitor visitor, Object data) { + return accept(visitor, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PatternNodeVisitor.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PatternNodeVisitor.java new file mode 100644 index 000000000..336a33e51 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PatternNodeVisitor.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + * Adrian Colyer refactoring for traversal and grouping by kind + *******************************************************************************/ +package org.aspectj.weaver.patterns; + + +/** + * A Pointcut or TypePattern visitor + * + * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> + */ +public interface PatternNodeVisitor { + + // Annotation type patterns + Object visit(AndAnnotationTypePattern node, Object data); + Object visit(AnyAnnotationTypePattern node, Object data); + Object visit(EllipsisAnnotationTypePattern node, Object data); + Object visit(ExactAnnotationTypePattern node, Object data); + Object visit(BindingAnnotationTypePattern node, Object data); + Object visit(NotAnnotationTypePattern node, Object data); + Object visit(OrAnnotationTypePattern node, Object data); + Object visit(WildAnnotationTypePattern node, Object data); + Object visit(AnnotationPatternList node, Object data); + + // Regular type patterns + Object visit(AndTypePattern node, Object data); + Object visit(AnyTypePattern node, Object data); + Object visit(AnyWithAnnotationTypePattern node, Object data); + Object visit(EllipsisTypePattern node, Object data); + Object visit(ExactTypePattern node, Object data); + Object visit(BindingTypePattern node, Object data); + Object visit(NotTypePattern node, Object data); + Object visit(NoTypePattern node, Object data); + Object visit(OrTypePattern node, Object data); + Object visit(WildTypePattern node, Object data); + Object visit(TypePatternList node, Object data); + Object visit(HasMemberTypePattern node, Object data); + Object visit(TypeCategoryTypePattern node, Object data); + + // Pointcuts + Object visit(AndPointcut node, Object data); + Object visit(CflowPointcut node, Object data); + Object visit(ConcreteCflowPointcut node, Object data); + Object visit(HandlerPointcut node, Object data); + Object visit(IfPointcut node, Object data); + Object visit(KindedPointcut node, Object data); + Object visit(Pointcut.MatchesNothingPointcut node, Object data); + Object visit(AnnotationPointcut node, Object data); + Object visit(ArgsAnnotationPointcut node, Object data); + Object visit(ArgsPointcut node, Object data); + Object visit(ThisOrTargetAnnotationPointcut node, Object data); + Object visit(ThisOrTargetPointcut node, Object data); + Object visit(WithinAnnotationPointcut node, Object data); + Object visit(WithinCodeAnnotationPointcut node, Object data); + Object visit(NotPointcut node, Object data); + Object visit(OrPointcut node, Object data); + Object visit(ReferencePointcut node, Object data); + Object visit(WithinPointcut node, Object data); + Object visit(WithincodePointcut node, Object data); + + // Per-clauses + Object visit(PerCflow node, Object data); + Object visit(PerFromSuper node, Object data); + Object visit(PerObject node, Object data); + Object visit(PerSingleton node, Object data); + Object visit(PerTypeWithin node, Object data); + + + // Declares + Object visit(DeclareAnnotation node, Object data); + Object visit(DeclareErrorOrWarning node, Object data); + Object visit(DeclareParents node, Object data); + Object visit(DeclarePrecedence node, Object data); + Object visit(DeclareSoft node, Object data); + + // Miscellaneous patterns + Object visit(ModifiersPattern node, Object data); + Object visit(NamePattern node, Object data); + Object visit(SignaturePattern node, Object data); + Object visit(ThrowsPattern node, Object data); + Object visit(TypeVariablePattern node, Object data); + Object visit(TypeVariablePatternList node,Object data); + + // Catch-all + Object visit(PatternNode node, Object data); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PatternParser.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PatternParser.java new file mode 100644 index 000000000..effecfeaf --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PatternParser.java @@ -0,0 +1,1914 @@ +/* ******************************************************************* + * Copyright (c) 2002,2010 + * 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: + * PARC initial implementation + * Adrian Colyer, IBM + * Andy Clement, IBM, SpringSource + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberKind; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.internal.tools.PointcutDesignatorHandlerBasedPointcut; +import org.aspectj.weaver.tools.ContextBasedMatcher; +import org.aspectj.weaver.tools.PointcutDesignatorHandler; + +/** + * @author PARC + * @author Adrian Colyer + * @author Andy Clement + */ +// XXX doesn't handle errors for extra tokens very well (sometimes ignores) +public class PatternParser { + + private ITokenSource tokenSource; + private ISourceContext sourceContext; + + /** not thread-safe, but this class is not intended to be... */ + private boolean allowHasTypePatterns = false; + + /** extension handlers used in weaver tools API only */ + private Set<PointcutDesignatorHandler> pointcutDesignatorHandlers = Collections.emptySet(); + private World world; + + /** + * Constructor for PatternParser. + */ + public PatternParser(ITokenSource tokenSource) { + super(); + this.tokenSource = tokenSource; + this.sourceContext = tokenSource.getSourceContext(); + } + + /** only used by weaver tools API */ + public void setPointcutDesignatorHandlers(Set<PointcutDesignatorHandler> handlers, World world) { + this.pointcutDesignatorHandlers = handlers; + this.world = world; + } + + public PerClause maybeParsePerClause() { + IToken tok = tokenSource.peek(); + if (tok == IToken.EOF) { + return null; + } + if (tok.isIdentifier()) { + String name = tok.getString(); + if (name.equals("issingleton")) { + return parsePerSingleton(); + } else if (name.equals("perthis")) { + return parsePerObject(true); + } else if (name.equals("pertarget")) { + return parsePerObject(false); + } else if (name.equals("percflow")) { + return parsePerCflow(false); + } else if (name.equals("percflowbelow")) { + return parsePerCflow(true); + } else if (name.equals("pertypewithin")) { // PTWIMPL Parse the pertypewithin clause + return parsePerTypeWithin(); + } else { + return null; + } + } + return null; + } + + private PerClause parsePerCflow(boolean isBelow) { + parseIdentifier(); + eat("("); + Pointcut entry = parsePointcut(); + eat(")"); + return new PerCflow(entry, isBelow); + } + + public boolean moreToParse() { + return tokenSource.hasMoreTokens(); + } + + private PerClause parsePerObject(boolean isThis) { + parseIdentifier(); + eat("("); + Pointcut entry = parsePointcut(); + eat(")"); + return new PerObject(entry, isThis); + } + + private PerClause parsePerTypeWithin() { + parseIdentifier(); + eat("("); + TypePattern withinTypePattern = parseTypePattern(); + eat(")"); + return new PerTypeWithin(withinTypePattern); + } + + private PerClause parsePerSingleton() { + parseIdentifier(); + eat("("); + eat(")"); + return new PerSingleton(); + } + + public Declare parseDeclare() { + int startPos = tokenSource.peek().getStart(); + + eatIdentifier("declare"); + String kind = parseIdentifier(); + Declare ret; + if (kind.equals("error")) { + eat(":"); + ret = parseErrorOrWarning(true); + } else if (kind.equals("warning")) { + eat(":"); + ret = parseErrorOrWarning(false); + } else if (kind.equals("precedence")) { + eat(":"); + ret = parseDominates(); + } else if (kind.equals("dominates")) { + throw new ParserException("name changed to declare precedence", tokenSource.peek(-2)); + } else if (kind.equals("parents")) { + ret = parseParents(); + } else if (kind.equals("soft")) { + eat(":"); + ret = parseSoft(); + } else { + throw new ParserException( + "expected one of error, warning, parents, soft, precedence, @type, @method, @constructor, @field", + tokenSource.peek(-1)); + } + int endPos = tokenSource.peek(-1).getEnd(); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + } + + public Declare parseDeclareAnnotation() { + int startPos = tokenSource.peek().getStart(); + + eatIdentifier("declare"); + eat("@"); + String kind = parseIdentifier(); + eat(":"); + Declare ret; + if (kind.equals("type")) { + ret = parseDeclareAtType(); + } else if (kind.equals("method")) { + ret = parseDeclareAtMethod(true); + } else if (kind.equals("field")) { + ret = parseDeclareAtField(); + } else if (kind.equals("constructor")) { + ret = parseDeclareAtMethod(false); + } else { + throw new ParserException("one of type, method, field, constructor", tokenSource.peek(-1)); + } + eat(";"); + int endPos = tokenSource.peek(-1).getEnd(); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + + } + + public DeclareAnnotation parseDeclareAtType() { + allowHasTypePatterns = true; + TypePattern p = parseTypePattern(); + allowHasTypePatterns = false; + return new DeclareAnnotation(DeclareAnnotation.AT_TYPE, p); + } + + public DeclareAnnotation parseDeclareAtMethod(boolean isMethod) { + ISignaturePattern sp = parseCompoundMethodOrConstructorSignaturePattern(isMethod);// parseMethodOrConstructorSignaturePattern(); + + if (!isMethod) { + return new DeclareAnnotation(DeclareAnnotation.AT_CONSTRUCTOR, sp); + } else { + return new DeclareAnnotation(DeclareAnnotation.AT_METHOD, sp); + } + } + + public DeclareAnnotation parseDeclareAtField() { + ISignaturePattern compoundFieldSignaturePattern = parseCompoundFieldSignaturePattern(); + DeclareAnnotation da = new DeclareAnnotation(DeclareAnnotation.AT_FIELD, compoundFieldSignaturePattern); + return da; + } + + public ISignaturePattern parseCompoundFieldSignaturePattern() { + int index = tokenSource.getIndex(); + try { + ISignaturePattern atomicFieldSignaturePattern = parseMaybeParenthesizedFieldSignaturePattern(); + + while (isEitherAndOrOr()) { + if (maybeEat("&&")) { + atomicFieldSignaturePattern = new AndSignaturePattern(atomicFieldSignaturePattern, + parseMaybeParenthesizedFieldSignaturePattern()); + } + if (maybeEat("||")) { + atomicFieldSignaturePattern = new OrSignaturePattern(atomicFieldSignaturePattern, + parseMaybeParenthesizedFieldSignaturePattern()); + } + } + return atomicFieldSignaturePattern; + } catch (ParserException e) { + // fallback in the case of a regular single field signature pattern that just happened to start with '(' + int nowAt = tokenSource.getIndex(); + tokenSource.setIndex(index); + try { + ISignaturePattern fsp = parseFieldSignaturePattern(); + return fsp; + } catch (Exception e2) { + tokenSource.setIndex(nowAt); + // throw the original + throw e; + } + } + } + + private boolean isEitherAndOrOr() { + String tokenstring = tokenSource.peek().getString(); + return tokenstring.equals("&&") || tokenstring.equals("||"); + } + + public ISignaturePattern parseCompoundMethodOrConstructorSignaturePattern(boolean isMethod) { + ISignaturePattern atomicMethodCtorSignaturePattern = parseMaybeParenthesizedMethodOrConstructorSignaturePattern(isMethod); + + while (isEitherAndOrOr()) { + if (maybeEat("&&")) { + atomicMethodCtorSignaturePattern = new AndSignaturePattern(atomicMethodCtorSignaturePattern, + parseMaybeParenthesizedMethodOrConstructorSignaturePattern(isMethod)); + } + if (maybeEat("||")) { + atomicMethodCtorSignaturePattern = new OrSignaturePattern(atomicMethodCtorSignaturePattern, + parseMaybeParenthesizedMethodOrConstructorSignaturePattern(isMethod)); + } + } + return atomicMethodCtorSignaturePattern; + } + + public DeclarePrecedence parseDominates() { + List<TypePattern> l = new ArrayList<TypePattern>(); + do { + l.add(parseTypePattern()); + } while (maybeEat(",")); + + return new DeclarePrecedence(l); + } + + private Declare parseParents() { + /* + * simplified design requires use of raw types for declare parents, no generic spec. allowed String[] typeParameters = + * maybeParseSimpleTypeVariableList(); + */ + eat(":"); + allowHasTypePatterns = true; + TypePattern p = parseTypePattern(false, false); + allowHasTypePatterns = false; + IToken t = tokenSource.next(); + if (!(t.getString().equals("extends") || t.getString().equals("implements"))) { + throw new ParserException("extends or implements", t); + } + boolean isExtends = t.getString().equals("extends"); + + List<TypePattern> l = new ArrayList<TypePattern>(); + do { + l.add(parseTypePattern()); + } while (maybeEat(",")); + + // XXX somewhere in the chain we need to enforce that we have only ExactTypePatterns + + DeclareParents decp = new DeclareParents(p, l, isExtends); + return decp; + } + + private Declare parseSoft() { + TypePattern p = parseTypePattern(); + eat(":"); + Pointcut pointcut = parsePointcut(); + return new DeclareSoft(p, pointcut); + } + + /** + * Attempt to parse a pointcut, if that fails then try again for a type pattern. + * + * @param isError true if it is declare error rather than declare warning + * @return the new declare + */ + private Declare parseErrorOrWarning(boolean isError) { + Pointcut pointcut = null; + int index = tokenSource.getIndex(); + try { + pointcut = parsePointcut(); + } catch (ParserException pe) { + try { + tokenSource.setIndex(index); + boolean oldValue = allowHasTypePatterns; + TypePattern typePattern = null; + try { + allowHasTypePatterns = true; + typePattern = parseTypePattern(); + } finally { + allowHasTypePatterns = oldValue; + } + eat(":"); + String message = parsePossibleStringSequence(true); + return new DeclareTypeErrorOrWarning(isError, typePattern, message); + } catch (ParserException pe2) { + // deliberately throw the original problem + throw pe; + } + } + eat(":"); + String message = parsePossibleStringSequence(true); + return new DeclareErrorOrWarning(isError, pointcut, message); + } + + public Pointcut parsePointcut(boolean shouldConsumeAllInput) { + Pointcut p = parsePointcut(); + if (shouldConsumeAllInput && tokenSource.hasMoreTokens()) { + throw new ParserException( + "Found unexpected data after parsing pointcut", + tokenSource.next()); + } + return p; + } + + public Pointcut parsePointcut() { + Pointcut p = parseAtomicPointcut(); + if (maybeEat("&&")) { + p = new AndPointcut(p, parseNotOrPointcut()); + } + + if (maybeEat("||")) { + p = new OrPointcut(p, parsePointcut()); + } + + return p; + } + + private Pointcut parseNotOrPointcut() { + Pointcut p = parseAtomicPointcut(); + if (maybeEat("&&")) { + p = new AndPointcut(p, parseNotOrPointcut()); + } + return p; + } + + private Pointcut parseAtomicPointcut() { + if (maybeEat("!")) { + int startPos = tokenSource.peek(-1).getStart(); + Pointcut p = new NotPointcut(parseAtomicPointcut(), startPos); + return p; + } + if (maybeEat("(")) { + Pointcut p = parsePointcut(); + eat(")"); + return p; + } + if (maybeEat("@")) { + int startPos = tokenSource.peek().getStart(); + Pointcut p = parseAnnotationPointcut(); + int endPos = tokenSource.peek(-1).getEnd(); + p.setLocation(sourceContext, startPos, endPos); + return p; + } + int startPos = tokenSource.peek().getStart(); + Pointcut p = parseSinglePointcut(); + int endPos = tokenSource.peek(-1).getEnd(); + p.setLocation(sourceContext, startPos, endPos); + return p; + } + + public Pointcut parseSinglePointcut() { + int start = tokenSource.getIndex(); + IToken t = tokenSource.peek(); + Pointcut p = t.maybeGetParsedPointcut(); + if (p != null) { + tokenSource.next(); + return p; + } + + String kind = parseIdentifier(); + // IToken possibleTypeVariableToken = tokenSource.peek(); + // String[] typeVariables = maybeParseSimpleTypeVariableList(); + if (kind.equals("execution") || kind.equals("call") || kind.equals("get") || kind.equals("set")) { + p = parseKindedPointcut(kind); + } else if (kind.equals("args")) { + p = parseArgsPointcut(); + } else if (kind.equals("this")) { + p = parseThisOrTargetPointcut(kind); + } else if (kind.equals("target")) { + p = parseThisOrTargetPointcut(kind); + } else if (kind.equals("within")) { + p = parseWithinPointcut(); + } else if (kind.equals("withincode")) { + p = parseWithinCodePointcut(); + } else if (kind.equals("cflow")) { + p = parseCflowPointcut(false); + } else if (kind.equals("cflowbelow")) { + p = parseCflowPointcut(true); + } else if (kind.equals("adviceexecution")) { + eat("("); + eat(")"); + p = new KindedPointcut(Shadow.AdviceExecution, new SignaturePattern(Member.ADVICE, ModifiersPattern.ANY, + TypePattern.ANY, TypePattern.ANY, NamePattern.ANY, TypePatternList.ANY, ThrowsPattern.ANY, + AnnotationTypePattern.ANY)); + } else if (kind.equals("handler")) { + eat("("); + TypePattern typePat = parseTypePattern(false, false); + eat(")"); + p = new HandlerPointcut(typePat); + } else if (kind.equals("lock") || kind.equals("unlock")) { + p = parseMonitorPointcut(kind); + } else if (kind.equals("initialization")) { + eat("("); + SignaturePattern sig = parseConstructorSignaturePattern(); + eat(")"); + p = new KindedPointcut(Shadow.Initialization, sig); + } else if (kind.equals("staticinitialization")) { + eat("("); + TypePattern typePat = parseTypePattern(false, false); + eat(")"); + p = new KindedPointcut(Shadow.StaticInitialization, new SignaturePattern(Member.STATIC_INITIALIZATION, + ModifiersPattern.ANY, TypePattern.ANY, typePat, NamePattern.ANY, TypePatternList.EMPTY, ThrowsPattern.ANY, + AnnotationTypePattern.ANY)); + } else if (kind.equals("preinitialization")) { + eat("("); + SignaturePattern sig = parseConstructorSignaturePattern(); + eat(")"); + p = new KindedPointcut(Shadow.PreInitialization, sig); + } else if (kind.equals("if")) { + // - annotation style only allows if(), if(true) or if(false) + // - if() means the body of the annotated method represents the if expression + // - anything else is an error because code cannot be put into the if() + // - code style will already have been processed and the call to maybeGetParsedPointcut() + // at the top of this method will have succeeded. + eat("("); + if (maybeEatIdentifier("true")) { + eat(")"); + p = new IfPointcut.IfTruePointcut(); + } else if (maybeEatIdentifier("false")) { + eat(")"); + p = new IfPointcut.IfFalsePointcut(); + } else { + if (!maybeEat(")")) { + throw new ParserException( + "in annotation style, if(...) pointcuts cannot contain code. Use if() and put the code in the annotated method", + t); + } + // TODO - Alex has some token stuff going on here to get a readable name in place of ""... + p = new IfPointcut(""); + } + } else { + boolean matchedByExtensionDesignator = false; + // see if a registered handler wants to parse it, otherwise + // treat as a reference pointcut + for (PointcutDesignatorHandler pcd : pointcutDesignatorHandlers) { + if (pcd.getDesignatorName().equals(kind)) { + p = parseDesignatorPointcut(pcd); + matchedByExtensionDesignator = true; + } + + } + if (!matchedByExtensionDesignator) { + tokenSource.setIndex(start); + p = parseReferencePointcut(); + } + } + return p; + } + + private void assertNoTypeVariables(String[] tvs, String errorMessage, IToken token) { + if (tvs != null) { + throw new ParserException(errorMessage, token); + } + } + + public Pointcut parseAnnotationPointcut() { + int start = tokenSource.getIndex(); + IToken t = tokenSource.peek(); + String kind = parseIdentifier(); + IToken possibleTypeVariableToken = tokenSource.peek(); + String[] typeVariables = maybeParseSimpleTypeVariableList(); + if (typeVariables != null) { + String message = "("; + assertNoTypeVariables(typeVariables, message, possibleTypeVariableToken); + } + tokenSource.setIndex(start); + if (kind.equals("annotation")) { + return parseAtAnnotationPointcut(); + } else if (kind.equals("args")) { + return parseArgsAnnotationPointcut(); + } else if (kind.equals("this") || kind.equals("target")) { + return parseThisOrTargetAnnotationPointcut(); + } else if (kind.equals("within")) { + return parseWithinAnnotationPointcut(); + } else if (kind.equals("withincode")) { + return parseWithinCodeAnnotationPointcut(); + } + throw new ParserException("pointcut name", t); + } + + private Pointcut parseAtAnnotationPointcut() { + parseIdentifier(); + eat("("); + if (maybeEat(")")) { + throw new ParserException("@AnnotationName or parameter", tokenSource.peek()); + } + ExactAnnotationTypePattern type = parseAnnotationNameOrVarTypePattern(); + eat(")"); + return new AnnotationPointcut(type); + } + + private SignaturePattern parseConstructorSignaturePattern() { + SignaturePattern ret = parseMethodOrConstructorSignaturePattern(); + if (ret.getKind() == Member.CONSTRUCTOR) { + return ret; + } + + throw new ParserException("constructor pattern required, found method pattern", ret); + } + + private Pointcut parseWithinCodePointcut() { + // parseIdentifier(); + eat("("); + SignaturePattern sig = parseMethodOrConstructorSignaturePattern(); + eat(")"); + return new WithincodePointcut(sig); + } + + private Pointcut parseCflowPointcut(boolean isBelow) { + // parseIdentifier(); + eat("("); + Pointcut entry = parsePointcut(); + eat(")"); + return new CflowPointcut(entry, isBelow, null); + } + + /** + * Method parseWithinPointcut. + * + * @return Pointcut + */ + private Pointcut parseWithinPointcut() { + // parseIdentifier(); + eat("("); + TypePattern type = parseTypePattern(); + eat(")"); + return new WithinPointcut(type); + } + + /** + * Method parseThisOrTargetPointcut. + * + * @return Pointcut + */ + private Pointcut parseThisOrTargetPointcut(String kind) { + eat("("); + TypePattern type = parseTypePattern(); + eat(")"); + return new ThisOrTargetPointcut(kind.equals("this"), type); + } + + private Pointcut parseThisOrTargetAnnotationPointcut() { + String kind = parseIdentifier(); + eat("("); + if (maybeEat(")")) { + throw new ParserException("expecting @AnnotationName or parameter, but found ')'", tokenSource.peek()); + } + ExactAnnotationTypePattern type = parseAnnotationNameOrVarTypePattern(); + eat(")"); + return new ThisOrTargetAnnotationPointcut(kind.equals("this"), type); + } + + private Pointcut parseWithinAnnotationPointcut() { + /* String kind = */parseIdentifier(); + eat("("); + if (maybeEat(")")) { + throw new ParserException("expecting @AnnotationName or parameter, but found ')'", tokenSource.peek()); + } + AnnotationTypePattern type = parseAnnotationNameOrVarTypePattern(); + eat(")"); + return new WithinAnnotationPointcut(type); + } + + private Pointcut parseWithinCodeAnnotationPointcut() { + /* String kind = */parseIdentifier(); + eat("("); + if (maybeEat(")")) { + throw new ParserException("expecting @AnnotationName or parameter, but found ')'", tokenSource.peek()); + } + ExactAnnotationTypePattern type = parseAnnotationNameOrVarTypePattern(); + eat(")"); + return new WithinCodeAnnotationPointcut(type); + } + + /** + * Method parseArgsPointcut. + * + * @return Pointcut + */ + private Pointcut parseArgsPointcut() { + // parseIdentifier(); + TypePatternList arguments = parseArgumentsPattern(false); + return new ArgsPointcut(arguments); + } + + private Pointcut parseArgsAnnotationPointcut() { + parseIdentifier(); + AnnotationPatternList arguments = parseArgumentsAnnotationPattern(); + return new ArgsAnnotationPointcut(arguments); + } + + private Pointcut parseReferencePointcut() { + TypePattern onType = parseTypePattern(); + NamePattern name = null; + if (onType.typeParameters.size() > 0) { + eat("."); + name = parseNamePattern(); + } else { + name = tryToExtractName(onType); + } + if (name == null) { + throw new ParserException("name pattern", tokenSource.peek()); + } + if (onType.toString().equals("")) { + onType = null; + } + + String simpleName = name.maybeGetSimpleName(); + if (simpleName == null) { + throw new ParserException("(", tokenSource.peek(-1)); + } + + TypePatternList arguments = parseArgumentsPattern(false); + return new ReferencePointcut(onType, simpleName, arguments); + } + + private Pointcut parseDesignatorPointcut(PointcutDesignatorHandler pcdHandler) { + eat("("); + int parenCount = 1; + StringBuffer pointcutBody = new StringBuffer(); + while (parenCount > 0) { + if (maybeEat("(")) { + parenCount++; + pointcutBody.append("("); + } else if (maybeEat(")")) { + parenCount--; + if (parenCount > 0) { + pointcutBody.append(")"); + } + } else { + pointcutBody.append(nextToken().getString()); + } + } + ContextBasedMatcher pcExpr = pcdHandler.parse(pointcutBody.toString()); + return new PointcutDesignatorHandlerBasedPointcut(pcExpr, world); + } + + public List<String> parseDottedIdentifier() { + List<String> ret = new ArrayList<String>(); + ret.add(parseIdentifier()); + while (maybeEat(".")) { + ret.add(parseIdentifier()); + } + return ret; + } + + private KindedPointcut parseKindedPointcut(String kind) { + eat("("); + SignaturePattern sig; + + Shadow.Kind shadowKind = null; + if (kind.equals("execution")) { + sig = parseMethodOrConstructorSignaturePattern(); + if (sig.getKind() == Member.METHOD) { + shadowKind = Shadow.MethodExecution; + } else if (sig.getKind() == Member.CONSTRUCTOR) { + shadowKind = Shadow.ConstructorExecution; + } + } else if (kind.equals("call")) { + sig = parseMethodOrConstructorSignaturePattern(); + if (sig.getKind() == Member.METHOD) { + shadowKind = Shadow.MethodCall; + } else if (sig.getKind() == Member.CONSTRUCTOR) { + shadowKind = Shadow.ConstructorCall; + } + } else if (kind.equals("get")) { + sig = parseFieldSignaturePattern(); + shadowKind = Shadow.FieldGet; + } else if (kind.equals("set")) { + sig = parseFieldSignaturePattern(); + shadowKind = Shadow.FieldSet; + } else { + throw new ParserException("bad kind: " + kind, tokenSource.peek()); + } + eat(")"); + return new KindedPointcut(shadowKind, sig); + } + + /** Covers the 'lock()' and 'unlock()' pointcuts */ + private KindedPointcut parseMonitorPointcut(String kind) { + eat("("); + // TypePattern type = TypePattern.ANY; + eat(")"); + + if (kind.equals("lock")) { + return new KindedPointcut(Shadow.SynchronizationLock, new SignaturePattern(Member.MONITORENTER, ModifiersPattern.ANY, + TypePattern.ANY, TypePattern.ANY, + // type, + NamePattern.ANY, TypePatternList.ANY, ThrowsPattern.ANY, AnnotationTypePattern.ANY)); + } else { + return new KindedPointcut(Shadow.SynchronizationUnlock, new SignaturePattern(Member.MONITORENTER, ModifiersPattern.ANY, + TypePattern.ANY, TypePattern.ANY, + // type, + NamePattern.ANY, TypePatternList.ANY, ThrowsPattern.ANY, AnnotationTypePattern.ANY)); + } + } + + public TypePattern parseTypePattern() { + return parseTypePattern(false, false); + } + + public TypePattern parseTypePattern(boolean insideTypeParameters, boolean parameterAnnotationsPossible) { + TypePattern p = parseAtomicTypePattern(insideTypeParameters, parameterAnnotationsPossible); + if (maybeEat("&&")) { + p = new AndTypePattern(p, parseNotOrTypePattern(insideTypeParameters, parameterAnnotationsPossible)); + } + + if (maybeEat("||")) { + p = new OrTypePattern(p, parseTypePattern(insideTypeParameters, parameterAnnotationsPossible)); + } + return p; + } + + private TypePattern parseNotOrTypePattern(boolean insideTypeParameters, boolean parameterAnnotationsPossible) { + TypePattern p = parseAtomicTypePattern(insideTypeParameters, parameterAnnotationsPossible); + if (maybeEat("&&")) { + p = new AndTypePattern(p, parseTypePattern(insideTypeParameters, parameterAnnotationsPossible)); + } + return p; + } + + // Need to differentiate in here between two kinds of annotation pattern - depending on where the ( is + + private TypePattern parseAtomicTypePattern(boolean insideTypeParameters, boolean parameterAnnotationsPossible) { + AnnotationTypePattern ap = maybeParseAnnotationPattern(); // might be parameter annotation pattern or type annotation + // pattern + if (maybeEat("!")) { + // int startPos = tokenSource.peek(-1).getStart(); + // ??? we lose source location for true start of !type + + // An annotation, if processed, is outside of the Not - so here we have to build + // an And pattern containing the annotation and the not as left and right children + // *unless* the annotation pattern was just 'Any' then we can skip building the + // And and just return the Not directly (pr228980) + TypePattern p = null; + TypePattern tp = parseAtomicTypePattern(insideTypeParameters, parameterAnnotationsPossible); + if (!(ap instanceof AnyAnnotationTypePattern)) { + p = new NotTypePattern(tp); + p = new AndTypePattern(setAnnotationPatternForTypePattern(TypePattern.ANY, ap, false), p); + } else { + p = new NotTypePattern(tp); + } + return p; + } + if (maybeEat("(")) { + int openParenPos = tokenSource.peek(-1).getStart(); + TypePattern p = parseTypePattern(insideTypeParameters, false); + if ((p instanceof NotTypePattern) && !(ap instanceof AnyAnnotationTypePattern)) { + // dont set the annotation on it, we don't want the annotation to be + // considered as part of the not, it is outside the not (pr228980) + TypePattern tp = setAnnotationPatternForTypePattern(TypePattern.ANY, ap, parameterAnnotationsPossible); + p = new AndTypePattern(tp, p); + } else { + p = setAnnotationPatternForTypePattern(p, ap, parameterAnnotationsPossible); + } + eat(")"); + int closeParenPos = tokenSource.peek(-1).getStart(); + boolean isVarArgs = maybeEat("..."); + if (isVarArgs) { + p.setIsVarArgs(isVarArgs); + } + boolean isIncludeSubtypes = maybeEat("+"); + if (isIncludeSubtypes) { + p.includeSubtypes = true; // need the test because (A+) should not set subtypes to false! + } + p.start = openParenPos; + p.end = closeParenPos; + return p; + } + int startPos = tokenSource.peek().getStart(); + if (ap.start != -1) { + startPos = ap.start; + } + TypePattern p = parseSingleTypePattern(insideTypeParameters); + int endPos = tokenSource.peek(-1).getEnd(); + p = setAnnotationPatternForTypePattern(p, ap, false); + p.setLocation(sourceContext, startPos, endPos); + return p; + } + + private TypePattern setAnnotationPatternForTypePattern(TypePattern t, AnnotationTypePattern ap, + boolean parameterAnnotationsPattern) { + TypePattern ret = t; + if (parameterAnnotationsPattern) { + ap.setForParameterAnnotationMatch(); + } + if (ap != AnnotationTypePattern.ANY) { + if (t == TypePattern.ANY) { + if (t.annotationPattern == AnnotationTypePattern.ANY) { + return new AnyWithAnnotationTypePattern(ap); + } else { + return new AnyWithAnnotationTypePattern(new AndAnnotationTypePattern(ap, t.annotationPattern)); + } + // ret = new WildTypePattern(new NamePattern[] { NamePattern.ANY }, false, 0, false, null); + } + if (t.annotationPattern == AnnotationTypePattern.ANY) { + ret.setAnnotationTypePattern(ap); + } else { + ret.setAnnotationTypePattern(new AndAnnotationTypePattern(ap, t.annotationPattern)); // ??? + } + } + return ret; + } + + public AnnotationTypePattern maybeParseAnnotationPattern() { + AnnotationTypePattern ret = AnnotationTypePattern.ANY; + AnnotationTypePattern nextPattern = null; + while ((nextPattern = maybeParseSingleAnnotationPattern()) != null) { + if (ret == AnnotationTypePattern.ANY) { + ret = nextPattern; + } else { + ret = new AndAnnotationTypePattern(ret, nextPattern); + } + } + return ret; + } + + // PVAL cope with annotation values at other places in this code + public AnnotationTypePattern maybeParseSingleAnnotationPattern() { + AnnotationTypePattern ret = null; + Map<String, String> values = null; + // LALR(2) - fix by making "!@" a single token + int startIndex = tokenSource.getIndex(); + if (maybeEat("!")) { + if (maybeEat("@")) { + if (maybeEat("(")) { + TypePattern p = parseTypePattern(); + ret = new NotAnnotationTypePattern(new WildAnnotationTypePattern(p)); + eat(")"); + return ret; + } else { + TypePattern p = parseSingleTypePattern(); + if (maybeEatAdjacent("(")) { + values = parseAnnotationValues(); + eat(")"); + ret = new NotAnnotationTypePattern(new WildAnnotationTypePattern(p, values)); + } else { + ret = new NotAnnotationTypePattern(new WildAnnotationTypePattern(p)); + } + return ret; + } + } else { + tokenSource.setIndex(startIndex); // not for us! + return ret; + } + } + if (maybeEat("@")) { + if (maybeEat("(")) { + TypePattern p = parseTypePattern(); + ret = new WildAnnotationTypePattern(p); + eat(")"); + return ret; + } else { + int atPos = tokenSource.peek(-1).getStart(); + TypePattern p = parseSingleTypePattern(); + if (maybeEatAdjacent("(")) { + values = parseAnnotationValues(); + eat(")"); + ret = new WildAnnotationTypePattern(p, values); + } else { + ret = new WildAnnotationTypePattern(p); + } + ret.start = atPos; + return ret; + } + } else { + tokenSource.setIndex(startIndex); // not for us! + return ret; + } + } + + // Parse annotation values. In an expression in @A(a=b,c=d) this method will be + // parsing the a=b,c=d.) + public Map<String, String> parseAnnotationValues() { + Map<String, String> values = new HashMap<String, String>(); + boolean seenDefaultValue = false; + do { + String possibleKeyString = parseAnnotationNameValuePattern(); + if (possibleKeyString == null) { + throw new ParserException("expecting simple literal ", tokenSource.peek(-1)); + } + // did they specify just a single entry 'v' or a keyvalue pair 'k=v' + if (maybeEat("=")) { + // it was a key! + String valueString = parseAnnotationNameValuePattern(); + if (valueString == null) { + throw new ParserException("expecting simple literal ", tokenSource.peek(-1)); + } + values.put(possibleKeyString, valueString); + } else if (maybeEat("!=")) { + // it was a key, with a != + String valueString = parseAnnotationNameValuePattern(); + if (valueString == null) { + throw new ParserException("expecting simple literal ", tokenSource.peek(-1)); + } + // negation is captured by adding a trailing ! to the key name + values.put(possibleKeyString + "!", valueString); + } else { + if (seenDefaultValue) { + throw new ParserException("cannot specify two default values", tokenSource.peek(-1)); + } + seenDefaultValue = true; + values.put("value", possibleKeyString); + } + } while (maybeEat(",")); // keep going whilst there are ',' + return values; + } + + public TypePattern parseSingleTypePattern() { + return parseSingleTypePattern(false); + } + + public TypePattern parseSingleTypePattern(boolean insideTypeParameters) { + if (insideTypeParameters && maybeEat("?")) { + return parseGenericsWildcardTypePattern(); + } + if (allowHasTypePatterns) { + if (maybeEatIdentifier("hasmethod")) { + return parseHasMethodTypePattern(); + } + if (maybeEatIdentifier("hasfield")) { + return parseHasFieldTypePattern(); + } + } + + // // Check for a type category + // IToken token = tokenSource.peek(); + // if (token.isIdentifier()) { + // String category = token.getString(); + // TypeCategoryTypePattern typeIsPattern = null; + // if (category.equals("isClass")) { + // typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.CLASS); + // } else if (category.equals("isAspect")) { + // typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ASPECT); + // } else if (category.equals("isInterface")) { + // typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.INTERFACE); + // } else if (category.equals("isInner")) { + // typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.INNER); + // } else if (category.equals("isAnonymous")) { + // typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ANONYMOUS); + // } else if (category.equals("isEnum")) { + // typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ENUM); + // } else if (category.equals("isAnnotation")) { + // typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ANNOTATION); + // } + // if (typeIsPattern != null) { + // tokenSource.next(); + // typeIsPattern.setLocation(tokenSource.getSourceContext(), token.getStart(), token.getEnd()); + // return typeIsPattern; + // } + // } + if (maybeEatIdentifier("is")) { + int pos = tokenSource.getIndex() - 1; + TypePattern typeIsPattern = parseIsTypePattern(); + if (typeIsPattern != null) { + return typeIsPattern; + } + // rewind as if we never tried to parse it as a typeIs + tokenSource.setIndex(pos); + } + + List<NamePattern> names = parseDottedNamePattern(); + + int dim = 0; + while (maybeEat("[")) { + eat("]"); + dim++; + } + + TypePatternList typeParameters = maybeParseTypeParameterList(); + int endPos = tokenSource.peek(-1).getEnd(); + + boolean includeSubtypes = maybeEat("+"); + + // TODO do we need to associate the + with either the type or the array? + while (maybeEat("[")) { + eat("]"); + dim++; + } + + boolean isVarArgs = maybeEat("..."); + + // ??? what about the source location of any's???? + if (names.size() == 1 && names.get(0).isAny() && dim == 0 && !isVarArgs && typeParameters == null) { + return TypePattern.ANY; + } + + // Notice we increase the dimensions if varargs is set. this is to allow type matching to + // succeed later: The actual signature at runtime of a method declared varargs is an array type of + // the original declared type (so Integer... becomes Integer[] in the bytecode). So, here for the + // pattern 'Integer...' we create a WildTypePattern 'Integer[]' with varargs set. If this matches + // during shadow matching, we confirm that the varargs flags match up before calling it a successful + // match. + return new WildTypePattern(names, includeSubtypes, dim + (isVarArgs ? 1 : 0), endPos, isVarArgs, typeParameters); + } + + public TypePattern parseHasMethodTypePattern() { + int startPos = tokenSource.peek(-1).getStart(); + eat("("); + SignaturePattern sp = parseMethodOrConstructorSignaturePattern(); + eat(")"); + int endPos = tokenSource.peek(-1).getEnd(); + HasMemberTypePattern ret = new HasMemberTypePattern(sp); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + } + + /** + * Attempt to parse a typeIs(<category>) construct. If it cannot be parsed we just return null and that should cause the caller + * to reset their position and attempt to consume it in another way. This means we won't have problems here: execution(* + * typeIs(..)) because someone has decided to call a method the same as our construct. + * + * @return a TypeIsTypePattern or null if could not be parsed + */ + public TypePattern parseIsTypePattern() { + int startPos = tokenSource.peek(-1).getStart(); // that will be the start of the 'typeIs' + if (!maybeEatAdjacent("(")) { + return null; + } + IToken token = tokenSource.next(); + TypeCategoryTypePattern typeIsPattern = null; + if (token.isIdentifier()) { + String category = token.getString(); + if (category.equals("ClassType")) { + typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.CLASS); + } else if (category.equals("AspectType")) { + typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ASPECT); + } else if (category.equals("InterfaceType")) { + typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.INTERFACE); + } else if (category.equals("InnerType")) { + typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.INNER); + } else if (category.equals("AnonymousType")) { + typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ANONYMOUS); + } else if (category.equals("EnumType")) { + typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ENUM); + } else if (category.equals("AnnotationType")) { + typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ANNOTATION); + } else if (category.equals("FinalType")) { + typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.FINAL); + } else if (category.equals("AbstractType")) { + typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ABSTRACT); + } + } + if (typeIsPattern == null) { + return null; + } + if (!maybeEat(")")) { + throw new ParserException(")", tokenSource.peek()); + } + int endPos = tokenSource.peek(-1).getEnd(); + typeIsPattern.setLocation(tokenSource.getSourceContext(), startPos, endPos); + return typeIsPattern; + } + + // if (names.size() == 1 && !names.get(0).isAny()) { + // if (maybeEatAdjacent("(")) { + // if (maybeEat(")")) { + // // likely to be one of isClass()/isInterface()/isInner()/isAnonymous()/isAspect() + // if (names.size() == 1) { + // NamePattern np = names.get(0); + // String simpleName = np.maybeGetSimpleName(); + // if (simpleName != null) { + + // return new TypeCategoryTypePattern(TypeCategoryTypePattern.ANNOTATION, np); + // } else { + // throw new ParserException( + // "not a supported type category, supported are isClass/isInterface/isEnum/isAnnotation/isInner/isAnonymous", + // tokenSource.peek(-3)); + // } + // } + // int stop = 1; + // // return new WildTypePattern(names, includeSubtypes, dim + (isVarArgs ? 1 : 0), endPos, isVarArgs, + // // typeParameters); + // } + // } else { + // throw new ParserException("category type pattern is missing closing parentheses", tokenSource.peek(-2)); + // } + // } + // } + + public TypePattern parseHasFieldTypePattern() { + int startPos = tokenSource.peek(-1).getStart(); + eat("("); + SignaturePattern sp = parseFieldSignaturePattern(); + eat(")"); + int endPos = tokenSource.peek(-1).getEnd(); + HasMemberTypePattern ret = new HasMemberTypePattern(sp); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + } + + public TypePattern parseGenericsWildcardTypePattern() { + List<NamePattern> names = new ArrayList<NamePattern>(); + names.add(new NamePattern("?")); + TypePattern upperBound = null; + TypePattern[] additionalInterfaceBounds = new TypePattern[0]; + TypePattern lowerBound = null; + if (maybeEatIdentifier("extends")) { + upperBound = parseTypePattern(false, false); + additionalInterfaceBounds = maybeParseAdditionalInterfaceBounds(); + } + if (maybeEatIdentifier("super")) { + lowerBound = parseTypePattern(false, false); + } + int endPos = tokenSource.peek(-1).getEnd(); + return new WildTypePattern(names, false, 0, endPos, false, null, upperBound, additionalInterfaceBounds, lowerBound); + } + + // private AnnotationTypePattern completeAnnotationPattern(AnnotationTypePattern p) { + // if (maybeEat("&&")) { + // return new AndAnnotationTypePattern(p,parseNotOrAnnotationPattern()); + // } + // if (maybeEat("||")) { + // return new OrAnnotationTypePattern(p,parseAnnotationTypePattern()); + // } + // return p; + // } + // + // protected AnnotationTypePattern parseAnnotationTypePattern() { + // AnnotationTypePattern ap = parseAtomicAnnotationPattern(); + // if (maybeEat("&&")) { + // ap = new AndAnnotationTypePattern(ap, parseNotOrAnnotationPattern()); + // } + // + // if (maybeEat("||")) { + // ap = new OrAnnotationTypePattern(ap, parseAnnotationTypePattern()); + // } + // return ap; + // } + // + // private AnnotationTypePattern parseNotOrAnnotationPattern() { + // AnnotationTypePattern p = parseAtomicAnnotationPattern(); + // if (maybeEat("&&")) { + // p = new AndAnnotationTypePattern(p,parseAnnotationTypePattern()); + // } + // return p; + // } + + protected ExactAnnotationTypePattern parseAnnotationNameOrVarTypePattern() { + ExactAnnotationTypePattern p = null; + int startPos = tokenSource.peek().getStart(); + if (maybeEat("@")) { + throw new ParserException("@Foo form was deprecated in AspectJ 5 M2: annotation name or var ", tokenSource.peek(-1)); + } + p = parseSimpleAnnotationName(); + int endPos = tokenSource.peek(-1).getEnd(); + p.setLocation(sourceContext, startPos, endPos); + // For optimized syntax that allows binding directly to annotation values (pr234943) + if (maybeEat("(")) { + String formalName = parseIdentifier(); + p = new ExactAnnotationFieldTypePattern(p, formalName); + eat(")"); + } + return p; + } + + /** + * @return + */ + private ExactAnnotationTypePattern parseSimpleAnnotationName() { + // the @ has already been eaten... + ExactAnnotationTypePattern p; + StringBuffer annotationName = new StringBuffer(); + annotationName.append(parseIdentifier()); + while (maybeEat(".")) { + annotationName.append('.'); + annotationName.append(parseIdentifier()); + } + UnresolvedType type = UnresolvedType.forName(annotationName.toString()); + p = new ExactAnnotationTypePattern(type, null); + return p; + } + + // private AnnotationTypePattern parseAtomicAnnotationPattern() { + // if (maybeEat("!")) { + // //int startPos = tokenSource.peek(-1).getStart(); + // //??? we lose source location for true start of !type + // AnnotationTypePattern p = new NotAnnotationTypePattern(parseAtomicAnnotationPattern()); + // return p; + // } + // if (maybeEat("(")) { + // AnnotationTypePattern p = parseAnnotationTypePattern(); + // eat(")"); + // return p; + // } + // int startPos = tokenSource.peek().getStart(); + // eat("@"); + // StringBuffer annotationName = new StringBuffer(); + // annotationName.append(parseIdentifier()); + // while (maybeEat(".")) { + // annotationName.append('.'); + // annotationName.append(parseIdentifier()); + // } + // UnresolvedType type = UnresolvedType.forName(annotationName.toString()); + // AnnotationTypePattern p = new ExactAnnotationTypePattern(type); + // int endPos = tokenSource.peek(-1).getEnd(); + // p.setLocation(sourceContext, startPos, endPos); + // return p; + // } + + public List<NamePattern> parseDottedNamePattern() { + List<NamePattern> names = new ArrayList<NamePattern>(); + StringBuffer buf = new StringBuffer(); + IToken previous = null; + boolean justProcessedEllipsis = false; // Remember if we just dealt with an ellipsis (PR61536) + boolean justProcessedDot = false; + boolean onADot = false; + + while (true) { + IToken tok = null; + int startPos = tokenSource.peek().getStart(); + String afterDot = null; + while (true) { + if (previous != null && previous.getString().equals(".")) { + justProcessedDot = true; + } + tok = tokenSource.peek(); + onADot = (tok.getString().equals(".")); + if (previous != null) { + if (!isAdjacent(previous, tok)) { + break; + } + } + if (tok.getString() == "*" || (tok.isIdentifier() && tok.getString() != "...")) { + buf.append(tok.getString()); + } else if (tok.getString() == "...") { + break; + } else if (tok.getLiteralKind() != null) { + // System.err.println("literal kind: " + tok.getString()); + String s = tok.getString(); + int dot = s.indexOf('.'); + if (dot != -1) { + buf.append(s.substring(0, dot)); + afterDot = s.substring(dot + 1); + previous = tokenSource.next(); + break; + } + buf.append(s); // ??? so-so + } else { + break; + } + previous = tokenSource.next(); + // XXX need to handle floats and other fun stuff + } + int endPos = tokenSource.peek(-1).getEnd(); + if (buf.length() == 0 && names.isEmpty()) { + throw new ParserException("name pattern", tok); + } + + if (buf.length() == 0 && justProcessedEllipsis) { + throw new ParserException("name pattern cannot finish with ..", tok); + } + if (buf.length() == 0 && justProcessedDot && !onADot) { + throw new ParserException("name pattern cannot finish with .", tok); + } + + if (buf.length() == 0) { + names.add(NamePattern.ELLIPSIS); + justProcessedEllipsis = true; + } else { + checkLegalName(buf.toString(), previous); + NamePattern ret = new NamePattern(buf.toString()); + ret.setLocation(sourceContext, startPos, endPos); + names.add(ret); + justProcessedEllipsis = false; + } + + if (afterDot == null) { + buf.setLength(0); + // no elipsis or dotted name part + if (!maybeEat(".")) { + break; + // go on + } else { + previous = tokenSource.peek(-1); + } + } else { + buf.setLength(0); + buf.append(afterDot); + afterDot = null; + } + } + // System.err.println("parsed: " + names); + return names; + } + + // supported form 'a.b.c.d' or just 'a' + public String parseAnnotationNameValuePattern() { + StringBuffer buf = new StringBuffer(); + IToken tok; + // int startPos = + tokenSource.peek().getStart(); + boolean dotOK = false; + int depth = 0; + while (true) { + tok = tokenSource.peek(); + // keep going until we hit ')' or '=' or ',' + if (tok.getString() == ")" && depth == 0) { + break; + } + if (tok.getString() == "!=" && depth == 0) { + break; + } + if (tok.getString() == "=" && depth == 0) { + break; + } + if (tok.getString() == "," && depth == 0) { + break; + } + if (tok == IToken.EOF) { + throw new ParserException("eof", tokenSource.peek()); + } + + // keep track of nested brackets + if (tok.getString() == "(") { + depth++; + } + if (tok.getString() == ")") { + depth--; + } + if (tok.getString() == "{") { + depth++; + } + if (tok.getString() == "}") { + depth--; + } + + if (tok.getString() == "." && !dotOK) { + throw new ParserException("dot not expected", tok); + } + buf.append(tok.getString()); + tokenSource.next(); + dotOK = true; + } + return buf.toString(); + } + + public NamePattern parseNamePattern() { + StringBuffer buf = new StringBuffer(); + IToken previous = null; + IToken tok; + int startPos = tokenSource.peek().getStart(); + while (true) { + tok = tokenSource.peek(); + if (previous != null) { + if (!isAdjacent(previous, tok)) { + break; + } + } + if (tok.getString() == "*" || tok.isIdentifier()) { + buf.append(tok.getString()); + } else if (tok.getLiteralKind() != null) { + // System.err.println("literal kind: " + tok.getString()); + String s = tok.getString(); + if (s.indexOf('.') != -1) { + break; + } + buf.append(s); // ??? so-so + } else { + break; + } + previous = tokenSource.next(); + // XXX need to handle floats and other fun stuff + } + int endPos = tokenSource.peek(-1).getEnd(); + if (buf.length() == 0) { + throw new ParserException("name pattern", tok); + } + + checkLegalName(buf.toString(), previous); + NamePattern ret = new NamePattern(buf.toString()); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + } + + private void checkLegalName(String s, IToken tok) { + char ch = s.charAt(0); + if (!(ch == '*' || Character.isJavaIdentifierStart(ch))) { + throw new ParserException("illegal identifier start (" + ch + ")", tok); + } + + for (int i = 1, len = s.length(); i < len; i++) { + ch = s.charAt(i); + if (!(ch == '*' || Character.isJavaIdentifierPart(ch))) { + throw new ParserException("illegal identifier character (" + ch + ")", tok); + } + } + + } + + private boolean isAdjacent(IToken first, IToken second) { + return first.getEnd() == second.getStart() - 1; + } + + public ModifiersPattern parseModifiersPattern() { + int requiredFlags = 0; + int forbiddenFlags = 0; + int start; + while (true) { + start = tokenSource.getIndex(); + boolean isForbidden = false; + isForbidden = maybeEat("!"); + IToken t = tokenSource.next(); + int flag = ModifiersPattern.getModifierFlag(t.getString()); + if (flag == -1) { + break; + } + if (isForbidden) { + forbiddenFlags |= flag; + } else { + requiredFlags |= flag; + } + } + + tokenSource.setIndex(start); + if (requiredFlags == 0 && forbiddenFlags == 0) { + return ModifiersPattern.ANY; + } else { + return new ModifiersPattern(requiredFlags, forbiddenFlags); + } + } + + public TypePatternList parseArgumentsPattern(boolean parameterAnnotationsPossible) { + List<TypePattern> patterns = new ArrayList<TypePattern>(); + eat("("); + + // () + if (maybeEat(")")) { + return new TypePatternList(); + } + + do { + if (maybeEat(".")) { // .. + eat("."); + patterns.add(TypePattern.ELLIPSIS); + } else { + patterns.add(parseTypePattern(false, parameterAnnotationsPossible)); + } + } while (maybeEat(",")); + eat(")"); + return new TypePatternList(patterns); + } + + public AnnotationPatternList parseArgumentsAnnotationPattern() { + List<AnnotationTypePattern> patterns = new ArrayList<AnnotationTypePattern>(); + eat("("); + if (maybeEat(")")) { + return new AnnotationPatternList(); + } + + do { + if (maybeEat(".")) { + eat("."); + patterns.add(AnnotationTypePattern.ELLIPSIS); + } else if (maybeEat("*")) { + patterns.add(AnnotationTypePattern.ANY); + } else { + patterns.add(parseAnnotationNameOrVarTypePattern()); + } + } while (maybeEat(",")); + eat(")"); + return new AnnotationPatternList(patterns); + } + + public ThrowsPattern parseOptionalThrowsPattern() { + IToken t = tokenSource.peek(); + if (t.isIdentifier() && t.getString().equals("throws")) { + tokenSource.next(); + List<TypePattern> required = new ArrayList<TypePattern>(); + List<TypePattern> forbidden = new ArrayList<TypePattern>(); + do { + boolean isForbidden = maybeEat("!"); + // ???might want an error for a second ! without a paren + TypePattern p = parseTypePattern(); + if (isForbidden) { + forbidden.add(p); + } else { + required.add(p); + } + } while (maybeEat(",")); + return new ThrowsPattern(new TypePatternList(required), new TypePatternList(forbidden)); + } + return ThrowsPattern.ANY; + } + + public SignaturePattern parseMethodOrConstructorSignaturePattern() { + int startPos = tokenSource.peek().getStart(); + AnnotationTypePattern annotationPattern = maybeParseAnnotationPattern(); + ModifiersPattern modifiers = parseModifiersPattern(); + TypePattern returnType = parseTypePattern(false, false); + + TypePattern declaringType; + NamePattern name = null; + MemberKind kind; + // here we can check for 'new' + if (maybeEatNew(returnType)) { + kind = Member.CONSTRUCTOR; + if (returnType.toString().length() == 0) { + declaringType = TypePattern.ANY; + } else { + declaringType = returnType; + } + returnType = TypePattern.ANY; + name = NamePattern.ANY; + } else { + kind = Member.METHOD; + IToken nameToken = tokenSource.peek(); + declaringType = parseTypePattern(false, false); + if (maybeEat(".")) { + nameToken = tokenSource.peek(); + name = parseNamePattern(); + } else { + name = tryToExtractName(declaringType); + if (declaringType.toString().equals("")) { + declaringType = TypePattern.ANY; + } + } + if (name == null) { + throw new ParserException("name pattern", tokenSource.peek()); + } + String simpleName = name.maybeGetSimpleName(); + // XXX should add check for any Java keywords + if (simpleName != null && simpleName.equals("new")) { + throw new ParserException("method name (not constructor)", nameToken); + } + } + + TypePatternList parameterTypes = parseArgumentsPattern(true); + + ThrowsPattern throwsPattern = parseOptionalThrowsPattern(); + SignaturePattern ret = new SignaturePattern(kind, modifiers, returnType, declaringType, name, parameterTypes, + throwsPattern, annotationPattern); + int endPos = tokenSource.peek(-1).getEnd(); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + } + + private boolean maybeEatNew(TypePattern returnType) { + if (returnType instanceof WildTypePattern) { + WildTypePattern p = (WildTypePattern) returnType; + if (p.maybeExtractName("new")) { + return true; + } + } + int start = tokenSource.getIndex(); + if (maybeEat(".")) { + String id = maybeEatIdentifier(); + if (id != null && id.equals("new")) { + return true; + } + tokenSource.setIndex(start); + } + + return false; + } + + public ISignaturePattern parseMaybeParenthesizedFieldSignaturePattern() { + boolean negated = tokenSource.peek().getString().equals("!") && tokenSource.peek(1).getString().equals("("); + if (negated) { + eat("!"); + } + ISignaturePattern result = null; + if (maybeEat("(")) { + result = parseCompoundFieldSignaturePattern(); + eat(")", "missing ')' - unbalanced parentheses around field signature pattern in declare @field"); + if (negated) { + result = new NotSignaturePattern(result); + } + } else { + result = parseFieldSignaturePattern(); + } + return result; + } + + public ISignaturePattern parseMaybeParenthesizedMethodOrConstructorSignaturePattern(boolean isMethod) { + boolean negated = tokenSource.peek().getString().equals("!") && tokenSource.peek(1).getString().equals("("); + if (negated) { + eat("!"); + } + ISignaturePattern result = null; + if (maybeEat("(")) { + result = parseCompoundMethodOrConstructorSignaturePattern(isMethod); + eat(")", "missing ')' - unbalanced parentheses around method/ctor signature pattern in declare annotation"); + if (negated) { + result = new NotSignaturePattern(result); + } + } else { + SignaturePattern sp = parseMethodOrConstructorSignaturePattern(); + boolean isConstructorPattern = (sp.getKind() == Member.CONSTRUCTOR); + if (isMethod && isConstructorPattern) { + throw new ParserException("method signature pattern", tokenSource.peek(-1)); + } + if (!isMethod && !isConstructorPattern) { + throw new ParserException("constructor signature pattern", tokenSource.peek(-1)); + } + result = sp; + } + + return result; + } + + public SignaturePattern parseFieldSignaturePattern() { + int startPos = tokenSource.peek().getStart(); + + // TypePatternList followMe = TypePatternList.ANY; + + AnnotationTypePattern annotationPattern = maybeParseAnnotationPattern(); + ModifiersPattern modifiers = parseModifiersPattern(); + TypePattern returnType = parseTypePattern(); + TypePattern declaringType = parseTypePattern(); + NamePattern name; + // System.err.println("parsed field: " + declaringType.toString()); + if (maybeEat(".")) { + name = parseNamePattern(); + } else { + name = tryToExtractName(declaringType); + if (name == null) { + throw new ParserException("name pattern", tokenSource.peek()); + } + if (declaringType.toString().equals("")) { + declaringType = TypePattern.ANY; + } + } + SignaturePattern ret = new SignaturePattern(Member.FIELD, modifiers, returnType, declaringType, name, TypePatternList.ANY, + ThrowsPattern.ANY, annotationPattern); + + int endPos = tokenSource.peek(-1).getEnd(); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + } + + private NamePattern tryToExtractName(TypePattern nextType) { + if (nextType == TypePattern.ANY) { + return NamePattern.ANY; + } else if (nextType instanceof WildTypePattern) { + WildTypePattern p = (WildTypePattern) nextType; + return p.extractName(); + } else { + return null; + } + } + + /** + * Parse type variable declarations for a generic method or at the start of a signature pointcut to identify type variable names + * in a generic type. + * + * @param includeParameterizedTypes + * @return + */ + public TypeVariablePatternList maybeParseTypeVariableList() { + if (!maybeEat("<")) { + return null; + } + List<TypeVariablePattern> typeVars = new ArrayList<TypeVariablePattern>(); + TypeVariablePattern t = parseTypeVariable(); + typeVars.add(t); + while (maybeEat(",")) { + TypeVariablePattern nextT = parseTypeVariable(); + typeVars.add(nextT); + } + eat(">"); + TypeVariablePattern[] tvs = new TypeVariablePattern[typeVars.size()]; + typeVars.toArray(tvs); + return new TypeVariablePatternList(tvs); + } + + // of the form execution<T,S,V> - allows identifiers only + public String[] maybeParseSimpleTypeVariableList() { + if (!maybeEat("<")) { + return null; + } + List<String> typeVarNames = new ArrayList<String>(); + do { + typeVarNames.add(parseIdentifier()); + } while (maybeEat(",")); + eat(">", "',' or '>'"); + String[] tvs = new String[typeVarNames.size()]; + typeVarNames.toArray(tvs); + return tvs; + } + + public TypePatternList maybeParseTypeParameterList() { + if (!maybeEat("<")) { + return null; + } + List<TypePattern> typePats = new ArrayList<TypePattern>(); + do { + TypePattern tp = parseTypePattern(true, false); + typePats.add(tp); + } while (maybeEat(",")); + eat(">"); + TypePattern[] tps = new TypePattern[typePats.size()]; + typePats.toArray(tps); + return new TypePatternList(tps); + } + + public TypeVariablePattern parseTypeVariable() { + TypePattern upperBound = null; + TypePattern[] additionalInterfaceBounds = null; + TypePattern lowerBound = null; + String typeVariableName = parseIdentifier(); + if (maybeEatIdentifier("extends")) { + upperBound = parseTypePattern(); + additionalInterfaceBounds = maybeParseAdditionalInterfaceBounds(); + } else if (maybeEatIdentifier("super")) { + lowerBound = parseTypePattern(); + } + return new TypeVariablePattern(typeVariableName, upperBound, additionalInterfaceBounds, lowerBound); + } + + private TypePattern[] maybeParseAdditionalInterfaceBounds() { + List<TypePattern> boundsList = new ArrayList<TypePattern>(); + while (maybeEat("&")) { + TypePattern tp = parseTypePattern(); + boundsList.add(tp); + } + if (boundsList.size() == 0) { + return null; + } + TypePattern[] ret = new TypePattern[boundsList.size()]; + boundsList.toArray(ret); + return ret; + } + + public String parsePossibleStringSequence(boolean shouldEnd) { + StringBuffer result = new StringBuffer(); + + IToken token = tokenSource.next(); + if (token.getLiteralKind() == null) { + throw new ParserException("string", token); + } + while (token.getLiteralKind().equals("string")) { + result.append(token.getString()); + boolean plus = maybeEat("+"); + if (!plus) { + break; + } + token = tokenSource.next(); + if (token.getLiteralKind() == null) { + throw new ParserException("string", token); + } + } + eatIdentifier(";"); + IToken t = tokenSource.next(); + if (shouldEnd && t != IToken.EOF) { + throw new ParserException("<string>;", token); + } + // bug 125027: since we've eaten the ";" we need to set the index + // to be one less otherwise the end position isn't set correctly. + int currentIndex = tokenSource.getIndex(); + tokenSource.setIndex(currentIndex - 1); + + return result.toString(); + + } + + public String parseStringLiteral() { + IToken token = tokenSource.next(); + String literalKind = token.getLiteralKind(); + if (literalKind == "string") { + return token.getString(); + } + + throw new ParserException("string", token); + } + + public String parseIdentifier() { + IToken token = tokenSource.next(); + if (token.isIdentifier()) { + return token.getString(); + } + throw new ParserException("identifier", token); + } + + public void eatIdentifier(String expectedValue) { + IToken next = tokenSource.next(); + if (!next.getString().equals(expectedValue)) { + throw new ParserException(expectedValue, next); + } + } + + public boolean maybeEatIdentifier(String expectedValue) { + IToken next = tokenSource.peek(); + if (next.getString().equals(expectedValue)) { + tokenSource.next(); + return true; + } else { + return false; + } + } + + public void eat(String expectedValue) { + eat(expectedValue, expectedValue); + } + + private void eat(String expectedValue, String expectedMessage) { + IToken next = nextToken(); + if (next.getString() != expectedValue) { + if (expectedValue.equals(">") && next.getString().startsWith(">")) { + // handle problem of >> and >>> being lexed as single tokens + pendingRightArrows = BasicToken.makeLiteral(next.getString().substring(1).intern(), "string", next.getStart() + 1, + next.getEnd()); + return; + } + throw new ParserException(expectedMessage, next); + } + } + + private IToken pendingRightArrows; + + private IToken nextToken() { + if (pendingRightArrows != null) { + IToken ret = pendingRightArrows; + pendingRightArrows = null; + return ret; + } else { + return tokenSource.next(); + } + } + + public boolean maybeEatAdjacent(String token) { + IToken next = tokenSource.peek(); + if (next.getString() == token) { + if (isAdjacent(tokenSource.peek(-1), next)) { + tokenSource.next(); + return true; + } + } + return false; + } + + public boolean maybeEat(String token) { + IToken next = tokenSource.peek(); + if (next.getString() == token) { + tokenSource.next(); + return true; + } else { + return false; + } + } + + public String maybeEatIdentifier() { + IToken next = tokenSource.peek(); + if (next.isIdentifier()) { + tokenSource.next(); + return next.getString(); + } else { + return null; + } + } + + public boolean peek(String token) { + IToken next = tokenSource.peek(); + return next.getString() == token; + } + + public void checkEof() { + IToken last = tokenSource.next(); + if (last != IToken.EOF) { + throw new ParserException("unexpected pointcut element: " + last.toString(), last); + } + } + + public PatternParser(String data) { + this(BasicTokenSource.makeTokenSource(data, null)); + } + + public PatternParser(String data, ISourceContext context) { + this(BasicTokenSource.makeTokenSource(data, context)); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerCflow.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerCflow.java new file mode 100644 index 000000000..1ad834e06 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerCflow.java @@ -0,0 +1,175 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.CrosscuttingMembers; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Test; + +public class PerCflow extends PerClause { + private final boolean isBelow; + private final Pointcut entry; + + public PerCflow(Pointcut entry, boolean isBelow) { + this.entry = entry; + this.isBelow = isBelow; + } + + // ----- + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + return FuzzyBoolean.YES; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + // assert bindings == null; + entry.resolve(scope); + } + + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + PerCflow ret = new PerCflow(entry.parameterizeWith(typeVariableMap, w), isBelow); + ret.copyLocationFrom(this); + return ret; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + Expr myInstance = Expr.makeCallExpr(AjcMemberMaker.perCflowAspectOfMethod(inAspect), Expr.NONE, inAspect); + state.setAspectInstance(myInstance); + return Test.makeCall(AjcMemberMaker.perCflowHasAspectMethod(inAspect), Expr.NONE); + } + + public PerClause concretize(ResolvedType inAspect) { + PerCflow ret = new PerCflow(entry, isBelow); + ret.inAspect = inAspect; + if (inAspect.isAbstract()) { + return ret; + } + + Member cflowStackField = new ResolvedMemberImpl(Member.FIELD, inAspect, Modifier.PUBLIC | Modifier.STATIC, + UnresolvedType.forName(NameMangler.CFLOW_STACK_TYPE), NameMangler.PERCFLOW_FIELD_NAME, UnresolvedType.NONE); + + World world = inAspect.getWorld(); + + CrosscuttingMembers xcut = inAspect.crosscuttingMembers; + + Collection<ShadowMunger> previousCflowEntries = xcut.getCflowEntries(); + Pointcut concreteEntry = entry.concretize(inAspect, inAspect, 0, null); // IntMap + // . + // EMPTY + // ) + // ; + List<ShadowMunger> innerCflowEntries = new ArrayList<ShadowMunger>(xcut.getCflowEntries()); + innerCflowEntries.removeAll(previousCflowEntries); + + xcut.addConcreteShadowMunger(Advice.makePerCflowEntry(world, concreteEntry, isBelow, cflowStackField, inAspect, + innerCflowEntries)); + + // ATAJ: add a munger to add the aspectOf(..) to the @AJ aspects + if (inAspect.isAnnotationStyleAspect() && !inAspect.isAbstract()) { + inAspect.crosscuttingMembers.addLateTypeMunger(inAspect.getWorld().getWeavingSupport() + .makePerClauseAspect(inAspect, getKind())); + } + + // ATAJ inline around advice support - don't use a late munger to allow + // around inling for itself + if (inAspect.isAnnotationStyleAspect() && !inAspect.getWorld().isXnoInline()) { + inAspect.crosscuttingMembers.addTypeMunger(inAspect.getWorld().getWeavingSupport() + .createAccessForInlineMunger(inAspect)); + } + + return ret; + } + + public void write(CompressingDataOutputStream s) throws IOException { + PERCFLOW.write(s); + entry.write(s); + s.writeBoolean(isBelow); + writeLocation(s); + } + + public static PerClause readPerClause(VersionedDataInputStream s, ISourceContext context) throws IOException { + PerCflow ret = new PerCflow(Pointcut.read(s, context), s.readBoolean()); + ret.readLocation(context, s); + return ret; + } + + public PerClause.Kind getKind() { + return PERCFLOW; + } + + public Pointcut getEntry() { + return entry; + } + + public String toString() { + return "percflow(" + inAspect + " on " + entry + ")"; + } + + public String toDeclarationString() { + if (isBelow) { + return "percflowbelow(" + entry + ")"; + } + return "percflow(" + entry + ")"; + } + + public boolean equals(Object other) { + if (!(other instanceof PerCflow)) { + return false; + } + PerCflow pc = (PerCflow) other; + return (pc.isBelow && isBelow) && ((pc.inAspect == null) ? (inAspect == null) : pc.inAspect.equals(inAspect)) + && ((pc.entry == null) ? (entry == null) : pc.entry.equals(entry)); + } + + public int hashCode() { + int result = 17; + result = 37 * result + (isBelow ? 0 : 1); + result = 37 * result + ((inAspect == null) ? 0 : inAspect.hashCode()); + result = 37 * result + ((entry == null) ? 0 : entry.hashCode()); + return result; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerClause.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerClause.java new file mode 100644 index 000000000..62b2b1b85 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerClause.java @@ -0,0 +1,91 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.patterns; + +import java.io.IOException; + +import org.aspectj.util.TypeSafeEnum; +import org.aspectj.weaver.*; + +// PTWIMPL New kind added to this class, can be (de)serialized +public abstract class PerClause extends Pointcut { + protected ResolvedType inAspect; + + public static PerClause readPerClause(VersionedDataInputStream s, ISourceContext context) throws IOException { + Kind kind = Kind.read(s); + if (kind == SINGLETON) return PerSingleton.readPerClause(s, context); + else if (kind == PERCFLOW) return PerCflow.readPerClause(s, context); + else if (kind == PEROBJECT) return PerObject.readPerClause(s, context); + else if (kind == FROMSUPER) return PerFromSuper.readPerClause(s, context); + else if (kind == PERTYPEWITHIN) return PerTypeWithin.readPerClause(s,context); + + throw new BCException("unknown kind: " + kind); + } + + public final Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + throw new RuntimeException("unimplemented: wrong concretize"); + } + + public abstract PerClause concretize(ResolvedType inAspect); + + public abstract PerClause.Kind getKind(); + + public abstract String toDeclarationString(); + + public static class Kind extends TypeSafeEnum { + public Kind(String name, int key) { super(name, key); } + + public static Kind read(VersionedDataInputStream s) throws IOException { + int key = s.readByte(); + switch(key) { + case 1: return SINGLETON; + case 2: return PERCFLOW; + case 3: return PEROBJECT; + case 4: return FROMSUPER; + case 5: return PERTYPEWITHIN; + } + throw new BCException("weird kind " + key); + } + } + + public static final Kind SINGLETON = new Kind("issingleton", 1); + public static final Kind PERCFLOW = new Kind("percflow", 2); + public static final Kind PEROBJECT = new Kind("perobject", 3); + public static final Kind FROMSUPER = new Kind("fromsuper", 4); + public static final Kind PERTYPEWITHIN = new Kind("pertypewithin",5); + + public static class KindAnnotationPrefix extends TypeSafeEnum { + private KindAnnotationPrefix(String name, int key) { + super(name, key); + } + + public String extractPointcut(String perClause) { + int from = getName().length(); + int to = perClause.length()-1; + if (!perClause.startsWith(getName()) + || !perClause.endsWith(")") + || from > perClause.length()) { + throw new RuntimeException("cannot read perclause " + perClause); + } + + return perClause.substring(from, to); + } + + public static final KindAnnotationPrefix PERCFLOW = new KindAnnotationPrefix("percflow(", 1); + public static final KindAnnotationPrefix PERCFLOWBELOW = new KindAnnotationPrefix("percflowbelow(", 2); + public static final KindAnnotationPrefix PERTHIS = new KindAnnotationPrefix("perthis(", 3); + public static final KindAnnotationPrefix PERTARGET = new KindAnnotationPrefix("pertarget(", 4); + public static final KindAnnotationPrefix PERTYPEWITHIN = new KindAnnotationPrefix("pertypewithin(", 5); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerFromSuper.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerFromSuper.java new file mode 100644 index 000000000..9875291f3 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerFromSuper.java @@ -0,0 +1,132 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; + +public class PerFromSuper extends PerClause { + private PerClause.Kind kind; + + public PerFromSuper(PerClause.Kind kind) { + this.kind = kind; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + throw new RuntimeException("unimplemented"); + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + throw new RuntimeException("unimplemented"); + } + + public void resolveBindings(IScope scope, Bindings bindings) { + // this method intentionally left blank + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + throw new RuntimeException("unimplemented"); + } + + public PerClause concretize(ResolvedType inAspect) { + PerClause p = lookupConcretePerClause(inAspect.getSuperclass()); + if (p == null) { + inAspect.getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.MISSING_PER_CLAUSE, inAspect.getSuperclass()), + getSourceLocation())); + return new PerSingleton().concretize(inAspect);// AV: fallback on something else NPE in AJDT + } else { + if (p.getKind() != kind) { + inAspect.getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.WRONG_PER_CLAUSE, kind, p.getKind()), + getSourceLocation())); + } + return p.concretize(inAspect); + } + } + + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + return this; + } + + public PerClause lookupConcretePerClause(ResolvedType lookupType) { + PerClause ret = lookupType.getPerClause(); + if (ret == null) { + return null; + } + if (ret instanceof PerFromSuper) { + return lookupConcretePerClause(lookupType.getSuperclass()); + } + return ret; + } + + public void write(CompressingDataOutputStream s) throws IOException { + FROMSUPER.write(s); + kind.write(s); + writeLocation(s); + } + + public static PerClause readPerClause(VersionedDataInputStream s, ISourceContext context) throws IOException { + PerFromSuper ret = new PerFromSuper(Kind.read(s)); + ret.readLocation(context, s); + return ret; + } + + public String toString() { + return "perFromSuper(" + kind + ", " + inAspect + ")"; + } + + public String toDeclarationString() { + return ""; + } + + public PerClause.Kind getKind() { + return kind; + } + + public boolean equals(Object other) { + if (!(other instanceof PerFromSuper)) { + return false; + } + PerFromSuper pc = (PerFromSuper) other; + return pc.kind.equals(kind) && ((pc.inAspect == null) ? (inAspect == null) : pc.inAspect.equals(inAspect)); + } + + public int hashCode() { + int result = 17; + result = 37 * result + kind.hashCode(); + result = 37 * result + ((inAspect == null) ? 0 : inAspect.hashCode()); + return result; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerObject.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerObject.java new file mode 100644 index 000000000..237a64a8e --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerObject.java @@ -0,0 +1,193 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.PerObjectInterfaceTypeMunger; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ResolvedTypeMunger; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; + +public class PerObject extends PerClause { + private final boolean isThis; + private final Pointcut entry; + + private static final int thisKindSet; + private static final int targetKindSet; + + static { + int thisFlags = Shadow.ALL_SHADOW_KINDS_BITS; + int targFlags = Shadow.ALL_SHADOW_KINDS_BITS; + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + Shadow.Kind kind = Shadow.SHADOW_KINDS[i]; + if (kind.neverHasThis()) { + thisFlags -= kind.bit; + } + if (kind.neverHasTarget()) { + targFlags -= kind.bit; + } + } + thisKindSet = thisFlags; + targetKindSet = targFlags; + } + + public PerObject(Pointcut entry, boolean isThis) { + this.entry = entry; + this.isThis = isThis; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public int couldMatchKinds() { + return isThis ? thisKindSet : targetKindSet; + } + + // ----- + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + // System.err.println("matches " + this + " ? " + shadow + ", " + + // shadow.hasTarget()); + // ??? could probably optimize this better by testing could match + if (isThis) { + return FuzzyBoolean.fromBoolean(shadow.hasThis()); + } else { + return FuzzyBoolean.fromBoolean(shadow.hasTarget()); + } + } + + public void resolveBindings(IScope scope, Bindings bindings) { + // assert bindings == null; + entry.resolve(scope); + } + + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + PerObject ret = new PerObject(entry.parameterizeWith(typeVariableMap, w), isThis); + ret.copyLocationFrom(this); + return ret; + } + + private Var getVar(Shadow shadow) { + return isThis ? shadow.getThisVar() : shadow.getTargetVar(); + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + Expr myInstance = Expr.makeCallExpr(AjcMemberMaker.perObjectAspectOfMethod(inAspect), new Expr[] { getVar(shadow) }, + inAspect); + state.setAspectInstance(myInstance); + return Test.makeCall(AjcMemberMaker.perObjectHasAspectMethod(inAspect), new Expr[] { getVar(shadow) }); + } + + public PerClause concretize(ResolvedType inAspect) { + PerObject ret = new PerObject(entry, isThis); + + ret.inAspect = inAspect; + if (inAspect.isAbstract()) { + return ret; + } + + World world = inAspect.getWorld(); + + Pointcut concreteEntry = entry.concretize(inAspect, inAspect, 0, null); + // concreteEntry = new AndPointcut(this, concreteEntry); + // concreteEntry.state = Pointcut.CONCRETE; + inAspect.crosscuttingMembers.addConcreteShadowMunger(Advice.makePerObjectEntry(world, concreteEntry, isThis, inAspect)); + + // FIXME AV - don't use lateMunger here due to test + // "inheritance, around advice and abstract pointcuts" + // see #75442 thread. Issue with weaving order. + ResolvedTypeMunger munger = new PerObjectInterfaceTypeMunger(inAspect, concreteEntry); + inAspect.crosscuttingMembers.addLateTypeMunger(world.getWeavingSupport().concreteTypeMunger(munger, inAspect)); + + // ATAJ: add a munger to add the aspectOf(..) to the @AJ aspects + if (inAspect.isAnnotationStyleAspect() && !inAspect.isAbstract()) { + inAspect.crosscuttingMembers.addLateTypeMunger(inAspect.getWorld().getWeavingSupport().makePerClauseAspect(inAspect, + getKind())); + } + + // ATAJ inline around advice support - don't use a late munger to allow + // around inling for itself + if (inAspect.isAnnotationStyleAspect() && !inAspect.getWorld().isXnoInline()) { + inAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport().createAccessForInlineMunger(inAspect)); + } + + return ret; + } + + public void write(CompressingDataOutputStream s) throws IOException { + PEROBJECT.write(s); + entry.write(s); + s.writeBoolean(isThis); + writeLocation(s); + } + + public static PerClause readPerClause(VersionedDataInputStream s, ISourceContext context) throws IOException { + PerClause ret = new PerObject(Pointcut.read(s, context), s.readBoolean()); + ret.readLocation(context, s); + return ret; + } + + public PerClause.Kind getKind() { + return PEROBJECT; + } + + public boolean isThis() { + return isThis; + } + + public String toString() { + return "per" + (isThis ? "this" : "target") + "(" + entry + ")"; + } + + public String toDeclarationString() { + return toString(); + } + + public Pointcut getEntry() { + return entry; + } + + public boolean equals(Object other) { + if (!(other instanceof PerObject)) { + return false; + } + PerObject pc = (PerObject) other; + return (pc.isThis && isThis) && ((pc.inAspect == null) ? (inAspect == null) : pc.inAspect.equals(inAspect)) + && ((pc.entry == null) ? (entry == null) : pc.entry.equals(entry)); + } + + public int hashCode() { + int result = 17; + result = 37 * result + (isThis ? 0 : 1); + result = 37 * result + ((inAspect == null) ? 0 : inAspect.hashCode()); + result = 37 * result + ((entry == null) ? 0 : entry.hashCode()); + return result; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerSingleton.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerSingleton.java new file mode 100644 index 000000000..f53c3a4a5 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerSingleton.java @@ -0,0 +1,175 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +public class PerSingleton extends PerClause { + + private ResolvedMember perSingletonAspectOfMethod; + + public PerSingleton() { + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.YES; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + return FuzzyBoolean.YES; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + // this method intentionally left blank + } + + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + return this; + } + + public Test findResidueInternal(Shadow shadow, ExposedState state) { + // TODO: the commented code is for slow Aspects.aspectOf() style - keep + // or remove + // + // Expr myInstance = + // Expr.makeCallExpr(AjcMemberMaker.perSingletonAspectOfMethod(inAspect), + // Expr.NONE, inAspect); + // + // state.setAspectInstance(myInstance); + // + // // we have no test + // // a NoAspectBoundException will be thrown if we need an instance of + // this + // // aspect before we are bound + // return Literal.TRUE; + // if (!Ajc5MemberMaker.isSlowAspect(inAspect)) { + if (perSingletonAspectOfMethod == null) { + // Build this just once + perSingletonAspectOfMethod = AjcMemberMaker.perSingletonAspectOfMethod(inAspect); + } + Expr myInstance = Expr.makeCallExpr(perSingletonAspectOfMethod, Expr.NONE, inAspect); + + state.setAspectInstance(myInstance); + + // we have no test + // a NoAspectBoundException will be thrown if we need an instance of + // this + // aspect before we are bound + return Literal.TRUE; + // } else { + // CallExpr callAspectOf =Expr.makeCallExpr( + // Ajc5MemberMaker.perSingletonAspectOfMethod(inAspect), + // new Expr[]{ + // Expr.makeStringConstantExpr(inAspect.getName(), inAspect), + // //FieldGet is using ResolvedType and I don't need that here + // new FieldGetOn(Member.ajClassField, shadow.getEnclosingType()) + // }, + // inAspect + // ); + // Expr castedCallAspectOf = new CastExpr(callAspectOf, + // inAspect.getName()); + // state.setAspectInstance(castedCallAspectOf); + // return Literal.TRUE; + // } + } + + public PerClause concretize(ResolvedType inAspect) { + PerSingleton ret = new PerSingleton(); + + ret.copyLocationFrom(this); + + World world = inAspect.getWorld(); + + ret.inAspect = inAspect; + + // ATAJ: add a munger to add the aspectOf(..) to the @AJ aspects + if (inAspect.isAnnotationStyleAspect() && !inAspect.isAbstract()) { + // TODO will those change be ok if we add a serializable aspect ? + // dig: + // "can't be Serializable/Cloneable unless -XserializableAspects" + if (getKind() == SINGLETON) { // pr149560 + inAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport().makePerClauseAspect(inAspect, getKind())); + } else { + inAspect.crosscuttingMembers.addLateTypeMunger(world.getWeavingSupport().makePerClauseAspect(inAspect, getKind())); + } + } + + // ATAJ inline around advice support + if (inAspect.isAnnotationStyleAspect() && !inAspect.getWorld().isXnoInline()) { + inAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport().createAccessForInlineMunger(inAspect)); + } + + return ret; + } + + public void write(CompressingDataOutputStream s) throws IOException { + SINGLETON.write(s); + writeLocation(s); + } + + public static PerClause readPerClause(VersionedDataInputStream s, ISourceContext context) throws IOException { + PerSingleton ret = new PerSingleton(); + ret.readLocation(context, s); + return ret; + } + + public PerClause.Kind getKind() { + return SINGLETON; + } + + public String toString() { + return "persingleton(" + inAspect + ")"; + } + + public String toDeclarationString() { + return ""; + } + + public boolean equals(Object other) { + if (!(other instanceof PerSingleton)) { + return false; + } + PerSingleton pc = (PerSingleton) other; + return ((pc.inAspect == null) ? (inAspect == null) : pc.inAspect.equals(inAspect)); + } + + public int hashCode() { + int result = 17; + result = 37 * result + ((inAspect == null) ? 0 : inAspect.hashCode()); + return result; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerThisOrTargetPointcutVisitor.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerThisOrTargetPointcutVisitor.java new file mode 100644 index 000000000..0020cefdc --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerThisOrTargetPointcutVisitor.java @@ -0,0 +1,233 @@ +/******************************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + *******************************************************************************/ +package org.aspectj.weaver.patterns; + +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; + +/** + * A visitor that turns a pointcut into a type pattern equivalent for a perthis or pertarget matching: - pertarget(target(Foo)) => + * Foo+ (this one is a special case..) - pertarget(execution(* Foo.do()) => Foo - perthis(call(* Foo.do()) => * - perthis(!call(* + * Foo.do()) => * (see how the ! has been absorbed here..) + * + * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> + */ +public class PerThisOrTargetPointcutVisitor extends AbstractPatternNodeVisitor { + + /** A maybe marker */ + private final static TypePattern MAYBE = new TypePatternMayBe(); + + private final boolean m_isTarget; + private final ResolvedType m_fromAspectType; + + public PerThisOrTargetPointcutVisitor(boolean isTarget, ResolvedType fromAspectType) { + m_isTarget = isTarget; + m_fromAspectType = fromAspectType; + } + + public TypePattern getPerTypePointcut(Pointcut perClausePointcut) { + Object o = perClausePointcut.accept(this, perClausePointcut); + if (o instanceof TypePattern) { + return (TypePattern) o; + } else { + throw new BCException("perClausePointcut visitor did not return a typepattern, it returned " + o + + (o == null ? "" : " of type " + o.getClass())); + } + } + + // -- visitor methods, all is like Identity visitor except when it comes to transform pointcuts + + public Object visit(WithinPointcut node, Object data) { + if (m_isTarget) { + // pertarget(.. && within(Foo)) => true + // pertarget(.. && !within(Foo)) => true as well ! + return MAYBE; + } else { + return node.getTypePattern(); + } + } + + public Object visit(WithincodePointcut node, Object data) { + if (m_isTarget) { + // pertarget(.. && withincode(* Foo.do())) => true + // pertarget(.. && !withincode(* Foo.do())) => true as well ! + return MAYBE; + } else { + return node.getSignature().getDeclaringType(); + } + } + + public Object visit(WithinAnnotationPointcut node, Object data) { + if (m_isTarget) { + return MAYBE; + } else { + return new AnyWithAnnotationTypePattern(node.getAnnotationTypePattern()); + } + } + + public Object visit(WithinCodeAnnotationPointcut node, Object data) { + if (m_isTarget) { + return MAYBE; + } else { + return MAYBE;// FIXME AV - can we optimize ? perthis(@withincode(Foo)) = hasmethod(..) + } + } + + public Object visit(KindedPointcut node, Object data) { + if (node.getKind().equals(Shadow.AdviceExecution)) { + return MAYBE;// TODO AV - can we do better ? + } else if (node.getKind().equals(Shadow.ConstructorExecution) || node.getKind().equals(Shadow.Initialization) + || node.getKind().equals(Shadow.MethodExecution) || node.getKind().equals(Shadow.PreInitialization) + || node.getKind().equals(Shadow.StaticInitialization)) { + SignaturePattern signaturePattern = node.getSignature(); + boolean isStarAnnotation = signaturePattern.isStarAnnotation(); + // For a method execution joinpoint, we check for an annotation pattern. If there is one we know it will be matched + // against the 'primary' joinpoint (the one in the type) - 'super'joinpoints can't match it. If this situation occurs + // we can re-use the HasMemberTypePattern to guard on whether the perthis/target should match. pr354470 + if (!m_isTarget && node.getKind().equals(Shadow.MethodExecution)) { + if (!isStarAnnotation) { + return new HasMemberTypePatternForPerThisMatching(signaturePattern); + } + } + return signaturePattern.getDeclaringType(); + } else if (node.getKind().equals(Shadow.ConstructorCall) || node.getKind().equals(Shadow.FieldGet) + || node.getKind().equals(Shadow.FieldSet) || node.getKind().equals(Shadow.MethodCall)) { + if (m_isTarget) { + return node.getSignature().getDeclaringType(); + } else { + return MAYBE; + } + } else if (node.getKind().equals(Shadow.ExceptionHandler)) { + return MAYBE; + } else { + throw new ParserException("Undetermined - should not happen: " + node.getKind().getSimpleName(), null); + } + } + + public Object visit(AndPointcut node, Object data) { + return new AndTypePattern(getPerTypePointcut(node.left), getPerTypePointcut(node.right)); + } + + public Object visit(OrPointcut node, Object data) { + return new OrTypePattern(getPerTypePointcut(node.left), getPerTypePointcut(node.right)); + } + + public Object visit(NotPointcut node, Object data) { + // TypePattern negated = getPerTypePointcut(node.getNegatedPointcut()); + // if (MAYBE.equals(negated)) { + // return MAYBE; + // } + // return new NotTypePattern(negated); + // AMC - the only safe thing to return here is maybe... + // see for example pr114054 + return MAYBE; + } + + public Object visit(ThisOrTargetAnnotationPointcut node, Object data) { + if (m_isTarget && !node.isThis()) { + return new AnyWithAnnotationTypePattern(node.getAnnotationTypePattern()); + } else if (!m_isTarget && node.isThis()) { + return new AnyWithAnnotationTypePattern(node.getAnnotationTypePattern()); + } else { + // perthis(@target(Foo)) + return MAYBE; + } + } + + public Object visit(ThisOrTargetPointcut node, Object data) { + if ((m_isTarget && !node.isThis()) || (!m_isTarget && node.isThis())) { + String pointcutString = node.getType().toString(); + // see pr115788 "<nothing>" means there was a problem resolving types - that will be reported so dont blow up + // the parser here.. + if (pointcutString.equals("<nothing>")) { + return new NoTypePattern(); + } + // pertarget(target(Foo)) => Foo+ for type pattern matching + // perthis(this(Foo)) => Foo+ for type pattern matching + // TODO AV - we do like a deep copy by parsing it again.. quite dirty, would need a clean deep copy + TypePattern copy = new PatternParser(pointcutString.replace('$', '.')).parseTypePattern(); + // TODO AV - see dirty replace from $ to . here as inner classes are with $ instead (#108488) + copy.includeSubtypes = true; + return copy; + } else { + // perthis(target(Foo)) => maybe + return MAYBE; + } + } + + public Object visit(ReferencePointcut node, Object data) { + // && pc_ref() + // we know there is no support for binding in perClause: perthis(pc_ref(java.lang.String)) + // TODO AV - may need some work for generics.. + + ResolvedPointcutDefinition pointcutDec; + ResolvedType searchStart = m_fromAspectType; + if (node.onType != null) { + searchStart = node.onType.resolve(m_fromAspectType.getWorld()); + if (searchStart.isMissing()) { + return MAYBE;// this should not happen since concretize will fails but just in case.. + } + } + pointcutDec = searchStart.findPointcut(node.name); + + return getPerTypePointcut(pointcutDec.getPointcut()); + } + + public Object visit(IfPointcut node, Object data) { + return TypePattern.ANY; + } + + public Object visit(HandlerPointcut node, Object data) { + // quiet unexpected since a KindedPointcut but do as if... + return MAYBE; + } + + public Object visit(CflowPointcut node, Object data) { + return MAYBE; + } + + public Object visit(ConcreteCflowPointcut node, Object data) { + return MAYBE; + } + + public Object visit(ArgsPointcut node, Object data) { + return MAYBE; + } + + public Object visit(ArgsAnnotationPointcut node, Object data) { + return MAYBE; + } + + public Object visit(AnnotationPointcut node, Object data) { + return MAYBE; + } + + public Object visit(Pointcut.MatchesNothingPointcut node, Object data) { + // a small hack since the usual MatchNothing has its toString = "<nothing>" which is not parseable back + // while I use back parsing for check purpose. + return new NoTypePattern() { + public String toString() { + return "false"; + } + }; + } + + /** + * A MayBe type pattern that acts as ANY except that !MAYBE = MAYBE + * + * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> + */ + private static class TypePatternMayBe extends AnyTypePattern { + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerTypeWithin.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerTypeWithin.java new file mode 100644 index 000000000..d912b52ed --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PerTypeWithin.java @@ -0,0 +1,249 @@ +/* ******************************************************************* + * Copyright (c) 2005 IBM + * 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.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.PerTypeWithinTargetTypeMunger; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ResolvedTypeMunger; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +// PTWIMPL Represents a parsed pertypewithin() +public class PerTypeWithin extends PerClause { + + private TypePattern typePattern; + + // Any shadow could be considered within a pertypewithin() type pattern + private static final int kindSet = Shadow.ALL_SHADOW_KINDS_BITS; + + public TypePattern getTypePattern() { + return typePattern; + } + + public PerTypeWithin(TypePattern p) { + typePattern = p; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public int couldMatchKinds() { + return kindSet; + } + + @Override + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + PerTypeWithin ret = new PerTypeWithin(typePattern.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + // ----- + @Override + public FuzzyBoolean fastMatch(FastMatchInfo info) { + if (typePattern.annotationPattern instanceof AnyAnnotationTypePattern) { + return isWithinType(info.getType()); + } + return FuzzyBoolean.MAYBE; + } + + @Override + protected FuzzyBoolean matchInternal(Shadow shadow) { + ResolvedType enclosingType = shadow.getIWorld().resolve(shadow.getEnclosingType(), true); + if (enclosingType.isMissing()) { + // PTWIMPL ?? Add a proper message + IMessage msg = new Message("Cant find type pertypewithin matching...", shadow.getSourceLocation(), true, + new ISourceLocation[] { getSourceLocation() }); + shadow.getIWorld().getMessageHandler().handleMessage(msg); + } + + // See pr106554 - we can't put advice calls in an interface when the + // advice is defined + // in a pertypewithin aspect - the JPs only exist in the static + // initializer and can't + // call the localAspectOf() method. + if (enclosingType.isInterface()) { + return FuzzyBoolean.NO; + } + if (!(enclosingType.canBeSeenBy(inAspect) || inAspect.isPrivilegedAspect())) { + return FuzzyBoolean.NO; + } + + typePattern.resolve(shadow.getIWorld()); + return isWithinType(enclosingType); + } + + @Override + public void resolveBindings(IScope scope, Bindings bindings) { + typePattern = typePattern.resolveBindings(scope, bindings, false, false); + } + + @Override + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + // Member ptwField = + // AjcMemberMaker.perTypeWithinField(shadow.getEnclosingType + // (),inAspect); + + Expr myInstance = Expr.makeCallExpr(AjcMemberMaker.perTypeWithinLocalAspectOf(shadow.getEnclosingType(), inAspect/* + * shadow. + * getEnclosingType + * ( ) + */), + Expr.NONE, inAspect); + state.setAspectInstance(myInstance); + + // this worked at one point + // Expr myInstance = + // Expr.makeFieldGet(ptwField,shadow.getEnclosingType() + // .resolve(shadow.getIWorld()));//inAspect); + // state.setAspectInstance(myInstance); + + // return Test.makeFieldGetCall(ptwField,null,Expr.NONE); + // cflowField, cflowCounterIsValidMethod, Expr.NONE + + // This is what is in the perObject variant of this ... + // Expr myInstance = + // Expr.makeCallExpr(AjcMemberMaker.perTypeWithinAspectOfMethod(inAspect) + // , + // new Expr[] {getVar(shadow)}, inAspect); + // state.setAspectInstance(myInstance); + // return + // Test.makeCall(AjcMemberMaker.perTypeWithinHasAspectMethod(inAspect), + // new Expr[] { getVar(shadow) }); + // + + return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; + } + + @Override + public PerClause concretize(ResolvedType inAspect) { + PerTypeWithin ret = new PerTypeWithin(typePattern); + ret.copyLocationFrom(this); + ret.inAspect = inAspect; + if (inAspect.isAbstract()) { + return ret; + } + + World world = inAspect.getWorld(); + + SignaturePattern sigpat = new SignaturePattern(Member.STATIC_INITIALIZATION, ModifiersPattern.ANY, TypePattern.ANY, + TypePattern.ANY,// typePattern, + NamePattern.ANY, TypePatternList.ANY, ThrowsPattern.ANY, AnnotationTypePattern.ANY); + + Pointcut staticInitStar = new KindedPointcut(Shadow.StaticInitialization, sigpat); + Pointcut withinTp = new WithinPointcut(typePattern); + Pointcut andPcut = new AndPointcut(staticInitStar, withinTp); + // We want the pointcut to be: + // 'staticinitialization(*) && within(<typepattern>)' - + // we *cannot* shortcut this to staticinitialization(<typepattern>) + // because it doesnt mean the same thing. + + // This munger will initialize the aspect instance field in the matched type + + inAspect.crosscuttingMembers.addConcreteShadowMunger(Advice.makePerTypeWithinEntry(world, andPcut, inAspect)); + + ResolvedTypeMunger munger = new PerTypeWithinTargetTypeMunger(inAspect, ret); + inAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport().concreteTypeMunger(munger, inAspect)); + + // ATAJ: add a munger to add the aspectOf(..) to the @AJ aspects + if (inAspect.isAnnotationStyleAspect() && !inAspect.isAbstract()) { + inAspect.crosscuttingMembers.addLateTypeMunger(world.getWeavingSupport().makePerClauseAspect(inAspect, getKind())); + } + + // ATAJ inline around advice support - don't use a late munger to allow + // around inling for itself + if (inAspect.isAnnotationStyleAspect() && !world.isXnoInline()) { + inAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport().createAccessForInlineMunger(inAspect)); + } + + return ret; + + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + PERTYPEWITHIN.write(s); + typePattern.write(s); + writeLocation(s); + } + + public static PerClause readPerClause(VersionedDataInputStream s, ISourceContext context) throws IOException { + PerClause ret = new PerTypeWithin(TypePattern.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + @Override + public PerClause.Kind getKind() { + return PERTYPEWITHIN; + } + + @Override + public String toString() { + return "pertypewithin(" + typePattern + ")"; + } + + @Override + public String toDeclarationString() { + return toString(); + } + + private FuzzyBoolean isWithinType(ResolvedType type) { + while (type != null) { + if (typePattern.matchesStatically(type)) { + return FuzzyBoolean.YES; + } + type = type.getDeclaringType(); + } + return FuzzyBoolean.NO; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PerTypeWithin)) { + return false; + } + PerTypeWithin pc = (PerTypeWithin) other; + return ((pc.inAspect == null) ? (inAspect == null) : pc.inAspect.equals(inAspect)) + && ((pc.typePattern == null) ? (typePattern == null) : pc.typePattern.equals(typePattern)); + } + + @Override + public int hashCode() { + int result = 17; + result = 37 * result + ((inAspect == null) ? 0 : inAspect.hashCode()); + result = 37 * result + ((typePattern == null) ? 0 : typePattern.hashCode()); + return result; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/Pointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/Pointcut.java new file mode 100644 index 000000000..f927286ec --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/Pointcut.java @@ -0,0 +1,436 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.util.TypeSafeEnum; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.Checker; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.PoliceExtensionUse; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +/** + * The lifecycle of Pointcuts is modeled by Pointcut.State. It has three things: + * + * <p> + * Creation -- SYMBOLIC -- then resolve(IScope) -- RESOLVED -- concretize(...) -- CONCRETE + * + * @author Erik Hilsdale + * @author Jim Hugunin + * + * A day in the life of a pointcut.... - AMC. ========================================== + * + * Pointcuts are created by the PatternParser, which is called by ajdt to parse a pointcut from the PseudoTokens AST node + * (which in turn are part of a PointcutDesignator AST node). + * + * Pointcuts are resolved by ajdt when an AdviceDeclaration or a PointcutDeclaration has its statements resolved. This + * happens as part of completeTypeBindings in the AjLookupEnvironment which is called after the diet parse phase of the + * compiler. Named pointcuts, and references to named pointcuts are instances of ReferencePointcut. + * + * At the end of the compilation process, the pointcuts are serialized (write method) into attributes in the class file. + * + * When the weaver loads the class files, it unpacks the attributes and deserializes the pointcuts (read). All aspects are + * added to the world, by calling addOrReplaceAspect on the crosscutting members set of the world. When aspects are added or + * replaced, the crosscutting members in the aspect are extracted as ShadowMungers (each holding a pointcut). The + * ShadowMungers are concretized, which concretizes the pointcuts. At this stage ReferencePointcuts are replaced by their + * declared content. + * + * During weaving, the weaver processes type by type. It first culls potentially matching ShadowMungers by calling the + * fastMatch method on their pointcuts. Only those that might match make it through to the next phase. At the next phase, + * all of the shadows within the type are created and passed to the pointcut for matching (match). + * + * When the actual munging happens, matched pointcuts are asked for their residue (findResidue) - the runtime test if any. + * Because of negation, findResidue may be called on pointcuts that could never match the shadow. + * + */ +public abstract class Pointcut extends PatternNode { + public static final class State extends TypeSafeEnum { + public State(String name, int key) { + super(name, key); + } + } + + /** + * ATAJ the name of the formal for which we don't want any warning when unbound since we consider them as implicitly bound. f.e. + * JoinPoint for @AJ advices + */ + public String[] m_ignoreUnboundBindingForNames = EMPTY_STRING_ARRAY; + + public static final String[] EMPTY_STRING_ARRAY = new String[0]; + + public static final State SYMBOLIC = new State("symbolic", 0); + public static final State RESOLVED = new State("resolved", 1); + public static final State CONCRETE = new State("concrete", 2); + + protected byte pointcutKind; + + public State state; + + protected int lastMatchedShadowId; + private FuzzyBoolean lastMatchedShadowResult; + private String[] typeVariablesInScope = EMPTY_STRING_ARRAY; + + protected boolean hasBeenParameterized = false; + + /** + * Constructor for Pattern. + */ + public Pointcut() { + super(); + this.state = SYMBOLIC; + } + + /** + * Could I match any shadows in the code defined within this type? + */ + public abstract FuzzyBoolean fastMatch(FastMatchInfo info); + + /** + * The set of ShadowKinds that this Pointcut could possibly match - an int whose bits are set according to the Kinds specified + * in Shadow.java + */ + public abstract int couldMatchKinds(); + + public String[] getTypeVariablesInScope() { + return typeVariablesInScope; + } + + public void setTypeVariablesInScope(String[] typeVars) { + this.typeVariablesInScope = typeVars; + } + + /** + * Do I really match this shadow? XXX implementors need to handle state + */ + public final FuzzyBoolean match(Shadow shadow) { + if (shadow.shadowId == lastMatchedShadowId) { + return lastMatchedShadowResult; + } + FuzzyBoolean ret; + // this next test will prevent a lot of un-needed matching going on.... + if (shadow.getKind().isSet(couldMatchKinds())) { + ret = matchInternal(shadow); + } else { + ret = FuzzyBoolean.NO; + } + lastMatchedShadowId = shadow.shadowId; + lastMatchedShadowResult = ret; + return ret; + } + + protected abstract FuzzyBoolean matchInternal(Shadow shadow); + + public static final byte KINDED = 1; + public static final byte WITHIN = 2; + public static final byte THIS_OR_TARGET = 3; + public static final byte ARGS = 4; + public static final byte AND = 5; + public static final byte OR = 6; + public static final byte NOT = 7; + public static final byte REFERENCE = 8; + public static final byte IF = 9; + public static final byte CFLOW = 10; + public static final byte WITHINCODE = 12; + public static final byte HANDLER = 13; + public static final byte IF_TRUE = 14; + public static final byte IF_FALSE = 15; + public static final byte ANNOTATION = 16; + public static final byte ATWITHIN = 17; + public static final byte ATWITHINCODE = 18; + public static final byte ATTHIS_OR_TARGET = 19; + + public static final byte NONE = 20; // DO NOT CHANGE OR REORDER THIS SEQUENCE, THIS VALUE CAN BE PUT OUT BY ASPECTJ1.2.1 + + public static final byte ATARGS = 21; + public static final byte USER_EXTENSION = 22; + + public byte getPointcutKind() { + return pointcutKind; + } + + // internal, only called from resolve + protected abstract void resolveBindings(IScope scope, Bindings bindings); + + /** + * Returns this pointcut mutated + */ + public final Pointcut resolve(IScope scope) { + assertState(SYMBOLIC); + Bindings bindingTable = new Bindings(scope.getFormalCount()); + IScope bindingResolutionScope = scope; + if (typeVariablesInScope.length > 0) { + bindingResolutionScope = new ScopeWithTypeVariables(typeVariablesInScope, scope); + } + this.resolveBindings(bindingResolutionScope, bindingTable); + bindingTable.checkAllBound(bindingResolutionScope); + this.state = RESOLVED; + return this; + } + + /** + * Returns a new pointcut Only used by test cases + */ + public final Pointcut concretize(ResolvedType inAspect, ResolvedType declaringType, int arity) { + Pointcut ret = concretize(inAspect, declaringType, IntMap.idMap(arity)); + // copy the unbound ignore list + ret.m_ignoreUnboundBindingForNames = m_ignoreUnboundBindingForNames; + return ret; + } + + // XXX this is the signature we're moving to + public final Pointcut concretize(ResolvedType inAspect, ResolvedType declaringType, int arity, ShadowMunger advice) { + // if (state == CONCRETE) return this; //??? + IntMap map = IntMap.idMap(arity); + map.setEnclosingAdvice(advice); + map.setConcreteAspect(inAspect); + return concretize(inAspect, declaringType, map); + } + + public boolean isDeclare(ShadowMunger munger) { + if (munger == null) { + return false; // ??? Is it actually an error if we get a null munger into this method. + } + if (munger instanceof Checker) { + return true; + } + if (((Advice) munger).getKind().equals(AdviceKind.Softener)) { + return true; + } + return false; + } + + public final Pointcut concretize(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + // !!! add this test -- assertState(RESOLVED); + Pointcut ret = this.concretize1(inAspect, declaringType, bindings); + if (shouldCopyLocationForConcretize()) { + ret.copyLocationFrom(this); + } + ret.state = CONCRETE; + // copy the unbound ignore list + ret.m_ignoreUnboundBindingForNames = m_ignoreUnboundBindingForNames; + return ret; + } + + protected boolean shouldCopyLocationForConcretize() { + return true; + } + + /** + * Resolves and removes ReferencePointcuts, replacing with basic ones + * + * @param inAspect the aspect to resolve relative to + * @param bindings a Map from formal index in the current lexical context -> formal index in the concrete advice that will run + * + * This must always return a new Pointcut object (even if the concretized Pointcut is identical to the resolved one). + * That behavior is assumed in many places. XXX fix implementors to handle state + */ + protected abstract Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings); + + // XXX implementors need to handle state + /** + * This can be called from NotPointcut even for Pointcuts that don't match the shadow + */ + public final Test findResidue(Shadow shadow, ExposedState state) { + // if (shadow.shadowId == lastMatchedShadowId) return lastMatchedShadowResidue; + Test ret = findResidueInternal(shadow, state); + // lastMatchedShadowResidue = ret; + lastMatchedShadowId = shadow.shadowId; + return ret; + } + + protected abstract Test findResidueInternal(Shadow shadow, ExposedState state); + + // XXX we're not sure whether or not this is needed + // XXX currently it's unused we're keeping it around as a stub + public void postRead(ResolvedType enclosingType) { + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte kind = s.readByte(); + Pointcut ret; + + switch (kind) { + case KINDED: + ret = KindedPointcut.read(s, context); + break; + case WITHIN: + ret = WithinPointcut.read(s, context); + break; + case THIS_OR_TARGET: + ret = ThisOrTargetPointcut.read(s, context); + break; + case ARGS: + ret = ArgsPointcut.read(s, context); + break; + case AND: + ret = AndPointcut.read(s, context); + break; + case OR: + ret = OrPointcut.read(s, context); + break; + case NOT: + ret = NotPointcut.read(s, context); + break; + case REFERENCE: + ret = ReferencePointcut.read(s, context); + break; + case IF: + ret = IfPointcut.read(s, context); + break; + case CFLOW: + ret = CflowPointcut.read(s, context); + break; + case WITHINCODE: + ret = WithincodePointcut.read(s, context); + break; + case HANDLER: + ret = HandlerPointcut.read(s, context); + break; + case IF_TRUE: + ret = IfPointcut.makeIfTruePointcut(RESOLVED); + break; + case IF_FALSE: + ret = IfPointcut.makeIfFalsePointcut(RESOLVED); + break; + case ANNOTATION: + ret = AnnotationPointcut.read(s, context); + break; + case ATWITHIN: + ret = WithinAnnotationPointcut.read(s, context); + break; + case ATWITHINCODE: + ret = WithinCodeAnnotationPointcut.read(s, context); + break; + case ATTHIS_OR_TARGET: + ret = ThisOrTargetAnnotationPointcut.read(s, context); + break; + case ATARGS: + ret = ArgsAnnotationPointcut.read(s, context); + break; + case NONE: + ret = makeMatchesNothing(RESOLVED); + break; + default: + throw new BCException("unknown kind: " + kind); + } + ret.state = RESOLVED; + ret.pointcutKind = kind; + return ret; + + } + + public void check(ISourceContext ctx, World world) { + // this is a quick visitor... + PoliceExtensionUse pointcutPolice = new PoliceExtensionUse(world, this); + this.accept(pointcutPolice, null); + if (pointcutPolice.synchronizationDesignatorEncountered()) { + world.setSynchronizationPointcutsInUse(); + } + } + + // public void prepare(Shadow shadow) {} + + // ---- test method + + public static Pointcut fromString(String str) { + PatternParser parser = new PatternParser(str); + return parser.parsePointcut(); + } + + static class MatchesNothingPointcut extends Pointcut { + @Override + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return Literal.FALSE; // can only get here if an earlier error occurred + } + + @Override + public int couldMatchKinds() { + return Shadow.NO_SHADOW_KINDS_BITS; + } + + @Override + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.NO; + } + + @Override + protected FuzzyBoolean matchInternal(Shadow shadow) { + return FuzzyBoolean.NO; + } + + @Override + public void resolveBindings(IScope scope, Bindings bindings) { + } + + @Override + public void postRead(ResolvedType enclosingType) { + } + + @Override + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + return makeMatchesNothing(state); + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(NONE); + } + + @Override + public String toString() { + return ""; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public Pointcut parameterizeWith(Map<String, UnresolvedType> typeVariableMap, World w) { + return this; + } + } + + // public static Pointcut MatchesNothing = new MatchesNothingPointcut(); + // ??? there could possibly be some good optimizations to be done at this point + public static Pointcut makeMatchesNothing(State state) { + Pointcut ret = new MatchesNothingPointcut(); + ret.state = state; + return ret; + } + + public void assertState(State state) { + if (this.state != state) { + throw new BCException("expected state: " + state + " got: " + this.state); + } + } + + public abstract Pointcut parameterizeWith(Map<String, UnresolvedType> typeVariableMap, World w); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PointcutEvaluationExpenseComparator.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PointcutEvaluationExpenseComparator.java new file mode 100644 index 000000000..5f3a3291f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PointcutEvaluationExpenseComparator.java @@ -0,0 +1,161 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.util.Comparator; + +import org.aspectj.weaver.Shadow; + +public class PointcutEvaluationExpenseComparator implements Comparator<Pointcut> { + + private static final int MATCHES_NOTHING = -1; + private static final int WITHIN = 1; + private static final int ATWITHIN = 2; + private static final int STATICINIT = 3; + private static final int ADVICEEXECUTION = 4; + private static final int HANDLER = 5; + private static final int GET_OR_SET = 6; + private static final int WITHINCODE = 7; + private static final int ATWITHINCODE = 8; + private static final int EXE_INIT_PREINIT = 9; + private static final int CALL_WITH_DECLARING_TYPE = 10; + private static final int THIS_OR_TARGET = 11; + private static final int CALL_WITHOUT_DECLARING_TYPE = 12; + private static final int ANNOTATION = 13; + private static final int AT_THIS_OR_TARGET = 14; + private static final int ARGS = 15; + private static final int AT_ARGS = 16; + private static final int CFLOW = 17; + private static final int IF = 18; + private static final int OTHER = 20; + + /** + * Compare 2 pointcuts based on an estimate of how expensive they may be to evaluate. + * + * within + * + * @within staticinitialization [make sure this has a fast match method] adviceexecution handler get, set withincode + * @withincode execution, initialization, preinitialization call + * @annotation this, target + * @this, @target args + * @args cflow, cflowbelow if + */ + public int compare(Pointcut p1, Pointcut p2) { + // important property for a well-defined comparator + if (p1.equals(p2)) { + return 0; + } + int result = getScore(p1) - getScore(p2); + if (result == 0) { + // they have the same evaluation expense, but are not 'equal' + // sort by hashCode + int p1code = p1.hashCode(); + int p2code = p2.hashCode(); + if (p1code == p2code) { + return 0; + } else if (p1code < p2code) { + return -1; + } else { + return +1; + } + } + return result; + } + + // a higher score means a more expensive evaluation + private int getScore(Pointcut p) { + if (p.couldMatchKinds() == Shadow.NO_SHADOW_KINDS_BITS) { + return MATCHES_NOTHING; + } + if (p instanceof WithinPointcut) { + return WITHIN; + } + if (p instanceof WithinAnnotationPointcut) { + return ATWITHIN; + } + if (p instanceof KindedPointcut) { + KindedPointcut kp = (KindedPointcut) p; + Shadow.Kind kind = kp.getKind(); + if (kind == Shadow.AdviceExecution) { + return ADVICEEXECUTION; + } else if ((kind == Shadow.ConstructorCall) || (kind == Shadow.MethodCall)) { + TypePattern declaringTypePattern = kp.getSignature().getDeclaringType(); + if (declaringTypePattern instanceof AnyTypePattern) { + return CALL_WITHOUT_DECLARING_TYPE; + } else { + return CALL_WITH_DECLARING_TYPE; + } + } else if ((kind == Shadow.ConstructorExecution) || (kind == Shadow.MethodExecution) || (kind == Shadow.Initialization) + || (kind == Shadow.PreInitialization)) { + return EXE_INIT_PREINIT; + } else if (kind == Shadow.ExceptionHandler) { + return HANDLER; + } else if ((kind == Shadow.FieldGet) || (kind == Shadow.FieldSet)) { + return GET_OR_SET; + } else if (kind == Shadow.StaticInitialization) { + return STATICINIT; + } else { + return OTHER; + } + } + if (p instanceof AnnotationPointcut) { + return ANNOTATION; + } + if (p instanceof ArgsPointcut) { + return ARGS; + } + if (p instanceof ArgsAnnotationPointcut) { + return AT_ARGS; + } + if (p instanceof CflowPointcut || p instanceof ConcreteCflowPointcut) { + return CFLOW; + } + if (p instanceof HandlerPointcut) { + return HANDLER; + } + if (p instanceof IfPointcut) { + return IF; + } + if (p instanceof ThisOrTargetPointcut) { + return THIS_OR_TARGET; + } + if (p instanceof ThisOrTargetAnnotationPointcut) { + return AT_THIS_OR_TARGET; + } + if (p instanceof WithincodePointcut) { + return WITHINCODE; + } + if (p instanceof WithinCodeAnnotationPointcut) { + return ATWITHINCODE; + } + if (p instanceof NotPointcut) { + return getScore(((NotPointcut) p).getNegatedPointcut()); + } + if (p instanceof AndPointcut) { + int leftScore = getScore(((AndPointcut) p).getLeft()); + int rightScore = getScore(((AndPointcut) p).getRight()); + if (leftScore < rightScore) { + return leftScore; + } else { + return rightScore; + } + } + if (p instanceof OrPointcut) { + int leftScore = getScore(((OrPointcut) p).getLeft()); + int rightScore = getScore(((OrPointcut) p).getRight()); + if (leftScore > rightScore) { + return leftScore; + } else { + return rightScore; + } + } + return OTHER; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PointcutRewriter.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PointcutRewriter.java new file mode 100644 index 000000000..81ff33bab --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/PointcutRewriter.java @@ -0,0 +1,443 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.util.Iterator; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.patterns.Pointcut.MatchesNothingPointcut; + +/** + * Performs term rewriting for pointcut expressions. + * + * @author colyer + * @author clement + */ +public class PointcutRewriter { + + private static final boolean WATCH_PROGRESS = false; + + /** + * Set forcerewrite if you want to override the checking for something already in DNF (useful for some testing) Repeated + * processing of something already in DNF is expensive (it ends up being done for every pointcut on every incremental compile) - + * so let's not do it if we don't have to. See pr113257 + */ + public Pointcut rewrite(Pointcut pc, boolean forceRewrite) { + Pointcut result = pc;// checkPC(result); + if (forceRewrite || !isDNF(pc)) { + if (WATCH_PROGRESS) { + System.out.println("Initial pointcut is ==> " + format(pc)); + } + result = distributeNot(result);// checkPC(result); + if (WATCH_PROGRESS) { + System.out.println("Distributing NOT gives ==> " + format(result)); + } + result = pullUpDisjunctions(result);// checkPC(result); + if (WATCH_PROGRESS) { + System.out.println("Pull up disjunctions gives ==> " + format(result)); + } + } else { + if (WATCH_PROGRESS) { + System.out.println("Not distributing NOTs or pulling up disjunctions, already DNF ==> " + format(pc)); + } + } + result = simplifyAnds(result); // checkPC(result); + if (WATCH_PROGRESS) { + System.out.println("Simplifying ANDs gives ==> " + format(result)); + } + result = removeNothings(result); // checkPC(result); + if (WATCH_PROGRESS) { + System.out.println("Removing nothings gives ==> " + format(result)); + } + result = sortOrs(result); // checkPC(result); + if (WATCH_PROGRESS) { + System.out.println("Sorting ORs gives ==> " + format(result)); + } + return result; + } + + // /** + // * Checks pointcuts - used for debugging. + // * - this variant checks if the context has been lost, since + // * that can indicate an NPE will happen later reporting a message (pr162657). + // * Not finished, but helped locate the problem ;) + // */ + // private void checkPC(Pointcut pc) { + // if (isNot(pc)) { + // NotPointcut npc = (NotPointcut)pc; + // checkPC(npc.getNegatedPointcut()); + // if (npc.getSourceContext()==null) { + // System.out.println("Lost context for "+npc); + // throw new RuntimeException("Lost context"); + // } + // } else if (isOr(pc)) { + // OrPointcut opc = (OrPointcut)pc; + // checkPC(opc.getLeft()); + // checkPC(opc.getRight()); + // if (opc.getSourceContext()==null) { + // System.out.println("Lost context for "+opc); + // throw new RuntimeException("Lost context"); + // } + // } else if (isAnd(pc)) { + // AndPointcut apc = (AndPointcut)pc; + // checkPC(apc.getLeft()); + // checkPC(apc.getRight()); + // if (apc.getSourceContext()==null) { + // System.out.println("Lost context for "+apc); + // throw new RuntimeException("Lost context"); + // } + // } else { + // if (pc.getSourceContext()==null) { + // System.out.println("Lost context for "+pc); + // throw new RuntimeException("Lost context"); + // } + // } + // } + + public Pointcut rewrite(Pointcut pc) { + return rewrite(pc, false); + } + + /** + * Check if a pointcut is in DNF - if it is then it should be lots of 'ORs' up the top with 'ANDs' beneath them. + */ + private boolean isDNF(Pointcut pc) { + return isDNFHelper(pc, true); + } + + /** + * Helper function for determining DNFness. Records when we have crossed the point of allowing ORs. + */ + private boolean isDNFHelper(Pointcut pc, boolean canStillHaveOrs) { + if (isAnd(pc)) { + AndPointcut ap = (AndPointcut) pc; + return isDNFHelper(ap.getLeft(), false) && isDNFHelper(ap.getRight(), false); + } else if (isOr(pc)) { + if (!canStillHaveOrs) { + return false; + } + OrPointcut op = (OrPointcut) pc; + return isDNFHelper(op.getLeft(), true) && isDNFHelper(op.getRight(), true); + } else if (isNot(pc)) { + return isDNFHelper(((NotPointcut) pc).getNegatedPointcut(), canStillHaveOrs); + } else { + return true; + } + } + + /** + * Allows formatting of the output pointcut for debugging... + */ + public static String format(Pointcut p) { + String s = p.toString(); + // Regex param needs '(' and '*' changing to '.' + // s = s.replaceAll("persingleton.pkg1.monitoring.ErrorMonitoring.","M"); + // s = s.replaceAll("args.BindingTypePattern.java.lang.Throwable, 0.","Z"); + // s = s.replaceAll("within.pkg1.monitoring.DoMonitorErrors+.","X"); + // s=s.replaceAll("within.pkg1.monitoring....","Y"); + // s=s.replaceAll("if.true.","N"); + return s; + } + + // !!X => X + // !(X && Y) => !X || !Y + // !(X || Y) => !X && !Y + private Pointcut distributeNot(Pointcut pc) { + if (isNot(pc)) { + NotPointcut npc = (NotPointcut) pc; + Pointcut notBody = distributeNot(npc.getNegatedPointcut()); + if (isNot(notBody)) { + // !!X => X + return ((NotPointcut) notBody).getNegatedPointcut(); + } else if (isAnd(notBody)) { + // !(X && Y) => !X || !Y + AndPointcut apc = (AndPointcut) notBody; + Pointcut newLeft = distributeNot(new NotPointcut(apc.getLeft(), npc.getStart())); + Pointcut newRight = distributeNot(new NotPointcut(apc.getRight(), npc.getStart())); + return new OrPointcut(newLeft, newRight); + } else if (isOr(notBody)) { + // !(X || Y) => !X && !Y + OrPointcut opc = (OrPointcut) notBody; + Pointcut newLeft = distributeNot(new NotPointcut(opc.getLeft(), npc.getStart())); + Pointcut newRight = distributeNot(new NotPointcut(opc.getRight(), npc.getStart())); + return new AndPointcut(newLeft, newRight); + } else { + return new NotPointcut(notBody, npc.getStart()); + } + } else if (isAnd(pc)) { + AndPointcut apc = (AndPointcut) pc; + Pointcut left = distributeNot(apc.getLeft()); + Pointcut right = distributeNot(apc.getRight()); + return new AndPointcut(left, right); + } else if (isOr(pc)) { + OrPointcut opc = (OrPointcut) pc; + Pointcut left = distributeNot(opc.getLeft()); + Pointcut right = distributeNot(opc.getRight()); + return new OrPointcut(left, right); + } else { + return pc; + } + } + + // A && (B || C) => (A && B) || (A && C) + // (A || B) && C => (A && C) || (B && C) + private Pointcut pullUpDisjunctions(Pointcut pc) { + if (isNot(pc)) { + NotPointcut npc = (NotPointcut) pc; + return new NotPointcut(pullUpDisjunctions(npc.getNegatedPointcut())); + } else if (isAnd(pc)) { + AndPointcut apc = (AndPointcut) pc; + // dive into left and right here... + Pointcut left = pullUpDisjunctions(apc.getLeft()); + Pointcut right = pullUpDisjunctions(apc.getRight()); + if (isOr(left) && !isOr(right)) { + // (A || B) && C => (A && C) || (B && C) + Pointcut leftLeft = ((OrPointcut) left).getLeft(); + Pointcut leftRight = ((OrPointcut) left).getRight(); + return pullUpDisjunctions(new OrPointcut(new AndPointcut(leftLeft, right), new AndPointcut(leftRight, right))); + } else if (isOr(right) && !isOr(left)) { + // A && (B || C) => (A && B) || (A && C) + Pointcut rightLeft = ((OrPointcut) right).getLeft(); + Pointcut rightRight = ((OrPointcut) right).getRight(); + return pullUpDisjunctions(new OrPointcut(new AndPointcut(left, rightLeft), new AndPointcut(left, rightRight))); + } else if (isOr(right) && isOr(left)) { + // (A || B) && (C || D) => (A && C) || (A && D) || (B && C) || (B && D) + Pointcut A = pullUpDisjunctions(((OrPointcut) left).getLeft()); + Pointcut B = pullUpDisjunctions(((OrPointcut) left).getRight()); + Pointcut C = pullUpDisjunctions(((OrPointcut) right).getLeft()); + Pointcut D = pullUpDisjunctions(((OrPointcut) right).getRight()); + Pointcut newLeft = new OrPointcut(new AndPointcut(A, C), new AndPointcut(A, D)); + Pointcut newRight = new OrPointcut(new AndPointcut(B, C), new AndPointcut(B, D)); + return pullUpDisjunctions(new OrPointcut(newLeft, newRight)); + } else { + return new AndPointcut(left, right); + } + } else if (isOr(pc)) { + OrPointcut opc = (OrPointcut) pc; + return new OrPointcut(pullUpDisjunctions(opc.getLeft()), pullUpDisjunctions(opc.getRight())); + } else { + return pc; + } + } + + /** + * Returns a NOTted form of the pointcut p - we cope with already NOTted pointcuts. + */ + public Pointcut not(Pointcut p) { + if (isNot(p)) { + return ((NotPointcut) p).getNegatedPointcut(); + } + return new NotPointcut(p); + } + + /** + * Passed an array of pointcuts, returns an AND tree with them in. + */ + public Pointcut createAndsFor(Pointcut[] ps) { + if (ps.length == 1) { + return ps[0]; // dumb case + } + if (ps.length == 2) { // recursion exit case + return new AndPointcut(ps[0], ps[1]); + } + // otherwise ... + Pointcut[] subset = new Pointcut[ps.length - 1]; + for (int i = 1; i < ps.length; i++) { + subset[i - 1] = ps[i]; + } + return new AndPointcut(ps[0], createAndsFor(subset)); + } + + // NOT: execution(* TP.*(..)) => within(TP) && execution(* *(..)) + // since this breaks when the pattern matches an interface + // NOT: withincode(* TP.*(..)) => within(TP) && withincode(* *(..)) + // since this is not correct when an aspect makes an ITD + // private Pointcut splitOutWithins(Pointcut pc) { + // if (isExecution(pc)) { + // KindedPointcut kpc = (KindedPointcut) pc; + // SignaturePattern sp = kpc.signature; + // TypePattern within = sp.getDeclaringType(); + // if (isAnyType(within)) return pc; + // SignaturePattern simplified = removeDeclaringTypePattern(sp); + // return new AndPointcut(new WithinPointcut(within), + // new KindedPointcut(kpc.kind,simplified)); + // } else if (isNot(pc)) { + // return new NotPointcut(splitOutWithins(((NotPointcut)pc).getNegatedPointcut())); + // } else if (isAnd(pc)) { + // AndPointcut apc = (AndPointcut) pc; + // return new AndPointcut(splitOutWithins(apc.getLeft()), + // splitOutWithins(apc.getRight())); + // } else if (isOr(pc)) { + // OrPointcut opc = (OrPointcut) pc; + // return new OrPointcut(splitOutWithins(opc.getLeft()), + // splitOutWithins(opc.getRight())); + // } else { + // return pc; + // } + // } + + // private SignaturePattern removeDeclaringTypePattern(SignaturePattern sp) { + // return new SignaturePattern( + // sp.getKind(), + // sp.getModifiers(), + // sp.getReturnType(), + // TypePattern.ANY, + // sp.getName(), + // sp.getParameterTypes(), + // sp.getThrowsPattern(), + // sp.getAnnotationPattern() + // ); + // } + + // this finds the root of each && tree and then aggregates all of the branches + // into a sorted set: + // - duplicates are removed + // - A && !A is replaced by a matchesNothingPointcut + // - the kind(s) matched by the set are evaluated + // - elements are sorted by evaluation complexity + // - the result is written out with the least expensive branch leftmost + private Pointcut simplifyAnds(Pointcut pc) { + if (isNot(pc)) { + NotPointcut npc = (NotPointcut) pc; + Pointcut notBody = npc.getNegatedPointcut(); + if (isNot(notBody)) { + // !!X => X + return simplifyAnds(((NotPointcut) notBody).getNegatedPointcut()); + } else { + return new NotPointcut(simplifyAnds(npc.getNegatedPointcut())); + } + } else if (isOr(pc)) { + OrPointcut opc = (OrPointcut) pc; + return new OrPointcut(simplifyAnds(opc.getLeft()), simplifyAnds(opc.getRight())); + } else if (isAnd(pc)) { + return simplifyAnd((AndPointcut) pc); + } else { + return pc; + } + } + + private Pointcut simplifyAnd(AndPointcut apc) { + SortedSet<Pointcut> nodes = new TreeSet<Pointcut>(new PointcutEvaluationExpenseComparator()); + collectAndNodes(apc, nodes); + // look for A and !A, or IfFalse + for (Iterator<Pointcut> iter = nodes.iterator(); iter.hasNext();) { + Pointcut element = iter.next(); + if (element instanceof NotPointcut) { + Pointcut body = ((NotPointcut) element).getNegatedPointcut(); + if (nodes.contains(body)) { + return Pointcut.makeMatchesNothing(body.state); + } + } + if (element instanceof IfPointcut) { + if (((IfPointcut) element).alwaysFalse()) { + return Pointcut.makeMatchesNothing(element.state); + } + } + // If it can't match anything, the whole AND can't match anything + if (element.couldMatchKinds() == Shadow.NO_SHADOW_KINDS_BITS) { + return element; + } + } + if (apc.couldMatchKinds() == Shadow.NO_SHADOW_KINDS_BITS) { + return Pointcut.makeMatchesNothing(apc.state); + } + // write out with cheapest on left + Iterator<Pointcut> iter = nodes.iterator(); + Pointcut result = iter.next(); + while (iter.hasNext()) { + Pointcut right = iter.next(); + result = new AndPointcut(result, right); + } + return result; + } + + private Pointcut sortOrs(Pointcut pc) { + SortedSet<Pointcut> nodes = new TreeSet<Pointcut>(new PointcutEvaluationExpenseComparator()); + collectOrNodes(pc, nodes); + // write out with cheapest on left + Iterator<Pointcut> iter = nodes.iterator(); + Pointcut result = iter.next(); + while (iter.hasNext()) { + Pointcut right = iter.next(); + result = new OrPointcut(result, right); + } + return result; + } + + /** + * Removes MATCHES_NOTHING pointcuts + */ + private Pointcut removeNothings(Pointcut pc) { + if (isAnd(pc)) { + AndPointcut apc = (AndPointcut) pc; + Pointcut right = removeNothings(apc.getRight()); + Pointcut left = removeNothings(apc.getLeft()); + if (left instanceof MatchesNothingPointcut || right instanceof MatchesNothingPointcut) { + return new MatchesNothingPointcut(); + } + return new AndPointcut(left, right); + } else if (isOr(pc)) { + OrPointcut opc = (OrPointcut) pc; + Pointcut right = removeNothings(opc.getRight()); + Pointcut left = removeNothings(opc.getLeft()); + if (left instanceof MatchesNothingPointcut && !(right instanceof MatchesNothingPointcut)) { + return right; + } else if (right instanceof MatchesNothingPointcut && !(left instanceof MatchesNothingPointcut)) { + return left; + } else if (!(left instanceof MatchesNothingPointcut) && !(right instanceof MatchesNothingPointcut)) { + return new OrPointcut(left, right); + } else if (left instanceof MatchesNothingPointcut && right instanceof MatchesNothingPointcut) { + return new MatchesNothingPointcut(); + } + } + return pc; + } + + private void collectAndNodes(AndPointcut apc, Set<Pointcut> nodesSoFar) { + Pointcut left = apc.getLeft(); + Pointcut right = apc.getRight(); + if (isAnd(left)) { + collectAndNodes((AndPointcut) left, nodesSoFar); + } else { + nodesSoFar.add(left); + } + if (isAnd(right)) { + collectAndNodes((AndPointcut) right, nodesSoFar); + } else { + nodesSoFar.add(right); + } + } + + private void collectOrNodes(Pointcut pc, Set<Pointcut> nodesSoFar) { + if (isOr(pc)) { + OrPointcut opc = (OrPointcut) pc; + collectOrNodes(opc.getLeft(), nodesSoFar); + collectOrNodes(opc.getRight(), nodesSoFar); + } else { + nodesSoFar.add(pc); + } + } + + private boolean isNot(Pointcut pc) { + return (pc instanceof NotPointcut); + } + + private boolean isAnd(Pointcut pc) { + return (pc instanceof AndPointcut); + } + + private boolean isOr(Pointcut pc) { + return (pc instanceof OrPointcut); + } + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ReferencePointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ReferencePointcut.java new file mode 100644 index 000000000..6e74a1a2f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ReferencePointcut.java @@ -0,0 +1,414 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.TypeVariableReference; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; + +/** + */ +// XXX needs check that arguments contains no WildTypePatterns +public class ReferencePointcut extends Pointcut { + public UnresolvedType onType; + public TypePattern onTypeSymbolic; + public String name; + public TypePatternList arguments; + + /** + * if this is non-null then when the pointcut is concretized the result will be parameterized too. + */ + private Map<String, UnresolvedType> typeVariableMap; + + // public ResolvedPointcut binding; + + public ReferencePointcut(TypePattern onTypeSymbolic, String name, TypePatternList arguments) { + this.onTypeSymbolic = onTypeSymbolic; + this.name = name; + this.arguments = arguments; + this.pointcutKind = REFERENCE; + } + + public ReferencePointcut(UnresolvedType onType, String name, TypePatternList arguments) { + this.onType = onType; + this.name = name; + this.arguments = arguments; + this.pointcutKind = REFERENCE; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + // ??? do either of these match methods make any sense??? + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + /** + * Do I really match this shadow? + */ + protected FuzzyBoolean matchInternal(Shadow shadow) { + return FuzzyBoolean.NO; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + if (onType != null) { + buf.append(onType); + buf.append("."); + // for (int i=0, len=fromType.length; i < len; i++) { + // buf.append(fromType[i]); + // buf.append("."); + // } + } + buf.append(name); + buf.append(arguments.toString()); + return buf.toString(); + } + + public void write(CompressingDataOutputStream s) throws IOException { + // XXX ignores onType + s.writeByte(Pointcut.REFERENCE); + if (onType != null) { + s.writeBoolean(true); + onType.write(s); + } else { + s.writeBoolean(false); + } + + s.writeUTF(name); + arguments.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + UnresolvedType onType = null; + if (s.readBoolean()) { + onType = UnresolvedType.read(s); + } + ReferencePointcut ret = new ReferencePointcut(onType, s.readUTF(), TypePatternList.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + if (onTypeSymbolic != null) { + onType = onTypeSymbolic.resolveExactType(scope, bindings); + // in this case we've already signaled an error + if (ResolvedType.isMissing(onType)) { + return; + } + } + + ResolvedType searchType; + if (onType != null) { + searchType = scope.getWorld().resolve(onType); + } else { + searchType = scope.getEnclosingType(); + } + if (searchType.isTypeVariableReference()) { + searchType = ((TypeVariableReference) searchType).getTypeVariable().getFirstBound().resolve(scope.getWorld()); + } + + arguments.resolveBindings(scope, bindings, true, true); + // XXX ensure that arguments has no ..'s in it + + // check that I refer to a real pointcut declaration and that I match + + ResolvedPointcutDefinition pointcutDef = searchType.findPointcut(name); + // if we're not a static reference, then do a lookup of outers + if (pointcutDef == null && onType == null) { + while (true) { + UnresolvedType declaringType = searchType.getDeclaringType(); + if (declaringType == null) { + break; + } + searchType = declaringType.resolve(scope.getWorld()); + pointcutDef = searchType.findPointcut(name); + if (pointcutDef != null) { + // make this a static reference + onType = searchType; + break; + } + } + } + + if (pointcutDef == null) { + scope.message(IMessage.ERROR, this, "can't find referenced pointcut " + name); + return; + } + + // check visibility + if (!pointcutDef.isVisible(scope.getEnclosingType())) { + scope.message(IMessage.ERROR, this, "pointcut declaration " + pointcutDef + " is not accessible"); + return; + } + + if (Modifier.isAbstract(pointcutDef.getModifiers())) { + if (onType != null && !onType.isTypeVariableReference()) { + scope.message(IMessage.ERROR, this, "can't make static reference to abstract pointcut"); + return; + } else if (!searchType.isAbstract()) { + scope.message(IMessage.ERROR, this, "can't use abstract pointcut in concrete context"); + return; + } + } + + ResolvedType[] parameterTypes = scope.getWorld().resolve(pointcutDef.getParameterTypes()); + + if (parameterTypes.length != arguments.size()) { + scope.message(IMessage.ERROR, this, "incompatible number of arguments to pointcut, expected " + parameterTypes.length + + " found " + arguments.size()); + return; + } + + // if (onType == null) onType = pointcutDef.getDeclaringType(); + if (onType != null) { + if (onType.isParameterizedType()) { + // build a type map mapping type variable names in the generic type to + // the type parameters presented + typeVariableMap = new HashMap<String, UnresolvedType>(); + ResolvedType underlyingGenericType = ((ResolvedType) onType).getGenericType(); + TypeVariable[] tVars = underlyingGenericType.getTypeVariables(); + ResolvedType[] typeParams = ((ResolvedType) onType).getResolvedTypeParameters(); + for (int i = 0; i < tVars.length; i++) { + typeVariableMap.put(tVars[i].getName(), typeParams[i]); + } + } else if (onType.isGenericType()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.CANT_REFERENCE_POINTCUT_IN_RAW_TYPE), + getSourceLocation())); + } + } + + for (int i = 0, len = arguments.size(); i < len; i++) { + TypePattern p = arguments.get(i); + // we are allowed to bind to pointcuts which use subtypes as this is type safe + if (typeVariableMap != null) { + p = p.parameterizeWith(typeVariableMap, scope.getWorld()); + } + if (p == TypePattern.NO) { + scope.message(IMessage.ERROR, this, "bad parameter to pointcut reference"); + return; + } + + boolean reportProblem = false; + if (parameterTypes[i].isTypeVariableReference() && p.getExactType().isTypeVariableReference()) { + UnresolvedType One = ((TypeVariableReference) parameterTypes[i]).getTypeVariable().getFirstBound(); + UnresolvedType Two = ((TypeVariableReference) p.getExactType()).getTypeVariable().getFirstBound(); + reportProblem = !One.resolve(scope.getWorld()).isAssignableFrom(Two.resolve(scope.getWorld())); + } else { + reportProblem = !p.matchesSubtypes(parameterTypes[i]) && !p.getExactType().equals(UnresolvedType.OBJECT); + } + if (reportProblem) { + scope.message(IMessage.ERROR, this, "incompatible type, expected " + parameterTypes[i].getName() + " found " + p + + ". Check the type specified in your pointcut"); + return; + } + } + + } + + public void postRead(ResolvedType enclosingType) { + arguments.postRead(enclosingType); + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + throw new RuntimeException("shouldn't happen"); + } + + // ??? This is not thread safe, but this class is not designed for multi-threading + private boolean concretizing = false; + + // declaring type is the type that declared the member referencing this pointcut. + // If it declares a matching private pointcut, then that pointcut should be used + // and not one in a subtype that happens to have the same name. + public Pointcut concretize1(ResolvedType searchStart, ResolvedType declaringType, IntMap bindings) { + if (concretizing) { + // Thread.currentThread().dumpStack(); + searchStart + .getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.CIRCULAR_POINTCUT, this), getSourceLocation())); + Pointcut p = Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + p.sourceContext = sourceContext; + return p; + } + + try { + concretizing = true; + + ResolvedPointcutDefinition pointcutDec; + if (onType != null) { + searchStart = onType.resolve(searchStart.getWorld()); + if (searchStart.isMissing()) { + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + if (onType.isTypeVariableReference()) { + // need to replace on type with the binding for the type variable + // in the declaring type + if (declaringType.isParameterizedType()) { + TypeVariable[] tvs = declaringType.getGenericType().getTypeVariables(); + String typeVariableName = ((TypeVariableReference) onType).getTypeVariable().getName(); + for (int i = 0; i < tvs.length; i++) { + if (tvs[i].getName().equals(typeVariableName)) { + ResolvedType realOnType = declaringType.getTypeParameters()[i].resolve(declaringType.getWorld()); + onType = realOnType; + searchStart = realOnType; + break; + } + } + } + } + + } + + if (declaringType == null) { + declaringType = searchStart; + } + pointcutDec = declaringType.findPointcut(name); + boolean foundMatchingPointcut = (pointcutDec != null && Modifier.isPrivate(pointcutDec.getModifiers())); + if (!foundMatchingPointcut) { + pointcutDec = searchStart.findPointcut(name); + if (pointcutDec == null) { + searchStart + .getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.error( + WeaverMessages.format(WeaverMessages.CANT_FIND_POINTCUT, name, searchStart.getName()), + getSourceLocation())); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + } + + if (pointcutDec.isAbstract()) { + // Thread.currentThread().dumpStack(); + ShadowMunger enclosingAdvice = bindings.getEnclosingAdvice(); + searchStart.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.ABSTRACT_POINTCUT, pointcutDec), getSourceLocation(), + (null == enclosingAdvice) ? null : enclosingAdvice.getSourceLocation()); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + // System.err.println("start: " + searchStart); + // ResolvedType[] parameterTypes = searchStart.getWorld().resolve(pointcutDec.getParameterTypes()); + + TypePatternList arguments = this.arguments.resolveReferences(bindings); + + IntMap newBindings = new IntMap(); + for (int i = 0, len = arguments.size(); i < len; i++) { + TypePattern p = arguments.get(i); + if (p == TypePattern.NO) { + continue; + } + // we are allowed to bind to pointcuts which use subtypes as this is type safe + // this will be checked in ReferencePointcut.resolveBindings(). Can't check it here + // as we don't know about any new parents added via decp. + if (p instanceof BindingTypePattern) { + newBindings.put(i, ((BindingTypePattern) p).getFormalIndex()); + } + } + + if (searchStart.isParameterizedType()) { + // build a type map mapping type variable names in the generic type to + // the type parameters presented + typeVariableMap = new HashMap<String, UnresolvedType>(); + ResolvedType underlyingGenericType = searchStart.getGenericType(); + TypeVariable[] tVars = underlyingGenericType.getTypeVariables(); + ResolvedType[] typeParams = searchStart.getResolvedTypeParameters(); + for (int i = 0; i < tVars.length; i++) { + typeVariableMap.put(tVars[i].getName(), typeParams[i]); + } + } + + newBindings.copyContext(bindings); + newBindings.pushEnclosingDefinition(pointcutDec); + try { + Pointcut ret = pointcutDec.getPointcut(); + if (typeVariableMap != null && !hasBeenParameterized) { + ret = ret.parameterizeWith(typeVariableMap, searchStart.getWorld()); + ret.hasBeenParameterized = true; + } + return ret.concretize(searchStart, declaringType, newBindings); + } finally { + newBindings.popEnclosingDefinitition(); + } + + } finally { + concretizing = false; + } + } + + /** + * make a version of this pointcut with any refs to typeVariables replaced by their entry in the map. Tricky thing is, we can't + * do this at the point in time this method will be called, so we make a version that will parameterize the pointcut it + * ultimately resolves to. + */ + public Pointcut parameterizeWith(Map<String, UnresolvedType> typeVariableMap, World w) { + ReferencePointcut ret = new ReferencePointcut(onType, name, arguments); + ret.onTypeSymbolic = onTypeSymbolic; + ret.typeVariableMap = typeVariableMap; + return ret; + } + + // We want to keep the original source location, not the reference location + protected boolean shouldCopyLocationForConcretize() { + return false; + } + + public boolean equals(Object other) { + if (!(other instanceof ReferencePointcut)) { + return false; + } + if (this == other) { + return true; + } + ReferencePointcut o = (ReferencePointcut) other; + return o.name.equals(name) && o.arguments.equals(arguments) + && ((o.onType == null) ? (onType == null) : o.onType.equals(onType)); + } + + public int hashCode() { + int result = 17; + result = 37 * result + ((onType == null) ? 0 : onType.hashCode()); + result = 37 * result + arguments.hashCode(); + result = 37 * result + name.hashCode(); + return result; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ScopeWithTypeVariables.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ScopeWithTypeVariables.java new file mode 100644 index 000000000..537fb2c22 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ScopeWithTypeVariables.java @@ -0,0 +1,130 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.IMessage.Kind; +import org.aspectj.weaver.IHasPosition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.UnresolvedTypeVariableReferenceType; +import org.aspectj.weaver.World; + +/** + * A scope that also considers type variables when looking up a type. + * + */ +public class ScopeWithTypeVariables implements IScope { + + private IScope delegateScope; + private String[] typeVariableNames; + private UnresolvedTypeVariableReferenceType[] typeVarTypeXs; + + public ScopeWithTypeVariables(String[] typeVarNames, IScope delegate) { + this.delegateScope = delegate; + this.typeVariableNames = typeVarNames; + this.typeVarTypeXs = new UnresolvedTypeVariableReferenceType[typeVarNames.length]; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#lookupType(java.lang.String, org.aspectj.weaver.IHasPosition) + */ + public UnresolvedType lookupType(String name, IHasPosition location) { + for (int i = 0; i < typeVariableNames.length; i++) { + if (typeVariableNames[i].equals(name)) { + if (typeVarTypeXs[i] == null) { + typeVarTypeXs[i] = new UnresolvedTypeVariableReferenceType(new TypeVariable(name)); + } + return typeVarTypeXs[i]; + } + } + return delegateScope.lookupType(name, location); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getWorld() + */ + public World getWorld() { + return delegateScope.getWorld(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getEnclosingType() + */ + public ResolvedType getEnclosingType() { + return delegateScope.getEnclosingType(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getMessageHandler() + */ + public IMessageHandler getMessageHandler() { + return delegateScope.getMessageHandler(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#lookupFormal(java.lang.String) + */ + public FormalBinding lookupFormal(String name) { + return delegateScope.lookupFormal(name); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getFormal(int) + */ + public FormalBinding getFormal(int i) { + return delegateScope.getFormal(i); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getFormalCount() + */ + public int getFormalCount() { + return delegateScope.getFormalCount(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getImportedPrefixes() + */ + public String[] getImportedPrefixes() { + return delegateScope.getImportedPrefixes(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getImportedNames() + */ + public String[] getImportedNames() { + return delegateScope.getImportedNames(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#message(org.aspectj.bridge.IMessage.Kind, org.aspectj.weaver.IHasPosition, java.lang.String) + */ + public void message(Kind kind, IHasPosition location, String message) { + delegateScope.message(kind, location, message); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#message(org.aspectj.bridge.IMessage.Kind, org.aspectj.weaver.IHasPosition, org.aspectj.weaver.IHasPosition, java.lang.String) + */ + public void message(Kind kind, IHasPosition location1, + IHasPosition location2, String message) { + delegateScope.message(kind,location1,location2,message); + } + + public void message(IMessage aMessage) { + delegateScope.message(aMessage); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/SignaturePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/SignaturePattern.java new file mode 100644 index 000000000..365b5b7a7 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/SignaturePattern.java @@ -0,0 +1,1009 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.AnnotationTargetKind; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ConcreteTypeMunger; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.JoinPointSignature; +import org.aspectj.weaver.JoinPointSignatureIterator; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberKind; +import org.aspectj.weaver.NewFieldTypeMunger; +import org.aspectj.weaver.ResolvableTypeList; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public class SignaturePattern extends PatternNode implements ISignaturePattern { + private MemberKind kind; + private ModifiersPattern modifiers; + private TypePattern returnType; + private TypePattern declaringType; + private NamePattern name; + private TypePatternList parameterTypes; + private int bits = 0x0000; + private static final int PARAMETER_ANNOTATION_MATCHING = 0x0001; + private static final int CHECKED_FOR_PARAMETER_ANNOTATION_MATCHING = 0x0002; + + private ThrowsPattern throwsPattern; + private AnnotationTypePattern annotationPattern; + private transient int hashcode = -1; + + private transient boolean isExactDeclaringTypePattern = false; + + public SignaturePattern(MemberKind kind, ModifiersPattern modifiers, TypePattern returnType, TypePattern declaringType, + NamePattern name, TypePatternList parameterTypes, ThrowsPattern throwsPattern, AnnotationTypePattern annotationPattern) { + this.kind = kind; + this.modifiers = modifiers; + this.returnType = returnType; + this.name = name; + this.declaringType = declaringType; + this.parameterTypes = parameterTypes; + this.throwsPattern = throwsPattern; + this.annotationPattern = annotationPattern; + this.isExactDeclaringTypePattern = (declaringType instanceof ExactTypePattern); + } + + @Override + public SignaturePattern resolveBindings(IScope scope, Bindings bindings) { + if (returnType != null) { + returnType = returnType.resolveBindings(scope, bindings, false, false); + checkForIncorrectTargetKind(returnType, scope, false); + } + if (declaringType != null) { + declaringType = declaringType.resolveBindings(scope, bindings, false, false); + checkForIncorrectTargetKind(declaringType, scope, false); + isExactDeclaringTypePattern = (declaringType instanceof ExactTypePattern); + } + if (parameterTypes != null) { + parameterTypes = parameterTypes.resolveBindings(scope, bindings, false, false); + checkForIncorrectTargetKind(parameterTypes, scope, false, true); + } + if (throwsPattern != null) { + throwsPattern = throwsPattern.resolveBindings(scope, bindings); + if (throwsPattern.getForbidden().getTypePatterns().length > 0 + || throwsPattern.getRequired().getTypePatterns().length > 0) { + checkForIncorrectTargetKind(throwsPattern, scope, false); + } + } + if (annotationPattern != null) { + annotationPattern = annotationPattern.resolveBindings(scope, bindings, false); + checkForIncorrectTargetKind(annotationPattern, scope, true); + } + hashcode = -1; + return this; + } + + private void checkForIncorrectTargetKind(PatternNode patternNode, IScope scope, boolean targetsOtherThanTypeAllowed) { + checkForIncorrectTargetKind(patternNode, scope, targetsOtherThanTypeAllowed, false); + + } + + // bug 115252 - adding an xlint warning if the annnotation target type is + // wrong. This logic, or similar, may have to be applied elsewhere in the case + // of pointcuts which don't go through SignaturePattern.resolveBindings(..) + private void checkForIncorrectTargetKind(PatternNode patternNode, IScope scope, boolean targetsOtherThanTypeAllowed, + boolean parameterTargettingAnnotationsAllowed) { + // return if we're not in java5 mode, if the unmatchedTargetKind Xlint + // warning has been turned off, or if the patternNode is * + if (!scope.getWorld().isInJava5Mode() || scope.getWorld().getLint().unmatchedTargetKind == null + || (patternNode instanceof AnyTypePattern)) { + return; + } + if (patternNode instanceof ExactAnnotationTypePattern) { + ResolvedType resolvedType = ((ExactAnnotationTypePattern) patternNode).getAnnotationType().resolve(scope.getWorld()); + if (targetsOtherThanTypeAllowed) { + AnnotationTargetKind[] targetKinds = resolvedType.getAnnotationTargetKinds(); + if (targetKinds == null) { + return; + } + reportUnmatchedTargetKindMessage(targetKinds, patternNode, scope, true); + } else if (!targetsOtherThanTypeAllowed && !resolvedType.canAnnotationTargetType()) { + // everything is incorrect since we've already checked whether we have the TYPE target annotation + AnnotationTargetKind[] targetKinds = resolvedType.getAnnotationTargetKinds(); + if (targetKinds == null) { + return; + } + reportUnmatchedTargetKindMessage(targetKinds, patternNode, scope, false); + } + } else { + TypePatternVisitor visitor = new TypePatternVisitor(scope, targetsOtherThanTypeAllowed, + parameterTargettingAnnotationsAllowed); + patternNode.traverse(visitor, null); + if (visitor.containedIncorrectTargetKind()) { + Set<ExactAnnotationTypePattern> keys = visitor.getIncorrectTargetKinds().keySet(); + for (Iterator<ExactAnnotationTypePattern> iter = keys.iterator(); iter.hasNext();) { + PatternNode node = iter.next(); + AnnotationTargetKind[] targetKinds = visitor.getIncorrectTargetKinds().get(node); + reportUnmatchedTargetKindMessage(targetKinds, node, scope, false); + } + } + } + } + + private void reportUnmatchedTargetKindMessage(AnnotationTargetKind[] annotationTargetKinds, PatternNode node, IScope scope, + boolean checkMatchesMemberKindName) { + StringBuffer targetNames = new StringBuffer("{"); + for (int i = 0; i < annotationTargetKinds.length; i++) { + AnnotationTargetKind targetKind = annotationTargetKinds[i]; + if (checkMatchesMemberKindName && kind.getName().equals(targetKind.getName())) { + return; + } + if (i < (annotationTargetKinds.length - 1)) { + targetNames.append("ElementType." + targetKind.getName() + ","); + } else { + targetNames.append("ElementType." + targetKind.getName() + "}"); + } + } + scope.getWorld().getLint().unmatchedTargetKind.signal(new String[] { node.toString(), targetNames.toString() }, + getSourceLocation(), new ISourceLocation[0]); + } + + /** + * Class which visits the nodes in the TypePattern tree until an ExactTypePattern is found. Once this is found it creates a new + * ExactAnnotationTypePattern and checks whether the targetKind (created via the @Target annotation) matches ElementType.TYPE if + * this is the only target kind which is allowed, or matches the signature pattern kind if there is no restriction. + */ + private class TypePatternVisitor extends AbstractPatternNodeVisitor { + + private IScope scope; + private Map<ExactAnnotationTypePattern, AnnotationTargetKind[]> incorrectTargetKinds = new HashMap<ExactAnnotationTypePattern, AnnotationTargetKind[]>(); + private boolean targetsOtherThanTypeAllowed; + private boolean parameterTargettingAnnotationsAllowed; + + /** + * @param requiredTarget - the signature pattern Kind + * @param scope + * @param parameterTargettingAnnotationsAllowed + */ + public TypePatternVisitor(IScope scope, boolean targetsOtherThanTypeAllowed, boolean parameterTargettingAnnotationsAllowed) { + this.scope = scope; + this.targetsOtherThanTypeAllowed = targetsOtherThanTypeAllowed; + this.parameterTargettingAnnotationsAllowed = parameterTargettingAnnotationsAllowed; + } + + @Override + public Object visit(WildAnnotationTypePattern node, Object data) { + node.getTypePattern().accept(this, data); + return node; + } + + /** + * Do the ExactAnnotationTypePatterns have the incorrect target? + */ + @Override + public Object visit(ExactAnnotationTypePattern node, Object data) { + ResolvedType resolvedType = node.getAnnotationType().resolve(scope.getWorld()); + if (targetsOtherThanTypeAllowed) { + AnnotationTargetKind[] targetKinds = resolvedType.getAnnotationTargetKinds(); + if (targetKinds == null) { + return data; + } + List<AnnotationTargetKind> incorrectTargets = new ArrayList<AnnotationTargetKind>(); + for (int i = 0; i < targetKinds.length; i++) { + if (targetKinds[i].getName().equals(kind.getName()) + || (targetKinds[i].getName().equals("PARAMETER") && node.isForParameterAnnotationMatch())) { + return data; + } + incorrectTargets.add(targetKinds[i]); + } + if (incorrectTargets.isEmpty()) { + return data; + } + AnnotationTargetKind[] kinds = new AnnotationTargetKind[incorrectTargets.size()]; + incorrectTargetKinds.put(node, incorrectTargets.toArray(kinds)); + } else if (!targetsOtherThanTypeAllowed && !resolvedType.canAnnotationTargetType()) { + AnnotationTargetKind[] targetKinds = resolvedType.getAnnotationTargetKinds(); + if (targetKinds == null) { + return data; + } + // exception here is if parameter annotations are allowed + if (parameterTargettingAnnotationsAllowed) { + for (int i = 0; i < targetKinds.length; i++) { + AnnotationTargetKind annotationTargetKind = targetKinds[i]; + if (annotationTargetKind.getName().equals("PARAMETER") && node.isForParameterAnnotationMatch()) { + return data; + } + } + } + incorrectTargetKinds.put(node, targetKinds); + } + return data; + } + + @Override + public Object visit(ExactTypePattern node, Object data) { + ExactAnnotationTypePattern eatp = new ExactAnnotationTypePattern(node.getExactType().resolve(scope.getWorld()), null); + eatp.accept(this, data); + return data; + } + + @Override + public Object visit(AndTypePattern node, Object data) { + node.getLeft().accept(this, data); + node.getRight().accept(this, data); + return node; + } + + @Override + public Object visit(OrTypePattern node, Object data) { + node.getLeft().accept(this, data); + node.getRight().accept(this, data); + return node; + } + + @Override + public Object visit(AnyWithAnnotationTypePattern node, Object data) { + node.getAnnotationPattern().accept(this, data); + return node; + } + + public boolean containedIncorrectTargetKind() { + return (incorrectTargetKinds.size() != 0); + } + + public Map<ExactAnnotationTypePattern, AnnotationTargetKind[]> getIncorrectTargetKinds() { + return incorrectTargetKinds; + } + } + + public void postRead(ResolvedType enclosingType) { + if (returnType != null) { + returnType.postRead(enclosingType); + } + if (declaringType != null) { + declaringType.postRead(enclosingType); + } + if (parameterTypes != null) { + parameterTypes.postRead(enclosingType); + } + } + + /** + * return a copy of this signature pattern in which every type variable reference is replaced by the corresponding entry in the + * map. + */ + @Override + public SignaturePattern parameterizeWith(Map<String, UnresolvedType> typeVariableMap, World w) { + SignaturePattern ret = new SignaturePattern(kind, modifiers, returnType.parameterizeWith(typeVariableMap, w), declaringType + .parameterizeWith(typeVariableMap, w), name, parameterTypes.parameterizeWith(typeVariableMap, w), throwsPattern + .parameterizeWith(typeVariableMap, w), annotationPattern.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public boolean matches(Member joinPointSignature, World world, boolean allowBridgeMethods) { + // fail (or succeed!) fast tests... + if (joinPointSignature == null) { + return false; + } + if (kind != joinPointSignature.getKind()) { + return false; + } + if (kind == Member.ADVICE) { + return true; + } + + // do the hard work then... + boolean subjectMatch = true; + boolean wantsAnnotationMatch = wantToMatchAnnotationPattern(); + JoinPointSignatureIterator candidateMatches = joinPointSignature.getJoinPointSignatures(world); + while (candidateMatches.hasNext()) { + JoinPointSignature aSig = candidateMatches.next(); + // System.out.println(aSig); + FuzzyBoolean matchResult = matchesExactly(aSig, world, allowBridgeMethods, subjectMatch); + if (matchResult.alwaysTrue()) { + return true; + } else if (matchResult.alwaysFalse()) { + return false; + } + // if we got a "MAYBE" it's worth looking at the other signatures + // The first signature is the subject signature - and against it we must match modifiers/annotations/throws + // see http://www.eclipse.org/aspectj/doc/next/adk15notebook/join-point-modifiers.html + subjectMatch = false; + // Early exit + if (wantsAnnotationMatch) { + return false; + } + } + return false; + } + + // Does this pattern match this exact signature (no declaring type mucking about + // or chasing up the hierarchy) + // return YES if it does, NO if it doesn't and no ancester member could match either, + // and MAYBE if it doesn't but an ancester member could. + private FuzzyBoolean matchesExactly(JoinPointSignature aMember, World inAWorld, boolean allowBridgeMethods, boolean subjectMatch) { + // Java5 introduces bridge methods, we match a call to them but nothing else... + if (aMember.isBridgeMethod() && !allowBridgeMethods) { + return FuzzyBoolean.MAYBE; + } + + // Only the subject is checked for modifiers + // see http://www.eclipse.org/aspectj/doc/next/adk15notebook/join-point-modifiers.html + if (subjectMatch && !modifiers.matches(aMember.getModifiers())) { + return FuzzyBoolean.NO; + } + + FuzzyBoolean matchesIgnoringAnnotations = FuzzyBoolean.YES; + if (kind == Member.STATIC_INITIALIZATION) { + matchesIgnoringAnnotations = matchesExactlyStaticInitialization(aMember, inAWorld); + } else if (kind == Member.FIELD) { + matchesIgnoringAnnotations = matchesExactlyField(aMember, inAWorld); + } else if (kind == Member.METHOD) { + matchesIgnoringAnnotations = matchesExactlyMethod(aMember, inAWorld, subjectMatch); + } else if (kind == Member.CONSTRUCTOR) { + matchesIgnoringAnnotations = matchesExactlyConstructor(aMember, inAWorld); + } + if (matchesIgnoringAnnotations.alwaysFalse()) { + return FuzzyBoolean.NO; + } + + // Only the subject is checked for annotations (239441/119749) + // see http://www.eclipse.org/aspectj/doc/next/adk15notebook/join-point-modifiers.html + if (subjectMatch) { + // The annotations must match if specified + if (!matchesAnnotations(aMember, inAWorld).alwaysTrue()) { + return FuzzyBoolean.NO; + } else { + return matchesIgnoringAnnotations; + } + } else { + // Unless they specified any annotation then it is a failure + if (annotationPattern instanceof AnyAnnotationTypePattern) { + return matchesIgnoringAnnotations; + } else { + return FuzzyBoolean.NO; + } + } + + // if (subjectMatch && !matchesAnnotations(aMember, inAWorld).alwaysTrue()) { + // return FuzzyBoolean.NO; + // } else { + // + // return matchesIgnoringAnnotations; + // } + + } + + private boolean wantToMatchAnnotationPattern() { + return !(annotationPattern instanceof AnyAnnotationTypePattern); + } + + /** + * Matches on declaring type + */ + private FuzzyBoolean matchesExactlyStaticInitialization(JoinPointSignature aMember, World world) { + return FuzzyBoolean.fromBoolean(declaringType.matchesStatically(aMember.getDeclaringType().resolve(world))); + } + + /** + * Matches on name, declaring type, field type + */ + private FuzzyBoolean matchesExactlyField(JoinPointSignature aField, World world) { + if (!name.matches(aField.getName())) { + return FuzzyBoolean.NO; + } + ResolvedType fieldDeclaringType = aField.getDeclaringType().resolve(world); + if (!declaringType.matchesStatically(fieldDeclaringType)) { + return FuzzyBoolean.MAYBE; + } + if (!returnType.matchesStatically(aField.getReturnType().resolve(world))) { + // looking bad, but there might be parameterization to consider... + if (!returnType.matchesStatically(aField.getGenericReturnType().resolve(world))) { + // ok, it's bad. + return FuzzyBoolean.MAYBE; + } + } + // passed all the guards... + return FuzzyBoolean.YES; + } + + /** + * Quickly detect if the joinpoint absolutely cannot match becaused the method parameters at the joinpoint cannot match against + * this signature pattern. + * + * @param methodJoinpoint the joinpoint to quickly match against + * @return true if it is impossible for the joinpoint to match this signature + */ + private boolean parametersCannotMatch(JoinPointSignature methodJoinpoint) { + if (methodJoinpoint.isVarargsMethod()) { + // just give up early (for now) + return false; + } + + int patternParameterCount = parameterTypes.size(); + + if (patternParameterCount == 0 || parameterTypes.ellipsisCount == 0) { + boolean equalCount = patternParameterCount == methodJoinpoint.getParameterTypes().length; + + // Quick rule: pattern specifies zero parameters, and joinpoint has parameters *OR* + if (patternParameterCount == 0 && !equalCount) { + return true; + } + + // Quick rule: pattern doesn't specify ellipsis and there are a different number of parameters on the + // method join point as compared with the pattern + if (parameterTypes.ellipsisCount == 0 && !equalCount) { + if (patternParameterCount > 0 && parameterTypes.get(patternParameterCount - 1).isVarArgs()) { + return false; + } + return true; + } + } + + return false; + } + + /** + * Matches on name, declaring type, return type, parameter types, throws types + */ + private FuzzyBoolean matchesExactlyMethod(JoinPointSignature aMethod, World world, boolean subjectMatch) { + if (parametersCannotMatch(aMethod)) { + // System.err.println("Parameter types pattern " + parameterTypes + " pcount: " + aMethod.getParameterTypes().length); + return FuzzyBoolean.NO; + } + // OPTIMIZE only for exact match do the pattern match now? Otherwise defer it until other fast checks complete? + if (!name.matches(aMethod.getName())) { + return FuzzyBoolean.NO; + } + // Check the throws pattern + if (subjectMatch && !throwsPattern.matches(aMethod.getExceptions(), world)) { + return FuzzyBoolean.NO; + } + + // '*' trivially matches everything, no need to check further + if (!declaringType.isStar()) { + if (!declaringType.matchesStatically(aMethod.getDeclaringType().resolve(world))) { + return FuzzyBoolean.MAYBE; + } + } + + // '*' would match any return value + if (!returnType.isStar()) { + boolean b = returnType.isBangVoid(); + if (b) { + String s = aMethod.getReturnType().getSignature(); + if (s.length() == 1 && s.charAt(0) == 'V') { + // it is void, so not a match + return FuzzyBoolean.NO; + } + } else { + if (returnType.isVoid()) { + String s = aMethod.getReturnType().getSignature(); + if (s.length() != 1 || s.charAt(0) != 'V') { + // it is not void, so not a match + return FuzzyBoolean.NO; + } + } else { + if (!returnType.matchesStatically(aMethod.getReturnType().resolve(world))) { + // looking bad, but there might be parameterization to consider... + if (!returnType.matchesStatically(aMethod.getGenericReturnType().resolve(world))) { + // ok, it's bad. + return FuzzyBoolean.MAYBE; + } + } + } + } + } + + // The most simple case: pattern is (..) will match anything + if (parameterTypes.size() == 1 && parameterTypes.get(0).isEllipsis()) { + return FuzzyBoolean.YES; + } + + if (!parameterTypes.canMatchSignatureWithNParameters(aMethod.getParameterTypes().length)) { + return FuzzyBoolean.NO; + } + + // OPTIMIZE both resolution of these types and their annotations should be deferred - just pass down a world and do it lower + // down + // ResolvedType[] resolvedParameters = world.resolve(aMethod.getParameterTypes()); + + ResolvableTypeList rtl = new ResolvableTypeList(world, aMethod.getParameterTypes()); + // Only fetch the parameter annotations if the pointcut is going to be matching on them + ResolvedType[][] parameterAnnotationTypes = null; + if (isMatchingParameterAnnotations()) { + parameterAnnotationTypes = aMethod.getParameterAnnotationTypes(); + if (parameterAnnotationTypes != null && parameterAnnotationTypes.length == 0) { + parameterAnnotationTypes = null; + } + } + + if (!parameterTypes.matches(rtl, TypePattern.STATIC, parameterAnnotationTypes).alwaysTrue()) { + // It could still be a match based on the generic sig parameter types of a parameterized type + if (!parameterTypes.matches(new ResolvableTypeList(world, aMethod.getGenericParameterTypes()), TypePattern.STATIC, + parameterAnnotationTypes).alwaysTrue()) { + return FuzzyBoolean.MAYBE; + // It could STILL be a match based on the erasure of the parameter types?? + // to be determined via test cases... + } + } + + // check that varargs specifications match + if (!matchesVarArgs(aMethod, world)) { + return FuzzyBoolean.MAYBE; + } + + // passed all the guards.. + return FuzzyBoolean.YES; + } + + /** + * Determine if any pattern in the parameter type pattern list is attempting to match on parameter annotations. + * + * @return true if a parameter type pattern wants to match on a parameter annotation + */ + private boolean isMatchingParameterAnnotations() { + if ((bits & CHECKED_FOR_PARAMETER_ANNOTATION_MATCHING) == 0) { + bits |= CHECKED_FOR_PARAMETER_ANNOTATION_MATCHING; + for (int tp = 0, max = parameterTypes.size(); tp < max; tp++) { + TypePattern typePattern = parameterTypes.get(tp); + if (isParameterAnnotationMatching(typePattern)) { + bits |= PARAMETER_ANNOTATION_MATCHING; + } + } + } + return (bits & PARAMETER_ANNOTATION_MATCHING) != 0; + } + + /** + * Walk the simple structure of a type pattern and determine if any leaf node is involved in parameter annotation matching. + */ + private boolean isParameterAnnotationMatching(TypePattern tp) { + if (tp instanceof OrTypePattern) { + OrTypePattern orAtp = (OrTypePattern) tp; + return (isParameterAnnotationMatching(orAtp.getLeft()) || isParameterAnnotationMatching(orAtp.getRight())); + } else if (tp instanceof AndTypePattern) { + AndTypePattern andAtp = (AndTypePattern) tp; + return (isParameterAnnotationMatching(andAtp.getLeft()) || isParameterAnnotationMatching(andAtp.getRight())); + } else if (tp instanceof NotTypePattern) { + NotTypePattern notAtp = (NotTypePattern) tp; + return (isParameterAnnotationMatching(notAtp.getNegatedPattern())); + } else { + AnnotationTypePattern atp = tp.getAnnotationPattern(); + return isParameterAnnotationMatching(atp); + } + } + + private boolean isParameterAnnotationMatching(AnnotationTypePattern tp) { + if (tp instanceof OrAnnotationTypePattern) { + OrAnnotationTypePattern orAtp = (OrAnnotationTypePattern) tp; + return (isParameterAnnotationMatching(orAtp.getLeft()) || isParameterAnnotationMatching(orAtp.getRight())); + } else if (tp instanceof AndAnnotationTypePattern) { + AndAnnotationTypePattern andAtp = (AndAnnotationTypePattern) tp; + return (isParameterAnnotationMatching(andAtp.getLeft()) || isParameterAnnotationMatching(andAtp.getRight())); + } else if (tp instanceof NotAnnotationTypePattern) { + NotAnnotationTypePattern notAtp = (NotAnnotationTypePattern) tp; + return (isParameterAnnotationMatching(notAtp.negatedPattern)); + } else { + return tp.isForParameterAnnotationMatch(); + } + } + + /** + * match on declaring type, parameter types, throws types + */ + private FuzzyBoolean matchesExactlyConstructor(JoinPointSignature aConstructor, World world) { + if (!declaringType.matchesStatically(aConstructor.getDeclaringType().resolve(world))) { + return FuzzyBoolean.NO; + } + + if (!parameterTypes.canMatchSignatureWithNParameters(aConstructor.getParameterTypes().length)) { + return FuzzyBoolean.NO; + } + ResolvedType[] resolvedParameters = world.resolve(aConstructor.getParameterTypes()); + + ResolvedType[][] parameterAnnotationTypes = aConstructor.getParameterAnnotationTypes(); + + if (parameterAnnotationTypes == null || parameterAnnotationTypes.length == 0) { + parameterAnnotationTypes = null; + } + + if (!parameterTypes.matches(resolvedParameters, TypePattern.STATIC, parameterAnnotationTypes).alwaysTrue()) { + // It could still be a match based on the generic sig parameter types of a parameterized type + if (!parameterTypes.matches(world.resolve(aConstructor.getGenericParameterTypes()), TypePattern.STATIC, parameterAnnotationTypes).alwaysTrue()) { + return FuzzyBoolean.MAYBE; + // It could STILL be a match based on the erasure of the parameter types?? + // to be determined via test cases... + } + } + + // check that varargs specifications match + if (!matchesVarArgs(aConstructor, world)) { + return FuzzyBoolean.NO; + } + + // Check the throws pattern + if (!throwsPattern.matches(aConstructor.getExceptions(), world)) { + return FuzzyBoolean.NO; + } + + // passed all the guards.. + return FuzzyBoolean.YES; + } + + /** + * We've matched against this method or constructor so far, but without considering varargs (which has been matched as a simple + * array thus far). Now we do the additional checks to see if the parties agree on whether the last parameter is varargs or a + * straight array. + */ + private boolean matchesVarArgs(JoinPointSignature aMethodOrConstructor, World inAWorld) { + if (parameterTypes.size() == 0) { + return true; + } + + TypePattern lastPattern = parameterTypes.get(parameterTypes.size() - 1); + boolean canMatchVarArgsSignature = lastPattern.isStar() || lastPattern.isVarArgs() || (lastPattern == TypePattern.ELLIPSIS); + + if (aMethodOrConstructor.isVarargsMethod()) { + // we have at least one parameter in the pattern list, and the method has a varargs signature + if (!canMatchVarArgsSignature) { + // XXX - Ideally the shadow would be included in the msg but we don't know it... + inAWorld.getLint().cantMatchArrayTypeOnVarargs.signal(aMethodOrConstructor.toString(), getSourceLocation()); + return false; + } + } else { + // the method ends with an array type, check that we don't *require* a varargs + if (lastPattern.isVarArgs()) { + return false; + } + } + + return true; + } + + private FuzzyBoolean matchesAnnotations(ResolvedMember member, World world) { + if (member == null) { + // world.getLint().unresolvableMember.signal(member.toString(), getSourceLocation()); + return FuzzyBoolean.NO; + } + annotationPattern.resolve(world); + + // optimization before we go digging around for annotations on ITDs + if (annotationPattern instanceof AnyAnnotationTypePattern) { + return FuzzyBoolean.YES; + } + + // fake members represent ITD'd fields - for their annotations we should go and look up the + // relevant member in the original aspect + if (member.isAnnotatedElsewhere() && member.getKind() == Member.FIELD) { + // FIXME asc duplicate of code in AnnotationPointcut.matchInternal()? same fixmes apply here. + // ResolvedMember [] mems = member.getDeclaringType().resolve(world).getDeclaredFields(); // FIXME asc should include + // supers with getInterTypeMungersIncludingSupers? + List<ConcreteTypeMunger> mungers = member.getDeclaringType().resolve(world).getInterTypeMungers(); + for (ConcreteTypeMunger typeMunger : mungers) { + if (typeMunger.getMunger() instanceof NewFieldTypeMunger) { + ResolvedMember fakerm = typeMunger.getSignature(); + ResolvedMember ajcMethod = AjcMemberMaker.interFieldInitializer(fakerm, typeMunger.getAspectType()); + ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod); + if (fakerm.equals(member)) { + member = rmm; + } + } + } + } + + if (annotationPattern.matches(member).alwaysTrue()) { + return FuzzyBoolean.YES; + } else { + // do NOT look at ancestor members... only the subject can have an annotation match + // see http://www.eclipse.org/aspectj/doc/next/adk15notebook/join-point-modifiers.html + return FuzzyBoolean.NO; + } + } + + private ResolvedMember findMethod(ResolvedType aspectType, ResolvedMember ajcMethod) { + ResolvedMember decMethods[] = aspectType.getDeclaredMethods(); + for (int i = 0; i < decMethods.length; i++) { + ResolvedMember member = decMethods[i]; + if (member.equals(ajcMethod)) { + return member; + } + } + return null; + } + + public boolean declaringTypeMatchAllowingForCovariance(Member member, UnresolvedType shadowDeclaringType, World world, + TypePattern returnTypePattern, ResolvedType sigReturn) { + + ResolvedType onType = shadowDeclaringType.resolve(world); + + // fastmatch + if (declaringType.matchesStatically(onType) && returnTypePattern.matchesStatically(sigReturn)) { + return true; + } + + Collection<ResolvedType> declaringTypes = member.getDeclaringTypes(world); + + boolean checkReturnType = true; + // XXX Possible enhancement? Doesn't seem to speed things up + // if (returnTypePattern.isStar()) { + // if (returnTypePattern instanceof WildTypePattern) { + // if (((WildTypePattern)returnTypePattern).getDimensions()==0) checkReturnType = false; + // } + // } + + // Sometimes that list includes types that don't explicitly declare the member we are after - + // they are on the list because their supertype is on the list, that's why we use + // lookupMethod rather than lookupMemberNoSupers() + for (ResolvedType type : declaringTypes) { + if (declaringType.matchesStatically(type)) { + if (!checkReturnType) { + return true; + } + ResolvedMember rm = type.lookupMethod(member); + if (rm == null) { + rm = type.lookupMethodInITDs(member); // It must be in here, or we have *real* problems + } + if (rm == null) { + continue; // might be currently looking at the generic type and we need to continue searching in case we hit a + } + // parameterized version of this same type... + UnresolvedType returnTypeX = rm.getReturnType(); + ResolvedType returnType = returnTypeX.resolve(world); + if (returnTypePattern.matchesStatically(returnType)) { + return true; + } + } + } + return false; + } + + // private Collection getDeclaringTypes(Signature sig) { + // List l = new ArrayList(); + // Class onType = sig.getDeclaringType(); + // String memberName = sig.getName(); + // if (sig instanceof FieldSignature) { + // Class fieldType = ((FieldSignature)sig).getFieldType(); + // Class superType = onType; + // while(superType != null) { + // try { + // Field f = (superType.getDeclaredField(memberName)); + // if (f.getType() == fieldType) { + // l.add(superType); + // } + // } catch (NoSuchFieldException nsf) {} + // superType = superType.getSuperclass(); + // } + // } else if (sig instanceof MethodSignature) { + // Class[] paramTypes = ((MethodSignature)sig).getParameterTypes(); + // Class superType = onType; + // while(superType != null) { + // try { + // superType.getDeclaredMethod(memberName,paramTypes); + // l.add(superType); + // } catch (NoSuchMethodException nsm) {} + // superType = superType.getSuperclass(); + // } + // } + // return l; + // } + + public NamePattern getName() { + return name; + } + + public TypePattern getDeclaringType() { + return declaringType; + } + + public MemberKind getKind() { + return kind; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + + if (annotationPattern != AnnotationTypePattern.ANY) { + buf.append(annotationPattern.toString()); + buf.append(' '); + } + + if (modifiers != ModifiersPattern.ANY) { + buf.append(modifiers.toString()); + buf.append(' '); + } + + if (kind == Member.STATIC_INITIALIZATION) { + buf.append(declaringType.toString()); + buf.append(".<clinit>()");// FIXME AV - bad, cannot be parsed again + } else if (kind == Member.HANDLER) { + buf.append("handler("); + buf.append(parameterTypes.get(0)); + buf.append(")"); + } else { + if (!(kind == Member.CONSTRUCTOR)) { + buf.append(returnType.toString()); + buf.append(' '); + } + if (declaringType != TypePattern.ANY) { + buf.append(declaringType.toString()); + buf.append('.'); + } + if (kind == Member.CONSTRUCTOR) { + buf.append("new"); + } else { + buf.append(name.toString()); + } + if (kind == Member.METHOD || kind == Member.CONSTRUCTOR) { + buf.append(parameterTypes.toString()); + } + // FIXME AV - throws is not printed here, weird + } + return buf.toString(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof SignaturePattern)) { + return false; + } + SignaturePattern o = (SignaturePattern) other; + return o.kind.equals(this.kind) && o.modifiers.equals(this.modifiers) && o.returnType.equals(this.returnType) + && o.declaringType.equals(this.declaringType) && o.name.equals(this.name) + && o.parameterTypes.equals(this.parameterTypes) && o.throwsPattern.equals(this.throwsPattern) + && o.annotationPattern.equals(this.annotationPattern); + } + + @Override + public int hashCode() { + if (hashcode == -1) { + hashcode = 17; + hashcode = 37 * hashcode + kind.hashCode(); + hashcode = 37 * hashcode + modifiers.hashCode(); + hashcode = 37 * hashcode + returnType.hashCode(); + hashcode = 37 * hashcode + declaringType.hashCode(); + hashcode = 37 * hashcode + name.hashCode(); + hashcode = 37 * hashcode + parameterTypes.hashCode(); + hashcode = 37 * hashcode + throwsPattern.hashCode(); + hashcode = 37 * hashcode + annotationPattern.hashCode(); + } + return hashcode; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + kind.write(s); + modifiers.write(s); + returnType.write(s); + declaringType.write(s); + name.write(s); + parameterTypes.write(s); + throwsPattern.write(s); + annotationPattern.write(s); + writeLocation(s); + } + + public static SignaturePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + // ISignaturePattern kind should already have been read by the time this read is entered + MemberKind kind = MemberKind.read(s); + ModifiersPattern modifiers = ModifiersPattern.read(s); + TypePattern returnType = TypePattern.read(s, context); + TypePattern declaringType = TypePattern.read(s, context); + NamePattern name = NamePattern.read(s); + TypePatternList parameterTypes = TypePatternList.read(s, context); + ThrowsPattern throwsPattern = ThrowsPattern.read(s, context); + + AnnotationTypePattern annotationPattern = AnnotationTypePattern.ANY; + + if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + annotationPattern = AnnotationTypePattern.read(s, context); + } + + SignaturePattern ret = new SignaturePattern(kind, modifiers, returnType, declaringType, name, parameterTypes, + throwsPattern, annotationPattern); + ret.readLocation(context, s); + return ret; + } + + /** + * @return + */ + public ModifiersPattern getModifiers() { + return modifiers; + } + + /** + * @return + */ + public TypePatternList getParameterTypes() { + return parameterTypes; + } + + /** + * @return + */ + public TypePattern getReturnType() { + return returnType; + } + + /** + * @return + */ + public ThrowsPattern getThrowsPattern() { + return throwsPattern; + } + + /** + * return true if last argument in params is an Object[] but the modifiers say this method was declared with varargs + * (Object...). We shouldn't be matching if this is the case. + */ + // private boolean matchedArrayAgainstVarArgs(TypePatternList params,int modifiers) { + // if (params.size()>0 && (modifiers & Constants.ACC_VARARGS)!=0) { + // // we have at least one parameter in the pattern list, and the method has a varargs signature + // TypePattern lastPattern = params.get(params.size()-1); + // if (lastPattern.isArray() && !lastPattern.isVarArgs) return true; + // } + // return false; + // } + public AnnotationTypePattern getAnnotationPattern() { + return annotationPattern; + } + + @Override + public boolean isStarAnnotation() { + return annotationPattern == AnnotationTypePattern.ANY; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public boolean isExactDeclaringTypePattern() { + return isExactDeclaringTypePattern; + } + + @Override + public boolean isMatchOnAnyName() { + return getName().isAny(); + } + + @Override + public List<ExactTypePattern> getExactDeclaringTypes() { + if (declaringType instanceof ExactTypePattern) { + List<ExactTypePattern> l = new ArrayList<ExactTypePattern>(); + l.add((ExactTypePattern) declaringType); + return l; + } else { + return Collections.emptyList(); + } + } + + @Override + public boolean couldEverMatch(ResolvedType type) { + return declaringType.matches(type, TypePattern.STATIC).maybeTrue(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/SimpleScope.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/SimpleScope.java new file mode 100644 index 000000000..119103d84 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/SimpleScope.java @@ -0,0 +1,173 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.SourceLocation; +import org.aspectj.weaver.IHasPosition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +public class SimpleScope implements IScope { + + private static final String[] NoStrings = new String[0]; + private static final String[] javaLangPrefixArray = new String[] { "java.lang.", }; + + private String[] importedPrefixes = javaLangPrefixArray; + private String[] importedNames = NoStrings; + private World world; + private ResolvedType enclosingType; + protected FormalBinding[] bindings; + + public SimpleScope(World world, FormalBinding[] bindings) { + super(); + this.world = world; + this.bindings = bindings; + } + + public UnresolvedType lookupType(String name, IHasPosition location) { + for (int i = 0; i < importedNames.length; i++) { + String importedName = importedNames[i]; + // make sure we're matching against the type name rather than part of it + // if (importedName.endsWith("." + name)) { + if (importedName.endsWith(name)) { + return world.resolve(importedName); + } + } + + // Check for a primitive + if (name.length() < 8 && Character.isLowerCase(name.charAt(0))) { + // could be a primitive + int len = name.length(); + if (len == 3) { + if (name.equals("int")) { + return UnresolvedType.INT; + } + } else if (len == 4) { + if (name.equals("void")) { + return UnresolvedType.VOID; + } else if (name.equals("byte")) { + return UnresolvedType.BYTE; + } else if (name.equals("char")) { + return UnresolvedType.CHAR; + } else if (name.equals("long")) { + return UnresolvedType.LONG; + } + } else if (len == 5) { + if (name.equals("float")) { + return UnresolvedType.FLOAT; + } else if (name.equals("short")) { + return UnresolvedType.SHORT; + } + } else if (len == 6) { + if (name.equals("double")) { + return UnresolvedType.DOUBLE; + } + } else if (len == 7) { + if (name.equals("boolean")) { + return UnresolvedType.BOOLEAN; + } + } + } + + // Is it fully qualified? + if (name.indexOf('.') != -1) { + return world.resolve(UnresolvedType.forName(name), true); + } + + for (String importedPrefix : importedPrefixes) { + ResolvedType tryType = world.resolve(UnresolvedType.forName(importedPrefix + name), true); + if (!tryType.isMissing()) { + return tryType; + } + } + + return world.resolve(UnresolvedType.forName(name), true); + } + + public IMessageHandler getMessageHandler() { + return world.getMessageHandler(); + } + + public FormalBinding lookupFormal(String name) { + for (int i = 0, len = bindings.length; i < len; i++) { + if (bindings[i].getName().equals(name)) { + return bindings[i]; + } + } + return null; + } + + public FormalBinding getFormal(int i) { + return bindings[i]; + } + + public int getFormalCount() { + return bindings.length; + } + + public String[] getImportedNames() { + return importedNames; + } + + public String[] getImportedPrefixes() { + return importedPrefixes; + } + + public void setImportedNames(String[] importedNames) { + this.importedNames = importedNames; + } + + public void setImportedPrefixes(String[] importedPrefixes) { + this.importedPrefixes = importedPrefixes; + } + + public static FormalBinding[] makeFormalBindings(UnresolvedType[] types, String[] names) { + int len = types.length; + FormalBinding[] bindings = new FormalBinding[len]; + for (int i = 0; i < len; i++) { + bindings[i] = new FormalBinding(types[i], names[i], i); + } + return bindings; + } + + public ISourceLocation makeSourceLocation(IHasPosition location) { + return new SourceLocation(ISourceLocation.NO_FILE, 0); + } + + public void message(IMessage.Kind kind, IHasPosition location1, IHasPosition location2, String message) { + message(kind, location1, message); + message(kind, location2, message); + } + + public void message(IMessage.Kind kind, IHasPosition location, String message) { + getMessageHandler().handleMessage(new Message(message, kind, null, makeSourceLocation(location))); + } + + public void message(IMessage aMessage) { + getMessageHandler().handleMessage(aMessage); + } + + public World getWorld() { + return world; + } + + public ResolvedType getEnclosingType() { + return enclosingType; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ThisOrTargetAnnotationPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ThisOrTargetAnnotationPointcut.java new file mode 100644 index 000000000..dfb509195 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ThisOrTargetAnnotationPointcut.java @@ -0,0 +1,337 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; + +/** + * @author colyer + * + * TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style - Code + * Templates + */ +public class ThisOrTargetAnnotationPointcut extends NameBindingPointcut { + + private boolean isThis; + private boolean alreadyWarnedAboutDEoW = false; + private ExactAnnotationTypePattern annotationTypePattern; + private String declarationText; + + private static final int thisKindSet; + private static final int targetKindSet; + + static { + int thisFlags = Shadow.ALL_SHADOW_KINDS_BITS; + int targFlags = Shadow.ALL_SHADOW_KINDS_BITS; + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + Shadow.Kind kind = Shadow.SHADOW_KINDS[i]; + if (kind.neverHasThis()) { + thisFlags -= kind.bit; + } + if (kind.neverHasTarget()) { + targFlags -= kind.bit; + } + } + thisKindSet = thisFlags; + targetKindSet = targFlags; + } + + /** + * + */ + public ThisOrTargetAnnotationPointcut(boolean isThis, ExactAnnotationTypePattern type) { + super(); + this.isThis = isThis; + this.annotationTypePattern = type; + this.pointcutKind = ATTHIS_OR_TARGET; + buildDeclarationText(); + } + + public ThisOrTargetAnnotationPointcut(boolean isThis, ExactAnnotationTypePattern type, ShadowMunger munger) { + this(isThis, type); + } + + public ExactAnnotationTypePattern getAnnotationTypePattern() { + return annotationTypePattern; + } + + @Override + public int couldMatchKinds() { + return isThis ? thisKindSet : targetKindSet; + } + + @Override + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + ExactAnnotationTypePattern newPattern = (ExactAnnotationTypePattern) this.annotationTypePattern.parameterizeWith( + typeVariableMap, w); + if (newPattern.getAnnotationType() instanceof ResolvedType) { + verifyRuntimeRetention(newPattern.getResolvedAnnotationType()); + } + ThisOrTargetAnnotationPointcut ret = new ThisOrTargetAnnotationPointcut(isThis, + (ExactAnnotationTypePattern) annotationTypePattern.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) + */ + @Override + public FuzzyBoolean fastMatch(FastMatchInfo info) { + return FuzzyBoolean.MAYBE; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) + */ + @Override + protected FuzzyBoolean matchInternal(Shadow shadow) { + if (!couldMatch(shadow)) { + return FuzzyBoolean.NO; + } + ResolvedType toMatchAgainst = (isThis ? shadow.getThisType() : shadow.getTargetType()).resolve(shadow.getIWorld()); + annotationTypePattern.resolve(shadow.getIWorld()); + if (annotationTypePattern.matchesRuntimeType(toMatchAgainst).alwaysTrue()) { + return FuzzyBoolean.YES; + } else { + // a subtype may match at runtime + return FuzzyBoolean.MAYBE; + } + } + + public boolean isThis() { + return isThis; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(org.aspectj.weaver.patterns.IScope, + * org.aspectj.weaver.patterns.Bindings) + */ + @Override + protected void resolveBindings(IScope scope, Bindings bindings) { + if (!scope.getWorld().isInJava5Mode()) { + scope.message(MessageUtil.error(WeaverMessages.format(isThis ? WeaverMessages.ATTHIS_ONLY_SUPPORTED_AT_JAVA5_LEVEL + : WeaverMessages.ATTARGET_ONLY_SUPPORTED_AT_JAVA5_LEVEL), getSourceLocation())); + return; + } + annotationTypePattern = (ExactAnnotationTypePattern) annotationTypePattern.resolveBindings(scope, bindings, true); + // must be either a Var, or an annotation type pattern + // if annotationType does not have runtime retention, this is an error + if (annotationTypePattern.annotationType == null) { + // it's a formal with a binding error + return; + } + ResolvedType rAnnotationType = (ResolvedType) annotationTypePattern.annotationType; + if (rAnnotationType.isTypeVariableReference()) { + return; // we'll deal with this next check when the type var is actually bound... + } + verifyRuntimeRetention(rAnnotationType); + + } + + private void verifyRuntimeRetention(ResolvedType rAnnotationType) { + if (!(rAnnotationType.isAnnotationWithRuntimeRetention())) { + IMessage m = MessageUtil.error(WeaverMessages.format(WeaverMessages.BINDING_NON_RUNTIME_RETENTION_ANNOTATION, + rAnnotationType.getName()), getSourceLocation()); + rAnnotationType.getWorld().getMessageHandler().handleMessage(m); + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#concretize1(org.aspectj.weaver.ResolvedType, org.aspectj.weaver.IntMap) + */ + @Override + protected Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + if (isDeclare(bindings.getEnclosingAdvice())) { + // Enforce rule about which designators are supported in declare + if (!alreadyWarnedAboutDEoW) { + inAspect.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.THIS_OR_TARGET_IN_DECLARE, isThis ? "this" : "target"), + bindings.getEnclosingAdvice().getSourceLocation(), null); + alreadyWarnedAboutDEoW = true; + } + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + ExactAnnotationTypePattern newType = (ExactAnnotationTypePattern) annotationTypePattern.remapAdviceFormals(bindings); + ThisOrTargetAnnotationPointcut ret = new ThisOrTargetAnnotationPointcut(isThis, newType, bindings.getEnclosingAdvice()); + ret.alreadyWarnedAboutDEoW = alreadyWarnedAboutDEoW; + ret.copyLocationFrom(this); + return ret; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#findResidue(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) + */ + /** + * The guard here is going to be the hasAnnotation() test - if it gets through (which we cannot determine until runtime) then we + * must have a TypeAnnotationAccessVar in place - this means we must *always* have one in place. + */ + @Override + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + if (!couldMatch(shadow)) { + return Literal.FALSE; + } + boolean alwaysMatches = match(shadow).alwaysTrue(); + Var var = isThis ? shadow.getThisVar() : shadow.getTargetVar(); + Var annVar = null; + + // Are annotations being bound? + UnresolvedType annotationType = annotationTypePattern.annotationType; + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern) annotationTypePattern; + annotationType = btp.annotationType; + + annVar = isThis ? shadow.getThisAnnotationVar(annotationType) : shadow.getTargetAnnotationVar(annotationType); + if (annVar == null) { + throw new RuntimeException("Impossible!"); + } + + state.set(btp.getFormalIndex(), annVar); + } + + if (alwaysMatches && (annVar == null)) {// change check to verify if its the 'generic' annVar that is being used + return Literal.TRUE; + } else { + ResolvedType rType = annotationType.resolve(shadow.getIWorld()); + return Test.makeHasAnnotation(var, rType); + } + } + + private boolean couldMatch(Shadow shadow) { + return isThis ? shadow.hasThis() : shadow.hasTarget(); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + @Override + public List<BindingPattern> getBindingAnnotationTypePatterns() { + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + List<BindingPattern> l = new ArrayList<BindingPattern>(); + l.add((BindingPattern)annotationTypePattern); + return l; + } else { + return Collections.emptyList(); + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + @Override + public List<BindingTypePattern> getBindingTypePatterns() { + return Collections.emptyList(); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.ATTHIS_OR_TARGET); + s.writeBoolean(isThis); + annotationTypePattern.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + boolean isThis = s.readBoolean(); + AnnotationTypePattern type = AnnotationTypePattern.read(s, context); + ThisOrTargetAnnotationPointcut ret = new ThisOrTargetAnnotationPointcut(isThis, (ExactAnnotationTypePattern) type); + ret.readLocation(context, s); + return ret; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ThisOrTargetAnnotationPointcut)) { + return false; + } + ThisOrTargetAnnotationPointcut other = (ThisOrTargetAnnotationPointcut) obj; + return (other.annotationTypePattern.equals(this.annotationTypePattern) && (other.isThis == this.isThis)); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return 17 + 37 * annotationTypePattern.hashCode() + (isThis ? 49 : 13); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + private void buildDeclarationText() { + StringBuffer buf = new StringBuffer(); + buf.append(isThis ? "@this(" : "@target("); + String annPatt = annotationTypePattern.toString(); + buf.append(annPatt.startsWith("@") ? annPatt.substring(1) : annPatt); + buf.append(")"); + this.declarationText = buf.toString(); + } + + @Override + public String toString() { + return this.declarationText; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ThisOrTargetPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ThisOrTargetPointcut.java new file mode 100644 index 000000000..a278e76c6 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ThisOrTargetPointcut.java @@ -0,0 +1,242 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; + +/** + * Corresponds to target or this pcd. + * + * <p> + * type is initially a WildTypePattern. If it stays that way, it's a this(Foo) type deal. however, the resolveBindings method may + * convert it to a BindingTypePattern, in which case, it's a this(foo) type deal. + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public class ThisOrTargetPointcut extends NameBindingPointcut { + private boolean isThis; + private TypePattern typePattern; + private String declarationText; + + private static final int thisKindSet; + private static final int targetKindSet; + + static { + int thisFlags = Shadow.ALL_SHADOW_KINDS_BITS; + int targFlags = Shadow.ALL_SHADOW_KINDS_BITS; + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + Shadow.Kind kind = Shadow.SHADOW_KINDS[i]; + if (kind.neverHasThis()) { + thisFlags -= kind.bit; + } + if (kind.neverHasTarget()) { + targFlags -= kind.bit; + } + } + thisKindSet = thisFlags; + targetKindSet = targFlags; + } + + public boolean isBinding() { + return (typePattern instanceof BindingTypePattern); + } + + public ThisOrTargetPointcut(boolean isThis, TypePattern type) { + this.isThis = isThis; + this.typePattern = type; + this.pointcutKind = THIS_OR_TARGET; + this.declarationText = (isThis ? "this(" : "target(") + type + ")"; + } + + public TypePattern getType() { + return typePattern; + } + + public boolean isThis() { + return isThis; + } + + @Override + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + ThisOrTargetPointcut ret = new ThisOrTargetPointcut(isThis, typePattern.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public int couldMatchKinds() { + return isThis ? thisKindSet : targetKindSet; + } + + @Override + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + private boolean couldMatch(Shadow shadow) { + return isThis ? shadow.hasThis() : shadow.hasTarget(); + } + + @Override + protected FuzzyBoolean matchInternal(Shadow shadow) { + if (!couldMatch(shadow)) { + return FuzzyBoolean.NO; + } + UnresolvedType typeToMatch = isThis ? shadow.getThisType() : shadow.getTargetType(); + // optimization for case of this(Object) or target(Object) + // works for an ExactTypePattern (and we know there are no annotations to match here of course) + if (typePattern.getExactType().equals(ResolvedType.OBJECT)) { + return FuzzyBoolean.YES; + } + return typePattern.matches(typeToMatch.resolve(shadow.getIWorld()), TypePattern.DYNAMIC); + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.THIS_OR_TARGET); + s.writeBoolean(isThis); + typePattern.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + boolean isThis = s.readBoolean(); + TypePattern type = TypePattern.read(s, context); + ThisOrTargetPointcut ret = new ThisOrTargetPointcut(isThis, type); + ret.readLocation(context, s); + return ret; + } + + @Override + public void resolveBindings(IScope scope, Bindings bindings) { + typePattern = typePattern.resolveBindings(scope, bindings, true, true); + + // look for parameterized type patterns which are not supported... + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + typePattern.traverse(visitor, null); + if (visitor.wellHasItThen/* ? */()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.THIS_AND_TARGET_DONT_SUPPORT_PARAMETERS), + getSourceLocation())); + } + // ??? handle non-formal + } + + @Override + public void postRead(ResolvedType enclosingType) { + typePattern.postRead(enclosingType); + } + + @Override + public List<BindingPattern> getBindingAnnotationTypePatterns() { + return Collections.emptyList(); + } + + @Override + public List<BindingTypePattern> getBindingTypePatterns() { + if (typePattern instanceof BindingTypePattern) { + List<BindingTypePattern> l = new ArrayList<BindingTypePattern>(); + l.add((BindingTypePattern)typePattern); + return l; + } else { + return Collections.emptyList(); + } + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ThisOrTargetPointcut)) { + return false; + } + ThisOrTargetPointcut o = (ThisOrTargetPointcut) other; + return o.isThis == this.isThis && o.typePattern.equals(this.typePattern); + } + + @Override + public int hashCode() { + int result = 17; + result = 37 * result + (isThis ? 0 : 1); + result = 37 * result + typePattern.hashCode(); + return result; + } + + @Override + public String toString() { + return declarationText; + } + + /** + * Residue is the remainder of the pointcut match that couldn't be performed with the purely static information at compile time + * and this method returns the residue of a pointcut at a particular shadow. + */ + @Override + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + if (!couldMatch(shadow)) { + return Literal.FALSE; + } + + // if no preference is specified, just say TRUE which means no residue + if (typePattern == TypePattern.ANY) { + return Literal.TRUE; + } + + Var var = isThis ? shadow.getThisVar() : shadow.getTargetVar(); + + return exposeStateForVar(var, typePattern, state, shadow.getIWorld()); + } + + @Override + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + if (isDeclare(bindings.getEnclosingAdvice())) { + // Enforce rule about which designators are supported in declare + inAspect.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.THIS_OR_TARGET_IN_DECLARE, isThis ? "this" : "target"), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + TypePattern newType = typePattern.remapAdviceFormals(bindings); + if (inAspect.crosscuttingMembers != null) { + inAspect.crosscuttingMembers.exposeType(newType.getExactType()); + } + + Pointcut ret = new ThisOrTargetPointcut(isThis, newType); + ret.copyLocationFrom(this); + return ret; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ThrowsPattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ThrowsPattern.java new file mode 100644 index 000000000..93557d8c1 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/ThrowsPattern.java @@ -0,0 +1,146 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public class ThrowsPattern extends PatternNode { + private TypePatternList required; + private TypePatternList forbidden; + + public static final ThrowsPattern ANY = new ThrowsPattern(TypePatternList.EMPTY, TypePatternList.EMPTY); + + public ThrowsPattern(TypePatternList required, TypePatternList forbidden) { + this.required = required; + this.forbidden = forbidden; + } + + public TypePatternList getRequired() { + return required; + } + + public TypePatternList getForbidden() { + return forbidden; + } + + public String toString() { + if (this == ANY) { + return ""; + } + + String ret = "throws " + required.toString(); + if (forbidden.size() > 0) { + ret = ret + " !(" + forbidden.toString() + ")"; + } + return ret; + } + + public boolean equals(Object other) { + if (!(other instanceof ThrowsPattern)) { + return false; + } + ThrowsPattern o = (ThrowsPattern) other; + boolean ret = o.required.equals(this.required) && o.forbidden.equals(this.forbidden); + return ret; + } + + public int hashCode() { + int result = 17; + result = 37 * result + required.hashCode(); + result = 37 * result + forbidden.hashCode(); + return result; + } + + public ThrowsPattern resolveBindings(IScope scope, Bindings bindings) { + if (this == ANY) { + return this; + } + required = required.resolveBindings(scope, bindings, false, false); + forbidden = forbidden.resolveBindings(scope, bindings, false, false); + return this; + } + + public ThrowsPattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + ThrowsPattern ret = new ThrowsPattern(required.parameterizeWith(typeVariableMap, w), forbidden.parameterizeWith( + typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + public boolean matches(UnresolvedType[] tys, World world) { + if (this == ANY) { + return true; + } + + // System.out.println("matching: " + this + " with " + Arrays.asList(tys)); + + ResolvedType[] types = world.resolve(tys); + // int len = types.length; + for (int j = 0, lenj = required.size(); j < lenj; j++) { + if (!matchesAny(required.get(j), types)) { + return false; + } + } + for (int j = 0, lenj = forbidden.size(); j < lenj; j++) { + if (matchesAny(forbidden.get(j), types)) { + return false; + } + } + return true; + } + + private boolean matchesAny(TypePattern typePattern, ResolvedType[] types) { + for (int i = types.length - 1; i >= 0; i--) { + if (typePattern.matchesStatically(types[i])) { + return true; + } + } + return false; + } + + public static ThrowsPattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + TypePatternList required = TypePatternList.read(s, context); + TypePatternList forbidden = TypePatternList.read(s, context); + if (required.size() == 0 && forbidden.size() == 0) { + return ANY; + } + ThrowsPattern ret = new ThrowsPattern(required, forbidden); + // XXXret.readLocation(context, s); + return ret; + } + + public void write(CompressingDataOutputStream s) throws IOException { + required.write(s); + forbidden.write(s); + // XXXwriteLocation(s); + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor, data); + forbidden.traverse(visitor, data); + required.traverse(visitor, data); + return ret; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypeCategoryTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypeCategoryTypePattern.java new file mode 100644 index 000000000..80ffc7556 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypeCategoryTypePattern.java @@ -0,0 +1,146 @@ +/* ******************************************************************* + * Copyright (c) 2010 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 + * Nieraj Singh + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * A TypeCategoryTypePattern matches on the category of a type, one of class/interface/aspect/inner/anonymous/enum/annotation, and + * these are specified in the pointcut via isClass() isInterface() isAspect() isInner() isAnonymous() isEnum() isAnnotation(). + * + * @author Andy Clement + * @since 1.6.9 + */ +public class TypeCategoryTypePattern extends TypePattern { + + public static final int CLASS = 1; + public static final int INTERFACE = 2; + public static final int ASPECT = 3; + public static final int INNER = 4; + public static final int ANONYMOUS = 5; + public static final int ENUM = 6; + public static final int ANNOTATION = 7; + public static final int FINAL = 8; + public static final int ABSTRACT = 9; + + private int category; + + private int VERSION = 1; + + public TypeCategoryTypePattern(int category) { + super(false); + this.category = category; + } + + public int getTypeCategory() { + return category; + } + + @Override + protected boolean matchesExactly(ResolvedType type) { + return isRightCategory(type); + } + + @Override + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + return isRightCategory(type); + } + + @Override + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + return FuzzyBoolean.fromBoolean(isRightCategory(type)); + } + + @Override + public TypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + return this; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof TypeCategoryTypePattern)) { + return false; + } + TypeCategoryTypePattern o = (TypeCategoryTypePattern) other; + return o.category == category; + } + + // TODO is sourcelocation part of the identity or just a 'nice to have' - if important it should be in hashcode/equals + // TODO but if that is the case it needs addressing for all type patterns + + @Override + public int hashCode() { + return category * 37; + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(TypePattern.TYPE_CATEGORY); + s.writeInt(VERSION); + s.writeInt(category); + writeLocation(s); + } + + @SuppressWarnings("unused") + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + int version = s.readInt(); + int category = s.readInt(); + TypeCategoryTypePattern tp = new TypeCategoryTypePattern(category); + tp.readLocation(context, s); + return tp; + } + + /** + * @return true if the supplied type is of the category specified for this type pattern + */ + private boolean isRightCategory(ResolvedType type) { + switch (category) { + case CLASS: + return type.isClass(); + case INTERFACE: + return type.isInterface(); + case ASPECT: + return type.isAspect(); + case ANONYMOUS: + return type.isAnonymous(); + case INNER: + return type.isNested(); + case ENUM: + return type.isEnum(); + case ANNOTATION: + return type.isAnnotation(); + case FINAL: + return Modifier.isFinal(type.getModifiers()); + case ABSTRACT: + return Modifier.isAbstract(type.getModifiers()); + } + return false; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypePattern.java new file mode 100644 index 000000000..2d657dee2 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypePattern.java @@ -0,0 +1,360 @@ +/* ******************************************************************* + * Copyright (c) 2002, 2010 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * Nieraj Singh + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeVariableReference; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +/** + * On creation, type pattern only contains WildTypePattern nodes, not BindingType or ExactType. + * + * <p> + * Then we call resolveBindings() during compilation During concretization of enclosing pointcuts, we call remapAdviceFormals + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public abstract class TypePattern extends PatternNode { + public static class MatchKind { + private String name; + + public MatchKind(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + } + + public static final MatchKind STATIC = new MatchKind("STATIC"); + public static final MatchKind DYNAMIC = new MatchKind("DYNAMIC"); + + public static final TypePattern ELLIPSIS = new EllipsisTypePattern(); + public static final TypePattern ANY = new AnyTypePattern(); + public static final TypePattern NO = new NoTypePattern(); + + protected boolean includeSubtypes; + protected boolean isVarArgs = false; + protected AnnotationTypePattern annotationPattern = AnnotationTypePattern.ANY; + protected TypePatternList typeParameters = TypePatternList.EMPTY; + + protected TypePattern(boolean includeSubtypes, boolean isVarArgs, TypePatternList typeParams) { + this.includeSubtypes = includeSubtypes; + this.isVarArgs = isVarArgs; + this.typeParameters = (typeParams == null ? TypePatternList.EMPTY : typeParams); + } + + protected TypePattern(boolean includeSubtypes, boolean isVarArgs) { + this(includeSubtypes, isVarArgs, null); + } + + public AnnotationTypePattern getAnnotationPattern() { + return annotationPattern; + } + + public boolean isVarArgs() { + return isVarArgs; + } + + public boolean isStarAnnotation() { + return annotationPattern == AnnotationTypePattern.ANY; + } + + public boolean isArray() { + return false; + } + + protected TypePattern(boolean includeSubtypes) { + this(includeSubtypes, false); + } + + public void setAnnotationTypePattern(AnnotationTypePattern annPatt) { + this.annotationPattern = annPatt; + } + + public void setTypeParameters(TypePatternList typeParams) { + this.typeParameters = typeParams; + } + + public TypePatternList getTypeParameters() { + return this.typeParameters; + } + + public void setIsVarArgs(boolean isVarArgs) { + this.isVarArgs = isVarArgs; + } + + // answer conservatively... + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + if (this.includeSubtypes || other.includeSubtypes) { + return true; + } + if (this.annotationPattern != AnnotationTypePattern.ANY) { + return true; + } + if (other.annotationPattern != AnnotationTypePattern.ANY) { + return true; + } + return false; + } + + // XXX non-final for Not, && and || + public boolean matchesStatically(ResolvedType type) { + if (includeSubtypes) { + return matchesSubtypes(type); + } else { + return matchesExactly(type); + } + } + + public abstract FuzzyBoolean matchesInstanceof(ResolvedType type); + + public final FuzzyBoolean matches(ResolvedType type, MatchKind kind) { + // FuzzyBoolean typeMatch = null; + // ??? This is part of gracefully handling missing references + if (type.isMissing()) { + return FuzzyBoolean.NO; + } + + if (kind == STATIC) { + return FuzzyBoolean.fromBoolean(matchesStatically(type)); + } else if (kind == DYNAMIC) { + // System.err.println("matching: " + this + " with " + type); + // typeMatch = matchesInstanceof(type); + // System.err.println(" got: " + ret); + // return typeMatch.and(annotationPattern.matches(type)); + return matchesInstanceof(type); + } else { + throw new IllegalArgumentException("kind must be DYNAMIC or STATIC"); + } + } + + protected abstract boolean matchesExactly(ResolvedType type); + + protected abstract boolean matchesExactly(ResolvedType type, ResolvedType annotatedType); + + protected boolean matchesSubtypes(ResolvedType type) { + // System.out.println("matching: " + this + " to " + type); + if (matchesExactly(type)) { + return true; + } + + // pr124808 + Iterator<ResolvedType> typesIterator = null; + if (type.isTypeVariableReference()) { + typesIterator = ((TypeVariableReference) type).getTypeVariable().getFirstBound().resolve(type.getWorld()) + .getDirectSupertypes(); + } else { + // pr223605 + if (type.isRawType()) { + type = type.getGenericType(); + } + typesIterator = type.getDirectSupertypes(); + } + + for (Iterator<ResolvedType> i = typesIterator; i.hasNext();) { + ResolvedType superType = i.next(); + if (matchesSubtypes(superType, type)) { + return true; + } + } + return false; + } + + protected boolean matchesSubtypes(ResolvedType superType, ResolvedType annotatedType) { + // System.out.println("matching2: " + this + " to " + superType); + if (matchesExactly(superType, annotatedType)) { + // System.out.println(" true"); + return true; + } + // If an ITD is applied, it will be put onto the generic type, not the parameterized or raw form + if (superType.isParameterizedType() || superType.isRawType()) { + superType = superType.getGenericType(); + } + // FuzzyBoolean ret = FuzzyBoolean.NO; // ??? -eh + for (Iterator<ResolvedType> i = superType.getDirectSupertypes(); i.hasNext();) { + ResolvedType superSuperType = (ResolvedType) i.next(); + if (matchesSubtypes(superSuperType, annotatedType)) { + return true; + } + } + return false; + } + + public UnresolvedType resolveExactType(IScope scope, Bindings bindings) { + TypePattern p = resolveBindings(scope, bindings, false, true); + if (!(p instanceof ExactTypePattern)) { + return ResolvedType.MISSING; + } + return ((ExactTypePattern) p).getType(); + } + + public UnresolvedType getExactType() { + if (this instanceof ExactTypePattern) { + return ((ExactTypePattern) this).getType(); + } else { + return ResolvedType.MISSING; + } + } + + protected TypePattern notExactType(IScope s) { + s.getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.EXACT_TYPE_PATTERN_REQD), getSourceLocation())); + return NO; + } + + // public boolean assertExactType(IMessageHandler m) { + // if (this instanceof ExactTypePattern) return true; + // + // //XXX should try harder to avoid multiple errors for one problem + // m.handleMessage(MessageUtil.error("exact type pattern required", getSourceLocation())); + // return false; + // } + + /** + * This can modify in place, or return a new TypePattern if the type changes. + */ + public TypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding, boolean requireExactType) { + annotationPattern = annotationPattern.resolveBindings(scope, bindings, allowBinding); + return this; + } + + public void resolve(World world) { + annotationPattern.resolve(world); + } + + /** + * return a version of this type pattern in which all type variable references have been replaced by their corresponding entry + * in the map. + */ + public abstract TypePattern parameterizeWith(Map<String, UnresolvedType> typeVariableMap, World w); + + public void postRead(ResolvedType enclosingType) { + } + + public boolean isEllipsis() { + return false; + } + + public boolean isStar() { + return false; + } + + /** + * This is called during concretization of pointcuts, it is used by BindingTypePattern to return a new BindingTypePattern with a + * formal index appropiate for the advice, rather than for the lexical declaration, i.e. this handles transforamtions through + * named pointcuts. + * + * <pre> + * pointcut foo(String name): args(name); + * --> This makes a BindingTypePattern(0) pointing to the 0th formal + * + * before(Foo f, String n): this(f) && foo(n) { ... } + * --> when resolveReferences is called on the args from the above, it + * will return a BindingTypePattern(1) + * + * before(Foo f): this(f) && foo(*) { ... } + * --> when resolveReferences is called on the args from the above, it + * will return an ExactTypePattern(String) + * </pre> + */ + public TypePattern remapAdviceFormals(IntMap bindings) { + return this; + } + + public static final byte WILD = 1; + public static final byte EXACT = 2; + public static final byte BINDING = 3; + public static final byte ELLIPSIS_KEY = 4; + public static final byte ANY_KEY = 5; + public static final byte NOT = 6; + public static final byte OR = 7; + public static final byte AND = 8; + public static final byte NO_KEY = 9; + public static final byte ANY_WITH_ANNO = 10; + public static final byte HAS_MEMBER = 11; + public static final byte TYPE_CATEGORY = 12; + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte key = s.readByte(); + switch (key) { + case WILD: + return WildTypePattern.read(s, context); + case EXACT: + return ExactTypePattern.read(s, context); + case BINDING: + return BindingTypePattern.read(s, context); + case ELLIPSIS_KEY: + return ELLIPSIS; + case ANY_KEY: + return ANY; + case NO_KEY: + return NO; + case NOT: + return NotTypePattern.read(s, context); + case OR: + return OrTypePattern.read(s, context); + case AND: + return AndTypePattern.read(s, context); + case ANY_WITH_ANNO: + return AnyWithAnnotationTypePattern.read(s, context); + case HAS_MEMBER: + return HasMemberTypePattern.read(s, context); + case TYPE_CATEGORY: + return TypeCategoryTypePattern.read(s, context); + } + throw new BCException("unknown TypePattern kind: " + key); + } + + public boolean isIncludeSubtypes() { + return includeSubtypes; + } + + /** + * For quickly recognizing the pattern '!void' + */ + public boolean isBangVoid() { + return false; + } + + /** + * for quickly recognizing the pattern 'void' + */ + public boolean isVoid() { + return false; + } + + public boolean hasFailedResolution() { + return false; + } + +} + + + diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypePatternList.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypePatternList.java new file mode 100644 index 000000000..da9d6e15a --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypePatternList.java @@ -0,0 +1,585 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvableTypeList; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public class TypePatternList extends PatternNode { + private TypePattern[] typePatterns; + int ellipsisCount = 0; + + public static final TypePatternList EMPTY = new TypePatternList(new TypePattern[] {}); + + public static final TypePatternList ANY = new TypePatternList(new TypePattern[] { new EllipsisTypePattern() }); // can't use + + // TypePattern.ELLIPSIS + // because of + // circular + // static + // dependency + // that + // introduces + + public TypePatternList() { + typePatterns = new TypePattern[0]; + ellipsisCount = 0; + } + + public TypePatternList(TypePattern[] arguments) { + this.typePatterns = arguments; + for (int i = 0; i < arguments.length; i++) { + if (arguments[i] == TypePattern.ELLIPSIS) { + ellipsisCount++; + } + } + } + + public TypePatternList(List<TypePattern> l) { + this((TypePattern[]) l.toArray(new TypePattern[l.size()])); + } + + public int size() { + return typePatterns.length; + } + + public TypePattern get(int index) { + return typePatterns[index]; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("("); + for (int i = 0, len = typePatterns.length; i < len; i++) { + TypePattern type = typePatterns[i]; + if (i > 0) { + buf.append(", "); + } + if (type == TypePattern.ELLIPSIS) { + buf.append(".."); + } else { + buf.append(type.toString()); + } + } + buf.append(")"); + return buf.toString(); + } + + /* + * return true iff this pattern could ever match a signature with the given number of parameters + */ + public boolean canMatchSignatureWithNParameters(int numParams) { + if (ellipsisCount == 0) { + return numParams == size(); + } else { + return (size() - ellipsisCount) <= numParams; + } + } + + public FuzzyBoolean matches(ResolvedType[] types, TypePattern.MatchKind kind) { + return matches(types, kind, null); + } + + // XXX shares much code with WildTypePattern and with NamePattern + /** + * When called with TypePattern.STATIC this will always return either FuzzyBoolean.YES or FuzzyBoolean.NO. + * + * When called with TypePattern.DYNAMIC this could return MAYBE if at runtime it would be possible for arguments of the given + * static types to dynamically match this, but it is not known for certain. + * + * This method will never return FuzzyBoolean.NEVER + */ + public FuzzyBoolean matches(ResolvedType[] types, TypePattern.MatchKind kind, ResolvedType[][] parameterAnnotations) { + int nameLength = types.length; + int patternLength = typePatterns.length; + + int nameIndex = 0; + int patternIndex = 0; + + if (ellipsisCount == 0) { + if (nameLength != patternLength) { + return FuzzyBoolean.NO; + } + FuzzyBoolean finalReturn = FuzzyBoolean.YES; + while (patternIndex < patternLength) { + ResolvedType t = types[nameIndex]; + FuzzyBoolean ret = null; + try { + if (parameterAnnotations != null) { + t.temporaryAnnotationTypes = parameterAnnotations[nameIndex]; + } + ret = typePatterns[patternIndex].matches(t, kind); + } finally { + t.temporaryAnnotationTypes = null; + } + patternIndex++; + nameIndex++; + if (ret == FuzzyBoolean.NO) { + return ret; + } + if (ret == FuzzyBoolean.MAYBE) { + finalReturn = ret; + } + } + return finalReturn; + } else if (ellipsisCount == 1) { + if (nameLength < patternLength - 1) { + return FuzzyBoolean.NO; + } + FuzzyBoolean finalReturn = FuzzyBoolean.YES; + while (patternIndex < patternLength) { + TypePattern p = typePatterns[patternIndex++]; + if (p == TypePattern.ELLIPSIS) { + nameIndex = nameLength - (patternLength - patternIndex); + } else { + ResolvedType t = types[nameIndex]; + FuzzyBoolean ret = null; + try { + if (parameterAnnotations != null) { + t.temporaryAnnotationTypes = parameterAnnotations[nameIndex]; + } + ret = p.matches(t, kind); + } finally { + t.temporaryAnnotationTypes = null; + } + nameIndex++; + if (ret == FuzzyBoolean.NO) { + return ret; + } + if (ret == FuzzyBoolean.MAYBE) { + finalReturn = ret; + } + } + } + return finalReturn; + } else { + // System.err.print("match(" + arguments + ", " + types + ") -> "); + FuzzyBoolean b = outOfStar(typePatterns, types, 0, 0, patternLength - ellipsisCount, nameLength, ellipsisCount, kind, + parameterAnnotations); + // System.err.println(b); + return b; + } + } + + private static FuzzyBoolean outOfStar(final TypePattern[] pattern, final ResolvedType[] target, int pi, int ti, int pLeft, + int tLeft, final int starsLeft, TypePattern.MatchKind kind, ResolvedType[][] parameterAnnotations) { + if (pLeft > tLeft) { + return FuzzyBoolean.NO; + } + FuzzyBoolean finalReturn = FuzzyBoolean.YES; + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length && pi < pattern.length) + if (tLeft == 0) { + return finalReturn; + } + if (pLeft == 0) { + if (starsLeft > 0) { + return finalReturn; + } else { + return FuzzyBoolean.NO; + } + } + if (pattern[pi] == TypePattern.ELLIPSIS) { + return inStar(pattern, target, pi + 1, ti, pLeft, tLeft, starsLeft - 1, kind, parameterAnnotations); + } + FuzzyBoolean ret = null; + try { + if (parameterAnnotations != null) { + target[ti].temporaryAnnotationTypes = parameterAnnotations[ti]; + } + ret = pattern[pi].matches(target[ti], kind); + } finally { + target[ti].temporaryAnnotationTypes = null; + } + if (ret == FuzzyBoolean.NO) { + return ret; + } + if (ret == FuzzyBoolean.MAYBE) { + finalReturn = ret; + } + pi++; + ti++; + pLeft--; + tLeft--; + } + } + + private static FuzzyBoolean inStar(final TypePattern[] pattern, final ResolvedType[] target, int pi, int ti, final int pLeft, + int tLeft, int starsLeft, TypePattern.MatchKind kind, ResolvedType[][] parameterAnnotations) { + // invariant: pLeft > 0, so we know we'll run out of stars and find a real char in pattern + TypePattern patternChar = pattern[pi]; + while (patternChar == TypePattern.ELLIPSIS) { + starsLeft--; + patternChar = pattern[++pi]; + } + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length) + if (pLeft > tLeft) { + return FuzzyBoolean.NO; + } + + FuzzyBoolean ff = null; + try { + if (parameterAnnotations != null) { + target[ti].temporaryAnnotationTypes = parameterAnnotations[ti]; + } + ff = patternChar.matches(target[ti], kind); + } finally { + target[ti].temporaryAnnotationTypes = null; + } + + if (ff.maybeTrue()) { + FuzzyBoolean xx = outOfStar(pattern, target, pi + 1, ti + 1, pLeft - 1, tLeft - 1, starsLeft, kind, + parameterAnnotations); + if (xx.maybeTrue()) { + return ff.and(xx); + } + } + ti++; + tLeft--; + } + } + + public FuzzyBoolean matches(ResolvableTypeList types, TypePattern.MatchKind kind, ResolvedType[][] parameterAnnotations) { + int nameLength = types.length; + int patternLength = typePatterns.length; + + int nameIndex = 0; + int patternIndex = 0; + + if (ellipsisCount == 0) { + if (nameLength != patternLength) { + return FuzzyBoolean.NO; + } + FuzzyBoolean finalReturn = FuzzyBoolean.YES; + while (patternIndex < patternLength) { + ResolvedType t = types.getResolved(nameIndex); + FuzzyBoolean ret = null; + try { + if (parameterAnnotations != null) { + t.temporaryAnnotationTypes = parameterAnnotations[nameIndex]; + } + ret = typePatterns[patternIndex].matches(t, kind); + } finally { + t.temporaryAnnotationTypes = null; + } + patternIndex++; + nameIndex++; + if (ret == FuzzyBoolean.NO) { + return ret; + } + if (ret == FuzzyBoolean.MAYBE) { + finalReturn = ret; + } + } + return finalReturn; + } else if (ellipsisCount == 1) { + if (nameLength < patternLength - 1) { + return FuzzyBoolean.NO; + } + FuzzyBoolean finalReturn = FuzzyBoolean.YES; + while (patternIndex < patternLength) { + TypePattern p = typePatterns[patternIndex++]; + if (p == TypePattern.ELLIPSIS) { + nameIndex = nameLength - (patternLength - patternIndex); + } else { + ResolvedType t = types.getResolved(nameIndex); + FuzzyBoolean ret = null; + try { + if (parameterAnnotations != null) { + t.temporaryAnnotationTypes = parameterAnnotations[nameIndex]; + } + ret = p.matches(t, kind); + } finally { + t.temporaryAnnotationTypes = null; + } + nameIndex++; + if (ret == FuzzyBoolean.NO) { + return ret; + } + if (ret == FuzzyBoolean.MAYBE) { + finalReturn = ret; + } + } + } + return finalReturn; + } else { + // System.err.print("match(" + arguments + ", " + types + ") -> "); + FuzzyBoolean b = outOfStar(typePatterns, types, 0, 0, patternLength - ellipsisCount, nameLength, ellipsisCount, kind, + parameterAnnotations); + // System.err.println(b); + return b; + } + } + + private static FuzzyBoolean outOfStar(final TypePattern[] pattern, ResolvableTypeList target, int pi, int ti, int pLeft, + int tLeft, final int starsLeft, TypePattern.MatchKind kind, ResolvedType[][] parameterAnnotations) { + if (pLeft > tLeft) { + return FuzzyBoolean.NO; + } + FuzzyBoolean finalReturn = FuzzyBoolean.YES; + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length && pi < pattern.length) + if (tLeft == 0) { + return finalReturn; + } + if (pLeft == 0) { + if (starsLeft > 0) { + return finalReturn; + } else { + return FuzzyBoolean.NO; + } + } + if (pattern[pi] == TypePattern.ELLIPSIS) { + return inStar(pattern, target, pi + 1, ti, pLeft, tLeft, starsLeft - 1, kind, parameterAnnotations); + } + FuzzyBoolean ret = null; + ResolvedType type = target.getResolved(ti); + try { + if (parameterAnnotations != null) { + type.temporaryAnnotationTypes = parameterAnnotations[ti]; + } + ret = pattern[pi].matches(type, kind); + } finally { + type.temporaryAnnotationTypes = null; + } + if (ret == FuzzyBoolean.NO) { + return ret; + } + if (ret == FuzzyBoolean.MAYBE) { + finalReturn = ret; + } + pi++; + ti++; + pLeft--; + tLeft--; + } + } + + private static FuzzyBoolean inStar(final TypePattern[] pattern, ResolvableTypeList target, int pi, int ti, final int pLeft, + int tLeft, int starsLeft, TypePattern.MatchKind kind, ResolvedType[][] parameterAnnotations) { + // invariant: pLeft > 0, so we know we'll run out of stars and find a real char in pattern + TypePattern patternChar = pattern[pi]; + while (patternChar == TypePattern.ELLIPSIS) { + starsLeft--; + patternChar = pattern[++pi]; + } + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length) + if (pLeft > tLeft) { + return FuzzyBoolean.NO; + } + + ResolvedType type = target.getResolved(ti); + FuzzyBoolean ff = null; + try { + if (parameterAnnotations != null) { + type.temporaryAnnotationTypes = parameterAnnotations[ti]; + } + ff = patternChar.matches(type, kind); + } finally { + type.temporaryAnnotationTypes = null; + } + + if (ff.maybeTrue()) { + FuzzyBoolean xx = outOfStar(pattern, target, pi + 1, ti + 1, pLeft - 1, tLeft - 1, starsLeft, kind, + parameterAnnotations); + if (xx.maybeTrue()) { + return ff.and(xx); + } + } + ti++; + tLeft--; + } + } + + /** + * Return a version of this type pattern list in which all type variable references are replaced by their corresponding entry in + * the map + * + * @param typeVariableMap + * @return + */ + public TypePatternList parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + TypePattern[] parameterizedPatterns = new TypePattern[typePatterns.length]; + for (int i = 0; i < parameterizedPatterns.length; i++) { + parameterizedPatterns[i] = typePatterns[i].parameterizeWith(typeVariableMap, w); + } + return new TypePatternList(parameterizedPatterns); + } + + public TypePatternList resolveBindings(IScope scope, Bindings bindings, boolean allowBinding, boolean requireExactType) { + for (int i = 0; i < typePatterns.length; i++) { + TypePattern p = typePatterns[i]; + if (p != null) { + typePatterns[i] = typePatterns[i].resolveBindings(scope, bindings, allowBinding, requireExactType); + } + } + return this; + } + + public TypePatternList resolveReferences(IntMap bindings) { + int len = typePatterns.length; + TypePattern[] ret = new TypePattern[len]; + for (int i = 0; i < len; i++) { + ret[i] = typePatterns[i].remapAdviceFormals(bindings); + } + return new TypePatternList(ret); + } + + public void postRead(ResolvedType enclosingType) { + for (int i = 0; i < typePatterns.length; i++) { + TypePattern p = typePatterns[i]; + p.postRead(enclosingType); + } + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof TypePatternList)) { + return false; + } + TypePatternList o = (TypePatternList) other; + int len = o.typePatterns.length; + if (len != this.typePatterns.length) { + return false; + } + for (int i = 0; i < len; i++) { + if (!this.typePatterns[i].equals(o.typePatterns[i])) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + int result = 41; + for (int i = 0, len = typePatterns.length; i < len; i++) { + result = 37 * result + typePatterns[i].hashCode(); + } + return result; + } + + public static TypePatternList read(VersionedDataInputStream s, ISourceContext context) throws IOException { + short len = s.readShort(); + TypePattern[] arguments = new TypePattern[len]; + for (int i = 0; i < len; i++) { + arguments[i] = TypePattern.read(s, context); + } + TypePatternList ret = new TypePatternList(arguments); + if (!s.isAtLeast169()) { + ret.readLocation(context, s); + } + return ret; + } + + @Override + public int getEnd() { + throw new IllegalStateException(); + } + + @Override + public ISourceContext getSourceContext() { + throw new IllegalStateException(); + } + + @Override + public ISourceLocation getSourceLocation() { + throw new IllegalStateException(); + } + + @Override + public int getStart() { + throw new IllegalStateException(); + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeShort(typePatterns.length); + for (int i = 0; i < typePatterns.length; i++) { + typePatterns[i].write(s); + } + // writeLocation(s); + } + + public TypePattern[] getTypePatterns() { + return typePatterns; + } + + public List<UnresolvedType> getExactTypes() { + List<UnresolvedType> ret = new ArrayList<UnresolvedType>(); + for (int i = 0; i < typePatterns.length; i++) { + UnresolvedType t = typePatterns[i].getExactType(); + if (!ResolvedType.isMissing(t)) { + ret.add(t); + } + } + return ret; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor, data); + for (int i = 0; i < typePatterns.length; i++) { + typePatterns[i].traverse(visitor, ret); + } + return ret; + } + + public boolean areAllExactWithNoSubtypesAllowed() { + for (int i = 0; i < typePatterns.length; i++) { + TypePattern array_element = typePatterns[i]; + if (!(array_element instanceof ExactTypePattern)) { + return false; + } else { + ExactTypePattern etp = (ExactTypePattern) array_element; + if (etp.isIncludeSubtypes()) { + return false; + } + } + } + return true; + } + + public String[] maybeGetCleanNames() { + String[] theParamNames = new String[typePatterns.length]; + for (int i = 0; i < typePatterns.length; i++) { + TypePattern string = typePatterns[i]; + if (!(string instanceof ExactTypePattern)) { + return null; + } + theParamNames[i] = ((ExactTypePattern) string).getExactType().getName(); + } + return theParamNames; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypePatternQuestions.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypePatternQuestions.java new file mode 100644 index 000000000..d4bdf2e58 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypePatternQuestions.java @@ -0,0 +1,106 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.patterns; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ResolvedType; + + +public class TypePatternQuestions { + private Map<Question,FuzzyBoolean> questionsAndAnswers = new HashMap<Question,FuzzyBoolean>(); + + public FuzzyBoolean askQuestion(TypePattern pattern, ResolvedType type, + TypePattern.MatchKind kind) + { + Question question = new Question(pattern, type, kind); + //??? should we use this table to do caching or is that a pessimization + //??? if we do that optimization we can also do error checking that the result + //??? doesn't change + FuzzyBoolean answer = question.ask(); + questionsAndAnswers.put(question, answer); + return answer; + } + + public Question anyChanges() { + for (Iterator<Map.Entry<Question,FuzzyBoolean>> i = questionsAndAnswers.entrySet().iterator(); i.hasNext(); ) { + Map.Entry<Question,FuzzyBoolean> entry = i.next(); + Question question = (Question)entry.getKey(); + FuzzyBoolean expectedAnswer = (FuzzyBoolean)entry.getValue(); + + FuzzyBoolean currentAnswer = question.ask(); + //System.out.println(question + ":" + currentAnswer); + if (currentAnswer != expectedAnswer) { + return question; + } + } + + return null; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("TypePatternQuestions{"); + for (Map.Entry<Question,FuzzyBoolean> entry: questionsAndAnswers.entrySet()) { + Question question = (Question)entry.getKey(); + FuzzyBoolean expectedAnswer = (FuzzyBoolean)entry.getValue(); + buf.append(question); + buf.append(":"); + buf.append(expectedAnswer); + buf.append(", "); + } + buf.append("}"); + return buf.toString(); + } + + + public class Question { + TypePattern pattern; + ResolvedType type; + TypePattern.MatchKind kind; + + public Question(TypePattern pattern, ResolvedType type, + TypePattern.MatchKind kind) { + super(); + this.pattern = pattern; + this.type = type; + this.kind = kind; + } + + public FuzzyBoolean ask() { + return pattern.matches(type, kind); + } + + public boolean equals(Object other) { + if (!(other instanceof Question)) return false; + Question o = (Question)other; + return o.pattern.equals(pattern) && o.type.equals(type) && o.kind == kind; + } + + public int hashCode() { + int result = 17; + result = 37*result + kind.hashCode(); + result = 37*result + pattern.hashCode(); + result = 37*result + type.hashCode(); + return result; + } + + public String toString() { + return "?(" + pattern + ", " + type + ", " + kind + ")"; + } + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypeVariablePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypeVariablePattern.java new file mode 100644 index 000000000..c2d4bd0ec --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypeVariablePattern.java @@ -0,0 +1,243 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; + +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; + +/** + * @author colyer Represents a type variable as declared as part of a type declaration, parameter declaration, or type parameter + * specification. + * <p> + * For example: + * </p> + * <ul> + * <li><T> T genericMethod(T t) {...}</li> + * <li>static <T extends Foo> T staticGenericMethod(T t) {...}</li> + * <li>Foo<T extends Bar & IGoo> + * </ul> + */ +public class TypeVariablePattern extends PatternNode { + + private static final String anything = "?"; + + private String name; // eg. "T" + private TypePattern upperBound; // default is object unless of the form T extends Bar + private TypePattern[] interfaceBounds; // additional upper bounds (must be interfaces) arising from + // declarations of the form T extends Bar & IGoo, IDoo + private TypePattern lowerBound; // only set if type variable is of the form T super Bar + + /** + * Create a named type variable with upper bound Object and no lower bounds. Use this constructor for the simple "T" case + */ + public TypeVariablePattern(String variableName) { + this.name = variableName; + this.upperBound = new ExactTypePattern(UnresolvedType.OBJECT, false, false); + this.lowerBound = null; + this.interfaceBounds = null; + } + + /** + * Create a named type variable with the given upper bound and no lower bounds Use this constructor for the T extends Foo case + * + * @param variableName + * @param upperBound + */ + public TypeVariablePattern(String variableName, TypePattern upperBound) { + this.name = variableName; + this.upperBound = upperBound; + this.lowerBound = null; + this.interfaceBounds = null; + } + + public TypeVariablePattern(String variableName, TypePattern upperLimit, TypePattern[] interfaceBounds, TypePattern lowerBound) { + this.name = variableName; + this.upperBound = upperLimit; + if (upperBound == null) { + upperBound = new ExactTypePattern(UnresolvedType.OBJECT, false, false); + } + this.interfaceBounds = interfaceBounds; + this.lowerBound = lowerBound; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public String getName() { + return name; + } + + public boolean isAnythingPattern() { + return name.equals(anything); + } + + public TypePattern getRawTypePattern() { + return upperBound; + } + + public TypePattern getUpperBound() { + return upperBound; + } + + public boolean hasLowerBound() { + return (lowerBound != null); + } + + public TypePattern getLowerBound() { + return lowerBound; + } + + public boolean hasAdditionalInterfaceBounds() { + return (interfaceBounds != null); + } + + public TypePattern[] getAdditionalInterfaceBounds() { + if (interfaceBounds != null) { + return interfaceBounds; + } else { + return new TypePattern[0]; + } + } + + public boolean equals(Object obj) { + if (!(obj instanceof TypeVariablePattern)) { + return false; + } + TypeVariablePattern other = (TypeVariablePattern) obj; + if (!name.equals(other.name)) { + return false; + } + if (!upperBound.equals(other.upperBound)) { + return false; + } + if (lowerBound != null) { + if (other.lowerBound == null) { + return false; + } + if (!lowerBound.equals(other.lowerBound)) { + return false; + } + } else { + if (other.lowerBound != null) { + return false; + } + } + if (interfaceBounds != null) { + if (other.interfaceBounds == null) { + return false; + } + if (interfaceBounds.length != other.interfaceBounds.length) { + return false; + } + for (int i = 0; i < interfaceBounds.length; i++) { + if (!interfaceBounds[i].equals(other.interfaceBounds[i])) { + return false; + } + } + } else { + if (other.interfaceBounds != null) { + return false; + } + } + return true; + } + + public int hashCode() { + int hashCode = 17 + (37 * name.hashCode()); + hashCode = hashCode * 37 + upperBound.hashCode(); + if (lowerBound != null) { + hashCode = hashCode * 37 + lowerBound.hashCode(); + } + if (interfaceBounds != null) { + for (int i = 0; i < interfaceBounds.length; i++) { + hashCode = 37 * hashCode + interfaceBounds[i].hashCode(); + } + } + return hashCode; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(name); + sb.append(getExtendsClause()); + if (interfaceBounds != null) { + sb.append(" & "); + for (int i = 0; i < interfaceBounds.length; i++) { + sb.append(interfaceBounds[i].toString()); + if (i < interfaceBounds.length) { + sb.append(","); + } + } + } + if (lowerBound != null) { + sb.append(" super "); + sb.append(lowerBound.toString()); + } + return sb.toString(); + } + + private String getExtendsClause() { + if (upperBound instanceof ExactTypePattern) { + ExactTypePattern bound = (ExactTypePattern) upperBound; + if (bound.type == UnresolvedType.OBJECT) { + return ""; + } + } + return " extends " + upperBound.toString(); + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeUTF(name); + upperBound.write(s); + if (interfaceBounds == null) { + s.writeInt(0); + } else { + s.writeInt(interfaceBounds.length); + for (int i = 0; i < interfaceBounds.length; i++) { + interfaceBounds[i].write(s); + } + } + s.writeBoolean(hasLowerBound()); + if (hasLowerBound()) { + lowerBound.write(s); + } + writeLocation(s); + } + + public static TypeVariablePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + TypeVariablePattern tv = null; + String name = s.readUTF(); + TypePattern upperBound = TypePattern.read(s, context); + TypePattern[] additionalInterfaceBounds = null; + int numInterfaceBounds = s.readInt(); + if (numInterfaceBounds > 0) { + additionalInterfaceBounds = new TypePattern[numInterfaceBounds]; + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + additionalInterfaceBounds[i] = TypePattern.read(s, context); + } + } + boolean hasLowerBound = s.readBoolean(); + TypePattern lowerBound = null; + if (hasLowerBound) { + lowerBound = TypePattern.read(s, context); + } + tv = new TypeVariablePattern(name, upperBound, additionalInterfaceBounds, lowerBound); + tv.readLocation(context, s); + return tv; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypeVariablePatternList.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypeVariablePatternList.java new file mode 100644 index 000000000..7bbe67872 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/TypeVariablePatternList.java @@ -0,0 +1,84 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; + +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.VersionedDataInputStream; + +/** + * @author colyer A list of type variable specifications, eg. <T,S> + */ +public class TypeVariablePatternList extends PatternNode { + + public static final TypeVariablePatternList EMPTY = new TypeVariablePatternList(new TypeVariablePattern[0]); + + private TypeVariablePattern[] patterns; + + public TypeVariablePatternList(TypeVariablePattern[] typeVars) { + this.patterns = typeVars; + } + + public TypeVariablePattern[] getTypeVariablePatterns() { + return this.patterns; + } + + public TypeVariablePattern lookupTypeVariable(String name) { + for (int i = 0; i < patterns.length; i++) { + if (patterns[i].getName().equals(name)) { + return patterns[i]; + } + } + return null; + } + + public boolean isEmpty() { + return ((patterns == null) || (patterns.length == 0)); + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeInt(patterns.length); + for (int i = 0; i < patterns.length; i++) { + patterns[i].write(s); + } + writeLocation(s); + } + + public static TypeVariablePatternList read(VersionedDataInputStream s, ISourceContext context) throws IOException { + TypeVariablePatternList ret = EMPTY; + int length = s.readInt(); + if (length > 0) { + TypeVariablePattern[] patterns = new TypeVariablePattern[length]; + for (int i = 0; i < patterns.length; i++) { + patterns[i] = TypeVariablePattern.read(s, context); + } + ret = new TypeVariablePatternList(patterns); + } + ret.readLocation(context, s); // redundant but safe to read location for EMPTY + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor, data); + for (int i = 0; i < patterns.length; i++) { + patterns[i].traverse(visitor, ret); + } + return ret; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WildAnnotationTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WildAnnotationTypePattern.java new file mode 100644 index 000000000..0941462d1 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WildAnnotationTypePattern.java @@ -0,0 +1,434 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +/** + * @author colyer + * @author Andy Clement + */ +public class WildAnnotationTypePattern extends AnnotationTypePattern { + + private TypePattern typePattern; + private boolean resolved = false; + Map<String, String> annotationValues; + + public WildAnnotationTypePattern(TypePattern typePattern) { + super(); + this.typePattern = typePattern; + this.setLocation(typePattern.getSourceContext(), typePattern.start, typePattern.end); + } + + public WildAnnotationTypePattern(TypePattern typePattern, Map<String, String> annotationValues) { + super(); + this.typePattern = typePattern; + this.annotationValues = annotationValues; + // PVAL make the location be from start of type pattern to end of values + this.setLocation(typePattern.getSourceContext(), typePattern.start, typePattern.end); + } + + public TypePattern getTypePattern() { + return typePattern; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#matches(org.aspectj.weaver.AnnotatedElement) + */ + @Override + public FuzzyBoolean matches(AnnotatedElement annotated) { + return matches(annotated, null); + } + + /** + * Resolve any annotation values specified, checking they are all well formed (valid names, valid values) + * + * @param annotationType the annotation type for which the values have been specified + * @param scope the scope within which to resolve type references (eg. Color.GREEN) + */ + protected void resolveAnnotationValues(ResolvedType annotationType, IScope scope) { + if (annotationValues == null) { + return; + } + // Check any values specified are OK: + // - the value names are for valid annotation fields + // - the specified values are of the correct type + // - for enums, check the specified values can be resolved in the specified scope + Map<String,String> replacementValues = new HashMap<String,String>(); + Set<String> keys = annotationValues.keySet(); + ResolvedMember[] ms = annotationType.getDeclaredMethods(); + for (String k: keys) { + String key = k; + // a trailing ! indicates the the user expressed key!=value rather than key=value as a match constraint + if (k.endsWith("!")) { + key = key.substring(0, k.length() - 1); + } + String v = annotationValues.get(k); + boolean validKey = false; + for (int i = 0; i < ms.length; i++) { + ResolvedMember resolvedMember = ms[i]; + if (resolvedMember.getName().equals(key) && resolvedMember.isAbstract()) { + validKey = true; + ResolvedType t = resolvedMember.getReturnType().resolve(scope.getWorld()); + if (t.isEnum()) { + // value must be an enum reference X.Y + int pos = v.lastIndexOf("."); + if (pos == -1) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "enum"), getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } else { + String typename = v.substring(0, pos); + ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld()); + v = rt.getSignature() + v.substring(pos + 1); // from 'Color.RED' to 'Lp/Color;RED' + replacementValues.put(k, v); + break; + } + } else if (t.isPrimitiveType()) { + if (t.getSignature().equals("I")) { + try { + int value = Integer.parseInt(v); + replacementValues.put(k, Integer.toString(value)); + break; + } catch (NumberFormatException nfe) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "int"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } else if (t.getSignature().equals("F")) { + try { + float value = Float.parseFloat(v); + replacementValues.put(k, Float.toString(value)); + break; + } catch (NumberFormatException nfe) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "float"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + + } else if (t.getSignature().equals("Z")) { + if (v.equalsIgnoreCase("true") || v.equalsIgnoreCase("false")) { + // is it ok ! + } else { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "boolean"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } else if (t.getSignature().equals("S")) { + try { + short value = Short.parseShort(v); + replacementValues.put(k, Short.toString(value)); + break; + } catch (NumberFormatException nfe) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "short"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } else if (t.getSignature().equals("J")) { + try { + replacementValues.put(k, Long.toString(Long.parseLong(v))); + break; + } catch (NumberFormatException nfe) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "long"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } else if (t.getSignature().equals("D")) { + try { + replacementValues.put(k, Double.toString(Double.parseDouble(v))); + break; + } catch (NumberFormatException nfe) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "double"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } else if (t.getSignature().equals("B")) { + try { + replacementValues.put(k, Byte.toString(Byte.parseByte(v))); + break; + } catch (NumberFormatException nfe) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "byte"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } else if (t.getSignature().equals("C")) { + if (v.length() != 3) { // '?' + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE, v, "char"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } else { + replacementValues.put(k, v.substring(1, 2)); + break; + } + } else { + throw new RuntimeException("Not implemented for " + t); + } + } else if (t.equals(ResolvedType.JL_STRING)) { + // nothing to do, it will be OK + } else if (t.equals(ResolvedType.JL_CLASS) || (t.isParameterizedOrGenericType() && t.getRawType().equals(ResolvedType.JL_CLASS))) { + String typename = v.substring(0, v.lastIndexOf('.')); // strip off '.class' + ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld()); + if (rt.isMissing()) { + IMessage m = MessageUtil.error("Unable to resolve type '" + v + "' specified for value '" + k + "'", + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + replacementValues.put(k, rt.getSignature()); + break; + } else { + if (t.isAnnotation()) { + if (v.indexOf("(") != -1) { + throw new RuntimeException( + "Compiler limitation: annotation values can only currently be marker annotations (no values): " + + v); + } + String typename = v.substring(1); + ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld()); + if (rt.isMissing()) { + IMessage m = MessageUtil.error( + "Unable to resolve type '" + v + "' specified for value '" + k + "'", getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + replacementValues.put(k, rt.getSignature()); + break; +// } else if (t.isArray()) { + // Looks like {} aren't pseudotokens in the parser so they don't get through for our pointcut parser +// // @Foo(value=[Foo.class]) +// String typename = v.substring(0, v.lastIndexOf('.')); // strip off '.class' +// ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld()); +// if (rt.isMissing()) { +// IMessage m = MessageUtil.error("Unable to resolve type '" + v + "' specified for value '" + k + "'", +// getSourceLocation()); +// scope.getWorld().getMessageHandler().handleMessage(m); +// } +// replacementValues.put(k, rt.getSignature()); + } else { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.UNSUPPORTED_ANNOTATION_VALUE_TYPE,t), getSourceLocation())); + replacementValues.put(k,""); + } + } + } + } + if (!validKey) { + IMessage m = MessageUtil.error(WeaverMessages.format(WeaverMessages.UNKNOWN_ANNOTATION_VALUE, annotationType, k), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } + annotationValues.putAll(replacementValues); + } + + @Override + public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) { + if (!resolved) { + throw new IllegalStateException("Can't match on an unresolved annotation type pattern"); + } + if (annotationValues != null && !typePattern.hasFailedResolution()) { + // PVAL improve this restriction, would allow '*(value=Color.RED)' + throw new IllegalStateException("Cannot use annotationvalues with a wild annotation pattern"); + } + if (isForParameterAnnotationMatch()) { + if (parameterAnnotations != null && parameterAnnotations.length != 0) { + for (int i = 0; i < parameterAnnotations.length; i++) { + if (typePattern.matches(parameterAnnotations[i], TypePattern.STATIC).alwaysTrue()) { + return FuzzyBoolean.YES; + } + } + } + } else { + // matches if the type of any of the annotations on the AnnotatedElement is + // matched by the typePattern. + ResolvedType[] annTypes = annotated.getAnnotationTypes(); + if (annTypes != null && annTypes.length != 0) { + for (int i = 0; i < annTypes.length; i++) { + if (typePattern.matches(annTypes[i], TypePattern.STATIC).alwaysTrue()) { + return FuzzyBoolean.YES; + } + } + } + } + return FuzzyBoolean.NO; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolve(org.aspectj.weaver.World) + */ + @Override + public void resolve(World world) { + if (!resolved) { + // attempt resolution - this helps with the Spring bug where they resolve() the pointcut in no scope (SPR-5307) + if (typePattern instanceof WildTypePattern && (annotationValues == null || annotationValues.isEmpty())) { + WildTypePattern wildTypePattern = (WildTypePattern) typePattern; + String fullyQualifiedName = wildTypePattern.maybeGetCleanName(); + if (fullyQualifiedName != null && fullyQualifiedName.indexOf(".") != -1) { + ResolvedType resolvedType = world.resolve(UnresolvedType.forName(fullyQualifiedName)); + if (resolvedType != null && !resolvedType.isMissing()) { + typePattern = new ExactTypePattern(resolvedType, false, false); + } + } + } + resolved = true; + } + } + + /** + * This can modify in place, or return a new TypePattern if the type changes. + */ + @Override + public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) { + if (!scope.getWorld().isInJava5Mode()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ANNOTATIONS_NEED_JAVA5), getSourceLocation())); + return this; + } + if (resolved) { + return this; + } + this.typePattern = typePattern.resolveBindings(scope, bindings, false, false); + resolved = true; + if (typePattern instanceof ExactTypePattern) { + ExactTypePattern et = (ExactTypePattern) typePattern; + if (!et.getExactType().resolve(scope.getWorld()).isAnnotation()) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE, et.getExactType().getName()), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + resolved = false; + } + ResolvedType annotationType = et.getExactType().resolve(scope.getWorld()); + resolveAnnotationValues(annotationType, scope); + ExactAnnotationTypePattern eatp = new ExactAnnotationTypePattern(annotationType, annotationValues); + eatp.copyLocationFrom(this); + if (isForParameterAnnotationMatch()) { + eatp.setForParameterAnnotationMatch(); + } + return eatp; + } else { + return this; + } + } + + @Override + public AnnotationTypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + WildAnnotationTypePattern ret = new WildAnnotationTypePattern(typePattern.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + ret.resolved = resolved; + return ret; + } + + private static final byte VERSION = 1; // rev if ser. form changes + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.WILD); + s.writeByte(VERSION); + typePattern.write(s); + writeLocation(s); + s.writeBoolean(isForParameterAnnotationMatch()); + // PVAL + if (annotationValues == null) { + s.writeInt(0); + } else { + s.writeInt(annotationValues.size()); + Set<String> key = annotationValues.keySet(); + for (Iterator<String> keys = key.iterator(); keys.hasNext();) { + String k = keys.next(); + s.writeUTF(k); + s.writeUTF(annotationValues.get(k)); + } + } + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + WildAnnotationTypePattern ret; + byte version = s.readByte(); + if (version > VERSION) { + throw new BCException("ExactAnnotationTypePattern was written by a newer version of AspectJ"); + } + TypePattern t = TypePattern.read(s, context); + ret = new WildAnnotationTypePattern(t); + ret.readLocation(context, s); + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160) { + if (s.readBoolean()) { + ret.setForParameterAnnotationMatch(); + } + } + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160M2) { + int annotationValueCount = s.readInt(); + if (annotationValueCount > 0) { + Map<String, String> aValues = new HashMap<String, String>(); + for (int i = 0; i < annotationValueCount; i++) { + String key = s.readUTF(); + String val = s.readUTF(); + aValues.put(key, val); + } + ret.annotationValues = aValues; + } + } + return ret; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof WildAnnotationTypePattern)) { + return false; + } + WildAnnotationTypePattern other = (WildAnnotationTypePattern) obj; + return other.typePattern.equals(typePattern) + && this.isForParameterAnnotationMatch() == other.isForParameterAnnotationMatch() + && (annotationValues == null ? other.annotationValues == null : annotationValues.equals(other.annotationValues)); + } + + @Override + public int hashCode() { + return (((17 + 37 * typePattern.hashCode()) * 37 + (isForParameterAnnotationMatch() ? 0 : 1)) * 37) + + (annotationValues == null ? 0 : annotationValues.hashCode()); + } + + @Override + public String toString() { + return "@(" + typePattern.toString() + ")"; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WildChildFinder.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WildChildFinder.java new file mode 100644 index 000000000..500d281e2 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WildChildFinder.java @@ -0,0 +1,68 @@ +/* ******************************************************************* + * Copyright (c) 2019 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +/** + * @author Tuomas Kiviaho + */ +public class WildChildFinder extends AbstractPatternNodeVisitor { + + private boolean wildChild; + + public WildChildFinder() { + super(); + } + + public boolean containedWildChild() { + return wildChild; + } + + @Override + public Object visit(WildAnnotationTypePattern node, Object data) { + node.getTypePattern().accept(this, data); + return node; + } + + @Override + public Object visit(WildTypePattern node, Object data) { + this.wildChild = true; + return super.visit(node, data); + } + + @Override + public Object visit(AndTypePattern node, Object data) { + node.getLeft().accept(this, data); + if (!this.wildChild) { + node.getRight().accept(this, data); + } + return node; + } + + @Override + public Object visit(OrTypePattern node, Object data) { + node.getLeft().accept(this, data); + if (!this.wildChild) { + node.getRight().accept(this, data); + } + return node; + } + + public Object visit(NotTypePattern node, Object data) { + node.getNegatedPattern().accept(this, data); + return node; + } + + @Override + public Object visit(AnyWithAnnotationTypePattern node, Object data) { + node.getAnnotationPattern().accept(this, data); + return node; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WildTypePattern.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WildTypePattern.java new file mode 100644 index 000000000..9081c5c02 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WildTypePattern.java @@ -0,0 +1,1405 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FileUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.BoundedReferenceType; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.IHasPosition; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeFactory; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.TypeVariableReference; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.UnresolvedTypeVariableReferenceType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +/** + * The PatternParser always creates WildTypePatterns for type patterns in pointcut expressions (apart from *, which is sometimes + * directly turned into TypePattern.ANY). resolveBindings() tries to work out what we've really got and turn it into a type pattern + * that we can use for matching. This will normally be either an ExactTypePattern or a WildTypePattern. + * + * Here's how the process pans out for various generic and parameterized patterns: (see GenericsWildTypePatternResolvingTestCase) + * + * Foo where Foo exists and is generic Parser creates WildTypePattern namePatterns={Foo} resolveBindings resolves Foo to RT(Foo - + * raw) return ExactTypePattern(LFoo;) + * + * Foo<String> where Foo exists and String meets the bounds Parser creates WildTypePattern namePatterns = {Foo}, + * typeParameters=WTP{String} resolveBindings resolves typeParameters to ExactTypePattern(String) resolves Foo to RT(Foo) returns + * ExactTypePattern(PFoo<String>; - parameterized) + * + * Foo<Str*> where Foo exists and takes one bound Parser creates WildTypePattern namePatterns = {Foo}, typeParameters=WTP{Str*} + * resolveBindings resolves typeParameters to WTP{Str*} resolves Foo to RT(Foo) returns WildTypePattern(name = Foo, typeParameters = + * WTP{Str*} isGeneric=false) + * + * Fo*<String> Parser creates WildTypePattern namePatterns = {Fo*}, typeParameters=WTP{String} resolveBindings resolves + * typeParameters to ETP{String} returns WildTypePattern(name = Fo*, typeParameters = ETP{String} isGeneric=false) + * + * + * Foo<?> + * + * Foo<? extends Number> + * + * Foo<? extends Number+> + * + * Foo<? super Number> + * + */ +public class WildTypePattern extends TypePattern { + private static final String GENERIC_WILDCARD_CHARACTER = "?"; // signature of ? is * + private static final String GENERIC_WILDCARD_SIGNATURE_CHARACTER = "*"; // signature of ? is * + private NamePattern[] namePatterns; + private boolean failedResolution = false; + int ellipsisCount; + String[] importedPrefixes; + String[] knownMatches; + int dim; + + // SECRETAPI - just for testing, turns off boundschecking temporarily... + public static boolean boundscheckingoff = false; + + // these next three are set if the type pattern is constrained by extends or super clauses, in which case the + // namePatterns must have length 1 + // TODO AMC: read/write/resolve of these fields + TypePattern upperBound; // extends Foo + TypePattern[] additionalInterfaceBounds; // extends Foo & A,B,C + TypePattern lowerBound; // super Foo + + // if we have type parameters, these fields indicate whether we should be a generic type pattern or a parameterized + // type pattern. We can only tell during resolve bindings. + private boolean isGeneric = true; + + WildTypePattern(NamePattern[] namePatterns, boolean includeSubtypes, int dim, boolean isVarArgs, TypePatternList typeParams) { + super(includeSubtypes, isVarArgs, typeParams); + this.namePatterns = namePatterns; + this.dim = dim; + ellipsisCount = 0; + for (int i = 0; i < namePatterns.length; i++) { + if (namePatterns[i] == NamePattern.ELLIPSIS) { + ellipsisCount++; + } + } + setLocation(namePatterns[0].getSourceContext(), namePatterns[0].getStart(), namePatterns[namePatterns.length - 1].getEnd()); + } + + public WildTypePattern(List<NamePattern> names, boolean includeSubtypes, int dim) { + this((NamePattern[]) names.toArray(new NamePattern[names.size()]), includeSubtypes, dim, false, TypePatternList.EMPTY); + + } + + public WildTypePattern(List<NamePattern> names, boolean includeSubtypes, int dim, int endPos) { + this(names, includeSubtypes, dim); + this.end = endPos; + } + + public WildTypePattern(List<NamePattern> names, boolean includeSubtypes, int dim, int endPos, boolean isVarArg) { + this(names, includeSubtypes, dim); + this.end = endPos; + this.isVarArgs = isVarArg; + } + + public WildTypePattern(List<NamePattern> names, boolean includeSubtypes, int dim, int endPos, boolean isVarArg, TypePatternList typeParams, + TypePattern upperBound, TypePattern[] additionalInterfaceBounds, TypePattern lowerBound) { + this((NamePattern[]) names.toArray(new NamePattern[names.size()]), includeSubtypes, dim, isVarArg, typeParams); + this.end = endPos; + this.upperBound = upperBound; + this.lowerBound = lowerBound; + this.additionalInterfaceBounds = additionalInterfaceBounds; + } + + public WildTypePattern(List<NamePattern> names, boolean includeSubtypes, int dim, int endPos, boolean isVarArg, TypePatternList typeParams) { + this((NamePattern[]) names.toArray(new NamePattern[names.size()]), includeSubtypes, dim, isVarArg, typeParams); + this.end = endPos; + } + + public NamePattern[] getNamePatterns() { + return namePatterns; + } + + public TypePattern getUpperBound() { + return upperBound; + } + + public TypePattern getLowerBound() { + return lowerBound; + } + + public TypePattern[] getAdditionalIntefaceBounds() { + return additionalInterfaceBounds; + } + + // called by parser after parsing a type pattern, must bump dim as well as setting flag + @Override + public void setIsVarArgs(boolean isVarArgs) { + this.isVarArgs = isVarArgs; + if (isVarArgs) { + this.dim += 1; + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern) + */ + @Override + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + if (super.couldEverMatchSameTypesAs(other)) { + return true; + } + // false is necessary but not sufficient + UnresolvedType otherType = other.getExactType(); + if (!ResolvedType.isMissing(otherType)) { + if (namePatterns.length > 0) { + if (!namePatterns[0].matches(otherType.getName())) { + return false; + } + } + } + if (other instanceof WildTypePattern) { + WildTypePattern owtp = (WildTypePattern) other; + String mySimpleName = namePatterns[0].maybeGetSimpleName(); + String yourSimpleName = owtp.namePatterns[0].maybeGetSimpleName(); + if (mySimpleName != null && yourSimpleName != null) { + return (mySimpleName.startsWith(yourSimpleName) || yourSimpleName.startsWith(mySimpleName)); + } + } + return true; + } + + // XXX inefficient implementation + // we don't know whether $ characters are from nested types, or were + // part of the declared type name (generated code often uses $s in type + // names). More work required on our part to get this right... + public static char[][] splitNames(String s, boolean convertDollar) { + List<char[]> ret = new ArrayList<char[]>(); + int startIndex = 0; + while (true) { + int breakIndex = s.indexOf('.', startIndex); // what about / + if (convertDollar && (breakIndex == -1)) { + breakIndex = s.indexOf('$', startIndex); // we treat $ like . here + } + if (breakIndex == -1) { + break; + } + char[] name = s.substring(startIndex, breakIndex).toCharArray(); + ret.add(name); + startIndex = breakIndex + 1; + } + ret.add(s.substring(startIndex).toCharArray()); + return ret.toArray(new char[ret.size()][]); + } + + /** + * @see org.aspectj.weaver.TypePattern#matchesExactly(IType) + */ + @Override + protected boolean matchesExactly(ResolvedType type) { + return matchesExactly(type, type); + } + + @Override + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + String targetTypeName = type.getName(); + + // System.err.println("match: " + targetTypeName + ", " + knownMatches); //Arrays.asList(importedPrefixes)); + // Ensure the annotation pattern is resolved + annotationPattern.resolve(type.getWorld()); + + return matchesExactlyByName(targetTypeName, type.isAnonymous(), type.isNested()) && matchesParameters(type, STATIC) + && matchesBounds(type, STATIC) + && annotationPattern.matches(annotatedType, type.temporaryAnnotationTypes).alwaysTrue(); + } + + // we've matched against the base (or raw) type, but if this type pattern specifies parameters or + // type variables we need to make sure we match against them too + private boolean matchesParameters(ResolvedType aType, MatchKind staticOrDynamic) { + if (!isGeneric && typeParameters.size() > 0) { + if (!aType.isParameterizedType()) { + return false; + } + // we have to match type parameters + return typeParameters.matches(aType.getResolvedTypeParameters(), staticOrDynamic).alwaysTrue(); + } + return true; + } + + // we've matched against the base (or raw) type, but if this type pattern specifies bounds because + // it is a ? extends or ? super deal then we have to match them too. + private boolean matchesBounds(ResolvedType aType, MatchKind staticOrDynamic) { + if (!(aType instanceof BoundedReferenceType)) { + return true; + } + BoundedReferenceType boundedRT = (BoundedReferenceType) aType; + if (upperBound == null && boundedRT.getUpperBound() != null) { + // for upper bound, null can also match against Object - but anything else and we're out. + if (!boundedRT.getUpperBound().getName().equals(UnresolvedType.OBJECT.getName())) { + return false; + } + } + if (lowerBound == null && boundedRT.getLowerBound() != null) { + return false; + } + if (upperBound != null) { + // match ? extends + if (aType.isGenericWildcard() && boundedRT.isSuper()) { + return false; + } + if (boundedRT.getUpperBound() == null) { + return false; + } + return upperBound.matches((ResolvedType) boundedRT.getUpperBound(), staticOrDynamic).alwaysTrue(); + } + if (lowerBound != null) { + // match ? super + if (!(boundedRT.isGenericWildcard() && boundedRT.isSuper())) { + return false; + } + return lowerBound.matches((ResolvedType) boundedRT.getLowerBound(), staticOrDynamic).alwaysTrue(); + } + return true; + } + + /** + * Used in conjunction with checks on 'isStar()' to tell you if this pattern represents '*' or '*[]' which are different ! + */ + public int getDimensions() { + return dim; + } + + @Override + public boolean isArray() { + return dim > 1; + } + + /** + * @param targetTypeName + * @return + */ + private boolean matchesExactlyByName(String targetTypeName, boolean isAnonymous, boolean isNested) { + // we deal with parameter matching separately... + if (targetTypeName.indexOf('<') != -1) { + targetTypeName = targetTypeName.substring(0, targetTypeName.indexOf('<')); + } + // we deal with bounds matching separately too... + if (targetTypeName.startsWith(GENERIC_WILDCARD_CHARACTER)) { + targetTypeName = GENERIC_WILDCARD_CHARACTER; + } + // XXX hack + if (knownMatches == null && importedPrefixes == null) { + return innerMatchesExactly(targetTypeName, isAnonymous, isNested); + } + + if (isNamePatternStar()) { + // we match if the dimensions match + int numDimensionsInTargetType = 0; + if (dim > 0) { + int index; + while ((index = targetTypeName.indexOf('[')) != -1) { + numDimensionsInTargetType++; + targetTypeName = targetTypeName.substring(index + 1); + } + if (numDimensionsInTargetType == dim) { + return true; + } else { + return false; + } + } + } + + // if our pattern is length 1, then known matches are exact matches + // if it's longer than that, then known matches are prefixes of a sort + if (namePatterns.length == 1) { + if (isAnonymous) { + // we've already ruled out "*", and no other name pattern should match an anonymous type + return false; + } + for (int i = 0, len = knownMatches.length; i < len; i++) { + if (knownMatches[i].equals(targetTypeName)) { + return true; + } + } + } else { + for (int i = 0, len = knownMatches.length; i < len; i++) { + String knownMatch = knownMatches[i]; + // String knownPrefix = knownMatches[i] + "$"; + // if (targetTypeName.startsWith(knownPrefix)) { + if (targetTypeName.startsWith(knownMatch) && targetTypeName.length() > knownMatch.length() + && targetTypeName.charAt(knownMatch.length()) == '$') { + int pos = lastIndexOfDotOrDollar(knownMatch); + if (innerMatchesExactly(targetTypeName.substring(pos + 1), isAnonymous, isNested)) { + return true; + } + } + } + } + + // if any prefixes match, strip the prefix and check that the rest matches + // assumes that prefixes have a dot at the end + for (int i = 0, len = importedPrefixes.length; i < len; i++) { + String prefix = importedPrefixes[i]; + // System.err.println("prefix match? " + prefix + " to " + targetTypeName); + if (targetTypeName.startsWith(prefix)) { + + if (innerMatchesExactly(targetTypeName.substring(prefix.length()), isAnonymous, isNested)) { + return true; + } + } + } + + return innerMatchesExactly(targetTypeName, isAnonymous, isNested); + } + + private int lastIndexOfDotOrDollar(String string) { + for (int pos = string.length() - 1; pos > -1; pos--) { + char ch = string.charAt(pos); + if (ch == '.' || ch == '$') { + return pos; + } + } + return -1; + } + + private boolean innerMatchesExactly(String s, boolean isAnonymous, boolean convertDollar /* isNested */) { + + List<char[]> ret = new ArrayList<char[]>(); + int startIndex = 0; + while (true) { + int breakIndex = s.indexOf('.', startIndex); // what about / + if (convertDollar && (breakIndex == -1)) { + breakIndex = s.indexOf('$', startIndex); // we treat $ like . here + } + if (breakIndex == -1) { + break; + } + char[] name = s.substring(startIndex, breakIndex).toCharArray(); + ret.add(name); + startIndex = breakIndex + 1; + } + ret.add(s.substring(startIndex).toCharArray()); + + int namesLength = ret.size(); + int patternsLength = namePatterns.length; + + int namesIndex = 0; + int patternsIndex = 0; + + if ((!namePatterns[patternsLength - 1].isAny()) && isAnonymous) { + return false; + } + + if (ellipsisCount == 0) { + if (namesLength != patternsLength) { + return false; + } + while (patternsIndex < patternsLength) { + if (!namePatterns[patternsIndex++].matches(ret.get(namesIndex++))) { + return false; + } + } + return true; + } else if (ellipsisCount == 1) { + if (namesLength < patternsLength - 1) { + return false; + } + while (patternsIndex < patternsLength) { + NamePattern p = namePatterns[patternsIndex++]; + if (p == NamePattern.ELLIPSIS) { + namesIndex = namesLength - (patternsLength - patternsIndex); + } else { + if (!p.matches(ret.get(namesIndex++))) { + return false; + } + } + } + return true; + } else { + // System.err.print("match(\"" + Arrays.asList(namePatterns) + "\", \"" + Arrays.asList(names) + "\") -> "); + boolean b = outOfStar(namePatterns, ret.toArray(new char[ret.size()][]), 0, 0, patternsLength - ellipsisCount, + namesLength, ellipsisCount); + // System.err.println(b); + return b; + } + } + + private static boolean outOfStar(final NamePattern[] pattern, final char[][] target, int pi, int ti, int pLeft, int tLeft, + final int starsLeft) { + if (pLeft > tLeft) { + return false; + } + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length && pi < pattern.length) + if (tLeft == 0) { + return true; + } + if (pLeft == 0) { + return (starsLeft > 0); + } + if (pattern[pi] == NamePattern.ELLIPSIS) { + return inStar(pattern, target, pi + 1, ti, pLeft, tLeft, starsLeft - 1); + } + if (!pattern[pi].matches(target[ti])) { + return false; + } + pi++; + ti++; + pLeft--; + tLeft--; + } + } + + private static boolean inStar(final NamePattern[] pattern, final char[][] target, int pi, int ti, final int pLeft, int tLeft, + int starsLeft) { + // invariant: pLeft > 0, so we know we'll run out of stars and find a real char in pattern + // of course, we probably can't parse multiple ..'s in a row, but this keeps the algorithm + // exactly parallel with that in NamePattern + NamePattern patternChar = pattern[pi]; + while (patternChar == NamePattern.ELLIPSIS) { + starsLeft--; + patternChar = pattern[++pi]; + } + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length) + if (pLeft > tLeft) { + return false; + } + if (patternChar.matches(target[ti])) { + if (outOfStar(pattern, target, pi + 1, ti + 1, pLeft - 1, tLeft - 1, starsLeft)) { + return true; + } + } + ti++; + tLeft--; + } + } + + /** + * @see org.aspectj.weaver.TypePattern#matchesInstanceof(IType) + */ + @Override + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + // XXX hack to let unmatched types just silently remain so + if (maybeGetSimpleName() != null) { + return FuzzyBoolean.NO; + } + + type.getWorld().getMessageHandler().handleMessage( + new Message("can't do instanceof matching on patterns with wildcards", IMessage.ERROR, null, getSourceLocation())); + return FuzzyBoolean.NO; + } + + public NamePattern extractName() { + if (isIncludeSubtypes() || isVarArgs() || isArray() || (typeParameters.size() > 0)) { + // we can't extract a name, the pattern is something like Foo+ and therefore + // it is not ok to treat Foo as a method name! + return null; + } + // System.err.println("extract from : " + Arrays.asList(namePatterns)); + int len = namePatterns.length; + if (len == 1 && !annotationPattern.isAny()) { + return null; // can't extract + } + NamePattern ret = namePatterns[len - 1]; + NamePattern[] newNames = new NamePattern[len - 1]; + System.arraycopy(namePatterns, 0, newNames, 0, len - 1); + namePatterns = newNames; + // System.err.println(" left : " + Arrays.asList(namePatterns)); + return ret; + } + + /** + * Method maybeExtractName. + * + * @param string + * @return boolean + */ + public boolean maybeExtractName(String string) { + int len = namePatterns.length; + NamePattern ret = namePatterns[len - 1]; + String simple = ret.maybeGetSimpleName(); + if (simple != null && simple.equals(string)) { + extractName(); + return true; + } + return false; + } + + /** + * If this type pattern has no '.' or '*' in it, then return a simple string + * + * otherwise, this will return null; + */ + public String maybeGetSimpleName() { + if (namePatterns.length == 1) { + return namePatterns[0].maybeGetSimpleName(); + } + return null; + } + + /** + * If this type pattern has no '*' or '..' in it + */ + public String maybeGetCleanName() { + if (namePatterns.length == 0) { + throw new RuntimeException("bad name: " + namePatterns); + } + // System.out.println("get clean: " + this); + StringBuffer buf = new StringBuffer(); + for (int i = 0, len = namePatterns.length; i < len; i++) { + NamePattern p = namePatterns[i]; + String simpleName = p.maybeGetSimpleName(); + if (simpleName == null) { + return null; + } + if (i > 0) { + buf.append("."); + } + buf.append(simpleName); + } + // System.out.println(buf); + return buf.toString(); + } + + @Override + public TypePattern parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + NamePattern[] newNamePatterns = new NamePattern[namePatterns.length]; + for (int i = 0; i < namePatterns.length; i++) { + newNamePatterns[i] = namePatterns[i]; + } + if (newNamePatterns.length == 1) { + String simpleName = newNamePatterns[0].maybeGetSimpleName(); + if (simpleName != null) { + if (typeVariableMap.containsKey(simpleName)) { + String newName = ((ReferenceType) typeVariableMap.get(simpleName)).getName().replace('$', '.'); + StringTokenizer strTok = new StringTokenizer(newName, "."); + newNamePatterns = new NamePattern[strTok.countTokens()]; + int index = 0; + while (strTok.hasMoreTokens()) { + newNamePatterns[index++] = new NamePattern(strTok.nextToken()); + } + } + } + } + WildTypePattern ret = new WildTypePattern(newNamePatterns, includeSubtypes, dim, isVarArgs, typeParameters + .parameterizeWith(typeVariableMap, w)); + ret.annotationPattern = this.annotationPattern.parameterizeWith(typeVariableMap, w); + if (additionalInterfaceBounds == null) { + ret.additionalInterfaceBounds = null; + } else { + ret.additionalInterfaceBounds = new TypePattern[additionalInterfaceBounds.length]; + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + ret.additionalInterfaceBounds[i] = additionalInterfaceBounds[i].parameterizeWith(typeVariableMap, w); + } + } + ret.upperBound = upperBound != null ? upperBound.parameterizeWith(typeVariableMap, w) : null; + ret.lowerBound = lowerBound != null ? lowerBound.parameterizeWith(typeVariableMap, w) : null; + ret.isGeneric = isGeneric; + ret.knownMatches = knownMatches; + ret.importedPrefixes = importedPrefixes; + ret.copyLocationFrom(this); + return ret; + } + + /** + * Need to determine if I'm really a pattern or a reference to a formal + * + * We may wish to further optimize the case of pattern vs. non-pattern + * + * We will be replaced by what we return + */ + @Override + public TypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding, boolean requireExactType) { + if (isNamePatternStar()) { + TypePattern anyPattern = maybeResolveToAnyPattern(scope, bindings, allowBinding, requireExactType); + if (anyPattern != null) { + if (requireExactType) { + scope.getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.WILDCARD_NOT_ALLOWED), getSourceLocation())); + return NO; + } else { + return anyPattern; + } + } + } + + TypePattern bindingTypePattern = maybeResolveToBindingTypePattern(scope, bindings, allowBinding, requireExactType); + if (bindingTypePattern != null) { + return bindingTypePattern; + } + + annotationPattern = annotationPattern.resolveBindings(scope, bindings, allowBinding); + + // resolve any type parameters + if (typeParameters != null && typeParameters.size() > 0) { + typeParameters.resolveBindings(scope, bindings, allowBinding, requireExactType); + isGeneric = false; + } + + // resolve any bounds + if (upperBound != null) { + upperBound = upperBound.resolveBindings(scope, bindings, allowBinding, requireExactType); + } + if (lowerBound != null) { + lowerBound = lowerBound.resolveBindings(scope, bindings, allowBinding, requireExactType); + // amc - additional interface bounds only needed if we support type vars again. + } + + String fullyQualifiedName = maybeGetCleanName(); + if (fullyQualifiedName != null) { + return resolveBindingsFromFullyQualifiedTypeName(fullyQualifiedName, scope, bindings, allowBinding, requireExactType); + } else { + if (requireExactType) { + scope.getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.WILDCARD_NOT_ALLOWED), getSourceLocation())); + return NO; + } + importedPrefixes = scope.getImportedPrefixes(); + knownMatches = preMatch(scope.getImportedNames()); + return this; // pattern contains wildcards so can't be resolved to an ExactTypePattern... + // XXX need to implement behavior for Lint.invalidWildcardTypeName + } + } + + private TypePattern maybeResolveToAnyPattern(IScope scope, Bindings bindings, boolean allowBinding, boolean requireExactType) { + // If there is an annotation specified we have to + // use a special variant of Any TypePattern called + // AnyWithAnnotation + if (annotationPattern == AnnotationTypePattern.ANY) { + if (dim == 0 && !isVarArgs && upperBound == null && lowerBound == null + && (additionalInterfaceBounds == null || additionalInterfaceBounds.length == 0)) { // pr72531 + return TypePattern.ANY; // ??? loses source location + } + } else if (!isVarArgs) { + annotationPattern = annotationPattern.resolveBindings(scope, bindings, allowBinding); + AnyWithAnnotationTypePattern ret = new AnyWithAnnotationTypePattern(annotationPattern); + ret.setLocation(sourceContext, start, end); + return ret; + } + return null; // can't resolve to a simple "any" pattern + } + + private TypePattern maybeResolveToBindingTypePattern(IScope scope, Bindings bindings, boolean allowBinding, + boolean requireExactType) { + String simpleName = maybeGetSimpleName(); + if (simpleName != null) { + FormalBinding formalBinding = scope.lookupFormal(simpleName); + if (formalBinding != null) { + if (bindings == null) { + scope.message(IMessage.ERROR, this, "negation doesn't allow binding"); + return this; + } + if (!allowBinding) { + scope.message(IMessage.ERROR, this, "name binding only allowed in target, this, and args pcds"); + return this; + } + + BindingTypePattern binding = new BindingTypePattern(formalBinding, isVarArgs); + binding.copyLocationFrom(this); + bindings.register(binding, scope); + + return binding; + } + } + return null; // not possible to resolve to a binding type pattern + } + + private TypePattern resolveBindingsFromFullyQualifiedTypeName(String fullyQualifiedName, IScope scope, Bindings bindings, + boolean allowBinding, boolean requireExactType) { + String originalName = fullyQualifiedName; + ResolvedType resolvedTypeInTheWorld = null; + UnresolvedType type; + + // System.out.println("resolve: " + cleanName); + // ??? this loop has too many inefficiencies to count + resolvedTypeInTheWorld = lookupTypeInWorldIncludingPrefixes(scope.getWorld(), fullyQualifiedName, scope + .getImportedPrefixes()); + + if (resolvedTypeInTheWorld.isGenericWildcard()) { + type = resolvedTypeInTheWorld; + } else { + type = lookupTypeInScope(scope, fullyQualifiedName, this); + } + if ((type instanceof ResolvedType) && ((ResolvedType) type).isMissing()) { + return resolveBindingsForMissingType(resolvedTypeInTheWorld, originalName, scope, bindings, allowBinding, + requireExactType); + } else { + return resolveBindingsForExactType(scope, type, fullyQualifiedName, requireExactType); + } + } + + private UnresolvedType lookupTypeInScope(IScope scope, String typeName, IHasPosition location) { + UnresolvedType type = null; + while (ResolvedType.isMissing(type = scope.lookupType(typeName, location))) { + int lastDot = typeName.lastIndexOf('.'); + if (lastDot == -1) { + break; + } + typeName = typeName.substring(0, lastDot) + '$' + typeName.substring(lastDot + 1); + } + return type; + } + + /** + * Searches the world for the ResolvedType with the given typeName. If one isn't found then for each of the supplied prefixes, + * it prepends the typeName with the prefix and searches the world for the ResolvedType with this new name. If one still isn't + * found then a MissingResolvedTypeWithKnownSignature is returned with the originally requested typeName (this ensures the + * typeName makes sense). + */ + private ResolvedType lookupTypeInWorldIncludingPrefixes(World world, String typeName, String[] prefixes) { + ResolvedType ret = lookupTypeInWorld(world, typeName); + if (!ret.isMissing()) { + return ret; + } + ResolvedType retWithPrefix = ret; + int counter = 0; + while (retWithPrefix.isMissing() && (counter < prefixes.length)) { + retWithPrefix = lookupTypeInWorld(world, prefixes[counter] + typeName); + counter++; + } + if (!retWithPrefix.isMissing()) { + return retWithPrefix; + } + return ret; + } + + private ResolvedType lookupTypeInWorld(World world, String typeName) { + UnresolvedType ut = UnresolvedType.forName(typeName); + ResolvedType ret = world.resolve(ut, true); + while (ret.isMissing()) { + int lastDot = typeName.lastIndexOf('.'); + if (lastDot == -1) { + break; + } + typeName = typeName.substring(0, lastDot) + '$' + typeName.substring(lastDot + 1); + ret = world.resolve(UnresolvedType.forName(typeName), true); + } + return ret; + } + + private TypePattern resolveBindingsForExactType(IScope scope, UnresolvedType aType, String fullyQualifiedName, + boolean requireExactType) { + TypePattern ret = null; + if (aType.isTypeVariableReference()) { + // we have to set the bounds on it based on the bounds of this pattern + ret = resolveBindingsForTypeVariable(scope, (UnresolvedTypeVariableReferenceType) aType); + } else if (typeParameters.size() > 0) { + ret = resolveParameterizedType(scope, aType, requireExactType); + } else if (upperBound != null || lowerBound != null) { + // this must be a generic wildcard with bounds + ret = resolveGenericWildcard(scope, aType); + } else { + if (dim != 0) { + aType = UnresolvedType.makeArray(aType, dim); + } + ret = new ExactTypePattern(aType, includeSubtypes, isVarArgs); + } + ret.setAnnotationTypePattern(annotationPattern); + ret.copyLocationFrom(this); + return ret; + } + + private TypePattern resolveGenericWildcard(IScope scope, UnresolvedType aType) { + if (!aType.getSignature().equals(GENERIC_WILDCARD_SIGNATURE_CHARACTER)) { + throw new IllegalStateException("Can only have bounds for a generic wildcard"); + } + boolean canBeExact = true; + if ((upperBound != null) && ResolvedType.isMissing(upperBound.getExactType())) { + canBeExact = false; + } + if ((lowerBound != null) && ResolvedType.isMissing(lowerBound.getExactType())) { + canBeExact = false; + } + if (canBeExact) { + ResolvedType type = null; + if (upperBound != null) { + if (upperBound.isIncludeSubtypes()) { + canBeExact = false; + } else { + ReferenceType upper = (ReferenceType) upperBound.getExactType().resolve(scope.getWorld()); + type = new BoundedReferenceType(upper, true, scope.getWorld()); + } + } else { + if (lowerBound.isIncludeSubtypes()) { + canBeExact = false; + } else { + ReferenceType lower = (ReferenceType) lowerBound.getExactType().resolve(scope.getWorld()); + type = new BoundedReferenceType(lower, false, scope.getWorld()); + } + } + if (canBeExact) { + // might have changed if we find out include subtypes is set on one of the bounds... + return new ExactTypePattern(type, includeSubtypes, isVarArgs); + } + } + + // we weren't able to resolve to an exact type pattern... + // leave as wild type pattern + importedPrefixes = scope.getImportedPrefixes(); + knownMatches = preMatch(scope.getImportedNames()); + return this; + } + + private TypePattern resolveParameterizedType(IScope scope, UnresolvedType aType, boolean requireExactType) { + ResolvedType rt = aType.resolve(scope.getWorld()); + if (!verifyTypeParameters(rt, scope, requireExactType)) { + return TypePattern.NO; // messages already isued + } + // Only if the type is exact *and* the type parameters are exact should we create an + // ExactTypePattern for this WildTypePattern + if (typeParameters.areAllExactWithNoSubtypesAllowed()) { + TypePattern[] typePats = typeParameters.getTypePatterns(); + UnresolvedType[] typeParameterTypes = new UnresolvedType[typePats.length]; + for (int i = 0; i < typeParameterTypes.length; i++) { + typeParameterTypes[i] = ((ExactTypePattern) typePats[i]).getExactType(); + } + // rt could be a parameterized type 156058 + if (rt.isParameterizedType()) { + rt = rt.getGenericType(); + } + ResolvedType type = TypeFactory.createParameterizedType(rt, typeParameterTypes, scope.getWorld()); + if (isGeneric) { + type = type.getGenericType(); + } + // UnresolvedType tx = UnresolvedType.forParameterizedTypes(aType,typeParameterTypes); + // UnresolvedType type = scope.getWorld().resolve(tx,true); + if (dim != 0) { + type = ResolvedType.makeArray(type, dim); + } + return new ExactTypePattern(type, includeSubtypes, isVarArgs); + } else { + // AMC... just leave it as a wild type pattern then? + importedPrefixes = scope.getImportedPrefixes(); + knownMatches = preMatch(scope.getImportedNames()); + return this; + } + } + + private TypePattern resolveBindingsForMissingType(ResolvedType typeFoundInWholeWorldSearch, String nameWeLookedFor, + IScope scope, Bindings bindings, boolean allowBinding, boolean requireExactType) { + if (requireExactType) { + if (!allowBinding) { + scope.getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.CANT_BIND_TYPE, nameWeLookedFor), + getSourceLocation())); + } else if (scope.getWorld().getLint().invalidAbsoluteTypeName.isEnabled()) { + scope.getWorld().getLint().invalidAbsoluteTypeName.signal(nameWeLookedFor, getSourceLocation()); + } + return NO; + } else if (scope.getWorld().getLint().invalidAbsoluteTypeName.isEnabled()) { + // Only put the lint warning out if we can't find it in the world + if (typeFoundInWholeWorldSearch.isMissing()) { + scope.getWorld().getLint().invalidAbsoluteTypeName.signal(nameWeLookedFor, getSourceLocation()); + this.failedResolution = true; + } + } + importedPrefixes = scope.getImportedPrefixes(); + knownMatches = preMatch(scope.getImportedNames()); + return this; + } + + /** + * We resolved the type to a type variable declared in the pointcut designator. Now we have to create either an exact type + * pattern or a wild type pattern for it, with upper and lower bounds set accordingly. XXX none of this stuff gets serialized + * yet + * + * @param scope + * @param tvrType + * @return + */ + private TypePattern resolveBindingsForTypeVariable(IScope scope, UnresolvedTypeVariableReferenceType tvrType) { + Bindings emptyBindings = new Bindings(0); + if (upperBound != null) { + upperBound = upperBound.resolveBindings(scope, emptyBindings, false, false); + } + if (lowerBound != null) { + lowerBound = lowerBound.resolveBindings(scope, emptyBindings, false, false); + } + if (additionalInterfaceBounds != null) { + TypePattern[] resolvedIfBounds = new TypePattern[additionalInterfaceBounds.length]; + for (int i = 0; i < resolvedIfBounds.length; i++) { + resolvedIfBounds[i] = additionalInterfaceBounds[i].resolveBindings(scope, emptyBindings, false, false); + } + additionalInterfaceBounds = resolvedIfBounds; + } + if (upperBound == null && lowerBound == null && additionalInterfaceBounds == null) { + // no bounds to worry about... + ResolvedType rType = tvrType.resolve(scope.getWorld()); + if (dim != 0) { + rType = ResolvedType.makeArray(rType, dim); + } + return new ExactTypePattern(rType, includeSubtypes, isVarArgs); + } else { + // we have to set bounds on the TypeVariable held by tvrType before resolving it + boolean canCreateExactTypePattern = true; + if (upperBound != null && ResolvedType.isMissing(upperBound.getExactType())) { + canCreateExactTypePattern = false; + } + if (lowerBound != null && ResolvedType.isMissing(lowerBound.getExactType())) { + canCreateExactTypePattern = false; + } + if (additionalInterfaceBounds != null) { + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + if (ResolvedType.isMissing(additionalInterfaceBounds[i].getExactType())) { + canCreateExactTypePattern = false; + } + } + } + if (canCreateExactTypePattern) { + TypeVariable tv = tvrType.getTypeVariable(); + if (upperBound != null) { + tv.setSuperclass(upperBound.getExactType()); + } + if (additionalInterfaceBounds != null) { + UnresolvedType[] ifBounds = new UnresolvedType[additionalInterfaceBounds.length]; + for (int i = 0; i < ifBounds.length; i++) { + ifBounds[i] = additionalInterfaceBounds[i].getExactType(); + } + tv.setAdditionalInterfaceBounds(ifBounds); + } + ResolvedType rType = tvrType.resolve(scope.getWorld()); + if (dim != 0) { + rType = ResolvedType.makeArray(rType, dim); + } + return new ExactTypePattern(rType, includeSubtypes, isVarArgs); + } + return this; // leave as wild type pattern then + } + } + + /** + * When this method is called, we have resolved the base type to an exact type. We also have a set of type patterns for the + * parameters. Time to perform some basic checks: - can the base type be parameterized? (is it generic) - can the type parameter + * pattern list match the number of parameters on the base type - do all parameter patterns meet the bounds of the respective + * type variables If any of these checks fail, a warning message is issued and we return false. + * + * @return + */ + private boolean verifyTypeParameters(ResolvedType baseType, IScope scope, boolean requireExactType) { + ResolvedType genericType = baseType.getGenericType(); + if (genericType == null) { + // issue message "does not match because baseType.getName() is not generic" + scope.message(MessageUtil.warn(WeaverMessages.format(WeaverMessages.NOT_A_GENERIC_TYPE, baseType.getName()), + getSourceLocation())); + return false; + } + int minRequiredTypeParameters = typeParameters.size(); + boolean foundEllipsis = false; + TypePattern[] typeParamPatterns = typeParameters.getTypePatterns(); + for (int i = 0; i < typeParamPatterns.length; i++) { + if (typeParamPatterns[i] instanceof WildTypePattern) { + WildTypePattern wtp = (WildTypePattern) typeParamPatterns[i]; + if (wtp.ellipsisCount > 0) { + foundEllipsis = true; + minRequiredTypeParameters--; + } + } + } + TypeVariable[] tvs = genericType.getTypeVariables(); + if ((tvs.length < minRequiredTypeParameters) || (!foundEllipsis && minRequiredTypeParameters != tvs.length)) { + // issue message "does not match because wrong no of type params" + String msg = WeaverMessages.format(WeaverMessages.INCORRECT_NUMBER_OF_TYPE_ARGUMENTS, genericType.getName(), + new Integer(tvs.length)); + if (requireExactType) { + scope.message(MessageUtil.error(msg, getSourceLocation())); + } else { + scope.message(MessageUtil.warn(msg, getSourceLocation())); + } + return false; + } + + // now check that each typeParameter pattern, if exact, matches the bounds + // of the type variable. + + // pr133307 - delay verification until type binding completion, these next few lines replace + // the call to checkBoundsOK + if (!boundscheckingoff) { + VerifyBoundsForTypePattern verification = new VerifyBoundsForTypePattern(scope, genericType, requireExactType, + typeParameters, getSourceLocation()); + scope.getWorld().getCrosscuttingMembersSet().recordNecessaryCheck(verification); + } + // return checkBoundsOK(scope,genericType,requireExactType); + + return true; + } + + /** + * By capturing the verification in this class, rather than performing it in verifyTypeParameters(), we can cope with situations + * where the interactions between generics and declare parents would otherwise cause us problems. For example, if verifying as + * we go along we may report a problem which would have been fixed by a declare parents that we haven't looked at yet. If we + * create and store a verification object, we can verify this later when the type system is considered 'complete' + */ + static class VerifyBoundsForTypePattern implements IVerificationRequired { + + private final IScope scope; + private final ResolvedType genericType; + private final boolean requireExactType; + private TypePatternList typeParameters = TypePatternList.EMPTY; + private final ISourceLocation sLoc; + + public VerifyBoundsForTypePattern(IScope scope, ResolvedType genericType, boolean requireExactType, + TypePatternList typeParameters, ISourceLocation sLoc) { + this.scope = scope; + this.genericType = genericType; + this.requireExactType = requireExactType; + this.typeParameters = typeParameters; + this.sLoc = sLoc; + } + + public void verify() { + TypeVariable[] tvs = genericType.getTypeVariables(); + TypePattern[] typeParamPatterns = typeParameters.getTypePatterns(); + if (typeParameters.areAllExactWithNoSubtypesAllowed()) { + for (int i = 0; i < tvs.length; i++) { + UnresolvedType ut = typeParamPatterns[i].getExactType(); + boolean continueCheck = true; + // FIXME asc dont like this but ok temporary measure. If the type parameter + // is itself a type variable (from the generic aspect) then assume it'll be + // ok... (see pr112105) Want to break this? Run GenericAspectK test. + if (ut.isTypeVariableReference()) { + continueCheck = false; + } + + // System.err.println("Verifying "+ut.getName()+" meets bounds for "+tvs[i]); + if (continueCheck && !tvs[i].canBeBoundTo(ut.resolve(scope.getWorld()))) { + // issue message that type parameter does not meet specification + String parameterName = ut.getName(); + if (ut.isTypeVariableReference()) { + parameterName = ((TypeVariableReference) ut).getTypeVariable().getDisplayName(); + } + String msg = WeaverMessages.format(WeaverMessages.VIOLATES_TYPE_VARIABLE_BOUNDS, parameterName, + new Integer(i + 1), tvs[i].getDisplayName(), genericType.getName()); + if (requireExactType) { + scope.message(MessageUtil.error(msg, sLoc)); + } else { + scope.message(MessageUtil.warn(msg, sLoc)); + } + } + } + } + } + } + + // pr133307 - moved to verification object + // public boolean checkBoundsOK(IScope scope,ResolvedType genericType,boolean requireExactType) { + // if (boundscheckingoff) return true; + // TypeVariable[] tvs = genericType.getTypeVariables(); + // TypePattern[] typeParamPatterns = typeParameters.getTypePatterns(); + // if (typeParameters.areAllExactWithNoSubtypesAllowed()) { + // for (int i = 0; i < tvs.length; i++) { + // UnresolvedType ut = typeParamPatterns[i].getExactType(); + // boolean continueCheck = true; + // // FIXME asc dont like this but ok temporary measure. If the type parameter + // // is itself a type variable (from the generic aspect) then assume it'll be + // // ok... (see pr112105) Want to break this? Run GenericAspectK test. + // if (ut.isTypeVariableReference()) { + // continueCheck = false; + // } + // + // if (continueCheck && + // !tvs[i].canBeBoundTo(ut.resolve(scope.getWorld()))) { + // // issue message that type parameter does not meet specification + // String parameterName = ut.getName(); + // if (ut.isTypeVariableReference()) parameterName = ((TypeVariableReference)ut).getTypeVariable().getDisplayName(); + // String msg = + // WeaverMessages.format( + // WeaverMessages.VIOLATES_TYPE_VARIABLE_BOUNDS, + // parameterName, + // new Integer(i+1), + // tvs[i].getDisplayName(), + // genericType.getName()); + // if (requireExactType) scope.message(MessageUtil.error(msg,getSourceLocation())); + // else scope.message(MessageUtil.warn(msg,getSourceLocation())); + // return false; + // } + // } + // } + // return true; + // } + + @Override + public boolean isStar() { + boolean annPatternStar = annotationPattern == AnnotationTypePattern.ANY; + return (isNamePatternStar() && annPatternStar && dim == 0); + } + + private boolean isNamePatternStar() { + return namePatterns.length == 1 && namePatterns[0].isAny(); + } + + /** + * @return those possible matches which I match exactly the last element of + */ + private String[] preMatch(String[] possibleMatches) { + // if (namePatterns.length != 1) return CollectionUtil.NO_STRINGS; + + List<String> ret = new ArrayList<String>(); + for (int i = 0, len = possibleMatches.length; i < len; i++) { + char[][] names = splitNames(possibleMatches[i], true); // ??? not most efficient + if (namePatterns[0].matches(names[names.length - 1])) { + ret.add(possibleMatches[i]); + continue; + } + if (possibleMatches[i].indexOf("$") != -1) { + names = splitNames(possibleMatches[i], false); // ??? not most efficient + if (namePatterns[0].matches(names[names.length - 1])) { + ret.add(possibleMatches[i]); + } + } + } + return ret.toArray(new String[ret.size()]); + } + + // public void postRead(ResolvedType enclosingType) { + // this.importedPrefixes = enclosingType.getImportedPrefixes(); + // this.knownNames = prematch(enclosingType.getImportedNames()); + // } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + if (annotationPattern != AnnotationTypePattern.ANY) { + buf.append('('); + buf.append(annotationPattern.toString()); + buf.append(' '); + } + for (int i = 0, len = namePatterns.length; i < len; i++) { + NamePattern name = namePatterns[i]; + if (name == null) { + buf.append("."); + } else { + if (i > 0) { + buf.append("."); + } + buf.append(name.toString()); + } + } + if (upperBound != null) { + buf.append(" extends "); + buf.append(upperBound.toString()); + } + if (lowerBound != null) { + buf.append(" super "); + buf.append(lowerBound.toString()); + } + if (typeParameters != null && typeParameters.size() != 0) { + buf.append("<"); + buf.append(typeParameters.toString()); + buf.append(">"); + } + if (includeSubtypes) { + buf.append('+'); + } + if (isVarArgs) { + buf.append("..."); + } + if (annotationPattern != AnnotationTypePattern.ANY) { + buf.append(')'); + } + return buf.toString(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof WildTypePattern)) { + return false; + } + WildTypePattern o = (WildTypePattern) other; + int len = o.namePatterns.length; + if (len != this.namePatterns.length) { + return false; + } + if (this.includeSubtypes != o.includeSubtypes) { + return false; + } + if (this.dim != o.dim) { + return false; + } + if (this.isVarArgs != o.isVarArgs) { + return false; + } + if (this.upperBound != null) { + if (o.upperBound == null) { + return false; + } + if (!this.upperBound.equals(o.upperBound)) { + return false; + } + } else { + if (o.upperBound != null) { + return false; + } + } + if (this.lowerBound != null) { + if (o.lowerBound == null) { + return false; + } + if (!this.lowerBound.equals(o.lowerBound)) { + return false; + } + } else { + if (o.lowerBound != null) { + return false; + } + } + if (!typeParameters.equals(o.typeParameters)) { + return false; + } + for (int i = 0; i < len; i++) { + if (!o.namePatterns[i].equals(this.namePatterns[i])) { + return false; + } + } + return (o.annotationPattern.equals(this.annotationPattern)); + } + + @Override + public int hashCode() { + int result = 17; + for (int i = 0, len = namePatterns.length; i < len; i++) { + result = 37 * result + namePatterns[i].hashCode(); + } + result = 37 * result + annotationPattern.hashCode(); + if (upperBound != null) { + result = 37 * result + upperBound.hashCode(); + } + if (lowerBound != null) { + result = 37 * result + lowerBound.hashCode(); + } + return result; + } + + private static final byte VERSION = 1; // rev on change + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(TypePattern.WILD); + s.writeByte(VERSION); + s.writeShort(namePatterns.length); + for (int i = 0; i < namePatterns.length; i++) { + namePatterns[i].write(s); + } + s.writeBoolean(includeSubtypes); + s.writeInt(dim); + s.writeBoolean(isVarArgs); + typeParameters.write(s); // ! change from M2 + // ??? storing this information with every type pattern is wasteful of .class + // file size. Storing it on enclosing types would be more efficient + FileUtil.writeStringArray(knownMatches, s); + FileUtil.writeStringArray(importedPrefixes, s); + writeLocation(s); + annotationPattern.write(s); + // generics info, new in M3 + s.writeBoolean(isGeneric); + s.writeBoolean(upperBound != null); + if (upperBound != null) { + upperBound.write(s); + } + s.writeBoolean(lowerBound != null); + if (lowerBound != null) { + lowerBound.write(s); + } + s.writeInt(additionalInterfaceBounds == null ? 0 : additionalInterfaceBounds.length); + if (additionalInterfaceBounds != null) { + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + additionalInterfaceBounds[i].write(s); + } + } + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + return readTypePattern150(s, context); + } else { + return readTypePatternOldStyle(s, context); + } + } + + public static TypePattern readTypePattern150(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte version = s.readByte(); + if (version > VERSION) { + throw new BCException("WildTypePattern was written by a more recent version of AspectJ, cannot read"); + } + int len = s.readShort(); + NamePattern[] namePatterns = new NamePattern[len]; + for (int i = 0; i < len; i++) { + namePatterns[i] = NamePattern.read(s); + } + boolean includeSubtypes = s.readBoolean(); + int dim = s.readInt(); + boolean varArg = s.readBoolean(); + TypePatternList typeParams = TypePatternList.read(s, context); + WildTypePattern ret = new WildTypePattern(namePatterns, includeSubtypes, dim, varArg, typeParams); + ret.knownMatches = FileUtil.readStringArray(s); + ret.importedPrefixes = FileUtil.readStringArray(s); + ret.readLocation(context, s); + ret.setAnnotationTypePattern(AnnotationTypePattern.read(s, context)); + // generics info, new in M3 + ret.isGeneric = s.readBoolean(); + if (s.readBoolean()) { + ret.upperBound = TypePattern.read(s, context); + } + if (s.readBoolean()) { + ret.lowerBound = TypePattern.read(s, context); + } + int numIfBounds = s.readInt(); + if (numIfBounds > 0) { + ret.additionalInterfaceBounds = new TypePattern[numIfBounds]; + for (int i = 0; i < numIfBounds; i++) { + ret.additionalInterfaceBounds[i] = TypePattern.read(s, context); + } + } + return ret; + } + + public static TypePattern readTypePatternOldStyle(VersionedDataInputStream s, ISourceContext context) throws IOException { + int len = s.readShort(); + NamePattern[] namePatterns = new NamePattern[len]; + for (int i = 0; i < len; i++) { + namePatterns[i] = NamePattern.read(s); + } + boolean includeSubtypes = s.readBoolean(); + int dim = s.readInt(); + WildTypePattern ret = new WildTypePattern(namePatterns, includeSubtypes, dim, false, null); + ret.knownMatches = FileUtil.readStringArray(s); + ret.importedPrefixes = FileUtil.readStringArray(s); + ret.readLocation(context, s); + return ret; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public boolean hasFailedResolution() { + return failedResolution; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WithinAnnotationPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WithinAnnotationPointcut.java new file mode 100644 index 000000000..42ec11dc2 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WithinAnnotationPointcut.java @@ -0,0 +1,241 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; + +/** + * @author colyer + * + * TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style - Code + * Templates + */ +public class WithinAnnotationPointcut extends NameBindingPointcut { + + private AnnotationTypePattern annotationTypePattern; + private String declarationText; + + /** + * + */ + public WithinAnnotationPointcut(AnnotationTypePattern type) { + super(); + this.annotationTypePattern = type; + this.pointcutKind = ATWITHIN; + buildDeclarationText(); + } + + public WithinAnnotationPointcut(AnnotationTypePattern type, ShadowMunger munger) { + this(type); + this.pointcutKind = ATWITHIN; + } + + public AnnotationTypePattern getAnnotationTypePattern() { + return annotationTypePattern; + } + + @Override + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + @Override + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + WithinAnnotationPointcut ret = new WithinAnnotationPointcut(this.annotationTypePattern.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) + */ + @Override + public FuzzyBoolean fastMatch(FastMatchInfo info) { + return annotationTypePattern.fastMatches(info.getType()); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) + */ + @Override + protected FuzzyBoolean matchInternal(Shadow shadow) { + ResolvedType enclosingType = shadow.getIWorld().resolve(shadow.getEnclosingType(), true); + if (enclosingType.isMissing()) { + shadow.getIWorld().getLint().cantFindType.signal(new String[] { WeaverMessages.format( + WeaverMessages.CANT_FIND_TYPE_WITHINPCD, shadow.getEnclosingType().getName()) }, shadow.getSourceLocation(), + new ISourceLocation[] { getSourceLocation() }); + // IMessage msg = new Message( + // WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_WITHINPCD, + // shadow.getEnclosingType().getName()), + // shadow.getSourceLocation(),true,new ISourceLocation[]{getSourceLocation()}); + // shadow.getIWorld().getMessageHandler().handleMessage(msg); + } + annotationTypePattern.resolve(shadow.getIWorld()); + return annotationTypePattern.matches(enclosingType); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(org.aspectj.weaver.patterns.IScope, + * org.aspectj.weaver.patterns.Bindings) + */ + @Override + protected void resolveBindings(IScope scope, Bindings bindings) { + if (!scope.getWorld().isInJava5Mode()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ATWITHIN_ONLY_SUPPORTED_AT_JAVA5_LEVEL), + getSourceLocation())); + return; + } + annotationTypePattern = annotationTypePattern.resolveBindings(scope, bindings, true); + // must be either a Var, or an annotation type pattern + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#concretize1(org.aspectj.weaver.ResolvedType, org.aspectj.weaver.IntMap) + */ + @Override + protected Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + ExactAnnotationTypePattern newType = (ExactAnnotationTypePattern) annotationTypePattern.remapAdviceFormals(bindings); + Pointcut ret = new WithinAnnotationPointcut(newType, bindings.getEnclosingAdvice()); + ret.copyLocationFrom(this); + return ret; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#findResidue(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) + */ + @Override + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern) annotationTypePattern; + UnresolvedType annotationType = btp.annotationType; + Var var = shadow.getWithinAnnotationVar(annotationType); + + // This should not happen, we shouldn't have gotten this far + // if we weren't going to find the annotation + if (var == null) { + throw new BCException("Impossible! annotation=[" + annotationType + "] shadow=[" + shadow + " at " + + shadow.getSourceLocation() + "] pointcut is at [" + getSourceLocation() + "]"); + } + + state.set(btp.getFormalIndex(), var); + } + return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; + } + + @Override + public List<BindingPattern> getBindingAnnotationTypePatterns() { + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + List<BindingPattern> l = new ArrayList<BindingPattern>(); + l.add((BindingPattern)annotationTypePattern); + return l; + } else { + return Collections.emptyList(); + } + } + + @Override + public List<BindingTypePattern> getBindingTypePatterns() { + return Collections.emptyList(); + } + + @Override + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.ATWITHIN); + annotationTypePattern.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern type = AnnotationTypePattern.read(s, context); + WithinAnnotationPointcut ret = new WithinAnnotationPointcut(type); + ret.readLocation(context, s); + return ret; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof WithinAnnotationPointcut)) { + return false; + } + WithinAnnotationPointcut other = (WithinAnnotationPointcut) obj; + return other.annotationTypePattern.equals(this.annotationTypePattern); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return 17 + 19 * annotationTypePattern.hashCode(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + private void buildDeclarationText() { + StringBuffer buf = new StringBuffer(); + buf.append("@within("); + String annPatt = annotationTypePattern.toString(); + buf.append(annPatt.startsWith("@") ? annPatt.substring(1) : annPatt); + buf.append(")"); + this.declarationText = buf.toString(); + } + + @Override + public String toString() { + return this.declarationText; + } + + @Override + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WithinCodeAnnotationPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WithinCodeAnnotationPointcut.java new file mode 100644 index 000000000..408c829f5 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WithinCodeAnnotationPointcut.java @@ -0,0 +1,230 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; + +/** + * @author colyer + * + * TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style - Code + * Templates + */ +public class WithinCodeAnnotationPointcut extends NameBindingPointcut { + + private ExactAnnotationTypePattern annotationTypePattern; + private String declarationText; + + private static final int matchedShadowKinds; + static { + int flags = Shadow.ALL_SHADOW_KINDS_BITS; + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + if (Shadow.SHADOW_KINDS[i].isEnclosingKind()) { + flags -= Shadow.SHADOW_KINDS[i].bit; + } + } + matchedShadowKinds = flags; + } + + public WithinCodeAnnotationPointcut(ExactAnnotationTypePattern type) { + super(); + this.annotationTypePattern = type; + this.pointcutKind = Pointcut.ATWITHINCODE; + buildDeclarationText(); + } + + public WithinCodeAnnotationPointcut(ExactAnnotationTypePattern type, ShadowMunger munger) { + this(type); + this.pointcutKind = Pointcut.ATWITHINCODE; + } + + public ExactAnnotationTypePattern getAnnotationTypePattern() { + return annotationTypePattern; + } + + public int couldMatchKinds() { + return matchedShadowKinds; + } + + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + WithinCodeAnnotationPointcut ret = new WithinCodeAnnotationPointcut((ExactAnnotationTypePattern) this.annotationTypePattern + .parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) + */ + public FuzzyBoolean fastMatch(FastMatchInfo info) { + return FuzzyBoolean.MAYBE; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) + */ + protected FuzzyBoolean matchInternal(Shadow shadow) { + Member member = shadow.getEnclosingCodeSignature(); + ResolvedMember rMember = member.resolve(shadow.getIWorld()); + + if (rMember == null) { + if (member.getName().startsWith(NameMangler.PREFIX)) { + return FuzzyBoolean.NO; + } + shadow.getIWorld().getLint().unresolvableMember.signal(member.toString(), getSourceLocation()); + return FuzzyBoolean.NO; + } + + annotationTypePattern.resolve(shadow.getIWorld()); + return annotationTypePattern.matches(rMember); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(org.aspectj.weaver.patterns.IScope, + * org.aspectj.weaver.patterns.Bindings) + */ + protected void resolveBindings(IScope scope, Bindings bindings) { + if (!scope.getWorld().isInJava5Mode()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ATWITHINCODE_ONLY_SUPPORTED_AT_JAVA5_LEVEL), + getSourceLocation())); + return; + } + annotationTypePattern = (ExactAnnotationTypePattern) annotationTypePattern.resolveBindings(scope, bindings, true); + // must be either a Var, or an annotation type pattern + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#concretize1(org.aspectj.weaver.ResolvedType, org.aspectj.weaver.IntMap) + */ + protected Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + ExactAnnotationTypePattern newType = (ExactAnnotationTypePattern) annotationTypePattern.remapAdviceFormals(bindings); + Pointcut ret = new WithinCodeAnnotationPointcut(newType, bindings.getEnclosingAdvice()); + ret.copyLocationFrom(this); + return ret; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#findResidue(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) + */ + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern) annotationTypePattern; + UnresolvedType annotationType = btp.annotationType; + Var var = shadow.getWithinCodeAnnotationVar(annotationType); + + // This should not happen, we shouldn't have gotten this far + // if we weren't going to find the annotation + if (var == null) { + throw new BCException("Impossible! annotation=[" + annotationType + "] shadow=[" + shadow + " at " + + shadow.getSourceLocation() + "] pointcut is at [" + getSourceLocation() + "]"); + } + + state.set(btp.getFormalIndex(), var); + } + if (matchInternal(shadow).alwaysTrue()) { + return Literal.TRUE; + } else { + return Literal.FALSE; + } + } + + public List<BindingPattern> getBindingAnnotationTypePatterns() { + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + List<BindingPattern> l = new ArrayList<BindingPattern>(); + l.add((BindingPattern)annotationTypePattern); + return l; + } else { + return Collections.emptyList(); + } + } + + public List<BindingTypePattern> getBindingTypePatterns() { + return Collections.emptyList(); + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.ATWITHINCODE); + annotationTypePattern.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern type = AnnotationTypePattern.read(s, context); + WithinCodeAnnotationPointcut ret = new WithinCodeAnnotationPointcut((ExactAnnotationTypePattern) type); + ret.readLocation(context, s); + return ret; + } + + public boolean equals(Object other) { + if (!(other instanceof WithinCodeAnnotationPointcut)) { + return false; + } + WithinCodeAnnotationPointcut o = (WithinCodeAnnotationPointcut) other; + return o.annotationTypePattern.equals(this.annotationTypePattern); + } + + public int hashCode() { + int result = 17; + result = 23 * result + annotationTypePattern.hashCode(); + return result; + } + + private void buildDeclarationText() { + StringBuffer buf = new StringBuffer(); + buf.append("@withincode("); + String annPatt = annotationTypePattern.toString(); + buf.append(annPatt.startsWith("@") ? annPatt.substring(1) : annPatt); + buf.append(")"); + this.declarationText = buf.toString(); + } + + public String toString() { + return this.declarationText; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WithinPointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WithinPointcut.java new file mode 100644 index 000000000..e5461b67e --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WithinPointcut.java @@ -0,0 +1,149 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +public class WithinPointcut extends Pointcut { + private TypePattern typePattern; + + public WithinPointcut(TypePattern type) { + this.typePattern = type; + this.pointcutKind = WITHIN; + } + + public TypePattern getTypePattern() { + return typePattern; + } + + private FuzzyBoolean isWithinType(ResolvedType type) { + while (type != null) { + if (typePattern.matchesStatically(type)) { + return FuzzyBoolean.YES; + } + type = type.getDeclaringType(); + } + return FuzzyBoolean.NO; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + @Override + public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) { + WithinPointcut ret = new WithinPointcut(this.typePattern.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + public FuzzyBoolean fastMatch(FastMatchInfo info) { + if (typePattern.annotationPattern instanceof AnyAnnotationTypePattern) { + return isWithinType(info.getType()); + } + return FuzzyBoolean.MAYBE; + // Possible alternative implementation that fast matches even annotation patterns: '@Foo *' +// typePattern.resolve(info.world); +// return isWithinType(info.getType()); + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + ResolvedType enclosingType = shadow.getIWorld().resolve(shadow.getEnclosingType(), true); + if (enclosingType.isMissing()) { + shadow.getIWorld().getLint().cantFindType.signal(new String[] { WeaverMessages.format( + WeaverMessages.CANT_FIND_TYPE_WITHINPCD, shadow.getEnclosingType().getName()) }, shadow.getSourceLocation(), + new ISourceLocation[] { getSourceLocation() }); + } + typePattern.resolve(shadow.getIWorld()); + return isWithinType(enclosingType); + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.WITHIN); + typePattern.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + TypePattern type = TypePattern.read(s, context); + WithinPointcut ret = new WithinPointcut(type); + ret.readLocation(context, s); + return ret; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + typePattern = typePattern.resolveBindings(scope, bindings, false, false); + + // look for parameterized type patterns which are not supported... + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + typePattern.traverse(visitor, null); + if (visitor.wellHasItThen/* ? */()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.WITHIN_PCD_DOESNT_SUPPORT_PARAMETERS), + getSourceLocation())); + } + } + + public void postRead(ResolvedType enclosingType) { + typePattern.postRead(enclosingType); + } + + public boolean couldEverMatchSameJoinPointsAs(WithinPointcut other) { + return typePattern.couldEverMatchSameTypesAs(other.typePattern); + } + + public boolean equals(Object other) { + if (!(other instanceof WithinPointcut)) { + return false; + } + WithinPointcut o = (WithinPointcut) other; + return o.typePattern.equals(this.typePattern); + } + + public int hashCode() { + return typePattern.hashCode(); + } + + public String toString() { + return "within(" + typePattern + ")"; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + Pointcut ret = new WithinPointcut(typePattern); + ret.copyLocationFrom(this); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WithincodePointcut.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WithincodePointcut.java new file mode 100644 index 000000000..bb4a1e72c --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/patterns/WithincodePointcut.java @@ -0,0 +1,141 @@ +/* ******************************************************************* + * 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 v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +public class WithincodePointcut extends Pointcut { + private SignaturePattern signature; + private static final int matchedShadowKinds; + static { + int flags = Shadow.ALL_SHADOW_KINDS_BITS; + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + if (Shadow.SHADOW_KINDS[i].isEnclosingKind()) { + flags -= Shadow.SHADOW_KINDS[i].bit; + } + } + // these next two are needed for inlining of field initializers + flags |= Shadow.ConstructorExecution.bit; + flags |= Shadow.Initialization.bit; + matchedShadowKinds = flags; + } + + public WithincodePointcut(SignaturePattern signature) { + this.signature = signature; + this.pointcutKind = WITHINCODE; + } + + public SignaturePattern getSignature() { + return signature; + } + + public int couldMatchKinds() { + return matchedShadowKinds; + } + + public Pointcut parameterizeWith(Map typeVariableMap, World w) { + WithincodePointcut ret = new WithincodePointcut(signature.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + // This will not match code in local or anonymous classes as if + // they were withincode of the outer signature + return FuzzyBoolean.fromBoolean(signature.matches(shadow.getEnclosingCodeSignature(), shadow.getIWorld(), false)); + } + + public void write(CompressingDataOutputStream s) throws IOException { + s.writeByte(Pointcut.WITHINCODE); + signature.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + WithincodePointcut ret = new WithincodePointcut(SignaturePattern.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + signature = signature.resolveBindings(scope, bindings); + + // look for inappropriate use of parameterized types and tell user... + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getDeclaringType().traverse(visitor, null); + if (visitor.wellHasItThen/* ? */()) { + scope.message(MessageUtil.error(WeaverMessages + .format(WeaverMessages.WITHINCODE_DOESNT_SUPPORT_PARAMETERIZED_DECLARING_TYPES), getSourceLocation())); + } + + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getThrowsPattern().traverse(visitor, null); + if (visitor.wellHasItThen/* ? */()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.NO_GENERIC_THROWABLES), getSourceLocation())); + } + } + + public void postRead(ResolvedType enclosingType) { + signature.postRead(enclosingType); + } + + public boolean equals(Object other) { + if (!(other instanceof WithincodePointcut)) { + return false; + } + WithincodePointcut o = (WithincodePointcut) other; + return o.signature.equals(this.signature); + } + + public int hashCode() { + int result = 43; + result = 37 * result + signature.hashCode(); + return result; + } + + public String toString() { + return "withincode(" + signature + ")"; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + Pointcut ret = new WithincodePointcut(signature); + ret.copyLocationFrom(this); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/AnnotationFinder.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/AnnotationFinder.java new file mode 100644 index 000000000..90ce368d9 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/AnnotationFinder.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * Copyright (c) 2005, 2017 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://eclipse.org/legal/epl-v10.html + * + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.reflect.Member; + +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +/** + * @author Adrian Colyer + * @author Andy Clement + */ +public interface AnnotationFinder { + + void setClassLoader(ClassLoader annotationLoader); + + void setWorld(World aWorld); + + Object getAnnotation(ResolvedType annotationType, Object onObject); + + Object getAnnotationFromMember(ResolvedType annotationType, Member aMember); + + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType, Member onMember); + + public String getAnnotationDefaultValue(Member onMember); + + Object getAnnotationFromClass(ResolvedType annotationType, Class<?> aClass); + + ResolvedType[] getAnnotations(Member onMember, boolean runtimeAnnotationsOnly); + + ResolvedType[][] getParameterAnnotationTypes(Member onMember); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/GenericSignatureInformationProvider.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/GenericSignatureInformationProvider.java new file mode 100644 index 000000000..fce972348 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/GenericSignatureInformationProvider.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import org.aspectj.weaver.UnresolvedType; + +/** + * This interface exists to support two different strategies for answering + * generic signature related questions on Java 5 and pre-Java 5. + */ +public interface GenericSignatureInformationProvider { + + UnresolvedType[] getGenericParameterTypes(ReflectionBasedResolvedMemberImpl resolvedMember); + + UnresolvedType getGenericReturnType(ReflectionBasedResolvedMemberImpl resolvedMember); + + boolean isBridge(ReflectionBasedResolvedMemberImpl resolvedMember); + + boolean isVarArgs(ReflectionBasedResolvedMemberImpl resolvedMember); + + boolean isSynthetic(ReflectionBasedResolvedMemberImpl resolvedMember); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/IReflectionWorld.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/IReflectionWorld.java new file mode 100644 index 000000000..60d90d357 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/IReflectionWorld.java @@ -0,0 +1,19 @@ +/* ******************************************************************* + * Copyright (c) 2005 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: + * Ron Bodkin initial implementation + * ******************************************************************/ + package org.aspectj.weaver.reflect; + +import org.aspectj.weaver.ResolvedType; + +public interface IReflectionWorld { + public AnnotationFinder getAnnotationFinder(); + public ResolvedType resolve(Class aClass); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/Java14GenericSignatureInformationProvider.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/Java14GenericSignatureInformationProvider.java new file mode 100644 index 000000000..91b32ff0e --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/Java14GenericSignatureInformationProvider.java @@ -0,0 +1,60 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import org.aspectj.weaver.UnresolvedType; + +/** + * Under JDK 1.4 or lower, we can't give generic signature info... + */ +public class Java14GenericSignatureInformationProvider implements + GenericSignatureInformationProvider { + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#getGenericParameterTypes(org.aspectj.weaver.reflect.ReflectionBasedResolvedMemberImpl) + */ + public UnresolvedType[] getGenericParameterTypes( + ReflectionBasedResolvedMemberImpl resolvedMember) { + return resolvedMember.getParameterTypes(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#getGenericReturnType(org.aspectj.weaver.reflect.ReflectionBasedResolvedMemberImpl) + */ + public UnresolvedType getGenericReturnType( + ReflectionBasedResolvedMemberImpl resolvedMember) { + return resolvedMember.getReturnType(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#isBridge() + */ + public boolean isBridge(ReflectionBasedResolvedMemberImpl resolvedMember) { + return false; + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#isVarArgs() + */ + public boolean isVarArgs(ReflectionBasedResolvedMemberImpl resolvedMember) { + return false; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#isSynthetic() + */ + public boolean isSynthetic(ReflectionBasedResolvedMemberImpl resolvedMember) { + return false; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/JoinPointMatchImpl.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/JoinPointMatchImpl.java new file mode 100644 index 000000000..69a5f4577 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/JoinPointMatchImpl.java @@ -0,0 +1,53 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import org.aspectj.weaver.tools.JoinPointMatch; +import org.aspectj.weaver.tools.PointcutParameter; + +/** + * @author colyer + * Implementation of JoinPointMatch for reflection based worlds. + */ +public class JoinPointMatchImpl implements JoinPointMatch { + + public final static JoinPointMatch NO_MATCH = new JoinPointMatchImpl(); + private final static PointcutParameter[] NO_BINDINGS = new PointcutParameter[0]; + + private boolean match; + private PointcutParameter[] bindings; + + public JoinPointMatchImpl(PointcutParameter[] bindings) { + this.match = true; + this.bindings = bindings; + } + + private JoinPointMatchImpl() { + this.match = false; + this.bindings = NO_BINDINGS; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.JoinPointMatch#matches() + */ + public boolean matches() { + return match; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.JoinPointMatch#getParameterBindings() + */ + public PointcutParameter[] getParameterBindings() { + return bindings; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/PointcutParameterImpl.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/PointcutParameterImpl.java new file mode 100644 index 000000000..722d64839 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/PointcutParameterImpl.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import org.aspectj.weaver.tools.PointcutParameter; + +public class PointcutParameterImpl implements PointcutParameter { + + String name; + Class type; + Object binding; + + public PointcutParameterImpl(String name, Class type) { + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public Class getType() { + return type; + } + + public Object getBinding() { + return binding; + } + + void setBinding(Object boundValue) { + this.binding = boundValue; + } + +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegate.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegate.java new file mode 100644 index 000000000..2aa83c957 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegate.java @@ -0,0 +1,403 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Collection; +import java.util.Collections; + +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.AnnotationTargetKind; +import org.aspectj.weaver.ConcreteTypeMunger; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ReferenceTypeDelegate; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.SourceContextImpl; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeakClassLoaderReference; +import org.aspectj.weaver.WeaverStateInfo; +import org.aspectj.weaver.World; +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.PerClause; + +/** + * @author colyer A delegate for a resolved type that uses runtime type information (java.lang.reflect) to answer questions. This + * class uses only Java 1.4 features to answer questions. In a Java 1.5 environment use the + * Java5ReflectionBasedReferenceTypeDelegate subtype. + */ +public class ReflectionBasedReferenceTypeDelegate implements ReferenceTypeDelegate { + + private static final ClassLoader bootClassLoader = new URLClassLoader(new URL[0]);// ReflectionBasedReferenceTypeDelegate.class. + // getClassLoader(); + + protected Class myClass = null; + protected WeakClassLoaderReference classLoaderReference = null; + protected World world; + private ReferenceType resolvedType; + private ResolvedMember[] fields = null; + private ResolvedMember[] methods = null; + private ResolvedType[] interfaces = null; + + public ReflectionBasedReferenceTypeDelegate(Class forClass, ClassLoader aClassLoader, World inWorld, ReferenceType resolvedType) { + initialize(resolvedType, forClass, aClassLoader, inWorld); + } + + /** for reflective construction only */ + public ReflectionBasedReferenceTypeDelegate() { + } + + public void initialize(ReferenceType aType, Class<?> aClass, ClassLoader aClassLoader, World aWorld) { + this.myClass = aClass; + this.resolvedType = aType; + this.world = aWorld; + this.classLoaderReference = new WeakClassLoaderReference((aClassLoader != null) ? aClassLoader : bootClassLoader); + } + + public Class<?> getClazz() { + return this.myClass; + } + + protected Class getBaseClass() { + return this.myClass; + } + + protected World getWorld() { + return this.world; + } + + public ReferenceType buildGenericType() { + throw new UnsupportedOperationException("Shouldn't be asking for generic type at 1.4 source level or lower"); + } + + public boolean isAspect() { + // we could do better than this in Java 5 by looking at the annotations + // on the type... + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#isAnnotationStyleAspect() + */ + public boolean isAnnotationStyleAspect() { + // we could do better than this in Java 5 by looking at the annotations + // on the type... + return false; + } + + public boolean isInterface() { + return this.myClass.isInterface(); + } + + public boolean isEnum() { + // cant be an enum in Java 1.4 or prior + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#isAnnotationWithRuntimeRetention () + */ + public boolean isAnnotationWithRuntimeRetention() { + // cant be an annotation in Java 1.4 or prior + return false; + } + + public boolean isAnnotation() { + // cant be an annotation in Java 1.4 or prior + return false; + } + + public String getRetentionPolicy() { + // cant be an annotation in Java 1.4 or prior + return null; + } + + public boolean canAnnotationTargetType() { + return false; + } + + public AnnotationTargetKind[] getAnnotationTargetKinds() { + return null; + } + + public boolean isClass() { + return !this.myClass.isInterface() && !this.myClass.isPrimitive() && !this.myClass.isArray(); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#isGeneric() + */ + public boolean isGeneric() { + // cant be generic in 1.4 + return false; + } + + public boolean isAnonymous() { + // this isn't in < Java 1.5 but I think we are moving beyond the need to support those levels + return this.myClass.isAnonymousClass(); + } + + public boolean isNested() { + // this isn't in < Java 1.5 but I think we are moving beyond the need to support those levels + return this.myClass.isMemberClass(); + } + + public ResolvedType getOuterClass() { + // this isn't in < Java 1.5 but I think we are moving beyond the need to support those levels + return ReflectionBasedReferenceTypeDelegateFactory.resolveTypeInWorld( + myClass.getEnclosingClass(),world); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#isExposedToWeaver() + */ + public boolean isExposedToWeaver() { + // reflection based types are never exposed to the weaver + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#hasAnnotation(org.aspectj.weaver .UnresolvedType) + */ + public boolean hasAnnotation(UnresolvedType ofType) { + // in Java 1.4 we cant have an annotation + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getAnnotations() + */ + public AnnotationAJ[] getAnnotations() { + // no annotations in Java 1.4 + return AnnotationAJ.EMPTY_ARRAY; + } + + public boolean hasAnnotations() { + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getAnnotationTypes() + */ + public ResolvedType[] getAnnotationTypes() { + // no annotations in Java 1.4 + return new ResolvedType[0]; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getDeclaredFields() + */ + public ResolvedMember[] getDeclaredFields() { + if (fields == null) { + Field[] reflectFields = this.myClass.getDeclaredFields(); + ResolvedMember[] rFields = new ResolvedMember[reflectFields.length]; + for (int i = 0; i < reflectFields.length; i++) { + rFields[i] = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(reflectFields[i], world); + } + this.fields = rFields; + } + return fields; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getDeclaredInterfaces() + */ + public ResolvedType[] getDeclaredInterfaces() { + if (interfaces == null) { + Class[] reflectInterfaces = this.myClass.getInterfaces(); + ResolvedType[] rInterfaces = new ResolvedType[reflectInterfaces.length]; + for (int i = 0; i < reflectInterfaces.length; i++) { + rInterfaces[i] = ReflectionBasedReferenceTypeDelegateFactory.resolveTypeInWorld(reflectInterfaces[i], world); + } + this.interfaces = rInterfaces; + } + return interfaces; + } + + public boolean isCacheable() { + return true; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getDeclaredMethods() + */ + public ResolvedMember[] getDeclaredMethods() { + if (methods == null) { + Method[] reflectMethods = this.myClass.getDeclaredMethods(); + Constructor[] reflectCons = this.myClass.getDeclaredConstructors(); + ResolvedMember[] rMethods = new ResolvedMember[reflectMethods.length + reflectCons.length]; + for (int i = 0; i < reflectMethods.length; i++) { + rMethods[i] = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(reflectMethods[i], world); + } + for (int i = 0; i < reflectCons.length; i++) { + rMethods[i + reflectMethods.length] = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember( + reflectCons[i], world); + } + this.methods = rMethods; + } + return methods; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getDeclaredPointcuts() + */ + public ResolvedMember[] getDeclaredPointcuts() { + return new ResolvedMember[0]; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getTypeVariables() + */ + public TypeVariable[] getTypeVariables() { + // no type variables in Java 1.4 + return new TypeVariable[0]; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getPerClause() + */ + public PerClause getPerClause() { + // no per clause... + return null; + } + + public Collection<Declare> getDeclares() { + return Collections.emptySet(); + } + + public Collection<ConcreteTypeMunger> getTypeMungers() { + return Collections.emptySet(); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getPrivilegedAccesses() + */ + public Collection getPrivilegedAccesses() { + // no aspect members..., not used for weaving + return Collections.EMPTY_SET; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getModifiers() + */ + public int getModifiers() { + return this.myClass.getModifiers(); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getSuperclass() + */ + public ResolvedType getSuperclass() { + if (this.myClass.getSuperclass() == null) { + if (myClass == Object.class) { + return null; + } + return world.resolve(UnresolvedType.OBJECT); + } + return ReflectionBasedReferenceTypeDelegateFactory.resolveTypeInWorld(this.myClass.getSuperclass(), world); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getWeaverState() + */ + public WeaverStateInfo getWeaverState() { + return null; + } + + public ReferenceType getResolvedTypeX() { + return this.resolvedType; + } + + public boolean doesNotExposeShadowMungers() { + return false; + } + + public String getDeclaredGenericSignature() { + // no generic sig in 1.4 + return null; + } + + public ReflectionBasedResolvedMemberImpl createResolvedMemberFor(Member aMember) { + return null; + } + + public String getSourcefilename() { + // crappy guess.. + return resolvedType.getName() + ".class"; + } + + public ISourceContext getSourceContext() { + return SourceContextImpl.UNKNOWN_SOURCE_CONTEXT; + } + + public boolean copySourceContext() { + return true; + } + + public int getCompilerVersion() { + return WeaverVersionInfo.getCurrentWeaverMajorVersion(); + } + + public void ensureConsistent() { + + } + + public boolean isWeavable() { + return false; + } + + public boolean hasBeenWoven() { + return false; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateFactory.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateFactory.java new file mode 100644 index 000000000..eee1b6f32 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateFactory.java @@ -0,0 +1,219 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +/** + * @author colyer Creates the appropriate ReflectionBasedReferenceTypeDelegate according to the VM level we are running at. Uses + * reflection to avoid 1.5 dependencies in 1.4 and 1.3 code base. + */ +public class ReflectionBasedReferenceTypeDelegateFactory { + + public static ReflectionBasedReferenceTypeDelegate createDelegate(ReferenceType forReferenceType, World inWorld, + ClassLoader usingClassLoader) { + try { + Class c = Class.forName(forReferenceType.getName(), false, usingClassLoader); + if (LangUtil.is15VMOrGreater()) { + ReflectionBasedReferenceTypeDelegate rbrtd = create15Delegate(forReferenceType, c, usingClassLoader, inWorld); + if (rbrtd != null) { + return rbrtd; // can be null if we didn't find the class the delegate logic loads + } + } + return new ReflectionBasedReferenceTypeDelegate(c, usingClassLoader, inWorld, forReferenceType); + } catch (ClassNotFoundException cnfEx) { + return null; + } + } + + public static ReflectionBasedReferenceTypeDelegate createDelegate(ReferenceType forReferenceType, World inWorld, + Class<?> clazz) { + if (LangUtil.is15VMOrGreater()) { + ReflectionBasedReferenceTypeDelegate rbrtd = create15Delegate(forReferenceType, clazz, clazz.getClassLoader(), inWorld); + if (rbrtd != null) { + return rbrtd; // can be null if we didn't find the class the delegate logic loads + } + } + return new ReflectionBasedReferenceTypeDelegate(clazz, clazz.getClassLoader(), inWorld, forReferenceType); + } + + public static ReflectionBasedReferenceTypeDelegate create14Delegate(ReferenceType forReferenceType, World inWorld, + ClassLoader usingClassLoader) { + try { + Class c = Class.forName(forReferenceType.getName(), false, usingClassLoader); + return new ReflectionBasedReferenceTypeDelegate(c, usingClassLoader, inWorld, forReferenceType); + } catch (ClassNotFoundException cnfEx) { + return null; + } + } + + // can return 'null' if we can't find the class + private static ReflectionBasedReferenceTypeDelegate create15Delegate(ReferenceType forReferenceType, Class forClass, + ClassLoader usingClassLoader, World inWorld) { + try { + Class delegateClass = Class.forName("org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate"); + ReflectionBasedReferenceTypeDelegate ret = (ReflectionBasedReferenceTypeDelegate) delegateClass.newInstance(); + ret.initialize(forReferenceType, forClass, usingClassLoader, inWorld); + return ret; + } catch (ClassNotFoundException cnfEx) { + throw new IllegalStateException( + "Attempted to create Java 1.5 reflection based delegate but org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate was not found on classpath"); + } catch (InstantiationException insEx) { + throw new IllegalStateException("Attempted to create Java 1.5 reflection based delegate but InstantiationException: " + + insEx + " occured"); + } catch (IllegalAccessException illAccEx) { + throw new IllegalStateException("Attempted to create Java 1.5 reflection based delegate but IllegalAccessException: " + + illAccEx + " occured"); + } + } + + private static GenericSignatureInformationProvider createGenericSignatureProvider(World inWorld) { + if (LangUtil.is15VMOrGreater()) { + try { + Class providerClass = Class.forName("org.aspectj.weaver.reflect.Java15GenericSignatureInformationProvider"); + Constructor cons = providerClass.getConstructor(new Class[] { World.class }); + GenericSignatureInformationProvider ret = (GenericSignatureInformationProvider) cons + .newInstance(new Object[] { inWorld }); + return ret; + } catch (ClassNotFoundException cnfEx) { + // drop through and create a 14 provider... + // throw new + // IllegalStateException("Attempted to create Java 1.5 generic signature provider but org.aspectj.weaver.reflect.Java15GenericSignatureInformationProvider was not found on classpath"); + } catch (NoSuchMethodException nsmEx) { + throw new IllegalStateException("Attempted to create Java 1.5 generic signature provider but: " + nsmEx + + " occured"); + } catch (InstantiationException insEx) { + throw new IllegalStateException("Attempted to create Java 1.5 generic signature provider but: " + insEx + + " occured"); + } catch (InvocationTargetException invEx) { + throw new IllegalStateException("Attempted to create Java 1.5 generic signature provider but: " + invEx + + " occured"); + } catch (IllegalAccessException illAcc) { + throw new IllegalStateException("Attempted to create Java 1.5 generic signature provider but: " + illAcc + + " occured"); + } + } + return new Java14GenericSignatureInformationProvider(); + } + + /** + * convert a java.lang.reflect.Member into a resolved member in the world + * + * @param reflectMember + * @param inWorld + * @return + */ + public static ResolvedMember createResolvedMember(Member reflectMember, World inWorld) { + if (reflectMember instanceof Method) { + return createResolvedMethod((Method) reflectMember, inWorld); + } else if (reflectMember instanceof Constructor) { + return createResolvedConstructor((Constructor) reflectMember, inWorld); + } else { + return createResolvedField((Field) reflectMember, inWorld); + } + } + + public static ResolvedMember createResolvedMethod(Method aMethod, World inWorld) { + ReflectionBasedResolvedMemberImpl ret = new ReflectionBasedResolvedMemberImpl(org.aspectj.weaver.Member.METHOD, + toResolvedType(aMethod.getDeclaringClass(), (IReflectionWorld) inWorld), aMethod.getModifiers(), toResolvedType( + aMethod.getReturnType(), (IReflectionWorld) inWorld), aMethod.getName(), toResolvedTypeArray( + aMethod.getParameterTypes(), inWorld), toResolvedTypeArray(aMethod.getExceptionTypes(), inWorld), aMethod); + if (inWorld instanceof IReflectionWorld) { + ret.setAnnotationFinder(((IReflectionWorld) inWorld).getAnnotationFinder()); + } + ret.setGenericSignatureInformationProvider(createGenericSignatureProvider(inWorld)); + return ret; + } + + public static ResolvedMember createResolvedAdviceMember(Method aMethod, World inWorld) { + ReflectionBasedResolvedMemberImpl ret = new ReflectionBasedResolvedMemberImpl(org.aspectj.weaver.Member.ADVICE, + toResolvedType(aMethod.getDeclaringClass(), (IReflectionWorld) inWorld), aMethod.getModifiers(), toResolvedType( + aMethod.getReturnType(), (IReflectionWorld) inWorld), aMethod.getName(), toResolvedTypeArray( + aMethod.getParameterTypes(), inWorld), toResolvedTypeArray(aMethod.getExceptionTypes(), inWorld), aMethod); + if (inWorld instanceof IReflectionWorld) { + ret.setAnnotationFinder(((IReflectionWorld) inWorld).getAnnotationFinder()); + } + ret.setGenericSignatureInformationProvider(createGenericSignatureProvider(inWorld)); + return ret; + } + + public static ResolvedMember createStaticInitMember(Class forType, World inWorld) { + return new ResolvedMemberImpl(org.aspectj.weaver.Member.STATIC_INITIALIZATION, toResolvedType(forType, + (IReflectionWorld) inWorld), Modifier.STATIC, UnresolvedType.VOID, "<clinit>", new UnresolvedType[0], + new UnresolvedType[0]); + } + + public static ResolvedMember createResolvedConstructor(Constructor aConstructor, World inWorld) { + ReflectionBasedResolvedMemberImpl ret = new ReflectionBasedResolvedMemberImpl(org.aspectj.weaver.Member.CONSTRUCTOR, + toResolvedType(aConstructor.getDeclaringClass(), (IReflectionWorld) inWorld), aConstructor.getModifiers(), + // to return what BCEL returns, the return type for ctor is void + UnresolvedType.VOID,// toResolvedType(aConstructor.getDeclaringClass(),(IReflectionWorld)inWorld), + "<init>", toResolvedTypeArray(aConstructor.getParameterTypes(), inWorld), toResolvedTypeArray( + aConstructor.getExceptionTypes(), inWorld), aConstructor); + if (inWorld instanceof IReflectionWorld) { + ret.setAnnotationFinder(((IReflectionWorld) inWorld).getAnnotationFinder()); + } + ret.setGenericSignatureInformationProvider(createGenericSignatureProvider(inWorld)); + return ret; + } + + public static ResolvedMember createResolvedField(Field aField, World inWorld) { + ReflectionBasedResolvedMemberImpl ret = new ReflectionBasedResolvedMemberImpl(org.aspectj.weaver.Member.FIELD, + toResolvedType(aField.getDeclaringClass(), (IReflectionWorld) inWorld), aField.getModifiers(), toResolvedType( + aField.getType(), (IReflectionWorld) inWorld), aField.getName(), new UnresolvedType[0], aField); + if (inWorld instanceof IReflectionWorld) { + ret.setAnnotationFinder(((IReflectionWorld) inWorld).getAnnotationFinder()); + } + ret.setGenericSignatureInformationProvider(createGenericSignatureProvider(inWorld)); + return ret; + } + + public static ResolvedMember createHandlerMember(Class exceptionType, Class inType, World inWorld) { + return new ResolvedMemberImpl(org.aspectj.weaver.Member.HANDLER, toResolvedType(inType, (IReflectionWorld) inWorld), + Modifier.STATIC, "<catch>", "(" + inWorld.resolve(exceptionType.getName()).getSignature() + ")V"); + } + + public static ResolvedType resolveTypeInWorld(Class aClass, World aWorld) { + // classes that represent arrays return a class name that is the signature of the array type, ho-hum... + String className = aClass.getName(); + if (aClass.isArray()) { + return aWorld.resolve(UnresolvedType.forSignature(className.replace('.', '/'))); + } else { + return aWorld.resolve(className); + } + } + + private static ResolvedType toResolvedType(Class aClass, IReflectionWorld aWorld) { + return aWorld.resolve(aClass); + } + + private static ResolvedType[] toResolvedTypeArray(Class[] classes, World inWorld) { + ResolvedType[] ret = new ResolvedType[classes.length]; + for (int i = 0; i < ret.length; i++) { + ret[i] = ((IReflectionWorld) inWorld).resolve(classes[i]); + } + return ret; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionBasedResolvedMemberImpl.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionBasedResolvedMemberImpl.java new file mode 100644 index 000000000..ba8f1330e --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionBasedResolvedMemberImpl.java @@ -0,0 +1,162 @@ +/* ******************************************************************* + * Copyright (c) 2005, 2017 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://eclipse.org/legal/epl-v10.html + * + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.reflect.Member; + +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.MemberKind; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; + +/** + * Subtype of ResolvedMemberImpl used in reflection world. Knows how to get annotations from a java.lang.reflect.Member + * + * @author Adrian Colyer + * @author Andy Clement + */ +public class ReflectionBasedResolvedMemberImpl extends ResolvedMemberImpl { + + private AnnotationFinder annotationFinder = null; + private GenericSignatureInformationProvider gsigInfoProvider = new Java14GenericSignatureInformationProvider(); + + /** + * If true then only runtime visible annotations have been resolved via reflection. If class retention + * annotations are also required (later) then the cache will have to be rebuilt using a more detailed + * dig into the class file. + */ + private boolean onlyRuntimeAnnotationsCached; + + private Member reflectMember; + + public ReflectionBasedResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, + UnresolvedType returnType, String name, UnresolvedType[] parameterTypes, Member reflectMember) { + super(kind, declaringType, modifiers, returnType, name, parameterTypes); + this.reflectMember = reflectMember; + } + + public ReflectionBasedResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, + UnresolvedType returnType, String name, UnresolvedType[] parameterTypes, UnresolvedType[] checkedExceptions, + Member reflectMember) { + super(kind, declaringType, modifiers, returnType, name, parameterTypes, checkedExceptions); + this.reflectMember = reflectMember; + } + + public ReflectionBasedResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, + UnresolvedType returnType, String name, UnresolvedType[] parameterTypes, UnresolvedType[] checkedExceptions, + ResolvedMember backingGenericMember, Member reflectMember) { + super(kind, declaringType, modifiers, returnType, name, parameterTypes, checkedExceptions, backingGenericMember); + this.reflectMember = reflectMember; + } + + public ReflectionBasedResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, String name, + String signature, Member reflectMember) { + super(kind, declaringType, modifiers, name, signature); + this.reflectMember = reflectMember; + } + + public Member getMember() { + return this.reflectMember; + } + + public void setGenericSignatureInformationProvider(GenericSignatureInformationProvider gsigProvider) { + this.gsigInfoProvider = gsigProvider; + } + + @Override + public UnresolvedType[] getGenericParameterTypes() { + return this.gsigInfoProvider.getGenericParameterTypes(this); + } + + @Override + public UnresolvedType getGenericReturnType() { + return this.gsigInfoProvider.getGenericReturnType(this); + } + + @Override + public boolean isSynthetic() { + return this.gsigInfoProvider.isSynthetic(this); + } + + @Override + public boolean isVarargsMethod() { + return this.gsigInfoProvider.isVarArgs(this); + } + + @Override + public boolean isBridgeMethod() { + return this.gsigInfoProvider.isBridge(this); + } + + public void setAnnotationFinder(AnnotationFinder finder) { + this.annotationFinder = finder; + } + + @Override + public boolean hasAnnotation(UnresolvedType ofType) { + boolean areRuntimeRetentionAnnotationsSufficient = false; + if (ofType instanceof ResolvedType) { + areRuntimeRetentionAnnotationsSufficient = ((ResolvedType)ofType).isAnnotationWithRuntimeRetention(); + } + unpackAnnotations(areRuntimeRetentionAnnotationsSufficient); + return super.hasAnnotation(ofType); + } + + @Override + public boolean hasAnnotations() { + unpackAnnotations(false); + return super.hasAnnotations(); + } + + @Override + public ResolvedType[] getAnnotationTypes() { + unpackAnnotations(false); + return super.getAnnotationTypes(); + } + + @Override + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + unpackAnnotations(false); + if (annotationFinder == null || annotationTypes == null) { + return null; + } + for (ResolvedType type : annotationTypes) { + if (type.getSignature().equals(ofType.getSignature())) { + return annotationFinder.getAnnotationOfType(ofType, reflectMember); + } + } + return null; + } + + @Override + public String getAnnotationDefaultValue() { + if (annotationFinder == null) { + return null; + } + return annotationFinder.getAnnotationDefaultValue(reflectMember); + } + + @Override + public ResolvedType[][] getParameterAnnotationTypes() { + if (parameterAnnotationTypes == null && annotationFinder != null) { + parameterAnnotationTypes = annotationFinder.getParameterAnnotationTypes(reflectMember); + } + return parameterAnnotationTypes; + } + + private void unpackAnnotations(boolean areRuntimeRetentionAnnotationsSufficient) { + if (annotationFinder != null && (annotationTypes == null || (!areRuntimeRetentionAnnotationsSufficient && onlyRuntimeAnnotationsCached))) { + annotationTypes = annotationFinder.getAnnotations(reflectMember, areRuntimeRetentionAnnotationsSufficient); + onlyRuntimeAnnotationsCached = areRuntimeRetentionAnnotationsSufficient; + } + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionFastMatchInfo.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionFastMatchInfo.java new file mode 100644 index 000000000..eb891c3bf --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionFastMatchInfo.java @@ -0,0 +1,42 @@ +/* ******************************************************************* + * Copyright (c) 2006 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.World; +import org.aspectj.weaver.patterns.FastMatchInfo; +import org.aspectj.weaver.tools.MatchingContext; + +/** + * An implementation of FastMatchInfo that can also expose a MatchingContext. + * + * @author Adrian Colyer + * @since 1.5.1 + */ +public class ReflectionFastMatchInfo extends FastMatchInfo { + + private final MatchingContext context; + + public ReflectionFastMatchInfo(ResolvedType type, Shadow.Kind kind, MatchingContext context, World world) { + super(type, kind, world); + this.context = context; + } + + /** + * @return Returns the matching context. + */ + public MatchingContext getMatchingContext() { + return this.context; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionShadow.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionShadow.java new file mode 100644 index 000000000..0c6277e59 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionShadow.java @@ -0,0 +1,366 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Var; +import org.aspectj.weaver.tools.MatchingContext; + +/** + * @author colyer + * + */ +public class ReflectionShadow extends Shadow { + + private final World world; + private final ResolvedType enclosingType; + private final ResolvedMember enclosingMember; + private final MatchingContext matchContext; + private Var thisVar = null; + private Var targetVar = null; + private Var[] argsVars = null; + private Var atThisVar = null; + private Var atTargetVar = null; + private Map atArgsVars = new HashMap(); + private Map withinAnnotationVar = new HashMap(); + private Map withinCodeAnnotationVar = new HashMap(); + private Map annotationVar = new HashMap(); + private AnnotationFinder annotationFinder; + + public static Shadow makeExecutionShadow(World inWorld, java.lang.reflect.Member forMethod, MatchingContext withContext) { + Kind kind = (forMethod instanceof Method) ? Shadow.MethodExecution : Shadow.ConstructorExecution; + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(forMethod, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + return new ReflectionShadow(inWorld, kind, signature, null, enclosingType, null, withContext); + } + + public static Shadow makeAdviceExecutionShadow(World inWorld, java.lang.reflect.Method forMethod, MatchingContext withContext) { + Kind kind = Shadow.AdviceExecution; + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedAdviceMember(forMethod, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + return new ReflectionShadow(inWorld, kind, signature, null, enclosingType, null, withContext); + } + + public static Shadow makeCallShadow(World inWorld, java.lang.reflect.Member aMember, java.lang.reflect.Member withinCode, + MatchingContext withContext) { + Shadow enclosingShadow = makeExecutionShadow(inWorld, withinCode, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(aMember, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(withinCode, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = aMember instanceof Method ? Shadow.MethodCall : Shadow.ConstructorCall; + return new ReflectionShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public static Shadow makeCallShadow(World inWorld, java.lang.reflect.Member aMember, Class thisClass, + MatchingContext withContext) { + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, thisClass, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(aMember, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(thisClass, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = aMember instanceof Method ? Shadow.MethodCall : Shadow.ConstructorCall; + return new ReflectionShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public static Shadow makeStaticInitializationShadow(World inWorld, Class forType, MatchingContext withContext) { + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(forType, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.StaticInitialization; + return new ReflectionShadow(inWorld, kind, signature, null, enclosingType, null, withContext); + } + + public static Shadow makePreInitializationShadow(World inWorld, Constructor forConstructor, MatchingContext withContext) { + Kind kind = Shadow.PreInitialization; + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(forConstructor, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + return new ReflectionShadow(inWorld, kind, signature, null, enclosingType, null, withContext); + } + + public static Shadow makeInitializationShadow(World inWorld, Constructor forConstructor, MatchingContext withContext) { + Kind kind = Shadow.Initialization; + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(forConstructor, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + return new ReflectionShadow(inWorld, kind, signature, null, enclosingType, null, withContext); + } + + public static Shadow makeHandlerShadow(World inWorld, Class exceptionType, Class withinType, MatchingContext withContext) { + Kind kind = Shadow.ExceptionHandler; + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, withinType, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createHandlerMember(exceptionType, withinType, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(withinType, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + return new ReflectionShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public static Shadow makeHandlerShadow(World inWorld, Class exceptionType, java.lang.reflect.Member withinCode, + MatchingContext withContext) { + Kind kind = Shadow.ExceptionHandler; + Shadow enclosingShadow = makeExecutionShadow(inWorld, withinCode, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createHandlerMember(exceptionType, + withinCode.getDeclaringClass(), inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(withinCode, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + return new ReflectionShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public static Shadow makeFieldGetShadow(World inWorld, Field forField, Class callerType, MatchingContext withContext) { + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, callerType, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(callerType, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.FieldGet; + return new ReflectionShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public static Shadow makeFieldGetShadow(World inWorld, Field forField, java.lang.reflect.Member inMember, + MatchingContext withContext) { + Shadow enclosingShadow = makeExecutionShadow(inWorld, inMember, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(inMember, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.FieldGet; + return new ReflectionShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public static Shadow makeFieldSetShadow(World inWorld, Field forField, Class callerType, MatchingContext withContext) { + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, callerType, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(callerType, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.FieldSet; + return new ReflectionShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public static Shadow makeFieldSetShadow(World inWorld, Field forField, java.lang.reflect.Member inMember, + MatchingContext withContext) { + Shadow enclosingShadow = makeExecutionShadow(inWorld, inMember, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(inMember, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.FieldSet; + return new ReflectionShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public ReflectionShadow(World world, Kind kind, Member signature, Shadow enclosingShadow, ResolvedType enclosingType, + ResolvedMember enclosingMember, MatchingContext withContext) { + super(kind, signature, enclosingShadow); + this.world = world; + this.enclosingType = enclosingType; + this.enclosingMember = enclosingMember; + this.matchContext = withContext; + if (world instanceof IReflectionWorld) { + this.annotationFinder = ((IReflectionWorld) world).getAnnotationFinder(); + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getIWorld() + */ + public World getIWorld() { + return world; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getThisVar() + */ + public Var getThisVar() { + if (thisVar == null && hasThis()) { + thisVar = ReflectionVar.createThisVar(getThisType().resolve(world), this.annotationFinder); + } + return thisVar; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getTargetVar() + */ + public Var getTargetVar() { + if (targetVar == null && hasTarget()) { + targetVar = ReflectionVar.createTargetVar(getThisType().resolve(world), this.annotationFinder); + } + return targetVar; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getEnclosingType() + */ + public UnresolvedType getEnclosingType() { + return this.enclosingType; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getArgVar(int) + */ + public Var getArgVar(int i) { + if (argsVars == null) { + this.argsVars = new Var[this.getArgCount()]; + for (int j = 0; j < this.argsVars.length; j++) { + this.argsVars[j] = ReflectionVar.createArgsVar(getArgType(j).resolve(world), j, this.annotationFinder); + } + } + if (i < argsVars.length) { + return argsVars[i]; + } else { + return null; + } + } + + public Var getThisJoinPointVar() { + return null; + } + + public Var getThisJoinPointStaticPartVar() { + return null; + } + + public Var getThisEnclosingJoinPointStaticPartVar() { + return null; + } + + public Var getThisAspectInstanceVar(ResolvedType aspectType) { + return null; + } + + public Var getKindedAnnotationVar(UnresolvedType forAnnotationType) { + ResolvedType annType = forAnnotationType.resolve(world); + if (annotationVar.get(annType) == null) { + Var v = ReflectionVar.createAtAnnotationVar(annType, this.annotationFinder); + annotationVar.put(annType, v); + } + return (Var) annotationVar.get(annType); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getWithinAnnotationVar(org.aspectj.weaver.UnresolvedType) + */ + public Var getWithinAnnotationVar(UnresolvedType forAnnotationType) { + ResolvedType annType = forAnnotationType.resolve(world); + if (withinAnnotationVar.get(annType) == null) { + Var v = ReflectionVar.createWithinAnnotationVar(annType, this.annotationFinder); + withinAnnotationVar.put(annType, v); + } + return (Var) withinAnnotationVar.get(annType); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getWithinCodeAnnotationVar(org.aspectj.weaver.UnresolvedType) + */ + public Var getWithinCodeAnnotationVar(UnresolvedType forAnnotationType) { + ResolvedType annType = forAnnotationType.resolve(world); + if (withinCodeAnnotationVar.get(annType) == null) { + Var v = ReflectionVar.createWithinCodeAnnotationVar(annType, this.annotationFinder); + withinCodeAnnotationVar.put(annType, v); + } + return (Var) withinCodeAnnotationVar.get(annType); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getThisAnnotationVar(org.aspectj.weaver.UnresolvedType) + */ + public Var getThisAnnotationVar(UnresolvedType forAnnotationType) { + if (atThisVar == null) { + atThisVar = ReflectionVar.createThisAnnotationVar(forAnnotationType.resolve(world), this.annotationFinder); + } + return atThisVar; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getTargetAnnotationVar(org.aspectj.weaver.UnresolvedType) + */ + public Var getTargetAnnotationVar(UnresolvedType forAnnotationType) { + if (atTargetVar == null) { + atTargetVar = ReflectionVar.createTargetAnnotationVar(forAnnotationType.resolve(world), this.annotationFinder); + } + return atTargetVar; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getArgAnnotationVar(int, org.aspectj.weaver.UnresolvedType) + */ + public Var getArgAnnotationVar(int i, UnresolvedType forAnnotationType) { + ResolvedType annType = forAnnotationType.resolve(world); + if (atArgsVars.get(annType) == null) { + Var[] vars = new Var[getArgCount()]; + atArgsVars.put(annType, vars); + } + Var[] vars = (Var[]) atArgsVars.get(annType); + if (i > (vars.length - 1)) + return null; + if (vars[i] == null) { + vars[i] = ReflectionVar.createArgsAnnotationVar(annType, i, this.annotationFinder); + } + return vars[i]; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getEnclosingCodeSignature() + */ + public Member getEnclosingCodeSignature() { + // XXX this code is copied from BcelShadow with one minor change... + if (getKind().isEnclosingKind()) { + return getSignature(); + } else if (getKind() == Shadow.PreInitialization) { + // PreInit doesn't enclose code but its signature + // is correctly the signature of the ctor. + return getSignature(); + } else if (enclosingShadow == null) { + return this.enclosingMember; + } else { + return enclosingShadow.getSignature(); + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getSourceLocation() + */ + public ISourceLocation getSourceLocation() { + return null; + } + + public MatchingContext getMatchingContext() { + return this.matchContext; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionVar.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionVar.java new file mode 100644 index 000000000..59876a737 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionVar.java @@ -0,0 +1,169 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.reflect.Member; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ast.Var; + +/** + * A variable at a reflection shadow, used by the residual tests. + */ +public final class ReflectionVar extends Var { + + static final int THIS_VAR = 0; + static final int TARGET_VAR = 1; + static final int ARGS_VAR = 2; + static final int AT_THIS_VAR = 3; + static final int AT_TARGET_VAR = 4; + static final int AT_ARGS_VAR = 5; + static final int AT_WITHIN_VAR = 6; + static final int AT_WITHINCODE_VAR = 7; + static final int AT_ANNOTATION_VAR = 8; + + private AnnotationFinder annotationFinder = null; + +// static { +// try { +// Class java15AnnotationFinder = Class.forName("org.aspectj.weaver.reflect.Java15AnnotationFinder"); +// annotationFinder = (AnnotationFinder) java15AnnotationFinder.newInstance(); +// } catch(ClassNotFoundException ex) { +// // must be on 1.4 or earlier +// } catch(IllegalAccessException ex) { +// // not so good +// throw new RuntimeException("AspectJ internal error",ex); +// } catch(InstantiationException ex) { +// throw new RuntimeException("AspectJ internal error",ex); +// } +// } + + private int argsIndex = 0; + private int varType; + + public static ReflectionVar createThisVar(ResolvedType type,AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(type,finder); + ret.varType = THIS_VAR; + return ret; + } + + public static ReflectionVar createTargetVar(ResolvedType type, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(type,finder); + ret.varType = TARGET_VAR; + return ret; + } + + public static ReflectionVar createArgsVar(ResolvedType type, int index, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(type,finder); + ret.varType = ARGS_VAR; + ret.argsIndex = index; + return ret; + } + + public static ReflectionVar createThisAnnotationVar(ResolvedType type, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(type,finder); + ret.varType = AT_THIS_VAR; + return ret; + } + + public static ReflectionVar createTargetAnnotationVar(ResolvedType type, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(type,finder); + ret.varType = AT_TARGET_VAR; + return ret; + } + + public static ReflectionVar createArgsAnnotationVar(ResolvedType type, int index, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(type,finder); + ret.varType = AT_ARGS_VAR; + ret.argsIndex = index; + return ret; + } + + public static ReflectionVar createWithinAnnotationVar(ResolvedType annType, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(annType,finder); + ret.varType = AT_WITHIN_VAR; + return ret; + } + + public static ReflectionVar createWithinCodeAnnotationVar(ResolvedType annType, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(annType,finder); + ret.varType = AT_WITHINCODE_VAR; + return ret; + } + + public static ReflectionVar createAtAnnotationVar(ResolvedType annType, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(annType,finder); + ret.varType = AT_ANNOTATION_VAR; + return ret; + } + + private ReflectionVar(ResolvedType type,AnnotationFinder finder) { + super(type); + this.annotationFinder = finder; + } + + + public Object getBindingAtJoinPoint(Object thisObject, Object targetObject, Object[] args) { + return getBindingAtJoinPoint(thisObject,targetObject,args,null,null,null); + } + /** + * At a join point with the given this, target, and args, return the object to which this + * var is bound. + * @param thisObject + * @param targetObject + * @param args + * @return + */ + public Object getBindingAtJoinPoint( + Object thisObject, + Object targetObject, + Object[] args, + Member subject, + Member withinCode, + Class withinType) { + switch( this.varType) { + case THIS_VAR: return thisObject; + case TARGET_VAR: return targetObject; + case ARGS_VAR: + if (this.argsIndex > (args.length - 1)) return null; + return args[argsIndex]; + case AT_THIS_VAR: + if (annotationFinder != null) { + return annotationFinder.getAnnotation(getType(), thisObject); + } else return null; + case AT_TARGET_VAR: + if (annotationFinder != null) { + return annotationFinder.getAnnotation(getType(), targetObject); + } else return null; + case AT_ARGS_VAR: + if (this.argsIndex > (args.length - 1)) return null; + if (annotationFinder != null) { + return annotationFinder.getAnnotation(getType(), args[argsIndex]); + } else return null; + case AT_WITHIN_VAR: + if (annotationFinder != null) { + return annotationFinder.getAnnotationFromClass(getType(), withinType); + } else return null; + case AT_WITHINCODE_VAR: + if (annotationFinder != null) { + return annotationFinder.getAnnotationFromMember(getType(), withinCode); + } else return null; + case AT_ANNOTATION_VAR: + if (annotationFinder != null) { + return annotationFinder.getAnnotationFromMember(getType(), subject); + } else return null; + } + + return null; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionWorld.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionWorld.java new file mode 100644 index 000000000..c784ff288 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ReflectionWorld.java @@ -0,0 +1,248 @@ +/* ******************************************************************* + * Copyright (c) 2005-2017 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://eclipse.org/legal/epl-v10.html + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.aspectj.bridge.AbortException; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.IWeavingSupport; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ReferenceTypeDelegate; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +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..). + * + * @author Adrian Colyer + * @author Andy Clement + */ +public class ReflectionWorld extends World implements IReflectionWorld { + + private static Map<WeakClassLoaderReference, ReflectionWorld> rworlds = Collections.synchronizedMap(new HashMap<WeakClassLoaderReference, ReflectionWorld>()); + + private WeakClassLoaderReference classLoaderReference; + private AnnotationFinder annotationFinder; + private boolean mustUseOneFourDelegates = false; // for testing + private Map<String,Class<?>> inProgressResolutionClasses = new HashMap<String,Class<?>>(); + + public static ReflectionWorld getReflectionWorldFor(WeakClassLoaderReference classLoaderReference) { + + // Temporarily do as before. Although the cache makes things faster it needs a bit more thought because + // if the world has pointcutdesignators registered then someone may inadvertently register additional + // ones on reusing a world (when they would be expecting a clean world). We can't automatically + // clear them because we don't know when they are finished with. + return new ReflectionWorld(classLoaderReference); + + /* + synchronized (rworlds) { + // Tidyup any no longer relevant entries... + for (Iterator<Map.Entry<WeakClassLoaderReference, ReflectionWorld>> it = rworlds.entrySet().iterator(); + it.hasNext();) { + Map.Entry<WeakClassLoaderReference, ReflectionWorld> entry = it.next(); + if (entry.getKey().getClassLoader() == null) { + it.remove(); + } + } + ReflectionWorld rworld = null; + if (classLoaderReference.getClassLoader() != null) { + rworld = rworlds.get(classLoaderReference); + if (rworld == null) { + rworld = new ReflectionWorld(classLoaderReference); + rworlds.put(classLoaderReference, rworld); + } + } + return rworld; + } + */ + } + + public static void cleanUpWorlds() { + synchronized (rworlds) { + rworlds.clear(); + } + } + + private ReflectionWorld() { + // super(); + // this.setMessageHandler(new ExceptionBasedMessageHandler()); + // setBehaveInJava5Way(LangUtil.is15VMOrGreater()); + // this.classLoaderReference = new + // WeakClassLoaderReference(ReflectionWorld.class.getClassLoader()); + // this.annotationFinder = + // makeAnnotationFinderIfAny(classLoaderReference.getClassLoader(), + // this); + } + + public ReflectionWorld(WeakClassLoaderReference classloaderRef) { + this.setMessageHandler(new ExceptionBasedMessageHandler()); + setBehaveInJava5Way(LangUtil.is15VMOrGreater()); + classLoaderReference = classloaderRef; + annotationFinder = makeAnnotationFinderIfAny(classLoaderReference.getClassLoader(), this); + } + + public ReflectionWorld(ClassLoader aClassLoader) { + super(); + this.setMessageHandler(new ExceptionBasedMessageHandler()); + setBehaveInJava5Way(LangUtil.is15VMOrGreater()); + classLoaderReference = new WeakClassLoaderReference(aClassLoader); + annotationFinder = makeAnnotationFinderIfAny(classLoaderReference.getClassLoader(), this); + } + + public ReflectionWorld(boolean forceUseOf14Delegates, ClassLoader aClassLoader) { + this(aClassLoader); + this.mustUseOneFourDelegates = forceUseOf14Delegates; + if (forceUseOf14Delegates) { + // Dont use 1.4 delegates and yet allow autoboxing + this.setBehaveInJava5Way(false); + } + } + + public static AnnotationFinder makeAnnotationFinderIfAny(ClassLoader loader, World world) { + AnnotationFinder annotationFinder = null; + try { + if (LangUtil.is15VMOrGreater()) { + Class<?> java15AnnotationFinder = Class.forName("org.aspectj.weaver.reflect.Java15AnnotationFinder"); + annotationFinder = (AnnotationFinder) java15AnnotationFinder.newInstance(); + annotationFinder.setClassLoader(loader); + annotationFinder.setWorld(world); + } + } catch (ClassNotFoundException ex) { + // must be on 1.4 or earlier + } catch (IllegalAccessException ex) { + // not so good + throw new BCException("AspectJ internal error", ex); + } catch (InstantiationException ex) { + throw new BCException("AspectJ internal error", ex); + } + return annotationFinder; + } + + public ClassLoader getClassLoader() { + return classLoaderReference.getClassLoader(); + } + + public AnnotationFinder getAnnotationFinder() { + return annotationFinder; + } + + public ResolvedType resolve(Class aClass) { + return resolve(this, aClass); + } + + public static ResolvedType resolve(World world, Class<?> aClass) { + // classes that represent arrays return a class name that is the + // signature of the array type, ho-hum... + String className = aClass.getName(); + if (aClass.isArray()) { + return world.resolve(UnresolvedType.forSignature(className.replace('.', '/'))); + } else { + return world.resolve(className); + } + } + + /** + * Resolve a type using the specified class. Normal resolution in a reflection + * world uses Class.forName() via the classloader (attached to this world) + * in order to find a named type then builds a reference type and a reference + * type delegate based on that. For some classes generated at runtime (e.g. + * proxy or lambda representation) the forName() call will not work. In those + * situations we should just use the clazz we have. + * + * Should the whole thing switch from using forName() to using the clazz objects? + * Possibly but that introduces a lot of change and we don't have a lot + * of test coverage for this scenario (reflection world). What we are doing + * right now is that this can optionally be used if the regular resolution + * scheme did not work. + * + * Although AspectJ is *not* multi threaded or re-entrant, Spring doesn't + * always respect that. There might be an issue here if two attempts are + * made to resolve the same thing at the same time via this method. + * + * @param clazz the class to use as the delegate for the resolved type + */ + public ResolvedType resolveUsingClass(Class<?> clazz) { + String signature = UnresolvedType.forName(clazz.getName()).getSignature(); + try { + inProgressResolutionClasses.put(signature, clazz); + return resolve(clazz.getName()); + } finally { + inProgressResolutionClasses.remove(signature); + } + } + + protected ReferenceTypeDelegate resolveDelegate(ReferenceType ty) { + ReferenceTypeDelegate result; + if (mustUseOneFourDelegates) { + result = ReflectionBasedReferenceTypeDelegateFactory.create14Delegate(ty, this, classLoaderReference.getClassLoader()); + } else { + result = ReflectionBasedReferenceTypeDelegateFactory.createDelegate(ty, this, classLoaderReference.getClassLoader()); + } + if (result == null && inProgressResolutionClasses.size() != 0) { + // Is it a class that cannot be loaded (i.e. it was generated) but we already know about? + Class<?> clazz = inProgressResolutionClasses.get(ty.getSignature()); + if (clazz != null) { + result = ReflectionBasedReferenceTypeDelegateFactory.createDelegate(ty,this,clazz); + } + } + return result; + } + + public static class ReflectionWorldException extends RuntimeException { + + private static final long serialVersionUID = -3432261918302793005L; + + public ReflectionWorldException(String message) { + super(message); + } + } + + private static class ExceptionBasedMessageHandler implements IMessageHandler { + + public boolean handleMessage(IMessage message) throws AbortException { + throw new ReflectionWorldException(message.toString()); + } + + public boolean isIgnoring(org.aspectj.bridge.IMessage.Kind kind) { + if (kind == IMessage.INFO) { + return true; + } else { + return false; + } + } + + public void dontIgnore(org.aspectj.bridge.IMessage.Kind kind) { + // empty + } + + public void ignore(org.aspectj.bridge.IMessage.Kind kind) { + // empty + } + + } + + public IWeavingSupport getWeavingSupport() { + return null; + } + + public boolean isLoadtimeWeaving() { + return true; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ShadowMatchImpl.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ShadowMatchImpl.java new file mode 100644 index 000000000..350a77ba9 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/ShadowMatchImpl.java @@ -0,0 +1,202 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.reflect.Member; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.And; +import org.aspectj.weaver.ast.Call; +import org.aspectj.weaver.ast.FieldGetCall; +import org.aspectj.weaver.ast.HasAnnotation; +import org.aspectj.weaver.ast.ITestVisitor; +import org.aspectj.weaver.ast.Instanceof; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Not; +import org.aspectj.weaver.ast.Or; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; +import org.aspectj.weaver.internal.tools.MatchingContextBasedTest; +import org.aspectj.weaver.patterns.ExposedState; +import org.aspectj.weaver.tools.DefaultMatchingContext; +import org.aspectj.weaver.tools.JoinPointMatch; +import org.aspectj.weaver.tools.MatchingContext; +import org.aspectj.weaver.tools.PointcutParameter; +import org.aspectj.weaver.tools.ShadowMatch; + +/** + * @author colyer Implementation of ShadowMatch for reflection based worlds. + */ +public class ShadowMatchImpl implements ShadowMatch { + + private FuzzyBoolean match; + private ExposedState state; + private Test residualTest; + private PointcutParameter[] params; + private Member withinCode; + private Member subject; + private Class<?> withinType; + private MatchingContext matchContext = new DefaultMatchingContext(); + + public ShadowMatchImpl(FuzzyBoolean match, Test test, ExposedState state, PointcutParameter[] params) { + this.match = match; + this.residualTest = test; + this.state = state; + this.params = params; + } + + public void setWithinCode(Member aMember) { + this.withinCode = aMember; + } + + public void setSubject(Member aMember) { + this.subject = aMember; + } + + public void setWithinType(Class<?> aClass) { + this.withinType = aClass; + } + + public boolean alwaysMatches() { + return match.alwaysTrue(); + } + + public boolean maybeMatches() { + return match.maybeTrue(); + } + + public boolean neverMatches() { + return match.alwaysFalse(); + } + + public JoinPointMatch matchesJoinPoint(Object thisObject, Object targetObject, Object[] args) { + if (neverMatches()) { + return JoinPointMatchImpl.NO_MATCH; + } + if (new RuntimeTestEvaluator(residualTest, thisObject, targetObject, args, this.matchContext).matches()) { + return new JoinPointMatchImpl(getPointcutParameters(thisObject, targetObject, args)); + } else { + return JoinPointMatchImpl.NO_MATCH; + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.tools.ShadowMatch#setMatchingContext(org.aspectj.weaver.tools.MatchingContext) + */ + public void setMatchingContext(MatchingContext aMatchContext) { + this.matchContext = aMatchContext; + } + + private PointcutParameter[] getPointcutParameters(Object thisObject, Object targetObject, Object[] args) { + Var[] vars = state.vars; + PointcutParameterImpl[] bindings = new PointcutParameterImpl[params.length]; + for (int i = 0; i < bindings.length; i++) { + bindings[i] = new PointcutParameterImpl(params[i].getName(), params[i].getType()); + bindings[i].setBinding(((ReflectionVar) vars[i]).getBindingAtJoinPoint(thisObject, targetObject, args, subject, + withinCode, withinType)); + } + return bindings; + } + + private static class RuntimeTestEvaluator implements ITestVisitor { + + private boolean matches = true; + private final Test test; + private final Object thisObject; + private final Object targetObject; + private final Object[] args; + private final MatchingContext matchContext; + + public RuntimeTestEvaluator(Test aTest, Object thisObject, Object targetObject, Object[] args, MatchingContext context) { + this.test = aTest; + this.thisObject = thisObject; + this.targetObject = targetObject; + this.args = args; + this.matchContext = context; + } + + public boolean matches() { + test.accept(this); + return matches; + } + + public void visit(And e) { + boolean leftMatches = new RuntimeTestEvaluator(e.getLeft(), thisObject, targetObject, args, matchContext).matches(); + if (!leftMatches) { + matches = false; + } else { + matches = new RuntimeTestEvaluator(e.getRight(), thisObject, targetObject, args, matchContext).matches(); + } + } + + public void visit(Instanceof instanceofTest) { + ReflectionVar v = (ReflectionVar) instanceofTest.getVar(); + Object value = v.getBindingAtJoinPoint(thisObject, targetObject, args); + World world = v.getType().getWorld(); + ResolvedType desiredType = instanceofTest.getType().resolve(world); + if (value == null) { + matches = false; + } else { + ResolvedType actualType = world.resolve(value.getClass().getName()); + matches = desiredType.isAssignableFrom(actualType); + } + } + + public void visit(MatchingContextBasedTest matchingContextTest) { + matches = matchingContextTest.matches(this.matchContext); + } + + public void visit(Not not) { + matches = !new RuntimeTestEvaluator(not.getBody(), thisObject, targetObject, args, matchContext).matches(); + } + + public void visit(Or or) { + boolean leftMatches = new RuntimeTestEvaluator(or.getLeft(), thisObject, targetObject, args, matchContext).matches(); + if (leftMatches) { + matches = true; + } else { + matches = new RuntimeTestEvaluator(or.getRight(), thisObject, targetObject, args, matchContext).matches(); + } + } + + public void visit(Literal literal) { + if (literal == Literal.FALSE) { + matches = false; + } else { + matches = true; + } + } + + public void visit(Call call) { + throw new UnsupportedOperationException("Can't evaluate call test at runtime"); + } + + public void visit(FieldGetCall fieldGetCall) { + throw new UnsupportedOperationException("Can't evaluate fieldGetCall test at runtime"); + } + + public void visit(HasAnnotation hasAnnotation) { + ReflectionVar v = (ReflectionVar) hasAnnotation.getVar(); + Object value = v.getBindingAtJoinPoint(thisObject, targetObject, args); + World world = v.getType().getWorld(); + ResolvedType actualVarType = world.resolve(value.getClass().getName()); + ResolvedType requiredAnnotationType = hasAnnotation.getAnnotationType().resolve(world); + matches = actualVarType.hasAnnotation(requiredAnnotationType); + } + + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/StandardShadow.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/StandardShadow.java new file mode 100644 index 000000000..c151b228e --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/StandardShadow.java @@ -0,0 +1,401 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Var; +import org.aspectj.weaver.tools.MatchingContext; + +/** + * @author colyer + * + */ +public class StandardShadow extends Shadow { + + private final World world; + private final ResolvedType enclosingType; + private final ResolvedMember enclosingMember; + private final MatchingContext matchContext; + private Var thisVar = null; + private Var targetVar = null; + private Var[] argsVars = null; + private Var atThisVar = null; + private Var atTargetVar = null; + private Map atArgsVars = new HashMap(); + private Map withinAnnotationVar = new HashMap(); + private Map withinCodeAnnotationVar = new HashMap(); + private Map annotationVar = new HashMap(); + private AnnotationFinder annotationFinder; + + public static Shadow makeExecutionShadow(World inWorld, java.lang.reflect.Member forMethod, MatchingContext withContext) { + Kind kind = (forMethod instanceof Method) ? Shadow.MethodExecution : Shadow.ConstructorExecution; + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(forMethod, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + return new StandardShadow(inWorld, kind, signature, null, enclosingType, null, withContext); + } + + public static Shadow makeExecutionShadow(World inWorld, ResolvedMember forMethod, MatchingContext withContext) { + Kind kind = forMethod.getName().equals("<init>") ? Shadow.ConstructorExecution : Shadow.MethodExecution; + // Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(forMethod, inWorld); + // ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + return new StandardShadow(inWorld, kind, forMethod, null, (ResolvedType) forMethod.getDeclaringType(), null, withContext); + } + + public static Shadow makeAdviceExecutionShadow(World inWorld, java.lang.reflect.Method forMethod, MatchingContext withContext) { + Kind kind = Shadow.AdviceExecution; + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedAdviceMember(forMethod, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + return new StandardShadow(inWorld, kind, signature, null, enclosingType, null, withContext); + } + + public static Shadow makeCallShadow(World inWorld, ResolvedMember aMember, ResolvedMember withinCode, + MatchingContext withContext) { + Shadow enclosingShadow = makeExecutionShadow(inWorld, withinCode, withContext); + // Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(aMember, inWorld); + // ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(withinCode, inWorld); + // ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = !aMember.getName().equals("<init>") ? Shadow.MethodCall : Shadow.ConstructorCall; + return new StandardShadow(inWorld, kind, aMember, enclosingShadow, (ResolvedType) withinCode.getDeclaringType(), + withinCode, withContext); + } + + public static Shadow makeCallShadow(World inWorld, java.lang.reflect.Member aMember, Class thisClass, + MatchingContext withContext) { + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, thisClass, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(aMember, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(thisClass, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = aMember instanceof Method ? Shadow.MethodCall : Shadow.ConstructorCall; + return new StandardShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public static Shadow makeStaticInitializationShadow(World inWorld, Class forType, MatchingContext withContext) { + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(forType, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.StaticInitialization; + return new StandardShadow(inWorld, kind, signature, null, enclosingType, null, withContext); + } + + public static Shadow makeStaticInitializationShadow(World inWorld, ResolvedType forType, MatchingContext withContext) { + ResolvedMember[] members = forType.getDeclaredMethods(); + int clinit = -1; + for (int i = 0; i < members.length && clinit == -1; i++) { + if (members[i].getName().equals("<clinit>")) { + clinit = i; + } + } + // Member signature = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(forType, inWorld); + Kind kind = Shadow.StaticInitialization; + if (clinit == -1) { + Member clinitMember = new ResolvedMemberImpl(org.aspectj.weaver.Member.STATIC_INITIALIZATION, forType, Modifier.STATIC, + UnresolvedType.VOID, "<clinit>", new UnresolvedType[0], new UnresolvedType[0]); + return new StandardShadow(inWorld, kind, clinitMember, null, forType, null, withContext); + } else { + return new StandardShadow(inWorld, kind, members[clinit], null, forType, null, withContext); + } + } + + public static Shadow makePreInitializationShadow(World inWorld, Constructor forConstructor, MatchingContext withContext) { + Kind kind = Shadow.PreInitialization; + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(forConstructor, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + return new StandardShadow(inWorld, kind, signature, null, enclosingType, null, withContext); + } + + public static Shadow makeInitializationShadow(World inWorld, Constructor forConstructor, MatchingContext withContext) { + Kind kind = Shadow.Initialization; + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(forConstructor, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + return new StandardShadow(inWorld, kind, signature, null, enclosingType, null, withContext); + } + + public static Shadow makeHandlerShadow(World inWorld, Class exceptionType, Class withinType, MatchingContext withContext) { + Kind kind = Shadow.ExceptionHandler; + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, withinType, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createHandlerMember(exceptionType, withinType, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(withinType, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + return new StandardShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public static Shadow makeHandlerShadow(World inWorld, Class exceptionType, java.lang.reflect.Member withinCode, + MatchingContext withContext) { + Kind kind = Shadow.ExceptionHandler; + Shadow enclosingShadow = makeExecutionShadow(inWorld, withinCode, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createHandlerMember(exceptionType, + withinCode.getDeclaringClass(), inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(withinCode, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + return new StandardShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public static Shadow makeFieldGetShadow(World inWorld, Field forField, Class callerType, MatchingContext withContext) { + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, callerType, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(callerType, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.FieldGet; + return new StandardShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public static Shadow makeFieldGetShadow(World inWorld, Field forField, java.lang.reflect.Member inMember, + MatchingContext withContext) { + Shadow enclosingShadow = makeExecutionShadow(inWorld, inMember, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(inMember, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.FieldGet; + return new StandardShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public static Shadow makeFieldSetShadow(World inWorld, Field forField, Class callerType, MatchingContext withContext) { + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, callerType, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(callerType, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.FieldSet; + return new StandardShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public static Shadow makeFieldSetShadow(World inWorld, Field forField, java.lang.reflect.Member inMember, + MatchingContext withContext) { + Shadow enclosingShadow = makeExecutionShadow(inWorld, inMember, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(inMember, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.FieldSet; + return new StandardShadow(inWorld, kind, signature, enclosingShadow, enclosingType, enclosingMember, withContext); + } + + public StandardShadow(World world, Kind kind, Member signature, Shadow enclosingShadow, ResolvedType enclosingType, + ResolvedMember enclosingMember, MatchingContext withContext) { + super(kind, signature, enclosingShadow); + this.world = world; + this.enclosingType = enclosingType; + this.enclosingMember = enclosingMember; + this.matchContext = withContext; + if (world instanceof IReflectionWorld) { + this.annotationFinder = ((IReflectionWorld) world).getAnnotationFinder(); + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getIWorld() + */ + public World getIWorld() { + return world; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getThisVar() + */ + public Var getThisVar() { + if (thisVar == null && hasThis()) { + thisVar = ReflectionVar.createThisVar(getThisType().resolve(world), this.annotationFinder); + } + return thisVar; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getTargetVar() + */ + public Var getTargetVar() { + if (targetVar == null && hasTarget()) { + targetVar = ReflectionVar.createTargetVar(getThisType().resolve(world), this.annotationFinder); + } + return targetVar; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getEnclosingType() + */ + public UnresolvedType getEnclosingType() { + return this.enclosingType; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getArgVar(int) + */ + public Var getArgVar(int i) { + if (argsVars == null) { + this.argsVars = new Var[this.getArgCount()]; + for (int j = 0; j < this.argsVars.length; j++) { + this.argsVars[j] = ReflectionVar.createArgsVar(getArgType(j).resolve(world), j, this.annotationFinder); + } + } + if (i < argsVars.length) { + return argsVars[i]; + } else { + return null; + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getThisJoinPointVar() + */ + public Var getThisJoinPointVar() { + // TODO Auto-generated method stub + return null; + } + + public Var getThisJoinPointStaticPartVar() { + return null; + } + + public Var getThisEnclosingJoinPointStaticPartVar() { + return null; + } + + public Var getThisAspectInstanceVar(ResolvedType aspectType) { + return null; + } + + public Var getKindedAnnotationVar(UnresolvedType forAnnotationType) { + ResolvedType annType = forAnnotationType.resolve(world); + if (annotationVar.get(annType) == null) { + Var v = ReflectionVar.createAtAnnotationVar(annType, this.annotationFinder); + annotationVar.put(annType, v); + } + return (Var) annotationVar.get(annType); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getWithinAnnotationVar(org.aspectj.weaver.UnresolvedType) + */ + public Var getWithinAnnotationVar(UnresolvedType forAnnotationType) { + ResolvedType annType = forAnnotationType.resolve(world); + if (withinAnnotationVar.get(annType) == null) { + Var v = ReflectionVar.createWithinAnnotationVar(annType, this.annotationFinder); + withinAnnotationVar.put(annType, v); + } + return (Var) withinAnnotationVar.get(annType); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getWithinCodeAnnotationVar(org.aspectj.weaver.UnresolvedType) + */ + public Var getWithinCodeAnnotationVar(UnresolvedType forAnnotationType) { + ResolvedType annType = forAnnotationType.resolve(world); + if (withinCodeAnnotationVar.get(annType) == null) { + Var v = ReflectionVar.createWithinCodeAnnotationVar(annType, this.annotationFinder); + withinCodeAnnotationVar.put(annType, v); + } + return (Var) withinCodeAnnotationVar.get(annType); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getThisAnnotationVar(org.aspectj.weaver.UnresolvedType) + */ + public Var getThisAnnotationVar(UnresolvedType forAnnotationType) { + if (atThisVar == null) { + atThisVar = ReflectionVar.createThisAnnotationVar(forAnnotationType.resolve(world), this.annotationFinder); + } + return atThisVar; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getTargetAnnotationVar(org.aspectj.weaver.UnresolvedType) + */ + public Var getTargetAnnotationVar(UnresolvedType forAnnotationType) { + if (atTargetVar == null) { + atTargetVar = ReflectionVar.createTargetAnnotationVar(forAnnotationType.resolve(world), this.annotationFinder); + } + return atTargetVar; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getArgAnnotationVar(int, org.aspectj.weaver.UnresolvedType) + */ + public Var getArgAnnotationVar(int i, UnresolvedType forAnnotationType) { + ResolvedType annType = forAnnotationType.resolve(world); + if (atArgsVars.get(annType) == null) { + Var[] vars = new Var[getArgCount()]; + atArgsVars.put(annType, vars); + } + Var[] vars = (Var[]) atArgsVars.get(annType); + if (i > (vars.length - 1)) + return null; + if (vars[i] == null) { + vars[i] = ReflectionVar.createArgsAnnotationVar(annType, i, this.annotationFinder); + } + return vars[i]; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getEnclosingCodeSignature() + */ + public Member getEnclosingCodeSignature() { + // XXX this code is copied from BcelShadow with one minor change... + if (getKind().isEnclosingKind()) { + return getSignature(); + } else if (getKind() == Shadow.PreInitialization) { + // PreInit doesn't enclose code but its signature + // is correctly the signature of the ctor. + return getSignature(); + } else if (enclosingShadow == null) { + return this.enclosingMember; + } else { + return enclosingShadow.getSignature(); + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getSourceLocation() + */ + public ISourceLocation getSourceLocation() { + return null; + } + + public MatchingContext getMatchingContext() { + return this.matchContext; + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/StandardShadowMatchImpl.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/StandardShadowMatchImpl.java new file mode 100644 index 000000000..baeab0334 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/reflect/StandardShadowMatchImpl.java @@ -0,0 +1,196 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.And; +import org.aspectj.weaver.ast.Call; +import org.aspectj.weaver.ast.FieldGetCall; +import org.aspectj.weaver.ast.HasAnnotation; +import org.aspectj.weaver.ast.ITestVisitor; +import org.aspectj.weaver.ast.Instanceof; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Not; +import org.aspectj.weaver.ast.Or; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.internal.tools.MatchingContextBasedTest; +import org.aspectj.weaver.patterns.ExposedState; +import org.aspectj.weaver.tools.DefaultMatchingContext; +import org.aspectj.weaver.tools.JoinPointMatch; +import org.aspectj.weaver.tools.MatchingContext; +import org.aspectj.weaver.tools.PointcutParameter; +import org.aspectj.weaver.tools.ShadowMatch; + +/** + * @author colyer Implementation of ShadowMatch for reflection based worlds. + */ +public class StandardShadowMatchImpl implements ShadowMatch { + + private FuzzyBoolean match; + private ExposedState state; + private Test residualTest; + private PointcutParameter[] params; + private ResolvedMember withinCode; + private ResolvedMember subject; + private ResolvedType withinType; + private MatchingContext matchContext = new DefaultMatchingContext(); + + public StandardShadowMatchImpl(FuzzyBoolean match, Test test, ExposedState state, PointcutParameter[] params) { + this.match = match; + this.residualTest = test; + this.state = state; + this.params = params; + } + + public void setWithinCode(ResolvedMember aMember) { + this.withinCode = aMember; + } + + public void setSubject(ResolvedMember aMember) { + this.subject = aMember; + } + + public void setWithinType(ResolvedType aClass) { + this.withinType = aClass; + } + + public boolean alwaysMatches() { + return match.alwaysTrue(); + } + + public boolean maybeMatches() { + return match.maybeTrue(); + } + + public boolean neverMatches() { + return match.alwaysFalse(); + } + + public JoinPointMatch matchesJoinPoint(Object thisObject, Object targetObject, Object[] args) { + if (neverMatches()) + return JoinPointMatchImpl.NO_MATCH; + if (new RuntimeTestEvaluator(residualTest, thisObject, targetObject, args, this.matchContext).matches()) { + return new JoinPointMatchImpl(getPointcutParameters(thisObject, targetObject, args)); + } else { + return JoinPointMatchImpl.NO_MATCH; + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.tools.ShadowMatch#setMatchingContext(org.aspectj.weaver.tools.MatchingContext) + */ + public void setMatchingContext(MatchingContext aMatchContext) { + this.matchContext = aMatchContext; + } + + private PointcutParameter[] getPointcutParameters(Object thisObject, Object targetObject, Object[] args) { + // Var[] vars = state.vars; + // PointcutParameterImpl[] bindings = new PointcutParameterImpl[params.length]; + // for (int i = 0; i < bindings.length; i++) { + // bindings[i] = new PointcutParameterImpl(params[i].getName(), params[i].getType()); + // bindings[i].setBinding(((ReflectionVar) vars[i]).getBindingAtJoinPoint(thisObject, targetObject, args, subject, + // withinCode, withinType)); + // } + // return bindings; + return null; + } + + private static class RuntimeTestEvaluator implements ITestVisitor { + + private boolean matches = true; + private final Test test; + private final Object thisObject; + private final Object targetObject; + private final Object[] args; + private final MatchingContext matchContext; + + public RuntimeTestEvaluator(Test aTest, Object thisObject, Object targetObject, Object[] args, MatchingContext context) { + this.test = aTest; + this.thisObject = thisObject; + this.targetObject = targetObject; + this.args = args; + this.matchContext = context; + } + + public boolean matches() { + test.accept(this); + return matches; + } + + public void visit(And e) { + boolean leftMatches = new RuntimeTestEvaluator(e.getLeft(), thisObject, targetObject, args, matchContext).matches(); + if (!leftMatches) { + matches = false; + } else { + matches = new RuntimeTestEvaluator(e.getRight(), thisObject, targetObject, args, matchContext).matches(); + } + } + + public void visit(Instanceof i) { + ReflectionVar v = (ReflectionVar) i.getVar(); + Object value = v.getBindingAtJoinPoint(thisObject, targetObject, args); + World world = v.getType().getWorld(); + ResolvedType desiredType = i.getType().resolve(world); + ResolvedType actualType = world.resolve(value.getClass().getName()); + matches = desiredType.isAssignableFrom(actualType); + } + + public void visit(MatchingContextBasedTest matchingContextTest) { + matches = matchingContextTest.matches(this.matchContext); + } + + public void visit(Not not) { + matches = !new RuntimeTestEvaluator(not.getBody(), thisObject, targetObject, args, matchContext).matches(); + } + + public void visit(Or or) { + boolean leftMatches = new RuntimeTestEvaluator(or.getLeft(), thisObject, targetObject, args, matchContext).matches(); + if (leftMatches) { + matches = true; + } else { + matches = new RuntimeTestEvaluator(or.getRight(), thisObject, targetObject, args, matchContext).matches(); + } + } + + public void visit(Literal literal) { + if (literal == Literal.FALSE) { + matches = false; + } else { + matches = true; + } + } + + public void visit(Call call) { + throw new UnsupportedOperationException("Can't evaluate call test at runtime"); + } + + public void visit(FieldGetCall fieldGetCall) { + throw new UnsupportedOperationException("Can't evaluate fieldGetCall test at runtime"); + } + + public void visit(HasAnnotation hasAnnotation) { + ReflectionVar v = (ReflectionVar) hasAnnotation.getVar(); + Object value = v.getBindingAtJoinPoint(thisObject, targetObject, args); + World world = v.getType().getWorld(); + ResolvedType actualVarType = world.resolve(value.getClass().getName()); + ResolvedType requiredAnnotationType = hasAnnotation.getAnnotationType().resolve(world); + matches = actualVarType.hasAnnotation(requiredAnnotationType); + } + + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/AbstractTrace.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/AbstractTrace.java new file mode 100644 index 000000000..52217f880 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/AbstractTrace.java @@ -0,0 +1,202 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +import java.io.File; +import java.lang.reflect.Array; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.regex.Pattern; + +import org.aspectj.bridge.IMessage.Kind; + +public abstract class AbstractTrace implements Trace { + + private static final Pattern packagePrefixPattern = Pattern.compile("([^.])[^.]*(\\.)"); + + protected Class<?> tracedClass; + + private static SimpleDateFormat timeFormat; + + protected AbstractTrace (Class clazz) { + this.tracedClass = clazz; + } + + public abstract void enter (String methodName, Object thiz, Object[] args); + + public abstract void enter(String methodName, Object thiz); + + public abstract void exit(String methodName, Object ret); + + public abstract void exit(String methodName, Throwable th); + + /* + * Convenience methods + */ + public void enter (String methodName) { + enter(methodName,null,null); + } + + public void enter (String methodName, Object thiz, Object arg) { + enter(methodName,thiz,new Object[] { arg }); + } + + public void enter (String methodName, Object thiz, boolean z) { + enter(methodName,thiz,new Boolean(z)); + } + + public void exit (String methodName, boolean b) { + exit(methodName,new Boolean(b)); + } + + public void exit (String methodName, int i) { + exit(methodName,new Integer(i)); + } + + public void event (String methodName, Object thiz, Object arg) { + event(methodName,thiz,new Object[] { arg }); + } + + public void warn(String message) { + warn(message,null); + } + + public void error(String message) { + error(message,null); + } + + public void fatal (String message) { + fatal(message,null); + } + + /* + * Formatting + */ + protected String formatMessage(String kind, String className, String methodName, Object thiz, Object[] args) { + StringBuffer message = new StringBuffer(); + Date now = new Date(); + message.append(formatDate(now)).append(" "); + message.append(Thread.currentThread().getName()).append(" "); + message.append(kind).append(" "); + message.append(formatClassName(className)); + message.append(".").append(methodName); + if (thiz != null) message.append(" ").append(formatObj(thiz)); + if (args != null) message.append(" ").append(formatArgs(args)); + return message.toString(); + } + + /** + * @param className full dotted class name + * @return short version of class name with package collapse to initials + */ + private String formatClassName(String className) { + return packagePrefixPattern.matcher(className).replaceAll("$1."); + } + + protected String formatMessage(String kind, String text, Throwable th) { + StringBuffer message = new StringBuffer(); + Date now = new Date(); + message.append(formatDate(now)).append(" "); + message.append(Thread.currentThread().getName()).append(" "); + message.append(kind).append(" "); + message.append(text); + if (th != null) message.append(" ").append(formatObj(th)); + return message.toString(); + } + + private static String formatDate (Date date) { + if (timeFormat == null) { + timeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); + } + + return timeFormat.format(date); + } + + /** + * Format objects safely avoiding toString which can cause recursion, + * NullPointerExceptions or highly verbose results. + * + * @param obj parameter to be formatted + * @return the formatted parameter + */ + protected Object formatObj(Object obj) { + + /* These classes have a safe implementation of toString() */ + if (obj == null + || obj instanceof String + || obj instanceof Number + || obj instanceof Boolean + || obj instanceof Exception + || obj instanceof Character + || obj instanceof Class + || obj instanceof File + || obj instanceof StringBuffer + || obj instanceof URL + || obj instanceof Kind + ) return obj; + else if (obj.getClass().isArray()) { + return formatArray(obj); + } + else if (obj instanceof Collection) { + return formatCollection((Collection)obj); + } + else try { + + // Classes can provide an alternative implementation of toString() + if (obj instanceof Traceable) { + return ((Traceable)obj).toTraceString(); + } + + // classname@hashcode + else return formatClassName(obj.getClass().getName()) + "@" + Integer.toHexString(System.identityHashCode(obj)); + + /* Object.hashCode() can be override and may thow an exception */ + } catch (Exception ex) { + return obj.getClass().getName() + "@FFFFFFFF"; + } + } + + protected String formatArray(Object obj) { + return obj.getClass().getComponentType().getName() + "[" + Array.getLength(obj) + "]"; + } + + protected String formatCollection(Collection<?> c) { + return c.getClass().getName() + "(" + c.size() + ")"; + } + + /** + * Format arguments into a comma separated list + * + * @param names array of argument names + * @param args array of arguments + * @return the formated list + */ + protected String formatArgs(Object[] args) { + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < args.length; i++) { + sb.append(formatObj(args[i])); + if (i < args.length-1) sb.append(", "); + } + + return sb.toString(); + } + + protected Object[] formatObjects(Object[] args) { + for (int i = 0; i < args.length; i++) { + args[i] = formatObj(args[i]); + } + + return args; + } +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/CommonsTrace.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/CommonsTrace.java new file mode 100644 index 000000000..25562c050 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/CommonsTrace.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class CommonsTrace extends AbstractTrace { + + private Log log; + private String className; + + public CommonsTrace (Class clazz) { + super(clazz); + this.log = LogFactory.getLog(clazz); + this.className = tracedClass.getName(); + } + + public void enter(String methodName, Object thiz, Object[] args) { + if (log.isDebugEnabled()) { + log.debug(formatMessage(">", className, methodName, thiz, args)); + } + } + + public void enter(String methodName, Object thiz) { + if (log.isDebugEnabled()) { + log.debug(formatMessage(">", className, methodName, thiz, null)); + } + } + + public void exit(String methodName, Object ret) { + if (log.isDebugEnabled()) { + log.debug(formatMessage("<", className, methodName, ret, null)); + } + } + + public void exit(String methodName, Throwable th) { + if (log.isDebugEnabled()) { + log.debug(formatMessage("<", className, methodName, th, null)); + } + } + + public void exit(String methodName) { + if (log.isDebugEnabled()) { + log.debug(formatMessage("<", className, methodName, null, null)); + } + } + + public void event(String methodName, Object thiz, Object[] args) { + if (log.isDebugEnabled()) { + log.debug(formatMessage("-", className, methodName, thiz, args)); + } + } + + public void event(String methodName) { + if (log.isDebugEnabled()) { + log.debug(formatMessage("-", className, methodName, null, null)); + } + } + + public boolean isTraceEnabled () { + return log.isDebugEnabled(); + } + + public void setTraceEnabled (boolean b) { + } + + public void debug (String message) { + if (log.isDebugEnabled()) { + log.debug(message); + } + } + + public void info(String message) { + if (log.isInfoEnabled()) { + log.info(message); + } + } + + public void warn (String message, Throwable th) { + if (log.isWarnEnabled()) { + log.warn(message,th); + } + } + + public void error (String message, Throwable th) { + if (log.isErrorEnabled()) { + log.error(message,th); + } + } + + public void fatal (String message, Throwable th) { + if (log.isFatalEnabled()) { + log.fatal(message,th); + } + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/CommonsTraceFactory.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/CommonsTraceFactory.java new file mode 100644 index 000000000..8f9f91e98 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/CommonsTraceFactory.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +import org.apache.commons.logging.LogFactory; +//OPTIMIZE move out of main weaver for now? +public class CommonsTraceFactory extends TraceFactory { + + private LogFactory logFactory = LogFactory.getFactory(); + + public Trace getTrace(Class clazz) { + return new CommonsTrace(clazz); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/ContextBasedMatcher.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/ContextBasedMatcher.java new file mode 100644 index 000000000..fc247dc90 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/ContextBasedMatcher.java @@ -0,0 +1,65 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +/** + * Pointcut expression interface for pointcut + * expressions returned by a + * PointcutDesignatorHandler. Provides an additional + * matching method for matching based on context + * information over and above that normally used + * by AspectJ. + * + * @see MatchingContext + * + */ +public interface ContextBasedMatcher { + + /** + * return true iff this matcher could ever match + * a join point in the given type + * @deprecated use couldMatchJoinPointsInType(Class,MatchingContext) instead + */ + boolean couldMatchJoinPointsInType(Class aClass); + + /** + * return true iff this matcher could ever match + * a join point in the given type, may also use any + * match context information available + * @since 1.5.1 + */ + boolean couldMatchJoinPointsInType(Class aClass, MatchingContext matchContext); + + /** + * return true if matchesStatically can ever return + * FuzzyBoolean.MAYBE (necessitating a per-join point test + * to determine matching at a given join point). + */ + boolean mayNeedDynamicTest(); + + /** + * Return FuzzyBoolean.YES if a join point with the given + * matching context is always matched. + * Return FuzzyBoolean.NO if a join point with the given + * matching context is never matched. + * Return FuzzyBoolean.MAYBE if a match cannot be determined + * statically (whilst generating a ShadowMatch), and must + * be determined on a per-join point basis. + */ + FuzzyBoolean matchesStatically(MatchingContext matchContext); + + /** + * Called during processing of ShadowMatch.matchesJoinPoint + * when matchesStatically returned FuzzyBoolean.MAYBE. + */ + boolean matchesDynamically(MatchingContext matchContext); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/DefaultMatchingContext.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/DefaultMatchingContext.java new file mode 100644 index 000000000..fa47a87a3 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/DefaultMatchingContext.java @@ -0,0 +1,56 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +import java.util.HashMap; +import java.util.Map; + +/** + * Default implementation of MatchingContext, backed + * by a Map. + */ +public class DefaultMatchingContext implements MatchingContext { + + private Map contextMap = new HashMap(); + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.MatchingContext#hasContextParameter(java.lang.String) + */ + public boolean hasContextBinding(String contextParameterName) { + return this.contextMap.containsKey(contextParameterName); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.MatchingContext#get(java.lang.String) + */ + public Object getBinding(String contextParameterName) { + return this.contextMap.get(contextParameterName); + } + + /** + * Add a context binding with the given name and value + * @param name + * @param value + */ + public void addContextBinding(String name, Object value) { + this.contextMap.put(name, value); + } + + /** + * Remove the context binding with the given name + * @param name + */ + public void removeContextBinding(String name) { + this.contextMap.remove(name); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/DefaultTrace.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/DefaultTrace.java new file mode 100644 index 000000000..7e2ab0d8f --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/DefaultTrace.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +import java.io.PrintStream; + +public class DefaultTrace extends AbstractTrace { + + private boolean traceEnabled = false; + private PrintStream print = System.err; + + public DefaultTrace(Class clazz) { + super(clazz); + } + + public boolean isTraceEnabled() { + return traceEnabled; + } + + public void setTraceEnabled(boolean b) { + traceEnabled = b; + } + + public void enter(String methodName, Object thiz, Object[] args) { + if (traceEnabled) { + println(formatMessage(">", tracedClass.getName(), methodName, thiz, args)); + } + } + + public void enter(String methodName, Object thiz) { + if (traceEnabled) { + println(formatMessage(">", tracedClass.getName(), methodName, thiz, null)); + } + } + + public void exit(String methodName, Object ret) { + if (traceEnabled) { + println(formatMessage("<", tracedClass.getName(), methodName, ret, null)); + } + } + + public void exit(String methodName) { + if (traceEnabled) { + println(formatMessage("<", tracedClass.getName(), methodName, null, null)); + } + } + + public void exit(String methodName, Throwable th) { + if (traceEnabled) { + println(formatMessage("<", tracedClass.getName(), methodName, th, null)); + } + } + + public void event(String methodName, Object thiz, Object[] args) { + if (traceEnabled) { + println(formatMessage("-", tracedClass.getName(), methodName, thiz, args)); + } + } + + public void event(String methodName) { + if (traceEnabled) { + println(formatMessage("-", tracedClass.getName(), methodName, null, null)); + } + } + + public void debug(String message) { + println(formatMessage("?", message, null)); + } + + public void info(String message) { + println(formatMessage("I", message, null)); + } + + public void warn(String message, Throwable th) { + println(formatMessage("W", message, th)); + if (th != null) + th.printStackTrace(); + } + + public void error(String message, Throwable th) { + println(formatMessage("E", message, th)); + if (th != null) + th.printStackTrace(); + } + + public void fatal(String message, Throwable th) { + println(formatMessage("X", message, th)); + if (th != null) + th.printStackTrace(); + } + + /** + * Template method that allows choice of destination for output + * + * @param s + * message to be traced + */ + protected void println(String s) { + print.println(s); + } + + public void setPrintStream(PrintStream printStream) { + this.print = printStream; + } + + // private static boolean isTracingEnabled = + // getBoolean("org.aspectj.weaver.tools.tracing",false); + // + // private static boolean getBoolean (String name, boolean def) { + // String defaultValue = String.valueOf(def); + // String value = System.getProperty(name,defaultValue); + // return Boolean.valueOf(value).booleanValue(); + // } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/DefaultTraceFactory.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/DefaultTraceFactory.java new file mode 100644 index 000000000..c7860589a --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/DefaultTraceFactory.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +public class DefaultTraceFactory extends TraceFactory { + + public final static String ENABLED_PROPERTY = "org.aspectj.tracing.enabled"; + public final static String FILE_PROPERTY = "org.aspectj.tracing.file"; + + private boolean tracingEnabled = getBoolean(ENABLED_PROPERTY,false); + private PrintStream print; + + public DefaultTraceFactory() { + String filename = System.getProperty(FILE_PROPERTY); + if (filename != null) { + File file = new File(filename); + try { + print = new PrintStream(new FileOutputStream(file)); + } + catch (IOException ex) { + if (debug) ex.printStackTrace(); + } + } + } + + public boolean isEnabled() { + return tracingEnabled; + } + + public Trace getTrace (Class clazz) { + DefaultTrace trace = new DefaultTrace(clazz); + trace.setTraceEnabled(tracingEnabled); + if (print != null) trace.setPrintStream(print); + return trace; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/FuzzyBoolean.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/FuzzyBoolean.java new file mode 100644 index 000000000..77079c094 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/FuzzyBoolean.java @@ -0,0 +1,37 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ + + +package org.aspectj.weaver.tools; + +/** + * This class implements a boolean that includes a "maybe" + */ +public final class FuzzyBoolean { + + // Note :- this implementation is not safe under serialization / deserialization + private String name; + + public static final FuzzyBoolean YES = new FuzzyBoolean("YES"); + public static final FuzzyBoolean NO = new FuzzyBoolean("NO"); + public static final FuzzyBoolean MAYBE = new FuzzyBoolean("MAYBE"); + + + public static final FuzzyBoolean fromBoolean(boolean b) { + return b ? YES : NO; + } + + public String toString() { return name; } + + private FuzzyBoolean() {} + + private FuzzyBoolean(String n) { this.name = n; } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/GeneratedClassHandler.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/GeneratedClassHandler.java new file mode 100644 index 000000000..6dd4fec18 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/GeneratedClassHandler.java @@ -0,0 +1,30 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation + * 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: + * Matthew Webster, Adrian Colyer, + * Martin Lippert initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +/** + * Interface implemented by weaving class loaders to allow classes generated by + * the weaving process to be defined. + */ +public interface GeneratedClassHandler { + + /** + * Accept class generated by WeavingAdaptor. The class loader should store + * the class definition in its local cache until called upon to load it. + * @param name class name + * @param originalBytes original class bytes + * @param weavedBytes woven class bytes + */ + public void acceptClass (String name, byte[] originalBytes, byte[] weavedBytes); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/ISupportsMessageContext.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/ISupportsMessageContext.java new file mode 100644 index 000000000..69e55b143 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/ISupportsMessageContext.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +import org.aspectj.bridge.IMessageContext; + +public interface ISupportsMessageContext { + + public void setMessageContext (IMessageContext messageContext); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/JoinPointMatch.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/JoinPointMatch.java new file mode 100644 index 000000000..588b3c0bc --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/JoinPointMatch.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +/** + * @author colyer + * The result of asking a ShadowMatch to match at a given join point. + */ +public interface JoinPointMatch { + + /** + * True if the pointcut expression has matched at this join point, and false + * otherwise + */ + boolean matches(); + + /** + * Get the parameter bindings at the matched join point. + * If the join point was not matched an empty array is returned. + */ + PointcutParameter[] getParameterBindings(); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/MatchingContext.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/MatchingContext.java new file mode 100644 index 000000000..e668a30b7 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/MatchingContext.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +/** + * When extending AspectJ's pointcut parsing and + * matching with custom PointcutDesignatorHandlers, + * it may be necessary to match based on context information + * at a join point not exposed simply by java.lang.reflect + * member information or argument values. The matching context + * interface provides an extension point for the specification + * of additional shadow and join point context that can be + * taken into account during the matching process. + * + * @see DefaultMatchingContext + */ +public interface MatchingContext { + + /** + * Returns true iff this matching context has a defined + * binding for the given context parameter. + * @param contextParameterName + */ + boolean hasContextBinding(String contextParameterName); + + /** + * returns the binding associated with the + * given context parameter name (or null if + * there is no such context). + * @param contextParameterName + * @return + */ + Object getBinding(String contextParameterName); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutDesignatorHandler.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutDesignatorHandler.java new file mode 100644 index 000000000..63f4a81e2 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutDesignatorHandler.java @@ -0,0 +1,49 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + + +/** + * The PointcutDesignator interface allows extension of the + * AspectJ pointcut language so that third-party tools integrating + * with AspectJ can add easily their own custom + * domain-specific designators and have them interoperate seamlessly + * with the standard AspectJ designators. + * + * A pointcut designator can only be used for matching, not for + * binding. + */ +public interface PointcutDesignatorHandler { + + /** + * The name of this pointcut designator. For example, + * if this designator handles a "bean(<NamePattern>) + * format designator, this method would return "bean". + * @return + */ + String getDesignatorName() ; + + /** + * Parse the given expression string + * and return a ContextBasedMatcher that can be used + * for matching. + * @param expression the body of the pointcut expression. + * For example, given the expression "bean(*DAO)" the parse + * method will be called with the argument "*DAO". + * @return a pointcut expression that can be used for + * matching. + * @throws IllegalArgumentException if the expression + * is ill-formed. + */ + ContextBasedMatcher parse(String expression); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutExpression.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutExpression.java new file mode 100644 index 000000000..6b598a50c --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutExpression.java @@ -0,0 +1,204 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ + +package org.aspectj.weaver.tools; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; + +/** + * Represents an AspectJ pointcut expression and provides convenience methods to determine + * whether or not the pointcut matches join points specified in terms of the + * java.lang.reflect interfaces. + */ +public interface PointcutExpression { + + /** + * Set the matching context to be used for + * subsequent calls to match. + * @see MatchingContext + */ + void setMatchingContext(MatchingContext aMatchContext); + + /** + * Determine whether or not this pointcut could ever match a join point in the given class. + * @param aClass the candidate class + * @return true iff this pointcut <i>may</i> match a join point within(aClass), and false otherwise + */ + boolean couldMatchJoinPointsInType(Class aClass); + + /** + * Returns true iff this pointcut contains any expression that might necessitate a dynamic test + * at some join point (e.g. args) + */ + boolean mayNeedDynamicTest(); + + /** + * Determine whether or not this pointcut matches the execution of a given method. + * @param aMethod the method being executed + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing the execution of the method. + */ + ShadowMatch matchesMethodExecution(Method aMethod ); + + /** + * Determine whether or not this pointcut matches the execution of a given constructor. + * @param aConstructor the constructor being executed + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing the execution of the constructor. + */ + ShadowMatch matchesConstructorExecution(Constructor aConstructor); + + /** + * Determine whether or not this pointcut matches the static initialization + * of the given class. + * @param aClass the class being statically initialized + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matchs join points representing the static initialization of the given type + */ + ShadowMatch matchesStaticInitialization(Class aClass); + + /** + * Determine whether or not this pointcut matches the execution of a given piece of advice. + * @param anAdviceMethod a method representing the advice being executed + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing the execution of the advice. + */ + ShadowMatch matchesAdviceExecution(Method anAdviceMethod); + + /** + * Determine whether or not this pointcut matches the initialization of an + * object initiated by a call to the given constructor. + * @param aConstructor the constructor initiating the initialization + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing initialization via the given constructor. + */ + ShadowMatch matchesInitialization(Constructor aConstructor); + + /** + * Determine whether or not this pointcut matches the pre-initialization of an + * object initiated by a call to the given constructor. + * @param aConstructor the constructor initiating the initialization + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing pre-initialization via the given constructor. + */ + ShadowMatch matchesPreInitialization(Constructor aConstructor); + + /** + * Determine whether or not this pointcut matches a method call to the given method, made during + * the execution of the given method or constructor. + * @param aMethod the method being called + * @param withinCode the Method or Constructor from within which the call is made + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing a call to this method during the execution of the given member. + */ + ShadowMatch matchesMethodCall(Method aMethod, Member withinCode); + + /** + * Determine whether or not this pointcut matches a method call to the given method, made outside + * of the scope of any method or constructor, but within the callerType (for example, during + * static initialization of the type). + * @param aMethod the method being called + * @param callerType the declared type of the caller + * @param receiverType the declared type of the recipient of the call + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing a call to this method during the execution of the given member. + */ + ShadowMatch matchesMethodCall(Method aMethod, Class callerType); + + /** + * Determine whether or not this pointcut matches a method call to the given constructor, made during + * the execution of the given method or constructor. + * @param aConstructor the constructor being called + * @param withinCode the Method or Constructor from within which the call is made + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing a call to this constructor during the execution of the given member. + */ + ShadowMatch matchesConstructorCall(Constructor aConstructor, Member withinCode); + + /** + * Determine whether or not this pointcut matches a method call to the given constructor, made outside + * of the scope of any method or constructor, but within the callerType. + * @param aConstructor the cosstructor being called + * @param callerType the declared type of the caller + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing a call to this constructor during the execution of the given member. + */ + ShadowMatch matchesConstructorCall(Constructor aConstructor, Class callerType); + + /** + * Determine whether or not this pointcut matches the execution of a given exception + * handler within the given method or constructor + * @param exceptionType the static type of the exception being handled + * @param withinCode the method or constructor in which the catch block is declared + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or + * never matches join points representing the handling of the given exception + */ + ShadowMatch matchesHandler(Class exceptionType, Member withinCode); + + /** + * Determine whether or not this pointcut matches the execution of a given exception + * handler outside of the scope of any method or constructor, but within the handling type. + * @param exceptionType the static type of the exception being handled + * @param handlingType the type in which the handler block is executing + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or + * never matches join points representing the handling of the given exception + */ + ShadowMatch matchesHandler(Class exceptionType, Class handlingType); + + /** + * Determine whether or not this pointcut matches a set of the given field from within the given + * method or constructor. + * @param aField the field being updated + * @param withinCode the Method or Constructor owning the call site + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or + * never matches field set join points for the given field and call site. + */ + ShadowMatch matchesFieldSet(Field aField, Member withinCode); + + /** + * Determine whether or not this pointcut matches a set of the given field outside of the + * scope of any method or constructor, but within the given type (for example, during + * static initialization). + * @param aField the field being updated + * @param withinType the type owning the call site + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or + * never matches field set join points for the given field and call site. + */ + ShadowMatch matchesFieldSet(Field aField, Class withinType); + + /** + * Determine whether or not this pointcut matches a get of the given field from within the given + * method or constructor. + * @param aField the field being updated + * @param withinCode the Method or Constructor owning the call site + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or + * never matches field get join points for the given field and call site. + */ + ShadowMatch matchesFieldGet(Field aField, Member withinCode); + + /** + * Determine whether or not this pointcut matches a get of the given field outside of the + * scope of any method or constructor, but within the given type (for example, during + * static initialization). + * @param aField the field being accessed + * @param withinType the type owning the call site + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or + * never matches field get join points for the given field and call site. + */ + ShadowMatch matchesFieldGet(Field aField, Class withinType); + + /** + * Return a string representation of this pointcut expression. + */ + String getPointcutExpression(); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutParameter.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutParameter.java new file mode 100644 index 000000000..a0eaa108a --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutParameter.java @@ -0,0 +1,36 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +/** + * @author colyer + * Represents a parameter in a pointcut expression. + * For example pointcut pc(String s) : .....; has a PointcutParameter of + * name "s" and type String. + */ +public interface PointcutParameter { + + /** + * The name of this parameter + */ + String getName(); + + /** + * The type of the parameter + */ + Class getType(); + + /** + * At a matched join point, the parameter binding. + */ + Object getBinding(); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutParser.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutParser.java new file mode 100644 index 000000000..ca0fa3aca --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutParser.java @@ -0,0 +1,582 @@ +/******************************************************************************* + * Copyright (c) 2004, 2017 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; + +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.SourceLocation; +import org.aspectj.weaver.BindingScope; +import org.aspectj.weaver.IHasPosition; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeakClassLoaderReference; +import org.aspectj.weaver.World; +import org.aspectj.weaver.internal.tools.PointcutExpressionImpl; +import org.aspectj.weaver.internal.tools.TypePatternMatcherImpl; +import org.aspectj.weaver.patterns.AndPointcut; +import org.aspectj.weaver.patterns.CflowPointcut; +import org.aspectj.weaver.patterns.FormalBinding; +import org.aspectj.weaver.patterns.IScope; +import org.aspectj.weaver.patterns.KindedPointcut; +import org.aspectj.weaver.patterns.NotPointcut; +import org.aspectj.weaver.patterns.OrPointcut; +import org.aspectj.weaver.patterns.ParserException; +import org.aspectj.weaver.patterns.PatternParser; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.SimpleScope; +import org.aspectj.weaver.patterns.ThisOrTargetAnnotationPointcut; +import org.aspectj.weaver.patterns.ThisOrTargetPointcut; +import org.aspectj.weaver.patterns.TypePattern; +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 + * + * @author Adrian Colyer + * @author Andy Clement + */ +public class PointcutParser { + + private ReflectionWorld world; + private WeakClassLoaderReference classLoaderReference; + private final Set<PointcutPrimitive> supportedPrimitives; + private final Set<PointcutDesignatorHandler> pointcutDesignators = new HashSet<PointcutDesignatorHandler>(); + + /** + * @return a Set containing every PointcutPrimitive except if, cflow, and cflowbelow (useful for passing to PointcutParser + * constructor). + */ + public static Set<PointcutPrimitive> getAllSupportedPointcutPrimitives() { + Set<PointcutPrimitive> primitives = new HashSet<PointcutPrimitive>(); + 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: + * <ul> + * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported + * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + * <li>The pointcut expression must be anonymous with no formals allowed. + * </ul> + * <p> + * When resolving types in pointcut expressions, the context classloader is used to find types. + * </p> + */ + 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: + * <ul> + * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported + * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + * <li>The pointcut expression must be anonymous with no formals allowed. + * </ul> + * <p> + * When resolving types in pointcut expressions, the context classloader is used to find types. + * </p> + * + * @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<PointcutPrimitive> 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: + * <ul> + * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported + * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + * <li>The pointcut expression must be anonymous with no formals allowed. + * </ul> + * <p> + * When resolving types in pointcut expressions, the given classloader is used to find types. + * </p> + */ + 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: + * <ul> + * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported + * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + * <li>The pointcut expression must be anonymous with no formals allowed. + * </ul> + * <p> + * When resolving types in pointcut expressions, the given classloader is used to find types. + * </p> + * + * @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<PointcutPrimitive> 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: + * <ul> + * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported + * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + * <li>The pointcut expression must be anonymous with no formals allowed. + * </ul> + */ + 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: + * <ul> + * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported + * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + * <li>The pointcut expression must be anonymous with no formals allowed. + * </ul> + * + * @param supportedPointcutKinds a set of PointcutPrimitives this parser should support + * @throws UnsupportedOperationException if the set contains if, cflow, or cflow below + */ + private PointcutParser(Set<PointcutPrimitive> supportedPointcutKinds) { + supportedPrimitives = supportedPointcutKinds; + for (PointcutPrimitive pointcutPrimitive : supportedPointcutKinds) { + if ((pointcutPrimitive == PointcutPrimitive.IF) || (pointcutPrimitive == PointcutPrimitive.CFLOW) + || (pointcutPrimitive == 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 = ReflectionWorld.getReflectionWorldFor(this.classLoaderReference); + } + + /** + * Set the classloader that this parser should use for type resolution. + * + * @param aLoader + * @param shareWorlds if true then two PointcutParsers operating using the same classloader will share a ReflectionWorld + */ + protected void setClassLoader(ClassLoader aLoader, boolean shareWorlds) { + this.classLoaderReference = new WeakClassLoaderReference(aLoader); + if (shareWorlds) { + world = ReflectionWorld.getReflectionWorldFor(this.classLoaderReference); + } else { + world = new ReflectionWorld(classLoaderReference); + } + } + + /** + * 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(); // more correctly: parsePointcut(true) + 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<PointcutPrimitive> 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 '"); + msg.append(ex.getMessage()); + msg.append("'"); + IHasPosition location = ex.getLocation(); + msg.append(" at character position "); + msg.append(location.getStart()); + msg.append("\n"); + msg.append(pc); + msg.append("\n"); + for (int i = 0; i < location.getStart(); i++) { + msg.append(" "); + } + for (int j = location.getStart(); j <= location.getEnd(); j++) { + msg.append("^"); + } + msg.append("\n"); + return msg.toString(); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutPrimitive.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutPrimitive.java new file mode 100644 index 000000000..ae3f607ff --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/PointcutPrimitive.java @@ -0,0 +1,50 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ + +package org.aspectj.weaver.tools; + +import org.aspectj.util.TypeSafeEnum; + +/** + * An enumeration of the different kinds of pointcut primitives + * supported by AspectJ. + */ +public final class PointcutPrimitive extends TypeSafeEnum { + + public static final PointcutPrimitive CALL = new PointcutPrimitive("call",1); + public static final PointcutPrimitive EXECUTION = new PointcutPrimitive("execution",2); + public static final PointcutPrimitive GET = new PointcutPrimitive("get",3); + public static final PointcutPrimitive SET = new PointcutPrimitive("set",4); + public static final PointcutPrimitive INITIALIZATION = new PointcutPrimitive("initialization",5); + public static final PointcutPrimitive PRE_INITIALIZATION = new PointcutPrimitive("preinitialization",6); + public static final PointcutPrimitive STATIC_INITIALIZATION = new PointcutPrimitive("staticinitialization",7); + public static final PointcutPrimitive HANDLER = new PointcutPrimitive("handler",8); + public static final PointcutPrimitive ADVICE_EXECUTION = new PointcutPrimitive("adviceexecution",9); + public static final PointcutPrimitive WITHIN = new PointcutPrimitive("within",10); + public static final PointcutPrimitive WITHIN_CODE = new PointcutPrimitive("withincode",11); + public static final PointcutPrimitive CFLOW = new PointcutPrimitive("cflow",12); + public static final PointcutPrimitive CFLOW_BELOW = new PointcutPrimitive("cflowbelow",13); + public static final PointcutPrimitive IF = new PointcutPrimitive("if",14); + public static final PointcutPrimitive THIS = new PointcutPrimitive("this",15); + public static final PointcutPrimitive TARGET = new PointcutPrimitive("target",16); + public static final PointcutPrimitive ARGS = new PointcutPrimitive("args",17); + public static final PointcutPrimitive REFERENCE = new PointcutPrimitive("reference pointcut",18); + public static final PointcutPrimitive AT_ANNOTATION = new PointcutPrimitive("@annotation",19); + public static final PointcutPrimitive AT_THIS = new PointcutPrimitive("@this",20); + public static final PointcutPrimitive AT_TARGET = new PointcutPrimitive("@target",21); + public static final PointcutPrimitive AT_ARGS = new PointcutPrimitive("@args",22); + public static final PointcutPrimitive AT_WITHIN = new PointcutPrimitive("@within",23); + public static final PointcutPrimitive AT_WITHINCODE = new PointcutPrimitive("@withincode",24); + + private PointcutPrimitive(String name, int key) { + super(name, key); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/ShadowMatch.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/ShadowMatch.java new file mode 100644 index 000000000..55e2581df --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/ShadowMatch.java @@ -0,0 +1,58 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +/** + * The result of asking a PointcutExpression to match at a shadow (method execution, + * handler, constructor call, and so on). + * + */ +public interface ShadowMatch { + + /** + * True iff the pointcut expression will match any join point at this + * shadow (for example, any call to the given method). + */ + boolean alwaysMatches(); + + /** + * True if the pointcut expression may match some join points at this + * shadow (for example, some calls to the given method may match, depending + * on the type of the caller). + * <p>If alwaysMatches is true, then maybeMatches is always true.</p> + */ + boolean maybeMatches(); + + /** + * True iff the pointcut expression can never match any join point at this + * shadow (for example, the pointcut will never match a call to the given + * method). + */ + boolean neverMatches(); + + /** + * Return the result of matching a join point at this shadow with the given + * this, target, and args. + * @param thisObject the object bound to this at the join point + * @param targetObject the object bound to target at the join point + * @param args the arguments at the join point + * @return + */ + JoinPointMatch matchesJoinPoint(Object thisObject, Object targetObject, Object[] args); + + /** + * Set a matching context to be used when matching + * join points. + * @see MatchingContext + */ + void setMatchingContext(MatchingContext aMatchContext); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/StandardPointcutExpression.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/StandardPointcutExpression.java new file mode 100644 index 000000000..63d908bd2 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/StandardPointcutExpression.java @@ -0,0 +1,233 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ + +package org.aspectj.weaver.tools; + +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; + +/** + * Represents an AspectJ pointcut expression and provides convenience methods to determine whether or not the pointcut matches join + * points specified in terms of the java.lang.reflect interfaces. + */ +public interface StandardPointcutExpression { + + /** + * Set the matching context to be used for subsequent calls to match. + * + * @see MatchingContext + */ + void setMatchingContext(MatchingContext aMatchContext); + + /** + * Determine whether or not this pointcut could ever match a join point in the given class. + * + * @param aClass the candidate class + * @return true iff this pointcut <i>may</i> match a join point within(aClass), and false otherwise + */ + boolean couldMatchJoinPointsInType(Class aClass); + + /** + * Returns true iff this pointcut contains any expression that might necessitate a dynamic test at some join point (e.g. args) + */ + boolean mayNeedDynamicTest(); + + /** + * Determine whether or not this pointcut matches the execution of a given method. + * + * @param aMethod the method being executed + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches join points representing the + * execution of the method. + */ + ShadowMatch matchesMethodExecution(ResolvedMember aMethod); + + // /** + // * Determine whether or not this pointcut matches the execution of a given constructor. + // * + // * @param aConstructor the constructor being executed + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches join points representing the + // * execution of the constructor. + // */ + // ShadowMatch matchesConstructorExecution(Constructor aConstructor); + // + /** + * Determine whether or not this pointcut matches the static initialization of the given class. + * + * @param aClass the class being statically initialized + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matchs join points representing the static + * initialization of the given type + */ + ShadowMatch matchesStaticInitialization(ResolvedType aType); + + // + // /** + // * Determine whether or not this pointcut matches the static initialization of the given class. + // * + // * @param aClass the class being statically initialized + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matchs join points representing the + // static + // * initialization of the given type + // */ + // ShadowMatch matchesStaticInitialization(ResolvedType type); + // + // /** + // * Determine whether or not this pointcut matches the execution of a given piece of advice. + // * + // * @param anAdviceMethod a method representing the advice being executed + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches join points representing the + // * execution of the advice. + // */ + // ShadowMatch matchesAdviceExecution(Method anAdviceMethod); + // + // /** + // * Determine whether or not this pointcut matches the initialization of an object initiated by a call to the given + // constructor. + // * + // * @param aConstructor the constructor initiating the initialization + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches join points representing + // * initialization via the given constructor. + // */ + // ShadowMatch matchesInitialization(Constructor aConstructor); + // + // /** + // * Determine whether or not this pointcut matches the pre-initialization of an object initiated by a call to the given + // * constructor. + // * + // * @param aConstructor the constructor initiating the initialization + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches join points representing + // * pre-initialization via the given constructor. + // */ + // ShadowMatch matchesPreInitialization(Constructor aConstructor); + // + /** + * Determine whether or not this pointcut matches a method call to the given method, made during the execution of the given + * method or constructor. + * + * @param aMethod the method being called + * @param withinCode the Method or Constructor from within which the call is made + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches join points representing a call to + * this method during the execution of the given member. + */ + ShadowMatch matchesMethodCall(ResolvedMember aMethod, ResolvedMember withinCode); + + // + // /** + // * Determine whether or not this pointcut matches a method call to the given method, made outside of the scope of any method + // or + // * constructor, but within the callerType (for example, during static initialization of the type). + // * + // * @param aMethod the method being called + // * @param callerType the declared type of the caller + // * @param receiverType the declared type of the recipient of the call + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches join points representing a call + // to + // * this method during the execution of the given member. + // */ + // ShadowMatch matchesMethodCall(Method aMethod, Class callerType); + // + // /** + // * Determine whether or not this pointcut matches a method call to the given constructor, made during the execution of the + // given + // * method or constructor. + // * + // * @param aConstructor the constructor being called + // * @param withinCode the Method or Constructor from within which the call is made + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches join points representing a call + // to + // * this constructor during the execution of the given member. + // */ + // ShadowMatch matchesConstructorCall(Constructor aConstructor, Member withinCode); + // + // /** + // * Determine whether or not this pointcut matches a method call to the given constructor, made outside of the scope of any + // * method or constructor, but within the callerType. + // * + // * @param aConstructor the cosstructor being called + // * @param callerType the declared type of the caller + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches join points representing a call + // to + // * this constructor during the execution of the given member. + // */ + // ShadowMatch matchesConstructorCall(Constructor aConstructor, Class callerType); + // + // /** + // * Determine whether or not this pointcut matches the execution of a given exception handler within the given method or + // * constructor + // * + // * @param exceptionType the static type of the exception being handled + // * @param withinCode the method or constructor in which the catch block is declared + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches join points representing the + // * handling of the given exception + // */ + // ShadowMatch matchesHandler(Class exceptionType, Member withinCode); + // + // /** + // * Determine whether or not this pointcut matches the execution of a given exception handler outside of the scope of any + // method + // * or constructor, but within the handling type. + // * + // * @param exceptionType the static type of the exception being handled + // * @param handlingType the type in which the handler block is executing + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches join points representing the + // * handling of the given exception + // */ + // ShadowMatch matchesHandler(Class exceptionType, Class handlingType); + // + // /** + // * Determine whether or not this pointcut matches a set of the given field from within the given method or constructor. + // * + // * @param aField the field being updated + // * @param withinCode the Method or Constructor owning the call site + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches field set join points for the + // given + // * field and call site. + // */ + // ShadowMatch matchesFieldSet(Field aField, Member withinCode); + // + // /** + // * Determine whether or not this pointcut matches a set of the given field outside of the scope of any method or constructor, + // * but within the given type (for example, during static initialization). + // * + // * @param aField the field being updated + // * @param withinType the type owning the call site + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches field set join points for the + // given + // * field and call site. + // */ + // ShadowMatch matchesFieldSet(Field aField, Class withinType); + // + // /** + // * Determine whether or not this pointcut matches a get of the given field from within the given method or constructor. + // * + // * @param aField the field being updated + // * @param withinCode the Method or Constructor owning the call site + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches field get join points for the + // given + // * field and call site. + // */ + // ShadowMatch matchesFieldGet(Field aField, Member withinCode); + // + // /** + // * Determine whether or not this pointcut matches a get of the given field outside of the scope of any method or constructor, + // * but within the given type (for example, during static initialization). + // * + // * @param aField the field being accessed + // * @param withinType the type owning the call site + // * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never matches field get join points for the + // given + // * field and call site. + // */ + // ShadowMatch matchesFieldGet(Field aField, Class withinType); + + /** + * Return a string representation of this pointcut expression. + */ + String getPointcutExpression(); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/StandardPointcutParser.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/StandardPointcutParser.java new file mode 100644 index 000000000..e549325af --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/StandardPointcutParser.java @@ -0,0 +1,508 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +import java.io.File; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Properties; +import java.util.Set; + +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.SourceLocation; +import org.aspectj.weaver.BindingScope; +import org.aspectj.weaver.IHasPosition; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.internal.tools.StandardPointcutExpressionImpl; +import org.aspectj.weaver.internal.tools.TypePatternMatcherImpl; +import org.aspectj.weaver.patterns.AndPointcut; +import org.aspectj.weaver.patterns.CflowPointcut; +import org.aspectj.weaver.patterns.FormalBinding; +import org.aspectj.weaver.patterns.IScope; +import org.aspectj.weaver.patterns.KindedPointcut; +import org.aspectj.weaver.patterns.NotPointcut; +import org.aspectj.weaver.patterns.OrPointcut; +import org.aspectj.weaver.patterns.ParserException; +import org.aspectj.weaver.patterns.PatternParser; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.SimpleScope; +import org.aspectj.weaver.patterns.ThisOrTargetAnnotationPointcut; +import org.aspectj.weaver.patterns.ThisOrTargetPointcut; +import org.aspectj.weaver.patterns.TypePattern; +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 + */ +public class StandardPointcutParser { + + private World world; + private final Set<PointcutPrimitive> supportedPrimitives; + private final Set<PointcutDesignatorHandler> pointcutDesignators = new HashSet<PointcutDesignatorHandler>(); + + /** + * @return a Set containing every PointcutPrimitive except if, cflow, and cflowbelow (useful for passing to PointcutParser + * constructor). + */ + public static Set<PointcutPrimitive> getAllSupportedPointcutPrimitives() { + Set<PointcutPrimitive> primitives = new HashSet<PointcutPrimitive>(); + 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: + * <ul> + * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported + * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + * <li>The pointcut expression must be anonymous with no formals allowed. + * </ul> + * <p> + * When resolving types in pointcut expressions, the context classloader is used to find types. + * </p> + */ + public static StandardPointcutParser getPointcutParserSupportingAllPrimitives(World world) { + StandardPointcutParser p = new StandardPointcutParser(world); + 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: + * <ul> + * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported + * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + * <li>The pointcut expression must be anonymous with no formals allowed. + * </ul> + * <p> + * When resolving types in pointcut expressions, the given classloader is used to find types. + * </p> + * + * @param supportedPointcutKinds a set of PointcutPrimitives this parser should support + * @throws UnsupportedOperationException if the set contains if, cflow, or cflow below + */ + public static StandardPointcutParser getPointcutParserSupportingSpecifiedPrimitives(Set supportedPointcutKinds, World world) { + StandardPointcutParser p = new StandardPointcutParser(supportedPointcutKinds, world); + return p; + } + + /** + * Create a pointcut parser that can parse the full AspectJ pointcut language with the following exceptions: + * <ul> + * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported + * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + * <li>The pointcut expression must be anonymous with no formals allowed. + * </ul> + */ + protected StandardPointcutParser(World world) { + supportedPrimitives = getAllSupportedPointcutPrimitives(); + this.world = world; + } + + /** + * 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: + * <ul> + * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported + * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + * <li>The pointcut expression must be anonymous with no formals allowed. + * </ul> + * + * @param supportedPointcutKinds a set of PointcutPrimitives this parser should support + * @throws UnsupportedOperationException if the set contains if, cflow, or cflow below + */ + private StandardPointcutParser(Set/* <PointcutPrimitives> */supportedPointcutKinds, World world) { + 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"); + } + } + this.world = world; + } + + // /** + // * 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 StandardPointcutExpression 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 StandardPointcutExpression parsePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters) + throws UnsupportedPointcutPrimitiveException, IllegalArgumentException { + StandardPointcutExpressionImpl 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 StandardPointcutExpressionImpl(pc, expression, formalParameters, getWorld()); + } catch (ParserException pEx) { + throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx)); + } catch (ReflectionWorld.ReflectionWorldException rwEx) { + rwEx.printStackTrace(); + 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); + } + if (inScope == null) { + SimpleScope ss = new SimpleScope(getWorld(), formalBindings); + ss.setImportedPrefixes(new String[] { "java.lang.", "java.util." }); + return ss; + } 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() { + } + }; + BindingScope bScope = new BindingScope(inType, sourceContext, formalBindings); + bScope.setImportedPrefixes(new String[] { "java.lang.", "java.util." }); + return bScope; + } + } + + 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 '"); + msg.append(ex.getMessage()); + msg.append("'"); + IHasPosition location = ex.getLocation(); + msg.append(" at character position "); + msg.append(location.getStart()); + msg.append("\n"); + msg.append(pc); + msg.append("\n"); + for (int i = 0; i < location.getStart(); i++) { + msg.append(" "); + } + for (int j = location.getStart(); j <= location.getEnd(); j++) { + msg.append("^"); + } + msg.append("\n"); + return msg.toString(); + } +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/Trace.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/Trace.java new file mode 100644 index 000000000..8b20032aa --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/Trace.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +public interface Trace { + + public void enter(String methodName, Object thiz, Object[] args); + + public void enter(String methodName, Object thiz); + + public void exit(String methodName, Object ret); + + public void exit(String methodName, Throwable th); + + public void exit(String methodName); + + public void event(String methodName); + + public void event(String methodName, Object thiz, Object[] args); + + public void debug(String message); + + public void info(String message); + + public void warn(String message); + + public void warn(String message, Throwable th); + + public void error(String message); + + public void error(String message, Throwable th); + + public void fatal(String message); + + public void fatal(String message, Throwable th); + + public void enter(String methodName, Object thiz, Object arg); + + public void enter(String methodName, Object thiz, boolean z); + + public void exit(String methodName, boolean b); + + public void exit(String methodName, int i); + + public void event(String methodName, Object thiz, Object arg); + + public boolean isTraceEnabled(); + + public void setTraceEnabled(boolean b); +}
\ No newline at end of file diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/TraceFactory.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/TraceFactory.java new file mode 100644 index 000000000..6d819de50 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/TraceFactory.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +import org.aspectj.util.LangUtil; + +public abstract class TraceFactory { + + public final static String DEBUG_PROPERTY = "org.aspectj.tracing.debug"; + public final static String FACTORY_PROPERTY = "org.aspectj.tracing.factory"; + public final static String DEFAULT_FACTORY_NAME = "default"; + + protected static boolean debug = getBoolean(DEBUG_PROPERTY,false); + private static TraceFactory instance; + + public Trace getTrace (Class clazz) { + return instance.getTrace(clazz); + } + + public static TraceFactory getTraceFactory () { + return instance; + } + + protected static boolean getBoolean(String name, boolean def) { + String defaultValue = String.valueOf(def); + String value = System.getProperty(name,defaultValue); + return Boolean.valueOf(value).booleanValue(); + } + + static { + + /* + * Allow user to override default behaviour or specify their own factory + */ + String factoryName = System.getProperty(FACTORY_PROPERTY); + if (factoryName != null) try { + if (factoryName.equals(DEFAULT_FACTORY_NAME)) { + instance = new DefaultTraceFactory(); + } + else { + Class factoryClass = Class.forName(factoryName); + instance = (TraceFactory)factoryClass.newInstance(); + } + } + catch (Throwable th) { + if (debug) th.printStackTrace(); + } + + /* + * Try to load external trace infrastructure using supplied factories + */ + if (instance == null) try { + if (LangUtil.is15VMOrGreater()) { + Class factoryClass = Class.forName("org.aspectj.weaver.tools.Jdk14TraceFactory"); + instance = (TraceFactory)factoryClass.newInstance(); + } else { + Class factoryClass = Class.forName("org.aspectj.weaver.tools.CommonsTraceFactory"); + instance = (TraceFactory)factoryClass.newInstance(); + } + } + catch (Throwable th) { + if (debug) th.printStackTrace(); + } + + /* + * Use default trace + */ + if (instance == null) { + instance = new DefaultTraceFactory(); + } + + if (debug) System.err.println("TraceFactory.instance=" + instance); + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/Traceable.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/Traceable.java new file mode 100644 index 000000000..957786e48 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/Traceable.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +public interface Traceable { + + public String toTraceString (); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/TypePatternMatcher.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/TypePatternMatcher.java new file mode 100644 index 000000000..be3b84025 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/TypePatternMatcher.java @@ -0,0 +1,23 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver.tools; + +/** + * A compiled AspectJ type pattern that can be used to + * match against types at runtime. + */ +public interface TypePatternMatcher { + + /** + * Does this type pattern matcher match the + * given type (Class). + */ + public boolean matches(Class aClass); +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/UnsupportedPointcutPrimitiveException.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/UnsupportedPointcutPrimitiveException.java new file mode 100644 index 000000000..6e0bbd661 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/UnsupportedPointcutPrimitiveException.java @@ -0,0 +1,44 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +import org.aspectj.weaver.WeaverMessages; + +/** + * @author colyer + * + */ +public class UnsupportedPointcutPrimitiveException extends RuntimeException { + + private static final long serialVersionUID = 3258689888517043251L; + + private PointcutPrimitive unsupportedPrimitive; + private String pointcutExpression; + + public UnsupportedPointcutPrimitiveException(String pcExpression, PointcutPrimitive primitive) { + super(WeaverMessages.format(WeaverMessages.UNSUPPORTED_POINTCUT_PRIMITIVE,pcExpression,primitive.getName())); + this.pointcutExpression = pcExpression; + this.unsupportedPrimitive = primitive; + } + + /** + * @return Returns the unsupportedPrimitive. + */ + public PointcutPrimitive getUnsupportedPrimitive() { + return unsupportedPrimitive; + } + + public String getInvalidPointcutExpression() { + return pointcutExpression; + } + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/WeavingClassLoader.java b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/WeavingClassLoader.java new file mode 100644 index 000000000..84987e19b --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/tools/WeavingClassLoader.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation + * 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: + * Matthew Webster, Adrian Colyer, + * Martin Lippert initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.tools; + +import java.net.URL; + +/** + * An interface for weaving class loaders to provide callbacks for a + * WeavingAdaptor. + */ +public interface WeavingClassLoader extends GeneratedClassHandler { + + /** + * Returns the aspects to be used by a WeavingAdaptor to weave classes + * defined by the class loader. + * @return the aspects used for weaving classes. + */ + public URL[] getAspectURLs (); + +} diff --git a/org.aspectj.matcher/src/main/java/org/aspectj/weaver/weaver-messages.properties b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/weaver-messages.properties new file mode 100644 index 000000000..2745ae9e7 --- /dev/null +++ b/org.aspectj.matcher/src/main/java/org/aspectj/weaver/weaver-messages.properties @@ -0,0 +1,201 @@ +##################################################################### +# Copyright (c) 2004 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: +# Adrian Colyer - Initial version +##################################################################### + +# Messages output by the AspectJ Weaver + +# Pointcut related messages... +argsInDeclare=args() pointcut designator cannot be used in declare statement +cflowInDeclare=cflow{0}() pointcut designator cannot be used in declare statement +ifInDeclare=if() pointcut designator cannot be used in declare statement +thisOrTargetInDeclare={0}() pointcut designator cannot be used in declare statement +abstractPointcut={0} is abstract +abstractPointcutNotMadeConcrete=inherited abstract {0} is not made concrete in {1} +conflictingInheritedPointcuts=conflicting inherited pointcuts in {0} +circularPointcutDeclaration=circular pointcut declaration involving: {0} +cantFindPointcut=can''t find pointcut ''{0}'' on {1} +exactTypePatternRequired=exact type pattern required +pointcutNotVisible=pointcut ''{0}'' is not visible from type ''{1}'' - cannot override +cantBindType=can''t bind type name ''{0}'' +wildcardTypePatternNotAllowed=wildcard type pattern not allowed, must use type name +fieldCantBeVoid=fields cannot have a void type +noNewArrayJoinpointsByDefault=There are no join points for array construction unless -Xjoinpoints:arrayconstruction is specified +unsupportedPointcutPrimitive=Pointcut expression ''{0}'' contains unsupported pointcut primitive ''{1}'' +missingTypePreventsMatch="Unable to determine match at this join point because the type ''{0}'' cannot be found" + +# Declare parents messages... +decpObject=can''t change the parents of java.lang.Object +cantExtendSelf=type ''{0}''can not extend itself +interfaceExtendClass=interface can not extend a class +decpHierarchy=can only insert a class into hierarchy, but {0} is not a subtype of {1} + +# declare precedence messages... +multipleMatchesInPrecedence=multiple matches for {0}, matches both {1} and {2} +circularityInPrecedenceStar=circularity in declare precedence, ''*'' occurs more than once +nonAspectTypesInPrecedence=Non-aspect types can only be specified in a declare precedence statement when subtypes are included. Non-aspect type is : {0} +circularityInPrecedenceTwo=circularity in declare precedence, ''{0}'' matches two patterns + +# declare soft messages... +notThrowable={0} is not a subtype of Throwable + +# itd messages... +itdConsOnAspect=can''t declare constructor on an aspect +returnTypeMismatch=can''t override {0} with {1} return types don''t match +paramTypeMismatch=can''t override {0} with {1} parameter types don''t match +visibilityReduction=can''t override {0} with {1} visibility is reduced +cantOverrideFinalMember=can''t override final {0} +doesntThrow=overriden method doesn't throw {0} +overriddenStatic={0} cannot override {1}; overridden method is static +overridingStatic={0} cannot override {1}; overriding method is static +itdConflict=intertype declaration from {0} conflicts with intertype declaration: {1} from {2} +itdMemberConflict=inter-type declaration from {0} conflicts with existing member: {1} +itdNonExposedImplementor=type {0} must be accessible for weaving interface inter type declaration from aspect {1} +itdAbstractMustBePublicOnInterface=abstract intertype method declaration ''{0}'' on interface {1} must be declared public (compiler limitation) + +# advice messages... +nonVoidReturn=applying to join point that doesn''t return void: {0} +incompatibleReturnType=incompatible return type applying to {0} +cantThrowChecked=can''t throw checked exception ''{0}'' at this join point ''{1}'' +circularDependency=circular advice precedence: can''t determine precedence between two or more pieces of advice that apply to the same join point: {0} + +# aspect messages.. +missingPerClause=expected per clause on super aspect not found on {0} +wrongPerClause=wrong kind of per clause on super, expected {0} but found {1} + +# Reweavable messages... +alreadyWoven=class ''{0}'' is already woven and has not been built in reweavable mode +reweavableMode=weaver operating in reweavable mode. Need to verify any required types exist. +processingReweavable=processing reweavable type {0}: {1} +missingReweavableType=type {0} is needed by reweavable type {1} +verifiedReweavableType=successfully verified type {0} exists. Originates from {1} +aspectNeeded=aspect {0} is needed when using type {1} +reweavableAspectNotRegistered=aspect ''{0}'' woven into ''{1}'' must be defined to the weaver (placed on the aspectpath, or defined in an aop.xml file if using LTW). + +# The infamous and deserving a category all of its own... +cantFindType=can''t find type {0} +cantFindCoreType=can''t find critical required type {0} +cantFindTypeWithinpcd=Unable to find type {0} whilst processing within() pointcut at this source location +cftDuringAroundWeave=Can't find type {0} whilst applying around advice +cftDuringAroundWeavePreinit=Can't find type {0} whilst applying around advice to preinitialization join point +cftExceptionType=Can't find exception type {0} whilst processing advice +cftArgType=Can't find type {0} whilst processing args() pcd +cantFindParentType=can''t find type {0} needed to evaluate methods inherited by subtype {1} +cantFindParentTypeNoSub=can''t find type {0} when attempting to find the set of methods it declares +cantFindTypeFields=can''t find fields of missing type {0} +cantFindTypeSuperclass=can''t determine superclass of missing type {0} +cantFindTypeInterfaces=can''t determine implemented interfaces of missing type {0} +cantFindTypeMethods=can''t determine methods of missing type {0} +cantFindTypePointcuts=can''t determine pointcuts declared in missing type {0} +cantFindTypeModifiers=can''t determine modifiers of missing type {0} +cantFindTypeAnnotation=can''t determine annotations of missing type {0} +cantFindTypeAssignable=can''t determine whether missing type {0} is an instance of {1} +cantFindTypeCoerceable=can''t determine whether missing type {0} can be coerced from {1} +cantFindTypeJoinPoint=can''t find type {0} whilst determining signatures of call or execution join point for {1}, this may cause a pointcut to fail to match at this join point +cantFindTypeInterfaceMethods=can''t find type {0} whilst determining all methods of an implementing subtype, this may cause a pointcut to fail to match at a call or execution join point, or an illegal method override via an ITD to go undetected + +# Implementation limitations... +decpBinaryLimitation=can''t use declare parents to change superclass of binary form ''{0}'' (implementation limitation) +overwriteJSR45=overwriting JSR45 information for {0} (compiler limitation) +ifInPerClause=if() pointcut designator cannot be used directly in a per clause (compiler limitation). Create a named pointcut containing the if() and refer to it +ifLexicallyInCflow=if not supported lexically within cflow (compiler limitation) +onlyBeforeOnHandler=Only before advice is supported on handler join points (compiler limitation) +noAroundOnSynchronization=Around advice is not supported on the lock and unlock join points (compiler limitation) +aroundOnPreInit=around on pre-initialization not supported (compiler limitation) +aroundOnInit=around on initialization not supported (compiler limitation) +aroundOnInterfaceStaticInit=around on staticinitialization of interface ''{0}'' not supported (compiler limitation) +unsupportedAnnotationValueType=Compiler limitation: annotation value support not implemented for type {0} + +# Bytecode generation nasties... +problemGeneratingMethod=problem generating method {0}.{1} : {2} +classTooBig=The class {0} exceeds the maximum class size supported by the JVM (constant pool too big). + +# Classpath messages +zipfileEntryMissing=zipfile classpath entry does not exist: {0} +zipfileEntryInvalid=zipfile classpath entry is invalid: {0} <{1}> +directoryEntryMissing=directory classpath entry does not exist: {0} +outjarInInputPath=-outjar cannot be a member of -injars, -inpath or -aspectpath + +# Lint messages +problemLoadingXLint=problem loading Xlint properties file: {0}, {1} +unableToLoadXLintDefault=couldn''t load XlintDefault.properties +errorLoadingXLintDefault=problem loading XlintDefault.properties, {0} +invalidXLintKey=invalid Xlint key: {0} +invalidXLintMessageKind=invalid Xlint message kind (must be one of ignore, warning, error): {0} + +# Binding of formals +unboundFormalInPC=the parameter {0} is not bound in [all branches of] pointcut +ambiguousBindingInPC=the binding of parameter {0} is ambiguous in pointcut +ambiguousBindingInOrPC=ambiguous binding of parameter(s) {0} across ''||'' in pointcut +negationDoesntAllowBinding=cannot bind a parameter in a negated expression + +# Java5 + +# Enum +itdcOnEnumNotAllowed=can''t make inter-type constructor declarations on enum types +itdmOnEnumNotAllowed=can''t make inter-type method declarations on enum types +itdfOnEnumNotAllowed=can''t make inter-type field declarations on enum types +cantDecpOnEnumToImplInterface=can''t use declare parents to make enum type {0} implement an interface +cantDecpOnEnumToExtendClass=can''t use declare parents to alter supertype of enum type {0} +cantDecpToMakeEnumSupertype=can''t use declare parents to make ''java.lang.Enum'' the parent of type {0} + +# Annotation +itdcOnAnnotationNotAllowed=can''t make inter-type constructor declarations on annotation types +itdmOnAnnotationNotAllowed=can''t make inter-type method declarations on annotation types +itdfOnAnnotationNotAllowed=can''t make inter-type field declarations on annotation types +cantDecpOnAnnotationToImplInterface=can''t use declare parents to make annotation type {0} implement an interface +cantDecpOnAnnotationToExtendClass=can''t use declare parents to alter supertype of annotation type {0} +cantDecpToMakeAnnotationSupertype=can''t use declare parents to make ''java.lang.annotation.Annotation'' the parent of type {0} +incorrectTargetForDeclareAnnotation={0} is not a valid target for annotation {1}, this annotation can only be applied to these element types {2} + +referenceToNonAnnotationType=Type referred to is not an annotation type: {0} +bindingNonRuntimeRetentionAnnotation=Annotation type {0} does not have runtime retention +noMatchBecauseSourceRetention=Failing match because annotation ''{0}'' on type ''{1}'' has SOURCE retention. Matching allowed when RetentionPolicy is CLASS or RUNTIME + +# Annotation value +invalidAnnotationValue=Invalid annotation value ''{0}'', expected {1} value +unknownAnnotationValue=The annotation ''{0}'' does not define a value named ''{1}'' + +# Generics +cantDecpMultipleParameterizations=Cannot declare parent {0} onto type {1} since it already has {2} in its hierarchy +noParameterizedTypePatternInHandler=a parameterized type pattern may not be used in a handler pointcut expression +incorrectNumberOfTypeArguments=Type pattern does not match because the wrong number of type parameters are specified: Type {0} requires {1} parameter(s) +violatesTypeVariableBounds=Type {0} does not meet the specification for type parameter {1} ({2}) in generic type {3} +notAGenericType=Type pattern does not match because {0} is not a generic type +noStaticInitJPsForParameterizedTypes=no static initialization join points for parameterized types, use raw type instead +noParameterizedTypePatternInWithin=parameterized type pattern not supported by 'within', use a raw type pattern instead +noParameterizedTypesInThisAndTarget=parameterized types not supported for this and target pointcuts (erasure limitation) +noParameterizedTypesInGetAndSet=can't use parameterized type patterns for the declaring type of a get or set pointcut expression (use the raw type instead) +noInitJPsForParameterizedTypes=no [pre]initialization join points for parameterized types, use raw type instead +noGenericThrowables=invalid throws pattern: a generic class may not be a direct or indirect subclass of Throwable +noParameterizedDeclaringTypesWithinCode=can't use parameterized type patterns for the declaring type of a withincode pointcut expression (use the raw type instead) +noParameterizedDeclaringTypesInExecution=can't use parameterized type patterns for the declaring type of an execution pointcut expression (use the raw type instead) +noParameterizedDeclaringTypesInCall=can't use parameterized type patterns for the declaring type of a call pointcut expression (use the raw type instead) +noRawTypePointcutReferences=cannot use a raw type reference to refer to a pointcut in a generic type (use a parameterized reference instead) + +hasMemberNotEnabled=the type pattern {0} can only be used when the -XhasMember option is set +mustKeepOverweavingOnceStart=the type {0} was previously subject to overweaving and after that can only be woven again in overweaving mode + +# Java5 features used in pre-Java 5 environment +atannotationNeedsJava5=the @annotation pointcut expression is only supported at Java 5 compliance level or above +atwithinNeedsJava5=the @within pointcut expression is only supported at Java 5 compliance level or above +atwithincodeNeedsJava5=the @withincode pointcut expression is only supported at Java 5 compliance level or above +atthisNeedsJava5=the @this pointcut expression is only supported at Java 5 compliance level or above +attargetNeedsJava5=the @target pointcut expression is only supported at Java 5 compliance level or above +atargsNeedsJava5=the @args pointcut expression is only supported at Java 5 compliance level or above +declareAtTypeNeedsJava5=declare @type is only supported at Java 5 compliance level or above +declareAtMethodNeedsJava5=declare @method is only supported at Java 5 compliance level or above +declareAtFieldNeedsJava5=declare @field is only supported at Java 5 compliance level or above +declareAtConsNeedsJava5=declare @constructor is only supported at Java 5 compliance level or above +annotationsRequireJava5=annotation type patterns are only supported at Java 5 compliance level or above + +# @AspectJ +returningFormalNotDeclaredInAdvice=the last parameter of this advice must be named ''{0}'' to bind the returning value +thrownFormalNotDeclaredInAdvice=the last parameter of this advice must be named ''{0}'' and be of a subtype of Throwable
\ No newline at end of file |