From 17dd6c14b76df62aef51a541482f93bf3eed26b4 Mon Sep 17 00:00:00 2001 From: Blair Conrad Date: Wed, 15 Jan 2014 21:59:20 -0500 Subject: [PATCH] #42 - extracted BuildInternalsVisibleMessageForType to ExceptionMessageBuilder. Simplified logic in the latter and introduced Type.GetBestName() to help. --- .../BasicClassProxyTestCase.cs | 4 +- .../BasicInterfaceProxyTestCase.cs | 4 +- .../ClassProxyConstructorsTestCase.cs | 4 +- .../ClassProxyWithTargetTestCase.cs | 4 +- .../InterceptorSelectorTestCase.cs | 4 +- ...terfaceProxyWithTargetInterfaceTestCase.cs | 4 +- src/Castle.Core/Castle.Core.csproj | 4 +- .../Core/Internal/ExceptionMessageBuilder .cs | 94 +++++++++++++++++++ .../Core/Internal/TypeExtensions.cs | 36 +++++++ .../DynamicProxy/DefaultProxyBuilder.cs | 75 +-------------- .../DynamicProxy/ProxyGenerator.cs | 4 +- 11 files changed, 150 insertions(+), 87 deletions(-) create mode 100644 src/Castle.Core/Core/Internal/ExceptionMessageBuilder .cs create mode 100644 src/Castle.Core/Core/Internal/TypeExtensions.cs diff --git a/src/Castle.Core.Tests/BasicClassProxyTestCase.cs b/src/Castle.Core.Tests/BasicClassProxyTestCase.cs index c2cc40d67c..2f5a2b2ff3 100644 --- a/src/Castle.Core.Tests/BasicClassProxyTestCase.cs +++ b/src/Castle.Core.Tests/BasicClassProxyTestCase.cs @@ -1,4 +1,4 @@ -// Copyright 2004-2013 Castle Project - http://www.castleproject.org/ +// Copyright 2004-2014 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. @@ -87,7 +87,7 @@ public void ProxyForNonPublicClass() var type = Type.GetType("System.AppDomainInitializerInfo, mscorlib"); var exception = Assert.Throws(() => generator.CreateClassProxy(type, new StandardInterceptor())); Assert.AreEqual( - "Can not create proxy for type System.AppDomainInitializerInfo because it is not accessible. Make the type public, or internal and mark your assembly with [assembly: InternalsVisibleTo(\"DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7\")] attribute, because assembly mscorlib is strong-named.", + "Can not create proxy for type System.AppDomainInitializerInfo because it is not accessible. Make it public, or internal and mark your assembly with [assembly: InternalsVisibleTo(\"DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7\")] attribute, because assembly mscorlib is strong-named.", exception.Message); } #endif diff --git a/src/Castle.Core.Tests/BasicInterfaceProxyTestCase.cs b/src/Castle.Core.Tests/BasicInterfaceProxyTestCase.cs index 710941d67b..e14a83d7dc 100644 --- a/src/Castle.Core.Tests/BasicInterfaceProxyTestCase.cs +++ b/src/Castle.Core.Tests/BasicInterfaceProxyTestCase.cs @@ -1,4 +1,4 @@ -// Copyright 2004-2013 Castle Project - http://www.castleproject.org/ +// Copyright 2004-2014 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. @@ -239,7 +239,7 @@ public void Cannot_proxy_generic_type_with_open_generic_type_parameter() public void Cannot_proxy_inaccessible_interface() { var exception = Assert.Throws(() => generator.CreateInterfaceProxyWithoutTarget(typeof(PrivateInterface), new IInterceptor[0])); - Assert.That(exception.Message, Is.StringStarting("Can not create proxy for type Castle.DynamicProxy.Tests.BasicInterfaceProxyTestCase+PrivateInterface because it is not accessible. Make the type public, or internal")); + Assert.That(exception.Message, Is.StringStarting("Can not create proxy for type Castle.DynamicProxy.Tests.BasicInterfaceProxyTestCase+PrivateInterface because it is not accessible. Make it public, or internal")); } [Test] diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/ClassProxyConstructorsTestCase.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/ClassProxyConstructorsTestCase.cs index 50f4c50e55..a1c0171e11 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/ClassProxyConstructorsTestCase.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/ClassProxyConstructorsTestCase.cs @@ -1,4 +1,4 @@ -// Copyright 2004-2013 Castle Project - http://www.castleproject.org/ +// Copyright 2004-2014 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. @@ -118,7 +118,7 @@ public void Cannot_proxy_generic_type_with_open_generic_type_parameter() public void Cannot_proxy_inaccessible_class() { var exception = Assert.Throws(() => generator.CreateClassProxy(typeof(PrivateClass), new IInterceptor[0])); - Assert.That(exception.Message, Is.StringStarting("Can not create proxy for type Castle.DynamicProxy.Tests.ClassProxyConstructorsTestCase+PrivateClass because it is not accessible. Make the type public, or internal")); + Assert.That(exception.Message, Is.StringStarting("Can not create proxy for type Castle.DynamicProxy.Tests.ClassProxyConstructorsTestCase+PrivateClass because it is not accessible. Make it public, or internal")); } [Test] diff --git a/src/Castle.Core.Tests/DynamicProxy.Tests/ClassProxyWithTargetTestCase.cs b/src/Castle.Core.Tests/DynamicProxy.Tests/ClassProxyWithTargetTestCase.cs index beb25ac960..a37e66fd5b 100644 --- a/src/Castle.Core.Tests/DynamicProxy.Tests/ClassProxyWithTargetTestCase.cs +++ b/src/Castle.Core.Tests/DynamicProxy.Tests/ClassProxyWithTargetTestCase.cs @@ -1,4 +1,4 @@ -// Copyright 2004-2013 Castle Project - http://www.castleproject.org/ +// Copyright 2004-2014 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. @@ -105,7 +105,7 @@ public void Can_proxy_purely_virtual_inherited_abstract_class() public void Cannot_proxy_inaccessible_class() { var exception = Assert.Throws(() => generator.CreateClassProxyWithTarget(new PrivateClass())); - Assert.That(exception.Message, Is.StringStarting("Can not create proxy for type Castle.DynamicProxy.Tests.ClassProxyWithTargetTestCase+PrivateClass because it is not accessible. Make the type public, or internal")); + Assert.That(exception.Message, Is.StringStarting("Can not create proxy for type Castle.DynamicProxy.Tests.ClassProxyWithTargetTestCase+PrivateClass because it is not accessible. Make it public, or internal")); } [Test] diff --git a/src/Castle.Core.Tests/InterceptorSelectorTestCase.cs b/src/Castle.Core.Tests/InterceptorSelectorTestCase.cs index b1376e69ae..347598d207 100644 --- a/src/Castle.Core.Tests/InterceptorSelectorTestCase.cs +++ b/src/Castle.Core.Tests/InterceptorSelectorTestCase.cs @@ -1,4 +1,4 @@ -// Copyright 2004-2013 Castle Project - http://www.castleproject.org/ +// Copyright 2004-2014 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. @@ -293,7 +293,7 @@ public void Can_proxy_same_type_with_and_without_selector_InterfaceProxyWithTarg public void Cannot_proxy_inaccessible_interface() { var exception = Assert.Throws(() => generator.CreateInterfaceProxyWithTarget(new PrivateClass(), new IInterceptor[0])); - Assert.That(exception.Message, Is.StringStarting("Can not create proxy for type Castle.DynamicProxy.Tests.InterceptorSelectorTestCase+PrivateInterface because it is not accessible. Make the type public, or internal")); + Assert.That(exception.Message, Is.StringStarting("Can not create proxy for type Castle.DynamicProxy.Tests.InterceptorSelectorTestCase+PrivateInterface because it is not accessible. Make it public, or internal")); } [Test] diff --git a/src/Castle.Core.Tests/InterfaceProxyWithTargetInterfaceTestCase.cs b/src/Castle.Core.Tests/InterfaceProxyWithTargetInterfaceTestCase.cs index 2fd7b3a5c3..c38639e78c 100644 --- a/src/Castle.Core.Tests/InterfaceProxyWithTargetInterfaceTestCase.cs +++ b/src/Castle.Core.Tests/InterfaceProxyWithTargetInterfaceTestCase.cs @@ -1,4 +1,4 @@ -// Copyright 2004-2013 Castle Project - http://www.castleproject.org/ +// Copyright 2004-2014 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. @@ -184,7 +184,7 @@ public void ChangeProxyTarget_should_not_affect_invocation_target() public void Cannot_proxy_inaccessible_interface() { var exception = Assert.Throws(() => generator.CreateInterfaceProxyWithTargetInterface(new PrivateClass(), new IInterceptor[0])); - Assert.That(exception.Message, Is.StringStarting("Can not create proxy for type Castle.DynamicProxy.Tests.InterfaceProxyWithTargetInterfaceTestCase+PrivateInterface because it is not accessible. Make the type public, or internal")); + Assert.That(exception.Message, Is.StringStarting("Can not create proxy for type Castle.DynamicProxy.Tests.InterfaceProxyWithTargetInterfaceTestCase+PrivateInterface because it is not accessible. Make it public, or internal")); } [Test] diff --git a/src/Castle.Core/Castle.Core.csproj b/src/Castle.Core/Castle.Core.csproj index 94d2ce81b4..25d3b23f92 100644 --- a/src/Castle.Core/Castle.Core.csproj +++ b/src/Castle.Core/Castle.Core.csproj @@ -147,7 +147,7 @@ AnyCPU prompt false - + @@ -367,7 +367,9 @@ + + diff --git a/src/Castle.Core/Core/Internal/ExceptionMessageBuilder .cs b/src/Castle.Core/Core/Internal/ExceptionMessageBuilder .cs new file mode 100644 index 0000000000..525ca314b2 --- /dev/null +++ b/src/Castle.Core/Core/Internal/ExceptionMessageBuilder .cs @@ -0,0 +1,94 @@ +// Copyright 2004-2014 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.Core.Internal +{ + using System; + using System.Linq; + using System.Reflection; + using Castle.DynamicProxy.Generators.Emitters; + + internal static class ExceptionMessageBuilder + { + /// + /// Creates a message to inform clients that a proxy couldn't be created due to reliance on an + /// inaccessible type (perhaps itself). + /// + /// the inaccessible type that prevents proxy creation + /// the type that couldn't be proxied + public static string CreateMessageForInaccessibleType(Type inaccessibleType, Type typeToProxy) + { + var targetAssembly = typeToProxy.Assembly; + + string strongNamedOrNotIndicator = " not"; // assume not strong-named + string assemblyToBeVisibleTo = "\"DynamicProxyGenAssembly2\""; // appropriate for non-strong-named + + if (targetAssembly.IsAssemblySigned()) + { + strongNamedOrNotIndicator = ""; + assemblyToBeVisibleTo = ReferencesCastleCore(targetAssembly) + ? assemblyToBeVisibleTo = "InternalsVisible.ToDynamicProxyGenAssembly2" + : assemblyToBeVisibleTo = '"' + InternalsVisible.ToDynamicProxyGenAssembly2 + '"'; + } + + string inaccessibleTypeDescription = inaccessibleType == typeToProxy + ? "it" + : "type " + inaccessibleType.GetBestName(); + + var messageFormat = + "Can not create proxy for type {0} because {1} is not accessible. " + + "Make it public, or internal and mark your assembly with " + + "[assembly: InternalsVisibleTo({2})] attribute, because assembly {3} " + + "is{4} strong-named."; + + return string.Format(messageFormat, + typeToProxy.GetBestName(), + inaccessibleTypeDescription, + assemblyToBeVisibleTo, + GetAssemblyName(targetAssembly), + strongNamedOrNotIndicator); + } + + private static string GetAssemblyName(Assembly targetAssembly) + { +#if SILVERLIGHT + // SILVERLIGHT doesn't allow us to call assembly.GetName() + var fullName = targetAssembly.FullName; + if (string.IsNullOrEmpty(fullName)) + { + return fullName; + } + var index = fullName.IndexOf(", Version=", StringComparison.OrdinalIgnoreCase); + if (index > 0) + { + return fullName.Substring(0, index); + } + return fullName; +#else + return targetAssembly.GetName().Name; +#endif + } + + private static bool ReferencesCastleCore(Assembly inspectedAssembly) + { +#if SILVERLIGHT + // no way to check that in SILVERLIGHT, so we just fall back to the solution that will definitely work + return false; +#else + return inspectedAssembly.GetReferencedAssemblies() + .Any(r => r.FullName == Assembly.GetExecutingAssembly().FullName); +#endif + } + } +} \ No newline at end of file diff --git a/src/Castle.Core/Core/Internal/TypeExtensions.cs b/src/Castle.Core/Core/Internal/TypeExtensions.cs new file mode 100644 index 0000000000..52b70fb01e --- /dev/null +++ b/src/Castle.Core/Core/Internal/TypeExtensions.cs @@ -0,0 +1,36 @@ +// Copyright 2004-2014 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.Core.Internal +{ + using System; + + internal static class TypeExtensions + { + /// + /// Find the best available name to describe a type. + /// + /// + /// Usually the best name will be , but + /// sometimes that's null (see http://msdn.microsoft.com/en-us/library/system.type.fullname%28v=vs.110%29.aspx) + /// in which case the method falls back to . + /// + /// the type to name + /// the best name + public static string GetBestName(this Type type) + { + return type.FullName ?? type.Name; + } + } +} diff --git a/src/Castle.Core/DynamicProxy/DefaultProxyBuilder.cs b/src/Castle.Core/DynamicProxy/DefaultProxyBuilder.cs index 424096d78e..3b11fdd2c4 100644 --- a/src/Castle.Core/DynamicProxy/DefaultProxyBuilder.cs +++ b/src/Castle.Core/DynamicProxy/DefaultProxyBuilder.cs @@ -1,4 +1,4 @@ -// Copyright 2004-2013 Castle Project - http://www.castleproject.org/ +// Copyright 2004-2014 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. @@ -122,11 +122,11 @@ private void AssertValidTypeForTarget(Type type, Type target) if (type.IsGenericTypeDefinition) { throw new GeneratorException(string.Format("Can not create proxy for type {0} because type {1} is an open generic type.", - target.FullName ?? target.Name, type.FullName ?? target.Name)); + target.GetBestName(), type.GetBestName())); } if (IsPublic(type) == false && IsAccessible(type) == false) { - throw new GeneratorException(BuildInternalsVisibleMessageForType(type, target)); + throw new GeneratorException(ExceptionMessageBuilder.CreateMessageForInaccessibleType(type, target)); } foreach (var typeArgument in type.GetGenericArguments()) { @@ -163,74 +163,5 @@ private static bool IsInternal(Type target) return isInternalNotNested || isNestedAndInternal; } - - private static string BuildInternalsVisibleMessageForType(Type type, Type target) - { - var targetAssembly = target.Assembly; - - string strongNamedOrNotIndicator = " not"; // assume not strong-named - string assemblyToBeVisibleTo = "\"DynamicProxyGenAssembly2\""; // appropriate for non-strong-named - - if (targetAssembly.IsAssemblySigned()) - { - strongNamedOrNotIndicator = ""; - if (ReferencesCastleCore(targetAssembly)) - { - assemblyToBeVisibleTo = "InternalsVisible.ToDynamicProxyGenAssembly2"; - } - else - { - assemblyToBeVisibleTo = '"' + InternalsVisible.ToDynamicProxyGenAssembly2 + '"'; - } - } - - var messageFormat = type == target - ? "Can not create proxy for type {0} " + - "because it is not accessible. " + - "Make the type public, or internal and mark your assembly with " + - "[assembly: InternalsVisibleTo({2})] attribute, because assembly {3} " + - "is{4} strong-named." - : "Can not create proxy for type {0} " + - "because type {1} is not accessible. " + - "Make it public, or internal and mark your assembly with " + - "[assembly: InternalsVisibleTo({2})] attribute, because assembly {3} " + - "is{4} strong-named."; - return string.Format(messageFormat, - target.FullName ?? target.Name, type.FullName ?? target.Name, assemblyToBeVisibleTo, -#if SILVERLIGHT - //SILVERLIGHT is retarded and doesn't allow us to call assembly.GetName() - GetAssemblyName(targetAssembly), -#else - targetAssembly.GetName().Name, -#endif - strongNamedOrNotIndicator); - } - -#if SILVERLIGHT - private static string GetAssemblyName(Assembly targetAssembly) - { - var fullName = targetAssembly.FullName; - if (string.IsNullOrEmpty(fullName)) - { - return fullName; - } - var index = fullName.IndexOf(", Version=", StringComparison.OrdinalIgnoreCase); - if (index > 0) - { - return fullName.Substring(0, index); - } - return fullName; - } -#endif - private static bool ReferencesCastleCore(Assembly inspectedAssembly) - { -#if SILVERLIGHT - // no way to check that in SILVELIGHT, so we just fall back to the solution that will definitely work - return false; -#else - return inspectedAssembly.GetReferencedAssemblies() - .Any(r => r.FullName == Assembly.GetExecutingAssembly().FullName); -#endif - } } } \ No newline at end of file diff --git a/src/Castle.Core/DynamicProxy/ProxyGenerator.cs b/src/Castle.Core/DynamicProxy/ProxyGenerator.cs index 28ecdf9bcc..7bd1fb1b7f 100644 --- a/src/Castle.Core/DynamicProxy/ProxyGenerator.cs +++ b/src/Castle.Core/DynamicProxy/ProxyGenerator.cs @@ -1,4 +1,4 @@ -// Copyright 2004-2013 Castle Project - http://www.castleproject.org/ +// Copyright 2004-2014 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. @@ -1487,7 +1487,7 @@ protected void CheckNotGenericTypeDefinition(Type type, string argumentName) if (type != null && type.IsGenericTypeDefinition) { throw new GeneratorException(string.Format("Can not create proxy for type {0} because it is an open generic type.", - type.FullName ?? type.Name)); + type.GetBestName())); } }