Package alt.jiapi.reflect

Source Code of alt.jiapi.reflect.JiapiMethod

/*
* Copyright (C) 2001 Mika Riekkinen, Joni Suominen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package alt.jiapi.reflect;

import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.List;
import java.util.LinkedList;

import org.apache.log4j.Category;

import alt.jiapi.Runtime;

import alt.jiapi.file.Attribute;
import alt.jiapi.file.ClassFile;
import alt.jiapi.file.CodeAttribute;
import alt.jiapi.file.ConstantPool;
import alt.jiapi.file.ExceptionsAttribute;
import alt.jiapi.file.LineNumberTableAttribute;
import alt.jiapi.file.LocalVariableTableAttribute;
import alt.jiapi.file.Method;
import alt.jiapi.file.SyntheticAttribute;

import alt.jiapi.reflect.instruction.Opcodes;

/*
* NOTE:
*       When instructions, exception table, etc. are modified,
*       make sure that update() method is called when finished.
*       This method call syncs changes back to alt.jiapi.file
*       package.
*       Currently, JiapiClass.getByteCode() triggers update()
*       method.
*/

/**
* This class represents a Method.
*
* @author Mika Riekkinen
* @author Joni Suominen
* @version $Revision: 1.34 $ $Date: 2005/06/13 10:28:27 $
*/
public class JiapiMethod {
    private static Category log = Runtime.getLogCategory(JiapiMethod.class);

    private Method method;
    private Signature signature;
    private JiapiClass declaringClass;

    private InstructionList instructions;
    private TryBlock[] tryBlocks = null;

    private List lineNumberTable;
    private List exceptionTable;


    /**
     * Constructor for JiapiMethod.
     */
    public JiapiMethod(Method m) {
        this.method = m;
        this.signature = new Signature(m.getDescriptor());
    }



    /**
     * Get the name of this Method.
     *
     * @return name of the method
     */
    public String getName() {
        return method.getName();
    }

    /**
     * Gets modifiers of this Method.
     *
     * @return Modifiers of this method
     * @see java.lang.reflect.Modifier
     */
    public int getModifiers() {
        return method.getAccessFlags();
    }

//      public abstract setModifiers(int modifiers);

    /**
     * Gets the return type of a method.
     *
     * @return the return type of a method
     */
    public String getReturnType() {
        return signature.getReturnType();
    }

    /**
     * Gets the signature of this method.
     *
     * @return method signature
     */
    public Signature getSignature() {
        return signature;
    }


    /**
     * Get the InstructionFactory.
     */
    public InstructionFactory getInstructionFactory() {
        return new InstructionFactory(method.getConstantPool());
    }


    /**
     * Get an InstructionList, that represents a byte-code of this method.
     *
     * @return InstructionList
     */
    public InstructionList getInstructionList() {
        CodeAttribute ca = null;
        if (instructions == null) {
            ca = (CodeAttribute)getAttribute("Code");

            if (ca == null) {
                return null;
            }

            byte[] byteCode = ca.getByteCode();
           
            instructions = new InstructionList(byteCode,
                                               method.getConstantPool());
            instructions.setDeclaringMethod(this);

            if (true/* configurable by jiapi.properties */) {
                // create private representation of
                // line number table, so that we can update
                // its offsets, when class is instrumented
                lineNumberTable = new LinkedList();
               
                LineNumberTableAttribute lnta = (LineNumberTableAttribute)ca.getAttribute(LineNumberTableAttribute.ATTRIBUTE_NAME);
               
                if (lnta != null) {
                    List entries = lnta.getEntries();
                    Iterator i = entries.iterator();
                    while(i.hasNext()) {
                        LineNumberTableAttribute.Entry entry =
                            (LineNumberTableAttribute.Entry)i.next();
                       
                        Instruction ins =
                            instructions.instructionAtOffset(entry.getStartPc());
                       
                        lineNumberTable.add(new LNTableEntry(entry, ins));
                    }
                }
                else {
                    //System.out.println("ERROR: could not get line number table");
                }
            }


            this.exceptionTable = new LinkedList();
            List eTable = ca.getExceptionTable();
           
            if (eTable != null) {
                Iterator i = eTable.iterator();
                while(i.hasNext()) {
                    CodeAttribute.ExceptionTableEntry entry =
                        (CodeAttribute.ExceptionTableEntry)i.next();
                   
                    Instruction start =
                        instructions.instructionAtOffset(entry.getStartPc());
                    Instruction end =
                        instructions.instructionAtOffset(entry.getEndPc());
                    Instruction handler =
                        instructions.instructionAtOffset(entry.getHandlerPc());
                   
                    this.exceptionTable.add(new ETEntry(entry, start, end,
                                                   handler));
                }
            }
            else {
                //System.out.println("ERROR: could not get exception table");
            }
        }
       
        return instructions;
    }


    /**
     * Get an array of try blocks within this method.
     * If a physical try blocks are constructed so, that one block
     * is inside another one, they are both returned separately in
     * TryBlock array. It is then the responsibility of the developer
     * to check whether or not try blocks have this kind of a relationship.
     *
     * @return an array of TryBlocks. If this method has no try blocks,
     *         an Array of length 0 is returned.
     */
    TryBlock[] getTryBlocks() {
        return null;
    }


    // Package protected for now!!!
    void addTryBlock(InstructionList tryBlock, String exceptionName,
                     Instruction handlerStart) {
    }
    // Alternative:
    void addTryBlock(TryBlock b) {
    }

    /**
     * Gets the class that declared this JiapiMethod.
     *
     * @return a JiapiClass which declares this method
     */
    public JiapiClass getDeclaringClass() {
        return declaringClass;
    }

    /**
     * Sets the declaring class.
     *
     * @param declaringClass A JiapiClass, that declares this JiapiField
     */
    void setDeclaringClass(JiapiClass declaringClass) {
        this.declaringClass = declaringClass;
    }
   

    /**
     * Gets the parameter types in this method's signature. Each
     * of the parameters are loaded with the same Loader as
     * the declaring class was loaded with.
     *
     * @return an array of parameter types
     * @exception ClassNotFoundException is thrown, if one of the types
     *            could not be loaded
     * @see #getParameterTypeNames()
     */
    public JiapiClass[] getParameterTypes() throws ClassNotFoundException {
        String[] paramNames = getParameterTypeNames();
        JiapiClass[] types = new JiapiClass[paramNames.length];
        Loader loader = getDeclaringClass().getLoader();

        int i = 0;
        try {
            for (i = 0; i < paramNames.length; i++) {
                types[i] = loader.loadClass(paramNames[i]);
            }
        }
        catch(java.io.IOException ioe) {
            throw new ClassNotFoundException(paramNames[i]);
        }

        return types;
    }

    /**
     * Gets the names of parameter types in this method's signature.
     * (e.g. "java.lang.String", "com.Foo", "int")
     *
     * @return an array of parameter type names
     */
    public String[] getParameterTypeNames() {
        return signature.getParameters();
    }

    /**
     * Gets the parameter types of exceptions this method can throw.
     * Types are loaded with the same Loader as declaring class was loaded
     * with.
     *
     * @return an array of parameter types
     * @exception ClassNotFoundException is thrown, if one of the types
     *            could not be loaded
     * @see #getExceptionNames()
     */
    public JiapiClass[] getExceptionTypes() throws ClassNotFoundException {
        String[] exceptionNames = getExceptionNames();
        JiapiClass[] types = new JiapiClass[exceptionNames.length];
        Loader loader = getDeclaringClass().getLoader();

        int i = 0;
        try {
            for (i = 0; i < exceptionNames.length; i++) {
                types[i] = loader.loadClass(exceptionNames[i]);
            }
        }
        catch(java.io.IOException ioe) {
            throw new ClassNotFoundException(exceptionNames[i]);
        }

        return types;
    }

    /**
     * Gets the names of exceptions this method can throw.
     * (e.g. "java.lang.ClassNotFoundException").
     *
     * @return an array of parameter type names
     */
    public String[] getExceptionNames() {
        ExceptionsAttribute a =
            (ExceptionsAttribute)getAttribute("Exceptions");

        if (a == null) {
            return new String[0];
        }

        return a.getExceptionNames();
    }

    /**
     * Adds a local variable for this method.
     *
     * @param type a type of the local variable
     * @param name a name of the local variable
     * @return a local variable
     */
    public LocalVariable addLocalVariable(String type, String name) {
        return null;
    }


    /*
     * CONSIDER adding this method
     *
     * Copies source JiapiMethod to this JiapiMethod.
     * ExceptionTables and all the other metadata is copied.
     * As well as InstructionList. InstructionList gets appended
     * to InstructionList of this JiapiMethod.
     */
    /*public*/ void copy(JiapiMethod source) {
        // TBDL
    }

    /**
     * Convert this JiapiMethod to String.
     */
    public String toString() {
        StringBuffer sb = new StringBuffer();
        String ms = Modifier.toString(getModifiers());
        sb.append(ms);
        if (ms.length() > 0) {
            sb.append(' ');
        }

        sb.append(getReturnType());
        sb.append(' ');
        sb.append(getName());
        sb.append('(');
        String[] params = getParameterTypeNames();
        for (int i = 0; i < params.length; i++) {
            sb.append(params[i]);
            if (i < params.length - 1) {
                sb.append(',');
            }
        }
        sb.append(')');

        String[] eNames = getExceptionNames();
        if (eNames.length > 0) {
            sb.append(" throws ");

            for(int i = 0; i < eNames.length; i++) {
                sb.append(eNames[i]);
                if (i < eNames.length - 1) {
                    sb.append(", ");
                }
            }
        }
       
        //getInstructionList();
//         if (this.exceptionTable != null) {
//             Iterator i = exceptionTable.iterator();
//             sb.append("Exception table:\nstart_pc end_pc handler_pc catch_type\n");
//             while(i.hasNext()) {
//                 ETEntry entry = (ETEntry)i.next();
//                 sb.append("start: " + entry.getStart() + " " +
//                           "end: " + entry.getEnd() + " " +
//                           "handler: " +entry.getHandler() + "\n");
//             }
//         }
//         else {
// //             System.out.println("Exception table was null");
//         }

//         sb.append("\nmax-locals: ");
//         sb.append(getMaxLocals());
//         sb.append(", max-stack: ");
//         sb.append(getMaxStack());

        return sb.toString();
    }



    /**
     * Gets all the method attributes defined in ClassFile.
     */
    private List getAttributes() {
        return method.getAttributes();
    }

    Method getMethod() {
        return method;
    }


    /**
     * Gets all the method attributes defined in ClassFile.
     */
    Attribute getAttribute(String name) {
        List l = method.getAttributes();
        Iterator i = l.iterator();

        while(i.hasNext()) {
            Attribute a = (Attribute)i.next();
            if (a != null) { // NOTE: Should never be null
                if (a.getName().equals(name)) {
                    return a;
                }
            }
        }

        return null;
    }


    // This method is called, when bytecode has changed, so that
    // we can update Method and CodeAttribute
    void update() {
        if (instructions != null) {
            instructions.updateOffsets();
            // Update branch offsets
            updateBranchOffsets();
           
            CodeAttribute ca = (CodeAttribute)getAttribute("Code");
            ca.setByteCode(instructions.getBytes());
           
            // Update max locals & max stack
            updateMaxLocals(ca, instructions);
            updateMaxStack(ca, instructions);
        }

        updateLineNumberTableOffsets();
        updateExceptionTableOffsets();
    }


    //
    // Update routines follow.
    // Update changes from reflect layer to file layer
    //
    private short updateMaxLocals(CodeAttribute ca, InstructionList il) {
        // maxLocals = locals + (this); // locals + 1;
        short maxLocals = (short)(getParameterTypeNames().length);

//         if (!Modifier.isStatic(getModifiers())) {
//             maxLocals++; // 'this'
//         }
       
        LocalVariableTableAttribute lvta = (LocalVariableTableAttribute)getAttribute(LocalVariableTableAttribute.ATTRIBUTE_NAME);

        // Add local variables other than ones in method signature
        if (lvta != null) {
            maxLocals += lvta.getLocalVariables().size();
        }


        short maxLocalsInInstructions = 0;
        for(int i = 0; i < il.size(); i++) {
            Instruction ins = il.get(i);
            switch(ins.getOpcode()) {
            case Opcodes.FLOAD_0:
            case Opcodes.FSTORE_0:
            case Opcodes.ILOAD_0:
            case Opcodes.ISTORE_0:
            case Opcodes.ALOAD_0:
            case Opcodes.ASTORE_0:
                if (maxLocals < 1) {
                    maxLocals = 1;
                }
                break;
               
            case Opcodes.FLOAD_1:
            case Opcodes.FSTORE_1:
            case Opcodes.LSTORE_0: // long occupies two slots
            case Opcodes.LLOAD_0:
            case Opcodes.DSTORE_0: // double occupies two slots
            case Opcodes.DLOAD_0:
            case Opcodes.ILOAD_1:
            case Opcodes.ISTORE_1:
            case Opcodes.ALOAD_1:
            case Opcodes.ASTORE_1:
                if (maxLocals < 2) {
                    maxLocals = 2;
                }
                break;
               
            case Opcodes.FLOAD_2:
            case Opcodes.FSTORE_2:
            case Opcodes.LSTORE_1: // long occupies two slots
            case Opcodes.LLOAD_1:
            case Opcodes.DSTORE_1: // double occupies two slots
            case Opcodes.DLOAD_1:
            case Opcodes.ILOAD_2:
            case Opcodes.ISTORE_2:
            case Opcodes.ALOAD_2:
            case Opcodes.ASTORE_2:
                if (maxLocals < 3) {
                    maxLocals = 3;
                }
                break;
               
            case Opcodes.FLOAD_3:
            case Opcodes.FSTORE_3:
            case Opcodes.LSTORE_2: // long occupies two slots
            case Opcodes.LLOAD_2:
            case Opcodes.DSTORE_2: // double occupies two slots
            case Opcodes.DLOAD_2:
            case Opcodes.ILOAD_3:
            case Opcodes.ISTORE_3:
            case Opcodes.ALOAD_3:
            case Opcodes.ASTORE_3:
                if (maxLocals < 4) {
                    maxLocals = 4;
                }
                break;
               
            case Opcodes.DSTORE_3: // double occupies two slots
            case Opcodes.DLOAD_3:
            case Opcodes.LSTORE_3: // long occupies two slots
            case Opcodes.LLOAD_3:
                if (maxLocals < 5) {
                    maxLocals = 5;
                }
                break;

            case Opcodes.DSTORE: // double occupies two slots
            case Opcodes.DLOAD:
            case Opcodes.LSTORE: // long occupies two slots
            case Opcodes.LLOAD:
                byte[] bytes_l = ins.getBytes();
                int mloc_l = bytes_l[1];
                if (maxLocals < mloc_l + 1) {
                    maxLocals = (short)(mloc_l + 1);
                }
                break;

            case Opcodes.FLOAD:
            case Opcodes.FSTORE:
            case Opcodes.ILOAD:
            case Opcodes.ISTORE:
            case Opcodes.ALOAD:
            case Opcodes.ASTORE:
                byte[] bytes = ins.getBytes();
                short mloc = bytes[1];
                if (maxLocals < mloc) {
                    maxLocals = mloc;
                }
                break;
            }
        }
       
        maxLocals++;
        ca.setMaxLocals(maxLocals);

        return maxLocals;
    }


    /*
     * Calculate max stack. If instructions' stack usage is positive,
     * increase maxStackUsage.
     */
    private short updateMaxStack(CodeAttribute ca, InstructionList il) {
        short maxStack = 0;
        short su = 0;

        if (il != null) { // Abstract methods do not have instructionlist
            for(int i = 0; i < il.size(); i++) {
                Instruction ins = il.get(i);
                su += ins.stackUsage();
               
                if (su > maxStack) {
                    maxStack = su;
                }
               
                // stack usage above says only stack consumption.
                // Some instructions both consume stack and produce
                // stack. Fix it here.
                if (ins.getOpcode() == Opcodes.GETSTATIC ||
                    ins.getOpcode() == Opcodes.GETFIELD) {
                    maxStack++;
                }



            }

            ca.setMaxStack(maxStack);
        }

        return maxStack;
    }

    /*
     * Update branch intructions' offsets.
     */
    private void updateBranchOffsets() {
        for(int i = 0; i < instructions.size(); i++) {
            Instruction ins = instructions.get(i);

            if (ins instanceof BranchInstruction) {
                BranchInstruction bIns = (BranchInstruction)ins;

    // Check, that target actually is found in this list
    // If not, Leave offsets as is, and hope for the
    // best
    if (instructions.indexOf(bIns.getTarget()) != -1) {
        int branchOffset = bIns.getOffset();
        int targetOffset = bIns.getTarget().getOffset();

        // Set offset of the branch to (target - branch)
        bIns.setTargetOffset(targetOffset - branchOffset);
    }
    else {
        log.warn("Target instruction for branch was not found. This is most likely because branch-instruction was copied from some other instruction-list, and target was not copied. Branch offset is left as is.");
    }
            }
        }
    }

    private void updateLineNumberTableOffsets() {
        if (lineNumberTable != null) {
            Iterator i = lineNumberTable.iterator();
            while(i.hasNext()) {
                LNTableEntry entry = (LNTableEntry)i.next();
                entry.update();
            }
        }
    }


    private void updateExceptionTableOffsets() {
        if (exceptionTable != null) {
            Iterator i = exceptionTable.iterator();
            while(i.hasNext()) {
                ETEntry entry = (ETEntry)i.next();
                entry.update();
            }
        }
    }

    public /*private*/ int getMaxLocals() {
        CodeAttribute ca = (CodeAttribute)getAttribute("Code");
        if (ca == null) {
            throw new NullPointerException("No Code attribute found");
        }
        ca.setByteCode(getInstructionList().getBytes());

        return updateMaxLocals(ca, getInstructionList());
    }

    public /*private*/ int getMaxStack() {
        CodeAttribute ca = (CodeAttribute)getAttribute("Code");
        //ca.setByteCode(instructions.getBytes());

        return updateMaxStack(ca, getInstructionList());
    }


    /**
     * ExceptionTableEntry.
     */
    private class ETEntry {
        private Instruction start;
        private Instruction end;
        private Instruction handler;
        private CodeAttribute.ExceptionTableEntry entry;
       
        ETEntry(CodeAttribute.ExceptionTableEntry entry, Instruction start,
                Instruction end, Instruction handler) {
//             System.out.println("Created ETEntry: " + start + ", " + end+
//                                ", " + handler);
            this.entry = entry;
            this.start = start;
            this.end = end;
            this.handler = handler;
        }
       
        /**
         * Called, when bytecode instrumentation is done.
         * This method updates offset of the instruction to reflect
         * offset changes.
         */
        void update() {
//             System.out.println("Updating ETEntry: start " + entry.getStartPc()+
//                                "-->" + start.getOffset() +
//                                entry.getEndPc() + "-->" + end.getOffset() +
//                                entry.getHandlerPc() + "-->"+handler.getOffset()
//                                );
            entry.setStartPc(start.getOffset());
            entry.setEndPc(end.getOffset());
            entry.setHandlerPc(handler.getOffset());
        }


        Instruction getStart() {
            return start;
        }
        Instruction getEnd() {
            return end;
        }
        Instruction getHandler() {
            return handler;
        }
    }


    /**
     * Checks, whether or not this JiapiMethod is synthetic.
     *
     * @return true, if this JiapiMethod is synthetic
     */
    public boolean isSynthetic() {
        return (method.getAttribute(SyntheticAttribute.ATTRIBUTE_NAME) !=null);
    }
}
TOP

Related Classes of alt.jiapi.reflect.JiapiMethod

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.