/* *******************************************************************
 * 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).

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, Jim Hugunin
/** * 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
/** * 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
Params:
  • constantPool –
/** * 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.
/** * 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;
Params:
  • lexicalPosition – must be greater than the lexicalPosition of any advice declared before this one in an aspect, otherwise, it can be any value.
/** * @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; } } }