User: xtoff
Date: 2009/11/15 08:48 AM

Added:
 /DynamicProxy/trunk/src/Castle.DynamicProxy.Tests/
  InvocationTypesCachingTestCase.cs

Modified:
 /DynamicProxy/trunk/src/Castle.DynamicProxy.Tests/
  Castle.DynamicProxy.Tests-vs2008.csproj
 /DynamicProxy/trunk/src/Castle.DynamicProxy/Contributors/
  InterfaceProxyTargetContributor.cs, 
InterfaceProxyWithoutTargetContributor.cs, MixinContributor.cs
 /DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/
  CacheKey.cs, INamingScope.cs, InterfaceInvocationTypeGenerator.cs, 
InvocationTypeGenerator.cs, NamingScope.cs
 /DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/Emitters/
  ClassEmitter.cs

Log:
 - Fixed DYNPROXY-ISSUE-105 - Invocation types should be cached and shared 
among proxy implementations.

File Changes:

Directory: /DynamicProxy/trunk/src/Castle.DynamicProxy.Tests/
=============================================================

File [modified]: Castle.DynamicProxy.Tests-vs2008.csproj
Delta lines: +60 -0
===================================================================

--- 
DynamicProxy/trunk/src/Castle.DynamicProxy.Tests/InvocationTypesCachingTestCase.cs
                          (rev 0)
+++ 
DynamicProxy/trunk/src/Castle.DynamicProxy.Tests/InvocationTypesCachingTestCase.cs
  2009-11-15 15:48:03 UTC (rev 6332)
@@ -0,0 +1,60 @@
+// Copyright 2004-2009 Castle Project - http://www.castleproject.org/
+// 
+// Licensed under the Apache License, Version 2.0 (the "License");
+// 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
+// 
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace Castle.DynamicProxy.Tests
+{
+       using Castle.Core.Interceptor;
+       using Castle.DynamicProxy.Tests.Interceptors;
+       using Castle.DynamicProxy.Tests.Interfaces;
+
+       using NUnit.Framework;
+
+       [TestFixture]
+       public class InvocationTypesCachingTestCase:BasePEVerifyTestCase
+       {
+               [Test]
+               public void Should_share_invocations_for_interface_methods()
+               {
+                       var interceptor1 = new KeepDataInterceptor();
+                       var interceptor2 = new KeepDataInterceptor();
+                       var first = 
generator.CreateInterfaceProxyWithTarget<IOne>(new One(), interceptor1);
+                       var second = 
generator.CreateInterfaceProxyWithTarget<IOne>(new OneTwo(), interceptor2);
+
+                       Assert.AreNotEqual(first.GetType(), second.GetType(), 
"proxy types are different");
+
+                       first.OneMethod();
+                       second.OneMethod();
+
+                       Assert.AreEqual(interceptor1.Invocation.GetType(), 
interceptor2.Invocation.GetType());
+               }
+
+               [Test]
+               public void 
Should_not_share_invocations_for_interface_methods_when_one_is_IChangeProxyTarget()
+               {
+                       var interceptor1 = new KeepDataInterceptor();
+                       var interceptor2 = new KeepDataInterceptor();
+                       var first = 
generator.CreateInterfaceProxyWithTarget<IOne>(new One(), interceptor1);
+                       var second = 
generator.CreateInterfaceProxyWithTargetInterface<IOne>(new OneTwo(), 
interceptor2);
+
+                       Assert.AreNotEqual(first.GetType(), second.GetType(), 
"proxy types are different");
+
+                       first.OneMethod();
+                       second.OneMethod();
+
+                       
Assert.IsNotInstanceOf<IChangeProxyTarget>(interceptor1.Invocation);
+                       
Assert.IsInstanceOf<IChangeProxyTarget>(interceptor2.Invocation);
+                       Assert.AreNotEqual(interceptor1.Invocation.GetType(), 
interceptor2.Invocation.GetType());
+               }
+       }
+}

File [added]: InvocationTypesCachingTestCase.cs
Delta lines: +0 -0
===================================================================

Directory: /DynamicProxy/trunk/src/Castle.DynamicProxy/Contributors/
====================================================================

File [modified]: InterfaceProxyTargetContributor.cs
Delta lines: +30 -10
===================================================================

--- 
DynamicProxy/trunk/src/Castle.DynamicProxy/Contributors/InterfaceProxyWithoutTargetContributor.cs
   2009-11-15 12:24:41 UTC (rev 6331)
+++ 
DynamicProxy/trunk/src/Castle.DynamicProxy/Contributors/InterfaceProxyWithoutTargetContributor.cs
   2009-11-15 15:48:03 UTC (rev 6332)
@@ -103,21 +103,17 @@
                        MethodGenerator generator;
                        if (method.Proxyable)
                        {
-                               var invocation = new 
InterfaceInvocationTypeGenerator(method.Method.DeclaringType,
-                                                                               
      method,
-                                                                               
      method.MethodOnTarget,
-                                                                               
      false)
-                                       .Generate(@class, options, 
namingScope).BuildType();
+                               var invocation = GetInvocationType(method, 
@class, options);
 
                                generator = new InterfaceMethodGenerator(method,
-                                                                        
invocation,
-                                                                        
@class.GetField("__interceptors"),
-                                                                        
createMethod,
-                                                                        
getTargetExpressionDelegate);
+                                                                               
                                 invocation,
+                                                                               
                                 @class.GetField("__interceptors"),
+                                                                               
                                 createMethod,
+                                                                               
                                 getTargetExpressionDelegate);
                        }
                        else
                        {
-                               generator= new 
MinimialisticMethodGenerator(method, createMethod, 
GeneratorUtil.ObtainInterfaceMethodAttributes);
+                               generator = new 
MinimialisticMethodGenerator(method, createMethod, 
GeneratorUtil.ObtainInterfaceMethodAttributes);
                        }
 
                        var proxiedMethod = generator.Generate(@class, options, 
namingScope);
@@ -126,5 +122,29 @@
                                proxiedMethod.DefineCustomAttribute(attribute);
                        }
                }
+
+               private Type GetInvocationType(MethodToGenerate method, 
ClassEmitter emitter, ProxyGenerationOptions options)
+               {
+                       var scope = emitter.ModuleScope;
+                       var key = new CacheKey(method.Method, null, null);
+
+                       // no locking required as we're already within a lock
+
+                       var invocation = scope.GetFromCache(key);
+                       if (invocation != null)
+                       {
+                               return invocation;
+                       }
+
+                       invocation = new 
InterfaceInvocationTypeGenerator(method.Method.DeclaringType,
+                                                                               
                                          method,
+                                                                               
                                          method.Method,
+                                                                               
                                          false)
+                               .Generate(emitter, options, 
namingScope).BuildType();
+
+                       scope.RegisterInCache(key, invocation);
+
+                       return invocation;
+               }
        }
 }

File [modified]: InterfaceProxyWithoutTargetContributor.cs
Delta lines: +31 -5
===================================================================

--- DynamicProxy/trunk/src/Castle.DynamicProxy/Contributors/MixinContributor.cs 
2009-11-15 12:24:41 UTC (rev 6331)
+++ DynamicProxy/trunk/src/Castle.DynamicProxy/Contributors/MixinContributor.cs 
2009-11-15 15:48:03 UTC (rev 6332)
@@ -17,6 +17,7 @@
        using System;
        using System.Diagnostics;
 
+       using Castle.Core.Interceptor;
        using Castle.DynamicProxy.Generators;
        using Castle.DynamicProxy.Generators.Emitters;
 
@@ -116,11 +117,7 @@
                        MethodGenerator generator;
                        if (method.Proxyable)
                        {
-                               var invocation = new 
InterfaceInvocationTypeGenerator(method.Method.DeclaringType,
-                                                                            
method,
-                                                                            
method.Method,
-                                                                            
canChangeTarget)
-                                       .Generate(emitter, options, 
namingScope).BuildType();
+                               var invocation = GetInvocationType(method, 
emitter, options);
 
                                var interceptors = 
emitter.GetField("__interceptors");
 
@@ -143,6 +140,35 @@
                        }
                }
 
+               private Type GetInvocationType(MethodToGenerate method, 
ClassEmitter emitter, ProxyGenerationOptions options)
+               {
+                       var scope = emitter.ModuleScope;
+                       Type[] interfaces = Type.EmptyTypes;
+                       if (canChangeTarget)
+                       {
+                               interfaces = new[] { typeof(IChangeProxyTarget) 
};
+                       }
+                       var key = new CacheKey(method.Method, interfaces, null);
+
+                       // no locking required as we're already within a lock
+
+                       var invocation = scope.GetFromCache(key);
+                       if(invocation!=null)
+                       {
+                               return invocation;
+                       }
+
+                       invocation = new 
InterfaceInvocationTypeGenerator(method.Method.DeclaringType,
+                                                                         
method,
+                                                                         
method.Method,
+                                                                         
canChangeTarget)
+                               .Generate(emitter, options, 
namingScope).BuildType();
+
+                       scope.RegisterInCache(key, invocation);
+
+                       return invocation;
+               }
+
                public void SetGetTargetExpression(GetTargetExpressionDelegate 
getTarget)
                {

File [modified]: MixinContributor.cs
Delta lines: +11 -12
===================================================================

--- DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/CacheKey.cs   
2009-11-15 12:24:41 UTC (rev 6331)
+++ DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/CacheKey.cs   
2009-11-15 15:48:03 UTC (rev 6332)
@@ -15,6 +15,7 @@
 namespace Castle.DynamicProxy.Generators
 {
        using System;
+       using System.Reflection;
 
 #if SILVERLIGHT
 #else
@@ -22,7 +23,7 @@
 #endif
        public class CacheKey
        {
-               private readonly Type targetType;
+               private readonly MemberInfo target;
                private readonly Type[] interfaces;
                private readonly ProxyGenerationOptions options;
                private readonly Type proxyForType;
@@ -30,12 +31,12 @@
                /// <summary>
                /// Initializes a new instance of the <see cref="CacheKey"/> 
class.
                /// </summary>
-               /// <param name="targetType">Type of the target.</param>
+               /// <param name="target">Type of the target.</param>
                /// <param name="interfaces">The interfaces.</param>
                /// <param name="options">The options.</param>
-               public CacheKey(Type targetType, Type[] interfaces, 
ProxyGenerationOptions options)
+               public CacheKey(MemberInfo target, Type[] interfaces, 
ProxyGenerationOptions options)
                {
-                       this.targetType = targetType;
+                       this.target = target;
                        this.interfaces = interfaces ?? Type.EmptyTypes;
                        this.options = options;
                }
@@ -48,15 +49,13 @@
 
                public override int GetHashCode()
                {
-                       int result = targetType.GetHashCode();
-                       if (interfaces != null)
+                       int result = target.GetHashCode();
+                       foreach (Type inter in interfaces)
                        {
-                               foreach (Type inter in interfaces)
-                               {
-                                       result += 29 + inter.GetHashCode();
-                               }
+                               result += 29 + inter.GetHashCode();
                        }
-                       result = 29*result + options.GetHashCode();
+                       if (options != null)
+                               result = 29*result + options.GetHashCode();
                        if (proxyForType != null)
                                result = 29*result + proxyForType.GetHashCode();
                        return result;
@@ -70,7 +69,7 @@
                        if (cacheKey == null) return false;
 
                        if (!Equals(proxyForType, cacheKey.proxyForType)) 
return false;
-                       if (!Equals(targetType, cacheKey.targetType)) return 
false;
+                       if (!Equals(target, cacheKey.target)) return false;
                        if (interfaces.Length != cacheKey.interfaces.Length) 
return false;
                        for (int i = 0; i < interfaces.Length; i++)

Directory: /DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/Emitters/
===========================================================================

File [modified]: ClassEmitter.cs
Delta lines: +2 -0
===================================================================

--- DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/INamingScope.cs       
2009-11-15 12:24:41 UTC (rev 6331)
+++ DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/INamingScope.cs       
2009-11-15 15:48:03 UTC (rev 6332)
@@ -39,5 +39,7 @@
                /// </summary>
                /// <returns>New naming scope.</returns>
                INamingScope SafeSubScope();
+
+               INamingScope ParentScope { get; }
        }
 }

Directory: /DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/
==================================================================

File [modified]: CacheKey.cs
Delta lines: +1 -1
===================================================================

--- 
DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/Emitters/ClassEmitter.cs  
    2009-11-15 12:24:41 UTC (rev 6331)
+++ 
DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/Emitters/ClassEmitter.cs  
    2009-11-15 15:48:03 UTC (rev 6332)
@@ -21,7 +21,7 @@
 
        public class ClassEmitter : AbstractTypeEmitter
        {
-               private ModuleScope moduleScope;
+               private readonly ModuleScope moduleScope;
                private const TypeAttributes DefaultAttributes = 
TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Serializable;
 

File [modified]: INamingScope.cs
Delta lines: +4 -5
===================================================================

--- 
DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/InterfaceInvocationTypeGenerator.cs
   2009-11-15 12:24:41 UTC (rev 6331)
+++ 
DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/InterfaceInvocationTypeGenerator.cs
   2009-11-15 15:48:03 UTC (rev 6332)
@@ -29,11 +29,10 @@
 
                protected override AbstractTypeEmitter GetEmitter(ClassEmitter 
@class, Type[] interfaces, INamingScope namingScope, MethodInfo methodInfo)
                {
-                       return new ClassEmitter(@class.ModuleScope,
-                                               namingScope.GetUniqueName(
-                                                       
string.Format("Castle.Invocations.{0}_{1}", methodInfo.DeclaringType.Name, 
methodInfo.Name)),
-                                               typeof(AbstractInvocation), 
interfaces);
-                       
+                       var suggestedName = 
string.Format("Castle.Proxies.Invocations.{0}_{1}", 
methodInfo.DeclaringType.Name,
+                                                         methodInfo.Name);
+                       var uniqueName = 
namingScope.ParentScope.GetUniqueName(suggestedName);
+                       return new ClassEmitter(@class.ModuleScope, uniqueName, 
typeof(AbstractInvocation), interfaces);
                }
 

File [modified]: InterfaceInvocationTypeGenerator.cs
Delta lines: +3 -3
===================================================================

--- 
DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/InvocationTypeGenerator.cs
    2009-11-15 12:24:41 UTC (rev 6331)
+++ 
DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/InvocationTypeGenerator.cs
    2009-11-15 15:48:03 UTC (rev 6332)
@@ -89,9 +89,9 @@
                protected virtual AbstractTypeEmitter GetEmitter(ClassEmitter 
@class, Type[] interfaces, INamingScope namingScope, MethodInfo methodInfo)
                {
                        return new NestedClassEmitter(@class,
-                                                     
namingScope.GetUniqueName("Invocation_" + methodInfo.Name),
-                                                     
typeof(AbstractInvocation),
-                                                     interfaces);
+                                                                               
  namingScope.GetUniqueName("Invocation_" + methodInfo.Name),
+                                                                               
  typeof(AbstractInvocation),
+                                                                               
  interfaces);
                }
 

File [modified]: InvocationTypeGenerator.cs
Delta lines: +16 -1
===================================================================

--- DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/NamingScope.cs        
2009-11-15 12:24:41 UTC (rev 6331)
+++ DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/NamingScope.cs        
2009-11-15 15:48:03 UTC (rev 6332)
@@ -20,7 +20,17 @@
        public class NamingScope : INamingScope
        {
                private readonly IDictionary<string,int> names = new 
Dictionary<string, int>();
+               private readonly INamingScope parentScope;
 
+               public NamingScope()
+               {
+               }
+
+               private NamingScope(INamingScope parent)
+               {
+                       parentScope = parent;
+               }
+
                public string GetUniqueName(string suggestedName)
                {
                        Debug.Assert(string.IsNullOrEmpty(suggestedName) == 
false,
@@ -40,7 +50,12 @@
 
                public INamingScope SafeSubScope()
                {
-                       return new NamingScope();
+                       return new NamingScope(this);
                }
+
+               public INamingScope ParentScope
+               {
+                       get { return parentScope;}
+               }
        }
 }

File [modified]: NamingScope.cs
Delta lines: +1 -0
===================================================================

--- 
DynamicProxy/trunk/src/Castle.DynamicProxy.Tests/Castle.DynamicProxy.Tests-vs2008.csproj
    2009-11-15 12:24:41 UTC (rev 6331)
+++ 
DynamicProxy/trunk/src/Castle.DynamicProxy.Tests/Castle.DynamicProxy.Tests-vs2008.csproj
    2009-11-15 15:48:03 UTC (rev 6332)
@@ -185,6 +185,7 @@
     <Compile Include="Interfaces\OneTwo.cs" />
     <Compile Include="Interfaces\Two.cs" />
     <Compile Include="InvocationMethodInvocationTargetTestCase.cs" />
+    <Compile Include="InvocationTypesCachingTestCase.cs" />
     <Compile Include="MethodEquivalenceTestCase.cs" />
     <Compile Include="MixinDataTestCase.cs" />

--

You received this message because you are subscribed to the Google Groups 
"Castle Project Commits" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/castle-project-commits?hl=.


Reply via email to