You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
308 lines
10 KiB
308 lines
10 KiB
7 years ago
|
/* *******************************************************************
|
||
|
* 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
|
||
|
* ******************************************************************/
|
||
|
|
||
7 years ago
|
package com.fr.third.aspectj.weaver;
|
||
7 years ago
|
|
||
|
import java.io.File;
|
||
|
import java.io.IOException;
|
||
|
import java.util.Collection;
|
||
|
import java.util.HashSet;
|
||
|
import java.util.Map;
|
||
|
import java.util.Set;
|
||
|
|
||
7 years ago
|
import com.fr.third.aspectj.bridge.IMessage;
|
||
|
import com.fr.third.aspectj.bridge.ISourceLocation;
|
||
|
import com.fr.third.aspectj.bridge.MessageUtil;
|
||
|
import com.fr.third.aspectj.bridge.SourceLocation;
|
||
|
import com.fr.third.aspectj.util.FuzzyBoolean;
|
||
|
import com.fr.third.aspectj.util.PartialOrder;
|
||
|
import com.fr.third.aspectj.weaver.patterns.PerClause;
|
||
|
import com.fr.third.aspectj.weaver.patterns.Pointcut;
|
||
|
import com.fr.third.aspectj.weaver.patterns.TypePattern;
|
||
7 years ago
|
|
||
|
/**
|
||
|
* 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;
|
||
|
// }
|
||
|
|
||
|
}
|