Home | History | Annotate | Download | only in instr
      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 
     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 // ----------------------------------------------------------------------------