Package org.objectweb.speedo.generation.enhancer.pc

Source Code of org.objectweb.speedo.generation.enhancer.pc.DefaultCodeAccessorModifier

/**
* Copyright (C) 2001-2005 France Telecom R&D
*
* 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 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 org.objectweb.speedo.generation.enhancer.pc;

import org.objectweb.asm.CodeAdapter;
import org.objectweb.asm.CodeVisitor;
import org.objectweb.asm.Constants;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.Attribute;
import org.objectweb.speedo.lib.Personality;
import org.objectweb.speedo.metadata.SpeedoField;
import org.objectweb.speedo.metadata.SpeedoClass;
import org.objectweb.speedo.metadata.SpeedoMetaInfo;
import org.objectweb.speedo.generation.enhancer.common.LoggedClassAdapter;
import org.objectweb.speedo.generation.enhancer.common.Util;
import org.objectweb.speedo.generation.lib.NamingRules;
import org.objectweb.util.monolog.api.Logger;
import org.objectweb.util.monolog.api.BasicLevel;

import java.util.List;
import java.util.ArrayList;
import java.util.Collection;

/**
* Replaces field accesses by calls to getter and setter methods.
*
* Adapted from modifyMethods and replaceInstruction in EnhancerTool.
*/
public class FieldAccessModifier extends LoggedClassAdapter {

    /**
     * Stack size variation corresponding to each JVM instruction. This stack
     * variation is equal to the size of the values produced by an instruction,
     * minus the size of the values consumed by this instruction.
     */

    final static int[] SIZE;

    /**
     * Computes the stack size variation corresponding to each JVM instruction.
     * Copied from the org.objectweb.asm.CodeWriter class.
     */

    static {
        int i;
        int[] b = new int[202];
        String s =
                "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDDCDCDEEEEEEEEE" +
                "EEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCDCDCEEEEDDDDDDDCDCDCEFEF" +
                "DDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFEDDDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE";
        for (i = 0; i < b.length; ++i) {
            b[i] = s.charAt(i) - 'E';
        }
        SIZE = b;
    }

    /**
     * A collections of SpeedoXMLDescriptors describing known persistent classes.
     */
    private final SpeedoClass speedoClass;
    private final SpeedoMetaInfo smi;;

    /**
     * Internal name of the visited class.
     */
    final String owner;

    /**
     * Internal name of the corresponding "XXXFields" class.
     */
    final String fieldsOwner;

    final int nbfields;

    /**
     * Constructs a new {@link FieldAccessModifier}.
     *
     * @param cv the class visitor to be used to generate the modified class
     * @param sc is the SpeedoMeta object representing the persistent class
     * visited
     * @param p is the personality of Speedo
     */
    public FieldAccessModifier(final ClassVisitor cv,
        final SpeedoClass sc,
        Logger logger,
        Personality p) {
        super(cv, p, logger);
        this.speedoClass = sc;
        this.smi = speedoClass.moPackage.xmlDescriptor.smi;
        owner = getJVMClassName(sc.getFQName());
        nbfields = speedoClass.computeFieldNumbers();
        fieldsOwner = NamingRules.fieldsName(owner);
    }

    // IMPLEMENTATION OF THE ClassVisitor INTERFACE //
    // ---------------------------------------------//

    public void visit(final int version, final int access,
                      final String name,
                      final String superName,
                      final String[] interfaces,
                      final String sourceFile) {
        cv.visit(version, access, name, superName, interfaces, sourceFile);
    }

    public CodeVisitor visitMethod(final int access,
                                   final String name,
                                   final String desc,
                                   final String[] exceptions,
                                   final Attribute attrs) {
        CodeVisitor covis = this.cv.visitMethod(access, name, desc, exceptions, attrs);
        if ((access & Constants.ACC_ABSTRACT) != 0) {
            // nothing to be modified for abstract methods
      logger.log(BasicLevel.DEBUG,
          "ignore the abstract method " + name + " " + desc);
            return covis;
        }
        if (name.startsWith("jdo")
                && !name.equals("jdoPreDelete")
                && !name.equals("jdoPreStore")
                && !name.equals("jdoPreClear")
                && !name.equals("jdoPostLoad")
                ) {
            // do not modify the accessor methods generated by the enhancer
      logger.log(BasicLevel.DEBUG,
          "ignoe the method " + name + " " + desc);
            return covis;
        }
        if (name.equals("<clinit>") || (access & Constants.ACC_STATIC) != 0) {
            // for static methods replace accesses to fields by calls to
      // accessor methods.
            return new DefaultCodeAccessorModifier(desc, false, covis, name, this);
        } else {
            // Fields accessing to 'this' are replaced by field accesses to
      // the state object (the added actual parameter), and a header
      // is added to call speedoReadIntention or speedoWriteIntention. In case of the
      // constructor the call to speedoReadIntention or speedoWriteIntention is added
      // after the super call.
      return new OptimisticCodeAccessorModifier(desc, covis, name, this);
        }
    }

    // OTHER METHODS //
    // --------------//

    /**
     * Looks for a specific <code>SpeedoField</code> in the object model.
     *
     * @param name the name of the field to be fetched
     * @param className the complete class name that the field belongs to
     * @return the corresponding SpeedoField if it exists, null either
     */
    SpeedoField fetchJDOField(final String name, final String className) {
        SpeedoClass c = smi.getSpeedoClass(className);
        if (c != null) {
          SpeedoField speedoField = null;
          //try to find the field in the current class
          // and in its parents (if any)
          while (speedoField == null && c != null) {
            speedoField = (SpeedoField) c.fields.get(name);
            c = c.getSuper();
          }
          return speedoField;
        } else {
            return null;
        }
    }

}
    /**
     * The default method code modifier, used for constructors, static methods
     * and class initializers. This code modifier does not modify accesses to
     * 'this' fields, but replaces accesses to other fields by calls to accessor
     * methods.
     */
    class DefaultCodeAccessorModifier extends CodeAdapter {

        /**
         * Descriptor of the visited method
         */
        protected String desc;

        /**
         * Types of the parameters of the visited method
         */
        protected Type[] types;

        /**
         * Total size of the formal parameters of the visited method
         */
        protected int params;

        /**
         * A label that designates the start of the original method code
         */
        private Label start;

        /**
         * A label that designates the start of the header added to the method
         */
        private Label header;

        /**
         * A list of instructions that have been visited but not yet regenerated
         * through {@link #cv cv}. This list is a list of Insn objects. It is
         * used to be able to modify an ALOAD 0 instruction long after it has
         * been visited by this visitor, when a GETFIELD or PUTFIELD instruction
         * is encountered.
         */
        protected List insns;

        /**
         * Symbolic state of the execution stack at the current bytecode
         * instruction. The last element corresponds to the top of the stack.
         * Each element is either <tt>null</tt> or an Integer object. The last
         * case signifies that, at execution time, and at the current bytecode
         * instruction, this stack element will contain 'this'. Moreover the
         * integer value indicates the index in 'insns' of the instruction that
         * pushed this value on the stack.
         */
        protected List stack;

        /**
         * Indicates the fields that the visited method may read on 'this'.
         * If the i-th bit is set to 1, then the i-th field may be read.
         */
        protected long[] readFields;

        /**
         * Indicates the fields that the visited method may write on 'this'.
         * If the i-th bit is set to 1, then the i-th field may be witten.
         */
        protected long[] writtenFields;


    boolean isConstructor;

    boolean firstIns = false;
   
    FieldAccessModifier cam;

        /**
         * Constructs a new {@link DefaultCodeAccessorModifier}.
         *
         * @param desc the descriptor of the visited method
         * @param hasHeader indicates if a header must be added to the visited
         * method
         * @param cv the code visitor to be used to generate the modified method
         */
        public DefaultCodeAccessorModifier(final String desc,
                                           final boolean hasHeader,
                                           final CodeVisitor cv,
                       final String name,
                       final FieldAccessModifier _cam) {
            super(cv);
            this.cam = _cam;
      isConstructor = name.equals("<init>");
      cam.getLogger().log(BasicLevel.INFO, "visit the method " + name + " " + desc
          + " (const=" + isConstructor
          + ", header=" + hasHeader + ")");
      firstIns = true;
            int size = (cam.nbfields/64) + ((cam.nbfields % 64) > 0 ? 1 : 0);
            writtenFields = new long[size];
            readFields = new long[size];
            this.desc = desc;
            this.types = Type.getArgumentTypes(desc);
            params = 1;
            for (int i = 0; i < types.length; ++i) {
                params += types[i].getSize();
            }
            this.insns = new ArrayList();
            this.stack = new ArrayList();
      if (hasHeader && !isConstructor) {
          cam.getLogger().log(BasicLevel.DEBUG, "Add Jump at the begin of method: " + name);
        addJumpHeader();
            }
        }

    protected void addJumpHeader() {
      start = new Label();
      header = new Label();
      flushInsns();
      cv.visitJumpInsn(Constants.GOTO, header);
      visitLabel(start);
    }

        // IMPLEMENTATION OF THE CodeVisitor INTERFACE //
        // --------------------------------------------//

        public void visitInsn(final int opcode) {
            if ((opcode >= Constants.IRETURN && opcode <= Constants.RETURN) ||
                    opcode == Constants.ATHROW) {
                flushInsns();
                cv.visitInsn(opcode);
            } else {
                if (opcode == Constants.DUP && stack.size() > 0) {
                    // optimization for DUP
                    stack.add(stack.get(stack.size() - 1));
                    cam.getLogger().log(BasicLevel.DEBUG, "stack=" + stack);
                } else {
                    updateStack(FieldAccessModifier.SIZE[opcode]);
                }
                insns.add(new Insn(opcode));
            }
        }

        public void visitIntInsn(final int opcode, final int operand) {
            updateStack(FieldAccessModifier.SIZE[opcode]);
            insns.add(new Insn(true, opcode, operand));
        }

        public void visitVarInsn(final int opcode, final int var) {
            if (opcode == Constants.RET) {
                flushInsns();
                cv.visitVarInsn(opcode, var);
            } else {
                if (opcode == Constants.ALOAD && var == 0) {
                    stack.add(new Integer(insns.size()));
          cam.getLogger().log(BasicLevel.DEBUG, "stack=" + stack);
                } else {
                    updateStack(FieldAccessModifier.SIZE[opcode]);
                }
                insns.add(new Insn(false, opcode, var));
            }
        }

        public void visitTypeInsn(final int opcode, final String desc) {
            updateStack(FieldAccessModifier.SIZE[opcode]);
            insns.add(new Insn(opcode, desc));
        }

        public void visitFieldInsn(final int opcode,
                                   final String owner,
                                   final String name,
                                   final String desc) {
      SpeedoField field = cam.fetchJDOField(name, owner.replace('/', '.'));
      char c = desc.charAt(0);
          int fieldSize = (c == 'D' || c == 'J' ? 2 : 1);
          //Calculate if the field type is reference to a persistence object
          // (use an accessor) or primitive field (direct access)
          // primitive fields or java.lang.* or java.math.*
          boolean directFieldAccess = c != 'L' || desc.startsWith("Ljava/lang/") || desc.startsWith("Ljava/math/");
          int stackSizeVariation;
          int thisInsnIndex = -1;
            switch (opcode) {
            case Constants.GETSTATIC:
                stackSizeVariation = fieldSize;
              //No modification
                break;
            case Constants.PUTSTATIC:
                stackSizeVariation = -fieldSize;
              //No modification
                break;
            case Constants.GETFIELD:
                stackSizeVariation = fieldSize - 1;
        if (field == null) {
          break;
        }
              if (directFieldAccess) {
                thisInsnIndex = getThisInsnIndex(0);
                //If the field holder is not 'this' then use a getter
                directFieldAccess = thisInsnIndex != -1;
              }
        if (directFieldAccess) { //direct field access
          readFields[field.number / 64] |= (1L << (field.number % 64));
          visitThisFieldInsn(
              opcode, owner, name, desc, thisInsnIndex);
          cam.getLogger().log(BasicLevel.DEBUG, "direct field use: " + name);
        } else { //use an getter
          String getterName = NamingRules.getterName(field);
          String getterDesc = "()" + desc;
          insns.add(new Insn(Constants.INVOKEVIRTUAL,
              owner, getterName, getterDesc));
          cam.getLogger().log(BasicLevel.DEBUG, "getter assignment: " + getterName);
        }
        updateStack(stackSizeVariation);
                return;
            case Constants.PUTFIELD:
                stackSizeVariation = 0 - (fieldSize + 1);
        if (field == null) {
          break;
        }
              if (directFieldAccess) {
                thisInsnIndex = getThisInsnIndex(fieldSize);
                //If the field holder is not 'this' then use a setter
                directFieldAccess = thisInsnIndex != -1;
              }
        if (directFieldAccess) { // direct field access
          writtenFields[field.number / 64] |= (1L << (field.number % 64));
          visitThisFieldInsn(opcode, owner, name, desc, thisInsnIndex);
          cam.getLogger().log(BasicLevel.DEBUG, "direct field assignment: " + name);
        } else { //use an setter
          String setterName = NamingRules.setterName(field);
          String setterDesc = "(" + desc + ")V";
          insns.add(new Insn(Constants.INVOKEVIRTUAL,
              owner, setterName, setterDesc));
          cam.getLogger().log(BasicLevel.DEBUG, "setter assignment: " + setterName);
        }
        updateStack(stackSizeVariation);
        return;
      default:
              stackSizeVariation = - fieldSize -1;
            }
      updateStack(stackSizeVariation);
      insns.add(new Insn(opcode, owner, name, desc));
        }

      /**
       * Calculates the index of the ALOAD0 instruction with the field index
       * @param fieldidx
       * @return -1 if not ALOAD0 found, otherwise an integer value
       */
      private int getThisInsnIndex(int fieldidx) {
        int stackSize = stack.size();
        if (stackSize > 0 && stackSize > fieldidx) {
          Integer i = (Integer) stack.get(stackSize - 1 - fieldidx);
          if (i != null) {
            return i.intValue();
          } else {
              cam.getLogger().log(BasicLevel.DEBUG, "i is null (stack: " + stack + ")");
          }
        } else {
            cam.getLogger().log(BasicLevel.DEBUG, "stack size: " + stackSize);
        }
        return -1;
      }
        public void visitMethodInsn(final int opcode,
                                    final String owner,
                                    final String name,
                                    final String desc) {
            // computes the stack size variation
            int size = opcode == Constants.INVOKESTATIC ? 0 : 1;
            int c = 1;
            while (true) {
                char car = desc.charAt(c++);
                if (car == ')') {
                    car = desc.charAt(c);
                    if (car == 'V') {
                        size = -size;
                    } else {
                        size = (car == 'D' || car == 'J' ? 2 : 1) - size;
                    }
                    break;
                } else if (car == 'L') {
                    while (desc.charAt(c++) != ';') {
                    }
                    size += 1;
                } else if (car == '[') {
                    while ((car = desc.charAt(c)) == '[') {
                        ++c;
                    }
                    if (car == 'D' || car == 'J') {
                        size -= 1;
                    }
                } else if (car == 'D' || car == 'J') {
                    size += 2;
                } else {
                    size += 1;
                }
            }
            updateStack(size);
            insns.add(new Insn(opcode, owner, name, desc));
      if (firstIns && isConstructor) {
        if (opcode == Constants.INVOKESPECIAL) {
          firstIns = false;
          generateConstructorHeader();
        }
      }
        }

        public void visitJumpInsn(final int opcode, final Label label) {
            flushInsns();
            cv.visitJumpInsn(opcode, label);
        }

        public void visitLabel(final Label label) {
            insns.add(new Insn(label));
        }

        public void visitLdcInsn(final Object cst) {
            int size;
            if (cst instanceof Double || cst instanceof Long) {
                size = 2;
            } else {
                size = 1;
            }
            updateStack(size);
            insns.add(new Insn(cst));
        }

        public void visitIincInsn(final int var, final int increment) {
            // no stack change
            insns.add(new Insn(var, increment));
        }

        public void visitTableSwitchInsn(final int min,
                                         final int max,
                                         final Label dflt,
                                         final Label labels[]) {
            flushInsns();
            cv.visitTableSwitchInsn(min, max, dflt, labels);
        }

        public void visitLookupSwitchInsn(final Label dflt,
                                          final int keys[],
                                          final Label labels[]) {
            flushInsns();
            cv.visitLookupSwitchInsn(dflt, keys, labels);
        }

        public void visitMultiANewArrayInsn(final String desc,
                                            final int dims) {
            updateStack(1 - dims);
            insns.add(new Insn(desc, dims));
        }

        public void visitTryCatchBlock(final Label start,
                                       final Label end,
                                       final Label handler,
                                       final String type) {
            flushInsns();
            super.visitTryCatchBlock(start, end, handler, type);
        }

        public void visitMaxs(final int maxStack, final int maxLocals) {
            flushInsns();
            if (header != null) {
                cv.visitLabel(header);
                generateMethodHeader();
                cv.visitJumpInsn(Constants.GOTO, start);
            }
            cv.visitMaxs(maxStack, maxLocals);
        }

        public void visitLocalVariable(final String name,
                                       final String desc,
                                       final Label start,
                                       final Label end,
                                       final int index) {
            flushInsns();
            super.visitLocalVariable(name, desc, start, end, index);
        }

        public void visitLineNumber(final int line, final Label start) {
            flushInsns();
            super.visitLineNumber(line, start);
        }

        // OTHER METHODS //
        // --------------//

        /**
         * Generates a header which is added at the begining of the the visited
         * method. The default implementation of this method does nothing.
         */
        protected void generateMethodHeader() {
            // does nothing
        }

    protected void generateConstructorHeader() {
      // does nothing
    }

        /**
         * Visits field access instruction on 'this'.
         *
         * @param opcode GETFIELD or PUTFIELD
         * @param owner internal name of the owner class of the field
         * @param name name of the field
         * @param desc type descriptor of te field
         * @param pushThisInsn index in {@link #insns insns} of the ALOAD 0
         * instruction that pushed the 'this' value which is, at this bytecode
         * instruction, at the top (for GETFIELD) or just under the top (for
         * PUTFIELD) of the stack.
         */
        protected void visitThisFieldInsn(final int opcode,
                                          final String owner,
                                          final String name,
                                          final String desc,
                                          final int pushThisInsn) {
            // keeps the field access instructions on 'this' unchanged
            insns.add(new Insn(opcode, owner, name, desc));
        }

        /**
         * Makes {@link #cv cv} visit the instructions stored in {@link #insns
         * insns}, and then clear this list, as well as the stack. This method
         * is used when the end of method is encountered, but also each time
         * a jump or return statement is visited, because we can not easily know
         * the stack state at the next instruction (this is possible but
         * complicated: we prefer to keep the algorithm simple. The cost of this
         * simplification is that some field accesses on 'this' will not be
         * statically detected, and will therefore be replaced by calls to
         * accessor methods, which is less efficient but safe).
         */
        protected void flushInsns() {
            int n = insns.size();
            for (int i = 0; i < n; ++i) {
                ((Insn) insns.get(i)).accept(cv);
            }
            insns.clear();
            stack.clear();
            cam.getLogger().log(BasicLevel.DEBUG, "stack=" + stack);
        }

        /**
         * Pushes some <tt>null</tt> elements on the stack, or pops some
         * elements.
         * @param n the number of elements to be pushed (if n is positive), or
         * to be popped (if n is negative).
         */
        private void updateStack(final int n) {
            int size = stack.size();
            if (n > 0) {
                for (int i = 0; i < n; ++i) {
                    stack.add(null);
                    cam.getLogger().log(BasicLevel.DEBUG, "stack=" + stack);
                }
            } else {
                int m = -n;
                if (m >= size) {
                    stack.clear();
                    cam.getLogger().log(BasicLevel.DEBUG, "stack=" + stack);
                } else {
                    for (int i = 0; i < m; ++i) {
                        stack.remove(--size);
            cam.getLogger().log(BasicLevel.DEBUG, "stack=" + stack);
                    }
                }
            }
        }
    }

     /**
     * The method code modifier used for all non static methods. This code modifier
     * replaces accesses to 'this' fields by accesses to the 'state' object, and
     * replaces accesses to other fields by calls to accessor methods. It also
     * adds a header to the method to call speedoReadIntention or speedoWriteIntention.
     */
    class OptimisticCodeAccessorModifier extends DefaultCodeAccessorModifier {

        /**
         * Constructs a new {@link OptimisticCodeAccessorModifier}.
         *
         * @param desc the descriptor of the PESSIMISTIC method, and not of
         * the optimistic one
         * @param cv the code visitor to be used to generate the modified method
         */
        public OptimisticCodeAccessorModifier(final String desc,
                                              final CodeVisitor cv,
                        String name,
                        FieldAccessModifier cam) {
            super(desc, true, cv, name, cam);
        }

        // OVERRIDEN METHODS //
        // ------------------//

        public void visitVarInsn(final int opcode, final int var) {
            // shifts local variables that are just after the original actual
            // parameters (ie without counting the added formal paramter), to
            // make room for the added actual parameter
            super.visitVarInsn(opcode, var >= params ? var + 1 : var);
        }

        public void visitIincInsn(final int var, final int increment) {
            // shifts local variables that are just after the original actual
            // parameters (ie without counting the added formal paramter), to
            // make room for the added actual parameter
            super.visitIincInsn(var >= params ? var + 1 : var, increment);
        }

        public void visitMaxs(final int maxStack, final int maxLocals) {
            boolean allUnmodified = true;
            for (int i=0; allUnmodified && i<cam.nbfields; i++) {
                allUnmodified &= readFields[i/64] == 0 & writtenFields[i/64] == 0;
            }
            if (allUnmodified) {
                super.visitMaxs(maxStack, maxLocals + (cam.nbfields/64) + 1);
            } else {
                super.visitMaxs(Math.max(maxStack, 4), maxLocals + (cam.nbfields/64) + 1);
            }
        }

     protected void generateConstructorHeader() {
       cam.getLogger().log(BasicLevel.DEBUG, "Create reference state after the super in the constructor");
       flushInsns();

       //if (speedoGetReferenceState() == null) {
       cv.visitVarInsn(Constants.ALOAD, 0);
       cv.visitMethodInsn(Constants.INVOKEVIRTUAL, cam.owner,
           "speedoGetReferenceState",
           "()Lorg/objectweb/speedo/mim/api/StateItf;");
       Label afterSetState = new Label();
       cv.visitJumpInsn(Constants.IFNONNULL, afterSetState);

       //creating the reference state by calling
       // 'speedoSetReferenceState(speedoCreateState())'
       cv.visitVarInsn(Constants.ALOAD, 0);
       cv.visitVarInsn(Constants.ALOAD, 0);
       cv.visitMethodInsn(Constants.INVOKEVIRTUAL, cam.owner,
           "speedoCreateState",
           "()Lorg/objectweb/speedo/mim/api/StateItf;");
       cv.visitMethodInsn(Constants.INVOKEVIRTUAL, cam.owner,
           "speedoSetReferenceState",
           "(Lorg/objectweb/speedo/mim/api/StateItf;)V");

       cv.visitLabel(afterSetState);

       //Load the reference state in a local variable
       cv.visitVarInsn(Constants.ALOAD, 0);
       cv.visitMethodInsn(Constants.INVOKEVIRTUAL, cam.owner,
           "speedoGetReferenceState",
           "()Lorg/objectweb/speedo/mim/api/StateItf;");
       cv.visitTypeInsn(Constants.CHECKCAST, NamingRules.fieldsName(cam.owner));
       cv.visitVarInsn(Constants.ASTORE, params);
     }

        protected void generateMethodHeader() {
            boolean allUnmodified = true;
            boolean hasWritten = false;
            for (int i=0; allUnmodified && i<cam.nbfields; i++) {
                hasWritten |= writtenFields[i/64] != 0;
                allUnmodified &= readFields[i/64] == 0 & writtenFields[i/64] == 0;
            }
      cam.getLogger().log(BasicLevel.DEBUG, "allUnmodified=" + allUnmodified);
            if (allUnmodified) {
                return;
            }
            // generate code corresponding to
            // 'XXXFields state = speedoRead/WriteIntention(fieldIds);'
      cv.visitVarInsn(Constants.ALOAD, 0);
            // we always use readFields, even for a writeIntention
            // (fields that are written but not read do not need to be loaded
            // from the database before being used)

      // define the long[] value such as new long[]{56L, 45L, 45646163}
          Util.visitIntConstant(cv, readFields.length);
          cv.visitIntInsn(Constants.NEWARRAY, Constants.T_LONG);
          StringBuffer sb = new StringBuffer();
            sb.append(hasWritten ? "speedoWriteIntention" : "speedoReadIntention");
            sb.append("(new long[]{");
            String sep = "";
          for(int i=0; i<readFields.length; i++) {
              cv.visitInsn(Constants.DUP);
              Util.visitIntConstant(cv, i);
        Util.visitLongConstant(cv, readFields[i] | writtenFields[i]);
            sb.append(sep);
            sep = ", ";
            sb.append(readFields[i] | writtenFields[i]);
            sb.append("L");
              cv.visitInsn(Constants.LASTORE);
          }
          sb.append(")");
          cam.getLogger().log(BasicLevel.INFO, sb.toString());
      cv.visitMethodInsn(
          Constants.INVOKEVIRTUAL,
          cam.owner,
          hasWritten ? "speedoWriteIntention" : "speedoReadIntention",
          "([J)Lorg/objectweb/speedo/mim/api/StateItf;");
            //Store return value
      cv.visitTypeInsn(Constants.CHECKCAST, NamingRules.fieldsName(cam.owner));
      cv.visitVarInsn(Constants.ASTORE, params);
        }
        protected void visitThisFieldInsn(final int opcode,
                                          final String owner,
                                          final String name,
                                          final String desc,
                                          final int pushThisInsn) {
            // replaces the field access instructions on 'this' by field
            // access instructions on the state object
          cam.getLogger().log(BasicLevel.DEBUG, "replace ALOAD0 at " + pushThisInsn);
            insns.set(pushThisInsn, new Insn(false, Constants.ALOAD, params));
            insns.add(new Insn(opcode, cam.fieldsOwner, name, desc));
        }
    }

    /**
     * Object representation of a sequential bytecode instruction. A sequential
     * bytecode instruction is any instruction that is not a jump, a return,
     * a ret or a switch instruction. A single class is used to represent
     * several types of bytecode instruction, in order to minimize the size of
     * the outer class.
     */
    class Insn {

        // the sequential bytecode instruction types
        final static int INSN = 1;
        final static int INT_INSN = 2;
        final static int VAR_INSN = 3;
        final static int TYPE_INSN = 4;
        final static int FIELD_INSN = 5;
        final static int METH_INSN = 6;
        final static int LABEL_INSN = 7;
        final static int LDC_INSN = 8;
        final static int IINC_INSN = 9;
        final static int MANA_INSN = 10;

        /**
         * Type of this instruction
         */
        private int type;

        /**
         * Opcode of this instruction
         */
        private int opcode;

        // operand(s) of this instruction

        private int int1, int2;
        private String str1, str2, str3;
        private Object obj;

        public Insn(final int opcode) {
            this.type = INSN;
            this.opcode = opcode;
        }

        public Insn(final boolean intInsn, final int opcode, final int val) {
            this.type = intInsn ? INT_INSN : VAR_INSN;
            this.opcode = opcode;
            this.int1 = val;
        }

        public Insn(final int opcode, final String desc) {
            this.type = TYPE_INSN;
            this.opcode = opcode;
            this.str1 = desc;
        }

        public Insn(final int opcode,
                    final String owner,
                    final String name,
                    final String desc) {
            this.type = opcode <= Constants.PUTFIELD ? FIELD_INSN : METH_INSN;
            this.opcode = opcode;
            this.str1 = owner;
            this.str2 = name;
            this.str3 = desc;
        }

        public Insn(final Label label) {
            this.type = LABEL_INSN;
            this.obj = label;
        }

        public Insn(final Object cst) {
            this.type = LDC_INSN;
            this.obj = cst;
        }

        public Insn(final int var, final int inc) {
            this.type = IINC_INSN;
            this.int1 = var;
            this.int2 = inc;
        }

        public Insn(final String desc, final int dims) {
            this.type = MANA_INSN;
            this.str1 = desc;
            this.int1 = dims;
        }

        /**
         * Makes the given code visitor visit this instruction.
         *
         * @param cv a code visitor
         */
        public void accept(final CodeVisitor cv) {
            switch (type) {
            case INSN:
                cv.visitInsn(opcode);
                break;
            case INT_INSN:
                cv.visitIntInsn(opcode, int1);
                break;
            case VAR_INSN:
                cv.visitVarInsn(opcode, int1);
                break;
            case TYPE_INSN:
                cv.visitTypeInsn(opcode, str1);
                break;
            case FIELD_INSN:
                cv.visitFieldInsn(opcode, str1, str2, str3);
                break;
            case METH_INSN:
                cv.visitMethodInsn(opcode, str1, str2, str3);
                break;
            case LABEL_INSN:
                cv.visitLabel((Label) obj);
                break;
            case LDC_INSN:
                cv.visitLdcInsn(obj);
                break;
            case IINC_INSN:
                cv.visitIincInsn(int1, int2);
                break;
            default:
                cv.visitMultiANewArrayInsn(str1, int1);
            }
        }
    }
TOP

Related Classes of org.objectweb.speedo.generation.enhancer.pc.DefaultCodeAccessorModifier

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.