Serialization of proxies with events in DP 2

2 views
Skip to first unread message

Fabian Schmied

unread,
Apr 25, 2007, 5:28:39 AM4/25/07
to castle-pro...@googlegroups.com
Hi all,

A few weeks ago, I've discovered a strange CLR bug concerning
ISerializable: when an object[] containing a delegate is manually
serialized via SerializationInfo, the object[]'s contents is not
correctly restored on deserialization.

I've reported this on Microsoft Connect (MS has been able to reproduce
the bug, but hasn't yet commented on when it will be fixed -
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=271173),
but unfortunately, the bug is triggered by DynamicProxy 2's
serialization mechanism

DP 2 serializes the members of the base class as an object[], so
whenever the base class holds a delegate or event, proxy
deserialization will be broken. I've already filed a JIRA issue for
this (http://support.castleproject.org/browse/DYNPROXY-56), but since
a project I'm working on has this problem right now, I just wanted to
ask what you think about this.

Some experiments have shown that it might be possible to work around
DP 2 triggering the bug by "flattening out" the array like this:

Instead of:
info.AddValue ("__data", dataObjectArray);

We could do:
info.AddValue ("__data_Length", dataObjectArray);
for (int i = 0; i < dataObjectArray.Length; ++i)
{
info.AddValue ("__data_" + i, dataObjectArray[i]);
}

That way, at least DP 2 won't be the one to trigger the bug.

What do you think about that?

Fabian

josh robb

unread,
Apr 25, 2007, 6:00:28 AM4/25/07
to castle-pro...@googlegroups.com
I'm pretty sure that the best way to get some feedback on this would
be to provide a patch which implements this.

j.

Fabian Schmied

unread,
Apr 25, 2007, 6:05:59 AM4/25/07
to castle-pro...@googlegroups.com
> I'm pretty sure that the best way to get some feedback on this would
> be to provide a patch which implements this.

Yes, however I don't want to write a patch if
- Somebody is already working on it, or
- Somebody finds a disadvantage of the proposed workaround/has a
better suggestion.

Therefore the call for opinions :)

Fabian

josh robb

unread,
Apr 25, 2007, 6:11:57 AM4/25/07
to castle-pro...@googlegroups.com
> Yes, however I don't want to write a patch if
> - Somebody is already working on it, or
> - Somebody finds a disadvantage of the proposed workaround/has a
> better suggestion.
>
> Therefore the call for opinions :)

It's my opinion that you'll get better feedback with a failing test
case and a patch.

;)

j.

Fabian Schmied

unread,
Apr 25, 2007, 6:34:21 AM4/25/07
to castle-pro...@googlegroups.com
> It's my opinion that you'll get better feedback with a failing test
> case and a patch.

Well, first I'll wait and see if there are any opinions on the issue.
If nobody answers, I at least know nobody is working on it :)

A failing test case is in the JIRA report, by the way.

Fabian

Ayende Rahien

unread,
Apr 25, 2007, 7:15:04 AM4/25/07
to castle-pro...@googlegroups.com
Fabian, I don't see any reason to not do it, frankly.

On 4/25/07, Fabian Schmied <fabian....@gmail.com > wrote:

Hamilton Verissimo

unread,
Apr 25, 2007, 8:54:52 AM4/25/07
to castle-pro...@googlegroups.com
The only thing related to DP2 that we're working here is to make the
PEverify check work again.

On 4/25/07, Fabian Schmied <fabian....@gmail.com> wrote:
>


--
Cheers,
hamilton verissimo
ham...@castlestronghold.com
http://www.castlestronghold.com/

Fabian Schmied

unread,
Apr 26, 2007, 7:01:25 AM4/26/07
to castle-pro...@googlegroups.com
Okay, I've implemented a fix, and it was really difficult to do so. My
original plan of just flattening out the array didn't work for all
error cases, so in case anyone has to touch the serialization code
again, here's the full story:

I actually had to fix two issues in the serialization code. The first
one was to avoid the delegate serialization bug, but in the course of
doing so, I found that there was also a problem with direct self
references, probably related to the fact that DP 2 implements
IObjectReference. The exception message I kept getting was "The object
with ID 7 implements the IObjectReference interface for which all
dependencies cannot be resolved."

Strangely, indirect self references (i.e. the proxy having a member
holding an object holding a reference back to the proxy) did work -
the .NET serialization engine apparently can handle, and performs a
"fixup" stage at the end of the deserialization process, where all
indirect self references are set to the correct values.

Investigating this, I found that the serialization engine also
performs fixups on delegates held indirectly by the proxy, but
apparently it forgets to do those fixups on directly held delegates.
This bug also occurs when the delegates are held in an object[] and
then even has effects on array elements completely unrelated to the
delegate. This is the bug I originally reported on MS Connect.

Therefore, my fix is actually quite simple:

- In the serialization process for class proxies, don't serialize the
members array directly, but add an "Indirection" object holding the
members array. This causes the fixups to be correctly applied to self
references and delegate members later on, because all members are now
serialized indirectly.

- When deserializing a class proxy, don't immediately populate the
class proxy's members in the constructor, because at this point of
time, no fixups have been performed. Instead, keep the "Indirection"
object for later.

- Make use of IDeserializationCallback (I implemented it on
ProxyObjectReference) and populate the class proxy's base members from
the callback method from the members indirectly held by the
"Indirection" object. At this point of time all fixups have been
performed and everything seems to work fine.

The patch is attached. All the preexisting tests are green (ignoring
the failing PEVerify stuff), and I've added a few additional ones. In
order to simplify the existing serialization code, I've added a
ProxySerializer helper class, which is called from the generated proxy
code and does all the serialization code which was previously directly
emitted into the proxy's GetObjectData() method.

Best regards,
Fabian

DP-56.patch

Fabian Schmied

unread,
Apr 26, 2007, 7:05:56 AM4/26/07
to castle-pro...@googlegroups.com
> (ignoring
> the failing PEVerify stuff),

Which is not caused by my code, I should have added :)

Fabian

josh robb

unread,
Apr 26, 2007, 7:13:08 AM4/26/07
to castle-pro...@googlegroups.com
Dude! Thats an impressive piece of work (and explanation!).

I'm curious about why ProxySerializer.Formatter is static. What
happens if two threads are Serializing proxies at the same time? Isn't
this a race condition or am I missing something?

j.

Fabian Schmied

unread,
Apr 26, 2007, 7:32:46 AM4/26/07
to castle-pro...@googlegroups.com
> Dude! Thats an impressive piece of work (and explanation!).

Thanks. I worked on this for quite a couple of hours...

> I'm curious about why ProxySerializer.Formatter is static. What
> happens if two threads are Serializing proxies at the same time? Isn't
> this a race condition or am I missing something?

Oops, sorry, that's a remainder of one of many experiments I've done.
ProxySerializer.Formatter isnt't used at all.

Fabian

Ayende Rahien

unread,
Apr 26, 2007, 8:24:28 AM4/26/07
to castle-pro...@googlegroups.com
The patch ended up as text again, can you try again?

Index: Castle.DynamicProxy.Tests/SerializableClassTestCase.cs
===================================================================
--- Castle.DynamicProxy.Tests/SerializableClassTestCase.cs      (revision 3735)
+++ Castle.DynamicProxy.Tests/SerializableClassTestCase.cs      (working copy)
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-//     http://www.apache.org/licenses/LICENSE-2.0
+//              http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -187,10 +187,12 @@
                public class C
                {
                        public int I;
+                       public C This;

                        public C (int i)
                        {
                                I = i;
+                               This = this;
                        }
                }

@@ -203,8 +205,223 @@
                        C otherProxy = (C) SerializeAndDeserialize (proxy);

                         Assert.AreEqual (proxy.I, otherProxy.I);
+                       Assert.AreSame (otherProxy, otherProxy.This);
                }

+               [Serializable]
+               public class EventHandlerClass
+               {
+                       public void TestHandler (object sender, EventArgs e)
+                       {
+                       }
+
+               }

+               [Serializable]
+               public class DelegateHolder
+               {
+                       public EventHandler DelegateMember;
+                       public ArrayList ComplexTypeMember;
+
+                       public DelegateHolder ()
+                       {
+                       }
+
+                       public void TestHandler (object sender, EventArgs e)
+                       {
+                       }
+               }
+
+               [Serializable]
+               public class IndirectDelegateHolder
+               {
+                       public DelegateHolder DelegateHolder = new DelegateHolder();
+
+                       public void TestHandler (object sender, EventArgs e)
+                       {
+                       }
+               }
+
+               [Test]
+               public void SerializeObjectsWithDelegateToOtherObject ()
+               {
+                       ProxyObjectReference.ResetScope ();
+
+                       EventHandlerClass eventHandlerInstance = new EventHandlerClass();
+                       DelegateHolder proxy = (DelegateHolder) generator.CreateClassProxy (typeof (DelegateHolder), new IInterceptor[] {new StandardInterceptor ()});
+
+                       proxy.DelegateMember = new EventHandler(eventHandlerInstance.TestHandler);
+                       proxy.ComplexTypeMember = new ArrayList (new int[] { 1, 2, 3 });
+                       proxy.ComplexTypeMember.Add (eventHandlerInstance);
+
+                       Assert.IsNotNull (proxy.DelegateMember);
+                       Assert.IsNotNull (proxy.DelegateMember.Target);
+
+                       Assert.IsNotNull (proxy.ComplexTypeMember);
+                       Assert.AreEqual (4, proxy.ComplexTypeMember.Count);
+                       Assert.AreEqual (1, proxy.ComplexTypeMember[0]);
+                       Assert.AreEqual (2, proxy.ComplexTypeMember[1]);
+                       Assert.AreEqual (3, proxy.ComplexTypeMember[2]);
+                       Assert.AreSame (proxy.ComplexTypeMember[3], proxy.DelegateMember.Target);
+
+                       DelegateHolder otherProxy = (DelegateHolder) (SerializeAndDeserialize (proxy));
+
+                       Assert.IsNotNull (otherProxy.DelegateMember);
+                       Assert.IsNotNull (otherProxy.DelegateMember.Target);
+
+                       Assert.IsNotNull (otherProxy.ComplexTypeMember);
+                       Assert.AreEqual (4, otherProxy.ComplexTypeMember.Count);
+                       Assert.AreEqual (1, otherProxy.ComplexTypeMember[0]);
+                       Assert.AreEqual (2, otherProxy.ComplexTypeMember[1]);
+                       Assert.AreEqual (3, otherProxy.ComplexTypeMember[2]);
+                       Assert.AreSame (otherProxy.ComplexTypeMember[3], otherProxy.DelegateMember.Target);
+               }
+
+               [Test]
+               public void SerializeObjectsWithDelegateToThisObject ()
+               {
+                       ProxyObjectReference.ResetScope ();
+
+                       DelegateHolder proxy = (DelegateHolder) generator.CreateClassProxy (typeof (DelegateHolder), new IInterceptor[] { new StandardInterceptor () });
+
+                       proxy.DelegateMember = new EventHandler (proxy.TestHandler);
+                       proxy.ComplexTypeMember = new ArrayList (new int[] { 1, 2, 3 });
+
+                       Assert.IsNotNull (proxy.DelegateMember);
+                       Assert.AreSame (proxy, proxy.DelegateMember.Target);
+
+                       Assert.IsNotNull (proxy.ComplexTypeMember);
+                       Assert.AreEqual (3, proxy.ComplexTypeMember.Count);
+                       Assert.AreEqual (1, proxy.ComplexTypeMember[0]);
+                       Assert.AreEqual (2, proxy.ComplexTypeMember[1]);
+                       Assert.AreEqual (3, proxy.ComplexTypeMember[2]);
+
+                       DelegateHolder otherProxy = (DelegateHolder) (SerializeAndDeserialize (proxy));
+
+                       Assert.IsNotNull (otherProxy.DelegateMember );
+                       Assert.AreSame (otherProxy, otherProxy.DelegateMember.Target);
+
+                       Assert.IsNotNull (otherProxy.ComplexTypeMember);
+                       Assert.AreEqual (3, otherProxy.ComplexTypeMember.Count);
+                       Assert.AreEqual (1, otherProxy.ComplexTypeMember[0]);
+                       Assert.AreEqual (2, otherProxy.ComplexTypeMember[1]);
+                       Assert.AreEqual (3, otherProxy.ComplexTypeMember[2]);
+               }
+
+               [Test]
+               public void SerializeObjectsWithIndirectDelegateToThisObject ()
+               {
+                       ProxyObjectReference.ResetScope ();
+
+                       IndirectDelegateHolder proxy = (IndirectDelegateHolder) generator.CreateClassProxy (typeof (IndirectDelegateHolder),
+                               new IInterceptor[] { new StandardInterceptor () });
+
+                       proxy.DelegateHolder.DelegateMember = new EventHandler (proxy.TestHandler);
+                       proxy.DelegateHolder.ComplexTypeMember = new ArrayList (new int[] { 1, 2, 3 });
+
+                       Assert.IsNotNull (proxy.DelegateHolder.DelegateMember);
+                       Assert.AreSame (proxy, proxy.DelegateHolder.DelegateMember.Target);
+
+                       Assert.IsNotNull (proxy.DelegateHolder.ComplexTypeMember);
+                       Assert.AreEqual (3, proxy.DelegateHolder.ComplexTypeMember.Count);
+                       Assert.AreEqual (1, proxy.DelegateHolder.ComplexTypeMember [0]);
+                       Assert.AreEqual (2, proxy.DelegateHolder.ComplexTypeMember[1]);
+                       Assert.AreEqual (3, proxy.DelegateHolder.ComplexTypeMember[2]);
+
+                       IndirectDelegateHolder otherProxy = (IndirectDelegateHolder) (SerializeAndDeserialize (proxy));
+
+                       Assert.IsNotNull (otherProxy.DelegateHolder.DelegateMember);
+                       Assert.AreSame (otherProxy, otherProxy.DelegateHolder.DelegateMember.Target);
+
+                       Assert.IsNotNull (otherProxy.DelegateHolder.ComplexTypeMember);
+                       Assert.AreEqual (3, otherProxy.DelegateHolder.ComplexTypeMember.Count);
+                       Assert.AreEqual (1, otherProxy.DelegateHolder.ComplexTypeMember [0]);
+                       Assert.AreEqual (2, otherProxy.DelegateHolder.ComplexTypeMember[1]);
+                       Assert.AreEqual (3, otherProxy.DelegateHolder.ComplexTypeMember[2]);
+               }
+
+               [Test]
+               public void SerializeObjectsWithIndirectDelegateToMember ()
+               {
+                       ProxyObjectReference.ResetScope ();
+
+                       IndirectDelegateHolder proxy = (IndirectDelegateHolder) generator.CreateClassProxy (typeof (IndirectDelegateHolder),
+                               new IInterceptor[] { new StandardInterceptor () });
+
+                       proxy.DelegateHolder.DelegateMember = new EventHandler ( proxy.DelegateHolder.TestHandler);
+                       proxy.DelegateHolder.ComplexTypeMember = new ArrayList (new int[] { 1, 2, 3 });
+
+                       Assert.IsNotNull (proxy.DelegateHolder.DelegateMember );
+                       Assert.AreSame (proxy.DelegateHolder, proxy.DelegateHolder.DelegateMember.Target);
+
+                       Assert.IsNotNull (proxy.DelegateHolder.ComplexTypeMember);
+                       Assert.AreEqual (3, proxy.DelegateHolder.ComplexTypeMember.Count);
+                       Assert.AreEqual (1, proxy.DelegateHolder.ComplexTypeMember[0]);
+                       Assert.AreEqual (2, proxy.DelegateHolder.ComplexTypeMember [1]);
+                       Assert.AreEqual (3, proxy.DelegateHolder.ComplexTypeMember[2]);
+
+                       IndirectDelegateHolder otherProxy = (IndirectDelegateHolder) (SerializeAndDeserialize (proxy));
+
+                       Assert.IsNotNull (otherProxy.DelegateHolder.DelegateMember);
+                       Assert.AreSame (otherProxy.DelegateHolder, otherProxy.DelegateHolder.DelegateMember.Target);
+
+                       Assert.IsNotNull (otherProxy.DelegateHolder.ComplexTypeMember);
+                       Assert.AreEqual (3, otherProxy.DelegateHolder.ComplexTypeMember.Count);
+                       Assert.AreEqual (1, otherProxy.DelegateHolder.ComplexTypeMember[0]);
+                       Assert.AreEqual (2, otherProxy.DelegateHolder.ComplexTypeMember[1]);
+                       Assert.AreEqual (3, otherProxy.DelegateHolder.ComplexTypeMember [2]);
+               }
+
+               [Serializable]
+               public class ClassWithIndirectSelfReference
+               {
+                       public ArrayList List = new ArrayList();
+
+                       public ClassWithIndirectSelfReference()
+                       {
+                               List.Add (this);
+                       }
+               }
+
+               [Test]
+               public void SerializeClassWithIndirectSelfReference()
+               {
+                       ProxyObjectReference.ResetScope ();
+
+                       ClassWithIndirectSelfReference proxy = (ClassWithIndirectSelfReference) generator.CreateClassProxy (typeof (ClassWithIndirectSelfReference),
+                               new Type[0], new StandardInterceptor ());
+                       Assert.AreSame (proxy, proxy.List[0]);
+
+                       ClassWithIndirectSelfReference otherProxy = (ClassWithIndirectSelfReference) SerializeAndDeserialize (proxy);
+                       Assert.AreSame (otherProxy, otherProxy.List[0]);
+               }
+
+               [Serializable]
+               public class ClassWithDirectAndIndirectSelfReference
+               {
+                       public ClassWithDirectAndIndirectSelfReference This;
+                       public ArrayList List = new ArrayList();
+
+                       public ClassWithDirectAndIndirectSelfReference ()
+                       {
+                               This = this;
+                               List.Add (this);
+                       }
+               }
+
+               [Test]
+               public void SerializeClassWithDirectAndIndirectSelfReference ()
+               {
+                       ProxyObjectReference.ResetScope ();
+
+                       ClassWithDirectAndIndirectSelfReference proxy = (ClassWithDirectAndIndirectSelfReference) generator.CreateClassProxy (typeof (ClassWithDirectAndIndirectSelfReference),
+                               new Type[0], new StandardInterceptor ());
+                       Assert.AreSame (proxy, proxy.This);
+
+                       ClassWithDirectAndIndirectSelfReference otherProxy = (ClassWithDirectAndIndirectSelfReference) SerializeAndDeserialize (proxy);
+                       Assert.AreSame (otherProxy, otherProxy.List[0]);
+                       Assert.AreSame (otherProxy, otherProxy.This);
+               }
+
        }
}
Index: Castle.DynamicProxy /Castle.DynamicProxy-vs2005.csproj
===================================================================
--- Castle.DynamicProxy/Castle.DynamicProxy-vs2005.csproj       (revision 3735)
+++ Castle.DynamicProxy/Castle.DynamicProxy- vs2005.csproj       (working copy)
@@ -268,6 +268,7 @@
     <Compile Include="ProxyGenerator.cs" />
     <Compile Include="RemotableInvocation.cs" />
     <Compile Include="Serialization\ProxyObjectReference.cs" />
+    <Compile Include="Serialization\ProxySerializer.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="..\..\..\CastleKey.snk">
Index: Castle.DynamicProxy /Generators/BaseProxyGenerator.cs
===================================================================
--- Castle.DynamicProxy/Generators/BaseProxyGenerator.cs        (revision 3735)
+++ Castle.DynamicProxy/Generators/BaseProxyGenerator.cs        (working copy)
@@ -1473,39 +1473,11 @@
                        if (interfaces == null)
                                interfaces = new Type[0];

-                       Type[] get_type_args = new Type[] {typeof (String), typeof (bool), typeof (bool)};
-                       Type[] key_and_object = new Type[] {typeof (String), typeof (Object)};
-                       MethodInfo addValueMethod = typeof (SerializationInfo).GetMethod("AddValue", key_and_object);
-
                        ArgumentReference arg1 = new ArgumentReference(typeof (SerializationInfo));
                        ArgumentReference arg2 = new ArgumentReference(typeof (StreamingContext));
                        MethodEmitter getObjectData = emitter.CreateMethod("GetObjectData",
                                                                           new ReturnReferenceExpression(typeof (void)), arg1, arg2);

-                       LocalReference typeLocal = getObjectData.CodeBuilder.DeclareLocal(typeof (Type));
-
-                       getObjectData.CodeBuilder.AddStatement(new AssignStatement(
-                                                               typeLocal,
-                                                               new MethodInvocationExpression(null,
-                                                                                              typeof (Type).GetMethod("GetType",
-                                                                                                                      get_type_args),
-                                                                                              new ConstReference(
-                                                                                               typeof (ProxyObjectReference).
-                                                                                                       AssemblyQualifiedName).ToExpression(),
-                                                                                              new ConstReference(1).ToExpression(),
-                                                                                              new ConstReference(0).ToExpression())));
-
-                       getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
-                                                               new MethodInvocationExpression(
-                                                                       arg1, typeof (SerializationInfo).GetMethod("SetType"),
-                                                                       typeLocal.ToExpression())));
-
-                       getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
-                                                               new MethodInvocationExpression(arg1, addValueMethod,
-                                                                                              new ConstReference("__interceptors").
-                                                                                               ToExpression(),
-                                                                                              interceptorsField.ToExpression())));
-
                        LocalReference interfacesLocal =
                                 getObjectData.CodeBuilder.DeclareLocal(typeof (String[]));

@@ -1520,18 +1492,11 @@
                                                                        new ConstReference(interfaces[i].AssemblyQualifiedName).ToExpression()));
                        }

-                       getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
-                                                               new MethodInvocationExpression(arg1, addValueMethod,
-                                                                                              new ConstReference("__interfaces").
-                                                                                               ToExpression(),
-                                                                                              interfacesLocal.ToExpression())));
+      getObjectData.CodeBuilder.AddStatement (
+        new ExpressionStatement (new MethodInvocationExpression (null, typeof (ProxySerializer).GetMethod ("SerializeBaseProxyData"),
+        arg1.ToExpression(), SelfReference.Self.ToExpression(), interceptorsField.ToExpression (),
+        interfacesLocal.ToExpression(), new TypeTokenExpression(emitter.BaseType))));

-                       getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
-                                                               new MethodInvocationExpression(arg1, addValueMethod,
-                                                                                              new ConstReference("__baseType").
-                                                                                               ToExpression(),
-                                                                                              new TypeTokenExpression( emitter.BaseType))));
-
                        CustomizeGetObjectData(getObjectData.CodeBuilder, arg1, arg2);

                        getObjectData.CodeBuilder.AddStatement(new ReturnStatement());
Index: Castle.DynamicProxy/Generators/ClassProxyGenerator.cs
===================================================================
--- Castle.DynamicProxy/Generators/ClassProxyGenerator.cs       (revision 3735)
+++ Castle.DynamicProxy /Generators/ClassProxyGenerator.cs       (working copy)
@@ -31,6 +31,7 @@
        using System.Collections.Specialized;
        using System.Runtime.Serialization;
        using Castle.DynamicProxy.Generators.Emitters.CodeBuilders ;
+  using Castle.DynamicProxy.Serialization;

        /// <summary>
        ///
@@ -336,17 +337,12 @@
                protected override void CustomizeGetObjectData(AbstractCodeBuilder codebuilder,
                                                               ArgumentReference arg1, ArgumentReference arg2)
                {
-                       Type[] key_and_object = new Type[] {typeof (String), typeof (Object)};
-                       Type[] key_and_bool = new Type[] {typeof (String), typeof (bool)};
-                       MethodInfo addValueMethod = typeof (SerializationInfo).GetMethod("AddValue", key_and_object);
-                       MethodInfo addValueBoolMethod = typeof (SerializationInfo).GetMethod("AddValue", key_and_bool);
+      codebuilder.AddStatement (new ExpressionStatement (new MethodInvocationExpression (null,
+        typeof (ProxySerializer).GetMethod ("SerializeClassProxyData"), arg1.ToExpression (),
+        new ConstReference (delegateToBaseGetObjectData).ToExpression (), new TypeTokenExpression (targetType),
+        SelfReference.Self.ToExpression())));

-                       codebuilder.AddStatement( new ExpressionStatement(
-                               new MethodInvocationExpression(arg1, addValueBoolMethod,
-                               new ConstReference("__delegateToBase").ToExpression(),
-                               new ConstReference( delegateToBaseGetObjectData ? 1 : 0 ).ToExpression() ) ) );
-
-                       if (delegateToBaseGetObjectData)
+      if (delegateToBaseGetObjectData)
                        {
                                MethodInfo baseGetObjectData = targetType.GetMethod("GetObjectData",
                                        new Type[] { typeof(SerializationInfo), typeof(StreamingContext) });
@@ -355,29 +351,6 @@
                                        new MethodInvocationExpression( baseGetObjectData,
                                                arg1.ToExpression(), arg2.ToExpression() )) );
                        }
-                       else
-                       {
-                               LocalReference members_ref = codebuilder.DeclareLocal( typeof(MemberInfo[]) );
-                               LocalReference data_ref = codebuilder.DeclareLocal( typeof(object[]) );
-
-                               MethodInfo getSerMembers = typeof(FormatterServices).GetMethod("GetSerializableMembers",
-                                       new Type[] { typeof(Type) });
-                               MethodInfo getObjData = typeof(FormatterServices).GetMethod("GetObjectData",
-                                       new Type[] { typeof(object), typeof(MemberInfo[]) });
-
-                               codebuilder.AddStatement( new AssignStatement( members_ref,
-                                       new MethodInvocationExpression( null, getSerMembers,
-                                       new TypeTokenExpression( targetType ) )) );
-
-                               codebuilder.AddStatement( new AssignStatement( data_ref,
-                                       new MethodInvocationExpression( null, getObjData,
-                                       SelfReference.Self.ToExpression(), members_ref.ToExpression() )) );
-
-                               codebuilder.AddStatement( new ExpressionStatement(
-                                       new MethodInvocationExpression(arg1, addValueMethod,
-                                       new ConstReference("__data").ToExpression(),
-                                       data_ref.ToExpression() ) ) );
-                       }
                }
        }
}
Index: Castle.DynamicProxy/Generators/InterfaceProxyWithTargetGenerator.cs
===================================================================
--- Castle.DynamicProxy/Generators/InterfaceProxyWithTargetGenerator.cs (revision 3735)
+++ Castle.DynamicProxy/Generators/InterfaceProxyWithTargetGenerator.cs (working copy)
@@ -13,6 +13,7 @@
// limitations under the License.

using System.Collections.Generic;
+using Castle.DynamicProxy.Serialization ;

namespace Castle.DynamicProxy.Generators
{
@@ -571,27 +572,9 @@
                protected override void CustomizeGetObjectData(AbstractCodeBuilder codebuilder, ArgumentReference arg1,
                                                                                                           ArgumentReference arg2)
                {
-                       Type[] key_and_object = new Type[] { typeof(String), typeof(Object) };
-                       Type[] key_and_int = new Type[] { typeof(String), typeof(int) };
-                       Type[] key_and_string = new Type[] { typeof(String), typeof(string) };
-                       MethodInfo addValueMethod = typeof(SerializationInfo).GetMethod("AddValue", key_and_object);
-                       MethodInfo addIntMethod = typeof(SerializationInfo).GetMethod("AddValue", key_and_int);
-                       MethodInfo addStringMethod = typeof(SerializationInfo).GetMethod("AddValue", key_and_string);
-
-                       codebuilder.AddStatement(new ExpressionStatement(
-                                                                               new MethodInvocationExpression(arg1, addValueMethod,
-                                                                                                                                                         new ConstReference("__target").ToExpression(),
-                                                                                                                                                         targetField.ToExpression())));
-
-                       codebuilder.AddStatement(new ExpressionStatement(
-                                                                       new MethodInvocationExpression(arg1, addIntMethod,
-                                                                                                                                                 new ConstReference("__interface_generator_type").ToExpression(),
-                                                                                                                                                 new ConstReference((int)GeneratorType).ToExpression())));
-
-                               codebuilder.AddStatement(new ExpressionStatement(
-                                                                       new MethodInvocationExpression(arg1, addStringMethod,
-                                                                                                                                                 new ConstReference("__theInterface").ToExpression(),
-                                                                                                                                                 new ConstReference(targetType.AssemblyQualifiedName).ToExpression())));
+      codebuilder.AddStatement (new ExpressionStatement (new MethodInvocationExpression (null,
+        typeof (ProxySerializer).GetMethod ("SerializeInterfaceProxyData"), arg1.ToExpression(), targetField.ToExpression (),
+        new ConstReference((int)GeneratorType).ToExpression(), new ConstReference(targetType.AssemblyQualifiedName).ToExpression())));
                }

                protected virtual InterfaceGeneratorType GeneratorType
Index: Castle.DynamicProxy/Serialization/ProxyObjectReference.cs
===================================================================
--- Castle.DynamicProxy/Serialization/ProxyObjectReference.cs   (revision 3735)
+++ Castle.DynamicProxy/Serialization/ProxyObjectReference.cs   (working copy)
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-//     http://www.apache.org/licenses/LICENSE-2.0
+//              http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -27,14 +27,14 @@
        /// Handles the deserialization of proxies.
        /// </summary>
        [Serializable]
-       public class ProxyObjectReference : IObjectReference, ISerializable
+       public class ProxyObjectReference : IObjectReference, ISerializable, IDeserializationCallback
        {
                private static ModuleScope _scope = new ModuleScope();

                private Type _baseType;
                private Type[] _interfaces;
                private IInterceptor[] _interceptors;
-               private object[] _data;
+               private ProxySerializer.Indirection _data;
                private object _proxy;

                /// <summary>
@@ -47,7 +47,7 @@

                protected ProxyObjectReference(SerializationInfo info, StreamingContext context)
                {
-                       _interceptors = (IInterceptor[])info.GetValue("__interceptors", typeof(IInterceptor[]));
+                       _interceptors = (IInterceptor[])info.GetValue("__interceptors", typeof (IInterceptor[]));
                        _baseType = (Type)info.GetValue("__baseType", typeof(Type));

                        String[] _interfaceNames = (String[])info.GetValue("__interfaces", typeof(String[]));
@@ -60,8 +60,6 @@
                        }

                        _proxy = RecreateProxy(info, context);
-
-                       InvokeCallback(_proxy);
                }

                protected virtual object RecreateProxy(SerializationInfo info, StreamingContext context)
@@ -88,13 +86,13 @@
                        switch (generatorType)
                        {
                                case InterfaceGeneratorType.WithTarget:
-                                       generator = new  InterfaceProxyWithTargetGenerator(_scope,theInterface);
+                                       generator = new InterfaceProxyWithTargetGenerator(_scope,theInterface);
                                        break;
                                case InterfaceGeneratorType.WithoutTarget :
                                        generator = new InterfaceProxyGeneratorWithoutTarget(_scope, theInterface);
                                        break;
                                case InterfaceGeneratorType.WithTargetInterface :
-                                       generator = new InterfaceProxyWithTargetInterfaceGenerator(_scope,  theInterface);
+                                       generator = new InterfaceProxyWithTargetInterfaceGenerator(_scope,      theInterface);
                                        break;
                                default:
                                        throw new InvalidOperationException(string.Format("Got value {0} for the interface generator type, which is not known for the purpose of serialization.", generatorType));
@@ -111,11 +109,6 @@
                {
                        bool delegateBaseSer = info.GetBoolean("__delegateToBase");

-                       if (!delegateBaseSer)
-                       {
-                               _data = (object[])info.GetValue("__data", typeof(object[]));
-                       }
-
                        object proxy = null;

                        ClassProxyGenerator cpGen = new ClassProxyGenerator(_scope, _baseType);
@@ -130,17 +123,15 @@
                        else
                        {
                                proxy = FormatterServices.GetSafeUninitializedObject(proxy_type);
+                               _data = ( ProxySerializer.Indirection) info.GetValue("__data", typeof (ProxySerializer.Indirection));

                                SetInterceptor(proxy, proxy_type);
-
-                               MemberInfo[] members = FormatterServices.GetSerializableMembers(_baseType);
-                               FormatterServices.PopulateObjectMembers(proxy, members, _data);
                        }

                        return proxy;
                }

-               private void SetInterceptor(object proxy, Type proxy_type)
+               private void SetInterceptor (object proxy, Type proxy_type)
                {
                        FieldInfo interceptorField = proxy_type.GetField("__interceptors");

@@ -171,5 +162,20 @@
                        // There is no need to implement this method as
                        // this class would never be serialized.
                }
+
+    // Class proxies must be populated in this method, since only at this point all members held by _data.IndirectedObject
+    // have been fixed up.
+               public void OnDeserialization (object sender)
+               {
+                       if (_data != null)
+                       {
+                               object[] objectData = (object[]) _data.IndirectedObject;
+
+                               MemberInfo[] members = FormatterServices.GetSerializableMembers (_baseType);
+                               FormatterServices.PopulateObjectMembers (_proxy, members, objectData);
+                               _data = null;
+      }
+      InvokeCallback (_proxy);
+    }
        }
}
Index: Castle.DynamicProxy/Serialization/ProxySerializer.cs
===================================================================
--- Castle.DynamicProxy/Serialization/ProxySerializer.cs        (revision 0)
+++ Castle.DynamicProxy/Serialization/ProxySerializer.cs        (revision 0)
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic ;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Text;
+using Castle.Core.Interceptor;
+using System.IO;
+using System.Runtime.Serialization.Formatters.Binary;
+
+namespace Castle.DynamicProxy.Serialization
+{
+       /// <summary>
+       /// Assists in serializing instances of the generated proxy types so that they can be deserialized via <see cref="ProxyObjectReference"/>.
+       /// </summary>
+       public class ProxySerializer
+       {
+               public static BinaryFormatter Formatter = new BinaryFormatter();
+
+               public static void SerializeBaseProxyData (SerializationInfo info, object proxy, IInterceptor[] interceptors,
+                               string[] interfaceNames, Type baseType)
+               {
+                       info.SetType (typeof (ProxyObjectReference));
+
+                       info.AddValue ("__interceptors", interceptors);
+                       info.AddValue ("__interfaces", interfaceNames);
+                       info.AddValue ("__baseType", baseType);
+               }
+
+
+               public static void SerializeInterfaceProxyData (SerializationInfo info, object target, int interfaceGeneratorType, string interfaceName)
+               {
+                       info.AddValue ("__target", target);
+                       info.AddValue ("__interface_generator_type", interfaceGeneratorType);
+                       info.AddValue ("__theInterface", interfaceName);
+               }
+
+               public static void SerializeClassProxyData (SerializationInfo info, bool delegateToBase, Type targetType, object proxy)
+               {
+                       info.AddValue ("__delegateToBase", delegateToBase);
+
+                       if (!delegateToBase)
+                       {
+                               MemberInfo[] members = FormatterServices.GetSerializableMembers (targetType);
+
+                               Indirection memberValues = new Indirection(FormatterServices.GetObjectData (proxy, members));
+                               // SubstitutIndirections (memberValues, proxy);
+                               info.AddValue ("__data", memberValues);
+                       }
+               }
+
+    /// <summary>
+    /// Used to circumvent a serialization bug, where direct self references and directly held delegates are not deserialized correctly.
+    /// </summary>
+               [Serializable]
+               public class Indirection
+               {
+                       public readonly object IndirectedObject;
+
+                       public Indirection (object indirectedObject)
+                       {
+                               IndirectedObject = indirectedObject;
+                       }
+               }
+       }
+}


Fabian Schmied

unread,
Apr 26, 2007, 8:29:43 AM4/26/07
to castle-pro...@googlegroups.com
> The patch ended up as text again, can you try again?

Hm, sorry, I've zipped it up. It's strange, because Gmail here shows
it as an ordinary file attachment.

Fabian

DP-56.patch.zip

Hamilton Verissimo

unread,
Jun 14, 2007, 7:09:31 PM6/14/07
to castle-pro...@googlegroups.com
I assume this is on the repository already. Am I right?

Thanks

On 4/26/07, Fabian Schmied <fabian....@gmail.com> wrote:

Fabian Schmied

unread,
Jun 15, 2007, 3:31:24 AM6/15/07
to castle-pro...@googlegroups.com
> I assume this is on the repository already. Am I right?

Nope (not in the current revision, that is), but I've already started
to work on it.

Fabian

Fabian Schmied

unread,
Jun 15, 2007, 4:08:23 AM6/15/07
to castle-pro...@googlegroups.com
> > I assume this is on the repository already. Am I right?
>
> Nope (not in the current revision, that is), but I've already started
> to work on it.

As a beginning, I've re-added and committed the test cases for DP-53,
DP-56, and DP-59. They are currently ignored, but I'm planning to get
at least some of them working later today.

Fabian

Fabian Schmied

unread,
Jun 15, 2007, 12:54:33 PM6/15/07
to castle-pro...@googlegroups.com
> As a beginning, I've re-added and committed the test cases for DP-53,
> DP-56, and DP-59. They are currently ignored, but I'm planning to get
> at least some of them working later today.

Re-done DP-53 and DP-56. DP-59 has to wait.

Fabian

Fabian Schmied

unread,
Jun 25, 2007, 5:59:18 AM6/25/07
to castle-pro...@googlegroups.com
I've now fixed the problem reported in DP-59 again as well,
serialization should now be up-to-date again, I think.

Fabian

Reply all
Reply to author
Forward
0 new messages