InstrProcessor.java revision a921fd048da6858dc24d4370e3bba15fc9cc69ca
1/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. 2 * 3 * This program and the accompanying materials are made available under 4 * the terms of the Common Public License v1.0 which accompanies this distribution, 5 * and is available at http://www.eclipse.org/legal/cpl-v10.html 6 * 7 * $Id: InstrProcessor.java,v 1.1.1.1.2.3 2004/07/17 16:57:14 vlad_r Exp $ 8 */ 9package com.vladium.emma.instr; 10 11import java.io.File; 12 13import com.vladium.util.Files; 14import com.vladium.util.IConstants; 15import com.vladium.util.IPathEnumerator; 16import com.vladium.util.asserts.$assert; 17import com.vladium.emma.IAppErrorCodes; 18import com.vladium.emma.EMMARuntimeException; 19import com.vladium.emma.Processor; 20import com.vladium.emma.filter.IInclExclFilter; 21 22// ---------------------------------------------------------------------------- 23/* 24 * This class was not meant to be public by design. It is made to to work around 25 * access bugs in reflective invocations. 26 */ 27/** 28 * @author Vlad Roubtsov, (C) 2003 29 */ 30public 31abstract class InstrProcessor extends Processor 32 implements IPathEnumerator.IPathHandler 33{ 34 // public: ................................................................ 35 36 37 public static final String PROPERTY_EXCLUDE_SYNTHETIC_METHODS = "instr.exclude_synthetic_methods"; 38 public static final String PROPERTY_EXCLUDE_BRIDGE_METHODS = "instr.exclude_bridge_methods"; 39 public static final String PROPERTY_DO_SUID_COMPENSATION = "instr.do_suid_compensation"; 40 41 public static final String DEFAULT_EXCLUDE_SYNTHETIC_METHODS = "true"; 42 public static final String DEFAULT_EXCLUDE_BRIDGE_METHODS = "true"; 43 public static final String DEFAULT_DO_SUID_COMPENSATION = "true"; 44 45 public static InstrProcessor create () 46 { 47 return new InstrProcessorST (); 48 } 49 50 /** 51 * 52 * @param path [null is equivalent to an empty array] 53 * @param canonical 54 */ 55 public synchronized final void setInstrPath (final String [] path, final boolean canonical) 56 { 57 if ((path == null) || (path.length == 0)) 58 m_instrPath = IConstants.EMPTY_FILE_ARRAY; 59 else 60 m_instrPath = Files.pathToFiles (path, canonical); 61 62 m_canonical = canonical; 63 } 64 65 public synchronized final void setDependsMode (final boolean enable) 66 { 67 m_dependsMode = enable; 68 } 69 70 /** 71 * 72 * @param specs [null is equivalent to no filtering (everything is included)] 73 */ 74 public synchronized final void setInclExclFilter (final String [] specs) 75 { 76 if (specs == null) 77 m_coverageFilter = null; 78 else 79 m_coverageFilter = IInclExclFilter.Factory.create (specs); 80 } 81 82 /** 83 * 84 * @param fileName [null unsets the previous override setting] 85 */ 86 public synchronized final void setMetaOutFile (final String fileName) 87 { 88 if (fileName == null) 89 m_mdataOutFile = null; 90 else 91 { 92 final File _file = new File (fileName); 93 94 if (_file.exists () && ! _file.isFile ()) 95 throw new IllegalArgumentException ("not a file: [" + _file.getAbsolutePath () + "]"); 96 97 m_mdataOutFile = _file; 98 } 99 } 100 101 /** 102 * 103 * @param merge [null unsets the previous override setting] 104 */ 105 public synchronized final void setMetaOutMerge (final Boolean merge) 106 { 107 m_mdataOutMerge = merge; 108 } 109 110 /** 111 * 112 * @param dir [null unsets the previous setting] 113 */ 114 public synchronized final void setInstrOutDir (final String dir) 115 { 116 if (dir == null) 117 m_outDir = null; 118 else 119 { 120 final File _outDir = new File (dir); 121 if (_outDir.exists () && ! _outDir.isDirectory ()) 122 throw new IllegalArgumentException ("not a directory: [" + _outDir.getAbsolutePath () + "]"); 123 124 m_outDir = _outDir; 125 } 126 } 127 128 /** 129 * 130 * @param mode [may not be null] 131 */ 132 public synchronized final void setOutMode (final OutMode mode) 133 { 134 if (mode == null) 135 throw new IllegalArgumentException ("null input: mode"); 136 137 m_outMode = mode; 138 } 139 140 // protected: ............................................................. 141 142 143 protected InstrProcessor () 144 { 145 m_dependsMode = true; 146 } 147 148 149 protected void validateState () 150 { 151 super.validateState (); 152 153 if ((m_instrPath == null) || (m_instrPath.length == 0)) 154 throw new IllegalStateException ("instrumentation path not set"); 155 156 // [m_coverageFilter can be null] 157 158 if (m_outMode == null) 159 throw new IllegalStateException ("output mode not set"); 160 161 if (m_outMode != OutMode.OUT_MODE_OVERWRITE) 162 { 163 if (m_outDir == null) 164 throw new IllegalStateException ("output directory not set"); 165 166 // for non-overwrite modes output directory must not overlap 167 // with the instr path: 168 169 // [the logic below does not quite catch all possibilities due to 170 // Class-Path: manifest attributes and dir nesting, but it should 171 // intercept most common mistakes] 172 173 if ($assert.ENABLED) 174 { 175 $assert.ASSERT (m_outDir != null, "m_outDir = null"); 176 $assert.ASSERT (m_instrPath != null, "m_instrPath = null"); 177 } 178 179 final File canonicalOutDir = Files.canonicalizeFile (m_outDir); 180 final File [] canonicalInstrPath; 181 182 if (m_canonical) 183 canonicalInstrPath = m_instrPath; 184 else 185 { 186 canonicalInstrPath = new File [m_instrPath.length]; 187 188 for (int ip = 0; ip < canonicalInstrPath.length; ++ ip) 189 { 190 canonicalInstrPath [ip] = Files.canonicalizeFile (m_instrPath [ip]); 191 } 192 } 193 194 // FR_SF988785: detect if the user attempted to use a parent of m_outDir as one of 195 // the input directories (prevents spurious "already instrumented" errors) 196 197 final int instrPathLength = canonicalInstrPath.length; 198 for (File dir = canonicalOutDir; dir != null; dir = dir.getParentFile ()) // getParentFile() does no real I/O 199 { 200 for (int ip = 0; ip < instrPathLength; ++ ip) 201 { 202 if (dir.equals (canonicalInstrPath [ip])) 203 throw new IllegalStateException ("output directory [" + canonicalOutDir + "] cannot be one of the instrumentation path directories (or a child thereof)"); 204 } 205 } 206 } 207 208 // [m_mdataOutFile can be null] 209 // [m_mdataOutMerge can be null] 210 } 211 212 protected void reset () 213 { 214 m_classCopies = m_classInstrs = 0; 215 } 216 217 protected final void createDir (final File dir, final boolean mkall) 218 throws EMMARuntimeException 219 { 220 if (mkall) 221 { 222 if (! dir.mkdirs () && ! dir.exists ()) 223 throw new EMMARuntimeException (IAppErrorCodes.OUT_MKDIR_FAILURE, new Object [] {dir.getAbsolutePath ()}); 224 } 225 else 226 { 227 if (! dir.mkdir () && ! dir.exists ()) 228 throw new EMMARuntimeException (IAppErrorCodes.OUT_MKDIR_FAILURE, new Object [] {dir.getAbsolutePath ()}); 229 } 230 } 231 232 protected final File getFullOutDir (final File pathDir, final boolean isClass) 233 { 234 if (m_outMode == OutMode.OUT_MODE_OVERWRITE) 235 { 236 return pathDir; 237 } 238 else if (m_outMode == OutMode.OUT_MODE_COPY) 239 { 240 return m_outDir; 241 } 242 else if (m_outMode == OutMode.OUT_MODE_FULLCOPY) 243 { 244 return isClass ? Files.newFile (m_outDir, CLASSES) : Files.newFile (m_outDir, LIB); 245 } 246 else throw new IllegalStateException ("invalid out mode state: " + m_outMode); 247 } 248 249 protected final File getFullOutFile (final File pathDir, final File file, final boolean isClass) 250 { 251 return Files.newFile (getFullOutDir (pathDir, isClass), file.getPath ()); 252 } 253 254 255 // caller-settable state [scoped to this runner instance]: 256 257 protected File [] m_instrPath; // required to be non-null/non-empty for run() 258 protected boolean m_dependsMode; 259 protected boolean m_canonical; 260 protected IInclExclFilter m_coverageFilter; // can be null for run() 261 protected OutMode m_outMode; // required to be set for run() 262 protected File m_outDir; // required to be non-null for run(), unless output mode is 'overwrite' 263 protected File m_mdataOutFile; // user override; can be null for run() 264 protected Boolean m_mdataOutMerge; // user override; can be null for run() 265 266 // internal run()-scoped state: 267 268 protected int m_classCopies, m_classInstrs; 269 270 protected static final String CLASSES = "classes"; 271 protected static final String LIB = "lib"; 272 protected static final boolean IN_CLASSES = true; 273 protected static final boolean IN_LIB = ! IN_CLASSES; 274 275 // package: ............................................................... 276 277// TODO: access level [public to workaround Sun's bugs in access level in reflective invocations] 278 public static final class OutMode 279 { 280 public static final OutMode OUT_MODE_COPY = new OutMode ("copy"); 281 public static final OutMode OUT_MODE_FULLCOPY = new OutMode ("fullcopy"); 282 public static final OutMode OUT_MODE_OVERWRITE = new OutMode ("overwrite"); 283 284 public String getName () 285 { 286 return m_name; 287 } 288 289 public String toString () 290 { 291 return m_name; 292 } 293 294 public static OutMode nameToMode (final String name) 295 { 296 if (OUT_MODE_COPY.m_name.equals (name)) 297 return OUT_MODE_COPY; 298 else if (OUT_MODE_FULLCOPY.m_name.equals (name)) 299 return OUT_MODE_FULLCOPY; 300 else if (OUT_MODE_OVERWRITE.m_name.equals (name)) 301 return OUT_MODE_OVERWRITE; 302 303 return null; 304 } 305 306 private OutMode (final String name) 307 { 308 m_name = name; 309 } 310 311 312 private final String m_name; 313 314 } // end of nested class 315 316 // private: ............................................................... 317 318} // end of class 319// ----------------------------------------------------------------------------