1 26 27 package net.sourceforge.groboutils.codecoverage.v2.compiler; 28 29 import java.io.ByteArrayInputStream ; 30 import java.io.ByteArrayOutputStream ; 31 import java.io.IOException ; 32 import java.util.ArrayList ; 33 import java.util.HashMap ; 34 import java.util.List ; 35 import java.util.Map ; 36 37 import net.sourceforge.groboutils.codecoverage.v2.datastore.AnalysisModuleSet; 38 import net.sourceforge.groboutils.codecoverage.v2.datastore.ClassRecord; 39 import net.sourceforge.groboutils.codecoverage.v2.util.ChecksumUtil; 40 import net.sourceforge.groboutils.codecoverage.v2.util.ClassSignatureUtil; 41 42 import org.apache.bcel.classfile.Attribute; 43 import org.apache.bcel.classfile.ClassParser; 44 import org.apache.bcel.classfile.JavaClass; 45 import org.apache.bcel.classfile.Method; 46 import org.apache.bcel.classfile.SourceFile; 47 import org.apache.bcel.generic.ConstantPoolGen; 48 import org.apache.bcel.generic.MethodGen; 49 50 58 public class ModifiedClass 59 { 60 private static final org.apache.log4j.Logger LOG = 61 org.apache.log4j.Logger.getLogger( ModifiedClass.class ); 62 63 private static final String ALREADY_POST_COMPILED_FLAG_1 = 66 "--..AlreadyPostCompiled"; 67 private static final String ALREADY_POST_COMPILED_FLAG_2 = 68 "..--" + (char)1; 71 private static final String ALREADY_POST_COMPILED_FLAG; 72 static { 73 StringBuffer sb = new StringBuffer ( ALREADY_POST_COMPILED_FLAG_1 ); 76 sb.append( ALREADY_POST_COMPILED_FLAG_2 ); 77 ALREADY_POST_COMPILED_FLAG = new String ( sb ); 78 } 79 80 private String className; 81 private JavaClass origClass; 82 private ConstantPoolGen constantPool; 84 private ModifiedMethod[] modMethods; 85 private byte[] finalModifiedClass; 86 87 90 private int classSigPoolIndex; 91 92 95 private int staticMethodPoolIndex; 96 97 100 private long checksum; 101 102 106 private static final String [] IGNORE_METHODS = { 107 "class$(Ljava/lang/String;)Ljava/lang/Class;", 108 }; 109 110 111 119 public ModifiedClass( String filename, byte[] originalClassFile ) 120 throws IOException 121 { 122 this( new ParseCoverageLogger(), filename, originalClassFile ); 123 } 124 125 126 134 public ModifiedClass( ParseCoverageLogger pcl, String filename, 135 byte[] originalClassFile ) 136 throws IOException 137 { 138 if (originalClassFile == null || filename == null || pcl == null) 139 { 140 throw new IllegalArgumentException ( "No null args." ); 141 } 142 143 updateChecksum( originalClassFile ); 144 updateClassGen( pcl, originalClassFile, filename ); 145 } 146 147 148 151 public String getClassName() 152 { 153 return this.className; 154 } 155 156 157 160 public long getClassCRC() 161 { 162 return this.checksum; 163 } 164 165 166 169 public String getClassSignature() 170 { 171 return ClassSignatureUtil.getInstance(). 172 createClassSignature( getClassName(), getClassCRC() ); 173 } 174 175 176 179 public String getSourceFileName() 180 { 181 String ret = ""; 182 Attribute attr[] = this.origClass.getAttributes(); 183 for (int i = 0; i < attr.length; ++i) 184 { 185 if (attr[i] instanceof SourceFile) 186 { 187 ret = ((SourceFile)attr[i]).getSourceFileName(); 188 break; 189 } 190 } 191 return ret; 192 } 193 194 195 198 public ClassRecord createClassRecord( AnalysisModuleSet ams ) 199 { 200 ModifiedMethod mmL[] = getMethods(); 201 String methodSigs[] = new String [ mmL.length ]; 202 for (int i = 0; i < mmL.length; ++i) 203 { 204 methodSigs[ mmL[i].getMethodIndex() ] = mmL[i].getMethodName(); 205 } 206 return new ClassRecord( getClassName(), getClassCRC(), 207 getSourceFileName(), methodSigs, ams ); 208 } 209 210 211 216 public ModifiedMethod[] getMethods() 217 { 218 if (this.modMethods == null) 219 { 220 checkClose(); 221 222 Method mL[] = this.origClass.getMethods(); 223 List methods = new ArrayList (); 224 short methodIndex = 0; 225 for (int i = 0; i < mL.length; ++i) 226 { 227 if (allowModification( mL[i] )) 228 { 229 ModifiedMethod mm = new ModifiedMethod( 230 methodIndex, 231 this.classSigPoolIndex, 232 this.staticMethodPoolIndex, 233 this.origClass, 234 mL[i], 235 new MethodGen( mL[i], this.className, this.constantPool ) 236 ); 237 methods.add( mm ); 238 ++methodIndex; 239 } 240 } 241 this.modMethods = (ModifiedMethod[])methods.toArray( 242 new ModifiedMethod[ methods.size() ] ); 243 } 244 return this.modMethods; 245 } 246 247 248 252 public byte[] getModifiedClass() 253 { 254 if (this.finalModifiedClass == null) 255 { 256 checkClose(); 257 258 updateClass(); 260 261 ByteArrayOutputStream baos = new ByteArrayOutputStream (); 262 try 263 { 264 this.origClass.dump( baos ); 265 } 266 catch (IOException e) 267 { 268 throw new IllegalStateException ("This should never be reached."); 269 } 270 271 this.origClass = null; 273 274 this.finalModifiedClass = baos.toByteArray(); 275 } 276 return this.finalModifiedClass; 277 } 278 279 280 284 public static final String getPostCompiledSignature() 285 { 286 return ALREADY_POST_COMPILED_FLAG; 287 } 288 289 290 293 294 299 private void updateClass() 300 { 301 ModifiedMethod mL[] = this.modMethods; 303 if (mL == null) 304 { 305 return; 308 } 309 310 this.modMethods = null; 312 313 Map mLmap = new HashMap (); 315 for (int i = 0; i < mL.length; ++i) 316 { 317 ModifiedMethod m = mL[i]; 319 mL[i] = null; 320 m.close(); 321 322 mLmap.put( m.getMethodName(), m.getNewMethod() ); 323 } 324 325 Method origMethods[] = this.origClass.getMethods(); 327 for (int i = 0; i < origMethods.length; ++i) 328 { 329 Method m = (Method)mLmap.get( createSignature( origMethods[i] ) ); 330 if (m != null) 331 { 332 LOG.debug( "replacing method ["+origMethods[i]+"]." ); 333 origMethods[i] = m; 334 } 335 } 336 this.origClass.setMethods( origMethods ); 337 338 this.origClass.setConstantPool( 339 this.constantPool.getFinalConstantPool() ); 340 } 341 342 343 346 private void updateClassGen( ParseCoverageLogger pcl, byte[] code, 347 String filename ) 348 throws IOException 349 { 350 ByteArrayInputStream bais = new ByteArrayInputStream ( code ); 351 ClassParser cp = new ClassParser( bais, filename ); 352 this.origClass = cp.parse(); 353 this.className = this.origClass.getClassName(); 354 this.constantPool = new ConstantPoolGen( 358 this.origClass.getConstantPool() ); 359 360 addClassSignature( this.constantPool ); 361 addMethodRef( pcl, this.constantPool ); 362 } 363 364 365 366 372 private void addClassSignature( ConstantPoolGen pool ) 373 { 374 if (pool == null) 375 { 376 throw new IllegalArgumentException ( "No null args." ); 377 } 378 379 if (pool.lookupUtf8( getPostCompiledSignature() ) != -1) 382 { 383 throw new AlreadyPostCompiledException( 384 "Class '"+this.className+"' has already been modified." ); 385 } 386 387 this.classSigPoolIndex = pool.addString( getClassSignature() ); 389 LOG.debug( "Added pool index "+this.classSigPoolIndex+" pointing to '"+ 390 getClassSignature() ); 391 392 pool.addString( getPostCompiledSignature() ); 394 } 395 396 397 398 401 private void addMethodRef( ParseCoverageLogger pcl, ConstantPoolGen pool ) 402 { 403 if (pool == null || pcl == null) 404 { 405 throw new IllegalArgumentException ( "No null args." ); 406 } 407 408 LOG.debug( "Adding methodref to pool for method: "+ 409 pcl.getClassName()+" # "+pcl.getMethodName() + ": "+ 410 pcl.getMethodSignature() ); 411 this.staticMethodPoolIndex = pool.addMethodref( 412 pcl.getClassName(), 413 pcl.getMethodName(), 414 pcl.getMethodSignature() ); 415 LOG.debug( "Methodref pool index = "+this.staticMethodPoolIndex ); 416 } 417 418 419 422 private void updateChecksum( byte[] code ) 423 { 424 if (code == null) 425 { 426 throw new IllegalArgumentException ("no null args"); 427 } 428 this.checksum = ChecksumUtil.getInstance().checksum( code ); 429 } 430 431 432 435 private void checkClose() 436 { 437 if (this.origClass == null) 438 { 439 throw new IllegalStateException ( 440 "Class has been closed from further modifications." ); 441 } 442 } 443 444 445 451 private boolean allowModification( Method m ) 452 { 453 if (!isMarkable( m )) 454 { 455 return false; 456 } 457 458 String sig = createSignature( m ); 459 for (int i = 0; i < IGNORE_METHODS.length; ++i) 460 { 461 if (IGNORE_METHODS[i].equals( sig )) 462 { 463 return false; 464 } 465 } 466 return true; 467 } 468 469 470 474 static boolean isMarkable( Method m ) 475 { 476 return (!m.isNative() && !m.isAbstract() && m.getCode() != null); 477 } 478 479 480 483 private String createSignature( Method m ) 484 { 485 String sig = m.getName() + m.getSignature(); 486 return sig; 487 } 488 } 489 490 | Popular Tags |