4 // Lluis Sanchez Gual (lluis@ideary.com)
6 // (C) 2003 Lluis Sanchez Gual
8 // FIXME: Implement the missing binary elements
\r
11 using System.Runtime.Serialization;
\r
13 using System.Collections;
\r
14 using System.Reflection;
\r
15 using System.Runtime.Remoting.Messaging;
\r
17 namespace System.Runtime.Serialization.Formatters.Binary
\r
19 internal class ObjectReader
\r
21 ISurrogateSelector _surrogateSelector;
\r
22 StreamingContext _context;
\r
24 ObjectManager _manager;
\r
25 Hashtable _registeredAssemblies = new Hashtable();
\r
26 Hashtable _typeMetadataCache = new Hashtable();
\r
28 object _lastObject = null;
\r
33 public Type[] MemberTypes;
\r
34 public string[] MemberNames;
\r
35 public int FieldCount;
\r
36 public bool NeedsSerializationInfo;
\r
39 class ArrayNullFiller
\r
41 public ArrayNullFiller(int count) { NullCount = count; }
\r
42 public int NullCount;
\r
45 public ObjectReader(ISurrogateSelector surrogateSelector, StreamingContext context)
\r
47 _manager = new ObjectManager (surrogateSelector, context);
\r
48 _surrogateSelector = surrogateSelector;
\r
52 public object ReadObjectGraph (BinaryReader reader, bool readHeaders, HeaderHandler headerHandler)
\r
54 object rootObject = null;
\r
55 Header[] headers = null;
\r
57 // Reads the objects. The first object in the stream is the
\r
60 while (ReadNextObject (reader))
\r
62 if (readHeaders && (headers == null))
\r
63 headers = (Header[])CurrentObject;
\r
65 if (rootObject == null) rootObject = CurrentObject;
\r
68 if (readHeaders && headerHandler != null)
\r
69 headerHandler (headers);
\r
74 public bool ReadNextObject (BinaryReader reader)
\r
76 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
77 if (element == BinaryElement.End)
\r
79 _manager.DoFixups();
\r
80 _manager.RaiseDeserializationEvent();
\r
84 SerializationInfo info;
\r
87 ReadObject (element, reader, out objectId, out _lastObject, out info);
\r
90 RegisterObject (objectId, _lastObject, info, 0, null, null);
\r
95 public object CurrentObject
\r
97 get { return _lastObject; }
\r
100 // Reads an object from the stream. The object is registered in the ObjectManager.
\r
101 // The result can be either the object instance
\r
102 // or the id of the object (when what is found in the stream is an object reference).
\r
103 // If an object instance is read, the objectId is set to 0.
\r
105 private void ReadObject (BinaryElement element, BinaryReader reader, out long objectId, out object value, out SerializationInfo info)
\r
109 case BinaryElement.RefTypeObject:
\r
110 ReadRefTypeObjectInstance (reader, out objectId, out value, out info);
\r
113 case BinaryElement.RuntimeObject:
\r
114 ReadObjectInstance (reader, true, out objectId, out value, out info);
\r
117 case BinaryElement.ExternalObject:
\r
118 ReadObjectInstance (reader, false, out objectId, out value, out info);
\r
121 case BinaryElement.String:
\r
123 ReadStringIntance (reader, out objectId, out value);
\r
126 case BinaryElement.GenericArray:
\r
128 ReadGenericArray (reader, out objectId, out value);
\r
131 case BinaryElement.BoxedPrimitiveTypeValue:
\r
132 value = ReadBoxedPrimitiveTypeValue (reader);
\r
137 case BinaryElement.NullValue:
\r
143 case BinaryElement.Assembly:
\r
144 ReadAssembly (reader);
\r
145 ReadObject ((BinaryElement)reader.ReadByte (), reader, out objectId, out value, out info);
\r
148 case BinaryElement.ArrayFiller8b:
\r
149 value = new ArrayNullFiller(reader.ReadByte());
\r
154 case BinaryElement.ArrayFiller32b:
\r
155 value = new ArrayNullFiller(reader.ReadInt32());
\r
160 case BinaryElement.ArrayOfPrimitiveType:
\r
161 ReadArrayOfPrimitiveType (reader, out objectId, out value);
\r
165 case BinaryElement.ArrayOfObject:
\r
166 ReadArrayOfObject (reader, out objectId, out value);
\r
170 case BinaryElement.ArrayOfString:
\r
171 ReadArrayOfString (reader, out objectId, out value);
\r
176 throw new SerializationException ("Unexpected binary element: " + (int)element);
\r
180 private void ReadAssembly (BinaryReader reader)
\r
182 long id = (long) reader.ReadUInt32 ();
\r
183 string assemblyName = reader.ReadString ();
\r
184 Assembly assembly = Assembly.Load (assemblyName);
\r
185 _registeredAssemblies [id] = assembly;
\r
188 private void ReadObjectInstance (BinaryReader reader, bool isRuntimeObject, out long objectId, out object value, out SerializationInfo info)
\r
190 objectId = (long) reader.ReadUInt32 ();
\r
192 TypeMetadata metadata = ReadTypeMetadata (reader, isRuntimeObject);
\r
193 ReadObjectContent (reader, metadata, objectId, out value, out info);
\r
196 private void ReadRefTypeObjectInstance (BinaryReader reader, out long objectId, out object value, out SerializationInfo info)
\r
198 objectId = (long) reader.ReadUInt32 ();
\r
199 long refTypeObjectId = (long) reader.ReadUInt32 ();
\r
201 // Gets the type of the referred object and its metadata
\r
203 object refObj = _manager.GetObject (refTypeObjectId);
\r
204 if (refObj == null) throw new SerializationException ("Invalid binary format");
\r
205 TypeMetadata metadata = (TypeMetadata)_typeMetadataCache [refObj.GetType()];
\r
207 ReadObjectContent (reader, metadata, objectId, out value, out info);
\r
210 private void ReadObjectContent (BinaryReader reader, TypeMetadata metadata, long objectId, out object objectInstance, out SerializationInfo info)
\r
212 objectInstance = FormatterServices.GetUninitializedObject (metadata.Type);
\r
213 info = metadata.NeedsSerializationInfo ? new SerializationInfo(metadata.Type, new FormatterConverter()) : null;
\r
215 for (int n=0; n<metadata.FieldCount; n++)
\r
216 ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberNames[n], null);
\r
219 private void RegisterObject (long objectId, object objectInstance, SerializationInfo info, long parentObjectId, MemberInfo parentObjectMemeber, int[] indices)
\r
221 if (!objectInstance.GetType().IsValueType || parentObjectId == 0)
\r
222 _manager.RegisterObject (objectInstance, objectId, info, 0, null, indices);
\r
224 _manager.RegisterObject (objectInstance, objectId, info, parentObjectId, parentObjectMemeber, indices);
\r
227 private void ReadStringIntance (BinaryReader reader, out long objectId, out object value)
\r
229 objectId = (long) reader.ReadUInt32 ();
\r
230 value = reader.ReadString ();
\r
233 private void ReadGenericArray (BinaryReader reader, out long objectId, out object val)
\r
235 objectId = (long) reader.ReadUInt32 ();
\r
236 ArrayStructure structure = (ArrayStructure) reader.ReadByte();
\r
238 int rank = reader.ReadInt32();
\r
240 bool emptyDim = false;
\r
241 int[] lengths = new int[rank];
\r
242 for (int n=0; n<rank; n++)
\r
244 lengths[n] = reader.ReadInt32();
\r
245 if (lengths[n] == 0) emptyDim = true;
\r
248 TypeTag code = (TypeTag) reader.ReadByte ();
\r
249 Type elementType = ReadType (reader, code);
\r
251 Array array = Array.CreateInstance (elementType, lengths);
\r
259 int[] indices = new int[rank];
\r
261 // Initialize indexes
\r
262 for (int dim = rank-1; dim >= 0; dim--)
\r
263 indices[dim] = array.GetLowerBound (dim);
\r
268 ReadValue (reader, array, objectId, null, elementType, null, indices);
\r
270 for (int dim = array.Rank-1; dim >= 0; dim--)
\r
273 if (indices[dim] > array.GetUpperBound (dim))
\r
277 indices[dim] = array.GetLowerBound (dim);
\r
278 continue; // Increment the next dimension's index
\r
280 end = true; // That was the last dimension. Finished.
\r
288 private object ReadBoxedPrimitiveTypeValue (BinaryReader reader)
\r
290 Type type = ReadType (reader, TypeTag.PrimitiveType);
\r
291 return ReadPrimitiveTypeValue (reader, type);
\r
294 private void ReadArrayOfPrimitiveType (BinaryReader reader, out long objectId, out object val)
\r
296 objectId = (long) reader.ReadUInt32 ();
\r
297 int length = reader.ReadInt32 ();
\r
298 Type elementType = ReadType (reader, TypeTag.PrimitiveType);
\r
300 Array array = Array.CreateInstance (elementType, length);
\r
301 for (int n = 0; n < length; n++)
\r
302 array.SetValue (ReadPrimitiveTypeValue (reader, elementType), n);
\r
307 private void ReadArrayOfObject (BinaryReader reader, out long objectId, out object array)
\r
309 ReadSimpleArray (reader, typeof (object), out objectId, out array);
\r
312 private void ReadArrayOfString (BinaryReader reader, out long objectId, out object array)
\r
314 ReadSimpleArray (reader, typeof (string), out objectId, out array);
\r
317 private void ReadSimpleArray (BinaryReader reader, Type elementType, out long objectId, out object val)
\r
319 objectId = (long) reader.ReadUInt32 ();
\r
320 int length = reader.ReadInt32 ();
\r
321 int[] indices = new int[1];
\r
323 Array array = Array.CreateInstance (elementType, length);
\r
324 for (int n = 0; n < length; n++)
\r
327 ReadValue (reader, array, objectId, null, elementType, null, indices);
\r
333 private TypeMetadata ReadTypeMetadata (BinaryReader reader, bool isRuntimeObject)
\r
335 TypeMetadata metadata = new TypeMetadata();
\r
337 string className = reader.ReadString ();
\r
338 int fieldCount = reader.ReadInt32 ();
\r
340 Type[] types = new Type[fieldCount];
\r
341 string[] names = new string[fieldCount];
\r
343 TypeTag[] codes = new TypeTag[fieldCount];
\r
345 for (int n=0; n<fieldCount; n++)
\r
346 names [n] = reader.ReadString ();
\r
348 for (int n=0; n<fieldCount; n++)
\r
349 codes [n] = (TypeTag) reader.ReadByte ();
\r
351 for (int n=0; n<fieldCount; n++)
\r
352 types [n] = ReadType (reader, codes[n]);
\r
356 if (!isRuntimeObject)
\r
358 long assemblyId = (long)reader.ReadUInt32();
\r
359 Assembly asm = (Assembly)_registeredAssemblies[assemblyId];
\r
360 metadata.Type = asm.GetType (className, true);
\r
363 metadata.Type = Type.GetType (className, true);
\r
365 metadata.MemberTypes = types;
\r
366 metadata.MemberNames = names;
\r
367 metadata.FieldCount = names.Length;
\r
369 // Now check if this objects needs a SerializationInfo struct for deserialziation.
\r
370 // SerializationInfo is needed if the object has to be deserialized using
\r
371 // a serialization surrogate, or if it implements ISerializable.
\r
373 if (_surrogateSelector != null)
\r
375 // check if the surrogate selector handles objects of the given type.
\r
376 ISurrogateSelector selector;
\r
377 ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (metadata.Type, _context, out selector);
\r
378 metadata.NeedsSerializationInfo = (surrogate != null);
\r
381 if (!metadata.NeedsSerializationInfo)
\r
383 // Check if the object is marked with the Serializable attribute
\r
385 if (!metadata.Type.IsSerializable)
\r
386 throw new SerializationException("Serializable objects must be marked with the Serializable attribute");
\r
388 metadata.NeedsSerializationInfo = (metadata.Type.GetInterface ("ISerializable") != null);
\r
391 // Registers the type's metadata so it can be reused later if
\r
392 // a RefTypeObject element is found
\r
394 if (!_typeMetadataCache.ContainsKey (metadata.Type))
\r
395 _typeMetadataCache [metadata.Type] = metadata;
\r
401 private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, int[] indices)
\r
403 // Reads a value from the stream and assigns it to the member of an object
\r
407 if (BinaryCommon.IsPrimitive (valueType))
\r
409 val = ReadPrimitiveTypeValue (reader, valueType);
\r
410 SetObjectValue (parentObject, fieldName, info, val, valueType, indices);
\r
416 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
418 if (element == BinaryElement.ObjectReference)
\r
420 // Just read the id of the referred object and record a fixup
\r
421 long childObjectId = (long) reader.ReadUInt32();
\r
422 RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, indices);
\r
427 SerializationInfo objectInfo;
\r
429 ReadObject (element, reader, out objectId, out val, out objectInfo);
\r
431 // There are two cases where the object cannot be assigned to the parent
\r
432 // and a fixup must be used:
\r
433 // 1) When what has been read is not an object, but an id of an object that
\r
434 // has not been read yet (an object reference). This is managed in the
\r
435 // previous block of code.
\r
436 // 2) When the read object is a value type object. Value type fields hold
\r
437 // copies of objects, not references. Thus, if the value object that
\r
438 // has been read has pending fixups, those fixups would be made to the
\r
439 // boxed copy in the ObjectManager, and not in the required object instance
\r
441 // First of all register the fixup, and then the object. ObjectManager is more
\r
442 // efficient if done in this order
\r
444 bool hasFixup = false;
\r
447 if (val.GetType().IsValueType)
\r
449 RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, indices);
\r
453 // Register the value
\r
455 if (info == null && !parentObject.GetType().IsArray)
\r
456 RegisterObject (objectId, val, objectInfo, parentObjectId, GetObjectMember(parentObject, fieldName), null);
\r
458 RegisterObject (objectId, val, objectInfo, parentObjectId, null, indices);
\r
460 // Assign the value to the parent object, unless there is a fixup
\r
463 SetObjectValue (parentObject, fieldName, info, val, valueType, indices);
\r
466 private void SetObjectValue (object parentObject, string fieldName, SerializationInfo info, object value, Type valueType, int[] indices)
\r
468 if (value is IObjectReference)
\r
469 value = ((IObjectReference)value).GetRealObject (_context);
\r
471 if (parentObject.GetType().IsArray)
\r
473 if (value is ArrayNullFiller)
\r
475 // It must be a single dimension array of objects.
\r
476 // Just increase the index. Elements are null by default.
\r
477 int count = ((ArrayNullFiller)value).NullCount;
\r
478 indices[0] += count - 1;
\r
481 ((Array)parentObject).SetValue (value, indices);
\r
483 else if (info != null) {
\r
484 info.AddValue (fieldName, value, valueType);
\r
487 MemberInfo member = GetObjectMember(parentObject, fieldName);
\r
488 if (member is FieldInfo)
\r
489 ((FieldInfo)member).SetValue (parentObject, value);
\r
491 ((PropertyInfo)member).SetValue (parentObject, value, null);
\r
495 private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, int[] indices)
\r
497 if (info != null) {
\r
498 _manager.RecordDelayedFixup (parentObjectId, fieldName, childObjectId);
\r
500 else if (parentObject.GetType().IsArray) {
\r
501 if (indices.Length == 1)
\r
502 _manager.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);
\r
504 _manager.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);
\r
507 _manager.RecordFixup (parentObjectId, GetObjectMember(parentObject, fieldName), childObjectId);
\r
511 private MemberInfo GetObjectMember (object parentObject, string fieldName)
\r
513 MemberInfo[] members = parentObject.GetType().GetMember (fieldName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
\r
514 if (members.Length > 1) throw new SerializationException ("There are two public members named \"" + fieldName + "\" in the class hirearchy of " + parentObject.GetType().FullName);
\r
515 if (members.Length == 0) throw new SerializationException ("Field \"" + fieldName + "\" not found in class " + parentObject.GetType().FullName);
\r
519 public Type ReadType (BinaryReader reader, TypeTag code)
\r
523 case TypeTag.PrimitiveType:
\r
524 return BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
526 case TypeTag.String:
\r
527 return typeof(string);
\r
529 case TypeTag.ObjectType:
\r
530 return typeof(object);
\r
532 case TypeTag.RuntimeType:
\r
534 string name = reader.ReadString ();
\r
535 return Type.GetType (name, true);
\r
538 case TypeTag.GenericType:
\r
540 string name = reader.ReadString ();
\r
541 long asmid = (long) reader.ReadUInt32();
\r
542 Assembly asm = (Assembly)_registeredAssemblies[asmid];
\r
543 return asm.GetType (name, true);
\r
546 case TypeTag.ArrayOfObject:
\r
547 return typeof(object[]);
\r
549 case TypeTag.ArrayOfString:
\r
550 return typeof(string[]);
\r
552 case TypeTag.ArrayOfPrimitiveType:
\r
553 Type elementType = BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
554 return Type.GetType(elementType.FullName + "[]");
\r
557 throw new NotSupportedException ("Unknow type tag");
\r
561 public static object ReadPrimitiveTypeValue (BinaryReader reader, Type type)
\r
563 if (type == null) return null;
\r
565 switch (Type.GetTypeCode (type))
\r
567 case TypeCode.Boolean:
\r
568 return reader.ReadBoolean();
\r
570 case TypeCode.Byte:
\r
571 return reader.ReadByte();
\r
573 case TypeCode.Char:
\r
574 return reader.ReadChar();
\r
576 case TypeCode.DateTime:
\r
577 long ticks = reader.ReadInt64();
\r
578 return new DateTime (ticks);
\r
580 case TypeCode.Decimal:
\r
581 return reader.ReadDecimal();
\r
583 case TypeCode.Double:
\r
584 return reader.ReadDouble();
\r
586 case TypeCode.Int16:
\r
587 return reader.ReadInt16();
\r
589 case TypeCode.Int32:
\r
590 return reader.ReadInt32();
\r
592 case TypeCode.Int64:
\r
593 return reader.ReadInt64();
\r
595 case TypeCode.SByte:
\r
596 return reader.ReadSByte();
\r
598 case TypeCode.Single:
\r
599 return reader.ReadSingle();
\r
601 case TypeCode.UInt16:
\r
602 return reader.ReadUInt16();
\r
604 case TypeCode.UInt32:
\r
605 return reader.ReadUInt32();
\r
607 case TypeCode.UInt64:
\r
608 return reader.ReadUInt64();
\r
610 case TypeCode.String:
\r
611 return reader.ReadString();
\r
614 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
\r