• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
9 package com.vladium.emma.instr;
10 
11 import java.io.File;
12 
13 import com.vladium.util.Files;
14 import com.vladium.util.IConstants;
15 import com.vladium.util.IPathEnumerator;
16 import com.vladium.util.asserts.$assert;
17 import com.vladium.emma.IAppErrorCodes;
18 import com.vladium.emma.EMMARuntimeException;
19 import com.vladium.emma.Processor;
20 import 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  */
30 public
31 abstract 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 
create()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      */
setInstrPath(final String [] path, final boolean canonical)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 
setDependsMode(final boolean enable)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      */
setInclExclFilter(final String [] specs)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      */
setMetaOutFile(final String fileName)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      */
setMetaOutMerge(final Boolean merge)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      */
setInstrOutDir(final String dir)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      */
setOutMode(final OutMode mode)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 
InstrProcessor()143     protected InstrProcessor ()
144     {
145         m_dependsMode = true;
146     }
147 
148 
validateState()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 
reset()212     protected void reset ()
213     {
214         m_classCopies = m_classInstrs = 0;
215     }
216 
createDir(final File dir, final boolean mkall)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 
getFullOutDir(final File pathDir, final boolean isClass)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 
getFullOutFile(final File pathDir, final File file, final boolean isClass)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 
getName()284         public String getName ()
285         {
286             return m_name;
287         }
288 
toString()289         public String toString ()
290         {
291             return m_name;
292         }
293 
nameToMode(final String name)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 
OutMode(final String name)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 // ----------------------------------------------------------------------------