KickJava   Java API By Example, From Geeks To Geeks.

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


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

5 package com.tc.object.bytecode;
6
7 import com.tc.asm.Label;
8 import com.tc.asm.MethodVisitor;
9 import com.tc.asm.Opcodes;
10 import com.tc.asm.Type;
11 import com.tc.asm.commons.AdviceAdapter;
12 import com.tc.exception.TCInternalError;
13 import com.tc.object.config.LockDefinition;
14 import com.tc.object.config.TransparencyClassSpec;
15 import com.tc.object.config.TransparencyCodeSpec;
16
17 /**
18  * @author steve
19  */

20 public class TransparencyCodeAdapter extends AdviceAdapter implements Opcodes {
21
22   private final boolean isAutolock;
23   private final int autoLockType;
24   private final int modifiers;
25   private final String JavaDoc methodName;
26   private final String JavaDoc signature;
27   private final String JavaDoc description;
28   private final String JavaDoc[] exceptions;
29   private final ManagerHelper mgrHelper;
30   private final InstrumentationSpec spec;
31   private final TransparencyCodeSpec codeSpec;
32   private final Label labelZero = new Label();
33
34   private int[] localVariablesForMethodCall;
35
36   private boolean visitInit = false;
37
38   public TransparencyCodeAdapter(InstrumentationSpec spec, boolean isAutolock, int autoLockType,
39                                  final MethodVisitor mv, final int modifiers, String JavaDoc originalMethodName,
40                                  String JavaDoc methodName, String JavaDoc methodDesc, String JavaDoc signature, final String JavaDoc[] exceptions) {
41     super(mv, modifiers, methodName, methodDesc);
42     this.spec = spec;
43     this.isAutolock = isAutolock;
44     this.autoLockType = autoLockType;
45     this.modifiers = modifiers;
46     this.methodName = methodName;
47     this.signature = signature;
48     this.description = methodDesc;
49     this.exceptions = exceptions;
50     this.mgrHelper = spec.getManagerHelper();
51     this.codeSpec = spec.getTransparencyClassSpec().getCodeSpec(originalMethodName, description, isAutolock);
52
53     if (!"<init>".equals(methodName)) {
54       visitInit = true;
55     }
56   }
57
58   private void storeStackValuesToLocalVariables(String JavaDoc methodInsnDesc) {
59     Type[] types = Type.getArgumentTypes(methodInsnDesc);
60     localVariablesForMethodCall = new int[types.length];
61     for (int i = 0; i < types.length; i++) {
62       localVariablesForMethodCall[i] = newLocal(types[i].getSize());
63     }
64     for (int i = types.length - 1; i >= 0; i--) {
65       super.visitVarInsn(types[i].getOpcode(ISTORE), localVariablesForMethodCall[i]);
66     }
67   }
68
69   private void loadLocalVariables(String JavaDoc methodInsnDesc) {
70     Type[] types = Type.getArgumentTypes(methodInsnDesc);
71     for (int i = 0; i < types.length; i++) {
72       super.visitVarInsn(types[i].getOpcode(ILOAD), localVariablesForMethodCall[i]);
73     }
74   }
75
76   public void visitMethodInsn(int opcode, String JavaDoc classname, String JavaDoc theMethodName, String JavaDoc desc) {
77     if (handleSubclassOfLogicalClassMethodInsn(opcode, classname, theMethodName, desc)) { return; }
78     if (codeSpec.isArraycopyInstrumentationReq(classname, theMethodName)) {
79       rewriteArraycopy();
80     } else if (classname.equals("java/lang/Object")) {
81       handleJavaLangObjectMethodCall(opcode, classname, theMethodName, desc);
82     } else {
83       super.visitMethodInsn(opcode, classname, theMethodName, desc);
84     }
85   }
86
87   private boolean handleSubclassOfLogicalClassMethodInsn(int opcode, String JavaDoc classname, String JavaDoc theMethodName, String JavaDoc desc) {
88     if (!spec.hasDelegatedToLogicalClass()) { return false; }
89     String JavaDoc logicalExtendingClassName = spec.getSuperClassNameSlashes();
90     if (INVOKESPECIAL == opcode && !spec.getClassNameSlashes().equals(classname) && !"<init>".equals(theMethodName)) {
91       spec.shouldProceedInstrumentation(modifiers, theMethodName, desc);
92       storeStackValuesToLocalVariables(desc);
93       super.visitMethodInsn(INVOKESPECIAL, spec.getClassNameSlashes(), ByteCodeUtil.fieldGetterMethod(ClassAdapterBase
94           .getDelegateFieldName(logicalExtendingClassName)), "()L" + logicalExtendingClassName + ";");
95       loadLocalVariables(desc);
96       super.visitMethodInsn(INVOKEVIRTUAL, logicalExtendingClassName, theMethodName, desc);
97       return true;
98     }
99     return false;
100   }
101
102   private TransparencyClassSpec getTransparencyClassSpec() {
103     return spec.getTransparencyClassSpec();
104   }
105
106   private void rewriteArraycopy() {
107     callArrayManagerMethod("arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V");
108   }
109
110   private void handleJavaLangObjectMethodCall(int opcode, String JavaDoc classname, String JavaDoc theMethodName, String JavaDoc desc) {
111     if (handleJavaLangObjectWaitNotifyCalls(opcode, classname, theMethodName, desc)) {
112       return;
113     } else if (handleJavaLangObjectCloneCall(opcode, classname, theMethodName, desc)) {
114       return;
115     } else {
116       super.visitMethodInsn(opcode, classname, theMethodName, desc);
117     }
118   }
119
120   /*
121    * The assumption here is that the compiler wouldnt call invokevirtual on a classname other than java.lang.Object when
122    * there is no implementation of clone() defined in that classes' hierarchy. If it does, it a bug in the compiler ;-)
123    * This adaption is needed for both PORTABLE and ADAPTABLE classes as we can have instance where Logical subclass of
124    * ADAPTABLE class calls clone() to make a copy of itself.
125    *
126    * @see AbstractMap and HashMap
127    */

128   private boolean handleJavaLangObjectCloneCall(int opcode, String JavaDoc classname, String JavaDoc theMethodName, String JavaDoc desc) {
129     if ("clone".equals(theMethodName) && "()Ljava/lang/Object;".equals(desc)) {
130       super.visitInsn(DUP);
131       super.visitInsn(DUP);
132       super.visitMethodInsn(INVOKESTATIC, "com/tc/object/bytecode/hook/impl/Util", "resolveAllReferencesBeforeClone",
133                             "(Ljava/lang/Object;)V");
134       super.visitMethodInsn(opcode, classname, theMethodName, desc);
135       super.visitMethodInsn(INVOKESTATIC, "com/tc/object/bytecode/hook/impl/Util",
136                             "fixTCObjectReferenceOfClonedObject",
137                             "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
138       return true;
139     }
140     return false;
141   }
142
143   private boolean handleJavaLangObjectWaitNotifyCalls(int opcode, String JavaDoc classname, String JavaDoc theMethodName, String JavaDoc desc) {
144     if (spec.isLogical() || !codeSpec.isWaitNotifyInstrumentationReq()) { return false; }
145
146     Type[] args = Type.getArgumentTypes(desc);
147
148     if (theMethodName.equals("notify") || theMethodName.equals("notifyAll")) {
149       if (args.length == 0) {
150         if (theMethodName.endsWith("All")) {
151           mgrHelper.callManagerMethod("objectNotifyAll", this);
152         } else {
153           mgrHelper.callManagerMethod("objectNotify", this);
154         }
155         return true;
156       }
157       throw new TCInternalError("Unexpected java.lang.Object method signature: " + theMethodName + " + " + desc);
158     } else if (theMethodName.equals("wait")) {
159
160       switch (args.length) {
161         case 0: {
162           mgrHelper.callManagerMethod("objectWait0", this);
163           return true;
164         }
165         case 1: {
166           if (args[0].equals(Type.LONG_TYPE)) {
167             mgrHelper.callManagerMethod("objectWait1", this);
168             return true;
169           }
170           throw new TCInternalError("Unexpected java.lang.Object method signature: " + theMethodName + " + " + desc);
171         }
172         case 2: {
173           if ((args[0].equals(Type.LONG_TYPE)) && (args[1].equals(Type.INT_TYPE))) {
174             mgrHelper.callManagerMethod("objectWait2", this);
175             return true;
176           }
177           throw new TCInternalError("Unexpected java.lang.Object method signature: " + theMethodName + " + " + desc);
178         }
179         default: {
180           throw new TCInternalError("Unexpected java.lang.Object method signature: " + theMethodName + " + " + desc);
181         }
182       }
183     } else { // neither wait(...) nor notify[All]()
184
return false;
185     }
186
187     // should be unreachable
188
}
189
190   private void callTCBeginWithLocks(MethodVisitor c) {
191     c.visitLabel(new Label());
192     LockDefinition[] defs = getTransparencyClassSpec().lockDefinitionsFor(modifiers, methodName, description,
193                                                                           exceptions);
194     for (int i = 0; i < defs.length; i++) {
195       if (!defs[i].isAutolock()) {
196         callTCBeginWithLock(defs[i], c);
197       }
198     }
199   }
200
201   private void callTCBeginWithLock(LockDefinition lock, MethodVisitor c) {
202     c.visitLdcInsn(ByteCodeUtil.generateNamedLockName(lock.getLockName()));
203     c.visitLdcInsn(new Integer JavaDoc(lock.getLockLevelAsInt()));
204     mgrHelper.callManagerMethod("beginLock", c);
205   }
206
207   private void callTCCommit(MethodVisitor c) {
208     LockDefinition[] locks = getTransparencyClassSpec().lockDefinitionsFor(modifiers, methodName, description,
209                                                                            exceptions);
210     for (int i = 0; i < locks.length; i++) {
211       if (!locks[i].isAutolock()) {
212         c.visitLdcInsn(ByteCodeUtil.generateNamedLockName(locks[i].getLockName()));
213         mgrHelper.callManagerMethod("commitLock", c);
214       }
215     }
216   }
217
218   public void visitInsn(int opCode) {
219     if (isMonitorInstrumentationReq(opCode)) {
220       switch (opCode) {
221         case MONITORENTER:
222           if (this.isAutolock) {
223             super.visitInsn(DUP);
224             super.visitLdcInsn(new Integer JavaDoc(autoLockType));
225             mgrHelper.callManagerMethod("monitorEnter", this);
226             super.visitInsn(opCode);
227           } else {
228             super.visitInsn(opCode);
229           }
230           return;
231         case MONITOREXIT:
232           if (this.isAutolock) {
233             super.visitInsn(DUP);
234             super.visitInsn(opCode);
235             mgrHelper.callManagerMethod("monitorExit", this);
236           } else {
237             super.visitInsn(opCode);
238           }
239           return;
240       }
241     }
242     if (isArrayOperatorInstrumentationReq(opCode)) {
243       switch (opCode) {
244         case AALOAD:
245           Label end = new Label();
246           Label notManaged = new Label();
247           Label noIndexException = new Label();
248           super.visitInsn(DUP2);
249           super.visitInsn(POP);
250           callArrayManagerMethod("getObject", "(Ljava/lang/Object;)Lcom/tc/object/TCObject;");
251           super.visitInsn(DUP);
252           super.visitJumpInsn(IFNULL, notManaged);
253           super.visitInsn(DUP2);
254           super.visitInsn(SWAP);
255           super.visitMethodInsn(INVOKEINTERFACE, "com/tc/object/TCObject", "checkArrayIndex",
256                                 "(I)Ljava/lang/ArrayIndexOutOfBoundsException;");
257           super.visitInsn(DUP);
258           super.visitJumpInsn(IFNULL, noIndexException);
259           super.visitInsn(SWAP);
260           super.visitInsn(POP);
261           super.visitInsn(SWAP);
262           super.visitInsn(POP);
263           super.visitInsn(SWAP);
264           super.visitInsn(POP);
265           super.visitInsn(ATHROW);
266           super.visitLabel(noIndexException);
267           super.visitInsn(POP);
268           super.visitInsn(DUP_X2);
269           super.visitInsn(DUP);
270           super.visitMethodInsn(INVOKEINTERFACE, "com/tc/object/TCObject", "getResolveLock", "()Ljava/lang/Object;");
271           super.visitInsn(MONITORENTER);
272           super.visitInsn(DUP2);
273           super.visitInsn(SWAP);
274           super.visitMethodInsn(INVOKEINTERFACE, "com/tc/object/TCObject", "resolveArrayReference", "(I)V");
275           super.visitInsn(POP);
276           super.visitInsn(opCode);
277           super.visitInsn(SWAP);
278           super.visitMethodInsn(INVOKEINTERFACE, "com/tc/object/TCObject", "getResolveLock", "()Ljava/lang/Object;");
279           super.visitInsn(MONITOREXIT);
280           super.visitJumpInsn(GOTO, end);
281           super.visitLabel(notManaged);
282           super.visitInsn(POP);
283           super.visitInsn(opCode);
284           super.visitLabel(end);
285           return;
286         case AASTORE:
287           callArrayManagerMethod("objectArrayChanged", "([Ljava/lang/Object;ILjava/lang/Object;)V");
288           return;
289         case LASTORE:
290           callArrayManagerMethod("longArrayChanged", "([JIJ)V");
291           return;
292         case SASTORE:
293           callArrayManagerMethod("shortArrayChanged", "([SIS)V");
294           return;
295         case IASTORE:
296           callArrayManagerMethod("intArrayChanged", "([III)V");
297           return;
298         case DASTORE:
299           callArrayManagerMethod("doubleArrayChanged", "([DID)V");
300           return;
301         case FASTORE:
302           callArrayManagerMethod("floatArrayChanged", "([FIF)V");
303           return;
304         case BASTORE:
305           callArrayManagerMethod("byteOrBooleanArrayChanged", "(Ljava/lang/Object;IB)V");
306           return;
307         case CASTORE:
308           callArrayManagerMethod("charArrayChanged", "([CIC)V");
309           return;
310       }
311     }
312     super.visitInsn(opCode);
313   }
314
315   private boolean isArrayOperatorInstrumentationReq(int opCode) {
316     return ((opCode == AALOAD || opCode == AASTORE || opCode == LASTORE || opCode == SASTORE || opCode == IASTORE
317              || opCode == DASTORE || opCode == FASTORE || opCode == BASTORE || opCode == CASTORE) && codeSpec
318         .isArrayOperatorInstrumentationReq());
319   }
320
321   private boolean isMonitorInstrumentationReq(int opCode) {
322     return ((opCode == MONITORENTER || opCode == MONITOREXIT) && codeSpec.isMonitorInstrumentationReq());
323   }
324
325   private void callArrayManagerMethod(String JavaDoc name, String JavaDoc desc) {
326     super.visitMethodInsn(INVOKESTATIC, ManagerUtil.CLASS, name, desc);
327   }
328
329   public void visitMaxs(int stack, int vars) {
330     super.visitMaxs(stack, vars + 1);
331   }
332
333   public void visitFieldInsn(final int opcode, final String JavaDoc classname, final String JavaDoc fieldName, final String JavaDoc desc) {
334     spec.shouldProceedInstrumentation(fieldName, desc);
335
336     if (!spec.needInstrumentFieldInsn() || !visitInit || !codeSpec.isFieldInstrumentationReq(fieldName)) {
337       super.visitFieldInsn(opcode, classname, fieldName, desc);
338       return;
339     }
340
341     if (spec.isPhysical()) {
342       // if (opcode == GETFIELD && (isRoot(classname, fieldName) || !isPrimitive(Type.getType(desc)))) {
343
if (opcode == GETFIELD) {
344         visitGetFieldInsn(classname, fieldName, desc);
345         return;
346       } else if (opcode == PUTFIELD) {
347         visitSetFieldInsn(classname, fieldName, desc);
348         return;
349       } else if (opcode == PUTSTATIC && isRoot(classname, fieldName)) {
350         String JavaDoc sDesc = "(" + desc + ")V";
351         visitMethodInsn(INVOKESTATIC, classname, ByteCodeUtil.fieldSetterMethod(fieldName), sDesc);
352         return;
353       } else if (opcode == GETSTATIC && isRoot(classname, fieldName)) {
354         String JavaDoc gDesc = "()" + desc;
355         visitMethodInsn(INVOKESTATIC, classname, ByteCodeUtil.fieldGetterMethod(fieldName), gDesc);
356         return;
357       }
358       super.visitFieldInsn(opcode, classname, fieldName, desc);
359     } else {
360       super.visitFieldInsn(opcode, classname, fieldName, desc);
361     }
362   }
363
364   private void visitSetFieldInsn(String JavaDoc classname, String JavaDoc fieldName, String JavaDoc desc) {
365     boolean inClassHierarchy = spec.isInClassHierarchy(classname);
366     if ((spec.isClassPortable() && inClassHierarchy) || isRoot(classname, fieldName)) {
367       // If the field is a root, we assume that the class is instrumented automatically.
368
// If it is not then bad things are gonna happen anyway.
369
visitUncheckedSetFieldInsn(classname, fieldName, desc);
370     } else if (spec.isClassAdaptable() && inClassHierarchy) {
371       visitSetFieldInsnOriginal(classname, fieldName, desc);
372     } else {
373       visitCheckedSetFieldInsn(classname, fieldName, desc);
374     }
375   }
376
377   private void visitSetFieldInsnOriginal(String JavaDoc classname, String JavaDoc fieldName, String JavaDoc desc) {
378     // System.err.println("Original :: My class : " + spec.getClassNameSlashes() + " set on : " + classname + " field :
379
// " + fieldName);
380
super.visitFieldInsn(PUTFIELD, classname, fieldName, desc);
381   }
382
383   private void visitUncheckedSetFieldInsn(String JavaDoc classname, String JavaDoc fieldName, String JavaDoc desc) {
384     // System.err.println("Unchecked :: My class : " + spec.getClassNameSlashes() + " set on : " + classname + " field :
385
// " + fieldName);
386
String JavaDoc sDesc = "(" + desc + ")V";
387     visitMethodInsn(INVOKEVIRTUAL, classname, ByteCodeUtil.fieldSetterMethod(fieldName), sDesc);
388   }
389
390   /**
391    * This method assumes that we dont have anyinfo on the class that we are setting the field to. so we take the
392    * conservative approach.
393    */

394   private void visitCheckedSetFieldInsn(String JavaDoc classname, String JavaDoc fieldName, String JavaDoc desc) {
395     // System.err.println("Checked :: My class : " + spec.getClassNameSlashes() + " set on : " + classname + " field : "
396
// + fieldName);
397
Type fieldType = Type.getType(desc);
398     Type reference = Type.getType("Ljava/lang/Object;");
399     String JavaDoc sDesc = "(" + desc + ")V";
400
401     swap(reference, fieldType);
402     super.visitInsn(DUP);
403     Label l1 = new Label();
404     super.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
405     mgrHelper.callManagerMethod("isPhysicallyInstrumented", this);
406     super.visitJumpInsn(IFEQ, l1);
407     swap(fieldType, reference);
408     visitMethodInsn(INVOKEVIRTUAL, classname, ByteCodeUtil.fieldSetterMethod(fieldName), sDesc);
409     Label l2 = new Label();
410     super.visitJumpInsn(GOTO, l2);
411     super.visitLabel(l1);
412     swap(fieldType, reference);
413     super.visitFieldInsn(PUTFIELD, classname, fieldName, desc);
414     super.visitLabel(l2);
415   }
416
417   private void visitGetFieldInsn(String JavaDoc classname, String JavaDoc fieldName, String JavaDoc desc) {
418     boolean inClassHierarchy = spec.isInClassHierarchy(classname);
419     if ((spec.isClassPortable() && inClassHierarchy) || isRoot(classname, fieldName)) {
420       // If the field is a root, we assume that the class is instrumented automatically.
421
// If it is not then bad things are gonna happen anyway.
422
visitUncheckedGetFieldInsn(classname, fieldName, desc);
423     } else if (spec.isClassAdaptable() && inClassHierarchy) {
424       visitGetFieldInsnOriginal(classname, fieldName, desc);
425     } else {
426       visitCheckedGetFieldInsn(classname, fieldName, desc);
427     }
428   }
429
430   private void visitGetFieldInsnOriginal(String JavaDoc classname, String JavaDoc fieldName, String JavaDoc desc) {
431     // System.err.println("Original :: My class : " + spec.getClassNameSlashes() + " get on : " + classname + " field :
432
// " + fieldName);
433
super.visitFieldInsn(GETFIELD, classname, fieldName, desc);
434   }
435
436   private void visitUncheckedGetFieldInsn(String JavaDoc classname, String JavaDoc fieldName, String JavaDoc desc) {
437     // System.err.println("Unchecked :: My class: " + spec.getClassNameSlashes() + " get on : " + classname + " field :
438
// " + fieldName);
439
String JavaDoc gDesc = "()" + desc;
440     visitMethodInsn(INVOKEVIRTUAL, classname, ByteCodeUtil.fieldGetterMethod(fieldName), gDesc);
441   }
442
443   /**
444    * This method assumes that we dont have anyinfo on the class that we are setting the field to. so we take the
445    * conservative approach.
446    */

447   private void visitCheckedGetFieldInsn(String JavaDoc classname, String JavaDoc fieldName, String JavaDoc desc) {
448     String JavaDoc gDesc = "()" + desc;
449     super.visitInsn(DUP);
450     Label l1 = new Label();
451     super.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
452     mgrHelper.callManagerMethod("isPhysicallyInstrumented", this);
453     super.visitJumpInsn(IFEQ, l1);
454     visitMethodInsn(INVOKEVIRTUAL, classname, ByteCodeUtil.fieldGetterMethod(fieldName), gDesc);
455     Label l2 = new Label();
456     super.visitJumpInsn(GOTO, l2);
457     super.visitLabel(l1);
458     super.visitFieldInsn(GETFIELD, classname, fieldName, desc);
459     super.visitLabel(l2);
460   }
461
462   private boolean isRoot(String JavaDoc classname, String JavaDoc fieldName) {
463     return getTransparencyClassSpec().isRoot(classname.replace('/', '.'), fieldName);
464   }
465
466   protected String JavaDoc getSignature() {
467     // This method added to silence warning about never reading "signature" field. If some code actually starts usiung
468
// that field, then you can kill this method
469
return this.signature;
470   }
471
472   protected void onMethodEnter() {
473     if ("<init>".equals(methodName)) {
474       visitInit = true;
475       if (getTransparencyClassSpec().isLockMethod(modifiers, methodName, description, exceptions)) {
476         callTCBeginWithLocks(this);
477         super.visitLabel(labelZero);
478       }
479     }
480   }
481
482   protected void onMethodExit(int opcode) {
483     if ("<init>".equals(methodName)
484         && getTransparencyClassSpec().isLockMethod(modifiers, methodName, description, exceptions)) {
485
486       if (opcode == RETURN) {
487         callTCCommit(this);
488       } else if (opcode == ATHROW) {
489         // nothing special to do here, exception handler for method will do the commit
490
} else {
491         // <init> should not be returning with any other opcodes
492
throw new AssertionError JavaDoc("unexpected exit instruction: " + opcode);
493       }
494     }
495   }
496
497   public void visitEnd() {
498     if ("<init>".equals(methodName)
499         && getTransparencyClassSpec().isLockMethod(modifiers, methodName, description, exceptions)) {
500
501       Label labelEnd = new Label();
502       super.visitLabel(labelEnd);
503       super.visitTryCatchBlock(labelZero, labelEnd, labelEnd, null);
504       int localVar = newLocal(1);
505       super.visitVarInsn(ASTORE, localVar);
506       callTCCommit(mv);
507       super.visitVarInsn(ALOAD, localVar);
508       mv.visitInsn(ATHROW);
509     }
510
511     super.visitEnd();
512   }
513
514 }
515
Popular Tags