blob: 41ca684a44c0eb87f071498fd82b2289a3ce3815 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
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 a POJO class.
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class PojoAdapter extends ClassAdapter implements Opcodes {
/**
* POJO interface.
*/
private final String m_pojo = "org/apache/felix/ipojo/Pojo";
/**
* The owner. m_owner : String
*/
private String m_owner;
/**
* Field list.
*/
private Set m_fields;
/**
* Method list.
*/
private List m_methods = new ArrayList();
/**
* Getter/Setter methods creator.
*/
private FieldAdapter m_getterSetterCreator = new FieldAdapter(cv);
/**
* Constructor.
* @param arg0 : class adapter on which delegate.
* @param fields : map of contained fields.
* @param m_fields2
*/
public PojoAdapter(ClassVisitor arg0, Map fields) {
super(arg0);
m_fields = fields.keySet();
}
/**
* Start visiting a class.
* Initialize the getter/setter generator, add the _cm field, add the pojo interface.
* @param version : class version
* @param access : class access
* @param name : class name
* @param signature : class signature
* @param superName : class super class
* @param interfaces : implemented interfaces
* @see org.objectweb.asm.ClassAdapter#visit(int, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
*/
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
m_owner = name;
m_getterSetterCreator.visit(version, access, name, signature, superName, interfaces);
addCMField();
addPOJOInterface(version, access, name, signature, superName, interfaces);
}
/**
* Visit an annotation.
* @param desc : annotation descriptor.
* @param visible : is the annotation visible at runtime.
* @return the annotation visitor.
* @see org.objectweb.asm.ClassAdapter#visitAnnotation(java.lang.String, boolean)
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return super.visitAnnotation(desc, visible);
}
/**
* Visit an attribute.
* @param attr : visited attribute
* @see org.objectweb.asm.ClassAdapter#visitAttribute(org.objectweb.asm.Attribute)
*/
public void visitAttribute(Attribute attr) {
super.visitAttribute(attr);
}
/**
* Visit end.
* Create helper methods.
* @see org.objectweb.asm.ClassAdapter#visitEnd()
*/
public void visitEnd() {
// Create the _cmSetter(ComponentManager cm) method
createComponentManagerSetter();
// Add the getComponentInstance
createGetComponentInstanceMethod();
m_methods.clear();
cv.visitEnd();
}
/**
* Visit a field.
* Call the adapter generating getter and setter methods.
* @param access : field access.
* @param name : field name
* @param desc : field descriptor
* @param signature : field signature
* @param value : field value (static field only)
* @return the field visitor.
* @see org.objectweb.asm.ClassAdapter#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
*/
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
// Add the field to the list.
if (((access & ACC_STATIC) == 0) && (!name.startsWith("class$"))) {
addFlagField(name);
m_getterSetterCreator.visitField(access, name, desc, signature, value);
}
return super.visitField(access, name, desc, signature, value);
}
/**
* Visit an inner class.
* @param name : class name
* @param outerName : outer name
* @param innerName : inner name
* @param access : class access
* @see org.objectweb.asm.ClassAdapter#visitInnerClass(java.lang.String, java.lang.String, java.lang.String, int)
*/
public void visitInnerClass(String name, String outerName, String innerName, int access) {
super.visitInnerClass(name, outerName, innerName, access);
}
/**
* Visit a method.
* Manipulate constructor and methods. Does nothing with clinit and class$
* @param access : method access
* @param name : method name
* @param desc : method descriptor
* @param signature : method signature
* @param exceptions : method exceptions
* @return the Method Visitor.
* @see org.objectweb.asm.ClassAdapter#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
*/
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// Avoid manipulating special method
if (name.equals("<clinit>") || name.equals("class$")) { return super.visitMethod(access, name, desc, signature, exceptions); }
// The constructor is manipulated separately
if (name.equals("<init>")) {
// 1) change the constructor descriptor (add a component manager arg as first argument)
String newDesc = desc.substring(1);
newDesc = "(Lorg/apache/felix/ipojo/InstanceManager;" + newDesc;
// Insert the new constructor
MethodVisitor mv = super.visitMethod(ACC_PUBLIC, "<init>", newDesc, null, null);
Type[] args = Type.getArgumentTypes(newDesc);
String id = "$init";
for (int i = 0; i < args.length; i++) {
String cn = args[i].getClassName();
if (cn.endsWith("[]")) {
cn = cn.replace('[', '$');
cn = cn.substring(0, cn.length() - 1);
}
cn = cn.replace('.', '_');
id += cn;
}
String flag = "_M" + id;
m_methods.add(id);
FieldVisitor flagField = cv.visitField(Opcodes.ACC_PRIVATE, flag, "Z", null, null);
if (flagField != null) {
flagField.visitEnd();
}
if (mv == null) {
return null;
} else {
return new ConstructorCodeAdapter(mv, m_owner, m_fields, access, name, newDesc);
}
} else { // "Normal methods"
// avoid manipulating static methods.
if ((access & ACC_STATIC) == ACC_STATIC) { return super.visitMethod(access, name, desc, signature, exceptions); }
Type[] args = Type.getArgumentTypes(desc);
String id = name;
for (int i = 0; i < args.length; i++) {
String cn = args[i].getClassName();
if (cn.endsWith("[]")) {
cn = cn.replace('[', '$');
cn = cn.substring(0, cn.length() - 1);
}
cn = cn.replace('.', '_');
id += cn;
}
String flag = "_M" + id;
m_methods.add(id);
FieldVisitor flagField = cv.visitField(Opcodes.ACC_PRIVATE, flag, "Z", null, null);
if (flagField != null) {
flagField.visitEnd();
}
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
return new MethodCodeAdapter(mv, m_owner, access, name, desc, m_fields);
}
}
/**
* Visit an outer class.
* @param owner : class owner
* @param name : class name
* @param desc : class descriptor.
* @see org.objectweb.asm.ClassAdapter#visitOuterClass(java.lang.String, java.lang.String, java.lang.String)
*/
public void visitOuterClass(String owner, String name, String desc) {
super.visitOuterClass(owner, name, desc);
}
/**
* Visit source.
* @param source : source
* @param debug : debug
* @see org.objectweb.asm.ClassAdapter#visitSource(java.lang.String, java.lang.String)
*/
public void visitSource(String source, String debug) {
super.visitSource(source, debug);
}
/**
* Add the instance manager field (_cm).
*/
private void addCMField() {
// Insert _cm field
FieldVisitor fv = super.visitField(ACC_PRIVATE, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;", null, null);
fv.visitEnd();
}
/**
* Add the POJO interface to the visited class.
* @param version : class version
* @param access : class access
* @param name : class name
* @param signature : class signature
* @param superName : super class
* @param interfaces : implemented interfaces.
*/
private void addPOJOInterface(int version, int access, String name, String signature, String superName, String[] interfaces) {
// Add the POJO interface to the interface list
// Check that the POJO interface is not already in the list
boolean found = false;
for (int i = 0; i < interfaces.length; i++) {
if (interfaces[i].equals(m_pojo)) {
found = true;
}
}
String[] itfs;
if (!found) {
itfs = new String[interfaces.length + 1];
for (int i = 0; i < interfaces.length; i++) {
itfs[i] = interfaces[i];
}
itfs[interfaces.length] = m_pojo;
} else {
itfs = interfaces;
}
String str = "";
for (int i = 0; i < itfs.length; i++) {
str += itfs[i] + " ";
}
cv.visit(version, access, name, signature, superName, itfs);
}
/**
* Create the setter method for the _cm field.
*/
private void createComponentManagerSetter() {
MethodVisitor mv = cv.visitMethod(ACC_PRIVATE, "_setComponentManager", "(Lorg/apache/felix/ipojo/InstanceManager;)V", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "getRegistredFields", "()Ljava/util/Set;");
mv.visitVarInsn(ASTORE, 2);
mv.visitVarInsn(ALOAD, 2);
Label endif = new Label();
mv.visitJumpInsn(IFNULL, endif);
Iterator it = m_fields.iterator();
while (it.hasNext()) {
String field = (String) it.next();
mv.visitVarInsn(ALOAD, 2);
mv.visitLdcInsn(field);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "contains", "(Ljava/lang/Object;)Z");
Label l3 = new Label();
mv.visitJumpInsn(IFEQ, l3);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_1);
mv.visitFieldInsn(PUTFIELD, m_owner, "_F" + field, "Z");
mv.visitLabel(l3);
}
mv.visitLabel(endif);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/felix/ipojo/InstanceManager", "getRegistredMethods", "()Ljava/util/Set;");
mv.visitVarInsn(ASTORE, 2);
mv.visitVarInsn(ALOAD, 2);
Label endif2 = new Label();
mv.visitJumpInsn(IFNULL, endif2);
for (int i = 0; i < m_methods.size(); i++) {
String methodId = (String) m_methods.get(i);
mv.visitVarInsn(ALOAD, 2);
mv.visitLdcInsn(methodId);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "contains", "(Ljava/lang/Object;)Z");
Label l3 = new Label();
mv.visitJumpInsn(IFEQ, l3);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_1);
mv.visitFieldInsn(PUTFIELD, m_owner, "_M" + methodId, "Z");
mv.visitLabel(l3);
}
mv.visitLabel(endif2);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
/**
* Create the getComponentInstance method.
*/
private void createGetComponentInstanceMethod() {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "getComponentInstance", "()Lorg/apache/felix/ipojo/ComponentInstance;", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, m_owner, "_cm", "Lorg/apache/felix/ipojo/InstanceManager;");
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
/**
* Add the flag field.
* @param name : name of the field
*/
private void addFlagField(String name) {
// Add the flag field
FieldVisitor flag = cv.visitField(Opcodes.ACC_PRIVATE, "_F" + name, "Z", null, null);
if (flag != null) {
flag.visitEnd();
}
}
}