Skip to content

Commit 7a83c82

Browse files
mosuemCommit Queue
authored andcommitted
[record_use] Don't record locations for usages and other refactorings
In preparation of a merge with the Dart2Js format in a follow up PR. - Treat default values as passed arguments - `foo(4)` is the same as `foo()` for all intents and purposes for a `foo([int i = 4])`. - Add signatures to lookup, as dart2js forgets them during compilation. - Encode non-constant arguments as `null`, while a `null` passed as an argument is a `NullConstant`. Tested:pkg/vm/test/transformations/record_use_test.dart Change-Id: Ifd2597d8d1b979627fa45fd4736a762031c17216 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/415620 Commit-Queue: Moritz Sümmermann <[email protected]> Reviewed-by: Martin Kustermann <[email protected]> Auto-Submit: Moritz Sümmermann <[email protected]>
1 parent 809573c commit 7a83c82

File tree

70 files changed

+2868
-1950
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+2868
-1950
lines changed

pkg/front_end/lib/src/kernel/record_use.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,26 @@ import 'package:kernel/ast.dart';
1212
import '../base/messages.dart' show messageRecordUseCannotBePlacedHere;
1313
import 'constant_evaluator.dart' show ErrorReporter;
1414

15+
// Coverage-ignore(suite): Not run.
1516
/// Get all of the `@RecordUse` annotations from `package:meta`
1617
/// that are attached to the specified [node].
1718
Iterable<InstanceConstant> findRecordUseAnnotation(Annotatable node) =>
1819
node.annotations
1920
.whereType<ConstantExpression>()
2021
.map((expression) => expression.constant)
2122
.whereType<InstanceConstant>()
22-
.where((instance) => isRecordUse(instance.classNode))
23-
.toList(growable: false);
23+
.where((instance) => isRecordUse(instance.classNode));
24+
25+
// Coverage-ignore(suite): Not run.
26+
bool hasRecordUseAnnotation(Annotatable node) =>
27+
findRecordUseAnnotation(node).isNotEmpty;
2428

2529
// Coverage-ignore(suite): Not run.
2630
final Uri _metaLibraryUri = new Uri(scheme: 'package', path: 'meta/meta.dart');
2731

32+
// Coverage-ignore(suite): Not run.
2833
bool isRecordUse(Class cls) =>
2934
cls.name == 'RecordUse' &&
30-
// Coverage-ignore(suite): Not run.
3135
cls.enclosingLibrary.importUri == _metaLibraryUri;
3236

3337
// Coverage-ignore(suite): Not run.

pkg/record_use/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
## 0.3.1-wip
1+
## 0.4.0
22

33
- Update SDK constraint to `^3.5.0`.
4+
- Rewrite API to expose less symbols.
5+
- Remove locations for easier caching.
46

57
## 0.3.0
68

pkg/record_use/README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,5 +104,14 @@ void main(List<String> arguments){
104104
}
105105
```
106106

107+
## Limitations
108+
As this is designed to work on both web and native platforms, we have to adapt
109+
to the platform pecularities. One of them is that javascript does not support
110+
named arguments, so the dart2js compiler rewrites functions to only accept named
111+
parameters.
112+
While you can use named parameters to record functions, we advise caution as the
113+
retrieval behavior might change once we work around this dart2js limitation and
114+
implement separate positional and named parameters.
115+
107116
## Contributing
108-
Contributions are welcome! Please open an issue or submit a pull request.
117+
Contributions are welcome! Please open an issue or submit a pull request.

pkg/record_use/example/record_use_example.dart

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44

55
import 'package:record_use/record_use.dart';
66

7-
void doStuff(RecordedUsages usage, Identifier callId, Identifier referenceId) {
7+
void doStuffInLinkHook(
8+
RecordedUsages usage,
9+
Identifier identifier1,
10+
Identifier identifier2,
11+
Identifier identifier3,
12+
) {
813
print(usage.metadata);
9-
print(usage.argumentsTo(callId));
10-
print(usage.instancesOf(referenceId));
11-
print(usage.hasNonConstArguments(callId));
14+
print(usage.constArgumentsFor(identifier1, 'void foo(int i)'));
15+
print(usage.constantsOf(identifier2));
16+
print(usage.hasNonConstArguments(identifier3));
1217
}

pkg/record_use/lib/record_use.dart

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,6 @@
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-
export 'src/public/arguments.dart'
6-
show Arguments, ConstArguments, NonConstArguments;
7-
export 'src/public/constant.dart'
8-
show
9-
BoolConstant,
10-
Constant,
11-
InstanceConstant,
12-
IntConstant,
13-
ListConstant,
14-
MapConstant,
15-
NullConstant,
16-
PrimitiveConstant,
17-
StringConstant;
18-
export 'src/public/identifier.dart' show Identifier;
19-
export 'src/public/location.dart' show Location;
20-
export 'src/public/metadata.dart' show Metadata;
21-
//Not exporting `Reference` as it is not used in the API
22-
export 'src/public/reference.dart' show CallReference, InstanceReference;
23-
export 'src/record_use.dart' show RecordedUsages;
5+
export 'src/identifier.dart' show Identifier;
6+
export 'src/metadata.dart' show Metadata, MetadataExt;
7+
export 'src/record_use.dart' show ConstantInstance, RecordedUsages;

pkg/record_use/lib/record_use_internal.dart

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,23 @@
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-
export 'record_use.dart';
6-
export 'src/internal/definition.dart' show Definition;
7-
export 'src/internal/usage.dart' show Usage;
8-
export 'src/internal/usage_record.dart' show UsageRecord;
5+
export 'src/constant.dart'
6+
show
7+
BoolConstant,
8+
Constant,
9+
InstanceConstant,
10+
IntConstant,
11+
ListConstant,
12+
MapConstant,
13+
NullConstant,
14+
PrimitiveConstant,
15+
StringConstant;
16+
export 'src/definition.dart' show Definition;
17+
export 'src/identifier.dart' show Identifier;
18+
export 'src/location.dart' show Location;
19+
export 'src/metadata.dart' show Metadata, MetadataExt;
20+
export 'src/record_use.dart' show RecordedUsages;
21+
export 'src/recordings.dart' show Recordings;
22+
export 'src/reference.dart'
23+
show CallReference, CallTearOff, CallWithArguments, InstanceReference;
24+
export 'src/signature.dart' show Signature;
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
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 'helper.dart';
6+
7+
const _typeKey = 'type';
8+
const _valueKey = 'value';
9+
10+
/// A constant value that can be recorded and serialized.
11+
///
12+
/// This supports basic constants such as [bool]s or [int]s, as well as
13+
/// [ListConstant], [MapConstant] or [InstanceConstant] for more complex
14+
/// structures.
15+
///
16+
/// This follows the AST constant concept from the Dart SDK.
17+
sealed class Constant {
18+
/// Creates a [Constant] object.
19+
const Constant();
20+
21+
/// Converts this [Constant] object to a JSON representation.
22+
///
23+
/// [constants] needs to be passed, as the [Constant]s are normalized and
24+
/// stored separately in the JSON.
25+
Map<String, Object?> toJson(Map<Constant, int> constants);
26+
27+
/// Converts this [Constant] to the value it represents.
28+
Object? toValue() => switch (this) {
29+
NullConstant() => null,
30+
PrimitiveConstant p => p.value,
31+
ListConstant<Constant> l => l.value.map((c) => c.toValue()).toList(),
32+
MapConstant<Constant> m => m.value.map(
33+
(key, value) => MapEntry(key, value.toValue()),
34+
),
35+
InstanceConstant i => i.fields.map(
36+
(key, value) => MapEntry(key, value.toValue()),
37+
),
38+
};
39+
40+
/// Creates a [Constant] object from its JSON representation.
41+
///
42+
/// [constants] needs to be passed, as the [Constant]s are normalized and
43+
/// stored separately in the JSON.
44+
static Constant fromJson(
45+
Map<String, Object?> value,
46+
List<Constant> constants,
47+
) => switch (value[_typeKey] as String) {
48+
NullConstant._type => const NullConstant(),
49+
BoolConstant._type => BoolConstant(value[_valueKey] as bool),
50+
IntConstant._type => IntConstant(value[_valueKey] as int),
51+
StringConstant._type => StringConstant(value[_valueKey] as String),
52+
ListConstant._type => ListConstant(
53+
(value[_valueKey] as List<dynamic>)
54+
.map((value) => value as int)
55+
.map((value) => constants[value])
56+
.toList(),
57+
),
58+
MapConstant._type => MapConstant(
59+
(value[_valueKey] as Map<String, Object?>).map(
60+
(key, value) => MapEntry(key, constants[value as int]),
61+
),
62+
),
63+
InstanceConstant._type => InstanceConstant(
64+
fields: (value[_valueKey] as Map<String, Object?>).map(
65+
(key, value) => MapEntry(key, constants[value as int]),
66+
),
67+
),
68+
String() =>
69+
throw UnimplementedError('This type is not a supported constant'),
70+
};
71+
}
72+
73+
/// Represents the `null` constant value.
74+
final class NullConstant extends Constant {
75+
/// The type identifier for JSON serialization.
76+
static const _type = 'Null';
77+
78+
/// Creates a [NullConstant] object.
79+
const NullConstant() : super();
80+
81+
@override
82+
Map<String, Object?> toJson(Map<Constant, int> constants) =>
83+
_toJson(_type, null);
84+
85+
@override
86+
bool operator ==(Object other) => other is NullConstant;
87+
88+
@override
89+
int get hashCode => 0;
90+
}
91+
92+
/// Represents a constant value of a primitive type.
93+
sealed class PrimitiveConstant<T extends Object> extends Constant {
94+
/// The underlying value of this constant.
95+
final T value;
96+
97+
/// Creates a [PrimitiveConstant] object with the given [value].
98+
const PrimitiveConstant(this.value);
99+
100+
@override
101+
int get hashCode => value.hashCode;
102+
103+
@override
104+
bool operator ==(Object other) {
105+
if (identical(this, other)) return true;
106+
107+
return other is PrimitiveConstant<T> && other.value == value;
108+
}
109+
110+
@override
111+
Map<String, Object?> toJson(Map<Constant, int> constants) => valueToJson();
112+
113+
/// Converts this primitive constant to a JSON representation.
114+
Map<String, Object?> valueToJson();
115+
}
116+
117+
/// Represents a constant boolean value.
118+
final class BoolConstant extends PrimitiveConstant<bool> {
119+
/// The type identifier for JSON serialization.
120+
static const _type = 'bool';
121+
122+
/// Creates a [BoolConstant] object with the given boolean [value].
123+
const BoolConstant(super.value);
124+
125+
@override
126+
Map<String, Object?> valueToJson() => _toJson(_type, value);
127+
}
128+
129+
/// Represents a constant integer value.
130+
final class IntConstant extends PrimitiveConstant<int> {
131+
/// The type identifier for JSON serialization.
132+
static const _type = 'int';
133+
134+
/// Creates an [IntConstant] object with the given integer [value].
135+
const IntConstant(super.value);
136+
137+
@override
138+
Map<String, Object?> valueToJson() => _toJson(_type, value);
139+
}
140+
141+
/// Represents a constant string value.
142+
final class StringConstant extends PrimitiveConstant<String> {
143+
/// The type identifier for JSON serialization.
144+
static const _type = 'String';
145+
146+
/// Creates a [StringConstant] object with the given string [value].
147+
const StringConstant(super.value);
148+
149+
@override
150+
Map<String, Object?> valueToJson() => _toJson(_type, value);
151+
}
152+
153+
/// Represents a constant list of [Constant] values.
154+
final class ListConstant<T extends Constant> extends Constant {
155+
/// The type identifier for JSON serialization.
156+
static const _type = 'list';
157+
158+
/// The underlying list of constant values.
159+
final List<T> value;
160+
161+
/// Creates a [ListConstant] object with the given list of [value]s.
162+
const ListConstant(this.value);
163+
164+
@override
165+
int get hashCode => deepHash(value);
166+
167+
@override
168+
bool operator ==(Object other) {
169+
if (identical(this, other)) return true;
170+
171+
return other is ListConstant && deepEquals(other.value, value);
172+
}
173+
174+
@override
175+
Map<String, Object?> toJson(Map<Constant, int> constants) =>
176+
_toJson(_type, value.map((constant) => constants[constant]).toList());
177+
}
178+
179+
/// Represents a constant map from string keys to [Constant] values.
180+
final class MapConstant<T extends Constant> extends Constant {
181+
/// The type identifier for JSON serialization.
182+
static const _type = 'map';
183+
184+
/// The underlying map of constant values.
185+
final Map<String, T> value;
186+
187+
/// Creates a [MapConstant] object with the given map of [value]s.
188+
const MapConstant(this.value);
189+
190+
@override
191+
int get hashCode => deepHash(value);
192+
193+
@override
194+
bool operator ==(Object other) {
195+
if (identical(this, other)) return true;
196+
197+
return other is MapConstant && deepEquals(other.value, value);
198+
}
199+
200+
@override
201+
Map<String, Object?> toJson(Map<Constant, int> constants) => _toJson(
202+
_type,
203+
value.map((key, constant) => MapEntry(key, constants[constant]!)),
204+
);
205+
}
206+
207+
/// A constant instance of a class with its fields
208+
///
209+
/// Only as far as they can also be represented by constants. This is more or
210+
/// less the same as a [MapConstant].
211+
final class InstanceConstant extends Constant {
212+
/// The type identifier for JSON serialization.
213+
static const _type = 'Instance';
214+
215+
/// The fields of this instance, mapped from field name to [Constant] value.
216+
final Map<String, Constant> fields;
217+
218+
/// Creates an [InstanceConstant] object with the given [fields].
219+
const InstanceConstant({required this.fields});
220+
221+
/// Creates an [InstanceConstant] object from JSON.
222+
///
223+
/// [json] is a map representing the JSON structure.
224+
/// [constants] is a list of [Constant] objects that are referenced by index
225+
/// in the JSON.
226+
factory InstanceConstant.fromJson(
227+
Map<String, Object?> json,
228+
List<Constant> constants,
229+
) {
230+
return InstanceConstant(
231+
fields: json.map(
232+
(key, constantIndex) => MapEntry(key, constants[constantIndex as int]),
233+
),
234+
);
235+
}
236+
237+
@override
238+
Map<String, Object?> toJson(Map<Constant, int> constants) => _toJson(
239+
_type,
240+
fields.isNotEmpty
241+
? fields.map(
242+
(name, constantIndex) => MapEntry(name, constants[constantIndex]!),
243+
)
244+
: null,
245+
);
246+
247+
@override
248+
bool operator ==(Object other) {
249+
if (identical(this, other)) return true;
250+
251+
return other is InstanceConstant && deepEquals(other.fields, fields);
252+
}
253+
254+
@override
255+
int get hashCode => deepHash(fields);
256+
}
257+
258+
/// Helper to create the JSON structure of constants by storing the value with
259+
/// the type.
260+
Map<String, Object?> _toJson(String type, Object? value) {
261+
return {_typeKey: type, if (value != null) _valueKey: value};
262+
}

0 commit comments

Comments
 (0)