KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > object > bytecode > InstrumentationSpec


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tc.object.bytecode;
5
6 import com.tc.asm.Type;
7 import com.tc.aspectwerkz.reflect.ClassInfo;
8 import com.tc.exception.TCLogicalSubclassNotPortableException;
9 import com.tc.object.LiteralValues;
10 import com.tc.object.Portability;
11 import com.tc.object.config.TransparencyClassSpec;
12 import com.tc.object.config.TransparencyClassSpecUtil;
13 import com.tc.util.Assert;
14
15 import java.lang.reflect.Field JavaDoc;
16 import java.lang.reflect.Method JavaDoc;
17 import java.lang.reflect.Modifier JavaDoc;
18 import java.util.Collection JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.HashSet JavaDoc;
21 import java.util.Map JavaDoc;
22 import java.util.Set JavaDoc;
23
24 class InstrumentationSpec {
25   public static final byte IS_NOT_NEEDED = 0x04;
26   public static final byte IS_NEEDED = 0x05;
27   public static final byte IS_PRESENT = 0x06;
28
29   private static final LiteralValues literalValues = new LiteralValues();
30
31   private byte instrumentationAction = TransparencyClassSpec.NOT_ADAPTABLE;
32
33   private byte managedMethods = IS_NOT_NEEDED;
34   private byte valuesGetterMethod = IS_NOT_NEEDED;
35   private byte valuesSetterMethod = IS_NOT_NEEDED;
36   private byte managedValuesGetterMethod = IS_NOT_NEEDED;
37   private byte managedValuesSetterMethod = IS_NOT_NEEDED;
38   private byte managedField = IS_NOT_NEEDED;
39   private byte delegateLogicalField = IS_NOT_NEEDED;
40   private byte writeObjectSerializedMethod = IS_NOT_NEEDED;
41   private byte readObjectSerializedMethod = IS_NOT_NEEDED;
42
43   private String JavaDoc classNameSlashes;
44   private String JavaDoc superNameSlashes;
45   private String JavaDoc classNameDots;
46   private String JavaDoc superNameDots;
47   private boolean isInterface;
48   private ParentClassInfo parentClassInfo;
49   private boolean classHierarchyInitialized = false;
50   private int classAccess;
51   private String JavaDoc classSignature;
52   private String JavaDoc[] classInterfaces;
53   private int classVersion;
54   private boolean hasVisitedField;
55   private boolean isSubclassOfLogicalClass;
56   private TransparencyClassSpec superClassSpec;
57
58   private final ClassInfo classInfo;
59   private final TransparencyClassSpec spec;
60   private final ManagerHelper mgrHelper;
61   private final ClassLoader JavaDoc caller;
62
63   private final Set JavaDoc classHierarchy;
64   private final Map JavaDoc shouldOverrideMethods;
65   private final Set JavaDoc logicalExtendingMethodSpec;
66   private final Set JavaDoc logicalExtendingFieldSpec;
67
68   InstrumentationSpec(ClassInfo classInfo, TransparencyClassSpec spec, ManagerHelper mgrHelper, ClassLoader JavaDoc caller) {
69     this.classInfo = classInfo;
70     this.spec = spec;
71     this.mgrHelper = mgrHelper;
72     this.caller = caller;
73     this.classHierarchy = new HashSet JavaDoc();
74     this.shouldOverrideMethods = new HashMap JavaDoc();
75     this.logicalExtendingMethodSpec = new HashSet JavaDoc();
76     this.logicalExtendingFieldSpec = new HashSet JavaDoc();
77   }
78
79   public ClassInfo getClassInfo() {
80     return classInfo;
81   }
82
83
84   // XXX no need to initialize because these values are in the classInfo
85
void initialize(int version, int access, String JavaDoc name, String JavaDoc signature, String JavaDoc superName, String JavaDoc[] interfaces,
86                   Portability portability) {
87     this.classNameSlashes = name;
88     this.superNameSlashes = superName;
89     this.classNameDots = name.replace('/', '.');
90     this.superNameDots = superName.replace('/', '.');
91     this.classHierarchy.add(this.classNameSlashes);
92     this.classHierarchy.add(this.superNameSlashes);
93     this.classAccess = access;
94     this.classSignature = signature;
95     this.classInterfaces = interfaces;
96     this.classVersion = version;
97     decideOnInstrumentationAction(portability);
98     handleSubclassOfLogicalClass(access, classNameDots, superNameDots);
99   }
100
101   private boolean isArray(String JavaDoc className) {
102     return literalValues.valueForClassName(className) == LiteralValues.ARRAY;
103   }
104
105   private void handleSubclassOfLogicalClass(int access, String JavaDoc className, String JavaDoc superName) {
106     if (isLogical()) { return; }
107     if (isArray(className)) { return; }
108     if (TransparencyClassSpecUtil.ignoreChecks(className)) { return; }
109     if (TransparencyClassSpecUtil.ignoreChecks(superName)) { return; }
110
111     superClassSpec = spec.getClassSpec(superName);
112     if (superClassSpec != null && superClassSpec.isLogical()) {
113       isSubclassOfLogicalClass = true;
114     }
115   }
116
117   public int getClassVersion() {
118     return classVersion;
119   }
120
121   public int getClassAccess() {
122     return classAccess;
123   }
124
125   private void initClassHierarchy() {
126     String JavaDoc superClassName = superNameSlashes.replace('/', '.');
127     try {
128       // As long as we are instrumenting anything other than Object, we are fine.
129
// Class superClazz = Class.forName(superClassName, false, this.caller).getSuperclass();
130
Class JavaDoc superClazz = Class.forName(superClassName, false, this.classInfo.getClassLoader()).getSuperclass();
131       while (superClazz != null) {
132         String JavaDoc superName = superClazz.getName();
133         classHierarchy.add(superName.replace('.', '/'));
134         superClazz = superClazz.getSuperclass();
135       }
136     } catch (ClassNotFoundException JavaDoc e) {
137       e.printStackTrace();
138     } finally {
139       classHierarchyInitialized = true;
140     }
141   }
142
143   String JavaDoc getClassNameSlashes() {
144     return this.classNameSlashes;
145   }
146
147   String JavaDoc getClassNameDots() {
148     return this.classNameDots;
149   }
150
151   String JavaDoc getSuperClassNameSlashes() {
152     return this.superNameSlashes;
153   }
154
155   String JavaDoc getSuperClassNameDots() {
156     return this.superNameDots;
157   }
158
159   String JavaDoc[] getClassInterfaces() {
160     return classInterfaces;
161   }
162
163   String JavaDoc getClassSignature() {
164     return classSignature;
165   }
166
167   void decideOnInstrumentationAction(Portability portability) {
168     Assert.assertNotNull(classNameSlashes);
169     Assert.assertNotNull(superNameSlashes);
170
171     this.isInterface = Modifier.isInterface(classAccess);
172
173     if (isInterface) {
174       this.instrumentationAction = TransparencyClassSpec.NOT_ADAPTABLE;
175     } else if (spec.getInstrumentationAction() == TransparencyClassSpec.PORTABLE) {
176       this.instrumentationAction = TransparencyClassSpec.PORTABLE;
177     } else if (spec.getInstrumentationAction() == TransparencyClassSpec.ADAPTABLE) {
178       this.instrumentationAction = TransparencyClassSpec.ADAPTABLE;
179     } else if (spec.isLogical() || spec.ignoreChecks()) {
180       // Logically managed classes need not have all super classes instrumented.
181
// currently THashMap and THashSet are not in bootjar and are instrumented during runtime.
182
this.instrumentationAction = TransparencyClassSpec.PORTABLE;
183     } else if (superClassChecks(portability)) {
184       this.instrumentationAction = TransparencyClassSpec.ADAPTABLE;
185     } else {
186       this.instrumentationAction = TransparencyClassSpec.PORTABLE;
187     }
188     decideOnInstrumentationsToDo(portability);
189   }
190
191   private void decideOnInstrumentationsToDo(Portability portability) {
192     if (this.instrumentationAction == TransparencyClassSpec.PORTABLE) {
193       if (!isLogical()) {
194         valuesGetterMethod = IS_NEEDED;
195         valuesSetterMethod = IS_NEEDED;
196         managedValuesGetterMethod = IS_NEEDED;
197         managedValuesSetterMethod = IS_NEEDED;
198       }
199       managedField = isNeedManagedField(portability);
200       managedMethods = managedField;
201     }
202   }
203
204   /**
205    * If the superclass does not need to be instrumented, we need to generate the managed field and method for the class.
206    * If there does not exist a portable specs for the superclass, we need to generate the managed field and method for
207    * the class. If the class is logically instrumented, we need to generate the managed field and method. If it is a
208    * subclass of a logically instrumented class, we do not need to generate the field.
209    */

210   private byte isNeedManagedField(Portability portability) {
211     if (isSubclassOfLogicalClass) { return IS_NOT_NEEDED; }
212     ClassInfo superClassInfo = classInfo.getSuperclass();
213     if (portability.isInstrumentationNotNeeded(superClassInfo.getName())) { return IS_NEEDED; }
214     if (!spec.hasPhysicallyPortableSpecs(superClassInfo)) { return IS_NEEDED; }
215     if (spec.isLogical()) { return IS_NEEDED; }
216     return IS_NOT_NEEDED;
217   }
218
219   private boolean superClassChecks(Portability portability) {
220     String JavaDoc superClassName = superNameSlashes.replace('/', '.');
221     if (portability.isInstrumentationNotNeeded(superClassName)) { return false; }
222
223     final Class JavaDoc superClazz;
224     try {
225       superClazz = Class.forName(superClassName, false, this.classInfo.getClassLoader());
226     } catch (ClassNotFoundException JavaDoc e) {
227       throw new RuntimeException JavaDoc(e);
228     }
229
230     if (!portability.isPortableClass(superClazz)) { return true; }
231     return false;
232   }
233
234   boolean isInClassHierarchy(String JavaDoc classname) {
235     boolean inClassHierarchy = classHierarchy.contains(classname);
236     if (inClassHierarchy || classHierarchyInitialized) return inClassHierarchy;
237     initClassHierarchy();
238     return classHierarchy.contains(classname);
239   }
240
241   boolean isClassNotAdaptable() {
242     return this.instrumentationAction == TransparencyClassSpec.NOT_ADAPTABLE;
243   }
244
245   boolean isClassAdaptable() {
246     return this.instrumentationAction == TransparencyClassSpec.ADAPTABLE;
247   }
248
249   boolean isClassPortable() {
250     return this.instrumentationAction == TransparencyClassSpec.PORTABLE;
251   }
252
253   boolean isSubclassofLogicalClass() {
254     return this.isSubclassOfLogicalClass;
255   }
256
257   void moveToLogicalIfNecessary() {
258     if (isSubclassOfLogicalClass && !hasVisitedField) {
259       spec.moveToLogical(superClassSpec);
260     }
261   }
262
263   boolean hasDelegatedToLogicalClass() {
264     return needDelegateField();
265   }
266
267   boolean needDelegateField() {
268     return this.delegateLogicalField == IS_NEEDED;
269   }
270
271   boolean isWriteObjectMethodNeeded() {
272     return this.writeObjectSerializedMethod == IS_NEEDED;
273   }
274
275   boolean isReadObjectMethodNeeded() {
276     return this.readObjectSerializedMethod == IS_NEEDED;
277   }
278
279   void handleSubclassOfLogicalClassWithFieldsIfNecessary(int access) {
280     if (ByteCodeUtil.isSynthetic(access) || Modifier.isStatic(access)) {
281       return;
282     } else if (isSubclassOfLogicalClass && !hasVisitedField) {
283       hasVisitedField = true;
284       try {
285         Class JavaDoc superClazz = Class.forName(superNameDots, false, this.classInfo.getClassLoader());
286
287         Method JavaDoc[] methods = superClazz.getMethods();
288         for (int i = 0; i < methods.length; i++) {
289           Method JavaDoc m = methods[i];
290           String JavaDoc methodName = m.getName();
291           int modifier = m.getModifiers();
292           if (!shouldVisitMethod(modifier, methodName) || Modifier.isFinal(modifier)) {
293             continue;
294           }
295
296           String JavaDoc methodDesc = Type.getMethodDescriptor(m);
297           shouldOverrideMethods.put(methodName + methodDesc, m);
298         }
299
300         methods = superClazz.getDeclaredMethods();
301         for (int i = 0; i < methods.length; i++) {
302           Method JavaDoc m = methods[i];
303           String JavaDoc methodName = m.getName();
304           int modifier = m.getModifiers();
305           if (shouldVisitMethod(modifier, methodName) && !Modifier.isFinal(modifier) && Modifier.isProtected(modifier)) {
306             String JavaDoc methodDesc = Type.getMethodDescriptor(m);
307             logicalExtendingMethodSpec.add(methodName + methodDesc);
308           }
309         }
310
311         Field JavaDoc[] fields = superClazz.getDeclaredFields();
312         for (int i = 0; i < fields.length; i++) {
313           Field JavaDoc f = fields[i];
314           String JavaDoc fieldName = f.getName();
315           int modifier = f.getModifiers();
316           if (!shouldVisitField(fieldName) || Modifier.isFinal(modifier) || Modifier.isPrivate(modifier)) {
317             continue;
318           }
319           String JavaDoc fieldDesc = Type.getDescriptor(f.getType());
320           logicalExtendingFieldSpec.add(fieldName + fieldDesc);
321         }
322       } catch (ClassNotFoundException JavaDoc e) {
323         e.printStackTrace();
324       }
325       delegateLogicalField = IS_NEEDED;
326       writeObjectSerializedMethod = IS_NEEDED;
327       readObjectSerializedMethod = IS_NEEDED;
328     }
329   }
330
331   void recordExistingFields(String JavaDoc name, String JavaDoc desc, String JavaDoc signature) {
332     if (ClassAdapterBase.MANAGED_FIELD_NAME.equals(name)) {
333       managedField = IS_PRESENT;
334     } else if (ByteCodeUtil.isParent(name)) {
335       Assert.assertNull(parentClassInfo);
336       this.parentClassInfo = new ParentClassInfo(name, desc);
337     }
338
339   }
340
341   void recordExistingMethods(String JavaDoc name, String JavaDoc desc, String JavaDoc signature) {
342     if (ClassAdapterBase.MANAGED_METHOD.equals(name)) {
343       managedMethods = IS_PRESENT;
344     } else if (ClassAdapterBase.VALUES_GETTER.endsWith(name)) {
345       valuesGetterMethod = IS_PRESENT;
346     } else if (ClassAdapterBase.VALUES_SETTER.endsWith(name)) {
347       valuesSetterMethod = IS_PRESENT;
348     } else if (ClassAdapterBase.MANAGED_VALUES_GETTER.endsWith(name)) {
349       managedValuesGetterMethod = IS_PRESENT;
350     } else if (ClassAdapterBase.MANAGED_VALUES_SETTER.endsWith(name)) {
351       managedValuesSetterMethod = IS_PRESENT;
352     } else if (LogicalClassSerializationAdapter.READ_OBJECT_SIGNATURE.equals(name + desc)) {
353       readObjectSerializedMethod = IS_NOT_NEEDED;
354     } else if (LogicalClassSerializationAdapter.WRITE_OBJECT_SIGNATURE.equals(name + desc)) {
355       writeObjectSerializedMethod = IS_NOT_NEEDED;
356     }
357     shouldOverrideMethods.remove(name + desc);
358   }
359
360   boolean shouldVisitField(String JavaDoc name) {
361     return !(name.startsWith(ByteCodeUtil.TC_FIELD_PREFIX));
362   }
363
364   boolean shouldVisitMethod(int methodAccess, String JavaDoc name) {
365     if (name.startsWith(ByteCodeUtil.TC_METHOD_PREFIX)) { return false; }
366     if (Modifier.isAbstract(methodAccess) || Modifier.isNative(methodAccess)) { return false; }
367     return true;
368   }
369
370   boolean isManagedMethodsNeeded() {
371     return (managedMethods == IS_NEEDED);
372   }
373
374   boolean isValuesGetterMethodNeeded() {
375     return (valuesGetterMethod == IS_NEEDED);
376   }
377
378   boolean isValuesSetterMethodNeeded() {
379     return (valuesSetterMethod == IS_NEEDED);
380   }
381
382   boolean isManagedValuesGetterMethodNeeded() {
383     return (managedValuesGetterMethod == IS_NEEDED);
384   }
385
386   boolean isManagedValuesSetterMethodNeeded() {
387     return (managedValuesSetterMethod == IS_NEEDED);
388   }
389
390   boolean isManagedFieldNeeded() {
391     return (managedField == IS_NEEDED);
392   }
393
394   TransparencyClassSpec getTransparencyClassSpec() {
395     return spec;
396   }
397
398   TransparencyClassSpec getSuperclassTransparencyClassSpec() {
399     return spec.getClassSpec(superNameDots);
400   }
401
402   boolean isLogical() {
403     return spec.isLogical();
404   }
405   
406   boolean needInstrumentFieldInsn() {
407     return !spec.isLogical() && !(isSubclassOfLogicalClass && !hasVisitedField);
408   }
409
410   void shouldProceedInstrumentation(int access, String JavaDoc name, String JavaDoc desc) {
411     if (isSubclassOfLogicalClass && shouldVisitMethod(access, name)
412         && logicalExtendingMethodSpec.contains(name + desc)) {
413       // Subclass of Logical class cannot override protected method. So, ignore all instrumentation.
414
throw new TCLogicalSubclassNotPortableException(classNameDots, superNameDots);
415     }
416   }
417
418   void shouldProceedInstrumentation(String JavaDoc fieldName, String JavaDoc fieldDesc) {
419     if (isSubclassOfLogicalClass && shouldVisitField(fieldName)
420         && logicalExtendingFieldSpec.contains(fieldName + fieldDesc)) { throw new TCLogicalSubclassNotPortableException(
421                                                                                                                         classNameDots,
422                                                                                                                         superNameDots); }
423   }
424
425   boolean isPhysical() {
426     return spec.isPhysical();
427   }
428
429   boolean generateNonStaticTCFields() {
430     return spec.generateNonStaticTCFields();
431   }
432
433   Collection JavaDoc getShouldOverrideMethods() {
434     return shouldOverrideMethods.values();
435   }
436
437   ManagerHelper getManagerHelper() {
438     return mgrHelper;
439   }
440
441   boolean isInner() {
442     return this.parentClassInfo != null;
443   }
444
445   String JavaDoc getParentClassType() {
446     Assert.assertTrue(isInner());
447     return parentClassInfo.getType();
448   }
449
450   String JavaDoc getParentClassFieldName() {
451     Assert.assertTrue(isInner());
452     return parentClassInfo.getFieldName();
453   }
454
455   private static class ParentClassInfo {
456     private final String JavaDoc type;
457     private final String JavaDoc fieldName;
458
459     ParentClassInfo(String JavaDoc fieldName, String JavaDoc type) {
460       this.fieldName = fieldName;
461       this.type = type;
462     }
463
464     String JavaDoc getFieldName() {
465       return fieldName;
466     }
467
468     String JavaDoc getType() {
469       return type;
470     }
471
472   }
473 }
474
Popular Tags