| // Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| package com.android.tools.r8.utils; |
| |
| import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION; |
| import static com.android.tools.r8.utils.FileUtils.isArchive; |
| |
| import com.android.tools.r8.ClassFileResourceProvider; |
| import com.android.tools.r8.DiagnosticsHandler; |
| import com.android.tools.r8.ProgramResource; |
| import com.android.tools.r8.ProgramResource.Kind; |
| import com.android.tools.r8.errors.CompilationError; |
| import com.android.tools.r8.origin.ArchiveEntryOrigin; |
| import com.android.tools.r8.origin.Origin; |
| import com.android.tools.r8.origin.PathOrigin; |
| import com.google.common.io.ByteStreams; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Files; |
| import java.nio.file.NoSuchFileException; |
| import java.nio.file.Path; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashSet; |
| import java.util.Set; |
| import java.util.function.Predicate; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipFile; |
| |
| /** |
| * Internal-only provider for providing class files when clients use the addClasspathFiles and |
| * addLibraryFiles API. |
| * |
| * <p>The purpose of the internal provider is that for the API use above we know it is safe to use |
| * the zip-file descriptor throughout compilation and close at the end of reading. It must also be |
| * safe to reopen it as currently our own tests reuse AndroidApp structures. |
| */ |
| class InternalArchiveClassFileProvider implements ClassFileResourceProvider, AutoCloseable { |
| private final Path path; |
| private final Origin origin; |
| private final Set<String> descriptors = new HashSet<>(); |
| |
| private ZipFile openedZipFile = null; |
| |
| /** |
| * Creates a lazy class-file program-resource provider. |
| * |
| * @param archive Zip archive to provide resources from. |
| */ |
| public InternalArchiveClassFileProvider(Path archive) throws IOException { |
| this(archive, entry -> true); |
| } |
| |
| /** |
| * Creates a lazy class-file program-resource provider with an include filter. |
| * |
| * @param archive Zip archive to provide resources from. |
| * @param include Predicate deciding if a given class-file entry should be provided. |
| */ |
| public InternalArchiveClassFileProvider(Path archive, Predicate<String> include) |
| throws IOException { |
| assert isArchive(archive); |
| path = archive; |
| origin = new PathOrigin(archive); |
| final Enumeration<? extends ZipEntry> entries = getOpenZipFile().entries(); |
| while (entries.hasMoreElements()) { |
| ZipEntry entry = entries.nextElement(); |
| String name = entry.getName(); |
| if (ZipUtils.isClassFile(name) && include.test(name)) { |
| descriptors.add(DescriptorUtils.guessTypeDescriptor(name)); |
| } |
| } |
| } |
| |
| @Override |
| public Set<String> getClassDescriptors() { |
| return Collections.unmodifiableSet(descriptors); |
| } |
| |
| @Override |
| public ProgramResource getProgramResource(String descriptor) { |
| if (!descriptors.contains(descriptor)) { |
| return null; |
| } |
| try { |
| ZipEntry zipEntry = getZipEntryFromDescriptor(descriptor); |
| try (InputStream inputStream = getOpenZipFile().getInputStream(zipEntry)) { |
| return ProgramResource.fromBytes( |
| new ArchiveEntryOrigin(zipEntry.getName(), origin), |
| Kind.CF, |
| ByteStreams.toByteArray(inputStream), |
| Collections.singleton(descriptor)); |
| } |
| } catch (IOException e) { |
| throw new CompilationError("Failed to read '" + descriptor, origin); |
| } |
| } |
| |
| private ZipFile getOpenZipFile() throws IOException { |
| if (openedZipFile == null) { |
| try { |
| openedZipFile = FileUtils.createZipFile(path.toFile(), StandardCharsets.UTF_8); |
| } catch (IOException e) { |
| if (!Files.exists(path)) { |
| throw new NoSuchFileException(path.toString()); |
| } else { |
| throw e; |
| } |
| } |
| } |
| return openedZipFile; |
| } |
| |
| @Override |
| public void finished(DiagnosticsHandler handler) throws IOException { |
| close(); |
| } |
| |
| @Override |
| public void close() throws IOException { |
| if (openedZipFile != null) { |
| openedZipFile.close(); |
| openedZipFile = null; |
| } |
| } |
| |
| private ZipEntry getZipEntryFromDescriptor(String descriptor) throws IOException { |
| return getOpenZipFile() |
| .getEntry(descriptor.substring(1, descriptor.length() - 1) + CLASS_EXTENSION); |
| } |
| } |