Initial commit
[mono.git] / mcs / class / referencesource / mscorlib / system / runtime / serialization / serializationinfo.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  SerializationInfo
9 **
10 **
11 ** Purpose: The structure for holding all of the data needed
12 **          for object serialization and deserialization.
13 **
14 **
15 ===========================================================*/
16 namespace System.Runtime.Serialization
17 {
18
19     using System;
20     using System.Reflection;
21     using System.Runtime.Remoting;
22 #if FEATURE_REMOTING
23     using System.Runtime.Remoting.Proxies;
24 #endif
25     using System.Globalization;
26     using System.Diagnostics.Contracts;
27     using System.Security;
28 #if FEATURE_CORECLR
29     using System.Runtime.CompilerServices;
30 #endif 
31
32     [System.Runtime.InteropServices.ComVisible(true)]
33 #if FEATURE_CORECLR
34     [FriendAccessAllowed]
35 #endif 
36     public sealed class SerializationInfo
37     {
38         private const int defaultSize = 4;
39         private const string s_mscorlibAssemblySimpleName = "mscorlib";
40         private const string s_mscorlibFileName = s_mscorlibAssemblySimpleName + ".dll";
41         
42         internal String[] m_members;
43         internal Object[] m_data;
44         internal Type[] m_types;
45         internal int m_currMember;
46         internal IFormatterConverter m_converter;
47         private String m_fullTypeName;
48         private String m_assemName;
49         private Type objectType;
50         private bool isFullTypeNameSetExplicit;
51         private bool isAssemblyNameSetExplicit;
52 #if FEATURE_SERIALIZATION
53         private bool requireSameTokenInPartialTrust;
54 #endif
55         [CLSCompliant(false)]
56         public SerializationInfo(Type type, IFormatterConverter converter)
57 #if FEATURE_SERIALIZATION
58             : this(type, converter, false)
59         {
60         }
61
62         [CLSCompliant(false)]
63         public SerializationInfo(Type type, IFormatterConverter converter, bool requireSameTokenInPartialTrust)
64 #endif
65         {
66             if ((object)type == null)
67             {
68                 throw new ArgumentNullException("type");
69             }
70
71             if (converter == null)
72             {
73                 throw new ArgumentNullException("converter");
74             }
75
76             Contract.EndContractBlock();
77
78             objectType = type;
79             m_fullTypeName = type.FullName;
80             m_assemName = type.Module.Assembly.FullName;
81
82             m_members = new String[defaultSize];
83             m_data = new Object[defaultSize];
84             m_types = new Type[defaultSize];
85
86             m_converter = converter;
87
88 #if FEATURE_SERIALIZATION
89             this.requireSameTokenInPartialTrust = requireSameTokenInPartialTrust;
90 #endif
91         }
92
93         public String FullTypeName
94         {
95             get
96             {
97                 return m_fullTypeName;
98             }
99             set
100             {
101                 if (null == value)
102                 {
103                     throw new ArgumentNullException("value");
104                 }
105                 Contract.EndContractBlock();
106            
107                 m_fullTypeName = value;
108                 isFullTypeNameSetExplicit = true;
109             }
110         }
111
112         public String AssemblyName
113         {
114             get
115             {
116                 return m_assemName;
117             }
118 #if FEATURE_SERIALIZATION
119             [SecuritySafeCritical]
120 #endif
121             set
122             {
123                 if (null == value)
124                 {
125                     throw new ArgumentNullException("value");
126                 }
127                 Contract.EndContractBlock();
128 #if FEATURE_SERIALIZATION
129                 if (this.requireSameTokenInPartialTrust)
130                 {
131                     DemandForUnsafeAssemblyNameAssignments(this.m_assemName, value);
132                 }
133 #endif
134                 m_assemName = value;
135                 isAssemblyNameSetExplicit = true;
136             }
137         }
138 #if FEATURE_SERIALIZATION
139         [SecuritySafeCritical]
140 #endif
141         public void SetType(Type type)
142         {
143             if ((object)type == null)
144             {
145                 throw new ArgumentNullException("type");
146             }
147             Contract.EndContractBlock();
148 #if FEATURE_SERIALIZATION
149             if (this.requireSameTokenInPartialTrust)
150             {
151                 DemandForUnsafeAssemblyNameAssignments(this.ObjectType.Assembly.FullName, type.Assembly.FullName);
152             }
153 #endif
154             if (!Object.ReferenceEquals(objectType, type))
155             {
156                 objectType = type;
157                 m_fullTypeName = type.FullName;
158                 m_assemName = type.Module.Assembly.FullName;
159                 isFullTypeNameSetExplicit = false;
160                 isAssemblyNameSetExplicit = false;
161             }
162         }
163
164         private static bool Compare(byte[] a, byte[] b)
165         {
166             // if either or both assemblies do not have public key token, we should demand, hence, returning false will force a demand
167             if (a == null || b == null || a.Length == 0 || b.Length == 0 || a.Length != b.Length)
168             {
169                 return false;
170             }
171             else
172             {
173                 for (int i = 0; i < a.Length; i++)
174                 {
175                     if (a[i] != b[i]) return false;
176                 }
177
178                 return true;
179             }
180         }
181
182         [SecuritySafeCritical]
183         internal static void DemandForUnsafeAssemblyNameAssignments(string originalAssemblyName, string newAssemblyName)
184         {
185             if (!IsAssemblyNameAssignmentSafe(originalAssemblyName, newAssemblyName))
186             {
187                 CodeAccessPermission.Demand(PermissionType.SecuritySerialization);
188             }
189         }
190
191         internal static bool IsAssemblyNameAssignmentSafe(string originalAssemblyName, string newAssemblyName)
192         {
193             if (originalAssemblyName == newAssemblyName)
194             {
195                 return true;
196             }
197
198             AssemblyName originalAssembly = new AssemblyName(originalAssemblyName);
199             AssemblyName newAssembly = new AssemblyName(newAssemblyName);
200
201             // mscorlib will get loaded by the runtime regardless of its string casing or its public key token,
202             // so setting the assembly name to mscorlib must always be protected by a demand
203             if (string.Equals(newAssembly.Name, s_mscorlibAssemblySimpleName, StringComparison.OrdinalIgnoreCase) ||
204                 string.Equals(newAssembly.Name, s_mscorlibFileName, StringComparison.OrdinalIgnoreCase))
205             {
206                 return false;
207             }
208
209             return Compare(originalAssembly.GetPublicKeyToken(), newAssembly.GetPublicKeyToken());
210         }
211
212         public int MemberCount
213         {
214             get
215             {
216                 return m_currMember;
217             }
218         }
219
220         public Type ObjectType
221         {
222             get
223             {
224                 return objectType;
225             }
226         }
227
228         public bool IsFullTypeNameSetExplicit
229         {
230             get
231             {
232                 return isFullTypeNameSetExplicit;
233             }
234         }
235
236         public bool IsAssemblyNameSetExplicit
237         {
238             get
239             {
240                 return isAssemblyNameSetExplicit;
241             }
242         }
243
244         public SerializationInfoEnumerator GetEnumerator()
245         {
246             return new SerializationInfoEnumerator(m_members, m_data, m_types, m_currMember);
247         }
248
249         private void ExpandArrays()
250         {
251             int newSize;
252             Contract.Assert(m_members.Length == m_currMember, "[SerializationInfo.ExpandArrays]m_members.Length == m_currMember");
253
254             newSize = (m_currMember * 2);
255
256             //
257             // In the pathological case, we may wrap
258             //
259             if (newSize < m_currMember)
260             {
261                 if (Int32.MaxValue > m_currMember)
262                 {
263                     newSize = Int32.MaxValue;
264                 }
265             }
266
267             //
268             // Allocate more space and copy the data
269             //
270             String[] newMembers = new String[newSize];
271             Object[] newData = new Object[newSize];
272             Type[] newTypes = new Type[newSize];
273
274             Array.Copy(m_members, newMembers, m_currMember);
275             Array.Copy(m_data, newData, m_currMember);
276             Array.Copy(m_types, newTypes, m_currMember);
277
278             //
279             // Assign the new arrys back to the member vars.
280             //
281             m_members = newMembers;
282             m_data = newData;
283             m_types = newTypes;
284         }
285
286         public void AddValue(String name, Object value, Type type)
287         {
288             if (null == name)
289             {
290                 throw new ArgumentNullException("name");
291             }
292
293             if ((object)type == null)
294             {
295                 throw new ArgumentNullException("type");
296             }
297             Contract.EndContractBlock();
298
299             //
300             // Walk until we find a member by the same name or until
301             // we reach the end.  If we find a member by the same name,
302             // throw.
303             for (int i = 0; i < m_currMember; i++)
304             {
305                 if (m_members[i].Equals(name))
306                 {
307                     BCLDebug.Trace("SER", "[SerializationInfo.AddValue]Tried to add ", name, " twice to the SI.");
308
309                     throw new SerializationException(Environment.GetResourceString("Serialization_SameNameTwice"));
310                 }
311             }
312
313             AddValue(name, value, type, m_currMember);
314
315         }
316
317         public void AddValue(String name, Object value)
318         {
319             if (null == value)
320             {
321                 AddValue(name, value, typeof(Object));
322             }
323             else
324             {
325                 AddValue(name, value, value.GetType());
326             }
327         }
328
329         public void AddValue(String name, bool value)
330         {
331             AddValue(name, (Object)value, typeof(bool));
332         }
333
334         public void AddValue(String name, char value)
335         {
336             AddValue(name, (Object)value, typeof(char));
337         }
338
339
340         [CLSCompliant(false)]
341         public void AddValue(String name, sbyte value)
342         {
343             AddValue(name, (Object)value, typeof(sbyte));
344         }
345
346         public void AddValue(String name, byte value)
347         {
348             AddValue(name, (Object)value, typeof(byte));
349         }
350
351         public void AddValue(String name, short value)
352         {
353             AddValue(name, (Object)value, typeof(short));
354         }
355
356         [CLSCompliant(false)]
357         public void AddValue(String name, ushort value)
358         {
359             AddValue(name, (Object)value, typeof(ushort));
360         }
361
362         public void AddValue(String name, int value)
363         {
364             AddValue(name, (Object)value, typeof(int));
365         }
366
367         [CLSCompliant(false)]
368         public void AddValue(String name, uint value)
369         {
370             AddValue(name, (Object)value, typeof(uint));
371         }
372
373         public void AddValue(String name, long value)
374         {
375             AddValue(name, (Object)value, typeof(long));
376         }
377
378         [CLSCompliant(false)]
379         public void AddValue(String name, ulong value)
380         {
381             AddValue(name, (Object)value, typeof(ulong));
382         }
383
384         public void AddValue(String name, float value)
385         {
386             AddValue(name, (Object)value, typeof(float));
387         }
388
389         public void AddValue(String name, double value)
390         {
391             AddValue(name, (Object)value, typeof(double));
392         }
393
394         public void AddValue(String name, decimal value)
395         {
396             AddValue(name, (Object)value, typeof(decimal));
397         }
398
399         public void AddValue(String name, DateTime value)
400         {
401             AddValue(name, (Object)value, typeof(DateTime));
402         }
403
404         internal void AddValue(String name, Object value, Type type, int index)
405         {
406             //
407             // If we need to expand the arrays, do so.
408             //
409             if (index >= m_members.Length)
410             {
411                 ExpandArrays();
412             }
413
414             //
415             // Add the data and then advance the counter.
416             //
417             m_members[index] = name;
418             m_data[index] = value;
419             m_types[index] = type;
420             m_currMember++;
421         }
422
423         /*=================================UpdateValue==================================
424         **Action: Finds the value if it exists in the current data.  If it does, we replace
425         **        the values, if not, we append it to the end.  This is useful to the 
426         **        ObjectManager when it's performing fixups, but shouldn't be used by 
427         **        clients.  Exposing out this functionality would allow children to overwrite
428         **        their parent's values.
429         **Returns: void
430         **Arguments: name  -- the name of the data to be updated.
431         **           value -- the new value.
432         **           type  -- the type of the data being added.
433         **Exceptions: None.  All error checking is done with asserts.
434         ==============================================================================*/
435         internal void UpdateValue(String name, Object value, Type type)
436         {
437             Contract.Assert(null != name, "[SerializationInfo.UpdateValue]name!=null");
438             Contract.Assert(null != value, "[SerializationInfo.UpdateValue]value!=null");
439             Contract.Assert(null != (object)type, "[SerializationInfo.UpdateValue]type!=null");
440
441             int index = FindElement(name);
442             if (index < 0)
443             {
444                 AddValue(name, value, type, m_currMember);
445             }
446             else
447             {
448                 m_members[index] = name;
449                 m_data[index] = value;
450                 m_types[index] = type;
451             }
452
453         }
454
455         private int FindElement(String name)
456         {
457             if (null == name)
458             {
459                 throw new ArgumentNullException("name");
460             }
461             Contract.EndContractBlock();
462             BCLDebug.Trace("SER", "[SerializationInfo.FindElement]Looking for ", name, " CurrMember is: ", m_currMember);
463             for (int i = 0; i < m_currMember; i++)
464             {
465                 Contract.Assert(m_members[i] != null, "[SerializationInfo.FindElement]Null Member in String array.");
466                 if (m_members[i].Equals(name))
467                 {
468                     return i;
469                 }
470             }
471             return -1;
472         }
473
474         /*==================================GetElement==================================
475         **Action: Use FindElement to get the location of a particular member and then return
476         **        the value of the element at that location.  The type of the member is
477         **        returned in the foundType field.
478         **Returns: The value of the element at the position associated with name.
479         **Arguments: name -- the name of the element to find.
480         **           foundType -- the type of the element associated with the given name.
481         **Exceptions: None.  FindElement does null checking and throws for elements not 
482         **            found.
483         ==============================================================================*/
484         private Object GetElement(String name, out Type foundType)
485         {
486             int index = FindElement(name);
487             if (index == -1)
488             {
489                 throw new SerializationException(Environment.GetResourceString("Serialization_NotFound", name));
490             }
491
492             Contract.Assert(index < m_data.Length, "[SerializationInfo.GetElement]index<m_data.Length");
493             Contract.Assert(index < m_types.Length, "[SerializationInfo.GetElement]index<m_types.Length");
494
495             foundType = m_types[index];
496             Contract.Assert((object)foundType != null, "[SerializationInfo.GetElement]foundType!=null");
497             return m_data[index];
498         }
499
500         [System.Runtime.InteropServices.ComVisible(true)]
501         // 
502         private Object GetElementNoThrow(String name, out Type foundType)
503         {
504             int index = FindElement(name);
505             if (index == -1)
506             {
507                 foundType = null;
508                 return null;
509             }
510
511             Contract.Assert(index < m_data.Length, "[SerializationInfo.GetElement]index<m_data.Length");
512             Contract.Assert(index < m_types.Length, "[SerializationInfo.GetElement]index<m_types.Length");
513
514             foundType = m_types[index];
515             Contract.Assert((object)foundType != null, "[SerializationInfo.GetElement]foundType!=null");
516             return m_data[index];
517         }
518
519         //
520         // The user should call one of these getters to get the data back in the 
521         // form requested.  
522         //
523
524         [System.Security.SecuritySafeCritical]  // auto-generated
525         public Object GetValue(String name, Type type)
526         {
527
528             if ((object)type == null)
529             {
530                 throw new ArgumentNullException("type");
531             }
532             Contract.EndContractBlock();
533
534             RuntimeType rt = type as RuntimeType;
535             if (rt == null)
536                 throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"));
537
538             Type foundType;
539             Object value;
540
541             value = GetElement(name, out foundType);
542 #if FEATURE_REMOTING
543             if (RemotingServices.IsTransparentProxy(value))
544             {
545                 RealProxy proxy = RemotingServices.GetRealProxy(value);
546                 if (RemotingServices.ProxyCheckCast(proxy, rt))
547                     return value;
548             }
549             else
550 #endif
551                 if (Object.ReferenceEquals(foundType, type) || type.IsAssignableFrom(foundType) || value == null)
552                 {
553                     return value;
554                 }
555
556             Contract.Assert(m_converter != null, "[SerializationInfo.GetValue]m_converter!=null");
557
558             return m_converter.Convert(value, type);
559         }
560
561         [System.Security.SecuritySafeCritical]  // auto-generated
562         [System.Runtime.InteropServices.ComVisible(true)]
563         // 
564         internal Object GetValueNoThrow(String name, Type type)
565         {
566             Type foundType;
567             Object value;
568
569             Contract.Assert((object)type != null, "[SerializationInfo.GetValue]type ==null");
570             Contract.Assert(type is RuntimeType, "[SerializationInfo.GetValue]type is not a runtime type");
571
572             value = GetElementNoThrow(name, out foundType);
573             if (value == null)
574                 return null;
575 #if FEATURE_REMOTING
576             if (RemotingServices.IsTransparentProxy(value))
577             {
578                 RealProxy proxy = RemotingServices.GetRealProxy(value);
579                 if (RemotingServices.ProxyCheckCast(proxy, (RuntimeType)type))
580                     return value;
581             }
582             else
583 #endif
584                 if (Object.ReferenceEquals(foundType, type) || type.IsAssignableFrom(foundType) || value == null)
585                 {
586                     return value;
587                 }
588
589             Contract.Assert(m_converter != null, "[SerializationInfo.GetValue]m_converter!=null");
590
591             return m_converter.Convert(value, type);
592         }
593
594         public bool GetBoolean(String name)
595         {
596             Type foundType;
597             Object value;
598
599             value = GetElement(name, out foundType);
600             if (Object.ReferenceEquals(foundType, typeof(bool)))
601             {
602                 return (bool)value;
603             }
604             return m_converter.ToBoolean(value);
605         }
606
607         public char GetChar(String name)
608         {
609             Type foundType;
610             Object value;
611
612             value = GetElement(name, out foundType);
613             if (Object.ReferenceEquals(foundType, typeof(char)))
614             {
615                 return (char)value;
616             }
617             return m_converter.ToChar(value);
618         }
619
620         [CLSCompliant(false)]
621         public sbyte GetSByte(String name)
622         {
623             Type foundType;
624             Object value;
625
626             value = GetElement(name, out foundType);
627             if (Object.ReferenceEquals(foundType, typeof(sbyte)))
628             {
629                 return (sbyte)value;
630             }
631             return m_converter.ToSByte(value);
632         }
633
634         public byte GetByte(String name)
635         {
636             Type foundType;
637             Object value;
638
639             value = GetElement(name, out foundType);
640             if (Object.ReferenceEquals(foundType, typeof(byte)))
641             {
642                 return (byte)value;
643             }
644             return m_converter.ToByte(value);
645         }
646
647         public short GetInt16(String name)
648         {
649             Type foundType;
650             Object value;
651
652             value = GetElement(name, out foundType);
653             if (Object.ReferenceEquals(foundType, typeof(short)))
654             {
655                 return (short)value;
656             }
657             return m_converter.ToInt16(value);
658         }
659
660         [CLSCompliant(false)]
661         public ushort GetUInt16(String name)
662         {
663             Type foundType;
664             Object value;
665
666             value = GetElement(name, out foundType);
667             if (Object.ReferenceEquals(foundType, typeof(ushort)))
668             {
669                 return (ushort)value;
670             }
671             return m_converter.ToUInt16(value);
672         }
673
674         public int GetInt32(String name)
675         {
676             Type foundType;
677             Object value;
678
679             value = GetElement(name, out foundType);
680             if (Object.ReferenceEquals(foundType, typeof(int)))
681             {
682                 return (int)value;
683             }
684             return m_converter.ToInt32(value);
685         }
686
687         [CLSCompliant(false)]
688         public uint GetUInt32(String name)
689         {
690             Type foundType;
691             Object value;
692
693             value = GetElement(name, out foundType);
694             if (Object.ReferenceEquals(foundType, typeof(uint)))
695             {
696                 return (uint)value;
697             }
698             return m_converter.ToUInt32(value);
699         }
700
701         public long GetInt64(String name)
702         {
703             Type foundType;
704             Object value;
705
706             value = GetElement(name, out foundType);
707             if (Object.ReferenceEquals(foundType, typeof(long)))
708             {
709                 return (long)value;
710             }
711             return m_converter.ToInt64(value);
712         }
713
714         [CLSCompliant(false)]
715         public ulong GetUInt64(String name)
716         {
717             Type foundType;
718             Object value;
719
720             value = GetElement(name, out foundType);
721             if (Object.ReferenceEquals(foundType, typeof(ulong)))
722             {
723                 return (ulong)value;
724             }
725             return m_converter.ToUInt64(value);
726         }
727
728         public float GetSingle(String name)
729         {
730             Type foundType;
731             Object value;
732
733             value = GetElement(name, out foundType);
734             if (Object.ReferenceEquals(foundType, typeof(float)))
735             {
736                 return (float)value;
737             }
738             return m_converter.ToSingle(value);
739         }
740
741
742         public double GetDouble(String name)
743         {
744             Type foundType;
745             Object value;
746
747             value = GetElement(name, out foundType);
748             if (Object.ReferenceEquals(foundType, typeof(double)))
749             {
750                 return (double)value;
751             }
752             return m_converter.ToDouble(value);
753         }
754
755         public decimal GetDecimal(String name)
756         {
757             Type foundType;
758             Object value;
759
760             value = GetElement(name, out foundType);
761             if (Object.ReferenceEquals(foundType, typeof(decimal)))
762             {
763                 return (decimal)value;
764             }
765             return m_converter.ToDecimal(value);
766         }
767
768         public DateTime GetDateTime(String name)
769         {
770             Type foundType;
771             Object value;
772
773             value = GetElement(name, out foundType);
774             if (Object.ReferenceEquals(foundType, typeof(DateTime)))
775             {
776                 return (DateTime)value;
777             }
778             return m_converter.ToDateTime(value);
779         }
780
781         public String GetString(String name)
782         {
783             Type foundType;
784             Object value;
785
786             value = GetElement(name, out foundType);
787             if (Object.ReferenceEquals(foundType, typeof(String)) || value == null)
788             {
789                 return (String)value;
790             }
791             return m_converter.ToString(value);
792         }
793
794         internal string[] MemberNames
795         {
796             get
797             {
798                 return m_members;
799             }
800
801         }
802
803         internal object[] MemberValues
804         {
805             get
806             {
807                 return m_data;
808             }
809         }
810
811     }
812 }