Debug abort without deprecation

Change-Id: Ia55f7c2ff302d5e58aaed110e5eb88cdffc7d1a9
Reviewed-on: https://dart-review.googlesource.com/63120
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Commit-Queue: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/front_end/lib/src/base/processed_options.dart b/pkg/front_end/lib/src/base/processed_options.dart
index 22fc6b6..fcd9d47 100644
--- a/pkg/front_end/lib/src/base/processed_options.dart
+++ b/pkg/front_end/lib/src/base/processed_options.dart
@@ -33,8 +33,6 @@
 
 import '../fasta/command_line_reporting.dart' as command_line_reporting;
 
-import '../fasta/deprecated_problems.dart' show deprecated_InputError;
-
 import '../fasta/fasta_codes.dart'
     show
         FormattedMessage,
@@ -55,9 +53,9 @@
 
 import '../fasta/messages.dart' show getLocation;
 
-import '../fasta/problems.dart' show unimplemented;
+import '../fasta/problems.dart' show DebugAbort, unimplemented;
 
-import '../fasta/severity.dart' show Severity, severityTexts;
+import '../fasta/severity.dart' show Severity;
 
 import '../fasta/ticker.dart' show Ticker;
 
@@ -219,9 +217,8 @@
       }
       _raw.onProblem(format(message, severity), severity, formattedContext);
       if (command_line_reporting.shouldThrowOn(severity)) {
-        if (verbose) print(StackTrace.current);
-        throw new deprecated_InputError(message.uri, message.charOffset,
-            "Compilation aborted due to fatal ${severityTexts[severity]}.");
+        throw new DebugAbort(
+            message.uri, message.charOffset, severity, StackTrace.current);
       }
       return;
     }
diff --git a/pkg/front_end/lib/src/fasta/command_line_reporting.dart b/pkg/front_end/lib/src/fasta/command_line_reporting.dart
index 28414b3..e54dad5 100644
--- a/pkg/front_end/lib/src/fasta/command_line_reporting.dart
+++ b/pkg/front_end/lib/src/fasta/command_line_reporting.dart
@@ -22,15 +22,13 @@
 
 import 'crash.dart' show Crash, safeToString;
 
-import 'deprecated_problems.dart' show deprecated_InputError;
-
 import 'fasta_codes.dart' show LocatedMessage;
 
-import 'messages.dart' show getLocation, getSourceLine, isVerbose;
+import 'messages.dart' show getLocation, getSourceLine;
 
-import 'problems.dart' show unhandled;
+import 'problems.dart' show DebugAbort, unhandled;
 
-import 'severity.dart' show Severity, severityPrefixes, severityTexts;
+import 'severity.dart' show Severity, severityPrefixes;
 
 import 'scanner/characters.dart' show $CARET, $SPACE, $TAB;
 
@@ -179,11 +177,7 @@
   }
   print(text);
   if (shouldThrowOn(severity)) {
-    if (isVerbose) print(StackTrace.current);
-    // TODO(sigmund,ahe): ensure there is no circularity when InputError is
-    // handled.
-    throw new deprecated_InputError(uri, charOffset,
-        "Compilation aborted due to fatal ${severityTexts[severity]}.");
+    throw new DebugAbort(uri, charOffset, severity, StackTrace.current);
   }
 }
 
diff --git a/pkg/front_end/lib/src/fasta/compiler_context.dart b/pkg/front_end/lib/src/fasta/compiler_context.dart
index e979f14..eca094e 100644
--- a/pkg/front_end/lib/src/fasta/compiler_context.dart
+++ b/pkg/front_end/lib/src/fasta/compiler_context.dart
@@ -111,6 +111,8 @@
     return context;
   }
 
+  static bool get isActive => Zone.current[compilerContextKey] != null;
+
   /// Perform [action] in a [Zone] where [this] will be available as
   /// `CompilerContext.current`.
   Future<T> runInContext<T>(Future<T> action(CompilerContext c)) {
diff --git a/pkg/front_end/lib/src/fasta/deprecated_problems.dart b/pkg/front_end/lib/src/fasta/deprecated_problems.dart
index 8e52ae6..004b591 100644
--- a/pkg/front_end/lib/src/fasta/deprecated_problems.dart
+++ b/pkg/front_end/lib/src/fasta/deprecated_problems.dart
@@ -11,9 +11,14 @@
 import 'crash.dart' show safeToString;
 
 import 'messages.dart'
-    show LocatedMessage, noLength, isVerbose, templateUnspecified;
+    show
+        LocatedMessage,
+        isVerbose,
+        noLength,
+        templateInternalProblemDebugAbort,
+        templateUnspecified;
 
-import 'severity.dart' show Severity;
+import 'severity.dart' show Severity, severityTexts;
 
 import 'crash.dart' show Crash, reportCrash, resetCrashReporting;
 
@@ -50,10 +55,29 @@
   ///
   /// Static method to discourage use and requiring call-sites to include the
   /// text `deprecated_`.
-  static LocatedMessage toMessage(deprecated_InputError error) {
-    return templateUnspecified
-        .withArguments(safeToString(error.error))
-        .withLocation(error.uri, error.charOffset, noLength);
+  static LocatedMessage deprecated_toMessage(deprecated_InputError error) {
+    if (error is DebugAbort) {
+      return error.toMessage();
+    } else {
+      return templateUnspecified
+          .withArguments(safeToString(error.error))
+          .withLocation(error.uri, error.charOffset, noLength);
+    }
+  }
+}
+
+class DebugAbort extends deprecated_InputError {
+  final StackTrace trace;
+
+  final Severity severity;
+
+  DebugAbort(Uri uri, int charOffset, this.severity, this.trace)
+      : super(uri, charOffset, "");
+
+  LocatedMessage toMessage() {
+    return templateInternalProblemDebugAbort
+        .withArguments(severityTexts[severity], "$trace")
+        .withLocation(uri, charOffset, noLength);
   }
 }
 
@@ -67,9 +91,11 @@
     return await action();
   } on Crash {
     rethrow;
+  } on DebugAbort {
+    rethrow;
   } on deprecated_InputError catch (e, s) {
     if (onInputError != null) {
-      return onInputError(deprecated_InputError.toMessage(e));
+      return onInputError(deprecated_InputError.deprecated_toMessage(e));
     } else {
       return reportCrash(e, s, currentUri());
     }
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index 12d35f9..e8c41e4 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -3316,6 +3316,27 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name, String string)>
+    templateInternalProblemDebugAbort =
+    const Template<Message Function(String name, String string)>(
+        messageTemplate: r"""Compilation aborted due to fatal '#name' at:
+#string""", withArguments: _withArgumentsInternalProblemDebugAbort);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name, String string)>
+    codeInternalProblemDebugAbort =
+    const Code<Message Function(String name, String string)>(
+        "InternalProblemDebugAbort", templateInternalProblemDebugAbort,
+        severity: Severity.internalProblem);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsInternalProblemDebugAbort(String name, String string) {
+  return new Message(codeInternalProblemDebugAbort,
+      message: """Compilation aborted due to fatal '${name}' at:
+${string}""", arguments: {'name': name, 'string': string});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeInternalProblemExtendingUnmodifiableScope =
     messageInternalProblemExtendingUnmodifiableScope;
 
diff --git a/pkg/front_end/lib/src/fasta/problems.dart b/pkg/front_end/lib/src/fasta/problems.dart
index d16e520..d0c8cc8 100644
--- a/pkg/front_end/lib/src/fasta/problems.dart
+++ b/pkg/front_end/lib/src/fasta/problems.dart
@@ -17,6 +17,8 @@
 
 import 'severity.dart' show Severity;
 
+export 'deprecated_problems.dart' show DebugAbort;
+
 /// Used to report an internal error.
 ///
 /// Internal errors should be avoided as best as possible, but are preferred
diff --git a/pkg/front_end/lib/src/fasta/source/diet_listener.dart b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
index 12d3cb3..a180656 100644
--- a/pkg/front_end/lib/src/fasta/source/diet_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
@@ -39,7 +39,7 @@
 
 import '../parser.dart' show Assert, MemberKind, Parser, optional;
 
-import '../problems.dart' show internalProblem, unexpected;
+import '../problems.dart' show DebugAbort, internalProblem, unexpected;
 
 import '../type_inference/type_inference_engine.dart' show TypeInferenceEngine;
 
@@ -752,6 +752,8 @@
       listener.checkEmpty(token.charOffset);
       listenerFinishFunction(listener, startToken, metadata, kind,
           metadataConstants, formals, asyncModifier, body);
+    } on DebugAbort {
+      rethrow;
     } on deprecated_InputError {
       rethrow;
     } catch (e, s) {
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index a6b4507..b5ce197 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -1394,6 +1394,10 @@
     #string
   severity: INTERNAL_PROBLEM
 
+InternalProblemDebugAbort:
+  template: "Compilation aborted due to fatal '#name' at:\n#string"
+  severity: INTERNAL_PROBLEM
+
 LocalDefinitionHidesExport:
   template: "Local definition of '#name' hides export from '#uri'."
   severity: IGNORED
diff --git a/pkg/front_end/tool/_fasta/command_line.dart b/pkg/front_end/tool/_fasta/command_line.dart
index 258e081..99ee0b3 100644
--- a/pkg/front_end/tool/_fasta/command_line.dart
+++ b/pkg/front_end/tool/_fasta/command_line.dart
@@ -4,7 +4,9 @@
 
 library fasta.tool.command_line;
 
-import 'dart:io' show exit;
+import 'dart:async' show Future;
+
+import 'dart:io' show exit, exitCode;
 
 import 'package:build_integration/file_system/single_root.dart'
     show SingleRootFileSystem;
@@ -25,6 +27,9 @@
 
 import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
 
+import 'package:front_end/src/fasta/deprecated_problems.dart'
+    show deprecated_InputError;
+
 import 'package:front_end/src/fasta/fasta_codes.dart'
     show
         Message,
@@ -33,7 +38,7 @@
         messageFastaUsageShort,
         templateUnspecified;
 
-import 'package:front_end/src/fasta/problems.dart' show unhandled;
+import 'package:front_end/src/fasta/problems.dart' show DebugAbort, unhandled;
 
 import 'package:front_end/src/fasta/severity.dart' show Severity;
 
@@ -458,3 +463,26 @@
   // TODO(ahe): Don't use [templateUnspecified].
   return templateUnspecified.withArguments("$sb");
 }
+
+Future<T> runProtectedFromAbort<T>(Future<T> Function() action,
+    [T failingValue]) async {
+  if (CompilerContext.isActive) {
+    throw "runProtectedFromAbort should be called from 'main',"
+        " that is, outside a compiler context.";
+  }
+  try {
+    return await action();
+  } on DebugAbort catch (e) {
+    print(e.toMessage().message);
+
+    // DebugAbort should never happen in production code, so we want test.py to
+    // treat this as a crash which is signalled by exiting with 255.
+    exit(255);
+  } on deprecated_InputError catch (e) {
+    exitCode = 1;
+    await CompilerContext.runWithDefaultOptions((c) => new Future<void>.sync(
+        () => c.report(
+            deprecated_InputError.deprecated_toMessage(e), Severity.error)));
+  }
+  return failingValue;
+}
diff --git a/pkg/front_end/tool/_fasta/entry_points.dart b/pkg/front_end/tool/_fasta/entry_points.dart
index a79de85..7635437 100644
--- a/pkg/front_end/tool/_fasta/entry_points.dart
+++ b/pkg/front_end/tool/_fasta/entry_points.dart
@@ -27,7 +27,7 @@
 import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
 
 import 'package:front_end/src/fasta/deprecated_problems.dart'
-    show deprecated_InputError, deprecated_inputError;
+    show deprecated_inputError;
 
 import 'package:front_end/src/fasta/dill/dill_target.dart' show DillTarget;
 
@@ -42,8 +42,6 @@
 import 'package:front_end/src/fasta/kernel/utils.dart'
     show printComponentText, writeComponentToFile;
 
-import 'package:front_end/src/fasta/severity.dart' show Severity;
-
 import 'package:front_end/src/fasta/ticker.dart' show Ticker;
 
 import 'package:front_end/src/fasta/uri_translator.dart' show UriTranslator;
@@ -55,7 +53,7 @@
 
 import 'additional_targets.dart' show installAdditionalTargets;
 
-import 'command_line.dart' show withGlobalOptions;
+import 'command_line.dart' show runProtectedFromAbort, withGlobalOptions;
 
 const bool summary = const bool.fromEnvironment("summary", defaultValue: false);
 
@@ -101,14 +99,7 @@
     if (i > 0) {
       print("\n");
     }
-    try {
-      await compilePlatform(arguments);
-    } on deprecated_InputError catch (e) {
-      exitCode = 1;
-      await CompilerContext.runWithDefaultOptions((c) => new Future<void>.sync(
-          () => c.report(deprecated_InputError.toMessage(e), Severity.error)));
-      return null;
-    }
+    await runProtectedFromAbort<void>(() => compilePlatform(arguments));
   }
 }
 
@@ -149,14 +140,10 @@
   }
 
   Future<bool> batchCompileArguments(List<String> arguments) async {
-    try {
-      return await withGlobalOptions("compile", arguments, true,
-          (CompilerContext c, _) => batchCompileImpl(c));
-    } on deprecated_InputError catch (e) {
-      await CompilerContext.runWithDefaultOptions((c) => new Future<void>.sync(
-          () => c.report(deprecated_InputError.toMessage(e), Severity.error)));
-      return false;
-    }
+    return runProtectedFromAbort<bool>(
+        () => withGlobalOptions("compile", arguments, true,
+            (CompilerContext c, _) => batchCompileImpl(c)),
+        false);
   }
 
   Future<bool> batchCompile(CompilerOptions options, Uri input, Uri output) {
@@ -203,7 +190,7 @@
 }
 
 Future<KernelTarget> outline(List<String> arguments) async {
-  try {
+  return await runProtectedFromAbort<KernelTarget>(() async {
     return await withGlobalOptions("outline", arguments, true,
         (CompilerContext c, _) async {
       if (c.options.verbose) {
@@ -213,16 +200,11 @@
           new CompileTask(c, new Ticker(isVerbose: c.options.verbose));
       return await task.buildOutline(c.options.output);
     });
-  } on deprecated_InputError catch (e) {
-    exitCode = 1;
-    await CompilerContext.runWithDefaultOptions((c) => new Future<void>.sync(
-        () => c.report(deprecated_InputError.toMessage(e), Severity.error)));
-    return null;
-  }
+  });
 }
 
 Future<Uri> compile(List<String> arguments) async {
-  try {
+  return await runProtectedFromAbort<Uri>(() async {
     return await withGlobalOptions("compile", arguments, true,
         (CompilerContext c, _) async {
       if (c.options.verbose) {
@@ -232,12 +214,7 @@
           new CompileTask(c, new Ticker(isVerbose: c.options.verbose));
       return await task.compile();
     });
-  } on deprecated_InputError catch (e) {
-    exitCode = 1;
-    await CompilerContext.runWithDefaultOptions((c) => new Future<void>.sync(
-        () => c.report(deprecated_InputError.toMessage(e), Severity.error)));
-    return null;
-  }
+  });
 }
 
 class CompileTask {