/* * Copyright 2001-2008 Geert Bevin <gbevin[remove] at uwyn dot com> * Licensed under the Apache License, Version 2.0 (the "License") * $Id: MetaDataClassAdapter.java 3965 2008-07-30 08:02:23Z gbevin $ */ package com.uwyn.rife.site.instrument; import java.io.Serializable; import java.lang.reflect.Method; import java.util.*; import com.uwyn.rife.asm.*; import com.uwyn.rife.site.MetaDataBeanAware; import com.uwyn.rife.site.MetaDataMerged; class MetaDataClassAdapter extends ClassAdapter implements Opcodes { final static String DELEGATE_VAR_NAME = "$Rife$Meta$Data$Delegate$"; private Map<String, List<String>> mExistingMethods = null; private Class mMetaData = null; private String mMetaDataInternalName = null; private String mBaseInternalName = null; MetaDataClassAdapter(Map<String, List<String>> existingMethods, Class metaData, ClassVisitor writer) { super(writer); mExistingMethods = existingMethods; mMetaData = metaData; mMetaDataInternalName = Type.getInternalName(mMetaData); } public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if (mBaseInternalName != null) { if (name.equals("<init>")) { return new MetaDataDefaultConstructorAdapter(mBaseInternalName, mMetaDataInternalName, super.visitMethod(access, name, desc, signature, exceptions)); } else if (name.equals("clone")) { return new MetaDataCloneableAdapter(mBaseInternalName, mMetaDataInternalName, super.visitMethod(access, name, desc, signature, exceptions)); } } return super.visitMethod(access, name, desc, signature, exceptions); } public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { // go over all the interfaces of the meta data class and its parents List<Class> meta_interfaces = new ArrayList<Class>(); Stack<Class> meta_classes = new Stack<Class>(); meta_classes.push(mMetaData); while (meta_classes.size() > 0) { Class current = meta_classes.pop(); if (current == Object.class) { continue; } Class super_class = current.getSuperclass(); if (super_class != null) { meta_classes.push(super_class); } Class[] current_interfaces = current.getInterfaces(); for (Class i : current_interfaces) { if (i == Cloneable.class || i == Serializable.class || i == MetaDataMerged.class || i == MetaDataBeanAware.class || i.getName().startsWith("com.tc.")) { continue; } if (!meta_interfaces.contains(i)) { meta_interfaces.add(i); } meta_classes.push(i); } } // only instrument this class when the meta data class actually implements interfaces if (meta_interfaces.size() > 0) { mBaseInternalName = name; // add a member variable that will be used to delegate the interface method calls to cv.visitField(ACC_PRIVATE|ACC_SYNTHETIC|ACC_TRANSIENT, DELEGATE_VAR_NAME, Type.getDescriptor(mMetaData), null, null); // obtain the already existing interfaces List<String> interfaces_merged = new ArrayList<String>(); interfaces_merged.addAll(Arrays.asList(interfaces)); // process all interfaces and add those that are not yet implemented // to the base class all the interface methods will be delegated to the // member variable delegate String internal_name; for (Class interface_class : meta_interfaces) { internal_name = Type.getInternalName(interface_class); if (!interfaces_merged.contains(internal_name)) { // implement and delegate all the methods of the interface List<String> descriptors; for (Method method : interface_class.getDeclaredMethods()) { // check if the class already has an implementation for this method // and if it's the case, don't implement it automatically descriptors = mExistingMethods.get(method.getName()); if (descriptors != null && descriptors.contains(Type.getMethodDescriptor(method))) { continue; } // convert the exceptions into internal types String[] exceptions_types = null; if (method.getExceptionTypes().length > 0) { List<String> exceptions_lists = new ArrayList<String>(method.getExceptionTypes().length); for (Class exception : method.getExceptionTypes()) { exceptions_lists.add(Type.getInternalName(exception)); } exceptions_types = new String[exceptions_lists.size()]; exceptions_lists.toArray(exceptions_types); } // implement the interface method to delegate the call to the synthetic member variable String method_descriptor = Type.getMethodDescriptor(method); int method_param_count = method.getParameterTypes().length; MethodVisitor mv = cv.visitMethod(ACC_PUBLIC|ACC_SYNTHETIC, method.getName(), method_descriptor, null, exceptions_types); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, mBaseInternalName, DELEGATE_VAR_NAME, "L"+mMetaDataInternalName+";"); // handle the method parameters correctly int param_count = 1; for (Class param : method.getParameterTypes()) { switch (Type.getType(param).getSort()) { case Type.INT: case Type.BOOLEAN: case Type.CHAR: case Type.BYTE: case Type.SHORT: mv.visitVarInsn(ILOAD, param_count); break; case Type.LONG: mv.visitVarInsn(LLOAD, param_count); break; case Type.FLOAT: mv.visitVarInsn(FLOAD, param_count); break; case Type.DOUBLE: mv.visitVarInsn(DLOAD, param_count); break; default: mv.visitVarInsn(ALOAD, param_count); break; } param_count++; } mv.visitMethodInsn(INVOKEVIRTUAL, mMetaDataInternalName, method.getName(), method_descriptor); // handle the return type correctly switch (Type.getReturnType(method).getSort()) { case Type.VOID: mv.visitInsn(RETURN); break; case Type.INT: case Type.BOOLEAN: case Type.CHAR: case Type.BYTE: case Type.SHORT: mv.visitInsn(IRETURN); break; case Type.LONG: mv.visitInsn(LRETURN); break; case Type.FLOAT: mv.visitInsn(FRETURN); break; case Type.DOUBLE: mv.visitInsn(DRETURN); break; default: mv.visitInsn(ARETURN); break; } mv.visitMaxs(method_param_count+1, method_param_count+2); mv.visitEnd(); } interfaces_merged.add(internal_name); } } // handle clonability correctly when the meta data class is tied to one // particular instance of the base class if (MetaDataBeanAware.class.isAssignableFrom(mMetaData)) { // implement the Cloneable interface in case this hasn't been done yet String cloneable_internal = Type.getInternalName(Cloneable.class); if (!interfaces_merged.contains(cloneable_internal)) { interfaces_merged.add(cloneable_internal); } // check if the clone method has to be added from scratch List<String> clone_method_descriptors = mExistingMethods.get("clone"); if (null == clone_method_descriptors || (!clone_method_descriptors.contains("()Ljava/lang/Object;") && !clone_method_descriptors.contains("()L"+mBaseInternalName+";"))) { MethodVisitor mv = cv.visitMethod(ACC_PUBLIC|ACC_SYNTHETIC, "clone", "()Ljava/lang/Object;", null, new String[] { "java/lang/CloneNotSupportedException" }); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "clone", "()Ljava/lang/Object;"); mv.visitTypeInsn(CHECKCAST, mBaseInternalName); mv.visitVarInsn(ASTORE, 1); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, mBaseInternalName, DELEGATE_VAR_NAME, "L"+mMetaDataInternalName+";"); mv.visitMethodInsn(INVOKEVIRTUAL, mMetaDataInternalName, "clone", "()Ljava/lang/Object;"); mv.visitTypeInsn(CHECKCAST, mMetaDataInternalName); mv.visitFieldInsn(PUTFIELD, mBaseInternalName, DELEGATE_VAR_NAME, "L"+mMetaDataInternalName+";"); mv.visitVarInsn(ALOAD, 1); mv.visitFieldInsn(GETFIELD, mBaseInternalName, DELEGATE_VAR_NAME, "L"+mMetaDataInternalName+";"); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(INVOKEVIRTUAL, mMetaDataInternalName, "setMetaDataBean", "(Ljava/lang/Object;)V"); mv.visitVarInsn(ALOAD, 1); mv.visitInsn(ARETURN); Label l0 = new Label(); mv.visitLabel(l0); mv.visitJumpInsn(GOTO, l0); mv.visitMaxs(2, 3); mv.visitEnd(); } } // use the new collection of interfaces for the class interfaces = new String[interfaces_merged.size()]; interfaces_merged.toArray(interfaces); } super.visit(version, access, name, signature, superName, interfaces); } }