Skip to content

Commit bc3a760

Browse files
mosuemCommit Queue
authored andcommitted
Record instances which are not annotations
There is no need to restrict the recording to annotations. It also opens up more interesting use cases, for example recording instances of `const StringAsset('id').load()` if the class `StringAsset` has the `RecordUse` annotation. Also includes a small refactoring in a separate commit. TESTED=pkg/vm/testcases/transformations/record_use/instance_not_annotation.dart Change-Id: I262995760a8ba8bc14c9cf01e5eb1b47f1278282 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/387700 Reviewed-by: Martin Kustermann <[email protected]> Commit-Queue: Moritz Sümmermann <[email protected]>
1 parent c549a73 commit bc3a760

File tree

13 files changed

+551
-69
lines changed

13 files changed

+551
-69
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:kernel/ast.dart';
6+
import 'package:front_end/src/kernel/record_use.dart' as recordUse;
7+
8+
/// Expose only the [collect] method of a [_ConstantCollector] to outside use.
9+
extension type ConstantCollector(_ConstantCollector _collector) {
10+
ConstantCollector.collectWith(
11+
Function(
12+
ConstantExpression context,
13+
InstanceConstant constant,
14+
) collector)
15+
: _collector = _ConstantCollector(collector);
16+
17+
void collect(ConstantExpression expression) => _collector.collect(expression);
18+
}
19+
20+
/// A visitor traversing constants and storing instance constants with the
21+
/// `@RecordUse` annotation using the [collector] callback.
22+
class _ConstantCollector implements ConstantVisitor {
23+
/// The collector callback which records the constant.
24+
final void Function(
25+
ConstantExpression context,
26+
InstanceConstant constant,
27+
) collector;
28+
29+
/// The expression in which the constant was found.
30+
ConstantExpression? _expression;
31+
32+
/// A cache to avoid having to re-check for annotations.
33+
final Map<Class, bool> _hasRecordUseAnnotation = {};
34+
35+
final Set<Constant> _visited = {};
36+
37+
_ConstantCollector(this.collector);
38+
39+
void collect(ConstantExpression node) {
40+
_expression = node;
41+
handleConstantReference(node.constant);
42+
_expression = null;
43+
}
44+
45+
void handleConstantReference(Constant constant) {
46+
if (_visited.add(constant)) {
47+
constant.accept(this);
48+
}
49+
}
50+
51+
@override
52+
void visitListConstant(ListConstant constant) {
53+
for (final entry in constant.entries) {
54+
handleConstantReference(entry);
55+
}
56+
}
57+
58+
@override
59+
void visitMapConstant(MapConstant constant) {
60+
for (final entry in constant.entries) {
61+
handleConstantReference(entry.key);
62+
handleConstantReference(entry.value);
63+
}
64+
}
65+
66+
@override
67+
void visitSetConstant(SetConstant constant) {
68+
for (final entry in constant.entries) {
69+
handleConstantReference(entry);
70+
}
71+
}
72+
73+
@override
74+
void visitRecordConstant(RecordConstant constant) {
75+
for (final value in constant.positional) {
76+
handleConstantReference(value);
77+
}
78+
for (final value in constant.named.values) {
79+
handleConstantReference(value);
80+
}
81+
}
82+
83+
@override
84+
void visitInstanceConstant(InstanceConstant constant) {
85+
assert(_expression != null);
86+
final classNode = constant.classNode;
87+
if (_hasRecordUseAnnotation[classNode] ??=
88+
recordUse.findRecordUseAnnotation(classNode).isNotEmpty) {
89+
collector(_expression!, constant);
90+
}
91+
for (final value in constant.fieldValues.values) {
92+
handleConstantReference(value);
93+
}
94+
}
95+
96+
@override
97+
visitAuxiliaryConstant(AuxiliaryConstant node) {
98+
throw UnsupportedError('Cannot record an `AuxiliaryConstant`.');
99+
}
100+
101+
@override
102+
visitBoolConstant(BoolConstant node) {}
103+
104+
@override
105+
visitConstructorTearOffConstant(ConstructorTearOffConstant node) {}
106+
107+
@override
108+
visitDoubleConstant(DoubleConstant node) {}
109+
110+
@override
111+
visitInstantiationConstant(InstantiationConstant node) {}
112+
113+
@override
114+
visitIntConstant(IntConstant node) {}
115+
116+
@override
117+
visitNullConstant(NullConstant node) {}
118+
119+
@override
120+
visitRedirectingFactoryTearOffConstant(
121+
RedirectingFactoryTearOffConstant node) {}
122+
123+
@override
124+
visitStaticTearOffConstant(StaticTearOffConstant node) {}
125+
126+
@override
127+
visitStringConstant(StringConstant node) {}
128+
129+
@override
130+
visitSymbolConstant(SymbolConstant node) {}
131+
132+
@override
133+
visitTypeLiteralConstant(TypeLiteralConstant node) {}
134+
135+
@override
136+
visitTypedefTearOffConstant(TypedefTearOffConstant node) {}
137+
138+
@override
139+
visitUnevaluatedConstant(UnevaluatedConstant node) {}
140+
}

pkg/vm/lib/transformations/record_use/record_call.dart

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,29 @@ import 'package:record_use/record_use_internal.dart';
99
import 'package:vm/metadata/loading_units.dart';
1010
import 'package:vm/transformations/record_use/record_use.dart';
1111

12-
class StaticCallRecorder {
13-
final Map<ast.Procedure, Usage<CallReference>> callsForMethod = {};
12+
/// Record calls and their constant arguments. Currently tracks
13+
/// * static or top-level method calls through [recordStaticInvocation]
14+
/// * tear-offs through [recordConstantExpression]
15+
///
16+
/// The result of adding calls can be fetched from [foundCalls].
17+
class CallRecorder {
18+
/// The collection of recorded calls found so far.
19+
Iterable<Usage<CallReference>> get foundCalls => _callsForMethod.values;
20+
21+
/// Keep track of the calls which are recorded, to easily add newly found
22+
/// ones.
23+
final Map<ast.Procedure, Usage<CallReference>> _callsForMethod = {};
24+
25+
/// The ordered list of loading units to retrieve the loading unit index from.
1426
final List<LoadingUnit> _loadingUnits;
15-
final Uri source;
1627

17-
StaticCallRecorder(this.source, this._loadingUnits);
28+
/// The source uri to base relative URIs off of.
29+
final Uri _source;
1830

19-
void recordStaticCall(ast.StaticInvocation node) {
31+
CallRecorder(this._source, this._loadingUnits);
32+
33+
/// Will record a static invocation if it is annotated with `@RecordUse`.
34+
void recordStaticInvocation(ast.StaticInvocation node) {
2035
final annotations = recordUse.findRecordUseAnnotation(node.target);
2136
if (annotations.isNotEmpty) {
2237
final call = _getCall(node.target);
@@ -26,18 +41,27 @@ class StaticCallRecorder {
2641
}
2742
}
2843

29-
void recordTearoff(ast.ConstantExpression node) {
44+
/// Will record a tear-off if the target is annotated with `@RecordUse`.
45+
void recordConstantExpression(ast.ConstantExpression node) {
3046
final constant = node.constant;
3147
if (constant is ast.StaticTearOffConstant) {
32-
final annotations = recordUse.findRecordUseAnnotation(constant.target);
33-
if (annotations.isNotEmpty) {
34-
final call = _getCall(constant.target);
35-
CallReference reference = _collectTearOff(constant, node);
36-
call.references.add(reference);
48+
final hasRecordUseAnnotation =
49+
recordUse.findRecordUseAnnotation(constant.target).isNotEmpty;
50+
if (hasRecordUseAnnotation) {
51+
_recordTearOff(constant, node);
3752
}
3853
}
3954
}
4055

56+
void _recordTearOff(
57+
ast.StaticTearOffConstant constant,
58+
ast.ConstantExpression node,
59+
) {
60+
final call = _getCall(constant.target);
61+
final reference = _collectTearOff(constant, node);
62+
call.references.add(reference);
63+
}
64+
4165
/// Record a tear off as a call with all non-const arguments.
4266
CallReference _collectTearOff(
4367
ast.StaticTearOffConstant constant,
@@ -53,7 +77,7 @@ class StaticCallRecorder {
5377
),
5478
);
5579
return CallReference(
56-
location: node.location!.recordLocation(source),
80+
location: node.location!.recordLocation(_source),
5781
arguments: Arguments(nonConstArguments: nonConstArguments),
5882
);
5983
}
@@ -62,10 +86,8 @@ class StaticCallRecorder {
6286
/// shared across multiple calls to the same method.
6387
Usage _getCall(ast.Procedure target) {
6488
final definition = _definitionFromMember(target);
65-
return callsForMethod.putIfAbsent(
66-
target,
67-
() => Usage(definition: definition, references: []),
68-
);
89+
return _callsForMethod[target] ??=
90+
Usage(definition: definition, references: []);
6991
}
7092

7193
CallReference _createCallReference(ast.StaticInvocation node) {
@@ -89,7 +111,7 @@ class StaticCallRecorder {
89111
final namedGrouped = _groupByNull(namedArguments);
90112

91113
return CallReference(
92-
location: node.location!.recordLocation(source),
114+
location: node.location!.recordLocation(_source),
93115
loadingUnit: loadingUnitForNode(node, _loadingUnits).toString(),
94116
arguments: Arguments(
95117
constArguments: ConstArguments(
@@ -126,15 +148,15 @@ class StaticCallRecorder {
126148

127149
Definition _definitionFromMember(ast.Member target) {
128150
final enclosingLibrary = target.enclosingLibrary;
129-
String file = getImportUri(enclosingLibrary, source);
151+
String file = getImportUri(enclosingLibrary, _source);
130152

131153
return Definition(
132154
identifier: Identifier(
133155
importUri: file,
134156
parent: target.enclosingClass?.name,
135157
name: target.name.text,
136158
),
137-
location: target.location!.recordLocation(source),
159+
location: target.location!.recordLocation(_source),
138160
loadingUnit:
139161
loadingUnitForNode(enclosingLibrary, _loadingUnits).toString(),
140162
);

pkg/vm/lib/transformations/record_use/record_instance.dart

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,78 +2,90 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import 'package:front_end/src/kernel/record_use.dart' as recordUse;
65
import 'package:kernel/ast.dart' as ast;
76
import 'package:record_use/record_use_internal.dart';
87
import 'package:vm/metadata/loading_units.dart';
98
import 'package:vm/transformations/record_use/record_use.dart';
109

11-
class InstanceUseRecorder {
12-
final Map<ast.Class, Usage<InstanceReference>> instancesForClass = {};
10+
import 'constant_collector.dart';
11+
12+
/// Record a const instance by calling [recordConstantExpression]. After all the
13+
/// const instances have been recorded, retrieve them using [foundInstances].
14+
class InstanceRecorder {
15+
/// The collection of recorded instances found so far.
16+
Iterable<Usage<InstanceReference>> get foundInstances =>
17+
_instancesForClass.values;
18+
19+
/// Keep track of the classes which are recorded, to easily add found
20+
/// instances.
21+
final Map<ast.Class, Usage<InstanceReference>> _instancesForClass = {};
22+
23+
/// The ordered list of loading units to retrieve the loading unit index from.
1324
final List<LoadingUnit> _loadingUnits;
14-
final Uri source;
1525

16-
InstanceUseRecorder(this.source, this._loadingUnits);
26+
/// The source uri to base relative URIs off of.
27+
final Uri _source;
28+
29+
/// A visitor traversing and collecting constants.
30+
late final ConstantCollector collector;
1731

18-
void recordAnnotationUse(ast.ConstantExpression node) {
19-
final constant = node.constant;
20-
if (constant is ast.InstanceConstant) {
21-
if (recordUse.findRecordUseAnnotation(constant.classNode).isNotEmpty) {
22-
_collectUseInformation(node, constant);
23-
}
24-
}
32+
InstanceRecorder(this._source, this._loadingUnits) {
33+
collector = ConstantCollector.collectWith(_collectInstance);
2534
}
2635

27-
void _collectUseInformation(
28-
ast.ConstantExpression node,
36+
void recordConstantExpression(ast.ConstantExpression node) =>
37+
collector.collect(node);
38+
39+
void _collectInstance(
40+
ast.ConstantExpression expression,
2941
ast.InstanceConstant constant,
3042
) {
3143
// Collect the name and definition location of the invocation. This is
3244
// shared across multiple calls to the same method.
3345
final existingInstance = _getCall(constant.classNode);
3446

3547
// Collect the (int, bool, double, or String) arguments passed in the call.
36-
existingInstance.references.add(_createInstanceReference(node, constant));
48+
existingInstance.references
49+
.add(_createInstanceReference(expression, constant));
3750
}
3851

3952
/// Collect the name and definition location of the invocation. This is
4053
/// shared across multiple calls to the same method.
4154
Usage<InstanceReference> _getCall(ast.Class cls) {
4255
final definition = _definitionFromClass(cls);
43-
return instancesForClass.putIfAbsent(
44-
cls,
45-
() => Usage(definition: definition, references: []),
46-
);
56+
return _instancesForClass[cls] ??=
57+
Usage(definition: definition, references: []);
4758
}
4859

4960
Definition _definitionFromClass(ast.Class cls) {
5061
final enclosingLibrary = cls.enclosingLibrary;
51-
String file = getImportUri(enclosingLibrary, source);
62+
final file = getImportUri(enclosingLibrary, _source);
5263

5364
return Definition(
5465
identifier: Identifier(importUri: file, name: cls.name),
55-
location: cls.location!.recordLocation(source),
66+
location: cls.location!.recordLocation(_source),
5667
loadingUnit:
5768
loadingUnitForNode(cls.enclosingLibrary, _loadingUnits).toString(),
5869
);
5970
}
6071

6172
InstanceReference _createInstanceReference(
62-
ast.ConstantExpression node,
73+
ast.ConstantExpression expression,
6374
ast.InstanceConstant constant,
6475
) =>
6576
InstanceReference(
66-
location: node.location!.recordLocation(source),
77+
location: expression.location!.recordLocation(_source),
6778
instanceConstant: _fieldsFromConstant(constant),
68-
loadingUnit: loadingUnitForNode(node, _loadingUnits).toString(),
79+
loadingUnit: loadingUnitForNode(expression, _loadingUnits).toString(),
6980
);
7081

7182
InstanceConstant _fieldsFromConstant(ast.InstanceConstant constant) =>
7283
InstanceConstant(
73-
fields: constant.fieldValues.map(
74-
(key, value) => MapEntry(
75-
key.asField.name.text,
76-
evaluateConstant(value),
84+
fields: constant.fieldValues.map(
85+
(key, value) => MapEntry(
86+
key.asField.name.text,
87+
evaluateConstant(value),
88+
),
7789
),
78-
));
90+
);
7991
}

0 commit comments

Comments
 (0)