org.aspectj.weaver.ResolvedTypeMunger.java Source code

Java tutorial

Introduction

Here is the source code for org.aspectj.weaver.ResolvedTypeMunger.java

Source

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

}