Package org.aspectj.weaver.bcel

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

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


package org.aspectj.weaver.bcel;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.Attribute;
import org.aspectj.apache.bcel.classfile.ConstantPool;
import org.aspectj.apache.bcel.classfile.Method;
import org.aspectj.apache.bcel.classfile.Synthetic;
import org.aspectj.apache.bcel.classfile.annotation.Annotation;
import org.aspectj.apache.bcel.generic.BranchHandle;
import org.aspectj.apache.bcel.generic.BranchInstruction;
import org.aspectj.apache.bcel.generic.CPInstruction;
import org.aspectj.apache.bcel.generic.ClassGenException;
import org.aspectj.apache.bcel.generic.CodeExceptionGen;
import org.aspectj.apache.bcel.generic.ConstantPoolGen;
import org.aspectj.apache.bcel.generic.Instruction;
import org.aspectj.apache.bcel.generic.InstructionHandle;
import org.aspectj.apache.bcel.generic.InstructionList;
import org.aspectj.apache.bcel.generic.InstructionTargeter;
import org.aspectj.apache.bcel.generic.LineNumberGen;
import org.aspectj.apache.bcel.generic.LocalVariableGen;
import org.aspectj.apache.bcel.generic.LocalVariableInstruction;
import org.aspectj.apache.bcel.generic.MethodGen;
import org.aspectj.apache.bcel.generic.ObjectType;
import org.aspectj.apache.bcel.generic.Select;
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.weaver.AjAttribute;
import org.aspectj.weaver.AnnotationX;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.MemberImpl;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.AjAttribute.WeaverVersionInfo;


/**
* A LazyMethodGen should be treated as a MethodGen.  It's our way of abstracting over the
* low-level Method objects.  It converts through {@link MethodGen} to create
* and to serialize, but that's it.
*
* <p> At any rate, there are two ways to create LazyMethodGens. 
* One is from a method, which
* does work through MethodGen to do the correct thing. 
* The other is the creation of a completely empty
* LazyMethodGen, and it is used when we're constructing code from scratch.
*
* <p> We stay away from targeters for rangey things like Shadows and Exceptions.
*/

public final class LazyMethodGen {
    private        int             accessFlags;
    private  Type            returnType;
    private final String          name;
    private  Type[]          argumentTypes;
    //private final String[]        argumentNames;
    private  String[]        declaredExceptions;
    private  InstructionList body; // leaving null for abstracts
    private  Attribute[]     attributes;
    private List  newAnnotations;
    private final LazyClassGen enclosingClass;  
    private BcelMethod memberView;
    private AjAttribute.EffectiveSignatureAttribute effectiveSignature;
    int highestLineNumber = 0;

  /** This is nonnull if this method is the result of an "inlining".  We currently
   * copy methods into other classes for around advice.  We add this field so
   * we can get JSR45 information correct.  If/when we do _actual_ inlining,
   * we'll need to subtype LineNumberTag to have external line numbers.
   */
  String fromFilename = null;

    private int             maxLocals;
   
    private boolean canInline = true;
//    private boolean hasExceptionHandlers;
   
    private boolean isSynthetic = false;
   
    /**
     * only used by {@link BcelClassWeaver}
     */
    List /*ShadowMungers*/ matchedShadows;
    List /*Test*/ matchedShadowTests;
   
    // Used for interface introduction
    // this is the type of the interface the method is technically on
    public ResolvedType definingType = null;
   
    public LazyMethodGen(
        int accessFlags,
        Type returnType,
        String name,
        Type[] paramTypes,
        String[] declaredExceptions,
        LazyClassGen enclosingClass)
    {
      //System.err.println("raw create of: " + name + ", " + enclosingClass.getName() + ", " + returnType);
    this.memberView = null; // ??? should be okay, since constructed ones aren't woven into
        this.accessFlags = accessFlags;
        this.returnType = returnType;
        this.name = name;
        this.argumentTypes = paramTypes;
        //this.argumentNames = Utility.makeArgNames(paramTypes.length);
        this.declaredExceptions = declaredExceptions;
        if (!Modifier.isAbstract(accessFlags)) {
            body = new InstructionList();
            setMaxLocals(calculateMaxLocals());
        } else {
            body = null;
        }
        this.attributes = new Attribute[0];
        this.enclosingClass = enclosingClass;
        assertGoodBody();

        // @AJ advice are not inlined by default since requires further analysis
        // and weaving ordering control
        // TODO AV - improve - note: no room for improvement as long as aspects are reweavable
        // since the inlined version with wrappers and an to be done annotation to keep
        // inline state will be garbaged due to reweavable impl
        if (memberView != null && isAdviceMethod()) {
            if (enclosingClass.getType().isAnnotationStyleAspect()) {
                //TODO we could check for @Around advice as well
                this.canInline = false;
            }
        }
    }
      
    private int calculateMaxLocals() {
        int ret = 0;
        if (!Modifier.isStatic(accessFlags)) ret++;
        for (int i = 0, len = argumentTypes.length; i < len; i++) {
            ret += argumentTypes[i].getSize();
        }
        return ret;  
    }
   
    private Method savedMethod = null;
    // build from an existing method, lazy build saves most work for initialization
    public LazyMethodGen(Method m, LazyClassGen enclosingClass) {
      savedMethod = m;
     
        this.enclosingClass = enclosingClass;
        if (!(m.isAbstract() || m.isNative()) && m.getCode() == null) {
          throw new RuntimeException("bad non-abstract method with no code: " + m + " on " + enclosingClass);
        }
        if ((m.isAbstract() || m.isNative()) && m.getCode() != null) {
          throw new RuntimeException("bad abstract method with code: " + m + " on " + enclosingClass);
        }
    this.memberView = new BcelMethod(enclosingClass.getBcelObjectType(), m);
       
    this.accessFlags = m.getAccessFlags();
    this.name = m.getName();

        // @AJ advice are not inlined by default since requires further analysis
        // and weaving ordering control
        // TODO AV - improve - note: no room for improvement as long as aspects are reweavable
        // since the inlined version with wrappers and an to be done annotation to keep
        // inline state will be garbaged due to reweavable impl
        if (memberView != null && isAdviceMethod()) {
            if (enclosingClass.getType().isAnnotationStyleAspect()) {
                //TODO we could check for @Around advice as well
                this.canInline = false;
            }
        }
    }
   
    public boolean hasDeclaredLineNumberInfo() {
      return (memberView != null && memberView.hasDeclarationLineNumberInfo());
    }
   
    public int getDeclarationLineNumber() {
      if (hasDeclaredLineNumberInfo()) {
        return memberView.getDeclarationLineNumber();
      } else {
        return -1;
      }
    }
   
  public int getDeclarationOffset() {
      if (hasDeclaredLineNumberInfo()) {
        return memberView.getDeclarationOffset();
      } else {
        return 0;
      }
  }
   
    public void addAnnotation(AnnotationX ax) {
      initialize();
    if (memberView==null) {
      // If member view is null, we manage them in newAnnotations
      if (newAnnotations==null) newAnnotations = new ArrayList();
      newAnnotations.add(ax);
    } else {
      memberView.addAnnotation(ax);
    }
    }
 

  public boolean hasAnnotation(UnresolvedType annotationTypeX) {
    initialize();
    if (memberView==null) {
      // Check local annotations first
      if (newAnnotations!=null) {
        for (Iterator iter = newAnnotations.iterator(); iter.hasNext();) {
          AnnotationX element = (AnnotationX) iter.next();
          if (element.getBcelAnnotation().getTypeName().equals(annotationTypeX.getName())) return true;
        }
      }
            memberView = new BcelMethod(getEnclosingClass().getBcelObjectType(), getMethod());
            return memberView.hasAnnotation(annotationTypeX);
    }
    return memberView.hasAnnotation(annotationTypeX);
  }
 
    private void initialize() {
      if (returnType != null) return;
     
      //System.err.println("initializing: " + getName() + ", " + enclosingClass.getName() + ", " + returnType + ", " + savedMethod);
     
    MethodGen gen = new MethodGen(savedMethod, enclosingClass.getName(), enclosingClass.getConstantPoolGen());
       
    this.returnType = gen.getReturnType();
    this.argumentTypes = gen.getArgumentTypes();

    this.declaredExceptions = gen.getExceptions();
    this.attributes = gen.getAttributes();
    //this.annotations = gen.getAnnotations();
    this.maxLocals = gen.getMaxLocals();
       
//    this.returnType = BcelWorld.makeBcelType(memberView.getReturnType());
//    this.argumentTypes = BcelWorld.makeBcelTypes(memberView.getParameterTypes());
//
//    this.declaredExceptions = UnresolvedType.getNames(memberView.getExceptions()); //gen.getExceptions();
//    this.attributes = new Attribute[0]; //gen.getAttributes();
//    this.maxLocals = savedMethod.getCode().getMaxLocals();
       
       
        if (gen.isAbstract() || gen.isNative()) {
            body = null;
        } else {
          //body = new InstructionList(savedMethod.getCode().getCode());
            body = gen.getInstructionList();
           
            unpackHandlers(gen);
            unpackLineNumbers(gen);
            unpackLocals(gen);
        }
        assertGoodBody();
       
    //System.err.println("initialized: " + this.getClassName() + "." + this.getName());
    }
   
    // XXX we're relying on the javac promise I've just made up that we won't have an early exception
    // in the list mask a later exception:  That is, for two exceptions E and F,
    // if E preceeds F, then either E \cup F = {}, or E \nonstrictsubset F.  So when we add F,
    // we add it on the _OUTSIDE_ of any handlers that share starts or ends with it.

  // with that in mind, we merrily go adding ranges for exceptions. 

    private void unpackHandlers(MethodGen gen) {
        CodeExceptionGen[] exns = gen.getExceptionHandlers();
        if (exns != null) {
            int len = exns.length;
//           if (len > 0) hasExceptionHandlers = true;
            int priority = len - 1;
            for (int i = 0; i < len; i++, priority--) {
                CodeExceptionGen exn = exns[i];
       
                InstructionHandle start =
                    Range.genStart(
                        body,
                        getOutermostExceptionStart(exn.getStartPC()));
                InstructionHandle end = Range.genEnd(body, getOutermostExceptionEnd(exn.getEndPC()));
                // this doesn't necessarily handle overlapping correctly!!!
                ExceptionRange er =
                    new ExceptionRange(
                        body,
                        exn.getCatchType() == null
                        ? null
                        : BcelWorld.fromBcel(exn.getCatchType()),
                        priority);
                er.associateWithTargets(start, end, exn.getHandlerPC());
                exn.setStartPC(null); // also removes from target
                exn.setEndPC(null); // also removes from target
                exn.setHandlerPC(null); // also removes from target
            }
            gen.removeExceptionHandlers();
        }
    }

  private InstructionHandle getOutermostExceptionStart(InstructionHandle ih) {
    while (true) {
      if (ExceptionRange.isExceptionStart(ih.getPrev())) {
        ih = ih.getPrev();
      } else {
        return ih;
      }
    }
  }   
  private InstructionHandle getOutermostExceptionEnd(InstructionHandle ih) {
    while (true) {
      if (ExceptionRange.isExceptionEnd(ih.getNext())) {
        ih = ih.getNext();
      } else {
        return ih;
      }
    }
  }

    private void unpackLineNumbers(MethodGen gen) {
        LineNumberTag lr = null;
        for (InstructionHandle ih = body.getStart(); ih != null; ih = ih.getNext()) {
            InstructionTargeter[] targeters = ih.getTargeters();
            if (targeters != null) {
                for (int i = targeters.length - 1; i >= 0; i--) {
                    InstructionTargeter targeter = targeters[i];
                    if (targeter instanceof LineNumberGen) {
                        LineNumberGen lng = (LineNumberGen) targeter;
                        lng.updateTarget(ih, null);
                        int lineNumber = lng.getSourceLine();
                        if (highestLineNumber < lineNumber) highestLineNumber = lineNumber;
                        lr = new LineNumberTag(lineNumber);
                    }
                }
            }
            if (lr != null) {
                ih.addTargeter(lr);
            }
        }
        gen.removeLineNumbers();
    }

    private void unpackLocals(MethodGen gen) {
        Set locals = new HashSet();
        for (InstructionHandle ih = body.getStart(); ih != null; ih = ih.getNext()) {
            InstructionTargeter[] targeters = ih.getTargeters();
            List ends = new ArrayList(0);
            if (targeters != null) {
                for (int i = targeters.length - 1; i >= 0; i--) {
                    InstructionTargeter targeter = targeters[i];
                    if (targeter instanceof LocalVariableGen) {
                        LocalVariableGen lng = (LocalVariableGen) targeter;
                        LocalVariableTag lr = new LocalVariableTag(BcelWorld.fromBcel(lng.getType()), lng.getName(), lng.getIndex(), lng.getStart().getPosition());
                        if (lng.getStart() == ih) {
                            locals.add(lr);
                        } else {
                            ends.add(lr);
                        }
                    }
                }
            }
            for (Iterator i = locals.iterator(); i.hasNext(); ) {
                ih.addTargeter((LocalVariableTag) i.next());
            }
            locals.removeAll(ends);
        }
        gen.removeLocalVariables();
    }
          
    // ===============
   
    public int allocateLocal(Type type) {
        return allocateLocal(type.getSize());
    }

    public int allocateLocal(int slots) {
        int max = getMaxLocals();
        setMaxLocals(max + slots);
        return max;
    }

    public Method getMethod() {
      if (savedMethod != null) return savedMethod;  //??? this relies on gentle treatment of constant pool
     
      try {
      MethodGen gen = pack();
      return gen.getMethod();
      } catch (ClassGenException e) {
        enclosingClass.getBcelObjectType().getResolvedTypeX().getWorld().showMessage(
          IMessage.ERROR,
        WeaverMessages.format(WeaverMessages.PROBLEM_GENERATING_METHOD,
                          this.getClassName(),
                    this.getName(),
                    e.getMessage()),
          this.getMemberView() == null ? null : this.getMemberView().getSourceLocation(), null);
//        throw e; PR 70201.... let the normal problem reporting infrastructure deal with this rather than crashing.
        body = null;
           MethodGen gen = pack();
           return gen.getMethod();
      }
    }
   
    public void markAsChanged() {
      initialize();
      savedMethod = null;
    }
   
    // =============================

  public String toString() {
    WeaverVersionInfo weaverVersion = enclosingClass.getBcelObjectType().getWeaverVersionAttribute();
    return toLongString(weaverVersion);
  }

    public String toShortString() {
        String access = org.aspectj.apache.bcel.classfile.Utility.accessToString(getAccessFlags());
       
        StringBuffer buf = new StringBuffer();
       
        if (!access.equals("")) {
            buf.append(access);
            buf.append(" ");
        }
    buf.append(
      org.aspectj.apache.bcel.classfile.Utility.signatureToString(
        getReturnType().getSignature(),
        true));
        buf.append(" ");
        buf.append(getName());
        buf.append("(");
    {
      int len = argumentTypes.length;
      if (len > 0) {
        buf.append(
          org.aspectj.apache.bcel.classfile.Utility.signatureToString(
            argumentTypes[0].getSignature(),
            true));
        for (int i = 1; i < argumentTypes.length; i++) {
          buf.append(", ");
          buf.append(
            org.aspectj.apache.bcel.classfile.Utility.signatureToString(
              argumentTypes[i].getSignature(),
              true));
        }
      }
    }
        buf.append(")");
       
        {
            int len = declaredExceptions != null ? declaredExceptions.length : 0;
            if (len > 0) {
                buf.append(" throws ");
                buf.append(declaredExceptions[0]);
                for (int i = 1; i < declaredExceptions.length; i++) {
                    buf.append(", ");
                    buf.append(declaredExceptions[i]);
               
            }
        }     
        return buf.toString();
    }

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

    public void print(WeaverVersionInfo weaverVersion) {
        print(System.out,weaverVersion);
    }

    public void print(PrintStream out, WeaverVersionInfo weaverVersion) {
        out.print("  " + toShortString());
        printAspectAttributes(out,weaverVersion);
       
        InstructionList body = getBody();
        if (body == null) {
            out.println(";");
            return;
        }
        out.println(":");
        new BodyPrinter(out).run();
        out.println("  end " + toShortString());
    }


  private void printAspectAttributes(PrintStream out, WeaverVersionInfo weaverVersion) {
    ISourceContext context = null;
    if (enclosingClass != null && enclosingClass.getType() != null) {
      context = enclosingClass.getType().getSourceContext();
    }
    List as = BcelAttributes.readAjAttributes(getClassName(),attributes, context,null,weaverVersion);
    if (! as.isEmpty()) {
      out.println("    " + as.get(0)); // XXX assuming exactly one attribute, munger...
    }
  }


    private class BodyPrinter {
        Map prefixMap = new HashMap();
        Map suffixMap = new HashMap();
        Map labelMap = new HashMap();

        InstructionList body;
        PrintStream out;
        ConstantPool pool;
        List ranges;

        BodyPrinter(PrintStream out) {
            this.pool = enclosingClass.getConstantPoolGen().getConstantPool();
            this.body = getBody();
            this.out = out;
        }

        void run() {
          //killNops();
            assignLabels();
            print();
        }

        // label assignment
        void assignLabels() {
            LinkedList exnTable = new LinkedList();
            String pendingLabel = null;
//            boolean hasPendingTargeters = false;
            int lcounter = 0;
            for (InstructionHandle ih = body.getStart(); ih != null; ih = ih.getNext()) {
                InstructionTargeter[] targeters = ih.getTargeters();
                if (targeters != null) {
                    for (int i = targeters.length - 1; i >= 0; i--) {
                        InstructionTargeter t = targeters[i];
                        if (t instanceof ExceptionRange) {
                            // assert isRangeHandle(h);
                            ExceptionRange r = (ExceptionRange) t;
                            if (r.getStart() == ih) {
                                insertHandler(r, exnTable);
                            }
                        } else if (t instanceof BranchInstruction) {
                            if (pendingLabel == null) {
                                pendingLabel = "L" + lcounter++;
                            }
                        } else {
                            // assert isRangeHandle(h)
                        }
                    }
                }
                if (pendingLabel != null) {
                    labelMap.put(ih, pendingLabel);
                    if (! Range.isRangeHandle(ih)) {
                        pendingLabel = null;
                    }
                }
            }
            int ecounter = 0;
            for (Iterator i = exnTable.iterator(); i.hasNext();) {
                ExceptionRange er = (ExceptionRange) i.next();
                String exceptionLabel =  "E" + ecounter++;
                labelMap.put(Range.getRealStart(er.getHandler()), exceptionLabel);
        labelMap.put(er.getHandler(), exceptionLabel);               
            }
        }
       
        // printing

        void print() {
            int depth = 0;
            int currLine = -1;
          bodyPrint:
            for (InstructionHandle ih = body.getStart(); ih != null; ih = ih.getNext()) {
                if (Range.isRangeHandle(ih)) {
                    Range r = Range.getRange(ih);
                    // don't print empty ranges, that is, ranges who contain no actual instructions
                for (InstructionHandle xx = r.getStart(); Range.isRangeHandle(xx); xx = xx.getNext()) {
                   if (xx == r.getEnd()) continue bodyPrint;
                }

                    // doesn't handle nested: if (r.getStart().getNext() == r.getEnd()) continue;
                    if (r.getStart() == ih) {
                        printRangeString(r, depth++);
                    } else {
                      if (r.getEnd() != ih) throw new RuntimeException("bad");
                        printRangeString(r, --depth);
                    }                   
                } else {
                    printInstruction(ih, depth);
                    int line = getLineNumber(ih, currLine);
                    if (line != currLine) {
                        currLine = line;
                        out.println("   (line " + line + ")");
                    } else {
                        out.println();
                    }
                }
            }
        }
       
        void printRangeString(Range r, int depth) {
            printDepth(depth);
            out.println(getRangeString(r, labelMap));
        }

       
        String getRangeString(Range r, Map labelMap) {
            if (r instanceof ExceptionRange) {
                ExceptionRange er = (ExceptionRange) r;
                return er.toString() + " -> " + labelMap.get(er.getHandler());
//               
//                + " PRI " + er.getPriority();
            } else {
                return r.toString();
            }
        }
       
        void printDepth(int depth) {
            pad(BODY_INDENT);
            while (depth > 0) {
                out.print("| ");
                depth--;
            }
        }


        void printLabel(String s, int depth) {
            int space = Math.max(CODE_INDENT - depth * 2, 0);
            if (s == null) {
                pad(space);
            } else {
                space = Math.max(space - (s.length() + 2), 0);
                pad(space);
                out.print(s);
                out.print(": ");
            }
        }

        void printInstruction(InstructionHandle h, int depth) {
            printDepth(depth);
            printLabel((String) labelMap.get(h), depth);  
          
            Instruction inst = h.getInstruction();
            if (inst instanceof CPInstruction) {
                CPInstruction cpinst = (CPInstruction) inst;
                out.print(Constants.OPCODE_NAMES[cpinst.getOpcode()].toUpperCase());
                out.print(" ");
                out.print(pool.constantToString(pool.getConstant(cpinst.getIndex())));
            } else if (inst instanceof Select) {
                Select sinst = (Select) inst;
                out.println(Constants.OPCODE_NAMES[sinst.getOpcode()].toUpperCase());
                int[] matches = sinst.getMatchs();
                InstructionHandle[] targets = sinst.getTargets();
                InstructionHandle defaultTarget = sinst.getTarget();
                for (int i = 0, len = matches.length; i < len; i++) {
                    printDepth(depth);
                    printLabel(null, depth);
                    out.print("  ");
                    out.print(matches[i]);
                    out.print(": \t");
                    out.println(labelMap.get(targets[i]));
                }
                printDepth(depth);
                printLabel(null, depth);
                out.print("  ");
                out.print("default: \t");
                out.print(labelMap.get(defaultTarget));
            } else if (inst instanceof BranchInstruction) {
                BranchInstruction brinst = (BranchInstruction) inst;
                out.print(Constants.OPCODE_NAMES[brinst.getOpcode()].toUpperCase());
                out.print(" ");
                out.print(labelMap.get(brinst.getTarget()));
            } else if (inst instanceof LocalVariableInstruction) {
                LocalVariableInstruction lvinst = (LocalVariableInstruction) inst;
                out.print(inst.toString(false).toUpperCase());
                int index = lvinst.getIndex();
                LocalVariableTag tag = getLocalVariableTag(h, index);
                if (tag != null) {
                    out.print("     // ");
                    out.print(tag.getType());
                    out.print(" ");
                    out.print(tag.getName());
                }
            } else {
                out.print(inst.toString(false).toUpperCase());
            }
        }




        static final int BODY_INDENT = 4;
        static final int CODE_INDENT = 16;

        void pad(int size) {
            for (int i = 0; i < size; i++) {
                out.print(" ");
            }
        }
    }


    static LocalVariableTag getLocalVariableTag(
        InstructionHandle ih,
        int index)
    {
        InstructionTargeter[] targeters = ih.getTargeters();
        if (targeters == null) return null;
        for (int i = targeters.length - 1; i >= 0; i--) {
            InstructionTargeter t = targeters[i];
            if (t instanceof LocalVariableTag) {
                LocalVariableTag lvt = (LocalVariableTag) t;
                if (lvt.getSlot() == index) return lvt;
            }
        }
        return null;
    }
   
    static int getLineNumber(
        InstructionHandle ih,
        int prevLine)
    {
        InstructionTargeter[] targeters = ih.getTargeters();
        if (targeters == null) return prevLine;
        for (int i = targeters.length - 1; i >= 0; i--) {
            InstructionTargeter t = targeters[i];
            if (t instanceof LineNumberTag) {
                return ((LineNumberTag)t).getLineNumber();
            }
        }
        return prevLine;
    }

  public boolean isStatic() {
    return Modifier.isStatic(getAccessFlags());
  }
   
  public boolean isAbstract() {
    return Modifier.isAbstract(getAccessFlags());
  }
 
  public boolean isBridgeMethod() {
    return (getAccessFlags() & Constants.ACC_BRIDGE) != 0;
  }
   
    public void addExceptionHandler(
            InstructionHandle start,
            InstructionHandle end,
            InstructionHandle handlerStart,
            ObjectType catchType,
            boolean highPriority) {
   
        InstructionHandle start1 = Range.genStart(body, start);
        InstructionHandle end1 = Range.genEnd(body, end);
               
        ExceptionRange er =
          new ExceptionRange(body, BcelWorld.fromBcel(catchType), highPriority);
        er.associateWithTargets(start1, end1, handlerStart);
    }

    public int getAccessFlags() {
        return accessFlags;
    }
   
    public void setAccessFlags(int newFlags) {
      this.accessFlags = newFlags;
    }

    public Type[] getArgumentTypes() {
      initialize();
        return argumentTypes;
    }

    public LazyClassGen getEnclosingClass() {
        return enclosingClass;
    }

    public int getMaxLocals() {
        return maxLocals;
    }

    public String getName() {
        return name;
    }

    public Type getReturnType() {
      initialize();
        return returnType;
    }

    public void setMaxLocals(int maxLocals) {
        this.maxLocals = maxLocals;
    }
   
    public InstructionList getBody() {
      markAsChanged();
        return body;
    }
      
    public boolean hasBody() {
      if (savedMethod != null) return savedMethod.getCode() != null;
      return body != null;
    }

    public Attribute[] getAttributes() {
        return attributes;
    }

    public String[] getDeclaredExceptions() {
        return declaredExceptions;
    }

    public String getClassName() {
        return enclosingClass.getName();
    }
   
   
    // ---- packing!
   
    public MethodGen pack() {
      //killNops();
        MethodGen gen =
            new MethodGen(
                getAccessFlags(),
                getReturnType(),
                getArgumentTypes(),
                null, //getArgumentNames(),
                getName(),
                getEnclosingClass().getName(),
                new InstructionList(),
                getEnclosingClass().getConstantPoolGen());
        for (int i = 0, len = declaredExceptions.length; i < len; i++) {
            gen.addException(declaredExceptions[i]);
        }
       
        for (int i = 0, len = attributes.length; i < len; i++) {
            gen.addAttribute(attributes[i]);
        }
       
        if (newAnnotations!=null) {
      for (Iterator iter = newAnnotations.iterator(); iter.hasNext();) {
        AnnotationX element = (AnnotationX) iter.next();
        gen.addAnnotation(new AnnotationGen(element.getBcelAnnotation(),gen.getConstantPool(),true));
      }
        }
   
        if (memberView!=null && memberView.getAnnotations()!=null && memberView.getAnnotations().length!=0) {
      AnnotationX[] ans = memberView.getAnnotations();
          for (int i = 0, len = ans.length; i < len; i++) {
      Annotation a= ans[i].getBcelAnnotation();
            gen.addAnnotation(new AnnotationGen(a,gen.getConstantPool(),true));
          }
        }
       
        if (isSynthetic) {
      ConstantPoolGen cpg = gen.getConstantPool();
      int index = cpg.addUtf8("Synthetic");
      gen.addAttribute(new Synthetic(index, 0, new byte[0], cpg.getConstantPool()));
        }
       
        if (hasBody()) {
            packBody(gen);
            gen.setMaxLocals();
            gen.setMaxStack();
        } else {
          gen.setInstructionList(null);
        }
        return gen;
    }
   
    public void makeSynthetic() {
      isSynthetic = true;
    }     
       
    /** fill the newly created method gen with our body,
     * inspired by InstructionList.copy()
     */
    public void packBody(MethodGen gen) {
        InstructionList fresh = gen.getInstructionList();
        Map map = copyAllInstructionsExceptRangeInstructionsInto(fresh);
       
        // at this point, no rangeHandles are in fresh.  Let's use that...

        /* Update branch targets and insert various attributes. 
         * Insert our exceptionHandlers
         * into a sorted list, so they can be added in order later.
         */
        InstructionHandle oldInstructionHandle = getBody().getStart();
        InstructionHandle newInstructionHandle = fresh.getStart();
        LinkedList exceptionList = new LinkedList();  

    // map from localvariabletag to instruction handle
        Map localVariableStarts = new HashMap();
        Map localVariableEnds = new HashMap();

        int currLine = -1;
       
        while (oldInstructionHandle != null) {
            if (map.get(oldInstructionHandle) == null) {
              // must be a range instruction since they're the only things we didn't copy across
                handleRangeInstruction(oldInstructionHandle, exceptionList);
                // just increment ih.
                oldInstructionHandle = oldInstructionHandle.getNext();
            } else {
                // assert map.get(ih) == jh
                Instruction oldInstruction = oldInstructionHandle.getInstruction();
                Instruction newInstruction = newInstructionHandle.getInstruction();
   
            if (oldInstruction instanceof BranchInstruction) {
              handleBranchInstruction(map, oldInstruction, newInstruction);
            }
               
                // now deal with line numbers
                // and store up info for local variables
                InstructionTargeter[] targeters = oldInstructionHandle.getTargeters();
        int lineNumberOffset = (fromFilename == null) ? 0: getEnclosingClass().getSourceDebugExtensionOffset(fromFilename);
                if (targeters != null) {
                    for (int k = targeters.length - 1; k >= 0; k--) {
                        InstructionTargeter targeter = targeters[k];
                        if (targeter instanceof LineNumberTag) {
                            int line = ((LineNumberTag)targeter).getLineNumber();
                            if (line != currLine) {
                                gen.addLineNumber(newInstructionHandle, line + lineNumberOffset);
                                currLine = line;
                            }
                        } else if (targeter instanceof LocalVariableTag) {
                            LocalVariableTag lvt = (LocalVariableTag) targeter;
                            if (localVariableStarts.get(lvt) == null) {
                              localVariableStarts.put(lvt, newInstructionHandle);
                            }
                            localVariableEnds.put(lvt, newInstructionHandle);
                        }
                    }
                }
               
                // now continue
                oldInstructionHandle = oldInstructionHandle.getNext();
                newInstructionHandle = newInstructionHandle.getNext();
            }
        }
 
        addExceptionHandlers(gen, map, exceptionList);      
        addLocalVariables(gen, localVariableStarts, localVariableEnds);
       
        // JAVAC adds line number tables (with just one entry) to generated accessor methods - this
        // keeps some tools that rely on finding at least some form of linenumbertable happy.
        // Let's check if we have one - if we don't then let's add one.
        // TODO Could be made conditional on whether line debug info is being produced
        if (gen.getLineNumbers().length==0) {
          gen.addLineNumber(gen.getInstructionList().getStart(),1);
        }
    }

  private void addLocalVariables(MethodGen gen, Map localVariableStarts, Map localVariableEnds) {
    // now add local variables
        gen.removeLocalVariables();

    // this next iteration _might_ be overkill, but we had problems with
    // bcel before with duplicate local variables.  Now that we're patching
    // bcel we should be able to do without it if we're paranoid enough
    // through the rest of the compiler.
       
        Map duplicatedLocalMap = new HashMap();
    List keys = new ArrayList();
        keys.addAll(localVariableStarts.keySet());
        for (Iterator iter = keys.iterator(); iter.hasNext(); ) {
            LocalVariableTag tag = (LocalVariableTag) iter.next();
          // have we already added one with the same slot number and start location? 
          // if so, just continue.
          InstructionHandle start = (InstructionHandle) localVariableStarts.get(tag);
          Set slots = (Set) duplicatedLocalMap.get(start);
           if (slots == null) {
             slots = new HashSet();
             duplicatedLocalMap.put(start, slots)
           }
           if (slots.contains(new Integer(tag.getSlot()))) {
             // we already have a var starting at this tag with this slot
             continue;
           }
           slots.add(new Integer(tag.getSlot()));

            gen.addLocalVariable(
                tag.getName(),
                BcelWorld.makeBcelType(tag.getType()),
                tag.getSlot(),
                (InstructionHandle) localVariableStarts.get(tag),
                (InstructionHandle) localVariableEnds.get(tag));
        }
  }

  private void addExceptionHandlers(MethodGen gen, Map map, LinkedList exnList) {
    // now add exception handlers
        for (Iterator iter = exnList.iterator(); iter.hasNext();) {
            ExceptionRange r = (ExceptionRange) iter.next();
            if (r.isEmpty()) continue;
            gen.addExceptionHandler(
                remap(r.getRealStart(), map),
                remap(r.getRealEnd(), map),
                remap(r.getHandler(), map),
                (r.getCatchType() == null)
                ? null
                : (ObjectType) BcelWorld.makeBcelType(r.getCatchType()));
        }
  }

  private void handleBranchInstruction(Map map, Instruction oldInstruction, Instruction newInstruction) {
      BranchInstruction oldBranchInstruction = (BranchInstruction) oldInstruction;
      BranchInstruction newBranchInstruction = (BranchInstruction) newInstruction;
      InstructionHandle oldTarget = oldBranchInstruction.getTarget(); // old target
  
//            try {
      // New target is in hash map
      newBranchInstruction.setTarget(remap(oldTarget, map));
//            } catch (NullPointerException e) {
//              print();
//              System.out.println("Was trying to remap " + bi);
//              System.out.println("who's target was supposedly " + itarget);
//              throw e;
//            }
  
        if (oldBranchInstruction instanceof Select) {
            // Either LOOKUPSWITCH or TABLESWITCH
            InstructionHandle[] oldTargets = ((Select) oldBranchInstruction).getTargets();
            InstructionHandle[] newTargets = ((Select) newBranchInstruction).getTargets();
  
            for (int k = oldTargets.length - 1; k >= 0; k--) {
                // Update all targets
              newTargets[k] = remap(oldTargets[k], map);
              newTargets[k].addTargeter(newBranchInstruction);
          }
      }
  }

  private void handleRangeInstruction(InstructionHandle ih, LinkedList exnList) {
    // we're a range instruction
    Range r = Range.getRange(ih);
    if (r instanceof ExceptionRange) {
        ExceptionRange er = (ExceptionRange) r;
        if (er.getStart() == ih) {
          //System.err.println("er " + er);
          if (!er.isEmpty()){
              // order is important, insert handlers in order of start
              insertHandler(er, exnList);
          }
        }
    } else {
        // we must be a shadow range or something equally useless,
        // so forget about doing anything
    }
  }

    /* Make copies of all instructions, append them to the new list
     * and associate old instruction references with the new ones, i.e.,
     * a 1:1 mapping.
     */
   private Map copyAllInstructionsExceptRangeInstructionsInto(InstructionList intoList) {
      HashMap map = new HashMap();
      for (InstructionHandle ih = getBody().getStart(); ih != null; ih = ih.getNext()) {
            if (Range.isRangeHandle(ih)) {
                continue;
            }
            Instruction i = ih.getInstruction();
            Instruction c = Utility.copyInstruction(i);

            if (c instanceof BranchInstruction)
                map.put(ih, intoList.append((BranchInstruction) c));
            else
                map.put(ih, intoList.append(c));
        }
      return map;
    }
   
  /** This procedure should not currently be used.
   */
//  public void killNops() {
//      InstructionHandle curr = body.getStart();
//      while (true) {
//      if (curr == null) break;
//      InstructionHandle next = curr.getNext();
//        if (curr.getInstruction() instanceof NOP) {
//          InstructionTargeter[] targeters = curr.getTargeters();
//          if (targeters != null) {
//            for (int i = 0, len = targeters.length; i < len; i++) {
//            InstructionTargeter targeter = targeters[i];
//            targeter.updateTarget(curr, next);
//            }
//          }
//        try {
//          body.delete(curr);
//        } catch (TargetLostException e) {
//        }
//        }
//      curr = next;
//      }
//  }

    private static InstructionHandle remap(InstructionHandle ih, Map map) {
        while (true) {
            Object ret = map.get(ih);
            if (ret == null) {
                ih = ih.getNext();
            } else {
                return (InstructionHandle) ret;
            }
        }
    }

    // Update to all these comments, ASC 11-01-2005
    // The right thing to do may be to do more with priorities as
    // we create new exception handlers, but that is a relatively
    // complex task.  In the meantime, just taking account of the
    // priority here enables a couple of bugs to be fixed to do
    // with using return or break in code that contains a finally
    // block (pr78021,pr79554).

    // exception ordering.
    // What we should be doing is dealing with priority inversions way earlier than we are
    // and counting on the tree structure.  In which case, the below code is in fact right.
   
   
    // XXX THIS COMMENT BELOW IS CURRENTLY WRONG.
    // An exception A preceeds an exception B in the exception table iff:
   
    // * A and B were in the original method, and A preceeded B in the original exception table
    // * If A has a higher priority than B, than it preceeds B.
    // * If A and B have the same priority, then the one whose START happens EARLIEST has LEAST priority.
    //   in short, the outermost exception has least priority.
    // we implement this with a LinkedList.  We could possibly implement this with a java.util.SortedSet,
    // but I don't trust the only implementation, TreeSet, to do the right thing.
   
    /* private */ static void insertHandler(ExceptionRange fresh, LinkedList l) {
      // Old implementation, simply:   l.add(0,fresh);
      for (ListIterator iter = l.listIterator(); iter.hasNext();) {
            ExceptionRange r = (ExceptionRange) iter.next();
//            int freal = fresh.getRealStart().getPosition();
//            int rreal = r.getRealStart().getPosition();
            if (fresh.getPriority() >= r.getPriority()) {
                iter.previous();
                iter.add(fresh);
                return;
            }
        }
     
      // we have reached the end
        l.add(fresh);       
    }


    public boolean isPrivate() {
        return Modifier.isPrivate(getAccessFlags());
    }
    public boolean isProtected() {
        return Modifier.isProtected(getAccessFlags());
    }
    public boolean isDefault() {
        return !(isProtected() || isPrivate() || isPublic());
    }
    public boolean isPublic() {
        return Modifier.isPublic(getAccessFlags());
    }


  // ----
 
  /** A good body is a body with the following properties:
   *
   * <ul>
   * <li> For each branch instruction S in body, target T of S is in body.
   * <li> For each branch instruction S in body, target T of S has S as a targeter.
   * <li> For each instruction T in body, for each branch instruction S that is a
   *      targeter of T, S is in body.
   * <li> For each non-range-handle instruction T in body, for each instruction S
   *      that is a targeter of T, S is
   *     either a branch instruction, an exception range or a tag
   * <li> For each range-handle instruction T in body, there is exactly one targeter S
   *      that is a range.
   * <li> For each range-handle instruction T in body, the range R targeting T is in body.
   * <li> For each instruction T in body, for each exception range R targeting T, R is
   *     in body.
   * <li> For each exception range R in body, let T := R.handler.  T is in body, and R is one
   *      of T's targeters
   * <li> All ranges are properly nested: For all ranges Q and R, if Q.start preceeds
   *      R.start, then R.end preceeds Q.end.
   * </ul>
   *
   * Where the shorthand "R is in body" means "R.start is in body, R.end is in body, and
   * any InstructionHandle stored in a field of R (such as an exception handle) is in body".
   */
 
  public void assertGoodBody() {
    if (true) return; // only enable for debugging, consider using cheaper toString()
    assertGoodBody(getBody(), toString()); //definingType.getNameAsIdentifier() + "." + getName()); //toString());
  }
 
  public static void assertGoodBody(InstructionList il, String from) {
    if (true) return// only to be enabled for debugging
    if (il == null) return;
    Set body = new HashSet();
    Stack ranges = new Stack();
    for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) {
      body.add(ih);
      if (ih.getInstruction() instanceof BranchInstruction) {
        body.add(ih.getInstruction());
      }
    }
   
    for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) {
      assertGoodHandle(ih, body, ranges, from);
      InstructionTargeter[] ts = ih.getTargeters();
      if (ts != null) {
        for (int i = ts.length - 1; i >= 0; i--) {
          assertGoodTargeter(ts[i], ih, body, from);
        }
      }
    }
  }

  private static void assertGoodHandle(InstructionHandle ih, Set body, Stack ranges, String from) {
    Instruction inst = ih.getInstruction();
    if ((inst instanceof BranchInstruction) ^ (ih instanceof BranchHandle)) {
      throw new BCException("bad instruction/handle pair in " + from);
    }
    if (Range.isRangeHandle(ih)) {
      assertGoodRangeHandle(ih, body, ranges, from);
    } else if (inst instanceof BranchInstruction) {
      assertGoodBranchInstruction((BranchHandle) ih, (BranchInstruction) inst, body, ranges, from);
    }
  }

  private static void assertGoodBranchInstruction(
    BranchHandle ih,
    BranchInstruction inst,
    Set body,
    Stack ranges,
    String from)
  {
    if (ih.getTarget() != inst.getTarget()) {
      throw new BCException("bad branch instruction/handle pair in " + from);
    }
    InstructionHandle target = ih.getTarget();
    assertInBody(target, body, from);
    assertTargetedBy(target, inst, from);
    if (inst instanceof Select) {
      Select sel = (Select) inst;
            InstructionHandle[] itargets = sel.getTargets();
            for (int k = itargets.length - 1; k >= 0; k--) {
        assertInBody(itargets[k], body, from);
        assertTargetedBy(itargets[k], inst, from);
            }
        }
  }

  /** ih is an InstructionHandle or a BranchInstruction */
  private static void assertInBody(Object ih, Set body, String from) {
    if (! body.contains(ih)) throw new BCException("thing not in body in " + from);
  }

    private static void assertGoodRangeHandle(InstructionHandle ih, Set body, Stack ranges, String from) {
    Range r = getRangeAndAssertExactlyOne(ih, from);
    assertGoodRange(r, body, from);
    if (r.getStart() == ih) {
      ranges.push(r);
    } else if (r.getEnd() == ih) {
      if (ranges.peek() != r) throw new BCException("bad range inclusion in " + from);
      ranges.pop();
    }
    }
   
    private static void assertGoodRange(Range r, Set body, String from) {
    assertInBody(r.getStart(), body, from);
    assertRangeHandle(r.getStart(), from);
    assertTargetedBy(r.getStart(), r, from);

    assertInBody(r.getEnd(), body, from);
    assertRangeHandle(r.getEnd(), from);
    assertTargetedBy(r.getEnd(), r, from);
   
    if (r instanceof ExceptionRange) {
      ExceptionRange er = (ExceptionRange) r;
      assertInBody(er.getHandler(), body, from);
      assertTargetedBy(er.getHandler(), r, from);
    }     
    }

  private static void assertRangeHandle(InstructionHandle ih, String from) {
    if (! Range.isRangeHandle(ih)) throw new BCException("bad range handle " + ih + " in " + from);   
  }


    private static void assertTargetedBy(
        InstructionHandle target,
        InstructionTargeter targeter,
        String from)
    {
      InstructionTargeter[] ts = target.getTargeters();
      if (ts == null) throw new BCException("bad targeting relationship in " + from);
      for (int i = ts.length - 1; i >= 0; i--) {
        if (ts[i] == targeter) return;
      }
    throw new RuntimeException("bad targeting relationship in " + from);
    }
 
  private static void assertTargets(InstructionTargeter targeter, InstructionHandle target, String from) {
    if (targeter instanceof Range) {
      Range r = (Range) targeter;
      if (r.getStart() == target || r.getEnd() == target) return;
      if (r instanceof ExceptionRange) {
        if (((ExceptionRange)r).getHandler() == target) return;
      }
    } else if (targeter instanceof BranchInstruction) {
      BranchInstruction bi = (BranchInstruction) targeter;
      if (bi.getTarget() == target) return;
      if (targeter instanceof Select) {
        Select sel = (Select) targeter;
              InstructionHandle[] itargets = sel.getTargets();
              for (int k = itargets.length - 1; k >= 0; k--) {
          if (itargets[k] == target) return;
              }
      }     
    } else if (targeter instanceof Tag) {
      return;
    }
    throw new BCException(targeter + " doesn't target " + target + " in " + from );
  }

    private static Range getRangeAndAssertExactlyOne(InstructionHandle ih, String from) {
      Range ret = null;
      InstructionTargeter[] ts = ih.getTargeters();
      if (ts == null) throw new BCException("range handle with no range in " + from);
      for (int i = ts.length - 1; i >= 0; i--) {
        if (ts[i] instanceof Range) {
          if (ret != null) throw new BCException("range handle with multiple ranges in " + from);
          ret = (Range) ts[i];
        }
      }
      if (ret == null) throw new BCException("range handle with no range in " + from);
    return ret;
    }

    private static void assertGoodTargeter(
        InstructionTargeter t,
        InstructionHandle ih,
        Set body,
        String from)
    {
      assertTargets(t, ih, from);
    if (t instanceof Range) {
      assertGoodRange((Range) t, body, from);
    } else if (t instanceof BranchInstruction) {
      assertInBody(t, body, from);
    }
    }
   
   
    // ----
   
    boolean isAdviceMethod() {
      return memberView.getAssociatedShadowMunger() != null;
    }
   
    boolean isAjSynthetic() {
      if (memberView == null) return true;
      return memberView.isAjSynthetic();
    }
   
    public ISourceLocation getSourceLocation() {
      if (memberView!=null) return memberView.getSourceLocation();
      return null;
    }
   
    public AjAttribute.EffectiveSignatureAttribute getEffectiveSignature() {
      //if (memberView == null) return null;
      if (effectiveSignature != null) return effectiveSignature;
      return memberView.getEffectiveSignature();
    }
   
    public void setEffectiveSignature(ResolvedMember member, Shadow.Kind kind, boolean shouldWeave) {
      this.effectiveSignature =
        new AjAttribute.EffectiveSignatureAttribute(member,kind,shouldWeave);
    }
   
  public String getSignature() {
    if (memberView!=null) return memberView.getSignature();
    return MemberImpl.typesToSignature(BcelWorld.fromBcel(getReturnType()),
                    BcelWorld.fromBcel(getArgumentTypes()),false);
  }
   
    public String getParameterSignature() {
        if (memberView!=null) return memberView.getParameterSignature();
        return MemberImpl.typesToSignature(BcelWorld.fromBcel(getArgumentTypes()));
    }

  public BcelMethod getMemberView() {
    return memberView;
  }

  public void forcePublic() {
    markAsChanged();
    accessFlags = Utility.makePublic(accessFlags);
  }

  public boolean getCanInline() {
    return canInline;
  }

  public void setCanInline(boolean canInline) {
    this.canInline = canInline;
  }

    /**
     * Adds an attribute to the method
     * @param attr
     */
    public void addAttribute(Attribute attr) {
        Attribute[] newAttributes = new Attribute[attributes.length + 1];
        System.arraycopy(attributes, 0, newAttributes, 0, attributes.length);
        newAttributes[attributes.length] = attr;
        attributes = newAttributes;
    }

}
TOP

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

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.