4 // Lluis Sanchez Gual (lluis@ideary.com)
\r
5 // Patrik Torstensson
\r
7 // (C) 2003 Lluis Sanchez Gual
\r
10 using System.Runtime.Serialization;
\r
12 using System.Collections;
\r
13 using System.Reflection;
\r
14 using System.Runtime.Remoting.Messaging;
\r
15 using System.Globalization;
\r
17 namespace System.Runtime.Serialization.Formatters.Binary
\r
19 internal class ObjectReader
\r
21 BinaryFormatter _formatter;
\r
22 ISurrogateSelector _surrogateSelector;
\r
23 StreamingContext _context;
\r
24 SerializationBinder _binder;
\r
27 TypeFilterLevel _filterLevel;
\r
30 ObjectManager _manager;
\r
31 Hashtable _registeredAssemblies = new Hashtable();
\r
32 Hashtable _typeMetadataCache = new Hashtable();
\r
34 object _lastObject = null;
\r
35 long _lastObjectID = 0;
\r
36 long _rootObjectID = 0;
\r
41 public Type[] MemberTypes;
\r
42 public string[] MemberNames;
\r
43 public MemberInfo[] MemberInfos;
\r
44 public int FieldCount;
\r
45 public bool NeedsSerializationInfo;
\r
48 class ArrayNullFiller
\r
50 public ArrayNullFiller(int count) { NullCount = count; }
\r
51 public int NullCount;
\r
54 public ObjectReader (BinaryFormatter formatter)
\r
56 _formatter = formatter;
\r
57 _surrogateSelector = formatter.SurrogateSelector;
\r
58 _context = formatter.Context;
\r
59 _binder = formatter.Binder;
\r
60 _manager = new ObjectManager (_surrogateSelector, _context);
\r
63 _filterLevel = formatter.FilterLevel;
\r
67 public void ReadObjectGraph (BinaryReader reader, bool readHeaders, out object result, out Header[] headers)
\r
71 // Reads the objects. The first object in the stream is the
\r
74 while (ReadNextObject (reader))
\r
76 if (readHeaders && (headers == null))
\r
77 headers = (Header[])CurrentObject;
\r
79 if (_rootObjectID == 0) _rootObjectID = _lastObjectID;
\r
82 result = _manager.GetObject (_rootObjectID);
\r
85 public bool ReadNextObject (BinaryReader reader)
\r
87 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
88 if (element == BinaryElement.End)
\r
90 _manager.DoFixups();
\r
92 _manager.RaiseDeserializationEvent();
\r
96 SerializationInfo info;
\r
99 ReadObject (element, reader, out objectId, out _lastObject, out info);
\r
101 if (objectId != 0) {
\r
102 RegisterObject (objectId, _lastObject, info, 0, null, null);
\r
103 _lastObjectID = objectId;
\r
109 public object CurrentObject
\r
111 get { return _lastObject; }
\r
114 // Reads an object from the stream. The object is registered in the ObjectManager.
\r
115 // The result can be either the object instance
\r
116 // or the id of the object (when what is found in the stream is an object reference).
\r
117 // If an object instance is read, the objectId is set to 0.
\r
119 private void ReadObject (BinaryElement element, BinaryReader reader, out long objectId, out object value, out SerializationInfo info)
\r
123 case BinaryElement.RefTypeObject:
\r
124 ReadRefTypeObjectInstance (reader, out objectId, out value, out info);
\r
127 case BinaryElement.RuntimeObject:
\r
128 ReadObjectInstance (reader, true, out objectId, out value, out info);
\r
131 case BinaryElement.ExternalObject:
\r
132 ReadObjectInstance (reader, false, out objectId, out value, out info);
\r
135 case BinaryElement.String:
\r
137 ReadStringIntance (reader, out objectId, out value);
\r
140 case BinaryElement.GenericArray:
\r
142 ReadGenericArray (reader, out objectId, out value);
\r
146 case BinaryElement.BoxedPrimitiveTypeValue:
\r
147 value = ReadBoxedPrimitiveTypeValue (reader);
\r
152 case BinaryElement.NullValue:
\r
158 case BinaryElement.Assembly:
\r
159 ReadAssembly (reader);
\r
160 ReadObject ((BinaryElement)reader.ReadByte (), reader, out objectId, out value, out info);
\r
163 case BinaryElement.ArrayFiller8b:
\r
164 value = new ArrayNullFiller(reader.ReadByte());
\r
169 case BinaryElement.ArrayFiller32b:
\r
170 value = new ArrayNullFiller(reader.ReadInt32());
\r
175 case BinaryElement.ArrayOfPrimitiveType:
\r
176 ReadArrayOfPrimitiveType (reader, out objectId, out value);
\r
180 case BinaryElement.ArrayOfObject:
\r
181 ReadArrayOfObject (reader, out objectId, out value);
\r
185 case BinaryElement.ArrayOfString:
\r
186 ReadArrayOfString (reader, out objectId, out value);
\r
191 throw new SerializationException ("Unexpected binary element: " + (int)element);
\r
195 private void ReadAssembly (BinaryReader reader)
\r
197 long id = (long) reader.ReadUInt32 ();
\r
198 string assemblyName = reader.ReadString ();
\r
199 _registeredAssemblies [id] = assemblyName;
\r
202 private void ReadObjectInstance (BinaryReader reader, bool isRuntimeObject, out long objectId, out object value, out SerializationInfo info)
\r
204 objectId = (long) reader.ReadUInt32 ();
\r
206 TypeMetadata metadata = ReadTypeMetadata (reader, isRuntimeObject);
\r
207 ReadObjectContent (reader, metadata, objectId, out value, out info);
\r
210 private void ReadRefTypeObjectInstance (BinaryReader reader, out long objectId, out object value, out SerializationInfo info)
\r
212 objectId = (long) reader.ReadUInt32 ();
\r
213 long refTypeObjectId = (long) reader.ReadUInt32 ();
\r
215 // Gets the type of the referred object and its metadata
\r
217 object refObj = _manager.GetObject (refTypeObjectId);
\r
218 if (refObj == null) throw new SerializationException ("Invalid binary format");
\r
219 TypeMetadata metadata = (TypeMetadata)_typeMetadataCache [refObj.GetType()];
\r
221 ReadObjectContent (reader, metadata, objectId, out value, out info);
\r
224 private void ReadObjectContent (BinaryReader reader, TypeMetadata metadata, long objectId, out object objectInstance, out SerializationInfo info)
\r
227 if (_filterLevel == TypeFilterLevel.Low)
\r
228 objectInstance = FormatterServices.GetSafeUninitializedObject (metadata.Type);
\r
231 objectInstance = FormatterServices.GetUninitializedObject (metadata.Type);
\r
233 info = metadata.NeedsSerializationInfo ? new SerializationInfo(metadata.Type, new FormatterConverter()) : null;
\r
235 if (metadata.MemberNames != null)
\r
236 for (int n=0; n<metadata.FieldCount; n++)
\r
237 ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberNames[n], null, null);
\r
239 for (int n=0; n<metadata.FieldCount; n++)
\r
240 ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberInfos[n].Name, metadata.MemberInfos[n], null);
\r
243 private void RegisterObject (long objectId, object objectInstance, SerializationInfo info, long parentObjectId, MemberInfo parentObjectMemeber, int[] indices)
\r
245 if (parentObjectId == 0) indices = null;
\r
247 if (!objectInstance.GetType().IsValueType || parentObjectId == 0)
\r
248 _manager.RegisterObject (objectInstance, objectId, info, 0, null, null);
\r
251 if (indices != null) indices = (int[])indices.Clone();
\r
252 _manager.RegisterObject (objectInstance, objectId, info, parentObjectId, parentObjectMemeber, indices);
\r
256 private void ReadStringIntance (BinaryReader reader, out long objectId, out object value)
\r
258 objectId = (long) reader.ReadUInt32 ();
\r
259 value = reader.ReadString ();
\r
262 private void ReadGenericArray (BinaryReader reader, out long objectId, out object val)
\r
264 objectId = (long) reader.ReadUInt32 ();
\r
265 ArrayStructure structure = (ArrayStructure) reader.ReadByte();
\r
267 int rank = reader.ReadInt32();
\r
269 bool emptyDim = false;
\r
270 int[] lengths = new int[rank];
\r
271 for (int n=0; n<rank; n++)
\r
273 lengths[n] = reader.ReadInt32();
\r
274 if (lengths[n] == 0) emptyDim = true;
\r
277 TypeTag code = (TypeTag) reader.ReadByte ();
\r
278 Type elementType = ReadType (reader, code);
\r
280 Array array = Array.CreateInstance (elementType, lengths);
\r
288 int[] indices = new int[rank];
\r
290 // Initialize indexes
\r
291 for (int dim = rank-1; dim >= 0; dim--)
\r
292 indices[dim] = array.GetLowerBound (dim);
\r
297 ReadValue (reader, array, objectId, null, elementType, null, null, indices);
\r
299 for (int dim = array.Rank-1; dim >= 0; dim--)
\r
302 if (indices[dim] > array.GetUpperBound (dim))
\r
306 indices[dim] = array.GetLowerBound (dim);
\r
307 continue; // Increment the next dimension's index
\r
309 end = true; // That was the last dimension. Finished.
\r
317 private object ReadBoxedPrimitiveTypeValue (BinaryReader reader)
\r
319 Type type = ReadType (reader, TypeTag.PrimitiveType);
\r
320 return ReadPrimitiveTypeValue (reader, type);
\r
323 private void ReadArrayOfPrimitiveType (BinaryReader reader, out long objectId, out object val)
\r
325 objectId = (long) reader.ReadUInt32 ();
\r
326 int length = reader.ReadInt32 ();
\r
327 Type elementType = ReadType (reader, TypeTag.PrimitiveType);
\r
329 Array array = Array.CreateInstance (elementType, length);
\r
330 for (int n = 0; n < length; n++)
\r
331 array.SetValue (ReadPrimitiveTypeValue (reader, elementType), n);
\r
336 private void ReadArrayOfObject (BinaryReader reader, out long objectId, out object array)
\r
338 ReadSimpleArray (reader, typeof (object), out objectId, out array);
\r
341 private void ReadArrayOfString (BinaryReader reader, out long objectId, out object array)
\r
343 ReadSimpleArray (reader, typeof (string), out objectId, out array);
\r
346 private void ReadSimpleArray (BinaryReader reader, Type elementType, out long objectId, out object val)
\r
348 objectId = (long) reader.ReadUInt32 ();
\r
349 int length = reader.ReadInt32 ();
\r
350 int[] indices = new int[1];
\r
352 Array array = Array.CreateInstance (elementType, length);
\r
353 for (int n = 0; n < length; n++)
\r
356 ReadValue (reader, array, objectId, null, elementType, null, null, indices);
\r
362 private TypeMetadata ReadTypeMetadata (BinaryReader reader, bool isRuntimeObject)
\r
364 TypeMetadata metadata = new TypeMetadata();
\r
366 string className = reader.ReadString ();
\r
367 int fieldCount = reader.ReadInt32 ();
\r
369 Type[] types = new Type[fieldCount];
\r
370 string[] names = new string[fieldCount];
\r
372 TypeTag[] codes = new TypeTag[fieldCount];
\r
374 for (int n=0; n<fieldCount; n++)
\r
375 names [n] = reader.ReadString ();
\r
377 for (int n=0; n<fieldCount; n++)
\r
378 codes [n] = (TypeTag) reader.ReadByte ();
\r
380 for (int n=0; n<fieldCount; n++)
\r
381 types [n] = ReadType (reader, codes[n]);
\r
385 if (!isRuntimeObject)
\r
387 long assemblyId = (long)reader.ReadUInt32();
\r
388 metadata.Type = GetDeserializationType (assemblyId, className);
\r
391 metadata.Type = Type.GetType (className, true);
\r
393 metadata.MemberTypes = types;
\r
394 metadata.MemberNames = names;
\r
395 metadata.FieldCount = names.Length;
\r
397 // Now check if this objects needs a SerializationInfo struct for deserialziation.
\r
398 // SerializationInfo is needed if the object has to be deserialized using
\r
399 // a serialization surrogate, or if it implements ISerializable.
\r
401 if (_surrogateSelector != null)
\r
403 // check if the surrogate selector handles objects of the given type.
\r
404 ISurrogateSelector selector;
\r
405 ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (metadata.Type, _context, out selector);
\r
406 metadata.NeedsSerializationInfo = (surrogate != null);
\r
409 if (!metadata.NeedsSerializationInfo)
\r
411 // Check if the object is marked with the Serializable attribute
\r
413 if (!metadata.Type.IsSerializable)
\r
414 throw new SerializationException("Serializable objects must be marked with the Serializable attribute");
\r
416 metadata.NeedsSerializationInfo = (metadata.Type.GetInterface ("ISerializable") != null);
\r
417 if (!metadata.NeedsSerializationInfo)
\r
419 metadata.MemberInfos = new MemberInfo [fieldCount];
\r
420 for (int n=0; n<fieldCount; n++)
\r
422 MemberInfo[] members = null;
\r
423 string memberName = names[n];
\r
425 int i = memberName.IndexOf ('+');
\r
427 string baseTypeName = names[n].Substring (0,i);
\r
428 memberName = names[n].Substring (i+1);
\r
429 Type t = metadata.Type.BaseType;
\r
430 while (t != null) {
\r
431 if (t.Name == baseTypeName) {
\r
432 members = t.GetMember (memberName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
\r
440 members = metadata.Type.GetMember (memberName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
\r
442 if (members == null || members.Length == 0) throw new SerializationException ("Field \"" + names[n] + "\" not found in class " + metadata.Type.FullName);
\r
443 if (members.Length > 1) throw new SerializationException ("There are two public members named \"" + names[n] + "\" in the class hirearchy of " + metadata.Type.FullName);
\r
444 metadata.MemberInfos [n] = members[0];
\r
446 metadata.MemberNames = null; // Info now in MemberInfos
\r
450 // Registers the type's metadata so it can be reused later if
\r
451 // a RefTypeObject element is found
\r
453 if (!_typeMetadataCache.ContainsKey (metadata.Type))
\r
454 _typeMetadataCache [metadata.Type] = metadata;
\r
460 private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, MemberInfo memberInfo, int[] indices)
\r
462 // Reads a value from the stream and assigns it to the member of an object
\r
466 if (BinaryCommon.IsPrimitive (valueType))
\r
468 val = ReadPrimitiveTypeValue (reader, valueType);
\r
469 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);
\r
475 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
477 if (element == BinaryElement.ObjectReference)
\r
479 // Just read the id of the referred object and record a fixup
\r
480 long childObjectId = (long) reader.ReadUInt32();
\r
481 RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, memberInfo, indices);
\r
486 SerializationInfo objectInfo;
\r
488 ReadObject (element, reader, out objectId, out val, out objectInfo);
\r
490 // There are two cases where the object cannot be assigned to the parent
\r
491 // and a fixup must be used:
\r
492 // 1) When what has been read is not an object, but an id of an object that
\r
493 // has not been read yet (an object reference). This is managed in the
\r
494 // previous block of code.
\r
495 // 2) When the read object is a value type object. Value type fields hold
\r
496 // copies of objects, not references. Thus, if the value object that
\r
497 // has been read has pending fixups, those fixups would be made to the
\r
498 // boxed copy in the ObjectManager, and not in the required object instance
\r
500 // First of all register the fixup, and then the object. ObjectManager is more
\r
501 // efficient if done in this order
\r
503 bool hasFixup = false;
\r
506 if (val.GetType().IsValueType)
\r
508 RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, memberInfo, indices);
\r
512 // Register the value
\r
514 if (info == null && !(parentObject is Array))
\r
515 RegisterObject (objectId, val, objectInfo, parentObjectId, memberInfo, null);
\r
517 RegisterObject (objectId, val, objectInfo, parentObjectId, null, indices);
\r
519 // Assign the value to the parent object, unless there is a fixup
\r
522 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);
\r
525 private void SetObjectValue (object parentObject, string fieldName, MemberInfo memberInfo, SerializationInfo info, object value, Type valueType, int[] indices)
\r
527 if (value is IObjectReference)
\r
528 value = ((IObjectReference)value).GetRealObject (_context);
\r
530 if (parentObject is Array)
\r
532 if (value is ArrayNullFiller)
\r
534 // It must be a single dimension array of objects.
\r
535 // Just increase the index. Elements are null by default.
\r
536 int count = ((ArrayNullFiller)value).NullCount;
\r
537 indices[0] += count - 1;
\r
540 ((Array)parentObject).SetValue (value, indices);
\r
542 else if (info != null) {
\r
543 info.AddValue (fieldName, value, valueType);
\r
546 if (memberInfo is FieldInfo)
\r
547 ((FieldInfo)memberInfo).SetValue (parentObject, value);
\r
549 ((PropertyInfo)memberInfo).SetValue (parentObject, value, null);
\r
553 private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, MemberInfo memberInfo, int[] indices)
\r
555 if (info != null) {
\r
556 _manager.RecordDelayedFixup (parentObjectId, fieldName, childObjectId);
\r
558 else if (parentObject is Array) {
\r
559 if (indices.Length == 1)
\r
560 _manager.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);
\r
562 _manager.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);
\r
565 _manager.RecordFixup (parentObjectId, memberInfo, childObjectId);
\r
569 private Type GetDeserializationType (long assemblyId, string className)
\r
571 string assemblyName = (string)_registeredAssemblies[assemblyId];
\r
573 if (_binder == null)
\r
575 Assembly assembly = Assembly.Load (assemblyName);
\r
576 return assembly.GetType (className, true);
\r
579 return _binder.BindToType (assemblyName, className);
\r
582 public Type ReadType (BinaryReader reader, TypeTag code)
\r
586 case TypeTag.PrimitiveType:
\r
587 return BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
589 case TypeTag.String:
\r
590 return typeof(string);
\r
592 case TypeTag.ObjectType:
\r
593 return typeof(object);
\r
595 case TypeTag.RuntimeType:
\r
597 string name = reader.ReadString ();
\r
598 return Type.GetType (name, true);
\r
601 case TypeTag.GenericType:
\r
603 string name = reader.ReadString ();
\r
604 long asmid = (long) reader.ReadUInt32();
\r
605 return GetDeserializationType (asmid, name);
\r
608 case TypeTag.ArrayOfObject:
\r
609 return typeof(object[]);
\r
611 case TypeTag.ArrayOfString:
\r
612 return typeof(string[]);
\r
614 case TypeTag.ArrayOfPrimitiveType:
\r
615 Type elementType = BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
616 return Type.GetType(elementType.FullName + "[]");
\r
619 throw new NotSupportedException ("Unknow type tag");
\r
623 public static object ReadPrimitiveTypeValue (BinaryReader reader, Type type)
\r
625 if (type == null) return null;
\r
627 switch (Type.GetTypeCode (type))
\r
629 case TypeCode.Boolean:
\r
630 return reader.ReadBoolean();
\r
632 case TypeCode.Byte:
\r
633 return reader.ReadByte();
\r
635 case TypeCode.Char:
\r
636 return reader.ReadChar();
\r
638 case TypeCode.DateTime:
\r
639 return new DateTime (reader.ReadInt64());
\r
641 case TypeCode.Decimal:
\r
642 return Decimal.Parse (reader.ReadString(), CultureInfo.InvariantCulture);
\r
644 case TypeCode.Double:
\r
645 return reader.ReadDouble();
\r
647 case TypeCode.Int16:
\r
648 return reader.ReadInt16();
\r
650 case TypeCode.Int32:
\r
651 return reader.ReadInt32();
\r
653 case TypeCode.Int64:
\r
654 return reader.ReadInt64();
\r
656 case TypeCode.SByte:
\r
657 return reader.ReadSByte();
\r
659 case TypeCode.Single:
\r
660 return reader.ReadSingle();
\r
662 case TypeCode.UInt16:
\r
663 return reader.ReadUInt16();
\r
665 case TypeCode.UInt32:
\r
666 return reader.ReadUInt32();
\r
668 case TypeCode.UInt64:
\r
669 return reader.ReadUInt64();
\r
671 case TypeCode.String:
\r
672 return reader.ReadString();
\r
675 if (type == typeof(TimeSpan))
\r
676 return new TimeSpan (reader.ReadInt64 ());
\r
678 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
\r