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 ISurrogateSelector _surrogateSelector;
\r
23 StreamingContext _context;
\r
24 SerializationBinder _binder;
\r
26 ObjectManager _manager;
\r
27 Hashtable _registeredAssemblies = new Hashtable();
\r
28 Hashtable _typeMetadataCache = new Hashtable();
\r
30 object _lastObject = null;
\r
31 long _lastObjectID = 0;
\r
32 long _rootObjectID = 0;
\r
37 public Type[] MemberTypes;
\r
38 public string[] MemberNames;
\r
39 public MemberInfo[] MemberInfos;
\r
40 public int FieldCount;
\r
41 public bool NeedsSerializationInfo;
\r
44 class ArrayNullFiller
\r
46 public ArrayNullFiller(int count) { NullCount = count; }
\r
47 public int NullCount;
\r
50 public ObjectReader(ISurrogateSelector surrogateSelector, StreamingContext context, SerializationBinder binder)
\r
52 _manager = new ObjectManager (surrogateSelector, context);
\r
53 _surrogateSelector = surrogateSelector;
\r
58 public void ReadObjectGraph (BinaryReader reader, bool readHeaders, out object result, out Header[] headers)
\r
62 // Reads the objects. The first object in the stream is the
\r
65 while (ReadNextObject (reader))
\r
67 if (readHeaders && (headers == null))
\r
68 headers = (Header[])CurrentObject;
\r
70 if (_rootObjectID == 0) _rootObjectID = _lastObjectID;
\r
73 result = _manager.GetObject (_rootObjectID);
\r
76 public bool ReadNextObject (BinaryReader reader)
\r
78 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
79 if (element == BinaryElement.End)
\r
81 _manager.DoFixups();
\r
83 _manager.RaiseDeserializationEvent();
\r
87 SerializationInfo info;
\r
90 ReadObject (element, reader, out objectId, out _lastObject, out info);
\r
92 if (objectId != 0) {
\r
93 RegisterObject (objectId, _lastObject, info, 0, null, null);
\r
94 _lastObjectID = objectId;
\r
100 public object CurrentObject
\r
102 get { return _lastObject; }
\r
105 // Reads an object from the stream. The object is registered in the ObjectManager.
\r
106 // The result can be either the object instance
\r
107 // or the id of the object (when what is found in the stream is an object reference).
\r
108 // If an object instance is read, the objectId is set to 0.
\r
110 private void ReadObject (BinaryElement element, BinaryReader reader, out long objectId, out object value, out SerializationInfo info)
\r
114 case BinaryElement.RefTypeObject:
\r
115 ReadRefTypeObjectInstance (reader, out objectId, out value, out info);
\r
118 case BinaryElement.RuntimeObject:
\r
119 ReadObjectInstance (reader, true, out objectId, out value, out info);
\r
122 case BinaryElement.ExternalObject:
\r
123 ReadObjectInstance (reader, false, out objectId, out value, out info);
\r
126 case BinaryElement.String:
\r
128 ReadStringIntance (reader, out objectId, out value);
\r
131 case BinaryElement.GenericArray:
\r
133 ReadGenericArray (reader, out objectId, out value);
\r
137 case BinaryElement.BoxedPrimitiveTypeValue:
\r
138 value = ReadBoxedPrimitiveTypeValue (reader);
\r
143 case BinaryElement.NullValue:
\r
149 case BinaryElement.Assembly:
\r
150 ReadAssembly (reader);
\r
151 ReadObject ((BinaryElement)reader.ReadByte (), reader, out objectId, out value, out info);
\r
154 case BinaryElement.ArrayFiller8b:
\r
155 value = new ArrayNullFiller(reader.ReadByte());
\r
160 case BinaryElement.ArrayFiller32b:
\r
161 value = new ArrayNullFiller(reader.ReadInt32());
\r
166 case BinaryElement.ArrayOfPrimitiveType:
\r
167 ReadArrayOfPrimitiveType (reader, out objectId, out value);
\r
171 case BinaryElement.ArrayOfObject:
\r
172 ReadArrayOfObject (reader, out objectId, out value);
\r
176 case BinaryElement.ArrayOfString:
\r
177 ReadArrayOfString (reader, out objectId, out value);
\r
182 throw new SerializationException ("Unexpected binary element: " + (int)element);
\r
186 private void ReadAssembly (BinaryReader reader)
\r
188 long id = (long) reader.ReadUInt32 ();
\r
189 string assemblyName = reader.ReadString ();
\r
190 _registeredAssemblies [id] = assemblyName;
\r
193 private void ReadObjectInstance (BinaryReader reader, bool isRuntimeObject, out long objectId, out object value, out SerializationInfo info)
\r
195 objectId = (long) reader.ReadUInt32 ();
\r
197 TypeMetadata metadata = ReadTypeMetadata (reader, isRuntimeObject);
\r
198 ReadObjectContent (reader, metadata, objectId, out value, out info);
\r
201 private void ReadRefTypeObjectInstance (BinaryReader reader, out long objectId, out object value, out SerializationInfo info)
\r
203 objectId = (long) reader.ReadUInt32 ();
\r
204 long refTypeObjectId = (long) reader.ReadUInt32 ();
\r
206 // Gets the type of the referred object and its metadata
\r
208 object refObj = _manager.GetObject (refTypeObjectId);
\r
209 if (refObj == null) throw new SerializationException ("Invalid binary format");
\r
210 TypeMetadata metadata = (TypeMetadata)_typeMetadataCache [refObj.GetType()];
\r
212 ReadObjectContent (reader, metadata, objectId, out value, out info);
\r
215 private void ReadObjectContent (BinaryReader reader, TypeMetadata metadata, long objectId, out object objectInstance, out SerializationInfo info)
\r
217 objectInstance = FormatterServices.GetUninitializedObject (metadata.Type);
\r
218 info = metadata.NeedsSerializationInfo ? new SerializationInfo(metadata.Type, new FormatterConverter()) : null;
\r
220 if (metadata.MemberNames != null)
\r
221 for (int n=0; n<metadata.FieldCount; n++)
\r
222 ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberNames[n], null, null);
\r
224 for (int n=0; n<metadata.FieldCount; n++)
\r
225 ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberInfos[n].Name, metadata.MemberInfos[n], null);
\r
228 private void RegisterObject (long objectId, object objectInstance, SerializationInfo info, long parentObjectId, MemberInfo parentObjectMemeber, int[] indices)
\r
230 if (parentObjectId == 0) indices = null;
\r
232 if (!objectInstance.GetType().IsValueType || parentObjectId == 0)
\r
233 _manager.RegisterObject (objectInstance, objectId, info, 0, null, null);
\r
236 if (indices != null) indices = (int[])indices.Clone();
\r
237 _manager.RegisterObject (objectInstance, objectId, info, parentObjectId, parentObjectMemeber, indices);
\r
241 private void ReadStringIntance (BinaryReader reader, out long objectId, out object value)
\r
243 objectId = (long) reader.ReadUInt32 ();
\r
244 value = reader.ReadString ();
\r
247 private void ReadGenericArray (BinaryReader reader, out long objectId, out object val)
\r
249 objectId = (long) reader.ReadUInt32 ();
\r
250 ArrayStructure structure = (ArrayStructure) reader.ReadByte();
\r
252 int rank = reader.ReadInt32();
\r
254 bool emptyDim = false;
\r
255 int[] lengths = new int[rank];
\r
256 for (int n=0; n<rank; n++)
\r
258 lengths[n] = reader.ReadInt32();
\r
259 if (lengths[n] == 0) emptyDim = true;
\r
262 TypeTag code = (TypeTag) reader.ReadByte ();
\r
263 Type elementType = ReadType (reader, code);
\r
265 Array array = Array.CreateInstance (elementType, lengths);
\r
273 int[] indices = new int[rank];
\r
275 // Initialize indexes
\r
276 for (int dim = rank-1; dim >= 0; dim--)
\r
277 indices[dim] = array.GetLowerBound (dim);
\r
282 ReadValue (reader, array, objectId, null, elementType, null, null, indices);
\r
284 for (int dim = array.Rank-1; dim >= 0; dim--)
\r
287 if (indices[dim] > array.GetUpperBound (dim))
\r
291 indices[dim] = array.GetLowerBound (dim);
\r
292 continue; // Increment the next dimension's index
\r
294 end = true; // That was the last dimension. Finished.
\r
302 private object ReadBoxedPrimitiveTypeValue (BinaryReader reader)
\r
304 Type type = ReadType (reader, TypeTag.PrimitiveType);
\r
305 return ReadPrimitiveTypeValue (reader, type);
\r
308 private void ReadArrayOfPrimitiveType (BinaryReader reader, out long objectId, out object val)
\r
310 objectId = (long) reader.ReadUInt32 ();
\r
311 int length = reader.ReadInt32 ();
\r
312 Type elementType = ReadType (reader, TypeTag.PrimitiveType);
\r
314 Array array = Array.CreateInstance (elementType, length);
\r
315 for (int n = 0; n < length; n++)
\r
316 array.SetValue (ReadPrimitiveTypeValue (reader, elementType), n);
\r
321 private void ReadArrayOfObject (BinaryReader reader, out long objectId, out object array)
\r
323 ReadSimpleArray (reader, typeof (object), out objectId, out array);
\r
326 private void ReadArrayOfString (BinaryReader reader, out long objectId, out object array)
\r
328 ReadSimpleArray (reader, typeof (string), out objectId, out array);
\r
331 private void ReadSimpleArray (BinaryReader reader, Type elementType, out long objectId, out object val)
\r
333 objectId = (long) reader.ReadUInt32 ();
\r
334 int length = reader.ReadInt32 ();
\r
335 int[] indices = new int[1];
\r
337 Array array = Array.CreateInstance (elementType, length);
\r
338 for (int n = 0; n < length; n++)
\r
341 ReadValue (reader, array, objectId, null, elementType, null, null, indices);
\r
347 private TypeMetadata ReadTypeMetadata (BinaryReader reader, bool isRuntimeObject)
\r
349 TypeMetadata metadata = new TypeMetadata();
\r
351 string className = reader.ReadString ();
\r
352 int fieldCount = reader.ReadInt32 ();
\r
354 Type[] types = new Type[fieldCount];
\r
355 string[] names = new string[fieldCount];
\r
357 TypeTag[] codes = new TypeTag[fieldCount];
\r
359 for (int n=0; n<fieldCount; n++)
\r
360 names [n] = reader.ReadString ();
\r
362 for (int n=0; n<fieldCount; n++)
\r
363 codes [n] = (TypeTag) reader.ReadByte ();
\r
365 for (int n=0; n<fieldCount; n++)
\r
366 types [n] = ReadType (reader, codes[n]);
\r
370 if (!isRuntimeObject)
\r
372 long assemblyId = (long)reader.ReadUInt32();
\r
373 metadata.Type = GetDeserializationType (assemblyId, className);
\r
376 metadata.Type = Type.GetType (className, true);
\r
378 metadata.MemberTypes = types;
\r
379 metadata.MemberNames = names;
\r
380 metadata.FieldCount = names.Length;
\r
382 // Now check if this objects needs a SerializationInfo struct for deserialziation.
\r
383 // SerializationInfo is needed if the object has to be deserialized using
\r
384 // a serialization surrogate, or if it implements ISerializable.
\r
386 if (_surrogateSelector != null)
\r
388 // check if the surrogate selector handles objects of the given type.
\r
389 ISurrogateSelector selector;
\r
390 ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (metadata.Type, _context, out selector);
\r
391 metadata.NeedsSerializationInfo = (surrogate != null);
\r
394 if (!metadata.NeedsSerializationInfo)
\r
396 // Check if the object is marked with the Serializable attribute
\r
398 if (!metadata.Type.IsSerializable)
\r
399 throw new SerializationException("Serializable objects must be marked with the Serializable attribute");
\r
401 metadata.NeedsSerializationInfo = (metadata.Type.GetInterface ("ISerializable") != null);
\r
402 if (!metadata.NeedsSerializationInfo)
\r
404 metadata.MemberInfos = new MemberInfo [fieldCount];
\r
405 for (int n=0; n<fieldCount; n++)
\r
407 MemberInfo[] members = metadata.Type.GetMember (names[n], MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
\r
408 if (members.Length > 1) throw new SerializationException ("There are two public members named \"" + names[n] + "\" in the class hirearchy of " + metadata.Type.FullName);
\r
409 if (members.Length == 0) throw new SerializationException ("Field \"" + names[n] + "\" not found in class " + metadata.Type.FullName);
\r
410 metadata.MemberInfos [n] = members[0];
\r
412 metadata.MemberNames = null; // Info now in MemberInfos
\r
416 // Registers the type's metadata so it can be reused later if
\r
417 // a RefTypeObject element is found
\r
419 if (!_typeMetadataCache.ContainsKey (metadata.Type))
\r
420 _typeMetadataCache [metadata.Type] = metadata;
\r
426 private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, MemberInfo memberInfo, int[] indices)
\r
428 // Reads a value from the stream and assigns it to the member of an object
\r
432 if (BinaryCommon.IsPrimitive (valueType))
\r
434 val = ReadPrimitiveTypeValue (reader, valueType);
\r
435 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);
\r
441 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
443 if (element == BinaryElement.ObjectReference)
\r
445 // Just read the id of the referred object and record a fixup
\r
446 long childObjectId = (long) reader.ReadUInt32();
\r
447 RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, memberInfo, indices);
\r
452 SerializationInfo objectInfo;
\r
454 ReadObject (element, reader, out objectId, out val, out objectInfo);
\r
456 // There are two cases where the object cannot be assigned to the parent
\r
457 // and a fixup must be used:
\r
458 // 1) When what has been read is not an object, but an id of an object that
\r
459 // has not been read yet (an object reference). This is managed in the
\r
460 // previous block of code.
\r
461 // 2) When the read object is a value type object. Value type fields hold
\r
462 // copies of objects, not references. Thus, if the value object that
\r
463 // has been read has pending fixups, those fixups would be made to the
\r
464 // boxed copy in the ObjectManager, and not in the required object instance
\r
466 // First of all register the fixup, and then the object. ObjectManager is more
\r
467 // efficient if done in this order
\r
469 bool hasFixup = false;
\r
472 if (val.GetType().IsValueType)
\r
474 RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, memberInfo, indices);
\r
478 // Register the value
\r
480 if (info == null && !parentObject.GetType().IsArray)
\r
481 RegisterObject (objectId, val, objectInfo, parentObjectId, memberInfo, null);
\r
483 RegisterObject (objectId, val, objectInfo, parentObjectId, null, indices);
\r
485 // Assign the value to the parent object, unless there is a fixup
\r
488 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);
\r
491 private void SetObjectValue (object parentObject, string fieldName, MemberInfo memberInfo, SerializationInfo info, object value, Type valueType, int[] indices)
\r
493 if (value is IObjectReference)
\r
494 value = ((IObjectReference)value).GetRealObject (_context);
\r
496 if (parentObject.GetType().IsArray)
\r
498 if (value is ArrayNullFiller)
\r
500 // It must be a single dimension array of objects.
\r
501 // Just increase the index. Elements are null by default.
\r
502 int count = ((ArrayNullFiller)value).NullCount;
\r
503 indices[0] += count - 1;
\r
506 ((Array)parentObject).SetValue (value, indices);
\r
508 else if (info != null) {
\r
509 info.AddValue (fieldName, value, valueType);
\r
512 if (memberInfo is FieldInfo)
\r
513 ((FieldInfo)memberInfo).SetValue (parentObject, value);
\r
515 ((PropertyInfo)memberInfo).SetValue (parentObject, value, null);
\r
519 private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, MemberInfo memberInfo, int[] indices)
\r
521 if (info != null) {
\r
522 _manager.RecordDelayedFixup (parentObjectId, fieldName, childObjectId);
\r
524 else if (parentObject.GetType().IsArray) {
\r
525 if (indices.Length == 1)
\r
526 _manager.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);
\r
528 _manager.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);
\r
531 _manager.RecordFixup (parentObjectId, memberInfo, childObjectId);
\r
535 private Type GetDeserializationType (long assemblyId, string className)
\r
537 string assemblyName = (string)_registeredAssemblies[assemblyId];
\r
539 if (_binder == null)
\r
541 Assembly assembly = Assembly.Load (assemblyName);
\r
542 return assembly.GetType (className, true);
\r
545 return _binder.BindToType (assemblyName, className);
\r
548 public Type ReadType (BinaryReader reader, TypeTag code)
\r
552 case TypeTag.PrimitiveType:
\r
553 return BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
555 case TypeTag.String:
\r
556 return typeof(string);
\r
558 case TypeTag.ObjectType:
\r
559 return typeof(object);
\r
561 case TypeTag.RuntimeType:
\r
563 string name = reader.ReadString ();
\r
564 return Type.GetType (name, true);
\r
567 case TypeTag.GenericType:
\r
569 string name = reader.ReadString ();
\r
570 long asmid = (long) reader.ReadUInt32();
\r
571 return GetDeserializationType (asmid, name);
\r
574 case TypeTag.ArrayOfObject:
\r
575 return typeof(object[]);
\r
577 case TypeTag.ArrayOfString:
\r
578 return typeof(string[]);
\r
580 case TypeTag.ArrayOfPrimitiveType:
\r
581 Type elementType = BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
582 return Type.GetType(elementType.FullName + "[]");
\r
585 throw new NotSupportedException ("Unknow type tag");
\r
589 public static object ReadPrimitiveTypeValue (BinaryReader reader, Type type)
\r
591 if (type == null) return null;
\r
593 switch (Type.GetTypeCode (type))
\r
595 case TypeCode.Boolean:
\r
596 return reader.ReadBoolean();
\r
598 case TypeCode.Byte:
\r
599 return reader.ReadByte();
\r
601 case TypeCode.Char:
\r
602 return reader.ReadChar();
\r
604 case TypeCode.DateTime:
\r
605 return new DateTime (reader.ReadInt64());
\r
607 case TypeCode.Decimal:
\r
608 return reader.ReadDecimal();
\r
610 case TypeCode.Double:
\r
611 return reader.ReadDouble();
\r
613 case TypeCode.Int16:
\r
614 return reader.ReadInt16();
\r
616 case TypeCode.Int32:
\r
617 return reader.ReadInt32();
\r
619 case TypeCode.Int64:
\r
620 return reader.ReadInt64();
\r
622 case TypeCode.SByte:
\r
623 return reader.ReadSByte();
\r
625 case TypeCode.Single:
\r
626 return reader.ReadSingle();
\r
628 case TypeCode.UInt16:
\r
629 return reader.ReadUInt16();
\r
631 case TypeCode.UInt32:
\r
632 return reader.ReadUInt32();
\r
634 case TypeCode.UInt64:
\r
635 return reader.ReadUInt64();
\r
637 case TypeCode.String:
\r
638 return reader.ReadString();
\r
641 if (type == typeof(TimeSpan))
\r
642 return new TimeSpan (reader.ReadInt64 ());
\r
644 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
\r