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 = null;
\r
424 string memberName = names[n];
\r
426 int i = memberName.IndexOf ('+');
\r
428 string baseTypeName = names[n].Substring (0,i);
\r
429 memberName = names[n].Substring (i+1);
\r
430 Type t = metadata.Type.BaseType;
\r
431 while (t != null) {
\r
432 if (t.Name == baseTypeName) {
\r
433 members = t.GetMember (memberName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
\r
441 members = metadata.Type.GetMember (memberName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
\r
443 if (members == null || members.Length == 0) throw new SerializationException ("Field \"" + names[n] + "\" not found in class " + metadata.Type.FullName);
\r
444 if (members.Length > 1) throw new SerializationException ("There are two public members named \"" + names[n] + "\" in the class hirearchy of " + metadata.Type.FullName);
\r
445 metadata.MemberInfos [n] = members[0];
\r
447 metadata.MemberNames = null; // Info now in MemberInfos
\r
451 // Registers the type's metadata so it can be reused later if
\r
452 // a RefTypeObject element is found
\r
454 if (!_typeMetadataCache.ContainsKey (metadata.Type))
\r
455 _typeMetadataCache [metadata.Type] = metadata;
\r
461 private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, MemberInfo memberInfo, int[] indices)
\r
463 // Reads a value from the stream and assigns it to the member of an object
\r
467 if (BinaryCommon.IsPrimitive (valueType))
\r
469 val = ReadPrimitiveTypeValue (reader, valueType);
\r
470 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);
\r
476 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
478 if (element == BinaryElement.ObjectReference)
\r
480 // Just read the id of the referred object and record a fixup
\r
481 long childObjectId = (long) reader.ReadUInt32();
\r
482 RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, memberInfo, indices);
\r
487 SerializationInfo objectInfo;
\r
489 ReadObject (element, reader, out objectId, out val, out objectInfo);
\r
491 // There are two cases where the object cannot be assigned to the parent
\r
492 // and a fixup must be used:
\r
493 // 1) When what has been read is not an object, but an id of an object that
\r
494 // has not been read yet (an object reference). This is managed in the
\r
495 // previous block of code.
\r
496 // 2) When the read object is a value type object. Value type fields hold
\r
497 // copies of objects, not references. Thus, if the value object that
\r
498 // has been read has pending fixups, those fixups would be made to the
\r
499 // boxed copy in the ObjectManager, and not in the required object instance
\r
501 // First of all register the fixup, and then the object. ObjectManager is more
\r
502 // efficient if done in this order
\r
504 bool hasFixup = false;
\r
507 if (val.GetType().IsValueType)
\r
509 RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, memberInfo, indices);
\r
513 // Register the value
\r
515 if (info == null && !(parentObject is Array))
\r
516 RegisterObject (objectId, val, objectInfo, parentObjectId, memberInfo, null);
\r
518 RegisterObject (objectId, val, objectInfo, parentObjectId, null, indices);
\r
520 // Assign the value to the parent object, unless there is a fixup
\r
523 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);
\r
526 private void SetObjectValue (object parentObject, string fieldName, MemberInfo memberInfo, SerializationInfo info, object value, Type valueType, int[] indices)
\r
528 if (value is IObjectReference)
\r
529 value = ((IObjectReference)value).GetRealObject (_context);
\r
531 if (parentObject is Array)
\r
533 if (value is ArrayNullFiller)
\r
535 // It must be a single dimension array of objects.
\r
536 // Just increase the index. Elements are null by default.
\r
537 int count = ((ArrayNullFiller)value).NullCount;
\r
538 indices[0] += count - 1;
\r
541 ((Array)parentObject).SetValue (value, indices);
\r
543 else if (info != null) {
\r
544 info.AddValue (fieldName, value, valueType);
\r
547 if (memberInfo is FieldInfo)
\r
548 ((FieldInfo)memberInfo).SetValue (parentObject, value);
\r
550 ((PropertyInfo)memberInfo).SetValue (parentObject, value, null);
\r
554 private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, MemberInfo memberInfo, int[] indices)
\r
556 if (info != null) {
\r
557 _manager.RecordDelayedFixup (parentObjectId, fieldName, childObjectId);
\r
559 else if (parentObject is Array) {
\r
560 if (indices.Length == 1)
\r
561 _manager.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);
\r
563 _manager.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);
\r
566 _manager.RecordFixup (parentObjectId, memberInfo, childObjectId);
\r
570 private Type GetDeserializationType (long assemblyId, string className)
\r
572 string assemblyName = (string)_registeredAssemblies[assemblyId];
\r
574 if (_binder == null)
\r
576 Assembly assembly = Assembly.Load (assemblyName);
\r
577 return assembly.GetType (className, true);
\r
580 return _binder.BindToType (assemblyName, className);
\r
583 public Type ReadType (BinaryReader reader, TypeTag code)
\r
587 case TypeTag.PrimitiveType:
\r
588 return BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
590 case TypeTag.String:
\r
591 return typeof(string);
\r
593 case TypeTag.ObjectType:
\r
594 return typeof(object);
\r
596 case TypeTag.RuntimeType:
\r
598 string name = reader.ReadString ();
\r
599 return Type.GetType (name, true);
\r
602 case TypeTag.GenericType:
\r
604 string name = reader.ReadString ();
\r
605 long asmid = (long) reader.ReadUInt32();
\r
606 return GetDeserializationType (asmid, name);
\r
609 case TypeTag.ArrayOfObject:
\r
610 return typeof(object[]);
\r
612 case TypeTag.ArrayOfString:
\r
613 return typeof(string[]);
\r
615 case TypeTag.ArrayOfPrimitiveType:
\r
616 Type elementType = BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
617 return Type.GetType(elementType.FullName + "[]");
\r
620 throw new NotSupportedException ("Unknow type tag");
\r
624 public static object ReadPrimitiveTypeValue (BinaryReader reader, Type type)
\r
626 if (type == null) return null;
\r
628 switch (Type.GetTypeCode (type))
\r
630 case TypeCode.Boolean:
\r
631 return reader.ReadBoolean();
\r
633 case TypeCode.Byte:
\r
634 return reader.ReadByte();
\r
636 case TypeCode.Char:
\r
637 return reader.ReadChar();
\r
639 case TypeCode.DateTime:
\r
640 return new DateTime (reader.ReadInt64());
\r
642 case TypeCode.Decimal:
\r
643 return reader.ReadDecimal();
\r
645 case TypeCode.Double:
\r
646 return reader.ReadDouble();
\r
648 case TypeCode.Int16:
\r
649 return reader.ReadInt16();
\r
651 case TypeCode.Int32:
\r
652 return reader.ReadInt32();
\r
654 case TypeCode.Int64:
\r
655 return reader.ReadInt64();
\r
657 case TypeCode.SByte:
\r
658 return reader.ReadSByte();
\r
660 case TypeCode.Single:
\r
661 return reader.ReadSingle();
\r
663 case TypeCode.UInt16:
\r
664 return reader.ReadUInt16();
\r
666 case TypeCode.UInt32:
\r
667 return reader.ReadUInt32();
\r
669 case TypeCode.UInt64:
\r
670 return reader.ReadUInt64();
\r
672 case TypeCode.String:
\r
673 return reader.ReadString();
\r
676 if (type == typeof(TimeSpan))
\r
677 return new TimeSpan (reader.ReadInt64 ());
\r
679 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
\r