2004-12-08 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / corlib / System.Runtime.Serialization.Formatters.Binary / ObjectReader.cs
index c47619a1206e526216ec8792930a8ad57aa35e37..134b936a3e1ed8e89bfa126cf0967ec4e932534d 100644 (file)
@@ -1,11 +1,33 @@
-// ObjectReader.cs
+// ObjectReader.cs\r
+//\r
+// Author:\r
+//   Lluis Sanchez Gual (lluis@ideary.com)\r
+//   Patrik Torstensson\r
+//\r
+// (C) 2003 Lluis Sanchez Gual\r
+
 //
-// Author:
-//   Lluis Sanchez Gual (lluis@ideary.com)
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-// (C) 2003 Lluis Sanchez Gual
-\r
-// FIXME: Implement the missing binary elements\r
 \r
 using System;\r
 using System.Runtime.Serialization;\r
@@ -13,25 +35,35 @@ using System.IO;
 using System.Collections;\r
 using System.Reflection;\r
 using System.Runtime.Remoting.Messaging;\r
+using System.Globalization;\r
 \r
 namespace System.Runtime.Serialization.Formatters.Binary\r
 {\r
        internal class ObjectReader\r
        {\r
+               BinaryFormatter _formatter;\r
                ISurrogateSelector _surrogateSelector;\r
                StreamingContext _context;\r
+               SerializationBinder _binder;\r
+               \r
+#if NET_1_1\r
+               TypeFilterLevel _filterLevel;\r
+#endif\r
 \r
                ObjectManager _manager;\r
                Hashtable _registeredAssemblies = new Hashtable();\r
                Hashtable _typeMetadataCache = new Hashtable();\r
 \r
                object _lastObject = null;\r
+               long _lastObjectID = 0;\r
+               long _rootObjectID = 0;\r
 \r
                class TypeMetadata\r
                {\r
                        public Type Type;\r
                        public Type[] MemberTypes;\r
                        public string[] MemberNames;\r
+                       public MemberInfo[] MemberInfos;\r
                        public int FieldCount;\r
                        public bool NeedsSerializationInfo;\r
                }\r
@@ -42,17 +74,22 @@ namespace System.Runtime.Serialization.Formatters.Binary
                        public int NullCount;\r
                }\r
 \r
-               public ObjectReader(ISurrogateSelector surrogateSelector, StreamingContext context)\r
+               public ObjectReader (BinaryFormatter formatter)\r
                {\r
-                       _manager = new ObjectManager (surrogateSelector, context);\r
-                       _surrogateSelector = surrogateSelector;\r
-                       _context = context;\r
+                       _formatter = formatter;\r
+                       _surrogateSelector = formatter.SurrogateSelector;\r
+                       _context = formatter.Context;\r
+                       _binder = formatter.Binder;\r
+                       _manager = new ObjectManager (_surrogateSelector, _context);\r
+                       \r
+#if NET_1_1\r
+                       _filterLevel = formatter.FilterLevel;\r
+#endif\r
                }\r
 \r
-               public object ReadObjectGraph (BinaryReader reader, bool readHeaders, HeaderHandler headerHandler)\r
+               public void ReadObjectGraph (BinaryReader reader, bool readHeaders, out object result, out Header[] headers)\r
                {\r
-                       object rootObject = null;\r
-                       Header[] headers = null;\r
+                       headers = null;\r
 \r
                        // Reads the objects. The first object in the stream is the\r
                        // root object.\r
@@ -62,13 +99,10 @@ namespace System.Runtime.Serialization.Formatters.Binary
                                if (readHeaders && (headers == null))\r
                                        headers = (Header[])CurrentObject;\r
                                else\r
-                                       if (rootObject == null) rootObject = CurrentObject;\r
+                                       if (_rootObjectID == 0) _rootObjectID = _lastObjectID;\r
                        }\r
 \r
-                       if (readHeaders && headerHandler != null)\r
-                               headerHandler (headers);\r
-\r
-                       return rootObject;\r
+                       result = _manager.GetObject (_rootObjectID);\r
                }\r
 \r
                public bool ReadNextObject (BinaryReader reader)\r
@@ -77,6 +111,7 @@ namespace System.Runtime.Serialization.Formatters.Binary
                        if (element == BinaryElement.End)\r
                        {\r
                                _manager.DoFixups();\r
+\r
                                _manager.RaiseDeserializationEvent();\r
                                return false;\r
                        }\r
@@ -86,9 +121,11 @@ namespace System.Runtime.Serialization.Formatters.Binary
 \r
                        ReadObject (element, reader, out objectId, out _lastObject, out info);\r
 \r
-                       if (objectId != 0) \r
+                       if (objectId != 0) {\r
                                RegisterObject (objectId, _lastObject, info, 0, null, null);\r
-\r
+                               _lastObjectID = objectId;               \r
+                       }\r
+       \r
                        return true;\r
                }\r
 \r
@@ -128,6 +165,7 @@ namespace System.Runtime.Serialization.Formatters.Binary
                                        ReadGenericArray (reader, out objectId, out value);\r
                                        break;\r
 \r
+\r
                                case BinaryElement.BoxedPrimitiveTypeValue:\r
                                        value = ReadBoxedPrimitiveTypeValue (reader);\r
                                        objectId = 0;\r
@@ -181,8 +219,7 @@ namespace System.Runtime.Serialization.Formatters.Binary
                {\r
                        long id = (long) reader.ReadUInt32 ();\r
                        string assemblyName = reader.ReadString ();\r
-                       Assembly assembly = Assembly.Load (assemblyName);\r
-                       _registeredAssemblies [id] = assembly;\r
+                       _registeredAssemblies [id] = assemblyName;\r
                }\r
 \r
                private void ReadObjectInstance (BinaryReader reader, bool isRuntimeObject, out long objectId, out object value, out SerializationInfo info)\r
@@ -209,19 +246,34 @@ namespace System.Runtime.Serialization.Formatters.Binary
 \r
                private void ReadObjectContent (BinaryReader reader, TypeMetadata metadata, long objectId, out object objectInstance, out SerializationInfo info)\r
                {\r
-                       objectInstance = FormatterServices.GetUninitializedObject (metadata.Type);\r
+#if NET_1_1\r
+                       if (_filterLevel == TypeFilterLevel.Low)\r
+                               objectInstance = FormatterServices.GetSafeUninitializedObject (metadata.Type);\r
+                       else\r
+#endif\r
+                               objectInstance = FormatterServices.GetUninitializedObject (metadata.Type);\r
+                               \r
                        info = metadata.NeedsSerializationInfo ? new SerializationInfo(metadata.Type, new FormatterConverter()) : null;\r
 \r
-                       for (int n=0; n<metadata.FieldCount; n++)\r
-                               ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberNames[n], null);\r
+                       if (metadata.MemberNames != null)\r
+                               for (int n=0; n<metadata.FieldCount; n++)\r
+                                       ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberNames[n], null, null);\r
+                       else\r
+                               for (int n=0; n<metadata.FieldCount; n++)\r
+                                       ReadValue (reader, objectInstance, objectId, info, metadata.MemberTypes[n], metadata.MemberInfos[n].Name, metadata.MemberInfos[n], null);\r
                }\r
 \r
                private void RegisterObject (long objectId, object objectInstance, SerializationInfo info, long parentObjectId, MemberInfo parentObjectMemeber, int[] indices)\r
                {\r
+                       if (parentObjectId == 0) indices = null;\r
+\r
                        if (!objectInstance.GetType().IsValueType || parentObjectId == 0)\r
-                               _manager.RegisterObject (objectInstance, objectId, info, 0, null, indices);\r
+                               _manager.RegisterObject (objectInstance, objectId, info, 0, null, null);\r
                        else\r
+                       {\r
+                               if (indices != null) indices = (int[])indices.Clone();\r
                                _manager.RegisterObject (objectInstance, objectId, info, parentObjectId, parentObjectMemeber, indices);\r
+                       }\r
                }\r
 \r
                private void ReadStringIntance (BinaryReader reader, out long objectId, out object value)\r
@@ -265,7 +317,7 @@ namespace System.Runtime.Serialization.Formatters.Binary
                        bool end = false;\r
                        while (!end)\r
                        {\r
-                               ReadValue (reader, array, objectId, null, elementType, null, indices);\r
+                               ReadValue (reader, array, objectId, null, elementType, null, null, indices);\r
 \r
                                for (int dim = array.Rank-1; dim >= 0; dim--)\r
                                {\r
@@ -324,7 +376,7 @@ namespace System.Runtime.Serialization.Formatters.Binary
                        for (int n = 0; n < length; n++)\r
                        {\r
                                indices[0] = n;\r
-                               ReadValue (reader, array, objectId, null, elementType, null, indices);\r
+                               ReadValue (reader, array, objectId, null, elementType, null, null, indices);\r
                                n = indices[0];\r
                        }\r
                        val = array;\r
@@ -356,8 +408,7 @@ namespace System.Runtime.Serialization.Formatters.Binary
                        if (!isRuntimeObject) \r
                        {\r
                                long assemblyId = (long)reader.ReadUInt32();\r
-                               Assembly asm = (Assembly)_registeredAssemblies[assemblyId];\r
-                               metadata.Type = asm.GetType (className, true);\r
+                               metadata.Type = GetDeserializationType (assemblyId, className);\r
                        }\r
                        else\r
                                metadata.Type = Type.GetType (className, true);\r
@@ -386,6 +437,37 @@ namespace System.Runtime.Serialization.Formatters.Binary
                                        throw new SerializationException("Serializable objects must be marked with the Serializable attribute");\r
 \r
                                metadata.NeedsSerializationInfo = (metadata.Type.GetInterface ("ISerializable") != null);\r
+                               if (!metadata.NeedsSerializationInfo)\r
+                               {\r
+                                       metadata.MemberInfos = new MemberInfo [fieldCount];\r
+                                       for (int n=0; n<fieldCount; n++)\r
+                                       {\r
+                                               MemberInfo[] members = null;\r
+                                               string memberName = names[n];\r
+                                               \r
+                                               int i = memberName.IndexOf ('+');\r
+                                               if (i != -1) {\r
+                                                       string baseTypeName = names[n].Substring (0,i);\r
+                                                       memberName = names[n].Substring (i+1);\r
+                                                       Type t = metadata.Type.BaseType;\r
+                                                       while (t != null) {\r
+                                                               if (t.Name == baseTypeName) {\r
+                                                                       members = t.GetMember (memberName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
+                                                                       break;\r
+                                                               }\r
+                                                               else\r
+                                                                       t = t.BaseType;\r
+                                                       }\r
+                                               }\r
+                                               else\r
+                                                       members = metadata.Type.GetMember (memberName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
+                                                       \r
+                                               if (members == null || members.Length == 0) throw new SerializationException ("Field \"" + names[n] + "\" not found in class " + metadata.Type.FullName);\r
+                                               if (members.Length > 1) throw new SerializationException ("There are two public members named \"" + names[n] + "\" in the class hirearchy of " + metadata.Type.FullName);\r
+                                               metadata.MemberInfos [n] = members[0];\r
+                                       }\r
+                                       metadata.MemberNames = null;    // Info now in MemberInfos\r
+                               }\r
                        }\r
 \r
                        // Registers the type's metadata so it can be reused later if\r
@@ -398,7 +480,7 @@ namespace System.Runtime.Serialization.Formatters.Binary
                }\r
 \r
 \r
-               private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, int[] indices)\r
+               private void ReadValue (BinaryReader reader, object parentObject, long parentObjectId, SerializationInfo info, Type valueType, string fieldName, MemberInfo memberInfo, int[] indices)\r
                {\r
                        // Reads a value from the stream and assigns it to the member of an object\r
 \r
@@ -407,7 +489,7 @@ namespace System.Runtime.Serialization.Formatters.Binary
                        if (BinaryCommon.IsPrimitive (valueType))\r
                        {\r
                                val = ReadPrimitiveTypeValue (reader, valueType);\r
-                               SetObjectValue (parentObject, fieldName, info, val, valueType, indices);\r
+                               SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);\r
                                return;\r
                        }\r
 \r
@@ -419,7 +501,7 @@ namespace System.Runtime.Serialization.Formatters.Binary
                        {\r
                                // Just read the id of the referred object and record a fixup\r
                                long childObjectId = (long) reader.ReadUInt32();\r
-                               RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, indices);\r
+                               RecordFixup (parentObjectId, childObjectId, parentObject, info, fieldName, memberInfo, indices);\r
                                return;\r
                        }\r
 \r
@@ -446,29 +528,29 @@ namespace System.Runtime.Serialization.Formatters.Binary
                        {\r
                                if (val.GetType().IsValueType)\r
                                {\r
-                                       RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, indices);\r
+                                       RecordFixup (parentObjectId, objectId, parentObject, info, fieldName, memberInfo, indices);\r
                                        hasFixup = true;\r
                                }\r
 \r
                                // Register the value\r
 \r
-                               if (info == null && !parentObject.GetType().IsArray)\r
-                                       RegisterObject (objectId, val, objectInfo, parentObjectId, GetObjectMember(parentObject, fieldName), null);\r
+                               if (info == null && !(parentObject is Array))\r
+                                       RegisterObject (objectId, val, objectInfo, parentObjectId, memberInfo, null);\r
                                else\r
                                        RegisterObject (objectId, val, objectInfo, parentObjectId, null, indices);\r
                        }\r
                        // Assign the value to the parent object, unless there is a fixup\r
                        \r
                        if (!hasFixup) \r
-                               SetObjectValue (parentObject, fieldName, info, val, valueType, indices);\r
+                               SetObjectValue (parentObject, fieldName, memberInfo, info, val, valueType, indices);\r
                }\r
 \r
-               private void SetObjectValue (object parentObject, string fieldName, SerializationInfo info, object value, Type valueType, int[] indices)\r
+               private void SetObjectValue (object parentObject, string fieldName, MemberInfo memberInfo, SerializationInfo info, object value, Type valueType, int[] indices)\r
                {\r
                        if (value is IObjectReference)\r
                                value = ((IObjectReference)value).GetRealObject (_context);\r
 \r
-                       if (parentObject.GetType().IsArray) \r
+                       if (parentObject is Array) \r
                        {\r
                                if (value is ArrayNullFiller) \r
                                {\r
@@ -484,36 +566,40 @@ namespace System.Runtime.Serialization.Formatters.Binary
                                info.AddValue (fieldName, value, valueType);\r
                        }\r
                        else {\r
-                               MemberInfo member = GetObjectMember(parentObject, fieldName);\r
-                               if (member is FieldInfo)\r
-                                       ((FieldInfo)member).SetValue (parentObject, value);\r
+                               if (memberInfo is FieldInfo)\r
+                                       ((FieldInfo)memberInfo).SetValue (parentObject, value);\r
                                else\r
-                                       ((PropertyInfo)member).SetValue (parentObject, value, null);\r
+                                       ((PropertyInfo)memberInfo).SetValue (parentObject, value, null);\r
                        }\r
                }\r
 \r
-               private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, int[] indices)\r
+               private void RecordFixup (long parentObjectId, long childObjectId, object parentObject, SerializationInfo info, string fieldName, MemberInfo memberInfo, int[] indices)\r
                {\r
                        if (info != null) {\r
                                _manager.RecordDelayedFixup (parentObjectId, fieldName, childObjectId);\r
                        }\r
-                       else if (parentObject.GetType().IsArray) {\r
+                       else if (parentObject is Array) {\r
                                if (indices.Length == 1)\r
                                        _manager.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);\r
                                else\r
                                        _manager.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);\r
                        }\r
                        else {\r
-                               _manager.RecordFixup (parentObjectId, GetObjectMember(parentObject, fieldName), childObjectId);\r
+                               _manager.RecordFixup (parentObjectId, memberInfo, childObjectId);\r
                        }\r
                }\r
 \r
-               private MemberInfo GetObjectMember (object parentObject, string fieldName)\r
+               private Type GetDeserializationType (long assemblyId, string className)\r
                {\r
-                       MemberInfo[] members = parentObject.GetType().GetMember (fieldName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
-                       if (members.Length > 1) throw new SerializationException ("There are two public members named \"" + fieldName + "\" in the class hirearchy of " + parentObject.GetType().FullName);\r
-                       if (members.Length == 0) throw new SerializationException ("Field \"" + fieldName + "\" not found in class " + parentObject.GetType().FullName);\r
-                       return members[0];\r
+                       string assemblyName = (string)_registeredAssemblies[assemblyId];\r
+\r
+                       if (_binder == null)\r
+                       {\r
+                               Assembly assembly = Assembly.Load (assemblyName);\r
+                               return assembly.GetType (className, true);\r
+                       }\r
+                       else\r
+                               return _binder.BindToType (assemblyName, className);\r
                }\r
 \r
                public Type ReadType (BinaryReader reader, TypeTag code)\r
@@ -539,8 +625,7 @@ namespace System.Runtime.Serialization.Formatters.Binary
                                {\r
                                        string name = reader.ReadString ();\r
                                        long asmid = (long) reader.ReadUInt32();\r
-                                       Assembly asm = (Assembly)_registeredAssemblies[asmid];\r
-                                       return asm.GetType (name, true);\r
+                                       return GetDeserializationType (asmid, name);\r
                                }\r
 \r
                                case TypeTag.ArrayOfObject:\r
@@ -574,11 +659,10 @@ namespace System.Runtime.Serialization.Formatters.Binary
                                        return reader.ReadChar();\r
 \r
                                case TypeCode.DateTime: \r
-                                       long ticks = reader.ReadInt64();\r
-                                       return new DateTime (ticks);\r
+                                       return new DateTime (reader.ReadInt64());\r
 \r
                                case TypeCode.Decimal:\r
-                                       return reader.ReadDecimal();\r
+                                       return Decimal.Parse (reader.ReadString(), CultureInfo.InvariantCulture);\r
 \r
                                case TypeCode.Double:\r
                                        return reader.ReadDouble();\r
@@ -611,8 +695,11 @@ namespace System.Runtime.Serialization.Formatters.Binary
                                        return reader.ReadString();\r
 \r
                                default:\r
-                                       throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);\r
+                                       if (type == typeof(TimeSpan))\r
+                                               return new TimeSpan (reader.ReadInt64 ());\r
+                                       else\r
+                                               throw new NotSupportedException ("Unsupported primitive type: " + type.FullName);\r
                        }\r
                }\r
        }\r
-}\r
+}