PageRenderTime 59ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/proguard-4.9/src/proguard/OutputWriter.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 296 lines | 192 code | 36 blank | 68 comment | 32 complexity | 9de005ea14737c54b19abb69b05f4ec7 MD5 | raw file
  1. /*
  2. * ProGuard -- shrinking, optimization, obfuscation, and preverification
  3. * of Java bytecode.
  4. *
  5. * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu)
  6. *
  7. * This program is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License as published by the Free
  9. * Software Foundation; either version 2 of the License, or (at your option)
  10. * any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful, but WITHOUT
  13. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  15. * more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program; if not, write to the Free Software Foundation, Inc.,
  19. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20. */
  21. package proguard;
  22. import proguard.classfile.ClassPool;
  23. import proguard.classfile.util.ClassUtil;
  24. import proguard.io.*;
  25. import java.io.IOException;
  26. import java.util.*;
  27. /**
  28. * This class writes the output class files.
  29. *
  30. * @author Eric Lafortune
  31. */
  32. public class OutputWriter
  33. {
  34. private final Configuration configuration;
  35. /**
  36. * Creates a new OutputWriter to write output class files as specified by
  37. * the given configuration.
  38. */
  39. public OutputWriter(Configuration configuration)
  40. {
  41. this.configuration = configuration;
  42. }
  43. /**
  44. * Writes the given class pool to class files, based on the current
  45. * configuration.
  46. */
  47. public void execute(ClassPool programClassPool) throws IOException
  48. {
  49. ClassPath programJars = configuration.programJars;
  50. // Perform a check on the first jar.
  51. ClassPathEntry firstEntry = programJars.get(0);
  52. if (firstEntry.isOutput())
  53. {
  54. throw new IOException("The output jar [" + firstEntry.getName() +
  55. "] must be specified after an input jar, or it will be empty.");
  56. }
  57. // Check if the first of two subsequent the output jars has a filter.
  58. for (int index = 0; index < programJars.size() - 1; index++)
  59. {
  60. ClassPathEntry entry = programJars.get(index);
  61. if (entry.isOutput())
  62. {
  63. if (entry.getFilter() == null &&
  64. entry.getJarFilter() == null &&
  65. entry.getWarFilter() == null &&
  66. entry.getEarFilter() == null &&
  67. entry.getZipFilter() == null &&
  68. programJars.get(index + 1).isOutput())
  69. {
  70. throw new IOException("The output jar [" + entry.getName() +
  71. "] must have a filter, or all subsequent output jars will be empty.");
  72. }
  73. }
  74. }
  75. // Check if the output jar names are different from the input jar names.
  76. for (int outIndex = 0; outIndex < programJars.size() - 1; outIndex++)
  77. {
  78. ClassPathEntry entry = programJars.get(outIndex);
  79. if (entry.isOutput())
  80. {
  81. for (int inIndex = 0; inIndex < programJars.size(); inIndex++)
  82. {
  83. ClassPathEntry otherEntry = programJars.get(inIndex);
  84. if (!otherEntry.isOutput() &&
  85. entry.getFile().equals(otherEntry.getFile()))
  86. {
  87. throw new IOException("The output jar [" + entry.getName() +
  88. "] must be different from all input jars.");
  89. }
  90. }
  91. }
  92. }
  93. // Check for potential problems with mixed-case class names on
  94. // case-insensitive file systems.
  95. if (configuration.obfuscate &&
  96. configuration.useMixedCaseClassNames &&
  97. configuration.classObfuscationDictionary == null &&
  98. (configuration.note == null ||
  99. !configuration.note.isEmpty()))
  100. {
  101. String os = System.getProperty("os.name").toLowerCase();
  102. if (os.startsWith("windows") ||
  103. os.startsWith("mac os"))
  104. {
  105. // Go over all program class path entries.
  106. for (int index = 0; index < programJars.size(); index++)
  107. {
  108. // Is it an output directory?
  109. ClassPathEntry entry = programJars.get(index);
  110. if (entry.isOutput() &&
  111. !entry.isJar() &&
  112. !entry.isWar() &&
  113. !entry.isEar() &&
  114. !entry.isZip())
  115. {
  116. System.out.println("Note: you're writing the processed class files to a directory [" + entry.getName() +"].");
  117. System.out.println(" This will likely cause problems with obfuscated mixed-case class names.");
  118. System.out.println(" You should consider writing the output to a jar file, or otherwise");
  119. System.out.println(" specify '-dontusemixedcaseclassnames'.");
  120. break;
  121. }
  122. }
  123. }
  124. }
  125. int firstInputIndex = 0;
  126. int lastInputIndex = 0;
  127. // Go over all program class path entries.
  128. for (int index = 0; index < programJars.size(); index++)
  129. {
  130. // Is it an input entry?
  131. ClassPathEntry entry = programJars.get(index);
  132. if (!entry.isOutput())
  133. {
  134. // Remember the index of the last input entry.
  135. lastInputIndex = index;
  136. }
  137. else
  138. {
  139. // Check if this the last output entry in a series.
  140. int nextIndex = index + 1;
  141. if (nextIndex == programJars.size() ||
  142. !programJars.get(nextIndex).isOutput())
  143. {
  144. // Write the processed input entries to the output entries.
  145. writeOutput(programClassPool,
  146. programJars,
  147. firstInputIndex,
  148. lastInputIndex + 1,
  149. nextIndex);
  150. // Start with the next series of input entries.
  151. firstInputIndex = nextIndex;
  152. }
  153. }
  154. }
  155. }
  156. /**
  157. * Transfers the specified input jars to the specified output jars.
  158. */
  159. private void writeOutput(ClassPool programClassPool,
  160. ClassPath classPath,
  161. int fromInputIndex,
  162. int fromOutputIndex,
  163. int toOutputIndex)
  164. throws IOException
  165. {
  166. try
  167. {
  168. // Construct the writer that can write jars, wars, ears, zips, and
  169. // directories, cascading over the specified output entries.
  170. DataEntryWriter writer =
  171. DataEntryWriterFactory.createDataEntryWriter(classPath,
  172. fromOutputIndex,
  173. toOutputIndex);
  174. // The writer will be used to write possibly obfuscated class files.
  175. DataEntryReader classRewriter =
  176. new ClassRewriter(programClassPool, writer);
  177. // The writer will also be used to write resource files.
  178. DataEntryReader resourceCopier =
  179. new DataEntryCopier(writer);
  180. DataEntryReader resourceRewriter = resourceCopier;
  181. // Wrap the resource writer with a filter and a data entry rewriter,
  182. // if required.
  183. if (configuration.adaptResourceFileContents != null)
  184. {
  185. resourceRewriter =
  186. new NameFilter(configuration.adaptResourceFileContents,
  187. new NameFilter("META-INF/MANIFEST.MF,META-INF/*.SF",
  188. new ManifestRewriter(programClassPool, writer),
  189. new DataEntryRewriter(programClassPool, writer)),
  190. resourceRewriter);
  191. }
  192. // Wrap the resource writer with a filter and a data entry renamer,
  193. // if required.
  194. if (configuration.adaptResourceFileNames != null)
  195. {
  196. Map packagePrefixMap = createPackagePrefixMap(programClassPool);
  197. resourceRewriter =
  198. new NameFilter(configuration.adaptResourceFileNames,
  199. new DataEntryObfuscator(programClassPool,
  200. packagePrefixMap,
  201. resourceRewriter),
  202. resourceRewriter);
  203. }
  204. DataEntryReader directoryRewriter = null;
  205. // Wrap the directory writer with a filter and a data entry renamer,
  206. // if required.
  207. if (configuration.keepDirectories != null)
  208. {
  209. Map packagePrefixMap = createPackagePrefixMap(programClassPool);
  210. directoryRewriter =
  211. new NameFilter(configuration.keepDirectories,
  212. new DataEntryRenamer(packagePrefixMap,
  213. resourceCopier,
  214. resourceCopier));
  215. }
  216. // Create the reader that can write class files and copy directories
  217. // and resource files to the main writer.
  218. DataEntryReader reader =
  219. new ClassFilter( classRewriter,
  220. new DirectoryFilter(directoryRewriter,
  221. resourceRewriter));
  222. // Go over the specified input entries and write their processed
  223. // versions.
  224. new InputReader(configuration).readInput(" Copying resources from program ",
  225. classPath,
  226. fromInputIndex,
  227. fromOutputIndex,
  228. reader);
  229. // Close all output entries.
  230. writer.close();
  231. }
  232. catch (IOException ex)
  233. {
  234. throw (IOException)new IOException("Can't write [" + classPath.get(fromOutputIndex).getName() + "] (" + ex.getMessage() + ")").initCause(ex);
  235. }
  236. }
  237. /**
  238. * Creates a map of old package prefixes to new package prefixes, based on
  239. * the given class pool.
  240. */
  241. private static Map createPackagePrefixMap(ClassPool classPool)
  242. {
  243. Map packagePrefixMap = new HashMap();
  244. Iterator iterator = classPool.classNames();
  245. while (iterator.hasNext())
  246. {
  247. String className = (String)iterator.next();
  248. String packagePrefix = ClassUtil.internalPackagePrefix(className);
  249. String mappedNewPackagePrefix = (String)packagePrefixMap.get(packagePrefix);
  250. if (mappedNewPackagePrefix == null ||
  251. !mappedNewPackagePrefix.equals(packagePrefix))
  252. {
  253. String newClassName = classPool.getClass(className).getName();
  254. String newPackagePrefix = ClassUtil.internalPackagePrefix(newClassName);
  255. packagePrefixMap.put(packagePrefix, newPackagePrefix);
  256. }
  257. }
  258. return packagePrefixMap;
  259. }
  260. }