blob: d74f223173e4bce55112232191afa0d8cd43ff3c [file] [log] [blame]
/**
* 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<GeneratedContent> get allTargets {
Map<dynamic, dynamic> experimentsYaml = loadYaml(new File(join(
normalize(join(pkgRoot.packageRoot, '../tools')),
'experimental_features.yaml'))
.readAsStringSync());
return <GeneratedContent>[
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<String> 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<String>().toList()..sort();
generateSection_KnownFeatures();
generateSection_BuildExperimentalFlagsArray();
generateSection_EnableString();
generateSection_ExperimentalFeature();
generateSection_IsEnabledByDefault();
generateSection_IsExpired();
generateSection_CurrentState();
}
void generateSection_BuildExperimentalFlagsArray() {
out.write('''
List<bool> _buildExperimentalFlagsArray() => <bool>[
''');
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"
bool get bogus_disabled => isEnabled(ExperimentalFeatures.bogus_disabled);
/// Current state for the flag "bogus-enabled"
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"
static const String bogus_disabled = 'bogus-disabled';
/// String to enable the experiment "bogus-enabled"
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('''
static const bogus_disabled = const ExperimentalFeature(
$index,
EnableString.bogus_disabled,
IsEnabledByDefault.bogus_disabled,
IsExpired.bogus_disabled,
null);
static const bogus_enabled = const ExperimentalFeature(
${index + 1},
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"
static const bool bogus_disabled = false;
/// Default state of the experiment "bogus-enabled"
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 = <String, ExperimentalFeature>{
''');
for (var key in keysSorted) {
var id = keyToIdentifier(key);
out.write('''
EnableString.$id: ExperimentalFeatures.$id,
''');
}
// TODO(danrubel): Remove bogus entries
out.write('''
EnableString.bogus_disabled: ExperimentalFeatures.bogus_disabled,
EnableString.bogus_enabled: ExperimentalFeatures.bogus_enabled,
};
''');
}
}