|  | // 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 'package:analyzer/dart/analysis/features.dart'; | 
|  | import 'package:analyzer/src/dart/analysis/experiments_impl.dart'; | 
|  | import 'package:analyzer/src/generated/utilities_general.dart'; | 
|  | import 'package:meta/meta.dart'; | 
|  | import 'package:pub_semver/src/version.dart'; | 
|  |  | 
|  | export 'package:analyzer/src/dart/analysis/experiments_impl.dart' | 
|  | show | 
|  | ConflictingFlags, | 
|  | ExperimentalFeature, | 
|  | IllegalUseOfExpiredFlag, | 
|  | UnnecessaryUseOfExpiredFlag, | 
|  | UnrecognizedFlag, | 
|  | validateFlags, | 
|  | ValidationResult; | 
|  |  | 
|  | 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; | 
|  |  | 
|  | /// 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); | 
|  |  | 
|  | /// The language version to use in tests. | 
|  | static final Version testingSdkLanguageVersion = Version.parse('2.10.0'); | 
|  |  | 
|  | /// The latest known language version. | 
|  | static final Version latestSdkLanguageVersion = Version.parse('2.10.0'); | 
|  |  | 
|  | static final FeatureSet latestWithNullSafety = ExperimentStatus.fromStrings2( | 
|  | sdkLanguageVersion: latestSdkLanguageVersion, | 
|  | flags: [EnableString.non_nullable], | 
|  | ); | 
|  |  | 
|  | /// 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(); | 
|  | } | 
|  |  | 
|  | /// Computes a set of features for use in a unit test.  Computes the set of | 
|  | /// features enabled in [sdkVersion], plus any specified [additionalFeatures]. | 
|  | /// | 
|  | /// If [sdkVersion] is not supplied (or is `null`), then the current set of | 
|  | /// enabled features is used as the starting point. | 
|  | @visibleForTesting | 
|  | factory ExperimentStatus.forTesting( | 
|  | // ignore:avoid_unused_constructor_parameters | 
|  | {String sdkVersion, | 
|  | List<Feature> additionalFeatures = const []}) { | 
|  | var explicitFlags = decodeExplicitFlags([]); | 
|  | for (ExperimentalFeature feature in additionalFeatures) { | 
|  | explicitFlags.enabled[feature.index] = true; | 
|  | } | 
|  |  | 
|  | var sdkLanguageVersion = latestSdkLanguageVersion; | 
|  | var flags = restrictEnableFlagsToVersion( | 
|  | sdkLanguageVersion: sdkLanguageVersion, | 
|  | explicitEnabledFlags: explicitFlags.enabled, | 
|  | explicitDisabledFlags: explicitFlags.disabled, | 
|  | version: sdkLanguageVersion, | 
|  | ); | 
|  |  | 
|  | return ExperimentStatus._( | 
|  | sdkLanguageVersion, | 
|  | explicitFlags.enabled, | 
|  | explicitFlags.disabled, | 
|  | flags, | 
|  | ); | 
|  | } | 
|  |  | 
|  | factory ExperimentStatus.fromStorage(List<int> encoded) { | 
|  | var allFlags = encoded.skip(2).map((e) => e != 0).toList(); | 
|  | var featureCount = allFlags.length ~/ 3; | 
|  | return ExperimentStatus._( | 
|  | Version(encoded[0], encoded[1], 0), | 
|  | allFlags.sublist(0, featureCount), | 
|  | allFlags.sublist(featureCount, featureCount * 2), | 
|  | allFlags.sublist(featureCount * 2, featureCount * 3), | 
|  | ); | 
|  | } | 
|  |  | 
|  | /// 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.fromStrings(List<String> flags) { | 
|  | return ExperimentStatus.fromStrings2( | 
|  | sdkLanguageVersion: latestSdkLanguageVersion, | 
|  | flags: flags, | 
|  | ); | 
|  | } | 
|  |  | 
|  | /// 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, | 
|  | // TODO(scheglov) use restrictEnableFlagsToVersion | 
|  | }) { | 
|  | 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() { | 
|  | return ExperimentStatus.fromStrings2( | 
|  | sdkLanguageVersion: latestSdkLanguageVersion, | 
|  | flags: [], | 
|  | ); | 
|  | } | 
|  |  | 
|  | ExperimentStatus._( | 
|  | this._sdkLanguageVersion, | 
|  | this._explicitEnabledFlags, | 
|  | this._explicitDisabledFlags, | 
|  | this._flags, | 
|  | ); | 
|  |  | 
|  | @override | 
|  | int get hashCode { | 
|  | int hash = 0; | 
|  | for (var flag in _flags) { | 
|  | hash = JenkinsSmiHash.combine(hash, flag.hashCode); | 
|  | } | 
|  | return JenkinsSmiHash.finish(hash); | 
|  | } | 
|  |  | 
|  | @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]. | 
|  | List<int> toStorage() { | 
|  | return [ | 
|  | _sdkLanguageVersion.major, | 
|  | _sdkLanguageVersion.minor, | 
|  | ..._explicitEnabledFlags.map((e) => e ? 1 : 0), | 
|  | ..._explicitDisabledFlags.map((e) => e ? 1 : 0), | 
|  | ..._flags.map((e) => e ? 1 : 0), | 
|  | ]; | 
|  | } | 
|  |  | 
|  | @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; | 
|  | } | 
|  | } |