Merge "Simplify error reporting."
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index eb2d0fe..77284e5 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.utils.AbortException;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -66,6 +67,34 @@
// Internal access to the internal options.
abstract InternalOptions getInternalOptions();
+ abstract static class InputFileOrigin extends PathOrigin {
+ private final String inputType;
+
+ public InputFileOrigin(String inputType, Path file) {
+ super(file);
+ this.inputType = inputType;
+ }
+
+ @Override
+ public String part() {
+ return inputType + " '" + super.part() + "'";
+ }
+ }
+
+ private static class ProgramInputOrigin extends InputFileOrigin {
+
+ public ProgramInputOrigin(Path file) {
+ super("program input", file);
+ }
+ }
+
+ private static class LibraryInputOrigin extends InputFileOrigin {
+
+ public LibraryInputOrigin(Path file) {
+ super("library input", file);
+ }
+ }
+
/**
* Base builder for commands.
*
@@ -147,7 +176,7 @@
app.addProgramFile(path);
programFiles.add(path);
} catch (IOException | CompilationError e) {
- error("Error with input file: ", path, e);
+ error(new ProgramInputOrigin(path), e);
}
});
});
@@ -181,7 +210,7 @@
try {
app.addLibraryFile(path);
} catch (IOException | CompilationError e) {
- error("Error with library file: ", path, e);
+ error(new LibraryInputOrigin(path), e);
}
});
});
@@ -289,9 +318,8 @@
void validate() {}
// Helper to signify an error.
- void error(String baseMessage, Path path, Throwable throwable) {
- reporter.error(new StringDiagnostic(
- baseMessage + throwable.getMessage(), new PathOrigin(path)), throwable);
+ void error(Origin origin, Throwable throwable) {
+ reporter.error(new ExceptionDiagnostic(throwable, origin));
}
// Helper to guard and handle exceptions.
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index 0394441..6d7ad87 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.utils.ArchiveBuilder;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.DirectoryBuilder;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.io.ByteStreams;
@@ -135,7 +135,7 @@
try {
outputBuilder.close();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, outputBuilder.getOrigin()));
+ handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
}
}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index c300bbf..fc58a4a 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -32,6 +32,13 @@
*/
public class D8Command extends BaseCompilerCommand {
+ private static class ClasspathInputOrigin extends InputFileOrigin {
+
+ public ClasspathInputOrigin(Path file) {
+ super("classpath input", file);
+ }
+ }
+
/**
* Builder for constructing a D8Command.
*
@@ -78,7 +85,7 @@
try {
getAppBuilder().addClasspathFile(file);
} catch (IOException e) {
- error("Error with classpath entry: ", file, e);
+ error(new ClasspathInputOrigin(file), e);
}
});
}
diff --git a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
index f93c338..ca86c70 100644
--- a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
@@ -8,8 +8,8 @@
import com.android.tools.r8.utils.ArchiveBuilder;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.DirectoryBuilder;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.io.ByteStreams;
@@ -156,7 +156,7 @@
try {
outputBuilder.close();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, outputBuilder.getOrigin()));
+ handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
}
}
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index 0f932d5..f26ab73 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -3,11 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.ArchiveBuilder;
import com.android.tools.r8.utils.DirectoryBuilder;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.io.ByteStreams;
@@ -126,6 +127,10 @@
}
}
+ public Origin getOrigin() {
+ return outputBuilder.getOrigin();
+ }
+
@Override
public DataResourceConsumer getDataResourceConsumer() {
return consumeDataResources ? this : null;
@@ -154,7 +159,7 @@
try {
outputBuilder.close();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, outputBuilder.getOrigin()));
+ handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
}
}
@@ -219,7 +224,7 @@
try {
prepareDirectory();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(directory)));
+ handler.error(new ExceptionDiagnostic(e, new PathOrigin(directory)));
}
outputBuilder.addFile(getDexFileName(fileIndex), data, handler);
}
@@ -240,7 +245,7 @@
try {
outputBuilder.close();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, outputBuilder.getOrigin()));
+ handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
}
}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 83f9dc2..10e5828 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -15,8 +15,9 @@
import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.Reporter;
@@ -286,14 +287,12 @@
return makeR8Command();
} catch (IOException e) {
- throw getReporter().fatalError(new IOExceptionDiagnostic(e), e);
- } catch (CompilationException e) {
- throw getReporter().fatalError(new StringDiagnostic(e.getMessage()), e);
+ throw getReporter()
+ .fatalError(new ExceptionDiagnostic(e, ExceptionUtils.extractIOExceptionOrigin(e)));
}
}
- private R8Command makeR8Command()
- throws IOException, CompilationException {
+ private R8Command makeR8Command() throws IOException {
Reporter reporter = getReporter();
DexItemFactory factory = new DexItemFactory();
ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
diff --git a/src/main/java/com/android/tools/r8/StringConsumer.java b/src/main/java/com/android/tools/r8/StringConsumer.java
index 23883e9..cccb72e 100644
--- a/src/main/java/com/android/tools/r8/StringConsumer.java
+++ b/src/main/java/com/android/tools/r8/StringConsumer.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
@@ -102,7 +102,8 @@
try {
Files.write(outputPath, string.getBytes(encoding));
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(outputPath)));
+ Origin origin = new PathOrigin(outputPath);
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
}
@@ -149,7 +150,7 @@
writer.write(string);
writer.flush();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/UsageInformationConsumer.java b/src/main/java/com/android/tools/r8/UsageInformationConsumer.java
index e949276..a572908 100644
--- a/src/main/java/com/android/tools/r8/UsageInformationConsumer.java
+++ b/src/main/java/com/android/tools/r8/UsageInformationConsumer.java
@@ -5,8 +5,8 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
@@ -89,7 +89,8 @@
try {
FileUtils.writeToFile(outputPath, null, data);
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(outputPath)));
+ Origin origin = new PathOrigin(outputPath);
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
}
@@ -125,7 +126,7 @@
try {
outputStream.write(data);
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
index 1de9606..b9131e5 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -24,9 +24,10 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
@@ -547,7 +548,7 @@
StandardOpenOption.TRUNCATE_EXISTING,
StandardOpenOption.WRITE);
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e));
+ handler.error(new ExceptionDiagnostic(e, new PathOrigin(output)));
}
}
}
@@ -566,7 +567,7 @@
try {
writeZipWithClasses(handler);
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e));
+ handler.error(new ExceptionDiagnostic(e, getOrigin()));
}
super.finished(handler);
}
diff --git a/src/main/java/com/android/tools/r8/dex/DexReader.java b/src/main/java/com/android/tools/r8/dex/DexReader.java
index 44bacc6..d8997f7 100644
--- a/src/main/java/com/android/tools/r8/dex/DexReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexReader.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.DexVersion;
import java.io.IOException;
+import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -38,6 +39,12 @@
// Parse the magic header and determine the dex file version.
private int parseMagic(ByteBuffer buffer) {
+ try {
+ buffer.get();
+ buffer.rewind();
+ } catch (BufferUnderflowException e) {
+ throw new CompilationError("Dex file is empty", origin);
+ }
int index = 0;
for (byte prefixByte : DEX_FILE_MAGIC_PREFIX) {
if (buffer.get(index++) != prefixByte) {
diff --git a/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java b/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
index 59aecfe..ba576e7 100644
--- a/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
+++ b/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
@@ -12,8 +12,8 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
import com.android.tools.r8.utils.OptionsParsing;
import com.android.tools.r8.utils.OptionsParsing.ParseContext;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -233,7 +233,7 @@
Files.newOutputStream(
path, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
return stream;
@@ -246,7 +246,7 @@
getStream(handler), getDexFileName(fileIndex), data, ZipEntry.DEFLATED, true);
hasWrittenSomething = true;
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
@@ -264,7 +264,7 @@
stream = null;
}
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index cf6c0a8..247f790 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -78,9 +78,12 @@
import com.android.tools.r8.utils.InternalOptions;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
@@ -105,6 +108,8 @@
*/
public class JarClassFileReader {
+ private static final byte[] CLASSFILE_HEADER = ByteBuffer.allocate(4).putInt(0xCAFEBABE).array();
+
// Hidden ASM "synthetic attribute" bit we need to clear.
private static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000;
// Descriptor used by ASM for missing annotations.
@@ -120,6 +125,24 @@
}
public void read(Origin origin, ClassKind classKind, InputStream input) throws IOException {
+ if (!input.markSupported()) {
+ input = new BufferedInputStream(input);
+ }
+ byte[] header = new byte[CLASSFILE_HEADER.length];
+ input.mark(header.length);
+ int size = 0;
+ while (size < header.length) {
+ int read = input.read(header, size, header.length - size);
+ if (read < 0) {
+ throw new CompilationError("Invalid empty classfile", origin);
+ }
+ size += read;
+ }
+ if (!Arrays.equals(CLASSFILE_HEADER, header)) {
+ throw new CompilationError("Invalid classfile header", origin);
+ }
+ input.reset();
+
ClassReader reader = new ClassReader(input);
int flags = SKIP_FRAMES;
if (application.options.enableCfFrontend) {
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index d6ddf5d..6252c40 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -301,8 +301,7 @@
}
/** Add filtered archives of program resources. */
- public Builder addFilteredProgramArchives(Collection<FilteredClassPath> filteredArchives)
- throws NoSuchFileException {
+ public Builder addFilteredProgramArchives(Collection<FilteredClassPath> filteredArchives) {
for (FilteredClassPath archive : filteredArchives) {
assert isArchive(archive.getPath());
ArchiveResourceProvider archiveResourceProvider =
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
index 4a7abc3..cd21d17 100644
--- a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
@@ -61,7 +61,7 @@
Files.newOutputStream(
archive, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
return stream;
@@ -71,9 +71,9 @@
if (e instanceof ZipException && e.getMessage().startsWith("duplicate entry")) {
// For now we stick to the Proguard behaviour, see section "Warning: can't write resource ...
// Duplicate zip entry" on https://www.guardsquare.com/en/proguard/manual/troubleshooting.
- handler.warning(new IOExceptionDiagnostic(e, origin));
+ handler.warning(new ExceptionDiagnostic(e, origin));
} else {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new ExceptionDiagnostic(e, origin));
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/DiagnosticWithThrowable.java b/src/main/java/com/android/tools/r8/utils/DiagnosticWithThrowable.java
index da439fe..b169297 100644
--- a/src/main/java/com/android/tools/r8/utils/DiagnosticWithThrowable.java
+++ b/src/main/java/com/android/tools/r8/utils/DiagnosticWithThrowable.java
@@ -14,4 +14,8 @@
assert throwable != null;
this.throwable = throwable;
}
+
+ public Throwable getThrowable() {
+ return throwable;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
index b793f19..2dc4a2d 100644
--- a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
@@ -39,7 +39,7 @@
try {
Files.createDirectories(target.getParent());
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(target)));
+ handler.error(new ExceptionDiagnostic(e, new PathOrigin(target)));
}
}
@@ -48,7 +48,7 @@
try (InputStream in = content.getByteStream()) {
addFile(name, ByteStreams.toByteArray(in), handler);
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, content.getOrigin()));
+ handler.error(new ExceptionDiagnostic(e, content.getOrigin()));
} catch (ResourceException e) {
handler.error(new StringDiagnostic("Failed to open input: " + e.getMessage(),
content.getOrigin()));
@@ -62,7 +62,7 @@
Files.createDirectories(target.getParent());
FileUtils.writeToFile(target, null, content);
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(target)));
+ handler.error(new ExceptionDiagnostic(e, new PathOrigin(target)));
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java b/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java
new file mode 100644
index 0000000..9fd91d9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java
@@ -0,0 +1,43 @@
+// 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 com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.io.FileNotFoundException;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.NoSuchFileException;
+
+public class ExceptionDiagnostic extends DiagnosticWithThrowable {
+
+ private final Origin origin;
+
+ public ExceptionDiagnostic(Throwable e, Origin origin) {
+ super(e);
+ this.origin = origin;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ Throwable e = getThrowable();
+ if (e instanceof NoSuchFileException || e instanceof FileNotFoundException) {
+ return "File not found: " + e.getMessage();
+ }
+ if (e instanceof FileAlreadyExistsException) {
+ return "File already exists: " + e.getMessage();
+ }
+ return e.getMessage();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index f4d3ae7..9c7f525 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -9,7 +9,11 @@
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
import java.io.IOException;
+import java.nio.file.FileSystemException;
+import java.nio.file.Paths;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -55,16 +59,13 @@
try {
action.run();
} catch (IOException e) {
- throw reporter.fatalError(new IOExceptionDiagnostic(e));
+ throw reporter.fatalError(new ExceptionDiagnostic(e, extractIOExceptionOrigin(e)));
} catch (CompilationException e) {
throw reporter.fatalError(new StringDiagnostic(compilerMessage.apply(e)), e);
} catch (CompilationError e) {
throw reporter.fatalError(e);
} catch (ResourceException e) {
- throw reporter.fatalError(
- e.getCause() instanceof IOException
- ? new IOExceptionDiagnostic((IOException) e.getCause(), e.getOrigin())
- : new StringDiagnostic(e.getMessage(), e.getOrigin()));
+ throw reporter.fatalError(new ExceptionDiagnostic(e, e.getOrigin()));
}
reporter.failIfPendingErrors();
} catch (AbortException e) {
@@ -89,6 +90,19 @@
cause.printStackTrace();
System.exit(STATUS_ERROR);
}
-
}
+
+ // We should try to avoid the use of this extraction as it signifies a point where we don't have
+ // enough context to associate a specific origin with an IOException. Concretely, we should move
+ // towards always catching IOException and rethrowing CompilationError with proper origins.
+ public static Origin extractIOExceptionOrigin(IOException e) {
+ if (e instanceof FileSystemException) {
+ FileSystemException fse = (FileSystemException) e;
+ if (fse.getFile() != null && !fse.getFile().isEmpty()) {
+ return new PathOrigin(Paths.get(fse.getFile()));
+ }
+ }
+ return Origin.unknown();
+ }
+
}
diff --git a/src/main/java/com/android/tools/r8/utils/IOExceptionDiagnostic.java b/src/main/java/com/android/tools/r8/utils/IOExceptionDiagnostic.java
deleted file mode 100644
index 642f2d7..0000000
--- a/src/main/java/com/android/tools/r8/utils/IOExceptionDiagnostic.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// 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 com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.position.Position;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.nio.file.FileAlreadyExistsException;
-import java.nio.file.FileSystemException;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.Paths;
-
-public class IOExceptionDiagnostic extends DiagnosticWithThrowable {
-
- private final Origin origin;
- private final String message;
-
- public IOExceptionDiagnostic(IOException e) {
- super(e);
- origin = extractOrigin(e);
- message = extractMessage(e);
- }
-
- public IOExceptionDiagnostic(IOException e, Origin origin) {
- super(e);
- this.origin = origin;
- message = extractMessage(e);
- }
-
- private static String extractMessage(IOException e) {
- String message = e.getMessage();
- if (message == null || message.isEmpty()) {
- if (e instanceof NoSuchFileException || e instanceof FileNotFoundException) {
- message = "File not found";
- } else if (e instanceof FileAlreadyExistsException) {
- message = "File already exists";
- }
- }
- return message;
- }
-
- private static Origin extractOrigin(IOException e) {
- Origin origin = Origin.unknown();
-
- if (e instanceof FileSystemException) {
- FileSystemException fse = (FileSystemException) e;
- if (fse.getFile() != null && !fse.getFile().isEmpty()) {
- origin = new PathOrigin(Paths.get(fse.getFile()));
- }
- }
- return origin;
- }
-
- @Override
- public Origin getOrigin() {
- return origin;
- }
-
- @Override
- public Position getPosition() {
- return Position.UNKNOWN;
- }
-
- @Override
- public String getDiagnosticMessage() {
- return message;
- }
-
-}
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index ed7bbf8..dc16cac 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -15,8 +15,10 @@
import com.android.tools.r8.origin.EmbeddedOrigin;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
+import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -139,7 +141,7 @@
Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar");
ProcessResult result =
ToolHelper.forkD8(Paths.get("."), input.toString(), "--output", existingDir.toString());
- assertEquals(0, result.exitCode);
+ assertEquals(result.toString(), 0, result.exitCode);
assertTrue(Files.exists(classesFiles.get(0)));
for (int i = 1; i < classesFiles.size(); i++) {
Path file = classesFiles.get(i);
@@ -360,6 +362,62 @@
.build());
}
+ @Test(expected = CompilationFailedException.class)
+ public void errorOnEmptyClassfile() throws IOException, CompilationFailedException {
+ Path emptyFile = temp.getRoot().toPath().resolve("empty-file.class");
+ FileUtils.writeToFile(emptyFile, null, new byte[0]);
+ DiagnosticsChecker.checkErrorsContains(
+ "empty",
+ handler ->
+ D8.run(
+ D8Command.builder(handler)
+ .addProgramFiles(emptyFile)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .build()));
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void errorOnInvalidClassfileHeader() throws IOException, CompilationFailedException {
+ Path emptyFile = temp.getRoot().toPath().resolve("empty-file.class");
+ FileUtils.writeToFile(emptyFile, null, new byte[] {'C', 'A', 'F', 'E', 'B', 'A', 'B', 'F'});
+ DiagnosticsChecker.checkErrorsContains(
+ "header",
+ handler ->
+ D8.run(
+ D8Command.builder(handler)
+ .addProgramFiles(emptyFile)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .build()));
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void errorOnEmptyDex() throws IOException, CompilationFailedException {
+ Path emptyFile = temp.getRoot().toPath().resolve("empty-file.dex");
+ FileUtils.writeToFile(emptyFile, null, new byte[0]);
+ DiagnosticsChecker.checkErrorsContains(
+ "empty",
+ handler ->
+ D8.run(
+ D8Command.builder(handler)
+ .addProgramFiles(emptyFile)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .build()));
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void errorOnInvalidDexHeader() throws IOException, CompilationFailedException {
+ Path emptyFile = temp.getRoot().toPath().resolve("empty-file.dex");
+ FileUtils.writeToFile(emptyFile, null, new byte[] {'C', 'A', 'F', 'E', 'B', 'A', 'B', 'E'});
+ DiagnosticsChecker.checkErrorsContains(
+ "header",
+ handler ->
+ D8.run(
+ D8Command.builder(handler)
+ .addProgramFiles(emptyFile)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .build()));
+ }
+
private D8Command parse(String... args) throws CompilationFailedException {
return D8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
index 7cd32c7..fc22895 100644
--- a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
+++ b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
@@ -40,6 +40,8 @@
try {
runner.run(handler);
} catch (CompilationFailedException e) {
+ System.out.println("Expecting match for '" + snippet + "'");
+ System.out.println("StdErr:\n" + handler.errors);
assertTrue(
"Expected to find snippet '"
+ snippet