InstructionHandle startTest = implementation.append(factory.createLoad(Type.STRING, 2));
// The first time it will be empty, the second time will be the first time's uninitialized jump instructions
for (int i = 0; i < tests.size(); ++i)
{
BranchInstruction branch = (BranchInstruction)tests.get(i);
// Initialize previous jump instruction to jump to the next branch
branch.setTarget(startTest);
}
tests.clear();
implementation.append(new PUSH(classGen.getConstantPool(), method.getName()));
implementation.append(factory.createInvoke(String.class.getName(), "equals", Type.BOOLEAN, new Type[]{Type.OBJECT}, Constants.INVOKEVIRTUAL));
// IFEQ compares the stack with 0, which means "if the previous comparison is false, ..."
BranchInstruction test1 = factory.createBranchInstruction(Constants.IFEQ, null);
tests.add(test1);
implementation.append(test1);
implementation.append(factory.createLoad(new ArrayType(Type.OBJECT, 1), 4));
implementation.append(new ARRAYLENGTH());
implementation.append(new PUSH(classGen.getConstantPool(), method.getParameterTypes().length));
// Here I should test if args.length == <num>, if not equal then go to the next branch
// Create branch instructions with no offset, since it cannot be handled now, see above
BranchInstruction test2 = factory.createBranchInstruction(Constants.IF_ICMPNE, null);
tests.add(test2);
implementation.append(test2);
// Here I am on the right method, unless someone created 2 methods with same names and same number of
// parameters but of different type. In this last case if we're lucky it's the right method and we go
// via direct call, otherwise we will have a class cast exception and go via reflection
// Cast and invoke
// Put the metadata on the stack, to access its 'mbean' field, that will be put on the stack
// It's also the start of the try block
InstructionHandle tryStart = implementation.append(factory.createLoad(new ObjectType(MBeanMetaData.class.getName()), 1));
implementation.append(factory.createInvoke(MBeanMetaData.class.getName(), "getMBean", Type.OBJECT, new Type[0], Constants.INVOKEVIRTUAL));
// Cast the 'mbean' field to the proper type, the stack will contain the casted mbean
implementation.append(factory.createCheckCast(new ObjectType(management)));
// Now add all the arguments to the stack
Class[] signature = method.getParameterTypes();
Type[] invokeSignature = new Type[signature.length];
for (int i = 0; i < signature.length; ++i)
{
Class param = signature[i];
// Load all args on the stack
implementation.append(factory.createLoad(new ArrayType(Type.OBJECT, 1), 4));
// I want index 'i'
implementation.append(new PUSH(classGen.getConstantPool(), i));
// Now on the stack there is args[i]
implementation.append(factory.createArrayLoad(Type.OBJECT));
// Save the signature for the invocation
invokeSignature[i] = convertClassToType(param);
if (param.isPrimitive())
{
// On the stack I have the wrapper object, I have to convert them to primitive
replaceObjectWithPrimitive(param, implementation, factory);
}
else if (param.isArray())
{
// Cast args[i] to the proper class
implementation.append(factory.createCheckCast((ReferenceType)invokeSignature[i]));
}
else
{
// Cast args[i] to the proper class
implementation.append(factory.createCheckCast((ReferenceType)invokeSignature[i]));
}
}
Class returnClass = method.getReturnType();
Type returnType = convertClassToType(returnClass);
// On the stack we now have the casted mbean and all the casted arguments, invoke
implementation.append(factory.createInvoke(management, method.getName(), returnType, invokeSignature, Constants.INVOKEINTERFACE));
if (returnClass == Void.TYPE)
{
implementation.append(InstructionConstants.ACONST_NULL);
}
else if (returnClass.isArray())
{
// Thanks to the fact that we can assign any array to Object, we do nothing here
}
else if (returnClass.isPrimitive())
{
replacePrimitiveWithObject(returnClass, methodGen, implementation, factory);
}
InstructionHandle tryEnd = implementation.append(factory.createReturn(Type.OBJECT));
// In case of class cast exception, eat the exception and call super (hence using reflection)
// catch (ClassCastException x) {/* do nothing */}
// On the stack there is the exception, we assign it to local variable 'x'
ObjectType exceptionTypeCCE = new ObjectType("java.lang.ClassCastException");
LocalVariableGen x = methodGen.addLocalVariable("x", exceptionTypeCCE, null, null);
InstructionHandle handler = implementation.append(factory.createStore(exceptionTypeCCE, x.getIndex()));
x.setStart(handler);
x.setEnd(handler);
methodGen.addExceptionHandler(tryStart, tryEnd, handler, exceptionTypeCCE);
// This catch block is followed by another one, and I don't exit with a throw or a return
BranchInstruction skip = factory.createBranchInstruction(Constants.GOTO, null);
catches.add(skip);
implementation.append(skip);
// An IllegalAccessError is thrown if the MBean interface or a parameter class is not public
// We eat it and fall back to call super (hence using reflection)