From 0b8e3f3071ebcb3b209620cd45ca56bbfbf41e49 Mon Sep 17 00:00:00 2001 From: Marcus Hoffmann Date: Fri, 2 Jul 2021 12:17:05 +0200 Subject: [PATCH] make tryGet* safer * check casts (for list and map) at cast time by recreating the list/map instead of throwing on access. * type warnings work now slightly differently: * when we expect nullable fields to be actually null within normal operation you can explicitly set the tryGet type param to an optional type and mute the warning. Otherwise it'll warn that something is null which we expect to something else. --- .../model/events/room_encrypted_content.dart | 4 +- lib/src/utils/try_get_map_extension.dart | 41 ++++++++++--------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/lib/src/model/events/room_encrypted_content.dart b/lib/src/model/events/room_encrypted_content.dart index b53c561c..060c328e 100644 --- a/lib/src/model/events/room_encrypted_content.dart +++ b/lib/src/model/events/room_encrypted_content.dart @@ -47,8 +47,8 @@ class RoomEncryptedContent { RoomEncryptedContent.fromJson(Map json) : algorithm = json.tryGet('algorithm') ?? '', senderKey = json.tryGet('sender_key') ?? '', - deviceId = json.tryGet('device_id'), - sessionId = json.tryGet('session_id'), + deviceId = json.tryGet('device_id'), + sessionId = json.tryGet('session_id'), ciphertextMegolm = json.silentTryGet('ciphertext'), // filter out invalid/incomplete CiphertextInfos ciphertextOlm = json diff --git a/lib/src/utils/try_get_map_extension.dart b/lib/src/utils/try_get_map_extension.dart index fe6a9d4d..41d0c768 100644 --- a/lib/src/utils/try_get_map_extension.dart +++ b/lib/src/utils/try_get_map_extension.dart @@ -21,15 +21,16 @@ * SOFTWARE. */ +import 'dart:core'; + import 'logs.dart'; extension TryGetMapExtension on Map { - T? tryGet(String key) { - final value = this[key]; - if (value != null && !(value is T)) { + T? tryGet(String key) { + final Object? value = this[key]; + if (value is! T) { Logs().w( - 'Expected "${T.runtimeType}" in event content for the Key "$key" but got "${value.runtimeType}".', - StackTrace.current); + 'Expected "$T" in event content for the Key "$key" but got "${value.runtimeType}".'); return null; } return value; @@ -37,43 +38,45 @@ extension TryGetMapExtension on Map { /// Same as tryGet but without logging any warnings. /// This is helpful if you have a field that can mean multiple things on purpose. - T? silentTryGet(String key) { - final value = this[key]; - if (value != null && !(value is T)) { + T? silentTryGet(String key) { + final Object? value = this[key]; + if (value is! T) { return null; } return value; } List? tryGetList(String key) { - final value = this[key]; - if (value != null && !(value is List)) { + final Object? value = this[key]; + if (value is! List) { Logs().w( - 'Expected "List<${T.runtimeType}>" in event content for the key "$key" but got "${value.runtimeType}".', + 'Expected "List<$T>" in event content for the key "$key" but got "${value.runtimeType}".', StackTrace.current); return null; } try { - return (value as List).cast(); + // copy entries to ensure type check failures here and not an access + return value.cast().toList(); } catch (_) { - Logs().w( - 'Unable to create "List<${T.runtimeType}>" in event content for the key "$key"'); + Logs() + .w('Unable to create "List<$T>" in event content for the key "$key"'); return null; } } Map? tryGetMap(String key) { - final value = this[key]; - if (value != null && !(value is Map)) { + final Object? value = this[key]; + if (value is! Map) { Logs().w( - 'Expected "Map<${A.runtimeType},${B.runtimeType}>" in event content for the key "$key" but got "${value.runtimeType}".'); + 'Expected "Map<$A,$B>" in event content for the key "$key" but got "${value.runtimeType}".'); return null; } try { - return (value as Map).cast(); + // copy map to ensure type check failures here and not an access + return Map.from(value.cast()); } catch (_) { Logs().w( - 'Unable to create "Map<${A.runtimeType},${B.runtimeType}>" in event content for the key "$key"'); + 'Unable to create "Map<$A,$B>" in event content for the key "$key"'); return null; } }