001: /*
002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: MetaDataClassAdapter.java 3703 2007-03-18 23:40:44Z gbevin $
007: */
008: package com.uwyn.rife.site.instrument;
009:
010: import java.io.Serializable;
011: import java.lang.reflect.Method;
012: import java.util.*;
013:
014: import com.uwyn.rife.asm.*;
015: import com.uwyn.rife.site.MetaDataBeanAware;
016: import com.uwyn.rife.site.MetaDataMerged;
017:
018: class MetaDataClassAdapter extends ClassAdapter implements Opcodes {
019: final static String DELEGATE_VAR_NAME = "$Rife$Meta$Data$Delegate$";
020:
021: private Map<String, List<String>> mExistingMethods = null;
022:
023: private Class mMetaData = null;
024: private String mMetaDataInternalName = null;
025: private String mBaseInternalName = null;
026:
027: MetaDataClassAdapter(Map<String, List<String>> existingMethods,
028: Class metaData, ClassVisitor writer) {
029: super (writer);
030:
031: mExistingMethods = existingMethods;
032: mMetaData = metaData;
033: mMetaDataInternalName = Type.getInternalName(mMetaData);
034: }
035:
036: public MethodVisitor visitMethod(int access, String name,
037: String desc, String signature, String[] exceptions) {
038: if (mBaseInternalName != null) {
039: if (name.equals("<init>")) {
040: return new MetaDataDefaultConstructorAdapter(
041: mBaseInternalName, mMetaDataInternalName, super
042: .visitMethod(access, name, desc,
043: signature, exceptions));
044: } else if (name.equals("clone")) {
045: return new MetaDataCloneableAdapter(mBaseInternalName,
046: mMetaDataInternalName, super .visitMethod(
047: access, name, desc, signature,
048: exceptions));
049: }
050: }
051:
052: return super .visitMethod(access, name, desc, signature,
053: exceptions);
054: }
055:
056: public void visit(int version, int access, String name,
057: String signature, String super Name, String[] interfaces) {
058: // go over all the interfaces of the meta data class and its parents
059: List<Class> meta_interfaces = new ArrayList<Class>();
060: Stack<Class> meta_classes = new Stack<Class>();
061: meta_classes.push(mMetaData);
062: while (meta_classes.size() > 0) {
063: Class current = meta_classes.pop();
064: if (current == Object.class) {
065: continue;
066: }
067:
068: Class super _class = current.getSuperclass();
069: if (super _class != null) {
070: meta_classes.push(super _class);
071: }
072:
073: Class[] current_interfaces = current.getInterfaces();
074: for (Class i : current_interfaces) {
075: if (i == Cloneable.class || i == Serializable.class
076: || i == MetaDataMerged.class
077: || i == MetaDataBeanAware.class
078: || i.getName().startsWith("com.tc.")) {
079: continue;
080: }
081:
082: if (!meta_interfaces.contains(i)) {
083: meta_interfaces.add(i);
084: }
085:
086: meta_classes.push(i);
087: }
088: }
089:
090: // only instrument this class when the meta data class actually implements interfaces
091: if (meta_interfaces.size() > 0) {
092: mBaseInternalName = name;
093:
094: // add a member variable that will be used to delegate the interface method calls to
095: cv.visitField(ACC_PRIVATE | ACC_SYNTHETIC,
096: DELEGATE_VAR_NAME, Type.getDescriptor(mMetaData),
097: null, null);
098:
099: // obtain the already existing interfaces
100: List<String> interfaces_merged = new ArrayList<String>();
101: interfaces_merged.addAll(Arrays.asList(interfaces));
102:
103: // process all interfaces and add those that are not yet implemented
104: // to the base class all the interface methods will be delegated to the
105: // member variable delegate
106: String internal_name;
107: for (Class interface_class : meta_interfaces) {
108: internal_name = Type.getInternalName(interface_class);
109: if (!interfaces_merged.contains(internal_name)) {
110: // implement and delegate all the methods of the interface
111: List<String> descriptors;
112: for (Method method : interface_class
113: .getDeclaredMethods()) {
114: // check if the class already has an implementation for this method
115: // and if it's the case, don't implement it automatically
116: descriptors = mExistingMethods.get(method
117: .getName());
118: if (descriptors != null
119: && descriptors.contains(Type
120: .getMethodDescriptor(method))) {
121: continue;
122: }
123:
124: // convert the exceptions into internal types
125: String[] exceptions_types = null;
126: if (method.getExceptionTypes().length > 0) {
127: List<String> exceptions_lists = new ArrayList<String>(
128: method.getExceptionTypes().length);
129: for (Class exception : method
130: .getExceptionTypes()) {
131: exceptions_lists.add(Type
132: .getInternalName(exception));
133: }
134:
135: exceptions_types = new String[exceptions_lists
136: .size()];
137: exceptions_lists.toArray(exceptions_types);
138: }
139:
140: // implement the interface method to delegate the call to the synthetic member variable
141: String method_descriptor = Type
142: .getMethodDescriptor(method);
143: int method_param_count = method
144: .getParameterTypes().length;
145:
146: MethodVisitor mv = cv.visitMethod(ACC_PUBLIC
147: | ACC_SYNTHETIC, method.getName(),
148: method_descriptor, null,
149: exceptions_types);
150: mv.visitCode();
151: mv.visitVarInsn(ALOAD, 0);
152: mv.visitFieldInsn(GETFIELD, mBaseInternalName,
153: DELEGATE_VAR_NAME, "L"
154: + mMetaDataInternalName + ";");
155:
156: // handle the method parameters correctly
157: int param_count = 1;
158: for (Class param : method.getParameterTypes()) {
159: switch (Type.getType(param).getSort()) {
160: case Type.INT:
161: case Type.BOOLEAN:
162: case Type.CHAR:
163: case Type.BYTE:
164: case Type.SHORT:
165: mv.visitVarInsn(ILOAD, param_count);
166: break;
167: case Type.LONG:
168: mv.visitVarInsn(LLOAD, param_count);
169: break;
170: case Type.FLOAT:
171: mv.visitVarInsn(FLOAD, param_count);
172: break;
173: case Type.DOUBLE:
174: mv.visitVarInsn(DLOAD, param_count);
175: break;
176: default:
177: mv.visitVarInsn(ALOAD, param_count);
178: break;
179: }
180: param_count++;
181: }
182:
183: mv.visitMethodInsn(INVOKEVIRTUAL,
184: mMetaDataInternalName,
185: method.getName(), method_descriptor);
186: // handle the return type correctly
187: switch (Type.getReturnType(method).getSort()) {
188: case Type.VOID:
189: mv.visitInsn(RETURN);
190: break;
191: case Type.INT:
192: case Type.BOOLEAN:
193: case Type.CHAR:
194: case Type.BYTE:
195: case Type.SHORT:
196: mv.visitInsn(IRETURN);
197: break;
198: case Type.LONG:
199: mv.visitInsn(LRETURN);
200: break;
201: case Type.FLOAT:
202: mv.visitInsn(FRETURN);
203: break;
204: case Type.DOUBLE:
205: mv.visitInsn(DRETURN);
206: break;
207: default:
208: mv.visitInsn(ARETURN);
209: break;
210: }
211: mv.visitMaxs(method_param_count + 1,
212: method_param_count + 2);
213: mv.visitEnd();
214: }
215:
216: interfaces_merged.add(internal_name);
217: }
218: }
219:
220: // handle clonability correctly when the meta data class is tied to one
221: // particular instance of the base class
222: if (MetaDataBeanAware.class.isAssignableFrom(mMetaData)) {
223: // implement the Cloneable interface in case this hasn't been done yet
224: String cloneable_internal = Type
225: .getInternalName(Cloneable.class);
226: if (!interfaces_merged.contains(cloneable_internal)) {
227: interfaces_merged.add(cloneable_internal);
228: }
229:
230: // check if the clone method has to be added from scratch
231: List<String> clone_method_descriptors = mExistingMethods
232: .get("clone");
233: if (null == clone_method_descriptors
234: || (!clone_method_descriptors
235: .contains("()Ljava/lang/Object;") && !clone_method_descriptors
236: .contains("()L" + mBaseInternalName
237: + ";"))) {
238: MethodVisitor mv = cv
239: .visitMethod(
240: ACC_PUBLIC | ACC_SYNTHETIC,
241: "clone",
242: "()Ljava/lang/Object;",
243: null,
244: new String[] { "java/lang/CloneNotSupportedException" });
245: mv.visitCode();
246: mv.visitVarInsn(ALOAD, 0);
247: mv.visitMethodInsn(INVOKESPECIAL,
248: "java/lang/Object", "clone",
249: "()Ljava/lang/Object;");
250: mv.visitTypeInsn(CHECKCAST, mBaseInternalName);
251: mv.visitVarInsn(ASTORE, 1);
252:
253: mv.visitVarInsn(ALOAD, 1);
254: mv.visitVarInsn(ALOAD, 0);
255: mv.visitFieldInsn(GETFIELD, mBaseInternalName,
256: DELEGATE_VAR_NAME, "L"
257: + mMetaDataInternalName + ";");
258: mv.visitMethodInsn(INVOKEVIRTUAL,
259: mMetaDataInternalName, "clone",
260: "()Ljava/lang/Object;");
261: mv.visitTypeInsn(CHECKCAST, mMetaDataInternalName);
262: mv.visitFieldInsn(PUTFIELD, mBaseInternalName,
263: DELEGATE_VAR_NAME, "L"
264: + mMetaDataInternalName + ";");
265: mv.visitVarInsn(ALOAD, 1);
266: mv.visitFieldInsn(GETFIELD, mBaseInternalName,
267: DELEGATE_VAR_NAME, "L"
268: + mMetaDataInternalName + ";");
269: mv.visitVarInsn(ALOAD, 1);
270: mv.visitMethodInsn(INVOKEVIRTUAL,
271: mMetaDataInternalName, "setMetaDataBean",
272: "(Ljava/lang/Object;)V");
273: mv.visitVarInsn(ALOAD, 1);
274: mv.visitInsn(ARETURN);
275: Label l0 = new Label();
276: mv.visitLabel(l0);
277: mv.visitJumpInsn(GOTO, l0);
278: mv.visitMaxs(2, 3);
279: mv.visitEnd();
280: }
281: }
282:
283: // use the new collection of interfaces for the class
284: interfaces = new String[interfaces_merged.size()];
285: interfaces_merged.toArray(interfaces);
286: }
287:
288: super.visit(version, access, name, signature, superName,
289: interfaces);
290: }
291: }
|