dd4f983cee
Closes https://github.com/dart-lang/sdk/pull/63551 GitOrigin-RevId: a14cbbd88c605d193e2a1b744727765c45d38278 Change-Id: Ied6c2fc4ee3b5343c6ab8e38080542d667175f4d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/510160 Reviewed-by: Daco Harkes <dacoharkes@google.com> Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Jens Johansen <jensj@google.com>
222 lines
6.8 KiB
Dart
222 lines
6.8 KiB
Dart
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import 'package:expect/expect.dart';
|
|
import 'package:kernel/ast.dart';
|
|
import 'package:kernel/transformations/track_widget_constructor_locations.dart';
|
|
|
|
void main() {
|
|
final Uri developerUri = Uri.parse('dart:developer');
|
|
final Library developerLib = new Library(developerUri, fileUri: developerUri);
|
|
|
|
final Class hasCreationLocationClass = new Class(
|
|
name: '_HasCreationLocation',
|
|
isAbstract: true,
|
|
fileUri: developerUri,
|
|
);
|
|
developerLib.addClass(hasCreationLocationClass);
|
|
|
|
final Class creationLocationClass = new Class(
|
|
name: 'CreationLocation',
|
|
fileUri: developerUri,
|
|
);
|
|
final Constructor creationLocationConstructor = new Constructor(
|
|
new FunctionNode(
|
|
null,
|
|
namedParameters: [
|
|
new Variable('file', type: const DynamicType()),
|
|
new Variable('line', type: const DynamicType()),
|
|
new Variable('column', type: const DynamicType()),
|
|
new Variable('name', type: const DynamicType()),
|
|
],
|
|
),
|
|
name: new Name('_', developerLib),
|
|
fileUri: developerUri,
|
|
);
|
|
creationLocationClass.addConstructor(creationLocationConstructor);
|
|
developerLib.addClass(creationLocationClass);
|
|
|
|
final Uri coreUri = Uri.parse('dart:core');
|
|
final Library coreLib = new Library(coreUri, fileUri: coreUri);
|
|
final Class pragmaClass = new Class(name: 'pragma', fileUri: coreUri);
|
|
coreLib.addClass(pragmaClass);
|
|
final Field pragmaNameField = new Field.immutable(
|
|
new Name('name'),
|
|
fileUri: coreUri,
|
|
);
|
|
pragmaClass.addField(pragmaNameField);
|
|
|
|
final Uri testUri = Uri.parse('package:test/test.dart');
|
|
final Library testLib = new Library(testUri, fileUri: testUri);
|
|
|
|
final Class myWidgetClass = new Class(name: 'MyWidget', fileUri: testUri);
|
|
myWidgetClass.addAnnotation(
|
|
new ConstantExpression(
|
|
new InstanceConstant(
|
|
pragmaClass.reference,
|
|
<DartType>[],
|
|
<Reference, Constant>{
|
|
pragmaNameField.fieldReference: new StringConstant(
|
|
'track-creation-locations',
|
|
),
|
|
},
|
|
),
|
|
),
|
|
);
|
|
myWidgetClass.addConstructor(
|
|
new Constructor(
|
|
new FunctionNode(new Block([])),
|
|
name: new Name(''),
|
|
fileUri: testUri,
|
|
),
|
|
);
|
|
testLib.addClass(myWidgetClass);
|
|
|
|
const int fileOffset = 100;
|
|
final Procedure mainProcedure = new Procedure(
|
|
new Name('main'),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(
|
|
new Block([
|
|
new ExpressionStatement(
|
|
new ConstructorInvocation(
|
|
myWidgetClass.constructors.first,
|
|
new Arguments([]),
|
|
)..fileOffset = fileOffset,
|
|
),
|
|
]),
|
|
),
|
|
isStatic: true,
|
|
fileUri: testUri,
|
|
);
|
|
testLib.addProcedure(mainProcedure);
|
|
|
|
final Extension extension =
|
|
new Extension(name: 'MyExtension', fileUri: testUri)
|
|
..fileOffset = fileOffset
|
|
..onType = const DynamicType();
|
|
testLib.addExtension(extension);
|
|
|
|
final Procedure factoryMethod =
|
|
new Procedure(
|
|
new Name('myFactory'),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(
|
|
new Block([
|
|
new ReturnStatement(
|
|
new ConstructorInvocation(
|
|
myWidgetClass.constructors.first,
|
|
new Arguments([]),
|
|
)..fileOffset = fileOffset,
|
|
),
|
|
]),
|
|
returnType: new InterfaceType(
|
|
myWidgetClass,
|
|
Nullability.nonNullable,
|
|
),
|
|
),
|
|
isStatic: true,
|
|
fileUri: testUri,
|
|
)
|
|
..fileOffset = fileOffset
|
|
..isExtensionMember = true;
|
|
factoryMethod.addAnnotation(
|
|
new ConstantExpression(
|
|
new InstanceConstant(
|
|
pragmaClass.reference,
|
|
<DartType>[],
|
|
<Reference, Constant>{
|
|
pragmaNameField.fieldReference: new StringConstant(
|
|
'track-creation-locations',
|
|
),
|
|
},
|
|
),
|
|
),
|
|
);
|
|
testLib.addProcedure(factoryMethod);
|
|
|
|
extension.memberDescriptors.add(
|
|
new ExtensionMemberDescriptor(
|
|
name: new Name('myFactory'),
|
|
kind: ExtensionMemberKind.Method,
|
|
memberReference: factoryMethod.reference,
|
|
tearOffReference: null,
|
|
),
|
|
);
|
|
|
|
final Block newBody = new Block([
|
|
new ExpressionStatement(
|
|
new ConstructorInvocation(
|
|
myWidgetClass.constructors.first,
|
|
new Arguments([]),
|
|
)..fileOffset = fileOffset,
|
|
),
|
|
new ExpressionStatement(
|
|
new StaticInvocation(factoryMethod, new Arguments([]))
|
|
..fileOffset = fileOffset,
|
|
),
|
|
]);
|
|
newBody.parent = mainProcedure.function;
|
|
mainProcedure.function.body = newBody;
|
|
|
|
final WidgetCreatorTracker tracker = new WidgetCreatorTracker();
|
|
tracker.transform([testLib], [developerLib, testLib], null);
|
|
|
|
// Verification
|
|
Expect.isTrue(
|
|
myWidgetClass.implementedTypes.any(
|
|
(s) => s.classNode == hasCreationLocationClass,
|
|
),
|
|
);
|
|
Expect.isTrue(myWidgetClass.fields.any((f) => f.name.text == '_location'));
|
|
|
|
final Constructor constructor = myWidgetClass.constructors.first;
|
|
const String creationLocationPrefix = r'$creationLocation';
|
|
Expect.isTrue(
|
|
constructor.function.namedParameters.any(
|
|
(p) => p.name!.startsWith(creationLocationPrefix),
|
|
),
|
|
);
|
|
|
|
final Block body = mainProcedure.function.body as Block;
|
|
final ExpressionStatement stmt = body.statements.first as ExpressionStatement;
|
|
final ConstructorInvocation invocation =
|
|
stmt.expression as ConstructorInvocation;
|
|
Expect.isTrue(
|
|
invocation.arguments.named.any(
|
|
(n) => n.name.startsWith(creationLocationPrefix),
|
|
),
|
|
);
|
|
|
|
final NamedExpression namedArg = invocation.arguments.named.firstWhere(
|
|
(n) => n.name.startsWith(creationLocationPrefix),
|
|
);
|
|
|
|
Expect.isTrue(namedArg.value is ConstructorInvocation);
|
|
final ConstructorInvocation locInvocation =
|
|
namedArg.value as ConstructorInvocation;
|
|
Expect.equals(creationLocationClass, locInvocation.target.enclosingClass);
|
|
|
|
Expect.isTrue(
|
|
factoryMethod.function.namedParameters.any(
|
|
(p) => p.name!.startsWith(creationLocationPrefix),
|
|
),
|
|
);
|
|
|
|
final ExpressionStatement stmt2 = body.statements[1] as ExpressionStatement;
|
|
final StaticInvocation staticInv = stmt2.expression as StaticInvocation;
|
|
Expect.isTrue(
|
|
staticInv.arguments.named.any(
|
|
(n) => n.name.startsWith(creationLocationPrefix),
|
|
),
|
|
);
|
|
final NamedExpression namedArg2 = staticInv.arguments.named.firstWhere(
|
|
(n) => n.name.startsWith(creationLocationPrefix),
|
|
);
|
|
Expect.isTrue(namedArg2.value is ConstructorInvocation);
|
|
final ConstructorInvocation locInvocation2 =
|
|
namedArg2.value as ConstructorInvocation;
|
|
Expect.equals(creationLocationClass, locInvocation2.target.enclosingClass);
|
|
}
|