Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions tools/const_finder/bin/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ void main(List<String> args) {
valueHelp: 'path/to/main.dill',
help: 'The path to a kernel file to parse, which was created from the '
'main-package-uri library.')
..addOption('main-library-uri',
help: 'The package: URI to treat as the main entrypoint library '
'(the same package used to create the kernel-file).',
valueHelp: 'package:hello_world/main.dart')
Comment on lines -46 to -49
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This just doesn't work. Gallery, for example, uses multiple libraries to define icons.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you add any tests to cover this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're making me sad. I'll update the test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

..addOption('class-library-uri',
help: 'The package: URI of the class to find.',
valueHelp: 'package:flutter/src/widgets/icon_data.dart')
Expand All @@ -73,7 +69,6 @@ void main(List<String> args) {

final ConstFinder finder = ConstFinder(
kernelFilePath: getArg<String>('kernel-file'),
targetLibraryUri: getArg<String>('main-library-uri'),
classLibraryUri: getArg<String>('class-library-uri'),
className: getArg<String>('class-name'),
);
Expand Down
51 changes: 15 additions & 36 deletions tools/const_finder/lib/const_finder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,25 @@ import 'package:meta/meta.dart';
class _ConstVisitor extends RecursiveVisitor<void> {
_ConstVisitor(
this.kernelFilePath,
this.targetLibraryUri,
this.classLibraryUri,
this.className,
) : assert(kernelFilePath != null),
assert(targetLibraryUri != null),
assert(classLibraryUri != null),
assert(className != null),
_visitedInstnaces = <String>{},
constantInstances = <Map<String, dynamic>>[],
nonConstantLocations = <Map<String, dynamic>>[];

/// The path to the file to open.
final String kernelFilePath;

/// The library URI for the main entrypoint of the target library.
final String targetLibraryUri;

/// The library URI for the class to find.
final String classLibraryUri;

/// The name of the class to find.
final String className;

final Set<String> _visitedInstnaces;
final List<Map<String, dynamic>> constantInstances;
final List<Map<String, dynamic>> nonConstantLocations;

Expand All @@ -43,6 +40,7 @@ class _ConstVisitor extends RecursiveVisitor<void> {
final Class parentClass = node.target.parent as Class;
if (!_matches(parentClass)) {
super.visitConstructorInvocation(node);
return;
}
nonConstantLocations.add(<String, dynamic>{
'file': node.location.file.toString(),
Expand All @@ -53,15 +51,22 @@ class _ConstVisitor extends RecursiveVisitor<void> {

@override
void visitInstanceConstantReference(InstanceConstant node) {
node.fieldValues.values.whereType<InstanceConstant>().forEach(visitInstanceConstantReference);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is important if the const is defined inline somewhere - it never gets visited by this method otherwise.

It has to be up here before we check for matches because it could either be in the class we care about or in some other class.

if (!_matches(node.classNode)) {
super.visitInstanceConstantReference(node);
return;
}
final Map<String, dynamic> instance = <String, dynamic>{};
for (MapEntry<Reference, Constant> kvp in node.fieldValues.entries) {
if (kvp.value is! PrimitiveConstant<dynamic>) {
continue;
}
final PrimitiveConstant<dynamic> value = kvp.value as PrimitiveConstant<dynamic>;
instance[kvp.key.canonicalName.name] = value.value;
}
constantInstances.add(instance);
if (_visitedInstnaces.add(instance.toString())) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cheating a little, but it's simpler than creating a class that can properly hash and equality compare a dynamic map.

It's good enough to dedup things when all the parameters are in order.

constantInstances.add(instance);
}
}
}

Expand All @@ -71,53 +76,27 @@ class ConstFinder {
/// be null.
///
/// The `kernelFilePath` is the path to a dill (kernel) file to process.
///
/// The `targetLibraryUri` is the `package:` URI of the main entrypoint to
/// search from.
///
///
///
ConstFinder({
@required String kernelFilePath,
@required String targetLibraryUri,
@required String classLibraryUri,
@required String className,
}) : _visitor = _ConstVisitor(
kernelFilePath,
targetLibraryUri,
classLibraryUri,
className,
);

final _ConstVisitor _visitor;

Library _getRoot() {
final Component binary = loadComponentFromBinary(_visitor.kernelFilePath);
return binary.libraries.firstWhere(
(Library library) => library.canonicalName.name == _visitor.targetLibraryUri,
orElse: () => throw LibraryNotFoundException._(_visitor.targetLibraryUri),
);
}

/// Finds all instances
Map<String, dynamic> findInstances() {
final Library root = _getRoot();
root.visitChildren(_visitor);
_visitor._visitedInstnaces.clear();
for (Library library in loadComponentFromBinary(_visitor.kernelFilePath).libraries) {
library.visitChildren(_visitor);
}
return <String, dynamic>{
'constantInstances': _visitor.constantInstances,
'nonConstantLocations': _visitor.nonConstantLocations,
};
}
}

/// Exception thrown by [ConstFinder.findInstances] when the target library
/// is not found.
class LibraryNotFoundException implements Exception {
const LibraryNotFoundException._(this.targetLibraryUri);

/// The library target URI that could not be found.
final String targetLibraryUri;

@override
String toString() => 'Could not find target library for "$targetLibraryUri".';
}
38 changes: 29 additions & 9 deletions tools/const_finder/test/const_finder_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ void _checkConsts() {
print('Checking for expected constants.');
final ConstFinder finder = ConstFinder(
kernelFilePath: constsDill,
targetLibraryUri: 'package:const_finder_fixtures/consts.dart',
classLibraryUri: 'package:const_finder_fixtures/target.dart',
className: 'Target',
);
Expand All @@ -43,8 +42,15 @@ void _checkConsts() {
jsonEncode(finder.findInstances()),
jsonEncode(<String, dynamic>{
'constantInstances': <Map<String, dynamic>>[
<String, dynamic>{'stringValue': '1', 'intValue': 1},
<String, dynamic>{'stringValue': '2', 'intValue': 2}
<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': 'package', 'intValue':-1, 'targetValue': null},
],
'nonConstantLocations': <dynamic>[],
}),
Expand All @@ -55,7 +61,6 @@ void _checkNonConsts() {
print('Checking for non-constant instances.');
final ConstFinder finder = ConstFinder(
kernelFilePath: constsAndNonDill,
targetLibraryUri: 'package:const_finder_fixtures/consts_and_non.dart',
classLibraryUri: 'package:const_finder_fixtures/target.dart',
className: 'Target',
);
Expand All @@ -64,19 +69,34 @@ void _checkNonConsts() {
jsonEncode(finder.findInstances()),
jsonEncode(<String, dynamic>{
'constantInstances': <dynamic>[
<String, dynamic>{'stringValue': '1', 'intValue': 1}
<String, dynamic>{'stringValue': '1', 'intValue': 1, '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://$fixtures/lib/consts_and_non.dart',
'line': 12,
'column': 26
'line': 14,
'column': 26,
},
<String, dynamic>{
'file': 'file://$fixtures/lib/consts_and_non.dart',
'line': 14,
'column': 26
'line': 17,
'column': 26,
},
<String, dynamic>{
'file': 'file://$fixtures/lib/consts_and_non.dart',
'line': 19,
'column': 26,
},
<String, dynamic>{
'file': 'file://$fixtures/pkg/package.dart',
'line': 10,
'column': 25,
}
]
}),
);
Expand Down
1 change: 1 addition & 0 deletions tools/const_finder/test/fixtures/.packages
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# Generated by pub on 2020-01-15 10:08:29.776333.
const_finder_fixtures:lib/
const_finder_fixtures_package:pkg/
30 changes: 27 additions & 3 deletions tools/const_finder/test/fixtures/lib/consts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,38 @@

import 'dart:core';

import 'package:const_finder_fixtures_package/package.dart';

import 'target.dart';

void main() {
const Target target1 = Target('1', 1);
const Target target2 = Target('2', 2);
const Target target1 = Target('1', 1, null);
const Target target2 = Target('2', 2, Target('4', 4, null));
// ignore: unused_local_variable
const Target target3 = Target('3', 3); // should be tree shaken out.
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.
// ignore: prefer_const_constructors
final IgnoreMe ignoreMe2 = IgnoreMe(const Target('8', 8, null));
// ignore: prefer_const_constructors
final IgnoreMe ignoreMe3 = IgnoreMe(const Target('9', 9, Target('10', 10, null)));
print(ignoreMe);
print(ignoreMe2);
print(ignoreMe3);

createTargetInPackage();
}

class IgnoreMe {
const IgnoreMe([this.target]);

final Target target;
}

void blah(Target target) {
print(target);
}
34 changes: 31 additions & 3 deletions tools/const_finder/test/fixtures/lib/consts_and_non.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,41 @@
// ignore_for_file: prefer_const_constructors
import 'dart:core';

import 'package:const_finder_fixtures_package/package.dart';

import 'target.dart';

void main() {
const Target target1 = Target('1', 1);
final Target target2 = Target('2', 2);
const Target target1 = Target('1', 1, null);
final Target target2 = Target('2', 2, const Target('4', 4, null));

// ignore: unused_local_variable
final Target target3 = Target('3', 3, Target('5', 5, null)); // should be tree shaken out.
// ignore: unused_local_variable
final Target target3 = Target('3', 3); // 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.
// ignore: prefer_const_constructors
final IgnoreMe ignoreMe2 = IgnoreMe(const Target('8', 8, null));
// ignore: prefer_const_constructors
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;
}

void blah(Target target) {
print(target);
}
3 changes: 2 additions & 1 deletion tools/const_finder/test/fixtures/lib/target.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
// found in the LICENSE file.

class Target {
const Target(this.stringValue, this.intValue);
const Target(this.stringValue, this.intValue, this.targetValue);

final String stringValue;
final int intValue;
final Target targetValue;

void hit() {
print('$stringValue $intValue');
Expand Down
12 changes: 12 additions & 0 deletions tools/const_finder/test/fixtures/pkg/package.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:const_finder_fixtures/target.dart';

void createTargetInPackage() {
const Target target = Target('package', -1, null);
target.hit();
}

void createNonConstTargetInPackage() {
// ignore: prefer_const_constructors
final Target target = Target('package_non', -2, null);
target.hit();
}