New test.
[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 \r
9 //\r
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)\r
11 //\r
12 // Permission is hereby granted, free of charge, to any person obtaining\r
13 // a copy of this software and associated documentation files (the\r
14 // "Software"), to deal in the Software without restriction, including\r
15 // without limitation the rights to use, copy, modify, merge, publish,\r
16 // distribute, sublicense, and/or sell copies of the Software, and to\r
17 // permit persons to whom the Software is furnished to do so, subject to\r
18 // the following conditions:\r
19 // \r
20 // The above copyright notice and this permission notice shall be\r
21 // included in all copies or substantial portions of the Software.\r
22 // \r
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
30 //\r
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                 byte[] arrayBuffer;\r
61                 int ArrayBufferLength = 4096;\r
62 \r
63                 class TypeMetadata\r
64                 {\r
65                         public Type Type;\r
66                         public Type[] MemberTypes;\r
67                         public string[] MemberNames;\r
68                         public MemberInfo[] MemberInfos;\r
69                         public int FieldCount;\r
70                         public bool NeedsSerializationInfo;\r
71                 }\r
72 \r
73                 class ArrayNullFiller\r
74                 {\r
75                         public ArrayNullFiller(int count) { NullCount = count; }\r
76                         public int NullCount;\r
77                 }\r
78 \r
79                 public ObjectReader (BinaryFormatter formatter)\r
80                 {\r
81                         _formatter = formatter;\r
82                         _surrogateSelector = formatter.SurrogateSelector;\r
83                         _context = formatter.Context;\r
84                         _binder = formatter.Binder;\r
85                         _manager = new ObjectManager (_surrogateSelector, _context);\r
86                         \r
87 #if NET_1_1\r
88                         _filterLevel = formatter.FilterLevel;\r
89 #endif\r
90                 }\r
91 \r
92                 public void ReadObjectGraph (BinaryReader reader, bool readHeaders, out object result, out Header[] headers)\r
93                 {\r
94                         headers = null;\r
95 \r
96                         // Reads the objects. The first object in the stream is the\r
97                         // root object.\r
98 \r
99                         while (ReadNextObject (reader))\r
100                         {\r
101                                 if (readHeaders && (headers == null))\r
102                                         headers = (Header[])CurrentObject;\r
103                                 else\r
104                                         if (_rootObjectID == 0) _rootObjectID = _lastObjectID;\r
105                         }\r
106 \r
107                         result = _manager.GetObject (_rootObjectID);\r
108                 }\r
109 \r
110                 public bool ReadNextObject (BinaryReader reader)\r
111                 {\r
112                         BinaryElement element = (BinaryElement)reader.ReadByte ();\r
113                         if (element == BinaryElement.End)\r
114                         {\r
115                                 _manager.DoFixups();\r
116 \r
117                                 _manager.RaiseDeserializationEvent();\r
118                                 return false;\r
119                         }\r
120 \r
121                         SerializationInfo info;\r
122                         long objectId;\r
123 \r
124                         ReadObject (element, reader, out objectId, out _lastObject, out info);\r
125 \r
126                         if (objectId != 0) {\r
127                                 RegisterObject (objectId, _lastObject, info, 0, null, null);\r
128                                 _lastObjectID = objectId;               \r
129                         }\r
130         \r
131                         return true;\r
132                 }\r
133 \r
134                 public object CurrentObject\r
135                 {\r
136                         get { return _lastObject; }\r
137                 }\r
138 \r
139                 // Reads an object from the stream. The object is registered in the ObjectManager.\r
140                 // The result can be either the object instance\r
141                 // or the id of the object (when what is found in the stream is an object reference).\r
142                 // If an object instance is read, the objectId is set to 0.\r
143                 \r
144                 private void ReadObject (BinaryElement element, BinaryReader reader, out long objectId, out object value, out SerializationInfo info)\r
145                 {\r
146                         switch (element)\r
147                         {\r
148                                 case BinaryElement.RefTypeObject:\r
149                                         ReadRefTypeObjectInstance (reader, out objectId, out value, out info);\r
150                                         break;\r
151 \r
152                                 case BinaryElement.UntypedRuntimeObject:\r
153                                         ReadObjectInstance (reader, true, false, out objectId, out value, out info);\r
154                                         break;\r
155 \r
156                                 case BinaryElement.UntypedExternalObject:\r
157                                         ReadObjectInstance (reader, false, false, out objectId, out value, out info);\r
158                                         break;\r
159 \r
160                                 case BinaryElement.RuntimeObject:\r
161                                         ReadObjectInstance (reader, true, true, out objectId, out value, out info);\r
162                                         break;\r
163 \r
164                                 case BinaryElement.ExternalObject:\r
165                                         ReadObjectInstance (reader, false, true, out objectId, out value, out info);\r
166                                         break;\r
167 \r
168                                 case BinaryElement.String:\r
169                                         info = null;\r
170                                         ReadStringIntance (reader, out objectId, out value);\r
171                                         break;\r
172 \r
173                                 case BinaryElement.GenericArray:\r
174                                         info = null;\r
175                                         ReadGenericArray (reader, out objectId, out value);\r
176                                         break;\r
177 \r
178 \r
179                                 case BinaryElement.BoxedPrimitiveTypeValue:\r
180                                         value = ReadBoxedPrimitiveTypeValue (reader);\r
181                                         objectId = 0;\r
182                                         info = null;\r
183                                         break;\r
184 \r
185                                 case BinaryElement.NullValue:\r
186                                         value = null;\r
187                                         objectId = 0;\r
188                                         info = null;\r
189                                         break;\r
190 \r
191                                 case BinaryElement.Assembly:\r
192                                         ReadAssembly (reader);\r
193                                         ReadObject ((BinaryElement)reader.ReadByte (), reader, out objectId, out value, out info);\r
194                                         break;\r
195 \r
196                                 case BinaryElement.ArrayFiller8b:\r
197                                         value = new ArrayNullFiller(reader.ReadByte());\r
198                                         objectId = 0;\r
199                                         info = null;\r
200                                         break;\r
201 \r
202                                 case BinaryElement.ArrayFiller32b:\r
203                                         value = new ArrayNullFiller(reader.ReadInt32());\r
204                                         objectId = 0;\r
205                                         info = null;\r
206                                         break;\r
207 \r
208                                 case BinaryElement.ArrayOfPrimitiveType:\r
209                                         ReadArrayOfPrimitiveType (reader, out objectId, out value);\r
210                                         info = null;\r
211                                         break;\r
212 \r
213                                 case BinaryElement.ArrayOfObject:\r
214                                         ReadArrayOfObject (reader, out objectId, out value);\r
215                                         info = null;\r
216                                         break;\r
217 \r
218                                 case BinaryElement.ArrayOfString:\r
219                                         ReadArrayOfString (reader, out objectId, out value);\r
220                                         info = null;\r
221                                         break;\r
222 \r
223                                 default:\r
224                                         throw new SerializationException ("Unexpected binary element: " + (int)element);\r
225                         }\r
226                 }\r
227 \r
228                 private void ReadAssembly (BinaryReader reader)\r
229                 {\r
230                         long id = (long) reader.ReadUInt32 ();\r
231                         string assemblyName = reader.ReadString ();\r
232                         _registeredAssemblies [id] = assemblyName;\r
233                 }\r
234 \r
235                 private void ReadObjectInstance (BinaryReader reader, bool isRuntimeObject, bool hasTypeInfo, out long objectId, out object value, out SerializationInfo info)\r
236                 {\r
237                         objectId = (long) reader.ReadUInt32 ();\r
238 \r
239                         TypeMetadata metadata = ReadTypeMetadata (reader, isRuntimeObject, hasTypeInfo);\r
240                         ReadObjectContent (reader, metadata, objectId, out value, out info);\r
241                 }\r
242 \r
243                 private void ReadRefTypeObjectInstance (BinaryReader reader, out long objectId, out object value, out SerializationInfo info)\r
244                 {\r
245                         objectId = (long) reader.ReadUInt32 ();\r
246                         long refTypeObjectId = (long) reader.ReadUInt32 ();\r
247 \r
248                         // Gets the type of the referred object and its metadata\r
249 \r
250                         object refObj = _manager.GetObject (refTypeObjectId);\r
251                         if (refObj == null) throw new SerializationException ("Invalid binary format");\r
252                         TypeMetadata metadata = (TypeMetadata)_typeMetadataCache [refObj.GetType()];\r
253 \r
254                         ReadObjectContent (reader, metadata, objectId, out value, out info);\r
255                 }\r
256 \r
257                 private void ReadObjectContent (BinaryReader reader, TypeMetadata metadata, long objectId, out object objectInstance, out SerializationInfo info)\r
258                 {\r
259 #if NET_1_1\r
260                         if (_filterLevel == TypeFilterLevel.Low)\r
261                                 objectInstance = FormatterServices.GetSafeUninitializedObject (metadata.Type);\r
262                         else\r
263 #endif\r
264                                 objectInstance = FormatterServices.GetUninitializedObject (metadata.Type);\r
265 #if NET_2_0
266                         _manager.RaiseOnDeserializingEvent (objectInstance);
267 #endif
268                                 \r
269                         info = metadata.NeedsSerializationInfo ? new SerializationInfo(metadata.Type, new FormatterConverter()) : null;\r
270 \r
271                         if (metadata.MemberNames != null)\r
272                                 for (int n=0; n<metadata.FieldCount; n++)\r
273                                         ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberNames[n], null, null);\r
274                         else\r
275                                 for (int n=0; n<metadata.FieldCount; n++)\r
276                                         ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberInfos[n].Name, metadata.MemberInfos[n], null);\r
277                 }\r
278 \r
279                 private void RegisterObject (long objectId, object objectInstance, SerializationInfo info, long parentObjectId, MemberInfo parentObjectMemeber, int[] indices)\r
280                 {\r
281                         if (parentObjectId == 0) indices = null;\r
282 \r
283                         if (!objectInstance.GetType().IsValueType || parentObjectId == 0)\r
284                                 _manager.RegisterObject (objectInstance, objectId, info, 0, null, null);\r
285                         else\r
286                         {\r
287                                 if (indices != null) indices = (int[])indices.Clone();\r
288                                 _manager.RegisterObject (objectInstance, objectId, info, parentObjectId, parentObjectMemeber, indices);\r
289                         }\r
290                 }\r
291 \r
292                 private void ReadStringIntance (BinaryReader reader, out long objectId, out object value)\r
293                 {\r
294                         objectId = (long) reader.ReadUInt32 ();\r
295                         value = reader.ReadString ();\r
296                 }\r
297 \r
298                 private void ReadGenericArray (BinaryReader reader, out long objectId, out object val)\r
299                 {\r
300                         objectId = (long) reader.ReadUInt32 ();\r
301                         // Array structure\r
302                         reader.ReadByte();\r
303 \r
304                         int rank = reader.ReadInt32();\r
305 \r
306                         bool emptyDim = false;\r
307                         int[] lengths = new int[rank];\r
308                         for (int n=0; n<rank; n++)\r
309                         {\r
310                                 lengths[n] = reader.ReadInt32();\r
311                                 if (lengths[n] == 0) emptyDim = true;\r
312                         }\r
313 \r
314                         TypeTag code = (TypeTag) reader.ReadByte ();\r
315                         Type elementType = ReadType (reader, code);\r
316 \r
317                         Array array = Array.CreateInstance (elementType, lengths);\r
318 \r
319                         if (emptyDim) \r
320                         { \r
321                                 val = array;\r
322                                 return;\r
323                         }\r
324 \r
325                         int[] indices = new int[rank];\r
326 \r
327                         // Initialize indexes\r
328                         for (int dim = rank-1; dim >= 0; dim--)\r
329                                 indices[dim] = array.GetLowerBound (dim);\r
330 \r
331                         bool end = false;\r
332                         while (!end)\r
333                         {\r
334                                 ReadValue (reader, array, objectId, null, elementType, null, null, indices);\r
335 \r
336                                 for (int dim = array.Rank-1; dim >= 0; dim--)\r
337                                 {\r
338                                         indices[dim]++;\r
339                                         if (indices[dim] > array.GetUpperBound (dim))\r
340                                         {\r
341                                                 if (dim > 0) \r
342                                                 {\r
343                                                         indices[dim] = array.GetLowerBound (dim);\r
344                                                         continue;       // Increment the next dimension's index\r
345                                                 }\r
346                                                 end = true;     // That was the last dimension. Finished.\r
347                                         }\r
348                                         break;\r
349                                 }\r
350                         }\r
351                         val = array;\r
352                 }\r
353 \r
354                 private object ReadBoxedPrimitiveTypeValue (BinaryReader reader)\r
355                 {\r
356                         Type type = ReadType (reader, TypeTag.PrimitiveType);\r
357                         return ReadPrimitiveTypeValue (reader, type);\r
358                 }\r
359 \r
360                 private void ReadArrayOfPrimitiveType (BinaryReader reader, out long objectId, out object val)\r
361                 {\r
362                         objectId = (long) reader.ReadUInt32 ();\r
363                         int length = reader.ReadInt32 ();\r
364                         Type elementType = ReadType (reader, TypeTag.PrimitiveType);\r
365 \r
366                         switch (Type.GetTypeCode (elementType))\r
367                         {\r
368                                 case TypeCode.Boolean: {\r
369                                         bool[] arr = new bool [length];\r
370                                         for (int n = 0; n < length; n++) arr [n] = reader.ReadBoolean();\r
371                                         val = arr;\r
372                                         break;\r
373                                 }\r
374 \r
375                                 case TypeCode.Byte: {\r
376                                         byte[] arr = new byte [length];\r
377                                         int pos = 0;\r
378                                         while (pos < length) {\r
379                                                 int nr = reader.Read (arr, pos, length - pos);\r
380                                                 if (nr == 0) break;\r
381                                                 pos += nr;\r
382                                         }\r
383                                         val = arr;\r
384                                         break;\r
385                                 }\r
386 \r
387                                 case TypeCode.Char: {\r
388                                         char[] arr = new char [length];\r
389                                         int pos = 0;\r
390                                         while (pos < length) {\r
391                                                 int nr = reader.Read (arr, pos, length - pos);\r
392                                                 if (nr == 0) break;\r
393                                                 pos += nr;\r
394                                         }\r
395                                         val = arr;\r
396                                         break;\r
397                                 }\r
398 \r
399                                 case TypeCode.DateTime: {\r
400                                         DateTime[] arr = new DateTime [length];\r
401                                         for (int n = 0; n < length; n++) {\r
402                                                 ulong nr = reader.ReadUInt64 ();\r
403                                                 const ulong mask = (1ul << 62) - 1;\r
404                                                 long ticks = (long) (nr & mask);\r
405 #if NET_2_0\r
406                                                 DateTimeKind kind = (DateTimeKind) (nr >> 62);\r
407                                                 arr [n] = new DateTime (ticks, kind);\r
408 #else\r
409                                                 arr [n] = new DateTime (ticks);\r
410 #endif\r
411                                         }\r
412                                         val = arr;\r
413                                         break;\r
414                                 }\r
415 \r
416                                 case TypeCode.Decimal: {\r
417                                         Decimal[] arr = new Decimal [length];\r
418                                         for (int n = 0; n < length; n++) arr [n] = reader.ReadDecimal();\r
419                                         val = arr;\r
420                                         break;\r
421                                 }\r
422 \r
423                                 case TypeCode.Double: {\r
424                                         Double[] arr = new Double [length];\r
425                                         if (length > 2)\r
426                                                 BlockRead (reader, arr, 8);\r
427                                         else\r
428                                                 for (int n = 0; n < length; n++) arr [n] = reader.ReadDouble();\r
429                                         val = arr;\r
430                                         break;\r
431                                 }\r
432 \r
433                                 case TypeCode.Int16: {\r
434                                         short[] arr = new short [length];\r
435                                         if (length > 2)\r
436                                                 BlockRead (reader, arr, 2);\r
437                                         else\r
438                                                 for (int n = 0; n < length; n++) arr [n] = reader.ReadInt16();\r
439                                         val = arr;\r
440                                         break;\r
441                                 }\r
442 \r
443                                 case TypeCode.Int32: {\r
444                                         int[] arr = new int [length];\r
445                                         if (length > 2)\r
446                                                 BlockRead (reader, arr, 4);\r
447                                         else\r
448                                                 for (int n = 0; n < length; n++) arr [n] = reader.ReadInt32();\r
449                                         val = arr;\r
450                                         break;\r
451                                 }\r
452 \r
453                                 case TypeCode.Int64: {\r
454                                         long[] arr = new long [length];\r
455                                         if (length > 2)\r
456                                                 BlockRead (reader, arr, 8);\r
457                                         else\r
458                                                 for (int n = 0; n < length; n++) arr [n] = reader.ReadInt64();\r
459                                         val = arr;\r
460                                         break;\r
461                                 }\r
462 \r
463                                 case TypeCode.SByte: {\r
464                                         sbyte[] arr = new sbyte [length];\r
465                                         if (length > 2)\r
466                                                 BlockRead (reader, arr, 1);\r
467                                         else\r
468                                                 for (int n = 0; n < length; n++) arr [n] = reader.ReadSByte();\r
469                                         val = arr;\r
470                                         break;\r
471                                 }\r
472 \r
473                                 case TypeCode.Single: {\r
474                                         float[] arr = new float [length];\r
475                                         if (length > 2)\r
476                                                 BlockRead (reader, arr, 4);\r
477                                         else\r
478                                                 for (int n = 0; n < length; n++) arr [n] = reader.ReadSingle();\r
479                                         val = arr;\r
480                                         break;\r
481                                 }\r
482 \r
483                                 case TypeCode.UInt16: {\r
484                                         ushort[] arr = new ushort [length];\r
485                                         if (length > 2)\r
486                                                 BlockRead (reader, arr, 2);\r
487                                         else\r
488                                                 for (int n = 0; n < length; n++) arr [n] = reader.ReadUInt16();\r
489                                         val = arr;\r
490                                         break;\r
491                                 }\r
492 \r
493                                 case TypeCode.UInt32: {\r
494                                         uint[] arr = new uint [length];\r
495                                         if (length > 2)\r
496                                                 BlockRead (reader, arr, 4);\r
497                                         else\r
498                                                 for (int n = 0; n < length; n++) arr [n] = reader.ReadUInt32();\r
499                                         val = arr;\r
500                                         break;\r
501                                 }\r
502 \r
503                                 case TypeCode.UInt64: {\r
504                                         ulong[] arr = new ulong [length];\r
505                                         if (length > 2)\r
506                                                 BlockRead (reader, arr, 8);\r
507                                         else\r
508                                                 for (int n = 0; n < length; n++) arr [n] = reader.ReadUInt64();\r
509                                         val = arr;\r
510                                         break;\r
511                                 }\r
512 \r
513                                 case TypeCode.String: {\r
514                                         string[] arr = new string [length];\r
515                                         for (int n = 0; n < length; n++) arr [n] = reader.ReadString();\r
516                                         val = arr;\r
517                                         break;\r
518                                 }\r
519 \r
520                                 default: {\r
521                                         if (elementType == typeof(TimeSpan)) {\r
522                                                 TimeSpan[] arr = new TimeSpan [length];\r
523                                                 for (int n = 0; n < length; n++) arr [n] = new TimeSpan (reader.ReadInt64 ());\r
524                                                 val = arr;\r
525                                         }\r
526                                         else\r
527                                                 throw new NotSupportedException ("Unsupported primitive type: " + elementType.FullName);\r
528                                         break;\r
529                                 }\r
530                         }                       \r
531                 }\r
532 \r
533                 private void BlockRead (BinaryReader reader, Array array, int dataSize)\r
534                 {\r
535                         int totalSize = Buffer.ByteLength (array);\r
536                         \r
537                         if (arrayBuffer == null || (totalSize > arrayBuffer.Length && arrayBuffer.Length != ArrayBufferLength))\r
538                                 arrayBuffer = new byte [totalSize <= ArrayBufferLength ? totalSize : ArrayBufferLength];\r
539                         \r
540                         int pos = 0;\r
541                         while (totalSize > 0) {\r
542                                 int size = totalSize < arrayBuffer.Length ? totalSize : arrayBuffer.Length;\r
543                                 int ap = 0;\r
544                                 do {\r
545                                         int nr = reader.Read (arrayBuffer, ap, size - ap);\r
546                                         if (nr == 0) break;\r
547                                         ap += nr;\r
548                                 } while (ap < size);\r
549                                 \r
550                                 if (!BitConverter.IsLittleEndian && dataSize > 1)\r
551                                         BinaryCommon.SwapBytes (arrayBuffer, size, dataSize);\r
552 \r
553                                 Buffer.BlockCopy (arrayBuffer, 0, array, pos, size);\r
554                                 totalSize -= size;\r
555                                 pos += size;\r
556                         }\r
557                 }\r
558                 \r
559 \r
560                 private void ReadArrayOfObject (BinaryReader reader, out long objectId, out object array)\r
561                 {\r
562                         ReadSimpleArray (reader, typeof (object), out objectId, out array);\r
563                 }\r
564                 \r
565                 private void ReadArrayOfString (BinaryReader reader, out long objectId, out object array)\r
566                 {\r
567                         ReadSimpleArray (reader, typeof (string), out objectId, out array);\r
568                 }\r
569 \r
570                 private void ReadSimpleArray (BinaryReader reader, Type elementType, out long objectId, out object val)\r
571                 {\r
572                         objectId = (long) reader.ReadUInt32 ();\r
573                         int length = reader.ReadInt32 ();\r
574                         int[] indices = new int[1];\r
575 \r
576                         Array array = Array.CreateInstance (elementType, length);\r
577                         for (int n = 0; n < length; n++)\r
578                         {\r
579                                 indices[0] = n;\r
580                                 ReadValue (reader, array, objectId, null, elementType, null, null, indices);\r
581                                 n = indices[0];\r
582                         }\r
583                         val = array;\r
584                 }\r
585 \r
586                 private TypeMetadata ReadTypeMetadata (BinaryReader reader, bool isRuntimeObject, bool hasTypeInfo)\r
587                 {\r
588                         TypeMetadata metadata = new TypeMetadata();\r
589 \r
590                         string className = reader.ReadString ();\r
591                         int fieldCount = reader.ReadInt32 ();\r
592 \r
593                         Type[] types = new Type[fieldCount];\r
594                         string[] names = new string[fieldCount];\r
595 \r
596                         for (int n=0; n<fieldCount; n++)\r
597                                 names [n] = reader.ReadString ();\r
598 \r
599                         if (hasTypeInfo)\r
600                         {\r
601                                 TypeTag[] codes = new TypeTag[fieldCount];\r
602 \r
603                                 for (int n=0; n<fieldCount; n++)\r
604                                         codes [n] = (TypeTag) reader.ReadByte ();\r
605         \r
606                                 for (int n=0; n<fieldCount; n++)\r
607                                         types [n] = ReadType (reader, codes[n]);\r
608                         }\r
609                         \r
610                         // Gets the type\r
611 \r
612                         if (!isRuntimeObject) \r
613                         {\r
614                                 long assemblyId = (long)reader.ReadUInt32();\r
615                                 metadata.Type = GetDeserializationType (assemblyId, className);\r
616                         }\r
617                         else\r
618                                 metadata.Type = Type.GetType (className, true);\r
619 \r
620                         metadata.MemberTypes = types;\r
621                         metadata.MemberNames = names;\r
622                         metadata.FieldCount = names.Length;\r
623 \r
624                         // Now check if this objects needs a SerializationInfo struct for deserialziation.\r
625                         // SerializationInfo is needed if the object has to be deserialized using\r
626                         // a serialization surrogate, or if it implements ISerializable.\r
627 \r
628                         if (_surrogateSelector != null)\r
629                         {\r
630                                 // check if the surrogate selector handles objects of the given type. \r
631                                 ISurrogateSelector selector;\r
632                                 ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (metadata.Type, _context, out selector);\r
633                                 metadata.NeedsSerializationInfo = (surrogate != null);\r
634                         }\r
635 \r
636                         if (!metadata.NeedsSerializationInfo)\r
637                         {\r
638                                 // Check if the object is marked with the Serializable attribute\r
639 \r
640                                 if (!metadata.Type.IsSerializable)\r
641                                         throw new SerializationException("Serializable objects must be marked with the Serializable attribute");\r
642 \r
643                                 metadata.NeedsSerializationInfo = (metadata.Type.GetInterface ("ISerializable") != null);\r
644                                 if (!metadata.NeedsSerializationInfo)\r
645                                 {\r
646                                         metadata.MemberInfos = new MemberInfo [fieldCount];\r
647                                         for (int n=0; n<fieldCount; n++)\r
648                                         {\r
649                                                 FieldInfo field = null;\r
650                                                 string memberName = names[n];\r
651                                                 \r
652                                                 int i = memberName.IndexOf ('+');\r
653                                                 if (i != -1) {\r
654                                                         string baseTypeName = names[n].Substring (0,i);\r
655                                                         memberName = names[n].Substring (i+1);\r
656                                                         Type t = metadata.Type.BaseType;\r
657                                                         while (t != null) {\r
658                                                                 if (t.Name == baseTypeName) {\r
659                                                                         field = t.GetField (memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
660                                                                         break;\r
661                                                                 }\r
662                                                                 else\r
663                                                                         t = t.BaseType;\r
664                                                         }\r
665                                                 }\r
666                                                 else\r
667                                                         field = metadata.Type.GetField (memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
668                                                         \r
669                                                 if (field == null) throw new SerializationException ("Field \"" + names[n] + "\" not found in class " + metadata.Type.FullName);\r
670                                                 metadata.MemberInfos [n] = field;\r
671                                                 \r
672                                                 if (!hasTypeInfo) {\r
673                                                         types [n] = field.FieldType;\r
674                                                 }\r
675                                         }\r
676                                         metadata.MemberNames = null;    // Info now in MemberInfos\r
677                                 }\r
678                         }\r
679 \r
680                         // Registers the type's metadata so it can be reused later if\r
681                         // a RefTypeObject element is found\r
682 \r
683                         if (!_typeMetadataCache.ContainsKey (metadata.Type))\r
684                                 _typeMetadataCache [metadata.Type] = metadata;\r
685 \r
686                         return metadata;\r
687                 }\r
688 \r
689 \r
690                 private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, MemberInfo memberInfo, int[] indices)\r
691                 {\r
692                         // Reads a value from the stream and assigns it to the member of an object\r
693 \r
694                         object val;\r
695 \r
696                         if (BinaryCommon.IsPrimitive (valueType))\r
697                         {\r
698                                 val = ReadPrimitiveTypeValue (reader, valueType);\r
699                                 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);\r
700                                 return;\r
701                         }\r
702 \r
703                         // Gets the object\r
704 \r
705                         BinaryElement element = (BinaryElement)reader.ReadByte ();\r
706 \r
707                         if (element == BinaryElement.ObjectReference)\r
708                         {\r
709                                 // Just read the id of the referred object and record a fixup\r
710                                 long childObjectId = (long) reader.ReadUInt32();\r
711                                 RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, memberInfo, indices);\r
712                                 return;\r
713                         }\r
714 \r
715                         long objectId;\r
716                         SerializationInfo objectInfo;\r
717 \r
718                         ReadObject (element, reader, out objectId, out val, out objectInfo);\r
719 \r
720                         // There are two cases where the object cannot be assigned to the parent\r
721                         // and a fixup must be used:\r
722                         //  1) When what has been read is not an object, but an id of an object that\r
723                         //     has not been read yet (an object reference). This is managed in the\r
724                         //     previous block of code.\r
725                         //  2) When the read object is a value type object. Value type fields hold\r
726                         //     copies of objects, not references. Thus, if the value object that\r
727                         //     has been read has pending fixups, those fixups would be made to the\r
728                         //     boxed copy in the ObjectManager, and not in the required object instance\r
729 \r
730                         // First of all register the fixup, and then the object. ObjectManager is more\r
731                         // efficient if done in this order\r
732 \r
733                         bool hasFixup = false;\r
734                         if (objectId != 0)\r
735                         {\r
736                                 if (val.GetType().IsValueType)\r
737                                 {\r
738                                         RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, memberInfo, indices);\r
739                                         hasFixup = true;\r
740                                 }\r
741 \r
742                                 // Register the value\r
743 \r
744                                 if (info == null && !(parentObject is Array))\r
745                                         RegisterObject (objectId, val, objectInfo, parentObjectId, memberInfo, null);\r
746                                 else\r
747                                         RegisterObject (objectId, val, objectInfo, parentObjectId, null, indices);\r
748                         }\r
749                         // Assign the value to the parent object, unless there is a fixup\r
750                         \r
751                         if (!hasFixup) \r
752                                 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);\r
753                 }\r
754 \r
755                 private void SetObjectValue (object parentObject, string fieldName, MemberInfo memberInfo, SerializationInfo info, object value, Type valueType, int[] indices)\r
756                 {\r
757                         if (value is IObjectReference)\r
758                                 value = ((IObjectReference)value).GetRealObject (_context);\r
759 \r
760                         if (parentObject is Array) \r
761                         {\r
762                                 if (value is ArrayNullFiller) \r
763                                 {\r
764                                         // It must be a single dimension array of objects.\r
765                                         // Just increase the index. Elements are null by default.\r
766                                         int count = ((ArrayNullFiller)value).NullCount;\r
767                                         indices[0] += count - 1;\r
768                                 }\r
769                                 else\r
770                                         ((Array)parentObject).SetValue (value, indices);\r
771                         }\r
772                         else if (info != null) {\r
773                                 info.AddValue (fieldName, value, valueType);\r
774                         }\r
775                         else {\r
776                                 if (memberInfo is FieldInfo)\r
777                                         ((FieldInfo)memberInfo).SetValue (parentObject, value);\r
778                                 else\r
779                                         ((PropertyInfo)memberInfo).SetValue (parentObject, value, null);\r
780                         }\r
781                 }\r
782 \r
783                 private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, MemberInfo memberInfo, int[] indices)\r
784                 {\r
785                         if (info != null) {\r
786                                 _manager.RecordDelayedFixup (parentObjectId, fieldName, childObjectId);\r
787                         }\r
788                         else if (parentObject is Array) {\r
789                                 if (indices.Length == 1)\r
790                                         _manager.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);\r
791                                 else\r
792                                         _manager.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);\r
793                         }\r
794                         else {\r
795                                 _manager.RecordFixup (parentObjectId, memberInfo, childObjectId);\r
796                         }\r
797                 }\r
798 \r
799                 private Type GetDeserializationType (long assemblyId, string className)\r
800                 {\r
801                         Type t;\r
802                         string assemblyName = (string)_registeredAssemblies[assemblyId];\r
803 \r
804                         if (_binder != null) {\r
805                                 t = _binder.BindToType (assemblyName, className);\r
806                                 if (t != null)\r
807                                         return t;\r
808                         }\r
809                                 \r
810                         Assembly assembly = Assembly.Load (assemblyName);\r
811                         t = assembly.GetType (className, true);\r
812                         if (t != null)\r
813                                 return t;\r
814                         throw new SerializationException ("Couldn't find type '" + className + "'.");\r
815                 }\r
816 \r
817                 public Type ReadType (BinaryReader reader, TypeTag code)\r
818                 {\r
819                         switch (code)\r
820                         {\r
821                                 case TypeTag.PrimitiveType:\r
822                                         return BinaryCommon.GetTypeFromCode (reader.ReadByte());\r
823 \r
824                                 case TypeTag.String:\r
825                                         return typeof(string);\r
826 \r
827                                 case TypeTag.ObjectType:\r
828                                         return typeof(object);\r
829 \r
830                                 case TypeTag.RuntimeType:\r
831                                 {\r
832                                         string name = reader.ReadString ();\r
833                                         return Type.GetType (name, true);\r
834                                 }\r
835 \r
836                                 case TypeTag.GenericType:\r
837                                 {\r
838                                         string name = reader.ReadString ();\r
839                                         long asmid = (long) reader.ReadUInt32();\r
840                                         return GetDeserializationType (asmid, name);\r
841                                 }\r
842 \r
843                                 case TypeTag.ArrayOfObject:\r
844                                         return typeof(object[]);\r
845 \r
846                                 case TypeTag.ArrayOfString:\r
847                                         return typeof(string[]);\r
848 \r
849                                 case TypeTag.ArrayOfPrimitiveType:\r
850                                         Type elementType = BinaryCommon.GetTypeFromCode (reader.ReadByte());\r
851                                         return Type.GetType(elementType.FullName + "[]");\r
852 \r
853                                 default:\r
854                                         throw new NotSupportedException ("Unknow type tag");\r
855                         }\r
856                 }\r
857                 \r
858                 public static object ReadPrimitiveTypeValue (BinaryReader reader, Type type)\r
859                 {\r
860                         if (type == null) return null;\r
861 \r
862                         switch (Type.GetTypeCode (type))\r
863                         {\r
864                                 case TypeCode.Boolean:\r
865                                         return reader.ReadBoolean();\r
866 \r
867                                 case TypeCode.Byte:\r
868                                         return reader.ReadByte();\r
869 \r
870                                 case TypeCode.Char:\r
871                                         return reader.ReadChar();\r
872 \r
873                                 case TypeCode.DateTime: \r
874                                         return new DateTime (reader.ReadInt64());\r
875 \r
876                                 case TypeCode.Decimal:\r
877                                         return Decimal.Parse (reader.ReadString(), CultureInfo.InvariantCulture);\r
878 \r
879                                 case TypeCode.Double:\r
880                                         return reader.ReadDouble();\r
881 \r
882                                 case TypeCode.Int16:\r
883                                         return reader.ReadInt16();\r
884 \r
885                                 case TypeCode.Int32:\r
886                                         return reader.ReadInt32();\r
887 \r
888                                 case TypeCode.Int64:\r
889                                         return reader.ReadInt64();\r
890 \r
891                                 case TypeCode.SByte:\r
892                                         return reader.ReadSByte();\r
893 \r
894                                 case TypeCode.Single:\r
895                                         return reader.ReadSingle();\r
896 \r
897                                 case TypeCode.UInt16:\r
898                                         return reader.ReadUInt16();\r
899 \r
900                                 case TypeCode.UInt32:\r
901                                         return reader.ReadUInt32();\r
902 \r
903                                 case TypeCode.UInt64:\r
904                                         return reader.ReadUInt64();\r
905 \r
906                                 case TypeCode.String:\r
907                                         return reader.ReadString();\r
908 \r
909                                 default:\r
910                                         if (type == typeof(TimeSpan))\r
911                                                 return new TimeSpan (reader.ReadInt64 ());\r
912                                         else\r
913                                                 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);\r
914                         }\r
915                 }\r
916         }\r
917 }\r