Minor cleanup to CPU profile screen code (#9324)

diff --git a/packages/devtools_app/lib/src/screens/profiler/cpu_profile_model.dart b/packages/devtools_app/lib/src/screens/profiler/cpu_profile_model.dart
index 4fdeda3..33413d7 100644
--- a/packages/devtools_app/lib/src/screens/profiler/cpu_profile_model.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/cpu_profile_model.dart
@@ -153,8 +153,8 @@
     required String processId,
   }) async {
     await transformer.processData(functionProfile, processId: processId);
-    if (codeProfile != null) {
-      await transformer.processData(codeProfile!, processId: processId);
+    if (codeProfile case final codeProfile?) {
+      await transformer.processData(codeProfile, processId: processId);
     }
   }
 }
@@ -166,11 +166,13 @@
     required this.cpuSamples,
     required this.profileMetaData,
     required this.rootedAtTags,
-  }) {
-    _cpuProfileRoot = CpuStackFrame.root(profileMetaData);
-  }
+  }) : cpuProfileRoot = CpuStackFrame.root(profileMetaData);
 
   factory CpuProfileData.fromJson(Map<String, Object?> json_) {
+    if (json_.isEmpty) {
+      return CpuProfileData.empty();
+    }
+
     final json = _CpuProfileDataJson(json_);
 
     // All CPU samples.
@@ -187,15 +189,17 @@
     final samplePeriod =
         observedSamplePeriod(samples) ?? json.samplePeriod ?? 0;
 
+    final timeOriginMicros = json.timeOriginMicros;
+    final timeExtentMicros = json.timeExtentMicros;
     final profileMetaData = CpuProfileMetaData(
       sampleCount: json.sampleCount ?? 0,
       samplePeriod: samplePeriod,
       stackDepth: json.stackDepth ?? 0,
-      time: (json.timeOriginMicros != null && json.timeExtentMicros != null)
+      time: (timeOriginMicros != null && timeExtentMicros != null)
           ? (TimeRange()
-              ..start = Duration(microseconds: json.timeOriginMicros!)
+              ..start = Duration(microseconds: timeOriginMicros)
               ..end = Duration(
-                microseconds: json.timeOriginMicros! + json.timeExtentMicros!,
+                microseconds: timeOriginMicros + timeExtentMicros,
               ))
           : null,
     );
@@ -329,10 +333,10 @@
       });
 
       for (final sample in tagProfile.cpuSamples) {
-        String? updatedId = idMapping[sample.leafId];
+        String? updatedId = idMapping[sample.leafId]!;
         samples.add(
           CpuSampleEvent(
-            leafId: updatedId!,
+            leafId: updatedId,
             userTag: sample.userTag,
             vmTag: sample.vmTag,
             traceJson: sample.toJson,
@@ -540,7 +544,17 @@
     );
   }
 
-  factory CpuProfileData.empty() => CpuProfileData.fromJson({});
+  factory CpuProfileData.empty() => CpuProfileData._(
+    stackFrames: {},
+    cpuSamples: [],
+    profileMetaData: CpuProfileMetaData(
+      sampleCount: 0,
+      samplePeriod: 0,
+      stackDepth: 0,
+      time: null,
+    ),
+    rootedAtTags: false,
+  );
 
   /// Generates [CpuProfileData] from the provided [cpuSamples].
   ///
@@ -556,31 +570,27 @@
     // of all stacks, regardless of entrypoint. This should never be seen in the
     // final output from this method.
     const kRootId = 0;
-    final traceObject = <String, Object?>{
-      CpuProfileData._sampleCountKey: cpuSamples.sampleCount,
-      CpuProfileData._samplePeriodKey: cpuSamples.samplePeriod,
-      CpuProfileData._stackDepthKey: cpuSamples.maxStackDepth,
-      CpuProfileData._timeOriginKey: cpuSamples.timeOriginMicros,
-      CpuProfileData._timeExtentKey: cpuSamples.timeExtentMicros,
-      CpuProfileData._stackFramesKey: cpuSamples.generateStackFramesJson(
-        isolateId: isolateId,
-        // We want to ensure that if [kRootId] ever changes, this change is
-        // propagated to [cpuSamples.generateStackFramesJson].
-        // ignore: avoid_redundant_argument_values
-        kRootId: kRootId,
-        buildCodeTree: buildCodeTree,
-      ),
-      CpuProfileData._traceEventsKey: [],
-    };
+
+    // Generate the stack frames first as it builds and tracks
+    // the timeline tree for each sample.
+    final stackFrames = cpuSamples.generateStackFramesJson(
+      isolateId: isolateId,
+      // We want to ensure that if [kRootId] ever changes, this change is
+      // propagated to [cpuSamples.generateStackFramesJson].
+      // ignore: avoid_redundant_argument_values
+      kRootId: kRootId,
+      buildCodeTree: buildCodeTree,
+    );
 
     // Build the trace events.
+    final traceEvents = <Map<String, Object?>>[];
     for (final sample in cpuSamples.samples ?? <vm_service.CpuSample>[]) {
       final tree = _CpuProfileTimelineTree.getTreeFromSample(sample)!;
       // Skip the root.
       if (tree.frameId == kRootId) {
         continue;
       }
-      (traceObject[CpuProfileData._traceEventsKey]! as List<Object?>).add({
+      traceEvents.add({
         'ph': 'P', // kind = sample event
         'name': '', // Blank to keep about:tracing happy
         'pid': cpuSamples.pid,
@@ -588,13 +598,20 @@
         'ts': sample.timestamp,
         'cat': 'Dart',
         CpuProfileData.stackFrameIdKey: '$isolateId-${tree.frameId}',
-        'args': {
-          if (sample.userTag != null) userTagKey: sample.userTag,
-          if (sample.vmTag != null) vmTagKey: sample.vmTag,
-        },
+        'args': {userTagKey: ?sample.userTag, vmTagKey: ?sample.vmTag},
       });
     }
 
+    final traceObject = <String, Object?>{
+      CpuProfileData._sampleCountKey: cpuSamples.sampleCount,
+      CpuProfileData._samplePeriodKey: cpuSamples.samplePeriod,
+      CpuProfileData._stackDepthKey: cpuSamples.maxStackDepth,
+      CpuProfileData._timeOriginKey: cpuSamples.timeOriginMicros,
+      CpuProfileData._timeExtentKey: cpuSamples.timeExtentMicros,
+      CpuProfileData._stackFramesKey: stackFrames,
+      CpuProfileData._traceEventsKey: traceEvents,
+    };
+
     await _addPackageUrisToTraceObject(isolateId, traceObject);
 
     return CpuProfileData.fromJson(traceObject);
@@ -674,6 +691,8 @@
 
   final CpuProfileMetaData profileMetaData;
 
+  final CpuStackFrame cpuProfileRoot;
+
   /// `true` if the CpuProfileData has tag-based roots.
   ///
   /// This value is used during the bottom-up transformation to ensure that the
@@ -687,7 +706,7 @@
     if (!processed) return <CpuStackFrame>[];
     return _callTreeRoots ??= [
       // Don't display the root node.
-      ..._cpuProfileRoot.children.map((e) => e.deepCopy()),
+      for (final rootChild in cpuProfileRoot.children) rootChild.deepCopy(),
     ];
   }
 
@@ -703,7 +722,7 @@
     assert(_bottomUpRoots == null);
     _bottomUpRoots = await BottomUpTransformer<CpuStackFrame>()
         .bottomUpRootsFor(
-          topDownRoot: _cpuProfileRoot,
+          topDownRoot: cpuProfileRoot,
           mergeSamples: mergeCpuProfileRoots,
           rootedAtTags: rootedAtTags,
         );
@@ -711,41 +730,15 @@
 
   List<CpuStackFrame>? _bottomUpRoots;
 
-  CpuStackFrame get cpuProfileRoot => _cpuProfileRoot;
+  late final Iterable<String> userTags = {
+    for (final cpuSample in cpuSamples)
+      if (cpuSample.userTag case final userTag?) userTag,
+  };
 
-  Iterable<String> get userTags {
-    if (_userTags != null) {
-      return _userTags!;
-    }
-    final tags = <String>{};
-    for (final cpuSample in cpuSamples) {
-      final tag = cpuSample.userTag;
-      if (tag != null) {
-        tags.add(tag);
-      }
-    }
-    _userTags = tags;
-    return _userTags!;
-  }
-
-  Iterable<String> get vmTags {
-    if (_vmTags != null) {
-      return _vmTags!;
-    }
-    final tags = <String>{};
-    for (final cpuSample in cpuSamples) {
-      final tag = cpuSample.vmTag;
-      if (tag != null) {
-        tags.add(tag);
-      }
-    }
-    return _vmTags = tags;
-  }
-
-  Iterable<String>? _userTags;
-  Iterable<String>? _vmTags;
-
-  late final CpuStackFrame _cpuProfileRoot;
+  late final Iterable<String> vmTags = {
+    for (final cpuSample in cpuSamples)
+      if (cpuSample.vmTag case final vmTag?) vmTag,
+  };
 
   CpuStackFrame? selectedStackFrame;
 
@@ -755,10 +748,10 @@
     _samplePeriodKey: profileMetaData.samplePeriod,
     _sampleCountKey: profileMetaData.sampleCount,
     _stackDepthKey: profileMetaData.stackDepth,
-    if (profileMetaData.time?.start != null)
-      _timeOriginKey: profileMetaData.time!.start!.inMicroseconds,
-    if (profileMetaData.time?.duration != null)
-      _timeExtentKey: profileMetaData.time!.duration.inMicroseconds,
+    if (profileMetaData.time?.start case final startTime?)
+      _timeOriginKey: startTime.inMicroseconds,
+    if (profileMetaData.time?.duration case final duration?)
+      _timeExtentKey: duration.inMicroseconds,
     _stackFramesKey: stackFramesJson,
     _traceEventsKey: cpuSamples.map((sample) => sample.toJson).toList(),
   };
@@ -766,13 +759,9 @@
   bool get isEmpty => profileMetaData.sampleCount == 0;
 
   @visibleForTesting
-  Map<String, Object?> get stackFramesJson {
-    final framesJson = <String, Object?>{};
-    for (final sf in stackFrames.values) {
-      framesJson.addAll(sf.toJson);
-    }
-    return framesJson;
-  }
+  Map<String, Object?> get stackFramesJson => {
+    for (final sf in stackFrames.values) ...sf.toJson,
+  };
 }
 
 extension type _CpuProfileDataJson(Map<String, Object?> json) {
@@ -978,7 +967,7 @@
 
   @override
   String get tooltip {
-    var prefix = '';
+    final String? prefix;
     if (isNative) {
       prefix = '[Native]';
     } else if (isDartCore) {
@@ -987,8 +976,10 @@
       prefix = '[Flutter]';
     } else if (isTag) {
       prefix = '[Tag]';
+    } else {
+      prefix = null;
     }
-    final nameWithPrefix = [prefix, name].join(' ');
+    final nameWithPrefix = [?prefix, name].join(' ');
     return [
       nameWithPrefix,
       durationText(totalTime),
@@ -1074,7 +1065,7 @@
       CpuProfileData.resolvedUrlKey: rawUrl,
       CpuProfileData.resolvedPackageUriKey: packageUri,
       CpuProfileData.sourceLineKey: sourceLine,
-      if (parentId != null) CpuProfileData.parentIdKey: parentId,
+      CpuProfileData.parentIdKey: ?parentId,
     },
   };
 
diff --git a/packages/devtools_app/lib/src/screens/profiler/cpu_profile_service.dart b/packages/devtools_app/lib/src/screens/profiler/cpu_profile_service.dart
index 86bb46c..024de99 100644
--- a/packages/devtools_app/lib/src/screens/profiler/cpu_profile_service.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/cpu_profile_service.dart
@@ -37,7 +37,7 @@
     const kSamples = 'samples';
     const kCodeStack = '_codeStack';
 
-    final rawSamples = (cpuSamples.json![kSamples] as List)
+    final rawSamples = (cpuSamples.json![kSamples] as List<Object?>)
         .cast<Map<String, Object?>>();
 
     bool buildCodeProfile = false;
@@ -73,7 +73,7 @@
     );
   }
 
-  Future clearSamples() {
+  Future<void> clearSamples() {
     return serviceConnection.serviceManager.service!.clearCpuSamples(
       serviceConnection
           .serviceManager
diff --git a/packages/devtools_app/lib/src/screens/profiler/sampling_rate.dart b/packages/devtools_app/lib/src/screens/profiler/sampling_rate.dart
index fabba88..ca63726 100644
--- a/packages/devtools_app/lib/src/screens/profiler/sampling_rate.dart
+++ b/packages/devtools_app/lib/src/screens/profiler/sampling_rate.dart
@@ -35,15 +35,9 @@
 }
 
 extension CpuSamplingRateExtension on CpuSamplingRate {
-  static CpuSamplingRate fromValue(String value) {
-    switch (value) {
-      case lowProfilePeriod:
-        return CpuSamplingRate.low;
-      case highProfilePeriod:
-        return CpuSamplingRate.high;
-      case mediumProfilePeriod:
-      default:
-        return CpuSamplingRate.medium;
-    }
-  }
+  static CpuSamplingRate fromValue(String value) => switch (value) {
+    lowProfilePeriod => CpuSamplingRate.low,
+    highProfilePeriod => CpuSamplingRate.high,
+    mediumProfilePeriod || _ => CpuSamplingRate.medium,
+  };
 }