4 // Lluis Sanchez Gual (lluis@ideary.com)
\r
5 // Patrik Torstensson
\r
7 // (C) 2003 Lluis Sanchez Gual
\r
9 // FIXME: Implement the missing binary elements
\r
12 using System.Runtime.Serialization;
\r
14 using System.Collections;
\r
15 using System.Reflection;
\r
16 using System.Runtime.Remoting.Messaging;
\r
18 namespace System.Runtime.Serialization.Formatters.Binary
\r
20 internal class ObjectReader
\r
22 BinaryFormatter _formatter;
\r
23 ISurrogateSelector _surrogateSelector;
\r
24 StreamingContext _context;
\r
25 SerializationBinder _binder;
\r
28 TypeFilterLevel _filterLevel;
\r
31 ObjectManager _manager;
\r
32 Hashtable _registeredAssemblies = new Hashtable();
\r
33 Hashtable _typeMetadataCache = new Hashtable();
\r
35 object _lastObject = null;
\r
36 long _lastObjectID = 0;
\r
37 long _rootObjectID = 0;
\r
42 public Type[] MemberTypes;
\r
43 public string[] MemberNames;
\r
44 public MemberInfo[] MemberInfos;
\r
45 public int FieldCount;
\r
46 public bool NeedsSerializationInfo;
\r
49 class ArrayNullFiller
\r
51 public ArrayNullFiller(int count) { NullCount = count; }
\r
52 public int NullCount;
\r
55 public ObjectReader (BinaryFormatter formatter)
\r
57 _formatter = formatter;
\r
58 _surrogateSelector = formatter.SurrogateSelector;
\r
59 _context = formatter.Context;
\r
60 _binder = formatter.Binder;
\r
61 _manager = new ObjectManager (_surrogateSelector, _context);
\r
64 _filterLevel = formatter.FilterLevel;
\r
68 public void ReadObjectGraph (BinaryReader reader, bool readHeaders, out object result, out Header[] headers)
\r
72 // Reads the objects. The first object in the stream is the
\r
75 while (ReadNextObject (reader))
\r
77 if (readHeaders && (headers == null))
\r
78 headers = (Header[])CurrentObject;
\r
80 if (_rootObjectID == 0) _rootObjectID = _lastObjectID;
\r
83 result = _manager.GetObject (_rootObjectID);
\r
86 public bool ReadNextObject (BinaryReader reader)
\r
88 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
89 if (element == BinaryElement.End)
\r
91 _manager.DoFixups();
\r
93 _manager.RaiseDeserializationEvent();
\r
97 SerializationInfo info;
\r
100 ReadObject (element, reader, out objectId, out _lastObject, out info);
\r
102 if (objectId != 0) {
\r
103 RegisterObject (objectId, _lastObject, info, 0, null, null);
\r
104 _lastObjectID = objectId;
\r
110 public object CurrentObject
\r
112 get { return _lastObject; }
\r
115 // Reads an object from the stream. The object is registered in the ObjectManager.
\r
116 // The result can be either the object instance
\r
117 // or the id of the object (when what is found in the stream is an object reference).
\r
118 // If an object instance is read, the objectId is set to 0.
\r
120 private void ReadObject (BinaryElement element, BinaryReader reader, out long objectId, out object value, out SerializationInfo info)
\r
124 case BinaryElement.RefTypeObject:
\r
125 ReadRefTypeObjectInstance (reader, out objectId, out value, out info);
\r
128 case BinaryElement.RuntimeObject:
\r
129 ReadObjectInstance (reader, true, out objectId, out value, out info);
\r
132 case BinaryElement.ExternalObject:
\r
133 ReadObjectInstance (reader, false, out objectId, out value, out info);
\r
136 case BinaryElement.String:
\r
138 ReadStringIntance (reader, out objectId, out value);
\r
141 case BinaryElement.GenericArray:
\r
143 ReadGenericArray (reader, out objectId, out value);
\r
147 case BinaryElement.BoxedPrimitiveTypeValue:
\r
148 value = ReadBoxedPrimitiveTypeValue (reader);
\r
153 case BinaryElement.NullValue:
\r
159 case BinaryElement.Assembly:
\r
160 ReadAssembly (reader);
\r
161 ReadObject ((BinaryElement)reader.ReadByte (), reader, out objectId, out value, out info);
\r
164 case BinaryElement.ArrayFiller8b:
\r
165 value = new ArrayNullFiller(reader.ReadByte());
\r
170 case BinaryElement.ArrayFiller32b:
\r
171 value = new ArrayNullFiller(reader.ReadInt32());
\r
176 case BinaryElement.ArrayOfPrimitiveType:
\r
177 ReadArrayOfPrimitiveType (reader, out objectId, out value);
\r
181 case BinaryElement.ArrayOfObject:
\r
182 ReadArrayOfObject (reader, out objectId, out value);
\r
186 case BinaryElement.ArrayOfString:
\r
187 ReadArrayOfString (reader, out objectId, out value);
\r
192 throw new SerializationException ("Unexpected binary element: " + (int)element);
\r
196 private void ReadAssembly (BinaryReader reader)
\r
198 long id = (long) reader.ReadUInt32 ();
\r
199 string assemblyName = reader.ReadString ();
\r
200 _registeredAssemblies [id] = assemblyName;
\r
203 private void ReadObjectInstance (BinaryReader reader, bool isRuntimeObject, out long objectId, out object value, out SerializationInfo info)
\r
205 objectId = (long) reader.ReadUInt32 ();
\r
207 TypeMetadata metadata = ReadTypeMetadata (reader, isRuntimeObject);
\r
208 ReadObjectContent (reader, metadata, objectId, out value, out info);
\r
211 private void ReadRefTypeObjectInstance (BinaryReader reader, out long objectId, out object value, out SerializationInfo info)
\r
213 objectId = (long) reader.ReadUInt32 ();
\r
214 long refTypeObjectId = (long) reader.ReadUInt32 ();
\r
216 // Gets the type of the referred object and its metadata
\r
218 object refObj = _manager.GetObject (refTypeObjectId);
\r
219 if (refObj == null) throw new SerializationException ("Invalid binary format");
\r
220 TypeMetadata metadata = (TypeMetadata)_typeMetadataCache [refObj.GetType()];
\r
222 ReadObjectContent (reader, metadata, objectId, out value, out info);
\r
225 private void ReadObjectContent (BinaryReader reader, TypeMetadata metadata, long objectId, out object objectInstance, out SerializationInfo info)
\r
228 if (_filterLevel == TypeFilterLevel.Low)
\r
229 objectInstance = FormatterServices.GetSafeUninitializedObject (metadata.Type);
\r
232 objectInstance = FormatterServices.GetUninitializedObject (metadata.Type);
\r
234 info = metadata.NeedsSerializationInfo ? new SerializationInfo(metadata.Type, new FormatterConverter()) : null;
\r
236 if (metadata.MemberNames != null)
\r
237 for (int n=0; n<metadata.FieldCount; n++)
\r
238 ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberNames[n], null, null);
\r
240 for (int n=0; n<metadata.FieldCount; n++)
\r
241 ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberInfos[n].Name, metadata.MemberInfos[n], null);
\r
244 private void RegisterObject (long objectId, object objectInstance, SerializationInfo info, long parentObjectId, MemberInfo parentObjectMemeber, int[] indices)
\r
246 if (parentObjectId == 0) indices = null;
\r
248 if (!objectInstance.GetType().IsValueType || parentObjectId == 0)
\r
249 _manager.RegisterObject (objectInstance, objectId, info, 0, null, null);
\r
252 if (indices != null) indices = (int[])indices.Clone();
\r
253 _manager.RegisterObject (objectInstance, objectId, info, parentObjectId, parentObjectMemeber, indices);
\r
257 private void ReadStringIntance (BinaryReader reader, out long objectId, out object value)
\r
259 objectId = (long) reader.ReadUInt32 ();
\r
260 value = reader.ReadString ();
\r
263 private void ReadGenericArray (BinaryReader reader, out long objectId, out object val)
\r
265 objectId = (long) reader.ReadUInt32 ();
\r
266 ArrayStructure structure = (ArrayStructure) reader.ReadByte();
\r
268 int rank = reader.ReadInt32();
\r
270 bool emptyDim = false;
\r
271 int[] lengths = new int[rank];
\r
272 for (int n=0; n<rank; n++)
\r
274 lengths[n] = reader.ReadInt32();
\r
275 if (lengths[n] == 0) emptyDim = true;
\r
278 TypeTag code = (TypeTag) reader.ReadByte ();
\r
279 Type elementType = ReadType (reader, code);
\r
281 Array array = Array.CreateInstance (elementType, lengths);
\r
289 int[] indices = new int[rank];
\r
291 // Initialize indexes
\r
292 for (int dim = rank-1; dim >= 0; dim--)
\r
293 indices[dim] = array.GetLowerBound (dim);
\r
298 ReadValue (reader, array, objectId, null, elementType, null, null, indices);
\r
300 for (int dim = array.Rank-1; dim >= 0; dim--)
\r
303 if (indices[dim] > array.GetUpperBound (dim))
\r
307 indices[dim] = array.GetLowerBound (dim);
\r
308 continue; // Increment the next dimension's index
\r
310 end = true; // That was the last dimension. Finished.
\r
318 private object ReadBoxedPrimitiveTypeValue (BinaryReader reader)
\r
320 Type type = ReadType (reader, TypeTag.PrimitiveType);
\r
321 return ReadPrimitiveTypeValue (reader, type);
\r
324 private void ReadArrayOfPrimitiveType (BinaryReader reader, out long objectId, out object val)
\r
326 objectId = (long) reader.ReadUInt32 ();
\r
327 int length = reader.ReadInt32 ();
\r
328 Type elementType = ReadType (reader, TypeTag.PrimitiveType);
\r
330 Array array = Array.CreateInstance (elementType, length);
\r
331 for (int n = 0; n < length; n++)
\r
332 array.SetValue (ReadPrimitiveTypeValue (reader, elementType), n);
\r
337 private void ReadArrayOfObject (BinaryReader reader, out long objectId, out object array)
\r
339 ReadSimpleArray (reader, typeof (object), out objectId, out array);
\r
342 private void ReadArrayOfString (BinaryReader reader, out long objectId, out object array)
\r
344 ReadSimpleArray (reader, typeof (string), out objectId, out array);
\r
347 private void ReadSimpleArray (BinaryReader reader, Type elementType, out long objectId, out object val)
\r
349 objectId = (long) reader.ReadUInt32 ();
\r
350 int length = reader.ReadInt32 ();
\r
351 int[] indices = new int[1];
\r
353 Array array = Array.CreateInstance (elementType, length);
\r
354 for (int n = 0; n < length; n++)
\r
357 ReadValue (reader, array, objectId, null, elementType, null, null, indices);
\r
363 private TypeMetadata ReadTypeMetadata (BinaryReader reader, bool isRuntimeObject)
\r
365 TypeMetadata metadata = new TypeMetadata();
\r
367 string className = reader.ReadString ();
\r
368 int fieldCount = reader.ReadInt32 ();
\r
370 Type[] types = new Type[fieldCount];
\r
371 string[] names = new string[fieldCount];
\r
373 TypeTag[] codes = new TypeTag[fieldCount];
\r
375 for (int n=0; n<fieldCount; n++)
\r
376 names [n] = reader.ReadString ();
\r
378 for (int n=0; n<fieldCount; n++)
\r
379 codes [n] = (TypeTag) reader.ReadByte ();
\r
381 for (int n=0; n<fieldCount; n++)
\r
382 types [n] = ReadType (reader, codes[n]);
\r
386 if (!isRuntimeObject)
\r
388 long assemblyId = (long)reader.ReadUInt32();
\r
389 metadata.Type = GetDeserializationType (assemblyId, className);
\r
392 metadata.Type = Type.GetType (className, true);
\r
394 metadata.MemberTypes = types;
\r
395 metadata.MemberNames = names;
\r
396 metadata.FieldCount = names.Length;
\r
398 // Now check if this objects needs a SerializationInfo struct for deserialziation.
\r
399 // SerializationInfo is needed if the object has to be deserialized using
\r
400 // a serialization surrogate, or if it implements ISerializable.
\r
402 if (_surrogateSelector != null)
\r
404 // check if the surrogate selector handles objects of the given type.
\r
405 ISurrogateSelector selector;
\r
406 ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (metadata.Type, _context, out selector);
\r
407 metadata.NeedsSerializationInfo = (surrogate != null);
\r
410 if (!metadata.NeedsSerializationInfo)
\r
412 // Check if the object is marked with the Serializable attribute
\r
414 if (!metadata.Type.IsSerializable)
\r
415 throw new SerializationException("Serializable objects must be marked with the Serializable attribute");
\r
417 metadata.NeedsSerializationInfo = (metadata.Type.GetInterface ("ISerializable") != null);
\r
418 if (!metadata.NeedsSerializationInfo)
\r
420 metadata.MemberInfos = new MemberInfo [fieldCount];
\r
421 for (int n=0; n<fieldCount; n++)
\r
423 MemberInfo[] members = metadata.Type.GetMember (names[n], MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
\r
424 if (members.Length > 1) throw new SerializationException ("There are two public members named \"" + names[n] + "\" in the class hirearchy of " + metadata.Type.FullName);
\r
425 if (members.Length == 0) throw new SerializationException ("Field \"" + names[n] + "\" not found in class " + metadata.Type.FullName);
\r
426 metadata.MemberInfos [n] = members[0];
\r
428 metadata.MemberNames = null; // Info now in MemberInfos
\r
432 // Registers the type's metadata so it can be reused later if
\r
433 // a RefTypeObject element is found
\r
435 if (!_typeMetadataCache.ContainsKey (metadata.Type))
\r
436 _typeMetadataCache [metadata.Type] = metadata;
\r
442 private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, MemberInfo memberInfo, int[] indices)
\r
444 // Reads a value from the stream and assigns it to the member of an object
\r
448 if (BinaryCommon.IsPrimitive (valueType))
\r
450 val = ReadPrimitiveTypeValue (reader, valueType);
\r
451 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);
\r
457 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
459 if (element == BinaryElement.ObjectReference)
\r
461 // Just read the id of the referred object and record a fixup
\r
462 long childObjectId = (long) reader.ReadUInt32();
\r
463 RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, memberInfo, indices);
\r
468 SerializationInfo objectInfo;
\r
470 ReadObject (element, reader, out objectId, out val, out objectInfo);
\r
472 // There are two cases where the object cannot be assigned to the parent
\r
473 // and a fixup must be used:
\r
474 // 1) When what has been read is not an object, but an id of an object that
\r
475 // has not been read yet (an object reference). This is managed in the
\r
476 // previous block of code.
\r
477 // 2) When the read object is a value type object. Value type fields hold
\r
478 // copies of objects, not references. Thus, if the value object that
\r
479 // has been read has pending fixups, those fixups would be made to the
\r
480 // boxed copy in the ObjectManager, and not in the required object instance
\r
482 // First of all register the fixup, and then the object. ObjectManager is more
\r
483 // efficient if done in this order
\r
485 bool hasFixup = false;
\r
488 if (val.GetType().IsValueType)
\r
490 RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, memberInfo, indices);
\r
494 // Register the value
\r
496 if (info == null && !(parentObject is Array))
\r
497 RegisterObject (objectId, val, objectInfo, parentObjectId, memberInfo, null);
\r
499 RegisterObject (objectId, val, objectInfo, parentObjectId, null, indices);
\r
501 // Assign the value to the parent object, unless there is a fixup
\r
504 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);
\r
507 private void SetObjectValue (object parentObject, string fieldName, MemberInfo memberInfo, SerializationInfo info, object value, Type valueType, int[] indices)
\r
509 if (value is IObjectReference)
\r
510 value = ((IObjectReference)value).GetRealObject (_context);
\r
512 if (parentObject is Array)
\r
514 if (value is ArrayNullFiller)
\r
516 // It must be a single dimension array of objects.
\r
517 // Just increase the index. Elements are null by default.
\r
518 int count = ((ArrayNullFiller)value).NullCount;
\r
519 indices[0] += count - 1;
\r
522 ((Array)parentObject).SetValue (value, indices);
\r
524 else if (info != null) {
\r
525 info.AddValue (fieldName, value, valueType);
\r
528 if (memberInfo is FieldInfo)
\r
529 ((FieldInfo)memberInfo).SetValue (parentObject, value);
\r
531 ((PropertyInfo)memberInfo).SetValue (parentObject, value, null);
\r
535 private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, MemberInfo memberInfo, int[] indices)
\r
537 if (info != null) {
\r
538 _manager.RecordDelayedFixup (parentObjectId, fieldName, childObjectId);
\r
540 else if (parentObject is Array) {
\r
541 if (indices.Length == 1)
\r
542 _manager.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);
\r
544 _manager.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);
\r
547 _manager.RecordFixup (parentObjectId, memberInfo, childObjectId);
\r
551 private Type GetDeserializationType (long assemblyId, string className)
\r
553 string assemblyName = (string)_registeredAssemblies[assemblyId];
\r
555 if (_binder == null)
\r
557 Assembly assembly = Assembly.Load (assemblyName);
\r
558 return assembly.GetType (className, true);
\r
561 return _binder.BindToType (assemblyName, className);
\r
564 public Type ReadType (BinaryReader reader, TypeTag code)
\r
568 case TypeTag.PrimitiveType:
\r
569 return BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
571 case TypeTag.String:
\r
572 return typeof(string);
\r
574 case TypeTag.ObjectType:
\r
575 return typeof(object);
\r
577 case TypeTag.RuntimeType:
\r
579 string name = reader.ReadString ();
\r
580 return Type.GetType (name, true);
\r
583 case TypeTag.GenericType:
\r
585 string name = reader.ReadString ();
\r
586 long asmid = (long) reader.ReadUInt32();
\r
587 return GetDeserializationType (asmid, name);
\r
590 case TypeTag.ArrayOfObject:
\r
591 return typeof(object[]);
\r
593 case TypeTag.ArrayOfString:
\r
594 return typeof(string[]);
\r
596 case TypeTag.ArrayOfPrimitiveType:
\r
597 Type elementType = BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
598 return Type.GetType(elementType.FullName + "[]");
\r
601 throw new NotSupportedException ("Unknow type tag");
\r
605 public static object ReadPrimitiveTypeValue (BinaryReader reader, Type type)
\r
607 if (type == null) return null;
\r
609 switch (Type.GetTypeCode (type))
\r
611 case TypeCode.Boolean:
\r
612 return reader.ReadBoolean();
\r
614 case TypeCode.Byte:
\r
615 return reader.ReadByte();
\r
617 case TypeCode.Char:
\r
618 return reader.ReadChar();
\r
620 case TypeCode.DateTime:
\r
621 return new DateTime (reader.ReadInt64());
\r
623 case TypeCode.Decimal:
\r
624 return reader.ReadDecimal();
\r
626 case TypeCode.Double:
\r
627 return reader.ReadDouble();
\r
629 case TypeCode.Int16:
\r
630 return reader.ReadInt16();
\r
632 case TypeCode.Int32:
\r
633 return reader.ReadInt32();
\r
635 case TypeCode.Int64:
\r
636 return reader.ReadInt64();
\r
638 case TypeCode.SByte:
\r
639 return reader.ReadSByte();
\r
641 case TypeCode.Single:
\r
642 return reader.ReadSingle();
\r
644 case TypeCode.UInt16:
\r
645 return reader.ReadUInt16();
\r
647 case TypeCode.UInt32:
\r
648 return reader.ReadUInt32();
\r
650 case TypeCode.UInt64:
\r
651 return reader.ReadUInt64();
\r
653 case TypeCode.String:
\r
654 return reader.ReadString();
\r
657 if (type == typeof(TimeSpan))
\r
658 return new TimeSpan (reader.ReadInt64 ());
\r
660 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
\r