blob: 7812271417027f38c2121bfec8b122071a2e3655 [file] [log] [blame]
/* *******************************************************************
* 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;
}
}
}