blob: 60ab622e65c2877a2c7246b721d909c6acb77ed1 [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.graph;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.function.Consumer;
public abstract class DexByteCodeWriter {
private interface OutputStreamProvider {
PrintStream get(DexClass clazz) throws IOException;
}
final DexApplication application;
final InternalOptions options;
DexByteCodeWriter(DexApplication application,
InternalOptions options) {
this.application = application;
this.options = options;
}
private void ensureParentExists(Path path) throws IOException {
Path parent = path.getParent();
if (parent != null) {
Files.createDirectories(parent);
}
}
private OutputStreamProvider oneFilePerClass(Path path) {
return (clazz) -> {
String className = DescriptorUtils.descriptorToJavaType(clazz.type.toDescriptorString(),
application.getProguardMap());
Path classOutput = path.resolve(className.replace('.', File.separatorChar)
+ getFileEnding());
ensureParentExists(classOutput);
return new PrintStream(Files.newOutputStream(classOutput));
};
}
public void write(Path path) throws IOException {
if (Files.isDirectory(path)) {
write(oneFilePerClass(path), PrintStream::close);
} else {
ensureParentExists(path);
try (PrintStream ps = new PrintStream(Files.newOutputStream(path))) {
write(ps);
}
}
}
public void write(PrintStream output) throws IOException {
List<Marker> markers = application.dexItemFactory.extractMarkers();
System.out.println("Number of markers: " + markers.size());
for (Marker marker : markers) {
output.println(marker.toString());
}
write(x -> output, x -> {});
}
private void write(OutputStreamProvider outputStreamProvider, Consumer<PrintStream> closer)
throws IOException {
Iterable<DexProgramClass> classes = application.classesWithDeterministicOrder();
for (DexProgramClass clazz : classes) {
if (anyMethodMatches(clazz)) {
PrintStream ps = outputStreamProvider.get(clazz);
try {
writeClass(clazz, ps);
} finally {
closer.accept(ps);
}
}
}
}
private boolean anyMethodMatches(DexClass clazz) {
return !options.hasMethodsFilter()
|| clazz.getMethodCollection().hasMethods(options::methodMatchesFilter);
}
private void writeClass(DexProgramClass clazz, PrintStream ps) {
writeClassHeader(clazz, ps);
writeFieldsHeader(clazz, ps);
clazz.forEachField(field -> writeField(field, ps));
writeFieldsFooter(clazz, ps);
writeMethodsHeader(clazz, ps);
clazz.forEachProgramMethod(method -> writeMethod(method, ps));
writeMethodsFooter(clazz, ps);
writeClassFooter(clazz, ps);
}
abstract String getFileEnding();
abstract void writeClassHeader(DexProgramClass clazz, PrintStream ps);
void writeFieldsHeader(DexProgramClass clazz, PrintStream ps) {
// Do nothing.
}
abstract void writeField(DexEncodedField field, PrintStream ps);
void writeFieldsFooter(DexProgramClass clazz, PrintStream ps) {
// Do nothing.
}
void writeMethodsHeader(DexProgramClass clazz, PrintStream ps) {
// Do nothing.
}
abstract void writeMethod(ProgramMethod method, PrintStream ps);
void writeMethodsFooter(DexProgramClass clazz, PrintStream ps) {
// Do nothing.
}
abstract void writeClassFooter(DexProgramClass clazz, PrintStream ps);
}