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
j.
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
It's my opinion that you'll get better feedback with a failing test
case and a patch.
;)
j.
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
On 4/25/07, Fabian Schmied <fabian....@gmail.com> wrote:
>
--
Cheers,
hamilton verissimo
ham...@castlestronghold.com
http://www.castlestronghold.com/
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
Which is not caused by my code, I should have added :)
Fabian
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.
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
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;
+ }
+ }
+ }
+}
Thanks
On 4/26/07, Fabian Schmied <fabian....@gmail.com> wrote:
Nope (not in the current revision, that is), but I've already started
to work on it.
Fabian
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
Re-done DP-53 and DP-56. DP-59 has to wait.
Fabian
Fabian