mirror of
https://github.com/dart-lang/sdk
synced 2024-09-30 06:21:48 +00:00
Use package:record_use
- Rename resource identifiers in the VM to usage recordings. - Use package:record_use for serialization. - Rename and use the experimental flag for this feature. - Recognize tear-offs and top-level methods as well. Next steps: - Add constant instance recording. - Expose API in package:native_assets_cli's link callback. TEST=pkg/vm/test/transformations/record_use_test.dart Change-Id: I8af3625165f78925ae943711245af93a239d1012 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/383040 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Moritz Sümmermann <mosum@google.com>
This commit is contained in:
parent
120dac365d
commit
81daf8e563
|
@ -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,
|
||||
|
|
|
@ -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<Null> 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<Null> 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<Null> 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<Null> 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<Null> codeRestPatternInMapPattern = messageRestPatternInMapPattern;
|
||||
|
||||
|
|
|
@ -41,8 +41,8 @@ final _knownFeatures = <String, ExperimentalFeature>{
|
|||
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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -103,8 +103,8 @@ Future<ProcessResult> 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<ProcessResult> generateKernelHelper({
|
||||
required String dartaotruntime,
|
||||
String? sourceFile,
|
||||
|
@ -115,7 +115,7 @@ Future<ProcessResult> generateKernelHelper({
|
|||
String? targetOS,
|
||||
List<String> extraGenKernelOptions = const [],
|
||||
String? nativeAssets,
|
||||
String? resourcesFile,
|
||||
String? recordedUsagesFile,
|
||||
String? depFile,
|
||||
bool enableAsserts = false,
|
||||
bool fromDill = false,
|
||||
|
@ -138,7 +138,8 @@ Future<ProcessResult> 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,
|
||||
|
|
|
@ -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<SnapshotGenerator> generate({
|
||||
String? resourcesFile,
|
||||
String? recordedUsagesFile,
|
||||
List<String>? extraOptions,
|
||||
}) =>
|
||||
_generator.generateKernel(
|
||||
resourcesFile: resourcesFile,
|
||||
recordedUsagesFile: recordedUsagesFile,
|
||||
extraOptions: extraOptions,
|
||||
);
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ class _Generator {
|
|||
}
|
||||
|
||||
Future<SnapshotGenerator> generateKernel({
|
||||
String? resourcesFile,
|
||||
String? recordedUsagesFile,
|
||||
List<String>? 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<void> generateKernel({
|
||||
required String sourceFile,
|
||||
required String outputFile,
|
||||
|
@ -382,7 +382,7 @@ Future<void> generateKernel({
|
|||
bool product = true,
|
||||
bool verbose = false,
|
||||
String? nativeAssets,
|
||||
String? resourcesFile,
|
||||
String? recordedUsagesFile,
|
||||
String? depFile,
|
||||
List<String>? extraOptions,
|
||||
}) async {
|
||||
|
@ -407,7 +407,7 @@ Future<void> generateKernel({
|
|||
...?extraOptions,
|
||||
],
|
||||
nativeAssets: nativeAssets,
|
||||
resourcesFile: resourcesFile,
|
||||
recordedUsagesFile: recordedUsagesFile,
|
||||
product: product,
|
||||
);
|
||||
await _forwardOutput(kernelResult);
|
||||
|
|
|
@ -99,7 +99,11 @@ class DartdevRunner extends CommandRunner<int> {
|
|||
final nativeAssetsExperimentEnabled =
|
||||
nativeAssetsEnabled(vmEnabledExperiments);
|
||||
if (nativeAssetsExperimentEnabled) {
|
||||
addCommand(BuildCommand(verbose: verbose));
|
||||
final recordUseExperimentEnabled = recordUseEnabled(vmEnabledExperiments);
|
||||
addCommand(BuildCommand(
|
||||
verbose: verbose,
|
||||
recordUseEnabled: recordUseExperimentEnabled,
|
||||
));
|
||||
}
|
||||
addCommand(CompileCommand(
|
||||
verbose: verbose,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -98,3 +98,6 @@ List<String> parseVmEnabledExperiments(List<String> vmArgs) {
|
|||
bool nativeAssetsEnabled(List<String> vmEnabledExperiments) =>
|
||||
vmEnabledExperiments
|
||||
.contains(ExperimentalFeatures.native_assets.enableString);
|
||||
|
||||
bool recordUseEnabled(List<String> vmEnabledExperiments) =>
|
||||
vmEnabledExperiments.contains(ExperimentalFeatures.record_use.enableString);
|
||||
|
|
|
@ -233,6 +233,78 @@ void main(List<String> 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<void> _withTempDir(Future<void> Function(Uri tempUri) fun) async {
|
||||
|
|
|
@ -62,11 +62,10 @@ Future<run_process.RunProcessResult> runProcess({
|
|||
throwOnUnexpectedExitCode: throwOnUnexpectedExitCode,
|
||||
);
|
||||
|
||||
Future<void> copyTestProjects(Uri copyTargetUri, Logger logger) async {
|
||||
final pkgNativeAssetsBuilderUri = Platform.script.resolve(
|
||||
'../../../../third_party/pkg/native/pkgs/native_assets_builder/');
|
||||
Future<void> 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<void> 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<void> nativeAssetsTest(
|
|||
String packageUnderTest,
|
||||
Future<void> 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<void> recordUseTest(
|
||||
String packageUnderTest,
|
||||
Future<void> Function(Uri) fun, {
|
||||
bool skipPubGet = false,
|
||||
}) async =>
|
||||
await runPackageTest(
|
||||
packageUnderTest,
|
||||
skipPubGet,
|
||||
fun,
|
||||
const ['drop_dylib_recording'],
|
||||
Platform.script.resolve('../../../record_use/'),
|
||||
);
|
||||
|
||||
Future<void> runPackageTest(
|
||||
String packageUnderTest,
|
||||
bool skipPubGet,
|
||||
Future<void> Function(Uri) fun,
|
||||
List<String> 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);
|
||||
|
|
|
@ -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<ExperimentalFlag, bool> 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<shared.ExperimentalFlag, ExperimentalFlag> 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,
|
||||
|
|
|
@ -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';
|
|
@ -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<InstanceConstant> 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<InstanceConstant> findResourceAnnotations(Annotatable node) =>
|
||||
Iterable<InstanceConstant> findRecordUseAnnotation(Annotatable node) =>
|
||||
node.annotations
|
||||
.whereType<ConstantExpression>()
|
||||
.map((expression) => expression.constant)
|
||||
.whereType<InstanceConstant>()
|
||||
.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<InstanceConstant> 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));
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -684,7 +684,7 @@ const Map<String, ({int hitCount, int missCount})> _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,
|
||||
),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -120,6 +120,7 @@ preexisting
|
|||
pubspec.yaml
|
||||
r
|
||||
re
|
||||
RecordUse
|
||||
refutable
|
||||
resource
|
||||
sdksummary
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
## 0.2.0
|
||||
|
||||
- Use maps instead of lists in serialization.
|
||||
|
||||
## 0.1.1
|
||||
|
||||
- Fix repository link.
|
||||
|
|
4
pkg/record_use/analysis_options.yaml
Normal file
4
pkg/record_use/analysis_options.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
include: package:dart_flutter_team_lints/analysis_options.yaml
|
||||
|
||||
analyzer:
|
||||
exclude: [test_data/**]
|
|
@ -35,11 +35,11 @@ class Definition {
|
|||
}
|
||||
|
||||
Map<String, dynamic> toJson(
|
||||
List<Identifier> identifiers,
|
||||
List<String> uris,
|
||||
Map<Identifier, int> identifiers,
|
||||
Map<String, int> uris,
|
||||
) =>
|
||||
{
|
||||
'id': identifiers.indexOf(identifier),
|
||||
'id': identifiers[identifier]!,
|
||||
'@': location.toJson(),
|
||||
'loadingUnit': loadingUnit,
|
||||
};
|
||||
|
|
|
@ -35,9 +35,9 @@ class Usage<T extends Reference> {
|
|||
);
|
||||
|
||||
Map<String, dynamic> toJson(
|
||||
List<Identifier> identifiers,
|
||||
List<String> uris,
|
||||
List<Constant> constants,
|
||||
Map<Identifier, int> identifiers,
|
||||
Map<String, int> uris,
|
||||
Map<Constant, int> constants,
|
||||
) =>
|
||||
{
|
||||
'definition': definition.toJson(identifiers, uris),
|
||||
|
|
|
@ -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<String, dynamic> json) {
|
||||
final uris = json['uris'] as List<String>;
|
||||
final uris = (json['uris'] as List).cast<String>();
|
||||
|
||||
final identifiers = (json['ids'] as List)
|
||||
.whereType<Map<String, dynamic>>()
|
||||
|
@ -66,9 +68,10 @@ class UsageRecord {
|
|||
final identifiers = <Identifier>{
|
||||
...calls.map((call) => call.definition.identifier),
|
||||
...instances.map((instance) => instance.definition.identifier),
|
||||
}.toList();
|
||||
}.asMapToIndices;
|
||||
|
||||
final uris = <String>{
|
||||
...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<Constant> {
|
|||
return constants;
|
||||
}
|
||||
}
|
||||
|
||||
extension _PrivateIterableExtension<T> on Iterable<T> {
|
||||
Map<T, int> get asMapToIndices => Map.fromEntries(
|
||||
mapIndexed((index, uri) => MapEntry(uri, index)),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class Arguments {
|
|||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson(List<Constant> constants) {
|
||||
Map<String, dynamic> toJson(Map<Constant, int> 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<String, dynamic>).map((key, value) =>
|
||||
MapEntry(int.parse(key), constants[value as int]))
|
||||
? (json['positional'] as Map<String, dynamic>).map((position,
|
||||
constantIndex) =>
|
||||
MapEntry(int.parse(position), constants[constantIndex as int]))
|
||||
: {},
|
||||
named: json['named'] != null
|
||||
? (json['named'] as Map<String, dynamic>)
|
||||
.map((key, value) => MapEntry(key, constants[value as int]))
|
||||
? (json['named'] as Map<String, dynamic>).map(
|
||||
(name, constantIndex) =>
|
||||
MapEntry(name, constants[constantIndex as int]))
|
||||
: {},
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson(List<Constant> constants) => {
|
||||
Map<String, dynamic> toJson(Map<Constant, int> 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<String, dynamic> json) =>
|
||||
NonConstArguments(
|
||||
positional:
|
||||
json['positional'] != null ? json['positional'] as List<int> : [],
|
||||
named: json['named'] != null ? json['named'] as List<String> : [],
|
||||
positional: json['positional'] != null
|
||||
? (json['positional'] as List).cast()
|
||||
: <int>[],
|
||||
named: json['named'] != null ? (json['named'] as List).cast() : [],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
|
|
|
@ -7,7 +7,7 @@ import '../helper.dart';
|
|||
sealed class Constant {
|
||||
const Constant();
|
||||
|
||||
Map<String, dynamic> toJson(List<Constant> constants);
|
||||
Map<String, dynamic> toJson(Map<Constant, int> constants);
|
||||
|
||||
static Constant fromJson(
|
||||
Map<String, dynamic> value, List<Constant> 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<dynamic>)
|
||||
.map((e) => constants[e as int])
|
||||
.map((value) => value as int)
|
||||
.map((value) => constants[value])
|
||||
.toList()),
|
||||
MapConstant._type => MapConstant(
|
||||
(value['value'] as Map<String, dynamic>)
|
||||
|
@ -33,7 +34,8 @@ final class NullConstant extends Constant {
|
|||
const NullConstant() : super();
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson(List<Constant> constants) => _toJson(_type, null);
|
||||
Map<String, dynamic> toJson(Map<Constant, int> constants) =>
|
||||
_toJson(_type, null);
|
||||
}
|
||||
|
||||
sealed class PrimitiveConstant<T extends Object> extends Constant {
|
||||
|
@ -52,7 +54,7 @@ sealed class PrimitiveConstant<T extends Object> extends Constant {
|
|||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson(List<Constant> constants) => valueToJson();
|
||||
Map<String, dynamic> toJson(Map<Constant, int> constants) => valueToJson();
|
||||
|
||||
Map<String, dynamic> valueToJson();
|
||||
}
|
||||
|
@ -102,9 +104,9 @@ final class ListConstant<T extends Constant> extends Constant {
|
|||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson(List<Constant> constants) => _toJson(
|
||||
Map<String, dynamic> toJson(Map<Constant, int> constants) => _toJson(
|
||||
_type,
|
||||
value.map((constant) => constants.indexOf(constant)).toList(),
|
||||
value.map((constant) => constants[constant]).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -126,10 +128,9 @@ final class MapConstant<T extends Constant> extends Constant {
|
|||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson(List<Constant> constants) => _toJson(
|
||||
Map<String, dynamic> toJson(Map<Constant, int> constants) => _toJson(
|
||||
_type,
|
||||
value
|
||||
.map((key, constant) => MapEntry(key, constants.indexOf(constant))),
|
||||
value.map((key, constant) => MapEntry(key, constants[constant]!)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ class Identifier {
|
|||
name: json['name'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson(List<String> uris) => {
|
||||
'uri': uris.indexOf(uri),
|
||||
Map<String, dynamic> toJson(Map<String, int> uris) => {
|
||||
'uri': uris[uri]!,
|
||||
if (parent != null) 'parent': parent,
|
||||
'name': name,
|
||||
};
|
||||
|
|
|
@ -18,15 +18,15 @@ final class InstanceConstant {
|
|||
) {
|
||||
return InstanceConstant(
|
||||
fields: (json['fields'] as Map<String, dynamic>).map(
|
||||
(key, value) => MapEntry(key, constants[value as int]),
|
||||
(key, constantIndex) => MapEntry(key, constants[constantIndex as int]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson(List<Constant> constants) => {
|
||||
Map<String, dynamic> toJson(Map<Constant, int> constants) => {
|
||||
if (fields.isNotEmpty)
|
||||
'fields': fields
|
||||
.map((key, value) => MapEntry(key, constants.indexOf(value))),
|
||||
'fields': fields.map((name, constantIndex) =>
|
||||
MapEntry(name, constants[constantIndex]!)),
|
||||
};
|
||||
|
||||
@override
|
||||
|
|
|
@ -22,9 +22,9 @@ class Location {
|
|||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson({List<String>? uris}) {
|
||||
Map<String, dynamic> toJson({Map<String, int>? uris}) {
|
||||
return {
|
||||
if (uris != null) 'uri': uris.indexOf(uri),
|
||||
if (uris != null) 'uri': uris[uri]!,
|
||||
'line': line,
|
||||
'column': column,
|
||||
};
|
||||
|
|
|
@ -15,7 +15,11 @@ sealed class Reference {
|
|||
|
||||
const Reference({this.loadingUnit, required this.location});
|
||||
|
||||
Map<String, dynamic> toJson(List<String> uris, List<Constant> constants) => {
|
||||
Map<String, dynamic> toJson(
|
||||
Map<String, int> uris,
|
||||
Map<Constant, int> constants,
|
||||
) =>
|
||||
{
|
||||
'loadingUnit': loadingUnit,
|
||||
'@': location.toJson(uris: uris),
|
||||
};
|
||||
|
@ -59,7 +63,10 @@ final class CallReference extends Reference {
|
|||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson(List<String> uris, List<Constant> constants) {
|
||||
Map<String, dynamic> toJson(
|
||||
Map<String, int> uris,
|
||||
Map<Constant, int> constants,
|
||||
) {
|
||||
final argumentJson = arguments?.toJson(constants) ?? {};
|
||||
return {
|
||||
if (argumentJson.isNotEmpty) 'arguments': argumentJson,
|
||||
|
@ -108,7 +115,11 @@ final class InstanceReference extends Reference {
|
|||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson(List<String> uris, List<Constant> constants) => {
|
||||
Map<String, dynamic> toJson(
|
||||
Map<String, int> uris,
|
||||
Map<Constant, int> constants,
|
||||
) =>
|
||||
{
|
||||
'instanceConstant': instanceConstant.toJson(constants),
|
||||
...super.toJson(uris, constants),
|
||||
};
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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<String, dynamic>;
|
||||
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', () {
|
||||
|
|
|
@ -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': [
|
||||
"instances": [
|
||||
{
|
||||
'definition': {
|
||||
'id': 1,
|
||||
'@': {'line': 15, 'column': 30},
|
||||
'loadingUnit': null
|
||||
"definition": {
|
||||
"id": 1,
|
||||
"@": {"line": 15, "column": 30},
|
||||
"loadingUnit": null
|
||||
},
|
||||
'references': [
|
||||
"references": [
|
||||
{
|
||||
'instanceConstant': {
|
||||
'fields': {'a': 13, 'b': 14}
|
||||
"instanceConstant": {
|
||||
"fields": {"a": 13, "b": 14}
|
||||
},
|
||||
'loadingUnit': '3',
|
||||
'@': {'uri': 0, 'line': 40, 'column': 30}
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"type": "String",
|
||||
"value": "add"
|
||||
}
|
||||
],
|
||||
"calls": [
|
||||
{
|
||||
"definition": {
|
||||
"id": 0,
|
||||
"@": {
|
||||
"line": 10,
|
||||
"column": 6
|
||||
},
|
||||
"loadingUnit": "1"
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"arguments": {
|
||||
"const": {
|
||||
"positional": {
|
||||
"0": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"loadingUnit": "1",
|
||||
"@": {
|
||||
"uri": 1,
|
||||
"line": 8,
|
||||
"column": 3
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}''';
|
||||
|
|
|
@ -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<String, dynamic>)
|
||||
.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<String, dynamic>)
|
||||
.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<String, dynamic>)
|
||||
.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<String, dynamic>)
|
||||
.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<String, dynamic>)
|
||||
.hasNonConstArguments(id),
|
||||
false);
|
||||
});
|
||||
}
|
||||
|
|
1
pkg/record_use/test_data/drop_dylib_recording/.gitignore
vendored
Normal file
1
pkg/record_use/test_data/drop_dylib_recording/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
bin/drop_dylib_link/
|
25
pkg/record_use/test_data/drop_dylib_recording/README.md
Normal file
25
pkg/record_use/test_data/drop_dylib_recording/README.md
Normal file
|
@ -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!`
|
|
@ -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<String> arguments) {
|
||||
getMathMethod(arguments.first);
|
||||
}
|
|
@ -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<String> arguments) {
|
||||
getMathMethod('add');
|
||||
}
|
|
@ -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<String> 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,
|
||||
);
|
||||
});
|
||||
}
|
64
pkg/record_use/test_data/drop_dylib_recording/hook/link.dart
Normal file
64
pkg/record_use/test_data/drop_dylib_recording/hook/link.dart
Normal file
|
@ -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<String> 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<String, dynamic>);
|
||||
|
||||
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'));
|
||||
});
|
||||
}
|
|
@ -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';
|
|
@ -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);
|
||||
}
|
|
@ -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<ffi.Int32 Function(ffi.Int32, ffi.Int32)>(
|
||||
assetId: 'package:drop_dylib_recording/dylib_add')
|
||||
external int add(
|
||||
int a,
|
||||
int b,
|
||||
);
|
||||
|
||||
@ffi.Native<ffi.Int32 Function(ffi.Int32, ffi.Int32)>(
|
||||
assetId: 'package:drop_dylib_recording/dylib_multiply')
|
||||
external int multiply(
|
||||
int a,
|
||||
int b,
|
||||
);
|
27
pkg/record_use/test_data/drop_dylib_recording/pubspec.yaml
Normal file
27
pkg/record_use/test_data/drop_dylib_recording/pubspec.yaml
Normal file
|
@ -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/
|
|
@ -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;
|
||||
}
|
|
@ -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 <stdint.h>
|
||||
|
||||
#if _WIN32
|
||||
#define MYLIB_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define MYLIB_EXPORT
|
||||
#endif
|
||||
|
||||
MYLIB_EXPORT int32_t add(int32_t a, int32_t b);
|
|
@ -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;
|
||||
}
|
|
@ -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 <stdint.h>
|
||||
|
||||
#if _WIN32
|
||||
#define MYLIB_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define MYLIB_EXPORT
|
||||
#endif
|
||||
|
||||
MYLIB_EXPORT intptr_t multiply(intptr_t a, intptr_t b);
|
26
pkg/record_use/test_data/manifest.yaml
Normal file
26
pkg/record_use/test_data/manifest.yaml
Normal file
|
@ -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
|
|
@ -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: <String>['vm', 'flutter', 'flutter_runner', 'dart_runner'],
|
||||
|
@ -205,7 +205,7 @@ Future<int> 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<int> 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<int> 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<Uri> additionalSources;
|
||||
final Uri? nativeAssets;
|
||||
final Uri? resourcesFile;
|
||||
final Uri? recordedUsages;
|
||||
final bool requireMain;
|
||||
final bool includePlatform;
|
||||
final List<String> deleteToStringPackageUris;
|
||||
|
@ -471,7 +471,7 @@ class KernelCompilationArguments {
|
|||
this.options,
|
||||
this.additionalSources = const <Uri>[],
|
||||
this.nativeAssets,
|
||||
this.resourcesFile,
|
||||
this.recordedUsages,
|
||||
this.requireMain = true,
|
||||
this.includePlatform = false,
|
||||
this.deleteToStringPackageUris = const <String>[],
|
||||
|
@ -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!);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
148
pkg/vm/lib/transformations/record_use/record_call.dart
Normal file
148
pkg/vm/lib/transformations/record_use/record_call.dart
Normal file
|
@ -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<ast.Procedure, Usage<CallReference>> callsForMethod = {};
|
||||
final List<LoadingUnit> _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<bool, List<MapEntry<T, Constant?>>> _groupByNull<T>(
|
||||
Iterable<MapEntry<T, Constant?>> 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(),
|
||||
);
|
||||
}
|
||||
}
|
164
pkg/vm/lib/transformations/record_use/record_use.dart
Normal file
164
pkg/vm/lib/transformations/record_use/record_use.dart
Normal file
|
@ -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<Usage<CallReference>> calls,
|
||||
Iterable<Usage<InstanceReference>> 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<LoadingUnit> loadingUnits,
|
||||
) {
|
||||
final importUri = library.importUri.toString();
|
||||
return loadingUnits
|
||||
.firstWhereOrNull((unit) => unit.libraryUris.contains(importUri))
|
||||
?.id ??
|
||||
-1;
|
||||
}
|
||||
|
||||
int loadingUnitForNode(ast.TreeNode node, List<LoadingUnit> loadingUnits) {
|
||||
final library = enclosingLibrary(node)!;
|
||||
return _loadingUnitForLibrary(library, loadingUnits);
|
||||
}
|
|
@ -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<Identifier> 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<Identifier> identifiers = [];
|
||||
final List<LoadingUnit> _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<ResourceFile> files;
|
||||
|
||||
Identifier({
|
||||
required this.name,
|
||||
required this.id,
|
||||
required this.uri,
|
||||
required this.nonConstant,
|
||||
required this.files,
|
||||
});
|
||||
|
||||
Map<String, dynamic> 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<ResourceReference> references;
|
||||
|
||||
ResourceFile({required this.part, required this.references});
|
||||
|
||||
Map<String, dynamic> 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<String, Object?> arguments;
|
||||
|
||||
ResourceReference({
|
||||
required this.uri,
|
||||
required this.line,
|
||||
required this.column,
|
||||
required this.arguments,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'@': {
|
||||
'uri': uri,
|
||||
'line': line,
|
||||
'column': column,
|
||||
},
|
||||
...arguments,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ResourceReference(uri: $uri, line: $line, column: $column, arguments: $arguments)';
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<String> 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)
|
|
@ -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<String> generate(AssetBundle bundle, List args, String string,
|
||||
AnotherClass object, int index) async {
|
||||
final message = await bundle.byIndex(string: string, index: index);
|
|
@ -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<dynamic>] core::List<dynamic> 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<core::String> 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<self::Message> */ ;
|
||||
return [@vm.direct-call.metadata=#lib::Message.generateString] [@vm.inferred-type.metadata=!? (skip check)] message.{self::Message::generateString}(args){(core::List<dynamic>, {required object: self::AnotherClass}) → asy::Future<core::String>};
|
||||
|
@ -73,6 +73,5 @@ static method main() → void {
|
|||
}
|
||||
constants {
|
||||
#C1 = "argument!"
|
||||
#C2 = "myresourceid"
|
||||
#C3 = meta::ResourceIdentifier {metadata:#C2}
|
||||
#C2 = meta::RecordUse {}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"metadata": {
|
||||
"comment": "Recorded usages of objects tagged with a `RecordUse` annotation",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"uris": [],
|
||||
"ids": [],
|
||||
"constants": []
|
||||
}
|
|
@ -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";
|
||||
}
|
|
@ -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 {}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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]
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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) {}
|
||||
}
|
|
@ -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) {}
|
||||
}
|
|
@ -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 {}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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) {}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 {}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 {}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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}{<object>}.{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<dart.core::bool>] core::_GrowableList::_literal2<core::bool>(true, false).{core::Iterable::first}{core::bool}));
|
||||
}
|
||||
constants {
|
||||
#C1 = meta::RecordUse {}
|
||||
#C2 = "a"
|
||||
#C3 = "a1"
|
||||
#C4 = "a2"
|
||||
#C5 = <core::String>[#C3, #C4]
|
||||
#C6 = "b"
|
||||
#C7 = "b1"
|
||||
#C8 = "b2"
|
||||
#C9 = <core::String>[#C7, #C8]
|
||||
#C10 = <core::String, core::List<core::String>>{#C2:#C5, #C6:#C9}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"_comment": "Resources referenced by annotated resource identifiers",
|
||||
"AppTag": "TBD",
|
||||
"environment": {
|
||||
"dart.tool.dart2js": false
|
||||
},
|
||||
"identifiers": []
|
||||
}
|
|
@ -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}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue