Package org.aspectj.weaver.bcel

Source Code of org.aspectj.weaver.bcel.LazyClassGen

/* *******************************************************************
* Copyright (c) 2002 Contributors
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
* Contributors:
*     PARC                 initial implementation
*     Andy Clement  6Jul05 generics - signature attribute
* ******************************************************************/


package org.aspectj.weaver.bcel;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.Attribute;
import org.aspectj.apache.bcel.classfile.ConstantUtf8;
import org.aspectj.apache.bcel.classfile.Field;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.classfile.Method;
import org.aspectj.apache.bcel.classfile.Signature;
import org.aspectj.apache.bcel.classfile.Unknown;
import org.aspectj.apache.bcel.classfile.annotation.Annotation;
import org.aspectj.apache.bcel.generic.ClassGen;
import org.aspectj.apache.bcel.generic.ConstantPoolGen;
import org.aspectj.apache.bcel.generic.FieldGen;
import org.aspectj.apache.bcel.generic.InstructionConstants;
import org.aspectj.apache.bcel.generic.InstructionFactory;
import org.aspectj.apache.bcel.generic.InstructionHandle;
import org.aspectj.apache.bcel.generic.InstructionList;
import org.aspectj.apache.bcel.generic.ObjectType;
import org.aspectj.apache.bcel.generic.PUSH;
import org.aspectj.apache.bcel.generic.RETURN;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.apache.bcel.generic.annotation.AnnotationGen;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.SourceLocation;
import org.aspectj.util.CollectionUtil;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.NameMangler;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.TypeVariable;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.WeaverStateInfo;
import org.aspectj.weaver.World;
import org.aspectj.weaver.AjAttribute.WeaverVersionInfo;

/**
* Lazy lazy lazy.
* We don't unpack the underlying class unless necessary.  Things
* like new methods and annotations accumulate in here until they
* must be written out, don't add them to the underlying MethodGen!
* Things are slightly different if this represents an Aspect.
*/
public final class LazyClassGen {
 
  int highestLineNumber = 0; // ---- JSR 45 info
 
  private SortedMap /* <String, InlinedSourceFileInfo> */ inlinedFiles = new TreeMap();
 
  private  boolean regenerateGenericSignatureAttribute = false;
 
  private BcelObjectType myType; // XXX is not set for types we create
  private ClassGen myGen;
  private ConstantPoolGen constantPoolGen;
  private World world;
    private String packageName = null;

    private List /*LazyMethodGen*/ methodGens  = new ArrayList();
    private List /*LazyClassGen*/  classGens   = new ArrayList();
    private List /*AnnotationGen*/ annotations = new ArrayList();
    private int childCounter = 0;
   
    private InstructionFactory fact;
   
  private boolean isSerializable = false;
  private boolean hasSerialVersionUIDField = false;
  private boolean hasClinit = false;
 
  // ---
 
  static class InlinedSourceFileInfo {
    int highestLineNumber;
    int offset;  // calculated
   
    InlinedSourceFileInfo(int highestLineNumber) {
      this.highestLineNumber = highestLineNumber;
    }
  } 
 
  void addInlinedSourceFileInfo(String fullpath, int highestLineNumber) {
    Object o = inlinedFiles.get(fullpath);
    if (o != null) {
      InlinedSourceFileInfo info = (InlinedSourceFileInfo) o;
      if (info.highestLineNumber < highestLineNumber) {
        info.highestLineNumber = highestLineNumber;
      }
    } else {
      inlinedFiles.put(fullpath, new InlinedSourceFileInfo(highestLineNumber));
    }
  }
 
  void calculateSourceDebugExtensionOffsets() {
    int i = roundUpToHundreds(highestLineNumber);
    for (Iterator iter = inlinedFiles.values().iterator(); iter.hasNext();) {
      InlinedSourceFileInfo element = (InlinedSourceFileInfo) iter.next();
      element.offset = i;
      i = roundUpToHundreds(i + element.highestLineNumber);
    }
  }

  private static int roundUpToHundreds(int i) {
    return ((i / 100) + 1) * 100;
  }
 
  int getSourceDebugExtensionOffset(String fullpath) {
    return ((InlinedSourceFileInfo) inlinedFiles.get(fullpath)).offset;
  }
 
  private Unknown getSourceDebugExtensionAttribute() {
    int nameIndex = constantPoolGen.addUtf8("SourceDebugExtension");
    String data = getSourceDebugExtensionString();
    //System.err.println(data);
    byte[] bytes = Utility.stringToUTF(data);
    int length = bytes.length;

    return new Unknown(nameIndex, length, bytes, constantPoolGen.getConstantPool());   
 

//  private LazyClassGen() {}
//  public static void main(String[] args) {
//    LazyClassGen m = new LazyClassGen();
//    m.highestLineNumber = 37;
//    m.inlinedFiles.put("boo/baz/foo.java", new InlinedSourceFileInfo( 83));
//    m.inlinedFiles.put("boo/barz/foo.java", new InlinedSourceFileInfo(292));
//    m.inlinedFiles.put("boo/baz/moo.java", new InlinedSourceFileInfo(128)); 
//    m.calculateSourceDebugExtensionOffsets();
//    System.err.println(m.getSourceDebugExtensionString());     
//  }
 
  // For the entire pathname, we're using package names.  This is probably wrong.
  private String getSourceDebugExtensionString() {
    StringBuffer out = new StringBuffer();
    String myFileName = getFileName();
    // header section
    out.append("SMAP\n");
    out.append(myFileName);
    out.append("\nAspectJ\n");
    // stratum section
    out.append("*S AspectJ\n");
    // file section
    out.append("*F\n");
    out.append("1 ");
    out.append(myFileName);
    out.append("\n");
    int i = 2;
    for (Iterator iter = inlinedFiles.keySet().iterator(); iter.hasNext();) {
      String element = (String) iter.next();
      int ii = element.lastIndexOf('/');
      if (ii == -1) {
        out.append(i++); out.append(' ');
        out.append(element); out.append('\n');       
      } else {
        out.append("+ "); out.append(i++); out.append(' ');
        out.append(element.substring(ii+1)); out.append('\n');
        out.append(element); out.append('\n');             
      }
    }
    // emit line section
    out.append("*L\n");
    out.append("1#1,");
    out.append(highestLineNumber);
    out.append(":1,1\n");
    i = 2
    for (Iterator iter = inlinedFiles.values().iterator(); iter.hasNext();) {
      InlinedSourceFileInfo element = (InlinedSourceFileInfo) iter.next();
      out.append("1#");
      out.append(i++); out.append(',');
      out.append(element.highestLineNumber); out.append(":");
      out.append(element.offset + 1); out.append(",1\n");
   
    // end section
    out.append("*E\n");
    // and finish up...
    return out.toString();
  }
 
  // ---- end JSR45-related stuff
 
    /** Emit disassembled class and newline to out */
    public static void disassemble(String path, String name, PrintStream out)
        throws IOException {
        if (null == out) {
            return;
        }
        //out.println("classPath: " + classPath);

        BcelWorld world = new BcelWorld(path);

        LazyClassGen clazz = new LazyClassGen(BcelWorld.getBcelObjectType(world.resolve(name)));
        clazz.print(out);
        out.println();
    }


    public int getNewGeneratedNameTag() {
        return childCounter++;
    }
   
    // ----

    public LazyClassGen(
        String class_name,
        String super_class_name,
        String file_name,
        int access_flags,
        String[] interfaces,
        World world)
    {
        myGen = new ClassGen(class_name, super_class_name, file_name, access_flags, interfaces);
    constantPoolGen = myGen.getConstantPool();
        fact = new InstructionFactory(myGen, constantPoolGen);
        regenerateGenericSignatureAttribute = true;
        this.world = world;
    }

  //Non child type, so it comes from a real type in the world.
    public LazyClassGen(BcelObjectType myType) {
      myGen = new ClassGen(myType.getJavaClass());
      constantPoolGen = myGen.getConstantPool();
    fact = new InstructionFactory(myGen, constantPoolGen);       
    this.myType = myType;
    this.world = myType.getResolvedTypeX().getWorld();

    /* Does this class support serialization */
    if (implementsSerializable(getType())) {
      isSerializable = true;      

//      ResolvedMember[] fields = getType().getDeclaredFields();
//      for (int i = 0; i < fields.length; i++) {
//        ResolvedMember field = fields[i];
//        if (field.getName().equals("serialVersionUID")
//          && field.isStatic() && field.getType().equals(ResolvedType.LONG)) {
//          hasSerialVersionUIDField = true;         
//        }
//      }
      hasSerialVersionUIDField = hasSerialVersionUIDField(getType());         

      ResolvedMember[] methods = getType().getDeclaredMethods();
      for (int i = 0; i < methods.length; i++) {
        ResolvedMember method = methods[i];
        if (method.getName().equals("<clinit>")) {
          hasClinit = true;         
        }
      }
    }

        Method[] methods = myGen.getMethods();
        for (int i = 0; i < methods.length; i++) {
            addMethodGen(new LazyMethodGen(methods[i], this));
        }
    }

  public static boolean hasSerialVersionUIDField (ResolvedType type) {

    ResolvedMember[] fields = type.getDeclaredFields();
    for (int i = 0; i < fields.length; i++) {
      ResolvedMember field = fields[i];
      if (field.getName().equals("serialVersionUID")
        && field.isStatic() && field.getType().equals(ResolvedType.LONG)) {
        return true;         
      }
    }
   
    return false;
  }

//  public void addAttribute(Attribute i) {
//    myGen.addAttribute(i);
//  }

  // ----

    public String getInternalClassName() {
        return getConstantPoolGen().getConstantPool().getConstantString(
            myGen.getClassNameIndex(),
            Constants.CONSTANT_Class);

    }

  public String getInternalFileName() {
    String str = getInternalClassName();
    int index = str.lastIndexOf('/');
    if (index == -1) {
      return getFileName();
    } else {
      return str.substring(0, index + 1) + getFileName();
   
  }


    public File getPackagePath(File root) {
        String str = getInternalClassName();
        int index = str.lastIndexOf('/');
        if (index == -1)
            return root;
        return new File(root, str.substring(0, index));
    }
   
    /** Returns the packagename - if its the default package we return an empty string
     */
    public String getPackageName() {
      if (packageName!=null) return packageName;
      String str = getInternalClassName();
      int index = str.indexOf("<");
      if (index!=-1) str = str.substring(0,index); // strip off the generics guff
      index= str.lastIndexOf("/");
      if (index==-1) return "";
      return str.substring(0,index).replace('/','.');
    }

    public String getClassId() {
        String str = getInternalClassName();
        int index = str.lastIndexOf('/');
        if (index == -1)
            return str;
        return str.substring(index + 1);
    }


  public void addMethodGen(LazyMethodGen gen) {
    //assert gen.getClassName() == super.getClassName();
    methodGens.add(gen);
    if (highestLineNumber < gen.highestLineNumber) highestLineNumber = gen.highestLineNumber;
  }

  public void addMethodGen(LazyMethodGen gen, ISourceLocation sourceLocation) {
    addMethodGen(gen);
    if (!gen.getMethod().isPrivate()) {
      warnOnAddedMethod(gen.getMethod(),sourceLocation);
    }
  }
 

  public void errorOnAddedField (Field field, ISourceLocation sourceLocation) {
    if (isSerializable && !hasSerialVersionUIDField) {
      getWorld().getLint().serialVersionUIDBroken.signal(
        new String[] {
          myType.getResolvedTypeX().getName().toString(),
          field.getName()
        },
        sourceLocation,
        null);              
    }
  }

  public void warnOnAddedInterface (String name, ISourceLocation sourceLocation) {
    warnOnModifiedSerialVersionUID(sourceLocation,"added interface " + name);
  }

  public void warnOnAddedMethod (Method method, ISourceLocation sourceLocation) {
    warnOnModifiedSerialVersionUID(sourceLocation,"added non-private method " + method.getName());
  }

  public void warnOnAddedStaticInitializer (Shadow shadow, ISourceLocation sourceLocation) {
    if (!hasClinit) {
      warnOnModifiedSerialVersionUID(sourceLocation,"added static initializer");
    }
  }

  public void warnOnModifiedSerialVersionUID (ISourceLocation sourceLocation, String reason) {
    if (isSerializable && !hasSerialVersionUIDField)
    getWorld().getLint().needsSerialVersionUIDField.signal(
      new String[] {
        myType.getResolvedTypeX().getName().toString(),
        reason
      },
      sourceLocation,
      null);              
  }

  public World getWorld () {
    return world;
  }

    public List getMethodGens() {
        return methodGens; //???Collections.unmodifiableList(methodGens);
    }
   
    // FIXME asc Should be collection returned here
    public Field[] getFieldGens() {
      return myGen.getFields();
    }
   
    public Field getField(String name) {
      Field[] allFields = myGen.getFields();
      if (allFields==null) return null;
      for (int i = 0; i < allFields.length; i++) {
      Field field = allFields[i];
      if (field.getName().equals(name)) return field;
    }
      return null;
    }
   
    // FIXME asc How do the ones on the underlying class surface if this just returns new ones added?
    // FIXME asc ...although no one calls this right now !
    public List getAnnotations() {
      return annotations;
    }

    private void writeBack(BcelWorld world) {
        if (getConstantPoolGen().getSize() > Short.MAX_VALUE) {
            reportClassTooBigProblem();
          return;
        }
       
        if (annotations.size()>0) {
          for (Iterator iter = annotations.iterator(); iter.hasNext();) {
        AnnotationGen element = (AnnotationGen) iter.next();
        myGen.addAnnotation(element);
      }
//      Attribute[] annAttributes  = org.aspectj.apache.bcel.classfile.Utility.getAnnotationAttributes(getConstantPoolGen(),annotations);
//        for (int i = 0; i < annAttributes.length; i++) {
//      Attribute attribute = annAttributes[i];
//      System.err.println("Adding attribute for "+attribute);
//      myGen.addAttribute(attribute);
//    }
        }
       
        // Add a weaver version attribute to the file being produced (if necessary...)
        boolean hasVersionAttribute = false;       
        Attribute[] attrs = myGen.getAttributes();
        for (int i = 0; i < attrs.length && !hasVersionAttribute; i++) {
      Attribute attribute = attrs[i];
      if (attribute.getName().equals("org.aspectj.weaver.WeaverVersion")) hasVersionAttribute=true;
    }       
        if (!hasVersionAttribute)
          myGen.addAttribute(BcelAttributes.bcelAttribute(new AjAttribute.WeaverVersionInfo(),getConstantPoolGen()));

        if (myType != null && myType.getWeaverState() != null) {
      myGen.addAttribute(BcelAttributes.bcelAttribute(
        new AjAttribute.WeaverState(myType.getWeaverState()),
        getConstantPoolGen()));
      }

        //FIXME ATAJ needed only for slow Aspects.aspectOf() - keep or remove
        //make a lot of test fail since the test compare weaved class file
        // based on some test data as text files...
//        if (!myGen.isInterface()) {
//          addAjClassField();
//        }

      addAjcInitializers();
     
        int len = methodGens.size();
        myGen.setMethods(new Method[0]);
       
        calculateSourceDebugExtensionOffsets();
        for (int i = 0; i < len; i++) {
            LazyMethodGen gen = (LazyMethodGen) methodGens.get(i);
            // we skip empty clinits
            if (isEmptyClinit(gen)) continue;
            myGen.addMethod(gen.getMethod());
        }
    if (inlinedFiles.size() != 0) {
      if (hasSourceDebugExtensionAttribute(myGen)) {
        world.showMessage(
          IMessage.WARNING,
          WeaverMessages.format(WeaverMessages.OVERWRITE_JSR45,getFileName()),
          null,
          null);
      }
      // 17Feb05 - ASC - Skip this for now - it crashes IBM 1.4.2 jvms (pr80430).  Will be revisited when contents
      // of attribute are confirmed to be correct.
      // myGen.addAttribute(getSourceDebugExtensionAttribute());
    }
   
    fixupGenericSignatureAttribute();
    }

    /**
     * When working with 1.5 generics, a signature attribute is attached to the type which indicates
     * how it was declared.  This routine ensures the signature attribute for what we are about
     * to write out is correct.  Basically its responsibilities are:
     *   1. Checking whether the attribute needs changing (i.e. did weaving change the type hierarchy)
     *   2. If it did, removing the old attribute
     *   3. Check if we need an attribute at all, are we generic? are our supertypes parameterized/generic?
     *   4. Build the new attribute which includes all typevariable, supertype and superinterface information
     */
  private void fixupGenericSignatureAttribute () {
   
    if (getWorld() != null && !getWorld().isInJava5Mode()) return;
   
    // TODO asc generics Temporarily assume that types we generate dont need a signature attribute (closure/etc).. will need revisiting no doubt...
    if (myType==null) return;
     
    // 1. Has anything changed that would require us to modify this attribute?
    if (!regenerateGenericSignatureAttribute) return;
   
    // 2. Find the old attribute
    Signature sigAttr = null;
    if (myType!=null) { // if null, this is a type built from scratch, it won't already have a sig attribute
      Attribute[] as = myGen.getAttributes();
      for (int i = 0; i < as.length; i++) {
        Attribute attribute = as[i];
        if (attribute.getName().equals("Signature")) sigAttr = (Signature)attribute;
      }
    }
   
    // 3. Do we need an attribute?
    boolean needAttribute = false;
    if (sigAttr!=null) needAttribute = true; // If we had one before, we definetly still need one as types can't be 'removed' from the hierarchy
   
    // check the interfaces
    if (!needAttribute) {
      if (myType==null) {
        boolean stop = true;
      }
      ResolvedType[] interfaceRTXs = myType.getDeclaredInterfaces();
      for (int i = 0; i < interfaceRTXs.length; i++) {
        ResolvedType typeX = interfaceRTXs[i];
        if (typeX.isGenericType() || typeX.isParameterizedType())  needAttribute = true;
      }
   
      // check the supertype
      ResolvedType superclassRTX = myType.getSuperclass();
      if (superclassRTX.isGenericType() || superclassRTX.isParameterizedType()) needAttribute = true;
    }
   
    if (needAttribute) {
      StringBuffer signature = new StringBuffer();
      // first, the type variables...
      TypeVariable[] tVars = myType.getTypeVariables();
      if (tVars.length>0) {
        signature.append("<");
        for (int i = 0; i < tVars.length; i++) {
          TypeVariable variable = tVars[i];
          if (i!=0) signature.append(",");
          signature.append(variable.getSignature());
        }
        signature.append(">");
      }
      // now the supertype
      String supersig = myType.getSuperclass().getSignatureForAttribute();
      signature.append(supersig);
      ResolvedType[] interfaceRTXs = myType.getDeclaredInterfaces();
      for (int i = 0; i < interfaceRTXs.length; i++) {
        String s = interfaceRTXs[i].getSignatureForAttribute();
        signature.append(s);
      }
      if (sigAttr!=null) myGen.removeAttribute(sigAttr);
      myGen.addAttribute(createSignatureAttribute(signature.toString()));
    }
  }
 
  /**
   * Helper method to create a signature attribute based on a string signature:
   *  e.g. "Ljava/lang/Object;LI<Ljava/lang/Double;>;"
   */
  private Signature createSignatureAttribute(String signature) {
    int nameIndex = constantPoolGen.addUtf8("Signature");
    int sigIndex  = constantPoolGen.addUtf8(signature);
    return new Signature(nameIndex,2,sigIndex,constantPoolGen.getConstantPool());
  }

  /**
   *
   */
  private void reportClassTooBigProblem() {
    // PR 59208
    // we've generated a class that is just toooooooooo big (you've been generating programs
    // again haven't you? come on, admit it, no-one writes classes this big by hand).
    // create an empty myGen so that we can give back a return value that doesn't upset the
    // rest of the process.
    myGen = new ClassGen(myGen.getClassName(), myGen.getSuperclassName(),
            myGen.getFileName(), myGen.getAccessFlags(), myGen.getInterfaceNames());
    // raise an error against this compilation unit.
    getWorld().showMessage(
        IMessage.ERROR,
        WeaverMessages.format(WeaverMessages.CLASS_TOO_BIG,
                          this.getClassName()),
          new SourceLocation(new File(myGen.getFileName()),0), null
          );
  }

  private static boolean hasSourceDebugExtensionAttribute(ClassGen gen) {
    ConstantPoolGen pool = gen.getConstantPool();
    Attribute[] attrs = gen.getAttributes();
    for (int i = 0; i < attrs.length; i++) {
      if ("SourceDebugExtension"
        .equals(((ConstantUtf8) pool.getConstant(attrs[i].getNameIndex())).getBytes())) {
        return true;
      }
    }
    return false;
  }

    public JavaClass getJavaClass(BcelWorld world) {
        writeBack(world);
        return myGen.getJavaClass();
    }
   
    public byte [] getJavaClassBytesIncludingReweavable(BcelWorld world){
        writeBack(world);
        byte [] wovenClassFileData = myGen.getJavaClass().getBytes();
        WeaverStateInfo wsi = myType.getWeaverState();//getOrCreateWeaverStateInfo();
        if(wsi != null && wsi.isReweavable()){ // && !reweavableDataInserted
            //reweavableDataInserted = true;
            return wsi.replaceKeyWithDiff(wovenClassFileData);
        } else{
            return wovenClassFileData;
        }
    }

    public void addGeneratedInner(LazyClassGen newClass) {
        classGens.add(newClass);
    }
   
    public void addInterface(UnresolvedType typeX, ISourceLocation sourceLocation) {
      regenerateGenericSignatureAttribute = true;
      myGen.addInterface(typeX.getRawName());
        if (!typeX.equals(UnresolvedType.SERIALIZABLE))
      warnOnAddedInterface(typeX.getName(),sourceLocation);
    }
   
  public void setSuperClass(ResolvedType typeX) {
      regenerateGenericSignatureAttribute = true;
      myType.addParent(typeX); // used for the attribute
      if (typeX.getGenericType()!=null) typeX = typeX.getGenericType();
    myGen.setSuperclassName(typeX.getName()); // used in the real class data
   }
   
    public String getSuperClassname() {
        return myGen.getSuperclassName();  
    }

    // FIXME asc not great that some of these ask the gen and some ask the type ! (see the related setters too)
  public ResolvedType getSuperClass() {
    return myType.getSuperclass();
  }
   
    public String[] getInterfaceNames() {
      return myGen.getInterfaceNames();
    }


    // non-recursive, may be a bug, ha ha.
    private List getClassGens() {
        List ret = new ArrayList();
        ret.add(this);
        ret.addAll(classGens);

        return ret;
    }


  public List getChildClasses(BcelWorld world) {
    if (classGens.isEmpty()) return Collections.EMPTY_LIST;
    List ret = new ArrayList();
    for (Iterator i = classGens.iterator(); i.hasNext();) {
      LazyClassGen clazz = (LazyClassGen) i.next();
      byte[] bytes = clazz.getJavaClass(world).getBytes();
      String name = clazz.getName();
      int index = name.lastIndexOf('$');
      // XXX this could be bad, check use of dollar signs.
      name = name.substring(index+1);
      ret.add(new UnwovenClassFile.ChildClass(name, bytes));
    }
    return ret;
  }

  public String toString() {
    return toShortString();
  }

    public String toShortString() {
        String s =
            org.aspectj.apache.bcel.classfile.Utility.accessToString(myGen.getAccessFlags(), true);
        if (s != "")
            s += " ";
        s += org.aspectj.apache.bcel.classfile.Utility.classOrInterface(myGen.getAccessFlags());
        s += " ";
        s += myGen.getClassName();
        return s;
    }

    public String toLongString() {
        ByteArrayOutputStream s = new ByteArrayOutputStream();
        print(new PrintStream(s));
        return new String(s.toByteArray());
    }

    public void print() { print(System.out); }
       
    public void print(PrintStream out) {
        List classGens = getClassGens();
        for (Iterator iter = classGens.iterator(); iter.hasNext();) {
            LazyClassGen element = (LazyClassGen) iter.next();
            element.printOne(out);
            if (iter.hasNext()) out.println();
        }
    }

    private void printOne(PrintStream out) {
        out.print(toShortString());
        out.print(" extends ");
        out.print(
            org.aspectj.apache.bcel.classfile.Utility.compactClassName(
                myGen.getSuperclassName(),
                false));

        int size = myGen.getInterfaces().length;

        if (size > 0) {
            out.print(" implements ");
            for (int i = 0; i < size; i++) {
                out.print(myGen.getInterfaceNames()[i]);
                if (i < size - 1)
                    out.print(", ");
            }
        }
        out.print(":");
        out.println();
        // XXX make sure to pass types correctly around, so this doesn't happen.
        if (myType != null) {
          myType.printWackyStuff(out);
        }
        Field[] fields = myGen.getFields();
        for (int i = 0, len = fields.length; i < len; i++) {
            out.print("  ");
            out.println(fields[i]);
        }
        List methodGens = getMethodGens();
        for (Iterator iter = methodGens.iterator(); iter.hasNext();) {
            LazyMethodGen gen = (LazyMethodGen) iter.next();
            // we skip empty clinits
            if (isEmptyClinit(gen)) continue;
            gen.print(out, (myType != null ? myType.getWeaverVersionAttribute() : WeaverVersionInfo.UNKNOWN));
            if (iter.hasNext()) out.println();
        }
//        out.println("  ATTRIBS: " + Arrays.asList(myGen.getAttributes()));
       
        out.println("end " + toShortString());
    }
   
    private boolean isEmptyClinit(LazyMethodGen gen) {
      if (!gen.getName().equals("<clinit>")) return false;
      //System.err.println("checking clinig: " + gen);
      InstructionHandle start = gen.getBody().getStart();
      while (start != null) {
        if (Range.isRangeHandle(start) || (start.getInstruction() instanceof RETURN)) {
          start = start.getNext();
        } else {
          return false;
        }
      }
     
      return true;
    }

    public ConstantPoolGen getConstantPoolGen() {
        return constantPoolGen;
    }
   
    public String getName() {
        return myGen.getClassName();
    }

  public boolean isWoven() {
    return myType.getWeaverState() != null;
  }
 
  public boolean isReweavable() {
    if (myType.getWeaverState()==null) return true;
        return myType.getWeaverState().isReweavable();
  }
 
  public Set getAspectsAffectingType() {
    if (myType.getWeaverState()==null) return null;
    return myType.getWeaverState().getAspectsAffectingType();
  }
   
  public WeaverStateInfo getOrCreateWeaverStateInfo(boolean inReweavableMode) {
    WeaverStateInfo ret = myType.getWeaverState();
    if (ret != null) return ret;
    ret = new WeaverStateInfo(inReweavableMode);
    myType.setWeaverState(ret);
    return ret;
  }

    public InstructionFactory getFactory() {
        return fact;
    }

    public LazyMethodGen getStaticInitializer() {
        for (Iterator i = methodGens.iterator(); i.hasNext();) {
            LazyMethodGen gen = (LazyMethodGen) i.next();
      if (gen.getName().equals("<clinit>")) return gen;
        }
        LazyMethodGen clinit = new LazyMethodGen(
          Modifier.STATIC,
          Type.VOID,
          "<clinit>",
          new Type[0],
          CollectionUtil.NO_STRINGS,
          this);
         clinit.getBody().insert(InstructionConstants.RETURN);
        methodGens.add(clinit);
        return clinit;
    }
   
    public LazyMethodGen getAjcPreClinit() {
        for (Iterator i = methodGens.iterator(); i.hasNext();) {
            LazyMethodGen gen = (LazyMethodGen) i.next();
      if (gen.getName().equals(NameMangler.AJC_PRE_CLINIT_NAME)) return gen;
        }
        LazyMethodGen ajcClinit = new LazyMethodGen(
          Modifier.STATIC,
          Type.VOID,
          NameMangler.AJC_PRE_CLINIT_NAME,
          new Type[0],
          CollectionUtil.NO_STRINGS,
          this);
         ajcClinit.getBody().insert(InstructionConstants.RETURN);
        methodGens.add(ajcClinit);
       
        getStaticInitializer().getBody().insert(Utility.createInvoke(getFactory(), ajcClinit));
        return ajcClinit;
    }
   
   
   
    // reflective thisJoinPoint support
    Map/*BcelShadow, Field*/ tjpFields = new HashMap();
    public static final ObjectType proceedingTjpType =
      new ObjectType("org.aspectj.lang.ProceedingJoinPoint");
    public static final ObjectType tjpType =
      new ObjectType("org.aspectj.lang.JoinPoint");
    public static final ObjectType staticTjpType =
      new ObjectType("org.aspectj.lang.JoinPoint$StaticPart");
    public static final ObjectType enclosingStaticTjpType =
      new ObjectType("org.aspectj.lang.JoinPoint$EnclosingStaticPart");
    private static final ObjectType sigType =
      new ObjectType("org.aspectj.lang.Signature");
//    private static final ObjectType slType =
//      new ObjectType("org.aspectj.lang.reflect.SourceLocation");
    private static final ObjectType factoryType =
      new ObjectType("org.aspectj.runtime.reflect.Factory");
    private static final ObjectType classType =
      new ObjectType("java.lang.Class");

    public Field getTjpField(BcelShadow shadow, final boolean isEnclosingJp) {
      Field ret = (Field)tjpFields.get(shadow);
      if (ret != null) return ret;
     
    int modifiers = Modifier.STATIC | Modifier.FINAL;
   
    // XXX - Do we ever inline before or after advice? If we do, then we
    // better include them in the check below. (or just change it to
    // shadow.getEnclosingMethod().getCanInline())
   
    // If the enclosing method is around advice, we could inline the join point
    // that has led to this shadow.  If we do that then the TJP we are creating
    // here must be PUBLIC so it is visible to the type in which the
    // advice is inlined. (PR71377)
    LazyMethodGen encMethod = shadow.getEnclosingMethod();
    boolean shadowIsInAroundAdvice = false;
    if (encMethod!=null && encMethod.getName().startsWith(NameMangler.PREFIX+"around")) {
      shadowIsInAroundAdvice = true;
    }
   
    if (getType().isInterface() || shadowIsInAroundAdvice) {
      modifiers |= Modifier.PUBLIC;
    }
    else {
      modifiers |= Modifier.PRIVATE;
    }
    ObjectType jpType = null;
    if (world.isTargettingAspectJRuntime12()) { // TAG:SUPPORTING12: We didn't have different staticjp types in 1.2
      jpType = staticTjpType;
    } else {
      jpType = isEnclosingJp?enclosingStaticTjpType:staticTjpType;
    }
    ret = new FieldGen(modifiers,jpType,"ajc$tjp_" + tjpFields.size(),getConstantPoolGen()).getField();
      addField(ret);
      tjpFields.put(shadow, ret);
      return ret;
    }


    //FIXME ATAJ needed only for slow Aspects.aspectOf - keep or remove
//    private void addAjClassField() {
//    // Andy: Why build it again??
//        Field ajClassField = new FieldGen(
//                Modifier.PRIVATE | Modifier.FINAL | Modifier.STATIC,
//                classType,
//                "aj$class",
//                getConstantPoolGen()).getField();
//        addField(ajClassField);
//
//        InstructionList il = new InstructionList();
//        il.append(new PUSH(getConstantPoolGen(), getClassName()));
//        il.append(fact.createInvoke("java.lang.Class", "forName", classType,
//                    new Type[] {Type.STRING}, Constants.INVOKESTATIC));
//        il.append(fact.createFieldAccess(getClassName(), ajClassField.getName(),
//            classType, Constants.PUTSTATIC));
//
//        getStaticInitializer().getBody().insert(il);
//    }

    private void addAjcInitializers() {
      if (tjpFields.size() == 0) return;
     
      InstructionList il = initializeAllTjps();

      getStaticInitializer().getBody().insert(il);
    }
   
   
    private InstructionList initializeAllTjps() {
      InstructionList list = new InstructionList();
      InstructionFactory fact = getFactory();
     
      // make a new factory
      list.append(fact.createNew(factoryType));
      list.append(InstructionFactory.createDup(1));
     
      list.append(new PUSH(getConstantPoolGen(), getFileName()));
     
      // load the current Class object
      //XXX check that this works correctly for inners/anonymous
      list.append(new PUSH(getConstantPoolGen(), getClassName()));
      //XXX do we need to worry about the fact the theorectically this could throw
      //a ClassNotFoundException
      list.append(fact.createInvoke("java.lang.Class", "forName", classType,
            new Type[] {Type.STRING}, Constants.INVOKESTATIC));
     
      list.append(fact.createInvoke(factoryType.getClassName(), "<init>",
            Type.VOID, new Type[] {Type.STRING, classType},
            Constants.INVOKESPECIAL));
           
      list.append(InstructionFactory.createStore(factoryType, 0));
     
      List entries = new ArrayList(tjpFields.entrySet());
      Collections.sort(entries, new Comparator() {
        public int compare(Object a, Object b) {
          Map.Entry ae = (Map.Entry) a;
          Map.Entry be = (Map.Entry) b;
          return ((Field) ae.getValue())
            .getName()
            .compareTo(((Field)be.getValue()).getName());
        }
      });

      for (Iterator i = entries.iterator(); i.hasNext(); ) {
        Map.Entry entry = (Map.Entry)i.next();
        initializeTjp(fact, list, (Field)entry.getValue(), (BcelShadow)entry.getKey());
      }
     
      return list;
    }
   
   
    private void initializeTjp(InstructionFactory fact, InstructionList list,
                   Field field, BcelShadow shadow)
    {
      Member sig = shadow.getSignature();
      //ResolvedMember mem = shadow.getSignature().resolve(shadow.getWorld());
     
      // load the factory
      list.append(InstructionFactory.createLoad(factoryType, 0));
     
      // load the kind
      list.append(new PUSH(getConstantPoolGen(), shadow.getKind().getName()));
     
      // create the signature
      list.append(InstructionFactory.createLoad(factoryType, 0));
     
      if (world.isTargettingAspectJRuntime12()) { // TAG:SUPPORTING12: We didn't have optimized factory methods in 1.2
          list.append(new PUSH(getConstantPoolGen(), sig.getSignatureString(shadow.getWorld())));
        list.append(fact.createInvoke(factoryType.getClassName(),
              sig.getSignatureMakerName(),
              new ObjectType(sig.getSignatureType()),
              new Type[] { Type.STRING },
              Constants.INVOKEVIRTUAL));
      } else   if (sig.getKind().equals(Member.METHOD)) {
        BcelWorld w = shadow.getWorld();
        // For methods, push the parts of the signature on.
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getModifiers(w))));
        list.append(new PUSH(getConstantPoolGen(),sig.getName()));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getDeclaringType())));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getParameterTypes())));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getParameterNames(w))));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getExceptions(w))));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getReturnType())));
        // And generate a call to the variant of makeMethodSig() that takes 7 strings
        list.append(fact.createInvoke(factoryType.getClassName(),
            sig.getSignatureMakerName(),
            new ObjectType(sig.getSignatureType()),
            new Type[] { Type.STRING,Type.STRING,Type.STRING,Type.STRING,Type.STRING,Type.STRING,Type.STRING },
            Constants.INVOKEVIRTUAL));
      } else if (sig.getKind().equals(Member.HANDLER)) {
        BcelWorld w = shadow.getWorld();
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getDeclaringType())));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getParameterTypes())));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getParameterNames(w))));
        list.append(fact.createInvoke(factoryType.getClassName(),
            sig.getSignatureMakerName(),
            new ObjectType(sig.getSignatureType()),
            new Type[] { Type.STRING, Type.STRING, Type.STRING },
            Constants.INVOKEVIRTUAL));     
      } else if(sig.getKind().equals(Member.CONSTRUCTOR)) {
        BcelWorld w = shadow.getWorld();
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getModifiers(w))))
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getDeclaringType())));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getParameterTypes())));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getParameterNames(w))));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getExceptions(w))));
        list.append(fact.createInvoke(factoryType.getClassName(),
            sig.getSignatureMakerName(),
            new ObjectType(sig.getSignatureType()),
            new Type[] { Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING },
            Constants.INVOKEVIRTUAL));     
      } else if(sig.getKind().equals(Member.FIELD)) {
        BcelWorld w = shadow.getWorld();
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getModifiers(w))));
        list.append(new PUSH(getConstantPoolGen(),sig.getName()));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getDeclaringType())));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getReturnType())));
        list.append(fact.createInvoke(factoryType.getClassName(),
            sig.getSignatureMakerName(),
            new ObjectType(sig.getSignatureType()),
            new Type[] { Type.STRING, Type.STRING, Type.STRING, Type.STRING },
            Constants.INVOKEVIRTUAL));     
      } else if(sig.getKind().equals(Member.ADVICE)) {
        BcelWorld w = shadow.getWorld();
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getModifiers(w))));
        list.append(new PUSH(getConstantPoolGen(),sig.getName()));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getDeclaringType())));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getParameterTypes())));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getParameterNames(w))));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getExceptions(w))));
        list.append(new PUSH(getConstantPoolGen(),makeString((sig.getReturnType()))));       
        list.append(fact.createInvoke(factoryType.getClassName(),
            sig.getSignatureMakerName(),
            new ObjectType(sig.getSignatureType()),
            new Type[] { Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING },
            Constants.INVOKEVIRTUAL));     
      } else if(sig.getKind().equals(Member.STATIC_INITIALIZATION)) {
        BcelWorld w = shadow.getWorld();
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getModifiers(w))));
        list.append(new PUSH(getConstantPoolGen(),makeString(sig.getDeclaringType())));
        list.append(fact.createInvoke(factoryType.getClassName(),
            sig.getSignatureMakerName(),
            new ObjectType(sig.getSignatureType()),
            new Type[] { Type.STRING, Type.STRING },
            Constants.INVOKEVIRTUAL));
      } else {
        list.append(new PUSH(getConstantPoolGen(), sig.getSignatureString(shadow.getWorld())));
        list.append(fact.createInvoke(factoryType.getClassName(),
             sig.getSignatureMakerName(),
             new ObjectType(sig.getSignatureType()),
             new Type[] { Type.STRING },
             Constants.INVOKEVIRTUAL));
      }    
     
      //XXX should load source location from shadow
      list.append(Utility.createConstant(fact, shadow.getSourceLine()));

        final String factoryMethod;
       
         if (world.isTargettingAspectJRuntime12()) { // TAG:SUPPORTING12: We didn't have makeESJP() in 1.2     
          list.append(fact.createInvoke(factoryType.getClassName(),
              "makeSJP", staticTjpType,
              new Type[] { Type.STRING, sigType, Type.INT},
              Constants.INVOKEVIRTUAL));
         
          // put it in the field 
          list.append(fact.createFieldAccess(getClassName(), field.getName(),staticTjpType, Constants.PUTSTATIC));
       
        } else {
          if (staticTjpType.equals(field.getType())) {
              factoryMethod = "makeSJP";
          } else if (enclosingStaticTjpType.equals(field.getType())) {
              factoryMethod = "makeESJP";
          } else {
              throw new Error("should not happen");
          }
        list.append(fact.createInvoke(factoryType.getClassName(),
            factoryMethod, field.getType(),
            new Type[] { Type.STRING, sigType, Type.INT},
            Constants.INVOKEVIRTUAL));
        // put it in the field 
        list.append(fact.createFieldAccess(getClassName(), field.getName(), field.getType(), Constants.PUTSTATIC));
      }
    }
   
   
    protected String makeString(int i) {
      return Integer.toString(i, 16)//??? expensive
    }
   
   
    protected String makeString(UnresolvedType t) {
      // this is the inverse of the odd behavior for Class.forName w/ arrays
      if (t.isArray()) {
        // this behavior matches the string used by the eclipse compiler for Foo.class literals
        return t.getSignature().replace('/', '.');
      } else {
        return t.getName();
      }
    }
         
    protected String makeString(UnresolvedType[] types) {
      if (types == null) return "";
      StringBuffer buf = new StringBuffer();
      for (int i = 0, len=types.length; i < len; i++) {
        buf.append(makeString(types[i]));
        buf.append(':');
      }
      return buf.toString();
    }
      
    protected String makeString(String[] names) {
      if (names == null) return "";
      StringBuffer buf = new StringBuffer();
      for (int i = 0, len=names.length; i < len; i++) {
        buf.append(names[i]);
        buf.append(':');
      }
      return buf.toString();
    }
      
  public ResolvedType getType() {
    if (myType == null) return null;
    return myType.getResolvedTypeX();
  }

  public BcelObjectType getBcelObjectType() {
    return myType;
  }

  public String getFileName() {
    return myGen.getFileName();
  }

  private void addField(Field field) {
    myGen.addField(field);
  }
 
  public void replaceField(Field oldF, Field newF){
    myGen.removeField(oldF);
    myGen.addField(newF);
  }
 
  public void addField(Field field, ISourceLocation sourceLocation) {
    addField(field);
    if (!(field.isPrivate()
      && (field.isStatic() || field.isTransient()))) {
      errorOnAddedField(field,sourceLocation);
    }
  }

  public String getClassName() {
    return myGen.getClassName();
  }

  public boolean isInterface() {
    return myGen.isInterface();
  }
   
    public boolean isAbstract() {
      return myGen.isAbstract();  
    }

  public LazyMethodGen getLazyMethodGen(Member m) {
    return getLazyMethodGen(m.getName(), m.getSignature(),false);
  }

  public LazyMethodGen getLazyMethodGen(String name, String signature) {
    return getLazyMethodGen(name,signature,false);
  }
 
  public LazyMethodGen getLazyMethodGen(String name, String signature,boolean allowMissing) {
    for (Iterator i = methodGens.iterator(); i.hasNext();) {
      LazyMethodGen gen = (LazyMethodGen) i.next();
      if (gen.getName().equals(name) && gen.getSignature().equals(signature))
        return gen;
    }
   
    if (!allowMissing) {
      throw new BCException("Class " + this.getName() + " does not have a method "  
        + name + " with signature " + signature);
    }
   
    return null;
  }
 
 
  public void forcePublic() {
    myGen.setAccessFlags(Utility.makePublic(myGen.getAccessFlags()));
  }

 
  public boolean hasAnnotation(UnresolvedType t) {
   
    // annotations on the real thing
    AnnotationGen agens[] = myGen.getAnnotations();
    if (agens==null) return false;
    for (int i = 0; i < agens.length; i++) {
      AnnotationGen gen = agens[i];
      if (t.equals(UnresolvedType.forSignature(gen.getTypeSignature()))) return true;
    }
   
    // annotations added during this weave
   
    return false;
  }
 
  public void addAnnotation(Annotation a) {
    if (!hasAnnotation(UnresolvedType.forSignature(a.getTypeSignature()))) {
      annotations.add(new AnnotationGen(a,getConstantPoolGen(),true));
    }
  }
 
  // this test is like asking:
  // if (UnresolvedType.SERIALIZABLE.resolve(getType().getWorld()).isAssignableFrom(getType())) {
    // only we don't do that because this forces us to find all the supertypes of the type,
  // and if one of them is missing we fail, and it's not worth failing just to put out
  // a warning message!
  private boolean implementsSerializable(ResolvedType aType) {
    ResolvedType[] interfaces = aType.getDeclaredInterfaces();
    for (int i = 0; i < interfaces.length; i++) {
      if (interfaces[i].getSignature().equals(UnresolvedType.SERIALIZABLE.getSignature())) {
        return true;
      } else {
        if (interfaces[i].isMissing()) continue;
        ResolvedType superInterface = interfaces[i].getSuperclass();
        if (superInterface != null && !superInterface.isMissing()) {
          if (implementsSerializable(superInterface)) return true;
        }
      }
    }
    ResolvedType superType = aType.getSuperclass();
    if (superType != null && !superType.isMissing()) {
      return implementsSerializable(superType);
    }
    return false;
  }


}
TOP

Related Classes of org.aspectj.weaver.bcel.LazyClassGen

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.