| /* ******************************************************************* |
| * 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(); |
| } |
| |
| } |