/* ******************************************************************* * 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 * @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 dataCompressor */ 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 lineBreak : lineBreaks) { s.writeShort(lineBreak - previous); previous = lineBreak; } } 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 start must be greater than the start 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; } } }