From 0d4858065a439d7b835bc9b936d3feccf5b6e023 Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 29 Aug 2024 14:49:11 +0200 Subject: [PATCH 01/13] Initial commit --- .github/workflows/record_use.yaml | 39 +++++++++++++++++++ README.md | 1 + pkgs/record_use/.gitignore | 7 ++++ pkgs/record_use/CHANGELOG.md | 3 ++ pkgs/record_use/README.md | 39 +++++++++++++++++++ pkgs/record_use/analysis_options.yaml | 9 +++++ .../example/record_use_example.dart | 10 +++++ pkgs/record_use/lib/record_use.dart | 5 +++ pkgs/record_use/lib/src/record_use_base.dart | 5 +++ pkgs/record_use/pubspec.yaml | 16 ++++++++ pkgs/record_use/test/record_use_test.dart | 20 ++++++++++ 11 files changed, 154 insertions(+) create mode 100644 .github/workflows/record_use.yaml create mode 100644 pkgs/record_use/.gitignore create mode 100644 pkgs/record_use/CHANGELOG.md create mode 100644 pkgs/record_use/README.md create mode 100644 pkgs/record_use/analysis_options.yaml create mode 100644 pkgs/record_use/example/record_use_example.dart create mode 100644 pkgs/record_use/lib/record_use.dart create mode 100644 pkgs/record_use/lib/src/record_use_base.dart create mode 100644 pkgs/record_use/pubspec.yaml create mode 100644 pkgs/record_use/test/record_use_test.dart diff --git a/.github/workflows/record_use.yaml b/.github/workflows/record_use.yaml new file mode 100644 index 000000000..05a2cc8f4 --- /dev/null +++ b/.github/workflows/record_use.yaml @@ -0,0 +1,39 @@ +name: 'package:record_use' +permissions: read-all + +on: + pull_request: + branches: [ main ] + paths: + - .github/workflows/record_use.yaml + - pkgs/record_use/** + push: + branches: [ main ] + paths: + - .github/workflows/record_use.yaml + - pkgs/record_use/** + schedule: + - cron: '0 0 * * 0' # weekly + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: pkgs/record_use/ + strategy: + matrix: + sdk: [stable, dev] + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 + - uses: dart-lang/setup-dart@2986c8e337a31eb7b455ce93dc984e9bf5797756 + with: + sdk: ${{matrix.sdk}} + + - run: dart pub get + + - run: dart analyze --fatal-infos + + - run: dart format --output=none --set-exit-if-changed . + + - run: dart test diff --git a/README.md b/README.md index 284111fec..51767c896 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ building and bundling. | [native_assets_builder](pkgs/native_assets_builder/) | This package is the backend that invokes build hooks. | [![pub package](https://img.shields.io/pub/v/native_assets_builder.svg)](https://pub.dev/packages/native_assets_builder) | | [native_assets_cli](pkgs/native_assets_cli/) | A library that contains the argument and file formats for implementing a native assets CLI. | [![pub package](https://img.shields.io/pub/v/native_assets_cli.svg)](https://pub.dev/packages/native_assets_cli) | | [native_toolchain_c](pkgs/native_toolchain_c/) | A library to invoke the native C compiler installed on the host machine. | [![pub package](https://img.shields.io/pub/v/native_toolchain_c.svg)](https://pub.dev/packages/native_toolchain_c) | +| [record_use](pkgs/record_use/) | The serialization logic and API for the usage recording SDK feature. | [![pub package](https://img.shields.io/pub/v/record_use.svg)](https://pub.dev/packages/record_use) | ## External packages diff --git a/pkgs/record_use/.gitignore b/pkgs/record_use/.gitignore new file mode 100644 index 000000000..3cceda557 --- /dev/null +++ b/pkgs/record_use/.gitignore @@ -0,0 +1,7 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ + +# Avoid committing pubspec.lock for library packages; see +# https://dart.dev/guides/libraries/private-files#pubspeclock. +pubspec.lock diff --git a/pkgs/record_use/CHANGELOG.md b/pkgs/record_use/CHANGELOG.md new file mode 100644 index 000000000..a0712a79e --- /dev/null +++ b/pkgs/record_use/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.1.0 + +- Initial version. diff --git a/pkgs/record_use/README.md b/pkgs/record_use/README.md new file mode 100644 index 000000000..8831761b8 --- /dev/null +++ b/pkgs/record_use/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/pkgs/record_use/analysis_options.yaml b/pkgs/record_use/analysis_options.yaml new file mode 100644 index 000000000..d342b5770 --- /dev/null +++ b/pkgs/record_use/analysis_options.yaml @@ -0,0 +1,9 @@ +include: package:dart_flutter_team_lints/analysis_options.yaml + +analyzer: + errors: + todo: ignore + language: + strict-casts: true + strict-inference: true + strict-raw-types: true diff --git a/pkgs/record_use/example/record_use_example.dart b/pkgs/record_use/example/record_use_example.dart new file mode 100644 index 000000000..d3c2906e5 --- /dev/null +++ b/pkgs/record_use/example/record_use_example.dart @@ -0,0 +1,10 @@ +// 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 main() { + var awesome = Awesome(); + print('awesome: ${awesome.isAwesome}'); +} diff --git a/pkgs/record_use/lib/record_use.dart b/pkgs/record_use/lib/record_use.dart new file mode 100644 index 000000000..ffa658ec2 --- /dev/null +++ b/pkgs/record_use/lib/record_use.dart @@ -0,0 +1,5 @@ +// 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/record_use_base.dart'; diff --git a/pkgs/record_use/lib/src/record_use_base.dart b/pkgs/record_use/lib/src/record_use_base.dart new file mode 100644 index 000000000..953d00735 --- /dev/null +++ b/pkgs/record_use/lib/src/record_use_base.dart @@ -0,0 +1,5 @@ +// 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 RecordUse {} diff --git a/pkgs/record_use/pubspec.yaml b/pkgs/record_use/pubspec.yaml new file mode 100644 index 000000000..ce4d16c99 --- /dev/null +++ b/pkgs/record_use/pubspec.yaml @@ -0,0 +1,16 @@ +name: record_use +description: > + The serialization logic and API for the usage recording SDK feature. +version: 0.1.0 +repository: https://github.com/dart-lang/native/tree/main/pkgs/record_use + +environment: + sdk: ^3.5.0 + +dependencies: + path: ^1.8.0 + +dev_dependencies: + dart_flutter_team_lints: ^3.2.0 + lints: ^4.0.0 + test: ^1.24.0 diff --git a/pkgs/record_use/test/record_use_test.dart b/pkgs/record_use/test/record_use_test.dart new file mode 100644 index 000000000..c29da356d --- /dev/null +++ b/pkgs/record_use/test/record_use_test.dart @@ -0,0 +1,20 @@ +// 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'; +import 'package:test/test.dart'; + +void main() { + group('A group of tests', () { + final awesome = Awesome(); + + setUp(() { + // Additional setup goes here. + }); + + test('First Test', () { + expect(awesome.isAwesome, isTrue); + }); + }); +} From e036dae3a06631ceec41ef1aab27b8bbab27176b Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 29 Aug 2024 14:53:48 +0200 Subject: [PATCH 02/13] Add JSON based implementation --- pkgs/record_use/.github/workflows/test.yaml | 34 +++ pkgs/record_use/LICENSE | 27 +++ pkgs/record_use/README.md | 113 +++++++--- pkgs/record_use/analysis_options.yaml | 8 + .../example/record_use_example.dart | 8 +- pkgs/record_use/lib/record_use.dart | 11 +- pkgs/record_use/lib/record_use_internal.dart | 14 ++ .../lib/src/data_classes/annotation.dart | 44 ++++ .../lib/src/data_classes/arguments.dart | 123 +++++++++++ .../lib/src/data_classes/definition.dart | 56 +++++ .../lib/src/data_classes/field.dart | 44 ++++ .../lib/src/data_classes/identifier.dart | 41 ++++ .../lib/src/data_classes/location.dart | 44 ++++ .../lib/src/data_classes/metadata.dart | 49 +++++ .../lib/src/data_classes/reference.dart | 102 +++++++++ .../lib/src/data_classes/usage.dart | 57 +++++ .../lib/src/data_classes/usage_record.dart | 97 +++++++++ pkgs/record_use/lib/src/record_use.dart | 130 +++++++++++ pkgs/record_use/pubspec.yaml | 5 +- pkgs/record_use/test/record_use_test.dart | 206 +++++++++++++++++- 20 files changed, 1172 insertions(+), 41 deletions(-) create mode 100644 pkgs/record_use/.github/workflows/test.yaml create mode 100644 pkgs/record_use/LICENSE create mode 100644 pkgs/record_use/lib/record_use_internal.dart create mode 100644 pkgs/record_use/lib/src/data_classes/annotation.dart create mode 100644 pkgs/record_use/lib/src/data_classes/arguments.dart create mode 100644 pkgs/record_use/lib/src/data_classes/definition.dart create mode 100644 pkgs/record_use/lib/src/data_classes/field.dart create mode 100644 pkgs/record_use/lib/src/data_classes/identifier.dart create mode 100644 pkgs/record_use/lib/src/data_classes/location.dart create mode 100644 pkgs/record_use/lib/src/data_classes/metadata.dart create mode 100644 pkgs/record_use/lib/src/data_classes/reference.dart create mode 100644 pkgs/record_use/lib/src/data_classes/usage.dart create mode 100644 pkgs/record_use/lib/src/data_classes/usage_record.dart create mode 100644 pkgs/record_use/lib/src/record_use.dart diff --git a/pkgs/record_use/.github/workflows/test.yaml b/pkgs/record_use/.github/workflows/test.yaml new file mode 100644 index 000000000..090666b70 --- /dev/null +++ b/pkgs/record_use/.github/workflows/test.yaml @@ -0,0 +1,34 @@ +name: test +permissions: read-all + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + schedule: + - cron: '0 0 * * 0' # weekly + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + sdk: [stable, dev] + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 + - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 + with: + sdk: ${{matrix.sdk}} + + - run: dart pub get + + - run: dart analyze --fatal-infos + + - run: dart format --output=none --set-exit-if-changed . + + - run: dart test + + - name: Run Chrome tests - wasm + run: dart test --platform chrome --compiler dart2wasm + if: always() && matrix.sdk == 'dev' diff --git a/pkgs/record_use/LICENSE b/pkgs/record_use/LICENSE new file mode 100644 index 000000000..b03a78868 --- /dev/null +++ b/pkgs/record_use/LICENSE @@ -0,0 +1,27 @@ +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. diff --git a/pkgs/record_use/README.md b/pkgs/record_use/README.md index 8831761b8..8ed1d4884 100644 --- a/pkgs/record_use/README.md +++ b/pkgs/record_use/README.md @@ -1,39 +1,102 @@ - +> [!CAUTION] +> This is an experimental package, and it's API can break at any time. Use at +> your own discretion. -TODO: Put a short description of the package here that helps potential users -know whether this package might be useful for them. +This package provides the data classes for the usage recording feature in the +Dart SDK. -## Features +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. -TODO: List what your package can do. Maybe include images, gifs, or videos. +- If placed on a static method, the annotation means that calls to the method +are being recorded. If the `arguments` parameter is set to `true`, then +arguments will also 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 placing -## Getting started +## 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; + } +} -TODO: List prerequisites and provide or point to information on how to -start using the package. +@RecordUse() +class RecordMetadata { + final Object metadata; -## Usage + const RecordMetadata(this.metadata); +} -TODO: Include short and useful examples for package users. Add longer examples -to `/example` folder. +``` +This code will generate a JSON 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 -const like = 'sample'; +import 'package:native_assets_cli/native_assets_cli.dart'; + +void main(List arguments){ + link(arguments, (config, output) async { + final uses = config.recordedUses; + + final args = uses.callReferencesTo(boolMetadataId)); + //[args] is an iterable of [Argument] classes, in this case containing "42" + + final fields = uses.instanceReferencesTo(recordMetadataId); + //[fields] is an iterable of [Field] classes, in this case containing + // {"arguments": "leroyjenkins"} + // {"arguments": 3.14} + // {"arguments": 42} + // {"arguments": true} + + ... // Do something with the information, such as tree-shaking native assets + }); +} ``` -## Additional information +## Installation +To install the record_use package, run the following command: + +```bash +dart pub add record_use +``` -TODO: Tell users more about the package: where to find more information, how to -contribute to the package, how to file issues, what response they can expect -from the package authors, and more. +## Contributing +Contributions are welcome! Please open an issue or submit a pull request. \ No newline at end of file diff --git a/pkgs/record_use/analysis_options.yaml b/pkgs/record_use/analysis_options.yaml index d342b5770..b841ea8dc 100644 --- a/pkgs/record_use/analysis_options.yaml +++ b/pkgs/record_use/analysis_options.yaml @@ -7,3 +7,11 @@ analyzer: strict-casts: true strict-inference: true strict-raw-types: true + +linter: + rules: + - conditional_uri_does_not_exist + - prefer_const_constructors + - prefer_final_locals + - prefer_relative_imports + - unnecessary_parenthesis diff --git a/pkgs/record_use/example/record_use_example.dart b/pkgs/record_use/example/record_use_example.dart index d3c2906e5..5a0c3e71c 100644 --- a/pkgs/record_use/example/record_use_example.dart +++ b/pkgs/record_use/example/record_use_example.dart @@ -4,7 +4,9 @@ import 'package:record_use/record_use.dart'; -void main() { - var awesome = Awesome(); - print('awesome: ${awesome.isAwesome}'); +void doStuff(RecordUse usage, Identifier callId, Identifier referenceId) { + print(usage.metadata); + print(usage.callReferencesTo(callId)); + print(usage.instanceReferencesTo(referenceId)); + print(usage.hasNonConstArguments(callId)); } diff --git a/pkgs/record_use/lib/record_use.dart b/pkgs/record_use/lib/record_use.dart index ffa658ec2..483b6388b 100644 --- a/pkgs/record_use/lib/record_use.dart +++ b/pkgs/record_use/lib/record_use.dart @@ -1,5 +1,12 @@ -// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// 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/record_use_base.dart'; +export 'src/data_classes/arguments.dart' + show Arguments, ConstArguments, NonConstArguments; +export 'src/data_classes/field.dart' show Field; +export 'src/data_classes/identifier.dart' show Identifier; +export 'src/data_classes/location.dart' show Location; +export 'src/data_classes/metadata.dart' show Metadata; +export 'src/data_classes/reference.dart' show CallReference, InstanceReference; +export 'src/record_use.dart' show RecordUse; diff --git a/pkgs/record_use/lib/record_use_internal.dart b/pkgs/record_use/lib/record_use_internal.dart new file mode 100644 index 000000000..61523b3d1 --- /dev/null +++ b/pkgs/record_use/lib/record_use_internal.dart @@ -0,0 +1,14 @@ +// 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/data_classes/annotation.dart'; +export 'src/data_classes/arguments.dart'; +export 'src/data_classes/definition.dart'; +export 'src/data_classes/field.dart'; +export 'src/data_classes/identifier.dart'; +export 'src/data_classes/location.dart'; +export 'src/data_classes/metadata.dart'; +export 'src/data_classes/reference.dart'; +export 'src/data_classes/usage.dart'; +export 'src/data_classes/usage_record.dart'; diff --git a/pkgs/record_use/lib/src/data_classes/annotation.dart b/pkgs/record_use/lib/src/data_classes/annotation.dart new file mode 100644 index 000000000..8f27f324c --- /dev/null +++ b/pkgs/record_use/lib/src/data_classes/annotation.dart @@ -0,0 +1,44 @@ +// 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'; + +import 'identifier.dart'; + +class Annotation { + final Identifier identifier; + final Map fields; + + Annotation({ + required this.identifier, + required this.fields, + }); + + factory Annotation.fromJson( + Map json, + List identifiers, + ) => + Annotation( + identifier: identifiers[json['id'] as int], + fields: json['fields'] as Map, + ); + + Map toJson(List identifiers) => { + 'id': identifiers.indexOf(identifier), + 'fields': fields, + }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + final mapEquals = const DeepCollectionEquality().equals; + + return other is Annotation && + other.identifier == identifier && + mapEquals(other.fields, fields); + } + + @override + int get hashCode => identifier.hashCode ^ fields.hashCode; +} diff --git a/pkgs/record_use/lib/src/data_classes/arguments.dart b/pkgs/record_use/lib/src/data_classes/arguments.dart new file mode 100644 index 000000000..1f681dc47 --- /dev/null +++ b/pkgs/record_use/lib/src/data_classes/arguments.dart @@ -0,0 +1,123 @@ +// 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'; + +class Arguments { + ConstArguments constArguments; + NonConstArguments nonConstArguments; + + Arguments({ + ConstArguments? constArguments, + NonConstArguments? nonConstArguments, + }) : constArguments = constArguments ?? ConstArguments(), + nonConstArguments = nonConstArguments ?? NonConstArguments(); + + factory Arguments.fromJson(Map json) { + final constJson = json['const'] as Map?; + final nonConstJson = json['nonConst'] as Map?; + return Arguments( + constArguments: + constJson != null ? ConstArguments.fromJson(constJson) : null, + nonConstArguments: nonConstJson != null + ? NonConstArguments.fromJson(nonConstJson) + : null, + ); + } + + Map toJson() { + final hasConst = + constArguments.named.isNotEmpty || constArguments.positional.isNotEmpty; + final hasNonConst = nonConstArguments.named.isNotEmpty || + nonConstArguments.positional.isNotEmpty; + return { + if (hasConst) 'const': constArguments.toJson(), + if (hasNonConst) 'nonConst': nonConstArguments.toJson(), + }; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Arguments && + other.constArguments == constArguments && + other.nonConstArguments == nonConstArguments; + } + + @override + int get hashCode => constArguments.hashCode ^ nonConstArguments.hashCode; +} + +class ConstArguments { + Map positional; + Map named; + + ConstArguments({Map? positional, Map? named}) + : named = named ?? {}, + positional = positional ?? {}; + + factory ConstArguments.fromJson(Map json) => ConstArguments( + positional: json['positional'] != null + ? (json['positional'] as Map) + .map((key, value) => MapEntry(int.parse(key), value)) + : {}, + named: + json['named'] != null ? json['named'] as Map : {}, + ); + + Map toJson() => { + if (positional.isNotEmpty) + 'positional': + positional.map((key, value) => MapEntry(key.toString(), value)), + if (named.isNotEmpty) 'named': named, + }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + final mapEquals = const DeepCollectionEquality().equals; + + return other is ConstArguments && + mapEquals(other.positional, positional) && + mapEquals(other.named, named); + } + + @override + int get hashCode => positional.hashCode ^ named.hashCode; +} + +class NonConstArguments { + List positional; + List named; // Assuming named arguments are strings (keys) + + NonConstArguments({List? positional, List? named}) + : named = named ?? [], + positional = positional ?? []; + + factory NonConstArguments.fromJson(Map json) => + NonConstArguments( + positional: + json['positional'] != null ? json['positional'] as List : [], + named: json['named'] != null ? json['named'] as List : [], + ); + + Map toJson() => { + if (positional.isNotEmpty) 'positional': positional, + if (named.isNotEmpty) 'named': named, + }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + final listEquals = const DeepCollectionEquality().equals; + + return other is NonConstArguments && + listEquals(other.positional, positional) && + listEquals(other.named, named); + } + + @override + int get hashCode => positional.hashCode ^ named.hashCode; +} diff --git a/pkgs/record_use/lib/src/data_classes/definition.dart b/pkgs/record_use/lib/src/data_classes/definition.dart new file mode 100644 index 000000000..06c811d1c --- /dev/null +++ b/pkgs/record_use/lib/src/data_classes/definition.dart @@ -0,0 +1,56 @@ +// 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'; +import 'location.dart'; + +class Definition { + final Identifier identifier; + final Location location; // Represents the '@' field in the JSON + final String? loadingUnit; + + Definition({ + required this.identifier, + required this.location, + this.loadingUnit, + }); + + factory Definition.fromJson( + Map json, + List identifiers, + ) { + final identifier = identifiers[json['id'] as int]; + return Definition( + identifier: identifier, + location: Location.fromJson( + json['@'] as Map, + identifier.uri, + null, + ), + loadingUnit: json['loadingUnit'] as String?, + ); + } + + Map toJson( + List identifiers, + List uris, + ) => + { + 'id': identifiers.indexOf(identifier), + '@': location.toJson(), + 'loadingUnit': 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 => identifier.hashCode ^ loadingUnit.hashCode; +} diff --git a/pkgs/record_use/lib/src/data_classes/field.dart b/pkgs/record_use/lib/src/data_classes/field.dart new file mode 100644 index 000000000..13e7b5c39 --- /dev/null +++ b/pkgs/record_use/lib/src/data_classes/field.dart @@ -0,0 +1,44 @@ +// 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 Field { + final String className; + final String name; + final Object? value; + + Field({ + required this.className, + required this.name, + required this.value, + }); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Field && + other.className == className && + other.name == name && + other.value == value; + } + + @override + int get hashCode => className.hashCode ^ name.hashCode ^ value.hashCode; + + Map toJson() { + return { + 'className': className, + 'name': name, + 'value': value, + }; + } + + factory Field.fromJson(Map map) { + return Field( + className: map['className'] as String, + name: map['name'] as String, + value: map['value'], + ); + } +} diff --git a/pkgs/record_use/lib/src/data_classes/identifier.dart b/pkgs/record_use/lib/src/data_classes/identifier.dart new file mode 100644 index 000000000..e5737e335 --- /dev/null +++ b/pkgs/record_use/lib/src/data_classes/identifier.dart @@ -0,0 +1,41 @@ +// 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 Identifier { + String uri; + String? parent; // Optional since not all elements have parents + String name; + + Identifier({ + required this.uri, + this.parent, + required this.name, + }); + + factory Identifier.fromJson(Map json, List uris) => + Identifier( + uri: uris[json['uri'] as int], + parent: json['parent'] as String?, + name: json['name'] as String, + ); + + Map toJson(List uris) => { + 'uri': uris.indexOf(uri), + if (parent != null) 'parent': parent, + 'name': name, + }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Identifier && + other.uri == uri && + other.parent == parent && + other.name == name; + } + + @override + int get hashCode => uri.hashCode ^ parent.hashCode ^ name.hashCode; +} diff --git a/pkgs/record_use/lib/src/data_classes/location.dart b/pkgs/record_use/lib/src/data_classes/location.dart new file mode 100644 index 000000000..6cd9d88d9 --- /dev/null +++ b/pkgs/record_use/lib/src/data_classes/location.dart @@ -0,0 +1,44 @@ +// 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 { + String uri; + int line; + int column; + Location({ + required this.uri, + required this.line, + required this.column, + }); + + factory Location.fromJson( + Map map, String? uri, List? uris) { + return Location( + uri: uri ?? uris![map['uri'] as int], + line: map['line'] as int, + column: map['column'] as int, + ); + } + + Map toJson({List? uris}) { + return { + if (uris != null) 'uri': uris.indexOf(uri), + 'line': line, + 'column': 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 => uri.hashCode ^ line.hashCode ^ column.hashCode; +} diff --git a/pkgs/record_use/lib/src/data_classes/metadata.dart b/pkgs/record_use/lib/src/data_classes/metadata.dart new file mode 100644 index 000000000..d2aee07be --- /dev/null +++ b/pkgs/record_use/lib/src/data_classes/metadata.dart @@ -0,0 +1,49 @@ +// 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'; + +class Metadata { + final String? comment; + final Version version; + + Metadata({ + this.comment, + required this.version, + }); + + factory Metadata.fromJson(Map json) => Metadata( + comment: json['comment'] as String?, + version: Version.parse(json['version'] as String), + ); + + Map toJson() => { + if (comment != null) 'comment': comment, + 'version': version.toString(), + }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Metadata && + other.comment == comment && + other.version == version; + } + + @override + int get hashCode { + return comment.hashCode ^ version.hashCode; + } + + @override + String toString() { + return ''' +Metadata( + comment: $comment, + version: $version, +) +'''; + } +} diff --git a/pkgs/record_use/lib/src/data_classes/reference.dart b/pkgs/record_use/lib/src/data_classes/reference.dart new file mode 100644 index 000000000..367cacf78 --- /dev/null +++ b/pkgs/record_use/lib/src/data_classes/reference.dart @@ -0,0 +1,102 @@ +// 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 'arguments.dart'; +import 'field.dart'; +import 'location.dart'; + +sealed class Reference { + final String? loadingUnit; + // Represents the "@" field in the JSON + + final Location location; + + const Reference({this.loadingUnit, required this.location}); + + Map toJson(List uris) => { + 'loadingUnit': loadingUnit, + '@': location.toJson(uris: uris), + }; + + @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 => loadingUnit.hashCode ^ location.hashCode; +} + +final class CallReference extends Reference { + final Arguments? arguments; + + const CallReference({ + required this.arguments, + super.loadingUnit, + required super.location, + }); + + factory CallReference.fromJson(Map json, List uris) { + return CallReference( + arguments: json['arguments'] != null + ? Arguments.fromJson(json['arguments'] as Map) + : null, + loadingUnit: json['loadingUnit'] as String?, + location: + Location.fromJson(json['@'] as Map, null, uris), + ); + } + + @override + Map toJson(List uris) { + final argumentJson = arguments?.toJson() ?? {}; + return { + if (argumentJson.isNotEmpty) 'arguments': argumentJson, + ...super.toJson(uris), + }; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is CallReference && other.arguments == arguments; + } + + @override + int get hashCode => arguments.hashCode; +} + +final class InstanceReference extends Reference { + final List fields; + + InstanceReference({ + super.loadingUnit, + required super.location, + required this.fields, + }); + + factory InstanceReference.fromJson( + Map json, List uris) { + return InstanceReference( + loadingUnit: json['loadingUnit'] as String?, + location: + Location.fromJson(json['@'] as Map, null, uris), + fields: (json['fields'] as List) + .map((fieldStr) => Field.fromJson(fieldStr as Map)) + .toList(), + ); + } + + @override + Map toJson(List uris) => { + if (fields.isNotEmpty) + 'fields': fields.map((field) => field.toJson()).toList(), + ...super.toJson(uris), + }; +} diff --git a/pkgs/record_use/lib/src/data_classes/usage.dart b/pkgs/record_use/lib/src/data_classes/usage.dart new file mode 100644 index 000000000..67abd4981 --- /dev/null +++ b/pkgs/record_use/lib/src/data_classes/usage.dart @@ -0,0 +1,57 @@ +// 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'; + +import 'definition.dart'; +import 'identifier.dart'; +import 'reference.dart'; + +class Usage { + final Definition definition; + final List references; + + Usage({ + required this.definition, + required this.references, + }); + + factory Usage.fromJson( + Map json, + List identifiers, + List uris, + T Function(Map, List) constr, + ) => + Usage( + definition: Definition.fromJson( + json['definition'] as Map, + identifiers, + ), + references: (json['references'] as List) + .map((x) => constr(x as Map, uris)) + .toList(), + ); + + Map toJson( + List identifiers, + List uris, + ) => + { + 'definition': definition.toJson(identifiers, uris), + 'references': references.map((x) => x.toJson(uris)).toList(), + }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + final listEquals = const DeepCollectionEquality().equals; + + return other is Usage && + other.definition == definition && + listEquals(other.references, references); + } + + @override + int get hashCode => definition.hashCode ^ references.hashCode; +} diff --git a/pkgs/record_use/lib/src/data_classes/usage_record.dart b/pkgs/record_use/lib/src/data_classes/usage_record.dart new file mode 100644 index 000000000..dd9371a16 --- /dev/null +++ b/pkgs/record_use/lib/src/data_classes/usage_record.dart @@ -0,0 +1,97 @@ +// 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'; + +import 'identifier.dart'; +import 'metadata.dart'; +import 'reference.dart'; +import 'usage.dart'; + +class UsageRecord { + final Metadata metadata; + final List> calls; + final List> instances; + + UsageRecord({ + required this.metadata, + required this.calls, + required this.instances, + }); + + factory UsageRecord.fromJson(Map json) { + final uris = json['uris'] as List; + final identifiers = (json['ids'] as List) + .whereType>() + .map( + (e) => Identifier.fromJson(e, uris), + ) + .toList(); + return UsageRecord( + metadata: Metadata.fromJson(json['metadata'] as Map), + calls: (json['calls'] as List?) + ?.map((x) => Usage.fromJson( + x as Map, + identifiers, + uris, + CallReference.fromJson, + )) + .toList() ?? + [], + instances: (json['instances'] as List?) + ?.map((x) => Usage.fromJson( + x as Map, + identifiers, + uris, + InstanceReference.fromJson, + )) + .toList() ?? + [], + ); + } + + Map toJson() { + final identifiers = { + ...calls.map((call) => call.definition.identifier), + ...instances.map((instance) => instance.definition.identifier), + }.toList(); + final uris = { + ...identifiers.map((e) => e.uri), + ...calls.expand((call) => [ + call.definition.location.uri, + ...call.references.map((reference) => reference.location.uri), + ]), + ...instances.expand((instance) => [ + instance.definition.location.uri, + ...instance.references.map((reference) => reference.location.uri), + ]), + }.toList(); + return { + 'metadata': metadata.toJson(), + 'uris': uris, + 'ids': identifiers.map((identifier) => identifier.toJson(uris)).toList(), + if (calls.isNotEmpty) + 'calls': calls + .map((reference) => reference.toJson(identifiers, uris)) + .toList(), + if (instances.isNotEmpty) + 'instances': instances + .map((reference) => reference.toJson(identifiers, uris)) + .toList(), + }; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + final listEquals = const DeepCollectionEquality().equals; + + return other is UsageRecord && + other.metadata == metadata && + listEquals(other.calls, calls); + } + + @override + int get hashCode => metadata.hashCode ^ calls.hashCode; +} diff --git a/pkgs/record_use/lib/src/record_use.dart b/pkgs/record_use/lib/src/record_use.dart new file mode 100644 index 000000000..ef52d5d63 --- /dev/null +++ b/pkgs/record_use/lib/src/record_use.dart @@ -0,0 +1,130 @@ +// 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'; + +import 'data_classes/arguments.dart'; +import 'data_classes/field.dart'; +import 'data_classes/identifier.dart'; +import 'data_classes/metadata.dart'; +import 'data_classes/reference.dart'; +import 'data_classes/usage.dart'; +import 'data_classes/usage_record.dart'; + +extension type RecordUse._(UsageRecord _recordUses) { + RecordUse.fromJson(Map json) + : this._(UsageRecord.fromJson(json)); + + /// Show the metadata for this recording of usages. + Metadata get metadata => _recordUses.metadata; + + /// Finds all arguments for calls to the [definition]. + /// + /// 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 `null`. + /// + /// 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 + /// ``` + /// argumentsForCallsTo(Identifier( + /// uri: 'path/to/file.dart', + /// parent: 'SomeClass', + /// name: 'someStaticMethod'), + /// ).first == + /// Arguments( + /// constArguments: ConstArguments(positional: {1: 42}), + /// ); + /// ``` + Iterable? callReferencesTo(Identifier definition) => + _callTo(definition) + ?.references + .map((reference) => reference.arguments) + .whereType(); + + /// Finds all fields of a const instance of the class at [definition]. + /// + /// 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 `null`. + /// + /// The types of fields supported are defined at + /// TODO: insert reference to the supported field types for serialization + /// + /// Example: + /// ```dart + /// void main() { + /// print(SomeClass.someStaticMethod(42)); + /// } + /// + /// class SomeClass { + /// @AnnotationClass('freddie') + /// static someStaticMethod(int i) { + /// return i + 1; + /// } + /// } + /// + /// @ResourceIdentifier() + /// class AnnotationClass { + /// final String s; + /// const AnnotationClass(this.s); + /// } + /// ``` + /// + /// Would mean that + /// ``` + /// fieldsForConstructionOf(Identifier( + /// uri: 'path/to/file.dart', + /// name: 'AnnotationClass'), + /// ).first == + /// [ + /// Field(name: "s", className: "AnnotationClass", value: "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>? instanceReferencesTo(Identifier definition) => + _recordUses.instances + .firstWhereOrNull( + (instance) => instance.definition.identifier == definition) + ?.references + .map((reference) => reference.fields); + + /// Checks if any call to [definition] has non-const arguments. + /// + /// 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 definition) => + _callTo(definition)?.references.any( + (reference) { + final nonConstArguments = reference.arguments?.nonConstArguments; + final hasNamed = nonConstArguments?.named.isNotEmpty ?? false; + final hasPositional = + nonConstArguments?.positional.isNotEmpty ?? false; + return hasNamed || hasPositional; + }, + ) ?? + false; + + Usage? _callTo(Identifier definition) => _recordUses.calls + .firstWhereOrNull((call) => call.definition.identifier == definition); +} diff --git a/pkgs/record_use/pubspec.yaml b/pkgs/record_use/pubspec.yaml index ce4d16c99..2429a8eb9 100644 --- a/pkgs/record_use/pubspec.yaml +++ b/pkgs/record_use/pubspec.yaml @@ -5,10 +5,11 @@ version: 0.1.0 repository: https://github.com/dart-lang/native/tree/main/pkgs/record_use environment: - sdk: ^3.5.0 + sdk: ^3.4.0 dependencies: - path: ^1.8.0 + collection: ^1.18.0 + pub_semver: ^2.1.4 dev_dependencies: dart_flutter_team_lints: ^3.2.0 diff --git a/pkgs/record_use/test/record_use_test.dart b/pkgs/record_use/test/record_use_test.dart index c29da356d..2adbfb1f3 100644 --- a/pkgs/record_use/test/record_use_test.dart +++ b/pkgs/record_use/test/record_use_test.dart @@ -2,19 +2,207 @@ // 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.dart'; +import 'package:record_use/record_use_internal.dart'; import 'package:test/test.dart'; void main() { - group('A group of tests', () { - final awesome = Awesome(); - - setUp(() { - // Additional setup goes here. - }); + test('Json->Object->Json', () { + expect(UsageRecord.fromJson(recordedUsesJson).toJson(), recordedUsesJson); + }); + test('Object->Json->Object', () { + expect(UsageRecord.fromJson(recordedUses.toJson()), recordedUses); + }); - test('First Test', () { - expect(awesome.isAwesome, isTrue); - }); + test('API calls', () { + expect( + RecordUse.fromJson(recordedUsesJson).callReferencesTo(callId), + recordedUses.calls.expand((e) => e.references).map((e) => e.arguments), + ); + }); + test('API instances', () { + expect( + RecordUse.fromJson(recordedUsesJson).instanceReferencesTo(instanceId), + recordedUses.instances.expand((e) => e.references).map((e) => e.fields), + ); }); } + +final callId = Identifier( + uri: Uri.parse('file://lib/_internal/js_runtime/lib/js_helper.dart') + .toString(), + parent: 'MyClass', + name: 'get:loadDeferredLibrary', +); +final instanceId = Identifier( + uri: Uri.parse('file://lib/_internal/js_runtime/lib/js_helper.dart') + .toString(), + name: 'MyAnnotation', +); + +final recordedUses = UsageRecord( + metadata: Metadata( + version: Version(1, 6, 2, pre: 'wip', build: '5.-.2.z'), + comment: + 'Recorded references at compile time and their argument values, as far' + ' as known, to definitions annotated with @RecordReference', + ), + instances: [ + Usage( + definition: Definition( + identifier: instanceId, + location: Location( + uri: Uri.parse('file://lib/_internal/js_runtime/lib/js_helper.dart') + .toString(), + line: 15, + column: 30, + ), + ), + references: [ + InstanceReference( + location: Location( + uri: Uri.parse('file://lib/_internal/js_runtime/lib/js_helper.dart') + .toString(), + line: 40, + column: 30, + ), + fields: [ + Field( + className: 'className', + name: 'a', + value: 42, + ), + ], + loadingUnit: '3', + ), + ], + ), + ], + calls: [ + Usage( + definition: Definition( + identifier: callId, + location: Location( + uri: Uri.parse('file://lib/_internal/js_runtime/lib/js_helper.dart') + .toString(), + line: 12, + column: 67, + ), + loadingUnit: 'part_15.js', + ), + references: [ + CallReference( + arguments: Arguments( + constArguments: ConstArguments( + positional: {0: 'lib_SHA1', 1: false, 2: 1}, + named: {'leroy': 'jenkins', 'freddy': 'mercury'}, + ), + ), + location: Location( + uri: Uri.parse( + 'file://benchmarks/OmnibusDeferred/dart/OmnibusDeferred.dart') + .toString(), + line: 14, + column: 49, + ), + loadingUnit: 'o.js', + ), + CallReference( + arguments: Arguments( + constArguments: ConstArguments( + positional: {0: 'lib_SHA1', 2: 0}, + named: {'leroy': 'jenkins'}, + ), + nonConstArguments: NonConstArguments( + positional: [1], + named: ['freddy'], + ), + ), + location: Location( + uri: Uri.parse( + 'file://benchmarks/OmnibusDeferred/dart/OmnibusDeferred.dart') + .toString(), + line: 14, + column: 48, + ), + loadingUnit: 'o.js', + ), + ], + ), + ], +); + +final recordedUsesJson = { + 'metadata': { + 'comment': + 'Recorded references at compile time and their argument values, as far' + ' as known, to definitions annotated with @RecordReference', + 'version': '1.6.2-wip+5.-.2.z' + }, + 'uris': [ + 'file://lib/_internal/js_runtime/lib/js_helper.dart', + 'file://benchmarks/OmnibusDeferred/dart/OmnibusDeferred.dart' + ], + 'ids': [ + {'uri': 0, 'parent': 'MyClass', 'name': 'get:loadDeferredLibrary'}, + {'uri': 0, 'name': 'MyAnnotation'} + ], + 'calls': [ + { + 'definition': { + 'id': 0, + '@': {'line': 12, 'column': 67}, + 'loadingUnit': 'part_15.js' + }, + 'references': [ + { + 'arguments': { + 'const': { + 'positional': {'0': 'lib_SHA1', '1': false, '2': 1}, + 'named': {'leroy': 'jenkins', 'freddy': 'mercury'} + } + }, + 'loadingUnit': 'o.js', + '@': {'uri': 1, 'line': 14, 'column': 49} + }, + { + 'arguments': { + 'const': { + 'positional': {'0': 'lib_SHA1', '2': 0}, + 'named': {'leroy': 'jenkins'} + }, + 'nonConst': { + 'positional': [1], + 'named': ['freddy'] + } + }, + 'loadingUnit': 'o.js', + '@': {'uri': 1, 'line': 14, 'column': 48} + } + ] + } + ], + 'instances': [ + { + 'definition': { + 'id': 1, + '@': {'line': 15, 'column': 30}, + 'loadingUnit': null + }, + 'references': [ + { + 'fields': [ + { + 'className': 'className', + 'name': 'a', + 'value': 42, + } + ], + 'loadingUnit': '3', + '@': {'uri': 0, 'line': 40, 'column': 30} + } + ] + } + ] +}; From 3d1f6e66314ca715df6466ef3351de70ee69eb3a Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 29 Aug 2024 15:23:01 +0200 Subject: [PATCH 03/13] Some fixes --- pkgs/record_use/README.md | 32 ++++++--- .../example/record_use_example.dart | 6 +- pkgs/record_use/lib/record_use.dart | 2 +- pkgs/record_use/lib/src/record_use.dart | 46 ++++++------- ...record_use_test.dart => storage_test.dart} | 36 +++++----- pkgs/record_use/test/usage_test.dart | 66 +++++++++++++++++++ 6 files changed, 130 insertions(+), 58 deletions(-) rename pkgs/record_use/test/{record_use_test.dart => storage_test.dart} (88%) create mode 100644 pkgs/record_use/test/usage_test.dart diff --git a/pkgs/record_use/README.md b/pkgs/record_use/README.md index 8ed1d4884..b2baf1de4 100644 --- a/pkgs/record_use/README.md +++ b/pkgs/record_use/README.md @@ -12,14 +12,14 @@ 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 calls to the method -are being recorded. If the `arguments` parameter is set to `true`, then -arguments will also be recorded, as far as they can be inferred at compile time. +- 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 placing +when using the class as an annotation. ## Example + ```dart import 'package:meta/meta.dart' show RecordUse; @@ -64,7 +64,7 @@ class RecordMetadata { } ``` -This code will generate a JSON file that contains both the `metadata` values of +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()`. @@ -76,11 +76,12 @@ void main(List arguments){ link(arguments, (config, output) async { final uses = config.recordedUses; - final args = uses.callReferencesTo(boolMetadataId)); - //[args] is an iterable of [Argument] classes, in this case containing "42" + final args = uses.argumentsTo(boolMetadataId)); + //[args] is an iterable of arguments, in this case containing "42" - final fields = uses.instanceReferencesTo(recordMetadataId); - //[fields] is an iterable of [Field] classes, in this case containing + final fields = uses.instancesOf(recordMetadataId); + //[fields] is an iterable of the fields of the class, in this case + //containing // {"arguments": "leroyjenkins"} // {"arguments": 3.14} // {"arguments": 42} @@ -98,5 +99,18 @@ To install the record_use package, run the following command: dart pub add record_use ``` +## Internals + +The data is stored in protobuf format. Two schemas are provided: + +### [usages_read](lib/src/proto/usages_read.proto) +This is the schema for the internal API for the storage format, which is used +in the SDK for writing the data, and in the [record_use](lib/src/record_use.dart) format for retrieving the +data for the queries from the user. + +### [usages_storage](lib/src/proto/usages_storage.proto) +This schema is for the storage of the data, and contains some optimizations such +as collecting all URIs in a table, to avoid repetitions. + ## Contributing Contributions are welcome! Please open an issue or submit a pull request. \ No newline at end of file diff --git a/pkgs/record_use/example/record_use_example.dart b/pkgs/record_use/example/record_use_example.dart index 5a0c3e71c..f3359ccd6 100644 --- a/pkgs/record_use/example/record_use_example.dart +++ b/pkgs/record_use/example/record_use_example.dart @@ -4,9 +4,9 @@ import 'package:record_use/record_use.dart'; -void doStuff(RecordUse usage, Identifier callId, Identifier referenceId) { +void doStuff(RecordedUsages usage, Identifier callId, Identifier referenceId) { print(usage.metadata); - print(usage.callReferencesTo(callId)); - print(usage.instanceReferencesTo(referenceId)); + print(usage.argumentsTo(callId)); + print(usage.instancesOf(referenceId)); print(usage.hasNonConstArguments(callId)); } diff --git a/pkgs/record_use/lib/record_use.dart b/pkgs/record_use/lib/record_use.dart index 483b6388b..51ef00e6c 100644 --- a/pkgs/record_use/lib/record_use.dart +++ b/pkgs/record_use/lib/record_use.dart @@ -9,4 +9,4 @@ export 'src/data_classes/identifier.dart' show Identifier; export 'src/data_classes/location.dart' show Location; export 'src/data_classes/metadata.dart' show Metadata; export 'src/data_classes/reference.dart' show CallReference, InstanceReference; -export 'src/record_use.dart' show RecordUse; +export 'src/record_use.dart' show RecordedUsages; diff --git a/pkgs/record_use/lib/src/record_use.dart b/pkgs/record_use/lib/src/record_use.dart index ef52d5d63..aa85bf4eb 100644 --- a/pkgs/record_use/lib/src/record_use.dart +++ b/pkgs/record_use/lib/src/record_use.dart @@ -4,22 +4,16 @@ import 'package:collection/collection.dart'; -import 'data_classes/arguments.dart'; -import 'data_classes/field.dart'; -import 'data_classes/identifier.dart'; -import 'data_classes/metadata.dart'; -import 'data_classes/reference.dart'; -import 'data_classes/usage.dart'; -import 'data_classes/usage_record.dart'; +import '../record_use_internal.dart'; -extension type RecordUse._(UsageRecord _recordUses) { - RecordUse.fromJson(Map json) +extension type RecordedUsages._(UsageRecord _usages) { + RecordedUsages.fromJson(Map json) : this._(UsageRecord.fromJson(json)); /// Show the metadata for this recording of usages. - Metadata get metadata => _recordUses.metadata; + Metadata get metadata => _usages.metadata; - /// Finds all arguments for calls to the [definition]. + /// Finds all const arguments for calls to the [method]. /// /// The definition must be annotated with `@RecordUse()`. If there are no /// calls to the definition, either because it was treeshaken, because it was @@ -44,7 +38,7 @@ extension type RecordUse._(UsageRecord _recordUses) { /// /// Would mean that /// ``` - /// argumentsForCallsTo(Identifier( + /// argumentsTo(Identifier( /// uri: 'path/to/file.dart', /// parent: 'SomeClass', /// name: 'someStaticMethod'), @@ -53,20 +47,18 @@ extension type RecordUse._(UsageRecord _recordUses) { /// constArguments: ConstArguments(positional: {1: 42}), /// ); /// ``` - Iterable? callReferencesTo(Identifier definition) => - _callTo(definition) - ?.references - .map((reference) => reference.arguments) - .whereType(); + Iterable? argumentsTo(Identifier method) => _callTo(method) + ?.references + .map((reference) => reference.arguments) + .whereType(); - /// Finds all fields of a const instance of the class at [definition]. + /// Finds all fields of a const instance of the class at [classIdentifier]. /// /// 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 `null`. /// /// The types of fields supported are defined at - /// TODO: insert reference to the supported field types for serialization /// /// Example: /// ```dart @@ -90,7 +82,7 @@ extension type RecordUse._(UsageRecord _recordUses) { /// /// Would mean that /// ``` - /// fieldsForConstructionOf(Identifier( + /// instancesOf(Identifier( /// uri: 'path/to/file.dart', /// name: 'AnnotationClass'), /// ).first == @@ -101,20 +93,20 @@ extension type RecordUse._(UsageRecord _recordUses) { /// /// 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>? instanceReferencesTo(Identifier definition) => - _recordUses.instances + Iterable>? instancesOf(Identifier classIdentifier) => + _usages.instances .firstWhereOrNull( - (instance) => instance.definition.identifier == definition) + (instance) => instance.definition.identifier == classIdentifier) ?.references .map((reference) => reference.fields); - /// Checks if any call to [definition] has non-const arguments. + /// Checks if any call to [method] has non-const arguments. /// /// 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 definition) => - _callTo(definition)?.references.any( + bool hasNonConstArguments(Identifier method) => + _callTo(method)?.references.any( (reference) { final nonConstArguments = reference.arguments?.nonConstArguments; final hasNamed = nonConstArguments?.named.isNotEmpty ?? false; @@ -125,6 +117,6 @@ extension type RecordUse._(UsageRecord _recordUses) { ) ?? false; - Usage? _callTo(Identifier definition) => _recordUses.calls + Usage? _callTo(Identifier definition) => _usages.calls .firstWhereOrNull((call) => call.definition.identifier == definition); } diff --git a/pkgs/record_use/test/record_use_test.dart b/pkgs/record_use/test/storage_test.dart similarity index 88% rename from pkgs/record_use/test/record_use_test.dart rename to pkgs/record_use/test/storage_test.dart index 2adbfb1f3..8eeb2e54c 100644 --- a/pkgs/record_use/test/record_use_test.dart +++ b/pkgs/record_use/test/storage_test.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import 'package:pub_semver/pub_semver.dart'; -import 'package:record_use/record_use.dart'; import 'package:record_use/record_use_internal.dart'; import 'package:test/test.dart'; @@ -14,19 +13,6 @@ void main() { test('Object->Json->Object', () { expect(UsageRecord.fromJson(recordedUses.toJson()), recordedUses); }); - - test('API calls', () { - expect( - RecordUse.fromJson(recordedUsesJson).callReferencesTo(callId), - recordedUses.calls.expand((e) => e.references).map((e) => e.arguments), - ); - }); - test('API instances', () { - expect( - RecordUse.fromJson(recordedUsesJson).instanceReferencesTo(instanceId), - recordedUses.instances.expand((e) => e.references).map((e) => e.fields), - ); - }); } final callId = Identifier( @@ -111,8 +97,15 @@ final recordedUses = UsageRecord( CallReference( arguments: Arguments( constArguments: ConstArguments( - positional: {0: 'lib_SHA1', 2: 0}, - named: {'leroy': 'jenkins'}, + positional: { + 0: 'lib_SHA1', + 2: 0, + 4: {'key': 99} + }, + named: { + 'leroy': 'jenkins', + 'albert': ['camus', 'einstein'] + }, ), nonConstArguments: NonConstArguments( positional: [1], @@ -169,8 +162,15 @@ final recordedUsesJson = { { 'arguments': { 'const': { - 'positional': {'0': 'lib_SHA1', '2': 0}, - 'named': {'leroy': 'jenkins'} + 'positional': { + '0': 'lib_SHA1', + '2': 0, + '4': {'key': 99} + }, + 'named': { + 'leroy': 'jenkins', + 'albert': ['camus', 'einstein'] + } }, 'nonConst': { 'positional': [1], diff --git a/pkgs/record_use/test/usage_test.dart b/pkgs/record_use/test/usage_test.dart new file mode 100644 index 000000000..877d18710 --- /dev/null +++ b/pkgs/record_use/test/usage_test.dart @@ -0,0 +1,66 @@ +// 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'; +import 'package:test/test.dart'; + +import 'storage_test.dart'; + +void main() { + test('All API calls', () { + expect( + RecordedUsages.fromJson(recordedUsesJson).argumentsTo(callId), + recordedUses.calls.expand((e) => e.references).map((e) => e.arguments), + ); + }); + test('All API instances', () { + expect( + RecordedUsages.fromJson(recordedUsesJson).instancesOf(instanceId), + recordedUses.instances.expand((e) => e.references).map((e) => e.fields), + ); + }); + test('Specific API calls', () { + final callId = Identifier( + uri: Uri.parse('file://lib/_internal/js_runtime/lib/js_helper.dart') + .toString(), + parent: 'MyClass', + name: 'get:loadDeferredLibrary', + ); + final arguments = + RecordedUsages.fromJson(recordedUsesJson).argumentsTo(callId)!.toList(); + expect( + arguments[0].constArguments.named, + {'leroy': 'jenkins', 'freddy': 'mercury'}, + ); + expect( + arguments[0].constArguments.positional, + {0: 'lib_SHA1', 1: false, 2: 1}, + ); + expect(arguments[1].constArguments.named, { + 'leroy': 'jenkins', + 'albert': ['camus', 'einstein'] + }); + expect(arguments[1].constArguments.positional, { + 0: 'lib_SHA1', + 2: 0, + 4: {'key': 99} + }); + }); + + test('Specific API instances', () { + final instanceId = Identifier( + uri: Uri.parse('file://lib/_internal/js_runtime/lib/js_helper.dart') + .toString(), + name: 'MyAnnotation', + ); + expect( + RecordedUsages.fromJson(recordedUsesJson) + .instancesOf(instanceId) + ?.first + .first + .value, + 42, + ); + }); +} From ad99f0c270bbb00a8046da7d6a252ba45b430750 Mon Sep 17 00:00:00 2001 From: Moritz Date: Fri, 30 Aug 2024 08:40:27 +0200 Subject: [PATCH 04/13] Address comments --- .github/workflows/record_use.yaml | 20 +++++++++++- pkgs/record_use/.github/workflows/test.yaml | 34 --------------------- pkgs/record_use/README.md | 20 ------------ 3 files changed, 19 insertions(+), 55 deletions(-) delete mode 100644 pkgs/record_use/.github/workflows/test.yaml diff --git a/.github/workflows/record_use.yaml b/.github/workflows/record_use.yaml index 05a2cc8f4..396b7b7ac 100644 --- a/.github/workflows/record_use.yaml +++ b/.github/workflows/record_use.yaml @@ -17,13 +17,16 @@ on: jobs: build: - runs-on: ubuntu-latest defaults: run: working-directory: pkgs/record_use/ strategy: matrix: + os: [ubuntu, windows] sdk: [stable, dev] + + runs-on: ${{ matrix.os }}-latest + steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 - uses: dart-lang/setup-dart@2986c8e337a31eb7b455ce93dc984e9bf5797756 @@ -37,3 +40,18 @@ jobs: - run: dart format --output=none --set-exit-if-changed . - run: dart test + + - name: Install coverage + run: dart pub global activate coverage + + - name: Collect coverage + run: dart pub global run coverage:test_with_coverage + + - name: Upload coverage + uses: coverallsapp/github-action@643bc377ffa44ace6394b2b5d0d3950076de9f63 + with: + flag-name: record_use + github-token: ${{ secrets.GITHUB_TOKEN }} + parallel: true + path-to-lcov: ./pkgs/jnigen/coverage/lcov.info + if: ${{ matrix.sdk == 'stable' }} \ No newline at end of file diff --git a/pkgs/record_use/.github/workflows/test.yaml b/pkgs/record_use/.github/workflows/test.yaml deleted file mode 100644 index 090666b70..000000000 --- a/pkgs/record_use/.github/workflows/test.yaml +++ /dev/null @@ -1,34 +0,0 @@ -name: test -permissions: read-all - -on: - pull_request: - branches: [ main ] - push: - branches: [ main ] - schedule: - - cron: '0 0 * * 0' # weekly - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - sdk: [stable, dev] - steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 - - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 - with: - sdk: ${{matrix.sdk}} - - - run: dart pub get - - - run: dart analyze --fatal-infos - - - run: dart format --output=none --set-exit-if-changed . - - - run: dart test - - - name: Run Chrome tests - wasm - run: dart test --platform chrome --compiler dart2wasm - if: always() && matrix.sdk == 'dev' diff --git a/pkgs/record_use/README.md b/pkgs/record_use/README.md index b2baf1de4..a324a14f9 100644 --- a/pkgs/record_use/README.md +++ b/pkgs/record_use/README.md @@ -92,25 +92,5 @@ void main(List arguments){ } ``` -## Installation -To install the record_use package, run the following command: - -```bash -dart pub add record_use -``` - -## Internals - -The data is stored in protobuf format. Two schemas are provided: - -### [usages_read](lib/src/proto/usages_read.proto) -This is the schema for the internal API for the storage format, which is used -in the SDK for writing the data, and in the [record_use](lib/src/record_use.dart) format for retrieving the -data for the queries from the user. - -### [usages_storage](lib/src/proto/usages_storage.proto) -This schema is for the storage of the data, and contains some optimizations such -as collecting all URIs in a table, to avoid repetitions. - ## Contributing Contributions are welcome! Please open an issue or submit a pull request. \ No newline at end of file From b1477d454ef2a3cc57f3f185f4ce496b216632b1 Mon Sep 17 00:00:00 2001 From: Moritz Date: Fri, 30 Aug 2024 09:24:32 +0200 Subject: [PATCH 05/13] fix typo --- .github/workflows/record_use.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/record_use.yaml b/.github/workflows/record_use.yaml index 396b7b7ac..75363e76d 100644 --- a/.github/workflows/record_use.yaml +++ b/.github/workflows/record_use.yaml @@ -53,5 +53,5 @@ jobs: flag-name: record_use github-token: ${{ secrets.GITHUB_TOKEN }} parallel: true - path-to-lcov: ./pkgs/jnigen/coverage/lcov.info + path-to-lcov: ./pkgs/record_use/coverage/lcov.info if: ${{ matrix.sdk == 'stable' }} \ No newline at end of file From 2d3e8c19833848243c0d3878136e36fcc78a912d Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 3 Sep 2024 09:49:43 +0200 Subject: [PATCH 06/13] Some changes --- pkgs/record_use/lib/record_use_internal.dart | 8 +------- pkgs/record_use/lib/src/data_classes/definition.dart | 4 +++- pkgs/record_use/lib/src/data_classes/reference.dart | 2 +- pkgs/record_use/lib/src/record_use.dart | 8 +++++++- pkgs/record_use/lib/src/record_use_base.dart | 5 ----- 5 files changed, 12 insertions(+), 15 deletions(-) delete mode 100644 pkgs/record_use/lib/src/record_use_base.dart diff --git a/pkgs/record_use/lib/record_use_internal.dart b/pkgs/record_use/lib/record_use_internal.dart index 61523b3d1..6887d6106 100644 --- a/pkgs/record_use/lib/record_use_internal.dart +++ b/pkgs/record_use/lib/record_use_internal.dart @@ -2,13 +2,7 @@ // 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/data_classes/annotation.dart'; -export 'src/data_classes/arguments.dart'; +export 'record_use.dart'; export 'src/data_classes/definition.dart'; -export 'src/data_classes/field.dart'; -export 'src/data_classes/identifier.dart'; -export 'src/data_classes/location.dart'; -export 'src/data_classes/metadata.dart'; -export 'src/data_classes/reference.dart'; export 'src/data_classes/usage.dart'; export 'src/data_classes/usage_record.dart'; diff --git a/pkgs/record_use/lib/src/data_classes/definition.dart b/pkgs/record_use/lib/src/data_classes/definition.dart index 06c811d1c..a7aaaae83 100644 --- a/pkgs/record_use/lib/src/data_classes/definition.dart +++ b/pkgs/record_use/lib/src/data_classes/definition.dart @@ -7,7 +7,9 @@ import 'location.dart'; class Definition { final Identifier identifier; - final Location location; // Represents the '@' field in the JSON + + /// Represents the '@' field in the JSON + final Location location; final String? loadingUnit; Definition({ diff --git a/pkgs/record_use/lib/src/data_classes/reference.dart b/pkgs/record_use/lib/src/data_classes/reference.dart index 367cacf78..4d1163ce5 100644 --- a/pkgs/record_use/lib/src/data_classes/reference.dart +++ b/pkgs/record_use/lib/src/data_classes/reference.dart @@ -8,8 +8,8 @@ import 'location.dart'; sealed class Reference { final String? loadingUnit; - // Represents the "@" field in the JSON + /// Represents the "@" field in the JSON final Location location; const Reference({this.loadingUnit, required this.location}); diff --git a/pkgs/record_use/lib/src/record_use.dart b/pkgs/record_use/lib/src/record_use.dart index aa85bf4eb..47ee28ea3 100644 --- a/pkgs/record_use/lib/src/record_use.dart +++ b/pkgs/record_use/lib/src/record_use.dart @@ -4,7 +4,13 @@ import 'package:collection/collection.dart'; -import '../record_use_internal.dart'; +import 'data_classes/arguments.dart'; +import 'data_classes/field.dart'; +import 'data_classes/identifier.dart'; +import 'data_classes/metadata.dart'; +import 'data_classes/reference.dart'; +import 'data_classes/usage.dart'; +import 'data_classes/usage_record.dart'; extension type RecordedUsages._(UsageRecord _usages) { RecordedUsages.fromJson(Map json) diff --git a/pkgs/record_use/lib/src/record_use_base.dart b/pkgs/record_use/lib/src/record_use_base.dart deleted file mode 100644 index 953d00735..000000000 --- a/pkgs/record_use/lib/src/record_use_base.dart +++ /dev/null @@ -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. - -class RecordUse {} From 85afe4fa596c146191da0fa963c3556fdfff333d Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 3 Sep 2024 10:01:22 +0200 Subject: [PATCH 07/13] Switch to `equatable` --- pkgs/record_use/lib/record_use.dart | 1 + pkgs/record_use/lib/record_use_internal.dart | 6 +- .../lib/src/data_classes/annotation.dart | 16 +----- .../lib/src/data_classes/arguments.dart | 55 +++++-------------- .../lib/src/data_classes/definition.dart | 15 ++--- .../lib/src/data_classes/field.dart | 20 ++----- .../lib/src/data_classes/identifier.dart | 22 +++----- .../lib/src/data_classes/location.dart | 23 +++----- .../lib/src/data_classes/metadata.dart | 20 ++----- .../lib/src/data_classes/reference.dart | 26 +++------ .../lib/src/data_classes/usage.dart | 16 +----- .../lib/src/data_classes/usage_record.dart | 16 +----- pkgs/record_use/pubspec.yaml | 1 + 13 files changed, 64 insertions(+), 173 deletions(-) diff --git a/pkgs/record_use/lib/record_use.dart b/pkgs/record_use/lib/record_use.dart index 51ef00e6c..96d1857cb 100644 --- a/pkgs/record_use/lib/record_use.dart +++ b/pkgs/record_use/lib/record_use.dart @@ -8,5 +8,6 @@ export 'src/data_classes/field.dart' show Field; export 'src/data_classes/identifier.dart' show Identifier; export 'src/data_classes/location.dart' show Location; export 'src/data_classes/metadata.dart' show Metadata; +//Not exporting `Reference` as it is not used in the API export 'src/data_classes/reference.dart' show CallReference, InstanceReference; export 'src/record_use.dart' show RecordedUsages; diff --git a/pkgs/record_use/lib/record_use_internal.dart b/pkgs/record_use/lib/record_use_internal.dart index 6887d6106..0d48e2dd1 100644 --- a/pkgs/record_use/lib/record_use_internal.dart +++ b/pkgs/record_use/lib/record_use_internal.dart @@ -3,6 +3,6 @@ // BSD-style license that can be found in the LICENSE file. export 'record_use.dart'; -export 'src/data_classes/definition.dart'; -export 'src/data_classes/usage.dart'; -export 'src/data_classes/usage_record.dart'; +export 'src/data_classes/definition.dart' show Definition; +export 'src/data_classes/usage.dart' show Usage; +export 'src/data_classes/usage_record.dart' show UsageRecord; diff --git a/pkgs/record_use/lib/src/data_classes/annotation.dart b/pkgs/record_use/lib/src/data_classes/annotation.dart index 8f27f324c..58ccd7227 100644 --- a/pkgs/record_use/lib/src/data_classes/annotation.dart +++ b/pkgs/record_use/lib/src/data_classes/annotation.dart @@ -2,11 +2,11 @@ // 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'; +import 'package:equatable/equatable.dart'; import 'identifier.dart'; -class Annotation { +class Annotation extends Equatable { final Identifier identifier; final Map fields; @@ -30,15 +30,5 @@ class Annotation { }; @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - final mapEquals = const DeepCollectionEquality().equals; - - return other is Annotation && - other.identifier == identifier && - mapEquals(other.fields, fields); - } - - @override - int get hashCode => identifier.hashCode ^ fields.hashCode; + List get props => [identifier, fields]; } diff --git a/pkgs/record_use/lib/src/data_classes/arguments.dart b/pkgs/record_use/lib/src/data_classes/arguments.dart index 1f681dc47..67809ed61 100644 --- a/pkgs/record_use/lib/src/data_classes/arguments.dart +++ b/pkgs/record_use/lib/src/data_classes/arguments.dart @@ -2,11 +2,11 @@ // 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'; +import 'package:equatable/equatable.dart'; -class Arguments { - ConstArguments constArguments; - NonConstArguments nonConstArguments; +class Arguments extends Equatable { + final ConstArguments constArguments; + final NonConstArguments nonConstArguments; Arguments({ ConstArguments? constArguments, @@ -38,21 +38,12 @@ class Arguments { } @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is Arguments && - other.constArguments == constArguments && - other.nonConstArguments == nonConstArguments; - } - - @override - int get hashCode => constArguments.hashCode ^ nonConstArguments.hashCode; + List get props => [constArguments, nonConstArguments]; } -class ConstArguments { - Map positional; - Map named; +class ConstArguments extends Equatable { + final Map positional; + final Map named; ConstArguments({Map? positional, Map? named}) : named = named ?? {}, @@ -75,22 +66,12 @@ class ConstArguments { }; @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - final mapEquals = const DeepCollectionEquality().equals; - - return other is ConstArguments && - mapEquals(other.positional, positional) && - mapEquals(other.named, named); - } - - @override - int get hashCode => positional.hashCode ^ named.hashCode; + List get props => [positional, named]; } -class NonConstArguments { - List positional; - List named; // Assuming named arguments are strings (keys) +class NonConstArguments extends Equatable { + final List positional; + final List named; NonConstArguments({List? positional, List? named}) : named = named ?? [], @@ -109,15 +90,5 @@ class NonConstArguments { }; @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - final listEquals = const DeepCollectionEquality().equals; - - return other is NonConstArguments && - listEquals(other.positional, positional) && - listEquals(other.named, named); - } - - @override - int get hashCode => positional.hashCode ^ named.hashCode; + List get props => [positional, named]; } diff --git a/pkgs/record_use/lib/src/data_classes/definition.dart b/pkgs/record_use/lib/src/data_classes/definition.dart index a7aaaae83..a254c50a7 100644 --- a/pkgs/record_use/lib/src/data_classes/definition.dart +++ b/pkgs/record_use/lib/src/data_classes/definition.dart @@ -2,10 +2,12 @@ // 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:equatable/equatable.dart'; + import 'identifier.dart'; import 'location.dart'; -class Definition { +class Definition extends Equatable { final Identifier identifier; /// Represents the '@' field in the JSON @@ -45,14 +47,5 @@ class Definition { }; @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 => identifier.hashCode ^ loadingUnit.hashCode; + List get props => [identifier, location, loadingUnit]; } diff --git a/pkgs/record_use/lib/src/data_classes/field.dart b/pkgs/record_use/lib/src/data_classes/field.dart index 13e7b5c39..a1d3a0f76 100644 --- a/pkgs/record_use/lib/src/data_classes/field.dart +++ b/pkgs/record_use/lib/src/data_classes/field.dart @@ -2,7 +2,9 @@ // 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 Field { +import 'package:equatable/equatable.dart'; + +class Field extends Equatable { final String className; final String name; final Object? value; @@ -13,19 +15,6 @@ class Field { required this.value, }); - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is Field && - other.className == className && - other.name == name && - other.value == value; - } - - @override - int get hashCode => className.hashCode ^ name.hashCode ^ value.hashCode; - Map toJson() { return { 'className': className, @@ -41,4 +30,7 @@ class Field { value: map['value'], ); } + + @override + List get props => [className, name, value]; } diff --git a/pkgs/record_use/lib/src/data_classes/identifier.dart b/pkgs/record_use/lib/src/data_classes/identifier.dart index e5737e335..86656d91c 100644 --- a/pkgs/record_use/lib/src/data_classes/identifier.dart +++ b/pkgs/record_use/lib/src/data_classes/identifier.dart @@ -2,10 +2,12 @@ // 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 Identifier { - String uri; - String? parent; // Optional since not all elements have parents - String name; +import 'package:equatable/equatable.dart'; + +class Identifier extends Equatable { + final String uri; + final String? parent; // Optional since not all elements have parents + final String name; Identifier({ required this.uri, @@ -27,15 +29,5 @@ class Identifier { }; @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is Identifier && - other.uri == uri && - other.parent == parent && - other.name == name; - } - - @override - int get hashCode => uri.hashCode ^ parent.hashCode ^ name.hashCode; + List get props => [uri, parent, name]; } diff --git a/pkgs/record_use/lib/src/data_classes/location.dart b/pkgs/record_use/lib/src/data_classes/location.dart index 6cd9d88d9..1a56578a2 100644 --- a/pkgs/record_use/lib/src/data_classes/location.dart +++ b/pkgs/record_use/lib/src/data_classes/location.dart @@ -2,10 +2,13 @@ // 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 { - String uri; - int line; - int column; +import 'package:equatable/equatable.dart'; + +class Location extends Equatable { + final String uri; + final int line; + final int column; + Location({ required this.uri, required this.line, @@ -30,15 +33,5 @@ class Location { } @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 => uri.hashCode ^ line.hashCode ^ column.hashCode; + List get props => [uri, line, column]; } diff --git a/pkgs/record_use/lib/src/data_classes/metadata.dart b/pkgs/record_use/lib/src/data_classes/metadata.dart index d2aee07be..24d10cea7 100644 --- a/pkgs/record_use/lib/src/data_classes/metadata.dart +++ b/pkgs/record_use/lib/src/data_classes/metadata.dart @@ -2,9 +2,10 @@ // 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:equatable/equatable.dart'; import 'package:pub_semver/pub_semver.dart'; -class Metadata { +class Metadata extends Equatable { final String? comment; final Version version; @@ -23,20 +24,6 @@ class Metadata { 'version': version.toString(), }; - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is Metadata && - other.comment == comment && - other.version == version; - } - - @override - int get hashCode { - return comment.hashCode ^ version.hashCode; - } - @override String toString() { return ''' @@ -46,4 +33,7 @@ Metadata( ) '''; } + + @override + List get props => [comment, version]; } diff --git a/pkgs/record_use/lib/src/data_classes/reference.dart b/pkgs/record_use/lib/src/data_classes/reference.dart index 4d1163ce5..8c20126df 100644 --- a/pkgs/record_use/lib/src/data_classes/reference.dart +++ b/pkgs/record_use/lib/src/data_classes/reference.dart @@ -2,11 +2,13 @@ // 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:equatable/equatable.dart'; + import 'arguments.dart'; import 'field.dart'; import 'location.dart'; -sealed class Reference { +sealed class Reference extends Equatable { final String? loadingUnit; /// Represents the "@" field in the JSON @@ -20,16 +22,7 @@ sealed class Reference { }; @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 => loadingUnit.hashCode ^ location.hashCode; + List get props => [loadingUnit, location]; } final class CallReference extends Reference { @@ -62,14 +55,7 @@ final class CallReference extends Reference { } @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is CallReference && other.arguments == arguments; - } - - @override - int get hashCode => arguments.hashCode; + List get props => super.props..add(arguments); } final class InstanceReference extends Reference { @@ -99,4 +85,6 @@ final class InstanceReference extends Reference { 'fields': fields.map((field) => field.toJson()).toList(), ...super.toJson(uris), }; + @override + List get props => super.props..add(fields); } diff --git a/pkgs/record_use/lib/src/data_classes/usage.dart b/pkgs/record_use/lib/src/data_classes/usage.dart index 67abd4981..793ea00ac 100644 --- a/pkgs/record_use/lib/src/data_classes/usage.dart +++ b/pkgs/record_use/lib/src/data_classes/usage.dart @@ -2,13 +2,13 @@ // 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'; +import 'package:equatable/equatable.dart'; import 'definition.dart'; import 'identifier.dart'; import 'reference.dart'; -class Usage { +class Usage extends Equatable { final Definition definition; final List references; @@ -43,15 +43,5 @@ class Usage { }; @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - final listEquals = const DeepCollectionEquality().equals; - - return other is Usage && - other.definition == definition && - listEquals(other.references, references); - } - - @override - int get hashCode => definition.hashCode ^ references.hashCode; + List get props => [definition, references]; } diff --git a/pkgs/record_use/lib/src/data_classes/usage_record.dart b/pkgs/record_use/lib/src/data_classes/usage_record.dart index dd9371a16..c104ddd31 100644 --- a/pkgs/record_use/lib/src/data_classes/usage_record.dart +++ b/pkgs/record_use/lib/src/data_classes/usage_record.dart @@ -2,14 +2,14 @@ // 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'; +import 'package:equatable/equatable.dart'; import 'identifier.dart'; import 'metadata.dart'; import 'reference.dart'; import 'usage.dart'; -class UsageRecord { +class UsageRecord extends Equatable { final Metadata metadata; final List> calls; final List> instances; @@ -83,15 +83,5 @@ class UsageRecord { } @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - final listEquals = const DeepCollectionEquality().equals; - - return other is UsageRecord && - other.metadata == metadata && - listEquals(other.calls, calls); - } - - @override - int get hashCode => metadata.hashCode ^ calls.hashCode; + List get props => [metadata, calls, instances]; } diff --git a/pkgs/record_use/pubspec.yaml b/pkgs/record_use/pubspec.yaml index 2429a8eb9..96483dd4a 100644 --- a/pkgs/record_use/pubspec.yaml +++ b/pkgs/record_use/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: collection: ^1.18.0 + equatable: ^2.0.5 pub_semver: ^2.1.4 dev_dependencies: From 65ef88e80e0425a9990a922040d313018fcd1c6c Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 3 Sep 2024 10:04:40 +0200 Subject: [PATCH 08/13] Add record_use to health checks --- .github/workflows/health.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/health.yaml b/.github/workflows/health.yaml index 3f1481968..94e082d5e 100644 --- a/.github/workflows/health.yaml +++ b/.github/workflows/health.yaml @@ -8,6 +8,7 @@ on: - "pkgs/native_assets_builder/**" - "pkgs/native_assets_cli/**" - "pkgs/native_toolchain_c/**" + - "pkgs/record_use/**" types: [opened, synchronize, reopened, labeled, unlabeled] jobs: health: From 1cb52f2d92d828e18d105d0d46abdb1486ca8ac0 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 3 Sep 2024 11:55:56 +0200 Subject: [PATCH 09/13] Address comments --- .../lib/src/api/link_config.dart | 5 ++++ .../lib/src/model/link_config.dart | 17 +++++++------- .../test/model/link_config_test.dart | 6 ++--- pkgs/record_use/README.md | 23 +++++++++++++++---- pkgs/record_use/lib/record_use.dart | 13 ++++++----- pkgs/record_use/lib/record_use_internal.dart | 6 ++--- .../definition.dart | 4 ++-- .../src/{data_classes => internal}/usage.dart | 4 ++-- .../usage_record.dart | 6 ++--- .../{data_classes => public}/annotation.dart | 0 .../{data_classes => public}/arguments.dart | 0 .../src/{data_classes => public}/field.dart | 0 .../{data_classes => public}/identifier.dart | 0 pkgs/record_use/lib/src/public/instance.dart | 16 +++++++++++++ .../{data_classes => public}/location.dart | 0 .../{data_classes => public}/metadata.dart | 0 .../{data_classes => public}/reference.dart | 0 pkgs/record_use/lib/src/record_use.dart | 19 +++++++-------- pkgs/record_use/test/usage_test.dart | 12 ++++------ 19 files changed, 84 insertions(+), 47 deletions(-) rename pkgs/record_use/lib/src/{data_classes => internal}/definition.dart (94%) rename pkgs/record_use/lib/src/{data_classes => internal}/usage.dart (94%) rename pkgs/record_use/lib/src/{data_classes => internal}/usage_record.dart (96%) rename pkgs/record_use/lib/src/{data_classes => public}/annotation.dart (100%) rename pkgs/record_use/lib/src/{data_classes => public}/arguments.dart (100%) rename pkgs/record_use/lib/src/{data_classes => public}/field.dart (100%) rename pkgs/record_use/lib/src/{data_classes => public}/identifier.dart (100%) create mode 100644 pkgs/record_use/lib/src/public/instance.dart rename pkgs/record_use/lib/src/{data_classes => public}/location.dart (100%) rename pkgs/record_use/lib/src/{data_classes => public}/metadata.dart (100%) rename pkgs/record_use/lib/src/{data_classes => public}/reference.dart (100%) diff --git a/pkgs/native_assets_cli/lib/src/api/link_config.dart b/pkgs/native_assets_cli/lib/src/api/link_config.dart index f6ccc54e1..fa1168f32 100644 --- a/pkgs/native_assets_cli/lib/src/api/link_config.dart +++ b/pkgs/native_assets_cli/lib/src/api/link_config.dart @@ -7,6 +7,7 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:cli_config/cli_config.dart'; import 'package:collection/collection.dart'; +import 'package:meta/meta.dart'; import 'package:pub_semver/pub_semver.dart'; import '../model/hook.dart'; @@ -31,6 +32,10 @@ abstract class LinkConfig implements HookConfig { /// `build.dart` script destined for this packages `link.dart`. Iterable get assets; + /// + @experimental + Uri? get recordedUsages; + /// Generate the [LinkConfig] from the input arguments to the linking script. factory LinkConfig.fromArguments(List arguments) => LinkConfigImpl.fromArguments(arguments); diff --git a/pkgs/native_assets_cli/lib/src/model/link_config.dart b/pkgs/native_assets_cli/lib/src/model/link_config.dart index 8c396bcf9..198b9e481 100644 --- a/pkgs/native_assets_cli/lib/src/model/link_config.dart +++ b/pkgs/native_assets_cli/lib/src/model/link_config.dart @@ -18,11 +18,12 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { // TODO: Placeholder for the resources.json file URL. We don't want to change // native_assets_builder when implementing the parsing. - final Uri? resourceIdentifierUri; + @override + final Uri? recordedUsages; LinkConfigImpl({ required this.assets, - this.resourceIdentifierUri, + this.recordedUsages, required super.outputDirectory, required super.packageName, required super.packageRoot, @@ -46,7 +47,7 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { LinkConfigImpl.dryRun({ required this.assets, - this.resourceIdentifierUri, + this.recordedUsages, required super.outputDirectory, required super.packageName, required super.packageRoot, @@ -72,8 +73,8 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { @override Map toJson() => { ...hookToJson(), - if (resourceIdentifierUri != null) - resourceIdentifierKey: resourceIdentifierUri!.toFilePath(), + if (recordedUsages != null) + resourceIdentifierKey: recordedUsages!.toFilePath(), assetsKey: AssetImpl.listToJson(assets, version), }.sortOnKey(); @@ -114,7 +115,7 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { targetMacOSVersion: HookConfigImpl.parseTargetMacOSVersion(config, dryRun, targetOS), assets: parseAssets(config), - resourceIdentifierUri: parseResourceIdentifier(config), + recordedUsages: parseResourceIdentifier(config), dryRun: dryRun, ); } @@ -133,7 +134,7 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { if (other is! LinkConfigImpl) { return false; } - if (other.resourceIdentifierUri != resourceIdentifierUri) { + if (other.recordedUsages != recordedUsages) { return false; } if (!const DeepCollectionEquality().equals(other.assets, assets)) { @@ -145,7 +146,7 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { @override int get hashCode => Object.hashAll([ super.hashCode, - resourceIdentifierUri, + recordedUsages, const DeepCollectionEquality().hash(assets), ]); diff --git a/pkgs/native_assets_cli/test/model/link_config_test.dart b/pkgs/native_assets_cli/test/model/link_config_test.dart index 3ded55f0b..069a7b768 100644 --- a/pkgs/native_assets_cli/test/model/link_config_test.dart +++ b/pkgs/native_assets_cli/test/model/link_config_test.dart @@ -78,7 +78,7 @@ void main() async { ), buildMode: BuildModeImpl.release, assets: assets, - resourceIdentifierUri: resources, + recordedUsages: resources, linkModePreference: LinkModePreferenceImpl.preferStatic, ); @@ -91,7 +91,7 @@ void main() async { targetAndroidNdkApi: 30, buildMode: BuildModeImpl.release, assets: [], - resourceIdentifierUri: null, + recordedUsages: null, linkModePreference: LinkModePreferenceImpl.preferStatic, ); @@ -330,7 +330,7 @@ void main() async { targetAndroidNdkApi: 30, buildMode: BuildModeImpl.release, assets: assets, - resourceIdentifierUri: resources, + recordedUsages: resources, linkModePreference: LinkModePreferenceImpl.preferStatic, ); final configFileContents = buildConfig.toJsonString(); diff --git a/pkgs/record_use/README.md b/pkgs/record_use/README.md index a324a14f9..151346f6f 100644 --- a/pkgs/record_use/README.md +++ b/pkgs/record_use/README.md @@ -70,16 +70,31 @@ methods annotated with `@RecordUse()`. This information can then be accessed in a link hook as follows: ```dart +import 'dart:convert'; + import 'package:native_assets_cli/native_assets_cli.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 arguments){ link(arguments, (config, output) async { - final uses = config.recordedUses; - - final args = uses.argumentsTo(boolMetadataId)); + 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(recordMetadataId); + final fields = uses.instancesOf(classId); //[fields] is an iterable of the fields of the class, in this case //containing // {"arguments": "leroyjenkins"} diff --git a/pkgs/record_use/lib/record_use.dart b/pkgs/record_use/lib/record_use.dart index 96d1857cb..38dc16cc1 100644 --- a/pkgs/record_use/lib/record_use.dart +++ b/pkgs/record_use/lib/record_use.dart @@ -2,12 +2,13 @@ // 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/data_classes/arguments.dart' +export 'src/public/arguments.dart' show Arguments, ConstArguments, NonConstArguments; -export 'src/data_classes/field.dart' show Field; -export 'src/data_classes/identifier.dart' show Identifier; -export 'src/data_classes/location.dart' show Location; -export 'src/data_classes/metadata.dart' show Metadata; +export 'src/public/field.dart' show Field; +export 'src/public/identifier.dart' show Identifier; +export 'src/public/instance.dart' show Instance; +export 'src/public/location.dart' show Location; +export 'src/public/metadata.dart' show Metadata; //Not exporting `Reference` as it is not used in the API -export 'src/data_classes/reference.dart' show CallReference, InstanceReference; +export 'src/public/reference.dart' show CallReference, InstanceReference; export 'src/record_use.dart' show RecordedUsages; diff --git a/pkgs/record_use/lib/record_use_internal.dart b/pkgs/record_use/lib/record_use_internal.dart index 0d48e2dd1..eb97752ca 100644 --- a/pkgs/record_use/lib/record_use_internal.dart +++ b/pkgs/record_use/lib/record_use_internal.dart @@ -3,6 +3,6 @@ // BSD-style license that can be found in the LICENSE file. export 'record_use.dart'; -export 'src/data_classes/definition.dart' show Definition; -export 'src/data_classes/usage.dart' show Usage; -export 'src/data_classes/usage_record.dart' show UsageRecord; +export 'src/internal/definition.dart' show Definition; +export 'src/internal/usage.dart' show Usage; +export 'src/internal/usage_record.dart' show UsageRecord; diff --git a/pkgs/record_use/lib/src/data_classes/definition.dart b/pkgs/record_use/lib/src/internal/definition.dart similarity index 94% rename from pkgs/record_use/lib/src/data_classes/definition.dart rename to pkgs/record_use/lib/src/internal/definition.dart index a254c50a7..407c4c0f7 100644 --- a/pkgs/record_use/lib/src/data_classes/definition.dart +++ b/pkgs/record_use/lib/src/internal/definition.dart @@ -4,8 +4,8 @@ import 'package:equatable/equatable.dart'; -import 'identifier.dart'; -import 'location.dart'; +import '../public/identifier.dart'; +import '../public/location.dart'; class Definition extends Equatable { final Identifier identifier; diff --git a/pkgs/record_use/lib/src/data_classes/usage.dart b/pkgs/record_use/lib/src/internal/usage.dart similarity index 94% rename from pkgs/record_use/lib/src/data_classes/usage.dart rename to pkgs/record_use/lib/src/internal/usage.dart index 793ea00ac..a3aeda611 100644 --- a/pkgs/record_use/lib/src/data_classes/usage.dart +++ b/pkgs/record_use/lib/src/internal/usage.dart @@ -5,8 +5,8 @@ import 'package:equatable/equatable.dart'; import 'definition.dart'; -import 'identifier.dart'; -import 'reference.dart'; +import '../public/identifier.dart'; +import '../public/reference.dart'; class Usage extends Equatable { final Definition definition; diff --git a/pkgs/record_use/lib/src/data_classes/usage_record.dart b/pkgs/record_use/lib/src/internal/usage_record.dart similarity index 96% rename from pkgs/record_use/lib/src/data_classes/usage_record.dart rename to pkgs/record_use/lib/src/internal/usage_record.dart index c104ddd31..aaee5ec31 100644 --- a/pkgs/record_use/lib/src/data_classes/usage_record.dart +++ b/pkgs/record_use/lib/src/internal/usage_record.dart @@ -4,9 +4,9 @@ import 'package:equatable/equatable.dart'; -import 'identifier.dart'; -import 'metadata.dart'; -import 'reference.dart'; +import '../public/identifier.dart'; +import '../public/metadata.dart'; +import '../public/reference.dart'; import 'usage.dart'; class UsageRecord extends Equatable { diff --git a/pkgs/record_use/lib/src/data_classes/annotation.dart b/pkgs/record_use/lib/src/public/annotation.dart similarity index 100% rename from pkgs/record_use/lib/src/data_classes/annotation.dart rename to pkgs/record_use/lib/src/public/annotation.dart diff --git a/pkgs/record_use/lib/src/data_classes/arguments.dart b/pkgs/record_use/lib/src/public/arguments.dart similarity index 100% rename from pkgs/record_use/lib/src/data_classes/arguments.dart rename to pkgs/record_use/lib/src/public/arguments.dart diff --git a/pkgs/record_use/lib/src/data_classes/field.dart b/pkgs/record_use/lib/src/public/field.dart similarity index 100% rename from pkgs/record_use/lib/src/data_classes/field.dart rename to pkgs/record_use/lib/src/public/field.dart diff --git a/pkgs/record_use/lib/src/data_classes/identifier.dart b/pkgs/record_use/lib/src/public/identifier.dart similarity index 100% rename from pkgs/record_use/lib/src/data_classes/identifier.dart rename to pkgs/record_use/lib/src/public/identifier.dart diff --git a/pkgs/record_use/lib/src/public/instance.dart b/pkgs/record_use/lib/src/public/instance.dart new file mode 100644 index 000000000..a9a644fb2 --- /dev/null +++ b/pkgs/record_use/lib/src/public/instance.dart @@ -0,0 +1,16 @@ +// 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:equatable/equatable.dart'; + +import 'field.dart'; + +class Instance extends Equatable { + final List fields; + + Instance({required this.fields}); + + @override + List get props => [fields]; +} diff --git a/pkgs/record_use/lib/src/data_classes/location.dart b/pkgs/record_use/lib/src/public/location.dart similarity index 100% rename from pkgs/record_use/lib/src/data_classes/location.dart rename to pkgs/record_use/lib/src/public/location.dart diff --git a/pkgs/record_use/lib/src/data_classes/metadata.dart b/pkgs/record_use/lib/src/public/metadata.dart similarity index 100% rename from pkgs/record_use/lib/src/data_classes/metadata.dart rename to pkgs/record_use/lib/src/public/metadata.dart diff --git a/pkgs/record_use/lib/src/data_classes/reference.dart b/pkgs/record_use/lib/src/public/reference.dart similarity index 100% rename from pkgs/record_use/lib/src/data_classes/reference.dart rename to pkgs/record_use/lib/src/public/reference.dart diff --git a/pkgs/record_use/lib/src/record_use.dart b/pkgs/record_use/lib/src/record_use.dart index 47ee28ea3..4e88a5ed3 100644 --- a/pkgs/record_use/lib/src/record_use.dart +++ b/pkgs/record_use/lib/src/record_use.dart @@ -4,13 +4,13 @@ import 'package:collection/collection.dart'; -import 'data_classes/arguments.dart'; -import 'data_classes/field.dart'; -import 'data_classes/identifier.dart'; -import 'data_classes/metadata.dart'; -import 'data_classes/reference.dart'; -import 'data_classes/usage.dart'; -import 'data_classes/usage_record.dart'; +import 'internal/usage.dart'; +import 'internal/usage_record.dart'; +import 'public/arguments.dart'; +import 'public/identifier.dart'; +import 'public/instance.dart'; +import 'public/metadata.dart'; +import 'public/reference.dart'; extension type RecordedUsages._(UsageRecord _usages) { RecordedUsages.fromJson(Map json) @@ -99,12 +99,13 @@ extension type RecordedUsages._(UsageRecord _usages) { /// /// 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>? instancesOf(Identifier classIdentifier) => + Iterable? instancesOf(Identifier classIdentifier) => _usages.instances .firstWhereOrNull( (instance) => instance.definition.identifier == classIdentifier) ?.references - .map((reference) => reference.fields); + .map((reference) => reference.fields) + .map((fields) => Instance(fields: fields)); /// Checks if any call to [method] has non-const arguments. /// diff --git a/pkgs/record_use/test/usage_test.dart b/pkgs/record_use/test/usage_test.dart index 877d18710..d4cabd83e 100644 --- a/pkgs/record_use/test/usage_test.dart +++ b/pkgs/record_use/test/usage_test.dart @@ -17,7 +17,9 @@ void main() { test('All API instances', () { expect( RecordedUsages.fromJson(recordedUsesJson).instancesOf(instanceId), - recordedUses.instances.expand((e) => e.references).map((e) => e.fields), + recordedUses.instances + .expand((e) => e.references) + .map((e) => Instance(fields: e.fields)), ); }); test('Specific API calls', () { @@ -55,12 +57,8 @@ void main() { name: 'MyAnnotation', ); expect( - RecordedUsages.fromJson(recordedUsesJson) - .instancesOf(instanceId) - ?.first - .first - .value, - 42, + RecordedUsages.fromJson(recordedUsesJson).instancesOf(instanceId)?.first, + Instance(fields: [Field(name: 'a', className: 'className', value: 42)]), ); }); } From 27f70958bfee7f874a3802924f25f7a6411970ea Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 3 Sep 2024 11:56:58 +0200 Subject: [PATCH 10/13] Sort imports --- pkgs/record_use/lib/src/internal/usage.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/record_use/lib/src/internal/usage.dart b/pkgs/record_use/lib/src/internal/usage.dart index a3aeda611..7e2532f5a 100644 --- a/pkgs/record_use/lib/src/internal/usage.dart +++ b/pkgs/record_use/lib/src/internal/usage.dart @@ -4,9 +4,9 @@ import 'package:equatable/equatable.dart'; -import 'definition.dart'; import '../public/identifier.dart'; import '../public/reference.dart'; +import 'definition.dart'; class Usage extends Equatable { final Definition definition; From 728fb3d096554bd2a659f7141caa56efd6453181 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 3 Sep 2024 13:28:24 +0200 Subject: [PATCH 11/13] Restructure classname of instances --- pkgs/record_use/lib/src/public/field.dart | 6 +----- pkgs/record_use/lib/src/public/instance.dart | 7 +++---- pkgs/record_use/lib/src/public/reference.dart | 6 +++++- pkgs/record_use/lib/src/record_use.dart | 7 +++++-- pkgs/record_use/test/storage_test.dart | 4 ++-- pkgs/record_use/test/usage_test.dart | 15 ++++++++++----- 6 files changed, 26 insertions(+), 19 deletions(-) diff --git a/pkgs/record_use/lib/src/public/field.dart b/pkgs/record_use/lib/src/public/field.dart index a1d3a0f76..423f91bb9 100644 --- a/pkgs/record_use/lib/src/public/field.dart +++ b/pkgs/record_use/lib/src/public/field.dart @@ -5,19 +5,16 @@ import 'package:equatable/equatable.dart'; class Field extends Equatable { - final String className; final String name; final Object? value; Field({ - required this.className, required this.name, required this.value, }); Map toJson() { return { - 'className': className, 'name': name, 'value': value, }; @@ -25,12 +22,11 @@ class Field extends Equatable { factory Field.fromJson(Map map) { return Field( - className: map['className'] as String, name: map['name'] as String, value: map['value'], ); } @override - List get props => [className, name, value]; + List get props => [name, value]; } diff --git a/pkgs/record_use/lib/src/public/instance.dart b/pkgs/record_use/lib/src/public/instance.dart index a9a644fb2..312d34025 100644 --- a/pkgs/record_use/lib/src/public/instance.dart +++ b/pkgs/record_use/lib/src/public/instance.dart @@ -4,12 +4,11 @@ import 'package:equatable/equatable.dart'; -import 'field.dart'; - class Instance extends Equatable { - final List fields; + final String className; + final Map fields; - Instance({required this.fields}); + Instance({required this.className, required this.fields}); @override List get props => [fields]; diff --git a/pkgs/record_use/lib/src/public/reference.dart b/pkgs/record_use/lib/src/public/reference.dart index 8c20126df..fc0d1494f 100644 --- a/pkgs/record_use/lib/src/public/reference.dart +++ b/pkgs/record_use/lib/src/public/reference.dart @@ -59,17 +59,20 @@ final class CallReference extends Reference { } final class InstanceReference extends Reference { + final String className; final List fields; InstanceReference({ super.loadingUnit, required super.location, required this.fields, + required this.className, }); factory InstanceReference.fromJson( Map json, List uris) { return InstanceReference( + className: json['className'] as String, loadingUnit: json['loadingUnit'] as String?, location: Location.fromJson(json['@'] as Map, null, uris), @@ -83,8 +86,9 @@ final class InstanceReference extends Reference { Map toJson(List uris) => { if (fields.isNotEmpty) 'fields': fields.map((field) => field.toJson()).toList(), + 'className': className, ...super.toJson(uris), }; @override - List get props => super.props..add(fields); + List get props => super.props..add([className, fields]); } diff --git a/pkgs/record_use/lib/src/record_use.dart b/pkgs/record_use/lib/src/record_use.dart index 4e88a5ed3..3a6f2164e 100644 --- a/pkgs/record_use/lib/src/record_use.dart +++ b/pkgs/record_use/lib/src/record_use.dart @@ -104,8 +104,11 @@ extension type RecordedUsages._(UsageRecord _usages) { .firstWhereOrNull( (instance) => instance.definition.identifier == classIdentifier) ?.references - .map((reference) => reference.fields) - .map((fields) => Instance(fields: fields)); + .map((reference) => Instance( + className: reference.className, + fields: Map.fromEntries(reference.fields + .map((field) => MapEntry(field.name, field.value))), + )); /// Checks if any call to [method] has non-const arguments. /// diff --git a/pkgs/record_use/test/storage_test.dart b/pkgs/record_use/test/storage_test.dart index 8eeb2e54c..3e32db415 100644 --- a/pkgs/record_use/test/storage_test.dart +++ b/pkgs/record_use/test/storage_test.dart @@ -47,6 +47,7 @@ final recordedUses = UsageRecord( ), references: [ InstanceReference( + className: 'className', location: Location( uri: Uri.parse('file://lib/_internal/js_runtime/lib/js_helper.dart') .toString(), @@ -55,7 +56,6 @@ final recordedUses = UsageRecord( ), fields: [ Field( - className: 'className', name: 'a', value: 42, ), @@ -192,9 +192,9 @@ final recordedUsesJson = { }, 'references': [ { + 'className': 'className', 'fields': [ { - 'className': 'className', 'name': 'a', 'value': 42, } diff --git a/pkgs/record_use/test/usage_test.dart b/pkgs/record_use/test/usage_test.dart index d4cabd83e..fe7c0a842 100644 --- a/pkgs/record_use/test/usage_test.dart +++ b/pkgs/record_use/test/usage_test.dart @@ -15,11 +15,16 @@ void main() { ); }); test('All API instances', () { + final references = recordedUses.instances.expand((e) => e.references); + final instances = + RecordedUsages.fromJson(recordedUsesJson).instancesOf(instanceId); expect( - RecordedUsages.fromJson(recordedUsesJson).instancesOf(instanceId), - recordedUses.instances - .expand((e) => e.references) - .map((e) => Instance(fields: e.fields)), + instances!.map((e) => e.className), + references.map((e) => e.className), + ); + expect( + instances.map((e) => e.fields.entries.map((e) => (e.key, e.value))), + references.map((e) => e.fields.map((e) => (e.name, e.value))), ); }); test('Specific API calls', () { @@ -58,7 +63,7 @@ void main() { ); expect( RecordedUsages.fromJson(recordedUsesJson).instancesOf(instanceId)?.first, - Instance(fields: [Field(name: 'a', className: 'className', value: 42)]), + Instance(className: 'className', fields: {'a': 42}), ); }); } From 621088c4ddced25acddad702db4e42d98df65490 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 3 Sep 2024 13:31:48 +0200 Subject: [PATCH 12/13] Undo native_assets_cli changes --- .../lib/src/api/link_config.dart | 5 ----- .../lib/src/model/link_config.dart | 17 ++++++++--------- .../test/model/link_config_test.dart | 6 +++--- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/pkgs/native_assets_cli/lib/src/api/link_config.dart b/pkgs/native_assets_cli/lib/src/api/link_config.dart index fa1168f32..f6ccc54e1 100644 --- a/pkgs/native_assets_cli/lib/src/api/link_config.dart +++ b/pkgs/native_assets_cli/lib/src/api/link_config.dart @@ -7,7 +7,6 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:cli_config/cli_config.dart'; import 'package:collection/collection.dart'; -import 'package:meta/meta.dart'; import 'package:pub_semver/pub_semver.dart'; import '../model/hook.dart'; @@ -32,10 +31,6 @@ abstract class LinkConfig implements HookConfig { /// `build.dart` script destined for this packages `link.dart`. Iterable get assets; - /// - @experimental - Uri? get recordedUsages; - /// Generate the [LinkConfig] from the input arguments to the linking script. factory LinkConfig.fromArguments(List arguments) => LinkConfigImpl.fromArguments(arguments); diff --git a/pkgs/native_assets_cli/lib/src/model/link_config.dart b/pkgs/native_assets_cli/lib/src/model/link_config.dart index 198b9e481..8c396bcf9 100644 --- a/pkgs/native_assets_cli/lib/src/model/link_config.dart +++ b/pkgs/native_assets_cli/lib/src/model/link_config.dart @@ -18,12 +18,11 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { // TODO: Placeholder for the resources.json file URL. We don't want to change // native_assets_builder when implementing the parsing. - @override - final Uri? recordedUsages; + final Uri? resourceIdentifierUri; LinkConfigImpl({ required this.assets, - this.recordedUsages, + this.resourceIdentifierUri, required super.outputDirectory, required super.packageName, required super.packageRoot, @@ -47,7 +46,7 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { LinkConfigImpl.dryRun({ required this.assets, - this.recordedUsages, + this.resourceIdentifierUri, required super.outputDirectory, required super.packageName, required super.packageRoot, @@ -73,8 +72,8 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { @override Map toJson() => { ...hookToJson(), - if (recordedUsages != null) - resourceIdentifierKey: recordedUsages!.toFilePath(), + if (resourceIdentifierUri != null) + resourceIdentifierKey: resourceIdentifierUri!.toFilePath(), assetsKey: AssetImpl.listToJson(assets, version), }.sortOnKey(); @@ -115,7 +114,7 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { targetMacOSVersion: HookConfigImpl.parseTargetMacOSVersion(config, dryRun, targetOS), assets: parseAssets(config), - recordedUsages: parseResourceIdentifier(config), + resourceIdentifierUri: parseResourceIdentifier(config), dryRun: dryRun, ); } @@ -134,7 +133,7 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { if (other is! LinkConfigImpl) { return false; } - if (other.recordedUsages != recordedUsages) { + if (other.resourceIdentifierUri != resourceIdentifierUri) { return false; } if (!const DeepCollectionEquality().equals(other.assets, assets)) { @@ -146,7 +145,7 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { @override int get hashCode => Object.hashAll([ super.hashCode, - recordedUsages, + resourceIdentifierUri, const DeepCollectionEquality().hash(assets), ]); diff --git a/pkgs/native_assets_cli/test/model/link_config_test.dart b/pkgs/native_assets_cli/test/model/link_config_test.dart index 069a7b768..3ded55f0b 100644 --- a/pkgs/native_assets_cli/test/model/link_config_test.dart +++ b/pkgs/native_assets_cli/test/model/link_config_test.dart @@ -78,7 +78,7 @@ void main() async { ), buildMode: BuildModeImpl.release, assets: assets, - recordedUsages: resources, + resourceIdentifierUri: resources, linkModePreference: LinkModePreferenceImpl.preferStatic, ); @@ -91,7 +91,7 @@ void main() async { targetAndroidNdkApi: 30, buildMode: BuildModeImpl.release, assets: [], - recordedUsages: null, + resourceIdentifierUri: null, linkModePreference: LinkModePreferenceImpl.preferStatic, ); @@ -330,7 +330,7 @@ void main() async { targetAndroidNdkApi: 30, buildMode: BuildModeImpl.release, assets: assets, - recordedUsages: resources, + resourceIdentifierUri: resources, linkModePreference: LinkModePreferenceImpl.preferStatic, ); final configFileContents = buildConfig.toJsonString(); From da6d5dbb9100cdc7e07d529d439a190aa86ecf06 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 3 Sep 2024 14:20:39 +0200 Subject: [PATCH 13/13] Remove `equatable` --- .../lib/src/internal/definition.dart | 16 +++++-- pkgs/record_use/lib/src/internal/usage.dart | 16 +++++-- .../lib/src/internal/usage_record.dart | 17 +++++-- .../record_use/lib/src/public/annotation.dart | 34 -------------- pkgs/record_use/lib/src/public/arguments.dart | 45 +++++++++++++++---- pkgs/record_use/lib/src/public/field.dart | 13 ++++-- .../record_use/lib/src/public/identifier.dart | 16 +++++-- pkgs/record_use/lib/src/public/instance.dart | 18 ++++++-- pkgs/record_use/lib/src/public/location.dart | 16 +++++-- pkgs/record_use/lib/src/public/metadata.dart | 14 ++++-- pkgs/record_use/lib/src/public/reference.dart | 39 +++++++++++++--- pkgs/record_use/pubspec.yaml | 1 - 12 files changed, 168 insertions(+), 77 deletions(-) delete mode 100644 pkgs/record_use/lib/src/public/annotation.dart diff --git a/pkgs/record_use/lib/src/internal/definition.dart b/pkgs/record_use/lib/src/internal/definition.dart index 407c4c0f7..b838d0663 100644 --- a/pkgs/record_use/lib/src/internal/definition.dart +++ b/pkgs/record_use/lib/src/internal/definition.dart @@ -2,12 +2,10 @@ // 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:equatable/equatable.dart'; - import '../public/identifier.dart'; import '../public/location.dart'; -class Definition extends Equatable { +class Definition { final Identifier identifier; /// Represents the '@' field in the JSON @@ -47,5 +45,15 @@ class Definition extends Equatable { }; @override - List get props => [identifier, location, loadingUnit]; + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Definition && + other.identifier == identifier && + other.location == location && + other.loadingUnit == loadingUnit; + } + + @override + int get hashCode => Object.hash(identifier, location, loadingUnit); } diff --git a/pkgs/record_use/lib/src/internal/usage.dart b/pkgs/record_use/lib/src/internal/usage.dart index 7e2532f5a..0f3aa1a66 100644 --- a/pkgs/record_use/lib/src/internal/usage.dart +++ b/pkgs/record_use/lib/src/internal/usage.dart @@ -2,13 +2,13 @@ // 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:equatable/equatable.dart'; +import 'package:collection/collection.dart'; import '../public/identifier.dart'; import '../public/reference.dart'; import 'definition.dart'; -class Usage extends Equatable { +class Usage { final Definition definition; final List references; @@ -43,5 +43,15 @@ class Usage extends Equatable { }; @override - List get props => [definition, references]; + bool operator ==(Object other) { + if (identical(this, other)) return true; + final listEquals = const DeepCollectionEquality().equals; + + return other is Usage && + other.definition == definition && + listEquals(other.references, references); + } + + @override + int get hashCode => Object.hash(definition, references); } diff --git a/pkgs/record_use/lib/src/internal/usage_record.dart b/pkgs/record_use/lib/src/internal/usage_record.dart index aaee5ec31..dc12fc986 100644 --- a/pkgs/record_use/lib/src/internal/usage_record.dart +++ b/pkgs/record_use/lib/src/internal/usage_record.dart @@ -2,14 +2,14 @@ // 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:equatable/equatable.dart'; +import 'package:collection/collection.dart'; import '../public/identifier.dart'; import '../public/metadata.dart'; import '../public/reference.dart'; import 'usage.dart'; -class UsageRecord extends Equatable { +class UsageRecord { final Metadata metadata; final List> calls; final List> instances; @@ -83,5 +83,16 @@ class UsageRecord extends Equatable { } @override - List get props => [metadata, calls, instances]; + bool operator ==(Object other) { + if (identical(this, other)) return true; + final listEquals = const DeepCollectionEquality().equals; + + return other is UsageRecord && + other.metadata == metadata && + listEquals(other.calls, calls) && + listEquals(other.instances, instances); + } + + @override + int get hashCode => Object.hash(metadata, calls, instances); } diff --git a/pkgs/record_use/lib/src/public/annotation.dart b/pkgs/record_use/lib/src/public/annotation.dart deleted file mode 100644 index 58ccd7227..000000000 --- a/pkgs/record_use/lib/src/public/annotation.dart +++ /dev/null @@ -1,34 +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:equatable/equatable.dart'; - -import 'identifier.dart'; - -class Annotation extends Equatable { - final Identifier identifier; - final Map fields; - - Annotation({ - required this.identifier, - required this.fields, - }); - - factory Annotation.fromJson( - Map json, - List identifiers, - ) => - Annotation( - identifier: identifiers[json['id'] as int], - fields: json['fields'] as Map, - ); - - Map toJson(List identifiers) => { - 'id': identifiers.indexOf(identifier), - 'fields': fields, - }; - - @override - List get props => [identifier, fields]; -} diff --git a/pkgs/record_use/lib/src/public/arguments.dart b/pkgs/record_use/lib/src/public/arguments.dart index 67809ed61..2396db766 100644 --- a/pkgs/record_use/lib/src/public/arguments.dart +++ b/pkgs/record_use/lib/src/public/arguments.dart @@ -1,10 +1,10 @@ +import 'package:collection/collection.dart'; + // 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:equatable/equatable.dart'; - -class Arguments extends Equatable { +class Arguments { final ConstArguments constArguments; final NonConstArguments nonConstArguments; @@ -38,10 +38,19 @@ class Arguments extends Equatable { } @override - List get props => [constArguments, nonConstArguments]; + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Arguments && + other.constArguments == constArguments && + other.nonConstArguments == nonConstArguments; + } + + @override + int get hashCode => Object.hash(constArguments, nonConstArguments); } -class ConstArguments extends Equatable { +class ConstArguments { final Map positional; final Map named; @@ -66,10 +75,20 @@ class ConstArguments extends Equatable { }; @override - List get props => [positional, named]; + bool operator ==(Object other) { + if (identical(this, other)) return true; + final mapEquals = const DeepCollectionEquality().equals; + + return other is ConstArguments && + mapEquals(other.positional, positional) && + mapEquals(other.named, named); + } + + @override + int get hashCode => Object.hash(positional, named); } -class NonConstArguments extends Equatable { +class NonConstArguments { final List positional; final List named; @@ -90,5 +109,15 @@ class NonConstArguments extends Equatable { }; @override - List get props => [positional, named]; + bool operator ==(Object other) { + if (identical(this, other)) return true; + final listEquals = const DeepCollectionEquality().equals; + + return other is NonConstArguments && + listEquals(other.positional, positional) && + listEquals(other.named, named); + } + + @override + int get hashCode => Object.hash(positional, named); } diff --git a/pkgs/record_use/lib/src/public/field.dart b/pkgs/record_use/lib/src/public/field.dart index 423f91bb9..075ba3017 100644 --- a/pkgs/record_use/lib/src/public/field.dart +++ b/pkgs/record_use/lib/src/public/field.dart @@ -2,9 +2,7 @@ // 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:equatable/equatable.dart'; - -class Field extends Equatable { +class Field { final String name; final Object? value; @@ -28,5 +26,12 @@ class Field extends Equatable { } @override - List get props => [name, value]; + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Field && other.name == name && other.value == value; + } + + @override + int get hashCode => Object.hash(name, value); } diff --git a/pkgs/record_use/lib/src/public/identifier.dart b/pkgs/record_use/lib/src/public/identifier.dart index 86656d91c..11845cdf9 100644 --- a/pkgs/record_use/lib/src/public/identifier.dart +++ b/pkgs/record_use/lib/src/public/identifier.dart @@ -2,9 +2,7 @@ // 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:equatable/equatable.dart'; - -class Identifier extends Equatable { +class Identifier { final String uri; final String? parent; // Optional since not all elements have parents final String name; @@ -29,5 +27,15 @@ class Identifier extends Equatable { }; @override - List get props => [uri, parent, name]; + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Identifier && + other.uri == uri && + other.parent == parent && + other.name == name; + } + + @override + int get hashCode => Object.hash(uri, parent, name); } diff --git a/pkgs/record_use/lib/src/public/instance.dart b/pkgs/record_use/lib/src/public/instance.dart index 312d34025..ae4affbf2 100644 --- a/pkgs/record_use/lib/src/public/instance.dart +++ b/pkgs/record_use/lib/src/public/instance.dart @@ -1,15 +1,25 @@ +import 'package:collection/collection.dart'; + // 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:equatable/equatable.dart'; - -class Instance extends Equatable { +class Instance { final String className; final Map fields; Instance({required this.className, required this.fields}); @override - List get props => [fields]; + bool operator ==(Object other) { + if (identical(this, other)) return true; + final mapEquals = const DeepCollectionEquality().equals; + + return other is Instance && + other.className == className && + mapEquals(other.fields, fields); + } + + @override + int get hashCode => Object.hash(className, fields); } diff --git a/pkgs/record_use/lib/src/public/location.dart b/pkgs/record_use/lib/src/public/location.dart index 1a56578a2..b1fe11665 100644 --- a/pkgs/record_use/lib/src/public/location.dart +++ b/pkgs/record_use/lib/src/public/location.dart @@ -2,9 +2,7 @@ // 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:equatable/equatable.dart'; - -class Location extends Equatable { +class Location { final String uri; final int line; final int column; @@ -33,5 +31,15 @@ class Location extends Equatable { } @override - List get props => [uri, line, column]; + 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); } diff --git a/pkgs/record_use/lib/src/public/metadata.dart b/pkgs/record_use/lib/src/public/metadata.dart index 24d10cea7..37d7351d0 100644 --- a/pkgs/record_use/lib/src/public/metadata.dart +++ b/pkgs/record_use/lib/src/public/metadata.dart @@ -2,10 +2,9 @@ // 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:equatable/equatable.dart'; import 'package:pub_semver/pub_semver.dart'; -class Metadata extends Equatable { +class Metadata { final String? comment; final Version version; @@ -35,5 +34,14 @@ Metadata( } @override - List get props => [comment, version]; + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Metadata && + other.comment == comment && + other.version == version; + } + + @override + int get hashCode => Object.hash(comment, version); } diff --git a/pkgs/record_use/lib/src/public/reference.dart b/pkgs/record_use/lib/src/public/reference.dart index fc0d1494f..86df50c49 100644 --- a/pkgs/record_use/lib/src/public/reference.dart +++ b/pkgs/record_use/lib/src/public/reference.dart @@ -2,13 +2,13 @@ // 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:equatable/equatable.dart'; +import 'package:collection/collection.dart'; import 'arguments.dart'; import 'field.dart'; import 'location.dart'; -sealed class Reference extends Equatable { +sealed class Reference { final String? loadingUnit; /// Represents the "@" field in the JSON @@ -22,7 +22,16 @@ sealed class Reference extends Equatable { }; @override - List get props => [loadingUnit, location]; + 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); } final class CallReference extends Reference { @@ -55,7 +64,15 @@ final class CallReference extends Reference { } @override - List get props => super.props..add(arguments); + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (!(super == other)) return false; + + return other is CallReference && other.arguments == arguments; + } + + @override + int get hashCode => Object.hash(arguments, super.hashCode); } final class InstanceReference extends Reference { @@ -89,6 +106,18 @@ final class InstanceReference extends Reference { 'className': className, ...super.toJson(uris), }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (!(super == other)) return false; + final listEquals = const DeepCollectionEquality().equals; + + return other is InstanceReference && + other.className == className && + listEquals(other.fields, fields); + } + @override - List get props => super.props..add([className, fields]); + int get hashCode => Object.hash(className, fields, super.hashCode); } diff --git a/pkgs/record_use/pubspec.yaml b/pkgs/record_use/pubspec.yaml index 96483dd4a..2429a8eb9 100644 --- a/pkgs/record_use/pubspec.yaml +++ b/pkgs/record_use/pubspec.yaml @@ -9,7 +9,6 @@ environment: dependencies: collection: ^1.18.0 - equatable: ^2.0.5 pub_semver: ^2.1.4 dev_dependencies: