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