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
25 ObjectManager _manager;
\r
26 Hashtable _registeredAssemblies = new Hashtable();
\r
27 Hashtable _typeMetadataCache = new Hashtable();
\r
29 object _lastObject = null;
\r
30 long _lastObjectID = 0;
\r
31 long _rootObjectID = 0;
\r
36 public Type[] MemberTypes;
\r
37 public string[] MemberNames;
\r
38 public int FieldCount;
\r
39 public bool NeedsSerializationInfo;
\r
42 class ArrayNullFiller
\r
44 public ArrayNullFiller(int count) { NullCount = count; }
\r
45 public int NullCount;
\r
48 public ObjectReader(ISurrogateSelector surrogateSelector, StreamingContext context)
\r
50 _manager = new ObjectManager (surrogateSelector, context);
\r
51 _surrogateSelector = surrogateSelector;
\r
55 public object ReadObjectGraph (BinaryReader reader, bool readHeaders, HeaderHandler headerHandler)
\r
57 Header[] headers = null;
\r
59 // Reads the objects. The first object in the stream is the
\r
62 while (ReadNextObject (reader))
\r
64 if (readHeaders && (headers == null))
\r
65 headers = (Header[])CurrentObject;
\r
67 if (_rootObjectID == 0) _rootObjectID = _lastObjectID;
\r
70 if (readHeaders && headerHandler != null)
\r
71 headerHandler (headers);
\r
73 return _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
82 _manager.RaiseDeserializationEvent();
\r
86 SerializationInfo info;
\r
89 ReadObject (element, reader, out objectId, out _lastObject, out info);
\r
91 if (objectId != 0) {
\r
92 RegisterObject (objectId, _lastObject, info, 0, null, null);
\r
93 _lastObjectID = objectId;
\r
99 public object CurrentObject
\r
101 get { return _lastObject; }
\r
104 // Reads an object from the stream. The object is registered in the ObjectManager.
\r
105 // The result can be either the object instance
\r
106 // or the id of the object (when what is found in the stream is an object reference).
\r
107 // If an object instance is read, the objectId is set to 0.
\r
109 private void ReadObject (BinaryElement element, BinaryReader reader, out long objectId, out object value, out SerializationInfo info)
\r
113 case BinaryElement.RefTypeObject:
\r
114 ReadRefTypeObjectInstance (reader, out objectId, out value, out info);
\r
117 case BinaryElement.RuntimeObject:
\r
118 ReadObjectInstance (reader, true, out objectId, out value, out info);
\r
121 case BinaryElement.ExternalObject:
\r
122 ReadObjectInstance (reader, false, out objectId, out value, out info);
\r
125 case BinaryElement.String:
\r
127 ReadStringIntance (reader, out objectId, out value);
\r
130 case BinaryElement.GenericArray:
\r
132 ReadGenericArray (reader, out objectId, out value);
\r
135 case BinaryElement.BoxedPrimitiveTypeValue:
\r
136 value = ReadBoxedPrimitiveTypeValue (reader);
\r
141 case BinaryElement.NullValue:
\r
147 case BinaryElement.Assembly:
\r
148 ReadAssembly (reader);
\r
149 ReadObject ((BinaryElement)reader.ReadByte (), reader, out objectId, out value, out info);
\r
152 case BinaryElement.ArrayFiller8b:
\r
153 value = new ArrayNullFiller(reader.ReadByte());
\r
158 case BinaryElement.ArrayFiller32b:
\r
159 value = new ArrayNullFiller(reader.ReadInt32());
\r
164 case BinaryElement.ArrayOfPrimitiveType:
\r
165 ReadArrayOfPrimitiveType (reader, out objectId, out value);
\r
169 case BinaryElement.ArrayOfObject:
\r
170 ReadArrayOfObject (reader, out objectId, out value);
\r
174 case BinaryElement.ArrayOfString:
\r
175 ReadArrayOfString (reader, out objectId, out value);
\r
180 throw new SerializationException ("Unexpected binary element: " + (int)element);
\r
184 private void ReadAssembly (BinaryReader reader)
\r
186 long id = (long) reader.ReadUInt32 ();
\r
187 string assemblyName = reader.ReadString ();
\r
188 Assembly assembly = Assembly.Load (assemblyName);
\r
189 _registeredAssemblies [id] = assembly;
\r
192 private void ReadObjectInstance (BinaryReader reader, bool isRuntimeObject, out long objectId, out object value, out SerializationInfo info)
\r
194 objectId = (long) reader.ReadUInt32 ();
\r
196 TypeMetadata metadata = ReadTypeMetadata (reader, isRuntimeObject);
\r
197 ReadObjectContent (reader, metadata, objectId, out value, out info);
\r
200 private void ReadRefTypeObjectInstance (BinaryReader reader, out long objectId, out object value, out SerializationInfo info)
\r
202 objectId = (long) reader.ReadUInt32 ();
\r
203 long refTypeObjectId = (long) reader.ReadUInt32 ();
\r
205 // Gets the type of the referred object and its metadata
\r
207 object refObj = _manager.GetObject (refTypeObjectId);
\r
208 if (refObj == null) throw new SerializationException ("Invalid binary format");
\r
209 TypeMetadata metadata = (TypeMetadata)_typeMetadataCache [refObj.GetType()];
\r
211 ReadObjectContent (reader, metadata, objectId, out value, out info);
\r
214 private void ReadObjectContent (BinaryReader reader, TypeMetadata metadata, long objectId, out object objectInstance, out SerializationInfo info)
\r
216 objectInstance = FormatterServices.GetUninitializedObject (metadata.Type);
\r
217 info = metadata.NeedsSerializationInfo ? new SerializationInfo(metadata.Type, new FormatterConverter()) : null;
\r
219 for (int n=0; n<metadata.FieldCount; n++)
\r
220 ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberNames[n], null);
\r
223 private void RegisterObject (long objectId, object objectInstance, SerializationInfo info, long parentObjectId, MemberInfo parentObjectMemeber, int[] indices)
\r
225 if (!objectInstance.GetType().IsValueType || parentObjectId == 0)
\r
226 _manager.RegisterObject (objectInstance, objectId, info, 0, null, indices);
\r
228 _manager.RegisterObject (objectInstance, objectId, info, parentObjectId, parentObjectMemeber, indices);
\r
231 private void ReadStringIntance (BinaryReader reader, out long objectId, out object value)
\r
233 objectId = (long) reader.ReadUInt32 ();
\r
234 value = reader.ReadString ();
\r
237 private void ReadGenericArray (BinaryReader reader, out long objectId, out object val)
\r
239 objectId = (long) reader.ReadUInt32 ();
\r
240 ArrayStructure structure = (ArrayStructure) reader.ReadByte();
\r
242 int rank = reader.ReadInt32();
\r
244 bool emptyDim = false;
\r
245 int[] lengths = new int[rank];
\r
246 for (int n=0; n<rank; n++)
\r
248 lengths[n] = reader.ReadInt32();
\r
249 if (lengths[n] == 0) emptyDim = true;
\r
252 TypeTag code = (TypeTag) reader.ReadByte ();
\r
253 Type elementType = ReadType (reader, code);
\r
255 Array array = Array.CreateInstance (elementType, lengths);
\r
263 int[] indices = new int[rank];
\r
265 // Initialize indexes
\r
266 for (int dim = rank-1; dim >= 0; dim--)
\r
267 indices[dim] = array.GetLowerBound (dim);
\r
272 ReadValue (reader, array, objectId, null, elementType, null, indices);
\r
274 for (int dim = array.Rank-1; dim >= 0; dim--)
\r
277 if (indices[dim] > array.GetUpperBound (dim))
\r
281 indices[dim] = array.GetLowerBound (dim);
\r
282 continue; // Increment the next dimension's index
\r
284 end = true; // That was the last dimension. Finished.
\r
292 private object ReadBoxedPrimitiveTypeValue (BinaryReader reader)
\r
294 Type type = ReadType (reader, TypeTag.PrimitiveType);
\r
295 return ReadPrimitiveTypeValue (reader, type);
\r
298 private void ReadArrayOfPrimitiveType (BinaryReader reader, out long objectId, out object val)
\r
300 objectId = (long) reader.ReadUInt32 ();
\r
301 int length = reader.ReadInt32 ();
\r
302 Type elementType = ReadType (reader, TypeTag.PrimitiveType);
\r
304 Array array = Array.CreateInstance (elementType, length);
\r
305 for (int n = 0; n < length; n++)
\r
306 array.SetValue (ReadPrimitiveTypeValue (reader, elementType), n);
\r
311 private void ReadArrayOfObject (BinaryReader reader, out long objectId, out object array)
\r
313 ReadSimpleArray (reader, typeof (object), out objectId, out array);
\r
316 private void ReadArrayOfString (BinaryReader reader, out long objectId, out object array)
\r
318 ReadSimpleArray (reader, typeof (string), out objectId, out array);
\r
321 private void ReadSimpleArray (BinaryReader reader, Type elementType, out long objectId, out object val)
\r
323 objectId = (long) reader.ReadUInt32 ();
\r
324 int length = reader.ReadInt32 ();
\r
325 int[] indices = new int[1];
\r
327 Array array = Array.CreateInstance (elementType, length);
\r
328 for (int n = 0; n < length; n++)
\r
331 ReadValue (reader, array, objectId, null, elementType, null, indices);
\r
337 private TypeMetadata ReadTypeMetadata (BinaryReader reader, bool isRuntimeObject)
\r
339 TypeMetadata metadata = new TypeMetadata();
\r
341 string className = reader.ReadString ();
\r
342 int fieldCount = reader.ReadInt32 ();
\r
344 Type[] types = new Type[fieldCount];
\r
345 string[] names = new string[fieldCount];
\r
347 TypeTag[] codes = new TypeTag[fieldCount];
\r
349 for (int n=0; n<fieldCount; n++)
\r
350 names [n] = reader.ReadString ();
\r
352 for (int n=0; n<fieldCount; n++)
\r
353 codes [n] = (TypeTag) reader.ReadByte ();
\r
355 for (int n=0; n<fieldCount; n++)
\r
356 types [n] = ReadType (reader, codes[n]);
\r
360 if (!isRuntimeObject)
\r
362 long assemblyId = (long)reader.ReadUInt32();
\r
363 Assembly asm = (Assembly)_registeredAssemblies[assemblyId];
\r
364 metadata.Type = asm.GetType (className, true);
\r
367 metadata.Type = Type.GetType (className, true);
\r
369 metadata.MemberTypes = types;
\r
370 metadata.MemberNames = names;
\r
371 metadata.FieldCount = names.Length;
\r
373 // Now check if this objects needs a SerializationInfo struct for deserialziation.
\r
374 // SerializationInfo is needed if the object has to be deserialized using
\r
375 // a serialization surrogate, or if it implements ISerializable.
\r
377 if (_surrogateSelector != null)
\r
379 // check if the surrogate selector handles objects of the given type.
\r
380 ISurrogateSelector selector;
\r
381 ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (metadata.Type, _context, out selector);
\r
382 metadata.NeedsSerializationInfo = (surrogate != null);
\r
385 if (!metadata.NeedsSerializationInfo)
\r
387 // Check if the object is marked with the Serializable attribute
\r
389 if (!metadata.Type.IsSerializable)
\r
390 throw new SerializationException("Serializable objects must be marked with the Serializable attribute");
\r
392 metadata.NeedsSerializationInfo = (metadata.Type.GetInterface ("ISerializable") != null);
\r
395 // Registers the type's metadata so it can be reused later if
\r
396 // a RefTypeObject element is found
\r
398 if (!_typeMetadataCache.ContainsKey (metadata.Type))
\r
399 _typeMetadataCache [metadata.Type] = metadata;
\r
405 private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, int[] indices)
\r
407 // Reads a value from the stream and assigns it to the member of an object
\r
411 if (BinaryCommon.IsPrimitive (valueType))
\r
413 val = ReadPrimitiveTypeValue (reader, valueType);
\r
414 SetObjectValue (parentObject, fieldName, info, val, valueType, indices);
\r
420 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
422 if (element == BinaryElement.ObjectReference)
\r
424 // Just read the id of the referred object and record a fixup
\r
425 long childObjectId = (long) reader.ReadUInt32();
\r
426 RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, indices);
\r
431 SerializationInfo objectInfo;
\r
433 ReadObject (element, reader, out objectId, out val, out objectInfo);
\r
435 // There are two cases where the object cannot be assigned to the parent
\r
436 // and a fixup must be used:
\r
437 // 1) When what has been read is not an object, but an id of an object that
\r
438 // has not been read yet (an object reference). This is managed in the
\r
439 // previous block of code.
\r
440 // 2) When the read object is a value type object. Value type fields hold
\r
441 // copies of objects, not references. Thus, if the value object that
\r
442 // has been read has pending fixups, those fixups would be made to the
\r
443 // boxed copy in the ObjectManager, and not in the required object instance
\r
445 // First of all register the fixup, and then the object. ObjectManager is more
\r
446 // efficient if done in this order
\r
448 bool hasFixup = false;
\r
451 if (val.GetType().IsValueType)
\r
453 RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, indices);
\r
457 // Register the value
\r
459 if (info == null && !parentObject.GetType().IsArray)
\r
460 RegisterObject (objectId, val, objectInfo, parentObjectId, GetObjectMember(parentObject, fieldName), null);
\r
462 RegisterObject (objectId, val, objectInfo, parentObjectId, null, indices);
\r
464 // Assign the value to the parent object, unless there is a fixup
\r
467 SetObjectValue (parentObject, fieldName, info, val, valueType, indices);
\r
470 private void SetObjectValue (object parentObject, string fieldName, SerializationInfo info, object value, Type valueType, int[] indices)
\r
472 if (value is IObjectReference)
\r
473 value = ((IObjectReference)value).GetRealObject (_context);
\r
475 if (parentObject.GetType().IsArray)
\r
477 if (value is ArrayNullFiller)
\r
479 // It must be a single dimension array of objects.
\r
480 // Just increase the index. Elements are null by default.
\r
481 int count = ((ArrayNullFiller)value).NullCount;
\r
482 indices[0] += count - 1;
\r
485 ((Array)parentObject).SetValue (value, indices);
\r
487 else if (info != null) {
\r
488 info.AddValue (fieldName, value, valueType);
\r
491 MemberInfo member = GetObjectMember(parentObject, fieldName);
\r
492 if (member is FieldInfo)
\r
493 ((FieldInfo)member).SetValue (parentObject, value);
\r
495 ((PropertyInfo)member).SetValue (parentObject, value, null);
\r
499 private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, int[] indices)
\r
501 if (info != null) {
\r
502 _manager.RecordDelayedFixup (parentObjectId, fieldName, childObjectId);
\r
504 else if (parentObject.GetType().IsArray) {
\r
505 if (indices.Length == 1)
\r
506 _manager.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);
\r
508 _manager.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);
\r
511 _manager.RecordFixup (parentObjectId, GetObjectMember(parentObject, fieldName), childObjectId);
\r
515 private MemberInfo GetObjectMember (object parentObject, string fieldName)
\r
517 MemberInfo[] members = parentObject.GetType().GetMember (fieldName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
\r
518 if (members.Length > 1) throw new SerializationException ("There are two public members named \"" + fieldName + "\" in the class hirearchy of " + parentObject.GetType().FullName);
\r
519 if (members.Length == 0) throw new SerializationException ("Field \"" + fieldName + "\" not found in class " + parentObject.GetType().FullName);
\r
523 public Type ReadType (BinaryReader reader, TypeTag code)
\r
527 case TypeTag.PrimitiveType:
\r
528 return BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
530 case TypeTag.String:
\r
531 return typeof(string);
\r
533 case TypeTag.ObjectType:
\r
534 return typeof(object);
\r
536 case TypeTag.RuntimeType:
\r
538 string name = reader.ReadString ();
\r
539 return Type.GetType (name, true);
\r
542 case TypeTag.GenericType:
\r
544 string name = reader.ReadString ();
\r
545 long asmid = (long) reader.ReadUInt32();
\r
546 Assembly asm = (Assembly)_registeredAssemblies[asmid];
\r
547 return asm.GetType (name, true);
\r
550 case TypeTag.ArrayOfObject:
\r
551 return typeof(object[]);
\r
553 case TypeTag.ArrayOfString:
\r
554 return typeof(string[]);
\r
556 case TypeTag.ArrayOfPrimitiveType:
\r
557 Type elementType = BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
558 return Type.GetType(elementType.FullName + "[]");
\r
561 throw new NotSupportedException ("Unknow type tag");
\r
565 public static object ReadPrimitiveTypeValue (BinaryReader reader, Type type)
\r
567 if (type == null) return null;
\r
569 switch (Type.GetTypeCode (type))
\r
571 case TypeCode.Boolean:
\r
572 return reader.ReadBoolean();
\r
574 case TypeCode.Byte:
\r
575 return reader.ReadByte();
\r
577 case TypeCode.Char:
\r
578 return reader.ReadChar();
\r
580 case TypeCode.DateTime:
\r
581 long ticks = reader.ReadInt64();
\r
582 return new DateTime (ticks);
\r
584 case TypeCode.Decimal:
\r
585 return reader.ReadDecimal();
\r
587 case TypeCode.Double:
\r
588 return reader.ReadDouble();
\r
590 case TypeCode.Int16:
\r
591 return reader.ReadInt16();
\r
593 case TypeCode.Int32:
\r
594 return reader.ReadInt32();
\r
596 case TypeCode.Int64:
\r
597 return reader.ReadInt64();
\r
599 case TypeCode.SByte:
\r
600 return reader.ReadSByte();
\r
602 case TypeCode.Single:
\r
603 return reader.ReadSingle();
\r
605 case TypeCode.UInt16:
\r
606 return reader.ReadUInt16();
\r
608 case TypeCode.UInt32:
\r
609 return reader.ReadUInt32();
\r
611 case TypeCode.UInt64:
\r
612 return reader.ReadUInt64();
\r
614 case TypeCode.String:
\r
615 return reader.ReadString();
\r
618 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
\r