137
* Reads the input jars (or directories).
131
* Reads the input class files.
139
133
private void readInput() throws IOException
141
135
if (configuration.verbose)
143
System.out.println("Reading jars...");
146
// Check if we have at least some program jars.
147
if (configuration.programJars == null)
149
throw new IOException("The input is empty. You have to specify one or more '-injars' options.");
152
// Read the input program jars.
153
readInput("Reading program ",
154
configuration.programJars,
155
createDataEntryClassPoolFiller(false));
157
// Check if we have at least some input class files.
158
if (programClassPool.size() == 0)
160
throw new IOException("The input doesn't contain any class files. Did you specify the proper '-injars' options?");
163
// Read all library jars.
164
if (configuration.libraryJars != null)
166
readInput("Reading library ",
167
configuration.libraryJars,
168
createDataEntryClassPoolFiller(true));
174
* Creates a DataEntryReader that will decode class files and put them in
175
* the proper class pool.
177
private DataEntryReader createDataEntryClassPoolFiller(boolean isLibrary)
179
// Get the proper class pool.
180
ClassPool classPool = isLibrary ?
184
// Prepare a data entry reader to filter all class files,
185
// which are then decoded to class files by a class file reader,
186
// which are then put in the class pool by a class pool filler.
189
new ClassFileReader(isLibrary,
190
configuration.skipNonPublicLibraryClasses,
191
configuration.skipNonPublicLibraryClassMembers,
193
new ClassPoolFiller(classPool, configuration.note)));
198
* Reads all input entries from the given class path.
200
private void readInput(String messagePrefix,
202
DataEntryReader reader) throws IOException
204
readInput(messagePrefix,
213
* Reads all input entries from the given section of the given class path.
215
private void readInput(String messagePrefix,
219
DataEntryReader reader) throws IOException
221
for (int index = fromIndex; index < toIndex; index++)
223
ClassPathEntry entry = classPath.get(index);
224
if (!entry.isOutput())
226
readInput(messagePrefix, entry, reader);
233
* Reads the given input class path entry.
235
private void readInput(String messagePrefix,
236
ClassPathEntry classPathEntry,
237
DataEntryReader dataEntryReader) throws IOException
241
// Create a reader that can unwrap jars, wars, ears, and zips.
242
DataEntryReader reader =
243
DataEntryReaderFactory.createDataEntryReader(messagePrefix,
247
// Create the data entry pump.
248
DirectoryPump directoryPump =
249
new DirectoryPump(classPathEntry.getFile());
251
// Pump the data entries into the reader.
252
directoryPump.pumpDataEntries(reader);
254
catch (IOException ex)
256
throw new IOException("Can't read [" + classPathEntry + "] (" + ex.getMessage() + ")");
262
* Initializes the cross-references between all class files.
137
System.out.println("Reading input...");
140
// Fill the program class pool and the library class pool.
141
new InputReader(configuration).execute(programClassPool, libraryClassPool);
146
* Initializes the cross-references between all classes, performs some
147
* basic checks, and shrinks the library class pool.
264
149
private void initialize() throws IOException
268
153
System.out.println("Initializing...");
271
int originalLibraryClassPoolSize = libraryClassPool.size();
273
// Initialize the class hierarchy for program class files.
274
ClassFileHierarchyInitializer classFileHierarchyInitializer =
275
new ClassFileHierarchyInitializer(programClassPool,
279
programClassPool.classFilesAccept(classFileHierarchyInitializer);
281
// Initialize the class hierarchy for library class files.
282
ClassFileHierarchyInitializer classFileHierarchyInitializer2 =
283
new ClassFileHierarchyInitializer(programClassPool,
287
libraryClassPool.classFilesAccept(classFileHierarchyInitializer2);
289
// Initialize the Class.forName and .class references.
290
ClassFileClassForNameReferenceInitializer classFileClassForNameReferenceInitializer =
291
new ClassFileClassForNameReferenceInitializer(programClassPool,
294
createNoteExceptionMatcher(configuration.keep));
296
programClassPool.classFilesAccept(
297
new AllMethodVisitor(
298
new AllAttrInfoVisitor(
299
new AllInstructionVisitor(classFileClassForNameReferenceInitializer))));
301
// Initialize the class references from program class members and attributes.
302
ClassFileReferenceInitializer classFileReferenceInitializer =
303
new ClassFileReferenceInitializer(programClassPool,
307
programClassPool.classFilesAccept(classFileReferenceInitializer);
309
// Reinitialize the library class pool with only those library classes
310
// whose hierarchies are referenced by the program classes.
311
ClassPool newLibraryClassPool = new ClassPool();
312
programClassPool.classFilesAccept(
313
new AllCpInfoVisitor(
314
new ReferencedClassFileVisitor(
315
new LibraryClassFileFilter(
316
new ClassFileHierarchyTraveler(true, true, true, false,
317
new LibraryClassFileFilter(
318
new ClassPoolFiller(newLibraryClassPool, false)))))));
320
libraryClassPool = newLibraryClassPool;
322
// Initialize the class references from library class members.
323
ClassFileReferenceInitializer classFileReferenceInitializer2 =
324
new ClassFileReferenceInitializer(programClassPool,
328
libraryClassPool.classFilesAccept(classFileReferenceInitializer2);
330
int noteCount = classFileClassForNameReferenceInitializer.getNoteCount();
333
System.err.println("Note: there were " + noteCount +
334
" class casts of dynamically created class instances.");
335
System.err.println(" You might consider explicitly keeping the mentioned classes and/or");
336
System.err.println(" their implementations (using '-keep').");
339
int hierarchyWarningCount = classFileHierarchyInitializer.getWarningCount();
340
if (hierarchyWarningCount > 0)
342
System.err.println("Warning: there were " + hierarchyWarningCount +
343
" unresolved references to superclasses or interfaces.");
344
System.err.println(" You may need to specify additional library jars (using '-libraryjars'),");
345
System.err.println(" or perhaps the '-dontskipnonpubliclibraryclasses' option.");
348
int referenceWarningCount = classFileReferenceInitializer.getWarningCount();
349
if (referenceWarningCount > 0)
351
System.err.println("Warning: there were " + referenceWarningCount +
352
" unresolved references to program class members.");
353
System.err.println(" Your input class files appear to be inconsistent.");
354
System.err.println(" You may need to recompile them and try again.");
357
if ((hierarchyWarningCount > 0 ||
358
referenceWarningCount > 0) &&
359
!configuration.ignoreWarnings)
361
System.err.println(" If you are sure the mentioned classes are not used anyway,");
362
System.err.println(" you could try your luck using the '-ignorewarnings' option.");
363
throw new IOException("Please correct the above warnings first.");
366
// Discard unused library classes.
367
if (configuration.verbose)
369
System.out.println("Removed unused library classes...");
370
System.out.println(" Original number of library classes: " + originalLibraryClassPoolSize);
371
System.out.println(" Final number of library classes: " + libraryClassPool.size());
377
* Extracts a list of exceptions for which not to print notes, from the
378
* keep configuration.
380
private ClassNameListMatcher createNoteExceptionMatcher(List noteExceptions)
382
if (noteExceptions != null)
384
List noteExceptionNames = new ArrayList(noteExceptions.size());
385
for (int index = 0; index < noteExceptions.size(); index++)
387
ClassSpecification classSpecification = (ClassSpecification)noteExceptions.get(index);
388
if (classSpecification.markClassFiles)
390
// If the class itself is being kept, it's ok.
391
String className = classSpecification.className;
392
if (className != null)
394
noteExceptionNames.add(className);
397
// If all of its extensions are being kept, it's ok too.
398
String extendsClassName = classSpecification.extendsClassName;
399
if (extendsClassName != null)
401
noteExceptionNames.add(extendsClassName);
406
if (noteExceptionNames.size() > 0)
408
return new ClassNameListMatcher(noteExceptionNames);
156
new Initializer(configuration).execute(programClassPool, libraryClassPool);
573
* Writes the output jars.
317
* Writes the output claaa files.
575
319
private void writeOutput() throws IOException
577
321
if (configuration.verbose)
579
System.out.println("Writing jars...");
582
ClassPath programJars = configuration.programJars;
584
// Perform a check on the first jar.
585
ClassPathEntry firstEntry = programJars.get(0);
586
if (firstEntry.isOutput())
588
throw new IOException("The output jar [" + firstEntry.getName() +
589
"] must be specified after an input jar, or it will be empty.");
592
// Perform some checks on the output jars.
593
for (int index = 0; index < programJars.size() - 1; index++)
595
ClassPathEntry entry = programJars.get(index);
596
if (entry.isOutput())
598
// Check if all but the last output jars have filters.
599
if (entry.getFilter() == null &&
600
entry.getJarFilter() == null &&
601
entry.getWarFilter() == null &&
602
entry.getEarFilter() == null &&
603
entry.getZipFilter() == null &&
604
programJars.get(index + 1).isOutput())
606
throw new IOException("The output jar [" + entry.getName() +
607
"] must have a filter, or all subsequent jars will be empty.");
610
// Check if the output jar name is different from the input jar names.
611
for (int inIndex = 0; inIndex < programJars.size(); inIndex++)
613
ClassPathEntry otherEntry = programJars.get(inIndex);
615
if (!otherEntry.isOutput() &&
616
entry.getFile().equals(otherEntry.getFile()))
618
throw new IOException("The output jar [" + entry.getName() +
619
"] must be different from all input jars.");
625
int firstInputIndex = 0;
626
int lastInputIndex = 0;
628
// Go over all program class path entries.
629
for (int index = 0; index < programJars.size(); index++)
631
// Is it an input entry?
632
ClassPathEntry entry = programJars.get(index);
633
if (!entry.isOutput())
635
// Remember the index of the last input entry.
636
lastInputIndex = index;
640
// Check if this the last output entry in a series.
641
int nextIndex = index + 1;
642
if (nextIndex == programJars.size() ||
643
!programJars.get(nextIndex).isOutput())
645
// Write the processed input entries to the output entries.
646
writeOutput(programJars,
651
// Start with the next series of input entries.
652
firstInputIndex = nextIndex;
662
* Transfers the specified input jars to the specified output jars.
664
private void writeOutput(ClassPath classPath,
672
// Construct the writer that can write jars, wars, ears, zips, and
673
// directories, cascading over the specified output entries.
674
DataEntryWriter writer =
675
DataEntryWriterFactory.createDataEntryWriter(classPath,
679
// Create the reader that can write class files and copy resource
680
// files to the above writer.
681
DataEntryReader reader =
682
new ClassFileFilter(new ClassFileRewriter(programClassPool,
684
new DataEntryCopier(writer));
686
// Read and handle the specified input entries.
687
readInput(" Copying resources from program ",
693
// Close all output entries.
696
catch (IOException ex)
698
throw new IOException("Can't write [" + classPath.get(fromOutputIndex).getName() + "] (" + ex.getMessage() + ")");
323
System.out.println("Writing output...");
326
// Write out the program class pool.
327
new OutputWriter(configuration).execute(programClassPool);