All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datanucleus.enhancer.asm.ASMImplementationGenerator Maven / Gradle / Ivy

/**********************************************************************
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.datanucleus.util.NucleusLogger;
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 interfaces = new ArrayList();
        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();
        String clsBytes = new String(bytes);
        NucleusLogger.GENERAL.info(">> ASMImplGen.ctr bytes=" + clsBytes);
    }

    /**
     * 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> createDefaultCtr");
        MethodVisitor visitor = writer.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null);
        visitor.visitCode();
        Label l0 = new Label();
        visitor.visitLabel(l0);
        visitor.visitVarInsn(Opcodes.ALOAD, 0);
        visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, fullSuperclassName.replace('.', '/'), "", "()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();
        NucleusLogger.GENERAL.info(">> createGetter for " + mmd.getFullFieldName() + " methodName=" + jdoGetterName);
        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, 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, false);

            // 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();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy