Package org.datanucleus.enhancer.asm

Source Code of org.datanucleus.enhancer.asm.ASMImplementationGenerator

/**********************************************************************
Copyright (c) 2007 Andy Jefferson and others. All rights reserved.
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.

Contributors:
    ...
**********************************************************************/
package org.datanucleus.enhancer.asm;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.JDOClassLoaderResolver;
import org.datanucleus.enhancer.AbstractImplementationGenerator;
import org.datanucleus.enhancer.ClassEnhancer;
import org.datanucleus.enhancer.EnhancerClassLoader;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.ClassMetaData;
import org.datanucleus.metadata.InterfaceMetaData;
import org.datanucleus.metadata.MetaDataManager;
import org.datanucleus.util.ClassUtils;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

/**
* Implementation generator using ASM bytecode manipulation library.
*/
public class ASMImplementationGenerator extends AbstractImplementationGenerator
{
    /** Writer for the implementation class. */
    ClassWriter writer;

    /** ASM class name of the implementation class. */
    String asmClassName;

    /** ASM type descriptor for the implementation class. */
    String asmTypeDescriptor;

    /**
     * Constructor for an implementation of a persistent interface.
     * @param interfaceMetaData MetaData for the persistent interface
     * @param implClassName Name of the implementation class to generate (omitting packages)
     * @param mmgr MetaData manager
     */
    public ASMImplementationGenerator(InterfaceMetaData interfaceMetaData, String implClassName, MetaDataManager mmgr)
    {
        super(interfaceMetaData, implClassName, mmgr);

        asmClassName = fullClassName.replace('.', '/');
        asmTypeDescriptor = "L" + asmClassName + ";";

        List<String> interfaces = new ArrayList<String>();
        InterfaceMetaData imd = interfaceMetaData;
        do
        {
            String intfTypeName = imd.getFullClassName().replace('.', '/');
            interfaces.add(intfTypeName);
            imd = (InterfaceMetaData) imd.getSuperAbstractClassMetaData();
        }
        while (imd != null);

        // Start the class
        writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        writer.visit(Opcodes.V1_3, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, fullClassName.replace('.', '/'),
            null, fullSuperclassName.replace('.', '/'),
            (String[])interfaces.toArray(new String[interfaces.size()]));

        // Create fields, default ctor, and methods
        createPropertyFields();
        createDefaultConstructor();
        createPropertyMethods();

        // End the class
        writer.visitEnd();
        bytes = writer.toByteArray();
    }

    /**
     * Constructor for an implementation of an abstract class.
     * @param cmd MetaData for the abstract class
     * @param implClassName Name of the implementation class to generate (omitting packages)
     * @param mmgr MetaData manager
     */
    public ASMImplementationGenerator(ClassMetaData cmd, String implClassName, MetaDataManager mmgr)
    {
        super(cmd, implClassName, mmgr);

        asmClassName = fullClassName.replace('.', '/');
        asmTypeDescriptor = "L" + asmClassName + ";";
        fullSuperclassName = cmd.getFullClassName();

        // Start the class
        writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        writer.visit(Opcodes.V1_3, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, fullClassName.replace('.', '/'),
            null, fullSuperclassName.replace('.', '/'), null);

        // Create fields, default ctor, and methods
        createPropertyFields();
        createDefaultConstructor();
        createPropertyMethods();

        // End the class
        writer.visitEnd();
        bytes = writer.toByteArray();
    }

    /**
     * Enhance the implementation of the class/interface.
     * @param clr ClassLoader resolver
     */
    @SuppressWarnings("unchecked")
    public void enhance(final ClassLoaderResolver clr)
    {
        // define the generated class in the classloader so we populate the metadata
        final EnhancerClassLoader loader = new EnhancerClassLoader();
        loader.defineClass(fullClassName, getBytes(), clr);

        // Create MetaData for implementation of interface
        final ClassLoaderResolver genclr = new JDOClassLoaderResolver(loader);
        final ClassMetaData implementationCmd;
        if (inputCmd instanceof InterfaceMetaData)
        {
            implementationCmd = new ClassMetaData((InterfaceMetaData)inputCmd, className, true);
        }
        else
        {
            implementationCmd = new ClassMetaData((ClassMetaData)inputCmd, className);
        }

        // Do as PrivilegedAction since populate()/initialise() use reflection to get additional fields
        AccessController.doPrivileged(new PrivilegedAction()
        {
            public Object run()
            {
                implementationCmd.populate(genclr, null, metaDataMgr);
                implementationCmd.initialise(genclr, metaDataMgr);
                return null;
            }
        });

        // enhance the class and update the byte definition
        ClassEnhancer gen = new ASMClassEnhancer(implementationCmd, genclr, metaDataMgr, getBytes());
        gen.enhance();
        bytes = gen.getClassBytes();
    }

    /**
     * Create the fields for the implementation.
     * @param acmd MetaData for the class/interface
     */
    protected void createPropertyFields(AbstractClassMetaData acmd)
    {
        if (acmd == null)
        {
            return;
        }

        AbstractMemberMetaData[] propertyMetaData = acmd.getManagedMembers();
        for (int i=0; i<propertyMetaData.length; i++)
        {
            writer.visitField(Opcodes.ACC_PRIVATE, propertyMetaData[i].getName(),
                Type.getDescriptor(propertyMetaData[i].getType()), null, null).visitEnd();
        }
    }

    /**
     * Create a default constructor, assuming that there is no persistent superclass.
     */
    protected void createDefaultConstructor()
    {
        MethodVisitor visitor = writer.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        visitor.visitCode();
        Label l0 = new Label();
        visitor.visitLabel(l0);
        visitor.visitVarInsn(Opcodes.ALOAD, 0);
        visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, fullSuperclassName.replace('.', '/'), "<init>", "()V");
        visitor.visitInsn(Opcodes.RETURN);
        Label l1 = new Label();
        visitor.visitLabel(l1);
        visitor.visitLocalVariable("this", asmTypeDescriptor, null, l0, l1, 0);
        visitor.visitMaxs(1, 1);
        visitor.visitEnd();
    }

    /**
     * Create a getter method for a /property.
     * @param mmd MetaData for the property
     */
    protected void createGetter(AbstractMemberMetaData mmd)
    {
        boolean isBoolean = mmd.getTypeName().equals("boolean");
        String getterName = ClassUtils.getJavaBeanGetterName(mmd.getName(), isBoolean);
        String jdoGetterName = ASMClassEnhancer.MN_GetterPrefix + mmd.getName();
        if (inputCmd instanceof InterfaceMetaData)
        {
            // Interface so generate getXXX
            String fieldDesc = Type.getDescriptor(mmd.getType());
            MethodVisitor visitor = writer.visitMethod(Opcodes.ACC_PUBLIC, getterName,
                "()" + fieldDesc, null, null);
            visitor.visitCode();
            Label l0 = new Label();
            visitor.visitLabel(l0);
            visitor.visitVarInsn(Opcodes.ALOAD, 0);
            visitor.visitFieldInsn(Opcodes.GETFIELD, asmClassName, mmd.getName(), fieldDesc);
            ASMUtils.addReturnForType(visitor, mmd.getType());
            Label l1 = new Label();
            visitor.visitLabel(l1);
            visitor.visitLocalVariable("this", asmTypeDescriptor, null, l0, l1, 0);
            visitor.visitMaxs(1, 1);
            visitor.visitEnd();
        }
        else
        {
            // Abstract class so generate getXXX
            String fieldDesc = Type.getDescriptor(mmd.getType());
            int getAccess = (mmd.isPublic() ? Opcodes.ACC_PUBLIC : 0) |
                (mmd.isProtected() ? Opcodes.ACC_PROTECTED : 0) |
                (mmd.isPrivate() ? Opcodes.ACC_PRIVATE : 0);
            MethodVisitor getVisitor = writer.visitMethod(getAccess, getterName, "()" + fieldDesc, null, null);
            JdoPropertyGetterAdapter.generateGetXXXMethod(getVisitor, mmd, asmClassName, asmTypeDescriptor, false);

            // Abstract class so generate jdoGetXXX
            int access = (mmd.isPublic() ? Opcodes.ACC_PUBLIC : 0) |
                (mmd.isProtected() ? Opcodes.ACC_PROTECTED : 0) |
                (mmd.isPrivate() ? Opcodes.ACC_PRIVATE : 0);
            MethodVisitor visitor = writer.visitMethod(access, jdoGetterName, "()" + fieldDesc, null, null);
            visitor.visitCode();
            Label l0 = new Label();
            visitor.visitLabel(l0);
            visitor.visitVarInsn(Opcodes.ALOAD, 0);
            visitor.visitFieldInsn(Opcodes.GETFIELD, asmClassName, mmd.getName(), fieldDesc);
            ASMUtils.addReturnForType(visitor, mmd.getType());
            Label l1 = new Label();
            visitor.visitLabel(l1);
            visitor.visitLocalVariable("this", asmTypeDescriptor, null, l0, l1, 0);
            visitor.visitMaxs(1, 1);
            visitor.visitEnd();
        }
    }

    /**
     * Create a setter method for a property.
     * @param mmd MetaData for the property
     */
    protected void createSetter(AbstractMemberMetaData mmd)
    {
        String setterName = ClassUtils.getJavaBeanSetterName(mmd.getName());
        String jdoSetterName = ASMClassEnhancer.MN_SetterPrefix + mmd.getName();
        if (inputCmd instanceof InterfaceMetaData)
        {
            // Interface so generate setXXX
            String fieldDesc = Type.getDescriptor(mmd.getType());
            MethodVisitor visitor = writer.visitMethod(Opcodes.ACC_PUBLIC, setterName,
                "(" + fieldDesc + ")V", null, null);
            visitor.visitCode();
            Label l0 = new Label();
            visitor.visitLabel(l0);
            visitor.visitVarInsn(Opcodes.ALOAD, 0);
            ASMUtils.addLoadForType(visitor, mmd.getType(), 1);
            visitor.visitFieldInsn(Opcodes.PUTFIELD, asmClassName, mmd.getName(), fieldDesc);
            visitor.visitInsn(Opcodes.RETURN);
            Label l2 = new Label();
            visitor.visitLabel(l2);
            visitor.visitLocalVariable("this", asmTypeDescriptor, null, l0, l2, 0);
            visitor.visitLocalVariable("val", fieldDesc, null, l0, l2, 1);
            visitor.visitMaxs(2, 2);
            visitor.visitEnd();
        }
        else
        {
            // Abstract class so generate setXXX
            String fieldDesc = Type.getDescriptor(mmd.getType());
            int setAccess = (mmd.isPublic() ? Opcodes.ACC_PUBLIC : 0) |
                (mmd.isProtected() ? Opcodes.ACC_PROTECTED : 0) |
                (mmd.isPrivate() ? Opcodes.ACC_PRIVATE : 0);
            MethodVisitor setVisitor = writer.visitMethod(setAccess, setterName, "(" + fieldDesc + ")V", null, null);
            JdoPropertySetterAdapter.generateSetXXXMethod(setVisitor, mmd, asmClassName, asmTypeDescriptor);

            // Abstract class so generate jdoSetXXX
            int access = (mmd.isPublic() ? Opcodes.ACC_PUBLIC : 0) |
                (mmd.isProtected() ? Opcodes.ACC_PROTECTED : 0) |
                (mmd.isPrivate() ? Opcodes.ACC_PRIVATE : 0);
            MethodVisitor visitor = writer.visitMethod(access, jdoSetterName, "(" + fieldDesc + ")V", null, null);
            visitor.visitCode();
            Label l0 = new Label();
            visitor.visitLabel(l0);
            visitor.visitVarInsn(Opcodes.ALOAD, 0);
            ASMUtils.addLoadForType(visitor, mmd.getType(), 1);
            visitor.visitFieldInsn(Opcodes.PUTFIELD, asmClassName, mmd.getName(), fieldDesc);
            visitor.visitInsn(Opcodes.RETURN);
            Label l2 = new Label();
            visitor.visitLabel(l2);
            visitor.visitLocalVariable("this", asmTypeDescriptor, null, l0, l2, 0);
            visitor.visitLocalVariable("val", fieldDesc, null, l0, l2, 1);
            visitor.visitMaxs(2, 2);
            visitor.visitEnd();
        }
    }
}
TOP

Related Classes of org.datanucleus.enhancer.asm.ASMImplementationGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.