4 // Lluis Sanchez Gual (lluis@ideary.com)
\r
5 // Patrik Torstensson
\r
7 // (C) 2003 Lluis Sanchez Gual
\r
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Runtime.Serialization;
\r
35 using System.Collections;
\r
36 using System.Reflection;
\r
37 using System.Runtime.Remoting.Messaging;
\r
38 using System.Globalization;
\r
40 namespace System.Runtime.Serialization.Formatters.Binary
\r
42 internal class ObjectReader
\r
44 BinaryFormatter _formatter;
\r
45 ISurrogateSelector _surrogateSelector;
\r
46 StreamingContext _context;
\r
47 SerializationBinder _binder;
\r
50 TypeFilterLevel _filterLevel;
\r
53 ObjectManager _manager;
\r
54 Hashtable _registeredAssemblies = new Hashtable();
\r
55 Hashtable _typeMetadataCache = new Hashtable();
\r
57 object _lastObject = null;
\r
58 long _lastObjectID = 0;
\r
59 long _rootObjectID = 0;
\r
64 public Type[] MemberTypes;
\r
65 public string[] MemberNames;
\r
66 public MemberInfo[] MemberInfos;
\r
67 public int FieldCount;
\r
68 public bool NeedsSerializationInfo;
\r
71 class ArrayNullFiller
\r
73 public ArrayNullFiller(int count) { NullCount = count; }
\r
74 public int NullCount;
\r
77 public ObjectReader (BinaryFormatter formatter)
\r
79 _formatter = formatter;
\r
80 _surrogateSelector = formatter.SurrogateSelector;
\r
81 _context = formatter.Context;
\r
82 _binder = formatter.Binder;
\r
83 _manager = new ObjectManager (_surrogateSelector, _context);
\r
86 _filterLevel = formatter.FilterLevel;
\r
90 public void ReadObjectGraph (BinaryReader reader, bool readHeaders, out object result, out Header[] headers)
\r
94 // Reads the objects. The first object in the stream is the
\r
97 while (ReadNextObject (reader))
\r
99 if (readHeaders && (headers == null))
\r
100 headers = (Header[])CurrentObject;
\r
102 if (_rootObjectID == 0) _rootObjectID = _lastObjectID;
\r
105 result = _manager.GetObject (_rootObjectID);
\r
108 public bool ReadNextObject (BinaryReader reader)
\r
110 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
111 if (element == BinaryElement.End)
\r
113 _manager.DoFixups();
\r
115 _manager.RaiseDeserializationEvent();
\r
119 SerializationInfo info;
\r
122 ReadObject (element, reader, out objectId, out _lastObject, out info);
\r
124 if (objectId != 0) {
\r
125 RegisterObject (objectId, _lastObject, info, 0, null, null);
\r
126 _lastObjectID = objectId;
\r
132 public object CurrentObject
\r
134 get { return _lastObject; }
\r
137 // Reads an object from the stream. The object is registered in the ObjectManager.
\r
138 // The result can be either the object instance
\r
139 // or the id of the object (when what is found in the stream is an object reference).
\r
140 // If an object instance is read, the objectId is set to 0.
\r
142 private void ReadObject (BinaryElement element, BinaryReader reader, out long objectId, out object value, out SerializationInfo info)
\r
146 case BinaryElement.RefTypeObject:
\r
147 ReadRefTypeObjectInstance (reader, out objectId, out value, out info);
\r
150 case BinaryElement.RuntimeObject:
\r
151 ReadObjectInstance (reader, true, out objectId, out value, out info);
\r
154 case BinaryElement.ExternalObject:
\r
155 ReadObjectInstance (reader, false, out objectId, out value, out info);
\r
158 case BinaryElement.String:
\r
160 ReadStringIntance (reader, out objectId, out value);
\r
163 case BinaryElement.GenericArray:
\r
165 ReadGenericArray (reader, out objectId, out value);
\r
169 case BinaryElement.BoxedPrimitiveTypeValue:
\r
170 value = ReadBoxedPrimitiveTypeValue (reader);
\r
175 case BinaryElement.NullValue:
\r
181 case BinaryElement.Assembly:
\r
182 ReadAssembly (reader);
\r
183 ReadObject ((BinaryElement)reader.ReadByte (), reader, out objectId, out value, out info);
\r
186 case BinaryElement.ArrayFiller8b:
\r
187 value = new ArrayNullFiller(reader.ReadByte());
\r
192 case BinaryElement.ArrayFiller32b:
\r
193 value = new ArrayNullFiller(reader.ReadInt32());
\r
198 case BinaryElement.ArrayOfPrimitiveType:
\r
199 ReadArrayOfPrimitiveType (reader, out objectId, out value);
\r
203 case BinaryElement.ArrayOfObject:
\r
204 ReadArrayOfObject (reader, out objectId, out value);
\r
208 case BinaryElement.ArrayOfString:
\r
209 ReadArrayOfString (reader, out objectId, out value);
\r
214 throw new SerializationException ("Unexpected binary element: " + (int)element);
\r
218 private void ReadAssembly (BinaryReader reader)
\r
220 long id = (long) reader.ReadUInt32 ();
\r
221 string assemblyName = reader.ReadString ();
\r
222 _registeredAssemblies [id] = assemblyName;
\r
225 private void ReadObjectInstance (BinaryReader reader, bool isRuntimeObject, out long objectId, out object value, out SerializationInfo info)
\r
227 objectId = (long) reader.ReadUInt32 ();
\r
229 TypeMetadata metadata = ReadTypeMetadata (reader, isRuntimeObject);
\r
230 ReadObjectContent (reader, metadata, objectId, out value, out info);
\r
233 private void ReadRefTypeObjectInstance (BinaryReader reader, out long objectId, out object value, out SerializationInfo info)
\r
235 objectId = (long) reader.ReadUInt32 ();
\r
236 long refTypeObjectId = (long) reader.ReadUInt32 ();
\r
238 // Gets the type of the referred object and its metadata
\r
240 object refObj = _manager.GetObject (refTypeObjectId);
\r
241 if (refObj == null) throw new SerializationException ("Invalid binary format");
\r
242 TypeMetadata metadata = (TypeMetadata)_typeMetadataCache [refObj.GetType()];
\r
244 ReadObjectContent (reader, metadata, objectId, out value, out info);
\r
247 private void ReadObjectContent (BinaryReader reader, TypeMetadata metadata, long objectId, out object objectInstance, out SerializationInfo info)
\r
250 if (_filterLevel == TypeFilterLevel.Low)
\r
251 objectInstance = FormatterServices.GetSafeUninitializedObject (metadata.Type);
\r
254 objectInstance = FormatterServices.GetUninitializedObject (metadata.Type);
\r
256 info = metadata.NeedsSerializationInfo ? new SerializationInfo(metadata.Type, new FormatterConverter()) : null;
\r
258 if (metadata.MemberNames != null)
\r
259 for (int n=0; n<metadata.FieldCount; n++)
\r
260 ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberNames[n], null, null);
\r
262 for (int n=0; n<metadata.FieldCount; n++)
\r
263 ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberInfos[n].Name, metadata.MemberInfos[n], null);
\r
266 private void RegisterObject (long objectId, object objectInstance, SerializationInfo info, long parentObjectId, MemberInfo parentObjectMemeber, int[] indices)
\r
268 if (parentObjectId == 0) indices = null;
\r
270 if (!objectInstance.GetType().IsValueType || parentObjectId == 0)
\r
271 _manager.RegisterObject (objectInstance, objectId, info, 0, null, null);
\r
274 if (indices != null) indices = (int[])indices.Clone();
\r
275 _manager.RegisterObject (objectInstance, objectId, info, parentObjectId, parentObjectMemeber, indices);
\r
279 private void ReadStringIntance (BinaryReader reader, out long objectId, out object value)
\r
281 objectId = (long) reader.ReadUInt32 ();
\r
282 value = reader.ReadString ();
\r
285 private void ReadGenericArray (BinaryReader reader, out long objectId, out object val)
\r
287 objectId = (long) reader.ReadUInt32 ();
\r
288 ArrayStructure structure = (ArrayStructure) reader.ReadByte();
\r
290 int rank = reader.ReadInt32();
\r
292 bool emptyDim = false;
\r
293 int[] lengths = new int[rank];
\r
294 for (int n=0; n<rank; n++)
\r
296 lengths[n] = reader.ReadInt32();
\r
297 if (lengths[n] == 0) emptyDim = true;
\r
300 TypeTag code = (TypeTag) reader.ReadByte ();
\r
301 Type elementType = ReadType (reader, code);
\r
303 Array array = Array.CreateInstance (elementType, lengths);
\r
311 int[] indices = new int[rank];
\r
313 // Initialize indexes
\r
314 for (int dim = rank-1; dim >= 0; dim--)
\r
315 indices[dim] = array.GetLowerBound (dim);
\r
320 ReadValue (reader, array, objectId, null, elementType, null, null, indices);
\r
322 for (int dim = array.Rank-1; dim >= 0; dim--)
\r
325 if (indices[dim] > array.GetUpperBound (dim))
\r
329 indices[dim] = array.GetLowerBound (dim);
\r
330 continue; // Increment the next dimension's index
\r
332 end = true; // That was the last dimension. Finished.
\r
340 private object ReadBoxedPrimitiveTypeValue (BinaryReader reader)
\r
342 Type type = ReadType (reader, TypeTag.PrimitiveType);
\r
343 return ReadPrimitiveTypeValue (reader, type);
\r
346 private void ReadArrayOfPrimitiveType (BinaryReader reader, out long objectId, out object val)
\r
348 objectId = (long) reader.ReadUInt32 ();
\r
349 int length = reader.ReadInt32 ();
\r
350 Type elementType = ReadType (reader, TypeTag.PrimitiveType);
\r
352 Array array = Array.CreateInstance (elementType, length);
\r
353 for (int n = 0; n < length; n++)
\r
354 array.SetValue (ReadPrimitiveTypeValue (reader, elementType), n);
\r
359 private void ReadArrayOfObject (BinaryReader reader, out long objectId, out object array)
\r
361 ReadSimpleArray (reader, typeof (object), out objectId, out array);
\r
364 private void ReadArrayOfString (BinaryReader reader, out long objectId, out object array)
\r
366 ReadSimpleArray (reader, typeof (string), out objectId, out array);
\r
369 private void ReadSimpleArray (BinaryReader reader, Type elementType, out long objectId, out object val)
\r
371 objectId = (long) reader.ReadUInt32 ();
\r
372 int length = reader.ReadInt32 ();
\r
373 int[] indices = new int[1];
\r
375 Array array = Array.CreateInstance (elementType, length);
\r
376 for (int n = 0; n < length; n++)
\r
379 ReadValue (reader, array, objectId, null, elementType, null, null, indices);
\r
385 private TypeMetadata ReadTypeMetadata (BinaryReader reader, bool isRuntimeObject)
\r
387 TypeMetadata metadata = new TypeMetadata();
\r
389 string className = reader.ReadString ();
\r
390 int fieldCount = reader.ReadInt32 ();
\r
392 Type[] types = new Type[fieldCount];
\r
393 string[] names = new string[fieldCount];
\r
395 TypeTag[] codes = new TypeTag[fieldCount];
\r
397 for (int n=0; n<fieldCount; n++)
\r
398 names [n] = reader.ReadString ();
\r
400 for (int n=0; n<fieldCount; n++)
\r
401 codes [n] = (TypeTag) reader.ReadByte ();
\r
403 for (int n=0; n<fieldCount; n++)
\r
404 types [n] = ReadType (reader, codes[n]);
\r
408 if (!isRuntimeObject)
\r
410 long assemblyId = (long)reader.ReadUInt32();
\r
411 metadata.Type = GetDeserializationType (assemblyId, className);
\r
414 metadata.Type = Type.GetType (className, true);
\r
416 metadata.MemberTypes = types;
\r
417 metadata.MemberNames = names;
\r
418 metadata.FieldCount = names.Length;
\r
420 // Now check if this objects needs a SerializationInfo struct for deserialziation.
\r
421 // SerializationInfo is needed if the object has to be deserialized using
\r
422 // a serialization surrogate, or if it implements ISerializable.
\r
424 if (_surrogateSelector != null)
\r
426 // check if the surrogate selector handles objects of the given type.
\r
427 ISurrogateSelector selector;
\r
428 ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (metadata.Type, _context, out selector);
\r
429 metadata.NeedsSerializationInfo = (surrogate != null);
\r
432 if (!metadata.NeedsSerializationInfo)
\r
434 // Check if the object is marked with the Serializable attribute
\r
436 if (!metadata.Type.IsSerializable)
\r
437 throw new SerializationException("Serializable objects must be marked with the Serializable attribute");
\r
439 metadata.NeedsSerializationInfo = (metadata.Type.GetInterface ("ISerializable") != null);
\r
440 if (!metadata.NeedsSerializationInfo)
\r
442 metadata.MemberInfos = new MemberInfo [fieldCount];
\r
443 for (int n=0; n<fieldCount; n++)
\r
445 MemberInfo[] members = null;
\r
446 string memberName = names[n];
\r
448 int i = memberName.IndexOf ('+');
\r
450 string baseTypeName = names[n].Substring (0,i);
\r
451 memberName = names[n].Substring (i+1);
\r
452 Type t = metadata.Type.BaseType;
\r
453 while (t != null) {
\r
454 if (t.Name == baseTypeName) {
\r
455 members = t.GetMember (memberName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
\r
463 members = metadata.Type.GetMember (memberName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
\r
465 if (members == null || members.Length == 0) throw new SerializationException ("Field \"" + names[n] + "\" not found in class " + metadata.Type.FullName);
\r
466 if (members.Length > 1) throw new SerializationException ("There are two public members named \"" + names[n] + "\" in the class hirearchy of " + metadata.Type.FullName);
\r
467 metadata.MemberInfos [n] = members[0];
\r
469 metadata.MemberNames = null; // Info now in MemberInfos
\r
473 // Registers the type's metadata so it can be reused later if
\r
474 // a RefTypeObject element is found
\r
476 if (!_typeMetadataCache.ContainsKey (metadata.Type))
\r
477 _typeMetadataCache [metadata.Type] = metadata;
\r
483 private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, MemberInfo memberInfo, int[] indices)
\r
485 // Reads a value from the stream and assigns it to the member of an object
\r
489 if (BinaryCommon.IsPrimitive (valueType))
\r
491 val = ReadPrimitiveTypeValue (reader, valueType);
\r
492 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);
\r
498 BinaryElement element = (BinaryElement)reader.ReadByte ();
\r
500 if (element == BinaryElement.ObjectReference)
\r
502 // Just read the id of the referred object and record a fixup
\r
503 long childObjectId = (long) reader.ReadUInt32();
\r
504 RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, memberInfo, indices);
\r
509 SerializationInfo objectInfo;
\r
511 ReadObject (element, reader, out objectId, out val, out objectInfo);
\r
513 // There are two cases where the object cannot be assigned to the parent
\r
514 // and a fixup must be used:
\r
515 // 1) When what has been read is not an object, but an id of an object that
\r
516 // has not been read yet (an object reference). This is managed in the
\r
517 // previous block of code.
\r
518 // 2) When the read object is a value type object. Value type fields hold
\r
519 // copies of objects, not references. Thus, if the value object that
\r
520 // has been read has pending fixups, those fixups would be made to the
\r
521 // boxed copy in the ObjectManager, and not in the required object instance
\r
523 // First of all register the fixup, and then the object. ObjectManager is more
\r
524 // efficient if done in this order
\r
526 bool hasFixup = false;
\r
529 if (val.GetType().IsValueType)
\r
531 RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, memberInfo, indices);
\r
535 // Register the value
\r
537 if (info == null && !(parentObject is Array))
\r
538 RegisterObject (objectId, val, objectInfo, parentObjectId, memberInfo, null);
\r
540 RegisterObject (objectId, val, objectInfo, parentObjectId, null, indices);
\r
542 // Assign the value to the parent object, unless there is a fixup
\r
545 SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);
\r
548 private void SetObjectValue (object parentObject, string fieldName, MemberInfo memberInfo, SerializationInfo info, object value, Type valueType, int[] indices)
\r
550 if (value is IObjectReference)
\r
551 value = ((IObjectReference)value).GetRealObject (_context);
\r
553 if (parentObject is Array)
\r
555 if (value is ArrayNullFiller)
\r
557 // It must be a single dimension array of objects.
\r
558 // Just increase the index. Elements are null by default.
\r
559 int count = ((ArrayNullFiller)value).NullCount;
\r
560 indices[0] += count - 1;
\r
563 ((Array)parentObject).SetValue (value, indices);
\r
565 else if (info != null) {
\r
566 info.AddValue (fieldName, value, valueType);
\r
569 if (memberInfo is FieldInfo)
\r
570 ((FieldInfo)memberInfo).SetValue (parentObject, value);
\r
572 ((PropertyInfo)memberInfo).SetValue (parentObject, value, null);
\r
576 private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, MemberInfo memberInfo, int[] indices)
\r
578 if (info != null) {
\r
579 _manager.RecordDelayedFixup (parentObjectId, fieldName, childObjectId);
\r
581 else if (parentObject is Array) {
\r
582 if (indices.Length == 1)
\r
583 _manager.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);
\r
585 _manager.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);
\r
588 _manager.RecordFixup (parentObjectId, memberInfo, childObjectId);
\r
592 private Type GetDeserializationType (long assemblyId, string className)
\r
594 string assemblyName = (string)_registeredAssemblies[assemblyId];
\r
596 if (_binder == null)
\r
598 Assembly assembly = Assembly.Load (assemblyName);
\r
599 return assembly.GetType (className, true);
\r
602 return _binder.BindToType (assemblyName, className);
\r
605 public Type ReadType (BinaryReader reader, TypeTag code)
\r
609 case TypeTag.PrimitiveType:
\r
610 return BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
612 case TypeTag.String:
\r
613 return typeof(string);
\r
615 case TypeTag.ObjectType:
\r
616 return typeof(object);
\r
618 case TypeTag.RuntimeType:
\r
620 string name = reader.ReadString ();
\r
621 return Type.GetType (name, true);
\r
624 case TypeTag.GenericType:
\r
626 string name = reader.ReadString ();
\r
627 long asmid = (long) reader.ReadUInt32();
\r
628 return GetDeserializationType (asmid, name);
\r
631 case TypeTag.ArrayOfObject:
\r
632 return typeof(object[]);
\r
634 case TypeTag.ArrayOfString:
\r
635 return typeof(string[]);
\r
637 case TypeTag.ArrayOfPrimitiveType:
\r
638 Type elementType = BinaryCommon.GetTypeFromCode (reader.ReadByte());
\r
639 return Type.GetType(elementType.FullName + "[]");
\r
642 throw new NotSupportedException ("Unknow type tag");
\r
646 public static object ReadPrimitiveTypeValue (BinaryReader reader, Type type)
\r
648 if (type == null) return null;
\r
650 switch (Type.GetTypeCode (type))
\r
652 case TypeCode.Boolean:
\r
653 return reader.ReadBoolean();
\r
655 case TypeCode.Byte:
\r
656 return reader.ReadByte();
\r
658 case TypeCode.Char:
\r
659 return reader.ReadChar();
\r
661 case TypeCode.DateTime:
\r
662 return new DateTime (reader.ReadInt64());
\r
664 case TypeCode.Decimal:
\r
665 return Decimal.Parse (reader.ReadString(), CultureInfo.InvariantCulture);
\r
667 case TypeCode.Double:
\r
668 return reader.ReadDouble();
\r
670 case TypeCode.Int16:
\r
671 return reader.ReadInt16();
\r
673 case TypeCode.Int32:
\r
674 return reader.ReadInt32();
\r
676 case TypeCode.Int64:
\r
677 return reader.ReadInt64();
\r
679 case TypeCode.SByte:
\r
680 return reader.ReadSByte();
\r
682 case TypeCode.Single:
\r
683 return reader.ReadSingle();
\r
685 case TypeCode.UInt16:
\r
686 return reader.ReadUInt16();
\r
688 case TypeCode.UInt32:
\r
689 return reader.ReadUInt32();
\r
691 case TypeCode.UInt64:
\r
692 return reader.ReadUInt64();
\r
694 case TypeCode.String:
\r
695 return reader.ReadString();
\r
698 if (type == typeof(TimeSpan))
\r
699 return new TimeSpan (reader.ReadInt64 ());
\r
701 throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);
\r