Add annotation-based creation location tracking

Closes https://github.com/dart-lang/sdk/pull/63011

GitOrigin-RevId: 926534c2f5d3cd1e9629cf25f6b028ab34c00e64
Change-Id: I88460a060faabeae8f611e88f23b2eb5fc99f21a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/491702
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
This commit is contained in:
Kilian Schulte
2026-04-28 03:06:03 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent efff5f70c1
commit 9e8fdef17c
58 changed files with 1851 additions and 205 deletions
+13 -6
View File
@@ -109,9 +109,16 @@ final ArgParser _argParser = ArgParser(allowTrailingOptions: true)
help: 'Print this help message.',
)
..addFlag(
'track-widget-creation',
help: 'Run a kernel transformer to track creation locations for widgets.',
'track-creation-locations',
help:
'Run a kernel transformer to track creation locations for'
' classes annotated with @pragma(\'track-creation-locations\').',
defaultsTo: false,
aliases: [
// TODO(http://dartbug.com/63225): Remove this once flutter is migrated
// to the new flag.
'track-widget-creation',
],
)
..addOption(
'invocation-modes',
@@ -186,7 +193,7 @@ Future<int> runCompilerWithCommandLineArguments(List<String> arguments) async {
final String? validateDynamicInterface = options['validate'];
final String messageVerbosity = options['verbosity'];
final String cfeInvocationModes = options['invocation-modes'];
final bool trackWidgetCreation = options['track-widget-creation'];
final bool trackCreationLocations = options['track-creation-locations'];
final List<String>? bytecodeGeneratorOptions = options['bytecode-options'];
final String libraryUrisPrefix = options['prefix-library-uris']!;
@@ -205,7 +212,7 @@ Future<int> runCompilerWithCommandLineArguments(List<String> arguments) async {
fileSystemRoots: fileSystemRoots,
messageVerbosity: messageVerbosity,
cfeInvocationModes: cfeInvocationModes,
trackWidgetCreation: trackWidgetCreation,
trackCreationLocations: trackCreationLocations,
bytecodeGeneratorOptions: bytecodeGeneratorOptions,
depfile: depfile,
depfileTarget: depfileTarget,
@@ -231,7 +238,7 @@ Future<int> runCompilerWithOptions({
String messageVerbosity = Verbosity.defaultValue,
void Function(String) printMessage = print,
String cfeInvocationModes = '',
bool trackWidgetCreation = false,
bool trackCreationLocations = false,
List<String>? bytecodeGeneratorOptions,
String? depfile,
String? depfileTarget,
@@ -287,7 +294,7 @@ Future<int> runCompilerWithOptions({
..verbosity = verbosity
..target = createFrontEndTarget(
targetName,
trackWidgetCreation: trackWidgetCreation,
trackCreationLocations: trackCreationLocations,
supportMirrors: false,
isClosureContextLoweringEnabled:
bytecodeOptions.isClosureContextLoweringEnabled,
@@ -270,7 +270,7 @@ Future<CompilerResult> _compile(
onWarning: print,
);
var trackWidgetCreation = argResults.flag('track-widget-creation');
var trackCreationLocations = argResults.flag('track-creation-locations');
var oldCompilerState = compilerState;
var recordUsedInputs = argResults.option('used-inputs-file') != null;
var additionalDills = summaryModules.keys.toList();
@@ -290,7 +290,9 @@ Future<CompilerResult> _compile(
packageFile != null ? sourcePathToUri(packageFile) : null,
sourcePathToUri(librarySpecPath),
additionalDills,
DevCompilerTarget(TargetFlags(trackWidgetCreation: trackWidgetCreation)),
DevCompilerTarget(
TargetFlags(trackCreationLocations: trackCreationLocations),
),
fileSystem: fileSystem,
explicitExperimentalFlags: explicitExperimentalFlags,
environmentDefines: declaredVariables,
@@ -318,7 +320,7 @@ Future<CompilerResult> _compile(
compilerState = await fe.initializeIncrementalCompiler(
oldCompilerState,
{
'trackWidgetCreation=$trackWidgetCreation',
'trackCreationLocations=$trackCreationLocations',
'multiRootScheme=${fileSystem.markerScheme}',
'multiRootRoots=${fileSystem.roots}',
},
@@ -330,7 +332,9 @@ Future<CompilerResult> _compile(
sourcePathToUri(librarySpecPath),
additionalDills,
inputDigests,
DevCompilerTarget(TargetFlags(trackWidgetCreation: trackWidgetCreation)),
DevCompilerTarget(
TargetFlags(trackCreationLocations: trackCreationLocations),
),
fileSystem: fileSystem,
explicitExperimentalFlags: explicitExperimentalFlags,
environmentDefines: declaredVariables,
@@ -368,10 +368,17 @@ class Options {
hide: true,
)
..addFlag(
'track-widget-creation',
help: 'Enable inspecting of Flutter widgets.',
'track-creation-locations',
help:
'Run a kernel transformer to track creation locations for'
' classes annotated with @pragma(\'track-creation-locations\').',
defaultsTo: false,
hide: true,
aliases: [
// TODO(http://dartbug.com/63225): Remove this once flutter is
// migrated to the new flag.
'track-widget-creation',
],
)
// TODO(jmesserly): add verbose help to show hidden options
..addOption(
@@ -201,7 +201,7 @@ class ExpressionCompilerWorker {
environmentDefines: environmentDefines,
explicitExperimentalFlags: explicitExperimentalFlags,
sdkRoot: _argToUri(parsedArgs.option('sdk-root')),
trackWidgetCreation: parsedArgs.flag('track-widget-creation'),
trackCreationLocations: parsedArgs.flag('track-creation-locations'),
moduleFormat: moduleFormat,
canaryFeatures: parsedArgs.flag('canary'),
enableAsserts: parsedArgs.flag('enable-asserts'),
@@ -227,7 +227,7 @@ class ExpressionCompilerWorker {
Map<String, String>? environmentDefines,
Map<ExperimentalFlag, bool> explicitExperimentalFlags = const {},
Uri? sdkRoot,
bool trackWidgetCreation = false,
bool trackCreationLocations = false,
ModuleFormat moduleFormat = ModuleFormat.amd,
bool canaryFeatures = false,
bool enableAsserts = true,
@@ -244,7 +244,7 @@ class ExpressionCompilerWorker {
..packagesFileUri = packagesFile
..librariesSpecificationUri = librariesSpecificationUri
..target = DevCompilerTarget(
TargetFlags(trackWidgetCreation: trackWidgetCreation),
TargetFlags(trackCreationLocations: trackCreationLocations),
)
..fileSystem = fileSystem
..omitPlatform = true
@@ -889,7 +889,15 @@ final argParser = ArgParser()
..addOption('asset-server-address')
..addOption('asset-server-port')
..addOption('module-format', defaultsTo: 'amd')
..addFlag('track-widget-creation', defaultsTo: false)
..addFlag(
'track-creation-locations',
defaultsTo: false,
aliases: [
// TODO(http://dartbug.com/63225): Remove this once flutter is
// migrated to the new flag.
'track-widget-creation',
],
)
..addFlag('sound-null-safety', negatable: true, defaultsTo: true)
..addFlag('canary', negatable: true, defaultsTo: false)
// Disable asserts in compiled code by default, which is different
+28 -3
View File
@@ -240,6 +240,28 @@ class DevCompilerTarget extends Target {
}
}
@override
void performOutlineTransformations(
Component component, {
List<Library>? libraries,
ChangedStructureNotifier? changedStructureNotifier,
}) {
super.performOutlineTransformations(
component,
libraries: libraries,
changedStructureNotifier: changedStructureNotifier,
);
// In addition to [performPreConstantEvaluationTransformations] this makes
// sure summaries are also transformed.
if (flags.trackCreationLocations) {
(_widgetTracker ??= WidgetCreatorTracker()).transform(
libraries ?? component.libraries,
component.libraries,
changedStructureNotifier,
);
}
}
@override
void performPreConstantEvaluationTransformations(
Component component,
@@ -249,9 +271,12 @@ class DevCompilerTarget extends Target {
void Function(String msg)? logger,
ChangedStructureNotifier? changedStructureNotifier,
}) {
if (flags.trackWidgetCreation) {
_widgetTracker ??= WidgetCreatorTracker();
_widgetTracker!.transform(component, libraries, changedStructureNotifier);
if (flags.trackCreationLocations) {
(_widgetTracker ??= WidgetCreatorTracker()).transform(
libraries,
component.libraries,
changedStructureNotifier,
);
}
}
+2 -2
View File
@@ -77,7 +77,7 @@ Future<MemoryComponentResult> componentFromMemory(
null,
sourcePathToUri(defaultLibrarySpecPath),
[],
DevCompilerTarget(TargetFlags(trackWidgetCreation: false)),
DevCompilerTarget(TargetFlags(trackCreationLocations: false)),
fileSystem: fe.HybridFileSystem(memoryFileSystem),
environmentDefines: {},
explicitExperimentalFlags: explicitExperimentalFlags,
@@ -134,7 +134,7 @@ Future<MemoryComponentResult> incrementalComponentFromMemory(
sourcePathToUri(defaultLibrarySpecPath),
[],
inputDigests,
DevCompilerTarget(TargetFlags(trackWidgetCreation: false)),
DevCompilerTarget(TargetFlags(trackCreationLocations: false)),
fileSystem: fe.HybridFileSystem(memoryFileSystem),
environmentDefines: {},
explicitExperimentalFlags: explicitExperimentalFlags,
@@ -443,7 +443,12 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
}
// Coverage-ignore(suite): Not run.
else if (componentWithDill != null) {
context.options.target.performOutlineTransformations(componentWithDill);
context.options.target.performOutlineTransformations(
componentWithDill,
libraries: currentKernelTarget.loader.libraries,
changedStructureNotifier:
currentKernelTarget.changedStructureNotifier,
);
}
_benchmarker
@@ -190,14 +190,14 @@ Future<void> main(List<String> arguments) async {
CompilerContext setupCompilerContext(
String targetString,
bool widgetTransformation,
bool trackCreationLocations,
Uri platformUri,
Uri mainUri,
) {
CompilerOptions options = getOptions();
TargetFlags targetFlags = new TargetFlags(
trackWidgetCreation: widgetTransformation,
trackCreationLocations: trackCreationLocations,
);
Target target;
switch (targetString) {
@@ -56,8 +56,8 @@ Future<void> main(List<String> arguments) async {
for (String s in arg.substring("--invalidate=".length).split(",")) {
settings.invalidate.add(Uri.base.resolve(s));
}
} else if (arg.startsWith("--widgetTransformation")) {
settings.widgetTransformation = true;
} else if (arg.startsWith("--track-creation-locations")) {
settings.trackCreationLocations = true;
} else if (arg.startsWith("--target=vm")) {
settings.targetString = "vm";
} else if (arg.startsWith("--target=flutter")) {
@@ -68,7 +68,7 @@ class TestMinimizerSettings {
bool invalidateAllAtOnce = false;
bool experimentalInvalidation = false;
bool serialize = false;
bool widgetTransformation = false;
bool trackCreationLocations = false;
final List<Uri> invalidate = [];
String targetString = "vm";
bool noTryToDeleteEmptyFilesUpFront = false;
@@ -103,7 +103,7 @@ class TestMinimizerSettings {
'initialOnlyOutline': initialOnlyOutline,
'experimentalInvalidation': experimentalInvalidation,
'serialize': serialize,
'widgetTransformation': widgetTransformation,
'trackCreationLocations': trackCreationLocations,
'invalidate': invalidate.map((uri) => uri.toString()).toList(),
'targetString': targetString,
'noTryToDeleteEmptyFilesUpFront': noTryToDeleteEmptyFilesUpFront,
@@ -134,7 +134,7 @@ class TestMinimizerSettings {
initialOnlyOutline = json["initialOnlyOutline"];
experimentalInvalidation = json["experimentalInvalidation"];
serialize = json["serialize"];
widgetTransformation = json["widgetTransformation"];
trackCreationLocations = json["trackCreationLocations"];
invalidate.clear();
invalidate.addAll(
(json["invalidate"] as List).map((uriString) => Uri.parse(uriString)),
@@ -802,8 +802,8 @@ class TestMinimizer {
# Reproduce a crash.
type: newworld""");
if (_settings.widgetTransformation) {
print("trackWidgetCreation: true");
if (_settings.trackCreationLocations) {
print("trackCreationLocations: true");
print("target: dartdevc # needed for widget creation to be run");
}
print("""
@@ -2259,7 +2259,7 @@ worlds:
}
TargetFlags targetFlags = new TargetFlags(
trackWidgetCreation: _settings.widgetTransformation,
trackCreationLocations: _settings.trackCreationLocations,
);
Target target;
switch (_settings.targetString) {
@@ -279,7 +279,9 @@ bool isEqualBitForBit(List<int> a, List<int> b) {
}
CompilerOptions getOptions(Uri sdkRoot) {
Target target = new FlutterTarget(new TargetFlags(trackWidgetCreation: true));
Target target = new FlutterTarget(
new TargetFlags(trackCreationLocations: true),
);
CompilerOptions options = new CompilerOptions()
..sdkRoot = sdkRoot
..target = target
+7 -9
View File
@@ -148,8 +148,8 @@ class NewWorldTestProperties {
defaultValue: false,
);
static const Property<bool> trackWidgetCreation = Property.optional(
'trackWidgetCreation',
static const Property<bool> trackCreationLocations = Property.optional(
'trackCreationLocations',
BoolValue(),
defaultValue: false,
);
@@ -724,10 +724,8 @@ class RunCompilations extends Step<TestData, TestData, Context> {
forceLateLoweringForTesting: NewWorldTestProperties
.forceLateLoweringForTesting
.read(map, keys),
trackWidgetCreation: NewWorldTestProperties.trackWidgetCreation.read(
map,
keys,
),
trackCreationLocations: NewWorldTestProperties.trackCreationLocations
.read(map, keys),
incrementalSerialization: NewWorldTestProperties
.incrementalSerialization
.read(map, keys),
@@ -1260,7 +1258,7 @@ class NewWorldTest {
final Map<String, Map<String, String>>? modules;
final bool omitPlatform;
final bool forceLateLoweringForTesting;
final bool trackWidgetCreation;
final bool trackCreationLocations;
final bool incrementalSerialization;
final String? targetName;
@@ -1279,7 +1277,7 @@ class NewWorldTest {
required this.modules,
required this.omitPlatform,
required this.forceLateLoweringForTesting,
required this.trackWidgetCreation,
required this.trackCreationLocations,
required this.incrementalSerialization,
required this.targetName,
});
@@ -1296,7 +1294,7 @@ class NewWorldTest {
forceLateLoweringsForTesting: forceLateLoweringForTesting
? LateLowering.all
: null,
trackWidgetCreation: trackWidgetCreation,
trackCreationLocations: trackCreationLocations,
);
Target target = new VmTarget(targetFlags);
if (targetName != null) {
@@ -83,7 +83,7 @@ Future<Component> processUri(
Component? fullComponent,
Uri packageUri,
) async {
TargetFlags targetFlags = new TargetFlags(trackWidgetCreation: false);
TargetFlags targetFlags = new TargetFlags(trackCreationLocations: false);
Target? target = new Dart2jsTarget("dart2js", targetFlags);
Uri sdkSummary = Uri.base.resolve("out/ReleaseX64/dart2js_outline.dill");
Stopwatch stopwatch = new Stopwatch()..start();
@@ -1452,6 +1452,7 @@ happen
happened
happens
hard
hardcoded
hare
has
hash
@@ -5,7 +5,7 @@
# Reproduce a crash.
type: newworld
trackWidgetCreation: true
trackCreationLocations: true
target: dartdevc # basically needed for widget creation to be run
worlds:
- entry: app/main.dart
@@ -5,7 +5,7 @@
# Reproduce a crash.
type: newworld
trackWidgetCreation: true
trackCreationLocations: true
target: dartdevc # basically needed for widget creation to be run
worlds:
- entry: app/main.dart
@@ -5,7 +5,7 @@
# Reproduce a crash.
type: newworld
trackWidgetCreation: true
trackCreationLocations: true
target: dartdevc # basically needed for widget creation to be run
worlds:
- entry: app/main.dart
@@ -5,7 +5,7 @@
# Reproduce a crash.
type: newworld
trackWidgetCreation: true
trackCreationLocations: true
target: dartdevc # basically needed for widget creation to be run
worlds:
- entry: app/main.dart
@@ -7,7 +7,7 @@
type: newworld
target: dartdevc
trackWidgetCreation: true
trackCreationLocations: true
worlds:
- entry: main.dart
skipClassHierarchyTest: true
@@ -4,7 +4,7 @@
type: newworld
target: dartdevc
trackWidgetCreation: true
trackCreationLocations: true
worlds:
- entry: main.dart
sources:
@@ -4,7 +4,7 @@
type: newworld
target: dartdevc
trackWidgetCreation: true
trackCreationLocations: true
worlds:
- entry: main.dart
sources:
@@ -4,7 +4,7 @@
type: newworld
target: dartdevc
trackWidgetCreation: true
trackCreationLocations: true
worlds:
- entry: main.dart
sources:
@@ -4,7 +4,7 @@
type: newworld
target: dartdevc
trackWidgetCreation: true
trackCreationLocations: true
worlds:
- entry: main.dart
sources:
@@ -4,7 +4,7 @@
type: newworld
target: dartdevc
trackWidgetCreation: true
trackCreationLocations: true
worlds:
- entry: main.dart
sources:
@@ -4,7 +4,7 @@
type: newworld
target: dartdevc
trackWidgetCreation: true
trackCreationLocations: true
worlds:
- entry: main.dart
sources:
@@ -4,7 +4,7 @@
type: newworld
target: dartdevc
trackWidgetCreation: true
trackCreationLocations: true
worlds:
- entry: main.dart
sources:
@@ -0,0 +1,58 @@
# 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.
type: newworld
target: dartdevc
trackCreationLocations: true
worlds:
- entry: main.dart
sources:
widget.dart: |
@pragma('track-creation-locations')
abstract class Widget {
const Widget();
}
class StandardWidget extends Widget {
const StandardWidget();
const StandardWidget.named(String name);
}
main.dart: |
import 'widget.dart';
class MyWidget extends Widget {
const MyWidget({this.child});
final Widget? child;
}
void main() {
var w1 = MyWidget();
var w2 = StandardWidget.named('w2');
const w3 = MyWidget();
}
expectedLibraryCount: 2
- entry: main.dart
worldType: updated
expectInitializeFromDill: false
invalidate:
- main.dart
sources:
main.dart: |
import 'widget.dart';
class MyWidget extends Widget {
const MyWidget({this.child});
final Widget? child;
}
void main() {
var w1 = MyWidget(child: StandardWidget());
const w2 = MyWidget(child: StandardWidget.named('w2'));
const w3 = MyWidget(child: w2);
}
expectedLibraryCount: 2
advancedInvalidation: bodiesOnly
@@ -0,0 +1,80 @@
main = main::main;
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///widget.dart";
class MyWidget extends wid::Widget /*hasConstConstructor*/ {
final field wid::Widget? child;
const constructor •({initializing-formal wid::Widget? child = #C1, dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C1}) → main::MyWidget
: main::MyWidget::child = child, super wid::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static synthetic method _#new#tearOff({wid::Widget? child = #C1}) → main::MyWidget
return new main::MyWidget::•(child: child, $creationLocationd_0dea112b090073317d4: #C6);
}
static method main() → void {
main::MyWidget w1 = new main::MyWidget::•($creationLocationd_0dea112b090073317d4: #C9);
wid::StandardWidget w2 = new wid::StandardWidget::named("w2", $creationLocationd_0dea112b090073317d4: #C13);
const main::MyWidget w3 = #C16;
}
}
library from "org-dartlang-test:///widget.dart" as wid {
@#C18
abstract class Widget extends dart.core::Object implements dart.developer::_HasCreationLocation /*hasConstConstructor*/ {
final field dart.developer::CreationLocation? dart.developer::_location;
const constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C1}) → wid::Widget
: super dart.core::Object::•(), wid::Widget::_location = $creationLocationd_0dea112b090073317d4
;
}
class StandardWidget extends wid::Widget /*hasConstConstructor*/ {
const constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C1}) → wid::StandardWidget
: super wid::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
const constructor named(dart.core::String name, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C1}) → wid::StandardWidget
: super wid::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static synthetic method _#new#tearOff() → wid::StandardWidget
return new wid::StandardWidget::•($creationLocationd_0dea112b090073317d4: #C21);
static synthetic method _#named#tearOff(dart.core::String name) → wid::StandardWidget
return new wid::StandardWidget::named(name, $creationLocationd_0dea112b090073317d4: #C23);
}
}
constants {
#C1 = null
#C2 = "org-dartlang-test:///main.dart"
#C3 = 4.0
#C4 = 9.0
#C5 = "MyWidget"
#C6 = dart.developer::CreationLocation {file:#C2, line:#C3, column:#C4, name:#C5}
#C7 = 10.0
#C8 = 12.0
#C9 = dart.developer::CreationLocation {file:#C2, line:#C7, column:#C8, name:#C5}
#C10 = 11.0
#C11 = 27.0
#C12 = "StandardWidget"
#C13 = dart.developer::CreationLocation {file:#C2, line:#C10, column:#C11, name:#C12}
#C14 = 14.0
#C15 = dart.developer::CreationLocation {file:#C2, line:#C8, column:#C14, name:#C5}
#C16 = main::MyWidget {child:#C1, _location:#C15}
#C17 = "track-creation-locations"
#C18 = dart.core::pragma {name:#C17, options:#C1}
#C19 = "org-dartlang-test:///widget.dart"
#C20 = 7.0
#C21 = dart.developer::CreationLocation {file:#C19, line:#C20, column:#C4, name:#C12}
#C22 = 8.0
#C23 = dart.developer::CreationLocation {file:#C19, line:#C22, column:#C4, name:#C12}
}
Constructor coverage from constants:
org-dartlang-test:///main.dart:
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
- MyWidget. (from org-dartlang-test:///main.dart:4:9)
- Widget. (from org-dartlang-test:///widget.dart:3:9)
org-dartlang-test:///widget.dart:
- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
@@ -0,0 +1,89 @@
main = main::main;
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///widget.dart";
class MyWidget extends wid::Widget /*hasConstConstructor*/ {
final field wid::Widget? child;
const constructor •({initializing-formal wid::Widget? child = #C1, dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C1}) → main::MyWidget
: main::MyWidget::child = child, super wid::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static synthetic method _#new#tearOff({wid::Widget? child = #C1}) → main::MyWidget
return new main::MyWidget::•(child: child, $creationLocationd_0dea112b090073317d4: #C6);
synthetic no-such-method-forwarder set dart.developer::_location(dart.developer::CreationLocation? value) → void
return throw{for-error-handling} dart.core::NoSuchMethodError::withInvocation(this, new dart.core::_Invocation::setter(#C7, value));
}
static method main() → void {
main::MyWidget w1 = new main::MyWidget::•(child: new wid::StandardWidget::•($creationLocationd_0dea112b090073317d4: #C11), $creationLocationd_0dea112b090073317d4: #C13);
const main::MyWidget w2 = #C20;
const main::MyWidget w3 = #C22;
}
}
library from "org-dartlang-test:///widget.dart" as wid {
@#C24
abstract class Widget extends dart.core::Object implements dart.developer::_HasCreationLocation /*hasConstConstructor*/ {
final field dart.developer::CreationLocation? dart.developer::_location;
const constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C1}) → wid::Widget
: super dart.core::Object::•(), wid::Widget::_location = $creationLocationd_0dea112b090073317d4
;
}
class StandardWidget extends wid::Widget /*hasConstConstructor*/ {
const constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C1}) → wid::StandardWidget
: super wid::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
const constructor named(dart.core::String name, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C1}) → wid::StandardWidget
: super wid::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static synthetic method _#new#tearOff() → wid::StandardWidget
return new wid::StandardWidget::•($creationLocationd_0dea112b090073317d4: #C27);
static synthetic method _#named#tearOff(dart.core::String name) → wid::StandardWidget
return new wid::StandardWidget::named(name, $creationLocationd_0dea112b090073317d4: #C29);
}
}
constants {
#C1 = null
#C2 = "org-dartlang-test:///main.dart"
#C3 = 4.0
#C4 = 9.0
#C5 = "MyWidget"
#C6 = dart.developer::CreationLocation {file:#C2, line:#C3, column:#C4, name:#C5}
#C7 = #org-dartlang-test:///main.dart::_location=
#C8 = 10.0
#C9 = 28.0
#C10 = "StandardWidget"
#C11 = dart.developer::CreationLocation {file:#C2, line:#C8, column:#C9, name:#C10}
#C12 = 12.0
#C13 = dart.developer::CreationLocation {file:#C2, line:#C8, column:#C12, name:#C5}
#C14 = 11.0
#C15 = 45.0
#C16 = dart.developer::CreationLocation {file:#C2, line:#C14, column:#C15, name:#C10}
#C17 = wid::StandardWidget {_location:#C16}
#C18 = 14.0
#C19 = dart.developer::CreationLocation {file:#C2, line:#C14, column:#C18, name:#C5}
#C20 = main::MyWidget {child:#C17, _location:#C19}
#C21 = dart.developer::CreationLocation {file:#C2, line:#C12, column:#C18, name:#C5}
#C22 = main::MyWidget {child:#C20, _location:#C21}
#C23 = "track-creation-locations"
#C24 = dart.core::pragma {name:#C23, options:#C1}
#C25 = "org-dartlang-test:///widget.dart"
#C26 = 7.0
#C27 = dart.developer::CreationLocation {file:#C25, line:#C26, column:#C4, name:#C10}
#C28 = 8.0
#C29 = dart.developer::CreationLocation {file:#C25, line:#C28, column:#C4, name:#C10}
}
Constructor coverage from constants:
org-dartlang-test:///widget.dart:
- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
org-dartlang-test:///main.dart:
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
- StandardWidget.named (from org-dartlang-test:///widget.dart:8:9)
- Widget. (from org-dartlang-test:///widget.dart:3:9)
- MyWidget. (from org-dartlang-test:///main.dart:4:9)
@@ -0,0 +1,42 @@
# 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.
type: newworld
target: dartdevc
trackCreationLocations: true
worlds:
- entry: main.dart
sources:
main.dart: |
import 'package:flutter/src/widgets/framework.dart';
class Foo extends Widget {}
flutter/lib/src/widgets/framework.dart: |
abstract class Bar {}
@pragma('track-creation-locations')
abstract class Widget extends Bar {}
.dart_tool/package_config.json: |
{
"configVersion": 2,
"packages": [
{
"name": "flutter",
"rootUri": "../flutter/lib"
}
]
}
expectedLibraryCount: 2
- entry: main.dart
worldType: updated
invalidate:
- main.dart
expectInitializeFromDill: false
sources:
main.dart: |
import 'package:flutter/src/widgets/framework.dart';
class Foo extends Widget {}
expectedLibraryCount: 2
advancedInvalidation: bodiesOnly
@@ -0,0 +1,49 @@
main = <No Member>;
library from "package:flutter/src/widgets/framework.dart" as fra {
abstract class Bar extends dart.core::Object {
synthetic constructor •() → fra::Bar
: super dart.core::Object::•()
;
}
@#C3
abstract class Widget extends fra::Bar implements dart.developer::_HasCreationLocation {
final field dart.developer::CreationLocation? dart.developer::_location;
synthetic constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → fra::Widget
: super fra::Bar::•(), fra::Widget::_location = $creationLocationd_0dea112b090073317d4
;
}
}
library from "org-dartlang-test:///main.dart" as main {
import "package:flutter/src/widgets/framework.dart";
class Foo extends fra::Widget {
synthetic constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → main::Foo
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static synthetic method _#new#tearOff() → main::Foo
return new main::Foo::•($creationLocationd_0dea112b090073317d4: #C8);
}
}
constants {
#C1 = "track-creation-locations"
#C2 = null
#C3 = dart.core::pragma {name:#C1, options:#C2}
#C4 = "org-dartlang-test:///main.dart"
#C5 = 3.0
#C6 = 7.0
#C7 = "Foo"
#C8 = dart.developer::CreationLocation {file:#C4, line:#C5, column:#C6, name:#C7}
}
Constructor coverage from constants:
org-dartlang-test:///main.dart:
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
org-dartlang-test:///flutter/lib/src/widgets/framework.dart:
- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
@@ -0,0 +1,52 @@
main = <No Member>;
library from "package:flutter/src/widgets/framework.dart" as fra {
abstract class Bar extends dart.core::Object {
synthetic constructor •() → fra::Bar
: super dart.core::Object::•()
;
}
@#C3
abstract class Widget extends fra::Bar implements dart.developer::_HasCreationLocation {
final field dart.developer::CreationLocation? dart.developer::_location;
synthetic constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → fra::Widget
: super fra::Bar::•(), fra::Widget::_location = $creationLocationd_0dea112b090073317d4
;
}
}
library from "org-dartlang-test:///main.dart" as main {
import "package:flutter/src/widgets/framework.dart";
class Foo extends fra::Widget {
synthetic constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → main::Foo
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static synthetic method _#new#tearOff() → main::Foo
return new main::Foo::•($creationLocationd_0dea112b090073317d4: #C8);
synthetic no-such-method-forwarder set dart.developer::_location(dart.developer::CreationLocation? value) → void
return throw{for-error-handling} dart.core::NoSuchMethodError::withInvocation(this, new dart.core::_Invocation::setter(#C9, value));
}
}
constants {
#C1 = "track-creation-locations"
#C2 = null
#C3 = dart.core::pragma {name:#C1, options:#C2}
#C4 = "org-dartlang-test:///main.dart"
#C5 = 3.0
#C6 = 7.0
#C7 = "Foo"
#C8 = dart.developer::CreationLocation {file:#C4, line:#C5, column:#C6, name:#C7}
#C9 = #org-dartlang-test:///main.dart::_location=
}
Constructor coverage from constants:
org-dartlang-test:///flutter/lib/src/widgets/framework.dart:
- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
org-dartlang-test:///main.dart:
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
@@ -0,0 +1,100 @@
# 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.
type: newworld
target: dartdevc
trackCreationLocations: true
worlds:
- entry: main.dart
sources:
main.dart: |
import 'foo.dart';
Foo newFoo = new Foo();
Bar newBar = new Bar();
Bar constBar = const Bar();
Baz newBaz = new Baz();
Boz newBoz = new Boz(createNew: true);
Boz constBoz = new Boz(createNew: false);
foo.dart: |
import 'package:flutter/src/widgets/framework.dart';
class Foo extends Widget {
factory Foo() => const Foo._();
const Foo._();
}
class Bar extends Widget {
const factory Bar() = Bar._;
const Bar._();
}
class Baz extends Widget {
factory Baz() => const Baz._();
const factory Baz._() = Baz.__;
const Baz.__();
}
class Boz extends Widget {
factory Boz({required bool createNew}) {
if (createNew) {
return new Boz._();
} else {
return const Boz._();
}
}
const Boz._();
}
flutter/lib/src/widgets/framework.dart: |
abstract class Bar {
const Bar();
}
@pragma('track-creation-locations')
abstract class Widget extends Bar {
const Widget();
}
.dart_tool/package_config.json: |
{
"configVersion": 2,
"packages": [
{
"name": "flutter",
"rootUri": "../flutter",
"packageUri": "lib/"
}
]
}
expectedLibraryCount: 3
- entry: main.dart
worldType: updated
invalidate:
- main.dart
expectInitializeFromDill: false
sources:
main.dart: |
import 'foo.dart';
expectedLibraryCount: 3
advancedInvalidation: outlineChange
- entry: main.dart
worldType: updated
invalidate:
- main.dart
expectInitializeFromDill: false
sources:
main.dart: |
import 'foo.dart';
Foo newFoo = new Foo();
Bar newBar = new Bar();
Bar constBar = const Bar();
Baz newBaz = new Baz();
Boz newBoz = new Boz(createNew: true);
Boz constBoz = new Boz(createNew: false);
expectedLibraryCount: 3
advancedInvalidation: outlineChange
@@ -0,0 +1,161 @@
main = <No Member>;
library from "package:flutter/src/widgets/framework.dart" as fra {
abstract class Bar extends dart.core::Object /*hasConstConstructor*/ {
const constructor •() → fra::Bar
: super dart.core::Object::•()
;
}
@#C3
abstract class Widget extends fra::Bar implements dart.developer::_HasCreationLocation /*hasConstConstructor*/ {
final field dart.developer::CreationLocation? dart.developer::_location;
const constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → fra::Widget
: super fra::Bar::•(), fra::Widget::_location = $creationLocationd_0dea112b090073317d4
;
}
}
library from "org-dartlang-test:///foo.dart" as foo {
import "package:flutter/src/widgets/framework.dart";
class Foo extends fra::Widget /*hasConstConstructor*/ {
const constructor _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static factory •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return #C9;
static synthetic method _#new#tearOff() → foo::Foo
return foo::Foo::•($creationLocationd_0dea112b090073317d4: #C11);
static synthetic method _#_#tearOff() → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: #C14);
}
class Bar extends fra::Widget /*hasConstConstructor*/ {
const constructor _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Bar
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static const factory •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Bar /* redirection-target: foo::Bar::_ */
return new foo::Bar::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
static synthetic method _#new#tearOff() → foo::Bar
return new foo::Bar::_($creationLocationd_0dea112b090073317d4: #C18);
static synthetic method _#_#tearOff() → foo::Bar
return new foo::Bar::_($creationLocationd_0dea112b090073317d4: #C20);
}
class Baz extends fra::Widget /*hasConstConstructor*/ {
const constructor __({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Baz
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static factory •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Baz
return #C24;
static synthetic method _#new#tearOff() → foo::Baz
return foo::Baz::•($creationLocationd_0dea112b090073317d4: #C25);
static const factory _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Baz /* redirection-target: foo::Baz::__ */
return new foo::Baz::__($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
static synthetic method _#_#tearOff() → foo::Baz
return new foo::Baz::__($creationLocationd_0dea112b090073317d4: #C27);
static synthetic method _#__#tearOff() → foo::Baz
return new foo::Baz::__($creationLocationd_0dea112b090073317d4: #C29);
}
class Boz extends fra::Widget /*hasConstConstructor*/ {
const constructor _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Boz
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static factory •({required dart.core::bool createNew, dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Boz {
if(createNew) {
return new foo::Boz::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
}
else {
return #C33;
}
}
static synthetic method _#new#tearOff({required dart.core::bool createNew}) → foo::Boz
return foo::Boz::•(createNew: createNew, $creationLocationd_0dea112b090073317d4: #C35);
static synthetic method _#_#tearOff() → foo::Boz
return new foo::Boz::_($creationLocationd_0dea112b090073317d4: #C37);
}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///foo.dart";
static field foo::Foo newFoo = foo::Foo::•($creationLocationd_0dea112b090073317d4: #C40);
static field foo::Bar newBar = new foo::Bar::_($creationLocationd_0dea112b090073317d4: #C42);
static field foo::Bar constBar = #C45;
static field foo::Baz newBaz = foo::Baz::•($creationLocationd_0dea112b090073317d4: #C47);
static field foo::Boz newBoz = foo::Boz::•(createNew: true, $creationLocationd_0dea112b090073317d4: #C48);
static field foo::Boz constBoz = foo::Boz::•(createNew: false, $creationLocationd_0dea112b090073317d4: #C50);
}
constants {
#C1 = "track-creation-locations"
#C2 = null
#C3 = dart.core::pragma {name:#C1, options:#C2}
#C4 = "org-dartlang-test:///foo.dart"
#C5 = 4.0
#C6 = 26.0
#C7 = "Foo"
#C8 = dart.developer::CreationLocation {file:#C4, line:#C5, column:#C6, name:#C7}
#C9 = foo::Foo {_location:#C8}
#C10 = 11.0
#C11 = dart.developer::CreationLocation {file:#C4, line:#C5, column:#C10, name:#C7}
#C12 = 6.0
#C13 = 9.0
#C14 = dart.developer::CreationLocation {file:#C4, line:#C12, column:#C13, name:#C7}
#C15 = 10.0
#C16 = 17.0
#C17 = "Bar"
#C18 = dart.developer::CreationLocation {file:#C4, line:#C15, column:#C16, name:#C17}
#C19 = 12.0
#C20 = dart.developer::CreationLocation {file:#C4, line:#C19, column:#C13, name:#C17}
#C21 = 16.0
#C22 = "Baz"
#C23 = dart.developer::CreationLocation {file:#C4, line:#C21, column:#C6, name:#C22}
#C24 = foo::Baz {_location:#C23}
#C25 = dart.developer::CreationLocation {file:#C4, line:#C21, column:#C10, name:#C22}
#C26 = 18.0
#C27 = dart.developer::CreationLocation {file:#C4, line:#C26, column:#C16, name:#C22}
#C28 = 20.0
#C29 = dart.developer::CreationLocation {file:#C4, line:#C28, column:#C13, name:#C22}
#C30 = 28.0
#C31 = "Boz"
#C32 = dart.developer::CreationLocation {file:#C4, line:#C30, column:#C28, name:#C31}
#C33 = foo::Boz {_location:#C32}
#C34 = 24.0
#C35 = dart.developer::CreationLocation {file:#C4, line:#C34, column:#C10, name:#C31}
#C36 = 32.0
#C37 = dart.developer::CreationLocation {file:#C4, line:#C36, column:#C13, name:#C31}
#C38 = "org-dartlang-test:///main.dart"
#C39 = 2.0
#C40 = dart.developer::CreationLocation {file:#C38, line:#C39, column:#C26, name:#C7}
#C41 = 3.0
#C42 = dart.developer::CreationLocation {file:#C38, line:#C41, column:#C26, name:#C17}
#C43 = 22.0
#C44 = dart.developer::CreationLocation {file:#C38, line:#C5, column:#C43, name:#C17}
#C45 = foo::Bar {_location:#C44}
#C46 = 5.0
#C47 = dart.developer::CreationLocation {file:#C38, line:#C46, column:#C26, name:#C22}
#C48 = dart.developer::CreationLocation {file:#C38, line:#C12, column:#C26, name:#C31}
#C49 = 7.0
#C50 = dart.developer::CreationLocation {file:#C38, line:#C49, column:#C28, name:#C31}
}
Constructor coverage from constants:
org-dartlang-test:///main.dart:
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
- Bar._ (from org-dartlang-test:///foo.dart:12:9)
- Widget. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:6:9)
- Bar. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:2:9)
org-dartlang-test:///foo.dart:
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
- Foo._ (from org-dartlang-test:///foo.dart:6:9)
- Widget. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:6:9)
- Bar. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:2:9)
- Baz.__ (from org-dartlang-test:///foo.dart:20:9)
- Boz._ (from org-dartlang-test:///foo.dart:32:9)
org-dartlang-test:///flutter/lib/src/widgets/framework.dart:
- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
@@ -0,0 +1,135 @@
main = <No Member>;
library from "package:flutter/src/widgets/framework.dart" as fra {
abstract class Bar extends dart.core::Object /*hasConstConstructor*/ {
const constructor •() → fra::Bar
: super dart.core::Object::•()
;
}
@#C3
abstract class Widget extends fra::Bar implements dart.developer::_HasCreationLocation /*hasConstConstructor*/ {
final field dart.developer::CreationLocation? dart.developer::_location;
const constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → fra::Widget
: super fra::Bar::•(), fra::Widget::_location = $creationLocationd_0dea112b090073317d4
;
}
}
library from "org-dartlang-test:///foo.dart" as foo {
import "package:flutter/src/widgets/framework.dart";
class Foo extends fra::Widget /*hasConstConstructor*/ {
const constructor _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static factory •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return #C9;
static synthetic method _#new#tearOff() → foo::Foo
return foo::Foo::•($creationLocationd_0dea112b090073317d4: #C11);
static synthetic method _#_#tearOff() → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: #C14);
}
class Bar extends fra::Widget /*hasConstConstructor*/ {
const constructor _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Bar
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static const factory •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Bar /* redirection-target: foo::Bar::_ */
return new foo::Bar::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
static synthetic method _#new#tearOff() → foo::Bar
return new foo::Bar::_($creationLocationd_0dea112b090073317d4: #C18);
static synthetic method _#_#tearOff() → foo::Bar
return new foo::Bar::_($creationLocationd_0dea112b090073317d4: #C20);
}
class Baz extends fra::Widget /*hasConstConstructor*/ {
const constructor __({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Baz
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static factory •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Baz
return #C24;
static synthetic method _#new#tearOff() → foo::Baz
return foo::Baz::•($creationLocationd_0dea112b090073317d4: #C25);
static const factory _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Baz /* redirection-target: foo::Baz::__ */
return new foo::Baz::__($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
static synthetic method _#_#tearOff() → foo::Baz
return new foo::Baz::__($creationLocationd_0dea112b090073317d4: #C27);
static synthetic method _#__#tearOff() → foo::Baz
return new foo::Baz::__($creationLocationd_0dea112b090073317d4: #C29);
}
class Boz extends fra::Widget /*hasConstConstructor*/ {
const constructor _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Boz
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static factory •({required dart.core::bool createNew, dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Boz {
if(createNew) {
return new foo::Boz::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
}
else {
return #C33;
}
}
static synthetic method _#new#tearOff({required dart.core::bool createNew}) → foo::Boz
return foo::Boz::•(createNew: createNew, $creationLocationd_0dea112b090073317d4: #C35);
static synthetic method _#_#tearOff() → foo::Boz
return new foo::Boz::_($creationLocationd_0dea112b090073317d4: #C37);
}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///foo.dart";
}
constants {
#C1 = "track-creation-locations"
#C2 = null
#C3 = dart.core::pragma {name:#C1, options:#C2}
#C4 = "org-dartlang-test:///foo.dart"
#C5 = 4.0
#C6 = 26.0
#C7 = "Foo"
#C8 = dart.developer::CreationLocation {file:#C4, line:#C5, column:#C6, name:#C7}
#C9 = foo::Foo {_location:#C8}
#C10 = 11.0
#C11 = dart.developer::CreationLocation {file:#C4, line:#C5, column:#C10, name:#C7}
#C12 = 6.0
#C13 = 9.0
#C14 = dart.developer::CreationLocation {file:#C4, line:#C12, column:#C13, name:#C7}
#C15 = 10.0
#C16 = 17.0
#C17 = "Bar"
#C18 = dart.developer::CreationLocation {file:#C4, line:#C15, column:#C16, name:#C17}
#C19 = 12.0
#C20 = dart.developer::CreationLocation {file:#C4, line:#C19, column:#C13, name:#C17}
#C21 = 16.0
#C22 = "Baz"
#C23 = dart.developer::CreationLocation {file:#C4, line:#C21, column:#C6, name:#C22}
#C24 = foo::Baz {_location:#C23}
#C25 = dart.developer::CreationLocation {file:#C4, line:#C21, column:#C10, name:#C22}
#C26 = 18.0
#C27 = dart.developer::CreationLocation {file:#C4, line:#C26, column:#C16, name:#C22}
#C28 = 20.0
#C29 = dart.developer::CreationLocation {file:#C4, line:#C28, column:#C13, name:#C22}
#C30 = 28.0
#C31 = "Boz"
#C32 = dart.developer::CreationLocation {file:#C4, line:#C30, column:#C28, name:#C31}
#C33 = foo::Boz {_location:#C32}
#C34 = 24.0
#C35 = dart.developer::CreationLocation {file:#C4, line:#C34, column:#C10, name:#C31}
#C36 = 32.0
#C37 = dart.developer::CreationLocation {file:#C4, line:#C36, column:#C13, name:#C31}
}
Constructor coverage from constants:
org-dartlang-test:///foo.dart:
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
- Foo._ (from org-dartlang-test:///foo.dart:6:9)
- Widget. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:6:9)
- Bar. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:2:9)
- Baz.__ (from org-dartlang-test:///foo.dart:20:9)
- Boz._ (from org-dartlang-test:///foo.dart:32:9)
org-dartlang-test:///flutter/lib/src/widgets/framework.dart:
- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
@@ -0,0 +1,161 @@
main = <No Member>;
library from "package:flutter/src/widgets/framework.dart" as fra {
abstract class Bar extends dart.core::Object /*hasConstConstructor*/ {
const constructor •() → fra::Bar
: super dart.core::Object::•()
;
}
@#C3
abstract class Widget extends fra::Bar implements dart.developer::_HasCreationLocation /*hasConstConstructor*/ {
final field dart.developer::CreationLocation? dart.developer::_location;
const constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → fra::Widget
: super fra::Bar::•(), fra::Widget::_location = $creationLocationd_0dea112b090073317d4
;
}
}
library from "org-dartlang-test:///foo.dart" as foo {
import "package:flutter/src/widgets/framework.dart";
class Foo extends fra::Widget /*hasConstConstructor*/ {
const constructor _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static factory •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Foo
return #C9;
static synthetic method _#new#tearOff() → foo::Foo
return foo::Foo::•($creationLocationd_0dea112b090073317d4: #C11);
static synthetic method _#_#tearOff() → foo::Foo
return new foo::Foo::_($creationLocationd_0dea112b090073317d4: #C14);
}
class Bar extends fra::Widget /*hasConstConstructor*/ {
const constructor _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Bar
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static const factory •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Bar /* redirection-target: foo::Bar::_ */
return new foo::Bar::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
static synthetic method _#new#tearOff() → foo::Bar
return new foo::Bar::_($creationLocationd_0dea112b090073317d4: #C18);
static synthetic method _#_#tearOff() → foo::Bar
return new foo::Bar::_($creationLocationd_0dea112b090073317d4: #C20);
}
class Baz extends fra::Widget /*hasConstConstructor*/ {
const constructor __({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Baz
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static factory •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Baz
return #C24;
static synthetic method _#new#tearOff() → foo::Baz
return foo::Baz::•($creationLocationd_0dea112b090073317d4: #C25);
static const factory _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Baz /* redirection-target: foo::Baz::__ */
return new foo::Baz::__($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
static synthetic method _#_#tearOff() → foo::Baz
return new foo::Baz::__($creationLocationd_0dea112b090073317d4: #C27);
static synthetic method _#__#tearOff() → foo::Baz
return new foo::Baz::__($creationLocationd_0dea112b090073317d4: #C29);
}
class Boz extends fra::Widget /*hasConstConstructor*/ {
const constructor _({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Boz
: super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static factory •({required dart.core::bool createNew, dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → foo::Boz {
if(createNew) {
return new foo::Boz::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
}
else {
return #C33;
}
}
static synthetic method _#new#tearOff({required dart.core::bool createNew}) → foo::Boz
return foo::Boz::•(createNew: createNew, $creationLocationd_0dea112b090073317d4: #C35);
static synthetic method _#_#tearOff() → foo::Boz
return new foo::Boz::_($creationLocationd_0dea112b090073317d4: #C37);
}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///foo.dart";
static field foo::Foo newFoo = foo::Foo::•($creationLocationd_0dea112b090073317d4: #C40);
static field foo::Bar newBar = new foo::Bar::_($creationLocationd_0dea112b090073317d4: #C42);
static field foo::Bar constBar = #C45;
static field foo::Baz newBaz = foo::Baz::•($creationLocationd_0dea112b090073317d4: #C47);
static field foo::Boz newBoz = foo::Boz::•(createNew: true, $creationLocationd_0dea112b090073317d4: #C48);
static field foo::Boz constBoz = foo::Boz::•(createNew: false, $creationLocationd_0dea112b090073317d4: #C50);
}
constants {
#C1 = "track-creation-locations"
#C2 = null
#C3 = dart.core::pragma {name:#C1, options:#C2}
#C4 = "org-dartlang-test:///foo.dart"
#C5 = 4.0
#C6 = 26.0
#C7 = "Foo"
#C8 = dart.developer::CreationLocation {file:#C4, line:#C5, column:#C6, name:#C7}
#C9 = foo::Foo {_location:#C8}
#C10 = 11.0
#C11 = dart.developer::CreationLocation {file:#C4, line:#C5, column:#C10, name:#C7}
#C12 = 6.0
#C13 = 9.0
#C14 = dart.developer::CreationLocation {file:#C4, line:#C12, column:#C13, name:#C7}
#C15 = 10.0
#C16 = 17.0
#C17 = "Bar"
#C18 = dart.developer::CreationLocation {file:#C4, line:#C15, column:#C16, name:#C17}
#C19 = 12.0
#C20 = dart.developer::CreationLocation {file:#C4, line:#C19, column:#C13, name:#C17}
#C21 = 16.0
#C22 = "Baz"
#C23 = dart.developer::CreationLocation {file:#C4, line:#C21, column:#C6, name:#C22}
#C24 = foo::Baz {_location:#C23}
#C25 = dart.developer::CreationLocation {file:#C4, line:#C21, column:#C10, name:#C22}
#C26 = 18.0
#C27 = dart.developer::CreationLocation {file:#C4, line:#C26, column:#C16, name:#C22}
#C28 = 20.0
#C29 = dart.developer::CreationLocation {file:#C4, line:#C28, column:#C13, name:#C22}
#C30 = 28.0
#C31 = "Boz"
#C32 = dart.developer::CreationLocation {file:#C4, line:#C30, column:#C28, name:#C31}
#C33 = foo::Boz {_location:#C32}
#C34 = 24.0
#C35 = dart.developer::CreationLocation {file:#C4, line:#C34, column:#C10, name:#C31}
#C36 = 32.0
#C37 = dart.developer::CreationLocation {file:#C4, line:#C36, column:#C13, name:#C31}
#C38 = "org-dartlang-test:///main.dart"
#C39 = 2.0
#C40 = dart.developer::CreationLocation {file:#C38, line:#C39, column:#C26, name:#C7}
#C41 = 3.0
#C42 = dart.developer::CreationLocation {file:#C38, line:#C41, column:#C26, name:#C17}
#C43 = 22.0
#C44 = dart.developer::CreationLocation {file:#C38, line:#C5, column:#C43, name:#C17}
#C45 = foo::Bar {_location:#C44}
#C46 = 5.0
#C47 = dart.developer::CreationLocation {file:#C38, line:#C46, column:#C26, name:#C22}
#C48 = dart.developer::CreationLocation {file:#C38, line:#C12, column:#C26, name:#C31}
#C49 = 7.0
#C50 = dart.developer::CreationLocation {file:#C38, line:#C49, column:#C28, name:#C31}
}
Constructor coverage from constants:
org-dartlang-test:///foo.dart:
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
- Foo._ (from org-dartlang-test:///foo.dart:6:9)
- Widget. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:6:9)
- Bar. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:2:9)
- Baz.__ (from org-dartlang-test:///foo.dart:20:9)
- Boz._ (from org-dartlang-test:///foo.dart:32:9)
org-dartlang-test:///flutter/lib/src/widgets/framework.dart:
- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
org-dartlang-test:///main.dart:
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
- Bar._ (from org-dartlang-test:///foo.dart:12:9)
- Widget. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:6:9)
- Bar. (from org-dartlang-test:///flutter/lib/src/widgets/framework.dart:2:9)
@@ -0,0 +1,44 @@
# 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.
# This tests that Jaspr-style components, including redirecting factory
# constructors, are correctly transformed to include tracking information.
type: newworld
target: dartdevc
trackCreationLocations: true
worlds:
- entry: main.dart
sources:
main.dart: |
@pragma('track-creation-locations')
abstract class Component {
const Component({this.key});
const factory Component.text(String text, {String? key}) = Text._;
final String? key;
}
class Text extends Component {
const Text._(this.text, {super.key});
final String text;
}
class MyComponent extends Component {
const MyComponent(this.children, {this.id, super.key});
final List<Component> children;
final String? id;
}
void main() {
var c1 = MyComponent([]);
var c2 = MyComponent([c1], id: '2');
const c3 = MyComponent([Component.text('Hello')]);
const c4 = MyComponent([c3, MyComponent([], id: 'x')], id: '4');
}
expectedLibraryCount: 1
@@ -0,0 +1,89 @@
main = main::main;
library from "org-dartlang-test:///main.dart" as main {
@#C3
abstract class Component extends dart.core::Object implements dart.developer::_HasCreationLocation /*hasConstConstructor*/ {
final field dart.core::String? key;
final field dart.developer::CreationLocation? dart.developer::_location;
const constructor •({initializing-formal dart.core::String? key = #C2, dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → main::Component
: main::Component::key = key, super dart.core::Object::•(), main::Component::_location = $creationLocationd_0dea112b090073317d4
;
static const factory text(dart.core::String text, {dart.core::String? key = #C2, dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → main::Component /* redirection-target: main::Text::_ */
return new main::Text::_(text, key: key, $creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
static synthetic method _#text#tearOff(dart.core::String text, {dart.core::String? key = #C2}) → main::Component
return new main::Text::_(text, key: key, $creationLocationd_0dea112b090073317d4: #C8);
}
class Text extends main::Component /*hasConstConstructor*/ {
final field dart.core::String text;
const constructor _(initializing-formal dart.core::String text, {super-initializing-formal dart.core::String? key = #C2, dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → main::Text
: main::Text::text = text, super main::Component::•(key: key, $creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static synthetic method _#_#tearOff(dart.core::String text, {dart.core::String? key = #C2}) → main::Text
return new main::Text::_(text, key: key, $creationLocationd_0dea112b090073317d4: #C11);
}
class MyComponent extends main::Component /*hasConstConstructor*/ {
final field dart.core::List<main::Component> children;
final field dart.core::String? id;
const constructor •(initializing-formal dart.core::List<main::Component> children, {initializing-formal dart.core::String? id = #C2, super-initializing-formal dart.core::String? key = #C2, dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = #C2}) → main::MyComponent
: main::MyComponent::children = children, main::MyComponent::id = id, super main::Component::•(key: key, $creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static synthetic method _#new#tearOff(dart.core::List<main::Component> children, {dart.core::String? id = #C2, dart.core::String? key = #C2}) → main::MyComponent
return new main::MyComponent::•(children, id: id, key: key, $creationLocationd_0dea112b090073317d4: #C13);
}
static method main() → void {
main::MyComponent c1 = new main::MyComponent::•(<main::Component>[], $creationLocationd_0dea112b090073317d4: #C16);
main::MyComponent c2 = new main::MyComponent::•(<main::Component>[c1], id: "2", $creationLocationd_0dea112b090073317d4: #C18);
const main::MyComponent c3 = #C27;
const main::MyComponent c4 = #C37;
}
}
constants {
#C1 = "track-creation-locations"
#C2 = null
#C3 = dart.core::pragma {name:#C1, options:#C2}
#C4 = "org-dartlang-test:///main.dart"
#C5 = 5.0
#C6 = 17.0
#C7 = "Text"
#C8 = dart.developer::CreationLocation {file:#C4, line:#C5, column:#C6, name:#C7}
#C9 = 11.0
#C10 = 9.0
#C11 = dart.developer::CreationLocation {file:#C4, line:#C9, column:#C10, name:#C7}
#C12 = "MyComponent"
#C13 = dart.developer::CreationLocation {file:#C4, line:#C6, column:#C10, name:#C12}
#C14 = 24.0
#C15 = 12.0
#C16 = dart.developer::CreationLocation {file:#C4, line:#C14, column:#C15, name:#C12}
#C17 = 25.0
#C18 = dart.developer::CreationLocation {file:#C4, line:#C17, column:#C15, name:#C12}
#C19 = "Hello"
#C20 = 27.0
#C21 = 37.0
#C22 = dart.developer::CreationLocation {file:#C4, line:#C20, column:#C21, name:#C7}
#C23 = main::Text {text:#C19, key:#C2, _location:#C22}
#C24 = <main::Component>[#C23]
#C25 = 14.0
#C26 = dart.developer::CreationLocation {file:#C4, line:#C20, column:#C25, name:#C12}
#C27 = main::MyComponent {children:#C24, id:#C2, key:#C2, _location:#C26}
#C28 = <main::Component>[]
#C29 = "x"
#C30 = 28.0
#C31 = 31.0
#C32 = dart.developer::CreationLocation {file:#C4, line:#C30, column:#C31, name:#C12}
#C33 = main::MyComponent {children:#C28, id:#C29, key:#C2, _location:#C32}
#C34 = <main::Component>[#C27, #C33]
#C35 = "4"
#C36 = dart.developer::CreationLocation {file:#C4, line:#C30, column:#C25, name:#C12}
#C37 = main::MyComponent {children:#C34, id:#C35, key:#C2, _location:#C36}
}
Constructor coverage from constants:
org-dartlang-test:///main.dart:
- pragma._ (from org-dartlang-sdk:///lib/core/annotations.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)
- CreationLocation._ (from org-dartlang-sdk:///lib/developer/creation_tracking.dart)
- Text._ (from org-dartlang-test:///main.dart:11:9)
- Component. (from org-dartlang-test:///main.dart:3:9)
- MyComponent. (from org-dartlang-test:///main.dart:17:9)
@@ -0,0 +1,64 @@
# 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.
type: newworld
target: dartdevc
trackCreationLocations: true
worlds:
- entry: main.dart
outlineOnly: true
skipOutlineBodyCheck: true
sources:
widget.dart: |
import 'dart:developer';
@pragma('track-creation-locations')
abstract class Widget {
const Widget();
}
class StandardWidget extends Widget {
const StandardWidget();
const StandardWidget.named(String name);
}
main.dart: |
import 'widget.dart';
class MyWidget extends Widget {
const MyWidget({this.child});
final Widget? child;
}
void main() {
var w1 = MyWidget();
var w2 = StandardWidget.named('w2');
const w3 = MyWidget();
}
expectedLibraryCount: 2
- entry: main.dart
worldType: updated
outlineOnly: true
skipOutlineBodyCheck: true
expectInitializeFromDill: false
invalidate:
- main.dart
sources:
main.dart: |
import 'widget.dart';
class MyWidget extends Widget {
const MyWidget({this.child});
final Widget? child;
}
void main() {
var w1 = MyWidget(child: StandardWidget());
const w2 = MyWidget(child: StandardWidget.named('w2'));
const w3 = MyWidget(child: w2);
}
expectedLibraryCount: 2
advancedInvalidation: bodiesOnly
@@ -0,0 +1,40 @@
main = main::main;
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///widget.dart";
class MyWidget extends wid::Widget /*hasConstConstructor*/ {
final field wid::Widget? child;
const constructor •({initializing-formal wid::Widget? child = null, dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = null}) → main::MyWidget
: main::MyWidget::child = child, super wid::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static synthetic method _#new#tearOff({wid::Widget? child}) → main::MyWidget
return new main::MyWidget::•(child: child, $creationLocationd_0dea112b090073317d4: const dart.developer::CreationLocation::_(file: "org-dartlang-test:///main.dart", line: 4, column: 9, name: "MyWidget"));
}
static method main() → void
;
}
library from "org-dartlang-test:///widget.dart" as wid {
import "dart:developer";
@/*original=dart.core::pragma::•*/ const dart.core::pragma::_("track-creation-locations")
abstract class Widget extends dart.core::Object implements dart.developer::_HasCreationLocation /*hasConstConstructor*/ {
final field dart.developer::CreationLocation? dart.developer::_location;
const constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = null}) → wid::Widget
: super dart.core::Object::•(), wid::Widget::_location = $creationLocationd_0dea112b090073317d4
;
}
class StandardWidget extends wid::Widget /*hasConstConstructor*/ {
const constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = null}) → wid::StandardWidget
: super wid::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
const constructor named(dart.core::String name, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = null}) → wid::StandardWidget
: super wid::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static synthetic method _#new#tearOff() → wid::StandardWidget
return new wid::StandardWidget::•($creationLocationd_0dea112b090073317d4: const dart.developer::CreationLocation::_(file: "org-dartlang-test:///widget.dart", line: 9, column: 9, name: "StandardWidget"));
static synthetic method _#named#tearOff(dart.core::String name) → wid::StandardWidget
return new wid::StandardWidget::named(name, $creationLocationd_0dea112b090073317d4: const dart.developer::CreationLocation::_(file: "org-dartlang-test:///widget.dart", line: 10, column: 9, name: "StandardWidget"));
}
}
@@ -0,0 +1,42 @@
main = main::main;
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///widget.dart";
class MyWidget extends wid::Widget /*hasConstConstructor*/ {
final field wid::Widget? child;
const constructor •({initializing-formal wid::Widget? child = null, dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = null}) → main::MyWidget
: main::MyWidget::child = child, super wid::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static synthetic method _#new#tearOff({wid::Widget? child}) → main::MyWidget
return new main::MyWidget::•(child: child, $creationLocationd_0dea112b090073317d4: const dart.developer::CreationLocation::_(file: "org-dartlang-test:///main.dart", line: 4, column: 9, name: "MyWidget"));
synthetic no-such-method-forwarder set dart.developer::_location(dart.developer::CreationLocation? value) → void
return throw{for-error-handling} dart.core::NoSuchMethodError::withInvocation(this, new dart.core::_Invocation::setter(#_location=, value));
}
static method main() → void
;
}
library from "org-dartlang-test:///widget.dart" as wid {
import "dart:developer";
@/*original=dart.core::pragma::•*/ const dart.core::pragma::_("track-creation-locations")
abstract class Widget extends dart.core::Object implements dart.developer::_HasCreationLocation /*hasConstConstructor*/ {
final field dart.developer::CreationLocation? dart.developer::_location;
const constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = null}) → wid::Widget
: super dart.core::Object::•(), wid::Widget::_location = $creationLocationd_0dea112b090073317d4
;
}
class StandardWidget extends wid::Widget /*hasConstConstructor*/ {
const constructor •({dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = null}) → wid::StandardWidget
: super wid::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
const constructor named(dart.core::String name, {dart.developer::CreationLocation? $creationLocationd_0dea112b090073317d4 = null}) → wid::StandardWidget
: super wid::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
;
static synthetic method _#new#tearOff() → wid::StandardWidget
return new wid::StandardWidget::•($creationLocationd_0dea112b090073317d4: const dart.developer::CreationLocation::_(file: "org-dartlang-test:///widget.dart", line: 9, column: 9, name: "StandardWidget"));
static synthetic method _#named#tearOff(dart.core::String name) → wid::StandardWidget
return new wid::StandardWidget::named(name, $creationLocationd_0dea112b090073317d4: const dart.developer::CreationLocation::_(file: "org-dartlang-test:///widget.dart", line: 10, column: 9, name: "StandardWidget"));
}
}
@@ -5,7 +5,7 @@
# Reproduce a crash.
type: newworld
trackWidgetCreation: true
trackCreationLocations: true
target: dartdevc # basically needed for widget creation to be run
worlds:
- entry: flutter_gallery/lib/main.dart
@@ -5,7 +5,7 @@
# Reproduce a crash.
type: newworld
trackWidgetCreation: true
trackCreationLocations: true
target: vm
worlds:
- entry: main.dart
@@ -5,7 +5,7 @@
# Reproduce a crash.
type: newworld
trackWidgetCreation: true
trackCreationLocations: true
target: vm
worlds:
- entry: main.dart
@@ -4,7 +4,7 @@
type: newworld
target: dartdevc
trackWidgetCreation: true
trackCreationLocations: true
worlds:
- entry: main.dart
sources:
+13 -5
View File
@@ -102,7 +102,15 @@ final ArgParser summaryArgsParser = new ArgParser()
..addFlag('reuse-compiler-result', defaultsTo: false)
..addFlag('use-incremental-compiler', defaultsTo: false)
..addOption('used-inputs')
..addFlag('track-widget-creation', defaultsTo: false)
..addFlag(
'track-creation-locations',
defaultsTo: false,
aliases: [
// TODO(http://dartbug.com/63225): Remove this once flutter is migrated
// to the new flag.
'track-widget-creation',
],
)
..addFlag('include-unsupported-platform-library-stubs', defaultsTo: false)
..addMultiOption(
'enable-experiment',
@@ -205,7 +213,7 @@ Future<ComputeKernelResult> computeKernel(
if (summaryOnly && !summary) {
throw new ArgumentError('--summary-only conflicts with --no-summary');
}
bool trackWidgetCreation = parsedArgs['track-widget-creation'] as bool;
bool trackCreationLocations = parsedArgs['track-creation-locations'] as bool;
bool includeUnsupportedPlatformLibraryStubs =
parsedArgs['include-unsupported-platform-library-stubs'] as bool;
@@ -214,7 +222,7 @@ Future<ComputeKernelResult> computeKernel(
String targetName =
(parsedArgs['target'] as String?) ?? (summaryOnly ? 'ddc' : 'vm');
TargetFlags targetFlags = new TargetFlags(
trackWidgetCreation: trackWidgetCreation,
trackCreationLocations: trackCreationLocations,
includeUnsupportedPlatformLibraryStubs:
includeUnsupportedPlatformLibraryStubs,
);
@@ -392,8 +400,8 @@ Future<ComputeKernelResult> computeKernel(
previousState,
{
"target=$targetName",
// trackWidgetCreation is in TargetFlags.
"trackWidgetCreation=$trackWidgetCreation",
// trackCreationLocations is in TargetFlags.
"trackCreationLocations=$trackCreationLocations",
// includeUnsupportedPlatformLibraryStubs is in TargetFlags.
"includeUnsupportedPlatformLibraryStubs="
"$includeUnsupportedPlatformLibraryStubs",
+10 -3
View File
@@ -261,9 +261,16 @@ ArgParser argParser = new ArgParser(allowTrailingOptions: true)
hide: true,
)
..addFlag(
'track-widget-creation',
help: 'Run a kernel transformer to track creation locations for widgets.',
'track-creation-locations',
help:
'Run a kernel transformer to track creation locations for'
' classes annotated with @pragma(\'track-creation-locations\').',
defaultsTo: false,
aliases: [
// TODO(http://dartbug.com/63225): Remove this once flutter is migrated
// to the new flag.
'track-widget-creation',
],
)
..addMultiOption(
'delete-tostring-package-uri',
@@ -800,7 +807,7 @@ class FrontendCompiler implements CompilerInterface {
final bool minimalKernel = options['minimal-kernel'];
compilerOptions.target = createFrontEndTarget(
options['target'],
trackWidgetCreation: options['track-widget-creation'],
trackCreationLocations: options['track-creation-locations'],
supportMirrors: options['support-mirrors'] ?? !(aot || minimalKernel),
includeUnsupportedPlatformLibraryStubs:
options['include-unsupported-platform-library-stubs'],
+23 -11
View File
@@ -12,7 +12,7 @@ import '../verifier.dart';
import 'changed_structure_notifier.dart';
class TargetFlags {
final bool trackWidgetCreation;
final bool trackCreationLocations;
final bool supportMirrors;
/// Whether the backend expects closure contexts to be present in the AST.
@@ -32,7 +32,7 @@ class TargetFlags {
final bool includeUnsupportedPlatformLibraryStubs;
const TargetFlags({
this.trackWidgetCreation = false,
this.trackCreationLocations = false,
this.supportMirrors = true,
this.isClosureContextLoweringEnabled = false,
this.constKeepLocalsIndicator,
@@ -43,7 +43,7 @@ class TargetFlags {
bool operator ==(other) {
if (identical(this, other)) return true;
return other is TargetFlags &&
trackWidgetCreation == other.trackWidgetCreation &&
trackCreationLocations == other.trackCreationLocations &&
supportMirrors == other.supportMirrors &&
includeUnsupportedPlatformLibraryStubs ==
other.includeUnsupportedPlatformLibraryStubs &&
@@ -53,7 +53,7 @@ class TargetFlags {
@override
int get hashCode {
int hash = 485786;
hash = 0x3fffffff & (hash * 31 + (hash ^ trackWidgetCreation.hashCode));
hash = 0x3fffffff & (hash * 31 + (hash ^ trackCreationLocations.hashCode));
hash = 0x3fffffff & (hash * 31 + (hash ^ supportMirrors.hashCode));
hash =
0x3fffffff &
@@ -340,7 +340,11 @@ abstract class Target {
/// This transformation is not applied when compiling full kernel programs to
/// prevent affecting the internal invariants of the compiler and accidentally
/// slowing down compilation.
void performOutlineTransformations(Component component) {}
void performOutlineTransformations(
Component component, {
List<Library>? libraries,
ChangedStructureNotifier? changedStructureNotifier,
}) {}
/// Perform target-specific operations on the [Component] storing the outlines
/// when generating summaries.
@@ -821,7 +825,7 @@ class TestTargetFlags extends TargetFlags {
final Set<String> unsupportedDartLibraries;
const TestTargetFlags({
bool trackWidgetCreation = false,
bool trackCreationLocations = false,
this.forceLateLoweringsForTesting,
this.forceLateLoweringSentinelForTesting,
this.forceStaticFieldLoweringForTesting,
@@ -831,7 +835,7 @@ class TestTargetFlags extends TargetFlags {
this.unsupportedDartLibraries = const {},
bool isClosureContextLoweringEnabled = false,
}) : super(
trackWidgetCreation: trackWidgetCreation,
trackCreationLocations: trackCreationLocations,
isClosureContextLoweringEnabled: isClosureContextLoweringEnabled,
);
}
@@ -1040,8 +1044,16 @@ class TargetWrapper extends Target {
}
@override
void performOutlineTransformations(Component component) {
_target.performOutlineTransformations(component);
void performOutlineTransformations(
Component component, {
List<Library>? libraries,
ChangedStructureNotifier? changedStructureNotifier,
}) {
_target.performOutlineTransformations(
component,
libraries: libraries,
changedStructureNotifier: changedStructureNotifier,
);
}
@override
@@ -1131,10 +1143,10 @@ mixin SummaryMixin on Target {
super.performOutlineComponentOperations(component);
if (!excludeNonSources) return;
List<Library> libraries = new List.of(component.libraries);
List<Library> componentLibraries = new List.of(component.libraries);
component.libraries.clear();
Set<Uri> include = sources.toSet();
for (Library library in libraries) {
for (Library library in componentLibraries) {
if (include.contains(library.importUri)) {
component.libraries.add(library);
} else {
@@ -61,7 +61,6 @@ void _maybeAddCreationLocationArgument(
Arguments arguments,
FunctionNode function,
Expression creationLocation,
Class locationClass,
) {
if (_hasNamedArgument(arguments, _creationLocationParameterName)) {
return;
@@ -120,15 +119,6 @@ bool _maybeAddNamedParameter(
/// and this is used as the location value for all Widget constructor
/// invocations within the method.
class _WidgetCallSiteTransformer extends Transformer {
/// The [Widget] class defined in the `package:flutter` library.
///
/// Used to perform is-tests to determine whether Dart constructor calls are
/// creating [Widget] objects.
final Class _widgetClass;
/// The _Location class defined in the `package:flutter` library.
final Class _locationClass;
final WidgetCreatorTracker _tracker;
/// The creation location parameter of the extension factory method enclosing
@@ -151,13 +141,8 @@ class _WidgetCallSiteTransformer extends Transformer {
/// of the library.
Library? _currentLibrary;
_WidgetCallSiteTransformer({
required Class widgetClass,
required Class locationClass,
required WidgetCreatorTracker tracker,
}) : _widgetClass = widgetClass,
_locationClass = locationClass,
_tracker = tracker;
_WidgetCallSiteTransformer({required WidgetCreatorTracker tracker})
: _tracker = tracker;
/// Builds a call to the const constructor of the _Location
/// object specifying the location where a constructor call was made and
@@ -169,7 +154,12 @@ class _WidgetCallSiteTransformer extends Transformer {
/// constructor call but it is convenient to bundle the location and names
/// of the parameters passed in so that tools can show parameter locations
/// without re-parsing the source code.
ConstructorInvocation _constructLocation(Location location, {String? name}) {
ConstructorInvocation _constructLocation(
Location location, {
required Class locationClass,
String? name,
}) {
final List<NamedExpression> arguments = <NamedExpression>[
new NamedExpression('file', new StringLiteral(location.file.toString())),
new NamedExpression('line', new IntLiteral(location.line)),
@@ -178,7 +168,7 @@ class _WidgetCallSiteTransformer extends Transformer {
];
return new ConstructorInvocation(
_locationClass.constructors.first,
locationClass.constructors.first,
new Arguments(<Expression>[], named: arguments),
isConst: true,
);
@@ -210,10 +200,6 @@ class _WidgetCallSiteTransformer extends Transformer {
return node;
}
bool _isSubclassOfWidget(Class clazz) {
return _tracker._isSubclassOf(clazz, _widgetClass);
}
bool _isWidgetFactory(Procedure node) {
return node.isExtensionMember &&
_hasNamedParameter(node.function, _creationLocationParameterName);
@@ -225,7 +211,10 @@ class _WidgetCallSiteTransformer extends Transformer {
final Procedure target = node.target;
if (target.isFactory) {
final Class constructedClass = target.enclosingClass!;
if (!_isSubclassOfWidget(constructedClass)) {
final _TrackingClasses? tracking = _tracker._getTrackingClasses(
constructedClass,
);
if (tracking == null) {
return node;
}
@@ -234,11 +223,16 @@ class _WidgetCallSiteTransformer extends Transformer {
target.function,
constructedClass: constructedClass,
isConst: node.isConst,
locationClass: tracking.locationClass,
);
return node;
}
if (_isWidgetFactory(target)) {
_addLocationArgument(node, target.function);
if (_isWidgetFactory(target) && _tracker._locationClass != null) {
_addLocationArgument(
node,
target.function,
locationClass: _tracker._locationClass!,
);
return node;
}
return node;
@@ -249,6 +243,7 @@ class _WidgetCallSiteTransformer extends Transformer {
FunctionNode function, {
Class? constructedClass,
bool isConst = false,
required Class locationClass,
}) {
Expression? location = _currentExtensionFactoryLocationParameter;
if (location == null ||
@@ -261,14 +256,10 @@ class _WidgetCallSiteTransformer extends Transformer {
function,
constructedClass,
isConst: isConst,
locationClass: locationClass,
);
}
_maybeAddCreationLocationArgument(
node.arguments,
function,
location,
_locationClass,
);
_maybeAddCreationLocationArgument(node.arguments, function, location);
}
@override
@@ -277,7 +268,10 @@ class _WidgetCallSiteTransformer extends Transformer {
final Constructor constructor = node.target;
final Class constructedClass = constructor.enclosingClass;
if (!_isSubclassOfWidget(constructedClass)) {
final _TrackingClasses? tracking = _tracker._getTrackingClasses(
constructedClass,
);
if (tracking == null) {
return node;
}
@@ -286,6 +280,7 @@ class _WidgetCallSiteTransformer extends Transformer {
constructor.function,
constructedClass: constructedClass,
isConst: node.isConst,
locationClass: tracking.locationClass,
);
return node;
}
@@ -295,6 +290,7 @@ class _WidgetCallSiteTransformer extends Transformer {
FunctionNode function,
Class? constructedClass, {
bool isConst = false,
required Class locationClass,
}) {
assert(constructedClass != null || !isConst);
@@ -302,9 +298,9 @@ class _WidgetCallSiteTransformer extends Transformer {
// argument to the factory constructor rather than the location
if (constructedClass != null &&
_currentFactory != null &&
_tracker._isSubclassOf(
_tracker._isSubclassWhere(
constructedClass,
_currentFactory!.enclosingClass!,
(Class c) => c == _currentFactory!.enclosingClass!,
) &&
// If the constructor invocation is constant we cannot refer to the
// location parameter of the surrounding factory since it isn't a
@@ -319,8 +315,13 @@ class _WidgetCallSiteTransformer extends Transformer {
}
}
if (node.location == null) {
return new NullLiteral();
}
return _constructLocation(
node.location!,
locationClass: locationClass,
name:
constructedClass?.name ??
// For extension factory methods we use the name of the method.
@@ -360,12 +361,20 @@ class _WidgetCallSiteTransformer extends Transformer {
/// invocations within the method.
class WidgetCreatorTracker {
bool _foundClasses = false;
late Class _widgetClass;
late Class _locationClass;
/// Marker interface indicating that a private _location field is
/// available.
late Class _hasCreationLocationClass;
/// The [Widget] class defined in the `package:flutter` library.
///
/// Used to perform is-tests to determine whether Dart constructor calls are
/// creating [Widget] objects.
Class? _widgetClass;
/// The _Location class defined in the `package:flutter` library.
Class? _locationClass;
/// The _HasCreationLocation class defined in the `package:flutter` library.
///
/// Marker interface indicating that a private _location field is available.
Class? _hasCreationLocationClass;
/// Annotation class used to mark an extension method as a "Widget factory".
///
@@ -374,20 +383,27 @@ class WidgetCreatorTracker {
/// method.
Class? _widgetFactoryClass;
void _resolveFlutterClasses(Iterable<Library> libraries) {
/// The _HasCreationLocation class defined in the `dart:developer` library.
Class? _developerHasCreationLocationClass;
/// The CreationLocation class defined in the `dart:developer` library.
Class? _developerCreationLocationClass;
void _resolveWellKnownClasses(Iterable<Library> libraries) {
// If the Widget or Debug location classes have been updated we need to get
// the latest version
bool foundWidgetClass = false;
bool foundHasCreationLocationClass = false;
bool foundLocationClass = false;
for (Library library in libraries) {
final Uri importUri = library.importUri;
// Legacy Case: Search for hardcoded Flutter classes.
// TODO(http://dartbug.com/63225): Remove this once Flutter is migrated
// to the new API.
if (importUri.isScheme('package')) {
if (importUri.path == 'flutter/src/widgets/framework.dart') {
for (Class class_ in library.classes) {
if (class_.name == 'Widget') {
_widgetClass = class_;
foundWidgetClass = true;
}
}
} else {
@@ -395,10 +411,8 @@ class WidgetCreatorTracker {
for (Class class_ in library.classes) {
if (class_.name == '_HasCreationLocation') {
_hasCreationLocationClass = class_;
foundHasCreationLocationClass = true;
} else if (class_.name == '_Location') {
_locationClass = class_;
foundLocationClass = true;
} else if (class_.name == '_WidgetFactory') {
_widgetFactoryClass = class_;
}
@@ -406,43 +420,62 @@ class WidgetCreatorTracker {
}
}
}
// New Case: Search for classes in `dart:developer`
if (importUri.isScheme('dart') && importUri.path == 'developer') {
for (Class class_ in library.classes) {
if (class_.name == '_HasCreationLocation') {
_developerHasCreationLocationClass = class_;
} else if (class_.name == 'CreationLocation') {
_developerCreationLocationClass = class_;
}
}
}
}
// TODO(johnniwinther): Require the [_widgetFactoryClass] once the
// `widgetFactory` is stable in flutter.
_foundClasses =
foundWidgetClass && foundHasCreationLocationClass && foundLocationClass;
(_widgetClass != null &&
_hasCreationLocationClass != null &&
_locationClass != null) ||
(_developerHasCreationLocationClass != null &&
_developerCreationLocationClass != null);
}
/// Modify [clazz] to add a field named [_locationFieldName] that is the
/// first parameter of all constructors of the class.
/// Modify [clazz] to add the location field that is
/// the first parameter of all constructors of the class.
///
/// This method should only be called for classes that implement but do not
/// extend [Widget].
void _transformClassImplementingWidget(
/// extend the tracking base class.
void _transformClassImplementingTrackingClass(
Class clazz,
_TrackingClasses tracking,
ChangedStructureNotifier? changedStructureNotifier,
) {
if (clazz.fields.any(
(Field field) => field.name.text == _locationFieldName,
(Field field) => field.name.text == tracking.locationFieldName,
)) {
// This class has already been transformed. Skip
return;
}
clazz.implementedTypes.add(
new Supertype(_hasCreationLocationClass, <DartType>[]),
new Supertype(tracking.hasCreationLocationClass, <DartType>[]),
);
changedStructureNotifier?.registerClassHierarchyChange(clazz);
// We intentionally use the library context of the _HasCreationLocation
// class for the private field even if [clazz] is in a different library
// so that all classes implementing Widget behave consistently.
// so that all classes implementing the tracking class behave consistently.
final Name fieldName = new Name(
_locationFieldName,
_hasCreationLocationClass.enclosingLibrary,
tracking.locationFieldName,
tracking.hasCreationLocationClass.enclosingLibrary,
);
final Field locationField = new Field.immutable(
fieldName,
type: new InterfaceType(_locationClass, clazz.enclosingLibrary.nullable),
type: new InterfaceType(
tracking.locationClass,
clazz.enclosingLibrary.nullable,
),
isFinal: true,
fieldReference: clazz.reference.canonicalName
?.getChildFromFieldWithName(fieldName)
@@ -470,7 +503,7 @@ class WidgetCreatorTracker {
final VariableDeclaration variable = new VariableDeclaration(
_creationLocationParameterName,
type: new InterfaceType(
_locationClass,
tracking.locationClass,
clazz.enclosingLibrary.nullable,
),
initializer: new NullLiteral(),
@@ -492,7 +525,6 @@ class WidgetCreatorTracker {
initializer.arguments,
initializer.target.function,
new VariableGet(variable),
_locationClass,
);
hasRedirectingInitializer = true;
break;
@@ -512,7 +544,7 @@ class WidgetCreatorTracker {
// constructor.initializers.add(new AssertInitializer(
// new AssertStatement(
// new IsExpression(
// new VariableGet(variable), _locationClass.thisType),
// new VariableGet(variable), tracking.locationClass.thisType),
// conditionStartOffset: constructor.fileOffset,
// conditionEndOffset: constructor.fileOffset,
// )));
@@ -525,7 +557,7 @@ class WidgetCreatorTracker {
/// Transform the given [libraries].
///
/// The libraries from [module] is searched for the Widget class,
/// The [moduleLibraries] are searched for the Widget class,
/// the _Location class, the _HasCreationLocation class and the
/// _WidgetFactory class.
/// If the component does not contain them, the ones from a previous run is
@@ -537,18 +569,18 @@ class WidgetCreatorTracker {
/// compilation where the class hierarchy is kept between compiles and thus
/// has to be kept up to date.
void transform(
Component module,
List<Library> libraries,
List<Library> moduleLibraries,
ChangedStructureNotifier? changedStructureNotifier,
) {
if (libraries.isEmpty) {
return;
}
_resolveFlutterClasses(module.libraries);
_resolveWellKnownClasses(moduleLibraries);
if (!_foundClasses) {
// This application doesn't actually use the package:flutter library.
// Neither package:flutter nor dart:developer tracking classes found.
return;
}
@@ -579,11 +611,7 @@ class WidgetCreatorTracker {
// Transform call sites to pass the location parameter.
final _WidgetCallSiteTransformer callsiteTransformer =
new _WidgetCallSiteTransformer(
widgetClass: _widgetClass,
locationClass: _locationClass,
tracker: this,
);
new _WidgetCallSiteTransformer(tracker: this);
for (Library library in libraries) {
callsiteTransformer.enterLibrary(library);
@@ -592,26 +620,110 @@ class WidgetCreatorTracker {
}
}
bool _isSubclassOfWidget(Class clazz) => _isSubclassOf(clazz, _widgetClass);
_TrackingClasses? _getTrackingClasses(Class clazz) {
// Legacy Case: Check for widget class.
if (_isSubclassOfWidget(clazz)) {
if (_hasCreationLocationClass != null && _locationClass != null) {
return new _TrackingClasses(
hasCreationLocationClass: _hasCreationLocationClass!,
locationClass: _locationClass!,
locationFieldName: _locationFieldName,
);
}
}
bool _isSubclassOf(Class a, Class b) {
// New Case: Check for 'pragma('track-creation-locations')' annotation.
if (_hasTrackCreationLocationsPragmaAnnotation(clazz)) {
if (_developerHasCreationLocationClass != null &&
_developerCreationLocationClass != null) {
return new _TrackingClasses(
hasCreationLocationClass: _developerHasCreationLocationClass!,
locationClass: _developerCreationLocationClass!,
locationFieldName: _locationFieldName,
);
}
}
return null;
}
bool _isSubclassOfWidget(Class clazz) {
if (_widgetClass == null) return false;
return _isSubclassWhere(clazz, (Class c) => c == _widgetClass);
}
bool _hasTrackCreationLocationsPragmaAnnotation(Class clazz) {
if (_developerHasCreationLocationClass == null) return false;
return _isSubclassWhere(clazz, (Class c) {
for (Expression annotation in c.annotations) {
// Case before constant evaluation (newly compiled modules).
if (annotation is RedirectingFactoryInvocation) {
final expression = annotation.expression;
if (expression is ConstructorInvocation) {
final Class enclosingClass = expression.target.enclosingClass;
if (enclosingClass.name == 'pragma' &&
enclosingClass.enclosingLibrary.importUri.toString() ==
'dart:core') {
if (expression.arguments.positional.isNotEmpty) {
final Expression firstArg =
expression.arguments.positional.first;
if (firstArg is StringLiteral &&
firstArg.value == 'track-creation-locations') {
return true;
}
}
}
}
}
// Case after constant evaluation (incremental or modular compilation).
if (annotation case ConstantExpression(
constant: final InstanceConstant constant,
)) {
final Class enclosingClass = constant.classNode;
if (enclosingClass.name == 'pragma' &&
enclosingClass.enclosingLibrary.importUri.toString() ==
'dart:core') {
for (final Constant value in constant.fieldValues.values) {
if (value case StringConstant(
value: 'track-creation-locations',
)) {
return true;
}
}
}
}
}
return false;
});
}
bool _isSubclassWhere(Class a, bool Function(Class b) predicate) {
// TODO(askesc): Cache results.
// TODO(askesc): Test for subtype rather than subclass.
Class? current = a;
while (current != null) {
if (current == b) return true;
if (predicate(current)) return true;
current = current.superclass;
}
return false;
}
bool _hasWidgetFactoryAnnotation(Procedure node) =>
_isAnnotatedWithNamedValueOfType(node, _widgetFactoryClass!);
_isAnnotatedWithNamedValueOfType(
node,
_widgetFactoryClass,
_widgetFactoryClass,
);
bool _isAnnotatedWithNamedValueOfType(
Annotatable node,
Class annotationClass,
Class? annotationClass,
Class? typeClass,
) {
if (annotationClass == null || typeClass == null) return false;
return node.annotations.any((annotation) {
if (annotation is! StaticGet) {
return false;
@@ -627,7 +739,7 @@ class WidgetCreatorTracker {
if (type.nullability == Nullability.nullable) {
return false;
}
return type.classNode == _widgetFactoryClass;
return type.classNode == typeClass;
});
}
@@ -637,7 +749,8 @@ class WidgetCreatorTracker {
Class clazz,
ChangedStructureNotifier? changedStructureNotifier,
) {
if (!_isSubclassOfWidget(clazz) ||
final _TrackingClasses? tracking = _getTrackingClasses(clazz);
if (tracking == null ||
!librariesToBeTransformed.contains(clazz.enclosingLibrary) ||
!transformedClasses.add(clazz)) {
return;
@@ -661,7 +774,7 @@ class WidgetCreatorTracker {
new VariableDeclaration(
_creationLocationParameterName,
type: new InterfaceType(
_locationClass,
tracking.locationClass,
clazz.enclosingLibrary.nullable,
),
initializer: new NullLiteral(),
@@ -672,8 +785,14 @@ class WidgetCreatorTracker {
// Handle the widget class and classes that implement but do not extend the
// widget class.
if (!_isSubclassOfWidget(clazz.superclass!)) {
_transformClassImplementingWidget(clazz, changedStructureNotifier);
if (clazz.superclass == null ||
_getTrackingClasses(clazz.superclass!)?.hasCreationLocationClass !=
tracking.hasCreationLocationClass) {
_transformClassImplementingTrackingClass(
clazz,
tracking,
changedStructureNotifier,
);
return;
}
@@ -688,7 +807,7 @@ class WidgetCreatorTracker {
final VariableDeclaration variable = new VariableDeclaration(
_creationLocationParameterName,
type: new InterfaceType(
_locationClass,
tracking.locationClass,
clazz.enclosingLibrary.nullable,
),
initializer: new NullLiteral(),
@@ -717,16 +836,17 @@ class WidgetCreatorTracker {
initializer.arguments,
initializer.target.function,
new VariableGet(variable),
_locationClass,
);
} else if (initializer is SuperInitializer &&
_isSubclassOfWidget(initializer.target.enclosingClass)) {
_maybeAddCreationLocationArgument(
initializer.arguments,
initializer.target.function,
new VariableGet(variable),
_locationClass,
);
} else if (initializer is SuperInitializer) {
final Class superclass = initializer.target.enclosingClass;
if (_getTrackingClasses(superclass)?.hasCreationLocationClass ==
tracking.hasCreationLocationClass) {
_maybeAddCreationLocationArgument(
initializer.arguments,
initializer.target.function,
new VariableGet(variable),
);
}
}
}
}
@@ -761,7 +881,7 @@ class WidgetCreatorTracker {
new VariableDeclaration(
_creationLocationParameterName,
type: new InterfaceType(
_locationClass,
_locationClass!,
extension.enclosingLibrary.nullable,
),
initializer: new NullLiteral(),
@@ -775,7 +895,7 @@ class WidgetCreatorTracker {
new VariableDeclaration(
_creationLocationParameterName,
type: new InterfaceType(
_locationClass,
_locationClass!,
extension.enclosingLibrary.nullable,
),
initializer: new NullLiteral(),
@@ -785,3 +905,17 @@ class WidgetCreatorTracker {
}
}
}
/// Holds the set of creation location classes, either
/// from `package:flutter` or `dart:developer`.
class _TrackingClasses {
final Class hasCreationLocationClass;
final Class locationClass;
final String locationFieldName;
_TrackingClasses({
required this.hasCreationLocationClass,
required this.locationClass,
required this.locationFieldName,
});
}
@@ -0,0 +1,131 @@
// 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 VariableDeclaration('file', type: const DynamicType()),
new VariableDeclaration('line', type: const DynamicType()),
new VariableDeclaration('column', type: const DynamicType()),
new VariableDeclaration('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 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);
}
@@ -114,9 +114,14 @@ final ArgParser _argParser = ArgParser(allowTrailingOptions: true)
help: 'Print this help message.',
)
..addFlag(
'track-widget-creation',
'track-creation-locations',
help: 'Run a kernel transformer to track creation locations for widgets.',
defaultsTo: false,
aliases: [
// TODO(http://dartbug.com/63225): Remove this once flutter is migrated
// to the new flag.
'track-widget-creation',
],
)
..addOption(
'invocation-modes',
@@ -180,7 +185,7 @@ Future<int> runCompilerWithCommandLineArguments(List<String> arguments) async {
final String? importDill = options['import-dill'];
final String messageVerbosity = options['verbosity'];
final String cfeInvocationModes = options['invocation-modes'];
final bool trackWidgetCreation = options['track-widget-creation'];
final bool trackCreationLocations = options['track-creation-locations'];
final TargetCPU targetCPU = TargetCPU.fromName(options['target-arch']);
final ImageFormat imageFormat = ImageFormat.fromName(options['image-format']);
@@ -225,7 +230,7 @@ Future<int> runCompilerWithCommandLineArguments(List<String> arguments) async {
..verbosity = verbosity
..target = createFrontEndTarget(
targetName,
trackWidgetCreation: trackWidgetCreation,
trackCreationLocations: trackCreationLocations,
supportMirrors: false,
isClosureContextLoweringEnabled: false,
);
+9 -4
View File
@@ -243,9 +243,14 @@ void declareCompilerOptions(ArgParser args) {
help: 'Print this help message.',
);
args.addFlag(
'track-widget-creation',
'track-creation-locations',
help: 'Run a kernel transformer to track creation locations for widgets.',
defaultsTo: false,
aliases: [
// TODO(http://dartbug.com/63225): Remove this once flutter is migrated
// to the new flag.
'track-widget-creation',
],
);
args.addMultiOption(
'delete-tostring-package-uri',
@@ -435,7 +440,7 @@ Future<int> runCompiler(ArgResults options, String usage) async {
compilerOptions.target = createFrontEndTarget(
targetName,
trackWidgetCreation: options['track-widget-creation'],
trackCreationLocations: options['track-creation-locations'],
supportMirrors: supportMirrors ?? !(aot || minimalKernel),
constKeepLocalsIndicator: !(aot || minimalKernel),
);
@@ -1014,7 +1019,7 @@ bool parseCommandLineDefines(
/// Create front-end target with given name.
Target? createFrontEndTarget(
String targetName, {
bool trackWidgetCreation = false,
bool trackCreationLocations = false,
bool supportMirrors = true,
bool includeUnsupportedPlatformLibraryStubs = false,
bool? constKeepLocalsIndicator,
@@ -1024,7 +1029,7 @@ Target? createFrontEndTarget(
installAdditionalTargets();
final TargetFlags targetFlags = new TargetFlags(
trackWidgetCreation: trackWidgetCreation,
trackCreationLocations: trackCreationLocations,
supportMirrors: supportMirrors,
includeUnsupportedPlatformLibraryStubs:
includeUnsupportedPlatformLibraryStubs,
-28
View File
@@ -2,18 +2,12 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:kernel/ast.dart' show Component, Library;
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/target/changed_structure_notifier.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/transformations/track_widget_constructor_locations.dart';
import 'package:vm/modular/target/vm.dart' show VmTarget;
class FlutterTarget extends VmTarget {
FlutterTarget(TargetFlags flags) : super(flags);
late final WidgetCreatorTracker _widgetTracker = WidgetCreatorTracker();
@override
String get name => 'flutter';
@@ -51,26 +45,4 @@ class FlutterTarget extends VmTarget {
@override
DartLibrarySupport get dartLibrarySupport =>
const CustomizedDartLibrarySupport(unsupported: {'mirrors'});
@override
void performPreConstantEvaluationTransformations(
Component component,
CoreTypes coreTypes,
List<Library> libraries,
DiagnosticReporter diagnosticReporter, {
void Function(String msg)? logger,
ChangedStructureNotifier? changedStructureNotifier,
}) {
super.performPreConstantEvaluationTransformations(
component,
coreTypes,
libraries,
diagnosticReporter,
logger: logger,
changedStructureNotifier: changedStructureNotifier,
);
if (flags.trackWidgetCreation) {
_widgetTracker.transform(component, libraries, changedStructureNotifier);
}
}
}
+11
View File
@@ -9,6 +9,7 @@ import 'package:kernel/core_types.dart';
import 'package:kernel/reference_from_index.dart';
import 'package:kernel/target/changed_structure_notifier.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/transformations/track_widget_constructor_locations.dart';
import '../transformations/call_site_annotator.dart' as callSiteAnnotator;
import '../transformations/deeply_immutable.dart' as deeply_immutable;
@@ -135,6 +136,8 @@ class VmTarget extends Target {
..parent = host;
}
late final WidgetCreatorTracker _widgetTracker = WidgetCreatorTracker();
@override
void performPreConstantEvaluationTransformations(
Component component,
@@ -153,6 +156,14 @@ class VmTarget extends Target {
changedStructureNotifier: changedStructureNotifier,
);
_patchVmConstants(coreTypes);
if (flags.trackCreationLocations) {
_widgetTracker.transform(
libraries,
component.libraries,
changedStructureNotifier,
);
}
}
@override
+1
View File
@@ -23,6 +23,7 @@ These pragmas are part of the VM's API and are safe for use in external code.
| `vm:align-loops` | Tells compiler to align all loop headers inside the function to an architecture specific boundary: currently 32 bytes on X64 and ARM64 (except Apple Silicon, which explicitly discourages aligning branch targets) |
| `vm:no-sanitize-thread` | Disable ThreadSanitizer instrumentation |
| `external-effect` | Declares a static method which will be treated as live code when performing any analysis of the program. The call itself (and its arguments) are then dropped from the running program.
| `track-creation-locations` | Marks a class, instances of which should have their creation locations tracked. Tracked creation locations are stored in a private field of the class and can be accessed through the `CreationLocation.of(object)` method in `dart:developer`. |
## Unsafe pragmas for general use
+95
View File
@@ -0,0 +1,95 @@
// 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.
part of dart.developer;
/// Holds the source code location where an object was created, when its class
/// was annotated with `@pragma('track-creation-locations')`.
///
/// When a class definition is annotated with `@pragma('track-creation-locations')`,
/// the Dart compiler injects the call-site location into any invocation of that
/// class's or any subclass's constructors and stores it in the created object.
///
/// The location of such an object can be read by calling [CreationLocation.of]
/// with the object as the argument.
///
/// ## Example
///
/// ```dart
/// import 'dart:developer';
///
/// // Marks this and any subclass to have their constructor call-sites tracked.
/// @pragma('track-creation-locations')
/// class TargetClass {
/// TargetClass();
/// }
///
/// void main() {
/// // The source-code location of this constructor call is injected into the object.
/// final instance = TargetClass();
///
/// final location = CreationLocation.of(instance);
/// print(location); // Will print the current file path, line 11, column 20
/// }
/// ```
///
/// ## Limitations
///
/// The compiler transformation relies on injecting a named parameter into the
/// target class's constructors. Since Dart semantics do not permit a function
/// to have both optional positional parameters and named parameters simultaneously,
/// this transformation **will silently skip** any constructor that declares optional
/// positional parameters. Calling [CreationLocation.of] on an object whose
/// constructor was skipped, will return `null`.
final class CreationLocation {
/// Returns the creation location of [object].
///
/// The provided object must be an instance of a class annotated with
/// `@pragma('track-creation-locations')`.
static CreationLocation? of(Object? object) {
if (object is _HasCreationLocation) {
return object._location;
}
return null;
}
const CreationLocation._({
required this.file,
required this.line,
required this.column,
this.name,
});
/// File path of the location.
final String file;
/// 1-based line number.
final int line;
/// 1-based column number.
final int column;
/// Optional name of the parameter or function at this location.
final String? name;
/// JSON representation of this location.
Map<String, Object?> toJsonMap() {
return <String, Object?>{
'file': file,
'line': line,
'column': column,
'name': name,
};
}
@override
String toString() => <String>[?name, file, '$line', '$column'].join(':');
}
/// Interface for classes that track the source code location their
/// constructor was called from.
abstract interface class _HasCreationLocation {
/// The location where the constructor was called.
CreationLocation? _location;
}
+1
View File
@@ -79,6 +79,7 @@ import 'dart:collection';
import 'dart:convert';
import 'dart:isolate' show Isolate, RawReceivePort, SendPort;
part 'creation_tracking.dart';
part 'extension.dart';
part 'http_profiling.dart';
part 'profiler.dart';
+1
View File
@@ -6,6 +6,7 @@ developer_sdk_sources = [
"developer.dart",
# The above file needs to be first if additional parts are added to the lib.
"creation_tracking.dart",
"extension.dart",
"profiler.dart",
"service.dart",