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 int FieldCount;
\r
40 public bool NeedsSerializationInfo;
\r
43 class ArrayNullFiller
\r
45 public ArrayNullFiller(int count) { NullCount = count; }
\r
46 public int NullCount;
\r
49 public ObjectReader(ISurrogateSelector surrogateSelector, StreamingContext context, SerializationBinder binder)
\r
51 _manager = new ObjectManager (surrogateSelector, context);
\r
52 _surrogateSelector = surrogateSelector;
\r
57 public object ReadObjectGraph (BinaryReader reader, bool readHeaders, HeaderHandler headerHandler)
\r
59 Header[] headers = null;
\r
61 // Reads the objects. The first object in the stream is the
\r
64 while (ReadNextObject (reader))
\r
66 if (readHeaders && (headers == null))
\r
67 headers = (Header[])CurrentObject;
\r
69 if (_rootObjectID == 0) _rootObjectID = _lastObjectID;
\r
72 if (readHeaders && headerHandler != null)
\r
73 headerHandler (headers);
\r
75 return _manager.GetObject (_rootObjectID);
\r
78 public bool ReadNextObject (BinaryReader reader)
\r
80 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
81 if (element == BinaryElement.End)
\r
83 _manager.DoFixups();
\r
84 _manager.RaiseDeserializationEvent();
\r
88 SerializationInfo info;
\r
91 ReadObject (element, reader, out objectId, out _lastObject, out info);
\r
93 if (objectId != 0) {
\r
94 RegisterObject (objectId, _lastObject, info, 0, null, null);
\r
95 _lastObjectID = objectId;
\r
101 public object CurrentObject
\r
103 get { return _lastObject; }
\r
106 // Reads an object from the stream. The object is registered in the ObjectManager.
\r
107 // The result can be either the object instance
\r
108 // or the id of the object (when what is found in the stream is an object reference).
\r
109 // If an object instance is read, the objectId is set to 0.
\r
111 private void ReadObject (BinaryElement element, BinaryReader reader, out long objectId, out object value, out SerializationInfo info)
\r
115 case BinaryElement.RefTypeObject:
\r
116 ReadRefTypeObjectInstance (reader, out objectId, out value, out info);
\r
119 case BinaryElement.RuntimeObject:
\r
120 ReadObjectInstance (reader, true, out objectId, out value, out info);
\r
123 case BinaryElement.ExternalObject:
\r
124 ReadObjectInstance (reader, false, out objectId, out value, out info);
\r
127 case BinaryElement.String:
\r
129 ReadStringIntance (reader, out objectId, out value);
\r
132 case BinaryElement.GenericArray:
\r
134 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 for (int n=0; n<metadata.FieldCount; n++)
\r
221 ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberNames[n], null);
\r
224 private void RegisterObject (long objectId, object objectInstance, SerializationInfo info, long parentObjectId, MemberInfo parentObjectMemeber, int[] indices)
\r
226 if (parentObjectId == 0) indices = null;
\r
228 if (!objectInstance.GetType().IsValueType || parentObjectId == 0)
\r
229 _manager.RegisterObject (objectInstance, objectId, info, 0, null, null);
\r
232 if (indices != null) indices = (int[])indices.Clone();
\r
233 _manager.RegisterObject (objectInstance, objectId, info, parentObjectId, parentObjectMemeber, indices);
\r
237 private void ReadStringIntance (BinaryReader reader, out long objectId, out object value)
\r
239 objectId = (long) reader.ReadUInt32 ();
\r
240 value = reader.ReadString ();
\r
243 private void ReadGenericArray (BinaryReader reader, out long objectId, out object val)
\r
245 objectId = (long) reader.ReadUInt32 ();
\r
246 ArrayStructure structure = (ArrayStructure) reader.ReadByte();
\r
248 int rank = reader.ReadInt32();
\r
250 bool emptyDim = false;
\r
251 int[] lengths = new int[rank];
\r
252 for (int n=0; n<rank; n++)
\r
254 lengths[n] = reader.ReadInt32();
\r
255 if (lengths[n] == 0) emptyDim = true;
\r
258 TypeTag code = (TypeTag) reader.ReadByte ();
\r
259 Type elementType = ReadType (reader, code);
\r
261 Array array = Array.CreateInstance (elementType, lengths);
\r
269 int[] indices = new int[rank];
\r
271 // Initialize indexes
\r
272 for (int dim = rank-1; dim >= 0; dim--)
\r
273 indices[dim] = array.GetLowerBound (dim);
\r
278 ReadValue (reader, array, objectId, null, elementType, null, indices);
\r
280 for (int dim = array.Rank-1; dim >= 0; dim--)
\r
283 if (indices[dim] > array.GetUpperBound (dim))
\r
287 indices[dim] = array.GetLowerBound (dim);
\r
288 continue; // Increment the next dimension's index
\r
290 end = true; // That was the last dimension. Finished.
\r
298 private object ReadBoxedPrimitiveTypeValue (BinaryReader reader)
\r
300 Type type = ReadType (reader, TypeTag.PrimitiveType);
\r
301 return ReadPrimitiveTypeValue (reader, type);
\r
304 private void ReadArrayOfPrimitiveType (BinaryReader reader, out long objectId, out object val)
\r
306 objectId = (long) reader.ReadUInt32 ();
\r
307 int length = reader.ReadInt32 ();
\r
308 Type elementType = ReadType (reader, TypeTag.PrimitiveType);
\r
310 Array array = Array.CreateInstance (elementType, length);
\r
311 for (int n = 0; n < length; n++)
\r
312 array.SetValue (ReadPrimitiveTypeValue (reader, elementType), n);
\r
317 private void ReadArrayOfObject (BinaryReader reader, out long objectId, out object array)
\r
319 ReadSimpleArray (reader, typeof (object), out objectId, out array);
\r
322 private void ReadArrayOfString (BinaryReader reader, out long objectId, out object array)
\r
324 ReadSimpleArray (reader, typeof (string), out objectId, out array);
\r
327 private void ReadSimpleArray (BinaryReader reader, Type elementType, out long objectId, out object val)
\r
329 objectId = (long) reader.ReadUInt32 ();
\r
330 int length = reader.ReadInt32 ();
\r
331 int[] indices = new int[1];
\r
333 Array array = Array.CreateInstance (elementType, length);
\r
334 for (int n = 0; n < length; n++)
\r
337 ReadValue (reader, array, objectId, null, elementType, null, indices);
\r
343 private TypeMetadata ReadTypeMetadata (BinaryReader reader, bool isRuntimeObject)
\r
345 TypeMetadata metadata = new TypeMetadata();
\r
347 string className = reader.ReadString ();
\r
348 int fieldCount = reader.ReadInt32 ();
\r
350 Type[] types = new Type[fieldCount];
\r
351 string[] names = new string[fieldCount];
\r
353 TypeTag[] codes = new TypeTag[fieldCount];
\r
355 for (int n=0; n<fieldCount; n++)
\r
356 names [n] = reader.ReadString ();
\r
358 for (int n=0; n<fieldCount; n++)
\r
359 codes [n] = (TypeTag) reader.ReadByte ();
\r
361 for (int n=0; n<fieldCount; n++)
\r
362 types [n] = ReadType (reader, codes[n]);
\r
366 if (!isRuntimeObject)
\r
368 long assemblyId = (long)reader.ReadUInt32();
\r
369 metadata.Type = GetDeserializationType (assemblyId, className);
\r
372 metadata.Type = Type.GetType (className, true);
\r
374 metadata.MemberTypes = types;
\r
375 metadata.MemberNames = names;
\r
376 metadata.FieldCount = names.Length;
\r
378 // Now check if this objects needs a SerializationInfo struct for deserialziation.
\r
379 // SerializationInfo is needed if the object has to be deserialized using
\r
380 // a serialization surrogate, or if it implements ISerializable.
\r
382 if (_surrogateSelector != null)
\r
384 // check if the surrogate selector handles objects of the given type.
\r
385 ISurrogateSelector selector;
\r
386 ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (metadata.Type, _context, out selector);
\r
387 metadata.NeedsSerializationInfo = (surrogate != null);
\r
390 if (!metadata.NeedsSerializationInfo)
\r
392 // Check if the object is marked with the Serializable attribute
\r
394 if (!metadata.Type.IsSerializable)
\r
395 throw new SerializationException("Serializable objects must be marked with the Serializable attribute");
\r
397 metadata.NeedsSerializationInfo = (metadata.Type.GetInterface ("ISerializable") != null);
\r
400 // Registers the type's metadata so it can be reused later if
\r
401 // a RefTypeObject element is found
\r
403 if (!_typeMetadataCache.ContainsKey (metadata.Type))
\r
404 _typeMetadataCache [metadata.Type] = metadata;
\r
410 private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, int[] indices)
\r
412 // Reads a value from the stream and assigns it to the member of an object
\r
416 if (BinaryCommon.IsPrimitive (valueType))
\r
418 val = ReadPrimitiveTypeValue (reader, valueType);
\r
419 SetObjectValue (parentObject, fieldName, info, val, valueType, indices);
\r
425 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
427 if (element == BinaryElement.ObjectReference)
\r
429 // Just read the id of the referred object and record a fixup
\r
430 long childObjectId = (long) reader.ReadUInt32();
\r
431 RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, indices);
\r
436 SerializationInfo objectInfo;
\r
438 ReadObject (element, reader, out objectId, out val, out objectInfo);
\r
440 // There are two cases where the object cannot be assigned to the parent
\r
441 // and a fixup must be used:
\r
442 // 1) When what has been read is not an object, but an id of an object that
\r
443 // has not been read yet (an object reference). This is managed in the
\r
444 // previous block of code.
\r
445 // 2) When the read object is a value type object. Value type fields hold
\r
446 // copies of objects, not references. Thus, if the value object that
\r
447 // has been read has pending fixups, those fixups would be made to the
\r
448 // boxed copy in the ObjectManager, and not in the required object instance
\r
450 // First of all register the fixup, and then the object. ObjectManager is more
\r
451 // efficient if done in this order
\r
453 bool hasFixup = false;
\r
456 if (val.GetType().IsValueType)
\r
458 RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, indices);
\r
462 // Register the value
\r
464 if (info == null && !parentObject.GetType().IsArray)
\r
465 RegisterObject (objectId, val, objectInfo, parentObjectId, GetObjectMember(parentObject, fieldName), null);
\r
467 RegisterObject (objectId, val, objectInfo, parentObjectId, null, indices);
\r
469 // Assign the value to the parent object, unless there is a fixup
\r
472 SetObjectValue (parentObject, fieldName, info, val, valueType, indices);
\r
475 private void SetObjectValue (object parentObject, string fieldName, SerializationInfo info, object value, Type valueType, int[] indices)
\r
477 if (value is IObjectReference)
\r
478 value = ((IObjectReference)value).GetRealObject (_context);
\r
480 if (parentObject.GetType().IsArray)
\r
482 if (value is ArrayNullFiller)
\r
484 // It must be a single dimension array of objects.
\r
485 // Just increase the index. Elements are null by default.
\r
486 int count = ((ArrayNullFiller)value).NullCount;
\r
487 indices[0] += count - 1;
\r
490 ((Array)parentObject).SetValue (value, indices);
\r
492 else if (info != null) {
\r
493 info.AddValue (fieldName, value, valueType);
\r
496 MemberInfo member = GetObjectMember(parentObject, fieldName);
\r
497 if (member is FieldInfo)
\r
498 ((FieldInfo)member).SetValue (parentObject, value);
\r
500 ((PropertyInfo)member).SetValue (parentObject, value, null);
\r
504 private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, int[] indices)
\r
506 if (info != null) {
\r
507 _manager.RecordDelayedFixup (parentObjectId, fieldName, childObjectId);
\r
509 else if (parentObject.GetType().IsArray) {
\r
510 if (indices.Length == 1)
\r
511 _manager.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);
\r
513 _manager.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);
\r
516 _manager.RecordFixup (parentObjectId, GetObjectMember(parentObject, fieldName), childObjectId);
\r
520 private MemberInfo GetObjectMember (object parentObject, string fieldName)
\r
522 MemberInfo[] members = parentObject.GetType().GetMember (fieldName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
\r
523 if (members.Length > 1) throw new SerializationException ("There are two public members named \"" + fieldName + "\" in the class hirearchy of " + parentObject.GetType().FullName);
\r
524 if (members.Length == 0) throw new SerializationException ("Field \"" + fieldName + "\" not found in class " + parentObject.GetType().FullName);
\r
528 private Type GetDeserializationType (long assemblyId, string className)
\r
530 string assemblyName = (string)_registeredAssemblies[assemblyId];
\r
532 if (_binder == null)
\r
534 Assembly assembly = Assembly.Load (assemblyName);
\r
535 return assembly.GetType (className, true);
\r
538 return _binder.BindToType (assemblyName, className);
\r
541 public Type ReadType (BinaryReader reader, TypeTag code)
\r
545 case TypeTag.PrimitiveType:
\r
546 return BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
548 case TypeTag.String:
\r
549 return typeof(string);
\r
551 case TypeTag.ObjectType:
\r
552 return typeof(object);
\r
554 case TypeTag.RuntimeType:
\r
556 string name = reader.ReadString ();
\r
557 return Type.GetType (name, true);
\r
560 case TypeTag.GenericType:
\r
562 string name = reader.ReadString ();
\r
563 long asmid = (long) reader.ReadUInt32();
\r
564 return GetDeserializationType (asmid, name);
\r
567 case TypeTag.ArrayOfObject:
\r
568 return typeof(object[]);
\r
570 case TypeTag.ArrayOfString:
\r
571 return typeof(string[]);
\r
573 case TypeTag.ArrayOfPrimitiveType:
\r
574 Type elementType = BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
575 return Type.GetType(elementType.FullName + "[]");
\r
578 throw new NotSupportedException ("Unknow type tag");
\r
582 public static object ReadPrimitiveTypeValue (BinaryReader reader, Type type)
\r
584 if (type == null) return null;
\r
586 switch (Type.GetTypeCode (type))
\r
588 case TypeCode.Boolean:
\r
589 return reader.ReadBoolean();
\r
591 case TypeCode.Byte:
\r
592 return reader.ReadByte();
\r
594 case TypeCode.Char:
\r
595 return reader.ReadChar();
\r
597 case TypeCode.DateTime:
\r
598 long ticks = reader.ReadInt64();
\r
599 return new DateTime (ticks);
\r
601 case TypeCode.Decimal:
\r
602 return reader.ReadDecimal();
\r
604 case TypeCode.Double:
\r
605 return reader.ReadDouble();
\r
607 case TypeCode.Int16:
\r
608 return reader.ReadInt16();
\r
610 case TypeCode.Int32:
\r
611 return reader.ReadInt32();
\r
613 case TypeCode.Int64:
\r
614 return reader.ReadInt64();
\r
616 case TypeCode.SByte:
\r
617 return reader.ReadSByte();
\r
619 case TypeCode.Single:
\r
620 return reader.ReadSingle();
\r
622 case TypeCode.UInt16:
\r
623 return reader.ReadUInt16();
\r
625 case TypeCode.UInt32:
\r
626 return reader.ReadUInt32();
\r
628 case TypeCode.UInt64:
\r
629 return reader.ReadUInt64();
\r
631 case TypeCode.String:
\r
632 return reader.ReadString();
\r
635 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
\r