blob: fcb89d4dd2a4919dea979824dce0b5c58e4a05ac [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.Resource;
import com.android.tools.r8.ResourceProvider;
import com.android.tools.r8.errors.CompilationError;
import com.google.common.io.ByteStreams;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;
/**
* Lazy resource provider based on preloaded/prebuilt context.
*
* NOTE: only handles classfile resources.
*/
public final class PreloadedResourceProvider implements ResourceProvider {
private final Map<String, byte[]> content;
private PreloadedResourceProvider(Map<String, byte[]> content) {
this.content = content;
}
@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 ResourceProvider fromArchive(Path archive) throws IOException {
assert isArchive(archive);
Builder builder = builder();
try (ZipInputStream stream = new ZipInputStream(new FileInputStream(archive.toFile()))) {
ZipEntry entry;
while ((entry = stream.getNextEntry()) != null) {
String name = entry.getName();
if (isClassFile(Paths.get(name))) {
builder.addResource(guessTypeDescriptor(name), ByteStreams.toByteArray(stream));
}
}
} catch (ZipException e) {
throw new CompilationError(
"Zip error while reading '" + archive + "': " + e.getMessage(), e);
}
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 descriptor = name.substring(0, name.length() - CLASS_EXTENSION.length());
if (descriptor.contains(".")) {
throw new CompilationError("Unexpected file name in the archive: " + name);
}
return 'L' + descriptor + ';';
}
/** 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 PreloadedResourceProvider build() {
assert content != null;
PreloadedResourceProvider provider = new PreloadedResourceProvider(content);
content = null;
return provider;
}
}
}