2004-12-08 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / corlib / System.Runtime.Serialization.Formatters.Binary / ObjectReader.cs
1 // ObjectReader.cs\r
2 //\r
3 // Author:\r
4 //   Lluis Sanchez Gual (lluis@ideary.com)\r
5 //   Patrik Torstensson\r
6 //\r
7 // (C) 2003 Lluis Sanchez Gual\r
8
9 //
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 \r
32 using System;\r
33 using System.Runtime.Serialization;\r
34 using System.IO;\r
35 using System.Collections;\r
36 using System.Reflection;\r
37 using System.Runtime.Remoting.Messaging;\r
38 using System.Globalization;\r
39 \r
40 namespace System.Runtime.Serialization.Formatters.Binary\r
41 {\r
42         internal class ObjectReader\r
43         {\r
44                 BinaryFormatter _formatter;\r
45                 ISurrogateSelector _surrogateSelector;\r
46                 StreamingContext _context;\r
47                 SerializationBinder _binder;\r
48                 \r
49 #if NET_1_1\r
50                 TypeFilterLevel _filterLevel;\r
51 #endif\r
52 \r
53                 ObjectManager _manager;\r
54                 Hashtable _registeredAssemblies = new Hashtable();\r
55                 Hashtable _typeMetadataCache = new Hashtable();\r
56 \r
57                 object _lastObject = null;\r
58                 long _lastObjectID = 0;\r
59                 long _rootObjectID = 0;\r
60 \r
61                 class TypeMetadata\r
62                 {\r
63                         public Type Type;\r
64                         public Type[] MemberTypes;\r
65                         public string[] MemberNames;\r
66                         public MemberInfo[] MemberInfos;\r
67                         public int FieldCount;\r
68                         public bool NeedsSerializationInfo;\r
69                 }\r
70 \r
71                 class ArrayNullFiller\r
72                 {\r
73                         public ArrayNullFiller(int count) { NullCount = count; }\r
74                         public int NullCount;\r
75                 }\r
76 \r
77                 public ObjectReader (BinaryFormatter formatter)\r
78                 {\r
79                         _formatter = formatter;\r
80                         _surrogateSelector = formatter.SurrogateSelector;\r
81                         _context = formatter.Context;\r
82                         _binder = formatter.Binder;\r
83                         _manager = new ObjectManager (_surrogateSelector, _context);\r
84                         \r
85 #if NET_1_1\r
86                         _filterLevel = formatter.FilterLevel;\r
87 #endif\r
88                 }\r
89 \r
90                 public void ReadObjectGraph (BinaryReader reader, bool readHeaders, out object result, out Header[] headers)\r
91                 {\r
92                         headers = null;\r
93 \r
94                         // Reads the objects. The first object in the stream is the\r
95                         // root object.\r
96 \r
97                         while (ReadNextObject (reader))\r
98                         {\r
99                                 if (readHeaders && (headers == null))\r
100                                         headers = (Header[])CurrentObject;\r
101                                 else\r
102                                         if (_rootObjectID == 0) _rootObjectID = _lastObjectID;\r
103                         }\r
104 \r
105                         result = _manager.GetObject (_rootObjectID);\r
106                 }\r
107 \r
108                 public bool ReadNextObject (BinaryReader reader)\r
109                 {\r
110                         BinaryElement element = (BinaryElement)reader.ReadByte ();\r
111                         if (element == BinaryElement.End)\r
112                         {\r
113                                 _manager.DoFixups();\r
114 \r
115                                 _manager.RaiseDeserializationEvent();\r
116                                 return false;\r
117                         }\r
118 \r
119                         SerializationInfo info;\r
120                         long objectId;\r
121 \r
122                         ReadObject (element, reader, out objectId, out _lastObject, out info);\r
123 \r
124                         if (objectId != 0) {\r
125                                 RegisterObject (objectId, _lastObject, info, 0, null, null);\r
126                                 _lastObjectID = objectId;               \r
127                         }\r
128         \r
129                         return true;\r
130                 }\r
131 \r
132                 public object CurrentObject\r
133                 {\r
134                         get { return _lastObject; }\r
135                 }\r
136 \r
137                 // Reads an object from the stream. The object is registered in the ObjectManager.\r
138                 // The result can be either the object instance\r
139                 // or the id of the object (when what is found in the stream is an object reference).\r
140                 // If an object instance is read, the objectId is set to 0.\r
141                 \r
142                 private void ReadObject (BinaryElement element, BinaryReader reader, out long objectId, out object value, out SerializationInfo info)\r
143                 {\r
144                         switch (element)\r
145                         {\r
146                                 case BinaryElement.RefTypeObject:\r
147                                         ReadRefTypeObjectInstance (reader, out objectId, out value, out info);\r
148                                         break;\r
149 \r
150                                 case BinaryElement.RuntimeObject:\r
151                                         ReadObjectInstance (reader, true, out objectId, out value, out info);\r
152                                         break;\r
153 \r
154                                 case BinaryElement.ExternalObject:\r
155                                         ReadObjectInstance (reader, false, out objectId, out value, out info);\r
156                                         break;\r
157 \r
158                                 case BinaryElement.String:\r
159                                         info = null;\r
160                                         ReadStringIntance (reader, out objectId, out value);\r
161                                         break;\r
162 \r
163                                 case BinaryElement.GenericArray:\r
164                                         info = null;\r
165                                         ReadGenericArray (reader, out objectId, out value);\r
166                                         break;\r
167 \r
168 \r
169                                 case BinaryElement.BoxedPrimitiveTypeValue:\r
170                                         value = ReadBoxedPrimitiveTypeValue (reader);\r
171                                         objectId = 0;\r
172                                         info = null;\r
173                                         break;\r
174 \r
175                                 case BinaryElement.NullValue:\r
176                                         value = null;\r
177                                         objectId = 0;\r
178                                         info = null;\r
179                                         break;\r
180 \r
181                                 case BinaryElement.Assembly:\r
182                                         ReadAssembly (reader);\r
183                                         ReadObject ((BinaryElement)reader.ReadByte (), reader, out objectId, out value, out info);\r
184                                         break;\r
185 \r
186                                 case BinaryElement.ArrayFiller8b:\r
187                                         value = new ArrayNullFiller(reader.ReadByte());\r
188                                         objectId = 0;\r
189                                         info = null;\r
190                                         break;\r
191 \r
192                                 case BinaryElement.ArrayFiller32b:\r
193                                         value = new ArrayNullFiller(reader.ReadInt32());\r
194                                         objectId = 0;\r
195                                         info = null;\r
196                                         break;\r
197 \r
198                                 case BinaryElement.ArrayOfPrimitiveType:\r
199                                         ReadArrayOfPrimitiveType (reader, out objectId, out value);\r
200                                         info = null;\r
201                                         break;\r
202 \r
203                                 case BinaryElement.ArrayOfObject:\r
204                                         ReadArrayOfObject (reader, out objectId, out value);\r
205                                         info = null;\r
206                                         break;\r
207 \r
208                                 case BinaryElement.ArrayOfString:\r
209                                         ReadArrayOfString (reader, out objectId, out value);\r
210                                         info = null;\r
211                                         break;\r
212 \r
213                                 default:\r
214                                         throw new SerializationException ("Unexpected binary element: " + (int)element);\r
215                         }\r
216                 }\r
217 \r
218                 private void ReadAssembly (BinaryReader reader)\r
219                 {\r
220                         long id = (long) reader.ReadUInt32 ();\r
221                         string assemblyName = reader.ReadString ();\r
222                         _registeredAssemblies [id] = assemblyName;\r
223                 }\r
224 \r
225                 private void ReadObjectInstance (BinaryReader reader, bool isRuntimeObject, out long objectId, out object value, out SerializationInfo info)\r
226                 {\r
227                         objectId = (long) reader.ReadUInt32 ();\r
228 \r
229                         TypeMetadata metadata = ReadTypeMetadata (reader, isRuntimeObject);\r
230                         ReadObjectContent (reader, metadata, objectId, out value, out info);\r
231                 }\r
232 \r
233                 private void ReadRefTypeObjectInstance (BinaryReader reader, out long objectId, out object value, out SerializationInfo info)\r
234                 {\r
235                         objectId = (long) reader.ReadUInt32 ();\r
236                         long refTypeObjectId = (long) reader.ReadUInt32 ();\r
237 \r
238                         // Gets the type of the referred object and its metadata\r
239 \r
240                         object refObj = _manager.GetObject (refTypeObjectId);\r
241                         if (refObj == null) throw new SerializationException ("Invalid binary format");\r
242                         TypeMetadata metadata = (TypeMetadata)_typeMetadataCache [refObj.GetType()];\r
243 \r
244                         ReadObjectContent (reader, metadata, objectId, out value, out info);\r
245                 }\r
246 \r
247                 private void ReadObjectContent (BinaryReader reader, TypeMetadata metadata, long objectId, out object objectInstance, out SerializationInfo info)\r
248                 {\r
249 #if NET_1_1\r
250                         if (_filterLevel == TypeFilterLevel.Low)\r
251                                 objectInstance = FormatterServices.GetSafeUninitializedObject (metadata.Type);\r
252                         else\r
253 #endif\r
254                                 objectInstance = FormatterServices.GetUninitializedObject (metadata.Type);\r
255                                 \r
256                         info = metadata.NeedsSerializationInfo ? new SerializationInfo(metadata.Type, new FormatterConverter()) : null;\r
257 \r
258                         if (metadata.MemberNames != null)\r
259                                 for (int n=0; n<metadata.FieldCount; n++)\r
260                                         ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberNames[n], null, null);\r
261                         else\r
262                                 for (int n=0; n<metadata.FieldCount; n++)\r
263                                         ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberInfos[n].Name, metadata.MemberInfos[n], null);\r
264                 }\r
265 \r
266                 private void RegisterObject (long objectId, object objectInstance, SerializationInfo info, long parentObjectId, MemberInfo parentObjectMemeber, int[] indices)\r
267                 {\r
268                         if (parentObjectId == 0) indices = null;\r
269 \r
270                         if (!objectInstance.GetType().IsValueType || parentObjectId == 0)\r
271                                 _manager.RegisterObject (objectInstance, objectId, info, 0, null, null);\r
272                         else\r
273                         {\r
274                                 if (indices != null) indices = (int[])indices.Clone();\r
275                                 _manager.RegisterObject (objectInstance, objectId, info, parentObjectId, parentObjectMemeber, indices);\r
276                         }\r
277                 }\r
278 \r
279                 private void ReadStringIntance (BinaryReader reader, out long objectId, out object value)\r
280                 {\r
281                         objectId = (long) reader.ReadUInt32 ();\r
282                         value = reader.ReadString ();\r
283                 }\r
284 \r
285                 private void ReadGenericArray (BinaryReader reader, out long objectId, out object val)\r
286                 {\r
287                         objectId = (long) reader.ReadUInt32 ();\r
288                         ArrayStructure structure = (ArrayStructure) reader.ReadByte();\r
289 \r
290                         int rank = reader.ReadInt32();\r
291 \r
292                         bool emptyDim = false;\r
293                         int[] lengths = new int[rank];\r
294                         for (int n=0; n<rank; n++)\r
295                         {\r
296                                 lengths[n] = reader.ReadInt32();\r
297                                 if (lengths[n] == 0) emptyDim = true;\r
298                         }\r
299 \r
300                         TypeTag code = (TypeTag) reader.ReadByte ();\r
301                         Type elementType = ReadType (reader, code);\r
302 \r
303                         Array array = Array.CreateInstance (elementType, lengths);\r
304 \r
305                         if (emptyDim) \r
306                         { \r
307                                 val = array;\r
308                                 return;\r
309                         }\r
310 \r
311                         int[] indices = new int[rank];\r
312 \r
313                         // Initialize indexes\r
314                         for (int dim = rank-1; dim >= 0; dim--)\r
315                                 indices[dim] = array.GetLowerBound (dim);\r
316 \r
317                         bool end = false;\r
318                         while (!end)\r
319                         {\r
320                                 ReadValue (reader, array, objectId, null, elementType, null, null, indices);\r
321 \r
322                                 for (int dim = array.Rank-1; dim >= 0; dim--)\r
323                                 {\r
324                                         indices[dim]++;\r
325                                         if (indices[dim] > array.GetUpperBound (dim))\r
326                                         {\r
327                                                 if (dim > 0) \r
328                                                 {\r
329                                                         indices[dim] = array.GetLowerBound (dim);\r
330                                                         continue;       // Increment the next dimension's index\r
331                                                 }\r
332                                                 end = true;     // That was the last dimension. Finished.\r
333                                         }\r
334                                         break;\r
335                                 }\r
336                         }\r
337                         val = array;\r
338                 }\r
339 \r
340                 private object ReadBoxedPrimitiveTypeValue (BinaryReader reader)\r
341                 {\r
342                         Type type = ReadType (reader, TypeTag.PrimitiveType);\r
343                         return ReadPrimitiveTypeValue (reader, type);\r
344                 }\r
345 \r
346                 private void ReadArrayOfPrimitiveType (BinaryReader reader, out long objectId, out object val)\r
347                 {\r
348                         objectId = (long) reader.ReadUInt32 ();\r
349                         int length = reader.ReadInt32 ();\r
350                         Type elementType = ReadType (reader, TypeTag.PrimitiveType);\r
351 \r
352                         Array array = Array.CreateInstance (elementType, length);\r
353                         for (int n = 0; n < length; n++)\r
354                                 array.SetValue (ReadPrimitiveTypeValue (reader, elementType), n);\r
355 \r
356                         val = array;\r
357                 }\r
358 \r
359                 private void ReadArrayOfObject (BinaryReader reader, out long objectId, out object array)\r
360                 {\r
361                         ReadSimpleArray (reader, typeof (object), out objectId, out array);\r
362                 }\r
363                 \r
364                 private void ReadArrayOfString (BinaryReader reader, out long objectId, out object array)\r
365                 {\r
366                         ReadSimpleArray (reader, typeof (string), out objectId, out array);\r
367                 }\r
368 \r
369                 private void ReadSimpleArray (BinaryReader reader, Type elementType, out long objectId, out object val)\r
370                 {\r
371                         objectId = (long) reader.ReadUInt32 ();\r
372                         int length = reader.ReadInt32 ();\r
373                         int[] indices = new int[1];\r
374 \r
375                         Array array = Array.CreateInstance (elementType, length);\r
376                         for (int n = 0; n < length; n++)\r
377                         {\r
378                                 indices[0] = n;\r
379                                 ReadValue (reader, array, objectId, null, elementType, null, null, indices);\r
380                                 n = indices[0];\r
381                         }\r
382                         val = array;\r
383                 }\r
384 \r
385                 private TypeMetadata ReadTypeMetadata (BinaryReader reader, bool isRuntimeObject)\r
386                 {\r
387                         TypeMetadata metadata = new TypeMetadata();\r
388 \r
389                         string className = reader.ReadString ();\r
390                         int fieldCount = reader.ReadInt32 ();\r
391 \r
392                         Type[] types = new Type[fieldCount];\r
393                         string[] names = new string[fieldCount];\r
394 \r
395                         TypeTag[] codes = new TypeTag[fieldCount];\r
396 \r
397                         for (int n=0; n<fieldCount; n++)\r
398                                 names [n] = reader.ReadString ();\r
399 \r
400                         for (int n=0; n<fieldCount; n++)\r
401                                 codes [n] = (TypeTag) reader.ReadByte ();\r
402 \r
403                         for (int n=0; n<fieldCount; n++)\r
404                                 types [n] = ReadType (reader, codes[n]);\r
405 \r
406                         // Gets the type\r
407 \r
408                         if (!isRuntimeObject) \r
409                         {\r
410                                 long assemblyId = (long)reader.ReadUInt32();\r
411                                 metadata.Type = GetDeserializationType (assemblyId, className);\r
412                         }\r
413                         else\r
414                                 metadata.Type = Type.GetType (className, true);\r
415 \r
416                         metadata.MemberTypes = types;\r
417                         metadata.MemberNames = names;\r
418                         metadata.FieldCount = names.Length;\r
419 \r
420                         // Now check if this objects needs a SerializationInfo struct for deserialziation.\r
421                         // SerializationInfo is needed if the object has to be deserialized using\r
422                         // a serialization surrogate, or if it implements ISerializable.\r
423 \r
424                         if (_surrogateSelector != null)\r
425                         {\r
426                                 // check if the surrogate selector handles objects of the given type. \r
427                                 ISurrogateSelector selector;\r
428                                 ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (metadata.Type, _context, out selector);\r
429                                 metadata.NeedsSerializationInfo = (surrogate != null);\r
430                         }\r
431 \r
432                         if (!metadata.NeedsSerializationInfo)\r
433                         {\r
434                                 // Check if the object is marked with the Serializable attribute\r
435 \r
436                                 if (!metadata.Type.IsSerializable)\r
437                                         throw new SerializationException("Serializable objects must be marked with the Serializable attribute");\r
438 \r
439                                 metadata.NeedsSerializationInfo = (metadata.Type.GetInterface ("ISerializable") != null);\r
440                                 if (!metadata.NeedsSerializationInfo)\r
441                                 {\r
442                                         metadata.MemberInfos = new MemberInfo [fieldCount];\r
443                                         for (int n=0; n<fieldCount; n++)\r
444                                         {\r
445                                                 MemberInfo[] members = null;\r
446                                                 string memberName = names[n];\r
447                                                 \r
448                                                 int i = memberName.IndexOf ('+');\r
449                                                 if (i != -1) {\r
450                                                         string baseTypeName = names[n].Substring (0,i);\r
451                                                         memberName = names[n].Substring (i+1);\r
452                                                         Type t = metadata.Type.BaseType;\r
453                                                         while (t != null) {\r
454                                                                 if (t.Name == baseTypeName) {\r
455                                                                         members = t.GetMember (memberName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
456                                                                         break;\r
457                                                                 }\r
458                                                                 else\r
459                                                                         t = t.BaseType;\r
460                                                         }\r
461                                                 }\r
462                                                 else\r
463                                                         members = metadata.Type.GetMember (memberName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
464                                                         \r
465                                                 if (members == null || members.Length == 0) throw new SerializationException ("Field \"" + names[n] + "\" not found in class " + metadata.Type.FullName);\r
466                                                 if (members.Length > 1) throw new SerializationException ("There are two public members named \"" + names[n] + "\" in the class hirearchy of " + metadata.Type.FullName);\r
467                                                 metadata.MemberInfos [n] = members[0];\r
468                                         }\r
469                                         metadata.MemberNames = null;    // Info now in MemberInfos\r
470                                 }\r
471                         }\r
472 \r
473                         // Registers the type's metadata so it can be reused later if\r
474                         // a RefTypeObject element is found\r
475 \r
476                         if (!_typeMetadataCache.ContainsKey (metadata.Type))\r
477                                 _typeMetadataCache [metadata.Type] = metadata;\r
478 \r
479                         return metadata;\r
480                 }\r
481 \r
482 \r
483                 private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, MemberInfo memberInfo, int[] indices)\r
484                 {\r
485                         // Reads a value from the stream and assigns it to the member of an object\r
486 \r
487                         object val;\r
488 \r
489                         if (BinaryCommon.IsPrimitive (valueType))\r
490                         {\r
491                                 val = ReadPrimitiveTypeValue (reader, valueType);\r
492                                 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);\r
493                                 return;\r
494                         }\r
495 \r
496                         // Gets the object\r
497 \r
498                         BinaryElement element = (BinaryElement)reader.ReadByte ();\r
499 \r
500                         if (element == BinaryElement.ObjectReference)\r
501                         {\r
502                                 // Just read the id of the referred object and record a fixup\r
503                                 long childObjectId = (long) reader.ReadUInt32();\r
504                                 RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, memberInfo, indices);\r
505                                 return;\r
506                         }\r
507 \r
508                         long objectId;\r
509                         SerializationInfo objectInfo;\r
510 \r
511                         ReadObject (element, reader, out objectId, out val, out objectInfo);\r
512 \r
513                         // There are two cases where the object cannot be assigned to the parent\r
514                         // and a fixup must be used:\r
515                         //  1) When what has been read is not an object, but an id of an object that\r
516                         //     has not been read yet (an object reference). This is managed in the\r
517                         //     previous block of code.\r
518                         //  2) When the read object is a value type object. Value type fields hold\r
519                         //     copies of objects, not references. Thus, if the value object that\r
520                         //     has been read has pending fixups, those fixups would be made to the\r
521                         //     boxed copy in the ObjectManager, and not in the required object instance\r
522 \r
523                         // First of all register the fixup, and then the object. ObjectManager is more\r
524                         // efficient if done in this order\r
525 \r
526                         bool hasFixup = false;\r
527                         if (objectId != 0)\r
528                         {\r
529                                 if (val.GetType().IsValueType)\r
530                                 {\r
531                                         RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, memberInfo, indices);\r
532                                         hasFixup = true;\r
533                                 }\r
534 \r
535                                 // Register the value\r
536 \r
537                                 if (info == null && !(parentObject is Array))\r
538                                         RegisterObject (objectId, val, objectInfo, parentObjectId, memberInfo, null);\r
539                                 else\r
540                                         RegisterObject (objectId, val, objectInfo, parentObjectId, null, indices);\r
541                         }\r
542                         // Assign the value to the parent object, unless there is a fixup\r
543                         \r
544                         if (!hasFixup) \r
545                                 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);\r
546                 }\r
547 \r
548                 private void SetObjectValue (object parentObject, string fieldName, MemberInfo memberInfo, SerializationInfo info, object value, Type valueType, int[] indices)\r
549                 {\r
550                         if (value is IObjectReference)\r
551                                 value = ((IObjectReference)value).GetRealObject (_context);\r
552 \r
553                         if (parentObject is Array) \r
554                         {\r
555                                 if (value is ArrayNullFiller) \r
556                                 {\r
557                                         // It must be a single dimension array of objects.\r
558                                         // Just increase the index. Elements are null by default.\r
559                                         int count = ((ArrayNullFiller)value).NullCount;\r
560                                         indices[0] += count - 1;\r
561                                 }\r
562                                 else\r
563                                         ((Array)parentObject).SetValue (value, indices);\r
564                         }\r
565                         else if (info != null) {\r
566                                 info.AddValue (fieldName, value, valueType);\r
567                         }\r
568                         else {\r
569                                 if (memberInfo is FieldInfo)\r
570                                         ((FieldInfo)memberInfo).SetValue (parentObject, value);\r
571                                 else\r
572                                         ((PropertyInfo)memberInfo).SetValue (parentObject, value, null);\r
573                         }\r
574                 }\r
575 \r
576                 private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, MemberInfo memberInfo, int[] indices)\r
577                 {\r
578                         if (info != null) {\r
579                                 _manager.RecordDelayedFixup (parentObjectId, fieldName, childObjectId);\r
580                         }\r
581                         else if (parentObject is Array) {\r
582                                 if (indices.Length == 1)\r
583                                         _manager.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);\r
584                                 else\r
585                                         _manager.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);\r
586                         }\r
587                         else {\r
588                                 _manager.RecordFixup (parentObjectId, memberInfo, childObjectId);\r
589                         }\r
590                 }\r
591 \r
592                 private Type GetDeserializationType (long assemblyId, string className)\r
593                 {\r
594                         string assemblyName = (string)_registeredAssemblies[assemblyId];\r
595 \r
596                         if (_binder == null)\r
597                         {\r
598                                 Assembly assembly = Assembly.Load (assemblyName);\r
599                                 return assembly.GetType (className, true);\r
600                         }\r
601                         else\r
602                                 return _binder.BindToType (assemblyName, className);\r
603                 }\r
604 \r
605                 public Type ReadType (BinaryReader reader, TypeTag code)\r
606                 {\r
607                         switch (code)\r
608                         {\r
609                                 case TypeTag.PrimitiveType:\r
610                                         return BinaryCommon.GetTypeFromCode (reader.ReadByte());\r
611 \r
612                                 case TypeTag.String:\r
613                                         return typeof(string);\r
614 \r
615                                 case TypeTag.ObjectType:\r
616                                         return typeof(object);\r
617 \r
618                                 case TypeTag.RuntimeType:\r
619                                 {\r
620                                         string name = reader.ReadString ();\r
621                                         return Type.GetType (name, true);\r
622                                 }\r
623 \r
624                                 case TypeTag.GenericType:\r
625                                 {\r
626                                         string name = reader.ReadString ();\r
627                                         long asmid = (long) reader.ReadUInt32();\r
628                                         return GetDeserializationType (asmid, name);\r
629                                 }\r
630 \r
631                                 case TypeTag.ArrayOfObject:\r
632                                         return typeof(object[]);\r
633 \r
634                                 case TypeTag.ArrayOfString:\r
635                                         return typeof(string[]);\r
636 \r
637                                 case TypeTag.ArrayOfPrimitiveType:\r
638                                         Type elementType = BinaryCommon.GetTypeFromCode (reader.ReadByte());\r
639                                         return Type.GetType(elementType.FullName + "[]");\r
640 \r
641                                 default:\r
642                                         throw new NotSupportedException ("Unknow type tag");\r
643                         }\r
644                 }\r
645                 \r
646                 public static object ReadPrimitiveTypeValue (BinaryReader reader, Type type)\r
647                 {\r
648                         if (type == null) return null;\r
649 \r
650                         switch (Type.GetTypeCode (type))\r
651                         {\r
652                                 case TypeCode.Boolean:\r
653                                         return reader.ReadBoolean();\r
654 \r
655                                 case TypeCode.Byte:\r
656                                         return reader.ReadByte();\r
657 \r
658                                 case TypeCode.Char:\r
659                                         return reader.ReadChar();\r
660 \r
661                                 case TypeCode.DateTime: \r
662                                         return new DateTime (reader.ReadInt64());\r
663 \r
664                                 case TypeCode.Decimal:\r
665                                         return Decimal.Parse (reader.ReadString(), CultureInfo.InvariantCulture);\r
666 \r
667                                 case TypeCode.Double:\r
668                                         return reader.ReadDouble();\r
669 \r
670                                 case TypeCode.Int16:\r
671                                         return reader.ReadInt16();\r
672 \r
673                                 case TypeCode.Int32:\r
674                                         return reader.ReadInt32();\r
675 \r
676                                 case TypeCode.Int64:\r
677                                         return reader.ReadInt64();\r
678 \r
679                                 case TypeCode.SByte:\r
680                                         return reader.ReadSByte();\r
681 \r
682                                 case TypeCode.Single:\r
683                                         return reader.ReadSingle();\r
684 \r
685                                 case TypeCode.UInt16:\r
686                                         return reader.ReadUInt16();\r
687 \r
688                                 case TypeCode.UInt32:\r
689                                         return reader.ReadUInt32();\r
690 \r
691                                 case TypeCode.UInt64:\r
692                                         return reader.ReadUInt64();\r
693 \r
694                                 case TypeCode.String:\r
695                                         return reader.ReadString();\r
696 \r
697                                 default:\r
698                                         if (type == typeof(TimeSpan))\r
699                                                 return new TimeSpan (reader.ReadInt64 ());\r
700                                         else\r
701                                                 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);\r
702                         }\r
703                 }\r
704         }\r
705 }