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