runWithContext<T> function

Future<T> runWithContext<T>(
  1. CompileContext instance,
  2. Future<T> run()
)

Executes run with CompileContext.current set to instance.

Any uncaught error thrown in run are logged as fatal messages. An error that does not implement BuildError (or UnresolvedAnnotationException) is reported as an "unhandled exception" with a stack trace encouraging the user to file a bug (these invariants should not happen for users).

Before completing, CompileContext.throwRecoverableErrors is checked, and any calls to CompileContext.reportAndRecover are reported as fatal errors (these are different than uncaught errors in that they allow compilation to complete, see CompileContext.reportAndRecover).

NOTE: Unless you are specifically testing runWithContext, tests should use CompileContext.overrideForTesting instead to set the current context.

Implementation

Future<T> runWithContext<T>(
  CompileContext instance,
  Future<T> Function() run,
) {
  ArgumentError.checkNotNull(instance, 'instance');
  // A call to runZoned with "onError" becomes a special kind of zone called an
  // "Error Zone", which no longer guarantees completion (that is, the function
  // executing or catching an unhandled error are considered exclusive).
  //
  // This completer is (manually) completed when the function finishes or fails.
  final buildCompletedOrFailed = Completer<T>.sync();
  runZonedGuarded(
    run,
    (e, s) {
      // Convert pkg/source_gen#UnresolvedAnnotationException into a BuildError.
      if (e is UnresolvedAnnotationException) {
        final eCasted = e;
        final convert = BuildError.forSourceSpan(
          spanForElement(eCasted.annotatedElement),
          'Could not resolve "${eCasted.annotationSource!.text}":\n'
          '${messages.analysisFailureReasons}',
        );
        e = convert;
      }
      if (e is BuildError) {
        log.severe(
          'An error occurred compiling ${instance.path}:\n$e',
          // TODO(b/170758093): Add conditional stack traces, perhaps behind a
          // --define flag for developers of the compiler to get information on
          // the exact location of a failure.
        );
      } else {
        log.severe(
          'Unhandled exception in the AngularDart compiler!\n\n'
          'Please report a bug: ${messages.urlFileBugs}',
          e.toString(),
          s,
        );
      }
      if (!buildCompletedOrFailed.isCompleted) {
        buildCompletedOrFailed.complete();
      }
    },
    zoneSpecification: ZoneSpecification(
      print: (_, __, ___, line) => log.info(line),
    ),
    zoneValues: {
      _compileContextKey: instance,
    },
  )?.then((result) {
    if (!buildCompletedOrFailed.isCompleted) {
      buildCompletedOrFailed.complete(result);
    }
  });
  instance.throwRecoverableErrors();
  return buildCompletedOrFailed.future;
}