[record_use] Only record definitions with a package: uri
Record only for definitions in libraries with a `package:` uri.
* Converts all the tests to be in packages.
* Bonus: This removes the uri-mapping in equality checks.
* These packages are added to the root pub workspace.
* For the dart2js tests we need to do some juggling to keep the
tests files in memory.
* Only support `package:` uris
* Removed all the `relativizeUri` code for file paths.
* Start emitting errors on non `package:` uris with a `@RecordUse()`
annotation.
Unrelated cleanups:
* We no longer support recording const instances in annotations, this
PR cleans up code in `kernel` and `type_flow`.
TEST=pkg/compiler/test/record_use/record_use_test.dart
TEST=pkg/dart2wasm/test/record_use_test.dart
TEST=pkg/vm/test/transformations/record_use_test.dart
Closes: https://github.com/dart-lang/native/issues/2891
Change-Id: I1bc6905291230375e185930d2c000700ac778f85
Cq-Include-Trybots: luci.dart.try:dart2wasm-asserts-linux-chrome-try,dart2wasm-asserts-minified-linux-d8-try,dart2wasm-linux-chrome-try,dart2wasm-linux-d8-try,dart2wasm-linux-firefox-try,dart2wasm-linux-jscm-chrome-try,dart2wasm-linux-optimized-jsc-try,pkg-linux-debug-try,pkg-linux-release-arm64-try,pkg-linux-release-try,pkg-mac-release-arm64-try,pkg-mac-release-try,pkg-win-release-arm64-try,pkg-win-release-try,dart2js-canary-linux-try,dart2js-hostasserts-linux-d8-try,dart2js-linux-chrome-try,dart2js-linux-firefox-try,dart2js-mac-chrome-try,dart2js-mac-safari-try,dart2js-minified-csp-linux-chrome-try,dart2js-minified-linux-d8-try,dart2js-unit-linux-x64-release-try,dart2js-win-chrome-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/478920
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
committed by
Commit Queue
parent
277119b883
commit
1cab2a8617
@@ -10,11 +10,7 @@
|
||||
/// null, List, Map, or constant objects.
|
||||
library;
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:compiler/src/elements/entities.dart';
|
||||
// ignore: implementation_imports
|
||||
import 'package:front_end/src/api_unstable/dart2js.dart' show relativizeUri;
|
||||
import 'package:compiler/src/deferred_load/output_unit.dart';
|
||||
import 'package:compiler/src/js_model/js_world.dart';
|
||||
import 'package:record_use/record_use_internal.dart';
|
||||
@@ -50,6 +46,9 @@ class RecordUseCollector {
|
||||
}
|
||||
|
||||
void _register(String loadingUnit, RecordedUse recordedUse) {
|
||||
if (!recordedUse.function.library.canonicalUri.isScheme('package')) {
|
||||
return;
|
||||
}
|
||||
final callReference = switch (recordedUse) {
|
||||
RecordedCallWithArguments() => CallWithArguments(
|
||||
loadingUnit: loadingUnit,
|
||||
@@ -76,11 +75,7 @@ class RecordUseCollector {
|
||||
identifier: Identifier(
|
||||
name: key.name!,
|
||||
scope: key.enclosingClass?.name,
|
||||
importUri: relativizeUri(
|
||||
Uri.base,
|
||||
key.library.canonicalUri,
|
||||
Platform.isWindows,
|
||||
),
|
||||
importUri: key.library.canonicalUri.toString(),
|
||||
),
|
||||
loadingUnit:
|
||||
outputUnitToName[_closedWorld.outputUnitData.outputUnitForMember(
|
||||
|
||||
+2
@@ -4,6 +4,7 @@
|
||||
|
||||
import 'dart:js_interop';
|
||||
|
||||
// ignore: experimental_member_use
|
||||
import 'package:meta/meta.dart' show RecordUse;
|
||||
|
||||
void main() {
|
||||
@@ -11,5 +12,6 @@ void main() {
|
||||
}
|
||||
|
||||
@JS()
|
||||
// ignore: experimental_member_use
|
||||
@RecordUse()
|
||||
external int someExternalFunction(int k);
|
||||
@@ -0,0 +1,14 @@
|
||||
# Copyright (c) 2026, 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.
|
||||
|
||||
name: record_use_js_test
|
||||
publish_to: none
|
||||
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
sdk: ^3.12.0-0
|
||||
|
||||
dependencies:
|
||||
meta: any
|
||||
@@ -3,10 +3,9 @@
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io' show Directory, File, Platform;
|
||||
import 'dart:io' show Directory, File;
|
||||
|
||||
import 'package:compiler/compiler_api.dart' as api show OutputType;
|
||||
import 'package:compiler/compiler_api.dart';
|
||||
import 'package:compiler/compiler_api.dart' as api;
|
||||
import 'package:compiler/src/commandline_options.dart' show Flags;
|
||||
import 'package:compiler/src/util/memory_compiler.dart';
|
||||
import 'package:expect/expect.dart' show Expect;
|
||||
@@ -23,37 +22,82 @@ const List<String> compilerOptions = [Flags.writeRecordedUses, Flags.testMode];
|
||||
/// Run `dart -DupdateExpectations=true pkg/vm/test/transformations/record_use_test.dart`
|
||||
/// to update the shared expectations to the VM output.
|
||||
Future<void> main() async {
|
||||
final vmTestCases = Directory('pkg/vm/testcases/transformations/record_use');
|
||||
final jsTestCases = Directory.fromUri(Platform.script.resolve('data'));
|
||||
final testFiles = [...jsTestCases.listSync(), ...vmTestCases.listSync()]
|
||||
.whereType<File>()
|
||||
.where((file) => file.path.endsWith('.dart'))
|
||||
.map(
|
||||
(file) => TestFile(
|
||||
file: file,
|
||||
basename: path.basename(file.path),
|
||||
contents: file.readAsStringSync(),
|
||||
uri: _createUri(path.basename(file.path)),
|
||||
),
|
||||
);
|
||||
final vmFiles = _getTestFiles(
|
||||
'pkg/vm/testcases/transformations/record_use/lib',
|
||||
'record_use_test',
|
||||
);
|
||||
final jsFiles = _getTestFiles(
|
||||
'pkg/compiler/test/record_use/data/lib',
|
||||
'record_use_js_test',
|
||||
);
|
||||
|
||||
final allFiles = {for (final file in testFiles) file.uri.path: file.contents};
|
||||
final testFiles = [...vmFiles, ...jsFiles];
|
||||
|
||||
final allFiles = {
|
||||
for (final file in vmFiles)
|
||||
'/record_use_test/lib/${file.basename}': file.contents,
|
||||
for (final file in jsFiles)
|
||||
'/record_use_js_test/lib/${file.basename}': file.contents,
|
||||
'/.dart_tool/package_config.json': jsonEncode({
|
||||
"configVersion": 2,
|
||||
"packages": [
|
||||
{
|
||||
"name": "record_use_test",
|
||||
"rootUri": "/record_use_test/",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.9",
|
||||
},
|
||||
{
|
||||
"name": "record_use_js_test",
|
||||
"rootUri": "/record_use_js_test/",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.9",
|
||||
},
|
||||
{
|
||||
"name": "meta",
|
||||
"rootUri": Directory.current.uri.resolve('pkg/meta/').toString(),
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.9",
|
||||
},
|
||||
],
|
||||
}),
|
||||
};
|
||||
for (final testFile in testFiles.where((element) => element.hasMain)) {
|
||||
final bool isThrowsTest = testFile.basename.contains('throws');
|
||||
test(
|
||||
'${testFile.file.path}',
|
||||
skip: dart2jsNotSupported.contains(testFile.basename),
|
||||
() async {
|
||||
final diagnosticCollector = DiagnosticCollector();
|
||||
final recordedUsages = await compileWithUsages(
|
||||
entryPoint: testFile.uri,
|
||||
memorySourceFiles: allFiles,
|
||||
diagnosticHandler: diagnosticCollector,
|
||||
expectSuccess: !isThrowsTest,
|
||||
);
|
||||
|
||||
if (isThrowsTest) {
|
||||
Expect.isTrue(recordedUsages == null);
|
||||
final errors = diagnosticCollector.errors
|
||||
.map((e) => e.text)
|
||||
.join('\n');
|
||||
if (testFile.basename.contains('invalid_location')) {
|
||||
Expect.contains('RecordUse', errors);
|
||||
Expect.contains(
|
||||
'annotation cannot be placed on this element',
|
||||
errors,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final goldenFile = File(testFile.file.path + '.json.expect');
|
||||
const update = bool.fromEnvironment('updateExpectations');
|
||||
if (!goldenFile.existsSync() || update) {
|
||||
await goldenFile.create();
|
||||
await goldenFile.writeAsString(recordedUsages);
|
||||
await goldenFile.writeAsString(recordedUsages!);
|
||||
} else {
|
||||
final actual = Recordings.fromJson(jsonDecode(recordedUsages));
|
||||
final actual = Recordings.fromJson(jsonDecode(recordedUsages!));
|
||||
final goldenContents = await goldenFile.readAsString();
|
||||
final golden = Recordings.fromJson(jsonDecode(goldenContents));
|
||||
final semanticEquals = actual.semanticEquals(
|
||||
@@ -63,8 +107,6 @@ Future<void> main() async {
|
||||
// Ensure test coverage of tear offs, add pragmas to prevent
|
||||
// optimiations if necessary.
|
||||
allowTearoffToStaticPromotion: false,
|
||||
uriMapping: (String uri) =>
|
||||
uri.replaceFirst('memory:sdk/tests/web/native/', ''),
|
||||
loadingUnitMapping: (String unit) =>
|
||||
const <String, String>{'out': '1', 'out_1': '2'}[unit] ?? unit,
|
||||
);
|
||||
@@ -80,6 +122,49 @@ Future<void> main() async {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
test('outside_package_throws', () async {
|
||||
final entryPoint = Uri.parse('memory:/outside.dart');
|
||||
final memorySourceFiles = {
|
||||
...allFiles,
|
||||
'/outside.dart': '''
|
||||
import 'package:meta/meta.dart' show RecordUse;
|
||||
class SomeClass {
|
||||
@RecordUse()
|
||||
static String someStaticMethod(int a) => a.toString();
|
||||
}
|
||||
void main() {
|
||||
print(SomeClass.someStaticMethod(42));
|
||||
}
|
||||
''',
|
||||
};
|
||||
final diagnosticCollector = DiagnosticCollector();
|
||||
await compileWithUsages(
|
||||
entryPoint: entryPoint,
|
||||
memorySourceFiles: memorySourceFiles,
|
||||
diagnosticHandler: diagnosticCollector,
|
||||
expectSuccess: false,
|
||||
);
|
||||
final errors = diagnosticCollector.errors.map((e) => e.text).join('\n');
|
||||
Expect.contains('RecordUse', errors);
|
||||
Expect.contains('package:', errors);
|
||||
});
|
||||
}
|
||||
|
||||
Iterable<TestFile> _getTestFiles(String dirPath, String packageName) {
|
||||
return Directory(dirPath)
|
||||
.listSync()
|
||||
.whereType<File>()
|
||||
.where((file) => file.path.endsWith('.dart'))
|
||||
.map(
|
||||
(file) => TestFile(
|
||||
file: file,
|
||||
basename: path.basename(file.path),
|
||||
contents: file.readAsStringSync(),
|
||||
uri: Uri.parse('package:$packageName/${path.basename(file.path)}'),
|
||||
packageName: packageName,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class TestFile {
|
||||
@@ -87,12 +172,14 @@ class TestFile {
|
||||
final String basename;
|
||||
final String contents;
|
||||
final Uri uri;
|
||||
final String packageName;
|
||||
|
||||
const TestFile({
|
||||
required this.file,
|
||||
required this.basename,
|
||||
required this.contents,
|
||||
required this.uri,
|
||||
required this.packageName,
|
||||
});
|
||||
|
||||
bool get hasMain => contents.contains('main()');
|
||||
@@ -100,30 +187,35 @@ class TestFile {
|
||||
|
||||
typedef CompiledOutput = Map<api.OutputType, Map<String, String>>;
|
||||
|
||||
Future<String> compileWithUsages({
|
||||
Future<String?> compileWithUsages({
|
||||
Uri? entryPoint,
|
||||
required Map<String, dynamic> memorySourceFiles,
|
||||
api.CompilerDiagnostics? diagnosticHandler,
|
||||
bool expectSuccess = true,
|
||||
}) async {
|
||||
final outputProvider = OutputCollector();
|
||||
|
||||
CompilationResult result = await runCompiler(
|
||||
api.CompilationResult result = await runCompiler(
|
||||
entryPoint: entryPoint,
|
||||
memorySourceFiles: memorySourceFiles,
|
||||
outputProvider: outputProvider,
|
||||
diagnosticHandler: diagnosticHandler,
|
||||
options: [Flags.writeRecordedUses],
|
||||
packageConfig: Uri.parse('memory:/.dart_tool/package_config.json'),
|
||||
);
|
||||
Expect.isTrue(result.isSuccess);
|
||||
if (expectSuccess) {
|
||||
Expect.isTrue(result.isSuccess);
|
||||
} else {
|
||||
if (result.isSuccess) {
|
||||
throw 'Compilation succeeded but was expected to fail.';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return outputProvider.outputMap[OutputType.recordedUses]!.values.first
|
||||
return outputProvider.outputMap[api.OutputType.recordedUses]!.values.first
|
||||
.toString();
|
||||
}
|
||||
|
||||
// Pretend this is a dart2js_native test to allow use of 'native' keyword
|
||||
// and import of private libraries.
|
||||
Uri _createUri(String fileName) {
|
||||
return Uri.parse('memory:sdk/tests/web/native/$fileName');
|
||||
}
|
||||
|
||||
const dart2jsNotSupported = {
|
||||
// No support for instance constants.
|
||||
// https://github.com/dart-lang/native/issues/2893
|
||||
|
||||
@@ -725,8 +725,7 @@ Future<CompilationResult> _runCodegenPhase(
|
||||
return moduleOutput.moduleImportName;
|
||||
}
|
||||
|
||||
record_use.transformComponent(
|
||||
component, options.recordedUsesFile!, options.mainUri,
|
||||
record_use.transformComponent(component, options.recordedUsesFile!,
|
||||
loadingUnitLookup: loadingUnitForNode);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,49 +12,83 @@ import 'util.dart';
|
||||
|
||||
final Uri _pkgVmDir = Platform.script.resolve('../../vm/');
|
||||
|
||||
Future<void> runTestCase(Uri source) async {
|
||||
Future<void> runTestCase(
|
||||
Uri sourceFileUri,
|
||||
Uri sourcePackageUri,
|
||||
Uri packagesFileUri,
|
||||
) async {
|
||||
final bool isThrowsTest = sourceFileUri.path.contains('throws');
|
||||
await withTempDir((String tempDir) async {
|
||||
final recordedUsesFile = path.join(tempDir, 'recorded_usages.json');
|
||||
await run([
|
||||
final List<String> args = [
|
||||
Platform.executable,
|
||||
'compile',
|
||||
'wasm',
|
||||
'-O2',
|
||||
source.toFilePath(),
|
||||
'--packages=${packagesFileUri.toFilePath()}',
|
||||
sourceFileUri.toFilePath(),
|
||||
'-o',
|
||||
path.join(tempDir, 'out.wasm'),
|
||||
'--enable-deferred-loading',
|
||||
'--extra-compiler-option=--recorded-uses=$recordedUsesFile',
|
||||
]);
|
||||
];
|
||||
|
||||
if (isThrowsTest) {
|
||||
final result = await Process.run(args.first, args.skip(1).toList());
|
||||
if (result.exitCode == 0) {
|
||||
throw 'Compilation succeeded for $sourceFileUri but was expected to fail.';
|
||||
}
|
||||
final errors = '${result.stdout}\n${result.stderr}';
|
||||
if (sourceFileUri.path.contains('invalid_location')) {
|
||||
if (!errors.contains('RecordUse') ||
|
||||
!errors.contains('cannot be placed on this element')) {
|
||||
throw 'Wrong error message for $sourceFileUri:\n$errors';
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
await run(args);
|
||||
|
||||
final actualSemantic = Recordings.fromJson(
|
||||
jsonDecode(File(recordedUsesFile).readAsStringSync()),
|
||||
);
|
||||
final goldenFile = File('${source.toFilePath()}.json.expect');
|
||||
final goldenContents = await goldenFile.readAsString();
|
||||
final golden = Recordings.fromJson(jsonDecode(goldenContents));
|
||||
final semanticEquals =
|
||||
actualSemantic.semanticEquals(golden, loadingUnitMapping: (unit) {
|
||||
final codeUnits = unit.codeUnits;
|
||||
int result = 0;
|
||||
int power = 1;
|
||||
for (final codeUnit in codeUnits) {
|
||||
result += (codeUnit - 35) * power;
|
||||
power *= 92;
|
||||
}
|
||||
return '$result';
|
||||
});
|
||||
final goldenFile = File('${sourceFileUri.toFilePath()}.json.expect');
|
||||
const update = bool.fromEnvironment('updateExpectations');
|
||||
|
||||
bool semanticEquals = false;
|
||||
if (goldenFile.existsSync()) {
|
||||
final goldenContents = await goldenFile.readAsString();
|
||||
final golden = Recordings.fromJson(jsonDecode(goldenContents));
|
||||
semanticEquals =
|
||||
actualSemantic.semanticEquals(golden, loadingUnitMapping: (unit) {
|
||||
final codeUnits = unit.codeUnits;
|
||||
int result = 0;
|
||||
int power = 1;
|
||||
for (final codeUnit in codeUnits) {
|
||||
result += (codeUnit - 35) * power;
|
||||
power *= 92;
|
||||
}
|
||||
return '$result';
|
||||
});
|
||||
}
|
||||
|
||||
if (update && !semanticEquals) {
|
||||
goldenFile.writeAsStringSync(jsonEncode(actualSemantic));
|
||||
print('Updated expectations for $source');
|
||||
goldenFile.writeAsStringSync(
|
||||
JsonEncoder.withIndent(' ').convert(actualSemantic.toJson()),
|
||||
);
|
||||
print('Updated expectations for $sourceFileUri');
|
||||
} else if (!semanticEquals) {
|
||||
print('Actual: ${actualSemantic.toJson()}');
|
||||
print('Expected: ${golden.toJson()}');
|
||||
final encoder = JsonEncoder.withIndent(' ');
|
||||
print('Actual:\n${encoder.convert(actualSemantic.toJson())}');
|
||||
if (goldenFile.existsSync()) {
|
||||
final goldenContents = await goldenFile.readAsString();
|
||||
print('Expected:\n$goldenContents');
|
||||
}
|
||||
print('To update expectations, run: dart -DupdateExpectations=true '
|
||||
'pkg/dart2wasm/test/record_use_test.dart '
|
||||
'${path.basename(source.toFilePath())}');
|
||||
throw 'Expectations for $source do not match';
|
||||
'${path.basename(sourceFileUri.toFilePath())}');
|
||||
throw 'Expectations for $sourceFileUri do not match';
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -62,15 +96,57 @@ Future<void> runTestCase(Uri source) async {
|
||||
Future<void> main(List<String> args) async {
|
||||
assert(args.isEmpty || args.length == 1);
|
||||
final filter = args.firstOrNull;
|
||||
final testCasesDir = Directory.fromUri(
|
||||
_pkgVmDir.resolve('testcases/transformations/record_use/'),
|
||||
final recordUseTestDir = _pkgVmDir.resolve(
|
||||
'testcases/transformations/record_use/',
|
||||
);
|
||||
final testCasesDir = Directory.fromUri(recordUseTestDir.resolve('lib/'));
|
||||
final packagesFileUri = _pkgVmDir.resolve(
|
||||
'../../.dart_tool/package_config.json',
|
||||
);
|
||||
|
||||
for (var fse in testCasesDir.listSync(recursive: true, followLinks: false)) {
|
||||
if (fse is! File) continue;
|
||||
if (fse.path.endsWith('.dart') &&
|
||||
!fse.path.contains('helper') &&
|
||||
(filter == null || fse.path.contains(filter))) {
|
||||
await runTestCase(fse.uri);
|
||||
final name = path.basename(fse.path);
|
||||
final packageUri = Uri.parse('package:record_use_test/$name');
|
||||
await runTestCase(fse.uri, packageUri, packagesFileUri);
|
||||
}
|
||||
}
|
||||
|
||||
await runOutsidePackageThrows(packagesFileUri);
|
||||
}
|
||||
|
||||
Future<void> runOutsidePackageThrows(Uri packagesFileUri) async {
|
||||
await withTempDir((String tempDir) async {
|
||||
final sourceFile = File(path.join(tempDir, 'outside.dart'));
|
||||
sourceFile.writeAsStringSync('''
|
||||
import 'package:meta/meta.dart' show RecordUse;
|
||||
class SomeClass {
|
||||
@RecordUse()
|
||||
static String someStaticMethod(int a) => a.toString();
|
||||
}
|
||||
void main() {
|
||||
print(SomeClass.someStaticMethod(42));
|
||||
}
|
||||
''');
|
||||
final result = await Process.run(Platform.executable, [
|
||||
'compile',
|
||||
'wasm',
|
||||
'-O2',
|
||||
'--packages=${packagesFileUri.toFilePath()}',
|
||||
sourceFile.path,
|
||||
'-o',
|
||||
path.join(tempDir, 'out.wasm'),
|
||||
]);
|
||||
|
||||
if (result.exitCode == 0) {
|
||||
throw 'Compilation succeeded but was expected to fail.';
|
||||
}
|
||||
final errors = '${result.stdout}\n${result.stderr}';
|
||||
if (!errors.contains('RecordUse') || !errors.contains('package:')) {
|
||||
throw 'Wrong error message for outside package test:\n$errors';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14996,6 +14996,13 @@ const MessageCode recordUseCannotBePlacedHere = const MessageCode(
|
||||
"""`RecordUse` annotation cannot be placed on this element.""",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode recordUseOutsideOfPackage = const MessageCode(
|
||||
"RecordUseOutsideOfPackage",
|
||||
problemMessage:
|
||||
"""`RecordUse` annotations are only supported in libraries with a `package:` URI.""",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode recordUsedAsCallable = const MessageCode(
|
||||
"RecordUsedAsCallable",
|
||||
|
||||
@@ -45,15 +45,32 @@ bool isRecordUse(Class cls) =>
|
||||
cls.enclosingLibrary.importUri == _metaLibraryUri;
|
||||
|
||||
// Coverage-ignore(suite): Not run.
|
||||
bool isBeingRecorded(Class cls) => isRecordUse(cls) || hasRecordUse(cls);
|
||||
bool _enclosedInLibraryWithPackageUri(Annotatable node) {
|
||||
final Library? library = switch (node) {
|
||||
Library l => l,
|
||||
Class c => c.enclosingLibrary,
|
||||
Member m => m.enclosingLibrary,
|
||||
_ => null,
|
||||
};
|
||||
return library?.importUri.isScheme('package') ?? false;
|
||||
}
|
||||
|
||||
// Coverage-ignore(suite): Not run.
|
||||
/// If [cls] annotation is in turn annotated by a recording annotation.
|
||||
bool hasRecordUse(Class cls) => cls.annotations
|
||||
.whereType<ConstantExpression>()
|
||||
.map((e) => e.constant)
|
||||
.whereType<InstanceConstant>()
|
||||
.any((annotation) => isRecordUse(annotation.classNode));
|
||||
bool isBeingRecorded(Annotatable node) {
|
||||
final bool hasAnnotation = hasRecordUseAnnotation(node);
|
||||
|
||||
if (!hasAnnotation) return false;
|
||||
|
||||
return _enclosedInLibraryWithPackageUri(node);
|
||||
}
|
||||
|
||||
// Coverage-ignore(suite): Not run.
|
||||
Uri? _getFileUri(Annotatable node) {
|
||||
if (node is Library) return node.fileUri;
|
||||
if (node is Class) return node.fileUri;
|
||||
if (node is Member) return node.fileUri;
|
||||
return node.location?.file;
|
||||
}
|
||||
|
||||
// Coverage-ignore(suite): Not run.
|
||||
/// Report if the resource annotations is placed on anything but a static
|
||||
@@ -63,6 +80,17 @@ void validateRecordUseDeclaration(
|
||||
ErrorReporter errorReporter,
|
||||
Iterable<InstanceConstant> resourceAnnotations,
|
||||
) {
|
||||
if (resourceAnnotations.isEmpty) return;
|
||||
|
||||
final Uri? fileUri = _getFileUri(node);
|
||||
if (fileUri == null) return;
|
||||
|
||||
if (!_enclosedInLibraryWithPackageUri(node)) {
|
||||
errorReporter.report(
|
||||
diag.recordUseOutsideOfPackage.withLocation(fileUri, node.fileOffset, 1),
|
||||
);
|
||||
}
|
||||
|
||||
final bool onNonStaticMethod =
|
||||
node is! Procedure || !node.isStatic || node.kind != ProcedureKind.Method;
|
||||
|
||||
@@ -72,7 +100,7 @@ void validateRecordUseDeclaration(
|
||||
if (onNonStaticMethod && onClassWithoutConstConstructor) {
|
||||
errorReporter.report(
|
||||
diag.recordUseCannotBePlacedHere.withLocation(
|
||||
node.location!.file,
|
||||
fileUri,
|
||||
node.fileOffset,
|
||||
1,
|
||||
),
|
||||
|
||||
@@ -140,6 +140,7 @@ front_end/PatchInjectionFailed/example: missingExample # Patching.
|
||||
front_end/PatternMatchingError/example: missingExample # Seemingly this is not issued as an error, but ends up in the kernel AST.
|
||||
front_end/PositionalAfterNamedArgument/example: missingExample # Seemingly only issued in Analyzer
|
||||
front_end/RecordUseCannotBePlacedHere/example: missingExample # No coverage.
|
||||
front_end/RecordUseOutsideOfPackage/example: missingExample # No coverage.
|
||||
front_end/SdkRootNotFound/example: missingExample # Issued on what is essentially a wrong setup.
|
||||
front_end/SdkSpecificationNotFound/example: missingExample # Issued on what is essentially a wrong setup.
|
||||
front_end/SdkSummaryNotFound/example: missingExample # Issued on what is essentially a wrong setup.
|
||||
|
||||
@@ -7175,6 +7175,10 @@ recordUseCannotBePlacedHere:
|
||||
parameters: none
|
||||
problemMessage: "`RecordUse` annotation cannot be placed on this element."
|
||||
|
||||
recordUseOutsideOfPackage:
|
||||
parameters: none
|
||||
problemMessage: "`RecordUse` annotations are only supported in libraries with a `package:` URI."
|
||||
|
||||
wasmImportOrExportInUserCode:
|
||||
parameters: none
|
||||
problemMessage: "Pragmas `wasm:import` and `wasm:export` are for internal use only and cannot be used by user code."
|
||||
|
||||
@@ -871,7 +871,7 @@ Future runGlobalTransformations(
|
||||
final recordedUsagesFile = args.recordedUsages;
|
||||
if (recordedUsagesFile != null) {
|
||||
assert(args.source != null);
|
||||
record_use.transformComponent(component, recordedUsagesFile, args.source!);
|
||||
record_use.transformComponent(component, recordedUsagesFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:front_end/src/kernel/record_use.dart' as recordUse;
|
||||
import 'package:front_end/src/kernel/record_use.dart' as record_use;
|
||||
|
||||
/// Expose only the [collect] method of a [_ConstantCollector] to outside use.
|
||||
extension type ConstantCollector(_ConstantCollector _collector) {
|
||||
@@ -79,7 +79,7 @@ class _ConstantCollector implements ConstantVisitor {
|
||||
void visitInstanceConstant(InstanceConstant constant) {
|
||||
assert(_expression != null);
|
||||
final classNode = constant.classNode;
|
||||
if (_hasRecordUseAnnotation[classNode] ??= recordUse.hasRecordUseAnnotation(
|
||||
if (_hasRecordUseAnnotation[classNode] ??= record_use.isBeingRecorded(
|
||||
classNode,
|
||||
)) {
|
||||
collector(_expression!, constant);
|
||||
|
||||
@@ -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:front_end/src/kernel/record_use.dart' as recordUse;
|
||||
import 'package:front_end/src/kernel/record_use.dart' show isBeingRecorded;
|
||||
import 'package:kernel/ast.dart' as ast;
|
||||
import 'package:record_use/record_use_internal.dart';
|
||||
import 'package:vm/transformations/record_use/record_use.dart';
|
||||
@@ -24,18 +24,15 @@ class CallRecorder {
|
||||
/// A function to look up the loading unit for a reference.
|
||||
final LoadingUnitLookup _loadingUnitLookup;
|
||||
|
||||
/// The source uri to base relative URIs off of.
|
||||
final Uri _source;
|
||||
|
||||
/// Whether to save line and column info as well as the URI.
|
||||
//TODO(mosum): add verbose mode to enable this
|
||||
bool exactLocation = false;
|
||||
|
||||
CallRecorder(this._source, this._loadingUnitLookup);
|
||||
CallRecorder(this._loadingUnitLookup);
|
||||
|
||||
/// Will record a static invocation if it is annotated with `@RecordUse`.
|
||||
void recordStaticInvocation(ast.StaticInvocation node) {
|
||||
if (recordUse.hasRecordUseAnnotation(node.target)) {
|
||||
if (isBeingRecorded(node.target)) {
|
||||
// Collect the (int, bool, double, or String) arguments passed in the call.
|
||||
final createCallReference = _createCallReference(node);
|
||||
_addToUsage(node.target, createCallReference);
|
||||
@@ -46,10 +43,7 @@ class CallRecorder {
|
||||
void recordConstantExpression(ast.ConstantExpression node) {
|
||||
final constant = node.constant;
|
||||
if (constant is ast.StaticTearOffConstant) {
|
||||
final hasRecordUseAnnotation = recordUse.hasRecordUseAnnotation(
|
||||
constant.target,
|
||||
);
|
||||
if (hasRecordUseAnnotation) {
|
||||
if (isBeingRecorded(constant.target)) {
|
||||
_addToUsage(
|
||||
constant.target,
|
||||
CallTearoff(loadingUnit: _loadingUnitLookup(node)),
|
||||
@@ -139,11 +133,11 @@ class CallRecorder {
|
||||
ast.Member target,
|
||||
) {
|
||||
final enclosingLibrary = target.enclosingLibrary;
|
||||
String file = getImportUri(enclosingLibrary, _source);
|
||||
final importUri = enclosingLibrary.importUri.toString();
|
||||
|
||||
return (
|
||||
identifier: Identifier(
|
||||
importUri: file,
|
||||
importUri: importUri,
|
||||
scope: target.enclosingClass?.name,
|
||||
name: target.name.text,
|
||||
),
|
||||
|
||||
@@ -22,9 +22,6 @@ class InstanceRecorder {
|
||||
/// A function to look up the loading unit for a reference.
|
||||
final LoadingUnitLookup _loadingUnitLookup;
|
||||
|
||||
/// The source uri to base relative URIs off of.
|
||||
final Uri _source;
|
||||
|
||||
/// A visitor traversing and collecting constants.
|
||||
late final ConstantCollector collector;
|
||||
|
||||
@@ -32,7 +29,7 @@ class InstanceRecorder {
|
||||
//TODO(mosum): add verbose mode to enable this
|
||||
bool exactLocation = false;
|
||||
|
||||
InstanceRecorder(this._source, this._loadingUnitLookup) {
|
||||
InstanceRecorder(this._loadingUnitLookup) {
|
||||
collector = ConstantCollector.collectWith(_collectInstance);
|
||||
}
|
||||
|
||||
@@ -66,10 +63,10 @@ class InstanceRecorder {
|
||||
ast.Class cls,
|
||||
) {
|
||||
final enclosingLibrary = cls.enclosingLibrary;
|
||||
final file = getImportUri(enclosingLibrary, _source);
|
||||
final importUri = enclosingLibrary.importUri.toString();
|
||||
|
||||
return (
|
||||
identifier: Identifier(importUri: file, name: cls.name),
|
||||
identifier: Identifier(importUri: importUri, name: cls.name),
|
||||
loadingUnit: _loadingUnitLookup(cls),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
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:record_use/record_use_internal.dart';
|
||||
@@ -44,14 +42,13 @@ LoadingUnitLookup _getDefaultLoadingUnitLookup(ast.Component component) {
|
||||
/// application.
|
||||
ast.Component transformComponent(
|
||||
ast.Component component,
|
||||
Uri recordedUsagesFile,
|
||||
Uri source, {
|
||||
Uri recordedUsagesFile, {
|
||||
LoadingUnitLookup? loadingUnitLookup,
|
||||
}) {
|
||||
loadingUnitLookup ??= _getDefaultLoadingUnitLookup(component);
|
||||
|
||||
final callRecorder = CallRecorder(source, loadingUnitLookup);
|
||||
final instanceRecorder = InstanceRecorder(source, loadingUnitLookup);
|
||||
final callRecorder = CallRecorder(loadingUnitLookup);
|
||||
final instanceRecorder = InstanceRecorder(loadingUnitLookup);
|
||||
component.accept(_RecordUseVisitor(callRecorder, instanceRecorder));
|
||||
|
||||
final usages = _usages(
|
||||
@@ -196,17 +193,6 @@ InstanceConstant evaluateInstanceConstant(ast.InstanceConstant constant) =>
|
||||
Never _unsupported(String constantType) =>
|
||||
throw UnsupportedError('$constantType is not supported for recording.');
|
||||
|
||||
String getImportUri(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;
|
||||
|
||||
@@ -7,7 +7,7 @@ library;
|
||||
|
||||
import 'dart:core' hide Type;
|
||||
|
||||
import 'package:front_end/src/api_prototype/record_use.dart' as recordUse;
|
||||
import 'package:front_end/src/api_prototype/record_use.dart' as record_use;
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/library_index.dart' show LibraryIndex;
|
||||
|
||||
@@ -226,7 +226,7 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
|
||||
visitField(Field field) {
|
||||
if (field.isInstanceMember &&
|
||||
field.enclosingClass!.hasConstConstructor &&
|
||||
recordUse.hasRecordUse(field.enclosingClass!)) {
|
||||
record_use.isBeingRecorded(field.enclosingClass!)) {
|
||||
// If a class has a `@RecordUse` annotation then a user-defined linker
|
||||
// script may want to inspect instance constants of the class, so we have
|
||||
// to preserve all fields.
|
||||
|
||||
@@ -9,7 +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/record_use.dart' as RecordUse;
|
||||
import 'package:front_end/src/api_prototype/record_use.dart' as record_use;
|
||||
import 'package:kernel/ast.dart' hide Statement, StatementVisitor;
|
||||
import 'package:kernel/ast.dart' as ast show Statement;
|
||||
import 'package:kernel/class_hierarchy.dart'
|
||||
@@ -344,7 +344,7 @@ class CleanupAnnotations extends RecursiveVisitor {
|
||||
protobufHandler?.usesAnnotationClass(cls) ?? false;
|
||||
return cls == pragmaClass ||
|
||||
usesProtobufAnnotation ||
|
||||
RecordUse.isBeingRecorded(cls);
|
||||
record_use.isRecordUse(cls);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -10,7 +10,6 @@ resolution: workspace
|
||||
|
||||
# Use 'any' constraints here; we get our versions from the DEPS file.
|
||||
dependencies:
|
||||
_fe_analyzer_shared: any
|
||||
args: any
|
||||
build_integration: any
|
||||
collection: any
|
||||
|
||||
@@ -21,12 +21,21 @@ import 'package:path/path.dart' as path;
|
||||
|
||||
final Uri _pkgVmDir = Platform.script.resolve('../..');
|
||||
|
||||
void runTestCaseAot(Uri source, bool throws) async {
|
||||
void runTestCaseAot(
|
||||
Uri sourceFileUri,
|
||||
Uri sourcePackageUri,
|
||||
Uri packagesFileUri,
|
||||
bool throws,
|
||||
) async {
|
||||
final target = VmTarget(TargetFlags(supportMirrors: false));
|
||||
|
||||
Component component;
|
||||
try {
|
||||
component = await compileTestCaseToKernelProgram(source, target: target);
|
||||
component = await compileTestCaseToKernelProgram(
|
||||
sourcePackageUri,
|
||||
target: target,
|
||||
packagesFileUri: packagesFileUri,
|
||||
);
|
||||
} catch (e) {
|
||||
if (throws) {
|
||||
return;
|
||||
@@ -52,7 +61,7 @@ void runTestCaseAot(Uri source, bool throws) async {
|
||||
useProtobufTreeShakerV2: true,
|
||||
treeShakeWriteOnlyFields: true,
|
||||
recordedUsages: recordedUsagesFile,
|
||||
source: source,
|
||||
source: sourcePackageUri,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -66,12 +75,16 @@ void runTestCaseAot(Uri source, bool throws) async {
|
||||
component.mainMethod!.enclosingLibrary,
|
||||
).replaceAll(_pkgVmDir.toString(), 'org-dartlang-test:///');
|
||||
|
||||
compareResultWithExpectationsFile(source, actual, expectFilePostfix: '.aot');
|
||||
compareResultWithExpectationsFile(
|
||||
sourceFileUri,
|
||||
actual,
|
||||
expectFilePostfix: '.aot',
|
||||
);
|
||||
|
||||
final actualSemantic = Recordings.fromJson(
|
||||
jsonDecode(File.fromUri(recordedUsagesFile).readAsStringSync()),
|
||||
);
|
||||
final goldenFile = File('${source.toFilePath()}.json.expect');
|
||||
final goldenFile = File('${sourceFileUri.toFilePath()}.json.expect');
|
||||
final update = bool.fromEnvironment('updateExpectations');
|
||||
|
||||
bool semanticEquals = false;
|
||||
@@ -83,7 +96,7 @@ void runTestCaseAot(Uri source, bool throws) async {
|
||||
|
||||
if (!semanticEquals || update) {
|
||||
compareResultWithExpectationsFile(
|
||||
source,
|
||||
sourceFileUri,
|
||||
File.fromUri(recordedUsagesFile).readAsStringSync(),
|
||||
expectFilePostfix: '.json',
|
||||
);
|
||||
@@ -94,9 +107,14 @@ void main(List<String> args) {
|
||||
assert(args.isEmpty || args.length == 1);
|
||||
final filter = args.firstOrNull;
|
||||
group('record-use-transformations', () {
|
||||
final testCasesDir = Directory.fromUri(
|
||||
_pkgVmDir.resolve('testcases/transformations/record_use/'),
|
||||
final recordUseTestDir = _pkgVmDir.resolve(
|
||||
'testcases/transformations/record_use/',
|
||||
);
|
||||
final testCasesDir = Directory.fromUri(recordUseTestDir.resolve('lib/'));
|
||||
final packagesFileUri = _pkgVmDir.resolve(
|
||||
'../../.dart_tool/package_config.json',
|
||||
);
|
||||
|
||||
for (var file
|
||||
in testCasesDir
|
||||
.listSync(recursive: true, followLinks: false)
|
||||
@@ -104,11 +122,42 @@ void main(List<String> args) {
|
||||
if (file.path.endsWith('.dart') &&
|
||||
!file.path.contains('helper') &&
|
||||
(filter == null || file.path.contains(filter))) {
|
||||
final name = path.basename(file.path);
|
||||
final packageUri = Uri.parse('package:record_use_test/$name');
|
||||
test(
|
||||
'${file.path} aot',
|
||||
() => runTestCaseAot(file.uri, file.path.contains('throws')),
|
||||
() => runTestCaseAot(
|
||||
file.uri,
|
||||
packageUri,
|
||||
packagesFileUri,
|
||||
file.path.contains('throws'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
test('outside_package_throws', () async {
|
||||
final sourceFileUri = recordUseTestDir.resolve(
|
||||
'outside_package_throws.dart',
|
||||
);
|
||||
final target = VmTarget(TargetFlags(supportMirrors: false));
|
||||
|
||||
bool failed = false;
|
||||
try {
|
||||
await compileTestCaseToKernelProgram(
|
||||
sourceFileUri,
|
||||
target: target,
|
||||
packagesFileUri: packagesFileUri,
|
||||
);
|
||||
} catch (e) {
|
||||
failed = true;
|
||||
final message = e.toString();
|
||||
expect(message, contains('RecordUse'));
|
||||
expect(message, contains('package:'));
|
||||
}
|
||||
if (!failed) {
|
||||
fail('Should have failed with a diagnostic error');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
"identifier": {
|
||||
"name": "someStaticMethod2",
|
||||
"scope": "SomeClass",
|
||||
"uri": "basic.dart"
|
||||
"uri": "package:record_use_test/basic.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+1
-1
@@ -28,7 +28,7 @@
|
||||
"identifier": {
|
||||
"name": "generate",
|
||||
"scope": "OtherClass",
|
||||
"uri": "complex.dart"
|
||||
"uri": "package:record_use_test/complex.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+1
-1
@@ -29,7 +29,7 @@
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"name": "someStaticMethod2",
|
||||
"uri": "const_argument_instance.dart"
|
||||
"uri": "package:record_use_test/const_argument_instance.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+1
-1
@@ -34,7 +34,7 @@
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"name": "doSomething",
|
||||
"uri": "enum_const_arg.dart"
|
||||
"uri": "package:record_use_test/enum_const_arg.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"name": "_extension#0|callWithArgs",
|
||||
"uri": "extension.dart"
|
||||
"uri": "package:record_use_test/extension.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"name": "MyClass",
|
||||
"uri": "instance_class.dart"
|
||||
"uri": "package:record_use_test/instance_class.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
},
|
||||
+1
-1
@@ -76,7 +76,7 @@
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"name": "MyClass",
|
||||
"uri": "instance_complex.dart"
|
||||
"uri": "package:record_use_test/instance_complex.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
},
|
||||
+1
-1
@@ -30,7 +30,7 @@
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"name": "MyClass",
|
||||
"uri": "instance_duplicates.dart"
|
||||
"uri": "package:record_use_test/instance_duplicates.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
},
|
||||
+2
-13
@@ -1,16 +1,11 @@
|
||||
library #lib;
|
||||
import self as self;
|
||||
import "package:meta/meta.dart" as meta;
|
||||
import "dart:core" as core;
|
||||
|
||||
import "package:meta/meta.dart" show RecordUse;
|
||||
abstract class MyClass extends core::Object /*hasConstConstructor*/ {
|
||||
|
||||
@#C1
|
||||
class MyClass extends core::Object /*hasConstConstructor*/ {
|
||||
|
||||
[@vm.inferred-type.metadata=dart.core::_Smi (value: 42)]
|
||||
[@vm.unreachable.metadata=]
|
||||
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1]
|
||||
[@vm.unboxing-info.metadata=[!regcc]]
|
||||
final field core::int i;
|
||||
}
|
||||
|
||||
@@ -20,12 +15,6 @@ static method main() → void {
|
||||
}
|
||||
|
||||
[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
|
||||
@#C3
|
||||
static method doSomething() → void {
|
||||
core::print("a");
|
||||
}
|
||||
constants {
|
||||
#C1 = meta::RecordUse {}
|
||||
#C2 = 42
|
||||
#C3 = self::MyClass {i:#C2}
|
||||
}
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"name": "MyClass",
|
||||
"uri": "instance_not_annotation.dart"
|
||||
"uri": "package:record_use_test/instance_not_annotation.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
},
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
library #lib;
|
||||
import self as self;
|
||||
import "package:record_use_test/loading_units_multiple_helper_shared.dart" as loa;
|
||||
import "package:record_use_test/loading_units_multiple_helper.dart" as loa2;
|
||||
|
||||
import "package:record_use_test/loading_units_multiple_helper_shared.dart";
|
||||
import "package:record_use_test/loading_units_multiple_helper.dart" deferred as helper;
|
||||
|
||||
|
||||
[@vm.inferred-return-type.metadata=dart.async::_Future]
|
||||
static method main() → void async /* emittedValueType= void */ {
|
||||
loa::SomeClass::someStaticMethod(42);
|
||||
await LoadLibrary(helper);
|
||||
let final dynamic #t1 = CheckLibraryIsLoaded(helper) in loa2::invokeDeferred();
|
||||
}
|
||||
+1
-1
@@ -31,7 +31,7 @@
|
||||
"identifier": {
|
||||
"name": "someStaticMethod",
|
||||
"scope": "SomeClass",
|
||||
"uri": "loading_units_multiple_helper_shared.dart"
|
||||
"uri": "package:record_use_test/loading_units_multiple_helper_shared.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+2
-2
@@ -2,10 +2,10 @@ library #lib;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "package:meta/meta.dart" as meta;
|
||||
import "loading_units_simple_helper.dart" as loa;
|
||||
import "package:record_use_test/loading_units_simple_helper.dart" as loa;
|
||||
|
||||
import "package:meta/meta.dart" show RecordUse;
|
||||
import "org-dartlang-test:///testcases/transformations/record_use/loading_units_simple_helper.dart" deferred as helper;
|
||||
import "package:record_use_test/loading_units_simple_helper.dart" deferred as helper;
|
||||
|
||||
abstract class SomeClass extends core::Object {
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@
|
||||
"identifier": {
|
||||
"name": "someStaticMethod",
|
||||
"scope": "SomeClass",
|
||||
"uri": "loading_units_simple.dart"
|
||||
"uri": "package:record_use_test/loading_units_simple.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
@@ -43,7 +43,7 @@
|
||||
"identifier": {
|
||||
"name": "someStaticMethod",
|
||||
"scope": "SomeClass",
|
||||
"uri": "loading_units_simple_helper.dart"
|
||||
"uri": "package:record_use_test/loading_units_simple_helper.dart"
|
||||
},
|
||||
"loading_unit": "2"
|
||||
}
|
||||
+1
-1
@@ -49,7 +49,7 @@
|
||||
"identifier": {
|
||||
"name": "someStaticMethod",
|
||||
"scope": "SomeClass",
|
||||
"uri": "map_complex_keys.dart"
|
||||
"uri": "package:record_use_test/map_complex_keys.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+1
-1
@@ -80,7 +80,7 @@
|
||||
"identifier": {
|
||||
"name": "someStaticMethod",
|
||||
"scope": "SomeClass",
|
||||
"uri": "named_and_positional.dart"
|
||||
"uri": "package:record_use_test/named_and_positional.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+1
-1
@@ -72,7 +72,7 @@
|
||||
"identifier": {
|
||||
"name": "someStaticMethod",
|
||||
"scope": "SomeClass",
|
||||
"uri": "named_both.dart"
|
||||
"uri": "package:record_use_test/named_both.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+1
-1
@@ -35,7 +35,7 @@
|
||||
"identifier": {
|
||||
"name": "someStaticMethod",
|
||||
"scope": "SomeClass",
|
||||
"uri": "named_optional.dart"
|
||||
"uri": "package:record_use_test/named_optional.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+1
-1
@@ -35,7 +35,7 @@
|
||||
"identifier": {
|
||||
"name": "someStaticMethod",
|
||||
"scope": "SomeClass",
|
||||
"uri": "named_required.dart"
|
||||
"uri": "package:record_use_test/named_required.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"name": "Ext|foo",
|
||||
"uri": "named_with_function_arg.dart"
|
||||
"uri": "package:record_use_test/named_with_function_arg.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+2
-2
@@ -33,7 +33,7 @@
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"name": "MyClass",
|
||||
"uri": "nested.dart"
|
||||
"uri": "package:record_use_test/nested.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
},
|
||||
@@ -54,7 +54,7 @@
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"name": "MyOtherClass",
|
||||
"uri": "nested.dart"
|
||||
"uri": "package:record_use_test/nested.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
},
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
"definition": {
|
||||
"identifier": {
|
||||
"name": "Recorded",
|
||||
"uri": "nested_instance_constant.dart"
|
||||
"uri": "package:record_use_test/nested_instance_constant.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
},
|
||||
+1
-1
@@ -6,7 +6,7 @@ import "package:meta/meta.dart" as meta;
|
||||
import "package:meta/meta.dart" show RecordUse;
|
||||
|
||||
part partfile_helper.dart;
|
||||
abstract class SomeClass extends core::Object { // from org-dartlang-test:///testcases/transformations/record_use/partfile_helper.dart
|
||||
abstract class SomeClass extends core::Object { // from org-dartlang-test:///testcases/transformations/record_use/lib/partfile_helper.dart
|
||||
|
||||
[@vm.inferred-return-type.metadata=int]
|
||||
[@vm.unboxing-info.metadata=(i)->i]
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
"identifier": {
|
||||
"name": "someStaticMethod",
|
||||
"scope": "SomeClass",
|
||||
"uri": "partfile_main.dart"
|
||||
"uri": "package:record_use_test/partfile_main.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+1
-1
@@ -45,7 +45,7 @@
|
||||
"identifier": {
|
||||
"name": "someStaticMethod",
|
||||
"scope": "SomeClass",
|
||||
"uri": "positional_both.dart"
|
||||
"uri": "package:record_use_test/positional_both.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+1
-1
@@ -45,7 +45,7 @@
|
||||
"identifier": {
|
||||
"name": "someStaticMethod",
|
||||
"scope": "SomeClass",
|
||||
"uri": "positional_both_with_type_argument.dart"
|
||||
"uri": "package:record_use_test/positional_both_with_type_argument.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
+1
-1
@@ -35,7 +35,7 @@
|
||||
"identifier": {
|
||||
"name": "someStaticMethod",
|
||||
"scope": "SomeClass",
|
||||
"uri": "positional_optional.dart"
|
||||
"uri": "package:record_use_test/positional_optional.dart"
|
||||
},
|
||||
"loading_unit": "1"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user