7a5c71ba05
(Part of https://github.com/dart-lang/sdk/issues/63288) This change migrates the frontend_server package to use the new constructor declaration syntax, described in https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations. This change was performed in an automated fashion, by (a) bumping the package's SDK constraint to `3.13.0-0`, (b) enabling the lints `unnecessary_type_name_in_constructor` and `unnecessary_const_in_enum_constructor`, (c) fixing the resulting lint failures using `dart fix`, and then (d) reformatting the affected files. To ease code review, I've reverted unrelated formatting changes. Change-Id: Icbc924a1faae2f1c8fe0d5fa07bb9bd66a6a6964 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/508369 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
544 lines
16 KiB
Dart
544 lines
16 KiB
Dart
// Copyright (c) 2023, 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:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:collection/collection.dart';
|
|
import 'package:front_end/src/api_unstable/vm.dart';
|
|
import 'package:kernel/const_finder.dart';
|
|
import 'package:path/path.dart' as path;
|
|
|
|
void expect<T>(T value, T expected) {
|
|
if (value != expected) {
|
|
stderr.writeln('------------------------');
|
|
stderr.writeln('Expected: $expected');
|
|
stderr.writeln('Actual: $value');
|
|
stderr.writeln('------------------------');
|
|
exitCode = -1;
|
|
}
|
|
}
|
|
|
|
void expectInstances(dynamic value, dynamic expected, Compiler compiler) {
|
|
// To ensure we ignore insertion order into maps as well as lists we use
|
|
// DeepCollectionEquality as well as sort the lists.
|
|
|
|
int compareByStringValue(dynamic a, dynamic b) {
|
|
return a['stringValue'].compareTo(b['stringValue']) as int;
|
|
}
|
|
|
|
value['constantInstances'].sort(compareByStringValue);
|
|
expected['constantInstances'].sort(compareByStringValue);
|
|
|
|
final Equality<Object?> equality;
|
|
if (compiler == Compiler.dart2js) {
|
|
equality = const Dart2JSDeepCollectionEquality();
|
|
} else {
|
|
equality = const DeepCollectionEquality();
|
|
}
|
|
if (!equality.equals(value, expected)) {
|
|
JsonEncoder encoder = new JsonEncoder.withIndent(" ");
|
|
stderr.writeln('------------------------');
|
|
stderr.writeln('Expected:');
|
|
stderr.writeln(encoder.convert(expected));
|
|
stderr.writeln('');
|
|
stderr.writeln('Actual:');
|
|
stderr.writeln(encoder.convert(value));
|
|
stderr.writeln('');
|
|
stderr.writeln(StackTrace.current);
|
|
stderr.writeln('------------------------');
|
|
exitCode = -1;
|
|
}
|
|
}
|
|
|
|
// This test is assuming the `dart` used to invoke the tests is compatible
|
|
// with the version of package:kernel in //third-party/dart/pkg/kernel
|
|
final String dart = Platform.resolvedExecutable;
|
|
final String bat = Platform.isWindows ? '.bat' : '';
|
|
|
|
void _checkRecursion(String dillPath, Compiler compiler) {
|
|
stdout.writeln('Checking recursive calls.');
|
|
final ConstFinder finder = new ConstFinder(
|
|
kernelFilePath: dillPath,
|
|
classLibraryUri: 'package:const_finder_fixtures/box.dart',
|
|
className: 'Box',
|
|
);
|
|
// Will timeout if we did things wrong.
|
|
jsonEncode(finder.findInstances());
|
|
}
|
|
|
|
void _checkConsts(String dillPath, Compiler compiler) {
|
|
stdout.writeln('Checking for expected constants.');
|
|
final ConstFinder finder = new ConstFinder(
|
|
kernelFilePath: dillPath,
|
|
classLibraryUri: 'package:const_finder_fixtures/target.dart',
|
|
className: 'Target',
|
|
);
|
|
final Map<String, Object?> expectation = <String, dynamic>{
|
|
'constantInstances': <Map<String, dynamic>>[
|
|
<String, dynamic>{
|
|
'stringValue': '100',
|
|
'intValue': 100,
|
|
'targetValue': null,
|
|
},
|
|
<String, dynamic>{
|
|
'stringValue': '102',
|
|
'intValue': 102,
|
|
'targetValue': null,
|
|
},
|
|
<String, dynamic>{'stringValue': '101', 'intValue': 101},
|
|
<String, dynamic>{
|
|
'stringValue': '103',
|
|
'intValue': 103,
|
|
'targetValue': null,
|
|
},
|
|
<String, dynamic>{
|
|
'stringValue': '105',
|
|
'intValue': 105,
|
|
'targetValue': null,
|
|
},
|
|
<String, dynamic>{'stringValue': '104', 'intValue': 104},
|
|
<String, dynamic>{
|
|
'stringValue': '106',
|
|
'intValue': 106,
|
|
'targetValue': null,
|
|
},
|
|
<String, dynamic>{
|
|
'stringValue': '108',
|
|
'intValue': 108,
|
|
'targetValue': null,
|
|
},
|
|
<String, dynamic>{'stringValue': '107', 'intValue': 107},
|
|
<String, dynamic>{'stringValue': '1', 'intValue': 1, 'targetValue': null},
|
|
<String, dynamic>{'stringValue': '4', 'intValue': 4, 'targetValue': null},
|
|
<String, dynamic>{'stringValue': '2', 'intValue': 2},
|
|
<String, dynamic>{'stringValue': '6', 'intValue': 6, 'targetValue': null},
|
|
<String, dynamic>{'stringValue': '8', 'intValue': 8, 'targetValue': null},
|
|
<String, dynamic>{
|
|
'stringValue': '10',
|
|
'intValue': 10,
|
|
'targetValue': null,
|
|
},
|
|
<String, dynamic>{'stringValue': '9', 'intValue': 9},
|
|
<String, dynamic>{'stringValue': '7', 'intValue': 7, 'targetValue': null},
|
|
<String, dynamic>{
|
|
'stringValue': '11',
|
|
'intValue': 11,
|
|
'targetValue': null,
|
|
},
|
|
<String, dynamic>{
|
|
'stringValue': '12',
|
|
'intValue': 12,
|
|
'targetValue': null,
|
|
},
|
|
<String, dynamic>{
|
|
'stringValue': 'package',
|
|
'intValue': -1,
|
|
'targetValue': null,
|
|
},
|
|
],
|
|
'nonConstantLocations': <dynamic>[],
|
|
};
|
|
if (compiler == Compiler.aot) {
|
|
expectation['nonConstantLocations'] = <Object?>[];
|
|
} else {
|
|
final String fixturesUrl = Platform.isWindows
|
|
? '/$fixtures'.replaceAll(Platform.pathSeparator, '/')
|
|
: fixtures;
|
|
|
|
// Without true tree-shaking, there is a non-const reference in a
|
|
// never-invoked function that will be present in the dill.
|
|
expectation['nonConstantLocations'] = <Object?>[
|
|
<String, dynamic>{
|
|
'file': 'file://$fixturesUrl/pkg/package.dart',
|
|
'line': 17,
|
|
'column': 25,
|
|
},
|
|
];
|
|
}
|
|
expectInstances(finder.findInstances(), expectation, compiler);
|
|
|
|
final ConstFinder finder2 = new ConstFinder(
|
|
kernelFilePath: dillPath,
|
|
classLibraryUri: 'package:const_finder_fixtures/target.dart',
|
|
className: 'MixedInTarget',
|
|
);
|
|
expectInstances(finder2.findInstances(), <String, dynamic>{
|
|
'constantInstances': <Map<String, dynamic>>[
|
|
<String, dynamic>{'val': '13'},
|
|
],
|
|
'nonConstantLocations': <dynamic>[],
|
|
}, compiler);
|
|
}
|
|
|
|
void _checkAnnotation(String dillPath, Compiler compiler) {
|
|
stdout.writeln(
|
|
'Checking constant instances in a class annotated with instance of '
|
|
'StaticIconProvider are ignored with $compiler',
|
|
);
|
|
final ConstFinder finder = new ConstFinder(
|
|
kernelFilePath: dillPath,
|
|
classLibraryUri: 'package:const_finder_fixtures/target.dart',
|
|
className: 'Target',
|
|
annotationClassName: 'StaticIconProvider',
|
|
annotationClassLibraryUri:
|
|
'package:const_finder_fixtures/static_icon_provider.dart',
|
|
);
|
|
final Map<String, dynamic> instances = finder.findInstances();
|
|
expectInstances(instances, <String, dynamic>{
|
|
'constantInstances': <Map<String, Object?>>[
|
|
<String, Object?>{
|
|
'stringValue': 'used1',
|
|
'intValue': 1,
|
|
'targetValue': null,
|
|
},
|
|
<String, Object?>{
|
|
'stringValue': 'used2',
|
|
'intValue': 2,
|
|
'targetValue': null,
|
|
},
|
|
],
|
|
// TODO(fujino): This should have non-constant locations from the use of
|
|
// a tear-off, see https://github.com/flutter/flutter/issues/116797
|
|
'nonConstantLocations': <Object?>[],
|
|
}, compiler);
|
|
}
|
|
|
|
void _checkNonConstsFrontend(String dillPath, Compiler compiler) {
|
|
stdout.writeln('Checking for non-constant instances with $compiler');
|
|
final ConstFinder finder = new ConstFinder(
|
|
kernelFilePath: dillPath,
|
|
classLibraryUri: 'package:const_finder_fixtures/target.dart',
|
|
className: 'Target',
|
|
);
|
|
final String fixturesUrl = Platform.isWindows
|
|
? '/$fixtures'.replaceAll(Platform.pathSeparator, '/')
|
|
: fixtures;
|
|
|
|
expectInstances(finder.findInstances(), <String, dynamic>{
|
|
'constantInstances': <dynamic>[
|
|
<String, dynamic>{'stringValue': '1', 'intValue': 1, 'targetValue': null},
|
|
<String, dynamic>{'stringValue': '4', 'intValue': 4, 'targetValue': null},
|
|
<String, dynamic>{'stringValue': '6', 'intValue': 6, 'targetValue': null},
|
|
<String, dynamic>{'stringValue': '8', 'intValue': 8, 'targetValue': null},
|
|
<String, dynamic>{
|
|
'stringValue': '10',
|
|
'intValue': 10,
|
|
'targetValue': null,
|
|
},
|
|
<String, dynamic>{'stringValue': '9', 'intValue': 9},
|
|
<String, dynamic>{'stringValue': '7', 'intValue': 7, 'targetValue': null},
|
|
],
|
|
'nonConstantLocations': <dynamic>[
|
|
<String, dynamic>{
|
|
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
|
'line': 17,
|
|
'column': 26,
|
|
},
|
|
<String, dynamic>{
|
|
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
|
'line': 20,
|
|
'column': 7,
|
|
},
|
|
<String, dynamic>{
|
|
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
|
'line': 20,
|
|
'column': 22,
|
|
},
|
|
<String, dynamic>{
|
|
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
|
'line': 21,
|
|
'column': 26,
|
|
},
|
|
<String, dynamic>{
|
|
'file': 'file://$fixturesUrl/pkg/package.dart',
|
|
'line': 17,
|
|
'column': 25,
|
|
},
|
|
],
|
|
}, compiler);
|
|
}
|
|
|
|
// Note, since web dills don't have tree shaking, we aren't able to eliminate
|
|
// an additional const versus _checkNonConstFrontend.
|
|
void _checkNonConstsWeb(String dillPath, Compiler compiler) {
|
|
assert(compiler == Compiler.dart2js);
|
|
stdout.writeln('Checking for non-constant instances with $compiler');
|
|
final ConstFinder finder = new ConstFinder(
|
|
kernelFilePath: dillPath,
|
|
classLibraryUri: 'package:const_finder_fixtures/target.dart',
|
|
className: 'Target',
|
|
);
|
|
|
|
final String fixturesUrl = Platform.isWindows
|
|
? '/$fixtures'.replaceAll(Platform.pathSeparator, '/')
|
|
: fixtures;
|
|
expectInstances(finder.findInstances(), <String, dynamic>{
|
|
'constantInstances': <dynamic>[
|
|
<String, dynamic>{'stringValue': '1', 'intValue': 1, 'targetValue': null},
|
|
<String, dynamic>{'stringValue': '4', 'intValue': 4, 'targetValue': null},
|
|
<String, dynamic>{'stringValue': '6', 'intValue': 6, 'targetValue': null},
|
|
<String, dynamic>{'stringValue': '8', 'intValue': 8, 'targetValue': null},
|
|
<String, dynamic>{
|
|
'stringValue': '10',
|
|
'intValue': 10,
|
|
'targetValue': null,
|
|
},
|
|
<String, dynamic>{'stringValue': '9', 'intValue': 9},
|
|
<String, dynamic>{'stringValue': '7', 'intValue': 7, 'targetValue': null},
|
|
<String, dynamic>{
|
|
'stringValue': 'package',
|
|
'intValue': -1,
|
|
'targetValue': null,
|
|
},
|
|
],
|
|
'nonConstantLocations': <dynamic>[
|
|
<String, dynamic>{
|
|
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
|
'line': 17,
|
|
'column': 26,
|
|
},
|
|
<String, dynamic>{
|
|
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
|
'line': 20,
|
|
'column': 7,
|
|
},
|
|
<String, dynamic>{
|
|
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
|
'line': 20,
|
|
'column': 22,
|
|
},
|
|
<String, dynamic>{
|
|
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
|
'line': 21,
|
|
'column': 26,
|
|
},
|
|
<String, dynamic>{
|
|
'file': 'file://$fixturesUrl/pkg/package.dart',
|
|
'line': 17,
|
|
'column': 25,
|
|
},
|
|
],
|
|
}, compiler);
|
|
}
|
|
|
|
void checkProcessResult(ProcessResult result) {
|
|
if (result.exitCode != 0) {
|
|
stdout.writeln(result.stdout);
|
|
stderr.writeln(result.stderr);
|
|
}
|
|
expect(result.exitCode, 0);
|
|
}
|
|
|
|
final String basePath = path.canonicalize(
|
|
path.join(path.dirname(Platform.script.toFilePath()), '..'),
|
|
);
|
|
final String fixtures = path.join(basePath, 'test', 'fixtures');
|
|
final String packageConfig = path.join(
|
|
fixtures,
|
|
'.dart_tool',
|
|
'package_config.json',
|
|
);
|
|
|
|
// TODO(johnniwinther): Add unittests to package:kernel.
|
|
// TODO(johnniwinther): Avoid emitting .deps files during testing.
|
|
Future<void> main() async {
|
|
final String frontendServer =
|
|
'pkg/frontend_server/bin/frontend_server_starter.dart';
|
|
final String sdkRoot = computePlatformBinariesLocation().toFilePath();
|
|
final String librariesSpec = 'sdk/lib/libraries.json';
|
|
|
|
final List<_Test> tests = <_Test>[
|
|
new _Test(
|
|
name: 'box_frontend',
|
|
dartSource: path.join(fixtures, 'lib', 'box.dart'),
|
|
frontendServer: frontendServer,
|
|
sdkRoot: sdkRoot,
|
|
librariesSpec: librariesSpec,
|
|
verify: _checkRecursion,
|
|
compiler: Compiler.aot,
|
|
),
|
|
new _Test(
|
|
name: 'box_web',
|
|
dartSource: path.join(fixtures, 'lib', 'box.dart'),
|
|
frontendServer: frontendServer,
|
|
sdkRoot: sdkRoot,
|
|
librariesSpec: librariesSpec,
|
|
verify: _checkRecursion,
|
|
compiler: Compiler.dart2js,
|
|
),
|
|
new _Test(
|
|
name: 'consts_frontend',
|
|
dartSource: path.join(fixtures, 'lib', 'consts.dart'),
|
|
frontendServer: frontendServer,
|
|
sdkRoot: sdkRoot,
|
|
librariesSpec: librariesSpec,
|
|
verify: _checkConsts,
|
|
compiler: Compiler.aot,
|
|
),
|
|
new _Test(
|
|
name: 'consts_web',
|
|
dartSource: path.join(fixtures, 'lib', 'consts.dart'),
|
|
frontendServer: frontendServer,
|
|
sdkRoot: sdkRoot,
|
|
librariesSpec: librariesSpec,
|
|
verify: _checkConsts,
|
|
compiler: Compiler.dart2js,
|
|
),
|
|
new _Test(
|
|
name: 'consts_and_non_frontend',
|
|
dartSource: path.join(fixtures, 'lib', 'consts_and_non.dart'),
|
|
frontendServer: frontendServer,
|
|
sdkRoot: sdkRoot,
|
|
librariesSpec: librariesSpec,
|
|
verify: _checkNonConstsFrontend,
|
|
compiler: Compiler.aot,
|
|
),
|
|
new _Test(
|
|
name: 'consts_and_non_web',
|
|
dartSource: path.join(fixtures, 'lib', 'consts_and_non.dart'),
|
|
frontendServer: frontendServer,
|
|
sdkRoot: sdkRoot,
|
|
librariesSpec: librariesSpec,
|
|
verify: _checkNonConstsWeb,
|
|
compiler: Compiler.dart2js,
|
|
),
|
|
new _Test(
|
|
name: 'static_icon_provider_frontend',
|
|
dartSource: path.join(fixtures, 'lib', 'static_icon_provider.dart'),
|
|
frontendServer: frontendServer,
|
|
sdkRoot: sdkRoot,
|
|
librariesSpec: librariesSpec,
|
|
verify: _checkAnnotation,
|
|
compiler: Compiler.aot,
|
|
),
|
|
new _Test(
|
|
name: 'static_icon_provider_web',
|
|
dartSource: path.join(fixtures, 'lib', 'static_icon_provider.dart'),
|
|
frontendServer: frontendServer,
|
|
sdkRoot: sdkRoot,
|
|
librariesSpec: librariesSpec,
|
|
verify: _checkAnnotation,
|
|
compiler: Compiler.dart2js,
|
|
),
|
|
];
|
|
try {
|
|
stdout.writeln('Generating kernel fixtures...');
|
|
|
|
for (final _Test test in tests) {
|
|
test.run();
|
|
}
|
|
} finally {
|
|
try {
|
|
for (final _Test test in tests) {
|
|
test.dispose();
|
|
}
|
|
} finally {
|
|
stdout.writeln(
|
|
'Tests ${exitCode == 0 ? 'succeeded' : 'failed'} '
|
|
'- exit code: $exitCode',
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
enum Compiler {
|
|
// Uses TFA tree-shaking.
|
|
aot,
|
|
// Does not have TFA tree-shaking.
|
|
dart2js,
|
|
}
|
|
|
|
class _Test {
|
|
new({
|
|
required this.name,
|
|
required this.dartSource,
|
|
required this.sdkRoot,
|
|
required this.verify,
|
|
required this.frontendServer,
|
|
required this.librariesSpec,
|
|
required this.compiler,
|
|
}) : dillPath = path.join(fixtures, '$name.dill');
|
|
|
|
final String name;
|
|
final String dartSource;
|
|
final String sdkRoot;
|
|
final String frontendServer;
|
|
final String librariesSpec;
|
|
final String dillPath;
|
|
void Function(String, Compiler) verify;
|
|
final Compiler compiler;
|
|
|
|
final List<String> resourcesToDispose = <String>[];
|
|
|
|
void run() {
|
|
stdout.writeln('Compiling $dartSource to $dillPath with $compiler');
|
|
|
|
if (compiler == Compiler.aot) {
|
|
_compileAOTDill();
|
|
} else {
|
|
_compileWebDill();
|
|
}
|
|
|
|
stdout.writeln('Testing $dillPath');
|
|
|
|
verify(dillPath, compiler);
|
|
}
|
|
|
|
void dispose() {
|
|
for (final String resource in resourcesToDispose) {
|
|
stdout.writeln('Deleting $resource');
|
|
new File(resource).deleteSync();
|
|
}
|
|
}
|
|
|
|
void _compileAOTDill() {
|
|
checkProcessResult(
|
|
Process.runSync(dart, <String>[
|
|
frontendServer,
|
|
'--sdk-root=$sdkRoot',
|
|
'--platform=vm_platform.dill',
|
|
'--target=vm',
|
|
'--aot',
|
|
'--tfa',
|
|
'--packages=$packageConfig',
|
|
'--output-dill=$dillPath',
|
|
dartSource,
|
|
]),
|
|
);
|
|
|
|
resourcesToDispose.add(dillPath);
|
|
}
|
|
|
|
void _compileWebDill() {
|
|
final ProcessResult result = Process.runSync(dart, <String>[
|
|
'compile',
|
|
'js',
|
|
'--libraries-spec=$librariesSpec',
|
|
'-Ddart.vm.product=true',
|
|
'-o',
|
|
dillPath,
|
|
'--packages=$packageConfig',
|
|
'--cfe-only',
|
|
dartSource,
|
|
]);
|
|
checkProcessResult(result);
|
|
|
|
resourcesToDispose.add(dillPath);
|
|
}
|
|
}
|
|
|
|
/// Equality that casts all [num]'s to [double] before comparing.
|
|
class Dart2JSDeepCollectionEquality extends DeepCollectionEquality {
|
|
const new();
|
|
|
|
@override
|
|
bool equals(Object? e1, Object? e2) {
|
|
if (e1 is num && e2 is num) {
|
|
return e1.toDouble() == e2.toDouble();
|
|
}
|
|
return super.equals(e1, e2);
|
|
}
|
|
}
|