Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IDynamicInterfaceCastable interface #37042

Merged
merged 14 commits into from Jun 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -223,6 +223,7 @@
<Compile Include="$(BclSourcesRoot)\System\Runtime\GCSettings.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComTypes\IEnumerable.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComTypes\IEnumerator.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\DynamicInterfaceCastableHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\Expando\IExpando.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\GCHandle.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\Marshal.CoreCLR.cs" />
Expand Down
Expand Up @@ -345,7 +345,8 @@ internal unsafe struct MethodTable
// Types that require non-trivial interface cast have this bit set in the category
private const uint enum_flag_NonTrivialInterfaceCast = 0x00080000 // enum_flag_Category_Array
| 0x40000000 // enum_flag_ComObject
| 0x00400000;// enum_flag_ICastable;
| 0x00400000 // enum_flag_ICastable;
| 0x00200000;// enum_flag_IDynamicInterfaceCastable;

private const int DebugClassNamePtr = // adjust for debug_m_szClassName
#if DEBUG
Expand Down
@@ -0,0 +1,44 @@
// 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.

namespace System.Runtime.InteropServices
{
/// <summary>
/// Helpers that allows VM to call into IDynamicInterfaceCastable methods without having to deal with RuntimeTypeHandle.
/// RuntimeTypeHandle is a struct and is always passed in stack in x86, which our VM call helpers don't
/// particularly like.
/// </summary>
internal static class DynamicInterfaceCastableHelpers
{
[Diagnostics.StackTraceHidden]
internal static bool IsInterfaceImplemented(IDynamicInterfaceCastable castable, RuntimeType interfaceType, bool throwIfNotImplemented)
{
bool isImplemented= castable.IsInterfaceImplemented(new RuntimeTypeHandle(interfaceType), throwIfNotImplemented);
if (!isImplemented && throwIfNotImplemented)
throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, castable.GetType(), interfaceType));

return isImplemented;
}

[Diagnostics.StackTraceHidden]
internal static RuntimeType? GetInterfaceImplementation(IDynamicInterfaceCastable castable, RuntimeType interfaceType)
{
RuntimeTypeHandle handle = castable.GetInterfaceImplementation(new RuntimeTypeHandle(interfaceType));
if (handle.Equals(default))
throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, castable.GetType(), interfaceType));

RuntimeType implType = handle.GetRuntimeType();
if (!implType.IsInterface)
throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_NotInterface, implType.ToString()));

if (!implType.IsDefined(typeof(DynamicInterfaceCastableImplementationAttribute), inherit: false))
throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_MissingImplementationAttribute, implType, nameof(DynamicInterfaceCastableImplementationAttribute)));

if (!implType.ImplementInterface(interfaceType))
throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_DoesNotImplementRequested, implType, interfaceType));

return implType;
}
}
}
1 change: 1 addition & 0 deletions src/coreclr/src/debug/daccess/nidump.cpp
Expand Up @@ -5503,6 +5503,7 @@ NativeImageDumper::EnumMnemonics s_MTFlagsHigh[] =

MTFLAG_ENTRY(HasFinalizer),
MTFLAG_ENTRY(IfNotInterfaceThenMarshalable),
MTFLAG_ENTRY(IDynamicInterfaceCastable),
#if defined(FEATURE_ICASTABLE)
MTFLAG_ENTRY(ICastable),
#endif
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/src/inc/dacvars.h
Expand Up @@ -193,6 +193,8 @@ DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_TypedReferenceMT, ::g_TypedRef
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pBaseCOMObject, ::g_pBaseCOMObject)
#endif

DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pIDynamicInterfaceCastableInterface, ::g_pIDynamicInterfaceCastableInterface)

#ifdef FEATURE_ICASTABLE
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pICastableInterface, ::g_pICastableInterface)
#endif // FEATURE_ICASTABLE
Expand Down
6 changes: 5 additions & 1 deletion src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs
Expand Up @@ -1777,7 +1777,11 @@ private TypeCompareState compareTypesForCast(CORINFO_CLASS_STRUCT_* fromClass, C

TypeCompareState result = TypeCompareState.May;

if (toType.IsNullable)
if (fromType.IsIDynamicInterfaceCastable)
{
result = TypeCompareState.May;
}
else if (toType.IsNullable)
{
// If casting to Nullable<T>, don't try to optimize
result = TypeCompareState.May;
Expand Down
Expand Up @@ -100,5 +100,13 @@ protected sealed internal override bool ComputeHasStaticConstructor(TypeDesc typ
}
return false;
}

protected sealed internal override bool IsIDynamicInterfaceCastableInterface(DefType type)
{
MetadataType t = (MetadataType)type;
return t.Module == SystemModule
&& t.Name == "IDynamicInterfaceCastable"
&& t.Namespace == "System.Runtime.InteropServices";
}
}
}
11 changes: 11 additions & 0 deletions src/coreclr/src/tools/Common/TypeSystem/Common/TypeDesc.cs
Expand Up @@ -657,5 +657,16 @@ public bool IsByRefLike
return (GetTypeFlags(TypeFlags.IsByRefLike | TypeFlags.AttributeCacheComputed) & TypeFlags.IsByRefLike) != 0;
}
}

/// <summary>
/// Gets a value indicating whether this type implements <code>IDynamicInterfaceCastable</code>
/// </summary>
public bool IsIDynamicInterfaceCastable
{
get
{
return (GetTypeFlags(TypeFlags.IsIDynamicInterfaceCastable | TypeFlags.IsIDynamicInterfaceCastableComputed) & TypeFlags.IsIDynamicInterfaceCastable) != 0;
}
}
}
}
3 changes: 3 additions & 0 deletions src/coreclr/src/tools/Common/TypeSystem/Common/TypeFlags.cs
Expand Up @@ -60,5 +60,8 @@ public enum TypeFlags
IsByRefLike = 0x04000,
AttributeCacheComputed = 0x08000,
IsIntrinsic = 0x10000,

IsIDynamicInterfaceCastable = 0x20000,
IsIDynamicInterfaceCastableComputed = 0x40000,
}
}
Expand Up @@ -778,12 +778,36 @@ internal TypeFlags ComputeTypeFlags(TypeDesc type, TypeFlags flags, TypeFlags ma
flags |= TypeFlags.HasStaticConstructorComputed;
}

// We are looking to compute IsIDynamicInterfaceCastable and we haven't yet assigned a value
if ((mask & TypeFlags.IsIDynamicInterfaceCastableComputed) == TypeFlags.IsIDynamicInterfaceCastableComputed)
{
TypeDesc typeDefinition = type.GetTypeDefinition();
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved
if (!typeDefinition.IsValueType)
{
foreach (DefType interfaceType in typeDefinition.RuntimeInterfaces)
{
if (IsIDynamicInterfaceCastableInterface(interfaceType))
{
flags |= TypeFlags.IsIDynamicInterfaceCastable;
break;
}
}
}

flags |= TypeFlags.IsIDynamicInterfaceCastableComputed;
}

return flags;
}

/// <summary>
/// Algorithm to control which types are considered to have static constructors
/// </summary>
protected internal abstract bool ComputeHasStaticConstructor(TypeDesc type);

/// <summary>
/// Determine if the type implements <code>IDynamicInterfaceCastable</code>
/// </summary>
protected internal abstract bool IsIDynamicInterfaceCastableInterface(DefType type);
}
}
1 change: 1 addition & 0 deletions src/coreclr/src/vm/CMakeLists.txt
Expand Up @@ -323,6 +323,7 @@ set(VM_SOURCES_WKS
autotrace.cpp
diagnosticserver.cpp
dllimportcallback.cpp
dynamicinterfacecastable.cpp
eeconfig.cpp
eecontract.cpp
eemessagebox.cpp
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/vm/amd64/asmconstants.h
Expand Up @@ -204,7 +204,7 @@ ASMCONSTANTS_C_ASSERT(METHODTABLE_EQUIVALENCE_FLAGS
#define METHODTABLE_EQUIVALENCE_FLAGS 0x0
#endif

#define METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS (0x00080000 + 0x40000000 + 0x00400000)
#define METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS (0x00080000 + 0x40000000 + 0x00400000 + 0x00200000)
ASMCONSTANTS_C_ASSERT(METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS
== MethodTable::enum_flag_NonTrivialInterfaceCast);

Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/src/vm/appdomain.cpp
Expand Up @@ -1608,6 +1608,8 @@ void SystemDomain::LoadBaseSystemClasses()
g_pBaseCOMObject = MscorlibBinder::GetClass(CLASS__COM_OBJECT);
#endif

g_pIDynamicInterfaceCastableInterface = MscorlibBinder::GetClass(CLASS__IDYNAMICINTERFACECASTABLE);

#ifdef FEATURE_ICASTABLE
g_pICastableInterface = MscorlibBinder::GetClass(CLASS__ICASTABLE);
#endif // FEATURE_ICASTABLE
Expand Down
90 changes: 90 additions & 0 deletions src/coreclr/src/vm/dynamicinterfacecastable.cpp
@@ -0,0 +1,90 @@
// 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.


#include "common.h"
#include "dynamicinterfacecastable.h"

namespace
{
BOOL CallIsInterfaceImplementated(OBJECTREF *objPROTECTED, const TypeHandle &interfaceTypeHandle, BOOL throwIfNotImplemented)
{
CONTRACT(BOOL) {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(objPROTECTED != NULL);
PRECONDITION(interfaceTypeHandle.IsInterface());
POSTCONDITION(!throwIfNotImplemented || RETVAL);
} CONTRACT_END;

PREPARE_NONVIRTUAL_CALLSITE(METHOD__DYNAMICINTERFACECASTABLEHELPERS__IS_INTERFACE_IMPLEMENTED);

OBJECTREF managedType = interfaceTypeHandle.GetManagedClassObject(); // GC triggers

DECLARE_ARGHOLDER_ARRAY(args, 3);
args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*objPROTECTED);
args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(managedType);
args[ARGNUM_2] = BOOL_TO_ARGHOLDER(throwIfNotImplemented);

BOOL isImplemented;
CALL_MANAGED_METHOD(isImplemented, CLR_BOOL, args);
INDEBUG(managedType = NULL); // managedType wasn't protected during the call

RETURN isImplemented;
}

OBJECTREF CallGetInterfaceImplementation(OBJECTREF *objPROTECTED, const TypeHandle &interfaceTypeHandle)
{
CONTRACT(OBJECTREF) {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(objPROTECTED != NULL);
PRECONDITION(interfaceTypeHandle.IsInterface());
POSTCONDITION(RETVAL != NULL);
} CONTRACT_END;

PREPARE_NONVIRTUAL_CALLSITE(METHOD__DYNAMICINTERFACECASTABLEHELPERS__GET_INTERFACE_IMPLEMENTATION);

OBJECTREF managedType = interfaceTypeHandle.GetManagedClassObject(); // GC triggers

DECLARE_ARGHOLDER_ARRAY(args, 2);
args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*objPROTECTED);
args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(managedType);

OBJECTREF implTypeRef;
CALL_MANAGED_METHOD_RETREF(implTypeRef, OBJECTREF, args);
INDEBUG(managedType = NULL); // managedType wasn't protected during the call

RETURN implTypeRef;
}
}

BOOL DynamicInterfaceCastable::IsInstanceOf(OBJECTREF *objPROTECTED, const TypeHandle &typeHandle, BOOL throwIfNotImplemented)
{
CONTRACT(BOOL) {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(objPROTECTED != NULL);
PRECONDITION(typeHandle.IsInterface());
} CONTRACT_END;

RETURN CallIsInterfaceImplementated(objPROTECTED, typeHandle, throwIfNotImplemented);
}

OBJECTREF DynamicInterfaceCastable::GetInterfaceImplementation(OBJECTREF *objPROTECTED, const TypeHandle &typeHandle)
{
CONTRACT(OBJECTREF) {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(objPROTECTED != NULL);
PRECONDITION(typeHandle.IsInterface());
POSTCONDITION(RETVAL != NULL);
} CONTRACT_END;

RETURN CallGetInterfaceImplementation(objPROTECTED, typeHandle);
}
16 changes: 16 additions & 0 deletions src/coreclr/src/vm/dynamicinterfacecastable.h
@@ -0,0 +1,16 @@
// 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.


#ifndef _DYNAMICINTERFACECASTABLE_H_
#define _DYNAMICINTERFACECASTABLE_H_

namespace DynamicInterfaceCastable
{
BOOL IsInstanceOf(OBJECTREF *objPROTECTED, const TypeHandle &interfaceTypeHandle, BOOL throwIfNotImplemented);

OBJECTREF GetInterfaceImplementation(OBJECTREF *objPROTECTED, const TypeHandle &interfaceTypeHandle);
}

#endif // _DYNAMICINTERFACECASTABLE_H_
12 changes: 9 additions & 3 deletions src/coreclr/src/vm/jithelpers.cpp
Expand Up @@ -25,6 +25,7 @@
#include "comdelegate.h"
#include "corprof.h"
#include "eeprofinterfaces.h"
#include "dynamicinterfacecastable.h"

#ifndef TARGET_UNIX
// Included for referencing __report_gsfailure
Expand Down Expand Up @@ -2116,12 +2117,12 @@ BOOL ObjIsInstanceOfCore(Object *pObject, TypeHandle toTypeHnd, BOOL throwCastEx
{
fCast = TRUE;
}
else
else if (toTypeHnd.IsInterface())
{
#ifdef FEATURE_COMINTEROP
// If we are casting a COM object from interface then we need to do a check to see
// if it implements the interface.
if (toTypeHnd.IsInterface() && pMT->IsComObjectType())
if (pMT->IsComObjectType())
{
fCast = ComObject::SupportsInterface(obj, toTypeHnd.AsMethodTable());
}
Expand All @@ -2130,7 +2131,7 @@ BOOL ObjIsInstanceOfCore(Object *pObject, TypeHandle toTypeHnd, BOOL throwCastEx
#ifdef FEATURE_ICASTABLE
// If type implements ICastable interface we give it a chance to tell us if it can be casted
// to a given type.
if (toTypeHnd.IsInterface() && pMT->IsICastable())
if (pMT->IsICastable())
{
// Make actuall call to ICastableHelpers.IsInstanceOfInterface(obj, interfaceTypeObj, out exception)
OBJECTREF exception = NULL;
Expand All @@ -2154,7 +2155,12 @@ BOOL ObjIsInstanceOfCore(Object *pObject, TypeHandle toTypeHnd, BOOL throwCastEx
}
GCPROTECT_END(); //exception
}
else
#endif // FEATURE_ICASTABLE
if (pMT->IsIDynamicInterfaceCastable())
{
fCast = DynamicInterfaceCastable::IsInstanceOf(&obj, toTypeHnd, throwCastException);
}
}

if (!fCast && throwCastException)
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/src/vm/jitinterface.cpp
Expand Up @@ -4501,8 +4501,8 @@ TypeCompareState CEEInfo::compareTypesForCast(
else
#endif // FEATURE_COMINTEROP

// If casting from ICastable, don't try to optimize
if (fromHnd.GetMethodTable()->IsICastable())
// If casting from ICastable or IDynamicInterfaceCastable, don't try to optimize
if (fromHnd.GetMethodTable()->IsICastable() || fromHnd.GetMethodTable()->IsIDynamicInterfaceCastable())
{
result = TypeCompareState::May;
}
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/src/vm/metasig.h
Expand Up @@ -589,6 +589,9 @@ DEFINE_METASIG_T(SM(ICastable_RtType_RefException_RetBool, C(ICASTABLE) C(CLASS)
DEFINE_METASIG_T(SM(ICastable_RtType_RetRtType, C(ICASTABLE) C(CLASS), C(CLASS)))
#endif // FEATURE_ICASTABLE

DEFINE_METASIG_T(SM(IDynamicInterfaceCastable_RuntimeType_Bool_RetBool, C(IDYNAMICINTERFACECASTABLE) C(CLASS) F, F))
DEFINE_METASIG_T(SM(IDynamicInterfaceCastable_RuntimeType_RetRtType, C(IDYNAMICINTERFACECASTABLE) C(CLASS), C(CLASS)))

DEFINE_METASIG_T(IM(ArrByte_Int_Int_AsyncCallback_Object_RetIAsyncResult, a(b) i i C(ASYNCCALLBACK) j, C(IASYNCRESULT)))
DEFINE_METASIG_T(IM(IAsyncResult_RetInt, C(IASYNCRESULT), i))
DEFINE_METASIG_T(IM(IAsyncResult_RetVoid, C(IASYNCRESULT), v))
Expand Down