| // 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 'dart:typed_data'; | |
| import 'package:_fe_analyzer_shared/src/experiments/flags.dart' | |
| as shared | |
| show ExperimentalFlag; | |
| import 'package:_fe_analyzer_shared/src/parser/experimental_features.dart' | |
| as parser | |
| show ExperimentalFeatures; | |
| import 'package:analyzer/dart/analysis/features.dart'; | |
| import 'package:analyzer/src/dart/analysis/experiments_impl.dart'; | |
| import 'package:meta/meta.dart'; | |
| import 'package:pub_semver/pub_semver.dart'; | |
| export 'package:analyzer/src/dart/analysis/experiments_impl.dart' | |
| show | |
| ConflictingFlags, | |
| ExperimentalFeature, | |
| IllegalUseOfExpiredFlag, | |
| UnnecessaryUseOfExpiredFlag, | |
| UnrecognizedFlag, | |
| ValidationResult, | |
| validateFlags; | |
| part 'experiments.g.dart'; | |
| /// Gets access to the private list of boolean flags in an [ExperimentStatus] | |
| /// object. For testing use only. | |
| @visibleForTesting | |
| List<bool> getExperimentalFlags_forTesting(ExperimentStatus status) => | |
| status._flags; | |
| /// Gets access to the private SDK language version in an [ExperimentStatus] | |
| /// object. For testing use only. | |
| @visibleForTesting | |
| Version getSdkLanguageVersion_forTesting(ExperimentStatus status) => | |
| status._sdkLanguageVersion; | |
| class ExperimentalFeaturesStatus implements parser.ExperimentalFeatures { | |
| final FeatureSet _featureSet; | |
| ExperimentalFeaturesStatus(this._featureSet); | |
| @override | |
| bool isExperimentEnabled(shared.ExperimentalFlag flag) { | |
| return _featureSet.isEnabled(fromSharedExperimentalFlags(flag)); | |
| } | |
| } | |
| /// A representation of the set of experiments that are active and whether they | |
| /// are enabled. | |
| class ExperimentStatus with _CurrentState implements FeatureSet { | |
| /// The current language version. | |
| static final Version currentVersion = Version.parse(_currentVersion); | |
| /// A map containing information about all known experimental flags. | |
| static final Map<String, ExperimentalFeature> knownFeatures = _knownFeatures; | |
| final Version _sdkLanguageVersion; | |
| final List<bool> _explicitEnabledFlags; | |
| final List<bool> _explicitDisabledFlags; | |
| final List<bool> _flags; | |
| factory ExperimentStatus() { | |
| return ExperimentStatus.latestLanguageVersion(); | |
| } | |
| factory ExperimentStatus.fromStorage(Uint8List encoded) { | |
| var byteIndex = 0; | |
| int getByte() { | |
| return encoded[byteIndex++]; | |
| } | |
| List<bool> getBoolList(int length) { | |
| var result = List<bool>.filled(length, false); | |
| var byteValue = 0; | |
| var bitIndex = 0; | |
| for (var i = 0; i < length; i++) { | |
| if (bitIndex == 0) { | |
| byteValue = getByte(); | |
| } | |
| if ((byteValue & (1 << bitIndex)) != 0) { | |
| result[i] = true; | |
| } | |
| bitIndex = (bitIndex + 1) % 8; | |
| } | |
| return result; | |
| } | |
| var sdkLanguageVersion = Version(getByte(), getByte(), 0); | |
| var featureCount = getByte(); | |
| return ExperimentStatus._( | |
| sdkLanguageVersion, | |
| getBoolList(featureCount), | |
| getBoolList(featureCount), | |
| getBoolList(featureCount), | |
| ); | |
| } | |
| /// Decodes the strings given in [flags] into a representation of the set of | |
| /// experiments that should be enabled. | |
| /// | |
| /// Always succeeds, even if the input flags are invalid. Expired and | |
| /// unrecognized flags are ignored, conflicting flags are resolved in favor of | |
| /// the flag appearing last. | |
| factory ExperimentStatus.fromStrings2({ | |
| required Version sdkLanguageVersion, | |
| required List<String> flags, | |
| }) { | |
| var explicitFlags = decodeExplicitFlags(flags); | |
| var decodedFlags = restrictEnableFlagsToVersion( | |
| sdkLanguageVersion: sdkLanguageVersion, | |
| explicitEnabledFlags: explicitFlags.enabled, | |
| explicitDisabledFlags: explicitFlags.disabled, | |
| version: sdkLanguageVersion, | |
| ); | |
| return ExperimentStatus._( | |
| sdkLanguageVersion, | |
| explicitFlags.enabled, | |
| explicitFlags.disabled, | |
| decodedFlags, | |
| ); | |
| } | |
| factory ExperimentStatus.latestLanguageVersion({ | |
| List<String> flags = const [], | |
| }) { | |
| return ExperimentStatus.fromStrings2( | |
| sdkLanguageVersion: currentVersion, | |
| flags: flags, | |
| ); | |
| } | |
| ExperimentStatus._( | |
| this._sdkLanguageVersion, | |
| this._explicitEnabledFlags, | |
| this._explicitDisabledFlags, | |
| this._flags, | |
| ); | |
| @override | |
| int get hashCode => Object.hashAll(_flags); | |
| @override | |
| bool operator ==(Object other) { | |
| if (other is ExperimentStatus) { | |
| if (_sdkLanguageVersion != other._sdkLanguageVersion) { | |
| return false; | |
| } | |
| if (!_equalListOfBool( | |
| _explicitEnabledFlags, | |
| other._explicitEnabledFlags, | |
| )) { | |
| return false; | |
| } | |
| if (!_equalListOfBool( | |
| _explicitDisabledFlags, | |
| other._explicitDisabledFlags, | |
| )) { | |
| return false; | |
| } | |
| if (!_equalListOfBool(_flags, other._flags)) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| /// Queries whether the given [feature] is enabled or disabled. | |
| @override | |
| bool isEnabled(covariant ExperimentalFeature feature) => | |
| _flags[feature.index]; | |
| @override | |
| FeatureSet restrictToVersion(Version version) { | |
| return ExperimentStatus._( | |
| _sdkLanguageVersion, | |
| _explicitEnabledFlags, | |
| _explicitDisabledFlags, | |
| restrictEnableFlagsToVersion( | |
| sdkLanguageVersion: _sdkLanguageVersion, | |
| explicitEnabledFlags: _explicitEnabledFlags, | |
| explicitDisabledFlags: _explicitDisabledFlags, | |
| version: version, | |
| ), | |
| ); | |
| } | |
| /// Encode into the format suitable for [ExperimentStatus.fromStorage]. | |
| Uint8List toStorage() { | |
| var flagsByteCount = (_flags.length + 7) ~/ 8; | |
| var result = Uint8List(2 + 1 + 3 * flagsByteCount); | |
| var resultIndex = 0; | |
| void addByte(int value) { | |
| assert(value >= 0 && value < 256); | |
| result[resultIndex++] = value; | |
| } | |
| void addBoolList(List<bool> values) { | |
| var byteValue = 0; | |
| var bitIndex = 0; | |
| for (var value in values) { | |
| if (value) { | |
| byteValue |= 1 << bitIndex; | |
| } | |
| bitIndex++; | |
| if (bitIndex == 8) { | |
| addByte(byteValue); | |
| byteValue = 0; | |
| bitIndex = 0; | |
| } | |
| } | |
| if (bitIndex != 0) { | |
| addByte(byteValue); | |
| } | |
| } | |
| addByte(_sdkLanguageVersion.major); | |
| addByte(_sdkLanguageVersion.minor); | |
| addByte(_flags.length); | |
| addBoolList(_explicitEnabledFlags); | |
| addBoolList(_explicitDisabledFlags); | |
| addBoolList(_flags); | |
| return result.sublist(0, resultIndex); | |
| } | |
| @override | |
| String toString() => experimentStatusToString(_flags); | |
| static bool _equalListOfBool(List<bool> first, List<bool> second) { | |
| if (first.length != second.length) return false; | |
| for (var i = 0; i < first.length; i++) { | |
| if (first[i] != second[i]) return false; | |
| } | |
| return true; | |
| } | |
| } |