📄 fieldaccessorgenerator.java
字号:
package org.drools.util.asm;
/*
* Copyright 2005 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.drools.asm.ClassWriter;
import org.drools.asm.Label;
import org.drools.asm.MethodVisitor;
import org.drools.asm.Opcodes;
import org.drools.asm.Type;
/**
* Will provide implementations of FieldAccessor as needed.
*
* There is no proxying involved.
*
* Uses ASM to generate a implementation of a FieldAccessor.
* Uses tableswitch so it is as fast as humanly possible.
* @deprecated Use ClassFieldExtractor instead.
* @author Michael Neale
* @author "Jeff Brown" <brown_j@ociweb.com>
*/
public class FieldAccessorGenerator {
private static FieldAccessorGenerator INSTANCE;
//used to make sure generated classes are unique...
private static final String GEN_PACKAGE_PREFIX = "org.drools.fieldaccess.";
private final Map cache = new HashMap();
private FieldAccessorGenerator() {
}
public static FieldAccessorGenerator getInstance() {
if ( FieldAccessorGenerator.INSTANCE == null ) {
FieldAccessorGenerator.INSTANCE = new FieldAccessorGenerator();
}
return FieldAccessorGenerator.INSTANCE;
}
/**
* Looks up an instance of a field accessor for the given class.
* If none is found it will generate one, and then cache it.
*/
public FieldAccessorMap getInstanceFor(final Class cls) throws Exception {
Object obj = this.cache.get( cls );
if ( obj == null ) {
obj = newInstanceFor( cls );
this.cache.put( cls,
obj );
}
return (FieldAccessorMap) obj;
}
/**
* Generate a new implementation for of a FieldAccessor for the given class.
* No caching. Uses ASM.
*/
public FieldAccessorMap newInstanceFor(final Class cls) throws Exception {
final ClassFieldInspector inspector = new ClassFieldInspector( cls );
final Method[] getters = (Method[]) inspector.getPropertyGetters().toArray( new Method[]{} );
final String generatedClassName = FieldAccessorGenerator.GEN_PACKAGE_PREFIX + cls.getName();
final byte[] generatedClass = AccessorClassFactory.generateClass( getters,
cls,
generatedClassName );
final ByteArrayClassLoader cl = new ByteArrayClassLoader( Thread.currentThread().getContextClassLoader() );
cl.addByteArray( generatedClassName,
generatedClass );
final FieldAccessor accessor = (FieldAccessor) cl.loadClass( generatedClassName ).newInstance();
final FieldAccessorMap map = new FieldAccessorMap( accessor,
inspector.getFieldNames() );
return map;
}
/**
* OK, deep breaths, this is where it all happens...
* If you don't know ASM, and a bit about bytecode, then move along, theres nothing to see here.
*
* @author Michael Neale
*/
static class AccessorClassFactory
implements
Opcodes {
private static final String GET_FIELD_BY_INDEX_METHOD_NAME = "getFieldByIndex";
private static String getShortName(final Class cls) {
final String name = cls.getName();
final String packageName = cls.getPackage().getName();
return name.substring( packageName.length() + 1,
name.length() );
}
public static byte[] generateClass(final Method getters[],
final Class targetClass,
String generatedClassName) throws Exception {
final ClassWriter cw = new ClassWriter( true );
generatedClassName = generatedClassName.replaceAll( "\\.",
"/" );
cw.visit( Opcodes.V1_2,
Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER,
generatedClassName,
null,
"java/lang/Object",
new String[]{Type.getInternalName( FieldAccessor.class )} );
cw.visitSource( getShortName( targetClass ) + ".java",
null );
doConstructor( cw );
doMethods( cw,
Type.getInternalName( targetClass ),
getters,
targetClass.isInterface() );
cw.visitEnd();
return cw.toByteArray();
}
private static void doMethods(final ClassWriter cw,
final String targetType,
final Method[] getters,
final boolean isInterface) {
MethodVisitor mv;
mv = cw.visitMethod( Opcodes.ACC_PUBLIC,
AccessorClassFactory.GET_FIELD_BY_INDEX_METHOD_NAME,
"(Ljava/lang/Object;I)Ljava/lang/Object;",
null,
null );
mv.visitCode();
final Label entry = new Label();
mv.visitLabel( entry );
mv.visitVarInsn( Opcodes.ALOAD,
1 );
mv.visitTypeInsn( Opcodes.CHECKCAST,
targetType );
final int target = 3;
mv.visitVarInsn( Opcodes.ASTORE,
target ); //this is the actual casted object
final Label start = new Label();
mv.visitLabel( start );
mv.visitVarInsn( Opcodes.ILOAD,
2 ); //the index, I think.
//END BOILERPLATE
final Label[] switchItems = new Label[getters.length];
for ( int i = 0; i < getters.length; i++ ) {
switchItems[i] = new Label();
}
//setup switch statment (with default)
final Label defaultSwitch = new Label();
mv.visitTableSwitchInsn( 0,
switchItems.length - 1,
defaultSwitch,
switchItems );
//START switch items
for ( int i = 0; i < getters.length; i++ ) {
final Method method = getters[i];
if ( method.getReturnType().isPrimitive() ) {
doSwitchItemBoxed( mv,
switchItems[i],
target,
targetType,
method.getName(),
method.getReturnType(),
isInterface );
} else {
doSwitchItemObject( mv,
switchItems[i],
target,
targetType,
method.getName(),
method.getReturnType(),
isInterface );
}
}
//the default item...
mv.visitLabel( defaultSwitch );
mv.visitInsn( Opcodes.ACONST_NULL );
mv.visitInsn( Opcodes.ARETURN );
final Label endLabel = new Label();
mv.visitLabel( endLabel );
mv.visitMaxs( 0,
0 ); //dummy values, its calculated anyway
mv.visitEnd();
}
/** a switch item that requires autoboxing */
private static void doSwitchItemBoxed(final MethodVisitor mv,
final Label switchItem,
final int target,
final String targetType,
final String targetMethod,
final Class scalarType,
final boolean isInterface) {
Class boxType = null;
boxType = getBoxType( scalarType );
final String scalarDescriptor = Type.getDescriptor( scalarType );
final String internalBoxName = Type.getInternalName( boxType );
mv.visitLabel( switchItem );
mv.visitTypeInsn( Opcodes.NEW,
internalBoxName );
mv.visitInsn( Opcodes.DUP );
mv.visitVarInsn( Opcodes.ALOAD,
target );
if ( isInterface ) {
mv.visitMethodInsn( Opcodes.INVOKEINTERFACE,
targetType,
targetMethod,
"()" + scalarDescriptor );
} else {
mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
targetType,
targetMethod,
"()" + scalarDescriptor );
}
mv.visitMethodInsn( Opcodes.INVOKESPECIAL,
internalBoxName,
"<init>",
"(" + scalarDescriptor + ")V" );
mv.visitInsn( Opcodes.ARETURN );
}
/** Work out the appropriate box type for a scalar/primitive class */
private static Class getBoxType(final Class scalarType) {
if ( scalarType == int.class ) {
return Integer.class;
} else if ( scalarType == boolean.class ) {
return Boolean.class;
} else if ( scalarType == char.class ) {
return Character.class;
} else if ( scalarType == byte.class ) {
return Byte.class;
} else if ( scalarType == short.class ) {
return Short.class;
} else if ( scalarType == long.class ) {
return Long.class;
} else if ( scalarType == float.class ) {
return Float.class;
} else if ( scalarType == double.class ) {
return Double.class;
} else if ( scalarType == void.class ) {
return Void.class;
} else {
throw new IllegalArgumentException( "Unknown scalar type: " + scalarType.getName() );
}
}
/** A regular switch item, which doesn't require boxing */
private static void doSwitchItemObject(final MethodVisitor mv,
final Label label,
final int target,
final String targetType,
final String targetMethod,
final Class returnClass,
final boolean isInterface) {
final String returnType = "()" + Type.getDescriptor( returnClass );
mv.visitLabel( label );
mv.visitVarInsn( Opcodes.ALOAD,
target );
if ( isInterface ) {
mv.visitMethodInsn( Opcodes.INVOKEINTERFACE,
targetType,
targetMethod,
returnType );
} else {
mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
targetType,
targetMethod,
returnType );
}
mv.visitInsn( Opcodes.ARETURN );
}
private static void doConstructor(final ClassWriter cw) {
MethodVisitor mv;
mv = cw.visitMethod( Opcodes.ACC_PUBLIC,
"<init>",
"()V",
null,
null );
mv.visitCode();
final Label l0 = new Label();
mv.visitLabel( l0 );
mv.visitLineNumber( 5,
l0 );
mv.visitVarInsn( Opcodes.ALOAD,
0 );
mv.visitMethodInsn( Opcodes.INVOKESPECIAL,
"java/lang/Object",
"<init>",
"()V" );
mv.visitInsn( Opcodes.RETURN );
final Label l1 = new Label();
mv.visitLabel( l1 );
// mv.visitLocalVariable( "this",
// "Lcom/something/MyObjectFieldAccessor;",
// null,
// l0,
// l1,
// 0 );
mv.visitMaxs( 1,
1 );
mv.visitEnd();
}
}
/**
* Simple classloader for the ASM generated accessors.
* @author Michael Neale
*/
static class ByteArrayClassLoader extends ClassLoader {
public ByteArrayClassLoader(final ClassLoader parent) {
super( parent );
}
public void addByteArray(final String name,
final byte[] bytes) {
defineClass( name,
bytes,
0,
bytes.length );
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -