1 package org.bsf.smartValueObject.tools; 2 3 import org.objectweb.asm.*; 4 import org.apache.commons.logging.Log; 5 import org.apache.commons.logging.LogFactory; 6 import org.bsf.smartValueObject.tools.Instrumentor; 7 import org.bsf.smartValueObject.Versionable; 8 import org.bsf.smartValueObject.Version; 9 10 import java.io.File ; 11 import java.io.FileInputStream ; 12 import java.io.InputStream ; 13 import java.util.*; 14 import java.lang.reflect.Method ; 15 16 24 public class ASMInstrumentor implements Instrumentor { 25 private static Log log = LogFactory.getLog(ASMInstrumentor.class); 26 private ClassWriter cw; 27 private String className; 28 29 public void modifyClass(String name) throws InstrumentorException { 30 modifyClass(null, name); 31 } 32 33 public void modifyClass(String basedir, String name) throws InstrumentorException { 34 String file; 35 if (basedir != null) { 36 file = basedir + File.separator + name; 37 } else { 38 file = name; 39 } 40 41 className = name 42 .substring(0, name.length() - 6) 43 .replace(File.separatorChar, '/'); 44 45 try { 46 InputStream is = new FileInputStream (file); 47 ClassReader cr = new ClassReader(is); 48 cw = new ClassWriter(true); 49 ClassVisitor cv = new SVOClassAdapter(className, cw); 50 cr.accept(cv, true); 51 } catch (Exception e) { 52 throw new InstrumentorException(e); 53 } 54 55 } 56 57 public byte[] getBytecode() throws InstrumentorException { 58 if (cw == null) { 59 throw new InstrumentorException(); 60 } 61 return cw.toByteArray(); 62 } 63 64 public Class defineClass() { 65 throw new UnsupportedOperationException (); 66 } 67 68 71 private class SVOClassAdapter extends ClassAdapter implements Constants { 72 private Set methods = new HashSet(); 73 private boolean methodsCreated = false; 74 private String internalName; 75 76 public SVOClassAdapter(String className, ClassVisitor cv) { 77 super(cv); 78 this.internalName = className; 79 } 80 81 public void visit(int access, String name, String superName, 82 String [] interfaces, String srcfile) { 83 log.debug("visit()"); 84 String [] newInterfaces; 85 if (interfaces == null) { 86 newInterfaces = new String [1]; 87 } else { 88 newInterfaces = new String [interfaces.length + 1]; 89 System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length); 90 } 91 92 newInterfaces[newInterfaces.length - 1] = VERSIONINTERFACE.replace('.', '/'); 93 94 cv.visit(access, name, superName, newInterfaces, srcfile); 95 createVersionableField(cv); 96 } 97 98 public CodeVisitor visitMethod(int i, String s, String s1, String [] strings, Attribute attribute) { 99 log.debug("visitMethod(" + s + ")"); 100 CodeVisitor mv = cv.visitMethod(i, s, s1, strings, attribute); 101 return mv == null ? null : new SVOCodeAdapter(s, mv, this); 102 } 103 104 public void visitInnerClass(String s, String s1, String s2, int i) { 105 log.debug("visitInnerClass()"); 106 if (!methodsCreated) 107 createTrapMethods(); 108 cv.visitInnerClass(s, s1, s2, i); 109 } 110 111 public void visitAttribute(Attribute attribute) { 112 log.debug("visitAttribute()"); 113 if (!methodsCreated) 114 createTrapMethods(); 115 cv.visitAttribute(attribute); 116 } 117 118 public void visitEnd() { 119 log.debug("visitEnd()"); 120 if (!methodsCreated) 121 createTrapMethods(); 122 cv.visitEnd(); 123 } 124 125 public void addMethod(MyMethod m) { 126 log.debug("addMethod(" + m + ")"); 127 methods.add(m); 128 } 129 130 public String getInternalName() { 131 return internalName; 132 } 133 134 private void createTrapMethods() { 135 log.debug("createTrapMethods()"); 136 for (Iterator it = methods.iterator(); it.hasNext(); ) { 137 MyMethod method = (MyMethod) it.next(); 138 CodeVisitor codevisitor = cv.visitMethod( 139 ACC_PRIVATE, 140 method.getName(), 141 "(" + method.getType() + ")V", 142 null, 143 null); 144 145 createTrapMethod(method, codevisitor); 146 } 147 148 createVersionableMethods(Versionable.class, VERSIONFIELD); 149 methodsCreated = true; 150 } 151 152 private void createTrapMethod(MyMethod m, CodeVisitor cv) { 153 log.debug("createTrapMethod(" + m + ")"); 154 155 if (m.hasSmartContainer()) { 156 createContainerTrap(m, cv); 157 return; 158 } 159 160 if (m.getType().length() == 1 || 162 m.getTypeInternalName().startsWith("java/lang")) { 163 Label fieldDifferent = new Label(); 164 Label fieldNull = new Label(); 165 166 cv.visitVarInsn(ALOAD, 0); 168 cv.visitFieldInsn(GETFIELD, getInternalName(), m.getField(), m.getType()); 169 170 if (m.getType().startsWith("L")) { 171 cv.visitInsn(DUP); 173 cv.visitJumpInsn(IFNULL, fieldNull); 174 cv.visitVarInsn(ALOAD, 1); 175 cv.visitMethodInsn(INVOKEVIRTUAL, m.getTypeInternalName(), "equals", "(Ljava/lang/Object;)Z"); 176 cv.visitJumpInsn(IFEQ, fieldDifferent); 177 } else if (m.getType().startsWith("D")) { 179 cv.visitVarInsn(DLOAD, 1); 180 cv.visitInsn(DCMPL); 181 cv.visitJumpInsn(IFNE, fieldDifferent); 182 } else if (m.getType().startsWith("F")) { 183 cv.visitVarInsn(FLOAD, 1); 184 cv.visitInsn(FCMPL); 185 cv.visitJumpInsn(IFNE, fieldDifferent); 186 } else if (m.getType().startsWith("J")) { 187 cv.visitVarInsn(LLOAD, 1); 188 cv.visitInsn(LCMP); 189 cv.visitJumpInsn(IFNE, fieldDifferent); 190 } else if (m.getType().startsWith("]")) { 191 cv.visitJumpInsn(GOTO, fieldDifferent); 192 } else { 193 cv.visitVarInsn(ILOAD, 1); 194 cv.visitJumpInsn(IF_ICMPNE, fieldDifferent); 195 } 196 197 cv.visitInsn(RETURN); 198 199 cv.visitLabel(fieldNull); 201 cv.visitInsn(POP); 203 cv.visitVarInsn(ALOAD, 1); 205 cv.visitJumpInsn(IFNONNULL, fieldDifferent); 206 207 cv.visitInsn(RETURN); 208 209 cv.visitLabel(fieldDifferent); 210 } 211 212 213 214 cv.visitVarInsn(ALOAD, 0); 215 cv.visitFieldInsn(GETFIELD, 216 getInternalName(), 217 VERSIONFIELD, 218 Type.getDescriptor(Versionable.class)); 219 220 cv.visitLdcInsn(m.getField()); 222 cv.visitMethodInsn(INVOKEINTERFACE, 223 Type.getDescriptor(Versionable.class), 224 VERSIONMETHOD, "(Ljava/lang/String;)V"); 225 226 cv.visitVarInsn(ALOAD, 0); 228 String type = m.getType(); 229 if (type.startsWith("L") || type.startsWith("[")) 230 cv.visitVarInsn(ALOAD, 1); 231 else if (type.startsWith("J")) 232 cv.visitVarInsn(LLOAD, 1); 233 else if (type.startsWith("D")) 234 cv.visitVarInsn(DLOAD, 1); 235 else if (type.startsWith("F")) 236 cv.visitVarInsn(FLOAD, 1); 237 else 238 cv.visitVarInsn(ILOAD, 1); 239 240 cv.visitFieldInsn(PUTFIELD, 241 getInternalName(), 242 m.getField(), 243 m.getType()); 244 cv.visitInsn(RETURN); 245 cv.visitMaxs(4,2); 246 } 247 248 private void createContainerTrap(MyMethod m, CodeVisitor cv) { 249 log.debug("createContainerTrap(" + m + ")"); 250 String typeClassName = m.getTypeClassName(); 251 String typeClassDesc = typeClassName.replace('.', '/'); 252 String replacement = SMARTCONTAINERS.getProperty(typeClassName); 253 254 if (replacement == null) { 255 log.debug("createContainerTrap: no replacement for " + typeClassName); 256 return; 257 } 258 259 String replacementDesc = replacement.replace('.', '/'); 260 String versionableDesc = VERSIONINTERFACE.replace('.', '/'); 261 262 cv.visitVarInsn(ALOAD, 0); 264 cv.visitTypeInsn(NEW, replacementDesc); 265 cv.visitInsn(DUP); 266 267 cv.visitVarInsn(ALOAD, 1); 269 270 cv.visitVarInsn(ALOAD, 0); 272 cv.visitFieldInsn(GETFIELD, getInternalName(), VERSIONFIELD, "L" + versionableDesc + ";"); 273 274 cv.visitMethodInsn(INVOKESPECIAL, 276 replacementDesc, 277 "<init>", 278 "(L" + typeClassDesc + ";L" + versionableDesc + ";)V"); 279 280 cv.visitFieldInsn(PUTFIELD, getInternalName(), m.getField(), m.getType()); 282 cv.visitInsn(RETURN); 283 cv.visitMaxs(4, 2); 284 } 285 286 private void createVersionableField(ClassVisitor cv) { 287 log.debug("createVersionableField()"); 288 cv.visitField( 289 ACC_PRIVATE, 290 VERSIONFIELD, 291 Type.getDescriptor(Versionable.class), 292 null, 293 null); 294 } 295 296 private void createVersionableMethods(Class clazz, String field) { 297 log.debug("createVersionableMethods(" + clazz + ", " + field); 298 java.lang.reflect.Method [] methods = clazz.getDeclaredMethods(); 299 for (int i = 0; i < methods.length; i++) { 300 Method method = methods[i]; 301 String name = method.getName(); 302 Class [] _exceptions = method.getExceptionTypes(); 303 String [] exceptions; 304 if (_exceptions.length != 0) { 305 exceptions = new String [_exceptions.length]; 306 for (int j = 0; j < exceptions.length; j++) { 307 Class exception = _exceptions[j]; 308 exceptions[j] = Type.getDescriptor(exception); 309 } 310 } else { 311 exceptions = null; 312 } 313 314 String desc = Type.getMethodDescriptor(method); 315 316 CodeVisitor codevisitor = cv.visitMethod(ACC_PUBLIC, 318 name, desc, exceptions, null); 319 codevisitor.visitVarInsn(ALOAD, 0); 321 codevisitor.visitFieldInsn(GETFIELD, 323 getInternalName(), 324 VERSIONFIELD, 325 Type.getDescriptor(Versionable.class)); 326 327 Class [] parameters = method.getParameterTypes(); 329 for (int j = 0; j < parameters.length; j++) { 330 Class parameter = parameters[j]; 331 if (!parameter.isPrimitive()) { 332 codevisitor.visitVarInsn(ALOAD, 1+j); 333 } else if (parameter == Double.TYPE) { 334 codevisitor.visitVarInsn(DLOAD, 1+j); 335 } else if (parameter == Float.TYPE) { 336 codevisitor.visitVarInsn(FLOAD, 1+j); 337 } else if (parameter == Long.TYPE) { 338 codevisitor.visitVarInsn(LLOAD, 1+j); 339 } else { 340 codevisitor.visitVarInsn(ILOAD, 1+j); 341 } 342 } 343 344 codevisitor.visitMethodInsn(INVOKEINTERFACE, 346 Type.getDescriptor(Versionable.class), 347 name, 348 desc); 349 350 Class returnType = method.getReturnType(); 352 if (returnType == Void.TYPE) { 353 codevisitor.visitInsn(RETURN); 354 } else if (returnType.isPrimitive()) { 355 if ( returnType == Double.TYPE) { 356 codevisitor.visitInsn(DRETURN); 357 } else if ( returnType == Float.TYPE) { 358 codevisitor.visitInsn(FRETURN); 359 } else if ( returnType == Long.TYPE ) { 360 codevisitor.visitInsn(LRETURN); 361 } else { 362 codevisitor.visitInsn(IRETURN); 363 } 364 } else { 365 codevisitor.visitInsn(ARETURN); 366 } 367 368 codevisitor.visitMaxs(1 + parameters.length, 1 + parameters.length); 369 } 370 } 371 } 372 373 376 private class SVOCodeAdapter extends CodeAdapter implements Constants { 377 private SVOClassAdapter ca; 378 private boolean isConstructor; 379 private boolean isInitialized; 380 private String methodName; 381 382 public SVOCodeAdapter(String methodName, CodeVisitor cv, SVOClassAdapter ca) { 383 super(cv); 384 this.ca =ca; 385 this.methodName = methodName; 386 isConstructor = methodName.startsWith("<init>"); 387 isInitialized = false; 388 } 389 390 public void visitFieldInsn(int i, String s, String s1, String s2) { 391 if (i == PUTFIELD) { 392 log.debug("visitFieldInsn(" + i + "," + s + "," + s1 + "," + s2 + ")"); 393 ca.addMethod(new MyMethod(methodName(s1), s1, s2)); 394 log.debug("visitMethodInsn(INVOKEVIRTUAL, " + className + "," + methodName(s1) + ", (" + s2 + ")V" + ")"); 395 cv.visitMethodInsn(INVOKEVIRTUAL, className, methodName(s1), "(" + s2 + ")V"); 396 } else { 397 cv.visitFieldInsn(i, s, s1, s2); 398 } 399 } 400 401 public void visitMethodInsn(int i, String s, String s1, String s2) { 402 if (isConstructor && i == INVOKESPECIAL && !isInitialized) { 405 cv.visitMethodInsn(i, s, s1, s2); 406 initVersionable(); 407 } else { 408 super.visitMethodInsn(i, s, s1, s2); 409 } 410 } 411 412 private String methodName(String field) { 413 return "write_" + field; 414 } 415 416 private void initVersionable() { 417 String versionDesc = Type.getDescriptor(Version.class); 418 String versionableDesc = Type.getDescriptor(Versionable.class); 419 String ownerDesc = ca.getInternalName(); 420 421 cv.visitVarInsn(ALOAD, 0); 422 cv.visitTypeInsn(NEW, versionDesc); 423 cv.visitInsn(DUP); 424 cv.visitMethodInsn(INVOKESPECIAL, versionDesc, "<init>", "()V"); 425 cv.visitFieldInsn(PUTFIELD, 426 ownerDesc, 427 VERSIONFIELD, 428 versionableDesc); 429 isInitialized = true; 430 } 431 } 432 433 436 private static class MyMethod { 437 private String name, field, type; 438 public MyMethod(String name, String field, String type) { 439 this.name = name; 440 this.field = field; 441 this.type = type; 442 } 443 444 public String getName() { return name; } 445 public String getType() { return type; } 446 public String getField() { return field; } 447 public String toString() { return "void " + name + "(" + type + " " + field + ")"; } 448 449 public String getTypeInternalName() { 450 if (!type.startsWith("L")) { 451 return null; 452 } 453 return type.substring(1, type.length()-1); 454 } 455 456 public String getTypeClassName() { 457 String internalName = getTypeInternalName(); 458 if (internalName == null) 459 return null; 460 else 461 return internalName.replace('/', '.'); 462 } 463 464 public boolean hasSmartContainer() { 465 String className = getTypeClassName(); 466 if (className == null) 467 return false; 468 else 469 return (SMARTCONTAINERS.containsKey(className)); 470 } 471 472 public boolean equals(Object o) { 473 if (o == null || ! (o instanceof MyMethod)) 474 return false; 475 476 MyMethod m = (MyMethod) o; 477 return (m.getName().equals(this.name) && 478 m.getType().equals(this.type) && 479 m.getField().equals(this.field)); 480 } 481 482 public int hashCode() { 483 return 42 + 484 this.name.hashCode() + 485 this.field.hashCode() + 486 this.type.hashCode(); 487 } 488 } 489 } 490 | Popular Tags |