/** * This file contains code to generate experimental flags * based on the information in tools/experimental_features.yaml. */ import 'dart:io'; import 'package:analysis_tool/tools.dart'; import 'package:front_end/src/fasta/scanner/characters.dart' show $MINUS, $_; import 'package:front_end/src/testing/package_root.dart' as pkgRoot; import 'package:path/path.dart'; import 'package:yaml/yaml.dart' show YamlMap, loadYaml; main() async { await GeneratedContent.generateAll( normalize(join(pkgRoot.packageRoot, 'analyzer')), allTargets); } List get allTargets { Map experimentsYaml = loadYaml(new File(join( normalize(join(pkgRoot.packageRoot, '../tools')), 'experimental_features.yaml')) .readAsStringSync()); return [ new GeneratedFile('lib/src/dart/analysis/experiments.g.dart', (String pkgPath) async { var generator = new _ExperimentsGenerator(experimentsYaml); generator.generateFormatCode(); return generator.out.toString(); }), ]; } String keyToIdentifier(String key) { var identifier = StringBuffer(); for (int index = 0; index < key.length; ++index) { var code = key.codeUnitAt(index); if (code == $MINUS) { code = $_; } identifier.writeCharCode(code); } return identifier.toString(); } class _ExperimentsGenerator { final Map experimentsYaml; List keysSorted; final out = new StringBuffer(''' // // THIS FILE IS GENERATED. DO NOT EDIT. // // Instead modify 'tools/experimental_features.yaml' and run // 'dart pkg/analyzer/tool/experiments/generate.dart' to update. part of 'experiments.dart'; '''); _ExperimentsGenerator(this.experimentsYaml); void generateFormatCode() { keysSorted = experimentsYaml.keys.cast().toList()..sort(); generateSection_KnownFeatures(); generateSection_BuildExperimentalFlagsArray(); generateSection_EnableString(); generateSection_ExperimentalFeature(); generateSection_IsEnabledByDefault(); generateSection_IsExpired(); generateSection_CurrentState(); } void generateSection_BuildExperimentalFlagsArray() { out.write(''' List _buildExperimentalFlagsArray() => [ '''); for (var key in keysSorted) { var id = keyToIdentifier(key); var entry = experimentsYaml[key] as YamlMap; bool shipped = entry['enabledIn'] != null; bool expired = entry['expired']; if (shipped || expired == true) { out.writeln('true, // $key'); } else { out.writeln('IsEnabledByDefault.$id,'); } } // TODO(danrubel): Remove bogus entries out.write(''' false, // bogus-disabled true, // bogus-enabled ]; '''); } void generateSection_CurrentState() { // TODO(danrubel): Remove bogus entries out.write(''' mixin _CurrentState { /// Current state for the flag "bogus-disabled" @deprecated bool get bogus_disabled => isEnabled(ExperimentalFeatures.bogus_disabled); /// Current state for the flag "bogus-enabled" @deprecated bool get bogus_enabled => isEnabled(ExperimentalFeatures.bogus_enabled); '''); for (var key in keysSorted) { var id = keyToIdentifier(key); out.write(''' /// Current state for the flag "$key" bool get $id => isEnabled(ExperimentalFeatures.$id); '''); } out.write(''' bool isEnabled(covariant ExperimentalFeature feature); }'''); } void generateSection_EnableString() { out.write(''' /// Constant strings for enabling each of the currently known experimental /// flags. class EnableString { '''); for (var key in keysSorted) { out.write(''' /// String to enable the experiment "$key" static const String ${keyToIdentifier(key)} = '$key'; '''); } // TODO(danrubel): Remove bogus entries out.write(''' /// String to enable the experiment "bogus-disabled" @deprecated static const String bogus_disabled = 'bogus-disabled'; /// String to enable the experiment "bogus-enabled" @deprecated static const String bogus_enabled = 'bogus-enabled'; }'''); } void generateSection_ExperimentalFeature() { out.write(''' class ExperimentalFeatures { '''); int index = 0; for (var key in keysSorted) { var id = keyToIdentifier(key); var help = (experimentsYaml[key] as YamlMap)['help'] ?? ''; var enabledIn = (experimentsYaml[key] as YamlMap)['enabledIn']; out.write(''' static const $id = const ExperimentalFeature( $index, EnableString.$id, IsEnabledByDefault.$id, IsExpired.$id, '$help' '''); if (enabledIn != null) { if (enabledIn is double) { enabledIn = '$enabledIn.0'; } out.write(",firstSupportedVersion: '$enabledIn'"); } out.writeln(');'); ++index; } // TODO(danrubel): Remove bogus entries out.write(''' @deprecated static const bogus_disabled = const ExperimentalFeature( $index, // ignore: deprecated_member_use_from_same_package EnableString.bogus_disabled, IsEnabledByDefault.bogus_disabled, IsExpired.bogus_disabled, null); @deprecated static const bogus_enabled = const ExperimentalFeature( ${index + 1}, // ignore: deprecated_member_use_from_same_package EnableString.bogus_enabled, IsEnabledByDefault.bogus_enabled, IsExpired.bogus_enabled, null, firstSupportedVersion: '1.0.0'); }'''); } void generateSection_IsEnabledByDefault() { out.write(''' /// Constant bools indicating whether each experimental flag is currently /// enabled by default. class IsEnabledByDefault { '''); for (var key in keysSorted) { var entry = experimentsYaml[key] as YamlMap; bool shipped = entry['enabledIn'] != null; out.write(''' /// Default state of the experiment "$key" static const bool ${keyToIdentifier(key)} = $shipped; '''); } // TODO(danrubel): Remove bogus entries out.write(''' /// Default state of the experiment "bogus-disabled" @deprecated static const bool bogus_disabled = false; /// Default state of the experiment "bogus-enabled" @deprecated static const bool bogus_enabled = true; }'''); } void generateSection_IsExpired() { out.write(''' /// Constant bools indicating whether each experimental flag is currently /// expired (meaning its enable/disable status can no longer be altered from the /// value in [IsEnabledByDefault]). class IsExpired { '''); for (var key in keysSorted) { var entry = experimentsYaml[key] as YamlMap; bool shipped = entry['enabledIn'] != null; bool expired = entry['expired']; out.write(''' /// Expiration status of the experiment "$key" static const bool ${keyToIdentifier(key)} = ${expired == true}; '''); if (shipped && expired == false) { throw 'Cannot mark shipped feature as "expired: false"'; } } // TODO(danrubel): Remove bogus entries out.write(''' /// Expiration status of the experiment "bogus-disabled" static const bool bogus_disabled = true; /// Expiration status of the experiment "bogus-enabled" static const bool bogus_enabled = true; }'''); } void generateSection_KnownFeatures() { out.write(''' /// A map containing information about all known experimental flags. const _knownFeatures = { '''); for (var key in keysSorted) { var id = keyToIdentifier(key); out.write(''' EnableString.$id: ExperimentalFeatures.$id, '''); } // TODO(danrubel): Remove bogus entries out.write(''' // ignore: deprecated_member_use_from_same_package EnableString.bogus_disabled: ExperimentalFeatures.bogus_disabled, // ignore: deprecated_member_use_from_same_package EnableString.bogus_enabled: ExperimentalFeatures.bogus_enabled, }; '''); } }