final ConstPool constPool = classfile.getConstPool();
final int targetTypeConstPoolIndex = constPool.addClassInfo( this.targetBean.getName() );
final String desc = GET_SETTER_DESC;
final MethodInfo setterMethodInfo = new MethodInfo( constPool, GENERATED_SETTER_NAME, desc );
final Bytecode code = new Bytecode( constPool, 4, 6 );
StackMapTable stackmap = null;
/* | this | bean | args | i | raw bean | exception | */
if ( setters.length > 0 ) {
// required to exception table
int start;
int end;
// iconst_0 // i
code.addIconst( 0 );
// istore_3 // store i
code.addIstore( 3 );
// aload_1 // load the bean
code.addAload( 1 );
// checkcast // cast the bean into a raw bean
code.addCheckcast( this.targetBean.getName() );
// astore 4 // store the raw bean
code.addAstore( 4 );
/* current stack len = 0 */
// start region to handling exception (BulkAccessorException)
start = code.currentPc();
int lastIndex = 0;
for ( int i = 0; i < setters.length; ++i ) {
if ( setters[i] != null ) {
final int diff = i - lastIndex;
if ( diff > 0 ) {
// iinc 3, 1
code.addOpcode( Opcode.IINC );
code.add( 3 );
code.add( diff );
lastIndex = i;
}
}
/* current stack len = 0 */
// aload 4 // load the raw bean
code.addAload( 4 );
// aload_2 // load the args
code.addAload( 2 );
// iconst_i
code.addIconst( i );
// aaload
code.addOpcode( Opcode.AALOAD );
// checkcast
final Class[] setterParamTypes = setters[i].getParameterTypes();
final Class setterParamType = setterParamTypes[0];
if ( setterParamType.isPrimitive() ) {
// checkcast (case of primitive type)
// invokevirtual (case of primitive type)
this.addUnwrapper( code, setterParamType );
}
else {
// checkcast (case of reference type)
code.addCheckcast( setterParamType.getName() );
}
/* current stack len = 2 */
final String rawSetterMethodDesc = RuntimeSupport.makeDescriptor( setters[i] );
if ( !this.targetBean.isInterface() ) {
// invokevirtual
code.addInvokevirtual( targetTypeConstPoolIndex, setters[i].getName(), rawSetterMethodDesc );
}
else {
// invokeinterface
final Class[] params = setters[i].getParameterTypes();
int size;
if ( params[0].equals( Double.TYPE ) || params[0].equals( Long.TYPE ) ) {
size = 3;
}
else {
size = 2;
}
code.addInvokeinterface( targetTypeConstPoolIndex, setters[i].getName(), rawSetterMethodDesc, size );
}
}
// end region to handling exception (BulkAccessorException)
end = code.currentPc();
// return
code.addOpcode( Opcode.RETURN );
/* current stack len = 0 */
// register in exception table
final int throwableTypeIndex = constPool.addClassInfo( THROWABLE_CLASS_NAME );
final int handlerPc = code.currentPc();
code.addExceptionHandler( start, end, handlerPc, throwableTypeIndex );
// astore 5 // store exception
code.addAstore( 5 );
// new // BulkAccessorException
code.addNew( BULKEXCEPTION_CLASS_NAME );
// dup
code.addOpcode( Opcode.DUP );
// aload 5 // load exception
code.addAload( 5 );
// iload_3 // i
code.addIload( 3 );
// invokespecial // BulkAccessorException.<init>
final String consDesc = "(Ljava/lang/Throwable;I)V";
code.addInvokespecial( BULKEXCEPTION_CLASS_NAME, MethodInfo.nameInit, consDesc );
// athrow
code.addOpcode( Opcode.ATHROW );
final StackMapTable.Writer writer = new StackMapTable.Writer(32);
final int[] localTags = {
StackMapTable.OBJECT,
StackMapTable.OBJECT,
StackMapTable.OBJECT,
StackMapTable.INTEGER
};
final int[] localData = {
constPool.getThisClassInfo(),
constPool.addClassInfo( "java/lang/Object" ),
constPool.addClassInfo( "[Ljava/lang/Object;" ),
0
};
final int[] stackTags = {
StackMapTable.OBJECT
};
final int[] stackData = {
throwableTypeIndex
};
writer.fullFrame( handlerPc, localTags, localData, stackTags, stackData );
stackmap = writer.toStackMapTable( constPool );
}
else {
// return
code.addOpcode( Opcode.RETURN );
}
final CodeAttribute ca = code.toCodeAttribute();
if ( stackmap != null ) {
ca.setAttribute( stackmap );
}
setterMethodInfo.setCodeAttribute( ca );
setterMethodInfo.setAccessFlags( AccessFlag.PUBLIC );