mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 16:31:07 +00:00
[cfe] Copy const_finder visitor from Flutter SDK
This copies the const_finder visitor from the Flutter SDK to the Dart SDK to avoid having source code dependency on package:kernel in the Flutter repository. Change-Id: I76f98453c1650e63623708a9f4860d80ab4b25aa Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/326645 Reviewed-by: Christopher Fujino <fujino@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: William Hesse <whesse@google.com>
This commit is contained in:
parent
3ee5bdfcf3
commit
f0933ab69e
|
@ -6,3 +6,7 @@ linter:
|
|||
- directives_ordering
|
||||
- sort_pub_dependencies
|
||||
- unawaited_futures
|
||||
|
||||
analyzer:
|
||||
exclude:
|
||||
- test/fixtures/**
|
||||
|
|
|
@ -23,5 +23,8 @@ dependencies:
|
|||
|
||||
# Use 'any' constraints here; we get our versions from the DEPS file.
|
||||
dev_dependencies:
|
||||
collection: any
|
||||
const_finder_fixtures: any
|
||||
const_finder_fixtures_package: any
|
||||
lints: any
|
||||
test: any
|
||||
|
|
583
pkg/frontend_server/test/const_finder_test.dart
Normal file
583
pkg/frontend_server/test/const_finder_test.dart
Normal file
|
@ -0,0 +1,583 @@
|
|||
// 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('Expected: $expected');
|
||||
stderr.writeln('Actual: $value');
|
||||
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)) {
|
||||
stderr.writeln('Expected: ${jsonEncode(expected)}');
|
||||
stderr.writeln('Actual: ${jsonEncode(value)}');
|
||||
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 = 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 = 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': 13,
|
||||
'column': 25,
|
||||
},
|
||||
];
|
||||
}
|
||||
expectInstances(
|
||||
finder.findInstances(),
|
||||
expectation,
|
||||
compiler,
|
||||
);
|
||||
|
||||
final ConstFinder finder2 = 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 = 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 = 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': 13,
|
||||
'column': 26,
|
||||
},
|
||||
<String, dynamic>{
|
||||
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
||||
'line': 16,
|
||||
'column': 7,
|
||||
},
|
||||
<String, dynamic>{
|
||||
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
||||
'line': 16,
|
||||
'column': 22,
|
||||
},
|
||||
<String, dynamic>{
|
||||
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
||||
'line': 17,
|
||||
'column': 26,
|
||||
},
|
||||
<String, dynamic>{
|
||||
'file': 'file://$fixturesUrl/pkg/package.dart',
|
||||
'line': 13,
|
||||
'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 = 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': 13,
|
||||
'column': 26,
|
||||
},
|
||||
<String, dynamic>{
|
||||
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
||||
'line': 16,
|
||||
'column': 7,
|
||||
},
|
||||
<String, dynamic>{
|
||||
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
||||
'line': 16,
|
||||
'column': 22,
|
||||
},
|
||||
<String, dynamic>{
|
||||
'file': 'file://$fixturesUrl/lib/consts_and_non.dart',
|
||||
'line': 17,
|
||||
'column': 26,
|
||||
},
|
||||
<String, dynamic>{
|
||||
'file': 'file://$fixturesUrl/pkg/package.dart',
|
||||
'line': 13,
|
||||
'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 frontendServer = 'pkg/frontend_server/bin/frontend_server_starter.dart';
|
||||
final sdkRoot = computePlatformBinariesLocation().toFilePath();
|
||||
final String librariesSpec = 'sdk/lib/libraries.json';
|
||||
|
||||
final List<_Test> tests = <_Test>[
|
||||
_Test(
|
||||
name: 'box_frontend',
|
||||
dartSource: path.join(fixtures, 'lib', 'box.dart'),
|
||||
frontendServer: frontendServer,
|
||||
sdkRoot: sdkRoot,
|
||||
librariesSpec: librariesSpec,
|
||||
verify: _checkRecursion,
|
||||
compiler: Compiler.aot,
|
||||
),
|
||||
_Test(
|
||||
name: 'box_web',
|
||||
dartSource: path.join(fixtures, 'lib', 'box.dart'),
|
||||
frontendServer: frontendServer,
|
||||
sdkRoot: sdkRoot,
|
||||
librariesSpec: librariesSpec,
|
||||
verify: _checkRecursion,
|
||||
compiler: Compiler.dart2js,
|
||||
),
|
||||
_Test(
|
||||
name: 'consts_frontend',
|
||||
dartSource: path.join(fixtures, 'lib', 'consts.dart'),
|
||||
frontendServer: frontendServer,
|
||||
sdkRoot: sdkRoot,
|
||||
librariesSpec: librariesSpec,
|
||||
verify: _checkConsts,
|
||||
compiler: Compiler.aot,
|
||||
),
|
||||
_Test(
|
||||
name: 'consts_web',
|
||||
dartSource: path.join(fixtures, 'lib', 'consts.dart'),
|
||||
frontendServer: frontendServer,
|
||||
sdkRoot: sdkRoot,
|
||||
librariesSpec: librariesSpec,
|
||||
verify: _checkConsts,
|
||||
compiler: Compiler.dart2js,
|
||||
),
|
||||
_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,
|
||||
),
|
||||
_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,
|
||||
),
|
||||
_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,
|
||||
),
|
||||
_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 {
|
||||
_Test({
|
||||
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');
|
||||
File(resource).deleteSync();
|
||||
}
|
||||
}
|
||||
|
||||
void _compileAOTDill() {
|
||||
checkProcessResult(Process.runSync(dart, <String>[
|
||||
frontendServer,
|
||||
'--sdk-root=$sdkRoot',
|
||||
'--platform=vm_platform_strong.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 Dart2JSDeepCollectionEquality();
|
||||
|
||||
@override
|
||||
bool equals(Object? e1, Object? e2) {
|
||||
if (e1 is num && e2 is num) {
|
||||
return e1.toDouble() == e2.toDouble();
|
||||
}
|
||||
return super.equals(e1, e2);
|
||||
}
|
||||
}
|
15
pkg/frontend_server/test/fixtures/.dart_tool/package_config.json
vendored
Normal file
15
pkg/frontend_server/test/fixtures/.dart_tool/package_config.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"configVersion": 2,
|
||||
"packages": [
|
||||
{
|
||||
"name": "const_finder_fixtures",
|
||||
"rootUri": "../lib/",
|
||||
"languageVersion": "2.17"
|
||||
},
|
||||
{
|
||||
"name": "const_finder_fixtures_package",
|
||||
"rootUri": "../pkg/",
|
||||
"languageVersion": "2.17"
|
||||
}
|
||||
]
|
||||
}
|
231
pkg/frontend_server/test/fixtures/lib/box.dart
vendored
Normal file
231
pkg/frontend_server/test/fixtures/lib/box.dart
vendored
Normal file
|
@ -0,0 +1,231 @@
|
|||
// 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.
|
||||
|
||||
// If canonicalization uses deep structural hashing without memoizing, this
|
||||
// will exhibit superlinear time.
|
||||
|
||||
// Compare with Dart version of this test at:
|
||||
// https://github.com/dart-lang/sdk/blob/ca3ad264a64937d5d336cd04dbf2746d1b7d8fc4/tests/language_2/canonicalize/hashing_memoize_instance_test.dart
|
||||
|
||||
class Box {
|
||||
const Box(this.content1, this.content2);
|
||||
final Object? content1;
|
||||
final Object? content2;
|
||||
}
|
||||
|
||||
const Box box1_0 = Box(null, null);
|
||||
const Box box1_1 = Box(box1_0, box1_0);
|
||||
const Box box1_2 = Box(box1_1, box1_1);
|
||||
const Box box1_3 = Box(box1_2, box1_2);
|
||||
const Box box1_4 = Box(box1_3, box1_3);
|
||||
const Box box1_5 = Box(box1_4, box1_4);
|
||||
const Box box1_6 = Box(box1_5, box1_5);
|
||||
const Box box1_7 = Box(box1_6, box1_6);
|
||||
const Box box1_8 = Box(box1_7, box1_7);
|
||||
const Box box1_9 = Box(box1_8, box1_8);
|
||||
const Box box1_10 = Box(box1_9, box1_9);
|
||||
const Box box1_11 = Box(box1_10, box1_10);
|
||||
const Box box1_12 = Box(box1_11, box1_11);
|
||||
const Box box1_13 = Box(box1_12, box1_12);
|
||||
const Box box1_14 = Box(box1_13, box1_13);
|
||||
const Box box1_15 = Box(box1_14, box1_14);
|
||||
const Box box1_16 = Box(box1_15, box1_15);
|
||||
const Box box1_17 = Box(box1_16, box1_16);
|
||||
const Box box1_18 = Box(box1_17, box1_17);
|
||||
const Box box1_19 = Box(box1_18, box1_18);
|
||||
const Box box1_20 = Box(box1_19, box1_19);
|
||||
const Box box1_21 = Box(box1_20, box1_20);
|
||||
const Box box1_22 = Box(box1_21, box1_21);
|
||||
const Box box1_23 = Box(box1_22, box1_22);
|
||||
const Box box1_24 = Box(box1_23, box1_23);
|
||||
const Box box1_25 = Box(box1_24, box1_24);
|
||||
const Box box1_26 = Box(box1_25, box1_25);
|
||||
const Box box1_27 = Box(box1_26, box1_26);
|
||||
const Box box1_28 = Box(box1_27, box1_27);
|
||||
const Box box1_29 = Box(box1_28, box1_28);
|
||||
const Box box1_30 = Box(box1_29, box1_29);
|
||||
const Box box1_31 = Box(box1_30, box1_30);
|
||||
const Box box1_32 = Box(box1_31, box1_31);
|
||||
const Box box1_33 = Box(box1_32, box1_32);
|
||||
const Box box1_34 = Box(box1_33, box1_33);
|
||||
const Box box1_35 = Box(box1_34, box1_34);
|
||||
const Box box1_36 = Box(box1_35, box1_35);
|
||||
const Box box1_37 = Box(box1_36, box1_36);
|
||||
const Box box1_38 = Box(box1_37, box1_37);
|
||||
const Box box1_39 = Box(box1_38, box1_38);
|
||||
const Box box1_40 = Box(box1_39, box1_39);
|
||||
const Box box1_41 = Box(box1_40, box1_40);
|
||||
const Box box1_42 = Box(box1_41, box1_41);
|
||||
const Box box1_43 = Box(box1_42, box1_42);
|
||||
const Box box1_44 = Box(box1_43, box1_43);
|
||||
const Box box1_45 = Box(box1_44, box1_44);
|
||||
const Box box1_46 = Box(box1_45, box1_45);
|
||||
const Box box1_47 = Box(box1_46, box1_46);
|
||||
const Box box1_48 = Box(box1_47, box1_47);
|
||||
const Box box1_49 = Box(box1_48, box1_48);
|
||||
const Box box1_50 = Box(box1_49, box1_49);
|
||||
const Box box1_51 = Box(box1_50, box1_50);
|
||||
const Box box1_52 = Box(box1_51, box1_51);
|
||||
const Box box1_53 = Box(box1_52, box1_52);
|
||||
const Box box1_54 = Box(box1_53, box1_53);
|
||||
const Box box1_55 = Box(box1_54, box1_54);
|
||||
const Box box1_56 = Box(box1_55, box1_55);
|
||||
const Box box1_57 = Box(box1_56, box1_56);
|
||||
const Box box1_58 = Box(box1_57, box1_57);
|
||||
const Box box1_59 = Box(box1_58, box1_58);
|
||||
const Box box1_60 = Box(box1_59, box1_59);
|
||||
const Box box1_61 = Box(box1_60, box1_60);
|
||||
const Box box1_62 = Box(box1_61, box1_61);
|
||||
const Box box1_63 = Box(box1_62, box1_62);
|
||||
const Box box1_64 = Box(box1_63, box1_63);
|
||||
const Box box1_65 = Box(box1_64, box1_64);
|
||||
const Box box1_66 = Box(box1_65, box1_65);
|
||||
const Box box1_67 = Box(box1_66, box1_66);
|
||||
const Box box1_68 = Box(box1_67, box1_67);
|
||||
const Box box1_69 = Box(box1_68, box1_68);
|
||||
const Box box1_70 = Box(box1_69, box1_69);
|
||||
const Box box1_71 = Box(box1_70, box1_70);
|
||||
const Box box1_72 = Box(box1_71, box1_71);
|
||||
const Box box1_73 = Box(box1_72, box1_72);
|
||||
const Box box1_74 = Box(box1_73, box1_73);
|
||||
const Box box1_75 = Box(box1_74, box1_74);
|
||||
const Box box1_76 = Box(box1_75, box1_75);
|
||||
const Box box1_77 = Box(box1_76, box1_76);
|
||||
const Box box1_78 = Box(box1_77, box1_77);
|
||||
const Box box1_79 = Box(box1_78, box1_78);
|
||||
const Box box1_80 = Box(box1_79, box1_79);
|
||||
const Box box1_81 = Box(box1_80, box1_80);
|
||||
const Box box1_82 = Box(box1_81, box1_81);
|
||||
const Box box1_83 = Box(box1_82, box1_82);
|
||||
const Box box1_84 = Box(box1_83, box1_83);
|
||||
const Box box1_85 = Box(box1_84, box1_84);
|
||||
const Box box1_86 = Box(box1_85, box1_85);
|
||||
const Box box1_87 = Box(box1_86, box1_86);
|
||||
const Box box1_88 = Box(box1_87, box1_87);
|
||||
const Box box1_89 = Box(box1_88, box1_88);
|
||||
const Box box1_90 = Box(box1_89, box1_89);
|
||||
const Box box1_91 = Box(box1_90, box1_90);
|
||||
const Box box1_92 = Box(box1_91, box1_91);
|
||||
const Box box1_93 = Box(box1_92, box1_92);
|
||||
const Box box1_94 = Box(box1_93, box1_93);
|
||||
const Box box1_95 = Box(box1_94, box1_94);
|
||||
const Box box1_96 = Box(box1_95, box1_95);
|
||||
const Box box1_97 = Box(box1_96, box1_96);
|
||||
const Box box1_98 = Box(box1_97, box1_97);
|
||||
const Box box1_99 = Box(box1_98, box1_98);
|
||||
|
||||
const Box box2_0 = Box(null, null);
|
||||
const Box box2_1 = Box(box2_0, box2_0);
|
||||
const Box box2_2 = Box(box2_1, box2_1);
|
||||
const Box box2_3 = Box(box2_2, box2_2);
|
||||
const Box box2_4 = Box(box2_3, box2_3);
|
||||
const Box box2_5 = Box(box2_4, box2_4);
|
||||
const Box box2_6 = Box(box2_5, box2_5);
|
||||
const Box box2_7 = Box(box2_6, box2_6);
|
||||
const Box box2_8 = Box(box2_7, box2_7);
|
||||
const Box box2_9 = Box(box2_8, box2_8);
|
||||
const Box box2_10 = Box(box2_9, box2_9);
|
||||
const Box box2_11 = Box(box2_10, box2_10);
|
||||
const Box box2_12 = Box(box2_11, box2_11);
|
||||
const Box box2_13 = Box(box2_12, box2_12);
|
||||
const Box box2_14 = Box(box2_13, box2_13);
|
||||
const Box box2_15 = Box(box2_14, box2_14);
|
||||
const Box box2_16 = Box(box2_15, box2_15);
|
||||
const Box box2_17 = Box(box2_16, box2_16);
|
||||
const Box box2_18 = Box(box2_17, box2_17);
|
||||
const Box box2_19 = Box(box2_18, box2_18);
|
||||
const Box box2_20 = Box(box2_19, box2_19);
|
||||
const Box box2_21 = Box(box2_20, box2_20);
|
||||
const Box box2_22 = Box(box2_21, box2_21);
|
||||
const Box box2_23 = Box(box2_22, box2_22);
|
||||
const Box box2_24 = Box(box2_23, box2_23);
|
||||
const Box box2_25 = Box(box2_24, box2_24);
|
||||
const Box box2_26 = Box(box2_25, box2_25);
|
||||
const Box box2_27 = Box(box2_26, box2_26);
|
||||
const Box box2_28 = Box(box2_27, box2_27);
|
||||
const Box box2_29 = Box(box2_28, box2_28);
|
||||
const Box box2_30 = Box(box2_29, box2_29);
|
||||
const Box box2_31 = Box(box2_30, box2_30);
|
||||
const Box box2_32 = Box(box2_31, box2_31);
|
||||
const Box box2_33 = Box(box2_32, box2_32);
|
||||
const Box box2_34 = Box(box2_33, box2_33);
|
||||
const Box box2_35 = Box(box2_34, box2_34);
|
||||
const Box box2_36 = Box(box2_35, box2_35);
|
||||
const Box box2_37 = Box(box2_36, box2_36);
|
||||
const Box box2_38 = Box(box2_37, box2_37);
|
||||
const Box box2_39 = Box(box2_38, box2_38);
|
||||
const Box box2_40 = Box(box2_39, box2_39);
|
||||
const Box box2_41 = Box(box2_40, box2_40);
|
||||
const Box box2_42 = Box(box2_41, box2_41);
|
||||
const Box box2_43 = Box(box2_42, box2_42);
|
||||
const Box box2_44 = Box(box2_43, box2_43);
|
||||
const Box box2_45 = Box(box2_44, box2_44);
|
||||
const Box box2_46 = Box(box2_45, box2_45);
|
||||
const Box box2_47 = Box(box2_46, box2_46);
|
||||
const Box box2_48 = Box(box2_47, box2_47);
|
||||
const Box box2_49 = Box(box2_48, box2_48);
|
||||
const Box box2_50 = Box(box2_49, box2_49);
|
||||
const Box box2_51 = Box(box2_50, box2_50);
|
||||
const Box box2_52 = Box(box2_51, box2_51);
|
||||
const Box box2_53 = Box(box2_52, box2_52);
|
||||
const Box box2_54 = Box(box2_53, box2_53);
|
||||
const Box box2_55 = Box(box2_54, box2_54);
|
||||
const Box box2_56 = Box(box2_55, box2_55);
|
||||
const Box box2_57 = Box(box2_56, box2_56);
|
||||
const Box box2_58 = Box(box2_57, box2_57);
|
||||
const Box box2_59 = Box(box2_58, box2_58);
|
||||
const Box box2_60 = Box(box2_59, box2_59);
|
||||
const Box box2_61 = Box(box2_60, box2_60);
|
||||
const Box box2_62 = Box(box2_61, box2_61);
|
||||
const Box box2_63 = Box(box2_62, box2_62);
|
||||
const Box box2_64 = Box(box2_63, box2_63);
|
||||
const Box box2_65 = Box(box2_64, box2_64);
|
||||
const Box box2_66 = Box(box2_65, box2_65);
|
||||
const Box box2_67 = Box(box2_66, box2_66);
|
||||
const Box box2_68 = Box(box2_67, box2_67);
|
||||
const Box box2_69 = Box(box2_68, box2_68);
|
||||
const Box box2_70 = Box(box2_69, box2_69);
|
||||
const Box box2_71 = Box(box2_70, box2_70);
|
||||
const Box box2_72 = Box(box2_71, box2_71);
|
||||
const Box box2_73 = Box(box2_72, box2_72);
|
||||
const Box box2_74 = Box(box2_73, box2_73);
|
||||
const Box box2_75 = Box(box2_74, box2_74);
|
||||
const Box box2_76 = Box(box2_75, box2_75);
|
||||
const Box box2_77 = Box(box2_76, box2_76);
|
||||
const Box box2_78 = Box(box2_77, box2_77);
|
||||
const Box box2_79 = Box(box2_78, box2_78);
|
||||
const Box box2_80 = Box(box2_79, box2_79);
|
||||
const Box box2_81 = Box(box2_80, box2_80);
|
||||
const Box box2_82 = Box(box2_81, box2_81);
|
||||
const Box box2_83 = Box(box2_82, box2_82);
|
||||
const Box box2_84 = Box(box2_83, box2_83);
|
||||
const Box box2_85 = Box(box2_84, box2_84);
|
||||
const Box box2_86 = Box(box2_85, box2_85);
|
||||
const Box box2_87 = Box(box2_86, box2_86);
|
||||
const Box box2_88 = Box(box2_87, box2_87);
|
||||
const Box box2_89 = Box(box2_88, box2_88);
|
||||
const Box box2_90 = Box(box2_89, box2_89);
|
||||
const Box box2_91 = Box(box2_90, box2_90);
|
||||
const Box box2_92 = Box(box2_91, box2_91);
|
||||
const Box box2_93 = Box(box2_92, box2_92);
|
||||
const Box box2_94 = Box(box2_93, box2_93);
|
||||
const Box box2_95 = Box(box2_94, box2_94);
|
||||
const Box box2_96 = Box(box2_95, box2_95);
|
||||
const Box box2_97 = Box(box2_96, box2_96);
|
||||
const Box box2_98 = Box(box2_97, box2_97);
|
||||
const Box box2_99 = Box(box2_98, box2_98);
|
||||
|
||||
Object confuse(Box x) {
|
||||
try {
|
||||
throw x;
|
||||
} catch (e) {
|
||||
return e;
|
||||
} // ignore: only_throw_errors
|
||||
}
|
||||
|
||||
void main() {
|
||||
if (!identical(confuse(box1_99), confuse(box2_99))) {
|
||||
throw Exception('box1_99 !== box2_99');
|
||||
}
|
||||
}
|
78
pkg/frontend_server/test/fixtures/lib/consts.dart
vendored
Normal file
78
pkg/frontend_server/test/fixtures/lib/consts.dart
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
// 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:core';
|
||||
|
||||
import 'package:const_finder_fixtures_package/package.dart';
|
||||
|
||||
import 'target.dart';
|
||||
|
||||
void main() {
|
||||
const Target target1 = Target('1', 1, null);
|
||||
const Target target2 = Target('2', 2, Target('4', 4, null));
|
||||
const Target target3 =
|
||||
Target('3', 3, Target('5', 5, null)); // should be tree shaken out.
|
||||
target1.hit();
|
||||
target2.hit();
|
||||
|
||||
blah(const Target('6', 6, null));
|
||||
|
||||
const IgnoreMe ignoreMe =
|
||||
IgnoreMe(Target('7', 7, null)); // IgnoreMe is ignored but 7 is not.
|
||||
final IgnoreMe ignoreMe2 = IgnoreMe(const Target('8', 8, null));
|
||||
final IgnoreMe ignoreMe3 =
|
||||
IgnoreMe(const Target('9', 9, Target('10', 10, null)));
|
||||
print(ignoreMe);
|
||||
print(ignoreMe2);
|
||||
print(ignoreMe3);
|
||||
|
||||
createTargetInPackage();
|
||||
|
||||
final StaticConstInitializer staticConstMap = StaticConstInitializer();
|
||||
staticConstMap.useOne(1);
|
||||
|
||||
const ExtendsTarget extendsTarget = ExtendsTarget('11', 11, null);
|
||||
extendsTarget.hit();
|
||||
const ImplementsTarget implementsTarget = ImplementsTarget('12', 12, null);
|
||||
implementsTarget.hit();
|
||||
|
||||
const MixedInTarget mixedInTraget = MixedInTarget('13');
|
||||
mixedInTraget.hit();
|
||||
}
|
||||
|
||||
class IgnoreMe {
|
||||
const IgnoreMe(this.target);
|
||||
|
||||
final Target target;
|
||||
|
||||
@override
|
||||
String toString() => target.toString();
|
||||
}
|
||||
|
||||
class StaticConstInitializer {
|
||||
static const List<Target> targets = <Target>[
|
||||
Target('100', 100, null),
|
||||
Target('101', 101, Target('102', 102, null)),
|
||||
];
|
||||
|
||||
static const Set<Target> targetSet = <Target>{
|
||||
Target('103', 103, null),
|
||||
Target('104', 104, Target('105', 105, null)),
|
||||
};
|
||||
|
||||
static const Map<int, Target> targetMap = <int, Target>{
|
||||
0: Target('106', 106, null),
|
||||
1: Target('107', 107, Target('108', 108, null)),
|
||||
};
|
||||
|
||||
void useOne(int index) {
|
||||
targets[index].hit();
|
||||
targetSet.skip(index).first.hit();
|
||||
targetMap[index]!.hit();
|
||||
}
|
||||
}
|
||||
|
||||
void blah(Target target) {
|
||||
print(target);
|
||||
}
|
46
pkg/frontend_server/test/fixtures/lib/consts_and_non.dart
vendored
Normal file
46
pkg/frontend_server/test/fixtures/lib/consts_and_non.dart
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
// 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:core';
|
||||
|
||||
import 'package:const_finder_fixtures_package/package.dart';
|
||||
|
||||
import 'target.dart';
|
||||
|
||||
void main() {
|
||||
const Target target1 = Target('1', 1, null);
|
||||
final Target target2 = Target('2', 2, const Target('4', 4, null));
|
||||
|
||||
final Target target3 =
|
||||
Target('3', 3, Target('5', 5, null)); // should be tree shaken out.
|
||||
final Target target6 = Target('6', 6, null); // should be tree shaken out.
|
||||
target1.hit();
|
||||
target2.hit();
|
||||
|
||||
blah(const Target('6', 6, null));
|
||||
|
||||
const IgnoreMe ignoreMe =
|
||||
IgnoreMe(Target('7', 7, null)); // IgnoreMe is ignored but 7 is not.
|
||||
final IgnoreMe ignoreMe2 = IgnoreMe(const Target('8', 8, null));
|
||||
final IgnoreMe ignoreMe3 =
|
||||
IgnoreMe(const Target('9', 9, Target('10', 10, null)));
|
||||
print(ignoreMe);
|
||||
print(ignoreMe2);
|
||||
print(ignoreMe3);
|
||||
|
||||
createNonConstTargetInPackage();
|
||||
}
|
||||
|
||||
class IgnoreMe {
|
||||
const IgnoreMe(this.target);
|
||||
|
||||
final Target target;
|
||||
|
||||
@override
|
||||
String toString() => target.toString();
|
||||
}
|
||||
|
||||
void blah(Target target) {
|
||||
print(target);
|
||||
}
|
31
pkg/frontend_server/test/fixtures/lib/static_icon_provider.dart
vendored
Normal file
31
pkg/frontend_server/test/fixtures/lib/static_icon_provider.dart
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
// 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 'target.dart';
|
||||
|
||||
void main() {
|
||||
Targets.used1.hit();
|
||||
Targets.used2.hit();
|
||||
final Target nonConstUsed3 = helper(Target.new);
|
||||
nonConstUsed3.hit();
|
||||
}
|
||||
|
||||
Target helper(Target Function(String, int, Target?) tearOff) {
|
||||
return tearOff('from tear-off', 3, null);
|
||||
}
|
||||
|
||||
@staticIconProvider
|
||||
class Targets {
|
||||
static const Target used1 = Target('used1', 1, null);
|
||||
static const Target used2 = Target('used2', 2, null);
|
||||
static const Target unused1 = Target('unused1', 1, null);
|
||||
}
|
||||
|
||||
// const_finder explicitly does not retain constants appearing within a class
|
||||
// with this annotation.
|
||||
class StaticIconProvider {
|
||||
const StaticIconProvider();
|
||||
}
|
||||
|
||||
const StaticIconProvider staticIconProvider = StaticIconProvider();
|
50
pkg/frontend_server/test/fixtures/lib/target.dart
vendored
Normal file
50
pkg/frontend_server/test/fixtures/lib/target.dart
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
// 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.
|
||||
|
||||
class Target {
|
||||
const Target(this.stringValue, this.intValue, this.targetValue);
|
||||
|
||||
final String stringValue;
|
||||
final int intValue;
|
||||
final Target? targetValue;
|
||||
|
||||
void hit() {
|
||||
print('$stringValue $intValue');
|
||||
}
|
||||
}
|
||||
|
||||
class ExtendsTarget extends Target {
|
||||
const ExtendsTarget(super.stringValue, super.intValue, super.targetValue);
|
||||
}
|
||||
|
||||
class ImplementsTarget implements Target {
|
||||
const ImplementsTarget(this.stringValue, this.intValue, this.targetValue);
|
||||
|
||||
@override
|
||||
final String stringValue;
|
||||
@override
|
||||
final int intValue;
|
||||
@override
|
||||
final Target? targetValue;
|
||||
|
||||
@override
|
||||
void hit() {
|
||||
print('ImplementsTarget - $stringValue $intValue');
|
||||
}
|
||||
}
|
||||
|
||||
mixin MixableTarget {
|
||||
String get val;
|
||||
|
||||
void hit() {
|
||||
print(val);
|
||||
}
|
||||
}
|
||||
|
||||
class MixedInTarget with MixableTarget {
|
||||
const MixedInTarget(this.val);
|
||||
|
||||
@override
|
||||
final String val;
|
||||
}
|
15
pkg/frontend_server/test/fixtures/pkg/package.dart
vendored
Normal file
15
pkg/frontend_server/test/fixtures/pkg/package.dart
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
// 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 'package:const_finder_fixtures/target.dart';
|
||||
|
||||
void createTargetInPackage() {
|
||||
const Target target = Target('package', -1, null);
|
||||
target.hit();
|
||||
}
|
||||
|
||||
void createNonConstTargetInPackage() {
|
||||
final Target target = Target('package_non', -2, null);
|
||||
target.hit();
|
||||
}
|
215
pkg/kernel/lib/const_finder.dart
Normal file
215
pkg/kernel/lib/const_finder.dart
Normal file
|
@ -0,0 +1,215 @@
|
|||
// 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:collection';
|
||||
|
||||
import 'kernel.dart';
|
||||
|
||||
class _ConstVisitor extends RecursiveVisitor<void> {
|
||||
_ConstVisitor(
|
||||
this.classLibraryUri,
|
||||
this.className,
|
||||
this.annotationClassLibraryUri,
|
||||
this.annotationClassName,
|
||||
) : _visitedInstances = <String>{},
|
||||
constantInstances = <Map<String, dynamic>>[],
|
||||
nonConstantLocations = <Map<String, dynamic>>[];
|
||||
|
||||
/// The library URI for the class to find.
|
||||
final String classLibraryUri;
|
||||
|
||||
/// The name of the class to find.
|
||||
final String className;
|
||||
|
||||
final Set<String> _visitedInstances;
|
||||
// TODO(johnniwinther): Use extension types to type these.
|
||||
final List<Map<String, dynamic>> constantInstances;
|
||||
final List<Map<String, dynamic>> nonConstantLocations;
|
||||
|
||||
bool inIgnoredClass = false;
|
||||
|
||||
/// Whether or not we are currently within the declaration of the target
|
||||
/// class.
|
||||
///
|
||||
/// We use this to determine when to skip tracking non-constant
|
||||
/// [ConstructorInvocation]s. This is because, in web builds, a static
|
||||
/// method is always created called _#new#tearOff() which returns the result
|
||||
/// of a non-constant invocation of the unnamed constructor.
|
||||
///
|
||||
/// For the following Dart class "FooBar":
|
||||
///
|
||||
/// class FooBar {
|
||||
/// const FooBar();
|
||||
/// }
|
||||
///
|
||||
/// The following kernel structure is generated:
|
||||
///
|
||||
/// class FooBar extends core::Object /*hasConstConstructor*/ {
|
||||
/// const constructor •() → min::FooBar
|
||||
/// : super core::Object::•()
|
||||
/// ;
|
||||
/// static method _#new#tearOff() → min::FooBar
|
||||
/// /* this is a non-const constructor invocation */
|
||||
/// return new min::FooBar::•();
|
||||
/// method noOp() → void {}
|
||||
/// }
|
||||
bool inTargetClass = false;
|
||||
|
||||
bool inTargetTearOff = false;
|
||||
|
||||
/// The name of the name of the class of the annotation marking classes
|
||||
/// whose constant references should be ignored.
|
||||
final String? annotationClassName;
|
||||
|
||||
/// The library URI of the class of the annotation marking classes whose
|
||||
/// constant references should be ignored.
|
||||
final String? annotationClassLibraryUri;
|
||||
|
||||
// A cache of previously evaluated classes.
|
||||
static final Map<Class, bool> _classHierarchyCache = <Class, bool>{};
|
||||
bool _matches(Class node) {
|
||||
final bool? result = _classHierarchyCache[node];
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
final bool exactMatch = node.name == className &&
|
||||
node.enclosingLibrary.importUri.toString() == classLibraryUri;
|
||||
final bool match = exactMatch ||
|
||||
node.supers.any((Supertype supertype) => _matches(supertype.classNode));
|
||||
_classHierarchyCache[node] = match;
|
||||
return match;
|
||||
}
|
||||
|
||||
// Avoid visiting the same constant more than once.
|
||||
// TODO(johnniwinther): Use [VisitOnceConstantVisitor] instead.
|
||||
final Set<Constant> _cache = LinkedHashSet<Constant>.identity();
|
||||
|
||||
@override
|
||||
void visitProcedure(Procedure node) {
|
||||
// TODO(johnniwinther): Use lowering predicates.
|
||||
final bool isTearOff = node.isStatic &&
|
||||
node.kind == ProcedureKind.Method &&
|
||||
node.name.text == '_#new#tearOff';
|
||||
if (inTargetClass && isTearOff) {
|
||||
inTargetTearOff = true;
|
||||
}
|
||||
super.visitProcedure(node);
|
||||
inTargetTearOff = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void defaultConstant(Constant node) {
|
||||
if (_cache.add(node)) {
|
||||
super.defaultConstant(node);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void defaultConstantReference(Constant node) {
|
||||
defaultConstant(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitConstructorInvocation(ConstructorInvocation node) {
|
||||
final Class parentClass = node.target.parent! as Class;
|
||||
if (!inTargetTearOff && _matches(parentClass)) {
|
||||
final Location location = node.location!;
|
||||
nonConstantLocations.add(<String, dynamic>{
|
||||
'file': location.file.toString(),
|
||||
'line': location.line,
|
||||
'column': location.column,
|
||||
});
|
||||
}
|
||||
super.visitConstructorInvocation(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitClass(Class node) {
|
||||
inTargetClass = _matches(node);
|
||||
// check if this is a class that we should ignore
|
||||
inIgnoredClass = _classShouldBeIgnored(node);
|
||||
super.visitClass(node);
|
||||
inTargetClass = false;
|
||||
inIgnoredClass = false;
|
||||
}
|
||||
|
||||
// If any annotations on the class match annotationClassName AND
|
||||
// annotationClassLibraryUri.
|
||||
bool _classShouldBeIgnored(Class node) {
|
||||
if (annotationClassName == null || annotationClassLibraryUri == null) {
|
||||
return false;
|
||||
}
|
||||
return node.annotations.any((Expression expression) {
|
||||
if (expression is! ConstantExpression) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Constant constant = expression.constant;
|
||||
return constant is InstanceConstant &&
|
||||
constant.classNode.name == annotationClassName &&
|
||||
constant.classNode.enclosingLibrary.importUri.toString() ==
|
||||
annotationClassLibraryUri;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void visitInstanceConstantReference(InstanceConstant node) {
|
||||
super.visitInstanceConstantReference(node);
|
||||
if (!_matches(node.classNode) || inIgnoredClass) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, dynamic> instance = <String, dynamic>{};
|
||||
for (final MapEntry<Reference, Constant> entry
|
||||
in node.fieldValues.entries) {
|
||||
if (entry.value is! PrimitiveConstant<dynamic>) {
|
||||
continue;
|
||||
}
|
||||
final PrimitiveConstant<dynamic> value =
|
||||
entry.value as PrimitiveConstant<dynamic>;
|
||||
instance[entry.key.asField.name.text] = value.value;
|
||||
}
|
||||
if (_visitedInstances.add(instance.toString())) {
|
||||
constantInstances.add(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A kernel AST visitor that finds const references.
|
||||
class ConstFinder {
|
||||
/// The path to the file to open.
|
||||
final String kernelFilePath;
|
||||
|
||||
/// Creates a new ConstFinder class.
|
||||
///
|
||||
/// The `kernelFilePath` is the path to a dill (kernel) file to process.
|
||||
ConstFinder({
|
||||
required this.kernelFilePath,
|
||||
required String classLibraryUri,
|
||||
required String className,
|
||||
String? annotationClassLibraryUri,
|
||||
String? annotationClassName,
|
||||
}) : _visitor = _ConstVisitor(
|
||||
classLibraryUri,
|
||||
className,
|
||||
annotationClassLibraryUri,
|
||||
annotationClassName,
|
||||
);
|
||||
|
||||
final _ConstVisitor _visitor;
|
||||
|
||||
/// Finds all instances
|
||||
// TODO(johnniwinther): Use extension types to type the result.
|
||||
Map<String, dynamic> findInstances() {
|
||||
_visitor._visitedInstances.clear();
|
||||
for (final Library library
|
||||
in loadComponentFromBinary(kernelFilePath).libraries) {
|
||||
library.visitChildren(_visitor);
|
||||
}
|
||||
return <String, dynamic>{
|
||||
'constantInstances': _visitor.constantInstances,
|
||||
'nonConstantLocations': _visitor.nonConstantLocations,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -46,6 +46,10 @@ void main(List<String> args) {
|
|||
platform('pkg/_fe_analyzer_shared/test/inheritance'),
|
||||
];
|
||||
|
||||
var frontendServerPackageDirs = [
|
||||
platform('pkg/frontend_server/test/fixtures'),
|
||||
];
|
||||
|
||||
var pkgVmPackageDirs = [
|
||||
platform('pkg/vm/testcases'),
|
||||
];
|
||||
|
@ -72,6 +76,7 @@ void main(List<String> args) {
|
|||
...makePackageConfigs(packageDirs),
|
||||
...makeCfePackageConfigs(cfePackageDirs),
|
||||
...makeFeAnalyzerSharedPackageConfigs(feAnalyzerSharedPackageDirs),
|
||||
...makeFrontendServerPackageConfigs(frontendServerPackageDirs),
|
||||
...makePkgVmPackageConfigs(pkgVmPackageDirs),
|
||||
];
|
||||
packages.sort((a, b) => a.name.compareTo(b.name));
|
||||
|
@ -175,6 +180,11 @@ Iterable<Package> makeFeAnalyzerSharedPackageConfigs(
|
|||
List<String> packageDirs) =>
|
||||
makeSpecialPackageConfigs('_fe_analyzer_shared', packageDirs);
|
||||
|
||||
/// Generates package configurations for the special pseudo-packages used by the
|
||||
/// frontend_server tests.
|
||||
Iterable<Package> makeFrontendServerPackageConfigs(List<String> packageDirs) =>
|
||||
makeSpecialPackageConfigs('frontend_server', packageDirs);
|
||||
|
||||
/// Generates package configurations for the special pseudo-packages used by the
|
||||
/// pkg/vm unit tests (`pkg/vm/test`).
|
||||
Iterable<Package> makePkgVmPackageConfigs(List<String> packageDirs) =>
|
||||
|
|
Loading…
Reference in a new issue