001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.object.bytecode;
006:
007: import com.tc.asm.Type;
008: import com.tc.aspectwerkz.reflect.ClassInfo;
009: import com.tc.aspectwerkz.reflect.ConstructorInfo;
010: import com.tc.aspectwerkz.reflect.FieldInfo;
011: import com.tc.aspectwerkz.reflect.MethodInfo;
012: import com.tc.aspectwerkz.reflect.impl.asm.AsmClassInfo;
013: import com.tc.aspectwerkz.reflect.impl.java.JavaClassInfo;
014: import com.tc.backport175.bytecode.AnnotationElement.Annotation;
015: import com.tc.exception.TCLogicalSubclassNotPortableException;
016: import com.tc.object.LiteralValues;
017: import com.tc.object.Portability;
018: import com.tc.object.config.TransparencyClassSpec;
019: import com.tc.object.config.TransparencyClassSpecUtil;
020: import com.tc.util.Assert;
021:
022: import java.lang.reflect.Field;
023: import java.lang.reflect.Method;
024: import java.lang.reflect.Modifier;
025: import java.util.Collection;
026: import java.util.HashMap;
027: import java.util.HashSet;
028: import java.util.Map;
029: import java.util.Set;
030:
031: class InstrumentationSpec {
032: public static final byte IS_NOT_NEEDED = 0x04;
033: public static final byte IS_NEEDED = 0x05;
034:
035: private static final LiteralValues literalValues = new LiteralValues();
036:
037: private byte instrumentationAction = TransparencyClassSpec.NOT_ADAPTABLE;
038:
039: private byte managedMethods = IS_NOT_NEEDED;
040: private byte valuesGetterMethod = IS_NOT_NEEDED;
041: private byte valuesSetterMethod = IS_NOT_NEEDED;
042: private byte managedValuesGetterMethod = IS_NOT_NEEDED;
043: private byte managedValuesSetterMethod = IS_NOT_NEEDED;
044: private byte managedField = IS_NOT_NEEDED;
045: private byte delegateLogicalField = IS_NOT_NEEDED;
046: private byte writeObjectSerializedMethod = IS_NOT_NEEDED;
047: private byte readObjectSerializedMethod = IS_NOT_NEEDED;
048:
049: private String classNameSlashes;
050: private String super NameSlashes;
051: private String classNameDots;
052: private String super NameDots;
053: private boolean isInterface;
054: private ParentClassInfo parentClassInfo;
055: private boolean classHierarchyInitialized = false;
056: private int classAccess;
057: private String classSignature;
058: private String[] classInterfaces;
059: private int classVersion;
060: private boolean hasVisitedField;
061: private boolean isSubclassOfLogicalClass;
062: private TransparencyClassSpec super ClassSpec;
063:
064: private final ClassInfo classInfo;
065: private final Map fieldInfoMap;
066: private final TransparencyClassSpec spec;
067: private final ManagerHelper mgrHelper;
068:
069: private final Set classHierarchy;
070: private final Map shouldOverrideMethods;
071: private final Set logicalExtendingMethodSpec;
072: private final Set logicalExtendingFieldSpec;
073: private final ClassLoader caller;
074:
075: InstrumentationSpec(ClassInfo classInfo,
076: TransparencyClassSpec spec, ManagerHelper mgrHelper,
077: ClassLoader caller) {
078: this .classInfo = classInfo;
079: this .spec = spec;
080: this .mgrHelper = mgrHelper;
081: this .caller = caller;
082: this .classHierarchy = new HashSet();
083: this .shouldOverrideMethods = new HashMap();
084: this .logicalExtendingMethodSpec = new HashSet();
085: this .logicalExtendingFieldSpec = new HashSet();
086: this .fieldInfoMap = buildFieldInfoMap();
087: }
088:
089: ClassLoader getCaller() {
090: return caller;
091: }
092:
093: private Map buildFieldInfoMap() {
094: Map rv = new HashMap();
095:
096: FieldInfo[] fields = this .getClassInfo().getFields();
097: for (int i = 0; i < fields.length; i++) {
098: FieldInfo fieldInfo = fields[i];
099: Object prev = rv.put(fieldInfo.getName(), fieldInfo);
100: if (prev != null) {
101: throw new AssertionError("replaced mapping for "
102: + fieldInfo.getName());
103: }
104: }
105:
106: return rv;
107: }
108:
109: public ClassInfo getClassInfo() {
110: return classInfo;
111: }
112:
113: // XXX: Most of this info is already in ClassInfo -- but not class verison ;-)
114: void initialize(int version, int access, String name,
115: String signature, String super Name, String[] interfaces,
116: Portability portability) {
117: this .classNameSlashes = name;
118: this .super NameSlashes = super Name;
119: this .classNameDots = name.replace('/', '.');
120: this .super NameDots = super Name.replace('/', '.');
121: this .classHierarchy.add(this .classNameSlashes);
122: this .classHierarchy.add(this .super NameSlashes);
123: this .classAccess = access;
124: this .classSignature = signature;
125: this .classInterfaces = interfaces;
126: this .classVersion = version;
127: decideOnInstrumentationAction(portability);
128: handleSubclassOfLogicalClass(access, classNameDots,
129: super NameDots);
130: }
131:
132: private boolean isArray(String className) {
133: return literalValues.valueForClassName(className) == LiteralValues.ARRAY;
134: }
135:
136: private void handleSubclassOfLogicalClass(int access,
137: String className, String super Name) {
138: if (isLogical()) {
139: return;
140: }
141: if (isArray(className)) {
142: return;
143: }
144: if (TransparencyClassSpecUtil.ignoreChecks(className)) {
145: return;
146: }
147: if (TransparencyClassSpecUtil.ignoreChecks(super Name)) {
148: return;
149: }
150:
151: super ClassSpec = spec.getClassSpec(super Name);
152: if (super ClassSpec != null && super ClassSpec.isLogical()) {
153: isSubclassOfLogicalClass = true;
154: }
155: }
156:
157: public int getClassVersion() {
158: return classVersion;
159: }
160:
161: public int getClassAccess() {
162: return classAccess;
163: }
164:
165: private void initClassHierarchy() {
166: String super ClassName = super NameSlashes.replace('/', '.');
167: try {
168: // As long as we are instrumenting anything other than Object, we are fine.
169: // Class superClazz = Class.forName(superClassName, false, this.caller).getSuperclass();
170: Class super Clazz = Class.forName(super ClassName, false,
171: this .classInfo.getClassLoader()).getSuperclass();
172: while (super Clazz != null) {
173: String super Name = super Clazz.getName();
174: classHierarchy.add(super Name.replace('.', '/'));
175: super Clazz = super Clazz.getSuperclass();
176: }
177: } catch (ClassNotFoundException e) {
178: e.printStackTrace();
179: } finally {
180: classHierarchyInitialized = true;
181: }
182: }
183:
184: String getClassNameSlashes() {
185: return this .classNameSlashes;
186: }
187:
188: String getClassNameDots() {
189: return this .classNameDots;
190: }
191:
192: String getSuperClassNameSlashes() {
193: return this .super NameSlashes;
194: }
195:
196: String getSuperClassNameDots() {
197: return this .super NameDots;
198: }
199:
200: String[] getClassInterfaces() {
201: return classInterfaces;
202: }
203:
204: String getClassSignature() {
205: return classSignature;
206: }
207:
208: void decideOnInstrumentationAction(Portability portability) {
209: Assert.assertNotNull(classNameSlashes);
210: Assert.assertNotNull(super NameSlashes);
211:
212: this .isInterface = Modifier.isInterface(classAccess);
213:
214: if (isInterface) {
215: this .instrumentationAction = TransparencyClassSpec.NOT_ADAPTABLE;
216: } else if (spec.getInstrumentationAction() == TransparencyClassSpec.PORTABLE) {
217: this .instrumentationAction = TransparencyClassSpec.PORTABLE;
218: } else if (spec.getInstrumentationAction() == TransparencyClassSpec.ADAPTABLE) {
219: this .instrumentationAction = TransparencyClassSpec.ADAPTABLE;
220: } else if (spec.isLogical() || spec.ignoreChecks()) {
221: // Logically managed classes need not have all super classes instrumented.
222: // currently THashMap and THashSet are not in boot jar and are instrumented during runtime.
223: this .instrumentationAction = TransparencyClassSpec.PORTABLE;
224: } else if (super ClassChecks(portability)) {
225: this .instrumentationAction = TransparencyClassSpec.ADAPTABLE;
226: } else {
227: this .instrumentationAction = TransparencyClassSpec.PORTABLE;
228: }
229: decideOnInstrumentationsToDo(portability);
230: }
231:
232: private void decideOnInstrumentationsToDo(Portability portability) {
233: if (this .instrumentationAction == TransparencyClassSpec.PORTABLE) {
234: if (!isLogical()) {
235: valuesGetterMethod = IS_NEEDED;
236: valuesSetterMethod = IS_NEEDED;
237: managedValuesGetterMethod = IS_NEEDED;
238: managedValuesSetterMethod = IS_NEEDED;
239: }
240: managedField = isNeedManagedField(portability);
241: managedMethods = managedField;
242: }
243: }
244:
245: /**
246: * If the superclass does not need to be instrumented, we need to generate the managed field and method for the class.
247: * If there does not exist a portable specs for the superclass, we need to generate the managed field and method for
248: * the class. If the class is logically instrumented, we need to generate the managed field and method. If it is a
249: * subclass of a logically instrumented class, we do not need to generate the field.
250: */
251: private byte isNeedManagedField(Portability portability) {
252: if (isSubclassOfLogicalClass) {
253: return IS_NOT_NEEDED;
254: }
255: ClassInfo super ClassInfo = classInfo.getSuperclass();
256: if (portability.isInstrumentationNotNeeded(super ClassInfo
257: .getName())) {
258: return IS_NEEDED;
259: }
260: if (!spec.hasPhysicallyPortableSpecs(super ClassInfo)) {
261: return IS_NEEDED;
262: }
263: if (spec.isLogical()) {
264: return IS_NEEDED;
265: }
266: return IS_NOT_NEEDED;
267: }
268:
269: private boolean super ClassChecks(Portability portability) {
270: String super ClassName = super NameSlashes.replace('/', '.');
271: if (portability.isInstrumentationNotNeeded(super ClassName)) {
272: return false;
273: }
274:
275: final Class super Clazz;
276: try {
277: super Clazz = Class.forName(super ClassName, false,
278: this .classInfo.getClassLoader());
279: } catch (ClassNotFoundException e) {
280: throw new RuntimeException(e);
281: }
282:
283: if (!portability.isPortableClass(super Clazz)) {
284: return true;
285: }
286: return false;
287: }
288:
289: boolean isInClassHierarchy(String classname) {
290: boolean inClassHierarchy = classHierarchy.contains(classname);
291: if (inClassHierarchy || classHierarchyInitialized)
292: return inClassHierarchy;
293: initClassHierarchy();
294: return classHierarchy.contains(classname);
295: }
296:
297: boolean isClassNotAdaptable() {
298: return this .instrumentationAction == TransparencyClassSpec.NOT_ADAPTABLE;
299: }
300:
301: boolean isClassAdaptable() {
302: return this .instrumentationAction == TransparencyClassSpec.ADAPTABLE;
303: }
304:
305: boolean isClassPortable() {
306: return this .instrumentationAction == TransparencyClassSpec.PORTABLE;
307: }
308:
309: boolean isSubclassofLogicalClass() {
310: return this .isSubclassOfLogicalClass;
311: }
312:
313: void moveToLogicalIfNecessary() {
314: if (isSubclassOfLogicalClass && !hasVisitedField) {
315: spec.moveToLogical(super ClassSpec);
316: }
317: }
318:
319: boolean hasDelegatedToLogicalClass() {
320: return needDelegateField();
321: }
322:
323: boolean needDelegateField() {
324: return this .delegateLogicalField == IS_NEEDED;
325: }
326:
327: boolean isWriteObjectMethodNeeded() {
328: return this .writeObjectSerializedMethod == IS_NEEDED;
329: }
330:
331: boolean isReadObjectMethodNeeded() {
332: return this .readObjectSerializedMethod == IS_NEEDED;
333: }
334:
335: void handleSubclassOfLogicalClassWithFieldsIfNecessary(int access) {
336: if (ByteCodeUtil.isSynthetic(access)
337: || Modifier.isStatic(access)) {
338: return;
339: } else if (isSubclassOfLogicalClass && !hasVisitedField) {
340: hasVisitedField = true;
341: try {
342: Class super Clazz = Class.forName(super NameDots, false,
343: this .classInfo.getClassLoader());
344:
345: Method[] methods = super Clazz.getMethods();
346: for (int i = 0; i < methods.length; i++) {
347: Method m = methods[i];
348: String methodName = m.getName();
349: int modifier = m.getModifiers();
350: if (!shouldVisitMethod(modifier, methodName)
351: || Modifier.isFinal(modifier)) {
352: continue;
353: }
354:
355: String methodDesc = Type.getMethodDescriptor(m);
356: shouldOverrideMethods.put(methodName + methodDesc,
357: m);
358: }
359:
360: methods = super Clazz.getDeclaredMethods();
361: for (int i = 0; i < methods.length; i++) {
362: Method m = methods[i];
363: String methodName = m.getName();
364: int modifier = m.getModifiers();
365: if (shouldVisitMethod(modifier, methodName)
366: && !Modifier.isFinal(modifier)
367: && Modifier.isProtected(modifier)) {
368: String methodDesc = Type.getMethodDescriptor(m);
369: logicalExtendingMethodSpec.add(methodName
370: + methodDesc);
371: }
372: }
373:
374: Field[] fields = super Clazz.getDeclaredFields();
375: for (int i = 0; i < fields.length; i++) {
376: Field f = fields[i];
377: String fieldName = f.getName();
378: int modifier = f.getModifiers();
379: if (!shouldVisitField(fieldName)
380: || Modifier.isFinal(modifier)
381: || Modifier.isPrivate(modifier)) {
382: continue;
383: }
384: String fieldDesc = Type.getDescriptor(f.getType());
385: logicalExtendingFieldSpec
386: .add(fieldName + fieldDesc);
387: }
388: } catch (ClassNotFoundException e) {
389: e.printStackTrace();
390: }
391: delegateLogicalField = IS_NEEDED;
392: writeObjectSerializedMethod = IS_NEEDED;
393: readObjectSerializedMethod = IS_NEEDED;
394: }
395: }
396:
397: void recordExistingFields(String name, String desc, String signature) {
398: if (ByteCodeUtil.isParent(name)) {
399: Assert.assertNull(parentClassInfo);
400: this .parentClassInfo = new ParentClassInfo(name, desc);
401: }
402: }
403:
404: void recordExistingMethods(String name, String desc,
405: String signature) {
406: if (LogicalClassSerializationAdapter.READ_OBJECT_SIGNATURE
407: .equals(name + desc)) {
408: readObjectSerializedMethod = IS_NOT_NEEDED;
409: } else if (LogicalClassSerializationAdapter.WRITE_OBJECT_SIGNATURE
410: .equals(name + desc)) {
411: writeObjectSerializedMethod = IS_NOT_NEEDED;
412: }
413: shouldOverrideMethods.remove(name + desc);
414: }
415:
416: boolean shouldVisitField(String name) {
417: return !(name.startsWith(ByteCodeUtil.TC_FIELD_PREFIX));
418: }
419:
420: boolean shouldVisitMethod(int methodAccess, String name) {
421: if (name.startsWith(ByteCodeUtil.TC_METHOD_PREFIX)) {
422: return false;
423: }
424: if (Modifier.isAbstract(methodAccess)
425: || Modifier.isNative(methodAccess)) {
426: return false;
427: }
428: return true;
429: }
430:
431: boolean isManagedMethodsNeeded() {
432: return (managedMethods == IS_NEEDED);
433: }
434:
435: boolean isValuesGetterMethodNeeded() {
436: return (valuesGetterMethod == IS_NEEDED);
437: }
438:
439: boolean isValuesSetterMethodNeeded() {
440: return (valuesSetterMethod == IS_NEEDED);
441: }
442:
443: boolean isManagedValuesGetterMethodNeeded() {
444: return (managedValuesGetterMethod == IS_NEEDED);
445: }
446:
447: boolean isManagedValuesSetterMethodNeeded() {
448: return (managedValuesSetterMethod == IS_NEEDED);
449: }
450:
451: boolean isManagedFieldNeeded() {
452: return (managedField == IS_NEEDED);
453: }
454:
455: TransparencyClassSpec getTransparencyClassSpec() {
456: return spec;
457: }
458:
459: TransparencyClassSpec getSuperclassTransparencyClassSpec() {
460: return spec.getClassSpec(super NameDots);
461: }
462:
463: boolean isLogical() {
464: return spec.isLogical();
465: }
466:
467: boolean needInstrumentFieldInsn() {
468: return !spec.isLogical()
469: && !(isSubclassOfLogicalClass && !hasVisitedField);
470: }
471:
472: void shouldProceedInstrumentation(int access, String name,
473: String desc) {
474: if (isSubclassOfLogicalClass && shouldVisitMethod(access, name)
475: && logicalExtendingMethodSpec.contains(name + desc)) {
476: // Subclass of Logical class cannot override protected method. So, ignore all instrumentation.
477: throw new TCLogicalSubclassNotPortableException(
478: classNameDots, super NameDots);
479: }
480: }
481:
482: void shouldProceedInstrumentation(String fieldName, String fieldDesc) {
483: if (isSubclassOfLogicalClass
484: && shouldVisitField(fieldName)
485: && logicalExtendingFieldSpec.contains(fieldName
486: + fieldDesc)) {
487: throw new TCLogicalSubclassNotPortableException(
488: classNameDots, super NameDots);
489: }
490: }
491:
492: boolean isPhysical() {
493: return spec.isPhysical();
494: }
495:
496: Collection getShouldOverrideMethods() {
497: return shouldOverrideMethods.values();
498: }
499:
500: ManagerHelper getManagerHelper() {
501: return mgrHelper;
502: }
503:
504: boolean isInner() {
505: return this .parentClassInfo != null;
506: }
507:
508: String getParentClassType() {
509: Assert.assertTrue(isInner());
510: return parentClassInfo.getType();
511: }
512:
513: String getParentClassFieldName() {
514: Assert.assertTrue(isInner());
515: return parentClassInfo.getFieldName();
516: }
517:
518: private static class ParentClassInfo {
519: private final String type;
520: private final String fieldName;
521:
522: ParentClassInfo(String fieldName, String type) {
523: this .fieldName = fieldName;
524: this .type = type;
525: }
526:
527: String getFieldName() {
528: return fieldName;
529: }
530:
531: String getType() {
532: return type;
533: }
534:
535: }
536:
537: public MethodInfo getMethodInfo(int access, String name, String desc) {
538: if (!"<init>".equals(name)) {
539: MethodInfo[] methods = classInfo.getMethods();
540: for (int i = 0; i < methods.length; i++) {
541: MethodInfo methodInfo = methods[i];
542: if (methodInfo.getName().equals(name)
543: && methodInfo.getSignature().equals(desc)) {
544: return methodInfo;
545: }
546: }
547: } else {
548: ConstructorInfo[] constructors = classInfo
549: .getConstructors();
550: for (int i = 0; i < constructors.length; i++) {
551: ConstructorInfo info = constructors[i];
552: if (info.getName().equals(name)
553: && info.getSignature().equals(desc)) {
554: return new ConstructorInfoWrapper(info);
555: }
556: }
557: // reflecting old DSO hack (see com.tc.object.bytecode.aspectwerkz.AsmMethodInfo)
558: name = "__INIT__";
559: }
560:
561: return new StubMethodInfo(name, desc, access, classInfo);
562: }
563:
564: public FieldInfo getFieldInfo(String fieldName) {
565: return (FieldInfo) fieldInfoMap.get(fieldName);
566: }
567:
568: private static final class StubMethodInfo implements MethodInfo {
569: private final String name;
570: private final String desc;
571: private final int access;
572: private final ClassInfo declaringClassInfo;
573:
574: private StubMethodInfo(String name, String desc, int access,
575: ClassInfo classInfo) {
576: this .name = name;
577: this .desc = desc;
578: this .access = access;
579: this .declaringClassInfo = classInfo;
580: }
581:
582: public ClassInfo[] getExceptionTypes() {
583: return new ClassInfo[0];
584: }
585:
586: public String[] getParameterNames() {
587: return new String[0];
588: }
589:
590: public ClassInfo[] getParameterTypes() {
591: Type[] types = Type.getArgumentTypes(desc);
592: ClassInfo[] parameterTypes = new ClassInfo[types.length];
593: for (int i = 0; i < types.length; i++) {
594: parameterTypes[i] = AsmClassInfo.getClassInfo(types[i]
595: .getClassName(), declaringClassInfo
596: .getClassLoader());
597: }
598: return parameterTypes;
599: }
600:
601: public ClassInfo getReturnType() {
602: Type type = Type.getReturnType(desc);
603: return AsmClassInfo.getClassInfo(type.getClassName(),
604: declaringClassInfo.getClassLoader());
605: }
606:
607: public ClassInfo getDeclaringType() {
608: return declaringClassInfo;
609: }
610:
611: public Annotation[] getAnnotations() {
612: return new Annotation[0];
613: }
614:
615: public String getGenericsSignature() {
616: return null;
617: }
618:
619: public int getModifiers() {
620: return access;
621: }
622:
623: public String getName() {
624: return name;
625: }
626:
627: public String getSignature() {
628: return desc;
629: }
630: }
631:
632: public class StubConstructorInfo implements ConstructorInfo {
633: private final String name;
634: private final String desc;
635: private final int access;
636: private final ClassInfo declaringClassInfo;
637:
638: private StubConstructorInfo(String name, String desc,
639: int access, ClassInfo classInfo) {
640: this .name = name;
641: this .desc = desc;
642: this .access = access;
643: this .declaringClassInfo = classInfo;
644: }
645:
646: public ClassInfo[] getExceptionTypes() {
647: return new ClassInfo[0];
648: }
649:
650: public ClassInfo[] getParameterTypes() {
651: Type[] types = Type.getArgumentTypes(desc);
652: ClassInfo[] parameterTypes = new ClassInfo[types.length];
653: for (int i = 0; i < types.length; i++) {
654: parameterTypes[i] = AsmClassInfo.getClassInfo(types[i]
655: .getClassName(), declaringClassInfo
656: .getClassLoader());
657: }
658: return parameterTypes;
659: }
660:
661: public ClassInfo getDeclaringType() {
662: return declaringClassInfo;
663: }
664:
665: public Annotation[] getAnnotations() {
666: return new Annotation[0];
667: }
668:
669: public String getGenericsSignature() {
670: return null;
671: }
672:
673: public int getModifiers() {
674: return access;
675: }
676:
677: public String getName() {
678: return name;
679: }
680:
681: public String getSignature() {
682: return desc;
683: }
684:
685: }
686:
687: public class ConstructorInfoWrapper implements MethodInfo {
688:
689: private final ConstructorInfo constructorInfo;
690:
691: public ConstructorInfoWrapper(ConstructorInfo constructorInfo) {
692: this .constructorInfo = constructorInfo;
693: }
694:
695: public String getName() {
696: // return constructorInfo.getName();
697: // reflecting old DSO hack (see com.tc.object.bytecode.aspectwerkz.AsmMethodInfo)
698: return "__INIT__";
699: }
700:
701: public int getModifiers() {
702: return constructorInfo.getModifiers();
703: }
704:
705: public ClassInfo[] getParameterTypes() {
706: return constructorInfo.getParameterTypes();
707: }
708:
709: public String getSignature() {
710: return constructorInfo.getSignature();
711: }
712:
713: public String getGenericsSignature() {
714: return constructorInfo.getGenericsSignature();
715: }
716:
717: public ClassInfo[] getExceptionTypes() {
718: return constructorInfo.getExceptionTypes();
719: }
720:
721: public Annotation[] getAnnotations() {
722: return constructorInfo.getAnnotations();
723: }
724:
725: public ClassInfo getDeclaringType() {
726: return constructorInfo.getDeclaringType();
727: }
728:
729: // specific to MethodInfo
730:
731: public String[] getParameterNames() {
732: return new String[0];
733: }
734:
735: public ClassInfo getReturnType() {
736: return JavaClassInfo.getClassInfo(void.class);
737: }
738:
739: }
740:
741: }
|