blob: ee11bcf680fec58db8909a7885b407a9d8d50f6d [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 com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DiagnosticsHandler;
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.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.FileSystemException;
import java.nio.file.Paths;
import java.util.function.Consumer;
public abstract class ExceptionUtils {
public static final int STATUS_ERROR = 1;
public static void withConsumeResourceHandler(
Reporter reporter, StringConsumer consumer, String data) {
withConsumeResourceHandler(reporter, handler -> consumer.accept(data, handler));
}
public static void withConsumeResourceHandler(
Reporter reporter, Consumer<DiagnosticsHandler> consumer) {
// Unchecked exceptions simply propagate out, aborting the compilation forcefully.
consumer.accept(reporter);
// Fail fast for now. We might consider delaying failure since consumer failure does not affect
// the compilation. We might need to be careful to correctly identify errors so as to exit
// compilation with an error code.
reporter.failIfPendingErrors();
}
public interface CompileAction {
void run() throws IOException, CompilationError, ResourceException;
}
public static void withD8CompilationHandler(Reporter reporter, CompileAction action)
throws CompilationFailedException {
withCompilationHandler(reporter, action);
}
public static void withR8CompilationHandler(Reporter reporter, CompileAction action)
throws CompilationFailedException {
withCompilationHandler(reporter, action);
}
public static void withMainDexListHandler(
Reporter reporter, CompileAction action) throws CompilationFailedException {
withCompilationHandler(reporter, action);
}
public static void withCompilationHandler(Reporter reporter, CompileAction action)
throws CompilationFailedException {
try {
try {
action.run();
} catch (IOException e) {
throw reporter.fatalError(new ExceptionDiagnostic(e, extractIOExceptionOrigin(e)));
} catch (CompilationError e) {
throw reporter.fatalError(e);
} catch (ResourceException e) {
throw reporter.fatalError(new ExceptionDiagnostic(e, e.getOrigin()));
} catch (AssertionError e) {
// Most of our assertions don't have a message, create a wrapper that has the stack as the
// message.
if (e.getMessage() == null) {
StringWriter stack = new StringWriter();
e.printStackTrace(new PrintWriter(stack));
e = new AssertionError(stack.toString(), e);
}
throw reporter.fatalError(new ExceptionDiagnostic(e, Origin.unknown()), e);
}
reporter.failIfPendingErrors();
} catch (AbortException e) {
throw new CompilationFailedException(e);
}
}
public interface MainAction {
void run() throws CompilationFailedException;
}
public static void withMainProgramHandler(MainAction action) {
try {
action.run();
} catch (CompilationFailedException | AbortException e) {
// Detail of the errors were already reported
System.err.println("Compilation failed");
System.exit(STATUS_ERROR);
} catch (RuntimeException e) {
System.err.println("Compilation failed with an internal error.");
Throwable cause = e.getCause() == null ? e : e.getCause();
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();
}
}