/// <summary> /// Called on every representation to perform a normalization pass /// to convert many common object types to those that can be serialized /// in a normalized way. This provides a small safety layer as well, /// ensuring that we are only serializing objects that are safe/supported /// to do so across all agents*clients or explicitly declare themselves /// as an IRepresentationObject (an opt-in "contract" that says that /// clients should be able to deserialize it). /// </summary> /// <param name="obj">The object to normalize.</param> object Normalize(object obj) { if (obj == null) { return(null); } if (obj is Representation) { // an object may be boxed in a Representation object if it has metadata // associated with it, such as whether the representation provider supports // "editing" via TryConvertFromRepresentation. We must still normalize the // value inside to ensure we can safely serialize it. If the value differs // after normalization, but is serializable, we re-box it with the normalized // value and the canEdit flag unset. var originalRepresentation = (Representation)obj; var normalizedRepresentationValue = Normalize(originalRepresentation.Value); if (Equals(originalRepresentation.Value, normalizedRepresentationValue)) { return(obj); } if (normalizedRepresentationValue == null) { return(null); } return(originalRepresentation.With( normalizedRepresentationValue, canEdit: false)); } if (agentRepresentationProvider != null) { try { var normalized = agentRepresentationProvider.NormalizeRepresentation(obj); if (normalized != null) { return(normalized); } } catch (Exception e) { Log.Error(TAG, "Agent-builtin normalizer raised an exception", e); } } if (obj is Enum) { return(new EnumValue((Enum)obj)); } if (obj is IInteractiveObject) { var interactive = (IInteractiveObject)obj; interactive.Handle = ObjectCache.Shared.GetHandle(interactive); return(interactive); } if (obj is IRepresentationObject) { return(obj); } if (obj is ISerializableObject && currentPreparePassAllowsISerializableObject) { return((JsonPayload)((ISerializableObject)obj).SerializeToString()); } if (obj is IFallbackRepresentationObject) { return(obj); } if (obj is Exception) { return(ExceptionNode.Create((Exception)obj)); } if (obj is MemberInfo) { try { var remoteMemberInfo = TypeMember.Create((MemberInfo)obj); if (remoteMemberInfo != null) { return(remoteMemberInfo); } } catch { } } if (obj is TimeSpan || obj is Guid) { return(obj); } if (obj is IntPtr) { return(new WordSizedNumber( obj, WordSizedNumberFlags.Pointer | WordSizedNumberFlags.Signed, (ulong)(IntPtr)obj)); } if (obj is UIntPtr) { return(new WordSizedNumber( obj, WordSizedNumberFlags.Pointer, (ulong)(UIntPtr)obj)); } if (Type.GetTypeCode(obj.GetType()) != TypeCode.Object) { return(obj); } if (obj is byte []) { return(obj); } return(null); }
/// <summary> /// Called on every representation to perform a normalization pass /// to convert many common object types to those that can be serialized /// in a normalized way. This provides a small safety layer as well, /// ensuring that we are only serializing objects that are safe/supported /// to do so across all agents*clients or explicitly declare themselves /// as an IRepresentationObject (an opt-in "contract" that says that /// clients should be able to deserialize it). /// </summary> /// <param name="obj">The object to normalize.</param> object Normalize(object obj) { switch (obj) { case null: return(null); case Representation originalRepresentation: // an object may be boxed in a Representation object if it has metadata // associated with it, such as whether the representation provider supports // "editing" via TryConvertFromRepresentation. We must still normalize the // value inside to ensure we can safely serialize it. If the value differs // after normalization, but is serializable, we re-box it with the normalized // value and the canEdit flag unset. var normalizedRepresentationValue = Normalize(originalRepresentation.Value); if (Equals(originalRepresentation.Value, normalizedRepresentationValue)) { return(obj); } if (normalizedRepresentationValue == null) { return(null); } return(originalRepresentation.With( normalizedRepresentationValue, canEdit: false)); } if (agentRepresentationProvider != null) { try { var normalized = agentRepresentationProvider.NormalizeRepresentation(obj); if (normalized != null) { return(normalized); } } catch (Exception e) { Log.Error(TAG, "Agent-builtin normalizer raised an exception", e); } } switch (obj) { case Enum enumValue: return(new EnumValue(enumValue)); case IInteractiveObject interactive: interactive.Handle = ObjectCache.Shared.GetHandle(interactive); return(interactive); case IRepresentationObject _: return(obj); case ISerializableObject iserializableObject when currentPreparePassAllowsISerializableObject: return((JsonPayload)iserializableObject.SerializeToString()); case IFallbackRepresentationObject _: return(obj); case Exception exception: return(ExceptionNode.Create(exception)); case MemberInfo memberInfo: try { var remoteMemberInfo = TypeMember.Create(memberInfo); if (remoteMemberInfo != null) { return(remoteMemberInfo); } } catch (Exception e) { Log.Warning(TAG, "unable to create TypeMember from MemberInfo", e); } return(null); case TimeSpan _: case Guid _: return(obj); case IntPtr intptr: return(new WordSizedNumber( obj, WordSizedNumberFlags.Pointer | WordSizedNumberFlags.Signed, (ulong)intptr)); case UIntPtr uintptr: return(new WordSizedNumber( obj, WordSizedNumberFlags.Pointer, (ulong)uintptr)); default: if (Type.GetTypeCode(obj.GetType()) != TypeCode.Object) { return(obj); } if (obj is byte []) { return(obj); } return(null); } }