blob: f985babb01e14673ad8158b64a04fb3ffaafb565 [file] [log] [blame]
/*
* Copyright 2006 The Apache Software Foundation
*
* 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.
*
*/
package org.apache.felix.ipojo.manipulation;
import java.util.HashMap;
import java.util.logging.Level;
import org.apache.felix.ipojo.plugin.IpojoPluginConfiguration;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* Manipulate the class.
* - Add a static component manager field (_cm) ok
* - Create getter and setter for each fields ok
* - Store information about field ok
* - Store information about implemented interfaces ok
* - Change GETFIELD and PUTFIELD to called the getter and setter method
* @author Clement Escoffier
*
*/
public class PreprocessClassAdapter extends ClassAdapter implements Opcodes {
/**
* The owner.
* m_owner : String
*/
private String m_owner;
/**
* Interfaces implemented by the component.
*/
private String[] m_itfs = new String[0];
/**
* Field hashmap [field name, type] discovered in the component class.
*/
private HashMap m_fields = new HashMap();
/**
* Constructor.
* @param cv : Class visitor
*/
public PreprocessClassAdapter(final ClassVisitor cv) {
super(cv);
}
/** The visit method.
* - Insert the _cm field
* - Create the _initialize method
* - Create the _cm setter method
* @see org.objectweb.asm.ClassVisitor#visit(int, int, String, String, String, String[])
* @param version : Version
* @param access : Access modifier
* @param name : name of the visited element
* @param signature : singature of the visited element
* @param superName : superclasses (extend clause)
* @param interfaces : implement clause
*/
public void visit(
final int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces) {
m_owner = name;
// Insert _cm field
super.visitField(ACC_PUBLIC + ACC_STATIC, "_cm", ManipulationProperty.IPOJO_INTERNAL_DESCRIPTOR + "ComponentManager;", null, null);
// Create the _cmSetter(ComponentManager cm) method
createComponentManagerSetter();
//Store the interfaces :
m_itfs = interfaces;
super.visit(version, access, name, signature, superName, interfaces);
}
/** visit method method :-).
* @see org.objectweb.asm.ClassVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
* @param access : access modifier
* @param name : name of the method
* @param desc : descriptor of the method
* @param signature : signature of the method
* @param exceptions : exception launched by the method
* @return MethodVisitor
*/
public MethodVisitor visitMethod(
final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access,
name,
desc,
signature,
exceptions);
if (mv == null) { return null; }
else { return new PreprocessCodeAdapter(mv, m_owner); }
}
/**
* Create the setter method for the _cm field.
* The generated method must be called only one time.
*/
private void createComponentManagerSetter() {
MethodVisitor mv = super.visitMethod(ACC_PUBLIC + ACC_STATIC, "setComponentManager", "(" + ManipulationProperty.IPOJO_INTERNAL_DESCRIPTOR + "ComponentManager;)V", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(PUTSTATIC, m_owner, "_cm", ManipulationProperty.IPOJO_INTERNAL_DESCRIPTOR + "ComponentManager;");
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
/** visit Field method.
* @see org.objectweb.asm.ClassVisitor#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
* @param access : acces modifier
* @param name : name of the field
* @param desc : description of the field
* @param signature : signature of the field
* @param value : value of the field
* @return FieldVisitor
*/
public FieldVisitor visitField(
final int access,
final String name,
final String desc,
final String signature,
final Object value) {
if ((access & ACC_STATIC) == 0) {
IpojoPluginConfiguration.getLogger().log(Level.INFO, "Manipulate the field declaration of " + name);
Type type = Type.getType(desc);
// Keep the field in the code
FieldVisitor fv = cv.visitField(access, name, desc, signature, value);
if (type.getSort() == Type.ARRAY) {
if (type.getInternalName().startsWith("L")) {
String internalType = type.getInternalName().substring(1);
String nameType = internalType.replace('/', '.');
m_fields.put(name, nameType + "[]");
}
else {
String nameType = type.getClassName().substring(0, type.getClassName().length() - 2);
m_fields.put(name, nameType);
}
// TODO : Getter & SETTER on array :
String gDesc = "()" + desc;
createArrayGetter(name, gDesc, type);
// Generates setter method
String sDesc = "(" + desc + ")V";
createArraySetter(name, sDesc, type);
}
else {
// Store information on the fields
m_fields.put(name, type.getClassName());
// Generate the getter method
String gDesc = "()" + desc;
createSimpleGetter(name, gDesc, type);
// Generates setter method
String sDesc = "(" + desc + ")V";
createSimpleSetter(name, sDesc, type);
}
return fv;
}
return super.visitField(access, name, desc, signature, value);
}
private void createArraySetter(String name, String desc, Type type) {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "_set" + name, desc, null, null);
String internalType = desc.substring(1);
internalType = internalType.substring(0, internalType.length() - 2);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, m_owner, name, internalType);
mv.visitFieldInsn(GETSTATIC, m_owner, "_cm", "Lorg/apache/felix/ipojo/ComponentManager;");
mv.visitLdcInsn(name);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/ComponentManager", "setterCallback", "(Ljava/lang/String;Ljava/lang/Object;)V");
mv.visitInsn(RETURN);
// End
mv.visitMaxs(0, 0);
mv.visitEnd();
}
private void createArrayGetter(String name, String desc, Type type) {
// try {
// System.out.println("Field Name : " + name);
// System.out.println("Desc : " + desc);
// System.out.println("ClassName : " + type.getClassName());
// System.out.println("Descriptor of the type : " + type.getDescriptor());
// System.out.println("Internal Name : " + type.getInternalName());
// } catch(Exception e) {}
//
String methodName = "_get" + name;
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, methodName, desc, null, null);
String internalType = desc.substring(2);
mv.visitVarInsn(ALOAD, 0);
//mv.visitFieldInsn(GETFIELD, m_owner, name, "["+type.getInternalName()+";");
mv.visitFieldInsn(GETFIELD, m_owner, name, internalType);
mv.visitVarInsn(ASTORE, 1);
mv.visitFieldInsn(GETSTATIC, m_owner, "_cm", "Lorg/apache/felix/ipojo/ComponentManager;");
mv.visitLdcInsn(name);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/ComponentManager", "getterCallback", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitVarInsn(ASTORE, 2);
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, internalType);
mv.visitVarInsn(ASTORE, 3);
Label l3a = new Label();
mv.visitLabel(l3a);
mv.visitVarInsn(ALOAD, 1);
Label l4a = new Label();
mv.visitJumpInsn(IFNULL, l4a);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 3);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z");
Label l5a = new Label();
mv.visitJumpInsn(IFNE, l5a);
mv.visitLabel(l4a);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 3);
mv.visitMethodInsn(INVOKEVIRTUAL, m_owner, "_set" + name, "(" + internalType + ")V");
mv.visitLabel(l5a);
mv.visitVarInsn(ALOAD, 3);
mv.visitInsn(ARETURN);
//End
mv.visitMaxs(0, 0);
mv.visitEnd();
}
/**
* Create the getter for service dependency.
* @param name : field of the dependency
* @param desc : description of the getter method
* @param type : type to return
*/
private void createSimpleGetter(String name, String desc, Type type) {
String methodName = "_get" + name;
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, methodName, desc, null, null);
switch (type.getSort()) {
case Type.BOOLEAN :
case Type.CHAR :
case Type.BYTE :
case Type.SHORT :
case Type.INT :
case Type.FLOAT :
case Type.LONG :
case Type.DOUBLE :
String internalName = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][0];
String boxingType = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][1];
String unboxingMethod = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][2];
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, m_owner, name, internalName);
mv.visitVarInsn(type.getOpcode(ISTORE), 1);
mv.visitTypeInsn(NEW, boxingType);
mv.visitInsn(DUP);
mv.visitVarInsn(type.getOpcode(ILOAD), 1);
mv.visitMethodInsn(INVOKESPECIAL, boxingType, "<init>", "(" + internalName + ")V");
mv.visitVarInsn(ASTORE, 2);
mv.visitFieldInsn(GETSTATIC, m_owner, "_cm", "Lorg/apache/felix/ipojo/ComponentManager;");
mv.visitLdcInsn(name);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/ComponentManager", "getterCallback", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitVarInsn(ASTORE, 3);
mv.visitVarInsn(ALOAD, 3);
mv.visitTypeInsn(CHECKCAST, boxingType);
mv.visitVarInsn(ASTORE, 4);
mv.visitVarInsn(ALOAD, 4);
mv.visitMethodInsn(INVOKEVIRTUAL, boxingType, unboxingMethod, "()" + internalName);
mv.visitVarInsn(type.getOpcode(ISTORE), 5);
Label l5 = new Label();
mv.visitLabel(l5);
mv.visitVarInsn(type.getOpcode(ILOAD), 1);
mv.visitVarInsn(type.getOpcode(ILOAD), 5);
Label l6 = new Label();
mv.visitJumpInsn(type.getOpcode(IF_ICMPEQ), l6);
Label l7 = new Label();
mv.visitLabel(l7);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(type.getOpcode(ILOAD), 5);
mv.visitMethodInsn(INVOKEVIRTUAL, m_owner, "_set" + name, "(" + internalName + ")V");
mv.visitLabel(l6);
mv.visitVarInsn(type.getOpcode(ILOAD), 5);
mv.visitInsn(type.getOpcode(IRETURN));
break;
case Type.OBJECT :
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, m_owner, name, "L" + type.getInternalName() + ";");
mv.visitVarInsn(ASTORE, 1);
mv.visitFieldInsn(GETSTATIC, m_owner, "_cm", "Lorg/apache/felix/ipojo/ComponentManager;");
mv.visitLdcInsn(name);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/ComponentManager", "getterCallback", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitVarInsn(ASTORE, 2);
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, type.getInternalName());
mv.visitVarInsn(ASTORE, 3);
Label l3b = new Label();
mv.visitLabel(l3b);
mv.visitVarInsn(ALOAD, 1);
Label l4b = new Label();
mv.visitJumpInsn(IFNULL, l4b);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 3);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z");
Label l5b = new Label();
mv.visitJumpInsn(IFNE, l5b);
mv.visitLabel(l4b);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 3);
mv.visitMethodInsn(INVOKEVIRTUAL, m_owner, "_set" + name, "(L" + type.getInternalName() + ";)V");
mv.visitLabel(l5b);
mv.visitVarInsn(ALOAD, 3);
mv.visitInsn(ARETURN);
break;
default :
IpojoPluginConfiguration.getLogger().log(Level.SEVERE, "Manipulation problem in " + m_owner + " : a type is not implemented : " + type);
break;
}
mv.visitMaxs(0, 0);
mv.visitEnd();
}
/**
* Create the setter method for one property.
* The name of the method is _set+name of the field
* @param name : name of the field representing a property
* @param desc : description of the setter method
* @param type : type of the property
*/
private void createSimpleSetter(String name, String desc, Type type) {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "_set" + name, desc, null, null);
switch(type.getSort()) {
case Type.BOOLEAN :
case Type.CHAR :
case Type.BYTE :
case Type.SHORT :
case Type.INT :
case Type.FLOAT :
case Type.LONG :
case Type.DOUBLE :
String internalName = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][0];
String boxingType = ManipulationProperty.PRIMITIVE_BOXING_INFORMATION[type.getSort()][1];
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(type.getOpcode(ILOAD), 1);
mv.visitFieldInsn(PUTFIELD, m_owner, name, internalName);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitTypeInsn(NEW, boxingType);
mv.visitInsn(DUP);
mv.visitVarInsn(type.getOpcode(ILOAD), 1);
mv.visitMethodInsn(INVOKESPECIAL, boxingType, "<init>", "(" + internalName + ")V");
mv.visitVarInsn(ASTORE, 2);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitFieldInsn(GETSTATIC, m_owner, "_cm", "Lorg/apache/felix/ipojo/ComponentManager;");
mv.visitLdcInsn(name);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/ComponentManager", "setterCallback", "(Ljava/lang/String;Ljava/lang/Object;)V");
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitInsn(RETURN);
break;
case (Type.OBJECT) :
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, m_owner, name, "L" + type.getInternalName() + ";");
mv.visitFieldInsn(GETSTATIC, m_owner, "_cm", "Lorg/apache/felix/ipojo/ComponentManager;");
mv.visitLdcInsn(name);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/ComponentManager", "setterCallback", "(Ljava/lang/String;Ljava/lang/Object;)V");
mv.visitInsn(RETURN);
break;
default :
IpojoPluginConfiguration.getLogger().log(Level.SEVERE, "Manipulation Error : Cannot create the setter method for the field : " + name + " (" + type + ")");
break;
}
mv.visitMaxs(0, 0);
mv.visitEnd();
}
/**
* @return the interfaces implemented by the component class.
*/
public String[] getInterfaces() {
return m_itfs;
}
/**
* @return the field hashmap [field_name, type]
*/
public HashMap getFields() {
return m_fields;
}
}