I want to use bytecode instrumentation to access an integer array field, that I've added to a class. To do that, I need the index of the field in the class file, but I can't find it.
To explain in more detail, instead of (desired is a declared local integer, whose index I've grabbed from the local variable table):
desired = Server.rand.nextInt(4);
I want to do:
desired = _MyField[Server.rand.nextInt(_MyField.length)];
In JASM that would be:
// field reference to later access an index in the array
aload <local variable index of "this"> (push reference of class)
getfield <fieldindex> (pop reference, push field reference to stack)
// rand is a public static field in class Server
getstatic java.util.Random com.wurmonline.server.Server.rand
// field reference to pass to arraylength and the Random.nextInt(int) call
aload <local variable index of "this">
getfield <fieldindex> (push reference of array)
arraylength (pop reference, push array.length value)
invokevirtual int java.util.Random.nextInt(int) (pop length for rng, push index)
iaload (pop index, push integer value from array index)
istore <index of local variable "desired"> (pop value, store in variable)
The field is added using Javassist, were sb is a StringBuilder containing the field initialiser (e.g. "new int[] { n, n, n, n, n };"):
ctClass.addField(
new CtField(
HookManager.getInstance().getClassPool().get("[I"),
"_OtherServerMissions",
ctClass),
sb.toString());
Looking at a class file using DirtyJOE, a byte code editor that works with Java8/Major class file version 52, I can't find an index value for fields. Nor can I seem to find one using CtField, or an applicable AttributeInfo.
for (CtField ctField : ctClass.getDeclaredFields())
So I'm left wondering where to get the fieldindex from.
I hope the explanation was clear enough for the poor souls familiar with the matter.
Sometimes it really helps writing out the problem in a way to explain it to others.
The FIELDINDEX passed to the GETFIELD opcode is a reference into the constant pool of the class. You will have to add a FieldrefInfo to the class' constant pool. The return value of addFieldrefInfo() is its index in the constant pool.
ConstPool constPool = ctClass.getClassFile().getConstPool();
[...]
sb = new StringBuilder();
sb.append("new int[] { ");
for (int i : _OtherServerMissions)
sb.append(String.format("%d,", i));
sb.deleteCharAt(sb.lastIndexOf(","));
sb.append(" };");
ctClass.addField(
new CtField(
HookManager.getInstance().getClassPool().get("[I"),
"_OtherServerMissions",
ctClass),
sb.toString());
int otherFieldIndex = constPool.addFieldrefInfo(constPool.getThisClassInfo(), "_OtherServerMissions", "[I");
logger.log(Level.INFO, "Added OtherFieldIndex as " + otherFieldIndex);
You will also have to determine the index of "this", to push a reference of the class instance (if applicable) on the stack before using GETFIELD.
int thisIndex = -1;
LocalVariableAttribute lva = (LocalVariableAttribute)generateMission
.getMethodInfo()
.getCodeAttribute()
.getAttribute(LocalVariableAttribute.tag);
for (int i = 0; thisIndex < 0 && i < lva.tableLength(); i++) {
if (lva.variableName(i).equals("this"))
thisIndex = lva.index(i);
}
If it's not in the local variable table already, I don't know how to add it at this point. Or how exactly to add to the local variable table in general. Feel free to leave a comment (if you have enough rep points, duh...) if you do know.
The fixed question now contains the correct java assembly code used with the values outlined here.