blob: b819a84736d69a0da8fc713df010199af7bdcb09 [file] [log] [blame]
// 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;
}
}
}