Add extension type to read performance data JSON (#6990)
diff --git a/packages/devtools_app/lib/src/screens/performance/performance_model.dart b/packages/devtools_app/lib/src/screens/performance/performance_model.dart
index 601951f..f1fdd62 100644
--- a/packages/devtools_app/lib/src/screens/performance/performance_model.dart
+++ b/packages/devtools_app/lib/src/screens/performance/performance_model.dart
@@ -285,89 +285,40 @@
class OfflinePerformanceData extends PerformanceData {
OfflinePerformanceData._({
- required List<Map<String, dynamic>>? traceEvents,
- required List<FlutterFrame>? frames,
- FlutterFrame? selectedFrame,
- required int? selectedFrameId,
- required TimelineEvent? selectedEvent,
- required double? displayRefreshRate,
- required CpuProfileData? cpuProfileData,
- required RasterStats? rasterStats,
- required RebuildCountModel? rebuildCountModel,
- }) : _selectedFrameId = selectedFrameId,
- super(
- traceEvents: traceEvents,
- frames: frames,
- selectedFrame: selectedFrame,
- selectedEvent: selectedEvent,
- displayRefreshRate: displayRefreshRate,
- cpuProfileData: cpuProfileData,
- rasterStats: rasterStats,
- rebuildCountModel: rebuildCountModel,
- );
+ required super.traceEvents,
+ required super.frames,
+ required super.selectedFrame,
+ required this.selectedFrameId,
+ required super.selectedEvent,
+ required super.displayRefreshRate,
+ required super.cpuProfileData,
+ required super.rasterStats,
+ required super.rebuildCountModel,
+ });
- static OfflinePerformanceData parse(Map<String, dynamic> json) {
- final List<Map<String, dynamic>> traceEvents =
- (json[PerformanceData.traceEventsKey] ?? [])
- .cast<Map<String, dynamic>>();
+ static OfflinePerformanceData parse(Map<String, Object?> json_) {
+ final json = _PerformanceDataJson(json_);
- final Map<String, dynamic> cpuProfileJson =
- json[PerformanceData.cpuProfileKey] ?? <String, Object>{};
- final CpuProfileData? cpuProfileData =
- cpuProfileJson.isNotEmpty ? CpuProfileData.parse(cpuProfileJson) : null;
-
- final Map<String, dynamic> rasterStatsJson =
- json[PerformanceData.rasterStatsKey] ?? <String, Object>{};
- final RasterStats? rasterStats =
- rasterStatsJson.isNotEmpty ? RasterStats.parse(rasterStatsJson) : null;
-
- final int? selectedFrameId = json[PerformanceData.selectedFrameIdKey];
-
- final List<Map<String, dynamic>> framesJson =
- ((json[PerformanceData.flutterFramesKey] as List?)?.cast<Object>() ??
- [])
- .map((f) => f as Map<String, dynamic>)
- .toList();
- final frames = framesJson
- .map((Map<String, dynamic> f) => FlutterFrame.parse(f))
- .toList();
+ final selectedFrameId = json.selectedFrameId;
+ final frames = json.frames;
final selectedFrame =
frames.firstWhereOrNull((frame) => frame.id == selectedFrameId);
- final Map<String, dynamic> selectedEventJson =
- json[PerformanceData.selectedEventKey] ?? {};
- final OfflineTimelineEvent? selectedEvent = selectedEventJson.isNotEmpty
- ? OfflineTimelineEvent(
- (selectedEventJson[TimelineEvent.firstTraceKey] ?? {})
- .cast<String, Object>(),
- )
- : null;
-
- final displayRefreshRate =
- json[PerformanceData.displayRefreshRateKey] ?? defaultRefreshRate;
-
- final Map<String, dynamic> rebuildCountModelJson =
- json[PerformanceData.rebuildCountModelKey] ?? {};
- final rebuildCountModel = rebuildCountModelJson.isNotEmpty
- ? RebuildCountModel.parse(rebuildCountModelJson)
- : null;
-
return OfflinePerformanceData._(
- traceEvents: traceEvents,
+ traceEvents: json.traceEvents,
selectedFrame: selectedFrame,
selectedFrameId: selectedFrameId,
frames: frames,
- selectedEvent: selectedEvent,
- displayRefreshRate: displayRefreshRate.toDouble(),
- cpuProfileData: cpuProfileData,
- rasterStats: rasterStats,
- rebuildCountModel: rebuildCountModel,
+ selectedEvent: json.selectedEvent,
+ displayRefreshRate: json.displayRefreshRate,
+ cpuProfileData: json.cpuProfile,
+ rasterStats: json.rasterStats,
+ rebuildCountModel: json.rebuildCountModel,
);
}
@override
- int? get selectedFrameId => _selectedFrameId;
- final int? _selectedFrameId;
+ final int? selectedFrameId;
/// Creates a new instance of [OfflinePerformanceData] with references to the
/// same objects contained in this instance.
@@ -391,6 +342,56 @@
}
}
+extension type _PerformanceDataJson(Map<String, Object?> json) {
+ List<Map<String, Object?>> get traceEvents =>
+ (json[PerformanceData.traceEventsKey] as List? ?? [])
+ .cast<Map>()
+ .map((e) => e.cast<String, Object?>())
+ .toList();
+
+ CpuProfileData? get cpuProfile {
+ final raw =
+ (json[PerformanceData.cpuProfileKey] as Map?)?.cast<String, Object>();
+ return raw == null || raw.isEmpty ? null : CpuProfileData.parse(raw);
+ }
+
+ RasterStats? get rasterStats {
+ final raw =
+ (json[PerformanceData.rasterStatsKey] as Map?)?.cast<String, Object>();
+ return raw == null || raw.isEmpty ? null : RasterStats.parse(raw);
+ }
+
+ int? get selectedFrameId => json[PerformanceData.selectedFrameIdKey] as int?;
+
+ List<FlutterFrame> get frames =>
+ (json[PerformanceData.flutterFramesKey] as List? ?? [])
+ .cast<Map>()
+ .map((f) => f.cast<String, dynamic>())
+ .map((f) => FlutterFrame.parse(f))
+ .toList();
+
+ OfflineTimelineEvent? get selectedEvent {
+ final raw = (json[PerformanceData.selectedEventKey] as Map? ?? {})
+ .cast<String, dynamic>();
+
+ if (raw.isEmpty) return null;
+
+ return OfflineTimelineEvent(
+ (raw[TimelineEvent.firstTraceKey] as Map? ?? {}).cast<String, Object>(),
+ );
+ }
+
+ double get displayRefreshRate =>
+ (json[PerformanceData.displayRefreshRateKey] as num?)?.toDouble() ??
+ defaultRefreshRate;
+
+ RebuildCountModel? get rebuildCountModel {
+ final raw = (json[PerformanceData.rebuildCountModelKey] as Map? ?? {})
+ .cast<String, dynamic>();
+ return raw.isNotEmpty ? RebuildCountModel.parse(raw) : null;
+ }
+}
+
/// Wrapper class for [TimelineEvent] that only includes information we need for
/// importing and exporting snapshots.
///
@@ -410,13 +411,13 @@
),
) {
time.end = Duration(
- microseconds: firstTrace[TraceEvent.timestampKey] +
- firstTrace[TraceEvent.durationKey],
+ microseconds: (firstTrace[TraceEvent.timestampKey] as int) +
+ (firstTrace[TraceEvent.durationKey] as int),
);
+ final typeArg =
+ (firstTrace[TraceEvent.argsKey] as Map)[TraceEvent.typeKey].toString();
type = TimelineEventType.values.firstWhere(
- (t) =>
- t.toString() ==
- firstTrace[TraceEvent.argsKey][TraceEvent.typeKey].toString(),
+ (t) => t.toString() == typeArg,
orElse: () => TimelineEventType.other,
);
}
@@ -707,13 +708,10 @@
}
Map<String, dynamic> get json {
- final modifiedTrace = Map.from(beginTraceEventJson);
- modifiedTrace[TraceEvent.argsKey]
- .addAll({TraceEvent.typeKey: type.toString()});
- if (!modifiedTrace.containsKey(TraceEvent.durationKey)) {
- modifiedTrace
- .addAll({TraceEvent.durationKey: time.duration.inMicroseconds});
- }
+ final modifiedTrace = {...beginTraceEventJson}
+ ..putIfAbsent(TraceEvent.durationKey, () => time.duration.inMicroseconds);
+ (modifiedTrace[TraceEvent.argsKey] as Map)[TraceEvent.typeKey] =
+ type.toString();
return {firstTraceKey: modifiedTrace};
}