diff --git a/pkg/_fe_analyzer_shared/lib/src/experiments/flags.dart b/pkg/_fe_analyzer_shared/lib/src/experiments/flags.dart index 2f24e7a0b5fb..b1b5cf90ef05 100644 --- a/pkg/_fe_analyzer_shared/lib/src/experiments/flags.dart +++ b/pkg/_fe_analyzer_shared/lib/src/experiments/flags.dart @@ -164,6 +164,13 @@ enum ExperimentalFlag { experimentEnabledVersion: const Version(3, 0), experimentReleasedVersion: const Version(3, 0)), + recordUse( + name: 'record-use', + isEnabledByDefault: false, + isExpired: false, + experimentEnabledVersion: defaultLanguageVersion, + experimentReleasedVersion: defaultLanguageVersion), + records( name: 'records', isEnabledByDefault: true, @@ -171,13 +178,6 @@ enum ExperimentalFlag { experimentEnabledVersion: const Version(3, 0), experimentReleasedVersion: const Version(3, 0)), - resourceIdentifiers( - name: 'resource-identifiers', - isEnabledByDefault: false, - isExpired: false, - experimentEnabledVersion: defaultLanguageVersion, - experimentReleasedVersion: defaultLanguageVersion), - sealedClass( name: 'sealed-class', isEnabledByDefault: true, diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart index 3ba620378795..eeabc4ce8cbb 100644 --- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart +++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart @@ -15039,6 +15039,17 @@ const MessageCode messageRecordTypeZeroFieldsButTrailingComma = correctionMessage: r"""Try removing the trailing comma.""", ); +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Code codeRecordUseCannotBePlacedHere = + messageRecordUseCannotBePlacedHere; + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const MessageCode messageRecordUseCannotBePlacedHere = const MessageCode( + "RecordUseCannotBePlacedHere", + problemMessage: + r"""`RecordUse` annotation cannot be placed on this element.""", +); + // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. const Code codeRecordUsedAsCallable = messageRecordUsedAsCallable; @@ -15225,28 +15236,6 @@ const MessageCode messageRequiredParameterWithDefault = const MessageCode( r"""Try removing the default value or making the parameter optional.""", ); -// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. -const Code codeResourceIdentifiersMultiple = - messageResourceIdentifiersMultiple; - -// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. -const MessageCode messageResourceIdentifiersMultiple = const MessageCode( - "ResourceIdentifiersMultiple", - problemMessage: - r"""Only one resource identifier pragma can be used at a time.""", -); - -// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. -const Code codeResourceIdentifiersNotStatic = - messageResourceIdentifiersNotStatic; - -// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. -const MessageCode messageResourceIdentifiersNotStatic = const MessageCode( - "ResourceIdentifiersNotStatic", - problemMessage: - r"""Resource identifier pragma can be used on a static method only.""", -); - // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. const Code codeRestPatternInMapPattern = messageRestPatternInMapPattern; diff --git a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart index 23cb13a57e56..a7774c230af5 100644 --- a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart +++ b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart @@ -41,8 +41,8 @@ final _knownFeatures = { ExperimentalFeatures.nonfunction_type_aliases, EnableString.null_aware_elements: ExperimentalFeatures.null_aware_elements, EnableString.patterns: ExperimentalFeatures.patterns, + EnableString.record_use: ExperimentalFeatures.record_use, EnableString.records: ExperimentalFeatures.records, - EnableString.resource_identifiers: ExperimentalFeatures.resource_identifiers, EnableString.sealed_class: ExperimentalFeatures.sealed_class, EnableString.set_literals: ExperimentalFeatures.set_literals, EnableString.spread_collections: ExperimentalFeatures.spread_collections, @@ -123,12 +123,12 @@ class EnableString { /// String to enable the experiment "patterns" static const String patterns = 'patterns'; + /// String to enable the experiment "record-use" + static const String record_use = 'record-use'; + /// String to enable the experiment "records" static const String records = 'records'; - /// String to enable the experiment "resource-identifiers" - static const String resource_identifiers = 'resource-identifiers'; - /// String to enable the experiment "sealed-class" static const String sealed_class = 'sealed-class'; @@ -383,8 +383,18 @@ class ExperimentalFeatures { releaseVersion: Version.parse('3.0.0'), ); - static final records = ExperimentalFeature( + static final record_use = ExperimentalFeature( index: 22, + enableString: EnableString.record_use, + isEnabledByDefault: IsEnabledByDefault.record_use, + isExpired: IsExpired.record_use, + documentation: 'Output arguments used by static functions.', + experimentalReleaseVersion: null, + releaseVersion: null, + ); + + static final records = ExperimentalFeature( + index: 23, enableString: EnableString.records, isEnabledByDefault: IsEnabledByDefault.records, isExpired: IsExpired.records, @@ -393,16 +403,6 @@ class ExperimentalFeatures { releaseVersion: Version.parse('3.0.0'), ); - static final resource_identifiers = ExperimentalFeature( - index: 23, - enableString: EnableString.resource_identifiers, - isEnabledByDefault: IsEnabledByDefault.resource_identifiers, - isExpired: IsExpired.resource_identifiers, - documentation: 'Output arguments used by static functions.', - experimentalReleaseVersion: null, - releaseVersion: null, - ); - static final sealed_class = ExperimentalFeature( index: 24, enableString: EnableString.sealed_class, @@ -565,12 +565,12 @@ class IsEnabledByDefault { /// Default state of the experiment "patterns" static const bool patterns = true; + /// Default state of the experiment "record-use" + static const bool record_use = false; + /// Default state of the experiment "records" static const bool records = true; - /// Default state of the experiment "resource-identifiers" - static const bool resource_identifiers = false; - /// Default state of the experiment "sealed-class" static const bool sealed_class = true; @@ -669,12 +669,12 @@ class IsExpired { /// Expiration status of the experiment "patterns" static const bool patterns = true; + /// Expiration status of the experiment "record-use" + static const bool record_use = false; + /// Expiration status of the experiment "records" static const bool records = true; - /// Expiration status of the experiment "resource-identifiers" - static const bool resource_identifiers = false; - /// Expiration status of the experiment "sealed-class" static const bool sealed_class = true; @@ -780,13 +780,12 @@ mixin _CurrentState { /// Current state for the flag "patterns" bool get patterns => isEnabled(ExperimentalFeatures.patterns); + /// Current state for the flag "record-use" + bool get record_use => isEnabled(ExperimentalFeatures.record_use); + /// Current state for the flag "records" bool get records => isEnabled(ExperimentalFeatures.records); - /// Current state for the flag "resource-identifiers" - bool get resource_identifiers => - isEnabled(ExperimentalFeatures.resource_identifiers); - /// Current state for the flag "sealed-class" bool get sealed_class => isEnabled(ExperimentalFeatures.sealed_class); diff --git a/pkg/analyzer/lib/src/test_utilities/mock_packages.dart b/pkg/analyzer/lib/src/test_utilities/mock_packages.dart index e0f38100654d..ef744d6cbb1d 100644 --- a/pkg/analyzer/lib/src/test_utilities/mock_packages.dart +++ b/pkg/analyzer/lib/src/test_utilities/mock_packages.dart @@ -154,26 +154,17 @@ class Immutable { const Immutable([this.reason = '']); } +@experimental +class RecordUse { + const RecordUse(); +} + class Required { final String reason; const Required([this.reason = '']); } -@experimental -class ResourceIdentifier { - final Object? metadata; - - const ResourceIdentifier([this.metadata]) - : assert( - metadata == null || - metadata is bool || - metadata is num || - metadata is String, - 'Valid metadata types are bool, int, double, and String.', - ); -} - @Target({ TargetKind.constructor, TargetKind.field, diff --git a/pkg/analyzer_utilities/lib/test/mock_packages/package_content/meta/lib/meta.dart b/pkg/analyzer_utilities/lib/test/mock_packages/package_content/meta/lib/meta.dart index 37966dcc20aa..2088d5383f53 100644 --- a/pkg/analyzer_utilities/lib/test/mock_packages/package_content/meta/lib/meta.dart +++ b/pkg/analyzer_utilities/lib/test/mock_packages/package_content/meta/lib/meta.dart @@ -236,7 +236,7 @@ const _Literal literal = _Literal(); /// any warnings. /// /// An example use could be the arguments to functions annotated with -/// [ResourceIdentifier], as only constant arguments can be made available +/// [RecordUse], as only constant arguments can be made available /// to the post-compile steps. /// /// ```dart @@ -539,6 +539,24 @@ class Immutable { const Immutable([this.reason = '']); } +/// Annotates a static method to be recorded. +/// +/// Applies to static functions, top-level functions, or extension methods. +/// +/// During compilation, all statically resolved calls to an annotated function +/// are registered, and information about the annotated functions, the calls, +/// and their arguments, is then made available to post-compile steps. +// TODO(srawlins): Enforce with `TargetKind.method`. +@experimental +class RecordUse { + /// Creates a [RecordUse] instance. + /// + /// This annotation can be placed as an annotation on functions whose + /// statically resolved calls should be registered together with the optional + /// [metadata] information. + const RecordUse(); +} + /// Used to annotate a named parameter `p` in a method or function `f`. /// /// See [required] for more details. @@ -567,38 +585,6 @@ class Required { const Required([this.reason = '']); } -/// Annotates a static method as referencing a native resource. -/// -/// Applies to static functions, top-level functions, or extension methods. -/// -/// During compilation, all statically resolved calls to an annotated function -/// are registered, and information about the annotated functions, the calls, -/// and their arguments, is then made available to post-compile steps. -// TODO(srawlins): Enforce with `TargetKind.method`. -@experimental -class ResourceIdentifier { - /// Information which is stored together with the function call. - /// - /// This could, for example, be the name of the package containing the - /// function annotated with this annotation. Allowed types are [bool], [int], - /// [double], and [String]. - final Object? metadata; - - /// Creates a [ResourceIdentifier] instance. - /// - /// This annotation can be placed as an annotation on functions whose - /// statically resolved calls should be registered together with the optional - /// [metadata] information. - const ResourceIdentifier([this.metadata]) - : assert( - metadata == null || - metadata is bool || - metadata is num || - metadata is String, - 'Valid metadata types are bool, int, double, and String.', - ); -} - /// See [useResult] for more details. @Target({ TargetKind.constructor, diff --git a/pkg/dart2native/lib/dart2native.dart b/pkg/dart2native/lib/dart2native.dart index 5293fe9168dc..1609f6a6239b 100644 --- a/pkg/dart2native/lib/dart2native.dart +++ b/pkg/dart2native/lib/dart2native.dart @@ -103,8 +103,8 @@ Future markExecutable(String outputFile) { /// Generates kernel by running the provided [genKernel] path. /// -/// Also takes a path to the [resourcesFile] JSON file, where the method calls -/// to static functions annotated with `@ResourceIdentifier` will be collected. +/// Also takes a path to the [recordedUsagesFile] JSON file, where the method +/// calls to static functions annotated with `@RecordUse` will be collected. Future generateKernelHelper({ required String dartaotruntime, String? sourceFile, @@ -115,7 +115,7 @@ Future generateKernelHelper({ String? targetOS, List extraGenKernelOptions = const [], String? nativeAssets, - String? resourcesFile, + String? recordedUsagesFile, String? depFile, bool enableAsserts = false, bool fromDill = false, @@ -138,7 +138,8 @@ Future generateKernelHelper({ ...defines.map((d) => '-D$d'), if (packages != null) '--packages=$packages', if (nativeAssets != null) '--native-assets=$nativeAssets', - if (resourcesFile != null) '--resources-file=$resourcesFile', + if (recordedUsagesFile != null) + '--recorded-usages-file=$recordedUsagesFile', if (depFile != null) '--depfile=$depFile', '--output=$kernelFile', ...extraGenKernelOptions, diff --git a/pkg/dart2native/lib/generate.dart b/pkg/dart2native/lib/generate.dart index 8bf6ec2d36ff..d6210392df41 100644 --- a/pkg/dart2native/lib/generate.dart +++ b/pkg/dart2native/lib/generate.dart @@ -65,14 +65,14 @@ extension type KernelGenerator._(_Generator _generator) { /// Generate a kernel file, /// - /// [resourcesFile] is the path to `resources.json`, where the tree-shaking - /// information collected during kernel compilation is stored. + /// [recordedUsagesFile] is the path to `recorded_usages.json`, where the + /// tree-shaking information collected during kernel compilation is stored. Future generate({ - String? resourcesFile, + String? recordedUsagesFile, List? extraOptions, }) => _generator.generateKernel( - resourcesFile: resourcesFile, + recordedUsagesFile: recordedUsagesFile, extraOptions: extraOptions, ); } @@ -192,7 +192,7 @@ class _Generator { } Future generateKernel({ - String? resourcesFile, + String? recordedUsagesFile, List? extraOptions, }) async { if (_verbose) { @@ -219,7 +219,7 @@ class _Generator { if (_depFile != null) '--depfile-target=$_outputPath', ...?extraOptions, ], - resourcesFile: resourcesFile, + recordedUsagesFile: recordedUsagesFile, aot: true, ); await _forwardOutput(kernelResult); @@ -366,7 +366,7 @@ class _Generator { /// /// [nativeAssets] is the path to `native_assets.yaml`. /// -/// [resourcesFile] is the path to `resources.json`. +/// [recordedUsagesFile] is the path to `recorded_usages.json`. Future generateKernel({ required String sourceFile, required String outputFile, @@ -382,7 +382,7 @@ Future generateKernel({ bool product = true, bool verbose = false, String? nativeAssets, - String? resourcesFile, + String? recordedUsagesFile, String? depFile, List? extraOptions, }) async { @@ -407,7 +407,7 @@ Future generateKernel({ ...?extraOptions, ], nativeAssets: nativeAssets, - resourcesFile: resourcesFile, + recordedUsagesFile: recordedUsagesFile, product: product, ); await _forwardOutput(kernelResult); diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart index 2b57c07dc528..bc33817e2620 100644 --- a/pkg/dartdev/lib/dartdev.dart +++ b/pkg/dartdev/lib/dartdev.dart @@ -99,7 +99,11 @@ class DartdevRunner extends CommandRunner { final nativeAssetsExperimentEnabled = nativeAssetsEnabled(vmEnabledExperiments); if (nativeAssetsExperimentEnabled) { - addCommand(BuildCommand(verbose: verbose)); + final recordUseExperimentEnabled = recordUseEnabled(vmEnabledExperiments); + addCommand(BuildCommand( + verbose: verbose, + recordUseEnabled: recordUseExperimentEnabled, + )); } addCommand(CompileCommand( verbose: verbose, diff --git a/pkg/dartdev/lib/src/commands/build.dart b/pkg/dartdev/lib/src/commands/build.dart index 3cd38faf17c4..b78c2c4fce21 100644 --- a/pkg/dartdev/lib/src/commands/build.dart +++ b/pkg/dartdev/lib/src/commands/build.dart @@ -29,8 +29,9 @@ class BuildCommand extends DartdevCommand { static const String outputOptionName = 'output'; static const String formatOptionName = 'format'; static const int genericErrorExitCode = 255; + final bool recordUseEnabled; - BuildCommand({bool verbose = false}) + BuildCommand({bool verbose = false, required this.recordUseEnabled}) : super(cmdName, 'Build a Dart application including native assets.', verbose) { argParser @@ -162,7 +163,10 @@ class BuildCommand extends DartdevCommand { final tempDir = Directory.systemTemp.createTempSync(); try { final packageConfig = await packageConfigUri(sourceUri); - final resources = path.join(tempDir.path, 'resources.json'); + String? recordedUsagesPath; + if (recordUseEnabled) { + recordedUsagesPath = path.join(tempDir.path, 'recorded_usages.json'); + } final generator = KernelGenerator( kind: format, sourceFile: sourceUri.toFilePath(), @@ -177,12 +181,13 @@ class BuildCommand extends DartdevCommand { ); final snapshotGenerator = await generator.generate( - resourcesFile: resources, + recordedUsagesFile: recordedUsagesPath, ); // Start linking here. final linkResult = await nativeAssetsBuildRunner.link( - resourceIdentifiers: Uri.file(resources), + resourceIdentifiers: + recordUseEnabled ? Uri.file(recordedUsagesPath!) : null, workingDirectory: workingDirectory, target: target, linkModePreference: LinkModePreferenceImpl.dynamic, diff --git a/pkg/dartdev/lib/src/experiments.dart b/pkg/dartdev/lib/src/experiments.dart index 5801be3c0c13..311e3d5f99c7 100644 --- a/pkg/dartdev/lib/src/experiments.dart +++ b/pkg/dartdev/lib/src/experiments.dart @@ -98,3 +98,6 @@ List parseVmEnabledExperiments(List vmArgs) { bool nativeAssetsEnabled(List vmEnabledExperiments) => vmEnabledExperiments .contains(ExperimentalFeatures.native_assets.enableString); + +bool recordUseEnabled(List vmEnabledExperiments) => + vmEnabledExperiments.contains(ExperimentalFeatures.record_use.enableString); diff --git a/pkg/dartdev/test/native_assets/build_test.dart b/pkg/dartdev/test/native_assets/build_test.dart index 22e9ccc125d0..bb3ef88d0382 100644 --- a/pkg/dartdev/test/native_assets/build_test.dart +++ b/pkg/dartdev/test/native_assets/build_test.dart @@ -233,6 +233,78 @@ void main(List args) { ); }); }); + + test('Tree-shaking: No assets are dropped', timeout: longTimeout, () async { + await recordUseTest('drop_dylib_recording', (dartAppUri) async { + // First try using all symbols, so no assets are treeshaken. + await runDart( + arguments: [ + '--enable-experiment=native-assets,record-use', + 'build', + 'bin/drop_dylib_recording_all.dart', + ], + workingDirectory: dartAppUri, + logger: logger, + expectExitCodeZero: true, + ); + + // The build directory exists + final allDirectory = + Directory.fromUri(dartAppUri.resolve('bin/drop_dylib_recording_all')); + expect(allDirectory.existsSync(), true); + + // No assets have been treeshaken + final addLib = + OSImpl.current.libraryFileName('add', DynamicLoadingBundledImpl()); + final mulitplyLib = OSImpl.current + .libraryFileName('multiply', DynamicLoadingBundledImpl()); + expect( + File.fromUri(allDirectory.uri.resolve('lib/$addLib')).existsSync(), + true, + ); + expect( + File.fromUri(allDirectory.uri.resolve('lib/$mulitplyLib')).existsSync(), + true, + ); + }); + }); + + test('Tree-shaking: An asset is dropped', timeout: longTimeout, () async { + await recordUseTest('drop_dylib_recording', (dartAppUri) async { + final addLib = + OSImpl.current.libraryFileName('add', DynamicLoadingBundledImpl()); + final mulitplyLib = OSImpl.current + .libraryFileName('multiply', DynamicLoadingBundledImpl()); + // Now try using the add symbol only, so the multiply library is + // tree-shaken. + await runDart( + arguments: [ + '--enable-experiment=native-assets,record-use', + 'build', + 'bin/drop_dylib_recording_shake.dart', + ], + workingDirectory: dartAppUri, + logger: logger, + expectExitCodeZero: true, + ); + + // The build directory exists + final shakeDirectory = Directory.fromUri( + dartAppUri.resolve('bin/drop_dylib_recording_shake')); + expect(shakeDirectory.existsSync(), true); + + // The multiply asset has been treeshaken + expect( + File.fromUri(shakeDirectory.uri.resolve('lib/$addLib')).existsSync(), + true, + ); + expect( + File.fromUri(shakeDirectory.uri.resolve('lib/$mulitplyLib')) + .existsSync(), + false, + ); + }); + }); } Future _withTempDir(Future Function(Uri tempUri) fun) async { diff --git a/pkg/dartdev/test/native_assets/helpers.dart b/pkg/dartdev/test/native_assets/helpers.dart index ad0912b0c475..40ab442e2c63 100644 --- a/pkg/dartdev/test/native_assets/helpers.dart +++ b/pkg/dartdev/test/native_assets/helpers.dart @@ -62,11 +62,10 @@ Future runProcess({ throwOnUnexpectedExitCode: throwOnUnexpectedExitCode, ); -Future copyTestProjects(Uri copyTargetUri, Logger logger) async { - final pkgNativeAssetsBuilderUri = Platform.script.resolve( - '../../../../third_party/pkg/native/pkgs/native_assets_builder/'); +Future copyTestProjects( + Uri copyTargetUri, Logger logger, Uri packageLocation) async { // Reuse the test projects from `pkg:native`. - final testProjectsUri = pkgNativeAssetsBuilderUri.resolve('test_data/'); + final testProjectsUri = packageLocation.resolve('test_data/'); final manifestUri = testProjectsUri.resolve('manifest.yaml'); final manifestFile = File.fromUri(manifestUri); final manifestString = await manifestFile.readAsString(); @@ -100,7 +99,7 @@ Future copyTestProjects(Uri copyTargetUri, Logger logger) async { final sourceString = await sourceFile.readAsString(); final modifiedString = sourceString.replaceAll( 'path: ../../', - 'path: ${pkgNativeAssetsBuilderUri.toFilePath().replaceAll('\\', '/')}', + 'path: ${packageLocation.toFilePath().replaceAll('\\', '/')}', ); await File.fromUri(targetUri).writeAsString(modifiedString); } @@ -158,16 +157,46 @@ Future nativeAssetsTest( String packageUnderTest, Future Function(Uri) fun, { bool skipPubGet = false, -}) async { - assert(const [ - 'add_asset_link', - 'dart_app', - 'drop_dylib_link', - 'native_add_duplicate', - 'native_add', - ].contains(packageUnderTest)); +}) async => + await runPackageTest( + packageUnderTest, + skipPubGet, + fun, + const [ + 'add_asset_link', + 'dart_app', + 'drop_dylib_link', + 'native_add_duplicate', + 'native_add', + 'treeshaking_native_libs', + ], + Platform.script.resolve( + '../../../../third_party/pkg/native/pkgs/native_assets_builder/'), + ); + +Future recordUseTest( + String packageUnderTest, + Future Function(Uri) fun, { + bool skipPubGet = false, +}) async => + await runPackageTest( + packageUnderTest, + skipPubGet, + fun, + const ['drop_dylib_recording'], + Platform.script.resolve('../../../record_use/'), + ); + +Future runPackageTest( + String packageUnderTest, + bool skipPubGet, + Future Function(Uri) fun, + List validPackages, + Uri packageLocation, +) async { + assert(validPackages.contains(packageUnderTest)); return await inTempDir((tempUri) async { - await copyTestProjects(tempUri, logger); + await copyTestProjects(tempUri, logger, packageLocation); final packageUri = tempUri.resolve('$packageUnderTest/'); if (!skipPubGet) { await runPubGet(workingDirectory: packageUri, logger: logger); diff --git a/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart b/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart index 1c989e54ac22..0ea94bdf94c8 100644 --- a/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart +++ b/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart @@ -235,6 +235,14 @@ class ExperimentalFlag { experimentEnabledVersion: const Version(3, 0), experimentReleasedVersion: const Version(3, 0)); + static const ExperimentalFlag recordUse = const ExperimentalFlag( + name: 'record-use', + isEnabledByDefault: false, + isExpired: false, + enabledVersion: defaultLanguageVersion, + experimentEnabledVersion: defaultLanguageVersion, + experimentReleasedVersion: defaultLanguageVersion); + static const ExperimentalFlag records = const ExperimentalFlag( name: 'records', isEnabledByDefault: true, @@ -243,14 +251,6 @@ class ExperimentalFlag { experimentEnabledVersion: const Version(3, 0), experimentReleasedVersion: const Version(3, 0)); - static const ExperimentalFlag resourceIdentifiers = const ExperimentalFlag( - name: 'resource-identifiers', - isEnabledByDefault: false, - isExpired: false, - enabledVersion: defaultLanguageVersion, - experimentEnabledVersion: defaultLanguageVersion, - experimentReleasedVersion: defaultLanguageVersion); - static const ExperimentalFlag sealedClass = const ExperimentalFlag( name: 'sealed-class', isEnabledByDefault: true, @@ -462,14 +462,14 @@ class GlobalFeatures { GlobalFeature get patterns => _patterns ??= _computeGlobalFeature(ExperimentalFlag.patterns); + GlobalFeature? _recordUse; + GlobalFeature get recordUse => + _recordUse ??= _computeGlobalFeature(ExperimentalFlag.recordUse); + GlobalFeature? _records; GlobalFeature get records => _records ??= _computeGlobalFeature(ExperimentalFlag.records); - GlobalFeature? _resourceIdentifiers; - GlobalFeature get resourceIdentifiers => _resourceIdentifiers ??= - _computeGlobalFeature(ExperimentalFlag.resourceIdentifiers); - GlobalFeature? _sealedClass; GlobalFeature get sealedClass => _sealedClass ??= _computeGlobalFeature(ExperimentalFlag.sealedClass); @@ -640,16 +640,16 @@ class LibraryFeatures { _patterns ??= globalFeatures._computeLibraryFeature( ExperimentalFlag.patterns, canonicalUri, libraryVersion); + LibraryFeature? _recordUse; + LibraryFeature get recordUse => + _recordUse ??= globalFeatures._computeLibraryFeature( + ExperimentalFlag.recordUse, canonicalUri, libraryVersion); + LibraryFeature? _records; LibraryFeature get records => _records ??= globalFeatures._computeLibraryFeature( ExperimentalFlag.records, canonicalUri, libraryVersion); - LibraryFeature? _resourceIdentifiers; - LibraryFeature get resourceIdentifiers => - _resourceIdentifiers ??= globalFeatures._computeLibraryFeature( - ExperimentalFlag.resourceIdentifiers, canonicalUri, libraryVersion); - LibraryFeature? _sealedClass; LibraryFeature get sealedClass => _sealedClass ??= globalFeatures._computeLibraryFeature( @@ -743,10 +743,10 @@ class LibraryFeatures { return nullAwareElements; case shared.ExperimentalFlag.patterns: return patterns; + case shared.ExperimentalFlag.recordUse: + return recordUse; case shared.ExperimentalFlag.records: return records; - case shared.ExperimentalFlag.resourceIdentifiers: - return resourceIdentifiers; case shared.ExperimentalFlag.sealedClass: return sealedClass; case shared.ExperimentalFlag.setLiterals: @@ -817,10 +817,10 @@ ExperimentalFlag? parseExperimentalFlag(String flag) { return ExperimentalFlag.nullAwareElements; case "patterns": return ExperimentalFlag.patterns; + case "record-use": + return ExperimentalFlag.recordUse; case "records": return ExperimentalFlag.records; - case "resource-identifiers": - return ExperimentalFlag.resourceIdentifiers; case "sealed-class": return ExperimentalFlag.sealedClass; case "set-literals": @@ -886,9 +886,8 @@ final Map defaultExperimentalFlags = { ExperimentalFlag.nullAwareElements: ExperimentalFlag.nullAwareElements.isEnabledByDefault, ExperimentalFlag.patterns: ExperimentalFlag.patterns.isEnabledByDefault, + ExperimentalFlag.recordUse: ExperimentalFlag.recordUse.isEnabledByDefault, ExperimentalFlag.records: ExperimentalFlag.records.isEnabledByDefault, - ExperimentalFlag.resourceIdentifiers: - ExperimentalFlag.resourceIdentifiers.isEnabledByDefault, ExperimentalFlag.sealedClass: ExperimentalFlag.sealedClass.isEnabledByDefault, ExperimentalFlag.setLiterals: ExperimentalFlag.setLiterals.isEnabledByDefault, ExperimentalFlag.spreadCollections: @@ -942,9 +941,8 @@ const Map sharedExperimentalFlags = { ExperimentalFlag.nonfunctionTypeAliases, shared.ExperimentalFlag.nullAwareElements: ExperimentalFlag.nullAwareElements, shared.ExperimentalFlag.patterns: ExperimentalFlag.patterns, + shared.ExperimentalFlag.recordUse: ExperimentalFlag.recordUse, shared.ExperimentalFlag.records: ExperimentalFlag.records, - shared.ExperimentalFlag.resourceIdentifiers: - ExperimentalFlag.resourceIdentifiers, shared.ExperimentalFlag.sealedClass: ExperimentalFlag.sealedClass, shared.ExperimentalFlag.setLiterals: ExperimentalFlag.setLiterals, shared.ExperimentalFlag.spreadCollections: ExperimentalFlag.spreadCollections, diff --git a/pkg/front_end/lib/src/api_prototype/resource_identifier.dart b/pkg/front_end/lib/src/api_prototype/record_use.dart similarity index 82% rename from pkg/front_end/lib/src/api_prototype/resource_identifier.dart rename to pkg/front_end/lib/src/api_prototype/record_use.dart index 6c3ccdeaaccb..75c23eefc8ae 100644 --- a/pkg/front_end/lib/src/api_prototype/resource_identifier.dart +++ b/pkg/front_end/lib/src/api_prototype/record_use.dart @@ -2,4 +2,4 @@ // 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 '../kernel/resource_identifier.dart'; +export '../kernel/record_use.dart'; diff --git a/pkg/front_end/lib/src/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/kernel/constant_evaluator.dart index 4d14d8777f38..6e5818d4c84f 100644 --- a/pkg/front_end/lib/src/kernel/constant_evaluator.dart +++ b/pkg/front_end/lib/src/kernel/constant_evaluator.dart @@ -46,7 +46,7 @@ import '../type_inference/matching_cache.dart'; import '../type_inference/matching_expressions.dart'; import 'constant_int_folder.dart'; import 'exhaustiveness.dart'; -import 'resource_identifier.dart' as ResourceIdentifiers; +import 'record_use.dart' as RecordUse; import 'static_weak_references.dart' show StaticWeakReferences; part 'constant_collection_builders.dart'; @@ -352,11 +352,14 @@ class ConstantsTransformer extends RemovingTransformer { parent, constantEvaluator.errorReporter); } final Iterable resourceAnnotations = - ResourceIdentifiers.findResourceAnnotations(parent); + RecordUse.findRecordUseAnnotation(parent); if (resourceAnnotations.isNotEmpty) { // Coverage-ignore-block(suite): Not run. - ResourceIdentifiers.validateResourceIdentifierDeclaration( - parent, constantEvaluator.errorReporter, resourceAnnotations); + RecordUse.validateRecordUseDeclaration( + parent, + constantEvaluator.errorReporter, + resourceAnnotations, + ); } } } diff --git a/pkg/front_end/lib/src/kernel/resource_identifier.dart b/pkg/front_end/lib/src/kernel/record_use.dart similarity index 51% rename from pkg/front_end/lib/src/kernel/resource_identifier.dart rename to pkg/front_end/lib/src/kernel/record_use.dart index ccbc6a47db7a..061085b4f797 100644 --- a/pkg/front_end/lib/src/kernel/resource_identifier.dart +++ b/pkg/front_end/lib/src/kernel/record_use.dart @@ -2,53 +2,46 @@ // 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. -/// Recognition and validation of resource identification annotations. +/// Recognition and validation of usage recording annotations. /// -/// A static method to be collected as a resource identifier can be annotated -/// with `@ResourceIdentifier('some-id-string')`. +/// A static method to be recorded can be annotated with `@RecordUse()`. library; import 'package:kernel/ast.dart'; -import '../base/messages.dart' - show - messageResourceIdentifiersNotStatic, - messageResourceIdentifiersMultiple; +import '../base/messages.dart' show messageRecordUseCannotBePlacedHere; import 'constant_evaluator.dart' show ErrorReporter; -/// Get all of the `@ResourceIdentifier` annotations from `package:meta` +/// Get all of the `@RecordUse` annotations from `package:meta` /// that are attached to the specified [node]. -Iterable findResourceAnnotations(Annotatable node) => +Iterable findRecordUseAnnotation(Annotatable node) => node.annotations .whereType() .map((expression) => expression.constant) .whereType() - .where((instance) => isResourceIdentifier(instance.classNode)) + .where((instance) => isRecordUse(instance.classNode)) .toList(growable: false); // Coverage-ignore(suite): Not run. final Uri _metaLibraryUri = new Uri(scheme: 'package', path: 'meta/meta.dart'); -bool isResourceIdentifier(Class classNode) => - classNode.name == 'ResourceIdentifier' && +bool isRecordUse(Class classNode) => + classNode.name == 'RecordUse' && // Coverage-ignore(suite): Not run. classNode.enclosingLibrary.importUri == _metaLibraryUri; // Coverage-ignore(suite): Not run. /// Report if the resource annotations is placed on anything but a static /// method. -void validateResourceIdentifierDeclaration( +void validateRecordUseDeclaration( Annotatable node, ErrorReporter errorReporter, Iterable resourceAnnotations, ) { - if (node is! Procedure || - !node.isStatic || - node.kind != ProcedureKind.Method) { - errorReporter.report(messageResourceIdentifiersNotStatic.withLocation( - node.location!.file, node.fileOffset, 1)); - } else if (resourceAnnotations.length > 1) { - errorReporter.report(messageResourceIdentifiersMultiple.withLocation( + final bool onNonStaticMethod = + node is! Procedure || !node.isStatic || node.kind != ProcedureKind.Method; + if (onNonStaticMethod) { + errorReporter.report(messageRecordUseCannotBePlacedHere.withLocation( node.location!.file, node.fileOffset, 1)); } } diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status index acb59649e5ff..91c5abbbfca0 100644 --- a/pkg/front_end/messages.status +++ b/pkg/front_end/messages.status @@ -954,10 +954,8 @@ RequiredNamedParameterHasDefaultValueError/analyzerCode: Fail RequiredNamedParameterHasDefaultValueError/example: Fail RequiredNamedParameterHasDefaultValueWarning/analyzerCode: Fail RequiredNamedParameterHasDefaultValueWarning/example: Fail -ResourceIdentifiersMultiple/analyzerCode: Fail -ResourceIdentifiersMultiple/example: Fail -ResourceIdentifiersNotStatic/analyzerCode: Fail -ResourceIdentifiersNotStatic/example: Fail +RecordUseCannotBePlacedHere/analyzerCode: Fail +RecordUseCannotBePlacedHere/example: Fail RestPatternInMapPattern/analyzerCode: Fail RestPatternMoreThanOne/analyzerCode: Fail RethrowNotCatch/example: Fail diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml index 5aa1e5f8e44b..9df7ab3843d1 100644 --- a/pkg/front_end/messages.yaml +++ b/pkg/front_end/messages.yaml @@ -7017,11 +7017,8 @@ DefaultInSwitchExpression: default => 'other' }; -ResourceIdentifiersNotStatic: - problemMessage: "Resource identifier pragma can be used on a static method only." - -ResourceIdentifiersMultiple: - problemMessage: "Only one resource identifier pragma can be used at a time." +RecordUseCannotBePlacedHere: + problemMessage: "`RecordUse` annotation cannot be placed on this element." WasmImportOrExportInUserCode: problemMessage: "Pragmas `wasm:import` and `wasm:export` are for internal use only and cannot be used by user code." diff --git a/pkg/front_end/test/coverage_suite_expected.dart b/pkg/front_end/test/coverage_suite_expected.dart index 2cc92e39ee28..733dfdd63353 100644 --- a/pkg/front_end/test/coverage_suite_expected.dart +++ b/pkg/front_end/test/coverage_suite_expected.dart @@ -684,7 +684,7 @@ const Map _expect = { missCount: 0, ), // 100.0%. - "package:front_end/src/kernel/resource_identifier.dart": ( + "package:front_end/src/kernel/record_use.dart": ( hitCount: 15, missCount: 0, ), diff --git a/pkg/front_end/test/lint_test.status b/pkg/front_end/test/lint_test.status index b74f4b13a3cf..fc89cbf2be41 100644 --- a/pkg/front_end/test/lint_test.status +++ b/pkg/front_end/test/lint_test.status @@ -11,7 +11,7 @@ front_end/lib/src/api_prototype/constant_evaluator/Exports: Fail front_end/lib/src/api_prototype/front_end/Exports: Fail front_end/lib/src/api_prototype/incremental_kernel_generator/Exports: Fail front_end/lib/src/api_prototype/lowering_predicates/Exports: Fail -front_end/lib/src/api_prototype/resource_identifier/Exports: Fail +front_end/lib/src/api_prototype/record_use/Exports: Fail front_end/lib/src/api_prototype/static_weak_references/Exports: Fail front_end/lib/src/api_prototype/terminal_color_support/Exports: Fail front_end/lib/src/api_prototype/testing/Exports: Fail diff --git a/pkg/front_end/test/spell_checking_list_messages.txt b/pkg/front_end/test/spell_checking_list_messages.txt index 3f9b7fd19529..0a0382933d39 100644 --- a/pkg/front_end/test/spell_checking_list_messages.txt +++ b/pkg/front_end/test/spell_checking_list_messages.txt @@ -120,6 +120,7 @@ preexisting pubspec.yaml r re +RecordUse refutable resource sdksummary diff --git a/pkg/meta/CHANGELOG.md b/pkg/meta/CHANGELOG.md index c50a8d4d755e..2ea6e1871325 100644 --- a/pkg/meta/CHANGELOG.md +++ b/pkg/meta/CHANGELOG.md @@ -57,6 +57,8 @@ extension type const FancyInt(@mustBeConst int _actual) {} ``` +- Renamed `@ResourceIdentifier` to `@RecordUse`. + ## 1.15.0 - Updated `@mustBeOverridden` to only flag missing overrides in concrete diff --git a/pkg/meta/lib/meta.dart b/pkg/meta/lib/meta.dart index 85c541ca0571..383a0d2a90de 100644 --- a/pkg/meta/lib/meta.dart +++ b/pkg/meta/lib/meta.dart @@ -236,7 +236,7 @@ const _Literal literal = _Literal(); /// any warnings. /// /// An example use could be the arguments to functions annotated with -/// [ResourceIdentifier], as only constant arguments can be made available +/// [RecordUse], as only constant arguments can be made available /// to the post-compile steps. /// /// ```dart @@ -539,6 +539,24 @@ class Immutable { const Immutable([this.reason = '']); } +/// Annotates a static method to be recorded. +/// +/// Applies to static functions, top-level functions, or extension methods. +/// +/// During compilation, all statically resolved calls to an annotated function +/// are registered, and information about the annotated functions, the calls, +/// and their arguments, is then made available to post-compile steps. +// TODO(srawlins): Enforce with `TargetKind.method`. +@experimental +class RecordUse { + /// Creates a [RecordUse] instance. + /// + /// This annotation can be placed as an annotation on functions whose + /// statically resolved calls should be registered together with the optional + /// [metadata] information. + const RecordUse(); +} + /// Used to annotate a named parameter `p` in a method or function `f`. /// /// See [required] for more details. @@ -567,38 +585,6 @@ class Required { const Required([this.reason = '']); } -/// Annotates a static method as referencing a native resource. -/// -/// Applies to static functions, top-level functions, or extension methods. -/// -/// During compilation, all statically resolved calls to an annotated function -/// are registered, and information about the annotated functions, the calls, -/// and their arguments, is then made available to post-compile steps. -// TODO(srawlins): Enforce with `TargetKind.method`. -@experimental -class ResourceIdentifier { - /// Information which is stored together with the function call. - /// - /// This could, for example, be the name of the package containing the - /// function annotated with this annotation. Allowed types are [bool], [int], - /// [double], and [String]. - final Object? metadata; - - /// Creates a [ResourceIdentifier] instance. - /// - /// This annotation can be placed as an annotation on functions whose - /// statically resolved calls should be registered together with the optional - /// [metadata] information. - const ResourceIdentifier([this.metadata]) - : assert( - metadata == null || - metadata is bool || - metadata is num || - metadata is String, - 'Valid metadata types are bool, int, double, and String.', - ); -} - /// See [useResult] for more details. @Target({ TargetKind.constructor, diff --git a/pkg/record_use/CHANGELOG.md b/pkg/record_use/CHANGELOG.md index 904447d0c6d9..3a541d92df04 100644 --- a/pkg/record_use/CHANGELOG.md +++ b/pkg/record_use/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0 + +- Use maps instead of lists in serialization. + ## 0.1.1 - Fix repository link. diff --git a/pkg/record_use/analysis_options.yaml b/pkg/record_use/analysis_options.yaml new file mode 100644 index 000000000000..127282661ec0 --- /dev/null +++ b/pkg/record_use/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:dart_flutter_team_lints/analysis_options.yaml + +analyzer: + exclude: [test_data/**] diff --git a/pkg/record_use/lib/src/internal/definition.dart b/pkg/record_use/lib/src/internal/definition.dart index 3d83a26df799..ed83f247c922 100644 --- a/pkg/record_use/lib/src/internal/definition.dart +++ b/pkg/record_use/lib/src/internal/definition.dart @@ -35,11 +35,11 @@ class Definition { } Map toJson( - List identifiers, - List uris, + Map identifiers, + Map uris, ) => { - 'id': identifiers.indexOf(identifier), + 'id': identifiers[identifier]!, '@': location.toJson(), 'loadingUnit': loadingUnit, }; diff --git a/pkg/record_use/lib/src/internal/usage.dart b/pkg/record_use/lib/src/internal/usage.dart index 723a668bd1a4..dbdc9cd59f04 100644 --- a/pkg/record_use/lib/src/internal/usage.dart +++ b/pkg/record_use/lib/src/internal/usage.dart @@ -35,9 +35,9 @@ class Usage { ); Map toJson( - List identifiers, - List uris, - List constants, + Map identifiers, + Map uris, + Map constants, ) => { 'definition': definition.toJson(identifiers, uris), diff --git a/pkg/record_use/lib/src/internal/usage_record.dart b/pkg/record_use/lib/src/internal/usage_record.dart index 97875a7aa944..1c59729bb9df 100644 --- a/pkg/record_use/lib/src/internal/usage_record.dart +++ b/pkg/record_use/lib/src/internal/usage_record.dart @@ -2,6 +2,8 @@ // 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 '../helper.dart'; import '../public/arguments.dart'; import '../public/constant.dart'; @@ -22,7 +24,7 @@ class UsageRecord { }); factory UsageRecord.fromJson(Map json) { - final uris = json['uris'] as List; + final uris = (json['uris'] as List).cast(); final identifiers = (json['ids'] as List) .whereType>() @@ -66,9 +68,10 @@ class UsageRecord { final identifiers = { ...calls.map((call) => call.definition.identifier), ...instances.map((instance) => instance.definition.identifier), - }.toList(); + }.asMapToIndices; + final uris = { - ...identifiers.map((e) => e.uri), + ...identifiers.keys.map((e) => e.uri), ...calls.expand((call) => [ call.definition.location.uri, ...call.references.map((reference) => reference.location.uri), @@ -77,7 +80,7 @@ class UsageRecord { instance.definition.location.uri, ...instance.references.map((reference) => reference.location.uri), ]), - }.toList(); + }.asMapToIndices; final constants = { ...calls.expand((e) => e.references @@ -87,12 +90,15 @@ class UsageRecord { ...instances .expand((element) => element.references) .expand((e) => e.instanceConstant.fields.values) - }.flatten().toList(); + }.flatten().asMapToIndices; return { 'metadata': metadata.toJson(), - 'uris': uris, - 'ids': identifiers.map((identifier) => identifier.toJson(uris)).toList(), - 'constants': constants.map((e) => e.toJson(constants)).toList(), + 'uris': uris.keys.toList(), + 'ids': identifiers.keys + .map((identifier) => identifier.toJson(uris)) + .toList(), + 'constants': + constants.keys.map((constant) => constant.toJson(constants)).toList(), if (calls.isNotEmpty) 'calls': calls .map((reference) => reference.toJson(identifiers, uris, constants)) @@ -132,3 +138,9 @@ extension on Iterable { return constants; } } + +extension _PrivateIterableExtension on Iterable { + Map get asMapToIndices => Map.fromEntries( + mapIndexed((index, uri) => MapEntry(uri, index)), + ); +} diff --git a/pkg/record_use/lib/src/public/arguments.dart b/pkg/record_use/lib/src/public/arguments.dart index 97f83e693738..8b20ea357495 100644 --- a/pkg/record_use/lib/src/public/arguments.dart +++ b/pkg/record_use/lib/src/public/arguments.dart @@ -31,7 +31,7 @@ class Arguments { ); } - Map toJson(List constants) { + Map toJson(Map constants) { final hasConst = constArguments.named.isNotEmpty || constArguments.positional.isNotEmpty; final hasNonConst = nonConstArguments.named.isNotEmpty || @@ -70,22 +70,24 @@ class ConstArguments { ) => ConstArguments( positional: json['positional'] != null - ? (json['positional'] as Map).map((key, value) => - MapEntry(int.parse(key), constants[value as int])) + ? (json['positional'] as Map).map((position, + constantIndex) => + MapEntry(int.parse(position), constants[constantIndex as int])) : {}, named: json['named'] != null - ? (json['named'] as Map) - .map((key, value) => MapEntry(key, constants[value as int])) + ? (json['named'] as Map).map( + (name, constantIndex) => + MapEntry(name, constants[constantIndex as int])) : {}, ); - Map toJson(List constants) => { + Map toJson(Map constants) => { if (positional.isNotEmpty) - 'positional': positional.map((key, value) => - MapEntry(key.toString(), constants.indexOf(value))), + 'positional': positional.map((position, constantIndex) => + MapEntry(position.toString(), constants[constantIndex]!)), if (named.isNotEmpty) - 'named': named - .map((key, value) => MapEntry(key, constants.indexOf(value))), + 'named': named.map((name, constantIndex) => + MapEntry(name, constants[constantIndex]!)), }; @override @@ -111,9 +113,10 @@ class NonConstArguments { factory NonConstArguments.fromJson(Map json) => NonConstArguments( - positional: - json['positional'] != null ? json['positional'] as List : [], - named: json['named'] != null ? json['named'] as List : [], + positional: json['positional'] != null + ? (json['positional'] as List).cast() + : [], + named: json['named'] != null ? (json['named'] as List).cast() : [], ); Map toJson() => { diff --git a/pkg/record_use/lib/src/public/constant.dart b/pkg/record_use/lib/src/public/constant.dart index adf0a250fc2a..2d583dda0f8b 100644 --- a/pkg/record_use/lib/src/public/constant.dart +++ b/pkg/record_use/lib/src/public/constant.dart @@ -7,7 +7,7 @@ import '../helper.dart'; sealed class Constant { const Constant(); - Map toJson(List constants); + Map toJson(Map constants); static Constant fromJson( Map value, List constants) => @@ -17,7 +17,8 @@ sealed class Constant { IntConstant._type => IntConstant(value['value'] as int), StringConstant._type => StringConstant(value['value'] as String), ListConstant._type => ListConstant((value['value'] as List) - .map((e) => constants[e as int]) + .map((value) => value as int) + .map((value) => constants[value]) .toList()), MapConstant._type => MapConstant( (value['value'] as Map) @@ -33,7 +34,8 @@ final class NullConstant extends Constant { const NullConstant() : super(); @override - Map toJson(List constants) => _toJson(_type, null); + Map toJson(Map constants) => + _toJson(_type, null); } sealed class PrimitiveConstant extends Constant { @@ -52,7 +54,7 @@ sealed class PrimitiveConstant extends Constant { } @override - Map toJson(List constants) => valueToJson(); + Map toJson(Map constants) => valueToJson(); Map valueToJson(); } @@ -102,9 +104,9 @@ final class ListConstant extends Constant { } @override - Map toJson(List constants) => _toJson( + Map toJson(Map constants) => _toJson( _type, - value.map((constant) => constants.indexOf(constant)).toList(), + value.map((constant) => constants[constant]).toList(), ); } @@ -126,10 +128,9 @@ final class MapConstant extends Constant { } @override - Map toJson(List constants) => _toJson( + Map toJson(Map constants) => _toJson( _type, - value - .map((key, constant) => MapEntry(key, constants.indexOf(constant))), + value.map((key, constant) => MapEntry(key, constants[constant]!)), ); } diff --git a/pkg/record_use/lib/src/public/identifier.dart b/pkg/record_use/lib/src/public/identifier.dart index 7b1446b05539..57112e591859 100644 --- a/pkg/record_use/lib/src/public/identifier.dart +++ b/pkg/record_use/lib/src/public/identifier.dart @@ -20,8 +20,8 @@ class Identifier { name: json['name'] as String, ); - Map toJson(List uris) => { - 'uri': uris.indexOf(uri), + Map toJson(Map uris) => { + 'uri': uris[uri]!, if (parent != null) 'parent': parent, 'name': name, }; diff --git a/pkg/record_use/lib/src/public/instance_constant.dart b/pkg/record_use/lib/src/public/instance_constant.dart index fe3294baf70e..aa20ee0fbdd4 100644 --- a/pkg/record_use/lib/src/public/instance_constant.dart +++ b/pkg/record_use/lib/src/public/instance_constant.dart @@ -18,15 +18,15 @@ final class InstanceConstant { ) { return InstanceConstant( fields: (json['fields'] as Map).map( - (key, value) => MapEntry(key, constants[value as int]), + (key, constantIndex) => MapEntry(key, constants[constantIndex as int]), ), ); } - Map toJson(List constants) => { + Map toJson(Map constants) => { if (fields.isNotEmpty) - 'fields': fields - .map((key, value) => MapEntry(key, constants.indexOf(value))), + 'fields': fields.map((name, constantIndex) => + MapEntry(name, constants[constantIndex]!)), }; @override diff --git a/pkg/record_use/lib/src/public/location.dart b/pkg/record_use/lib/src/public/location.dart index f06fb191bc83..480bcc2fdeaf 100644 --- a/pkg/record_use/lib/src/public/location.dart +++ b/pkg/record_use/lib/src/public/location.dart @@ -22,9 +22,9 @@ class Location { ); } - Map toJson({List? uris}) { + Map toJson({Map? uris}) { return { - if (uris != null) 'uri': uris.indexOf(uri), + if (uris != null) 'uri': uris[uri]!, 'line': line, 'column': column, }; diff --git a/pkg/record_use/lib/src/public/reference.dart b/pkg/record_use/lib/src/public/reference.dart index 5d0970bd9491..e266022b2a77 100644 --- a/pkg/record_use/lib/src/public/reference.dart +++ b/pkg/record_use/lib/src/public/reference.dart @@ -15,7 +15,11 @@ sealed class Reference { const Reference({this.loadingUnit, required this.location}); - Map toJson(List uris, List constants) => { + Map toJson( + Map uris, + Map constants, + ) => + { 'loadingUnit': loadingUnit, '@': location.toJson(uris: uris), }; @@ -59,7 +63,10 @@ final class CallReference extends Reference { } @override - Map toJson(List uris, List constants) { + Map toJson( + Map uris, + Map constants, + ) { final argumentJson = arguments?.toJson(constants) ?? {}; return { if (argumentJson.isNotEmpty) 'arguments': argumentJson, @@ -108,7 +115,11 @@ final class InstanceReference extends Reference { } @override - Map toJson(List uris, List constants) => { + Map toJson( + Map uris, + Map constants, + ) => + { 'instanceConstant': instanceConstant.toJson(constants), ...super.toJson(uris, constants), }; diff --git a/pkg/record_use/pubspec.yaml b/pkg/record_use/pubspec.yaml index 892126ddee6c..7827687b90b1 100644 --- a/pkg/record_use/pubspec.yaml +++ b/pkg/record_use/pubspec.yaml @@ -1,7 +1,7 @@ name: record_use description: > The serialization logic and API for the usage recording SDK feature. -version: 0.1.1 +version: 0.2.0 repository: https://github.com/dart-lang/sdk/tree/main/pkg/record_use environment: diff --git a/pkg/record_use/test/storage_test.dart b/pkg/record_use/test/storage_test.dart index 3b2623d7c9d4..7a8da25faa31 100644 --- a/pkg/record_use/test/storage_test.dart +++ b/pkg/record_use/test/storage_test.dart @@ -2,24 +2,27 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:convert'; + import 'package:record_use/record_use_internal.dart'; import 'package:test/test.dart'; import 'test_data.dart'; void main() { + final json = jsonDecode(recordedUsesJson) as Map; test( 'JSON', - () => expect(recordedUses.toJson(), recordedUsesJson), + () => expect(recordedUses.toJson(), json), ); test( 'Object', - () => expect(UsageRecord.fromJson(recordedUsesJson), recordedUses), + () => expect(UsageRecord.fromJson(json), recordedUses), ); test('Json->Object->Json', () { - expect(UsageRecord.fromJson(recordedUsesJson).toJson(), recordedUsesJson); + expect(UsageRecord.fromJson(json).toJson(), json); }); test('Object->Json->Object', () { diff --git a/pkg/record_use/test/test_data.dart b/pkg/record_use/test/test_data.dart index 52efeff3fd6c..eef4ddf79f6c 100644 --- a/pkg/record_use/test/test_data.dart +++ b/pkg/record_use/test/test_data.dart @@ -130,98 +130,149 @@ final recordedUses = UsageRecord( ], ); -final recordedUsesJson = { - 'metadata': { - 'comment': - 'Recorded references at compile time and their argument values, as far' - ' as known, to definitions annotated with @RecordUse', - 'version': '1.6.2-wip+5.-.2.z' +final recordedUsesJson = '''{ + "metadata": { + "comment": + "Recorded references at compile time and their argument values, as far as known, to definitions annotated with @RecordUse", + "version": "1.6.2-wip+5.-.2.z" }, - 'uris': [ - 'file://lib/_internal/js_runtime/lib/js_helper.dart', - 'file://benchmarks/OmnibusDeferred/dart/OmnibusDeferred.dart' + "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'} + "ids": [ + {"uri": 0, "parent": "MyClass", "name": "get:loadDeferredLibrary"}, + {"uri": 0, "name": "MyAnnotation"} ], - 'constants': [ - {'type': 'String', 'value': 'jenkins'}, - {'type': 'String', 'value': 'mercury'}, - {'type': 'String', 'value': 'lib_SHA1'}, - {'type': 'bool', 'value': false}, - {'type': 'int', 'value': 1}, - {'type': 'String', 'value': 'camus'}, - {'type': 'String', 'value': 'einstein'}, - {'type': 'String', 'value': 'insert'}, + "constants": [ + {"type": "String", "value": "jenkins"}, + {"type": "String", "value": "mercury"}, + {"type": "String", "value": "lib_SHA1"}, + {"type": "bool", "value": false}, + {"type": "int", "value": 1}, + {"type": "String", "value": "camus"}, + {"type": "String", "value": "einstein"}, + {"type": "String", "value": "insert"}, { - 'type': 'list', - 'value': [6, 7, 3] + "type": "list", + "value": [6, 7, 3] }, { - 'type': 'list', - 'value': [5, 8, 6] + "type": "list", + "value": [5, 8, 6] }, - {'type': 'int', 'value': 0}, - {'type': 'int', 'value': 99}, + {"type": "int", "value": 0}, + {"type": "int", "value": 99}, { - 'type': 'map', - 'value': {'key': 11} + "type": "map", + "value": {"key": 11} }, - {'type': 'int', 'value': 42}, - {'type': 'Null'} + {"type": "int", "value": 42}, + {"type": "Null"} ], - 'calls': [ + "calls": [ { - 'definition': { - 'id': 0, - '@': {'line': 12, 'column': 67}, - 'loadingUnit': 'part_15.js' + "definition": { + "id": 0, + "@": {"line": 12, "column": 67}, + "loadingUnit": "part_15.js" }, - 'references': [ + "references": [ { - 'arguments': { - 'const': { - 'positional': {'0': 2, '1': 3, '2': 4}, - 'named': {'leroy': 0, 'freddy': 1} + "arguments": { + "const": { + "positional": {"0": 2, "1": 3, "2": 4}, + "named": {"leroy": 0, "freddy": 1} } }, - 'loadingUnit': 'o.js', - '@': {'uri': 1, 'line': 14, 'column': 49} + "loadingUnit": "o.js", + "@": {"uri": 1, "line": 14, "column": 49} }, { - 'arguments': { - 'const': { - 'positional': {'0': 2, '2': 10, '4': 12}, - 'named': {'leroy': 0, 'albert': 9} + "arguments": { + "const": { + "positional": {"0": 2, "2": 10, "4": 12}, + "named": {"leroy": 0, "albert": 9} }, - 'nonConst': { - 'positional': [1], - 'named': ['freddy'] + "nonConst": { + "positional": [1], + "named": ["freddy"] } }, - 'loadingUnit': 'o.js', - '@': {'uri': 1, 'line': 14, 'column': 48} + "loadingUnit": "o.js", + "@": {"uri": 1, "line": 14, "column": 48} + } + ] + } + ], + "instances": [ + { + "definition": { + "id": 1, + "@": {"line": 15, "column": 30}, + "loadingUnit": null + }, + "references": [ + { + "instanceConstant": { + "fields": {"a": 13, "b": 14} + }, + "loadingUnit": "3", + "@": {"uri": 0, "line": 40, "column": 30} } ] } + ] +}'''; + +final recordedUsesJson2 = '''{ + "metadata": { + "comment": "Recorded usages of objects tagged with a `RecordUse` annotation", + "version": "0.1.0" + }, + "uris": [ + "package:drop_dylib_recording/src/drop_dylib_recording.dart", + "drop_dylib_recording_shake.dart" + ], + "ids": [ + { + "uri": 0, + "name": "getMathMethod" + } ], - 'instances': [ + "constants": [ { - 'definition': { - 'id': 1, - '@': {'line': 15, 'column': 30}, - 'loadingUnit': null + "type": "String", + "value": "add" + } + ], + "calls": [ + { + "definition": { + "id": 0, + "@": { + "line": 10, + "column": 6 + }, + "loadingUnit": "1" }, - 'references': [ + "references": [ { - 'instanceConstant': { - 'fields': {'a': 13, 'b': 14} + "arguments": { + "const": { + "positional": { + "0": 0 + } + } }, - 'loadingUnit': '3', - '@': {'uri': 0, 'line': 40, 'column': 30} + "loadingUnit": "1", + "@": { + "uri": 1, + "line": 8, + "column": 3 + } } ] } ] -}; +}'''; diff --git a/pkg/record_use/test/usage_test.dart b/pkg/record_use/test/usage_test.dart index ae9e5fd90dd0..e9723ee1d758 100644 --- a/pkg/record_use/test/usage_test.dart +++ b/pkg/record_use/test/usage_test.dart @@ -2,6 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:convert'; + import 'package:record_use/record_use_internal.dart'; import 'package:test/test.dart'; @@ -10,17 +12,22 @@ import 'test_data.dart'; void main() { test('All API calls', () { expect( - RecordedUsages.fromJson(recordedUsesJson).argumentsTo(callId), + RecordedUsages.fromJson( + jsonDecode(recordedUsesJson) as Map) + .argumentsTo(callId), recordedUses.calls.expand((e) => e.references).map((e) => e.arguments), ); }); + test('All API instances', () { final references = recordedUses.instances.expand((instance) => instance.references); - final instances = - RecordedUsages.fromJson(recordedUsesJson).instancesOf(instanceId); + final instances = RecordedUsages.fromJson( + jsonDecode(recordedUsesJson) as Map) + .instancesOf(instanceId); expect(instances, references); }); + test('Specific API calls', () { final callId = Identifier( uri: Uri.parse('file://lib/_internal/js_runtime/lib/js_helper.dart') @@ -28,8 +35,10 @@ void main() { parent: 'MyClass', name: 'get:loadDeferredLibrary', ); - final arguments = - RecordedUsages.fromJson(recordedUsesJson).argumentsTo(callId)!.toList(); + final arguments = RecordedUsages.fromJson( + jsonDecode(recordedUsesJson) as Map) + .argumentsTo(callId)! + .toList(); expect( arguments[0].constArguments.named, const { @@ -71,7 +80,10 @@ void main() { name: 'MyAnnotation', ); expect( - RecordedUsages.fromJson(recordedUsesJson).instancesOf(instanceId)?.first, + RecordedUsages.fromJson( + jsonDecode(recordedUsesJson) as Map) + .instancesOf(instanceId) + ?.first, InstanceReference( instanceConstant: const InstanceConstant( fields: {'a': IntConstant(42), 'b': NullConstant()}, @@ -81,4 +93,17 @@ void main() { ), ); }); + + test('HasNonConstInstance', () { + final id = const Identifier( + uri: 'package:drop_dylib_recording/src/drop_dylib_recording.dart', + name: 'getMathMethod', + ); + + expect( + RecordedUsages.fromJson( + jsonDecode(recordedUsesJson2) as Map) + .hasNonConstArguments(id), + false); + }); } diff --git a/pkg/record_use/test_data/drop_dylib_recording/.gitignore b/pkg/record_use/test_data/drop_dylib_recording/.gitignore new file mode 100644 index 000000000000..3c200b5c7b47 --- /dev/null +++ b/pkg/record_use/test_data/drop_dylib_recording/.gitignore @@ -0,0 +1 @@ +bin/drop_dylib_link/ diff --git a/pkg/record_use/test_data/drop_dylib_recording/README.md b/pkg/record_use/test_data/drop_dylib_recording/README.md new file mode 100644 index 000000000000..f1b7103439bb --- /dev/null +++ b/pkg/record_use/test_data/drop_dylib_recording/README.md @@ -0,0 +1,25 @@ +This sample builds a native library for adding and multiplying. It then uses +the recorded usages feature to tree-shake unused libraries out. + +## Usage: + +### Keep all: +``` +devdart --enable-experiment=native-assets,record-use build bin/drop_dylib_recording_all.dart +``` +The `lib/` folder now contains both libraries +``` +./bin/drop_dylib_recording_all/drop_dylib_recording_all.exe add +``` +Prints `Hello world: 7!` + + +### Treeshake: +``` +devdart --enable-experiment=native-assets,record-use build bin/drop_dylib_recording_shake.dart +``` +The `lib/` folder now contains only the `add` library. +``` +./bin/drop_dylib_recording_shake/drop_dylib_recording_shake.exe +``` +Prints `Hello world: 7!` diff --git a/pkg/vm/testcases/transformations/resource_identifier/wrongly_typed_metadata_throws.dart b/pkg/record_use/test_data/drop_dylib_recording/bin/drop_dylib_recording_all.dart similarity index 51% rename from pkg/vm/testcases/transformations/resource_identifier/wrongly_typed_metadata_throws.dart rename to pkg/record_use/test_data/drop_dylib_recording/bin/drop_dylib_recording_all.dart index 603246368d79..0ba8f46f45ad 100644 --- a/pkg/vm/testcases/transformations/resource_identifier/wrongly_typed_metadata_throws.dart +++ b/pkg/record_use/test_data/drop_dylib_recording/bin/drop_dylib_recording_all.dart @@ -2,15 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:meta/meta.dart' show ResourceIdentifier; +import 'package:drop_dylib_recording/drop_dylib_recording.dart'; -void main() { - print(SomeClass.setMetadata(42)); -} - -class SomeClass { - @ResourceIdentifier({'a set'}) - static setMetadata(int i) { - return i + 1; - } +void main(List arguments) { + getMathMethod(arguments.first); } diff --git a/pkg/record_use/test_data/drop_dylib_recording/bin/drop_dylib_recording_shake.dart b/pkg/record_use/test_data/drop_dylib_recording/bin/drop_dylib_recording_shake.dart new file mode 100644 index 000000000000..ca477635b69b --- /dev/null +++ b/pkg/record_use/test_data/drop_dylib_recording/bin/drop_dylib_recording_shake.dart @@ -0,0 +1,9 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:drop_dylib_recording/drop_dylib_recording.dart'; + +void main(List arguments) { + getMathMethod('add'); +} diff --git a/pkg/record_use/test_data/drop_dylib_recording/hook/build.dart b/pkg/record_use/test_data/drop_dylib_recording/hook/build.dart new file mode 100644 index 000000000000..41030887e9bc --- /dev/null +++ b/pkg/record_use/test_data/drop_dylib_recording/hook/build.dart @@ -0,0 +1,45 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:logging/logging.dart'; +import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_toolchain_c/native_toolchain_c.dart'; + +void main(List arguments) async { + await build(arguments, (config, output) async { + final logger = Logger('') + ..level = Level.ALL + ..onRecord.listen((record) { + print('${record.level.name}: ${record.time}: ${record.message}'); + }); + final linkInPackage = config.linkingEnabled ? config.packageName : null; + await CBuilder.library( + name: 'add', + assetName: 'dylib_add', + sources: [ + 'src/native_add.c', + ], + linkModePreference: LinkModePreference.dynamic, + ).run( + config: config, + output: output, + logger: logger, + linkInPackage: linkInPackage, + ); + + await CBuilder.library( + name: 'multiply', + assetName: 'dylib_multiply', + sources: [ + 'src/native_multiply.c', + ], + linkModePreference: LinkModePreference.dynamic, + ).run( + config: config, + output: output, + logger: logger, + linkInPackage: linkInPackage, + ); + }); +} diff --git a/pkg/record_use/test_data/drop_dylib_recording/hook/link.dart b/pkg/record_use/test_data/drop_dylib_recording/hook/link.dart new file mode 100644 index 000000000000..7c114a6f6c52 --- /dev/null +++ b/pkg/record_use/test_data/drop_dylib_recording/hook/link.dart @@ -0,0 +1,64 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:convert'; +import 'dart:io'; + +import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:record_use/record_use.dart'; + +final id = const Identifier( + uri: 'package:drop_dylib_recording/src/drop_dylib_recording.dart', + name: 'getMathMethod', +); + +void main(List arguments) async { + await link(arguments, (config, output) async { + final file = File.fromUri(config.recordedUsagesFile!); + final string = await file.readAsString(); + final usages = + RecordedUsages.fromJson(jsonDecode(string) as Map); + + print(''' +Received ${config.assets.length} assets: ${config.assets.map((e) => e.id)}. +'''); + final f = File.fromUri(config.outputDirectory.resolve('debug.txt')) + ..createSync(); + f.writeAsStringSync(config.assets + .map( + (e) => e.id, + ) + .join('\n')); + f.writeAsStringSync('\nnow', mode: FileMode.append); + + if (usages.hasNonConstArguments(id)) { + //Keep all assets + output.addAssets(config.assets); + f.writeAsStringSync('\nhasNonConstargs', mode: FileMode.append); + + f.writeAsStringSync( + '\n${usages.argumentsTo(id)!.first.nonConstArguments.toJson()}', + mode: FileMode.append); + } else { + f.writeAsStringSync('\nno-hasNonConstargs', mode: FileMode.append); + //Tree-shake unused assets + final arguments = usages.argumentsTo(id) ?? []; + for (final argument in arguments) { + f.writeAsStringSync('\nArg: $argument', mode: FileMode.append); + final symbol = + (argument.constArguments.positional[0] as StringConstant).value; + f.writeAsStringSync('\nsymbol: $symbol', mode: FileMode.append); + + output.addAssets( + config.assets.where((asset) => asset.id.endsWith(symbol)), + ); + } + } + + print(''' +Keeping only ${output.assets.map((e) => e.id)}. +'''); + output.addDependency(config.packageRoot.resolve('hook/link.dart')); + }); +} diff --git a/pkg/record_use/test_data/drop_dylib_recording/lib/drop_dylib_recording.dart b/pkg/record_use/test_data/drop_dylib_recording/lib/drop_dylib_recording.dart new file mode 100644 index 000000000000..4041f227cb41 --- /dev/null +++ b/pkg/record_use/test_data/drop_dylib_recording/lib/drop_dylib_recording.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/drop_dylib_recording.dart'; diff --git a/pkg/record_use/test_data/drop_dylib_recording/lib/src/drop_dylib_recording.dart b/pkg/record_use/test_data/drop_dylib_recording/lib/src/drop_dylib_recording.dart new file mode 100644 index 000000000000..4c6e232e83a9 --- /dev/null +++ b/pkg/record_use/test_data/drop_dylib_recording/lib/src/drop_dylib_recording.dart @@ -0,0 +1,24 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:meta/meta.dart'; + +import 'drop_dylib_recording_bindings.dart' as bindings; + +@RecordUse() +void getMathMethod(String symbol) { + if (symbol == 'add') { + print('Hello world: ${_MyMath.add(3, 4)}!'); + } else if (symbol == 'multiply') { + print('Hello world: ${_MyMath.multiply(3, 4)}!'); + } else { + throw ArgumentError('Must pass either "add" or "multiply"'); + } +} + +class _MyMath { + static int add(int a, int b) => bindings.add(a, b); + + static int multiply(int a, int b) => bindings.multiply(a, b); +} diff --git a/pkg/record_use/test_data/drop_dylib_recording/lib/src/drop_dylib_recording_bindings.dart b/pkg/record_use/test_data/drop_dylib_recording/lib/src/drop_dylib_recording_bindings.dart new file mode 100644 index 000000000000..8a1c412b3e4b --- /dev/null +++ b/pkg/record_use/test_data/drop_dylib_recording/lib/src/drop_dylib_recording_bindings.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:ffi' as ffi; + +@ffi.Native( + assetId: 'package:drop_dylib_recording/dylib_add') +external int add( + int a, + int b, +); + +@ffi.Native( + assetId: 'package:drop_dylib_recording/dylib_multiply') +external int multiply( + int a, + int b, +); diff --git a/pkg/record_use/test_data/drop_dylib_recording/pubspec.yaml b/pkg/record_use/test_data/drop_dylib_recording/pubspec.yaml new file mode 100644 index 000000000000..7300f6982e0d --- /dev/null +++ b/pkg/record_use/test_data/drop_dylib_recording/pubspec.yaml @@ -0,0 +1,27 @@ +name: drop_dylib_recording +description: Generate two dylibs, remove one in linking based on recorded usage. +version: 1.0.0 + +publish_to: none + +environment: + sdk: ^3.0.0 + +dependencies: + logging: ^1.1.1 + meta: any + native_assets_cli: + path: ../../../../third_party/pkg/native/pkgs/native_assets_cli/ + native_toolchain_c: + path: ../../../../third_party/pkg/native/pkgs/native_toolchain_c/ + record_use: + path: ../../../record_use/ + +dev_dependencies: + lints: any + test: any + + +dependency_overrides: + meta: + path: ../../../meta/ \ No newline at end of file diff --git a/pkg/record_use/test_data/drop_dylib_recording/src/native_add.c b/pkg/record_use/test_data/drop_dylib_recording/src/native_add.c new file mode 100644 index 000000000000..570e754fe1bc --- /dev/null +++ b/pkg/record_use/test_data/drop_dylib_recording/src/native_add.c @@ -0,0 +1,9 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "native_add.h" + +MYLIB_EXPORT int32_t add(int32_t a, int32_t b) { + return a + b; +} diff --git a/pkg/record_use/test_data/drop_dylib_recording/src/native_add.h b/pkg/record_use/test_data/drop_dylib_recording/src/native_add.h new file mode 100644 index 000000000000..275878fe8569 --- /dev/null +++ b/pkg/record_use/test_data/drop_dylib_recording/src/native_add.h @@ -0,0 +1,13 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include + +#if _WIN32 +#define MYLIB_EXPORT __declspec(dllexport) +#else +#define MYLIB_EXPORT +#endif + +MYLIB_EXPORT int32_t add(int32_t a, int32_t b); diff --git a/pkg/record_use/test_data/drop_dylib_recording/src/native_multiply.c b/pkg/record_use/test_data/drop_dylib_recording/src/native_multiply.c new file mode 100644 index 000000000000..37ad92677e9c --- /dev/null +++ b/pkg/record_use/test_data/drop_dylib_recording/src/native_multiply.c @@ -0,0 +1,9 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "native_multiply.h" + +MYLIB_EXPORT intptr_t multiply(intptr_t a, intptr_t b) { + return a * b; +} diff --git a/pkg/record_use/test_data/drop_dylib_recording/src/native_multiply.h b/pkg/record_use/test_data/drop_dylib_recording/src/native_multiply.h new file mode 100644 index 000000000000..1c326f4cae57 --- /dev/null +++ b/pkg/record_use/test_data/drop_dylib_recording/src/native_multiply.h @@ -0,0 +1,13 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include + +#if _WIN32 +#define MYLIB_EXPORT __declspec(dllexport) +#else +#define MYLIB_EXPORT +#endif + +MYLIB_EXPORT intptr_t multiply(intptr_t a, intptr_t b); diff --git a/pkg/record_use/test_data/manifest.yaml b/pkg/record_use/test_data/manifest.yaml new file mode 100644 index 000000000000..15cd129909db --- /dev/null +++ b/pkg/record_use/test_data/manifest.yaml @@ -0,0 +1,26 @@ +- drop_dylib_recording/pubspec.yaml +- drop_dylib_recording/lib/src/drop_dylib_recording_bindings.dart +- drop_dylib_recording/lib/src/drop_dylib_recording.dart +- drop_dylib_recording/lib/drop_dylib_recording.dart +- drop_dylib_recording/README.md +- drop_dylib_recording/src/native_add.h +- drop_dylib_recording/src/native_multiply.h +- drop_dylib_recording/src/native_add.c +- drop_dylib_recording/src/native_multiply.c +- drop_dylib_recording/hook/link.dart +- drop_dylib_recording/hook/build.dart +- drop_dylib_recording/bin/drop_dylib_recording_all.dart +- drop_dylib_recording/bin/drop_dylib_recording_shake.dart +- drop_dylib_recording/pubspec.yaml +- drop_dylib_recording/lib/src/drop_dylib_recording_bindings.dart +- drop_dylib_recording/lib/src/drop_dylib_recording.dart +- drop_dylib_recording/lib/drop_dylib_recording.dart +- drop_dylib_recording/README.md +- drop_dylib_recording/src/native_add.h +- drop_dylib_recording/src/native_multiply.h +- drop_dylib_recording/src/native_add.c +- drop_dylib_recording/src/native_multiply.c +- drop_dylib_recording/hook/link.dart +- drop_dylib_recording/hook/build.dart +- drop_dylib_recording/bin/drop_dylib_recording_all.dart +- drop_dylib_recording/bin/drop_dylib_recording_shake.dart diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart index 61ec5e60d2a5..a85e49239ccd 100644 --- a/pkg/vm/lib/kernel_front_end.dart +++ b/pkg/vm/lib/kernel_front_end.dart @@ -61,7 +61,7 @@ import 'transformations/no_dynamic_invocations_annotator.dart' as no_dynamic_invocations_annotator show transformComponent; import 'transformations/obfuscation_prohibitions_annotator.dart' as obfuscationProhibitions; -import 'transformations/resource_identifier.dart' as resource_identifier; +import 'transformations/record_use/record_use.dart' as record_use; import 'transformations/to_string_transformer.dart' as to_string_transformer; import 'transformations/type_flow/transformer.dart' as globalTypeFlow show transformComponent; @@ -113,8 +113,8 @@ void declareCompilerOptions(ArgParser args) { args.addOption('native-assets', help: 'Provide the native-assets mapping for @Native external functions.'); - args.addOption('resources-file', - help: 'The path to store the collected usages of resource identifiers.'); + args.addOption('recorded-usages-file', + help: 'The path to store the recorded usages.'); args.addOption('target', help: 'Target model that determines what core libraries are available', allowed: ['vm', 'flutter', 'flutter_runner', 'dart_runner'], @@ -205,7 +205,7 @@ Future runCompiler(ArgResults options, String usage) async { } final String? nativeAssetsPath = options['native-assets']; - final String? resourcesFilePath = options['resources-file']; + final String? recordedUsagesFile = options['recorded-usages-file']; final bool splitOutputByPackages = options['split-output-by-packages']; final String? input = options.rest.singleOrNull; if ((input == null && (nativeAssetsPath == null || splitOutputByPackages)) || @@ -293,8 +293,8 @@ Future runCompiler(ArgResults options, String usage) async { final Uri? nativeAssetsUri = nativeAssetsPath == null ? null : resolveInputUri(nativeAssetsPath); - final Uri? resourcesFileUri = - resourcesFilePath == null ? null : resolveInputUri(resourcesFilePath); + final Uri? recordedUsagesUri = + recordedUsagesFile == null ? null : resolveInputUri(recordedUsagesFile); final String? dynamicInterfaceFilePath = options['dynamic-interface']; final Uri? dynamicInterfaceUri = dynamicInterfaceFilePath == null @@ -340,7 +340,7 @@ Future runCompiler(ArgResults options, String usage) async { options: compilerOptions, additionalSources: additionalSources, nativeAssets: nativeAssetsUri, - resourcesFile: resourcesFileUri, + recordedUsages: recordedUsagesUri, includePlatform: additionalDills.isNotEmpty, deleteToStringPackageUris: options['delete-tostring-package-uri'], keepClassNamesImplementing: options['keep-class-names-implementing'], @@ -449,7 +449,7 @@ class KernelCompilationArguments { final CompilerOptions? options; final List additionalSources; final Uri? nativeAssets; - final Uri? resourcesFile; + final Uri? recordedUsages; final bool requireMain; final bool includePlatform; final List deleteToStringPackageUris; @@ -471,7 +471,7 @@ class KernelCompilationArguments { this.options, this.additionalSources = const [], this.nativeAssets, - this.resourcesFile, + this.recordedUsages, this.requireMain = true, this.includePlatform = false, this.deleteToStringPackageUris = const [], @@ -660,9 +660,10 @@ Future runGlobalTransformations(Target target, Component component, deferred_loading.transformComponent(component, coreTypes, target); - final resourcesFile = args.resourcesFile; - if (resourcesFile != null) { - resource_identifier.transformComponent(component, resourcesFile); + final recordedUsagesFile = args.recordedUsages; + if (recordedUsagesFile != null) { + assert(args.source != null); + record_use.transformComponent(component, recordedUsagesFile, args.source!); } } diff --git a/pkg/vm/lib/transformations/record_use/record_call.dart b/pkg/vm/lib/transformations/record_use/record_call.dart new file mode 100644 index 000000000000..c8930c35c5a9 --- /dev/null +++ b/pkg/vm/lib/transformations/record_use/record_call.dart @@ -0,0 +1,148 @@ +// 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 'package:front_end/src/kernel/record_use.dart' as recordUse; +import 'package:kernel/ast.dart' as ast; +import 'package:record_use/record_use_internal.dart'; +import 'package:vm/metadata/loading_units.dart'; +import 'package:vm/transformations/record_use/record_use.dart'; + +class StaticCallRecorder { + final Map> callsForMethod = {}; + final List _loadingUnits; + final Uri source; + + StaticCallRecorder(this.source, this._loadingUnits); + + void recordStaticCall(ast.StaticInvocation node) { + final annotations = recordUse.findRecordUseAnnotation(node.target); + if (annotations.isNotEmpty) { + final call = _getCall(node.target); + + // Collect the (int, bool, double, or String) arguments passed in the call. + call.references.add(_createCallReference(node)); + } + } + + void recordTearoff(ast.ConstantExpression node) { + final constant = node.constant; + if (constant is ast.StaticTearOffConstant) { + final annotations = recordUse.findRecordUseAnnotation(constant.target); + if (annotations.isNotEmpty) { + final call = _getCall(constant.target); + CallReference reference = _collectTearOff(constant, node); + call.references.add(reference); + } + } + } + + /// Record a tear off as a call with all non-const arguments. + CallReference _collectTearOff( + ast.StaticTearOffConstant constant, + ast.TreeNode node, + ) { + final function = constant.target.function; + final nonConstArguments = NonConstArguments( + named: + function.namedParameters.map((parameter) => parameter.name!).toList(), + positional: List.generate( + function.positionalParameters.length, + (index) => index, + ), + ); + return CallReference( + location: node.location!.recordLocation(getIdentifierUri( + enclosingLibrary(node)!, + source, + )), + arguments: Arguments(nonConstArguments: nonConstArguments), + ); + } + + /// Collect the name and definition location of the invocation. This is + /// shared across multiple calls to the same method. + Usage _getCall(ast.Procedure target) { + final definition = _definitionFromMember(target); + return callsForMethod.putIfAbsent( + target, + () => Usage(definition: definition, references: []), + ); + } + + CallReference _createCallReference(ast.StaticInvocation node) { + // Get rid of the artificial `this` argument for extension methods. + final int argumentStart; + if (node.target.isExtensionMember || node.target.isExtensionTypeMember) { + argumentStart = 1; + } else { + argumentStart = 0; + } + + final positionalArguments = node.arguments.positional + .skip(argumentStart) + .mapIndexed((i, argument) => MapEntry(i, _evaluateLiteral(argument))); + final namedArguments = node.arguments.named.map( + (argument) => MapEntry(argument.name, _evaluateLiteral(argument.value)), + ); + + // Group by the evaluated literal - if it exists, the argument was const. + final positionalGrouped = _groupByNull(positionalArguments); + final namedGrouped = _groupByNull(namedArguments); + + return CallReference( + location: node.location!.recordLocation(getIdentifierUri( + enclosingLibrary(node)!, + source, + )), + loadingUnit: loadingUnitForNode(node, _loadingUnits).toString(), + arguments: Arguments( + constArguments: ConstArguments( + positional: positionalGrouped[false] != null + ? Map.fromEntries(positionalGrouped[false]! + .map((e) => MapEntry(e.key, e.value!))) + : null, + named: namedGrouped[false] != null + ? Map.fromEntries( + namedGrouped[false]!.map((e) => MapEntry(e.key, e.value!))) + : null, + ), + nonConstArguments: NonConstArguments( + positional: positionalGrouped[true]?.map((e) => e.key).toList(), + named: namedGrouped[true]?.map((e) => e.key).toList(), + ), + ), + ); + } + + Map>> _groupByNull( + Iterable> arguments) => + groupBy(arguments, (entry) => entry.value == null); + + Constant? _evaluateLiteral(ast.Expression expression) { + if (expression is ast.BasicLiteral) { + return evaluateLiteral(expression); + } else if (expression is ast.ConstantExpression) { + return evaluateConstant(expression.constant); + } else { + return null; + } + } + + Definition _definitionFromMember(ast.Member target) { + final enclosingLibrary = target.enclosingLibrary; + String file = getIdentifierUri(enclosingLibrary, source); + + return Definition( + identifier: Identifier( + uri: file, + parent: target.enclosingClass?.name, + name: target.name.text, + ), + location: target.location!.recordLocation(file), + loadingUnit: + loadingUnitForNode(enclosingLibrary, _loadingUnits).toString(), + ); + } +} diff --git a/pkg/vm/lib/transformations/record_use/record_use.dart b/pkg/vm/lib/transformations/record_use/record_use.dart new file mode 100644 index 000000000000..2af30040fe98 --- /dev/null +++ b/pkg/vm/lib/transformations/record_use/record_use.dart @@ -0,0 +1,164 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:convert'; +import 'dart:io'; + +import 'package:_fe_analyzer_shared/src/util/relativize.dart' + show relativizeUri; +import 'package:collection/collection.dart'; +import 'package:kernel/ast.dart' as ast; +import 'package:pub_semver/pub_semver.dart'; +import 'package:record_use/record_use_internal.dart'; +import 'package:vm/metadata/loading_units.dart'; +import 'package:vm/transformations/record_use/record_call.dart'; + +/// Collect calls to methods annotated with `@RecordUse`. +/// +/// Identify and collect all calls to static methods annotated in the given +/// [component]. This requires the deferred loading to be handled already to +/// also save which loading unit the call is made in. Write the result into a +/// JSON at [recordedUsagesFile]. +/// +/// The purpose of this feature is to be able to pass the recorded information +/// to packages in a post-compilation step, allowing them to remove or modify +/// assets based on the actual usage in the code prior to bundling in the final +/// application. +ast.Component transformComponent( + ast.Component component, + Uri recordedUsagesFile, + Uri source, +) { + final tag = LoadingUnitsMetadataRepository.repositoryTag; + final loadingMetadata = + component.metadata[tag] as LoadingUnitsMetadataRepository; + final loadingUnits = loadingMetadata.mapping[component]?.loadingUnits ?? []; + + final staticCallRecorder = StaticCallRecorder(source, loadingUnits); + component.accept(_RecordUseVisitor( + staticCallRecorder, + )); + + final usages = _usages(staticCallRecorder.callsForMethod.values, []); + var usagesStorageFormat = usages.toJson(); + File.fromUri(recordedUsagesFile).writeAsStringSync( + JsonEncoder.withIndent(' ').convert(usagesStorageFormat), + ); + + return component; +} + +class _RecordUseVisitor extends ast.RecursiveVisitor { + final StaticCallRecorder staticCallRecorder; + _RecordUseVisitor(this.staticCallRecorder); + + @override + void visitStaticInvocation(ast.StaticInvocation node) { + staticCallRecorder.recordStaticCall(node); + node.visitChildren(this); + } + + @override + void visitConstantExpression(ast.ConstantExpression node) { + staticCallRecorder.recordTearoff(node); + super.visitConstantExpression(node); + } +} + +UsageRecord _usages( + Iterable> calls, + Iterable> instances, +) { + return UsageRecord( + metadata: Metadata( + comment: + 'Recorded usages of objects tagged with a `RecordUse` annotation', + version: Version(0, 1, 0), + ), + calls: calls.toList(), + instances: instances.toList(), + ); +} + +Constant evaluateConstant(ast.Constant constant) => switch (constant) { + ast.NullConstant() => NullConstant(), + ast.BoolConstant() => BoolConstant(constant.value), + ast.IntConstant() => IntConstant(constant.value), + ast.DoubleConstant() => _unsupported('DoubleConstant'), + ast.StringConstant() => StringConstant(constant.value), + ast.SymbolConstant() => StringConstant(constant.name), + ast.MapConstant() => MapConstant(Map.fromEntries( + constant.entries.map((e) => MapEntry( + (e.key as ast.StringConstant).value, evaluateConstant(e.value))), + )), + ast.ListConstant() => + ListConstant(constant.entries.map(evaluateConstant).toList()), + // The following are not supported, but theoretically could be, so they + // are listed explicitly here. + ast.InstanceConstant() => _unsupported('InstanceConstant'), + ast.AuxiliaryConstant() => _unsupported('AuxiliaryConstant'), + ast.SetConstant() => _unsupported('SetConstant'), + ast.RecordConstant() => _unsupported('RecordConstant'), + ast.InstantiationConstant() => _unsupported('InstantiationConstant'), + ast.TearOffConstant() => _unsupported('TearOffConstant'), + ast.TypedefTearOffConstant() => _unsupported('TypedefTearOffConstant'), + ast.TypeLiteralConstant() => _unsupported('TypeLiteralConstant'), + ast.UnevaluatedConstant() => _unsupported('UnevaluatedConstant'), + }; + +Constant evaluateLiteral(ast.BasicLiteral expression) => switch (expression) { + ast.NullLiteral() => NullConstant(), + ast.IntLiteral() => IntConstant(expression.value), + ast.BoolLiteral() => BoolConstant(expression.value), + ast.StringLiteral() => StringConstant(expression.value), + ast.DoubleLiteral() => _unsupported('DoubleLiteral'), + ast.BasicLiteral() => _unsupported(expression.runtimeType.toString()), + }; + +Never _unsupported(String constantType) => + throw UnsupportedError('$constantType is not supported for recording.'); + +extension RecordUseLocation on ast.Location { + Location recordLocation(String uri) => Location( + uri: uri, + line: line, + column: column, + ); +} + +String getIdentifierUri(ast.Library library, Uri source) { + String file; + final importUri = library.importUri; + if (importUri.isScheme('file')) { + file = relativizeUri(source, library.fileUri, Platform.isWindows); + } else { + file = library.importUri.toString(); + } + return file; +} + +ast.Library? enclosingLibrary(ast.TreeNode node) { + while (node is! ast.Library) { + final parent = node.parent; + if (parent == null) return null; + node = parent; + } + return node; +} + +int _loadingUnitForLibrary( + ast.Library library, + List loadingUnits, +) { + final importUri = library.importUri.toString(); + return loadingUnits + .firstWhereOrNull((unit) => unit.libraryUris.contains(importUri)) + ?.id ?? + -1; +} + +int loadingUnitForNode(ast.TreeNode node, List loadingUnits) { + final library = enclosingLibrary(node)!; + return _loadingUnitForLibrary(library, loadingUnits); +} diff --git a/pkg/vm/lib/transformations/resource_identifier.dart b/pkg/vm/lib/transformations/resource_identifier.dart deleted file mode 100644 index 9ec5f3fa23dd..000000000000 --- a/pkg/vm/lib/transformations/resource_identifier.dart +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'dart:convert'; -import 'dart:io'; - -import 'package:_fe_analyzer_shared/src/util/relativize.dart' - show relativizeUri; -import 'package:collection/collection.dart'; -import 'package:front_end/src/api_prototype/resource_identifier.dart' - as ResourceIdentifiers; -import 'package:kernel/ast.dart'; -import 'package:kernel/kernel.dart'; -import 'package:vm/metadata/loading_units.dart'; - -/// Collect calls to methods annotated with `@ResourceIdentifier`. -/// -/// Identify and collect all calls to static methods annotated in the given -/// [component]. This requires the deferred loading to be handled already to -/// also save which loading unit the call is made in. Write the result into a -/// JSON at [resourcesFile]. -/// -/// The purpose of this feature is to be able to pass the recorded information -/// to packages in a post-compilation step, allowing them to remove or modify -/// assets based on the actual usage in the code prior to bundling in the final -/// application. -Component transformComponent(Component component, Uri resourcesFile) { - final tag = LoadingUnitsMetadataRepository.repositoryTag; - final loadingMetadata = - component.metadata[tag] as LoadingUnitsMetadataRepository; - final loadingUnits = loadingMetadata.mapping[component]?.loadingUnits ?? []; - - final visitor = _ResourceIdentifierVisitor(loadingUnits); - for (final library in component.libraries) { - library.visitChildren(visitor); - } - - File.fromUri(resourcesFile).writeAsStringSync(_toJson(visitor.identifiers)); - - return component; -} - -String _toJson(List identifiers) { - return JsonEncoder.withIndent(' ').convert({ - '_comment': 'Resources referenced by annotated resource identifiers', - 'AppTag': 'TBD', - 'environment': { - 'dart.tool.dart2js': false, - }, - 'identifiers': identifiers, - }); -} - -class _ResourceIdentifierVisitor extends RecursiveVisitor { - final List identifiers = []; - final List _loadingUnits; - - _ResourceIdentifierVisitor(this._loadingUnits); - - @override - void visitStaticInvocation(StaticInvocation node) { - final annotations = - ResourceIdentifiers.findResourceAnnotations(node.target); - if (annotations.isNotEmpty) { - _collectCallInformation(node, _firstResourceId(annotations.first)); - annotations.forEach(node.target.annotations.remove); - } - node.visitChildren(this); - } - - /// In case a method has multiple `ResourceIdentifier` annotations, we just - /// take the first. - String _firstResourceId(InstanceConstant instance) { - final fields = instance.fieldValues; - final firstField = fields.entries.first; - final fieldValue = firstField.value; - return _evaluateConstant(fieldValue); - } - - String _evaluateConstant(Constant fieldValue) { - if (fieldValue case NullConstant()) { - return ''; - } else if (fieldValue case PrimitiveConstant()) { - return fieldValue.value.toString(); - } else { - // TODO(https://dartbug.com/55407): Support Map and List. - return throw UnsupportedError( - 'The type ${fieldValue.runtimeType} is not a ' - 'supported metadata type for `@ResourceIdentifier` annotations'); - } - } - - /// Collects all the information needed to transform [node]. - void _collectCallInformation(StaticInvocation node, String resourceId) { - // Collect the name and definition location of the invocation. This is - // shared across multiple calls to the same method. - final identifier = _identifierOf(node, resourceId); - identifiers.add(identifier); - - // Collect the call location and loading unit of the call. - final resourceFile = _resourceFile(node, identifier); - identifier.files.add(resourceFile); - - // Collect the (int, bool, double, or String) arguments passed in the call. - final reference = _reference(node); - resourceFile.references.add(reference); - } - - Identifier _identifierOf(StaticInvocation node, String resourceId) { - final identifierUri = relativizeUri( - Uri.base, node.target.enclosingLibrary.fileUri, Platform.isWindows); - - return identifiers - .where((id) => id.name == node.name.text && id.uri == identifierUri) - .firstOrNull ?? - Identifier( - name: node.name.text, - id: resourceId, - uri: identifierUri, - nonConstant: !node.isConst, - files: [], - ); - } - - static Library? _enclosingLibrary(TreeNode node) { - while (node is! Library) { - final parent = node.parent; - if (parent == null) return null; - node = parent; - } - return node; - } - - ResourceFile _resourceFile(StaticInvocation node, Identifier identifier) { - final enclosingLibrary = _enclosingLibrary(node)!; - final importUri = enclosingLibrary.importUri.toString(); - final id = _loadingUnits - .firstWhereOrNull( - (element) => element.libraryUris.contains(importUri)) - ?.id ?? - -1; - final resourceFile = - identifier.files.firstWhereOrNull((element) => element.part == id); - return resourceFile ?? ResourceFile(part: id, references: []); - } - - ResourceReference _reference(StaticInvocation node) { - // Get rid of the artificial `this` argument for extension methods. - final int argumentStart; - if (node.target.isExtensionMember || node.target.isExtensionTypeMember) { - argumentStart = 1; - } else { - argumentStart = 0; - } - final arguments = { - // TODO(mosuem): Support more than just literals here, - // by adding visitors for enum indices and other const expressions. - for (var i = argumentStart; i < node.arguments.positional.length; i++) - if (_evaluateLiteral(node.arguments.positional[i]) case var value?) - '${i + 1 - argumentStart}': value, - for (var argument in node.arguments.named) - if (_evaluateLiteral(argument.value) case var value?) - argument.name: value, - }; - - final location = node.location!; - return ResourceReference( - uri: relativizeUri(Uri.base, location.file, Platform.isWindows), - line: location.line, - column: location.column, - arguments: arguments, - ); - } - - static Object? _evaluateLiteral(Expression expression) => - expression is BasicLiteral ? expression.value : null; -} - -// TODO(mosum): Expose these classes externally, as they will have to be used -// when parsing the generated JSON file. -/// A method with a `@ResourceIdentifier` annotation. -/// -/// Each identifier has a list of [ResourceReference]s (method invocations). -/// These references are organized per [ResourceFile]. -class Identifier { - /// The uri of the library which contains [name]. - final String uri; - - // TODO(https://dartbug.com/55494): Add the surrounding class/extension. - // TODO(https://dartbug.com/55494): Support extension getters/setters. - // Or make fully qualitified, non-conflicting canonical names in another way. - /// The name of the method that has a `@ResourceIdentifier` annotation. - final String name; - - // TODO(https://dartbug.com/55494): Rename to metadata? - /// The metadata field of the first `@ResourceIdentifier` annotation on this - /// method. - final String id; - - // TODO(dacoharkes): Replace with `isConstant` or `isConst`. - /// Whether the method is not `const`. - final bool nonConstant; - - final List files; - - Identifier({ - required this.name, - required this.id, - required this.uri, - required this.nonConstant, - required this.files, - }); - - Map toJson() { - return { - 'name': name, - 'id': id, - 'uri': uri, - 'nonConstant': nonConstant, - 'files': files, - }; - } - - @override - String toString() { - return 'Identifier(name: $name, id: $id, uri: $uri, nonConstant: $nonConstant, files: $files)'; - } -} - -// TODO(https://dartbug.com/55494): Rename to loading unit. This 'File' refers -// to an output file, not a source file. -/// A loading unit. -/// -/// With deferred loading, Dart is compiled into separate loading units. -/// -/// [ResourceReference]s are in a loading unit. Knowing from which loading -/// unit a resource is used means that loading such resource can be deferred -/// to when that loading unit is loaded. -class ResourceFile { - /// Unique identifier for the loading unit. - /// - /// Loading units are constructed by the Dart compiler based on the `deferred` - /// keyword. As such these parts are not stable. - /// - /// By convention, these unique identifiers are integers in the VM backend. - final int part; - - /// The invocations of a method with a `@ResourceIdentifier` annotation. - final List references; - - ResourceFile({required this.part, required this.references}); - - Map toJson() { - return { - 'part': part, - 'references': references, - }; - } - - @override - String toString() => 'ResourceFile(part: $part, references: $references)'; -} - -/// An invocation of a method with a `@ResourceIdentifier` annotation. -class ResourceReference { - // TODO(https://dartbug.com/55494): Make source locations optional. - /// Library uri of the invocation. - final String uri; - - // TODO(https://dartbug.com/55494): Make source locations optional. - /// Line number of the invocation. - final int line; - - // TODO(https://dartbug.com/55494): Make source locations optional. - /// Column of the invocation. - final int column; - - // TODO(https://dartbug.com/55494): Should positional arguments be 0 indexed? - /// The mapping from parameters to constant argument value. - /// - /// The map only contains entries for the arguments which are constant. (Note - /// that `null` is a valid constant argument.) - /// - /// For arguments to positional parameters, the keys in this map are - /// [int.toString] of the position, 1 indexed. - /// - /// For arguments to named parameters, the keys in this map are the name of - /// the parameter. - final Map arguments; - - ResourceReference({ - required this.uri, - required this.line, - required this.column, - required this.arguments, - }); - - Map toJson() { - return { - '@': { - 'uri': uri, - 'line': line, - 'column': column, - }, - ...arguments, - }; - } - - @override - String toString() { - return 'ResourceReference(uri: $uri, line: $line, column: $column, arguments: $arguments)'; - } -} diff --git a/pkg/vm/lib/transformations/type_flow/signature_shaking.dart b/pkg/vm/lib/transformations/type_flow/signature_shaking.dart index 7f1465effb27..0ef59afb5a7e 100644 --- a/pkg/vm/lib/transformations/type_flow/signature_shaking.dart +++ b/pkg/vm/lib/transformations/type_flow/signature_shaking.dart @@ -6,8 +6,7 @@ import 'package:kernel/ast.dart'; import 'package:kernel/core_types.dart'; import 'package:kernel/type_environment.dart'; import 'package:front_end/src/api_unstable/vm.dart' show isExtensionTypeThis; -import 'package:front_end/src/api_prototype/resource_identifier.dart' - as ResourceIdentifiers; +import 'package:front_end/src/api_prototype/record_use.dart' as RecordUse; import 'analysis.dart'; import 'table_selector_assigner.dart'; @@ -212,10 +211,10 @@ class _ParameterInfo { } /// Disable signature shaking for annotated methods, to prevent removal of - /// parameters. The consumers of resources.json expect constant argument - /// values to be present for all parameters. + /// parameters. The consumers of recorded_usages.json expect constant + /// argument values to be present for all parameters. if (member is Procedure && - ResourceIdentifiers.findResourceAnnotations(member).isNotEmpty) { + RecordUse.findRecordUseAnnotation(member).isNotEmpty) { isChecked = true; } } diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart index 6bc509779f33..fe428d7239e3 100644 --- a/pkg/vm/lib/transformations/type_flow/transformer.dart +++ b/pkg/vm/lib/transformations/type_flow/transformer.dart @@ -9,8 +9,7 @@ import 'dart:core' hide Type; import 'package:front_end/src/api_prototype/static_weak_references.dart' show StaticWeakReferences; -import 'package:front_end/src/api_prototype/resource_identifier.dart' - as ResourceIdentifiers; +import 'package:front_end/src/api_prototype/record_use.dart' as RecordUse; import 'package:kernel/ast.dart' hide Statement, StatementVisitor; import 'package:kernel/ast.dart' as ast show Statement; import 'package:kernel/class_hierarchy.dart' @@ -279,7 +278,7 @@ class CleanupAnnotations extends RecursiveVisitor { /// We do not want to eliminate /// * `pragma`s /// * Protobuf annotations - /// * `ResourceIdentifier` annotations + /// * `RecordUse` annotations /// /// as we need these later in the pipeline. bool _keepAnnotation(Expression annotation) { @@ -289,11 +288,8 @@ class CleanupAnnotations extends RecursiveVisitor { final cls = constant.classNode; final usesProtobufAnnotation = protobufHandler?.usesAnnotationClass(cls) ?? false; - bool usesResourceIdentifier = - ResourceIdentifiers.isResourceIdentifier(cls); - return cls == pragmaClass || - usesProtobufAnnotation || - usesResourceIdentifier; + bool usesRecordUse = RecordUse.isRecordUse(cls); + return cls == pragmaClass || usesProtobufAnnotation || usesRecordUse; } } return false; diff --git a/pkg/vm/pubspec.yaml b/pkg/vm/pubspec.yaml index df235286b57c..428053b9c5d3 100644 --- a/pkg/vm/pubspec.yaml +++ b/pkg/vm/pubspec.yaml @@ -15,7 +15,9 @@ dependencies: crypto: any front_end: any kernel: any + record_use: any package_config: any + pub_semver: any yaml: any # Use 'any' constraints here; we get our versions from the DEPS file. diff --git a/pkg/vm/test/transformations/resource_identifier_test.dart b/pkg/vm/test/transformations/record_use_test.dart similarity index 88% rename from pkg/vm/test/transformations/resource_identifier_test.dart rename to pkg/vm/test/transformations/record_use_test.dart index 33f0f5d29fbf..4dae03557f1c 100644 --- a/pkg/vm/test/transformations/resource_identifier_test.dart +++ b/pkg/vm/test/transformations/record_use_test.dart @@ -36,9 +36,9 @@ void runTestCaseAot(Uri source, bool throws) async { final nopErrorDetector = ErrorDetector(); var tempDir = Directory.systemTemp.createTempSync().path; - var resourcesFile = Uri( + var recordedUsagesFile = Uri( scheme: 'file', - path: path.join(tempDir, 'resources.json'), + path: path.join(tempDir, 'recorded_usages.json'), ); runGlobalTransformations( target, @@ -49,7 +49,8 @@ void runTestCaseAot(Uri source, bool throws) async { enableAsserts: false, useProtobufTreeShakerV2: true, treeShakeWriteOnlyFields: true, - resourcesFile: resourcesFile, + recordedUsages: recordedUsagesFile, + source: source, )); verifyComponent( @@ -64,7 +65,7 @@ void runTestCaseAot(Uri source, bool throws) async { compareResultWithExpectationsFile(source, actual, expectFilePostfix: '.aot'); compareResultWithExpectationsFile( source, - File.fromUri(resourcesFile).readAsStringSync(), + File.fromUri(recordedUsagesFile).readAsStringSync(), expectFilePostfix: '.json', ); } @@ -72,9 +73,9 @@ void runTestCaseAot(Uri source, bool throws) async { void main(List args) { assert(args.isEmpty || args.length == 1); final filter = args.firstOrNull; - group('resource-identifier-transformations', () { + group('record-use-transformations', () { final testCasesDir = Directory.fromUri( - _pkgVmDir.resolve('testcases/transformations/resource_identifier')); + _pkgVmDir.resolve('testcases/transformations/record_use/')); for (var file in testCasesDir .listSync(recursive: true, followLinks: false) diff --git a/pkg/vm/testcases/transformations/resource_identifier/complex.dart b/pkg/vm/testcases/transformations/record_use/complex.dart similarity index 92% rename from pkg/vm/testcases/transformations/resource_identifier/complex.dart rename to pkg/vm/testcases/transformations/record_use/complex.dart index 38617fe09b0e..63a9e6f6bb6c 100644 --- a/pkg/vm/testcases/transformations/resource_identifier/complex.dart +++ b/pkg/vm/testcases/transformations/record_use/complex.dart @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:async'; -import 'package:meta/meta.dart' show ResourceIdentifier; +import 'package:meta/meta.dart' show RecordUse; void main() { print(OtherClass().someMethod(argument: 'argument!')); @@ -26,7 +26,7 @@ class OtherClass { ); } - @ResourceIdentifier('myresourceid') + @RecordUse() static Future generate(AssetBundle bundle, List args, String string, AnotherClass object, int index) async { final message = await bundle.byIndex(string: string, index: index); diff --git a/pkg/vm/testcases/transformations/resource_identifier/complex.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/complex.dart.aot.expect similarity index 97% rename from pkg/vm/testcases/transformations/resource_identifier/complex.dart.aot.expect rename to pkg/vm/testcases/transformations/record_use/complex.dart.aot.expect index 9c54557707f9..27cb3094d244 100644 --- a/pkg/vm/testcases/transformations/resource_identifier/complex.dart.aot.expect +++ b/pkg/vm/testcases/transformations/record_use/complex.dart.aot.expect @@ -6,7 +6,7 @@ import "package:meta/meta.dart" as meta; import "dart:collection" as col; import "dart:async"; -import "package:meta/meta.dart" show ResourceIdentifier; +import "package:meta/meta.dart" show RecordUse; class OtherClass extends core::Object { @@ -33,7 +33,7 @@ class OtherClass extends core::Object { [@vm.inferred-return-type.metadata=dart.async::_Future] [@vm.unboxing-info.metadata=(b,b,b,b,i)->b] - @#C3 + @#C2 static method generate([@vm.inferred-arg-type.metadata=#lib::AssetBundle] self::AssetBundle bundle, [@vm.inferred-arg-type.metadata=dart.core::_GrowableList] core::List args, [@vm.inferred-arg-type.metadata=dart.core::_OneByteString (value: "somestring")] core::String string, [@vm.inferred-arg-type.metadata=#lib::AnotherClass] self::AnotherClass object, [@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 42)] core::int index) → asy::Future async /* emittedValueType= core::String */ { final self::Message message = await [@vm.direct-call.metadata=#lib::AssetBundle.byIndex] [@vm.inferred-type.metadata=!? (skip check)] bundle.{self::AssetBundle::byIndex}(){({required index: core::int, required string: core::String}) → self::Message} /* runtimeCheckType= asy::Future */ ; return [@vm.direct-call.metadata=#lib::Message.generateString] [@vm.inferred-type.metadata=!? (skip check)] message.{self::Message::generateString}(args){(core::List, {required object: self::AnotherClass}) → asy::Future}; @@ -73,6 +73,5 @@ static method main() → void { } constants { #C1 = "argument!" - #C2 = "myresourceid" - #C3 = meta::ResourceIdentifier {metadata:#C2} + #C2 = meta::RecordUse {} } diff --git a/pkg/vm/testcases/transformations/record_use/complex.dart.json.expect b/pkg/vm/testcases/transformations/record_use/complex.dart.json.expect new file mode 100644 index 000000000000..9aa4ab1dff44 --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/complex.dart.json.expect @@ -0,0 +1,59 @@ +{ + "metadata": { + "comment": "Recorded usages of objects tagged with a `RecordUse` annotation", + "version": "0.1.0" + }, + "uris": [ + "complex.dart" + ], + "ids": [ + { + "uri": 0, + "parent": "OtherClass", + "name": "generate" + } + ], + "constants": [ + { + "type": "int", + "value": 42 + } + ], + "calls": [ + { + "definition": { + "id": 0, + "@": { + "line": 30, + "column": 25 + }, + "loadingUnit": "1" + }, + "references": [ + { + "arguments": { + "const": { + "positional": { + "4": 0 + } + }, + "nonConst": { + "positional": [ + 0, + 1, + 2, + 3 + ] + } + }, + "loadingUnit": "1", + "@": { + "uri": 0, + "line": 20, + "column": 18 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/different.dart b/pkg/vm/testcases/transformations/record_use/different.dart similarity index 100% rename from pkg/vm/testcases/transformations/resource_identifier/different.dart rename to pkg/vm/testcases/transformations/record_use/different.dart diff --git a/pkg/vm/testcases/transformations/resource_identifier/different.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/different.dart.aot.expect similarity index 100% rename from pkg/vm/testcases/transformations/resource_identifier/different.dart.aot.expect rename to pkg/vm/testcases/transformations/record_use/different.dart.aot.expect diff --git a/pkg/vm/testcases/transformations/record_use/different.dart.json.expect b/pkg/vm/testcases/transformations/record_use/different.dart.json.expect new file mode 100644 index 000000000000..d95dcddd5268 --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/different.dart.json.expect @@ -0,0 +1,9 @@ +{ + "metadata": { + "comment": "Recorded usages of objects tagged with a `RecordUse` annotation", + "version": "0.1.0" + }, + "uris": [], + "ids": [], + "constants": [] +} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/extension.dart b/pkg/vm/testcases/transformations/record_use/extension.dart similarity index 81% rename from pkg/vm/testcases/transformations/resource_identifier/extension.dart rename to pkg/vm/testcases/transformations/record_use/extension.dart index f9c4434d1413..76cb9db93f3a 100644 --- a/pkg/vm/testcases/transformations/resource_identifier/extension.dart +++ b/pkg/vm/testcases/transformations/record_use/extension.dart @@ -2,7 +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:meta/meta.dart' show ResourceIdentifier; +import 'package:meta/meta.dart' show RecordUse; void main() { SomeClass().callWithArgs('42'); @@ -11,7 +11,7 @@ void main() { class SomeClass {} extension on SomeClass { - @ResourceIdentifier('id') + @RecordUse() void callWithArgs(String s) { s += "suffix"; } diff --git a/pkg/vm/testcases/transformations/resource_identifier/extension.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/extension.dart.aot.expect similarity index 89% rename from pkg/vm/testcases/transformations/resource_identifier/extension.dart.aot.expect rename to pkg/vm/testcases/transformations/record_use/extension.dart.aot.expect index b4ba96ea877c..ff09c360f61d 100644 --- a/pkg/vm/testcases/transformations/resource_identifier/extension.dart.aot.expect +++ b/pkg/vm/testcases/transformations/record_use/extension.dart.aot.expect @@ -3,7 +3,7 @@ import self as self; import "dart:core" as core; import "package:meta/meta.dart" as meta; -import "package:meta/meta.dart" show ResourceIdentifier; +import "package:meta/meta.dart" show RecordUse; class SomeClass extends core::Object { synthetic constructor •() → self::SomeClass @@ -20,11 +20,10 @@ static method main() → void { } [@vm.inferred-return-type.metadata=dart.core::Null? (value: null)] -@#C2 +@#C1 static extension-member method _extension#0|callWithArgs([@vm.inferred-arg-type.metadata=#lib::SomeClass] lowered final self::SomeClass #this, [@vm.inferred-arg-type.metadata=dart.core::_OneByteString (value: "42")] core::String s) → void { s = [@vm.direct-call.metadata=dart.core::_StringBase.+] [@vm.inferred-type.metadata=!? (skip check)] s.{core::String::+}("suffix"){(core::String) → core::String}; } constants { - #C1 = "id" - #C2 = meta::ResourceIdentifier {metadata:#C1} + #C1 = meta::RecordUse {} } diff --git a/pkg/vm/testcases/transformations/record_use/extension.dart.json.expect b/pkg/vm/testcases/transformations/record_use/extension.dart.json.expect new file mode 100644 index 000000000000..69aeac0d2c8c --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/extension.dart.json.expect @@ -0,0 +1,50 @@ +{ + "metadata": { + "comment": "Recorded usages of objects tagged with a `RecordUse` annotation", + "version": "0.1.0" + }, + "uris": [ + "extension.dart" + ], + "ids": [ + { + "uri": 0, + "name": "_extension#0|callWithArgs" + } + ], + "constants": [ + { + "type": "String", + "value": "42" + } + ], + "calls": [ + { + "definition": { + "id": 0, + "@": { + "line": 15, + "column": 8 + }, + "loadingUnit": "1" + }, + "references": [ + { + "arguments": { + "const": { + "positional": { + "0": 0 + } + } + }, + "loadingUnit": "1", + "@": { + "uri": 0, + "line": 8, + "column": 15 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple.dart b/pkg/vm/testcases/transformations/record_use/loading_units_multiple.dart similarity index 100% rename from pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple.dart rename to pkg/vm/testcases/transformations/record_use/loading_units_multiple.dart diff --git a/pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/loading_units_multiple.dart.aot.expect similarity index 63% rename from pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple.dart.aot.expect rename to pkg/vm/testcases/transformations/record_use/loading_units_multiple.dart.aot.expect index f7990afc775a..7aaabe9a5b84 100644 --- a/pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple.dart.aot.expect +++ b/pkg/vm/testcases/transformations/record_use/loading_units_multiple.dart.aot.expect @@ -3,8 +3,8 @@ import self as self; import "loading_units_multiple_helper_shared.dart" as loa; import "loading_units_multiple_helper.dart" as loa2; -import "org-dartlang-test:///testcases/transformations/resource_identifier/loading_units_multiple_helper_shared.dart"; -import "org-dartlang-test:///testcases/transformations/resource_identifier/loading_units_multiple_helper.dart" deferred as helper; +import "org-dartlang-test:///testcases/transformations/record_use/loading_units_multiple_helper_shared.dart"; +import "org-dartlang-test:///testcases/transformations/record_use/loading_units_multiple_helper.dart" deferred as helper; [@vm.inferred-return-type.metadata=dart.async::_Future] diff --git a/pkg/vm/testcases/transformations/record_use/loading_units_multiple.dart.json.expect b/pkg/vm/testcases/transformations/record_use/loading_units_multiple.dart.json.expect new file mode 100644 index 000000000000..246bd5b90afb --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/loading_units_multiple.dart.json.expect @@ -0,0 +1,68 @@ +{ + "metadata": { + "comment": "Recorded usages of objects tagged with a `RecordUse` annotation", + "version": "0.1.0" + }, + "uris": [ + "loading_units_multiple_helper_shared.dart", + "loading_units_multiple.dart", + "loading_units_multiple_helper.dart" + ], + "ids": [ + { + "uri": 0, + "parent": "SomeClass", + "name": "someStaticMethod" + } + ], + "constants": [ + { + "type": "int", + "value": 42 + } + ], + "calls": [ + { + "definition": { + "id": 0, + "@": { + "line": 9, + "column": 15 + }, + "loadingUnit": "1" + }, + "references": [ + { + "arguments": { + "const": { + "positional": { + "0": 0 + } + } + }, + "loadingUnit": "1", + "@": { + "uri": 1, + "line": 12, + "column": 13 + } + }, + { + "arguments": { + "const": { + "positional": { + "0": 0 + } + } + }, + "loadingUnit": "2", + "@": { + "uri": 2, + "line": 8, + "column": 13 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple_helper.dart b/pkg/vm/testcases/transformations/record_use/loading_units_multiple_helper.dart similarity index 100% rename from pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple_helper.dart rename to pkg/vm/testcases/transformations/record_use/loading_units_multiple_helper.dart diff --git a/pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple_helper_shared.dart b/pkg/vm/testcases/transformations/record_use/loading_units_multiple_helper_shared.dart similarity index 76% rename from pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple_helper_shared.dart rename to pkg/vm/testcases/transformations/record_use/loading_units_multiple_helper_shared.dart index 3ca5ca480d7a..0d9da2600be5 100644 --- a/pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple_helper_shared.dart +++ b/pkg/vm/testcases/transformations/record_use/loading_units_multiple_helper_shared.dart @@ -2,9 +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:meta/meta.dart' show ResourceIdentifier; +import 'package:meta/meta.dart' show RecordUse; class SomeClass { - @ResourceIdentifier('id') + @RecordUse() static void someStaticMethod(int i) {} } diff --git a/pkg/vm/testcases/transformations/resource_identifier/loading_units_simple.dart b/pkg/vm/testcases/transformations/record_use/loading_units_simple.dart similarity index 88% rename from pkg/vm/testcases/transformations/resource_identifier/loading_units_simple.dart rename to pkg/vm/testcases/transformations/record_use/loading_units_simple.dart index 2af90d23fb4e..96d5d5daba8b 100644 --- a/pkg/vm/testcases/transformations/resource_identifier/loading_units_simple.dart +++ b/pkg/vm/testcases/transformations/record_use/loading_units_simple.dart @@ -6,7 +6,7 @@ // Both are only used in their respective loading units. // So, no dominant loading unit logic is applied. -import 'package:meta/meta.dart' show ResourceIdentifier; +import 'package:meta/meta.dart' show RecordUse; import 'loading_units_simple_helper.dart' deferred as helper; @@ -19,6 +19,6 @@ void main() async { } class SomeClass { - @ResourceIdentifier('id') + @RecordUse() static void someStaticMethod(int i) {} } diff --git a/pkg/vm/testcases/transformations/resource_identifier/loading_units_simple.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/loading_units_simple.dart.aot.expect similarity index 74% rename from pkg/vm/testcases/transformations/resource_identifier/loading_units_simple.dart.aot.expect rename to pkg/vm/testcases/transformations/record_use/loading_units_simple.dart.aot.expect index 290f2909421f..be08525a696a 100644 --- a/pkg/vm/testcases/transformations/resource_identifier/loading_units_simple.dart.aot.expect +++ b/pkg/vm/testcases/transformations/record_use/loading_units_simple.dart.aot.expect @@ -4,14 +4,14 @@ import "dart:core" as core; import "package:meta/meta.dart" as meta; import "loading_units_simple_helper.dart" as loa; -import "package:meta/meta.dart" show ResourceIdentifier; -import "org-dartlang-test:///testcases/transformations/resource_identifier/loading_units_simple_helper.dart" deferred as helper; +import "package:meta/meta.dart" show RecordUse; +import "org-dartlang-test:///testcases/transformations/record_use/loading_units_simple_helper.dart" deferred as helper; abstract class SomeClass extends core::Object { [@vm.inferred-return-type.metadata=dart.core::Null? (value: null)] [@vm.unboxing-info.metadata=(i)->b] - @#C2 + @#C1 static method someStaticMethod([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 42)] core::int i) → void {} } @@ -22,6 +22,5 @@ static method main() → void async /* emittedValueType= void */ { let final dynamic #t1 = CheckLibraryIsLoaded(helper) in loa::invokeDeferred(); } constants { - #C1 = "id" - #C2 = meta::ResourceIdentifier {metadata:#C1} + #C1 = meta::RecordUse {} } diff --git a/pkg/vm/testcases/transformations/record_use/loading_units_simple.dart.json.expect b/pkg/vm/testcases/transformations/record_use/loading_units_simple.dart.json.expect new file mode 100644 index 000000000000..1aedeb7f6ad2 --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/loading_units_simple.dart.json.expect @@ -0,0 +1,84 @@ +{ + "metadata": { + "comment": "Recorded usages of objects tagged with a `RecordUse` annotation", + "version": "0.1.0" + }, + "uris": [ + "loading_units_simple.dart", + "loading_units_simple_helper.dart" + ], + "ids": [ + { + "uri": 0, + "parent": "SomeClass", + "name": "someStaticMethod" + }, + { + "uri": 1, + "parent": "SomeClass", + "name": "someStaticMethod" + } + ], + "constants": [ + { + "type": "int", + "value": 42 + } + ], + "calls": [ + { + "definition": { + "id": 0, + "@": { + "line": 23, + "column": 15 + }, + "loadingUnit": "1" + }, + "references": [ + { + "arguments": { + "const": { + "positional": { + "0": 0 + } + } + }, + "loadingUnit": "1", + "@": { + "uri": 0, + "line": 14, + "column": 13 + } + } + ] + }, + { + "definition": { + "id": 1, + "@": { + "line": 13, + "column": 15 + }, + "loadingUnit": "2" + }, + "references": [ + { + "arguments": { + "const": { + "positional": { + "0": 0 + } + } + }, + "loadingUnit": "2", + "@": { + "uri": 1, + "line": 8, + "column": 13 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/loading_units_simple_helper.dart b/pkg/vm/testcases/transformations/record_use/loading_units_simple_helper.dart similarity index 80% rename from pkg/vm/testcases/transformations/resource_identifier/loading_units_simple_helper.dart rename to pkg/vm/testcases/transformations/record_use/loading_units_simple_helper.dart index 94d6403a7770..0b364f2992a6 100644 --- a/pkg/vm/testcases/transformations/resource_identifier/loading_units_simple_helper.dart +++ b/pkg/vm/testcases/transformations/record_use/loading_units_simple_helper.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:meta/meta.dart' show ResourceIdentifier; +import 'package:meta/meta.dart' show RecordUse; void invokeDeferred() { SomeClass.someStaticMethod(42); } class SomeClass { - @ResourceIdentifier('id') + @RecordUse() static void someStaticMethod(int i) {} } diff --git a/pkg/vm/testcases/transformations/resource_identifier/simple.dart b/pkg/vm/testcases/transformations/record_use/simple.dart similarity index 80% rename from pkg/vm/testcases/transformations/resource_identifier/simple.dart rename to pkg/vm/testcases/transformations/record_use/simple.dart index 0e43f5589fe8..0bc4db0ff096 100644 --- a/pkg/vm/testcases/transformations/resource_identifier/simple.dart +++ b/pkg/vm/testcases/transformations/record_use/simple.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:meta/meta.dart' show ResourceIdentifier; +import 'package:meta/meta.dart' show RecordUse; void main() { print(SomeClass.someStaticMethod(42)); } class SomeClass { - @ResourceIdentifier('id') + @RecordUse() static someStaticMethod(int i) { return i + 1; } diff --git a/pkg/vm/testcases/transformations/resource_identifier/simple.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/simple.dart.aot.expect similarity index 85% rename from pkg/vm/testcases/transformations/resource_identifier/simple.dart.aot.expect rename to pkg/vm/testcases/transformations/record_use/simple.dart.aot.expect index 40e79376b759..70c840eb34b3 100644 --- a/pkg/vm/testcases/transformations/resource_identifier/simple.dart.aot.expect +++ b/pkg/vm/testcases/transformations/record_use/simple.dart.aot.expect @@ -3,13 +3,13 @@ import self as self; import "dart:core" as core; import "package:meta/meta.dart" as meta; -import "package:meta/meta.dart" show ResourceIdentifier; +import "package:meta/meta.dart" show RecordUse; abstract class SomeClass extends core::Object { [@vm.inferred-return-type.metadata=int] [@vm.unboxing-info.metadata=(i)->i] - @#C2 + @#C1 static method someStaticMethod([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 42)] core::int i) → dynamic { return [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}; } @@ -20,6 +20,5 @@ static method main() → void { core::print([@vm.inferred-type.metadata=int] self::SomeClass::someStaticMethod(42)); } constants { - #C1 = "id" - #C2 = meta::ResourceIdentifier {metadata:#C1} + #C1 = meta::RecordUse {} } diff --git a/pkg/vm/testcases/transformations/record_use/simple.dart.json.expect b/pkg/vm/testcases/transformations/record_use/simple.dart.json.expect new file mode 100644 index 000000000000..d67172044d86 --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/simple.dart.json.expect @@ -0,0 +1,51 @@ +{ + "metadata": { + "comment": "Recorded usages of objects tagged with a `RecordUse` annotation", + "version": "0.1.0" + }, + "uris": [ + "simple.dart" + ], + "ids": [ + { + "uri": 0, + "parent": "SomeClass", + "name": "someStaticMethod" + } + ], + "constants": [ + { + "type": "int", + "value": 42 + } + ], + "calls": [ + { + "definition": { + "id": 0, + "@": { + "line": 13, + "column": 10 + }, + "loadingUnit": "1" + }, + "references": [ + { + "arguments": { + "const": { + "positional": { + "0": 0 + } + } + }, + "loadingUnit": "1", + "@": { + "uri": 0, + "line": 8, + "column": 19 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/empty_metadata.dart b/pkg/vm/testcases/transformations/record_use/tearoff.dart similarity index 55% rename from pkg/vm/testcases/transformations/resource_identifier/empty_metadata.dart rename to pkg/vm/testcases/transformations/record_use/tearoff.dart index f907cfd5b88a..5666bf3ce19c 100644 --- a/pkg/vm/testcases/transformations/resource_identifier/empty_metadata.dart +++ b/pkg/vm/testcases/transformations/record_use/tearoff.dart @@ -1,15 +1,17 @@ -// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:meta/meta.dart' show ResourceIdentifier; +import 'package:meta/meta.dart' show RecordUse; void main() { - print(SomeClass.someStaticMethod(42)); + print(m(SomeClass.someStaticMethod)(42)); } +Function m(Function f) => f; + class SomeClass { - @ResourceIdentifier() + @RecordUse() static someStaticMethod(int i) { return i + 1; } diff --git a/pkg/vm/testcases/transformations/record_use/tearoff.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/tearoff.dart.aot.expect new file mode 100644 index 000000000000..3cade3728821 --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/tearoff.dart.aot.expect @@ -0,0 +1,29 @@ +library #lib; +import self as self; +import "dart:core" as core; +import "package:meta/meta.dart" as meta; + +import "package:meta/meta.dart" show RecordUse; + +abstract class SomeClass extends core::Object { + + [@vm.inferred-return-type.metadata=int] + [@vm.unboxing-info.metadata=(b)->i] + @#C1 + static method someStaticMethod(core::int i) → dynamic { + return [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}; + } +} + +[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)] +static method main() → void { + core::print([@vm.direct-call.metadata=closure 0 in #lib::SomeClass.someStaticMethod] [@vm.inferred-type.metadata=int (receiver not int)] [@vm.inferred-type.metadata=dart.core::_Closure (value: #lib::SomeClass.someStaticMethod) (closure 0 in #lib::SomeClass.someStaticMethod)] self::m()(42)); +} + +[@vm.inferred-return-type.metadata=dart.core::_Closure (value: #lib::SomeClass.someStaticMethod) (closure 0 in #lib::SomeClass.someStaticMethod)] +static method m() → core::Function + return #C2; +constants { + #C1 = meta::RecordUse {} + #C2 = static-tearoff self::SomeClass::someStaticMethod +} diff --git a/pkg/vm/testcases/transformations/record_use/tearoff.dart.json.expect b/pkg/vm/testcases/transformations/record_use/tearoff.dart.json.expect new file mode 100644 index 000000000000..a07d7c228bb1 --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/tearoff.dart.json.expect @@ -0,0 +1,46 @@ +{ + "metadata": { + "comment": "Recorded usages of objects tagged with a `RecordUse` annotation", + "version": "0.1.0" + }, + "uris": [ + "tearoff.dart" + ], + "ids": [ + { + "uri": 0, + "parent": "SomeClass", + "name": "someStaticMethod" + } + ], + "constants": [], + "calls": [ + { + "definition": { + "id": 0, + "@": { + "line": 15, + "column": 10 + }, + "loadingUnit": "1" + }, + "references": [ + { + "arguments": { + "nonConst": { + "positional": [ + 0 + ] + } + }, + "loadingUnit": null, + "@": { + "uri": 0, + "line": 11, + "column": 27 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/record_use/top_level_method.dart b/pkg/vm/testcases/transformations/record_use/top_level_method.dart new file mode 100644 index 000000000000..edd00aa40fea --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/top_level_method.dart @@ -0,0 +1,14 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:meta/meta.dart' show RecordUse; + +void main() { + print(someTopLevelMethod(42)); +} + +@RecordUse() +int someTopLevelMethod(int i) { + return i + 1; +} diff --git a/pkg/vm/testcases/transformations/record_use/top_level_method.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/top_level_method.dart.aot.expect new file mode 100644 index 000000000000..fbb3cafcbf34 --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/top_level_method.dart.aot.expect @@ -0,0 +1,22 @@ +library #lib; +import self as self; +import "dart:core" as core; +import "package:meta/meta.dart" as meta; + +import "package:meta/meta.dart" show RecordUse; + + +[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)] +static method main() → void { + core::print([@vm.inferred-type.metadata=int] self::someTopLevelMethod(42)); +} + +[@vm.inferred-return-type.metadata=int] +[@vm.unboxing-info.metadata=(i)->i] +@#C1 +static method someTopLevelMethod([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 42)] core::int i) → core::int { + return [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}; +} +constants { + #C1 = meta::RecordUse {} +} diff --git a/pkg/vm/testcases/transformations/record_use/top_level_method.dart.json.expect b/pkg/vm/testcases/transformations/record_use/top_level_method.dart.json.expect new file mode 100644 index 000000000000..0f5883aa2c9c --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/top_level_method.dart.json.expect @@ -0,0 +1,50 @@ +{ + "metadata": { + "comment": "Recorded usages of objects tagged with a `RecordUse` annotation", + "version": "0.1.0" + }, + "uris": [ + "top_level_method.dart" + ], + "ids": [ + { + "uri": 0, + "name": "someTopLevelMethod" + } + ], + "constants": [ + { + "type": "int", + "value": 42 + } + ], + "calls": [ + { + "definition": { + "id": 0, + "@": { + "line": 12, + "column": 5 + }, + "loadingUnit": "1" + }, + "references": [ + { + "arguments": { + "const": { + "positional": { + "0": 0 + } + } + }, + "loadingUnit": "1", + "@": { + "uri": 0, + "line": 8, + "column": 9 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/record_use/types_of_arguments.dart b/pkg/vm/testcases/transformations/record_use/types_of_arguments.dart new file mode 100644 index 000000000000..7fbbc5a724ba --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/types_of_arguments.dart @@ -0,0 +1,23 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:meta/meta.dart' show RecordUse; + +void main() { + print(SomeClass.someStaticMethod(42)); + print(SomeClass.someStaticMethod(null)); + print(SomeClass.someStaticMethod('s')); + print(SomeClass.someStaticMethod(true)); + print(SomeClass.someStaticMethod(const { + 'a': ['a1', 'a2'], + 'b': ['b1', 'b2'], + })); + + print(SomeClass.someStaticMethod([true, false].first)); +} + +class SomeClass { + @RecordUse() + static String someStaticMethod(Object? a) => a.runtimeType.toString(); +} diff --git a/pkg/vm/testcases/transformations/record_use/types_of_arguments.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/types_of_arguments.dart.aot.expect new file mode 100644 index 000000000000..edba503871b3 --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/types_of_arguments.dart.aot.expect @@ -0,0 +1,36 @@ +library #lib; +import self as self; +import "dart:core" as core; +import "package:meta/meta.dart" as meta; + +import "package:meta/meta.dart" show RecordUse; + +abstract class SomeClass extends core::Object { + + [@vm.inferred-return-type.metadata=!] + @#C1 + static method someStaticMethod(core::Object? a) → core::String + return [@vm.direct-call.metadata=dart.core::_AbstractType.toString] [@vm.inferred-type.metadata=! (skip check)] [@vm.inferred-type.metadata=!] a.{core::Object::runtimeType}{}.{core::Type}.{core::Type::toString}(){() → core::String}; +} + +[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)] +static method main() → void { + core::print([@vm.inferred-type.metadata=!] self::SomeClass::someStaticMethod(42)); + core::print([@vm.inferred-type.metadata=!] self::SomeClass::someStaticMethod(null)); + core::print([@vm.inferred-type.metadata=!] self::SomeClass::someStaticMethod("s")); + core::print([@vm.inferred-type.metadata=!] self::SomeClass::someStaticMethod(true)); + core::print([@vm.inferred-type.metadata=!] self::SomeClass::someStaticMethod(#C10)); + core::print([@vm.inferred-type.metadata=!] self::SomeClass::someStaticMethod([@vm.direct-call.metadata=dart.core::_GrowableList.first] [@vm.inferred-type.metadata=dart.core::bool] [@vm.inferred-type.metadata=dart.core::_GrowableList] core::_GrowableList::_literal2(true, false).{core::Iterable::first}{core::bool})); +} +constants { + #C1 = meta::RecordUse {} + #C2 = "a" + #C3 = "a1" + #C4 = "a2" + #C5 = [#C3, #C4] + #C6 = "b" + #C7 = "b1" + #C8 = "b2" + #C9 = [#C7, #C8] + #C10 = >{#C2:#C5, #C6:#C9} +} diff --git a/pkg/vm/testcases/transformations/record_use/types_of_arguments.dart.json.expect b/pkg/vm/testcases/transformations/record_use/types_of_arguments.dart.json.expect new file mode 100644 index 000000000000..f7ee989219f4 --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/types_of_arguments.dart.json.expect @@ -0,0 +1,174 @@ +{ + "metadata": { + "comment": "Recorded usages of objects tagged with a `RecordUse` annotation", + "version": "0.1.0" + }, + "uris": [ + "types_of_arguments.dart" + ], + "ids": [ + { + "uri": 0, + "parent": "SomeClass", + "name": "someStaticMethod" + } + ], + "constants": [ + { + "type": "int", + "value": 42 + }, + { + "type": "Null" + }, + { + "type": "String", + "value": "s" + }, + { + "type": "bool", + "value": true + }, + { + "type": "String", + "value": "a1" + }, + { + "type": "String", + "value": "a2" + }, + { + "type": "list", + "value": [ + 4, + 5 + ] + }, + { + "type": "String", + "value": "b1" + }, + { + "type": "String", + "value": "b2" + }, + { + "type": "list", + "value": [ + 7, + 8 + ] + }, + { + "type": "map", + "value": { + "a": 6, + "b": 9 + } + } + ], + "calls": [ + { + "definition": { + "id": 0, + "@": { + "line": 22, + "column": 17 + }, + "loadingUnit": "1" + }, + "references": [ + { + "arguments": { + "const": { + "positional": { + "0": 0 + } + } + }, + "loadingUnit": "1", + "@": { + "uri": 0, + "line": 8, + "column": 19 + } + }, + { + "arguments": { + "const": { + "positional": { + "0": 1 + } + } + }, + "loadingUnit": "1", + "@": { + "uri": 0, + "line": 9, + "column": 19 + } + }, + { + "arguments": { + "const": { + "positional": { + "0": 2 + } + } + }, + "loadingUnit": "1", + "@": { + "uri": 0, + "line": 10, + "column": 19 + } + }, + { + "arguments": { + "const": { + "positional": { + "0": 3 + } + } + }, + "loadingUnit": "1", + "@": { + "uri": 0, + "line": 11, + "column": 19 + } + }, + { + "arguments": { + "const": { + "positional": { + "0": 10 + } + } + }, + "loadingUnit": "1", + "@": { + "uri": 0, + "line": 12, + "column": 19 + } + }, + { + "arguments": { + "nonConst": { + "positional": [ + 0 + ] + } + }, + "loadingUnit": "1", + "@": { + "uri": 0, + "line": 17, + "column": 19 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/complex.dart.json.expect b/pkg/vm/testcases/transformations/resource_identifier/complex.dart.json.expect deleted file mode 100644 index ac9237d0dc3d..000000000000 --- a/pkg/vm/testcases/transformations/resource_identifier/complex.dart.json.expect +++ /dev/null @@ -1,30 +0,0 @@ -{ - "_comment": "Resources referenced by annotated resource identifiers", - "AppTag": "TBD", - "environment": { - "dart.tool.dart2js": false - }, - "identifiers": [ - { - "name": "generate", - "id": "myresourceid", - "uri": "pkg/vm/testcases/transformations/resource_identifier/complex.dart", - "nonConstant": true, - "files": [ - { - "part": 1, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/complex.dart", - "line": 20, - "column": 18 - }, - "5": 42 - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart b/pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart deleted file mode 100644 index a2c0c0d40491..000000000000 --- a/pkg/vm/testcases/transformations/resource_identifier/complex_metadata.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:meta/meta.dart' show ResourceIdentifier; - -void main() { - print(SomeClass.stringMetadata(42)); - print(SomeClass.doubleMetadata(42)); - print(SomeClass.intMetadata(42)); - print(SomeClass.boolMetadata(42)); -} - -class SomeClass { - @ResourceIdentifier('leroyjenkins') - static stringMetadata(int i) { - return i + 1; - } - - @ResourceIdentifier(3.14) - static doubleMetadata(int i) { - return i + 1; - } - - @ResourceIdentifier(42) - static intMetadata(int i) { - return i + 1; - } - - @ResourceIdentifier(true) - static boolMetadata(int i) { - return i + 1; - } -} diff --git a/pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart.aot.expect b/pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart.aot.expect deleted file mode 100644 index 478ed3d28ef1..000000000000 --- a/pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart.aot.expect +++ /dev/null @@ -1,55 +0,0 @@ -library #lib; -import self as self; -import "dart:core" as core; -import "package:meta/meta.dart" as meta; - -import "package:meta/meta.dart" show ResourceIdentifier; - -abstract class SomeClass extends core::Object { - - [@vm.inferred-return-type.metadata=int] - [@vm.unboxing-info.metadata=(i)->i] - @#C2 - static method stringMetadata([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 42)] core::int i) → dynamic { - return [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}; - } - - [@vm.inferred-return-type.metadata=int] - [@vm.unboxing-info.metadata=(i)->i] - @#C4 - static method doubleMetadata([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 42)] core::int i) → dynamic { - return [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}; - } - - [@vm.inferred-return-type.metadata=int] - [@vm.unboxing-info.metadata=(i)->i] - @#C6 - static method intMetadata([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 42)] core::int i) → dynamic { - return [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}; - } - - [@vm.inferred-return-type.metadata=int] - [@vm.unboxing-info.metadata=(i)->i] - @#C8 - static method boolMetadata([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 42)] core::int i) → dynamic { - return [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}; - } -} - -[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)] -static method main() → void { - core::print([@vm.inferred-type.metadata=int] self::SomeClass::stringMetadata(42)); - core::print([@vm.inferred-type.metadata=int] self::SomeClass::doubleMetadata(42)); - core::print([@vm.inferred-type.metadata=int] self::SomeClass::intMetadata(42)); - core::print([@vm.inferred-type.metadata=int] self::SomeClass::boolMetadata(42)); -} -constants { - #C1 = "leroyjenkins" - #C2 = meta::ResourceIdentifier {metadata:#C1} - #C3 = 3.14 - #C4 = meta::ResourceIdentifier {metadata:#C3} - #C5 = 42 - #C6 = meta::ResourceIdentifier {metadata:#C5} - #C7 = true - #C8 = meta::ResourceIdentifier {metadata:#C7} -} diff --git a/pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart.json.expect b/pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart.json.expect deleted file mode 100644 index 79bc85103697..000000000000 --- a/pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart.json.expect +++ /dev/null @@ -1,93 +0,0 @@ -{ - "_comment": "Resources referenced by annotated resource identifiers", - "AppTag": "TBD", - "environment": { - "dart.tool.dart2js": false - }, - "identifiers": [ - { - "name": "stringMetadata", - "id": "leroyjenkins", - "uri": "pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart", - "nonConstant": true, - "files": [ - { - "part": 1, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart", - "line": 8, - "column": 19 - }, - "1": 42 - } - ] - } - ] - }, - { - "name": "doubleMetadata", - "id": "3.14", - "uri": "pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart", - "nonConstant": true, - "files": [ - { - "part": 1, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart", - "line": 9, - "column": 19 - }, - "1": 42 - } - ] - } - ] - }, - { - "name": "intMetadata", - "id": "42", - "uri": "pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart", - "nonConstant": true, - "files": [ - { - "part": 1, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart", - "line": 10, - "column": 19 - }, - "1": 42 - } - ] - } - ] - }, - { - "name": "boolMetadata", - "id": "true", - "uri": "pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart", - "nonConstant": true, - "files": [ - { - "part": 1, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/complex_metadata.dart", - "line": 11, - "column": 19 - }, - "1": 42 - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/different.dart.json.expect b/pkg/vm/testcases/transformations/resource_identifier/different.dart.json.expect deleted file mode 100644 index 59e1a5f76075..000000000000 --- a/pkg/vm/testcases/transformations/resource_identifier/different.dart.json.expect +++ /dev/null @@ -1,8 +0,0 @@ -{ - "_comment": "Resources referenced by annotated resource identifiers", - "AppTag": "TBD", - "environment": { - "dart.tool.dart2js": false - }, - "identifiers": [] -} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/empty_metadata.dart.aot.expect b/pkg/vm/testcases/transformations/resource_identifier/empty_metadata.dart.aot.expect deleted file mode 100644 index 12a183a54729..000000000000 --- a/pkg/vm/testcases/transformations/resource_identifier/empty_metadata.dart.aot.expect +++ /dev/null @@ -1,25 +0,0 @@ -library #lib; -import self as self; -import "dart:core" as core; -import "package:meta/meta.dart" as meta; - -import "package:meta/meta.dart" show ResourceIdentifier; - -abstract class SomeClass extends core::Object { - - [@vm.inferred-return-type.metadata=int] - [@vm.unboxing-info.metadata=(i)->i] - @#C2 - static method someStaticMethod([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 42)] core::int i) → dynamic { - return [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}; - } -} - -[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)] -static method main() → void { - core::print([@vm.inferred-type.metadata=int] self::SomeClass::someStaticMethod(42)); -} -constants { - #C1 = null - #C2 = meta::ResourceIdentifier {metadata:#C1} -} diff --git a/pkg/vm/testcases/transformations/resource_identifier/empty_metadata.dart.json.expect b/pkg/vm/testcases/transformations/resource_identifier/empty_metadata.dart.json.expect deleted file mode 100644 index 8a02ea2479cf..000000000000 --- a/pkg/vm/testcases/transformations/resource_identifier/empty_metadata.dart.json.expect +++ /dev/null @@ -1,30 +0,0 @@ -{ - "_comment": "Resources referenced by annotated resource identifiers", - "AppTag": "TBD", - "environment": { - "dart.tool.dart2js": false - }, - "identifiers": [ - { - "name": "someStaticMethod", - "id": "", - "uri": "pkg/vm/testcases/transformations/resource_identifier/empty_metadata.dart", - "nonConstant": true, - "files": [ - { - "part": 1, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/empty_metadata.dart", - "line": 8, - "column": 19 - }, - "1": 42 - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/extension.dart.json.expect b/pkg/vm/testcases/transformations/resource_identifier/extension.dart.json.expect deleted file mode 100644 index 2a341d9e87b2..000000000000 --- a/pkg/vm/testcases/transformations/resource_identifier/extension.dart.json.expect +++ /dev/null @@ -1,30 +0,0 @@ -{ - "_comment": "Resources referenced by annotated resource identifiers", - "AppTag": "TBD", - "environment": { - "dart.tool.dart2js": false - }, - "identifiers": [ - { - "name": "_extension#0|callWithArgs", - "id": "id", - "uri": "pkg/vm/testcases/transformations/resource_identifier/extension.dart", - "nonConstant": true, - "files": [ - { - "part": 1, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/extension.dart", - "line": 8, - "column": 15 - }, - "1": "42" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple.dart.json.expect b/pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple.dart.json.expect deleted file mode 100644 index ca67f387f8aa..000000000000 --- a/pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple.dart.json.expect +++ /dev/null @@ -1,77 +0,0 @@ -{ - "_comment": "Resources referenced by annotated resource identifiers", - "AppTag": "TBD", - "environment": { - "dart.tool.dart2js": false - }, - "identifiers": [ - { - "name": "someStaticMethod", - "id": "id", - "uri": "pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple_helper_shared.dart", - "nonConstant": true, - "files": [ - { - "part": 1, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple.dart", - "line": 12, - "column": 13 - }, - "1": 42 - } - ] - }, - { - "part": 2, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple_helper.dart", - "line": 8, - "column": 13 - }, - "1": 42 - } - ] - } - ] - }, - { - "name": "someStaticMethod", - "id": "id", - "uri": "pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple_helper_shared.dart", - "nonConstant": true, - "files": [ - { - "part": 1, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple.dart", - "line": 12, - "column": 13 - }, - "1": 42 - } - ] - }, - { - "part": 2, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/loading_units_multiple_helper.dart", - "line": 8, - "column": 13 - }, - "1": 42 - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/loading_units_simple.dart.json.expect b/pkg/vm/testcases/transformations/resource_identifier/loading_units_simple.dart.json.expect deleted file mode 100644 index 3a73a8c56d62..000000000000 --- a/pkg/vm/testcases/transformations/resource_identifier/loading_units_simple.dart.json.expect +++ /dev/null @@ -1,51 +0,0 @@ -{ - "_comment": "Resources referenced by annotated resource identifiers", - "AppTag": "TBD", - "environment": { - "dart.tool.dart2js": false - }, - "identifiers": [ - { - "name": "someStaticMethod", - "id": "id", - "uri": "pkg/vm/testcases/transformations/resource_identifier/loading_units_simple.dart", - "nonConstant": true, - "files": [ - { - "part": 1, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/loading_units_simple.dart", - "line": 14, - "column": 13 - }, - "1": 42 - } - ] - } - ] - }, - { - "name": "someStaticMethod", - "id": "id", - "uri": "pkg/vm/testcases/transformations/resource_identifier/loading_units_simple_helper.dart", - "nonConstant": true, - "files": [ - { - "part": 2, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/loading_units_simple_helper.dart", - "line": 8, - "column": 13 - }, - "1": 42 - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/pkg/vm/testcases/transformations/resource_identifier/simple.dart.json.expect b/pkg/vm/testcases/transformations/resource_identifier/simple.dart.json.expect deleted file mode 100644 index 9d251f6ac5fb..000000000000 --- a/pkg/vm/testcases/transformations/resource_identifier/simple.dart.json.expect +++ /dev/null @@ -1,30 +0,0 @@ -{ - "_comment": "Resources referenced by annotated resource identifiers", - "AppTag": "TBD", - "environment": { - "dart.tool.dart2js": false - }, - "identifiers": [ - { - "name": "someStaticMethod", - "id": "id", - "uri": "pkg/vm/testcases/transformations/resource_identifier/simple.dart", - "nonConstant": true, - "files": [ - { - "part": 1, - "references": [ - { - "@": { - "uri": "pkg/vm/testcases/transformations/resource_identifier/simple.dart", - "line": 8, - "column": 19 - }, - "1": 42 - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/tools/experimental_features.yaml b/tools/experimental_features.yaml index e2d3f95fee81..231bb1585b5a 100644 --- a/tools/experimental_features.yaml +++ b/tools/experimental_features.yaml @@ -125,7 +125,7 @@ features: native-assets: help: "Compile and bundle native assets." - resource-identifiers: + record-use: help: "Output arguments used by static functions." wildcard-variables: diff --git a/tools/package_deps/bin/package_deps.dart b/tools/package_deps/bin/package_deps.dart index 6128f72eeb4d..a2815be5d7cf 100644 --- a/tools/package_deps/bin/package_deps.dart +++ b/tools/package_deps/bin/package_deps.dart @@ -346,7 +346,7 @@ class Package implements Comparable { 'pkg/front_end/testcases/', 'pkg/linter/test/rules/', 'pkg/linter/test_data/', - 'pkg/native_assets_builder/test/test_projects/', + 'pkg/record_use/test_data/', 'pkg/vm/testcases/', }; if (excludedPaths.contains(uriPath)) {