| // Copyright (c) 2017, 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 static com.android.tools.r8.utils.FileUtils.isClassFile; |
| |
| import com.android.tools.r8.ClassFileResourceProvider; |
| import com.android.tools.r8.Resource; |
| import com.android.tools.r8.errors.CompilationError; |
| import com.android.tools.r8.shaking.FilteredClassPath; |
| import com.google.common.collect.Sets; |
| import com.google.common.io.ByteStreams; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipFile; |
| |
| /** |
| * Lazy Java class file resource provider based on preloaded/prebuilt context. |
| */ |
| public final class PreloadedClassFileProvider implements ClassFileResourceProvider { |
| |
| private final Map<String, byte[]> content; |
| |
| private PreloadedClassFileProvider(Map<String, byte[]> content) { |
| this.content = content; |
| } |
| |
| @Override |
| public Set<String> getClassDescriptors() { |
| return Sets.newHashSet(content.keySet()); |
| } |
| |
| @Override |
| public Resource getResource(String descriptor) { |
| byte[] bytes = content.get(descriptor); |
| if (bytes == null) { |
| return null; |
| } |
| return Resource.fromBytes(Resource.Kind.CLASSFILE, bytes, Collections.singleton(descriptor)); |
| } |
| |
| /** |
| * Create preloaded content resource provider from archive file. |
| */ |
| public static ClassFileResourceProvider fromArchive(FilteredClassPath archive) |
| throws IOException { |
| assert isArchive(archive.getPath()); |
| Builder builder = builder(); |
| try (ZipFile zipFile = new ZipFile(archive.getPath().toFile())) { |
| final Enumeration<? extends ZipEntry> entries = zipFile.entries(); |
| while (entries.hasMoreElements()) { |
| ZipEntry entry = entries.nextElement(); |
| String name = entry.getName(); |
| Path entryPath = Paths.get(name); |
| if (isClassFile(entryPath) && archive.matchesFile(entryPath)) { |
| try (InputStream entryStream = zipFile.getInputStream(entry)) { |
| builder.addResource(guessTypeDescriptor(name), ByteStreams.toByteArray(entryStream)); |
| } |
| } |
| } |
| } |
| |
| return builder.build(); |
| } |
| |
| public static ClassFileResourceProvider fromClassData(String descriptor, byte[] data) |
| throws IOException { |
| Builder builder = builder(); |
| builder.addResource(descriptor, data); |
| return builder.build(); |
| } |
| |
| // Guess class descriptor from location of the class file. |
| static String guessTypeDescriptor(Path name) { |
| return guessTypeDescriptor(name.toString()); |
| } |
| |
| // Guess class descriptor from location of the class file. |
| private static String guessTypeDescriptor(String name) { |
| assert name != null; |
| assert name.endsWith(CLASS_EXTENSION) : |
| "Name " + name + " must have " + CLASS_EXTENSION + " suffix"; |
| String fileName = |
| File.separatorChar == '/' ? name.toString() : |
| name.toString().replace(File.separatorChar, '/'); |
| String descriptor = fileName.substring(0, fileName.length() - CLASS_EXTENSION.length()); |
| if (descriptor.contains(".")) { |
| throw new CompilationError("Unexpected file name in the archive: " + fileName); |
| } |
| return 'L' + descriptor + ';'; |
| } |
| |
| @Override |
| public String toString() { |
| return content.size() + " preloaded resources"; |
| } |
| |
| /** |
| * Create a new empty builder. |
| */ |
| public static Builder builder() { |
| return new Builder(); |
| } |
| |
| public static final class Builder { |
| |
| private Map<String, byte[]> content = new HashMap<>(); |
| |
| private Builder() { |
| } |
| |
| public Builder addResource(String descriptor, byte[] bytes) { |
| assert content != null; |
| assert descriptor != null; |
| assert bytes != null; |
| assert !content.containsKey(descriptor); |
| content.put(descriptor, bytes); |
| return this; |
| } |
| |
| public PreloadedClassFileProvider build() { |
| assert content != null; |
| PreloadedClassFileProvider provider = new PreloadedClassFileProvider(content); |
| content = null; |
| return provider; |
| } |
| } |
| } |