1 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 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 methodName; 26 private final String signature; 27 private final String description; 28 private final String [] 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 originalMethodName, 40 String methodName, String methodDesc, String signature, final String [] 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 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 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 classname, String theMethodName, String 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 classname, String theMethodName, String desc) { 88 if (!spec.hasDelegatedToLogicalClass()) { return false; } 89 String 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 classname, String theMethodName, String 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 128 private boolean handleJavaLangObjectCloneCall(int opcode, String classname, String theMethodName, String 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 classname, String theMethodName, String 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 { return false; 185 } 186 187 } 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 (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 (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 name, String 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 classname, final String fieldName, final String 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) { 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 sDesc = "(" + desc + ")V"; 351 visitMethodInsn(INVOKESTATIC, classname, ByteCodeUtil.fieldSetterMethod(fieldName), sDesc); 352 return; 353 } else if (opcode == GETSTATIC && isRoot(classname, fieldName)) { 354 String 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 classname, String fieldName, String desc) { 365 boolean inClassHierarchy = spec.isInClassHierarchy(classname); 366 if ((spec.isClassPortable() && inClassHierarchy) || isRoot(classname, fieldName)) { 367 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 classname, String fieldName, String desc) { 378 super.visitFieldInsn(PUTFIELD, classname, fieldName, desc); 381 } 382 383 private void visitUncheckedSetFieldInsn(String classname, String fieldName, String desc) { 384 String sDesc = "(" + desc + ")V"; 387 visitMethodInsn(INVOKEVIRTUAL, classname, ByteCodeUtil.fieldSetterMethod(fieldName), sDesc); 388 } 389 390 394 private void visitCheckedSetFieldInsn(String classname, String fieldName, String desc) { 395 Type fieldType = Type.getType(desc); 398 Type reference = Type.getType("Ljava/lang/Object;"); 399 String 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 classname, String fieldName, String desc) { 418 boolean inClassHierarchy = spec.isInClassHierarchy(classname); 419 if ((spec.isClassPortable() && inClassHierarchy) || isRoot(classname, fieldName)) { 420 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 classname, String fieldName, String desc) { 431 super.visitFieldInsn(GETFIELD, classname, fieldName, desc); 434 } 435 436 private void visitUncheckedGetFieldInsn(String classname, String fieldName, String desc) { 437 String gDesc = "()" + desc; 440 visitMethodInsn(INVOKEVIRTUAL, classname, ByteCodeUtil.fieldGetterMethod(fieldName), gDesc); 441 } 442 443 447 private void visitCheckedGetFieldInsn(String classname, String fieldName, String desc) { 448 String 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 classname, String fieldName) { 463 return getTransparencyClassSpec().isRoot(classname.replace('/', '.'), fieldName); 464 } 465 466 protected String getSignature() { 467 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 } else { 491 throw new AssertionError ("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 |