Version 2.17.0-262.0.dev

Merge commit 'bdf81502206a4da1975dfc2cecbf0d9ec0bb1628' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
index af3bf0f..e94c61e 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
@@ -69,15 +69,27 @@
 /// Entrypoint to be spawned with [Isolate.spawnUri] or [Process.start].
 ///
 /// Supports the client side of the macro expansion protocol.
-void main(_, [SendPort? sendPort]) {
+void main(List<String> arguments, [SendPort? sendPort]) {
   // Function that sends the result of a [Serializer] using either [sendPort]
   // or [stdout].
   void Function(Serializer) sendResult;
 
-  // The stream for incoming messages, could be either a ReceivePort or stdin.
+  // The stream for incoming messages, could be either a ReceivePort, stdin, or
+  // a socket.
   Stream<Object?> messageStream;
 
-  withSerializationMode($_modeMarker, () {
+  String? socketAddress;
+  int? socketPort;
+  if (arguments.isNotEmpty) {
+    if (arguments.length != 2) {
+      throw new ArgumentError(
+          'Expected exactly two or zero arguments, got \$arguments.');
+    }
+    socketAddress = arguments.first;
+    socketPort = int.parse(arguments[1]);
+  }
+
+  withSerializationMode($_modeMarker, () async {
     if (sendPort != null) {
       ReceivePort receivePort = new ReceivePort();
       messageStream = receivePort;
@@ -87,12 +99,20 @@
       // isolate.
       sendPort.send(receivePort.sendPort);
     } else {
-      sendResult = _sendStdoutResult;
+      late Stream<List<int>> inputStream;
+      if (socketAddress != null && socketPort != null) {
+        var socket = await Socket.connect(socketAddress, socketPort);
+        sendResult = _sendIOSinkResultFactory(socket);
+        inputStream = socket;
+      } else {
+        sendResult = _sendIOSinkResultFactory(stdout);
+        inputStream = stdin;
+      }
       if (serializationMode == SerializationMode.byteDataClient) {
-        messageStream = MessageGrouper(stdin).messageStream;
+        messageStream = MessageGrouper(inputStream).messageStream;
       } else if (serializationMode == SerializationMode.jsonClient) {
-        messageStream = stdin
-          .transform(const Utf8Decoder())
+        messageStream = const Utf8Decoder()
+          .bind(inputStream)
           .transform(const LineSplitter())
           .map((line) => jsonDecode(line)!);
       } else {
@@ -339,26 +359,28 @@
   }
 }
 
-/// Sends [serializer.result] to [stdout].
+/// Returns a function which takes a [Serializer] and sends its result to
+/// [sink].
 ///
 /// Serializes the result to a string if using JSON.
-void _sendStdoutResult(Serializer serializer) {
-  if (serializationMode == SerializationMode.jsonClient) {
-    stdout.writeln(jsonEncode(serializer.result));
-  } else if (serializationMode == SerializationMode.byteDataClient) {
-    Uint8List result = (serializer as ByteDataSerializer).result;
-    int length = result.lengthInBytes;
-    stdout.add([
-      length >> 24 & 0xff,
-      length >> 16 & 0xff,
-      length >> 8 & 0xff,
-      length & 0xff,
-    ]);
-    stdout.add(result);
-  } else {
-    throw new UnsupportedError(
-        'Unsupported serialization mode \$serializationMode for '
-        'ProcessExecutor');
-  }
-}
+void Function(Serializer) _sendIOSinkResultFactory(IOSink sink) =>
+    (Serializer serializer) {
+      if (serializationMode == SerializationMode.jsonClient) {
+        sink.writeln(jsonEncode(serializer.result));
+      } else if (serializationMode == SerializationMode.byteDataClient) {
+        Uint8List result = (serializer as ByteDataSerializer).result;
+        int length = result.lengthInBytes;
+        sink.add([
+          length >> 24 & 0xff,
+          length >> 16 & 0xff,
+          length >> 8 & 0xff,
+          length & 0xff,
+        ]);
+        sink.add(result);
+      } else {
+        throw new UnsupportedError(
+            'Unsupported serialization mode \$serializationMode for '
+            'ProcessExecutor');
+      }
+    };
 ''';
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/process_executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/process_executor.dart
index ed67219..07b02f0 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/process_executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/process_executor.dart
@@ -23,19 +23,25 @@
 /// programs spawned must use the corresponding `client` variant.
 ///
 /// This is the only public api exposed by this library.
-Future<MacroExecutor> start(SerializationMode serializationMode) async =>
+Future<MacroExecutor> start(SerializationMode serializationMode,
+        CommunicationChannel communicationChannel) async =>
     new MultiMacroExecutor((Uri library, String name,
         {Uri? precompiledKernelUri}) {
+      // TODO: We actually assume this is a full precompiled AOT binary, and
+      // not a kernel file. We launch it directly using `Process.start`.
       if (precompiledKernelUri == null) {
         throw new UnsupportedError(
             'This environment requires a non-null `precompiledKernelUri` to be '
             'passed when loading macros.');
       }
-
-      // TODO: We actually assume this is a full precompiled AOT binary, and not
-      // a kernel file. We launch it directly using `Process.start`.
-      return _SingleProcessMacroExecutor.start(
-          library, name, serializationMode, precompiledKernelUri.toFilePath());
+      switch (communicationChannel) {
+        case CommunicationChannel.stdio:
+          return _SingleProcessMacroExecutor.startWithStdio(library, name,
+              serializationMode, precompiledKernelUri.toFilePath());
+        case CommunicationChannel.socket:
+          return _SingleProcessMacroExecutor.startWithSocket(library, name,
+              serializationMode, precompiledKernelUri.toFilePath());
+      }
     });
 
 /// Actual implementation of the separate process based macro executor.
@@ -55,8 +61,65 @@
       : super(
             messageStream: messageStream, serializationMode: serializationMode);
 
-  static Future<_SingleProcessMacroExecutor> start(Uri library, String name,
-      SerializationMode serializationMode, String programPath) async {
+  static Future<_SingleProcessMacroExecutor> startWithSocket(
+      Uri library,
+      String name,
+      SerializationMode serializationMode,
+      String programPath) async {
+    late ServerSocket serverSocket;
+    // Try an ipv6 address loopback first, and fall back on ipv4.
+    try {
+      serverSocket = await ServerSocket.bind(InternetAddress.loopbackIPv6, 0);
+    } on SocketException catch (_) {
+      serverSocket = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
+    }
+    Process process = await Process.start(programPath, [
+      serverSocket.address.address,
+      serverSocket.port.toString(),
+    ]);
+    process.stderr
+        .transform(const Utf8Decoder())
+        .listen((content) => throw new RemoteException(content));
+    process.stdout.transform(const Utf8Decoder()).listen(
+        (event) => print('Stdout from MacroExecutor at $programPath:\n$event'));
+
+    Completer<Socket> clientCompleter = new Completer();
+    serverSocket.listen((client) {
+      clientCompleter.complete(client);
+    });
+    Socket client = await clientCompleter.future;
+
+    Stream<Object> messageStream;
+
+    if (serializationMode == SerializationMode.byteDataServer) {
+      messageStream = new MessageGrouper(client).messageStream;
+    } else if (serializationMode == SerializationMode.jsonServer) {
+      messageStream = const Utf8Decoder()
+          .bind(client)
+          .transform(const LineSplitter())
+          .map((line) => jsonDecode(line)!);
+    } else {
+      throw new UnsupportedError(
+          'Unsupported serialization mode \$serializationMode for '
+          'ProcessExecutor');
+    }
+
+    return new _SingleProcessMacroExecutor(
+        onClose: () {
+          client.close();
+          serverSocket.close();
+          process.kill();
+        },
+        messageStream: messageStream,
+        outSink: client,
+        serializationMode: serializationMode);
+  }
+
+  static Future<_SingleProcessMacroExecutor> startWithStdio(
+      Uri library,
+      String name,
+      SerializationMode serializationMode,
+      String programPath) async {
     Process process = await Process.start(programPath, []);
     process.stderr
         .transform(const Utf8Decoder())
@@ -115,3 +178,8 @@
     }
   }
 }
+
+enum CommunicationChannel {
+  socket,
+  stdio,
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index 36be330..a9f8bfa 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -5642,6 +5642,33 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String
+            name)> templateInstanceAndSynthesizedStaticConflict = const Template<
+        Message Function(String name)>(
+    problemMessageTemplate:
+        r"""This instance member conflicts with the synthesized static member called '#name'.""",
+    withArguments: _withArgumentsInstanceAndSynthesizedStaticConflict);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)>
+    codeInstanceAndSynthesizedStaticConflict =
+    const Code<Message Function(String name)>(
+        "InstanceAndSynthesizedStaticConflict",
+        analyzerCodes: <String>["CONFLICTING_STATIC_AND_INSTANCE"]);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsInstanceAndSynthesizedStaticConflict(String name) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(codeInstanceAndSynthesizedStaticConflict,
+      problemMessage:
+          """This instance member conflicts with the synthesized static member called '${name}'.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(int count, int count2)>
     templateInstantiationTooFewArguments =
     const Template<Message Function(int count, int count2)>(
diff --git a/pkg/_fe_analyzer_shared/test/macros/executor/executor_test.dart b/pkg/_fe_analyzer_shared/test/macros/executor/executor_test.dart
index e6cf230..c70ad25 100644
--- a/pkg/_fe_analyzer_shared/test/macros/executor/executor_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/executor/executor_test.dart
@@ -11,7 +11,9 @@
 import 'package:_fe_analyzer_shared/src/macros/executor/isolated_executor.dart'
     as isolatedExecutor;
 import 'package:_fe_analyzer_shared/src/macros/executor/process_executor.dart'
-    as processExecutor;
+    as processExecutor show start;
+import 'package:_fe_analyzer_shared/src/macros/executor/process_executor.dart'
+    hide start;
 
 import 'package:test/test.dart';
 
@@ -26,11 +28,15 @@
   late File simpleMacroFile;
   late Directory tmpDir;
 
-  for (var executorKind in ['Isolated', 'Process']) {
+  for (var executorKind in [
+    'Isolated',
+    'ProcessSocket',
+    'ProcessStdio',
+  ]) {
     group('$executorKind executor', () {
       for (var mode in [
         SerializationMode.byteDataServer,
-        SerializationMode.jsonServer
+        SerializationMode.jsonServer,
       ]) {
         final clientMode = mode == SerializationMode.byteDataServer
             ? SerializationMode.byteDataClient
@@ -42,7 +48,12 @@
                 File(Platform.script.resolve('simple_macro.dart').toFilePath());
             executor = executorKind == 'Isolated'
                 ? await isolatedExecutor.start(mode)
-                : await processExecutor.start(mode);
+                : executorKind == 'ProcessSocket'
+                    ? await processExecutor.start(
+                        mode, CommunicationChannel.socket)
+                    : await processExecutor.start(
+                        mode, CommunicationChannel.stdio);
+
             tmpDir = Directory.systemTemp.createTempSync('executor_test');
             macroUri = simpleMacroFile.absolute.uri;
 
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index 57f98ce..3db8862 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -256,7 +256,10 @@
 
         var lengthRestricted =
             suggestionBuilders.take(params.maxResults).toList();
-        completionPerformance.suggestionCount = lengthRestricted.length;
+        completionPerformance.computedSuggestionCount =
+            suggestionBuilders.length;
+        completionPerformance.transmittedSuggestionCount =
+            lengthRestricted.length;
 
         var suggestions = lengthRestricted.map((e) => e.build()).toList();
 
@@ -477,7 +480,10 @@
             );
           });
 
-          completionPerformance.suggestionCount = suggestionBuilders.length;
+          completionPerformance.computedSuggestionCount =
+              suggestionBuilders.length;
+          completionPerformance.transmittedSuggestionCount =
+              suggestionBuilders.length;
         } finally {
           ifMatchesRequestClear(completionRequest);
         }
diff --git a/pkg/analysis_server/lib/src/lsp/client_configuration.dart b/pkg/analysis_server/lib/src/lsp/client_configuration.dart
index 6a97756..a5ef8f7 100644
--- a/pkg/analysis_server/lib/src/lsp/client_configuration.dart
+++ b/pkg/analysis_server/lib/src/lsp/client_configuration.dart
@@ -151,6 +151,14 @@
 /// known editors allow per-file configuration and it allows us to keep the
 /// settings cached, invalidated only when WorkspaceFolders change.
 class LspResourceClientConfiguration {
+  /// The maximum number of completions to return for completion requests by
+  /// default.
+  ///
+  /// This has been set fairly high initially to avoid changing behaviour too
+  /// much. The Dart-Code extension will override this default with its own
+  /// to gather feedback and then this can be adjusted accordingly.
+  static const defaultMaxCompletions = 2000;
+
   final Map<String, Object?> _settings;
   final LspResourceClientConfiguration? _fallback;
 
@@ -187,6 +195,15 @@
   int? get lineLength =>
       _settings['lineLength'] as int? ?? _fallback?.lineLength;
 
+  /// Maximum number of CompletionItems per completion request.
+  ///
+  /// If more than this are available, the list is truncated and isIncomplete
+  /// is set to true.
+  int get maxCompletionItems =>
+      _settings['maxCompletionItems'] as int? ??
+      _fallback?.maxCompletionItems ??
+      defaultMaxCompletions;
+
   /// Whether to rename files when renaming classes inside them where the file
   /// and class name match.
   ///
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index f385bd1..fed7a76 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -78,13 +78,31 @@
       final pathContext = server.resourceProvider.pathContext;
       final fileExtension = pathContext.extension(path.result);
 
+      final maxResults = server.clientConfiguration
+          .forResource(path.result)
+          .maxCompletionItems;
+
+      CompletionPerformance? completionPerformance;
       if (fileExtension == '.dart' && !unit.isError) {
+        final result = unit.result;
+        var performanceOperation = OperationPerformanceImpl('<root>');
+        completionPerformance = CompletionPerformance(
+          operation: performanceOperation,
+          path: result.path,
+          content: result.content,
+          offset: offset,
+        );
+        server.performanceStats.completion.add(completionPerformance);
+
         serverResultsFuture = _getServerDartItems(
           clientCapabilities,
-          unit.result,
+          result,
+          completionPerformance,
+          performanceOperation,
           offset,
           triggerCharacter,
           token,
+          maxResults: maxResults,
         );
       } else if (fileExtension == '.yaml') {
         YamlCompletionGenerator? generator;
@@ -124,14 +142,25 @@
       if (serverResults.isError) return serverResults;
       if (pluginResults.isError) return pluginResults;
 
+      final untruncatedItems = serverResults.result.items
+          .followedBy(pluginResults.result.items)
+          .toList();
+
+      final truncatedItems = untruncatedItems.length > maxResults
+          ? (untruncatedItems..sort(sortTextComparer)).sublist(0, maxResults)
+          : untruncatedItems;
+
+      // If we're tracing performance (only Dart), record the number of results
+      // after truncation.
+      completionPerformance?.transmittedSuggestionCount = truncatedItems.length;
+
       return success(CompletionList(
         // If any set of the results is incomplete, the whole batch must be
         // marked as such.
         isIncomplete: serverResults.result.isIncomplete ||
-            pluginResults.result.isIncomplete,
-        items: serverResults.result.items
-            .followedBy(pluginResults.result.items)
-            .toList(),
+            pluginResults.result.isIncomplete ||
+            truncatedItems.length != untruncatedItems.length,
+        items: truncatedItems,
       ));
     });
   }
@@ -232,25 +261,19 @@
   Future<ErrorOr<CompletionList>> _getServerDartItems(
     LspClientCapabilities capabilities,
     ResolvedUnitResult unit,
+    CompletionPerformance completionPerformance,
+    OperationPerformanceImpl operationPerformance,
     int offset,
     String? triggerCharacter,
-    CancellationToken token,
-  ) async {
+    CancellationToken token, {
+    required int maxResults,
+  }) async {
     final useSuggestionSets =
         suggestFromUnimportedLibraries && capabilities.applyEdit;
 
-    var performance = OperationPerformanceImpl('<root>');
-    return await performance.runAsync(
+    return await operationPerformance.runAsync(
       'request',
       (performance) async {
-        final completionPerformance = CompletionPerformance(
-          operation: performance,
-          path: unit.path,
-          content: unit.content,
-          offset: offset,
-        );
-        server.performanceStats.completion.add(completionPerformance);
-
         final completionRequest = DartCompletionRequest.forResolvedUnit(
           resolvedUnit: unit,
           offset: offset,
@@ -474,7 +497,9 @@
               .where((e) => fuzzyMatcher.score(e.filterText ?? e.label) > 0)
               .toList();
 
-          completionPerformance.suggestionCount = results.length;
+          // Transmitted count will be set after combining with plugins.
+          completionPerformance.computedSuggestionCount =
+              matchingResults.length;
 
           return success(
               CompletionList(isIncomplete: false, items: matchingResults));
@@ -620,4 +645,37 @@
 
     return true; // Any other trigger character can be handled always.
   }
+
+  /// Compares [CompletionItem]s by the `sortText` field, which is derived from
+  /// relevance.
+  ///
+  /// For items with the same relevance, shorter items are sorted first so that
+  /// truncation always removes longer items first (which can be included by
+  /// typing more of their characters).
+  static int sortTextComparer(CompletionItem item1, CompletionItem item2) {
+    // Note: It should never be the case that we produce items without sortText
+    // but if they're null, fall back to label which is what the client would do
+    // when sorting.
+    final item1Text = item1.sortText ?? item1.label;
+    final item2Text = item2.sortText ?? item2.label;
+
+    // If both items have the same text, this means they had the same relevance.
+    // In this case, sort by the length of the name ascending, so that shorter
+    // items are first. This is because longer items can be obtained by typing
+    // additional characters where shorter ones may not.
+    //
+    // For example, with:
+    //   - String aaa1;
+    //   - String aaa2;
+    //   - ...
+    //   - String aaa(N); // up to past the truncation amount
+    //   - String aaa;    // declared last, same prefix
+    //
+    // Typing 'aaa' should not allow 'aaa' to be truncated before 'aaa1'.
+    if (item1Text == item2Text) {
+      return item1.label.length.compareTo(item2.label.length);
+    }
+
+    return item1Text.compareTo(item2Text);
+  }
 }
diff --git a/pkg/analysis_server/lib/src/services/completion/completion_performance.dart b/pkg/analysis_server/lib/src/services/completion/completion_performance.dart
index 7815def..c41731b 100644
--- a/pkg/analysis_server/lib/src/services/completion/completion_performance.dart
+++ b/pkg/analysis_server/lib/src/services/completion/completion_performance.dart
@@ -38,7 +38,8 @@
   final OperationPerformance operation;
   final String path;
   final String snippet;
-  int suggestionCount = -1;
+  int computedSuggestionCount = -1;
+  int transmittedSuggestionCount = -1;
 
   CompletionPerformance({
     required this.operation,
@@ -47,12 +48,17 @@
     required int offset,
   }) : snippet = _computeCompletionSnippet(content, offset);
 
+  String get computedSuggestionCountStr {
+    if (computedSuggestionCount < 1) return '';
+    return '$computedSuggestionCount';
+  }
+
   int get elapsedInMilliseconds {
     return operation.elapsed.inMilliseconds;
   }
 
-  String get suggestionCountStr {
-    if (suggestionCount < 1) return '';
-    return '$suggestionCount';
+  String get transmittedSuggestionCountStr {
+    if (transmittedSuggestionCount < 1) return '';
+    return '$transmittedSuggestionCount';
   }
 }
diff --git a/pkg/analysis_server/lib/src/status/diagnostics.dart b/pkg/analysis_server/lib/src/status/diagnostics.dart
index 68863ac..dfaacc9 100644
--- a/pkg/analysis_server/lib/src/status/diagnostics.dart
+++ b/pkg/analysis_server/lib/src/status/diagnostics.dart
@@ -205,12 +205,13 @@
     // emit the data as a table
     buf.writeln('<table>');
     buf.writeln(
-        '<tr><th>Time</th><th>Results</th><th>Source</th><th>Snippet</th></tr>');
+        '<tr><th>Time</th><th>Computed Results</th><th>Transmitted Results</th><th>Source</th><th>Snippet</th></tr>');
     for (var completion in completions) {
       var shortName = pathContext.basename(completion.path);
       buf.writeln('<tr>'
           '<td class="pre right">${printMilliseconds(completion.elapsedInMilliseconds)}</td>'
-          '<td class="right">${completion.suggestionCountStr}</td>'
+          '<td class="right">${completion.computedSuggestionCountStr}</td>'
+          '<td class="right">${completion.transmittedSuggestionCountStr}</td>'
           '<td>${escape(shortName)}</td>'
           '<td><code>${escape(completion.snippet)}</code></td>'
           '</tr>');
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index 92744a4..2387349 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -973,6 +973,80 @@
     expect(item.detail, isNot(contains('deprecated')));
   }
 
+  Future<void> test_isIncomplete_falseIfAllIncluded() async {
+    final content = '''
+import 'a.dart';
+void f() {
+  A a = A();
+  a.^
+}
+    ''';
+
+    // Create a class with fields aaa1 to aaa500 in the other file.
+    newFile2(
+      join(projectFolderPath, 'lib', 'a.dart'),
+      [
+        'class A {',
+        for (var i = 1; i <= 500; i++) 'String get aaa$i => "";',
+        '}',
+      ].join('\n'),
+    );
+
+    final initialAnalysis = waitForAnalysisComplete();
+    await initialize(
+        workspaceCapabilities:
+            withApplyEditSupport(emptyWorkspaceClientCapabilities));
+    await openFile(mainFileUri, withoutMarkers(content));
+    await initialAnalysis;
+    final res =
+        await getCompletionList(mainFileUri, positionFromMarker(content));
+
+    // Expect everything (hashCode etc. will take it over 500).
+    expect(res.items, hasLength(greaterThanOrEqualTo(500)));
+    expect(res.isIncomplete, isFalse);
+  }
+
+  Future<void> test_isIncomplete_trueIfNotAllIncluded() async {
+    final content = '''
+import 'a.dart';
+void f() {
+  A a = A();
+  a.^
+}
+    ''';
+
+    // Create a class with fields aaa1 to aaa500 in the other file.
+    newFile2(
+      join(projectFolderPath, 'lib', 'a.dart'),
+      [
+        'class A {',
+        for (var i = 1; i <= 500; i++) '  String get aaa$i => "";',
+        '  String get aaa => "";',
+        '}',
+      ].join('\n'),
+    );
+
+    final initialAnalysis = waitForAnalysisComplete();
+    await provideConfig(
+      () => initialize(
+          workspaceCapabilities: withApplyEditSupport(
+              withConfigurationSupport(emptyWorkspaceClientCapabilities))),
+      {'maxCompletionItems': 200},
+    );
+    await openFile(mainFileUri, withoutMarkers(content));
+    await initialAnalysis;
+    final res =
+        await getCompletionList(mainFileUri, positionFromMarker(content));
+
+    // Should be capped at 200 and marked as incomplete.
+    expect(res.items, hasLength(200));
+    expect(res.isIncomplete, isTrue);
+
+    // Also ensure 'aaa' is included, since relevance sorting should have
+    // put it at the top.
+    expect(res.items.map((item) => item.label).contains('aaa'), isTrue);
+  }
+
   Future<void> test_namedArg_insertReplaceRanges() async {
     /// Helper to check multiple completions in the same template file.
     Future<void> check(
diff --git a/pkg/analyzer/test/generated/test_support.dart b/pkg/analyzer/test/generated/test_support.dart
index b843ceb..e2f886f 100644
--- a/pkg/analyzer/test/generated/test_support.dart
+++ b/pkg/analyzer/test/generated/test_support.dart
@@ -227,33 +227,37 @@
     if (buffer.isNotEmpty) {
       errors.sort((first, second) => first.offset.compareTo(second.offset));
       buffer.writeln();
-      buffer.writeln('To accept the current state, expect:');
-      for (AnalysisError actual in errors) {
-        List<DiagnosticMessage> contextMessages = actual.contextMessages;
-        buffer.write('  error(');
-        buffer.write(actual.errorCode);
-        buffer.write(', ');
-        buffer.write(actual.offset);
-        buffer.write(', ');
-        buffer.write(actual.length);
-        if (contextMessages.isNotEmpty) {
-          buffer.write(', contextMessages: [');
-          for (int i = 0; i < contextMessages.length; i++) {
-            DiagnosticMessage message = contextMessages[i];
-            if (i > 0) {
+      if (errors.isEmpty) {
+        buffer.writeln('To accept the current state, expect no errors.');
+      } else {
+        buffer.writeln('To accept the current state, expect:');
+        for (AnalysisError actual in errors) {
+          List<DiagnosticMessage> contextMessages = actual.contextMessages;
+          buffer.write('  error(');
+          buffer.write(actual.errorCode);
+          buffer.write(', ');
+          buffer.write(actual.offset);
+          buffer.write(', ');
+          buffer.write(actual.length);
+          if (contextMessages.isNotEmpty) {
+            buffer.write(', contextMessages: [');
+            for (int i = 0; i < contextMessages.length; i++) {
+              DiagnosticMessage message = contextMessages[i];
+              if (i > 0) {
+                buffer.write(', ');
+              }
+              buffer.write('message(\'');
+              buffer.write(message.filePath);
+              buffer.write('\', ');
+              buffer.write(message.offset);
               buffer.write(', ');
+              buffer.write(message.length);
+              buffer.write(')');
             }
-            buffer.write('message(\'');
-            buffer.write(message.filePath);
-            buffer.write('\', ');
-            buffer.write(message.offset);
-            buffer.write(', ');
-            buffer.write(message.length);
-            buffer.write(')');
+            buffer.write(']');
           }
-          buffer.write(']');
+          buffer.writeln('),');
         }
-        buffer.writeln('),');
       }
       fail(buffer.toString());
     }
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index f323ee3..3c936c66 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -171,14 +171,15 @@
 
   void setOutput(Iterator<String> arguments) {
     outputSpecified = true;
+    String option = arguments.current;
     String path;
-    if (arguments.current == '-o') {
+    if (option == '-o' || option == '--out' || option == '--output') {
       if (!arguments.moveNext()) {
-        helpAndFail('Error: Missing file after -o option.');
+        helpAndFail("Missing file after '$option' option.");
       }
       path = arguments.current;
     } else {
-      path = extractParameter(arguments.current);
+      path = extractParameter(option);
     }
     out = Uri.base.resolve(fe.nativeToUriPath(path));
   }
@@ -186,7 +187,7 @@
   void setOptimizationLevel(String argument) {
     int value = int.tryParse(extractParameter(argument));
     if (value == null || value < 0 || value > 4) {
-      helpAndFail("Error: Unsupported optimization level '$argument', "
+      helpAndFail("Unsupported optimization level '$argument', "
           "supported levels are: 0, 1, 2, 3, 4");
       return;
     }
@@ -474,14 +475,14 @@
       passThrough(argument);
       return;
     }
-    helpAndFail("Error: Unsupported dump-info format '$argument', "
+    helpAndFail("Unsupported dump-info format '$argument', "
         "supported formats are: json or binary");
   }
 
   String nullSafetyMode = null;
   void setNullSafetyMode(String argument) {
     if (nullSafetyMode != null && nullSafetyMode != argument) {
-      helpAndFail("Error: cannot specify both $nullSafetyMode and $argument.");
+      helpAndFail("Cannot specify both $nullSafetyMode and $argument.");
     }
     nullSafetyMode = argument;
     passThrough(argument);
@@ -573,7 +574,8 @@
     OptionHandler('${Flags.codegenShards}=.+', setCodegenShards),
     OptionHandler(Flags.cfeOnly, setCfeOnly),
     OptionHandler(Flags.debugGlobalInference, passThrough),
-    OptionHandler('--out=.+|-o.*', setOutput, multipleArguments: true),
+    OptionHandler('--output(?:=.+)?|--out(?:=.+)?|-o.*', setOutput,
+        multipleArguments: true),
     OptionHandler('-O.*', setOptimizationLevel),
     OptionHandler(Flags.allowMockCompilation, ignoreOption),
     OptionHandler(Flags.fastStartup, ignoreOption),
@@ -1171,7 +1173,7 @@
   -h, /h, /?, --help
     Print this usage information (add -v for information about all options).
 
-  -o <file name>, --out=<file name>
+  -o <file name>, --output=<file name>
     Write the output to <file name>.
 
   -m, --minify
diff --git a/pkg/compiler/lib/src/universe/feature.dart b/pkg/compiler/lib/src/universe/feature.dart
index a35a348..8358202 100644
--- a/pkg/compiler/lib/src/universe/feature.dart
+++ b/pkg/compiler/lib/src/universe/feature.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2016, the Dart 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.
+
 // TODO(sigmund): rename universe => world
 /// Describes individual features that may be seen in a program. Most features
 /// can be described only by name using the [Feature] enum, some features are
diff --git a/pkg/compiler/lib/src/util/sink_adapter.dart b/pkg/compiler/lib/src/util/sink_adapter.dart
index bcb9b33..1b093a4 100644
--- a/pkg/compiler/lib/src/util/sink_adapter.dart
+++ b/pkg/compiler/lib/src/util/sink_adapter.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2018, the Dart 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.
+
 import '../../compiler.dart' as api;
 
 class BinaryOutputSinkAdapter implements Sink<List<int>> {
diff --git a/pkg/compiler/test/end_to_end/data/exit_code_helper.dart b/pkg/compiler/test/end_to_end/data/exit_code_helper.dart
index ab73b3a..4ce6da8 100644
--- a/pkg/compiler/test/end_to_end/data/exit_code_helper.dart
+++ b/pkg/compiler/test/end_to_end/data/exit_code_helper.dart
@@ -1 +1,5 @@
+// Copyright (c) 2013, the Dart 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.
+
 void main() {}
diff --git a/pkg/compiler/test/sourcemaps/stacktrace/extension_method.dart b/pkg/compiler/test/sourcemaps/stacktrace/extension_method.dart
index 5c8302a..9a5ddf5 100644
--- a/pkg/compiler/test/sourcemaps/stacktrace/extension_method.dart
+++ b/pkg/compiler/test/sourcemaps/stacktrace/extension_method.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2019, the Dart 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.
+
 // @dart = 2.7
 
 class MyClass {
diff --git a/pkg/front_end/lib/src/api_prototype/compiler_options.dart b/pkg/front_end/lib/src/api_prototype/compiler_options.dart
index 71c417b..450b24b 100644
--- a/pkg/front_end/lib/src/api_prototype/compiler_options.dart
+++ b/pkg/front_end/lib/src/api_prototype/compiler_options.dart
@@ -22,16 +22,13 @@
 import 'experimental_flags.dart'
     show
         AllowedExperimentalFlags,
-        defaultExperimentalFlags,
         ExperimentalFlag,
-        expiredExperimentalFlags,
+        GlobalFeatures,
         parseExperimentalFlag;
 
 import 'experimental_flags.dart' as flags
     show
         getExperimentEnabledVersionInLibrary,
-        isExperimentEnabled,
-        isExperimentEnabledInLibrary,
         isExperimentEnabledInLibraryByVersion;
 
 import 'file_system.dart' show FileSystem;
@@ -275,29 +272,14 @@
   /// Verbosity level used for filtering emitted messages.
   Verbosity verbosity = Verbosity.all;
 
-  /// Returns `true` if the experiment with the given [flag] is enabled, either
-  /// explicitly or implicitly.
-  ///
-  /// Note that libraries can still opt out of the experiment by having a lower
-  /// language version than required for the experiment.
-  bool isExperimentEnabled(ExperimentalFlag flag) {
-    return flags.isExperimentEnabled(flag,
-        explicitExperimentalFlags: explicitExperimentalFlags,
-        defaultExperimentFlagsForTesting: defaultExperimentFlagsForTesting);
-  }
+  GlobalFeatures? _globalFeatures;
 
-  /// Returns `true` if the experiment with the given [flag] is enabled either
-  /// explicitly or implicitly for the library with the given [importUri].
-  ///
-  /// Note that the library can still opt out of the experiment by having a
-  /// lower language version than required for the experiment. See
-  /// [getExperimentEnabledVersionInLibrary].
-  bool isExperimentEnabledInLibrary(ExperimentalFlag flag, Uri importUri) {
-    return flags.isExperimentEnabledInLibrary(flag, importUri,
-        defaultExperimentFlagsForTesting: defaultExperimentFlagsForTesting,
-        explicitExperimentalFlags: explicitExperimentalFlags,
-        allowedExperimentalFlags: allowedExperimentalFlagsForTesting);
-  }
+  GlobalFeatures get globalFeatures => _globalFeatures ??= new GlobalFeatures(
+      explicitExperimentalFlags,
+      defaultExperimentFlagsForTesting: defaultExperimentFlagsForTesting,
+      experimentEnabledVersionForTesting: experimentEnabledVersionForTesting,
+      experimentReleasedVersionForTesting: experimentReleasedVersionForTesting,
+      allowedExperimentalFlags: allowedExperimentalFlagsForTesting);
 
   /// Returns the minimum language version needed for a library with the given
   /// [importUri] to opt in to the experiment with the given [flag].
@@ -443,8 +425,8 @@
               "Experiment specified with conflicting values: " + experiment);
         }
       } else {
-        if (expiredExperimentalFlags[flag]!) {
-          if (value != defaultExperimentalFlags[flag]) {
+        if (flag.isExpired) {
+          if (value != flag.isEnabledByDefault) {
             /// Produce an error when the value is not the default value.
             if (value) {
               onError("Enabling experiment " +
@@ -455,7 +437,7 @@
                   experiment +
                   " is no longer supported.");
             }
-            value = defaultExperimentalFlags[flag]!;
+            value = flag.isEnabledByDefault;
           } else if (onWarning != null) {
             /// Produce a warning when the value is the default value.
             if (value) {
diff --git a/pkg/front_end/lib/src/api_prototype/experimental_flags.dart b/pkg/front_end/lib/src/api_prototype/experimental_flags.dart
index aa49e7b..340761c 100644
--- a/pkg/front_end/lib/src/api_prototype/experimental_flags.dart
+++ b/pkg/front_end/lib/src/api_prototype/experimental_flags.dart
@@ -4,6 +4,8 @@
 
 import 'package:kernel/kernel.dart' show Version;
 
+import '../fasta/fasta_codes.dart';
+
 part 'experimental_flags_generated.dart';
 
 /// The set of experiments enabled for SDK and packages.
@@ -50,8 +52,6 @@
 bool isExperimentEnabled(ExperimentalFlag flag,
     {Map<ExperimentalFlag, bool>? explicitExperimentalFlags,
     Map<ExperimentalFlag, bool>? defaultExperimentFlagsForTesting}) {
-  assert(defaultExperimentalFlags.containsKey(flag),
-      "No default value for $flag.");
   bool? enabled;
   if (explicitExperimentalFlags != null) {
     enabled = explicitExperimentalFlags[flag];
@@ -59,8 +59,8 @@
   if (defaultExperimentFlagsForTesting != null) {
     enabled ??= defaultExperimentFlagsForTesting[flag];
   }
-  enabled ??= defaultExperimentalFlags[flag];
-  return enabled!;
+  enabled ??= flag.isEnabledByDefault;
+  return enabled;
 }
 
 /// Returns `true` if [flag] is enabled in the library with the [canonicalUri]
@@ -80,8 +80,6 @@
     {Map<ExperimentalFlag, bool>? defaultExperimentFlagsForTesting,
     Map<ExperimentalFlag, bool>? explicitExperimentalFlags,
     AllowedExperimentalFlags? allowedExperimentalFlags}) {
-  assert(defaultExperimentalFlags.containsKey(flag),
-      "No default value for $flag.");
   bool? enabled;
   if (explicitExperimentalFlags != null) {
     enabled = explicitExperimentalFlags[flag];
@@ -89,8 +87,8 @@
   if (defaultExperimentFlagsForTesting != null) {
     enabled ??= defaultExperimentFlagsForTesting[flag];
   }
-  enabled ??= defaultExperimentalFlags[flag];
-  if (!enabled!) {
+  enabled ??= flag.isEnabledByDefault;
+  if (!enabled) {
     allowedExperimentalFlags ??= defaultAllowedExperimentalFlags;
     Set<ExperimentalFlag>? allowedFlags;
     if (canonicalUri.isScheme('dart')) {
@@ -140,11 +138,11 @@
   if (defaultExperimentFlagsForTesting != null) {
     enabledByDefault = defaultExperimentFlagsForTesting[flag];
   }
-  enabledByDefault ??= defaultExperimentalFlags[flag];
+  enabledByDefault ??= flag.isEnabledByDefault;
 
   bool enabledExplicitly = explicitExperimentalFlags[flag] ?? false;
 
-  if (!enabledByDefault! ||
+  if (!enabledByDefault ||
       enabledExplicitly ||
       (allowedFlags != null && allowedFlags.contains(flag))) {
     // If the feature is not enabled by default or is enabled by the allowed
@@ -152,17 +150,18 @@
     if (experimentReleasedVersionForTesting != null) {
       version = experimentReleasedVersionForTesting[flag];
     }
-    version ??= experimentReleasedVersion[flag];
+    version ??= flag.experimentReleasedVersion;
   } else {
     // If the feature is enabled by default and is not enabled by the allowed
     // list use the enabled version.
     if (experimentEnabledVersionForTesting != null) {
       version = experimentEnabledVersionForTesting[flag];
     }
-    version ??= experimentEnabledVersion[flag];
+    version ??= flag.experimentEnabledVersion;
   }
+  // ignore: unnecessary_null_comparison
   assert(version != null, "No version for enabling $flag in $canonicalUri.");
-  return version!;
+  return version;
 }
 
 bool isExperimentEnabledInLibraryByVersion(
@@ -172,14 +171,11 @@
     AllowedExperimentalFlags? allowedExperimentalFlags,
     Map<ExperimentalFlag, Version>? experimentEnabledVersionForTesting,
     Map<ExperimentalFlag, Version>? experimentReleasedVersionForTesting}) {
-  assert(defaultExperimentalFlags.containsKey(flag),
-      "No default value for $flag.");
-
   bool? enabledByDefault;
   if (defaultExperimentFlagsForTesting != null) {
     enabledByDefault = defaultExperimentFlagsForTesting[flag];
   }
-  enabledByDefault ??= defaultExperimentalFlags[flag];
+  enabledByDefault ??= flag.isEnabledByDefault;
 
   bool enabledExplicitly = explicitExperimentalFlags[flag] ?? false;
 
@@ -203,7 +199,7 @@
     enabledByAllowed = allowedFlags.contains(flag);
   }
 
-  if (enabledByDefault! || enabledExplicitly || enabledByAllowed) {
+  if (enabledByDefault || enabledExplicitly || enabledByAllowed) {
     // The feature is enabled depending on the library language version.
     Version? enabledVersion;
     if (!enabledByDefault || enabledExplicitly || enabledByAllowed) {
@@ -212,18 +208,65 @@
       if (experimentReleasedVersionForTesting != null) {
         enabledVersion = experimentReleasedVersionForTesting[flag]!;
       }
-      enabledVersion ??= experimentReleasedVersion[flag];
+      enabledVersion ??= flag.experimentReleasedVersion;
     } else {
       // If the feature is enabled by default and is not enabled by the allowed
       // list use the enabled version.
       if (experimentEnabledVersionForTesting != null) {
         enabledVersion = experimentEnabledVersionForTesting[flag];
       }
-      enabledVersion ??= experimentEnabledVersion[flag];
+      enabledVersion ??= flag.experimentEnabledVersion;
     }
-    return version >= enabledVersion!;
+    return version >= enabledVersion;
   } else {
     // The feature is not enabled, regardless of library language version.
     return false;
   }
 }
+
+/// Common interface for the state of an experimental feature.
+abstract class ExperimentalFeature {
+  /// The flag for the experimental feature.
+  final ExperimentalFlag flag;
+
+  ExperimentalFeature(this.flag);
+
+  /// `true` if this feature is enabled.
+  bool get isEnabled;
+}
+
+/// The global state of an experimental feature.
+class GlobalFeature extends ExperimentalFeature {
+  @override
+  final bool isEnabled;
+
+  GlobalFeature(ExperimentalFlag flag, this.isEnabled) : super(flag);
+}
+
+/// The state of an experimental feature within a specific library.
+class LibraryFeature extends ExperimentalFeature {
+  /// `true` if this feature is supported in the library as defined by the
+  /// default language version for its containing package/sdk.
+  ///
+  /// The feature might still not be enabled if the language version of the
+  /// library itself is below the [enabledVersion] for the feature in the
+  /// containing package/sdk.
+  final bool isSupported;
+
+  @override
+  final bool isEnabled;
+
+  /// The minimum language version for enabling this feature in this library.
+  final Version enabledVersion;
+
+  LibraryFeature(ExperimentalFlag flag, this.isSupported, this.enabledVersion,
+      this.isEnabled)
+      : super(flag);
+
+  // TODO(johnniwinther): We should emit a different message when the
+  // experiment is not released yet. The current message indicates that
+  // changing the sdk version can solve the problem.
+  /// Returns a [Message] for reporting that this feature is not enabled.
+  Message get notEnabledMessage => templateExperimentNotEnabled.withArguments(
+      flag.name, enabledVersion.toText());
+}
diff --git a/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart b/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart
index e6a113c..4d56950 100644
--- a/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart
+++ b/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart
@@ -9,52 +9,471 @@
 
 part of 'experimental_flags.dart';
 
-enum ExperimentalFlag {
-  alternativeInvalidationStrategy,
-  constFunctions,
-  constantUpdate2018,
-  constructorTearoffs,
-  controlFlowCollections,
-  enhancedEnums,
-  extensionMethods,
-  extensionTypes,
-  genericMetadata,
-  inferenceUpdate1,
-  macros,
-  namedArgumentsAnywhere,
-  nonNullable,
-  nonfunctionTypeAliases,
-  setLiterals,
-  spreadCollections,
-  superParameters,
-  testExperiment,
-  tripleShift,
-  valueClass,
-  variance,
+/// An experiment flag including its fixed properties.
+class ExperimentalFlag {
+  /// The name of this flag as used in the --enable-experiment option.
+  final String name;
+
+  /// `true` if this experimental feature is enabled by default.
+  ///
+  /// When `true`, the feature can still be disabled in individual libraries
+  /// with a language version below the [experimentEnabledVersion], and if not
+  /// [isExpired], the feature can also be disabled by using a 'no-' prefix
+  /// in the --enable-experiment option.
+  final bool isEnabledByDefault;
+
+  /// `true` if this feature can no longer be changed using the
+  /// --enable-experiment option.
+  ///
+  /// Libraries can still opt out of the feature by using a language version
+  /// below the [experimentEnabledVersion].
+  final bool isExpired;
+  final Version enabledVersion;
+
+  /// The minimum version that supports this feature.
+  ///
+  /// If the feature is not enabled by default, this is the current language
+  /// version.
+  final Version experimentEnabledVersion;
+
+  /// The minimum version that supports this feature in allowed libraries.
+  ///
+  /// Allowed libraries are specified in
+  ///
+  ///    sdk/lib/_internal/allowed_experiments.json
+  final Version experimentReleasedVersion;
+
+  const ExperimentalFlag(
+      {required this.name,
+      required this.isEnabledByDefault,
+      required this.isExpired,
+      required this.enabledVersion,
+      required this.experimentEnabledVersion,
+      required this.experimentReleasedVersion});
+  static const ExperimentalFlag alternativeInvalidationStrategy =
+      const ExperimentalFlag(
+          name: 'alternative-invalidation-strategy',
+          isEnabledByDefault: false,
+          isExpired: false,
+          enabledVersion: const Version(2, 17),
+          experimentEnabledVersion: const Version(2, 17),
+          experimentReleasedVersion: const Version(2, 17));
+
+  static const ExperimentalFlag constFunctions = const ExperimentalFlag(
+      name: 'const-functions',
+      isEnabledByDefault: false,
+      isExpired: false,
+      enabledVersion: const Version(2, 17),
+      experimentEnabledVersion: const Version(2, 17),
+      experimentReleasedVersion: const Version(2, 17));
+
+  static const ExperimentalFlag constantUpdate2018 = const ExperimentalFlag(
+      name: 'constant-update-2018',
+      isEnabledByDefault: true,
+      isExpired: true,
+      enabledVersion: const Version(2, 0),
+      experimentEnabledVersion: const Version(2, 0),
+      experimentReleasedVersion: const Version(2, 0));
+
+  static const ExperimentalFlag constructorTearoffs = const ExperimentalFlag(
+      name: 'constructor-tearoffs',
+      isEnabledByDefault: true,
+      isExpired: true,
+      enabledVersion: const Version(2, 15),
+      experimentEnabledVersion: const Version(2, 15),
+      experimentReleasedVersion: const Version(2, 15));
+
+  static const ExperimentalFlag controlFlowCollections = const ExperimentalFlag(
+      name: 'control-flow-collections',
+      isEnabledByDefault: true,
+      isExpired: true,
+      enabledVersion: const Version(2, 0),
+      experimentEnabledVersion: const Version(2, 0),
+      experimentReleasedVersion: const Version(2, 0));
+
+  static const ExperimentalFlag enhancedEnums = const ExperimentalFlag(
+      name: 'enhanced-enums',
+      isEnabledByDefault: true,
+      isExpired: false,
+      enabledVersion: const Version(2, 17),
+      experimentEnabledVersion: const Version(2, 17),
+      experimentReleasedVersion: const Version(2, 17));
+
+  static const ExperimentalFlag extensionMethods = const ExperimentalFlag(
+      name: 'extension-methods',
+      isEnabledByDefault: true,
+      isExpired: true,
+      enabledVersion: const Version(2, 6),
+      experimentEnabledVersion: const Version(2, 6),
+      experimentReleasedVersion: const Version(2, 6));
+
+  static const ExperimentalFlag extensionTypes = const ExperimentalFlag(
+      name: 'extension-types',
+      isEnabledByDefault: false,
+      isExpired: false,
+      enabledVersion: const Version(2, 17),
+      experimentEnabledVersion: const Version(2, 17),
+      experimentReleasedVersion: const Version(2, 17));
+
+  static const ExperimentalFlag genericMetadata = const ExperimentalFlag(
+      name: 'generic-metadata',
+      isEnabledByDefault: true,
+      isExpired: true,
+      enabledVersion: const Version(2, 14),
+      experimentEnabledVersion: const Version(2, 14),
+      experimentReleasedVersion: const Version(2, 14));
+
+  static const ExperimentalFlag inferenceUpdate1 = const ExperimentalFlag(
+      name: 'inference-update-1',
+      isEnabledByDefault: false,
+      isExpired: false,
+      enabledVersion: const Version(2, 17),
+      experimentEnabledVersion: const Version(2, 17),
+      experimentReleasedVersion: const Version(2, 17));
+
+  static const ExperimentalFlag macros = const ExperimentalFlag(
+      name: 'macros',
+      isEnabledByDefault: false,
+      isExpired: false,
+      enabledVersion: const Version(2, 17),
+      experimentEnabledVersion: const Version(2, 17),
+      experimentReleasedVersion: const Version(2, 17));
+
+  static const ExperimentalFlag namedArgumentsAnywhere = const ExperimentalFlag(
+      name: 'named-arguments-anywhere',
+      isEnabledByDefault: true,
+      isExpired: false,
+      enabledVersion: const Version(2, 17),
+      experimentEnabledVersion: const Version(2, 17),
+      experimentReleasedVersion: const Version(2, 17));
+
+  static const ExperimentalFlag nonNullable = const ExperimentalFlag(
+      name: 'non-nullable',
+      isEnabledByDefault: true,
+      isExpired: true,
+      enabledVersion: const Version(2, 12),
+      experimentEnabledVersion: const Version(2, 12),
+      experimentReleasedVersion: const Version(2, 10));
+
+  static const ExperimentalFlag nonfunctionTypeAliases = const ExperimentalFlag(
+      name: 'nonfunction-type-aliases',
+      isEnabledByDefault: true,
+      isExpired: true,
+      enabledVersion: const Version(2, 13),
+      experimentEnabledVersion: const Version(2, 13),
+      experimentReleasedVersion: const Version(2, 13));
+
+  static const ExperimentalFlag setLiterals = const ExperimentalFlag(
+      name: 'set-literals',
+      isEnabledByDefault: true,
+      isExpired: true,
+      enabledVersion: const Version(2, 0),
+      experimentEnabledVersion: const Version(2, 0),
+      experimentReleasedVersion: const Version(2, 0));
+
+  static const ExperimentalFlag spreadCollections = const ExperimentalFlag(
+      name: 'spread-collections',
+      isEnabledByDefault: true,
+      isExpired: true,
+      enabledVersion: const Version(2, 0),
+      experimentEnabledVersion: const Version(2, 0),
+      experimentReleasedVersion: const Version(2, 0));
+
+  static const ExperimentalFlag superParameters = const ExperimentalFlag(
+      name: 'super-parameters',
+      isEnabledByDefault: true,
+      isExpired: false,
+      enabledVersion: const Version(2, 17),
+      experimentEnabledVersion: const Version(2, 17),
+      experimentReleasedVersion: const Version(2, 17));
+
+  static const ExperimentalFlag testExperiment = const ExperimentalFlag(
+      name: 'test-experiment',
+      isEnabledByDefault: false,
+      isExpired: false,
+      enabledVersion: const Version(2, 17),
+      experimentEnabledVersion: const Version(2, 17),
+      experimentReleasedVersion: const Version(2, 17));
+
+  static const ExperimentalFlag tripleShift = const ExperimentalFlag(
+      name: 'triple-shift',
+      isEnabledByDefault: true,
+      isExpired: true,
+      enabledVersion: const Version(2, 14),
+      experimentEnabledVersion: const Version(2, 14),
+      experimentReleasedVersion: const Version(2, 14));
+
+  static const ExperimentalFlag valueClass = const ExperimentalFlag(
+      name: 'value-class',
+      isEnabledByDefault: false,
+      isExpired: false,
+      enabledVersion: const Version(2, 17),
+      experimentEnabledVersion: const Version(2, 17),
+      experimentReleasedVersion: const Version(2, 17));
+
+  static const ExperimentalFlag variance = const ExperimentalFlag(
+      name: 'variance',
+      isEnabledByDefault: false,
+      isExpired: false,
+      enabledVersion: const Version(2, 17),
+      experimentEnabledVersion: const Version(2, 17),
+      experimentReleasedVersion: const Version(2, 17));
 }
 
-const Version enableAlternativeInvalidationStrategyVersion =
-    const Version(2, 17);
-const Version enableConstFunctionsVersion = const Version(2, 17);
-const Version enableConstantUpdate2018Version = const Version(2, 0);
-const Version enableConstructorTearoffsVersion = const Version(2, 15);
-const Version enableControlFlowCollectionsVersion = const Version(2, 0);
-const Version enableEnhancedEnumsVersion = const Version(2, 17);
-const Version enableExtensionMethodsVersion = const Version(2, 6);
-const Version enableExtensionTypesVersion = const Version(2, 17);
-const Version enableGenericMetadataVersion = const Version(2, 14);
-const Version enableInferenceUpdate1Version = const Version(2, 17);
-const Version enableMacrosVersion = const Version(2, 17);
-const Version enableNamedArgumentsAnywhereVersion = const Version(2, 17);
-const Version enableNonNullableVersion = const Version(2, 12);
-const Version enableNonfunctionTypeAliasesVersion = const Version(2, 13);
-const Version enableSetLiteralsVersion = const Version(2, 0);
-const Version enableSpreadCollectionsVersion = const Version(2, 0);
-const Version enableSuperParametersVersion = const Version(2, 17);
-const Version enableTestExperimentVersion = const Version(2, 17);
-const Version enableTripleShiftVersion = const Version(2, 14);
-const Version enableValueClassVersion = const Version(2, 17);
-const Version enableVarianceVersion = const Version(2, 17);
+/// Interface for accessing the global state of experimental features.
+class GlobalFeatures {
+  final Map<ExperimentalFlag, bool> explicitExperimentalFlags;
+  final AllowedExperimentalFlags? allowedExperimentalFlags;
+  final Map<ExperimentalFlag, bool>? defaultExperimentFlagsForTesting;
+  final Map<ExperimentalFlag, Version>? experimentEnabledVersionForTesting;
+  final Map<ExperimentalFlag, Version>? experimentReleasedVersionForTesting;
+
+  GlobalFeatures(this.explicitExperimentalFlags,
+      {this.allowedExperimentalFlags,
+      this.defaultExperimentFlagsForTesting,
+      this.experimentEnabledVersionForTesting,
+      this.experimentReleasedVersionForTesting});
+
+  GlobalFeature _computeGlobalFeature(ExperimentalFlag flag) {
+    return new GlobalFeature(
+        flag,
+        isExperimentEnabled(flag,
+            defaultExperimentFlagsForTesting: defaultExperimentFlagsForTesting,
+            explicitExperimentalFlags: explicitExperimentalFlags));
+  }
+
+  LibraryFeature _computeLibraryFeature(
+      ExperimentalFlag flag, Uri canonicalUri, Version libraryVersion) {
+    return new LibraryFeature(
+        flag,
+        isExperimentEnabledInLibrary(flag, canonicalUri,
+            defaultExperimentFlagsForTesting: defaultExperimentFlagsForTesting,
+            explicitExperimentalFlags: explicitExperimentalFlags,
+            allowedExperimentalFlags: allowedExperimentalFlags),
+        getExperimentEnabledVersionInLibrary(
+            flag, canonicalUri, explicitExperimentalFlags,
+            allowedExperimentalFlags: allowedExperimentalFlags,
+            defaultExperimentFlagsForTesting: defaultExperimentFlagsForTesting,
+            experimentEnabledVersionForTesting:
+                experimentEnabledVersionForTesting,
+            experimentReleasedVersionForTesting:
+                experimentReleasedVersionForTesting),
+        isExperimentEnabledInLibraryByVersion(
+            flag, canonicalUri, libraryVersion,
+            defaultExperimentFlagsForTesting: defaultExperimentFlagsForTesting,
+            explicitExperimentalFlags: explicitExperimentalFlags,
+            allowedExperimentalFlags: allowedExperimentalFlags));
+  }
+
+  GlobalFeature? _alternativeInvalidationStrategy;
+  GlobalFeature get alternativeInvalidationStrategy =>
+      _alternativeInvalidationStrategy ??= _computeGlobalFeature(
+          ExperimentalFlag.alternativeInvalidationStrategy);
+
+  GlobalFeature? _constFunctions;
+  GlobalFeature get constFunctions => _constFunctions ??=
+      _computeGlobalFeature(ExperimentalFlag.constFunctions);
+
+  GlobalFeature? _constantUpdate2018;
+  GlobalFeature get constantUpdate2018 => _constantUpdate2018 ??=
+      _computeGlobalFeature(ExperimentalFlag.constantUpdate2018);
+
+  GlobalFeature? _constructorTearoffs;
+  GlobalFeature get constructorTearoffs => _constructorTearoffs ??=
+      _computeGlobalFeature(ExperimentalFlag.constructorTearoffs);
+
+  GlobalFeature? _controlFlowCollections;
+  GlobalFeature get controlFlowCollections => _controlFlowCollections ??=
+      _computeGlobalFeature(ExperimentalFlag.controlFlowCollections);
+
+  GlobalFeature? _enhancedEnums;
+  GlobalFeature get enhancedEnums =>
+      _enhancedEnums ??= _computeGlobalFeature(ExperimentalFlag.enhancedEnums);
+
+  GlobalFeature? _extensionMethods;
+  GlobalFeature get extensionMethods => _extensionMethods ??=
+      _computeGlobalFeature(ExperimentalFlag.extensionMethods);
+
+  GlobalFeature? _extensionTypes;
+  GlobalFeature get extensionTypes => _extensionTypes ??=
+      _computeGlobalFeature(ExperimentalFlag.extensionTypes);
+
+  GlobalFeature? _genericMetadata;
+  GlobalFeature get genericMetadata => _genericMetadata ??=
+      _computeGlobalFeature(ExperimentalFlag.genericMetadata);
+
+  GlobalFeature? _inferenceUpdate1;
+  GlobalFeature get inferenceUpdate1 => _inferenceUpdate1 ??=
+      _computeGlobalFeature(ExperimentalFlag.inferenceUpdate1);
+
+  GlobalFeature? _macros;
+  GlobalFeature get macros =>
+      _macros ??= _computeGlobalFeature(ExperimentalFlag.macros);
+
+  GlobalFeature? _namedArgumentsAnywhere;
+  GlobalFeature get namedArgumentsAnywhere => _namedArgumentsAnywhere ??=
+      _computeGlobalFeature(ExperimentalFlag.namedArgumentsAnywhere);
+
+  GlobalFeature? _nonNullable;
+  GlobalFeature get nonNullable =>
+      _nonNullable ??= _computeGlobalFeature(ExperimentalFlag.nonNullable);
+
+  GlobalFeature? _nonfunctionTypeAliases;
+  GlobalFeature get nonfunctionTypeAliases => _nonfunctionTypeAliases ??=
+      _computeGlobalFeature(ExperimentalFlag.nonfunctionTypeAliases);
+
+  GlobalFeature? _setLiterals;
+  GlobalFeature get setLiterals =>
+      _setLiterals ??= _computeGlobalFeature(ExperimentalFlag.setLiterals);
+
+  GlobalFeature? _spreadCollections;
+  GlobalFeature get spreadCollections => _spreadCollections ??=
+      _computeGlobalFeature(ExperimentalFlag.spreadCollections);
+
+  GlobalFeature? _superParameters;
+  GlobalFeature get superParameters => _superParameters ??=
+      _computeGlobalFeature(ExperimentalFlag.superParameters);
+
+  GlobalFeature? _testExperiment;
+  GlobalFeature get testExperiment => _testExperiment ??=
+      _computeGlobalFeature(ExperimentalFlag.testExperiment);
+
+  GlobalFeature? _tripleShift;
+  GlobalFeature get tripleShift =>
+      _tripleShift ??= _computeGlobalFeature(ExperimentalFlag.tripleShift);
+
+  GlobalFeature? _valueClass;
+  GlobalFeature get valueClass =>
+      _valueClass ??= _computeGlobalFeature(ExperimentalFlag.valueClass);
+
+  GlobalFeature? _variance;
+  GlobalFeature get variance =>
+      _variance ??= _computeGlobalFeature(ExperimentalFlag.variance);
+}
+
+/// Interface for accessing the state of experimental features within a
+/// specific library.
+class LibraryFeatures {
+  final GlobalFeatures globalFeatures;
+  final Uri canonicalUri;
+  final Version libraryVersion;
+
+  LibraryFeatures(this.globalFeatures, this.canonicalUri, this.libraryVersion);
+
+  LibraryFeature? _alternativeInvalidationStrategy;
+  LibraryFeature get alternativeInvalidationStrategy =>
+      _alternativeInvalidationStrategy ??=
+          globalFeatures._computeLibraryFeature(
+              ExperimentalFlag.alternativeInvalidationStrategy,
+              canonicalUri,
+              libraryVersion);
+
+  LibraryFeature? _constFunctions;
+  LibraryFeature get constFunctions =>
+      _constFunctions ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.constFunctions, canonicalUri, libraryVersion);
+
+  LibraryFeature? _constantUpdate2018;
+  LibraryFeature get constantUpdate2018 =>
+      _constantUpdate2018 ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.constantUpdate2018, canonicalUri, libraryVersion);
+
+  LibraryFeature? _constructorTearoffs;
+  LibraryFeature get constructorTearoffs =>
+      _constructorTearoffs ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.constructorTearoffs, canonicalUri, libraryVersion);
+
+  LibraryFeature? _controlFlowCollections;
+  LibraryFeature get controlFlowCollections =>
+      _controlFlowCollections ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.controlFlowCollections,
+          canonicalUri,
+          libraryVersion);
+
+  LibraryFeature? _enhancedEnums;
+  LibraryFeature get enhancedEnums =>
+      _enhancedEnums ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.enhancedEnums, canonicalUri, libraryVersion);
+
+  LibraryFeature? _extensionMethods;
+  LibraryFeature get extensionMethods =>
+      _extensionMethods ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.extensionMethods, canonicalUri, libraryVersion);
+
+  LibraryFeature? _extensionTypes;
+  LibraryFeature get extensionTypes =>
+      _extensionTypes ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.extensionTypes, canonicalUri, libraryVersion);
+
+  LibraryFeature? _genericMetadata;
+  LibraryFeature get genericMetadata =>
+      _genericMetadata ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.genericMetadata, canonicalUri, libraryVersion);
+
+  LibraryFeature? _inferenceUpdate1;
+  LibraryFeature get inferenceUpdate1 =>
+      _inferenceUpdate1 ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.inferenceUpdate1, canonicalUri, libraryVersion);
+
+  LibraryFeature? _macros;
+  LibraryFeature get macros =>
+      _macros ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.macros, canonicalUri, libraryVersion);
+
+  LibraryFeature? _namedArgumentsAnywhere;
+  LibraryFeature get namedArgumentsAnywhere =>
+      _namedArgumentsAnywhere ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.namedArgumentsAnywhere,
+          canonicalUri,
+          libraryVersion);
+
+  LibraryFeature? _nonNullable;
+  LibraryFeature get nonNullable =>
+      _nonNullable ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.nonNullable, canonicalUri, libraryVersion);
+
+  LibraryFeature? _nonfunctionTypeAliases;
+  LibraryFeature get nonfunctionTypeAliases =>
+      _nonfunctionTypeAliases ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.nonfunctionTypeAliases,
+          canonicalUri,
+          libraryVersion);
+
+  LibraryFeature? _setLiterals;
+  LibraryFeature get setLiterals =>
+      _setLiterals ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.setLiterals, canonicalUri, libraryVersion);
+
+  LibraryFeature? _spreadCollections;
+  LibraryFeature get spreadCollections =>
+      _spreadCollections ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.spreadCollections, canonicalUri, libraryVersion);
+
+  LibraryFeature? _superParameters;
+  LibraryFeature get superParameters =>
+      _superParameters ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.superParameters, canonicalUri, libraryVersion);
+
+  LibraryFeature? _testExperiment;
+  LibraryFeature get testExperiment =>
+      _testExperiment ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.testExperiment, canonicalUri, libraryVersion);
+
+  LibraryFeature? _tripleShift;
+  LibraryFeature get tripleShift =>
+      _tripleShift ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.tripleShift, canonicalUri, libraryVersion);
+
+  LibraryFeature? _valueClass;
+  LibraryFeature get valueClass =>
+      _valueClass ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.valueClass, canonicalUri, libraryVersion);
+
+  LibraryFeature? _variance;
+  LibraryFeature get variance =>
+      _variance ??= globalFeatures._computeLibraryFeature(
+          ExperimentalFlag.variance, canonicalUri, libraryVersion);
+}
 
 ExperimentalFlag? parseExperimentalFlag(String flag) {
   switch (flag) {
@@ -104,102 +523,44 @@
   return null;
 }
 
-const Map<ExperimentalFlag, bool> defaultExperimentalFlags = {
-  ExperimentalFlag.alternativeInvalidationStrategy: false,
-  ExperimentalFlag.constFunctions: false,
-  ExperimentalFlag.constantUpdate2018: true,
-  ExperimentalFlag.constructorTearoffs: true,
-  ExperimentalFlag.controlFlowCollections: true,
-  ExperimentalFlag.enhancedEnums: true,
-  ExperimentalFlag.extensionMethods: true,
-  ExperimentalFlag.extensionTypes: false,
-  ExperimentalFlag.genericMetadata: true,
-  ExperimentalFlag.inferenceUpdate1: false,
-  ExperimentalFlag.macros: false,
-  ExperimentalFlag.namedArgumentsAnywhere: true,
-  ExperimentalFlag.nonNullable: true,
-  ExperimentalFlag.nonfunctionTypeAliases: true,
-  ExperimentalFlag.setLiterals: true,
-  ExperimentalFlag.spreadCollections: true,
-  ExperimentalFlag.superParameters: true,
-  ExperimentalFlag.testExperiment: false,
-  ExperimentalFlag.tripleShift: true,
-  ExperimentalFlag.valueClass: false,
-  ExperimentalFlag.variance: false,
+final Map<ExperimentalFlag, bool> defaultExperimentalFlags = {
+  ExperimentalFlag.alternativeInvalidationStrategy:
+      ExperimentalFlag.alternativeInvalidationStrategy.isEnabledByDefault,
+  ExperimentalFlag.constFunctions:
+      ExperimentalFlag.constFunctions.isEnabledByDefault,
+  ExperimentalFlag.constantUpdate2018:
+      ExperimentalFlag.constantUpdate2018.isEnabledByDefault,
+  ExperimentalFlag.constructorTearoffs:
+      ExperimentalFlag.constructorTearoffs.isEnabledByDefault,
+  ExperimentalFlag.controlFlowCollections:
+      ExperimentalFlag.controlFlowCollections.isEnabledByDefault,
+  ExperimentalFlag.enhancedEnums:
+      ExperimentalFlag.enhancedEnums.isEnabledByDefault,
+  ExperimentalFlag.extensionMethods:
+      ExperimentalFlag.extensionMethods.isEnabledByDefault,
+  ExperimentalFlag.extensionTypes:
+      ExperimentalFlag.extensionTypes.isEnabledByDefault,
+  ExperimentalFlag.genericMetadata:
+      ExperimentalFlag.genericMetadata.isEnabledByDefault,
+  ExperimentalFlag.inferenceUpdate1:
+      ExperimentalFlag.inferenceUpdate1.isEnabledByDefault,
+  ExperimentalFlag.macros: ExperimentalFlag.macros.isEnabledByDefault,
+  ExperimentalFlag.namedArgumentsAnywhere:
+      ExperimentalFlag.namedArgumentsAnywhere.isEnabledByDefault,
+  ExperimentalFlag.nonNullable: ExperimentalFlag.nonNullable.isEnabledByDefault,
+  ExperimentalFlag.nonfunctionTypeAliases:
+      ExperimentalFlag.nonfunctionTypeAliases.isEnabledByDefault,
+  ExperimentalFlag.setLiterals: ExperimentalFlag.setLiterals.isEnabledByDefault,
+  ExperimentalFlag.spreadCollections:
+      ExperimentalFlag.spreadCollections.isEnabledByDefault,
+  ExperimentalFlag.superParameters:
+      ExperimentalFlag.superParameters.isEnabledByDefault,
+  ExperimentalFlag.testExperiment:
+      ExperimentalFlag.testExperiment.isEnabledByDefault,
+  ExperimentalFlag.tripleShift: ExperimentalFlag.tripleShift.isEnabledByDefault,
+  ExperimentalFlag.valueClass: ExperimentalFlag.valueClass.isEnabledByDefault,
+  ExperimentalFlag.variance: ExperimentalFlag.variance.isEnabledByDefault,
 };
-
-const Map<ExperimentalFlag, bool> expiredExperimentalFlags = {
-  ExperimentalFlag.alternativeInvalidationStrategy: false,
-  ExperimentalFlag.constFunctions: false,
-  ExperimentalFlag.constantUpdate2018: true,
-  ExperimentalFlag.constructorTearoffs: true,
-  ExperimentalFlag.controlFlowCollections: true,
-  ExperimentalFlag.enhancedEnums: false,
-  ExperimentalFlag.extensionMethods: true,
-  ExperimentalFlag.extensionTypes: false,
-  ExperimentalFlag.genericMetadata: true,
-  ExperimentalFlag.inferenceUpdate1: false,
-  ExperimentalFlag.macros: false,
-  ExperimentalFlag.namedArgumentsAnywhere: false,
-  ExperimentalFlag.nonNullable: true,
-  ExperimentalFlag.nonfunctionTypeAliases: true,
-  ExperimentalFlag.setLiterals: true,
-  ExperimentalFlag.spreadCollections: true,
-  ExperimentalFlag.superParameters: false,
-  ExperimentalFlag.testExperiment: false,
-  ExperimentalFlag.tripleShift: true,
-  ExperimentalFlag.valueClass: false,
-  ExperimentalFlag.variance: false,
-};
-
-const Map<ExperimentalFlag, Version> experimentEnabledVersion = {
-  ExperimentalFlag.alternativeInvalidationStrategy: const Version(2, 17),
-  ExperimentalFlag.constFunctions: const Version(2, 17),
-  ExperimentalFlag.constantUpdate2018: const Version(2, 0),
-  ExperimentalFlag.constructorTearoffs: const Version(2, 15),
-  ExperimentalFlag.controlFlowCollections: const Version(2, 0),
-  ExperimentalFlag.enhancedEnums: const Version(2, 17),
-  ExperimentalFlag.extensionMethods: const Version(2, 6),
-  ExperimentalFlag.extensionTypes: const Version(2, 17),
-  ExperimentalFlag.genericMetadata: const Version(2, 14),
-  ExperimentalFlag.inferenceUpdate1: const Version(2, 17),
-  ExperimentalFlag.macros: const Version(2, 17),
-  ExperimentalFlag.namedArgumentsAnywhere: const Version(2, 17),
-  ExperimentalFlag.nonNullable: const Version(2, 12),
-  ExperimentalFlag.nonfunctionTypeAliases: const Version(2, 13),
-  ExperimentalFlag.setLiterals: const Version(2, 0),
-  ExperimentalFlag.spreadCollections: const Version(2, 0),
-  ExperimentalFlag.superParameters: const Version(2, 17),
-  ExperimentalFlag.testExperiment: const Version(2, 17),
-  ExperimentalFlag.tripleShift: const Version(2, 14),
-  ExperimentalFlag.valueClass: const Version(2, 17),
-  ExperimentalFlag.variance: const Version(2, 17),
-};
-
-const Map<ExperimentalFlag, Version> experimentReleasedVersion = {
-  ExperimentalFlag.alternativeInvalidationStrategy: const Version(2, 17),
-  ExperimentalFlag.constFunctions: const Version(2, 17),
-  ExperimentalFlag.constantUpdate2018: const Version(2, 0),
-  ExperimentalFlag.constructorTearoffs: const Version(2, 15),
-  ExperimentalFlag.controlFlowCollections: const Version(2, 0),
-  ExperimentalFlag.enhancedEnums: const Version(2, 17),
-  ExperimentalFlag.extensionMethods: const Version(2, 6),
-  ExperimentalFlag.extensionTypes: const Version(2, 17),
-  ExperimentalFlag.genericMetadata: const Version(2, 14),
-  ExperimentalFlag.inferenceUpdate1: const Version(2, 17),
-  ExperimentalFlag.macros: const Version(2, 17),
-  ExperimentalFlag.namedArgumentsAnywhere: const Version(2, 17),
-  ExperimentalFlag.nonNullable: const Version(2, 10),
-  ExperimentalFlag.nonfunctionTypeAliases: const Version(2, 13),
-  ExperimentalFlag.setLiterals: const Version(2, 0),
-  ExperimentalFlag.spreadCollections: const Version(2, 0),
-  ExperimentalFlag.superParameters: const Version(2, 17),
-  ExperimentalFlag.testExperiment: const Version(2, 17),
-  ExperimentalFlag.tripleShift: const Version(2, 14),
-  ExperimentalFlag.valueClass: const Version(2, 17),
-  ExperimentalFlag.variance: const Version(2, 17),
-};
-
 const AllowedExperimentalFlags defaultAllowedExperimentalFlags =
     const AllowedExperimentalFlags(
         sdkDefaultExperiments: {},
diff --git a/pkg/front_end/lib/src/base/processed_options.dart b/pkg/front_end/lib/src/base/processed_options.dart
index d129e23..959eb38 100644
--- a/pkg/front_end/lib/src/base/processed_options.dart
+++ b/pkg/front_end/lib/src/base/processed_options.dart
@@ -364,35 +364,8 @@
   Target get target =>
       _target ??= _raw.target ?? new NoneTarget(new TargetFlags());
 
-  /// Returns `true` if the [flag] is enabled globally by default.
-  bool isExperimentEnabledByDefault(flags.ExperimentalFlag flag) {
-    return flags.isExperimentEnabled(flag,
-        defaultExperimentFlagsForTesting:
-            _raw.defaultExperimentFlagsForTesting);
-  }
-
-  /// Returns `true` if the [flag] is enabled globally.
-  ///
-  /// This is `true` either if the [flag] is passed through an explicit
-  /// `--enable-experiment` option or if the [flag] is expired and on by
-  /// default.
-  bool isExperimentEnabledGlobally(flags.ExperimentalFlag flag) {
-    return flags.isExperimentEnabled(flag,
-        explicitExperimentalFlags: _raw.explicitExperimentalFlags,
-        defaultExperimentFlagsForTesting:
-            _raw.defaultExperimentFlagsForTesting);
-  }
-
-  /// Returns `true` if the experiment with the given [flag] is enabled either
-  /// explicitly or implicitly for the library with the given [importUri].
-  ///
-  /// Note that the library can still opt out of the experiment by having a
-  /// lower language version than required for the experiment. See
-  /// [getExperimentEnabledVersionInLibrary].
-  bool isExperimentEnabledInLibrary(
-      flags.ExperimentalFlag flag, Uri importUri) {
-    return _raw.isExperimentEnabledInLibrary(flag, importUri);
-  }
+  /// Returns the global state of the experimental features.
+  flags.GlobalFeatures get globalFeatures => _raw.globalFeatures;
 
   /// Returns the minimum language version needed for a library with the given
   /// [importUri] to opt in to the experiment with the given [flag].
diff --git a/pkg/front_end/lib/src/fasta/builder/extension_builder.dart b/pkg/front_end/lib/src/fasta/builder/extension_builder.dart
index 93079f4..f0deb80 100644
--- a/pkg/front_end/lib/src/fasta/builder/extension_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/extension_builder.dart
@@ -75,7 +75,7 @@
   DartType buildType(LibraryBuilder library,
       NullabilityBuilder nullabilityBuilder, List<TypeBuilder>? arguments) {
     if (library is SourceLibraryBuilder &&
-        library.enableExtensionTypesInLibrary) {
+        library.libraryFeatures.extensionTypes.isEnabled) {
       return buildTypeWithBuiltArguments(
           library,
           nullabilityBuilder.build(library),
@@ -90,7 +90,7 @@
   DartType buildTypeWithBuiltArguments(LibraryBuilder library,
       Nullability nullability, List<DartType> arguments) {
     if (library is SourceLibraryBuilder &&
-        library.enableExtensionTypesInLibrary) {
+        library.libraryFeatures.extensionTypes.isEnabled) {
       return new ExtensionType(extension, nullability, arguments);
     } else {
       throw new UnsupportedError(
diff --git a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
index d885de8..6855fda 100644
--- a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
@@ -16,7 +16,6 @@
         messageTypeVariableInStaticContext,
         messageTypedefCause,
         noLength,
-        templateExperimentNotEnabled,
         templateExtendingRestricted,
         templateNotAType,
         templateSupertypeIsIllegal,
@@ -269,10 +268,9 @@
     }
     if (_declaration!.isExtension &&
         library is SourceLibraryBuilder &&
-        !library.enableExtensionTypesInLibrary) {
-      Message message = templateExperimentNotEnabled.withArguments(
-          'extension-types',
-          library.enableExtensionTypesVersionInLibrary.toText());
+        !library.libraryFeatures.extensionTypes.isEnabled) {
+      Message message =
+          library.libraryFeatures.extensionTypes.notEnabledMessage;
       int typeNameLength = nameLength;
       int typeNameOffset = nameOffset;
       library.addProblem(message, typeNameOffset, typeNameLength, fileUri);
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index 5dc9c13..410e9b1 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -62,8 +62,6 @@
 
 import '../api_prototype/compiler_options.dart' show CompilerOptions;
 
-import '../api_prototype/experimental_flags.dart';
-
 import '../api_prototype/file_system.dart' show FileSystem, FileSystemEntity;
 
 import '../api_prototype/incremental_kernel_generator.dart'
@@ -983,7 +981,7 @@
     }
     // Check compilation mode up against what we've seen here and set
     // `hasInvalidNnbdModeLibrary` accordingly.
-    if (c.options.isExperimentEnabledGlobally(ExperimentalFlag.nonNullable)) {
+    if (c.options.globalFeatures.nonNullable.isEnabled) {
       switch (c.options.nnbdMode) {
         case NnbdMode.Weak:
           // Don't expect strong or invalid.
@@ -1150,8 +1148,10 @@
     Set<LibraryBuilder> originalNotReusedLibraries;
     Set<Uri>? missingSources;
 
-    if (!context.options.isExperimentEnabledGlobally(
-        ExperimentalFlag.alternativeInvalidationStrategy)) return null;
+    if (!context
+        .options.globalFeatures.alternativeInvalidationStrategy.isEnabled) {
+      return null;
+    }
     if (_modulesToLoad != null) return null;
     if (reusedResult.directlyInvalidated.isEmpty) return null;
     if (reusedResult.invalidatedBecauseOfPackageUpdate) return null;
@@ -1199,8 +1199,7 @@
           enableTripleShift:
               /* should this be on the library? */
               /* this is effectively what the constant evaluator does */
-              context.options
-                  .isExperimentEnabledGlobally(ExperimentalFlag.tripleShift));
+              context.options.globalFeatures.tripleShift.isEnabled);
       String? before = textualOutline(previousSource, scannerConfiguration,
           performModelling: true);
       if (before == null) {
@@ -2603,8 +2602,7 @@
 
         // Compute "output nnbd mode".
         NonNullableByDefaultCompiledMode compiledMode;
-        if (context.options
-            .isExperimentEnabledGlobally(ExperimentalFlag.nonNullable)) {
+        if (context.options.globalFeatures.nonNullable.isEnabled) {
           switch (context.options.nnbdMode) {
             case NnbdMode.Weak:
               compiledMode = NonNullableByDefaultCompiledMode.Weak;
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index e4866f3..b9b2813 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -33,6 +33,7 @@
 import 'package:_fe_analyzer_shared/src/scanner/token_impl.dart'
     show isBinaryOperator, isMinusOperator, isUserDefinableOperator;
 import 'package:_fe_analyzer_shared/src/util/link.dart';
+import 'package:front_end/src/api_prototype/experimental_flags.dart';
 import 'package:front_end/src/fasta/kernel/benchmarker.dart' show Benchmarker;
 import 'package:kernel/ast.dart';
 import 'package:kernel/class_hierarchy.dart';
@@ -67,13 +68,7 @@
 import '../constant_context.dart' show ConstantContext;
 import '../dill/dill_library_builder.dart' show DillLibraryBuilder;
 import '../fasta_codes.dart' as fasta;
-import '../fasta_codes.dart'
-    show
-        LocatedMessage,
-        Message,
-        Template,
-        noLength,
-        templateExperimentNotEnabled;
+import '../fasta_codes.dart' show LocatedMessage, Message, Template, noLength;
 import '../identifiers.dart'
     show Identifier, InitializedIdentifier, QualifiedName, flattenName;
 import '../messages.dart' as messages show getLocationFromUri;
@@ -601,24 +596,7 @@
   DartType get implicitTypeArgument => const ImplicitTypeArgument();
 
   @override
-  bool get enableExtensionTypesInLibrary {
-    return libraryBuilder.enableExtensionTypesInLibrary;
-  }
-
-  @override
-  bool get enableConstFunctionsInLibrary {
-    return libraryBuilder.enableConstFunctionsInLibrary;
-  }
-
-  @override
-  bool get enableConstructorTearOffsInLibrary {
-    return libraryBuilder.enableConstructorTearOffsInLibrary;
-  }
-
-  @override
-  bool get enableNamedArgumentsAnywhereInLibrary {
-    return libraryBuilder.enableNamedArgumentsAnywhereInLibrary;
-  }
+  LibraryFeatures get libraryFeatures => libraryBuilder.libraryFeatures;
 
   void _enterLocalState({bool inLateLocalInitializer: false}) {
     _localInitializerState =
@@ -1893,7 +1871,7 @@
               buildProblem(fasta.messageEnumConstructorSuperInitializer,
                   superInitializer.fileOffset, noLength))
             ..parent = constructor;
-        } else if (libraryBuilder.enableSuperParametersInLibrary) {
+        } else if (libraryFeatures.superParameters.isEnabled) {
           ArgumentsImpl arguments = superInitializer.arguments as ArgumentsImpl;
 
           if (positionalSuperParametersAsArguments != null) {
@@ -1926,7 +1904,7 @@
         RedirectingInitializer redirectingInitializer =
             initializers.last as RedirectingInitializer;
         if (sourceClassBuilder is SourceEnumBuilder &&
-            libraryBuilder.enableEnhancedEnumsInLibrary) {
+            libraryFeatures.enhancedEnums.isEnabled) {
           ArgumentsImpl arguments =
               redirectingInitializer.arguments as ArgumentsImpl;
           List<Expression> enumSyntheticArguments = [
@@ -1971,7 +1949,7 @@
       ArgumentsImpl arguments;
       List<Expression>? positionalArguments;
       List<NamedExpression>? namedArguments;
-      if (libraryBuilder.enableSuperParametersInLibrary) {
+      if (libraryFeatures.superParameters.isEnabled) {
         positionalArguments = positionalSuperParametersAsArguments;
         namedArguments = namedSuperParametersAsArguments;
       }
@@ -2020,7 +1998,7 @@
         initializer = buildSuperInitializer(
             true, superTarget, arguments, builder.charOffset);
       }
-      if (libraryBuilder.enableSuperParametersInLibrary) {
+      if (libraryFeatures.superParameters.isEnabled) {
         InitializerInferenceResult inferenceResult =
             typeInferrer.inferInitializer(this, initializer);
         builder.addInitializer(initializer, this,
@@ -2066,7 +2044,7 @@
       return;
     }
     List<Object?>? argumentsOriginalOrder;
-    if (libraryBuilder.enableNamedArgumentsAnywhereInLibrary) {
+    if (libraryFeatures.namedArgumentsAnywhere.isEnabled) {
       argumentsOriginalOrder = new List<Object?>.of(arguments);
     }
     int firstNamedArgumentIndex = arguments.length;
@@ -2084,7 +2062,7 @@
         argumentsOriginalOrder?[i] = argument;
         if (i > firstNamedArgumentIndex) {
           hasNamedBeforePositional = true;
-          if (!libraryBuilder.enableNamedArgumentsAnywhereInLibrary) {
+          if (!libraryFeatures.namedArgumentsAnywhere.isEnabled) {
             arguments[i] = new NamedExpression(
                 "#$i",
                 buildProblem(fasta.messageExpectedNamedArgument,
@@ -2100,7 +2078,7 @@
     if (firstNamedArgumentIndex < arguments.length) {
       List<Expression> positional;
       List<NamedExpression> named;
-      if (libraryBuilder.enableNamedArgumentsAnywhereInLibrary) {
+      if (libraryFeatures.namedArgumentsAnywhere.isEnabled) {
         positional = new List<Expression>.filled(
             positionalCount, dummyExpression,
             growable: true);
@@ -2905,7 +2883,7 @@
       if (constantContext != ConstantContext.none &&
           !variableBuilder.isConst &&
           !member.isConstructor &&
-          !enableConstFunctionsInLibrary) {
+          !libraryFeatures.constFunctions.isEnabled) {
         return new IncompleteErrorGenerator(
             this, token, fasta.messageNotAConstantExpression);
       }
@@ -4120,7 +4098,7 @@
     if (name is Generator) {
       bool allowPotentiallyConstantType;
       if (libraryBuilder.isNonNullableByDefault) {
-        if (enableConstructorTearOffsInLibrary) {
+        if (libraryFeatures.constructorTearoffs.isEnabled) {
           allowPotentiallyConstantType = true;
         } else {
           allowPotentiallyConstantType = inIsOrAsOperatorType;
@@ -4188,7 +4166,7 @@
     if (typeVariables != null) {
       for (TypeVariableBuilder builder in typeVariables) {
         if (builder.parameter.annotations.isNotEmpty) {
-          if (!libraryBuilder.enableGenericMetadataInLibrary) {
+          if (!libraryFeatures.genericMetadata.isEnabled) {
             addProblem(fasta.messageAnnotationOnFunctionTypeTypeVariable,
                 builder.charOffset, builder.name.length);
           }
@@ -4385,7 +4363,9 @@
       buildDartType(type, allowPotentiallyConstantType: false);
     }
     Token? varOrFinalOrConst = pop(NullValue.Token) as Token?;
-    if (superKeyword != null && varOrFinalOrConst != null) {
+    if (superKeyword != null &&
+        varOrFinalOrConst != null &&
+        optional('var', varOrFinalOrConst)) {
       handleRecoverableError(
           fasta.templateExtraneousModifier.withArguments(varOrFinalOrConst),
           varOrFinalOrConst,
@@ -5239,7 +5219,7 @@
     String name = pop() as String;
     List<TypeBuilder>? typeArguments = pop() as List<TypeBuilder>?;
     if (inMetadata && typeArguments != null) {
-      if (!libraryBuilder.enableGenericMetadataInLibrary) {
+      if (!libraryFeatures.genericMetadata.isEnabled) {
         handleRecoverableError(fasta.messageMetadataTypeArguments,
             nameLastToken.next!, nameLastToken.next!);
       }
@@ -5291,7 +5271,8 @@
       {required int instantiationOffset,
       required int invocationOffset,
       required bool inImplicitCreationContext}) {
-    if (enableConstructorTearOffsInLibrary && inImplicitCreationContext) {
+    if (libraryFeatures.constructorTearoffs.isEnabled &&
+        inImplicitCreationContext) {
       Expression receiver = receiverFunction();
       if (typeArguments != null) {
         if (receiver is StaticTearOff &&
@@ -5565,7 +5546,7 @@
         target = b.member;
       }
       if (type.isEnum &&
-          !(libraryBuilder.enableEnhancedEnumsInLibrary &&
+          !(libraryFeatures.enhancedEnums.isEnabled &&
               target is Procedure &&
               target.kind == ProcedureKind.Factory)) {
         return buildProblem(fasta.messageEnumInstantiation,
@@ -5610,7 +5591,7 @@
   @override
   void handleConstFactory(Token constKeyword) {
     debugEvent("ConstFactory");
-    if (!libraryBuilder.enableConstFunctionsInLibrary) {
+    if (!libraryFeatures.constFunctions.isEnabled) {
       handleRecoverableError(
           fasta.messageConstFactory, constKeyword, constKeyword);
     }
@@ -6837,7 +6818,7 @@
     TypeVariableBuilder variable = typeVariables[index];
     variable.bound = bound;
     if (variance != null) {
-      if (!libraryBuilder.enableVarianceInLibrary) {
+      if (!libraryFeatures.variance.isEnabled) {
         reportVarianceModifierNotEnabled(variance);
       }
       variable.variance = Variance.fromString(variance.lexeme);
@@ -7198,7 +7179,7 @@
     ]));
     List<TypeBuilder>? typeArguments =
         pop() as List<TypeBuilder>?; // typeArguments
-    if (libraryBuilder.enableConstructorTearOffsInLibrary) {
+    if (libraryFeatures.constructorTearoffs.isEnabled) {
       Object? operand = pop();
       if (operand is Generator) {
         push(operand.applyTypeArguments(
@@ -7217,13 +7198,8 @@
           ..fileOffset = openAngleBracket.charOffset);
       }
     } else {
-      addProblem(
-          templateExperimentNotEnabled.withArguments(
-              'constructor-tearoffs',
-              libraryBuilder.enableConstructorTearOffsVersionInLibrary
-                  .toText()),
-          openAngleBracket.charOffset,
-          noLength);
+      addProblem(libraryFeatures.constructorTearoffs.notEnabledMessage,
+          openAngleBracket.charOffset, noLength);
     }
   }
 
@@ -7308,7 +7284,7 @@
       {bool isConstantExpression: false, bool isNullAware: false}) {
     if (constantContext != ConstantContext.none &&
         !isConstantExpression &&
-        !enableConstFunctionsInLibrary) {
+        !libraryFeatures.constFunctions.isEnabled) {
       return buildProblem(
           fasta.templateNotConstantExpression
               .withArguments('Method invocation'),
@@ -7338,7 +7314,7 @@
       bool isImplicitCall: false}) {
     if (constantContext != ConstantContext.none &&
         !isConstantExpression &&
-        !enableConstFunctionsInLibrary) {
+        !libraryFeatures.constFunctions.isEnabled) {
       return buildProblem(
           fasta.templateNotConstantExpression
               .withArguments('Method invocation'),
@@ -7480,15 +7456,8 @@
 
   @override
   void handleNewAsIdentifier(Token token) {
-    if (!libraryBuilder.enableConstructorTearOffsInLibrary) {
-      addProblem(
-          templateExperimentNotEnabled.withArguments(
-              'constructor-tearoffs',
-              libraryBuilder.enableConstructorTearOffsVersionInLibrary
-                  .toText()),
-          token.charOffset,
-          token.length);
-    }
+    reportIfNotEnabled(
+        libraryFeatures.constructorTearoffs, token.charOffset, token.length);
   }
 }
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
index 4c64564..927fca16 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -1503,7 +1503,7 @@
       {bool isTypeArgumentsInForest = false}) {
     if (_helper.constantContext != ConstantContext.none &&
         !_helper.isIdentical(readTarget) &&
-        !_helper.enableConstFunctionsInLibrary) {
+        !_helper.libraryFeatures.constFunctions.isEnabled) {
       return _helper.buildProblem(
           templateNotConstantExpression.withArguments('Method invocation'),
           offset,
@@ -3085,7 +3085,7 @@
                     _helper.libraryBuilder.nonNullableBuilder, typeArguments,
                     allowPotentiallyConstantType: true, forTypeLiteral: true),
                 allowPotentiallyConstantType:
-                    _helper.enableConstructorTearOffsInLibrary));
+                    _helper.libraryFeatures.constructorTearoffs.isEnabled));
       }
     }
     return _expression!;
@@ -3110,7 +3110,7 @@
           usedAsClassFileUri: _uri);
 
       bool isConstructorTearOff = send is PropertySelector &&
-          _helper.enableConstructorTearOffsInLibrary &&
+          _helper.libraryFeatures.constructorTearoffs.isEnabled &&
           declarationBuilder is ClassBuilder;
       List<TypeBuilder>? aliasedTypeArguments = typeArguments
           ?.map((unknownType) => _helper.validateTypeVariableUse(unknownType,
@@ -3173,7 +3173,7 @@
               "Unexpected non-null typeArguments of "
               "an IncompletePropertyAccessGenerator object: "
               "'${send.typeArguments.runtimeType}'.");
-          if (_helper.enableConstructorTearOffsInLibrary &&
+          if (_helper.libraryFeatures.constructorTearoffs.isEnabled &&
               declarationBuilder is ClassBuilder) {
             MemberBuilder? constructor =
                 declarationBuilder.findConstructorOrFactory(
@@ -4786,7 +4786,8 @@
   /// Report an error if the selector name "new" when the constructor-tearoff
   /// feature is enabled.
   void reportNewAsSelector() {
-    if (name.text == 'new' && _helper.enableConstructorTearOffsInLibrary) {
+    if (name.text == 'new' &&
+        _helper.libraryFeatures.constructorTearoffs.isEnabled) {
       _helper.addProblem(messageNewAsSelector, fileOffset, name.text.length);
     }
   }
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
index a141e17..41ab864 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
@@ -9,20 +9,19 @@
 import 'package:kernel/type_algebra.dart';
 import 'package:kernel/type_environment.dart';
 
+import '../../api_prototype/experimental_flags.dart';
 import '../builder/builder.dart';
 import '../builder/formal_parameter_builder.dart';
 import '../builder/named_type_builder.dart';
 import '../builder/prefix_builder.dart';
 import '../builder/type_builder.dart';
 import '../builder/type_declaration_builder.dart';
-
 import '../constant_context.dart' show ConstantContext;
 import '../fasta_codes.dart' show LocatedMessage;
 import '../messages.dart' show Message;
 import '../scope.dart';
 import '../source/source_library_builder.dart' show SourceLibraryBuilder;
 import '../type_inference/inference_helper.dart' show InferenceHelper;
-
 import 'constness.dart' show Constness;
 import 'forest.dart' show Forest;
 import 'internal_ast.dart';
@@ -57,13 +56,7 @@
 
   Member? lookupSuperMember(Name name, {bool isSetter});
 
-  bool get enableExtensionTypesInLibrary;
-
-  bool get enableConstFunctionsInLibrary;
-
-  bool get enableConstructorTearOffsInLibrary;
-
-  bool get enableNamedArgumentsAnywhereInLibrary;
+  LibraryFeatures get libraryFeatures;
 
   Expression_Generator_Builder scopeLookup(
       Scope scope, String name, Token token,
diff --git a/pkg/front_end/lib/src/fasta/kernel/hierarchy/members_node.dart b/pkg/front_end/lib/src/fasta/kernel/hierarchy/members_node.dart
index 1657161..8672ea7 100644
--- a/pkg/front_end/lib/src/fasta/kernel/hierarchy/members_node.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/hierarchy/members_node.dart
@@ -37,6 +37,7 @@
         templateCantInferTypeDueToNoCombinedSignature,
         templateDuplicatedDeclaration,
         templateDuplicatedDeclarationCause,
+        templateInstanceAndSynthesizedStaticConflict,
         templateMissingImplementationCause,
         templateMissingImplementationNotAbstract;
 import '../../names.dart' show noSuchMethodName;
@@ -531,12 +532,23 @@
         staticMember = b;
         instanceMember = a;
       }
-      classBuilder.libraryBuilder.addProblem(messageStaticAndInstanceConflict,
-          staticMember.charOffset, name.length, staticMember.fileUri,
-          context: <LocatedMessage>[
-            messageStaticAndInstanceConflictCause.withLocation(
-                instanceMember.fileUri, instanceMember.charOffset, name.length)
-          ]);
+      if (!staticMember.isSynthesized) {
+        classBuilder.libraryBuilder.addProblem(messageStaticAndInstanceConflict,
+            staticMember.charOffset, name.length, staticMember.fileUri,
+            context: <LocatedMessage>[
+              messageStaticAndInstanceConflictCause.withLocation(
+                  instanceMember.fileUri,
+                  instanceMember.charOffset,
+                  name.length)
+            ]);
+      } else {
+        classBuilder.libraryBuilder.addProblem(
+            templateInstanceAndSynthesizedStaticConflict
+                .withArguments(staticMember.name.text),
+            instanceMember.charOffset,
+            name.length,
+            instanceMember.fileUri);
+      }
     } else {
       // This message can be reported twice (when merging localMembers with
       // classSetters, or localSetters with classMembers). By ensuring that
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 07ea5fb..f7b6898 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -6461,7 +6461,7 @@
       TypeLiteral node, DartType typeContext) {
     DartType inferredType =
         inferrer.coreTypes.typeRawType(inferrer.libraryBuilder.nonNullable);
-    if (inferrer.libraryBuilder.enableConstructorTearOffsInLibrary) {
+    if (inferrer.libraryFeatures.constructorTearoffs.isEnabled) {
       inferrer.libraryBuilder.checkBoundsInType(
           node.type,
           inferrer.typeSchemaEnvironment,
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index 7912ac66..176a1b9 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -17,7 +17,8 @@
 import 'package:kernel/type_environment.dart' show TypeEnvironment;
 import 'package:package_config/package_config.dart' hide LanguageVersion;
 
-import '../../api_prototype/experimental_flags.dart' show ExperimentalFlag;
+import '../../api_prototype/experimental_flags.dart'
+    show ExperimentalFlag, GlobalFeatures;
 import '../../api_prototype/file_system.dart' show FileSystem;
 import '../../base/nnbd_mode.dart';
 import '../../base/processed_options.dart' show ProcessedOptions;
@@ -171,9 +172,7 @@
     loader = createLoader();
   }
 
-  bool isExperimentEnabledInLibrary(ExperimentalFlag flag, Uri importUri) {
-    return _options.isExperimentEnabledInLibrary(flag, importUri);
-  }
+  GlobalFeatures get globalFeatures => _options.globalFeatures;
 
   Version getExperimentEnabledVersionInLibrary(
       ExperimentalFlag flag, Uri importUri) {
@@ -186,20 +185,6 @@
         flag, importUri, version);
   }
 
-  /// Returns `true` if the [flag] is enabled by default.
-  bool isExperimentEnabledByDefault(ExperimentalFlag flag) {
-    return _options.isExperimentEnabledByDefault(flag);
-  }
-
-  /// Returns `true` if the [flag] is enabled globally.
-  ///
-  /// This is `true` either if the [flag] is passed through an explicit
-  /// `--enable-experiment` option or if the [flag] is expired and on by
-  /// default.
-  bool isExperimentEnabledGlobally(ExperimentalFlag flag) {
-    return _options.isExperimentEnabledGlobally(flag);
-  }
-
   Uri? translateUri(Uri uri) => uriTranslator.translate(uri);
 
   /// Returns a reference to the constructor of
@@ -704,7 +689,7 @@
         nameRoot: nameRoot, libraries: libraries, uriToSource: uriToSource));
 
     NonNullableByDefaultCompiledMode? compiledMode = null;
-    if (isExperimentEnabledGlobally(ExperimentalFlag.nonNullable)) {
+    if (globalFeatures.nonNullable.isEnabled) {
       switch (loader.nnbdMode) {
         case NnbdMode.Weak:
           compiledMode = NonNullableByDefaultCompiledMode.Weak;
@@ -1595,12 +1580,10 @@
             new KernelConstantErrorReporter(loader),
             evaluationMode,
             evaluateAnnotations: true,
-            enableTripleShift:
-                isExperimentEnabledGlobally(ExperimentalFlag.tripleShift),
-            enableConstFunctions:
-                isExperimentEnabledGlobally(ExperimentalFlag.constFunctions),
-            enableConstructorTearOff: isExperimentEnabledGlobally(
-                ExperimentalFlag.constructorTearoffs),
+            enableTripleShift: globalFeatures.tripleShift.isEnabled,
+            enableConstFunctions: globalFeatures.constFunctions.isEnabled,
+            enableConstructorTearOff:
+                globalFeatures.constructorTearoffs.isEnabled,
             errorOnUnevaluatedConstant: errorOnUnevaluatedConstant);
     ticker.logMs("Evaluated constants");
 
@@ -1617,8 +1600,7 @@
     });
     ticker.logMs("Added constant coverage");
 
-    if (loader.target.context.options
-        .isExperimentEnabledGlobally(ExperimentalFlag.valueClass)) {
+    if (loader.target.context.options.globalFeatures.valueClass.isEnabled) {
       valueClass.transformComponent(component!, loader.coreTypes,
           loader.hierarchy, loader.referenceFromIndex, environment);
       ticker.logMs("Lowered value classes");
@@ -1652,12 +1634,9 @@
       new KernelConstantErrorReporter(loader),
       evaluationMode,
       evaluateAnnotations: true,
-      enableTripleShift:
-          isExperimentEnabledGlobally(ExperimentalFlag.tripleShift),
-      enableConstFunctions:
-          isExperimentEnabledGlobally(ExperimentalFlag.constFunctions),
-      enableConstructorTearOff:
-          isExperimentEnabledGlobally(ExperimentalFlag.constructorTearoffs),
+      enableTripleShift: globalFeatures.tripleShift.isEnabled,
+      enableConstFunctions: globalFeatures.constFunctions.isEnabled,
+      enableConstructorTearOff: globalFeatures.constructorTearoffs.isEnabled,
       errorOnUnevaluatedConstant: errorOnUnevaluatedConstant,
     );
     ticker.logMs("Evaluated constants");
@@ -1676,7 +1655,7 @@
     // because the SDK might be agnostic and therefore needs to be weakened
     // for legacy mode.
     assert(
-        isExperimentEnabledGlobally(ExperimentalFlag.nonNullable) ||
+        globalFeatures.nonNullable.isEnabled ||
             loader.nnbdMode == NnbdMode.Weak,
         "Non-weak nnbd mode found without experiment enabled: "
         "${loader.nnbdMode}.");
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 2d46643..05e5c4f 100644
--- a/pkg/front_end/lib/src/fasta/source/diet_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
@@ -1132,7 +1132,7 @@
           ? ""
           : nameOrQualified as String;
     }
-    if (libraryBuilder.enableConstructorTearOffsInLibrary) {
+    if (libraryFeatures.constructorTearoffs.isEnabled) {
       suffix = suffix == "new" ? "" : suffix;
     }
     declaration = currentClass!.constructorScope.local[suffix];
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index c9b3083..3793ca5 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -591,16 +591,9 @@
     checkEmpty(importKeyword.charOffset);
     if (prefix is ParserRecovery) return;
 
-    if (!libraryBuilder.enableMacrosInLibrary) {
-      if (augmentToken != null) {
-        // TODO(johnniwinther): We should emit a different message when the
-        // experiment is not released yet. The current message indicates that
-        // changing the sdk version can solve the problem.
-        addProblem(
-            templateExperimentNotEnabled.withArguments(
-                'macros', libraryBuilder.enableMacrosVersionInLibrary.toText()),
-            augmentToken.charOffset,
-            augmentToken.length);
+    if (augmentToken != null) {
+      if (reportIfNotEnabled(libraryFeatures.macros, augmentToken.charOffset,
+          augmentToken.length)) {
         augmentToken = null;
       }
     }
@@ -875,16 +868,9 @@
     libraryBuilder.setCurrentClassName(name.lexeme);
     inAbstractClass = abstractToken != null;
     push(abstractToken != null ? abstractMask : 0);
-    if (!libraryBuilder.enableMacrosInLibrary) {
-      if (macroToken != null) {
-        // TODO(johnniwinther): We should emit a different message when the
-        // experiment is not released yet. The current message indicates that
-        // changing the sdk version can solve the problem.
-        addProblem(
-            templateExperimentNotEnabled.withArguments(
-                'macros', libraryBuilder.enableMacrosVersionInLibrary.toText()),
-            macroToken.charOffset,
-            macroToken.length);
+    if (macroToken != null) {
+      if (reportIfNotEnabled(
+          libraryFeatures.macros, macroToken.charOffset, macroToken.length)) {
         macroToken = null;
       }
     }
@@ -975,13 +961,9 @@
     libraryBuilder.currentTypeParameterScopeBuilder.markAsNamedMixinApplication(
         name.lexeme, name.charOffset, typeVariables);
     push(abstractToken != null ? abstractMask : 0);
-    if (!libraryBuilder.enableMacrosInLibrary) {
-      if (macroToken != null) {
-        addProblem(
-            templateExperimentNotEnabled.withArguments(
-                'macros', libraryBuilder.enableMacrosVersionInLibrary.toText()),
-            macroToken.next!.charOffset,
-            macroToken.next!.length);
+    if (macroToken != null) {
+      if (reportIfNotEnabled(libraryFeatures.macros,
+          macroToken.next!.charOffset, macroToken.next!.length)) {
         macroToken = null;
       }
     }
@@ -996,14 +978,10 @@
             .popNonNullable(stack, interfacesCount, dummyTypeBuilder) ??
         NullValue.TypeBuilderList);
 
-    if (!libraryBuilder.enableEnhancedEnumsInLibrary &&
-        implementsKeyword != null &&
+    if (implementsKeyword != null &&
         declarationContext == DeclarationContext.Enum) {
-      addProblem(
-          templateExperimentNotEnabled.withArguments('enhanced-enums',
-              libraryBuilder.enableEnhancedEnumsVersionInLibrary.toText()),
-          implementsKeyword.charOffset,
-          -1);
+      reportIfNotEnabled(libraryFeatures.enhancedEnums,
+          implementsKeyword.charOffset, implementsKeyword.length);
     }
   }
 
@@ -1403,11 +1381,8 @@
             hiddenMembersOrTypes: hiddenMembersOrTypes ?? const <String>[],
             hiddenOperators: hiddenOperators ?? const <Operator>[]);
 
-    if (showKeyword != null && !libraryBuilder.enableExtensionTypesInLibrary) {
-      addProblem(
-          templateExperimentNotEnabled.withArguments('extension-types',
-              libraryBuilder.enableExtensionTypesVersionInLibrary.toText()),
-          showKeyword.charOffset,
+    if (showKeyword != null) {
+      reportIfNotEnabled(libraryFeatures.extensionTypes, showKeyword.charOffset,
           showKeyword.length);
     }
     Object? onType = pop();
@@ -1431,13 +1406,9 @@
         ? extensionKeyword.charOffset
         : metadata.first.charOffset;
     bool isExtensionTypeDeclaration = typeKeyword != null;
-    if (!libraryBuilder.enableExtensionTypesInLibrary &&
-        isExtensionTypeDeclaration) {
-      addProblem(
-          templateExperimentNotEnabled.withArguments('extension-types',
-              libraryBuilder.enableExtensionTypesVersionInLibrary.toText()),
-          extensionKeyword.next!.charOffset,
-          extensionKeyword.next!.length);
+    if (isExtensionTypeDeclaration) {
+      reportIfNotEnabled(libraryFeatures.extensionTypes,
+          extensionKeyword.next!.charOffset, extensionKeyword.next!.length);
     }
     libraryBuilder.addExtensionDeclaration(
         metadata,
@@ -1939,7 +1910,7 @@
       if (constructorName != null) {
         if (isConst &&
             bodyKind != MethodBody.Abstract &&
-            !libraryBuilder.enableConstFunctionsInLibrary) {
+            !libraryFeatures.constFunctions.isEnabled) {
           addProblem(messageConstConstructorWithBody, varFinalOrConstOffset, 5);
           modifiers &= ~constMask;
         }
@@ -2272,13 +2243,9 @@
       MemberKind memberKind) {
     debugEvent("FormalParameter");
 
-    if (superKeyword != null &&
-        !libraryBuilder.enableSuperParametersInLibrary) {
-      addProblem(
-          templateExperimentNotEnabled.withArguments('super-parameters',
-              libraryBuilder.enableSuperParametersVersionInLibrary.toText()),
-          superKeyword.charOffset,
-          superKeyword.length);
+    if (superKeyword != null) {
+      reportIfNotEnabled(libraryFeatures.superParameters,
+          superKeyword.charOffset, superKeyword.length);
     }
 
     int charOffset = popCharOffset();
@@ -2692,7 +2659,7 @@
         return;
       }
       if (type is FunctionTypeBuilder &&
-          !libraryBuilder.enableNonfunctionTypeAliasesInLibrary) {
+          !libraryFeatures.nonfunctionTypeAliases.isEnabled) {
         if (type.nullabilityBuilder.build(libraryBuilder) ==
                 Nullability.nullable &&
             libraryBuilder.isNonNullableByDefault) {
@@ -2717,7 +2684,7 @@
           // of a generic function).
           aliasedType = type;
         }
-      } else if (libraryBuilder.enableNonfunctionTypeAliasesInLibrary) {
+      } else if (libraryFeatures.nonfunctionTypeAliases.isEnabled) {
         if (type is TypeBuilder) {
           aliasedType = type;
         } else {
@@ -2977,7 +2944,7 @@
     if (typeParameters != null) {
       typeParameters[index].bound = bound;
       if (variance != null) {
-        if (!libraryBuilder.enableVarianceInLibrary) {
+        if (!libraryFeatures.variance.isEnabled) {
           reportVarianceModifierNotEnabled(variance);
         }
         typeParameters[index].variance = Variance.fromString(variance.lexeme);
@@ -2989,13 +2956,9 @@
   void endTypeVariables(Token beginToken, Token endToken) {
     debugEvent("endTypeVariables");
 
-    if (!libraryBuilder.enableEnhancedEnumsInLibrary &&
-        declarationContext == DeclarationContext.Enum) {
-      addProblem(
-          templateExperimentNotEnabled.withArguments('enhanced-enums',
-              libraryBuilder.enableEnhancedEnumsVersionInLibrary.toText()),
-          beginToken.charOffset,
-          -1);
+    if (declarationContext == DeclarationContext.Enum) {
+      reportIfNotEnabled(
+          libraryFeatures.enhancedEnums, beginToken.charOffset, noLength);
     }
 
     // Peek to leave type parameters on top of stack.
@@ -3103,7 +3066,7 @@
       // omitted only within an enum element declaration.
       if (libraryBuilder.currentTypeParameterScopeBuilder.kind ==
           TypeParameterScopeKind.enumDeclaration) {
-        if (libraryBuilder.enableEnhancedEnumsInLibrary) {
+        if (libraryFeatures.enhancedEnums.isEnabled) {
           push(libraryBuilder.addConstructorReference(
               libraryBuilder.currentTypeParameterScopeBuilder.name,
               typeArguments,
@@ -3113,13 +3076,8 @@
           // For entries that consist of their name only, all of the elements
           // of the constructor reference should be null.
           if (typeArguments != null || suffix != null) {
-            addProblem(
-                templateExperimentNotEnabled.withArguments(
-                    'enhanced-enums',
-                    libraryBuilder.enableEnhancedEnumsVersionInLibrary
-                        .toText()),
-                charOffset,
-                -1);
+            addProblem(libraryFeatures.enhancedEnums.notEnabledMessage,
+                charOffset, noLength);
           }
           push(NullValue.ConstructorReference);
         }
@@ -3228,13 +3186,8 @@
   void endEnumFactoryMethod(
       Token beginToken, Token factoryKeyword, Token endToken) {
     debugEvent("EnumFactoryMethod");
-    if (!libraryBuilder.enableEnhancedEnumsInLibrary) {
-      addProblem(
-          templateExperimentNotEnabled.withArguments('enhanced-enums',
-              libraryBuilder.enableEnhancedEnumsVersionInLibrary.toText()),
-          beginToken.charOffset,
-          -1);
-    }
+    reportIfNotEnabled(
+        libraryFeatures.enhancedEnums, beginToken.charOffset, noLength);
 
     _endFactoryMethod(beginToken, factoryKeyword, endToken);
   }
@@ -3242,13 +3195,8 @@
   @override
   void endEnumMethod(Token? getOrSet, Token beginToken, Token beginParam,
       Token? beginInitializers, Token endToken) {
-    if (!libraryBuilder.enableEnhancedEnumsInLibrary) {
-      addProblem(
-          templateExperimentNotEnabled.withArguments('enhanced-enums',
-              libraryBuilder.enableEnhancedEnumsVersionInLibrary.toText()),
-          beginToken.charOffset,
-          -1);
-    }
+    reportIfNotEnabled(
+        libraryFeatures.enhancedEnums, beginToken.charOffset, noLength);
 
     _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers,
         endToken, _MethodKind.enumMethod);
@@ -3266,13 +3214,9 @@
       int count,
       Token beginToken,
       Token endToken) {
-    if (!libraryBuilder.enableEnhancedEnumsInLibrary) {
-      addProblem(
-          templateExperimentNotEnabled.withArguments('enhanced-enums',
-              libraryBuilder.enableEnhancedEnumsVersionInLibrary.toText()),
-          beginToken.charOffset,
-          -1);
-    }
+    reportIfNotEnabled(
+        libraryFeatures.enhancedEnums, beginToken.charOffset, noLength);
+
     endClassFields(
         abstractToken,
         augmentToken,
@@ -3289,13 +3233,8 @@
   @override
   void endEnumConstructor(Token? getOrSet, Token beginToken, Token beginParam,
       Token? beginInitializers, Token endToken) {
-    if (!libraryBuilder.enableEnhancedEnumsInLibrary) {
-      addProblem(
-          templateExperimentNotEnabled.withArguments('enhanced-enums',
-              libraryBuilder.enableEnhancedEnumsVersionInLibrary.toText()),
-          beginToken.charOffset,
-          -1);
-    }
+    reportIfNotEnabled(
+        libraryFeatures.enhancedEnums, beginToken.charOffset, noLength);
 
     _endClassMethod(getOrSet, beginToken, beginParam, beginInitializers,
         endToken, _MethodKind.enumConstructor);
@@ -3310,7 +3249,7 @@
   @override
   void handleConstFactory(Token constKeyword) {
     debugEvent("ConstFactory");
-    if (!libraryBuilder.enableConstFunctionsInLibrary) {
+    if (!libraryFeatures.constFunctions.isEnabled) {
       handleRecoverableError(messageConstFactory, constKeyword, constKeyword);
     }
   }
@@ -3438,13 +3377,8 @@
       ]),
     ]));
 
-    if (!libraryBuilder.enableEnhancedEnumsInLibrary) {
-      addProblem(
-          templateExperimentNotEnabled.withArguments('enhanced-enums',
-              libraryBuilder.enableEnhancedEnumsVersionInLibrary.toText()),
-          withKeyword.charOffset,
-          -1);
-    }
+    reportIfNotEnabled(libraryFeatures.enhancedEnums, withKeyword.charOffset,
+        withKeyword.length);
 
     Object? mixins = pop();
     if (mixins is ParserRecovery) {
@@ -3512,15 +3446,8 @@
 
   @override
   void handleNewAsIdentifier(Token token) {
-    if (!libraryBuilder.enableConstructorTearOffsInLibrary) {
-      addProblem(
-          templateExperimentNotEnabled.withArguments(
-              'constructor-tearoffs',
-              libraryBuilder.enableConstructorTearOffsVersionInLibrary
-                  .toText()),
-          token.charOffset,
-          token.length);
-    }
+    reportIfNotEnabled(
+        libraryFeatures.constructorTearoffs, token.charOffset, token.length);
   }
 }
 
diff --git a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
index 3cfb2bf..d914a49 100644
--- a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
@@ -727,7 +727,7 @@
     // Moreover, it checks that `FutureOr` and `void` are not among the
     // supertypes and that `Enum` is not implemented by non-abstract classes.
 
-    if (libraryBuilder.enableEnhancedEnumsInLibrary && !isEnum) {
+    if (libraryBuilder.libraryFeatures.enhancedEnums.isEnabled && !isEnum) {
       bool hasEnumSuperinterface = false;
       List<Supertype> interfaces =
           hierarchyBuilder.getNodeFromClass(cls).superclasses;
@@ -1322,29 +1322,28 @@
 
   void checkBoundsInSupertype(
       Supertype supertype, TypeEnvironment typeEnvironment) {
-    SourceLibraryBuilder libraryBuilder2 = this.libraryBuilder;
-    Library library = libraryBuilder2.library;
+    Library library = libraryBuilder.library;
 
     List<TypeArgumentIssue> issues = findTypeArgumentIssues(
         new InterfaceType(
             supertype.classNode, library.nonNullable, supertype.typeArguments),
         typeEnvironment,
-        libraryBuilder2.isNonNullableByDefault
+        libraryBuilder.isNonNullableByDefault
             ? SubtypeCheckMode.withNullabilities
             : SubtypeCheckMode.ignoringNullabilities,
         allowSuperBounded: false,
         isNonNullableByDefault: library.isNonNullableByDefault,
         areGenericArgumentsAllowed:
-            libraryBuilder2.enableGenericMetadataInLibrary);
+            libraryBuilder.libraryFeatures.genericMetadata.isEnabled);
     for (TypeArgumentIssue issue in issues) {
       DartType argument = issue.argument;
       TypeParameter typeParameter = issue.typeParameter;
-      bool inferred = libraryBuilder2.inferredTypes.contains(argument);
+      bool inferred = libraryBuilder.inferredTypes.contains(argument);
       if (issue.isGenericTypeAsArgumentIssue) {
         if (inferred) {
           // Supertype can't be or contain super-bounded types, so null is
           // passed for super-bounded hint here.
-          libraryBuilder2.reportTypeArgumentIssue(
+          libraryBuilder.reportTypeArgumentIssue(
               templateGenericFunctionTypeInferredAsActualTypeArgument
                   .withArguments(argument, library.isNonNullableByDefault),
               fileUri,
@@ -1355,7 +1354,7 @@
         } else {
           // Supertype can't be or contain super-bounded types, so null is
           // passed for super-bounded hint here.
-          libraryBuilder2.reportTypeArgumentIssue(
+          libraryBuilder.reportTypeArgumentIssue(
               messageGenericFunctionTypeUsedAsActualTypeArgument,
               fileUri,
               charOffset,
@@ -1371,7 +1370,7 @@
                 template) {
           // Supertype can't be or contain super-bounded types, so null is
           // passed for super-bounded hint here.
-          libraryBuilder2.reportTypeArgumentIssue(
+          libraryBuilder.reportTypeArgumentIssue(
               template.withArguments(
                   argument,
                   typeParameter.bound,
diff --git a/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart b/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart
index 4d57b10..10a8838 100644
--- a/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart
@@ -284,7 +284,8 @@
         staticFieldNameScheme,
         fieldReference: valuesFieldReference,
         fieldGetterReference: valuesGetterReference,
-        fieldSetterReference: valuesSetterReference);
+        fieldSetterReference: valuesSetterReference,
+        isSynthesized: true);
     members["values"] = valuesBuilder;
 
     DeclaredSourceConstructorBuilder? synthesizedDefaultConstructorBuilder;
@@ -676,7 +677,7 @@
         typeArguments.add(typeBuilder.build(libraryBuilder));
       }
     }
-    if (libraryBuilder.enableEnhancedEnumsInLibrary) {
+    if (libraryBuilder.libraryFeatures.enhancedEnums.isEnabled) {
       // We need to create a BodyBuilder to solve the following: 1) if
       // the arguments token is provided, we'll use the BodyBuilder to
       // parse them and perform inference, 2) if the type arguments
diff --git a/pkg/front_end/lib/src/fasta/source/source_field_builder.dart b/pkg/front_end/lib/src/fasta/source/source_field_builder.dart
index 346eda3..e5d89e8 100644
--- a/pkg/front_end/lib/src/fasta/source/source_field_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_field_builder.dart
@@ -70,6 +70,8 @@
   @override
   final bool isTopLevel;
 
+  final bool isSynthesized;
+
   SourceFieldBuilder(
       this.metadata,
       this.type,
@@ -88,7 +90,8 @@
       Reference? lateIsSetSetterReference,
       Reference? lateGetterReference,
       Reference? lateSetterReference,
-      Token? constInitializerToken})
+      Token? constInitializerToken,
+      this.isSynthesized = false})
       : _constInitializerToken = constInitializerToken,
         super(libraryBuilder, charOffset) {
     bool isInstanceMember = fieldNameScheme.isInstanceMember;
@@ -758,6 +761,9 @@
   bool get isProperty => true;
 
   @override
+  bool get isSynthesized => memberBuilder.isSynthesized;
+
+  @override
   bool isSameDeclaration(ClassMember other) {
     return other is SourceFieldMember && memberBuilder == other.memberBuilder;
   }
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 72c27c2..177d445 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -310,143 +310,13 @@
   TypeParameterScopeBuilder get libraryTypeParameterScopeBuilderForTesting =>
       _libraryTypeParameterScopeBuilder;
 
-  bool? _enableConstFunctionsInLibrary;
-  bool? _enableVarianceInLibrary;
-  bool? _enableNonfunctionTypeAliasesInLibrary;
-  bool? _enableNonNullableInLibrary;
-  Version? _enableNonNullableVersionInLibrary;
-  Version? _enableConstructorTearoffsVersionInLibrary;
-  Version? _enableExtensionTypesVersionInLibrary;
-  Version? _enableNamedArgumentsAnywhereVersionInLibrary;
-  Version? _enableSuperParametersVersionInLibrary;
-  Version? _enableEnhancedEnumsVersionInLibrary;
-  Version? _enableMacrosVersionInLibrary;
-  bool? _enableTripleShiftInLibrary;
-  bool? _enableExtensionMethodsInLibrary;
-  bool? _enableGenericMetadataInLibrary;
-  bool? _enableExtensionTypesInLibrary;
-  bool? _enableEnhancedEnumsInLibrary;
-  bool? _enableConstructorTearOffsInLibrary;
-  bool? _enableNamedArgumentsAnywhereInLibrary;
-  bool? _enableSuperParametersInLibrary;
-  bool? _enableMacrosInLibrary;
+  LibraryFeatures? _libraryFeatures;
 
-  bool get enableConstFunctionsInLibrary => _enableConstFunctionsInLibrary ??=
-      loader.target.isExperimentEnabledInLibraryByVersion(
-          ExperimentalFlag.constFunctions,
-          _packageUri ?? importUri,
-          languageVersion.version);
-
-  bool get enableVarianceInLibrary => _enableVarianceInLibrary ??= loader.target
-      .isExperimentEnabledInLibraryByVersion(ExperimentalFlag.variance,
+  /// Returns the state of the experimental features within this library.
+  LibraryFeatures get libraryFeatures =>
+      _libraryFeatures ??= new LibraryFeatures(loader.target.globalFeatures,
           _packageUri ?? importUri, languageVersion.version);
 
-  bool get enableNonfunctionTypeAliasesInLibrary =>
-      _enableNonfunctionTypeAliasesInLibrary ??= loader.target
-          .isExperimentEnabledInLibraryByVersion(
-              ExperimentalFlag.nonfunctionTypeAliases,
-              _packageUri ?? importUri,
-              languageVersion.version);
-
-  /// Returns `true` if the 'non-nullable' experiment is enabled for this
-  /// library.
-  ///
-  /// Note that the library might still opt out of the experiment by having
-  /// a version that is too low for opting in to the experiment.
-  bool get enableNonNullableInLibrary => _enableNonNullableInLibrary ??=
-      loader.target.isExperimentEnabledInLibrary(
-          ExperimentalFlag.nonNullable, _packageUri ?? importUri);
-
-  Version get enableNonNullableVersionInLibrary =>
-      _enableNonNullableVersionInLibrary ??= loader.target
-          .getExperimentEnabledVersionInLibrary(
-              ExperimentalFlag.nonNullable, _packageUri ?? importUri);
-
-  bool get enableConstructorTearOffsInLibrary =>
-      _enableConstructorTearOffsInLibrary ??= loader.target
-          .isExperimentEnabledInLibraryByVersion(
-              ExperimentalFlag.constructorTearoffs,
-              _packageUri ?? importUri,
-              languageVersion.version);
-
-  Version get enableConstructorTearOffsVersionInLibrary =>
-      _enableConstructorTearoffsVersionInLibrary ??= loader.target
-          .getExperimentEnabledVersionInLibrary(
-              ExperimentalFlag.constructorTearoffs, _packageUri ?? importUri);
-
-  bool get enableTripleShiftInLibrary => _enableTripleShiftInLibrary ??=
-      loader.target.isExperimentEnabledInLibraryByVersion(
-          ExperimentalFlag.tripleShift,
-          _packageUri ?? importUri,
-          languageVersion.version);
-
-  bool get enableExtensionMethodsInLibrary =>
-      _enableExtensionMethodsInLibrary ??= loader.target
-          .isExperimentEnabledInLibraryByVersion(
-              ExperimentalFlag.extensionMethods,
-              _packageUri ?? importUri,
-              languageVersion.version);
-
-  bool get enableGenericMetadataInLibrary => _enableGenericMetadataInLibrary ??=
-      loader.target.isExperimentEnabledInLibraryByVersion(
-          ExperimentalFlag.genericMetadata,
-          _packageUri ?? importUri,
-          languageVersion.version);
-
-  bool get enableExtensionTypesInLibrary => _enableExtensionTypesInLibrary ??=
-      loader.target.isExperimentEnabledInLibraryByVersion(
-          ExperimentalFlag.extensionTypes,
-          _packageUri ?? importUri,
-          languageVersion.version);
-
-  Version get enableExtensionTypesVersionInLibrary =>
-      _enableExtensionTypesVersionInLibrary ??= loader.target
-          .getExperimentEnabledVersionInLibrary(
-              ExperimentalFlag.extensionTypes, _packageUri ?? importUri);
-
-  bool get enableNamedArgumentsAnywhereInLibrary =>
-      _enableNamedArgumentsAnywhereInLibrary ??= loader.target
-          .isExperimentEnabledInLibraryByVersion(
-              ExperimentalFlag.namedArgumentsAnywhere,
-              _packageUri ?? importUri,
-              languageVersion.version);
-
-  Version get enableNamedArgumentsAnywhereVersionInLibrary =>
-      _enableNamedArgumentsAnywhereVersionInLibrary ??= loader.target
-          .getExperimentEnabledVersionInLibrary(
-              ExperimentalFlag.namedArgumentsAnywhere,
-              _packageUri ?? importUri);
-
-  bool get enableSuperParametersInLibrary => _enableSuperParametersInLibrary ??=
-      loader.target.isExperimentEnabledInLibraryByVersion(
-          ExperimentalFlag.superParameters,
-          _packageUri ?? importUri,
-          languageVersion.version);
-
-  Version get enableSuperParametersVersionInLibrary =>
-      _enableSuperParametersVersionInLibrary ??= loader.target
-          .getExperimentEnabledVersionInLibrary(
-              ExperimentalFlag.superParameters, _packageUri ?? importUri);
-
-  bool get enableEnhancedEnumsInLibrary => _enableEnhancedEnumsInLibrary ??=
-      loader.target.isExperimentEnabledInLibraryByVersion(
-          ExperimentalFlag.enhancedEnums,
-          _packageUri ?? importUri,
-          languageVersion.version);
-
-  Version get enableEnhancedEnumsVersionInLibrary =>
-      _enableEnhancedEnumsVersionInLibrary ??= loader.target
-          .getExperimentEnabledVersionInLibrary(
-              ExperimentalFlag.enhancedEnums, _packageUri ?? importUri);
-
-  bool get enableMacrosInLibrary => _enableMacrosInLibrary ??= loader.target
-      .isExperimentEnabledInLibraryByVersion(ExperimentalFlag.macros,
-          _packageUri ?? importUri, languageVersion.version);
-
-  Version get enableMacrosVersionInLibrary => _enableMacrosVersionInLibrary ??=
-      loader.target.getExperimentEnabledVersionInLibrary(
-          ExperimentalFlag.macros, _packageUri ?? importUri);
-
   void _updateLibraryNNBDSettings() {
     library.isNonNullableByDefault = isNonNullableByDefault;
     switch (loader.nnbdMode) {
@@ -571,8 +441,8 @@
   }
 
   bool _computeIsNonNullableByDefault() =>
-      enableNonNullableInLibrary &&
-      languageVersion.version >= enableNonNullableVersionInLibrary;
+      libraryFeatures.nonNullable.isSupported &&
+      languageVersion.version >= libraryFeatures.nonNullable.enabledVersion;
 
   LanguageVersion get languageVersion {
     assert(
@@ -711,7 +581,7 @@
       prefix = name as String;
       suffix = null;
     }
-    if (enableConstructorTearOffsInLibrary) {
+    if (libraryFeatures.constructorTearoffs.isEnabled) {
       suffix = suffix == "new" ? "" : suffix;
     }
     if (prefix == className) {
@@ -2635,7 +2505,8 @@
     if (constructorBuilder.isConst) {
       currentTypeParameterScopeBuilder.declaresConstConstructor = true;
     }
-    if (constructorBuilder.isConst || enableSuperParametersInLibrary) {
+    if (constructorBuilder.isConst ||
+        libraryFeatures.superParameters.isEnabled) {
       // const constructors will have their initializers compiled and written
       // into the outline. In case of super-parameters language feature, the
       // super initializers are required to infer the types of super parameters.
@@ -2999,7 +2870,7 @@
     if (typeVariables != null) {
       for (TypeVariableBuilder builder in typeVariables) {
         if (builder.metadata != null) {
-          if (!enableGenericMetadataInLibrary) {
+          if (!libraryFeatures.genericMetadata.isEnabled) {
             addProblem(messageAnnotationOnFunctionTypeTypeVariable,
                 builder.charOffset, builder.name.length, builder.fileUri);
           }
@@ -3589,7 +3460,7 @@
   /// any errors were reported.
   bool _recursivelyReportGenericFunctionTypesAsBoundsForVariable(
       TypeVariableBuilder typeVariable) {
-    if (enableGenericMetadataInLibrary) return false;
+    if (libraryFeatures.genericMetadata.isEnabled) return false;
 
     bool hasReportedErrors = false;
     hasReportedErrors =
@@ -3609,7 +3480,7 @@
   /// reported.
   bool _recursivelyReportGenericFunctionTypesAsBoundsForType(
       TypeBuilder? typeBuilder) {
-    if (enableGenericMetadataInLibrary) return false;
+    if (libraryFeatures.genericMetadata.isEnabled) return false;
 
     List<FunctionTypeBuilder> genericFunctionTypeBuilders =
         <FunctionTypeBuilder>[];
@@ -3637,7 +3508,7 @@
   /// Returns `true` if any errors were reported.
   bool _reportGenericFunctionTypeAsBoundIfNeeded(
       TypeVariableBuilder typeVariable) {
-    if (enableGenericMetadataInLibrary) return false;
+    if (libraryFeatures.genericMetadata.isEnabled) return false;
 
     TypeBuilder? bound = typeVariable.bound;
     bool isUnaliasedGenericFunctionType = bound is FunctionTypeBuilder &&
@@ -3687,7 +3558,7 @@
 
       bool haveErroneousBounds = false;
       if (!inErrorRecovery) {
-        if (!enableGenericMetadataInLibrary) {
+        if (!libraryFeatures.genericMetadata.isEnabled) {
           for (TypeVariableBuilder variable in variables) {
             haveErroneousBounds =
                 _recursivelyReportGenericFunctionTypesAsBoundsForVariable(
@@ -4207,7 +4078,8 @@
               : SubtypeCheckMode.ignoringNullabilities,
           allowSuperBounded: true,
           isNonNullableByDefault: library.isNonNullableByDefault,
-          areGenericArgumentsAllowed: enableGenericMetadataInLibrary);
+          areGenericArgumentsAllowed:
+              libraryFeatures.genericMetadata.isEnabled);
       for (TypeArgumentIssue issue in issues) {
         DartType argument = issue.argument;
         TypeParameter typeParameter = issue.typeParameter;
@@ -4284,7 +4156,8 @@
               : SubtypeCheckMode.ignoringNullabilities,
           allowSuperBounded: true,
           isNonNullableByDefault: library.isNonNullableByDefault,
-          areGenericArgumentsAllowed: enableGenericMetadataInLibrary);
+          areGenericArgumentsAllowed:
+              libraryFeatures.genericMetadata.isEnabled);
       for (TypeArgumentIssue issue in issues) {
         DartType argument = issue.argument;
         TypeParameter typeParameter = issue.typeParameter;
@@ -4394,7 +4267,7 @@
             : SubtypeCheckMode.ignoringNullabilities,
         allowSuperBounded: allowSuperBounded,
         isNonNullableByDefault: library.isNonNullableByDefault,
-        areGenericArgumentsAllowed: enableGenericMetadataInLibrary);
+        areGenericArgumentsAllowed: libraryFeatures.genericMetadata.isEnabled);
     reportTypeArgumentIssues(issues, fileUri, offset, inferred: inferred);
   }
 
@@ -4461,7 +4334,7 @@
             : SubtypeCheckMode.ignoringNullabilities,
         bottomType,
         isNonNullableByDefault: library.isNonNullableByDefault,
-        areGenericArgumentsAllowed: enableGenericMetadataInLibrary);
+        areGenericArgumentsAllowed: libraryFeatures.genericMetadata.isEnabled);
     if (issues.isNotEmpty) {
       DartType? targetReceiver;
       if (klass != null) {
@@ -4547,7 +4420,7 @@
             : SubtypeCheckMode.ignoringNullabilities,
         bottomType,
         isNonNullableByDefault: library.isNonNullableByDefault,
-        areGenericArgumentsAllowed: enableGenericMetadataInLibrary);
+        areGenericArgumentsAllowed: libraryFeatures.genericMetadata.isEnabled);
     reportTypeArgumentIssues(issues, fileUri, offset,
         typeArgumentsInfo: getTypeArgumentsInfo(arguments),
         targetReceiver: receiverType,
@@ -4580,7 +4453,7 @@
             : SubtypeCheckMode.ignoringNullabilities,
         bottomType,
         isNonNullableByDefault: library.isNonNullableByDefault,
-        areGenericArgumentsAllowed: enableGenericMetadataInLibrary);
+        areGenericArgumentsAllowed: libraryFeatures.genericMetadata.isEnabled);
     reportTypeArgumentIssues(issues, fileUri, offset,
         typeArgumentsInfo: getTypeArgumentsInfo(arguments),
         // TODO(johnniwinther): Special-case messaging on function type
@@ -4617,7 +4490,7 @@
             : SubtypeCheckMode.ignoringNullabilities,
         bottomType,
         isNonNullableByDefault: library.isNonNullableByDefault,
-        areGenericArgumentsAllowed: enableGenericMetadataInLibrary);
+        areGenericArgumentsAllowed: libraryFeatures.genericMetadata.isEnabled);
     reportTypeArgumentIssues(issues, fileUri, offset,
         targetReceiver: functionType,
         typeArgumentsInfo: inferred
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index 280a71d..3d323b0 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -815,10 +815,10 @@
   Template<SummaryTemplate> get outlineSummaryTemplate =>
       templateSourceOutlineSummary;
 
-  Future<Token> tokenize(SourceLibraryBuilder library,
+  Future<Token> tokenize(SourceLibraryBuilder libraryBuilder,
       {bool suppressLexicalErrors: false}) async {
     target.benchmarker?.beginSubdivide(BenchmarkSubdivides.tokenize);
-    Uri fileUri = library.fileUri;
+    Uri fileUri = libraryBuilder.fileUri;
 
     // Lookup the file URI in the cache.
     List<int>? bytes = sourceBytes[fileUri];
@@ -827,18 +827,18 @@
       // Error recovery.
       if (fileUri.isScheme(untranslatableUriScheme)) {
         Message message =
-            templateUntranslatableUri.withArguments(library.importUri);
-        library.addProblemAtAccessors(message);
-        bytes = synthesizeSourceForMissingFile(library.importUri, null);
+            templateUntranslatableUri.withArguments(libraryBuilder.importUri);
+        libraryBuilder.addProblemAtAccessors(message);
+        bytes = synthesizeSourceForMissingFile(libraryBuilder.importUri, null);
       } else if (!fileUri.hasScheme) {
         target.benchmarker?.endSubdivide();
         return internalProblem(
             templateInternalProblemUriMissingScheme.withArguments(fileUri),
             -1,
-            library.importUri);
+            libraryBuilder.importUri);
       } else if (fileUri.isScheme(SourceLibraryBuilder.MALFORMED_URI_SCHEME)) {
-        library.addProblemAtAccessors(messageExpectedUri);
-        bytes = synthesizeSourceForMissingFile(library.importUri, null);
+        libraryBuilder.addProblemAtAccessors(messageExpectedUri);
+        bytes = synthesizeSourceForMissingFile(libraryBuilder.importUri, null);
       }
       if (bytes != null) {
         Uint8List zeroTerminatedBytes = new Uint8List(bytes.length + 1);
@@ -857,8 +857,9 @@
       } on FileSystemException catch (e) {
         Message message =
             templateCantReadFile.withArguments(fileUri, e.message);
-        library.addProblemAtAccessors(message);
-        rawBytes = synthesizeSourceForMissingFile(library.importUri, message);
+        libraryBuilder.addProblemAtAccessors(message);
+        rawBytes =
+            synthesizeSourceForMissingFile(libraryBuilder.importUri, message);
       }
       Uint8List zeroTerminatedBytes = new Uint8List(rawBytes.length + 1);
       zeroTerminatedBytes.setRange(0, rawBytes.length, rawBytes);
@@ -872,36 +873,38 @@
         configuration: new ScannerConfiguration(
             enableTripleShift: target.isExperimentEnabledInLibraryByVersion(
                 ExperimentalFlag.tripleShift,
-                library.importUri,
-                library.packageLanguageVersion.version),
+                libraryBuilder.importUri,
+                libraryBuilder.packageLanguageVersion.version),
             enableExtensionMethods:
                 target.isExperimentEnabledInLibraryByVersion(
                     ExperimentalFlag.extensionMethods,
-                    library.importUri,
-                    library.packageLanguageVersion.version),
+                    libraryBuilder.importUri,
+                    libraryBuilder.packageLanguageVersion.version),
             enableNonNullable: target.isExperimentEnabledInLibraryByVersion(
                 ExperimentalFlag.nonNullable,
-                library.importUri,
-                library.packageLanguageVersion.version),
-            forAugmentationLibrary: library.isAugmentation),
+                libraryBuilder.importUri,
+                libraryBuilder.packageLanguageVersion.version),
+            forAugmentationLibrary: libraryBuilder.isAugmentation),
         languageVersionChanged:
             (Scanner scanner, LanguageVersionToken version) {
       if (!suppressLexicalErrors) {
-        library.registerExplicitLanguageVersion(
+        libraryBuilder.registerExplicitLanguageVersion(
             new Version(version.major, version.minor),
             offset: version.offset,
             length: version.length);
       }
       scanner.configuration = new ScannerConfiguration(
-          enableTripleShift: library.enableTripleShiftInLibrary,
-          enableExtensionMethods: library.enableExtensionMethodsInLibrary,
-          enableNonNullable: library.isNonNullableByDefault);
+          enableTripleShift:
+              libraryBuilder.libraryFeatures.tripleShift.isEnabled,
+          enableExtensionMethods:
+              libraryBuilder.libraryFeatures.extensionMethods.isEnabled,
+          enableNonNullable: libraryBuilder.isNonNullableByDefault);
     });
     Token token = result.tokens;
     if (!suppressLexicalErrors) {
       List<int> source = getSource(bytes);
-      Uri importUri = library.importUri;
-      if (library.isPatch) {
+      Uri importUri = libraryBuilder.importUri;
+      if (libraryBuilder.isPatch) {
         // For patch files we create a "fake" import uri.
         // We cannot use the import uri from the patched library because
         // several different files would then have the same import uri,
@@ -910,19 +913,19 @@
         // represented several files?
         List<String> newPathSegments =
             new List<String>.of(importUri.pathSegments);
-        newPathSegments.add(library.fileUri.pathSegments.last);
+        newPathSegments.add(libraryBuilder.fileUri.pathSegments.last);
         newPathSegments[0] = "${newPathSegments[0]}-patch";
         importUri = importUri.replace(pathSegments: newPathSegments);
       }
       target.addSourceInformation(
-          importUri, library.fileUri, result.lineStarts, source);
+          importUri, libraryBuilder.fileUri, result.lineStarts, source);
     }
-    library.issuePostponedProblems();
-    library.markLanguageVersionFinal();
+    libraryBuilder.issuePostponedProblems();
+    libraryBuilder.markLanguageVersionFinal();
     while (token is ErrorToken) {
       if (!suppressLexicalErrors) {
         ErrorToken error = token;
-        library.addProblem(error.assertionMessage, offsetForToken(token),
+        libraryBuilder.addProblem(error.assertionMessage, offsetForToken(token),
             lengthForToken(token), fileUri);
       }
       token = token.next!;
@@ -1900,7 +1903,7 @@
   }
 
   bool checkEnumSupertypeIsDenylisted(SourceClassBuilder cls) {
-    if (!cls.libraryBuilder.enableEnhancedEnumsInLibrary) {
+    if (!cls.libraryBuilder.libraryFeatures.enhancedEnums.isEnabled) {
       cls.addProblem(
           templateEnumSupertypeOfNonAbstractClass.withArguments(cls.name),
           cls.charOffset,
diff --git a/pkg/front_end/lib/src/fasta/source/stack_listener_impl.dart b/pkg/front_end/lib/src/fasta/source/stack_listener_impl.dart
index 3e8326c..be0fc08 100644
--- a/pkg/front_end/lib/src/fasta/source/stack_listener_impl.dart
+++ b/pkg/front_end/lib/src/fasta/source/stack_listener_impl.dart
@@ -12,6 +12,7 @@
 
 import 'package:kernel/ast.dart';
 
+import '../../api_prototype/experimental_flags.dart';
 import '../fasta_codes.dart';
 
 import '../problems.dart' as problems
@@ -22,6 +23,8 @@
 abstract class StackListenerImpl extends StackListener {
   SourceLibraryBuilder get libraryBuilder;
 
+  LibraryFeatures get libraryFeatures => libraryBuilder.libraryFeatures;
+
   AsyncMarker asyncMarkerFromTokens(Token? asyncToken, Token? starToken) {
     if (asyncToken == null || identical(asyncToken.stringValue, "sync")) {
       if (starToken == null) {
@@ -75,11 +78,11 @@
     assert(!libraryBuilder.isNonNullableByDefault);
     // ignore: unnecessary_null_comparison
     assert(token != null);
-    if (libraryBuilder.enableNonNullableInLibrary) {
+    if (libraryFeatures.nonNullable.isSupported) {
       if (libraryBuilder.languageVersion.isExplicit) {
         addProblem(
             templateNonNullableOptOutExplicit.withArguments(
-                libraryBuilder.enableNonNullableVersionInLibrary.toText()),
+                libraryFeatures.nonNullable.enabledVersion.toText()),
             token.charOffset,
             token.charCount,
             context: <LocatedMessage>[
@@ -91,16 +94,16 @@
       } else {
         addProblem(
             templateNonNullableOptOutImplicit.withArguments(
-                libraryBuilder.enableNonNullableVersionInLibrary.toText()),
+                libraryFeatures.nonNullable.enabledVersion.toText()),
             token.charOffset,
             token.charCount);
       }
     } else {
       if (libraryBuilder.languageVersion.version <
-          libraryBuilder.enableNonNullableVersionInLibrary) {
+          libraryFeatures.nonNullable.enabledVersion) {
         addProblem(
             templateExperimentDisabledInvalidLanguageVersion.withArguments(
-                libraryBuilder.enableNonNullableVersionInLibrary.toText()),
+                libraryFeatures.nonNullable.enabledVersion.toText()),
             token.offset,
             noLength);
       } else {
@@ -110,6 +113,18 @@
     }
   }
 
+  /// Reports an error if [feature] is not enabled, using [charOffset] and
+  /// [length] for the location of the message.
+  ///
+  /// Return `true` if the [feature] is not enabled.
+  bool reportIfNotEnabled(LibraryFeature feature, int charOffset, int length) {
+    if (!feature.isEnabled) {
+      addProblem(feature.notEnabledMessage, charOffset, length);
+      return true;
+    }
+    return false;
+  }
+
   void reportErrorIfNullableType(Token? questionMark) {
     if (questionMark != null) {
       reportMissingNonNullableSupport(questionMark);
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index e19c27a..b99f8f5 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -15,6 +15,7 @@
 import 'package:kernel/type_algebra.dart';
 import 'package:kernel/type_environment.dart';
 
+import '../../api_prototype/experimental_flags.dart';
 import '../../base/instrumentation.dart'
     show
         Instrumentation,
@@ -193,7 +194,7 @@
               new TypeOperationsCfe(engine.typeSchemaEnvironment),
               assignedVariables,
               respectImplicitlyTypedVarInitializers:
-                  libraryBuilder.enableConstructorTearOffsInLibrary)
+                  libraryFeatures.constructorTearoffs.isEnabled)
           : new FlowAnalysis.legacy(
               new TypeOperationsCfe(engine.typeSchemaEnvironment),
               assignedVariables);
@@ -253,6 +254,8 @@
 
   NnbdMode get nnbdMode => libraryBuilder.loader.nnbdMode;
 
+  LibraryFeatures get libraryFeatures => libraryBuilder.libraryFeatures;
+
   DartType get bottomType =>
       isNonNullableByDefault ? const NeverType.nonNullable() : const NullType();
 
@@ -817,7 +820,7 @@
       }
     }
     ImplicitInstantiation? implicitInstantiation;
-    if (coerceExpression && libraryBuilder.enableConstructorTearOffsInLibrary) {
+    if (coerceExpression && libraryFeatures.constructorTearoffs.isEnabled) {
       implicitInstantiation =
           computeImplicitInstantiation(expressionType, contextType);
       if (implicitInstantiation != null) {
@@ -1249,7 +1252,7 @@
       target = isReceiverTypePotentiallyNullable
           ? const ObjectAccessTarget.nullableCallFunction()
           : const ObjectAccessTarget.callFunction();
-    } else if (libraryBuilder.enableExtensionTypesInLibrary &&
+    } else if (libraryFeatures.extensionTypes.isEnabled &&
         receiverBound is ExtensionType) {
       target = _findDirectExtensionTypeMember(receiverBound, name, fileOffset,
           isSetter: isSetter,
@@ -2291,7 +2294,7 @@
     }
 
     List<VariableDeclaration>? localHoistedExpressions;
-    if (libraryBuilder.enableNamedArgumentsAnywhereInLibrary &&
+    if (libraryFeatures.namedArgumentsAnywhere.isEnabled &&
         arguments.argumentsOriginalOrder != null &&
         hoistedExpressions == null &&
         !isTopLevel) {
@@ -2337,7 +2340,7 @@
     // TODO(paulberry): if we are doing top level inference and type arguments
     // were omitted, report an error.
     List<Object?> argumentsEvaluationOrder;
-    if (libraryBuilder.enableNamedArgumentsAnywhereInLibrary &&
+    if (libraryFeatures.namedArgumentsAnywhere.isEnabled &&
         arguments.argumentsOriginalOrder != null) {
       if (staticTarget?.isExtensionMember ?? false) {
         // Add the receiver.
@@ -2368,7 +2371,7 @@
     // vector, and none of the arguments is hoisted. That way the legacy
     // behavior is preserved.
     int hoistingEndIndex;
-    if (libraryBuilder.enableNamedArgumentsAnywhereInLibrary) {
+    if (libraryFeatures.namedArgumentsAnywhere.isEnabled) {
       hoistingEndIndex = argumentsEvaluationOrder.length - 1;
       for (int i = argumentsEvaluationOrder.length - 2;
           i >= 0 && hoistingEndIndex == i + 1;
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 98030ea..d7d8f6a1 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -475,6 +475,7 @@
 InitializerOutsideConstructor/example: Fail
 InputFileNotFound/analyzerCode: Fail
 InputFileNotFound/example: Fail
+InstanceAndSynthesizedStaticConflict/example: Fail
 InstantiationNonGenericFunctionType/analyzerCode: Fail
 InstantiationTooFewArguments/analyzerCode: Fail
 InstantiationTooManyArguments/analyzerCode: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 7079da9..dfd77a5 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -4597,6 +4597,10 @@
   problemMessage: "This is the instance member."
   severity: CONTEXT
 
+InstanceAndSynthesizedStaticConflict:
+  problemMessage: "This instance member conflicts with the synthesized static member called '#name'."
+  analyzerCode: CONFLICTING_STATIC_AND_INSTANCE
+
 FfiAbiSpecificIntegerInvalid:
   # Used by dart:ffi
   problemMessage: "Classes extending 'AbiSpecificInteger' must have exactly one const constructor, no other members, and no type arguments."
diff --git a/pkg/front_end/test/constant_evaluator_benchmark.dart b/pkg/front_end/test/constant_evaluator_benchmark.dart
index c86c066..8319bc8 100644
--- a/pkg/front_end/test/constant_evaluator_benchmark.dart
+++ b/pkg/front_end/test/constant_evaluator_benchmark.dart
@@ -87,12 +87,11 @@
             new SilentErrorReporter(),
             evaluationMode,
             evaluateAnnotations: true,
-            enableTripleShift: target
-                .isExperimentEnabledGlobally(ExperimentalFlag.tripleShift),
-            enableConstFunctions: target
-                .isExperimentEnabledGlobally(ExperimentalFlag.constFunctions),
-            enableConstructorTearOff: target.isExperimentEnabledGlobally(
-                ExperimentalFlag.constructorTearoffs),
+            enableTripleShift: target.globalFeatures.tripleShift.isEnabled,
+            enableConstFunctions:
+                target.globalFeatures.constFunctions.isEnabled,
+            enableConstructorTearOff:
+                target.globalFeatures.constructorTearoffs.isEnabled,
             errorOnUnevaluatedConstant:
                 incrementalCompiler.context.options.errorOnUnevaluatedConstant);
         print("Transformed constants with $environmentDefinesDescription"
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index 81a45e4..c2ab5a9 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -869,7 +869,7 @@
       target.backendTarget,
       result.component,
       result.options.environmentDefines,
-      target.isExperimentEnabledGlobally(ExperimentalFlag.tripleShift),
+      target.globalFeatures.tripleShift.isEnabled,
       environment,
       !target.backendTarget.supportsSetLiterals,
       result.options.errorOnUnevaluatedConstant,
diff --git a/pkg/front_end/test/incremental_suite.dart b/pkg/front_end/test/incremental_suite.dart
index e72f3f2..84d3b6c 100644
--- a/pkg/front_end/test/incremental_suite.dart
+++ b/pkg/front_end/test/incremental_suite.dart
@@ -23,7 +23,7 @@
     show CompilerOptions, parseExperimentalArguments, parseExperimentalFlags;
 
 import 'package:front_end/src/api_prototype/experimental_flags.dart'
-    show ExperimentalFlag, experimentEnabledVersion;
+    show ExperimentalFlag;
 import 'package:front_end/src/api_prototype/incremental_kernel_generator.dart'
     show IncrementalCompilerResult;
 import "package:front_end/src/api_prototype/memory_file_system.dart"
@@ -392,7 +392,7 @@
 
   String doStringReplacements(String input) {
     Version enableNonNullableVersion =
-        experimentEnabledVersion[ExperimentalFlag.nonNullable]!;
+        ExperimentalFlag.nonNullable.experimentEnabledVersion;
     String output = input.replaceAll("%NNBD_VERSION_MARKER%",
         "${enableNonNullableVersion.major}.${enableNonNullableVersion.minor}");
     return output;
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index a85eeaf..099e6f8 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -639,6 +639,7 @@
 interior
 interleaved
 intermediate
+internet
 interpolations
 interrupted
 intersects
@@ -651,6 +652,7 @@
 ints
 invariants
 io
+ipv
 isolate
 isolated
 isolates
@@ -734,6 +736,7 @@
 logged
 logically
 lookahead
+loopback
 lots
 lp
 lparen
@@ -968,6 +971,7 @@
 pulled
 pure
 puts
+pv
 q
 q'i
 qi
diff --git a/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart
new file mode 100644
index 0000000..78f8244
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2022, the Dart 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.
+
+enum E {
+  e1,
+  e2;
+
+  void set values(List<E> val) {} // Error.
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.strong.expect b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.strong.expect
new file mode 100644
index 0000000..f257434
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.strong.expect
@@ -0,0 +1,40 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart:9:12: Error: This instance member conflicts with the synthesized static member called 'values'.
+//   void set values(List<E> val) {} // Error.
+//            ^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class E extends core::_Enum /*isEnum*/  {
+  static const field core::List<self::E> values = #C7;
+  static const field self::E e1 = #C3;
+  static const field self::E e2 = #C6;
+  const constructor •(core::int index, core::String name) → self::E
+    : super core::_Enum::•(index, name)
+    ;
+  method toString() → core::String
+    return "E.${this.{core::_Enum::_name}{core::String}}";
+  set values(core::List<self::E> val) → void {}
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = 0
+  #C2 = "e1"
+  #C3 = self::E {index:#C1, _name:#C2}
+  #C4 = 1
+  #C5 = "e2"
+  #C6 = self::E {index:#C4, _name:#C5}
+  #C7 = <self::E>[#C3, #C6]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///member_values_conflicts.dart:
+- E. (from org-dartlang-testcase:///member_values_conflicts.dart:5:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:103:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.strong.transformed.expect b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.strong.transformed.expect
new file mode 100644
index 0000000..f257434
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.strong.transformed.expect
@@ -0,0 +1,40 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart:9:12: Error: This instance member conflicts with the synthesized static member called 'values'.
+//   void set values(List<E> val) {} // Error.
+//            ^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class E extends core::_Enum /*isEnum*/  {
+  static const field core::List<self::E> values = #C7;
+  static const field self::E e1 = #C3;
+  static const field self::E e2 = #C6;
+  const constructor •(core::int index, core::String name) → self::E
+    : super core::_Enum::•(index, name)
+    ;
+  method toString() → core::String
+    return "E.${this.{core::_Enum::_name}{core::String}}";
+  set values(core::List<self::E> val) → void {}
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = 0
+  #C2 = "e1"
+  #C3 = self::E {index:#C1, _name:#C2}
+  #C4 = 1
+  #C5 = "e2"
+  #C6 = self::E {index:#C4, _name:#C5}
+  #C7 = <self::E>[#C3, #C6]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///member_values_conflicts.dart:
+- E. (from org-dartlang-testcase:///member_values_conflicts.dart:5:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:103:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.textual_outline.expect b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.textual_outline.expect
new file mode 100644
index 0000000..77502aa
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+enum E {
+  e1,
+  e2;
+
+  void set values(List<E> val) {}
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..77502aa
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.textual_outline_modelled.expect
@@ -0,0 +1,8 @@
+enum E {
+  e1,
+  e2;
+
+  void set values(List<E> val) {}
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.weak.expect b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.weak.expect
new file mode 100644
index 0000000..756f859
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.weak.expect
@@ -0,0 +1,40 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart:9:12: Error: This instance member conflicts with the synthesized static member called 'values'.
+//   void set values(List<E> val) {} // Error.
+//            ^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class E extends core::_Enum /*isEnum*/  {
+  static const field core::List<self::E> values = #C7;
+  static const field self::E e1 = #C3;
+  static const field self::E e2 = #C6;
+  const constructor •(core::int index, core::String name) → self::E
+    : super core::_Enum::•(index, name)
+    ;
+  method toString() → core::String
+    return "E.${this.{core::_Enum::_name}{core::String}}";
+  set values(core::List<self::E> val) → void {}
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = 0
+  #C2 = "e1"
+  #C3 = self::E {index:#C1, _name:#C2}
+  #C4 = 1
+  #C5 = "e2"
+  #C6 = self::E {index:#C4, _name:#C5}
+  #C7 = <self::E*>[#C3, #C6]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///member_values_conflicts.dart:
+- E. (from org-dartlang-testcase:///member_values_conflicts.dart:5:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:103:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.weak.modular.expect b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.weak.modular.expect
new file mode 100644
index 0000000..756f859
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.weak.modular.expect
@@ -0,0 +1,40 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart:9:12: Error: This instance member conflicts with the synthesized static member called 'values'.
+//   void set values(List<E> val) {} // Error.
+//            ^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class E extends core::_Enum /*isEnum*/  {
+  static const field core::List<self::E> values = #C7;
+  static const field self::E e1 = #C3;
+  static const field self::E e2 = #C6;
+  const constructor •(core::int index, core::String name) → self::E
+    : super core::_Enum::•(index, name)
+    ;
+  method toString() → core::String
+    return "E.${this.{core::_Enum::_name}{core::String}}";
+  set values(core::List<self::E> val) → void {}
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = 0
+  #C2 = "e1"
+  #C3 = self::E {index:#C1, _name:#C2}
+  #C4 = 1
+  #C5 = "e2"
+  #C6 = self::E {index:#C4, _name:#C5}
+  #C7 = <self::E*>[#C3, #C6]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///member_values_conflicts.dart:
+- E. (from org-dartlang-testcase:///member_values_conflicts.dart:5:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:103:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.weak.outline.expect b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.weak.outline.expect
new file mode 100644
index 0000000..5cd70a0
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.weak.outline.expect
@@ -0,0 +1,32 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart:9:12: Error: This instance member conflicts with the synthesized static member called 'values'.
+//   void set values(List<E> val) {} // Error.
+//            ^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class E extends core::_Enum /*isEnum*/  {
+  static const field core::List<self::E> values = const <self::E>[self::E::e1, self::E::e2];
+  static const field self::E e1 = const self::E::•(0, "e1");
+  static const field self::E e2 = const self::E::•(1, "e2");
+  const constructor •(core::int index, core::String name) → self::E
+    : super core::_Enum::•(index, name)
+    ;
+  method toString() → core::String
+    return "E.${this.{core::_Enum::_name}{core::String}}";
+  set values(core::List<self::E> val) → void
+    ;
+}
+static method main() → dynamic
+  ;
+
+
+Extra constant evaluation status:
+Evaluated: ListLiteral @ org-dartlang-testcase:///member_values_conflicts.dart:5:6 -> ListConstant(const <E*>[const E{_Enum.index: 0, _Enum._name: "e1"}, const E{_Enum.index: 1, _Enum._name: "e2"}])
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///member_values_conflicts.dart:6:3 -> InstanceConstant(const E{_Enum.index: 0, _Enum._name: "e1"})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///member_values_conflicts.dart:7:3 -> InstanceConstant(const E{_Enum.index: 1, _Enum._name: "e2"})
+Extra constant evaluation: evaluated: 8, effectively constant: 3
diff --git a/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.weak.transformed.expect b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.weak.transformed.expect
new file mode 100644
index 0000000..756f859
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart.weak.transformed.expect
@@ -0,0 +1,40 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/enhanced_enums/member_values_conflicts.dart:9:12: Error: This instance member conflicts with the synthesized static member called 'values'.
+//   void set values(List<E> val) {} // Error.
+//            ^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class E extends core::_Enum /*isEnum*/  {
+  static const field core::List<self::E> values = #C7;
+  static const field self::E e1 = #C3;
+  static const field self::E e2 = #C6;
+  const constructor •(core::int index, core::String name) → self::E
+    : super core::_Enum::•(index, name)
+    ;
+  method toString() → core::String
+    return "E.${this.{core::_Enum::_name}{core::String}}";
+  set values(core::List<self::E> val) → void {}
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = 0
+  #C2 = "e1"
+  #C3 = self::E {index:#C1, _name:#C2}
+  #C4 = 1
+  #C5 = "e2"
+  #C6 = self::E {index:#C4, _name:#C5}
+  #C7 = <self::E*>[#C3, #C6]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///member_values_conflicts.dart:
+- E. (from org-dartlang-testcase:///member_values_conflicts.dart:5:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:103:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/super_parameters/issue48714.dart b/pkg/front_end/testcases/super_parameters/issue48714.dart
new file mode 100644
index 0000000..f813a85
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48714.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2022, the Dart 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.
+
+class Base {
+  int value;
+  Base(this.value);
+}
+
+class Extended extends Base {
+  Extended.one(final super.value); // Ok.
+  Extended.two(var super.value); // Error.
+  Extended.three(const super.value); // Error.
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/super_parameters/issue48714.dart.strong.expect b/pkg/front_end/testcases/super_parameters/issue48714.dart.strong.expect
new file mode 100644
index 0000000..fb8c1e75
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48714.dart.strong.expect
@@ -0,0 +1,35 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/super_parameters/issue48714.dart:13:18: Error: Can't have modifier 'const' here.
+// Try removing 'const'.
+//   Extended.three(const super.value); // Error.
+//                  ^^^^^
+//
+// pkg/front_end/testcases/super_parameters/issue48714.dart:12:16: Error: Can't have modifier 'var' here.
+// Try removing 'var'.
+//   Extended.two(var super.value); // Error.
+//                ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Base extends core::Object {
+  field core::int value;
+  constructor •(core::int value) → self::Base
+    : self::Base::value = value, super core::Object::•()
+    ;
+}
+class Extended extends self::Base {
+  constructor one(final core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+  constructor two(core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+  constructor three(core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/super_parameters/issue48714.dart.strong.transformed.expect b/pkg/front_end/testcases/super_parameters/issue48714.dart.strong.transformed.expect
new file mode 100644
index 0000000..fb8c1e75
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48714.dart.strong.transformed.expect
@@ -0,0 +1,35 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/super_parameters/issue48714.dart:13:18: Error: Can't have modifier 'const' here.
+// Try removing 'const'.
+//   Extended.three(const super.value); // Error.
+//                  ^^^^^
+//
+// pkg/front_end/testcases/super_parameters/issue48714.dart:12:16: Error: Can't have modifier 'var' here.
+// Try removing 'var'.
+//   Extended.two(var super.value); // Error.
+//                ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Base extends core::Object {
+  field core::int value;
+  constructor •(core::int value) → self::Base
+    : self::Base::value = value, super core::Object::•()
+    ;
+}
+class Extended extends self::Base {
+  constructor one(final core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+  constructor two(core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+  constructor three(core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/super_parameters/issue48714.dart.textual_outline.expect b/pkg/front_end/testcases/super_parameters/issue48714.dart.textual_outline.expect
new file mode 100644
index 0000000..c5daa03
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48714.dart.textual_outline.expect
@@ -0,0 +1,10 @@
+class Base {
+  int value;
+  Base(this.value);
+}
+class Extended extends Base {
+  Extended.one(final super.value);
+  Extended.two(var super.value);
+  Extended.three(const super.value);
+}
+main() {}
diff --git a/pkg/front_end/testcases/super_parameters/issue48714.dart.weak.expect b/pkg/front_end/testcases/super_parameters/issue48714.dart.weak.expect
new file mode 100644
index 0000000..fb8c1e75
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48714.dart.weak.expect
@@ -0,0 +1,35 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/super_parameters/issue48714.dart:13:18: Error: Can't have modifier 'const' here.
+// Try removing 'const'.
+//   Extended.three(const super.value); // Error.
+//                  ^^^^^
+//
+// pkg/front_end/testcases/super_parameters/issue48714.dart:12:16: Error: Can't have modifier 'var' here.
+// Try removing 'var'.
+//   Extended.two(var super.value); // Error.
+//                ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Base extends core::Object {
+  field core::int value;
+  constructor •(core::int value) → self::Base
+    : self::Base::value = value, super core::Object::•()
+    ;
+}
+class Extended extends self::Base {
+  constructor one(final core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+  constructor two(core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+  constructor three(core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/super_parameters/issue48714.dart.weak.modular.expect b/pkg/front_end/testcases/super_parameters/issue48714.dart.weak.modular.expect
new file mode 100644
index 0000000..fb8c1e75
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48714.dart.weak.modular.expect
@@ -0,0 +1,35 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/super_parameters/issue48714.dart:13:18: Error: Can't have modifier 'const' here.
+// Try removing 'const'.
+//   Extended.three(const super.value); // Error.
+//                  ^^^^^
+//
+// pkg/front_end/testcases/super_parameters/issue48714.dart:12:16: Error: Can't have modifier 'var' here.
+// Try removing 'var'.
+//   Extended.two(var super.value); // Error.
+//                ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Base extends core::Object {
+  field core::int value;
+  constructor •(core::int value) → self::Base
+    : self::Base::value = value, super core::Object::•()
+    ;
+}
+class Extended extends self::Base {
+  constructor one(final core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+  constructor two(core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+  constructor three(core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/super_parameters/issue48714.dart.weak.outline.expect b/pkg/front_end/testcases/super_parameters/issue48714.dart.weak.outline.expect
new file mode 100644
index 0000000..288205c
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48714.dart.weak.outline.expect
@@ -0,0 +1,27 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/super_parameters/issue48714.dart:13:18: Error: Can't have modifier 'const' here.
+// Try removing 'const'.
+//   Extended.three(const super.value); // Error.
+//                  ^^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Base extends core::Object {
+  field core::int value;
+  constructor •(core::int value) → self::Base
+    ;
+}
+class Extended extends self::Base {
+  constructor one(final core::int value) → self::Extended
+    ;
+  constructor two(core::int value) → self::Extended
+    ;
+  constructor three(core::int value) → self::Extended
+    ;
+}
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/super_parameters/issue48714.dart.weak.transformed.expect b/pkg/front_end/testcases/super_parameters/issue48714.dart.weak.transformed.expect
new file mode 100644
index 0000000..fb8c1e75
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48714.dart.weak.transformed.expect
@@ -0,0 +1,35 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/super_parameters/issue48714.dart:13:18: Error: Can't have modifier 'const' here.
+// Try removing 'const'.
+//   Extended.three(const super.value); // Error.
+//                  ^^^^^
+//
+// pkg/front_end/testcases/super_parameters/issue48714.dart:12:16: Error: Can't have modifier 'var' here.
+// Try removing 'var'.
+//   Extended.two(var super.value); // Error.
+//                ^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class Base extends core::Object {
+  field core::int value;
+  constructor •(core::int value) → self::Base
+    : self::Base::value = value, super core::Object::•()
+    ;
+}
+class Extended extends self::Base {
+  constructor one(final core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+  constructor two(core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+  constructor three(core::int value) → self::Extended
+    : super self::Base::•(value)
+    ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index df71eb8..fab8f81 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -217,6 +217,7 @@
 regress/issue_41265.crash: FormatterCrash
 super_parameters/issue47741: FormatterCrash
 super_parameters/issue47922: FormatterCrash
+super_parameters/issue48714: FormatterCrash
 super_parameters/opt_out: FormatterCrash
 super_parameters/var_before_super: FormatterCrash
 triple_shift/invalid_operator: FormatterCrash
diff --git a/pkg/front_end/tool/_fasta/generate_experimental_flags.dart b/pkg/front_end/tool/_fasta/generate_experimental_flags.dart
index e321747..7b29209 100644
--- a/pkg/front_end/tool/_fasta/generate_experimental_flags.dart
+++ b/pkg/front_end/tool/_fasta/generate_experimental_flags.dart
@@ -120,36 +120,185 @@
 
   sb.write('''
 
-enum ExperimentalFlag {
+/// An experiment flag including its fixed properties.
+class ExperimentalFlag {
+  /// The name of this flag as used in the --enable-experiment option.
+  final String name;
+
+  /// `true` if this experimental feature is enabled by default.
+  ///
+  /// When `true`, the feature can still be disabled in individual libraries
+  /// with a language version below the [experimentEnabledVersion], and if not
+  /// [isExpired], the feature can also be disabled by using a 'no-' prefix
+  /// in the --enable-experiment option.
+  final bool isEnabledByDefault;
+
+  /// `true` if this feature can no longer be changed using the
+  /// --enable-experiment option.
+  ///
+  /// Libraries can still opt out of the feature by using a language version
+  /// below the [experimentEnabledVersion].
+  final bool isExpired;
+  final Version enabledVersion;
+
+  /// The minimum version that supports this feature.
+  ///
+  /// If the feature is not enabled by default, this is the current language
+  /// version.
+  final Version experimentEnabledVersion;
+
+  /// The minimum version that supports this feature in allowed libraries.
+  ///
+  /// Allowed libraries are specified in
+  /// 
+  ///    sdk/lib/_internal/allowed_experiments.json
+  final Version experimentReleasedVersion;
+
+  const ExperimentalFlag(
+      {required this.name,
+      required this.isEnabledByDefault,
+      required this.isExpired,
+      required this.enabledVersion,
+      required this.experimentEnabledVersion,
+      required this.experimentReleasedVersion});
 ''');
   for (String key in keys) {
-    sb.writeln('  ${keyToIdentifier(key)},');
+    String identifier = keyToIdentifier(key);
+    int enabledInMajor;
+    int enabledInMinor;
+    String? enabledIn =
+        getAsVersionNumberString((features[key] as YamlMap)['enabledIn']);
+    if (enabledIn == null) {
+      enabledInMajor = currentVersionMajor;
+      enabledInMinor = currentVersionMinor;
+    } else {
+      List<String> split = enabledIn.split(".");
+      enabledInMajor = int.parse(split[0]);
+      enabledInMinor = int.parse(split[1]);
+    }
+    bool? expired = (features[key] as YamlMap)['expired'];
+    bool shipped = (features[key] as YamlMap)['enabledIn'] != null;
+    if (shipped) {
+      if (expired == false) {
+        throw 'Cannot mark shipped feature "$key" as "expired: false"';
+      }
+    }
+    int releaseMajor;
+    int releaseMinor;
+    String? experimentalReleaseVersion = getAsVersionNumberString(
+        (features[key] as YamlMap)['experimentalReleaseVersion']);
+    if (experimentalReleaseVersion != null) {
+      List<String> split = experimentalReleaseVersion.split(".");
+      releaseMajor = int.parse(split[0]);
+      releaseMinor = int.parse(split[1]);
+    } else if (enabledIn != null) {
+      List<String> split = enabledIn.split(".");
+      releaseMajor = int.parse(split[0]);
+      releaseMinor = int.parse(split[1]);
+    } else {
+      releaseMajor = currentVersionMajor;
+      releaseMinor = currentVersionMinor;
+    }
+
+    sb.writeln('''
+  static const ExperimentalFlag ${identifier} =
+    const ExperimentalFlag(
+      name: '$key',
+      isEnabledByDefault: $shipped,
+      isExpired: ${expired == true},
+      enabledVersion: const Version($enabledInMajor, $enabledInMinor),
+      experimentEnabledVersion: const Version($enabledInMajor, $enabledInMinor),
+      experimentReleasedVersion: const Version($releaseMajor, $releaseMinor));
+''');
+  }
+  sb.write('''
+}
+''');
+
+  sb.write('''
+/// Interface for accessing the global state of experimental features. 
+class GlobalFeatures {
+  final Map<ExperimentalFlag, bool> explicitExperimentalFlags;
+  final AllowedExperimentalFlags? allowedExperimentalFlags;
+  final Map<ExperimentalFlag, bool>? defaultExperimentFlagsForTesting;
+  final Map<ExperimentalFlag, Version>? experimentEnabledVersionForTesting;
+  final Map<ExperimentalFlag, Version>? experimentReleasedVersionForTesting;
+
+  GlobalFeatures(this.explicitExperimentalFlags,
+      {this.allowedExperimentalFlags,
+      this.defaultExperimentFlagsForTesting,
+      this.experimentEnabledVersionForTesting,
+      this.experimentReleasedVersionForTesting});
+
+  GlobalFeature _computeGlobalFeature(ExperimentalFlag flag) {
+    return new GlobalFeature(
+        flag,
+        isExperimentEnabled(flag,
+            defaultExperimentFlagsForTesting: defaultExperimentFlagsForTesting,
+            explicitExperimentalFlags: explicitExperimentalFlags));
+  }
+
+  LibraryFeature _computeLibraryFeature(
+      ExperimentalFlag flag, Uri canonicalUri, Version libraryVersion) {
+    return new LibraryFeature(
+        flag,
+        isExperimentEnabledInLibrary(flag, canonicalUri,
+            defaultExperimentFlagsForTesting: defaultExperimentFlagsForTesting,
+            explicitExperimentalFlags: explicitExperimentalFlags,
+            allowedExperimentalFlags: allowedExperimentalFlags),
+        getExperimentEnabledVersionInLibrary(
+            flag, canonicalUri, explicitExperimentalFlags,
+            allowedExperimentalFlags: allowedExperimentalFlags,
+            defaultExperimentFlagsForTesting: defaultExperimentFlagsForTesting,
+            experimentEnabledVersionForTesting:
+                experimentEnabledVersionForTesting,
+            experimentReleasedVersionForTesting:
+                experimentReleasedVersionForTesting),
+        isExperimentEnabledInLibraryByVersion(
+            flag, canonicalUri, libraryVersion,
+            defaultExperimentFlagsForTesting: defaultExperimentFlagsForTesting,
+            explicitExperimentalFlags: explicitExperimentalFlags,
+            allowedExperimentalFlags: allowedExperimentalFlags));
+  }
+''');
+  for (String key in keys) {
+    String identifier = keyToIdentifier(key);
+    sb.write('''
+
+  GlobalFeature? _${identifier};
+  GlobalFeature get ${identifier} =>
+      _${identifier} ??= _computeGlobalFeature(ExperimentalFlag.${identifier});    
+''');
   }
   sb.write('''
 }
 
+/// Interface for accessing the state of experimental features within a
+/// specific library.
+class LibraryFeatures {
+  final GlobalFeatures globalFeatures;
+  final Uri canonicalUri;
+  final Version libraryVersion;
+
+  LibraryFeatures(this.globalFeatures, this.canonicalUri, this.libraryVersion);
+''');
+  for (String key in keys) {
+    String identifier = keyToIdentifier(key);
+    sb.write('''
+
+  LibraryFeature? _${identifier};
+  LibraryFeature get ${identifier} => _${identifier} ??= globalFeatures
+      ._computeLibraryFeature(
+          ExperimentalFlag.${identifier},
+          canonicalUri,
+          libraryVersion);
+''');
+  }
+  sb.write('''
+}
 ''');
 
-  for (String key in keys) {
-    int major;
-    int minor;
-    String? enabledIn =
-        getAsVersionNumberString((features[key] as YamlMap)['enabledIn']);
-    if (enabledIn == null) {
-      major = currentVersionMajor;
-      minor = currentVersionMinor;
-    } else {
-      List<String> split = enabledIn.split(".");
-      major = int.parse(split[0]);
-      minor = int.parse(split[1]);
-    }
-    sb.writeln('  const Version enable'
-        '${keyToIdentifier(key, upperCaseFirst: true)}'
-        'Version = const Version($major, $minor);');
-  }
-
   sb.write('''
-
 ExperimentalFlag? parseExperimentalFlag(String flag) {
   switch (flag) {
 ''');
@@ -161,78 +310,15 @@
   return null;
 }
 
-const Map<ExperimentalFlag, bool> defaultExperimentalFlags = {
+final Map<ExperimentalFlag, bool> defaultExperimentalFlags = {
 ''');
   for (String key in keys) {
-    bool? expired = (features[key] as YamlMap)['expired'];
-    bool shipped = (features[key] as YamlMap)['enabledIn'] != null;
-    sb.writeln('  ExperimentalFlag.${keyToIdentifier(key)}: ${shipped},');
-    if (shipped) {
-      if (expired == false) {
-        throw 'Cannot mark shipped feature "$key" as "expired: false"';
-      }
-    }
+    sb.writeln('''
+  ExperimentalFlag.${keyToIdentifier(key)}:
+      ExperimentalFlag.${keyToIdentifier(key)}.isEnabledByDefault,''');
   }
   sb.write('''
 };
-
-const Map<ExperimentalFlag, bool> expiredExperimentalFlags = {
-''');
-  for (String key in keys) {
-    bool expired = (features[key] as YamlMap)['expired'] == true;
-    sb.writeln('  ExperimentalFlag.${keyToIdentifier(key)}: ${expired},');
-  }
-  sb.write('''
-};
-
-const Map<ExperimentalFlag, Version> experimentEnabledVersion = {
-''');
-  for (String key in keys) {
-    int major;
-    int minor;
-    String? enabledIn =
-        getAsVersionNumberString((features[key] as YamlMap)['enabledIn']);
-    if (enabledIn != null) {
-      List<String> split = enabledIn.split(".");
-      major = int.parse(split[0]);
-      minor = int.parse(split[1]);
-    } else {
-      major = currentVersionMajor;
-      minor = currentVersionMinor;
-    }
-    sb.writeln('  ExperimentalFlag.${keyToIdentifier(key)}: '
-        'const Version($major, $minor),');
-  }
-  sb.write('''
-};
-
-const Map<ExperimentalFlag, Version> experimentReleasedVersion = {
-''');
-  for (String key in keys) {
-    int major;
-    int minor;
-    String? enabledIn =
-        getAsVersionNumberString((features[key] as YamlMap)['enabledIn']);
-    String? experimentalReleaseVersion = getAsVersionNumberString(
-        (features[key] as YamlMap)['experimentalReleaseVersion']);
-    if (experimentalReleaseVersion != null) {
-      List<String> split = experimentalReleaseVersion.split(".");
-      major = int.parse(split[0]);
-      minor = int.parse(split[1]);
-    } else if (enabledIn != null) {
-      List<String> split = enabledIn.split(".");
-      major = int.parse(split[0]);
-      minor = int.parse(split[1]);
-    } else {
-      major = currentVersionMajor;
-      minor = currentVersionMinor;
-    }
-    sb.writeln('  ExperimentalFlag.${keyToIdentifier(key)}: '
-        'const Version($major, $minor),');
-  }
-  sb.write('''
-};
-
 ''');
 
   Uri allowListFile = computeAllowListFile(repoDir);
diff --git a/pkg/frontend_server/lib/compute_kernel.dart b/pkg/frontend_server/lib/compute_kernel.dart
index 3754d31..e51cafc 100644
--- a/pkg/frontend_server/lib/compute_kernel.dart
+++ b/pkg/frontend_server/lib/compute_kernel.dart
@@ -7,7 +7,6 @@
 /// A library to invoke the CFE to compute kernel summary files.
 ///
 /// Used by `utils/bazel/kernel_worker.dart`.
-
 import 'dart:async';
 import 'dart:io';
 
@@ -22,7 +21,6 @@
 import 'package:compiler/src/kernel/dart2js_target.dart';
 import 'package:dev_compiler/src/kernel/target.dart';
 import 'package:front_end/src/api_prototype/incremental_kernel_generator.dart';
-import 'package:front_end/src/api_prototype/experimental_flags.dart';
 import 'package:front_end/src/api_unstable/bazel_worker.dart' as fe;
 import 'package:front_end/src/fasta/kernel/macro/macro.dart';
 import 'package:kernel/ast.dart' show Component, Library, Reference;
@@ -299,8 +297,7 @@
   // Either set up or reset the state for macros based on experiment status.
   // TODO: Make this a part of `initializeCompiler`, if/when we want to make it
   // more widely supported.
-  if (state.processedOpts
-      .isExperimentEnabledGlobally(ExperimentalFlag.macros)) {
+  if (state.processedOpts.globalFeatures.macros.isEnabled) {
     enableMacros = true;
     forceEnableMacros = true;
 
@@ -324,8 +321,8 @@
             () => isolatedExecutor.start(serializationMode);
         break;
       case 'aot':
-        state.options.macroExecutorProvider =
-            () => processExecutor.start(serializationMode);
+        state.options.macroExecutorProvider = () => processExecutor.start(
+            serializationMode, processExecutor.CommunicationChannel.socket);
         break;
       default:
         throw ArgumentError('Unrecognized precompiled macro format $format');
diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart
index a9fe7dd..2a303b8 100644
--- a/pkg/frontend_server/lib/frontend_server.dart
+++ b/pkg/frontend_server/lib/frontend_server.dart
@@ -521,7 +521,7 @@
     }
 
     if (nullSafety == null &&
-        compilerOptions.isExperimentEnabled(ExperimentalFlag.nonNullable)) {
+        compilerOptions.globalFeatures.nonNullable.isEnabled) {
       await autoDetectNullSafetyMode(_mainSource, compilerOptions);
     }
 
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index e77ba6b..e228973 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -25,7 +25,6 @@
         InvocationMode,
         DiagnosticMessage,
         DiagnosticMessageHandler,
-        ExperimentalFlag,
         FileSystem,
         FileSystemEntity,
         NnbdMode,
@@ -277,7 +276,7 @@
     ..verbosity = verbosity;
 
   if (nullSafety == null &&
-      compilerOptions.isExperimentEnabled(ExperimentalFlag.nonNullable)) {
+      compilerOptions.globalFeatures.nonNullable.isEnabled) {
     await autoDetectNullSafetyMode(mainUri, compilerOptions);
   }
 
diff --git a/tools/VERSION b/tools/VERSION
index 825b9b2..41c7365 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 261
+PRERELEASE 262
 PRERELEASE_PATCH 0
\ No newline at end of file