diff options
author | Atsushi Kanamori <AtsushiKan@users.noreply.github.com> | 2016-09-20 21:14:53 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-09-20 21:14:53 +0300 |
commit | a060c564d4a99b239deb445a49fa0f8d37b3c477 (patch) | |
tree | b90397a0a779a5718767d8238b10220e3f8b73ca | |
parent | 4727f4e72b32236359f97db1bef6fe8f8e591798 (diff) | |
parent | eed79980b5a07a915cbc19743ca3c024204eada4 (diff) |
Merge pull request #1884 from AtsushiKan/ai
Port Activate.CreateInstance() overloads
13 files changed, 222 insertions, 89 deletions
diff --git a/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs b/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs index ba4317a18..5221f9181 100644 --- a/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs +++ b/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs @@ -20,6 +20,7 @@ using System; using System.Reflection; using System.Diagnostics; +using System.Globalization; using RhCorElementType = System.Runtime.RuntimeImports.RhCorElementType; @@ -128,5 +129,8 @@ namespace Internal.Reflection.Augments public abstract PropertyInfo GetImplicitlyOverriddenBaseClassProperty(PropertyInfo p); public abstract Binder CreateDefaultBinder(); + + public abstract object ActivatorCreateInstance(Type type, bool nonPublic); + public abstract object ActivatorCreateInstance(Type type, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes); } } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs index d70da2314..2cce3c48b 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs @@ -32,7 +32,6 @@ namespace Internal.Runtime.Augments public abstract void RegisterModule(IntPtr moduleHandle); // Api's that are exposed in System.Runtime but are really reflection apis. - public abstract Object ActivatorCreateInstance(Type type, Object[] args); public abstract Type GetType(string typeName, Func<AssemblyName, Assembly> assemblyResolver, Func<Assembly, string, bool, Type> typeResolver, bool throwOnError, bool ignoreCase); public abstract IntPtr TryGetDefaultConstructorForType(RuntimeTypeHandle runtimeTypeHandle); diff --git a/src/System.Private.CoreLib/src/Resources/Strings.resx b/src/System.Private.CoreLib/src/Resources/Strings.resx index ab376425d..f9f9c4418 100644 --- a/src/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/System.Private.CoreLib/src/Resources/Strings.resx @@ -1666,4 +1666,13 @@ <data name="PlatformNotSupported_ReflectionOnly" xml:space="preserve"> <value>ReflectionOnly loading is not supported on this platform.</value> </data> + <data name="MissingMember_Name" xml:space="preserve"> + <value>Member '{0}' not found.</value> + </data> + <data name="MissingMethod_Name" xml:space="preserve"> + <value>Method '{0}' not found.</value> + </data> + <data name="MissingField_Name" xml:space="preserve"> + <value>Field '{0}' not found.</value> + </data> </root> diff --git a/src/System.Private.CoreLib/src/System/Activator.cs b/src/System.Private.CoreLib/src/System/Activator.cs index 0ebba05b9..dbf022043 100644 --- a/src/System.Private.CoreLib/src/System/Activator.cs +++ b/src/System.Private.CoreLib/src/System/Activator.cs @@ -7,13 +7,11 @@ // methods for late bound support. // -using System; -using System.Runtime; using System.Reflection; -using System.Collections.Generic; +using System.Globalization; using System.Runtime.CompilerServices; -using Internal.Runtime.Augments; +using Internal.Reflection.Augments; namespace System { @@ -84,20 +82,18 @@ namespace System } } - public static object CreateInstance(Type type) - { - return RuntimeAugments.Callbacks.ActivatorCreateInstance(type, Array.Empty<Object>()); - } + public static object CreateInstance(Type type) => CreateInstance(type, nonPublic: false); + public static object CreateInstance(Type type, bool nonPublic) => ReflectionAugments.ReflectionCoreCallbacks.ActivatorCreateInstance(type, nonPublic); - public static object CreateInstance(Type type, params object[] args) + public static object CreateInstance(Type type, params object[] args) => CreateInstance(type, ConstructorDefault, null, args, null, null); + public static object CreateInstance(Type type, object[] args, object[] activationAttributes) => CreateInstance(type, ConstructorDefault, null, args, null, activationAttributes); + public static object CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture) => CreateInstance(type, bindingAttr, binder, args, culture, null); + public static object CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes) { - return RuntimeAugments.Callbacks.ActivatorCreateInstance(type, args); + return ReflectionAugments.ReflectionCoreCallbacks.ActivatorCreateInstance(type, bindingAttr, binder, args, culture, activationAttributes); } - public static object CreateInstance(Type type, bool nonPublic) - { - throw new NotImplementedException(); - } + private const BindingFlags ConstructorDefault = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance; } } diff --git a/src/System.Private.CoreLib/src/System/MissingFieldException.cs b/src/System.Private.CoreLib/src/System/MissingFieldException.cs index f42cdb390..9548e9da5 100644 --- a/src/System.Private.CoreLib/src/System/MissingFieldException.cs +++ b/src/System.Private.CoreLib/src/System/MissingFieldException.cs @@ -34,11 +34,17 @@ namespace System SetErrorCode(__HResults.COR_E_MISSINGFIELD); } + public MissingFieldException(string className, string methodName) + { + ClassName = className; + MemberName = methodName; + } + public override String Message { get { - return base.Message; + return ClassName == null ? base.Message : SR.Format(SR.MissingField_Name, ClassName + "." + MemberName + (Signature != null ? " " + FormatSignature(Signature) : string.Empty)); } } } diff --git a/src/System.Private.CoreLib/src/System/MissingMemberException.cs b/src/System.Private.CoreLib/src/System/MissingMemberException.cs index 2b61dabc7..946c2882f 100644 --- a/src/System.Private.CoreLib/src/System/MissingMemberException.cs +++ b/src/System.Private.CoreLib/src/System/MissingMemberException.cs @@ -37,10 +37,34 @@ namespace System SetErrorCode(__HResults.COR_E_MISSINGMEMBER); } - public override String Message + public MissingMemberException(string className, string memberName) + { + ClassName = className; + MemberName = memberName; + } + + public override string Message { get - { return base.Message; } + { + return ClassName == null ? base.Message : SR.Format(SR.MissingMember_Name, ClassName + "." + MemberName + (Signature != null ? " " + FormatSignature(Signature) : string.Empty)); + } } + + internal static string FormatSignature(byte[] signature) + { + // This is not the correct implementation, however, it's probably not worth the time to port given that + // (1) it's for a diagnostic + // (2) Signature is non-null when this exception is created from the native runtime. Which we don't do in .Net Native. + // (3) Only other time the signature is non-null is if this exception object is deserialized from a persisted blob from an older runtime. + return string.Empty; + } + + // If ClassName != null, GetMessage will construct on the fly using it + // and the other variables. This allows customization of the + // format depending on the language environment. + protected string ClassName; + protected string MemberName; + protected byte[] Signature; } } diff --git a/src/System.Private.CoreLib/src/System/MissingMethodException.cs b/src/System.Private.CoreLib/src/System/MissingMethodException.cs index c6eccb2ee..b60746d52 100644 --- a/src/System.Private.CoreLib/src/System/MissingMethodException.cs +++ b/src/System.Private.CoreLib/src/System/MissingMethodException.cs @@ -36,11 +36,17 @@ namespace System SetErrorCode(__HResults.COR_E_MISSINGMETHOD); } - public override String Message + public MissingMethodException(string className, string methodName) + { + ClassName = className; + MemberName = methodName; + } + + public override string Message { get { - return base.Message; + return ClassName == null ? base.Message : SR.Format(SR.MissingMethod_Name, ClassName + "." + MemberName + (Signature != null ? " " + FormatSignature(Signature) : string.Empty)); } } } diff --git a/src/System.Private.CoreLib/src/System/Reflection/Assembly.cs b/src/System.Private.CoreLib/src/System/Reflection/Assembly.cs index ac4bd2a59..b13485916 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/Assembly.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/Assembly.cs @@ -99,7 +99,7 @@ namespace System.Reflection if (t == null) return null; - throw new NotImplementedException(); //return Activator.CreateInstance(t, bindingAttr, binder, args, culture, activationAttributes); + return Activator.CreateInstance(t, bindingAttr, binder, args, culture, activationAttributes); } public virtual event ModuleResolveEventHandler ModuleResolve { add { throw NotImplemented.ByDesign; } remove { throw NotImplemented.ByDesign; } } diff --git a/src/System.Private.Reflection.Core/src/Resources/Strings.resx b/src/System.Private.Reflection.Core/src/Resources/Strings.resx index 23a27779f..a5a11c894 100644 --- a/src/System.Private.Reflection.Core/src/Resources/Strings.resx +++ b/src/System.Private.Reflection.Core/src/Resources/Strings.resx @@ -273,4 +273,22 @@ <data name="PlatformNotSupported_CustomBinder" xml:space="preserve"> <value>Passing a binder object other than Type.DefaultBinder is not supported on this platform.</value> </data> + <data name="Arg_MustBeType" xml:space="preserve"> + <value>Type must be a type provided by the runtime.</value> + </data> + <data name="Arg_NoDefCTor" xml:space="preserve"> + <value>No parameterless constructor defined for this object.</value> + </data> + <data name="NotSupported_ActivAttr" xml:space="preserve"> + <value>Activation Attributes are not supported.</value> + </data> + <data name="NotSupported_CallToVarArg" xml:space="preserve"> + <value>Vararg calling convention not supported.</value> + </data> + <data name="Acc_CreateGenericEx" xml:space="preserve"> + <value>Cannot create an instance of {0} because Type.ContainsGenericParameters is true.</value> + </data> + <data name="Acc_CreateVoid" xml:space="preserve"> + <value>Cannot dynamically create an instance of System.Void.</value> + </data> </root> diff --git a/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj b/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj index fee2d946f..ed156f13f 100644 --- a/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj +++ b/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj @@ -35,6 +35,7 @@ </ItemGroup> <ItemGroup> + <Compile Include="System\ActivatorImplementation.cs" /> <Compile Include="System\DBNull.cs" /> <Compile Include="System\Empty.cs" /> <Compile Include="System\Reflection\Runtime\Assemblies\AssemblyNameHelpers.StrongName.cs" /> diff --git a/src/System.Private.Reflection.Core/src/System/ActivatorImplementation.cs b/src/System.Private.Reflection.Core/src/System/ActivatorImplementation.cs new file mode 100644 index 000000000..96d35c5ec --- /dev/null +++ b/src/System.Private.Reflection.Core/src/System/ActivatorImplementation.cs @@ -0,0 +1,127 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using System.Diagnostics; +using System.Globalization; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.BindingFlagSupport; + +using Internal.Runtime.Augments; + +namespace System +{ + internal static class ActivatorImplementation + { + public static object CreateInstance(Type type, bool nonPublic) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + + type = type.UnderlyingSystemType; + CreateInstanceCheckType(type); + + // This short-circuit depends on the fact that the toolchain prohibits valuetypes with nullary constructors. Unfortunately, we can't check for the presence of nullary + // constructors without risking a MissingMetadataException, and we can't regress the prior N behavior that allowed CreateInstance on valuetypes to work regardless of metadata. + if (type.IsValueType) + return RuntimeAugments.NewObject(type.TypeHandle); + + BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; + if (nonPublic) + bindingFlags |= BindingFlags.NonPublic; + ConstructorInfo constructor = type.GetConstructor(bindingFlags, null, CallingConventions.Any, Array.Empty<Type>(), null); + if (constructor == null) + throw new MissingMethodException(SR.Arg_NoDefCTor); + object result = constructor.Invoke(Array.Empty<object>()); + return result; + } + + public static object CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + + // If they didn't specify a lookup, then we will provide the default lookup. + const BindingFlags LookupMask = (BindingFlags)0x000000FF; + if ((bindingAttr & LookupMask) == 0) + bindingAttr |= BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance; + + if (activationAttributes != null && activationAttributes.Length > 0) + throw new NotSupportedException(SR.NotSupported_ActivAttr); + + type = type.UnderlyingSystemType; + CreateInstanceCheckType(type); + + if (args == null) + args = Array.Empty<object>(); + int numArgs = args.Length; + + // This short-circuit depends on the fact that the toolchain prohibits valuetypes with nullary constructors. Unfortunately, we can't check for the presence of nullary + // constructors without risking a MissingMetadataException, and we can't regress the prior N behavior that allowed CreateInstance on valuetypes to work regardless of metadata. + if (numArgs == 0 && type.IsValueType) + return RuntimeAugments.NewObject(type.TypeHandle); + + Type[] argTypes = new Type[numArgs]; + for (int i = 0; i < numArgs; i++) + { + argTypes[i] = args[i]?.GetType(); + } + + ConstructorInfo[] candidates = type.GetConstructors(bindingAttr); + ListBuilder<MethodBase> matches = new ListBuilder<MethodBase>(candidates.Length); + for (int i = 0; i < candidates.Length; i++) + { + if (candidates[i].QualifiesBasedOnParameterCount(bindingAttr, CallingConventions.Any, argTypes)) + matches.Add(candidates[i]); + } + if (matches.Count == 0) + throw new MissingMethodException(SR.Arg_NoDefCTor); + + if (binder == null) + binder = Type.DefaultBinder; + + object state = null; + MethodBase invokeMethod = binder.BindToMethod(bindingAttr, matches.ToArray(), ref args, null, culture, null, out state); + if (invokeMethod.GetParametersNoCopy().Length == 0) + { + if (args.Length != 0) + { + + Debug.Assert((invokeMethod.CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs); + throw new NotSupportedException(SR.NotSupported_CallToVarArg); + } + + // Desktop compat: CoreClr invokes a "fast-path" here (call Activator.CreateInstance(type, true)) that also + // bypasses the binder.ReorderArgumentArray() call. That "fast-path" isn't a fast-path for us so we won't do that + // but we'll still null out the "state" variable to bypass the Reorder call. + // + // The only time this matters at all is if (1) a third party binder is being used and (2) it actually reordered the array + // which it shouldn't have done because (a) we didn't request it to bind arguments by name, and (b) it's kinda hard to + // reorder a zero-length args array. But who knows what a third party binder will do if we make a call to it that we didn't + // used to do, so we'll preserve the CoreClr order of calls just to be safe. + state = null; + } + + object result = ((ConstructorInfo)invokeMethod).Invoke(bindingAttr, binder, args, culture); + if (state != null) + binder.ReorderArgumentArray(ref args, state); + return result; + } + + private static void CreateInstanceCheckType(Type type) + { + if (type == null || !type.IsRuntimeImplemented()) + throw new ArgumentException(SR.Arg_MustBeType); + + if (type.ContainsGenericParameters) + throw new ArgumentException(SR.Format(SR.Acc_CreateGenericEx, type)); + + Type elementType = type; + while (elementType.HasElementType) + elementType = elementType.GetElementType(); + if (elementType.Equals(CommonRuntimeTypes.Void)) + throw new NotSupportedException(SR.Acc_CreateVoid); + } + } +} diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs index c47091ff1..6f639c404 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs @@ -5,6 +5,7 @@ using System; using System.Reflection; using System.Diagnostics; +using System.Globalization; using System.Collections.Generic; using System.Reflection.Runtime.General; using System.Reflection.Runtime.TypeInfos; @@ -167,5 +168,15 @@ namespace System.Reflection.Runtime.General RuntimeTypeInfo reflectedType = contextTypeInfo; return RuntimeFieldInfo.GetRuntimeFieldInfo(fieldHandle, definingTypeInfo, contextTypeInfo, reflectedType); } + + public sealed override object ActivatorCreateInstance(Type type, bool nonPublic) + { + return ActivatorImplementation.CreateInstance(type, nonPublic); + } + + public sealed override object ActivatorCreateInstance(Type type, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes) + { + return ActivatorImplementation.CreateInstance(type, bindingAttr, binder, args, culture, activationAttributes); + } } } diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs index f8bc2e070..83d563496 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs @@ -42,74 +42,6 @@ namespace Internal.Reflection.Execution ModuleList.Instance.RegisterModule(moduleHandle); } - public sealed override Object ActivatorCreateInstance(Type type, Object[] args) - { - if (type == null) - throw new ArgumentNullException("type"); - - if (args == null) - args = new Object[] { }; - - TypeInfo typeInfo = type.GetTypeInfo(); - - // All value types have an implied default constructor that has no representation in metadata. - if (args.Length == 0 && typeInfo.IsValueType) - return RuntimeAugments.NewObject(type.TypeHandle); - - LowLevelList<MethodBase> candidates = new LowLevelList<MethodBase>(); - foreach (ConstructorInfo constructor in typeInfo.DeclaredConstructors) - { - if (constructor.IsStatic) - continue; - if (!constructor.IsPublic) - continue; - - // Project N does not support varargs - if it ever does, we'll probably have port over more desktop policy for this code... - if (0 != (constructor.CallingConvention & CallingConventions.VarArgs)) - throw new PlatformNotSupportedException(SR.PlatformNotSupported_VarArgs); - - // The default binder rules allow omitting optional parameters but Activator.CreateInstance() doesn't. Thus, if the # of arguments doesn't match - // the # of parameters, do the pre-verification that the desktop does and ensure that the method has a "params" argument and that the argument count - // isn't shorter by more than one. - ParameterInfo[] parameters = constructor.GetParametersNoCopy(); - if (args.Length != parameters.Length) - { - if (args.Length < parameters.Length - 1) - continue; - if (parameters.Length == 0) - continue; - ParameterInfo finalParameter = parameters[parameters.Length - 1]; - if (!finalParameter.ParameterType.IsArray) - continue; - - bool hasParamArray = false; - foreach (CustomAttributeData cad in finalParameter.CustomAttributes) - { - if (cad.AttributeType.Equals(typeof(ParamArrayAttribute))) - { - hasParamArray = true; - break; - } - } - if (!hasParamArray) - continue; - } - - candidates.Add(constructor); - } - - if (candidates.Count == 0) - throw new MissingMethodException(SR.Format(SR.MissingConstructor_Name, type)); - - MethodBase[] candidatesArray = candidates.ToArray(); - Binder binder = Type.DefaultBinder; - object ignore; - BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance; - ConstructorInfo match = (ConstructorInfo)binder.BindToMethod(bindingAttr, candidatesArray, ref args, null, null, null, out ignore); - Object newObject = match.Invoke(args); - return newObject; - } - public sealed override Type GetType(string typeName, Func<AssemblyName, Assembly> assemblyResolver, Func<Assembly, string, bool, Type> typeResolver, bool throwOnError, bool ignoreCase) { return _executionDomain.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, ReflectionExecution.DefaultAssemblyNamesForGetType); |