[deps] Roll dart-lang/native
This roll moves `package:record_use` to the dart-lang/native repo. Change-Id: I31183dc8b72272d7e94ed3031ca0b8bfca583e0d Cq-Include-Trybots: luci.dart.try:pkg-linux-debug-try,pkg-linux-release-arm64-try,pkg-linux-release-try,pkg-mac-release-arm64-try,pkg-mac-release-try,pkg-win-release-arm64-try,pkg-win-release-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/463662 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Michael Goderbauer <goderbauer@google.com>
This commit is contained in:
committed by
Commit Queue
parent
2c804fa251
commit
0d052e3969
@@ -140,7 +140,7 @@ vars = {
|
||||
"i18n_rev": "dd8a792a8492370a594706c8304d2eb8db844d7a",
|
||||
"leak_tracker_rev": "f5620600a5ce1c44f65ddaa02001e200b096e14c", # rolled manually
|
||||
"material_color_utilities_rev": "799b6ba2f3f1c28c67cc7e0b4f18e0c7d7f3c03e",
|
||||
"native_rev": "3ec573500f743d4a1393f7802143aef50fec0a47", # rolled manually while native assets are experimental
|
||||
"native_rev": "43a691c2152dd9b699259e29356d10edab17ec14", # rolled manually while record_use is experimental
|
||||
"protobuf_rev": "3ed04a91ff4de8493226e8fd0d89678c2986edf6",
|
||||
"pub_rev": "f7f1891e2de3d795532f45ec214f88ac912ffcd6", # rolled manually
|
||||
"shelf_rev": "b924de80e122d1e17b1132bedb02ddb1445b337c",
|
||||
|
||||
@@ -114,7 +114,8 @@ Future<void> copyTestProjects(Uri copyTargetUri, Logger logger,
|
||||
'code_assets',
|
||||
'data_assets',
|
||||
'hooks',
|
||||
'native_toolchain_c'
|
||||
'native_toolchain_c',
|
||||
'record_use',
|
||||
])
|
||||
package: {
|
||||
'path': sdkRoot
|
||||
@@ -124,9 +125,6 @@ Future<void> copyTestProjects(Uri copyTargetUri, Logger logger,
|
||||
'meta': {
|
||||
'path': sdkRoot.resolve('pkg/meta/').toFilePath(),
|
||||
},
|
||||
'record_use': {
|
||||
'path': sdkRoot.resolve('pkg/record_use/').toFilePath(),
|
||||
},
|
||||
};
|
||||
final userDefinesWorkspace = {};
|
||||
for (final pubspecPath in pubspecPaths) {
|
||||
@@ -285,7 +283,7 @@ Future<void> recordUseTest(
|
||||
packageUnderTest,
|
||||
fun,
|
||||
const ['drop_dylib_recording', 'drop_data_asset'],
|
||||
sdkRootUri.resolve('pkg/record_use/'),
|
||||
sdkRootUri.resolve('third_party/pkg/native/pkgs/record_use/'),
|
||||
sdkRootUri,
|
||||
false,
|
||||
);
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
## 0.4.2
|
||||
|
||||
- Fix empty instance parsing.
|
||||
|
||||
## 0.4.1
|
||||
|
||||
- Fix bug in signature parsing.
|
||||
|
||||
## 0.4.0
|
||||
|
||||
- Update SDK constraint to `^3.5.0`.
|
||||
- Rewrite API to expose less symbols.
|
||||
- Remove locations for easier caching.
|
||||
|
||||
## 0.3.0
|
||||
|
||||
- Make `InstanceConstant` a `Constant`.
|
||||
- Separate import from location uri.
|
||||
|
||||
## 0.2.0
|
||||
|
||||
- Use maps instead of lists in serialization.
|
||||
|
||||
## 0.1.1
|
||||
|
||||
- Fix repository link.
|
||||
|
||||
## 0.1.0
|
||||
|
||||
- Initial version.
|
||||
@@ -1,27 +0,0 @@
|
||||
Copyright 2024, the Dart project authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -1,5 +0,0 @@
|
||||
set noparent
|
||||
file:/tools/OWNERS_ECOSYSTEM
|
||||
file:/tools/OWNERS_VM
|
||||
# Global owners
|
||||
file:/OWNERS
|
||||
@@ -1,117 +0,0 @@
|
||||
> [!CAUTION]
|
||||
> This is an experimental package, and it's API can break at any time. Use at
|
||||
> your own discretion.
|
||||
|
||||
This package provides the data classes for the usage recording feature in the
|
||||
Dart SDK.
|
||||
|
||||
Dart objects with the `@RecordUse` annotation are being recorded at compile
|
||||
time, providing the user with information. The information depends on the object
|
||||
being recorded.
|
||||
|
||||
- If placed on a static method, the annotation means that arguments passed to
|
||||
the method will be recorded, as far as they can be inferred at compile time.
|
||||
- If placed on a class with a constant constructor, the annotation means that
|
||||
any constant instance of the class will be recorded. This is particularly useful
|
||||
when using the class as an annotation.
|
||||
|
||||
## Example
|
||||
|
||||
```dart
|
||||
import 'package:meta/meta.dart' show RecordUse;
|
||||
|
||||
void main() {
|
||||
print(SomeClass.stringMetadata(42));
|
||||
print(SomeClass.doubleMetadata(42));
|
||||
print(SomeClass.intMetadata(42));
|
||||
print(SomeClass.boolMetadata(42));
|
||||
}
|
||||
|
||||
class SomeClass {
|
||||
@RecordMetadata('leroyjenkins')
|
||||
@RecordUse()
|
||||
static stringMetadata(int i) {
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
@RecordMetadata(3.14)
|
||||
@RecordUse()
|
||||
static doubleMetadata(int i) {
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
@RecordMetadata(42)
|
||||
@RecordUse()
|
||||
static intMetadata(int i) {
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
@RecordMetadata(true)
|
||||
@RecordUse()
|
||||
static boolMetadata(int i) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@RecordUse()
|
||||
class RecordMetadata {
|
||||
final Object metadata;
|
||||
|
||||
const RecordMetadata(this.metadata);
|
||||
}
|
||||
|
||||
```
|
||||
This code will generate a data file that contains both the `metadata` values of
|
||||
the `RecordMetadata` instances, as well as the arguments for the different
|
||||
methods annotated with `@RecordUse()`.
|
||||
|
||||
This information can then be accessed in a link hook as follows:
|
||||
```dart
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:hooks/hooks.dart';
|
||||
import 'package:record_use/record_use_internal.dart';
|
||||
|
||||
final methodId = Identifier(
|
||||
uri: 'myfile.dart',
|
||||
name: 'myMethod',
|
||||
);
|
||||
|
||||
final classId = Identifier(
|
||||
uri: 'myfile.dart',
|
||||
name: 'myClass',
|
||||
);
|
||||
|
||||
void main(List<String> arguments){
|
||||
link(arguments, (config, output) async {
|
||||
final usesUri = config.recordedUses;
|
||||
final usesJson = await File,fromUri(usesUri).readAsString();
|
||||
final uses = UsageRecord.fromJson(jsonDecode(usesJson));
|
||||
|
||||
final args = uses.argumentsTo(methodId));
|
||||
//[args] is an iterable of arguments, in this case containing "42"
|
||||
|
||||
final fields = uses.instancesOf(classId);
|
||||
//[fields] is an iterable of the fields of the class, in this case
|
||||
//containing
|
||||
// {"arguments": "leroyjenkins"}
|
||||
// {"arguments": 3.14}
|
||||
// {"arguments": 42}
|
||||
// {"arguments": true}
|
||||
|
||||
... // Do something with the information, such as tree-shaking native assets
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Limitations
|
||||
As this is designed to work on both web and native platforms, we have to adapt
|
||||
to the platform pecularities. One of them is that javascript does not support
|
||||
named arguments, so the dart2js compiler rewrites functions to only accept named
|
||||
parameters.
|
||||
While you can use named parameters to record functions, we advise caution as the
|
||||
retrieval behavior might change once we work around this dart2js limitation and
|
||||
implement separate positional and named parameters.
|
||||
|
||||
## Contributing
|
||||
Contributions are welcome! Please open an issue or submit a pull request.
|
||||
@@ -1,4 +0,0 @@
|
||||
include: package:dart_flutter_team_lints/analysis_options.yaml
|
||||
|
||||
analyzer:
|
||||
exclude: [test_data/**]
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright (c) 2024, 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:record_use/record_use.dart';
|
||||
|
||||
void doStuffInLinkHook(
|
||||
RecordedUsages usage,
|
||||
Identifier identifier1,
|
||||
Identifier identifier2,
|
||||
Identifier identifier3,
|
||||
) {
|
||||
print(usage.metadata);
|
||||
print(usage.constArgumentsFor(identifier1));
|
||||
print(usage.constantsOf(identifier2));
|
||||
print(usage.hasNonConstArguments(identifier3));
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright (c) 2024, 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.
|
||||
|
||||
export 'src/identifier.dart' show Identifier;
|
||||
export 'src/metadata.dart' show Metadata, MetadataExt;
|
||||
export 'src/record_use.dart' show ConstantInstance, RecordedUsages;
|
||||
export 'src/recorded_usage_from_file.dart' show parseFromFile;
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright (c) 2024, 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.
|
||||
|
||||
export 'src/constant.dart'
|
||||
show
|
||||
BoolConstant,
|
||||
Constant,
|
||||
InstanceConstant,
|
||||
IntConstant,
|
||||
ListConstant,
|
||||
MapConstant,
|
||||
NullConstant,
|
||||
PrimitiveConstant,
|
||||
StringConstant;
|
||||
export 'src/definition.dart' show Definition;
|
||||
export 'src/identifier.dart' show Identifier;
|
||||
export 'src/location.dart' show Location;
|
||||
export 'src/metadata.dart' show Metadata, MetadataExt;
|
||||
export 'src/record_use.dart' show RecordedUsages;
|
||||
export 'src/recordings.dart'
|
||||
show FlattenConstantsExtension, MapifyIterableExtension, Recordings;
|
||||
export 'src/reference.dart'
|
||||
show CallReference, CallTearOff, CallWithArguments, InstanceReference;
|
||||
export 'src/version.dart' show version;
|
||||
@@ -1,262 +0,0 @@
|
||||
// Copyright (c) 2024, 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 'helper.dart';
|
||||
|
||||
const _typeKey = 'type';
|
||||
const _valueKey = 'value';
|
||||
|
||||
/// A constant value that can be recorded and serialized.
|
||||
///
|
||||
/// This supports basic constants such as [bool]s or [int]s, as well as
|
||||
/// [ListConstant], [MapConstant] or [InstanceConstant] for more complex
|
||||
/// structures.
|
||||
///
|
||||
/// This follows the AST constant concept from the Dart SDK.
|
||||
sealed class Constant {
|
||||
/// Creates a [Constant] object.
|
||||
const Constant();
|
||||
|
||||
/// Converts this [Constant] object to a JSON representation.
|
||||
///
|
||||
/// [constants] needs to be passed, as the [Constant]s are normalized and
|
||||
/// stored separately in the JSON.
|
||||
Map<String, Object?> toJson(Map<Constant, int> constants);
|
||||
|
||||
/// Converts this [Constant] to the value it represents.
|
||||
Object? toValue() => switch (this) {
|
||||
NullConstant() => null,
|
||||
PrimitiveConstant p => p.value,
|
||||
ListConstant<Constant> l => l.value.map((c) => c.toValue()).toList(),
|
||||
MapConstant<Constant> m => m.value.map(
|
||||
(key, value) => MapEntry(key, value.toValue()),
|
||||
),
|
||||
InstanceConstant i => i.fields.map(
|
||||
(key, value) => MapEntry(key, value.toValue()),
|
||||
),
|
||||
};
|
||||
|
||||
/// Creates a [Constant] object from its JSON representation.
|
||||
///
|
||||
/// [constants] needs to be passed, as the [Constant]s are normalized and
|
||||
/// stored separately in the JSON.
|
||||
static Constant fromJson(
|
||||
Map<String, Object?> value,
|
||||
List<Constant> constants,
|
||||
) => switch (value[_typeKey] as String) {
|
||||
NullConstant._type => const NullConstant(),
|
||||
BoolConstant._type => BoolConstant(value[_valueKey] as bool),
|
||||
IntConstant._type => IntConstant(value[_valueKey] as int),
|
||||
StringConstant._type => StringConstant(value[_valueKey] as String),
|
||||
ListConstant._type => ListConstant(
|
||||
(value[_valueKey] as List<dynamic>)
|
||||
.map((value) => value as int)
|
||||
.map((value) => constants[value])
|
||||
.toList(),
|
||||
),
|
||||
MapConstant._type => MapConstant(
|
||||
(value[_valueKey] as Map<String, Object?>).map(
|
||||
(key, value) => MapEntry(key, constants[value as int]),
|
||||
),
|
||||
),
|
||||
InstanceConstant._type => InstanceConstant(
|
||||
fields: (value[_valueKey] as Map<String, Object?>? ?? {}).map(
|
||||
(key, value) => MapEntry(key, constants[value as int]),
|
||||
),
|
||||
),
|
||||
String() =>
|
||||
throw UnimplementedError('This type is not a supported constant'),
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents the `null` constant value.
|
||||
final class NullConstant extends Constant {
|
||||
/// The type identifier for JSON serialization.
|
||||
static const _type = 'Null';
|
||||
|
||||
/// Creates a [NullConstant] object.
|
||||
const NullConstant() : super();
|
||||
|
||||
@override
|
||||
Map<String, Object?> toJson(Map<Constant, int> constants) =>
|
||||
_toJson(_type, null);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => other is NullConstant;
|
||||
|
||||
@override
|
||||
int get hashCode => 0;
|
||||
}
|
||||
|
||||
/// Represents a constant value of a primitive type.
|
||||
sealed class PrimitiveConstant<T extends Object> extends Constant {
|
||||
/// The underlying value of this constant.
|
||||
final T value;
|
||||
|
||||
/// Creates a [PrimitiveConstant] object with the given [value].
|
||||
const PrimitiveConstant(this.value);
|
||||
|
||||
@override
|
||||
int get hashCode => value.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is PrimitiveConstant<T> && other.value == value;
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Object?> toJson(Map<Constant, int> constants) => valueToJson();
|
||||
|
||||
/// Converts this primitive constant to a JSON representation.
|
||||
Map<String, Object?> valueToJson();
|
||||
}
|
||||
|
||||
/// Represents a constant boolean value.
|
||||
final class BoolConstant extends PrimitiveConstant<bool> {
|
||||
/// The type identifier for JSON serialization.
|
||||
static const _type = 'bool';
|
||||
|
||||
/// Creates a [BoolConstant] object with the given boolean [value].
|
||||
const BoolConstant(super.value);
|
||||
|
||||
@override
|
||||
Map<String, Object?> valueToJson() => _toJson(_type, value);
|
||||
}
|
||||
|
||||
/// Represents a constant integer value.
|
||||
final class IntConstant extends PrimitiveConstant<int> {
|
||||
/// The type identifier for JSON serialization.
|
||||
static const _type = 'int';
|
||||
|
||||
/// Creates an [IntConstant] object with the given integer [value].
|
||||
const IntConstant(super.value);
|
||||
|
||||
@override
|
||||
Map<String, Object?> valueToJson() => _toJson(_type, value);
|
||||
}
|
||||
|
||||
/// Represents a constant string value.
|
||||
final class StringConstant extends PrimitiveConstant<String> {
|
||||
/// The type identifier for JSON serialization.
|
||||
static const _type = 'String';
|
||||
|
||||
/// Creates a [StringConstant] object with the given string [value].
|
||||
const StringConstant(super.value);
|
||||
|
||||
@override
|
||||
Map<String, Object?> valueToJson() => _toJson(_type, value);
|
||||
}
|
||||
|
||||
/// Represents a constant list of [Constant] values.
|
||||
final class ListConstant<T extends Constant> extends Constant {
|
||||
/// The type identifier for JSON serialization.
|
||||
static const _type = 'list';
|
||||
|
||||
/// The underlying list of constant values.
|
||||
final List<T> value;
|
||||
|
||||
/// Creates a [ListConstant] object with the given list of [value]s.
|
||||
const ListConstant(this.value);
|
||||
|
||||
@override
|
||||
int get hashCode => deepHash(value);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is ListConstant && deepEquals(other.value, value);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Object?> toJson(Map<Constant, int> constants) =>
|
||||
_toJson(_type, value.map((constant) => constants[constant]).toList());
|
||||
}
|
||||
|
||||
/// Represents a constant map from string keys to [Constant] values.
|
||||
final class MapConstant<T extends Constant> extends Constant {
|
||||
/// The type identifier for JSON serialization.
|
||||
static const _type = 'map';
|
||||
|
||||
/// The underlying map of constant values.
|
||||
final Map<String, T> value;
|
||||
|
||||
/// Creates a [MapConstant] object with the given map of [value]s.
|
||||
const MapConstant(this.value);
|
||||
|
||||
@override
|
||||
int get hashCode => deepHash(value);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is MapConstant && deepEquals(other.value, value);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Object?> toJson(Map<Constant, int> constants) => _toJson(
|
||||
_type,
|
||||
value.map((key, constant) => MapEntry(key, constants[constant]!)),
|
||||
);
|
||||
}
|
||||
|
||||
/// A constant instance of a class with its fields
|
||||
///
|
||||
/// Only as far as they can also be represented by constants. This is more or
|
||||
/// less the same as a [MapConstant].
|
||||
final class InstanceConstant extends Constant {
|
||||
/// The type identifier for JSON serialization.
|
||||
static const _type = 'Instance';
|
||||
|
||||
/// The fields of this instance, mapped from field name to [Constant] value.
|
||||
final Map<String, Constant> fields;
|
||||
|
||||
/// Creates an [InstanceConstant] object with the given [fields].
|
||||
const InstanceConstant({required this.fields});
|
||||
|
||||
/// Creates an [InstanceConstant] object from JSON.
|
||||
///
|
||||
/// [json] is a map representing the JSON structure.
|
||||
/// [constants] is a list of [Constant] objects that are referenced by index
|
||||
/// in the JSON.
|
||||
factory InstanceConstant.fromJson(
|
||||
Map<String, Object?> json,
|
||||
List<Constant> constants,
|
||||
) {
|
||||
return InstanceConstant(
|
||||
fields: json.map(
|
||||
(key, constantIndex) => MapEntry(key, constants[constantIndex as int]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Object?> toJson(Map<Constant, int> constants) => _toJson(
|
||||
_type,
|
||||
fields.isNotEmpty
|
||||
? fields.map(
|
||||
(name, constantIndex) => MapEntry(name, constants[constantIndex]!),
|
||||
)
|
||||
: null,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is InstanceConstant && deepEquals(other.fields, fields);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => deepHash(fields);
|
||||
}
|
||||
|
||||
/// Helper to create the JSON structure of constants by storing the value with
|
||||
/// the type.
|
||||
Map<String, Object?> _toJson(String type, Object? value) {
|
||||
return {_typeKey: type, if (value != null) _valueKey: value};
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright (c) 2024, 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 'identifier.dart' show Identifier;
|
||||
|
||||
/// A defintion is an [identifier] with its [loadingUnit].
|
||||
class Definition {
|
||||
final Identifier identifier;
|
||||
final String? loadingUnit;
|
||||
|
||||
const Definition({required this.identifier, this.loadingUnit});
|
||||
|
||||
static const _identifierKey = 'identifier';
|
||||
static const _loadingUnitKey = 'loading_unit';
|
||||
|
||||
factory Definition.fromJson(Map<String, Object?> json) {
|
||||
return Definition(
|
||||
identifier: Identifier.fromJson(
|
||||
json[_identifierKey] as Map<String, Object?>,
|
||||
),
|
||||
loadingUnit: json[_loadingUnitKey] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
_identifierKey: identifier.toJson(),
|
||||
if (loadingUnit != null) _loadingUnitKey: loadingUnit,
|
||||
};
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is Definition &&
|
||||
other.identifier == identifier &&
|
||||
other.loadingUnit == loadingUnit;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(identifier, loadingUnit);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) 2024, 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:collection/collection.dart';
|
||||
|
||||
final deepEquals = const DeepCollectionEquality().equals;
|
||||
|
||||
final deepHash = const DeepCollectionEquality().hash;
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (c) 2024, 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.
|
||||
|
||||
/// Represents a unique identifier for a code element, such as a class, method,
|
||||
/// or field, within a Dart program.
|
||||
///
|
||||
/// An [Identifier] is used to pinpoint a specific element based on its
|
||||
/// location and name. It consists of:
|
||||
///
|
||||
/// - `importUri`: The URI of the library where the element is defined.
|
||||
/// - `parent`: The name of the parent element (e.g., the class name for a
|
||||
/// method or field). This is optional, as not all elements have parents (e.g.
|
||||
/// top-level functions).
|
||||
/// - `name`: The name of the element itself.
|
||||
class Identifier {
|
||||
/// The URI of the library where the element is defined.
|
||||
///
|
||||
/// This is given in the form of its package import uri, so that it is OS- and
|
||||
/// user independent.
|
||||
final String importUri;
|
||||
|
||||
/// The name of the parent element (e.g., the class name for a method or
|
||||
/// field). This is optional, as not all elements have parents (e.g. top-level
|
||||
/// functions).
|
||||
final String? scope;
|
||||
|
||||
/// The name of the element itself.
|
||||
final String name;
|
||||
|
||||
/// Creates an [Identifier] object.
|
||||
///
|
||||
/// [importUri] is the URI of the library where the element is defined.
|
||||
/// [scope] is the optional name of the parent element.
|
||||
/// [name] is the name of the element.
|
||||
const Identifier({required this.importUri, this.scope, required this.name});
|
||||
|
||||
static const String _uriKey = 'uri';
|
||||
static const String _scopeKey = 'scope';
|
||||
static const String _nameKey = 'name';
|
||||
|
||||
/// Creates an [Identifier] object from its JSON representation.
|
||||
factory Identifier.fromJson(Map<String, Object?> json) => Identifier(
|
||||
importUri: json[_uriKey] as String,
|
||||
scope: json[_scopeKey] as String?,
|
||||
name: json[_nameKey] as String,
|
||||
);
|
||||
|
||||
/// Converts this [Identifier] object to a JSON representation.
|
||||
Map<String, Object?> toJson() => {
|
||||
_uriKey: importUri,
|
||||
if (scope != null) _scopeKey: scope,
|
||||
_nameKey: name,
|
||||
};
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is Identifier &&
|
||||
other.importUri == importUri &&
|
||||
other.scope == scope &&
|
||||
other.name == name;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(importUri, scope, name);
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright (c) 2024, 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 Location {
|
||||
final String uri;
|
||||
final int? line;
|
||||
final int? column;
|
||||
|
||||
const Location({required this.uri, this.line, this.column});
|
||||
|
||||
static const _uriKey = 'uri';
|
||||
static const _lineKey = 'line';
|
||||
static const _columnKey = 'column';
|
||||
|
||||
factory Location.fromJson(Map<String, Object?> map) {
|
||||
return Location(
|
||||
uri: map[_uriKey] as String,
|
||||
line: map[_lineKey] as int?,
|
||||
column: map[_columnKey] as int?,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, Object?> toJson() {
|
||||
return {
|
||||
_uriKey: uri,
|
||||
if (line != null) _lineKey: line,
|
||||
if (line != null) _columnKey: column,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is Location &&
|
||||
other.uri == uri &&
|
||||
other.line == line &&
|
||||
other.column == column;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(uri, line, column);
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright (c) 2024, 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:pub_semver/pub_semver.dart';
|
||||
|
||||
import 'helper.dart';
|
||||
|
||||
/// Metadata attached to a recorded usages file.
|
||||
///
|
||||
/// Whatever [Metadata] should be added to the usage recording. Care should be
|
||||
/// applied to not include non-deterministic or dynamic data such as timestamps,
|
||||
/// as this would mess with the usage recording caching.
|
||||
class Metadata {
|
||||
/// The underlying data.
|
||||
///
|
||||
/// Together with the metadata extension [MetadataExt], this makes the
|
||||
/// metadata extensible by the user implementing the recording. For example,
|
||||
/// dart2js might want to store different metadata than the Dart VM.
|
||||
final Map<String, Object?> json;
|
||||
|
||||
const Metadata._({required this.json});
|
||||
|
||||
factory Metadata.fromJson(Map<String, Object?> json) =>
|
||||
Metadata._(json: json);
|
||||
|
||||
@override
|
||||
bool operator ==(covariant Metadata other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return deepEquals(other.json, json);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => deepHash(json);
|
||||
}
|
||||
|
||||
extension MetadataExt on Metadata {
|
||||
Version get version => Version.parse(json['version'] as String);
|
||||
String get comment => json['comment'] as String;
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
// Copyright (c) 2024, 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 '../record_use_internal.dart';
|
||||
|
||||
/// Holds all information recorded during compilation.
|
||||
///
|
||||
/// This can be queried using the methods provided, which each take an
|
||||
/// [Identifier] which must be annotated with `@RecordUse` from `package:meta`.
|
||||
extension type RecordedUsages._(Recordings _recordings) {
|
||||
RecordedUsages.fromJson(Map<String, Object?> json)
|
||||
: this._(Recordings.fromJson(json));
|
||||
|
||||
/// Show the metadata for this recording of usages.
|
||||
Metadata get metadata => _recordings.metadata;
|
||||
|
||||
/// Finds all const arguments for calls to the [identifier].
|
||||
///
|
||||
/// The definition must be annotated with `@RecordUse()`. If there are no
|
||||
/// calls to the definition, either because it was treeshaken, because it was
|
||||
/// not annotated, or because it does not exist, returns empty.
|
||||
///
|
||||
/// Returns an empty iterable if the arguments were not collected.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// import 'package:meta/meta.dart' show ResourceIdentifier;
|
||||
/// void main() {
|
||||
/// print(SomeClass.someStaticMethod(42));
|
||||
/// }
|
||||
///
|
||||
/// class SomeClass {
|
||||
/// @ResourceIdentifier('id')
|
||||
/// static someStaticMethod(int i) {
|
||||
/// return i + 1;
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Would mean that
|
||||
/// ```
|
||||
/// argumentsTo(Identifier(
|
||||
/// uri: 'path/to/file.dart',
|
||||
/// parent: 'SomeClass',
|
||||
/// name: 'someStaticMethod'),
|
||||
/// ).first ==
|
||||
/// [
|
||||
/// {1: 42}
|
||||
/// ]
|
||||
/// ```
|
||||
Iterable<({Map<String, Object?> named, List<Object?> positional})>
|
||||
constArgumentsFor(Identifier identifier) {
|
||||
return _recordings.calls[identifier]?.whereType<CallWithArguments>().map(
|
||||
(call) => (
|
||||
named: call.namedArguments.map(
|
||||
(name, argument) => MapEntry(name, argument?.toValue()),
|
||||
),
|
||||
positional:
|
||||
call.positionalArguments
|
||||
.map((argument) => argument?.toValue())
|
||||
.toList(),
|
||||
),
|
||||
) ??
|
||||
[];
|
||||
}
|
||||
|
||||
/// Finds all constant fields of a const instance of the class [identifier].
|
||||
///
|
||||
/// The definition must be annotated with `@RecordUse()`. If there are
|
||||
/// no instances of the definition, either because it was treeshaken, because
|
||||
/// it was not annotated, or because it does not exist, returns empty.
|
||||
///
|
||||
/// The types of fields supported are defined at
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// void main() {
|
||||
/// print(SomeClass.someStaticMethod(42));
|
||||
/// }
|
||||
///
|
||||
/// class SomeClass {
|
||||
/// @AnnotationClass('freddie')
|
||||
/// static someStaticMethod(int i) {
|
||||
/// return i + 1;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// @RecordUse()
|
||||
/// class AnnotationClass {
|
||||
/// final String s;
|
||||
/// const AnnotationClass(this.s);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Would mean that
|
||||
/// ```
|
||||
/// constantsOf(Identifier(
|
||||
/// uri: 'path/to/file.dart',
|
||||
/// name: 'AnnotationClass'),
|
||||
/// ).first['s'] == 'freddie';
|
||||
/// ```
|
||||
///
|
||||
/// What kinds of fields can be recorded depends on the implementation of
|
||||
/// https://dart-review.googlesource.com/c/sdk/+/369620/13/pkg/vm/lib/transformations/record_use/record_instance.dart
|
||||
Iterable<ConstantInstance> constantsOf(Identifier identifier) {
|
||||
return _recordings.instances[identifier]?.map(
|
||||
(reference) => ConstantInstance(reference.instanceConstant.fields),
|
||||
) ??
|
||||
[];
|
||||
}
|
||||
|
||||
/// Checks if any call to [identifier] has non-const arguments, or if any
|
||||
/// tear-off was recorded.
|
||||
///
|
||||
/// The definition must be annotated with `@RecordUse()`. If there are no
|
||||
/// calls to the definition, either because it was treeshaken, because it was
|
||||
/// not annotated, or because it does not exist, returns `false`.
|
||||
bool hasNonConstArguments(Identifier identifier) {
|
||||
return (_recordings.calls[identifier] ?? []).any(
|
||||
(element) => switch (element) {
|
||||
CallTearOff() => true,
|
||||
CallWithArguments call => call.positionalArguments.any(
|
||||
(argument) => argument == null,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension type ConstantInstance(Map<String, Constant> _fields) {
|
||||
bool hasField(String key) => _fields.containsKey(key);
|
||||
|
||||
Object? operator [](String key) {
|
||||
if (!hasField(key)) {
|
||||
throw ArgumentError('No field with name $key found.');
|
||||
}
|
||||
return _fields[key]!.toValue();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import 'dart:convert' show jsonDecode;
|
||||
import 'dart:io' show File;
|
||||
|
||||
import '../record_use_internal.dart' show RecordedUsages;
|
||||
|
||||
RecordedUsages? parseFromFile(Uri? recordedUsagesFile) {
|
||||
if (recordedUsagesFile == null) {
|
||||
return null;
|
||||
}
|
||||
final usagesContent = File.fromUri(recordedUsagesFile).readAsStringSync();
|
||||
final usagesJson = jsonDecode(usagesContent) as Map<String, dynamic>;
|
||||
return RecordedUsages.fromJson(usagesJson);
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
// Copyright (c) 2024, 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 'constant.dart';
|
||||
import 'definition.dart';
|
||||
import 'helper.dart';
|
||||
import 'identifier.dart';
|
||||
import 'location.dart' show Location;
|
||||
import 'metadata.dart';
|
||||
import 'reference.dart';
|
||||
|
||||
/// [Recordings] combines recordings of calls and instances with metadata.
|
||||
///
|
||||
/// This class acts as the top-level container for recorded usage information.
|
||||
/// The metadata provides context for the recording, such as version and
|
||||
/// commentary. The [callsForDefinition] and [instancesForDefinition] store the
|
||||
/// core data, associating each [Definition] with its corresponding [Reference]
|
||||
/// details.
|
||||
///
|
||||
/// The class uses a normalized JSON format, allowing the reuse of locations and
|
||||
/// constants across multiple recordings to optimize storage.
|
||||
class Recordings {
|
||||
/// [Metadata] such as the recording protocol version.
|
||||
final Metadata metadata;
|
||||
|
||||
/// The collected [CallReference]s for each [Definition].
|
||||
final Map<Definition, List<CallReference>> callsForDefinition;
|
||||
|
||||
late final Map<Identifier, List<CallReference>> calls = callsForDefinition
|
||||
.map((definition, calls) => MapEntry(definition.identifier, calls));
|
||||
|
||||
/// The collected [InstanceReference]s for each [Definition].
|
||||
final Map<Definition, List<InstanceReference>> instancesForDefinition;
|
||||
|
||||
late final Map<Identifier, List<InstanceReference>> instances =
|
||||
instancesForDefinition.map(
|
||||
(definition, instances) => MapEntry(definition.identifier, instances),
|
||||
);
|
||||
|
||||
static const _metadataKey = 'metadata';
|
||||
static const _constantsKey = 'constants';
|
||||
static const _locationsKey = 'locations';
|
||||
static const _recordingsKey = 'recordings';
|
||||
static const _callsKey = 'calls';
|
||||
static const _instancesKey = 'instances';
|
||||
static const _definitionKey = 'definition';
|
||||
|
||||
Recordings({
|
||||
required this.metadata,
|
||||
required this.callsForDefinition,
|
||||
required this.instancesForDefinition,
|
||||
});
|
||||
|
||||
/// Decodes a JSON representation into a [Recordings] object.
|
||||
///
|
||||
/// The format is specifically designed to reduce redundancy and improve
|
||||
/// efficiency. Identifiers and constants are stored in separate tables,
|
||||
/// allowing them to be referenced by index in the `recordings` map.
|
||||
factory Recordings.fromJson(Map<String, Object?> json) {
|
||||
if (json case {
|
||||
_constantsKey: List<Object?>? constantJsons,
|
||||
_locationsKey: List<Object?>? locationJsons,
|
||||
_recordingsKey: List<Object?>? recordingJsons,
|
||||
}) {
|
||||
final constants = <Constant>[];
|
||||
for (final constantJsonObj in constantJsons ?? []) {
|
||||
final constantJson = constantJsonObj as Map<String, Object?>;
|
||||
final constant = Constant.fromJson(constantJson, constants);
|
||||
if (!constants.contains(constant)) {
|
||||
constants.add(constant);
|
||||
}
|
||||
}
|
||||
final locations = <Location>[];
|
||||
for (final locationJsonObj in locationJsons ?? []) {
|
||||
final locationJson = locationJsonObj as Map<String, Object?>;
|
||||
final location = Location.fromJson(locationJson);
|
||||
if (!locations.contains(location)) {
|
||||
locations.add(location);
|
||||
}
|
||||
}
|
||||
|
||||
final recordings =
|
||||
recordingJsons?.whereType<Map<String, Object?>>() ?? [];
|
||||
|
||||
final recordedCalls = recordings.where(
|
||||
(recording) => recording[_callsKey] != null,
|
||||
);
|
||||
final recordedInstances = recordings.where(
|
||||
(recording) => recording[_instancesKey] != null,
|
||||
);
|
||||
|
||||
return Recordings(
|
||||
metadata: Metadata.fromJson(json[_metadataKey] as Map<String, Object?>),
|
||||
callsForDefinition: {
|
||||
for (final recording in recordedCalls)
|
||||
Definition.fromJson(
|
||||
recording[_definitionKey] as Map<String, Object?>,
|
||||
):
|
||||
(recording[_callsKey] as List)
|
||||
.map(
|
||||
(json) => CallReference.fromJson(
|
||||
json as Map<String, Object?>,
|
||||
constants,
|
||||
locations,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
},
|
||||
instancesForDefinition: {
|
||||
for (final recording in recordedInstances)
|
||||
Definition.fromJson(
|
||||
recording[_definitionKey] as Map<String, Object?>,
|
||||
):
|
||||
(recording[_instancesKey] as List)
|
||||
.map(
|
||||
(json) => InstanceReference.fromJson(
|
||||
json as Map<String, Object?>,
|
||||
constants,
|
||||
locations,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
},
|
||||
);
|
||||
} else {
|
||||
throw ArgumentError('''
|
||||
Invalid JSON format for Recordings:
|
||||
${const JsonEncoder.withIndent(' ').convert(json)}
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes this object into a JSON representation.
|
||||
///
|
||||
/// This method normalizes identifiers and constants for storage efficiency.
|
||||
Map<String, Object?> toJson() {
|
||||
final constants =
|
||||
{
|
||||
...callsForDefinition.values
|
||||
.expand((calls) => calls)
|
||||
.whereType<CallWithArguments>()
|
||||
.expand(
|
||||
(call) => [
|
||||
...call.positionalArguments,
|
||||
...call.namedArguments.values,
|
||||
],
|
||||
)
|
||||
.nonNulls,
|
||||
...instancesForDefinition.values
|
||||
.expand((instances) => instances)
|
||||
.expand(
|
||||
(instance) => {
|
||||
...instance.instanceConstant.fields.values,
|
||||
instance.instanceConstant,
|
||||
},
|
||||
),
|
||||
}.flatten().asMapToIndices;
|
||||
final locations =
|
||||
{
|
||||
...callsForDefinition.values
|
||||
.expand((calls) => calls)
|
||||
.map((call) => call.location)
|
||||
.nonNulls,
|
||||
...instancesForDefinition.values
|
||||
.expand((instances) => instances)
|
||||
.map((instance) => instance.location)
|
||||
.nonNulls,
|
||||
}.asMapToIndices;
|
||||
return {
|
||||
_metadataKey: metadata.json,
|
||||
if (constants.isNotEmpty)
|
||||
_constantsKey:
|
||||
constants.keys
|
||||
.map((constant) => constant.toJson(constants))
|
||||
.toList(),
|
||||
if (locations.isNotEmpty)
|
||||
_locationsKey:
|
||||
locations.keys.map((location) => location.toJson()).toList(),
|
||||
if (callsForDefinition.isNotEmpty || instancesForDefinition.isNotEmpty)
|
||||
_recordingsKey: [
|
||||
if (callsForDefinition.isNotEmpty)
|
||||
...callsForDefinition.entries.map(
|
||||
(entry) => {
|
||||
_definitionKey: entry.key.toJson(),
|
||||
_callsKey:
|
||||
entry.value
|
||||
.map((call) => call.toJson(constants, locations))
|
||||
.toList(),
|
||||
},
|
||||
),
|
||||
if (instancesForDefinition.isNotEmpty)
|
||||
...instancesForDefinition.entries.map(
|
||||
(entry) => {
|
||||
_definitionKey: entry.key.toJson(),
|
||||
_instancesKey:
|
||||
entry.value
|
||||
.map(
|
||||
(instance) => instance.toJson(constants, locations),
|
||||
)
|
||||
.toList(),
|
||||
},
|
||||
),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant Recordings other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.metadata == metadata &&
|
||||
deepEquals(other.callsForDefinition, callsForDefinition) &&
|
||||
deepEquals(other.instancesForDefinition, instancesForDefinition);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
metadata.hashCode,
|
||||
deepHash(callsForDefinition),
|
||||
deepHash(instancesForDefinition),
|
||||
);
|
||||
}
|
||||
|
||||
extension FlattenConstantsExtension on Iterable<Constant> {
|
||||
Set<Constant> flatten() {
|
||||
final constants = <Constant>{};
|
||||
for (final constant in this) {
|
||||
depthFirstSearch(constant, constants);
|
||||
}
|
||||
return constants;
|
||||
}
|
||||
|
||||
void depthFirstSearch(Constant constant, Set<Constant> collected) {
|
||||
final children = switch (constant) {
|
||||
ListConstant<Constant>() => constant.value,
|
||||
MapConstant<Constant>() => constant.value.values,
|
||||
InstanceConstant() => constant.fields.values,
|
||||
_ => <Constant>[],
|
||||
};
|
||||
for (final child in children) {
|
||||
if (!collected.contains(child)) {
|
||||
depthFirstSearch(child, collected);
|
||||
}
|
||||
}
|
||||
collected.add(constant);
|
||||
}
|
||||
}
|
||||
|
||||
extension MapifyIterableExtension<T> on Iterable<T> {
|
||||
/// Transform list to map, faster than using list.indexOf
|
||||
Map<T, int> get asMapToIndices {
|
||||
var i = 0;
|
||||
return {for (final element in this) element: i++};
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
// Copyright (c) 2024, 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 'constant.dart';
|
||||
import 'helper.dart';
|
||||
import 'identifier.dart';
|
||||
import 'location.dart' show Location;
|
||||
|
||||
const _loadingUnitKey = 'loading_unit';
|
||||
|
||||
/// A reference to *something*.
|
||||
///
|
||||
/// The something might be a call or an instance, matching a [CallReference] or
|
||||
/// an [InstanceReference].
|
||||
/// All references have in common that they occur in a [loadingUnit], which we
|
||||
/// record to be able to piece together which loading units are "related", for
|
||||
/// example all needing the same asset.
|
||||
sealed class Reference {
|
||||
final String? loadingUnit;
|
||||
final Location? location;
|
||||
|
||||
const Reference({required this.loadingUnit, required this.location});
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is Reference &&
|
||||
other.loadingUnit == loadingUnit &&
|
||||
other.location == location;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(loadingUnit, location);
|
||||
|
||||
Map<String, Object?> toJson(
|
||||
Map<Constant, int> constants,
|
||||
Map<Location, int> locations,
|
||||
) => {_loadingUnitKey: loadingUnit, _locationKey: locations[location]};
|
||||
}
|
||||
|
||||
const _locationKey = '@';
|
||||
const _positionalKey = 'positional';
|
||||
const _namedKey = 'named';
|
||||
const _typeKey = 'type';
|
||||
|
||||
/// A reference to a call to some [Identifier].
|
||||
///
|
||||
/// This might be an actual call, in which case we record the arguments, or a
|
||||
/// tear-off, in which case we can't record the arguments.
|
||||
sealed class CallReference extends Reference {
|
||||
const CallReference({required super.loadingUnit, required super.location});
|
||||
|
||||
static CallReference fromJson(
|
||||
Map<String, Object?> json,
|
||||
List<Constant> constants,
|
||||
List<Location> locations,
|
||||
) {
|
||||
final loadingUnit = json[_loadingUnitKey] as String?;
|
||||
final locationIndex = json[_locationKey] as int?;
|
||||
final location = locationIndex == null ? null : locations[locationIndex];
|
||||
return json[_typeKey] == 'tearoff'
|
||||
? CallTearOff(loadingUnit: loadingUnit, location: location)
|
||||
: CallWithArguments(
|
||||
positionalArguments:
|
||||
(json[_positionalKey] as List<dynamic>? ?? [])
|
||||
.whereType<int?>()
|
||||
.map(
|
||||
(constantsIndex) =>
|
||||
constantsIndex != null
|
||||
? constants[constantsIndex]
|
||||
: null,
|
||||
)
|
||||
.toList(),
|
||||
namedArguments: (json[_namedKey] as Map<String, Object?>? ?? {})
|
||||
.map((key, value) => MapEntry(key, value as int?))
|
||||
.map(
|
||||
(name, constantsIndex) => MapEntry(
|
||||
name,
|
||||
constantsIndex != null ? constants[constantsIndex] : null,
|
||||
),
|
||||
),
|
||||
loadingUnit: loadingUnit,
|
||||
location: location,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a call to some [Identifier] with [positionalArguments] and
|
||||
/// [namedArguments].
|
||||
final class CallWithArguments extends CallReference {
|
||||
final List<Constant?> positionalArguments;
|
||||
final Map<String, Constant?> namedArguments;
|
||||
|
||||
const CallWithArguments({
|
||||
required this.positionalArguments,
|
||||
required this.namedArguments,
|
||||
required super.loadingUnit,
|
||||
required super.location,
|
||||
});
|
||||
|
||||
@override
|
||||
Map<String, Object?> toJson(
|
||||
Map<Constant, int> constants,
|
||||
Map<Location, int> locations,
|
||||
) {
|
||||
final positionalJson =
|
||||
positionalArguments.map((constant) => constants[constant]).toList();
|
||||
final namedJson = namedArguments.map(
|
||||
(name, constant) => MapEntry(name, constants[constant]),
|
||||
);
|
||||
return {
|
||||
_typeKey: 'with_arguments',
|
||||
if (positionalJson.isNotEmpty) _positionalKey: positionalJson,
|
||||
if (namedJson.isNotEmpty) _namedKey: namedJson,
|
||||
...super.toJson(constants, locations),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (!(super == other)) return false;
|
||||
|
||||
return other is CallWithArguments &&
|
||||
deepEquals(other.positionalArguments, positionalArguments) &&
|
||||
deepEquals(other.namedArguments, namedArguments);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
deepHash(positionalArguments),
|
||||
deepHash(namedArguments),
|
||||
super.hashCode,
|
||||
);
|
||||
}
|
||||
|
||||
/// A reference to a tear-off use of the [Identifier]. This means that we can't
|
||||
/// record the arguments possibly passed to the method somewhere else.
|
||||
final class CallTearOff extends CallReference {
|
||||
const CallTearOff({required super.loadingUnit, required super.location});
|
||||
|
||||
@override
|
||||
Map<String, Object?> toJson(
|
||||
Map<Constant, int> constants,
|
||||
Map<Location, int> locations,
|
||||
) {
|
||||
return {_typeKey: 'tearoff', ...super.toJson(constants, locations)};
|
||||
}
|
||||
}
|
||||
|
||||
final class InstanceReference extends Reference {
|
||||
final InstanceConstant instanceConstant;
|
||||
|
||||
const InstanceReference({
|
||||
required this.instanceConstant,
|
||||
required super.loadingUnit,
|
||||
required super.location,
|
||||
});
|
||||
|
||||
static const _constantKey = 'constant_index';
|
||||
|
||||
factory InstanceReference.fromJson(
|
||||
Map<String, Object?> json,
|
||||
List<Constant> constants,
|
||||
List<Location> locations,
|
||||
) {
|
||||
final locationIndex = json[_locationKey] as int?;
|
||||
return InstanceReference(
|
||||
instanceConstant:
|
||||
constants[json[_constantKey] as int] as InstanceConstant,
|
||||
loadingUnit: json[_loadingUnitKey] as String?,
|
||||
location: locationIndex == null ? null : locations[locationIndex],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Object?> toJson(
|
||||
Map<Constant, int> constants,
|
||||
Map<Location, int> locations,
|
||||
) => {
|
||||
_constantKey: constants[instanceConstant]!,
|
||||
...super.toJson(constants, locations),
|
||||
};
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (!(super == other)) return false;
|
||||
|
||||
return other is InstanceReference &&
|
||||
other.instanceConstant == instanceConstant;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(instanceConstant, super.hashCode);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright (c) 2025, 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:pub_semver/pub_semver.dart' show Version;
|
||||
|
||||
final version = Version(0, 4, 0);
|
||||
@@ -1,19 +0,0 @@
|
||||
name: record_use
|
||||
description: >
|
||||
The serialization logic and API for the usage recording SDK feature.
|
||||
version: 0.4.2
|
||||
repository: https://github.com/dart-lang/sdk/tree/main/pkg/record_use
|
||||
|
||||
environment:
|
||||
sdk: ^3.7.0
|
||||
|
||||
resolution: workspace
|
||||
|
||||
dependencies:
|
||||
collection: ^1.18.0
|
||||
pub_semver: ^2.1.4
|
||||
|
||||
dev_dependencies:
|
||||
dart_flutter_team_lints: any
|
||||
lints: any
|
||||
test: any
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright (c) 2024, 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 'package:record_use/record_use_internal.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'test_data.dart';
|
||||
|
||||
void main() {
|
||||
group('object 1', () {
|
||||
final json = jsonDecode(recordedUsesJson) as Map<String, Object?>;
|
||||
test('JSON', () => expect(recordedUses.toJson(), json));
|
||||
|
||||
test('Object', () => expect(Recordings.fromJson(json), recordedUses));
|
||||
|
||||
test('Json->Object->Json', () {
|
||||
expect(Recordings.fromJson(json).toJson(), json);
|
||||
});
|
||||
|
||||
test('Object->Json->Object', () {
|
||||
expect(Recordings.fromJson(recordedUses.toJson()), recordedUses);
|
||||
});
|
||||
});
|
||||
|
||||
group('object 2', () {
|
||||
final json2 = jsonDecode(recordedUsesJson2) as Map<String, Object?>;
|
||||
test('JSON', () => expect(recordedUses2.toJson(), json2));
|
||||
|
||||
test('Object', () => expect(Recordings.fromJson(json2), recordedUses2));
|
||||
|
||||
test('Json->Object->Json', () {
|
||||
expect(Recordings.fromJson(json2).toJson(), json2);
|
||||
});
|
||||
|
||||
test('Object->Json->Object', () {
|
||||
expect(Recordings.fromJson(recordedUses2.toJson()), recordedUses2);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
// Copyright (c) 2024, 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:pub_semver/pub_semver.dart';
|
||||
import 'package:record_use/record_use_internal.dart';
|
||||
|
||||
final callId = Identifier(
|
||||
importUri:
|
||||
Uri.parse(
|
||||
'file://lib/_internal/js_runtime/lib/js_helper.dart',
|
||||
).toString(),
|
||||
scope: 'MyClass',
|
||||
name: 'get:loadDeferredLibrary',
|
||||
);
|
||||
final instanceId = Identifier(
|
||||
importUri:
|
||||
Uri.parse(
|
||||
'file://lib/_internal/js_runtime/lib/js_helper.dart',
|
||||
).toString(),
|
||||
name: 'MyAnnotation',
|
||||
);
|
||||
|
||||
final recordedUses = Recordings(
|
||||
metadata: Metadata.fromJson({
|
||||
'version': Version(1, 6, 2, pre: 'wip', build: '5.-.2.z').toString(),
|
||||
'comment':
|
||||
'Recorded references at compile time and their argument values, as'
|
||||
' far as known, to definitions annotated with @RecordUse',
|
||||
}),
|
||||
callsForDefinition: {
|
||||
Definition(identifier: callId, loadingUnit: 'part_15.js'): [
|
||||
const CallWithArguments(
|
||||
positionalArguments: [
|
||||
StringConstant('lib_SHA1'),
|
||||
BoolConstant(false),
|
||||
IntConstant(1),
|
||||
],
|
||||
namedArguments: {
|
||||
'freddy': StringConstant('mercury'),
|
||||
'leroy': StringConstant('jenkins'),
|
||||
},
|
||||
loadingUnit: 'o.js',
|
||||
location: Location(uri: 'lib/test.dart'),
|
||||
),
|
||||
const CallWithArguments(
|
||||
positionalArguments: [
|
||||
StringConstant('lib_SHA1'),
|
||||
MapConstant<IntConstant>({'key': IntConstant(99)}),
|
||||
ListConstant([
|
||||
StringConstant('camus'),
|
||||
ListConstant([
|
||||
StringConstant('einstein'),
|
||||
StringConstant('insert'),
|
||||
BoolConstant(false),
|
||||
]),
|
||||
StringConstant('einstein'),
|
||||
]),
|
||||
],
|
||||
namedArguments: {
|
||||
'freddy': IntConstant(0),
|
||||
'leroy': StringConstant('jenkins'),
|
||||
},
|
||||
loadingUnit: 'o.js',
|
||||
location: Location(uri: 'lib/test2.dart'),
|
||||
),
|
||||
],
|
||||
},
|
||||
instancesForDefinition: {
|
||||
Definition(identifier: instanceId): [
|
||||
const InstanceReference(
|
||||
instanceConstant: InstanceConstant(
|
||||
fields: {'a': IntConstant(42), 'b': NullConstant()},
|
||||
),
|
||||
loadingUnit: '3',
|
||||
location: Location(uri: 'lib/test3.dart'),
|
||||
),
|
||||
const InstanceReference(
|
||||
instanceConstant: InstanceConstant(fields: {}),
|
||||
loadingUnit: '3',
|
||||
location: Location(uri: 'lib/test3.dart'),
|
||||
),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
final recordedUses2 = Recordings(
|
||||
metadata: Metadata.fromJson({
|
||||
'version': Version(1, 6, 2, pre: 'wip', build: '5.-.2.z').toString(),
|
||||
'comment':
|
||||
'Recorded references at compile time and their argument values, as'
|
||||
' far as known, to definitions annotated with @RecordUse',
|
||||
}),
|
||||
callsForDefinition: {
|
||||
Definition(identifier: callId, loadingUnit: 'part_15.js'): [
|
||||
const CallWithArguments(
|
||||
positionalArguments: [BoolConstant(false), IntConstant(1)],
|
||||
namedArguments: {
|
||||
'freddy': StringConstant('mercury'),
|
||||
'answer': IntConstant(42),
|
||||
},
|
||||
loadingUnit: 'o.js',
|
||||
location: Location(uri: 'lib/test3.dart'),
|
||||
),
|
||||
],
|
||||
},
|
||||
instancesForDefinition: {},
|
||||
);
|
||||
|
||||
final recordedUsesJson = '''{
|
||||
"metadata": {
|
||||
"version": "1.6.2-wip+5.-.2.z",
|
||||
"comment": "Recorded references at compile time and their argument values, as far as known, to definitions annotated with @RecordUse"
|
||||
},
|
||||
"constants": [
|
||||
{
|
||||
"type": "String",
|
||||
"value": "lib_SHA1"
|
||||
},
|
||||
{
|
||||
"type": "bool",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"type": "int",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"value": "mercury"
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"value": "jenkins"
|
||||
},
|
||||
{
|
||||
"type": "int",
|
||||
"value": 99
|
||||
},
|
||||
{
|
||||
"type": "map",
|
||||
"value": {
|
||||
"key": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"value": "camus"
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"value": "einstein"
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"value": "insert"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"value": [
|
||||
8,
|
||||
9,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"value": [
|
||||
7,
|
||||
10,
|
||||
8
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "int",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"type": "int",
|
||||
"value": 42
|
||||
},
|
||||
{
|
||||
"type": "Null"
|
||||
},
|
||||
{
|
||||
"type": "Instance",
|
||||
"value": {
|
||||
"a": 13,
|
||||
"b": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Instance"
|
||||
}
|
||||
],
|
||||
"locations": [
|
||||
{
|
||||
"uri": "lib/test.dart"
|
||||
},
|
||||
{
|
||||
"uri": "lib/test2.dart"
|
||||
},
|
||||
{
|
||||
"uri": "lib/test3.dart"
|
||||
}
|
||||
],
|
||||
"recordings": [
|
||||
{
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"uri": "file://lib/_internal/js_runtime/lib/js_helper.dart",
|
||||
"scope": "MyClass",
|
||||
"name": "get:loadDeferredLibrary"
|
||||
},
|
||||
"loading_unit": "part_15.js"
|
||||
},
|
||||
"calls": [
|
||||
{
|
||||
"type": "with_arguments",
|
||||
"positional": [
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
"named": {
|
||||
"freddy": 3,
|
||||
"leroy": 4
|
||||
},
|
||||
"loading_unit": "o.js",
|
||||
"@": 0
|
||||
},
|
||||
{
|
||||
"type": "with_arguments",
|
||||
"positional": [
|
||||
0,
|
||||
6,
|
||||
11
|
||||
],
|
||||
"named": {
|
||||
"freddy": 12,
|
||||
"leroy": 4
|
||||
},
|
||||
"loading_unit": "o.js",
|
||||
"@": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"uri": "file://lib/_internal/js_runtime/lib/js_helper.dart",
|
||||
"name": "MyAnnotation"
|
||||
}
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
"constant_index": 15,
|
||||
"loading_unit": "3",
|
||||
"@": 2
|
||||
},
|
||||
{
|
||||
"constant_index": 16,
|
||||
"loading_unit": "3",
|
||||
"@": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}''';
|
||||
|
||||
final recordedUsesJson2 = '''{
|
||||
"metadata": {
|
||||
"version": "1.6.2-wip+5.-.2.z",
|
||||
"comment": "Recorded references at compile time and their argument values, as far as known, to definitions annotated with @RecordUse"
|
||||
},
|
||||
"constants": [
|
||||
{
|
||||
"type": "bool",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"type": "int",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"value": "mercury"
|
||||
},
|
||||
{
|
||||
"type": "int",
|
||||
"value": 42
|
||||
}
|
||||
],
|
||||
"locations": [
|
||||
{
|
||||
"uri": "lib/test3.dart"
|
||||
}
|
||||
],
|
||||
"recordings": [
|
||||
{
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"uri": "file://lib/_internal/js_runtime/lib/js_helper.dart",
|
||||
"scope": "MyClass",
|
||||
"name": "get:loadDeferredLibrary"
|
||||
},
|
||||
"loading_unit": "part_15.js"
|
||||
},
|
||||
"calls": [
|
||||
{
|
||||
"type": "with_arguments",
|
||||
"positional": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"named": {
|
||||
"freddy": 2,
|
||||
"answer": 3
|
||||
},
|
||||
"loading_unit": "o.js",
|
||||
"@": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}''';
|
||||
@@ -1,127 +0,0 @@
|
||||
// Copyright (c) 2024, 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 'package:record_use/record_use_internal.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'test_data.dart';
|
||||
|
||||
void main() {
|
||||
test('All API calls', () {
|
||||
expect(
|
||||
RecordedUsages.fromJson(
|
||||
jsonDecode(recordedUsesJson) as Map<String, Object?>,
|
||||
)
|
||||
.constArgumentsFor(
|
||||
Identifier(
|
||||
importUri:
|
||||
Uri.parse(
|
||||
'file://lib/_internal/js_runtime/lib/js_helper.dart',
|
||||
).toString(),
|
||||
scope: 'MyClass',
|
||||
name: 'get:loadDeferredLibrary',
|
||||
),
|
||||
)
|
||||
.length,
|
||||
2,
|
||||
);
|
||||
});
|
||||
|
||||
test('All API instances', () {
|
||||
final instance =
|
||||
RecordedUsages.fromJson(
|
||||
jsonDecode(recordedUsesJson) as Map<String, Object?>,
|
||||
)
|
||||
.constantsOf(
|
||||
Identifier(
|
||||
importUri:
|
||||
Uri.parse(
|
||||
'file://lib/_internal/js_runtime/lib/js_helper.dart',
|
||||
).toString(),
|
||||
name: 'MyAnnotation',
|
||||
),
|
||||
)
|
||||
.first;
|
||||
final instanceMap =
|
||||
recordedUses.instancesForDefinition.values
|
||||
.expand((usage) => usage)
|
||||
.map(
|
||||
(instance) => instance.instanceConstant.fields.map(
|
||||
(key, constant) => MapEntry(key, constant.toValue()),
|
||||
),
|
||||
)
|
||||
.first;
|
||||
for (final entry in instanceMap.entries) {
|
||||
expect(instance[entry.key], entry.value);
|
||||
}
|
||||
});
|
||||
|
||||
test('Specific API calls', () {
|
||||
var arguments =
|
||||
RecordedUsages.fromJson(
|
||||
jsonDecode(recordedUsesJson) as Map<String, Object?>,
|
||||
)
|
||||
.constArgumentsFor(
|
||||
Identifier(
|
||||
importUri:
|
||||
Uri.parse(
|
||||
'file://lib/_internal/js_runtime/lib/js_helper.dart',
|
||||
).toString(),
|
||||
scope: 'MyClass',
|
||||
name: 'get:loadDeferredLibrary',
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
var (named: named0, positional: positional0) = arguments[0];
|
||||
expect(named0, const {'freddy': 'mercury', 'leroy': 'jenkins'});
|
||||
expect(positional0, const ['lib_SHA1', false, 1]);
|
||||
var (named: named1, positional: positional1) = arguments[1];
|
||||
expect(named1, const {'freddy': 0, 'leroy': 'jenkins'});
|
||||
expect(positional1, const [
|
||||
'lib_SHA1',
|
||||
{'key': 99},
|
||||
[
|
||||
'camus',
|
||||
['einstein', 'insert', false],
|
||||
'einstein',
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
test('Specific API instances', () {
|
||||
final instance =
|
||||
RecordedUsages.fromJson(
|
||||
jsonDecode(recordedUsesJson) as Map<String, Object?>,
|
||||
)
|
||||
.constantsOf(
|
||||
Identifier(
|
||||
importUri:
|
||||
Uri.parse(
|
||||
'file://lib/_internal/js_runtime/lib/js_helper.dart',
|
||||
).toString(),
|
||||
name: 'MyAnnotation',
|
||||
),
|
||||
)
|
||||
.first;
|
||||
expect(instance['a'], 42);
|
||||
expect(instance['b'], null);
|
||||
});
|
||||
|
||||
test('HasNonConstInstance', () {
|
||||
expect(
|
||||
RecordedUsages.fromJson(
|
||||
jsonDecode(recordedUsesJson2) as Map<String, Object?>,
|
||||
).hasNonConstArguments(
|
||||
const Identifier(
|
||||
importUri:
|
||||
'package:drop_dylib_recording/src/drop_dylib_recording.dart',
|
||||
name: 'getMathMethod',
|
||||
),
|
||||
),
|
||||
false,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
bin/drop_data_asset*/
|
||||
@@ -1,17 +0,0 @@
|
||||
# Tree-shaking Data Assets with Record Use
|
||||
|
||||
This sample demonstrates how the `record-use` feature can be utilized to
|
||||
tree-shake (remove) unused data assets from the build output.
|
||||
|
||||
## Usage
|
||||
|
||||
The `record-use` and `native-assets` experiments need to be enabled.
|
||||
|
||||
### JS
|
||||
|
||||
Run either `dart compile js --write-resources bin/drop_data_asset_calls.dart` or `dart compile js --write-resources bin/drop_data_asset_instances.dart`.
|
||||
|
||||
### Native
|
||||
|
||||
Run either `dart --enable-experiment=native-assets,record-use build bin/drop_data_asset_calls.dart` or `dart --enable-experiment=native-assets,record-use build bin/drop_data_asset_instances.dart`.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
leroy
|
||||
@@ -1 +0,0 @@
|
||||
freddy
|
||||
@@ -1 +0,0 @@
|
||||
jenkins
|
||||
@@ -1 +0,0 @@
|
||||
mercury
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) 2025, 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:drop_data_asset/drop_data_asset.dart';
|
||||
|
||||
void main(List<String> arguments) {
|
||||
print('Hello world: ${MyMath.add(3, 4)}!');
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) 2025, 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:drop_data_asset/drop_data_asset.dart';
|
||||
|
||||
void main(List<String> arguments) {
|
||||
print('Hello world: ${MyMath.double(3)}!');
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright (c) 2025, 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:data_assets/data_assets.dart';
|
||||
import 'package:hooks/hooks.dart';
|
||||
|
||||
void main(List<String> arguments) async {
|
||||
await build(arguments, (input, output) async {
|
||||
for (var element in ['add', 'multiply', 'double', 'square']) {
|
||||
output.assets.data.add(
|
||||
DataAsset(
|
||||
file: input.packageRoot.resolve('assets/$element.txt'),
|
||||
name: element,
|
||||
package: input.packageName,
|
||||
),
|
||||
routing:
|
||||
input.config.linkingEnabled
|
||||
? ToLinkHook(input.packageName)
|
||||
: const ToAppBundle(),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
// Copyright (c) 2025, 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:data_assets/data_assets.dart';
|
||||
import 'package:hooks/hooks.dart';
|
||||
import 'package:record_use/record_use.dart';
|
||||
|
||||
void main(List<String> arguments) async {
|
||||
await link(arguments, (input, output) async {
|
||||
final recordedUsagesFile = input.recordedUsagesFile;
|
||||
if (recordedUsagesFile == null) {
|
||||
throw ArgumentError(
|
||||
'Enable the --enable-experiments=record-use experiment to use this app.',
|
||||
);
|
||||
}
|
||||
final usages = await recordedUsages(recordedUsagesFile);
|
||||
final dataAssets = input.assets.data;
|
||||
print('Received assets: ${dataAssets.map((a) => a.id).join(', ')}.');
|
||||
|
||||
final symbols = <String>{};
|
||||
|
||||
// Tree-shake unused assets using calls
|
||||
for (final methodName in ['add', 'multiply']) {
|
||||
final calls = usages.constArgumentsFor(
|
||||
Identifier(
|
||||
importUri:
|
||||
'package:${input.packageName}/src/${input.packageName}.dart',
|
||||
scope: 'MyMath',
|
||||
name: methodName,
|
||||
),
|
||||
'int add(int a, int b)',
|
||||
);
|
||||
print('Checking calls to $methodName...');
|
||||
for (final call in calls) {
|
||||
print(
|
||||
'A call was made to "$methodName" with the arguments ('
|
||||
'${call.positional[0] as int},${call.positional[1] as int})',
|
||||
);
|
||||
symbols.add(methodName);
|
||||
}
|
||||
}
|
||||
|
||||
// Tree-shake unused assets
|
||||
final instances = usages.constantsOf(
|
||||
Identifier(
|
||||
importUri: 'package:${input.packageName}/src/${input.packageName}.dart',
|
||||
name: 'RecordCallToC',
|
||||
),
|
||||
);
|
||||
for (final instance in instances) {
|
||||
final symbol = instance['symbol'] as String;
|
||||
print('An instance of "$instance" was found with the field "$symbol"');
|
||||
symbols.add(symbol);
|
||||
}
|
||||
|
||||
final neededCodeAssets = [
|
||||
for (final asset in dataAssets)
|
||||
if (symbols.any(asset.id.endsWith)) asset,
|
||||
];
|
||||
|
||||
print('Keeping only ${neededCodeAssets.map((e) => e.id).join(', ')}.');
|
||||
output.assets.data.addAll(neededCodeAssets);
|
||||
|
||||
output.addDependency(recordedUsagesFile);
|
||||
});
|
||||
}
|
||||
|
||||
Future<RecordedUsages> recordedUsages(Uri recordedUsagesFile) async {
|
||||
final file = File.fromUri(recordedUsagesFile);
|
||||
final string = await file.readAsString();
|
||||
final usages = RecordedUsages.fromJson(
|
||||
jsonDecode(string) as Map<String, Object?>,
|
||||
);
|
||||
return usages;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// Copyright (c) 2025, 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.
|
||||
|
||||
export 'src/drop_data_asset.dart';
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) 2025, 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:meta/meta.dart';
|
||||
|
||||
class MyMath {
|
||||
@RecordUse()
|
||||
static int add(int a, int b) => a + b;
|
||||
|
||||
@RecordUse()
|
||||
static int multiply(int a, int b) => a * b;
|
||||
|
||||
@RecordCallToC('double')
|
||||
static int double(int a) => a + a;
|
||||
|
||||
@RecordCallToC('square')
|
||||
static int square(int a) => a * a;
|
||||
}
|
||||
|
||||
@RecordUse()
|
||||
class RecordCallToC {
|
||||
final String symbol;
|
||||
|
||||
const RecordCallToC(this.symbol);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
name: drop_data_asset
|
||||
description: Add four data assets, remove three in linking based on recorded usage.
|
||||
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ^3.7.0
|
||||
|
||||
dependencies:
|
||||
hooks: any
|
||||
data_assets: any
|
||||
logging: any
|
||||
meta: any
|
||||
record_use:
|
||||
path: ../../../record_use/
|
||||
|
||||
dev_dependencies:
|
||||
lints: any
|
||||
test: any
|
||||
|
||||
dependency_overrides:
|
||||
meta:
|
||||
path: ../../../meta/
|
||||
hooks:
|
||||
path: ../../../../third_party/pkg/native/pkgs/hooks/
|
||||
data_assets:
|
||||
path: ../../../../third_party/pkg/native/pkgs/data_assets/
|
||||
@@ -1 +0,0 @@
|
||||
bin/drop_dylib_recording*/
|
||||
@@ -1,35 +0,0 @@
|
||||
This sample builds a native library for adding and multiplying. It then uses
|
||||
the recorded usages feature to tree-shake unused libraries out.
|
||||
|
||||
## Usage:
|
||||
|
||||
### Keep all:
|
||||
```
|
||||
dart --enable-experiment=record-use build bin/drop_dylib_recording_all.dart
|
||||
```
|
||||
The `lib/` folder now contains both libraries
|
||||
```
|
||||
./bin/drop_dylib_recording_all/drop_dylib_recording_all.exe add
|
||||
```
|
||||
Prints `Hello world: 7!`
|
||||
|
||||
|
||||
### Treeshake using calls:
|
||||
```
|
||||
dart --enable-experiment=record-use build bin/drop_dylib_recording_calls.dart
|
||||
```
|
||||
The `lib/` folder now contains only the `add` library.
|
||||
```
|
||||
./bin/drop_dylib_recording_calls/drop_dylib_recording_calls.exe
|
||||
```
|
||||
Prints `Hello world: 7!`
|
||||
|
||||
### Treeshake using instances:
|
||||
```
|
||||
dart --enable-experiment=record-use build bin/drop_dylib_recording_instances.dart
|
||||
```
|
||||
The `lib/` folder now contains only the `add` library.
|
||||
```
|
||||
./bin/drop_dylib_recording_calls/drop_dylib_recording_instances.exe
|
||||
```
|
||||
Prints `Hello world: 7!`
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) 2024, 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:drop_dylib_recording/drop_dylib_recording.dart';
|
||||
|
||||
void main(List<String> arguments) {
|
||||
print('Hello world: ${MyMath.add(3, 4)}!');
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) 2024, 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:drop_dylib_recording/drop_dylib_recording.dart';
|
||||
|
||||
void main(List<String> arguments) {
|
||||
print('Hello world: ${MyMath.double(3)}!');
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright (c) 2024, 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:logging/logging.dart';
|
||||
import 'package:hooks/hooks.dart';
|
||||
import 'package:code_assets/code_assets.dart';
|
||||
import 'package:native_toolchain_c/native_toolchain_c.dart';
|
||||
|
||||
void main(List<String> arguments) async {
|
||||
await build(arguments, (input, output) async {
|
||||
final logger =
|
||||
Logger('')
|
||||
..level = Level.ALL
|
||||
..onRecord.listen((record) {
|
||||
print('${record.level.name}: ${record.time}: ${record.message}');
|
||||
});
|
||||
final List<AssetRouting> routing =
|
||||
input.config.linkingEnabled
|
||||
? [ToLinkHook(input.packageName)]
|
||||
: const [ToAppBundle()];
|
||||
await CBuilder.library(
|
||||
name: 'add',
|
||||
assetName: 'dylib_add',
|
||||
sources: ['src/native_add.c'],
|
||||
linkModePreference: LinkModePreference.dynamic,
|
||||
).run(input: input, output: output, logger: logger, routing: routing);
|
||||
|
||||
await CBuilder.library(
|
||||
name: 'multiply',
|
||||
assetName: 'dylib_multiply',
|
||||
sources: ['src/native_multiply.c'],
|
||||
linkModePreference: LinkModePreference.dynamic,
|
||||
).run(input: input, output: output, logger: logger, routing: routing);
|
||||
});
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright (c) 2024, 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:hooks/hooks.dart';
|
||||
import 'package:code_assets/code_assets.dart';
|
||||
import 'package:record_use/record_use.dart';
|
||||
|
||||
void main(List<String> arguments) async {
|
||||
await link(arguments, (input, output) async {
|
||||
final recordedUsagesFile = input.recordedUsagesFile;
|
||||
if (recordedUsagesFile == null) {
|
||||
throw ArgumentError(
|
||||
'Enable the --enable-experiments=record-use experiment to use this app.',
|
||||
);
|
||||
}
|
||||
final usages = await recordedUsages(recordedUsagesFile);
|
||||
final codeAssets = input.assets.code;
|
||||
print('Received assets: ${codeAssets.map((a) => a.id).join(', ')}.');
|
||||
|
||||
final symbols = <String>{};
|
||||
final argumentsFile = await File.fromUri(
|
||||
input.outputDirectory.resolve('arguments.txt'),
|
||||
).create();
|
||||
|
||||
final dataLines = <String>[];
|
||||
// Tree-shake unused assets using calls
|
||||
for (final methodName in ['add', 'multiply']) {
|
||||
final calls = usages.constArgumentsFor(
|
||||
Identifier(
|
||||
importUri:
|
||||
'package:drop_dylib_recording/src/drop_dylib_recording.dart',
|
||||
scope: 'MyMath',
|
||||
name: methodName,
|
||||
),
|
||||
);
|
||||
for (var call in calls) {
|
||||
dataLines.add(
|
||||
'A call was made to "$methodName" with the arguments ('
|
||||
'${call.positional[0] as int},${call.positional[1] as int})',
|
||||
);
|
||||
symbols.add(methodName);
|
||||
}
|
||||
}
|
||||
|
||||
argumentsFile.writeAsStringSync(dataLines.join('\n'));
|
||||
|
||||
// Tree-shake unused assets
|
||||
final instances = usages.constantsOf(
|
||||
Identifier(
|
||||
importUri: 'package:drop_dylib_recording/src/drop_dylib_recording.dart',
|
||||
name: 'RecordCallToC',
|
||||
),
|
||||
);
|
||||
for (final instance in instances) {
|
||||
final symbol = instance['symbol'] as String;
|
||||
symbols.add(symbol);
|
||||
}
|
||||
|
||||
final neededCodeAssets = [
|
||||
for (final codeAsset in codeAssets)
|
||||
if (symbols.any(codeAsset.id.endsWith)) codeAsset,
|
||||
];
|
||||
|
||||
print('Keeping only ${neededCodeAssets.map((e) => e.id).join(', ')}.');
|
||||
output.assets.code.addAll(neededCodeAssets);
|
||||
|
||||
output.addDependency(recordedUsagesFile);
|
||||
});
|
||||
}
|
||||
|
||||
Future<RecordedUsages> recordedUsages(Uri recordedUsagesFile) async {
|
||||
final file = File.fromUri(recordedUsagesFile);
|
||||
final string = await file.readAsString();
|
||||
final usages = RecordedUsages.fromJson(
|
||||
jsonDecode(string) as Map<String, Object?>,
|
||||
);
|
||||
return usages;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// Copyright (c) 2024, 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.
|
||||
|
||||
export 'src/drop_dylib_recording.dart';
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright (c) 2024, 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:meta/meta.dart';
|
||||
|
||||
import 'drop_dylib_recording_bindings.dart' as bindings;
|
||||
|
||||
class MyMath {
|
||||
@RecordUse()
|
||||
static int add(int a, int b) => bindings.add(a, b);
|
||||
|
||||
@RecordUse()
|
||||
static int multiply(int a, int b) => bindings.multiply(a, b);
|
||||
|
||||
@RecordCallToC('add')
|
||||
static int double(int a) => bindings.add(a, a);
|
||||
|
||||
@RecordCallToC('multiply')
|
||||
static int square(int a) => bindings.multiply(a, a);
|
||||
}
|
||||
|
||||
@RecordUse()
|
||||
class RecordCallToC {
|
||||
final String symbol;
|
||||
|
||||
const RecordCallToC(this.symbol);
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
// Copyright (c) 2024, 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:ffi' as ffi;
|
||||
|
||||
@ffi.Native<ffi.Int32 Function(ffi.Int32, ffi.Int32)>(
|
||||
assetId: 'package:drop_dylib_recording/dylib_add',
|
||||
)
|
||||
external int add(int a, int b);
|
||||
|
||||
@ffi.Native<ffi.Int32 Function(ffi.Int32, ffi.Int32)>(
|
||||
assetId: 'package:drop_dylib_recording/dylib_multiply',
|
||||
)
|
||||
external int multiply(int a, int b);
|
||||
@@ -1,28 +0,0 @@
|
||||
name: drop_dylib_recording
|
||||
description: Generate two dylibs, remove one in linking based on recorded usage.
|
||||
version: 1.0.0
|
||||
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ^3.0.0
|
||||
|
||||
dependencies:
|
||||
logging: ^1.1.1
|
||||
meta: any
|
||||
hooks:
|
||||
path: ../../../../third_party/pkg/native/pkgs/hooks/
|
||||
code_assets:
|
||||
path: ../../../../third_party/pkg/native/pkgs/code_assets/
|
||||
native_toolchain_c:
|
||||
path: ../../../../third_party/pkg/native/pkgs/native_toolchain_c/
|
||||
record_use:
|
||||
path: ../../../record_use/
|
||||
|
||||
dev_dependencies:
|
||||
lints: any
|
||||
test: any
|
||||
|
||||
dependency_overrides:
|
||||
meta:
|
||||
path: ../../../meta/
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) 2024, 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.
|
||||
|
||||
#include "native_add.h"
|
||||
|
||||
MYLIB_EXPORT int32_t add(int32_t a, int32_t b) {
|
||||
return a + b;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) 2024, 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.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if _WIN32
|
||||
#define MYLIB_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define MYLIB_EXPORT
|
||||
#endif
|
||||
|
||||
MYLIB_EXPORT int32_t add(int32_t a, int32_t b);
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) 2024, 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.
|
||||
|
||||
#include "native_multiply.h"
|
||||
|
||||
MYLIB_EXPORT intptr_t multiply(intptr_t a, intptr_t b) {
|
||||
return a * b;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) 2024, 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.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if _WIN32
|
||||
#define MYLIB_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define MYLIB_EXPORT
|
||||
#endif
|
||||
|
||||
MYLIB_EXPORT intptr_t multiply(intptr_t a, intptr_t b);
|
||||
@@ -1,24 +0,0 @@
|
||||
- drop_dylib_recording/pubspec.yaml
|
||||
- drop_dylib_recording/lib/src/drop_dylib_recording_bindings.dart
|
||||
- drop_dylib_recording/lib/src/drop_dylib_recording.dart
|
||||
- drop_dylib_recording/lib/drop_dylib_recording.dart
|
||||
- drop_dylib_recording/src/native_add.h
|
||||
- drop_dylib_recording/src/native_multiply.h
|
||||
- drop_dylib_recording/src/native_add.c
|
||||
- drop_dylib_recording/src/native_multiply.c
|
||||
- drop_dylib_recording/hook/link.dart
|
||||
- drop_dylib_recording/hook/build.dart
|
||||
- drop_dylib_recording/bin/drop_dylib_recording_calls.dart
|
||||
- drop_dylib_recording/bin/drop_dylib_recording_instances.dart
|
||||
- drop_data_asset/pubspec.yaml
|
||||
- drop_data_asset/assets/double.txt
|
||||
- drop_data_asset/assets/square.txt
|
||||
- drop_data_asset/assets/add.txt
|
||||
- drop_data_asset/assets/multiply.txt
|
||||
- drop_data_asset/lib/drop_data_asset.dart
|
||||
- drop_data_asset/lib/src/drop_data_asset.dart
|
||||
- drop_data_asset/README.md
|
||||
- drop_data_asset/hook/link.dart
|
||||
- drop_data_asset/hook/build.dart
|
||||
- drop_data_asset/bin/drop_data_asset_instances.dart
|
||||
- drop_data_asset/bin/drop_data_asset_calls.dart
|
||||
@@ -46,6 +46,7 @@ final testSuiteDirectories = [
|
||||
Path('third_party/pkg/native/pkgs/hooks_runner'),
|
||||
Path('third_party/pkg/native/pkgs/hooks'),
|
||||
Path('third_party/pkg/native/pkgs/native_toolchain_c'),
|
||||
Path('third_party/pkg/native/pkgs/record_use'),
|
||||
Path('third_party/pkg/package_config'),
|
||||
Path('utils/tests/peg'),
|
||||
];
|
||||
|
||||
+2
-1
@@ -63,7 +63,6 @@ workspace:
|
||||
- pkg/native_compiler
|
||||
- pkg/native_stack_traces
|
||||
- pkg/node_preamble
|
||||
- pkg/record_use
|
||||
- pkg/reload_test
|
||||
- pkg/scrape
|
||||
- pkg/server_plugin
|
||||
@@ -227,6 +226,8 @@ dependency_overrides:
|
||||
path: third_party/pkg/native/pkgs/pub_formats
|
||||
pub_semver:
|
||||
path: third_party/pkg/tools/pkgs/pub_semver
|
||||
record_use:
|
||||
path: third_party/pkg/native/pkgs/record_use
|
||||
regression_tests:
|
||||
path: third_party/pkg/test/integration_tests/regression
|
||||
shelf:
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
!/pkg/hooks_runner.status
|
||||
!/pkg/hooks.status
|
||||
!/pkg/native_toolchain_c.status
|
||||
!/pkg/record_use.status
|
||||
|
||||
# These packages are authored in third_party.
|
||||
!/pkg/dap
|
||||
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) 2025, 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.
|
||||
|
||||
example/*: SkipByDesign # These test projects don't work without running pub in the project itself.
|
||||
lib/src/*: SkipByDesign # These are not tests.
|
||||
test/json_schema/*: SkipByDesign # Dev dependency not in Dart SDK.
|
||||
|
||||
@@ -183,7 +183,6 @@
|
||||
"pkg/kernel/",
|
||||
"pkg/meta/",
|
||||
"pkg/native_stack_traces/",
|
||||
"pkg/hooks_runner/",
|
||||
"pkg/pkg.status",
|
||||
"pkg/smith/",
|
||||
"pkg/status_file/",
|
||||
@@ -3101,6 +3100,7 @@
|
||||
"hooks_runner",
|
||||
"hooks",
|
||||
"native_toolchain_c",
|
||||
"record_use",
|
||||
"package_config",
|
||||
"pkg/pkg/dartdev/test/native_assets"
|
||||
]
|
||||
@@ -3228,6 +3228,7 @@
|
||||
"hooks_runner",
|
||||
"hooks",
|
||||
"native_toolchain_c",
|
||||
"record_use",
|
||||
"package_config",
|
||||
"pkg/pkg/dartdev/test/native_assets"
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user