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}; }