未验证 提交 ffa24bd9 编写于 作者: L Levi Broderick 提交者: GitHub

Improve performance of Activator.CreateInstance (#32520)

- Use modern C# calli features to invoke allocator and ctor
- Share arg validation code between CreateInstance and GetUninitializedObject
- Improve exception message when CreateInstance fails
- Lay foundation for future work in Activator
Co-authored-by: NJan Kotas <jkotas@microsoft.com>
上级 15033649
......@@ -220,6 +220,7 @@
<Compile Include="$(BclSourcesRoot)\System\Runtime\Versioning\CompatibilitySwitch.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeArgumentHandle.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeHandles.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeType.ActivatorCache.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeType.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Security\DynamicSecurityMethodAttribute.cs" />
<Compile Include="$(BclSourcesRoot)\System\StartupHookProvider.cs" />
......
......@@ -208,12 +208,6 @@ internal static bool HasElementType(RuntimeType type)
return outHandles;
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object CreateInstance(RuntimeType type, bool publicOnly, bool wrapExceptions, ref bool canBeCached, ref RuntimeMethodHandleInternal ctor, ref bool hasNoDefaultCtor);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object Allocate(RuntimeType type);
internal static object CreateInstanceForAnotherGenericParameter(RuntimeType type, RuntimeType genericParameter)
{
object? instantiatedObject = null;
......@@ -258,6 +252,51 @@ internal static object CreateInstanceForAnotherGenericParameter(RuntimeType type
int cTypeHandles,
ObjectHandleOnStack instantiatedObject);
/// <summary>
/// Given a RuntimeType, returns information about how to activate it via calli
/// semantics. This method will ensure the type object is fully initialized within
/// the VM, but it will not call any static ctors on the type.
/// </summary>
internal static void GetActivationInfo(
RuntimeType rt,
out delegate*<void*, object> pfnAllocator,
out void* vAllocatorFirstArg,
out delegate*<object, void> pfnCtor,
out bool ctorIsPublic)
{
Debug.Assert(rt != null);
delegate*<void*, object> pfnAllocatorTemp = default;
void* vAllocatorFirstArgTemp = default;
delegate*<object, void> pfnCtorTemp = default;
Interop.BOOL fCtorIsPublicTemp = default;
GetActivationInfo(
ObjectHandleOnStack.Create(ref rt),
&pfnAllocatorTemp, &vAllocatorFirstArgTemp,
&pfnCtorTemp, &fCtorIsPublicTemp);
pfnAllocator = pfnAllocatorTemp;
vAllocatorFirstArg = vAllocatorFirstArgTemp;
pfnCtor = pfnCtorTemp;
ctorIsPublic = fCtorIsPublicTemp != Interop.BOOL.FALSE;
}
[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void GetActivationInfo(
ObjectHandleOnStack pRuntimeType,
delegate*<void*, object>* ppfnAllocator,
void** pvAllocatorFirstArg,
delegate*<object, void>* ppfnCtor,
Interop.BOOL* pfCtorIsPublic);
#if FEATURE_COMINTEROP
// Referenced by unmanaged layer (see GetActivationInfo).
// First parameter is ComClassFactory*.
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object AllocateComObject(void* pClassFactory);
#endif
internal RuntimeType GetRuntimeType()
{
return m_type;
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace System
{
internal sealed partial class RuntimeType
{
/// <summary>
/// A cache which allows optimizing <see cref="Activator.CreateInstance"/>,
/// <see cref="RuntimeType.CreateInstanceDefaultCtor"/>, and related APIs.
/// </summary>
private sealed unsafe class ActivatorCache
{
// The managed calli to the newobj allocator, plus its first argument (MethodTable*).
// In the case of the COM allocator, first arg is ComClassFactory*, not MethodTable*.
private readonly delegate*<void*, object?> _pfnAllocator;
private readonly void* _allocatorFirstArg;
// The managed calli to the parameterless ctor, taking "this" (as object) as its first argument.
private readonly delegate*<object?, void> _pfnCtor;
private readonly bool _ctorIsPublic;
#if DEBUG
private readonly RuntimeType _originalRuntimeType;
#endif
internal ActivatorCache(RuntimeType rt)
{
Debug.Assert(rt != null);
#if DEBUG
_originalRuntimeType = rt;
#endif
// The check below is redundant since these same checks are performed at the
// unmanaged layer, but this call will throw slightly different exceptions
// than the unmanaged layer, and callers might be dependent on this.
rt.CreateInstanceCheckThis();
try
{
RuntimeTypeHandle.GetActivationInfo(rt,
out _pfnAllocator!, out _allocatorFirstArg,
out _pfnCtor!, out _ctorIsPublic);
}
catch (Exception ex)
{
// Exception messages coming from the runtime won't include
// the type name. Let's include it here to improve the
// debugging experience for our callers.
string friendlyMessage = SR.Format(SR.Activator_CannotCreateInstance, rt, ex.Message);
switch (ex)
{
case ArgumentException: throw new ArgumentException(friendlyMessage);
case PlatformNotSupportedException: throw new PlatformNotSupportedException(friendlyMessage);
case NotSupportedException: throw new NotSupportedException(friendlyMessage);
case MethodAccessException: throw new MethodAccessException(friendlyMessage);
case MissingMethodException: throw new MissingMethodException(friendlyMessage);
case MemberAccessException: throw new MemberAccessException(friendlyMessage);
}
throw; // can't make a friendlier message, rethrow original exception
}
// Activator.CreateInstance returns null given typeof(Nullable<T>).
if (_pfnAllocator == null)
{
Debug.Assert(Nullable.GetUnderlyingType(rt) != null,
"Null allocator should only be returned for Nullable<T>.");
static object? ReturnNull(void* _) => null;
_pfnAllocator = &ReturnNull;
}
// If no ctor is provided, we have Nullable<T>, a ctorless value type T,
// or a ctorless __ComObject. In any case, we should replace the
// ctor call with our no-op stub. The unmanaged GetActivationInfo layer
// would have thrown an exception if 'rt' were a normal reference type
// without a ctor.
if (_pfnCtor == null)
{
static void CtorNoopStub(object? uninitializedObject) { }
_pfnCtor = &CtorNoopStub; // we use null singleton pattern if no ctor call is necessary
Debug.Assert(_ctorIsPublic); // implicit parameterless ctor is always considered public
}
// We don't need to worry about invoking cctors here. The runtime will figure it
// out for us when the instance ctor is called. For value types, because we're
// creating a boxed default(T), the static cctor is called when *any* instance
// method is invoked.
}
internal bool CtorIsPublic => _ctorIsPublic;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal object? CreateUninitializedObject(RuntimeType rt)
{
// We don't use RuntimeType, but we force the caller to pass it so
// that we can keep it alive on their behalf. Once the object is
// constructed, we no longer need the reference to the type instance,
// as the object itself will keep the type alive.
#if DEBUG
if (_originalRuntimeType != rt)
{
Debug.Fail("Caller passed the wrong RuntimeType to this routine."
+ Environment.NewLineConst + "Expected: " + (_originalRuntimeType ?? (object)"<null>")
+ Environment.NewLineConst + "Actual: " + (rt ?? (object)"<null>"));
}
#endif
object? retVal = _pfnAllocator(_allocatorFirstArg);
GC.KeepAlive(rt);
return retVal;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject);
}
}
}
......@@ -17,12 +17,6 @@
namespace System
{
// this is a work around to get the concept of a calli. It's not as fast but it would be interesting to
// see how it compares to the current implementation.
// This delegate will disappear at some point in favor of calli
internal delegate void CtorDelegate(object instance);
// Keep this in sync with FormatFlags defined in typestring.h
internal enum TypeNameFormatFlags
{
......@@ -3968,113 +3962,45 @@ private void CreateInstanceCheckThis()
return instance;
}
// the cache entry
private sealed class ActivatorCache
{
// the delegate containing the call to the ctor
internal readonly RuntimeMethodHandleInternal _hCtorMethodHandle;
internal MethodAttributes _ctorAttributes;
internal CtorDelegate? _ctor;
// Lazy initialization was performed
internal volatile bool _isFullyInitialized;
private static ConstructorInfo? s_delegateCtorInfo;
internal ActivatorCache(RuntimeMethodHandleInternal rmh)
{
_hCtorMethodHandle = rmh;
}
private void Initialize()
{
if (!_hCtorMethodHandle.IsNullHandle())
{
_ctorAttributes = RuntimeMethodHandle.GetAttributes(_hCtorMethodHandle);
// The default ctor path is optimized for reference types only
ConstructorInfo delegateCtorInfo = s_delegateCtorInfo ??= typeof(CtorDelegate).GetConstructor(new Type[] { typeof(object), typeof(IntPtr) })!;
// No synchronization needed here. In the worst case we create extra garbage
_ctor = (CtorDelegate)delegateCtorInfo.Invoke(new object?[] { null, RuntimeMethodHandle.GetFunctionPointer(_hCtorMethodHandle) });
}
_isFullyInitialized = true;
}
public void EnsureInitialized()
{
if (!_isFullyInitialized)
Initialize();
}
}
/// <summary>
/// The slow path of CreateInstanceDefaultCtor
/// Helper to invoke the default (parameterless) constructor.
/// </summary>
private object? CreateInstanceDefaultCtorSlow(bool publicOnly, bool wrapExceptions, bool fillCache)
[DebuggerStepThrough]
[DebuggerHidden]
internal object? CreateInstanceDefaultCtor(bool publicOnly, bool skipCheckThis, bool fillCache, bool wrapExceptions)
{
RuntimeMethodHandleInternal runtimeCtor = default;
bool canBeCached = false;
bool hasNoDefaultCtor = false;
// Get or create the cached factory. Creating the cache will fail if one
// of our invariant checks fails; e.g., no appropriate ctor found.
//
// n.b. In coreclr we ignore 'skipCheckThis' (assumed to be false)
// and 'fillCache' (assumed to be true).
object instance = RuntimeTypeHandle.CreateInstance(this, publicOnly, wrapExceptions, ref canBeCached, ref runtimeCtor, ref hasNoDefaultCtor);
if (hasNoDefaultCtor)
if (GenericCache is not ActivatorCache cache)
{
throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this));
cache = new ActivatorCache(this);
GenericCache = cache;
}
if (canBeCached && fillCache)
if (!cache.CtorIsPublic && publicOnly)
{
// cache the ctor
GenericCache = new ActivatorCache(runtimeCtor);
throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this));
}
return instance;
}
// Compat: allocation always takes place outside the try block so that OOMs
// bubble up to the caller; the ctor invocation is within the try block so
// that it can be wrapped in TIE if needed.
/// <summary>
/// Helper to invoke the default (parameterless) constructor.
/// </summary>
[DebuggerStepThrough]
[DebuggerHidden]
internal object? CreateInstanceDefaultCtor(bool publicOnly, bool skipCheckThis, bool fillCache, bool wrapExceptions)
{
// Call the cached
if (GenericCache is ActivatorCache cacheEntry)
object? obj = cache.CreateUninitializedObject(this);
try
{
cacheEntry.EnsureInitialized();
if (publicOnly)
{
if (cacheEntry._ctor != null &&
(cacheEntry._ctorAttributes & MethodAttributes.MemberAccessMask) != MethodAttributes.Public)
{
throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this));
}
}
// Allocate empty object and call the default constructor if present.
object instance = RuntimeTypeHandle.Allocate(this);
Debug.Assert(cacheEntry._ctor != null || IsValueType);
if (cacheEntry._ctor != null)
{
try
{
cacheEntry._ctor(instance);
}
catch (Exception e) when (wrapExceptions)
{
throw new TargetInvocationException(e);
}
}
return instance;
cache.CallConstructor(obj);
}
catch (Exception e) when (wrapExceptions)
{
throw new TargetInvocationException(e);
}
if (!skipCheckThis)
CreateInstanceCheckThis();
return CreateInstanceDefaultCtorSlow(publicOnly, wrapExceptions, fillCache);
return obj;
}
internal void InvalidateCachedNestedType() => Cache.InvalidateCachedNestedType();
......
......@@ -371,6 +371,9 @@ DEFINE_CLASS(RT_TYPE_HANDLE, System, RuntimeTypeHandle)
DEFINE_METHOD(RT_TYPE_HANDLE, GET_TYPE_HELPER, GetTypeHelper, SM_Type_ArrType_IntPtr_int_RetType)
DEFINE_METHOD(RT_TYPE_HANDLE, PVOID_CTOR, .ctor, IM_RuntimeType_RetVoid)
DEFINE_METHOD(RT_TYPE_HANDLE, GETVALUEINTERNAL, GetValueInternal, SM_RuntimeTypeHandle_RetIntPtr)
#ifdef FEATURE_COMINTEROP
DEFINE_METHOD(RT_TYPE_HANDLE, ALLOCATECOMOBJECT, AllocateComObject, SM_VoidPtr_RetObj)
#endif
DEFINE_FIELD(RT_TYPE_HANDLE, M_TYPE, m_type)
DEFINE_CLASS_U(Reflection, RtFieldInfo, NoClass)
......
......@@ -189,7 +189,6 @@ FCFuncStart(gSystem_RuntimeType)
FCFuncEnd()
FCFuncStart(gCOMTypeHandleFuncs)
FCFuncElement("CreateInstance", RuntimeTypeHandle::CreateInstance)
QCFuncElement("CreateInstanceForAnotherGenericParameter", RuntimeTypeHandle::CreateInstanceForAnotherGenericParameter)
QCFuncElement("GetGCHandle", RuntimeTypeHandle::GetGCHandle)
QCFuncElement("FreeGCHandle", RuntimeTypeHandle::FreeGCHandle)
......@@ -239,7 +238,10 @@ FCFuncStart(gCOMTypeHandleFuncs)
FCFuncElement("IsGenericTypeDefinition", RuntimeTypeHandle::IsGenericTypeDefinition)
FCFuncElement("ContainsGenericVariables", RuntimeTypeHandle::ContainsGenericVariables)
FCFuncElement("SatisfiesConstraints", RuntimeTypeHandle::SatisfiesConstraints)
FCFuncElement("Allocate", RuntimeTypeHandle::Allocate) //for A.CI
QCFuncElement("GetActivationInfo", RuntimeTypeHandle::GetActivationInfo)
#ifdef FEATURE_COMINTEROP
FCFuncElement("AllocateComObject", RuntimeTypeHandle::AllocateComObject)
#endif // FEATURE_COMINTEROP
FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles)
FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal)
FCFuncElement("IsEquivalentTo", RuntimeTypeHandle::IsEquivalentTo)
......
......@@ -464,6 +464,7 @@ DEFINE_METASIG(IM(RefObject_RetBool, r(j), F))
DEFINE_METASIG_T(IM(Class_RetObj, C(CLASS), j))
DEFINE_METASIG(IM(Int_VoidPtr_RetVoid, i P(v), v))
DEFINE_METASIG(IM(VoidPtr_RetVoid, P(v), v))
DEFINE_METASIG(SM(VoidPtr_RetObj, P(v), j))
DEFINE_METASIG_T(IM(Str_RetModule, s, C(MODULE)))
DEFINE_METASIG_T(SM(Assembly_Str_RetAssembly, C(ASSEMBLY) s, C(ASSEMBLY)))
......
......@@ -9150,7 +9150,7 @@ BOOL MethodTable::HasExplicitOrImplicitPublicDefaultConstructor()
}
//==========================================================================================
MethodDesc *MethodTable::GetDefaultConstructor()
MethodDesc *MethodTable::GetDefaultConstructor(BOOL forceBoxedEntryPoint /* = FALSE */)
{
WRAPPER_NO_CONTRACT;
_ASSERTE(HasDefaultConstructor());
......@@ -9161,7 +9161,7 @@ MethodDesc *MethodTable::GetDefaultConstructor()
// returns pCanonMD immediately.
return MethodDesc::FindOrCreateAssociatedMethodDesc(pCanonMD,
this,
FALSE /* no BoxedEntryPointStub */,
forceBoxedEntryPoint,
Instantiation(), /* no method instantiation */
FALSE /* no allowInstParam */);
}
......
......@@ -823,7 +823,7 @@ public:
BOOL HasDefaultConstructor();
void SetHasDefaultConstructor();
WORD GetDefaultConstructorSlot();
MethodDesc *GetDefaultConstructor();
MethodDesc *GetDefaultConstructor(BOOL forceBoxedEntryPoint = FALSE);
BOOL HasExplicitOrImplicitPublicDefaultConstructor();
......
......@@ -338,183 +338,6 @@ FCIMPL7(void, RuntimeFieldHandle::SetValue, ReflectFieldObject *pFieldUNSAFE, Ob
}
FCIMPLEND
//A.CI work
FCIMPL1(Object*, RuntimeTypeHandle::Allocate, ReflectClassBaseObject* pTypeUNSAFE)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pTypeUNSAFE));
}
CONTRACTL_END
REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);
TypeHandle type = refType->GetType();
// Handle the nullable<T> special case
if (Nullable::IsNullableType(type)) {
return OBJECTREFToObject(Nullable::BoxedNullableNull(type));
}
OBJECTREF rv = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_1(refType);
rv = AllocateObject(type.GetMethodTable());
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(rv);
}//Allocate
FCIMPLEND
FCIMPL6(Object*, RuntimeTypeHandle::CreateInstance, ReflectClassBaseObject* refThisUNSAFE,
CLR_BOOL publicOnly,
CLR_BOOL wrapExceptions,
CLR_BOOL* pbCanBeCached,
MethodDesc** pConstructor,
CLR_BOOL* pbHasNoDefaultCtor) {
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(refThisUNSAFE));
PRECONDITION(CheckPointer(pbCanBeCached));
PRECONDITION(CheckPointer(pConstructor));
PRECONDITION(CheckPointer(pbHasNoDefaultCtor));
PRECONDITION(*pbCanBeCached == false);
PRECONDITION(*pConstructor == NULL);
PRECONDITION(*pbHasNoDefaultCtor == false);
}
CONTRACTL_END;
if (refThisUNSAFE == NULL)
FCThrow(kNullReferenceException);
MethodDesc* pMeth;
OBJECTREF rv = NULL;
REFLECTCLASSBASEREF refThis = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(refThisUNSAFE);
TypeHandle thisTH = refThis->GetType();
Assembly *pAssem = thisTH.GetAssembly();
HELPER_METHOD_FRAME_BEGIN_RET_2(rv, refThis);
MethodTable* pVMT;
// Get the type information associated with refThis
if (thisTH.IsNull() || thisTH.IsTypeDesc()) {
*pbHasNoDefaultCtor = true;
goto DoneCreateInstance;
}
pVMT = thisTH.AsMethodTable();
pVMT->EnsureInstanceActive();
#ifdef FEATURE_COMINTEROP
// If this is __ComObject then create the underlying COM object.
if (IsComObjectClass(refThis->GetType())) {
#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
SyncBlock* pSyncBlock = refThis->GetSyncBlock();
void* pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory();
if (!pClassFactory)
COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY);
// create an instance of the Com Object
rv = ((ComClassFactory*)pClassFactory)->CreateInstance(NULL);
#else // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY);
#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
}
else
#endif // FEATURE_COMINTEROP
{
// if this is an abstract class then we will fail this
if (pVMT->IsAbstract()) {
if (pVMT->IsInterface())
COMPlusThrow(kMissingMethodException,W("Acc_CreateInterface"));
else
COMPlusThrow(kMissingMethodException,W("Acc_CreateAbst"));
}
else if (pVMT->ContainsGenericVariables()) {
COMPlusThrow(kArgumentException,W("Acc_CreateGeneric"));
}
if (pVMT->IsByRefLike())
COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike"));
if (pVMT->IsSharedByGenericInstantiations())
COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
if (!pVMT->HasDefaultConstructor())
{
// We didn't find the parameterless constructor,
// if this is a Value class we can simply allocate one and return it
if (!pVMT->IsValueType()) {
*pbHasNoDefaultCtor = true;
goto DoneCreateInstance;
}
// Handle the nullable<T> special case
if (Nullable::IsNullableType(thisTH)) {
rv = Nullable::BoxedNullableNull(thisTH);
}
else
rv = pVMT->Allocate();
*pbCanBeCached = true;
}
else // !pVMT->HasDefaultConstructor()
{
pMeth = pVMT->GetDefaultConstructor();
// Validate the method can be called by this caller
DWORD attr = pMeth->GetAttrs();
if (!IsMdPublic(attr) && publicOnly) {
*pbHasNoDefaultCtor = true;
goto DoneCreateInstance;
}
// We've got the class, lets allocate it and call the constructor
OBJECTREF o;
o = AllocateObject(pVMT);
GCPROTECT_BEGIN(o);
MethodDescCallSite ctor(pMeth, &o);
// Copy "this" pointer
ARG_SLOT arg;
if (pVMT->IsValueType())
arg = PtrToArgSlot(o->UnBox());
else
arg = ObjToArgSlot(o);
// Call the method
TryCallMethod(&ctor, &arg, wrapExceptions);
rv = o;
GCPROTECT_END();
// No need to set these if they cannot be cached. In particular, if the type is a value type with a custom
// parameterless constructor, don't allow caching and have subsequent calls come back here to allocate an object and
// call the constructor.
if (!pVMT->IsValueType())
{
*pbCanBeCached = true;
*pConstructor = pMeth;
}
}
}
DoneCreateInstance:
;
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(rv);
}
FCIMPLEND
void QCALLTYPE RuntimeTypeHandle::CreateInstanceForAnotherGenericParameter(
QCall::TypeHandle pTypeHandle,
TypeHandle* pInstArray,
......@@ -2174,6 +1997,262 @@ lExit: ;
}
FCIMPLEND
/*
* Given a TypeHandle, validates whether it's legal to construct a real
* instance of that type. Throws an exception if the instantiation would
* be illegal; e.g., type is void or a pointer or an open generic. This
* doesn't guarantee that a ctor will succeed, only that the VM is able
* to support an instance of this type on the heap.
* ==========
* The 'fForGetUninitializedInstance' parameter controls the type of
* exception that is thrown if a check fails.
*/
void RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated(
TypeHandle typeHandle,
bool fGetUninitializedObject)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
// Don't allow void
if (typeHandle.GetSignatureCorElementType() == ELEMENT_TYPE_VOID)
{
COMPlusThrow(kArgumentException, W("NotSupported_Type"));
}
// Don't allow arrays, pointers, byrefs, or function pointers
if (typeHandle.IsTypeDesc() || typeHandle.IsArray())
{
COMPlusThrow(fGetUninitializedObject ? kArgumentException : kMissingMethodException, W("NotSupported_Type"));
}
MethodTable* pMT = typeHandle.AsMethodTable();
PREFIX_ASSUME(pMT != NULL);
// Don't allow creating instances of delegates
if (pMT->IsDelegate())
{
COMPlusThrow(kArgumentException, W("NotSupported_Type"));
}
// Don't allow string or string-like (variable length) types.
if (pMT->HasComponentSize())
{
COMPlusThrow(fGetUninitializedObject ? kArgumentException : kMissingMethodException, W("Argument_NoUninitializedStrings"));
}
// Don't allow abstract classes or interface types
if (pMT->IsAbstract())
{
RuntimeExceptionKind exKind = fGetUninitializedObject ? kMemberAccessException : kMissingMethodException;
if (pMT->IsInterface())
COMPlusThrow(exKind, W("Acc_CreateInterface"));
else
COMPlusThrow(exKind, W("Acc_CreateAbst"));
}
// Don't allow generic variables (e.g., the 'T' from List<T>)
// or open generic types (List<>).
if (typeHandle.ContainsGenericVariables())
{
COMPlusThrow(kMemberAccessException, W("Acc_CreateGeneric"));
}
// Don't allow generics instantiated over __Canon
if (pMT->IsSharedByGenericInstantiations())
{
COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
}
// Don't allow ref structs
if (pMT->IsByRefLike())
{
COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike"));
}
}
/*
* Given a RuntimeType, queries info on how to instantiate the object.
* pRuntimeType - [required] the RuntimeType object
* ppfnAllocator - [required, null-init] fnptr to the allocator
* mgd sig: void* -> object
* pvAllocatorFirstArg - [required, null-init] first argument to the allocator
* (normally, but not always, the MethodTable*)
* ppfnCtor - [required, null-init] the instance's parameterless ctor,
* mgd sig object -> void, or null if no ctor is needed for this type
* pfCtorIsPublic - [required, null-init] whether the parameterless ctor is public
* ==========
* This method will not run the type's static cctor.
* This method will not allocate an instance of the target type.
*/
void QCALLTYPE RuntimeTypeHandle::GetActivationInfo(
QCall::ObjectHandleOnStack pRuntimeType,
PCODE* ppfnAllocator,
void** pvAllocatorFirstArg,
PCODE* ppfnCtor,
BOOL* pfCtorIsPublic
)
{
CONTRACTL{
QCALL_CHECK;
PRECONDITION(CheckPointer(ppfnAllocator));
PRECONDITION(CheckPointer(pvAllocatorFirstArg));
PRECONDITION(CheckPointer(ppfnCtor));
PRECONDITION(CheckPointer(pfCtorIsPublic));
PRECONDITION(*ppfnAllocator == NULL);
PRECONDITION(*pvAllocatorFirstArg == NULL);
PRECONDITION(*ppfnCtor == NULL);
PRECONDITION(*pfCtorIsPublic == FALSE);
}
CONTRACTL_END;
TypeHandle typeHandle = NULL;
BEGIN_QCALL;
{
GCX_COOP();
// We need to take the RuntimeType itself rather than the RuntimeTypeHandle,
// as the COM CLSID is stored in the RuntimeType object's sync block, and we
// might need to pull it out later in this method.
typeHandle = ((REFLECTCLASSBASEREF)pRuntimeType.Get())->GetType();
}
ValidateTypeAbleToBeInstantiated(typeHandle, false /* fGetUninitializedObject */);
MethodTable* pMT = typeHandle.AsMethodTable();
PREFIX_ASSUME(pMT != NULL);
#ifdef FEATURE_COMINTEROP
// COM allocation can involve the __ComObject base type (with attached CLSID) or a
// VM-implemented [ComImport] class. For CreateInstance, the flowchart is:
// - For __ComObject,
// .. on Windows, bypass normal newobj logic and use ComClassFactory::CreateInstance.
// .. on non-Windows, treat as a normal class, type has no special handling in VM.
// - For [ComImport] class, treat as a normal class. VM will replace default
// ctor with COM activation logic on supported platforms, else ctor itself will PNSE.
// IsComObjectClass is the correct way to check for __ComObject specifically
if (IsComObjectClass(typeHandle))
{
void* pClassFactory = NULL;
#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
{
// Need to enter cooperative mode to manipulate OBJECTREFs
GCX_COOP();
SyncBlock* pSyncBlock = pRuntimeType.Get()->GetSyncBlock();
pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory();
}
#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
if (pClassFactory == NULL)
{
// no factory *or* unmanaged activation is not enabled in this runtime
COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY);
}
// managed sig: ComClassFactory* -> object (via FCALL)
*ppfnAllocator = CoreLibBinder::GetMethod(METHOD__RT_TYPE_HANDLE__ALLOCATECOMOBJECT)->GetMultiCallableAddrOfCode();
*pvAllocatorFirstArg = pClassFactory;
*ppfnCtor = NULL; // no ctor call needed; activation handled entirely by the allocator
*pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent
}
else
#endif // FEATURE_COMINTEROP
if (pMT->IsNullable())
{
// CreateInstance returns null given Nullable<T>
*ppfnAllocator = NULL;
*pvAllocatorFirstArg = NULL;
*ppfnCtor = NULL;
*pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent
}
else
{
// managed sig: MethodTable* -> object (via JIT helper)
*ppfnAllocator = CEEJitInfo::getHelperFtnStatic(CEEInfo::getNewHelperStatic(pMT));
*pvAllocatorFirstArg = pMT;
if (pMT->HasDefaultConstructor())
{
// managed sig: object -> void
// for ctors on value types, lookup boxed entry point stub
MethodDesc* pMD = pMT->GetDefaultConstructor(pMT->IsValueType() /* forceBoxedEntryPoint */);
_ASSERTE(pMD != NULL);
PCODE pCode = pMD->GetMultiCallableAddrOfCode();
_ASSERTE(pCode != NULL);
*ppfnCtor = pCode;
*pfCtorIsPublic = pMD->IsPublic();
}
else if (pMT->IsValueType())
{
*ppfnCtor = NULL; // no ctor call needed; we're creating a boxed default(T)
*pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent
}
else
{
// reference type with no parameterless ctor - we can't instantiate this
COMPlusThrow(kMissingMethodException, W("Arg_NoDefCTorWithoutTypeName"));
}
}
pMT->EnsureInstanceActive();
END_QCALL;
}
/*
* Given a ComClassFactory*, calls the COM allocator
* and returns a RCW.
*/
FCIMPL1(Object*, RuntimeTypeHandle::AllocateComObject,
void* pClassFactory)
{
CONTRACTL{
FCALL_CHECK;
PRECONDITION(CheckPointer(pClassFactory));
}
CONTRACTL_END;
OBJECTREF rv = NULL;
bool allocated = false;
HELPER_METHOD_FRAME_BEGIN_RET_1(rv);
#ifdef FEATURE_COMINTEROP
#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
{
if (pClassFactory != NULL)
{
rv = ((ComClassFactory*)pClassFactory)->CreateInstance(NULL);
allocated = true;
}
}
#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
#endif // FEATURE_COMINTEROP
if (!allocated)
{
#ifdef FEATURE_COMINTEROP
COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY);
#else // FEATURE_COMINTEROP
COMPlusThrow(kPlatformNotSupportedException, IDS_EE_NO_BACKING_CLASS_FACTORY);
#endif // FEATURE_COMINTEROP
}
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(rv);
}
FCIMPLEND
//*************************************************************************************************
//*************************************************************************************************
//*************************************************************************************************
......@@ -2191,36 +2270,9 @@ FCIMPL1(Object*, ReflectionSerialization::GetUninitializedObject, ReflectClassBa
TypeHandle type = objType->GetType();
// Don't allow void, arrays, pointers, byrefs or function pointers.
if (type.IsTypeDesc() || type.IsArray() || type.GetSignatureCorElementType() == ELEMENT_TYPE_VOID)
COMPlusThrow(kArgumentException, W("Argument_InvalidValue"));
RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated(type, true /* fForGetUninitializedInstance */);
MethodTable *pMT = type.AsMethodTable();
PREFIX_ASSUME(pMT != NULL);
//We don't allow unitialized Strings or Utf8Strings.
if (pMT == g_pStringClass) {
COMPlusThrow(kArgumentException, W("Argument_NoUninitializedStrings"));
}
// if this is an abstract class or an interface type then we will
// fail this
if (pMT->IsAbstract()) {
COMPlusThrow(kMemberAccessException,W("Acc_CreateAbst"));
}
if (pMT->ContainsGenericVariables()) {
COMPlusThrow(kMemberAccessException,W("Acc_CreateGeneric"));
}
if (pMT->IsByRefLike()) {
COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike"));
}
// Never allow allocation of generics actually instantiated over __Canon
if (pMT->IsSharedByGenericInstantiations()) {
COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
}
MethodTable* pMT = type.AsMethodTable();
// Never allow the allocation of an unitialized ContextBoundObject derived type, these must always be created with a paired
// transparent proxy or the jit will get confused.
......@@ -2235,6 +2287,7 @@ FCIMPL1(Object*, ReflectionSerialization::GetUninitializedObject, ReflectClassBa
if (Nullable::IsNullableType(pMT))
pMT = pMT->GetInstantiation()[0].GetMethodTable();
// Allocation will invoke any precise static cctors as needed.
retVal = pMT->Allocate();
HELPER_METHOD_FRAME_END();
......@@ -2581,4 +2634,3 @@ FCIMPL2(FC_BOOL_RET, ReflectionEnum::InternalHasFlag, Object *pRefThis, Object*
FC_RETURN_BOOL(cmp);
}
FCIMPLEND
......@@ -122,13 +122,16 @@ class RuntimeTypeHandle {
public:
// Static method on RuntimeTypeHandle
static FCDECL1(Object*, Allocate, ReflectClassBaseObject *refType) ; //A.CI work
static FCDECL6(Object*, CreateInstance, ReflectClassBaseObject* refThisUNSAFE,
CLR_BOOL publicOnly,
CLR_BOOL wrapExceptions,
CLR_BOOL *pbCanBeCached,
MethodDesc** pConstructor,
CLR_BOOL *pbHasNoDefaultCtor);
static
void QCALLTYPE GetActivationInfo(
QCall::ObjectHandleOnStack pRuntimeType,
PCODE* ppfnAllocator,
void** pvAllocatorFirstArg,
PCODE* ppfnCtor,
BOOL* pfCtorIsPublic);
static FCDECL1(Object*, AllocateComObject, void* pClassFactory);
static
void QCALLTYPE MakeByRef(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType);
......@@ -193,6 +196,7 @@ public:
static FCDECL2(FC_BOOL_RET, IsInstanceOfType, ReflectClassBaseObject *pType, Object *object);
static FCDECL6(FC_BOOL_RET, SatisfiesConstraints, PTR_ReflectClassBaseObject pGenericParameter, TypeHandle *typeContextArgs, INT32 typeContextCount, TypeHandle *methodContextArgs, INT32 methodContextCount, PTR_ReflectClassBaseObject pGenericArgument);
static
FCDECL1(FC_BOOL_RET, HasInstantiation, PTR_ReflectClassBaseObject pType);
......@@ -255,6 +259,10 @@ public:
static
PVOID QCALLTYPE AllocateTypeAssociatedMemory(QCall::TypeHandle type, UINT32 size);
// Helper methods not called by managed code
static void ValidateTypeAbleToBeInstantiated(TypeHandle typeHandle, bool fGetUninitializedObject);
};
class RuntimeMethodHandle {
......
......@@ -601,6 +601,9 @@
<data name="Arg_NoAccessSpec" xml:space="preserve">
<value>Must specify binding flags describing the invoke operation required (BindingFlags.InvokeMethod CreateInstance GetField SetField GetProperty SetProperty).</value>
</data>
<data name="Arg_NoDefCTorWithoutTypeName" xml:space="preserve">
<value>No parameterless constructor defined.</value>
</data>
<data name="Arg_NoDefCTor" xml:space="preserve">
<value>No parameterless constructor defined for type '{0}'.</value>
</data>
......@@ -3748,4 +3751,7 @@
<data name="NotSupported_CodeBase" xml:space="preserve">
<value>CodeBase is not supported on assemblies loaded from a single-file bundle.</value>
</data>
<data name="Activator_CannotCreateInstance" xml:space="preserve">
<value>Cannot dynamically create an instance of type '{0}'. Reason: {1}</value>
</data>
</root>
......@@ -89,25 +89,13 @@ static bool ActivateCOMType()
return true;
}
catch (TargetInvocationException e)
catch (PlatformNotSupportedException) when (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && e.InnerException is PlatformNotSupportedException)
{
return true;
}
Console.WriteLine($"Caught unexpected {nameof(PlatformNotSupportedException)}: {e}");
return false;
return true;
}
catch(COMException e)
catch (COMException) when (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return true;
}
Console.WriteLine($"Caught unexpected {nameof(COMException)}: {e}");
return false;
return true;
}
catch (Exception e)
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册