a0f467f0e9
The analyzer already has an enum with the same name, but a slightly different declaration. (The analyzer's enum declares only severities of `error`, `warning`, and `info`, whereas the CFE's enum also declares severities of `context`, `ignored`, and `internalProblem`). I'm currently embarking on an arc of work that I hope will eventually culminate in unifying the analyzer and CFE diagnostic message representations (and their severities) into a single set of classes. Until that unification is complete, both representations will have to co-exist in the `_fe_analyzer_shared` package. To reduce confusion during that time period, I would like the classes to have distinct names. Since the analyzer's `Severity` enum is exposed through the analyzer public API, analyzer clients may depend on the name. So it makes sense to rename the CFE's `Severity` enum. Tested: standard trybots Change-Id: I95622950f49b1754267e441e4636e046045629bb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/442102 Reviewed-by: Liam Appelbe <liama@google.com> Reviewed-by: Mayank Patke <fishythefish@google.com> Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Ömer Ağacan <omersa@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com>
262 lines
7.4 KiB
Dart
262 lines
7.4 KiB
Dart
// 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.
|
|
|
|
import 'package:front_end/src/api_unstable/vm.dart';
|
|
import 'package:vm/kernel_front_end.dart';
|
|
import 'package:yaml/yaml.dart';
|
|
|
|
import 'diagnostic_message.dart';
|
|
|
|
class NativeAssetsValidator {
|
|
final ErrorDetector errorDetector;
|
|
List<Uri> involvedFiles = [];
|
|
|
|
NativeAssetsValidator(this.errorDetector);
|
|
|
|
void _reportError(
|
|
String message, {
|
|
CfeSeverity severity = CfeSeverity.error,
|
|
}) {
|
|
errorDetector(
|
|
NativeAssetsDiagnosticMessage(
|
|
message: message,
|
|
involvedFiles: involvedFiles,
|
|
severity: severity,
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Parses and validates [nativeAssetsYamlString].
|
|
///
|
|
/// Returns the parsed result if valid, otherwise `null`.
|
|
///
|
|
/// Reports errors to [errorDetector].
|
|
Map? parseAndValidate(String nativeAssetsYamlString) {
|
|
late final Object? nativeAssetsYaml;
|
|
try {
|
|
nativeAssetsYaml = loadYaml(nativeAssetsYamlString) as Object?;
|
|
} on YamlException catch (e) {
|
|
_reportError("File not formatted as yaml: $e $nativeAssetsYamlString.");
|
|
return null;
|
|
}
|
|
if (nativeAssetsYaml == null) {
|
|
_reportError("File not formatted as yaml: $nativeAssetsYamlString.");
|
|
return null;
|
|
}
|
|
|
|
final nativeAssetsYamlPruned = _validateAndPrune(nativeAssetsYaml);
|
|
|
|
return nativeAssetsYamlPruned;
|
|
}
|
|
|
|
static const _formatVersionKey = 'format-version';
|
|
static const _nativeAssetsKey = 'native-assets';
|
|
|
|
/// Modifies [object] to prune invalid data that only causes warnings.
|
|
///
|
|
/// Returns `null` if invalid.
|
|
Map? _validateAndPrune(Object object) {
|
|
if (object is! Map) {
|
|
_reportError('Unexpected root object: $object. Expected a Map.');
|
|
return null;
|
|
}
|
|
var isValid = true;
|
|
if (!object.containsKey(_formatVersionKey)) {
|
|
_reportError('Expected $_formatVersionKey in $object.');
|
|
isValid = false;
|
|
} else {
|
|
isValid &= _validateFormatVersion(object[_formatVersionKey]);
|
|
}
|
|
if (!object.containsKey(_nativeAssetsKey)) {
|
|
_reportError('Expected $_nativeAssetsKey in $object.');
|
|
isValid = false;
|
|
} else {
|
|
var nativeAssets = object[_nativeAssetsKey];
|
|
if (nativeAssets is Map) {
|
|
// Make native-assets modifiable.
|
|
nativeAssets = Map.from(nativeAssets);
|
|
object = Map.from(object);
|
|
object[_nativeAssetsKey] = nativeAssets;
|
|
}
|
|
isValid &= _validateNativeAssets(nativeAssets);
|
|
}
|
|
if (!isValid) return null;
|
|
return object;
|
|
}
|
|
|
|
/// The VM cannot consume newer formats with breaking changes.
|
|
static const _maximumMajor = 1;
|
|
|
|
/// This VM cannot consume too old versions.
|
|
static const _minimumMajor = 1;
|
|
static const _minimumMinor = 0;
|
|
static const _minimumPatch = 0;
|
|
static const _minimumVersion = [_minimumMajor, _minimumMinor, _minimumPatch];
|
|
|
|
/// Validate [_formatVersionKey] contents.
|
|
///
|
|
/// Checks the major version upper bound and all version lower bounds.
|
|
bool _validateFormatVersion(Object object) {
|
|
if (object is! List || object.length != 3) {
|
|
_reportError(
|
|
'Unexpected format version: $object. Expected a List with length 3.',
|
|
);
|
|
return false;
|
|
}
|
|
final major = object[0];
|
|
final minor = object[1];
|
|
final patch = object[2];
|
|
if (major > _maximumMajor) {
|
|
_reportError(
|
|
'Unexpected format version: $object. Major version above $_maximumMajor not supported.',
|
|
);
|
|
return false;
|
|
}
|
|
if (major > _minimumMajor) return true;
|
|
if (major < _minimumMajor) {
|
|
_reportError(
|
|
'Unexpected format version: $object. Versions below $_minimumVersion not supported.',
|
|
);
|
|
return false;
|
|
}
|
|
if (minor > _minimumMinor) return true;
|
|
if (minor < _minimumMinor) {
|
|
_reportError(
|
|
'Unexpected format version: $object. Versions below $_minimumVersion not supported.',
|
|
);
|
|
return false;
|
|
}
|
|
if (patch < _minimumPatch) {
|
|
_reportError(
|
|
'Unexpected format version: $object. Versions below $_minimumVersion not supported.',
|
|
);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Validate [_nativeAssetsKey] contents.
|
|
bool _validateNativeAssets(Object object) {
|
|
if (object is! Map) {
|
|
_reportError('Unexpected native-assets: $object. Expected a Map.');
|
|
return false;
|
|
}
|
|
var isValid = true;
|
|
final invalidTargets = [];
|
|
for (final entry in object.entries) {
|
|
final target = entry.key;
|
|
final validTarget = _validateTarget(target);
|
|
if (!validTarget) {
|
|
invalidTargets.add(target);
|
|
} else {
|
|
isValid &= _validateAssets(entry.value);
|
|
}
|
|
}
|
|
for (final target in invalidTargets) {
|
|
object.remove(target);
|
|
}
|
|
return isValid;
|
|
}
|
|
|
|
// TODO(http://dartbug.com/49803): Get this from `package:native` when that
|
|
// is merged.
|
|
static const _validTargets = [
|
|
'android_arm',
|
|
'android_arm64',
|
|
'android_ia32',
|
|
'android_x64',
|
|
'fuchsia_arm64',
|
|
'fuchsia_x64',
|
|
'ios_arm',
|
|
'ios_arm64',
|
|
'ios_x64',
|
|
'iossimulator_arm64',
|
|
'iossimulator_x64',
|
|
'linux_arm',
|
|
'linux_arm64',
|
|
'linux_ia32',
|
|
'linux_riscv32',
|
|
'linux_riscv64',
|
|
'linux_x64',
|
|
'macos_arm64',
|
|
'macos_x64',
|
|
'windows_arm64',
|
|
'windows_ia32',
|
|
'windows_x64',
|
|
];
|
|
|
|
/// An invalid target is considered only a warning.
|
|
///
|
|
/// For example, we may remove ia32 at some point, but we might have existing
|
|
/// yamls lingering around.
|
|
bool _validateTarget(Object object) {
|
|
if (!_validTargets.contains(object)) {
|
|
_reportError(
|
|
'Unexpected target: $object. Valid targets: $_validTargets.',
|
|
severity: CfeSeverity.warning,
|
|
);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool _validateAssets(Object object) {
|
|
if (object is! Map) {
|
|
_reportError('Unexpected assets mapping: $object. Expected a Map.');
|
|
return false;
|
|
}
|
|
var isValid = true;
|
|
for (final entry in object.entries) {
|
|
isValid &= _validateAsset(entry.key);
|
|
isValid &= _validateAssetPath(entry.value);
|
|
}
|
|
return isValid;
|
|
}
|
|
|
|
bool _validateAsset(Object object) {
|
|
if (object is! String) {
|
|
_reportError('Unexpected assets name: $object. Expected a String.');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
final _pathTypesWithPath = ['absolute', 'relative', 'system'];
|
|
|
|
late final _validPathTypes = [..._pathTypesWithPath, 'executable', 'process']
|
|
..sort();
|
|
|
|
bool _validateAssetPath(Object object) {
|
|
if (object is! List || object.isEmpty) {
|
|
_reportError(
|
|
'Unexpected asset path: $object. Expected a non-empty List.',
|
|
);
|
|
return false;
|
|
}
|
|
final pathType = object[0];
|
|
if (pathType is! String || !_validPathTypes.contains(pathType)) {
|
|
_reportError(
|
|
'Unexpected path type: $pathType. Valid path types: $_validPathTypes.',
|
|
);
|
|
return false;
|
|
}
|
|
final needsPath = _pathTypesWithPath.contains(pathType);
|
|
final listLength = 1 + (needsPath ? 1 : 0);
|
|
if (object.length != listLength) {
|
|
_reportError(
|
|
'Unexpected asset path: $object. Expected list with $listLength elements.',
|
|
);
|
|
return false;
|
|
}
|
|
if (needsPath) {
|
|
final path = object[1];
|
|
if (path is! String) {
|
|
_reportError('Unexpected path: $path. Expected a String.');
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|