⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fieldaccessorgenerator.java

📁 jboss规则引擎
💻 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 + -