summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt10
-rw-r--r--src/debug/daccess/nidump.cpp6
-rw-r--r--src/inc/dacvars.h4
-rw-r--r--src/mscorlib/model.xml58
-rw-r--r--src/mscorlib/mscorlib.shared.sources.props1
-rw-r--r--src/mscorlib/src/System/Runtime/CompilerServices/ICastable.cs63
-rw-r--r--src/vm/amd64/asmconstants.h8
-rw-r--r--src/vm/appdomain.cpp4
-rw-r--r--src/vm/i386/asmconstants.h7
-rw-r--r--src/vm/jithelpers.cpp44
-rw-r--r--src/vm/metasig.h7
-rw-r--r--src/vm/methodtable.cpp56
-rw-r--r--src/vm/methodtable.h23
-rw-r--r--src/vm/methodtablebuilder.cpp27
-rw-r--r--src/vm/mscorlib.h8
-rw-r--r--src/vm/prestub.cpp29
-rw-r--r--src/vm/vars.cpp5
-rw-r--r--src/vm/vars.hpp4
-rw-r--r--src/vm/virtualcallstub.cpp132
-rw-r--r--src/vm/virtualcallstub.h6
-rw-r--r--tests/src/Interop/ICastable/Castable.cs219
-rw-r--r--tests/src/Interop/ICastable/Castable.csproj42
-rw-r--r--tests/src/Interop/ICastable/app.config7
-rw-r--r--tests/src/Interop/ICastable/packages.config3
-rw-r--r--tests/src/dir.targets3
25 files changed, 662 insertions, 114 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 217e408af7..952006812b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -378,7 +378,7 @@ else ()
endif (IS_64BIT_BUILD EQUAL 1)
if(WIN32)
-add_definitions(-D_CRT_SECURE_NO_WARNINGS)
+ add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif(WIN32)
add_definitions(-DNTMAKEENV)
add_definitions(-D_BLD_CLR)
@@ -397,17 +397,19 @@ if (CLR_CMAKE_PLATFORM_UNIX)
endif(CLR_CMAKE_PLATFORM_UNIX)
add_definitions(-DFEATURE_ASYNC_IO)
add_definitions(-DFEATURE_BCL_FORMATTING)
-if(WIN32)
- add_definitions(-DFEATURE_CLASSIC_COMINTEROP)
-endif(WIN32)
add_definitions(-DFEATURE_COLLECTIBLE_TYPES)
+
if(WIN32)
+ add_definitions(-DFEATURE_CLASSIC_COMINTEROP)
add_definitions(-DFEATURE_APPX)
add_definitions(-DFEATURE_COMINTEROP)
add_definitions(-DFEATURE_COMINTEROP_APARTMENT_SUPPORT)
add_definitions(-DFEATURE_COMINTEROP_UNMANAGED_ACTIVATION)
add_definitions(-DFEATURE_COMINTEROP_WINRT_MANAGED_ACTIVATION)
endif(WIN32)
+
+add_definitions(-DFEATURE_ICASTABLE)
+
add_definitions(-DFEATURE_CORECLR)
add_definitions(-DFEATURE_CORESYSTEM)
add_definitions(-DFEATURE_CORRUPTING_EXCEPTIONS)
diff --git a/src/debug/daccess/nidump.cpp b/src/debug/daccess/nidump.cpp
index 3d60db3d57..470643d252 100644
--- a/src/debug/daccess/nidump.cpp
+++ b/src/debug/daccess/nidump.cpp
@@ -5669,7 +5669,7 @@ NativeImageDumper::EnumMnemonics s_MTFlagsLow[] =
#if defined(FEATURE_REMOTING)
MTFLAG_ENTRY(ContextStatic),
#endif
- MTFLAG_ENTRY(UNUSED_ComponentSize_2),
+ MTFLAG_ENTRY(HasRemotingVtsInfo),
MTFLAG_ENTRY(HasVariance),
MTFLAG_ENTRY(HasDefaultCtor),
MTFLAG_ENTRY(HasPreciseInitCctors),
@@ -5717,7 +5717,9 @@ NativeImageDumper::EnumMnemonics s_MTFlagsHigh[] =
#if defined(FEATURE_COMINTEROP)
MTFLAG_ENTRY(IfInterfaceThenHasGuidInfo),
#endif
- MTFLAG_ENTRY(HasRemotingVtsInfo),
+#if defined(FEATURE_ICASTABLE)
+ MTFLAG_ENTRY(ICastable),
+#endif
MTFLAG_ENTRY(HasIndirectParent),
MTFLAG_ENTRY(ContainsPointers),
MTFLAG_ENTRY(HasTypeEquivalence),
diff --git a/src/inc/dacvars.h b/src/inc/dacvars.h
index 35006f1aa5..560d63a795 100644
--- a/src/inc/dacvars.h
+++ b/src/inc/dacvars.h
@@ -234,6 +234,10 @@ DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pBaseCOMObject, ::g_pBaseCOMOb
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pBaseRuntimeClass, ::g_pBaseRuntimeClass)
#endif
+#ifdef FEATURE_ICASTABLE
+DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pICastableInterface, ::g_pICastableInterface)
+#endif // FEATURE_ICASTABLE
+
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pPrepareConstrainedRegionsMethod, ::g_pPrepareConstrainedRegionsMethod)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pExecuteBackoutCodeHelperMethod, ::g_pExecuteBackoutCodeHelperMethod)
diff --git a/src/mscorlib/model.xml b/src/mscorlib/model.xml
index d3ae0262a0..63fed69d46 100644
--- a/src/mscorlib/model.xml
+++ b/src/mscorlib/model.xml
@@ -1007,7 +1007,7 @@
<Member MemberType="Property" Name="Default" />
<Member Name="GetAssemblyName(System.String)" />
<Member Name="GetLoadContext(System.Reflection.Assembly)" />
- <Member Name="InitializeDefaultContext(System.Runtime.Loader.AssemblyLoadContext)" />
+ <Member Name="InitializeDefaultContext(System.Runtime.Loader.AssemblyLoadContext)" />
<Member Name="LoadFromAssemblyName(System.Reflection.AssemblyName)" />
<Member Name="Load(System.Reflection.AssemblyName)" />
<Member Name="LoadFromAssemblyPath(System.String)" />
@@ -2039,7 +2039,7 @@
</Type>
<Type Name="System.ExecutionEngineException" />
- <Type Name="System.FieldAccessException">
+ <Type Name="System.FieldAccessException">
<Member Name="#ctor" />
<Member Name="#ctor(System.String)" />
<Member Name="#ctor(System.String,System.Exception)" />
@@ -5750,6 +5750,10 @@
</Type>
<Type Name="System.Runtime.CompilerServices.IsCopyConstructed"> <!-- for MC++ -->
</Type>
+ <Type Name="System.Runtime.CompilerServices.ICastable">
+ <Member Name="IsInstanceOfInterface(System.RuntimeTypeHandle,System.Exception@)" /> <!-- EE -->
+ <Member Name="GetImplType(System.RuntimeTypeHandle)" /> <!-- EE -->
+ </Type>
<Type Name="System.Runtime.CompilerServices.IsExplicitlyDereferenced"> <!-- for MC++ -->
</Type>
<Type Name="System.Runtime.CompilerServices.IsImplicitlyDereferenced"> <!-- for MC++ -->
@@ -9772,19 +9776,19 @@
<Member MemberType="Field" Name="Xml" />
</Type>
- <Type Name="System.Diagnostics.Tracing.EventDescriptor">
- <Member Name="#ctor(System.Int32,System.Byte,System.Byte,System.Int64)" />
+ <Type Name="System.Diagnostics.Tracing.EventDescriptor">
+ <Member Name="#ctor(System.Int32,System.Byte,System.Byte,System.Int64)" />
<Member Name="#ctor(System.Int32,System.Byte,System.Byte,System.Byte,System.Byte,System.Int32,System.Int64)" />
<Member Name="get_EventId" />
<Member Name="get_Channel" />
<Member Name="get_Level" />
<Member Name="get_Opcode" />
- <Member Name="get_Task" />
- <Member Name="get_Version" />
- <Member Name="Equals(System.Object)" />
- <Member Name="Equals(System.Diagnostics.Tracing.EventDescriptor)" />
- </Type>
-
+ <Member Name="get_Task" />
+ <Member Name="get_Version" />
+ <Member Name="Equals(System.Object)" />
+ <Member Name="Equals(System.Diagnostics.Tracing.EventDescriptor)" />
+ </Type>
+
<Type Name="System.Diagnostics.Tracing.EventChannel">
<Member MemberType="Field" Name="Admin" />
<Member MemberType="Field" Name="Analytic" />
@@ -9810,7 +9814,7 @@
<Type Name="System.Diagnostics.Tracing.EventDataAttribute">
<Member Name="#ctor" />
<Member MemberType="Property" Name="Name" />
- <Member MemberType="Property" Name="Level" />
+ <Member MemberType="Property" Name="Level" />
<Member MemberType="Property" Name="Opcode" />
<Member MemberType="Property" Name="Keywords" />
<Member MemberType="Property" Name="Tags" />
@@ -9849,7 +9853,7 @@
<Member Name="OnEventWritten(System.Diagnostics.Tracing.EventWrittenEventArgs)" />
</Type>
- <Type Name="System.Diagnostics.Tracing.EventManifestOptions">
+ <Type Name="System.Diagnostics.Tracing.EventManifestOptions">
<Member MemberType="Field" Name="AllCultures" />
<Member MemberType="Field" Name="AllowEventSourceOverride" />
<Member MemberType="Field" Name="None" />
@@ -9874,11 +9878,11 @@
<Type Name="System.Diagnostics.Tracing.EventSource">
<Member Name="#ctor" />
<Member Name="#ctor(System.Boolean)" />
- <Member Name="#ctor(System.Diagnostics.Tracing.EventSourceSettings)" />
- <Member Name="#ctor(System.Diagnostics.Tracing.EventSourceSettings,System.String[])" />
- <Member Name="#ctor(System.String)" />
- <Member Name="#ctor(System.String,System.Diagnostics.Tracing.EventSourceSettings)" />
- <Member Name="#ctor(System.String,System.Diagnostics.Tracing.EventSourceSettings,System.String[])" />
+ <Member Name="#ctor(System.Diagnostics.Tracing.EventSourceSettings)" />
+ <Member Name="#ctor(System.Diagnostics.Tracing.EventSourceSettings,System.String[])" />
+ <Member Name="#ctor(System.String)" />
+ <Member Name="#ctor(System.String,System.Diagnostics.Tracing.EventSourceSettings)" />
+ <Member Name="#ctor(System.String,System.Diagnostics.Tracing.EventSourceSettings,System.String[])" />
<Member Name="get_Guid" />
<Member Name="get_Name" />
<Member MemberType="Property" Name="ConstructionException" />
@@ -9889,7 +9893,7 @@
<Member Name="GenerateManifest(System.Type,System.String,System.Diagnostics.Tracing.EventManifestOptions)" />
<Member Name="GetGuid(System.Type)" />
<Member Name="GetName(System.Type)" />
- <Member Name="GetTrait(System.String)" />
+ <Member Name="GetTrait(System.String)" />
<Member Name="GetSources" />
<Member Name="IsEnabled" />
<Member Name="IsEnabled(System.Diagnostics.Tracing.EventLevel,System.Diagnostics.Tracing.EventKeywords)" />
@@ -9927,7 +9931,7 @@
<Member Name="WriteEventCore(System.Int32,System.Int32,System.Diagnostics.Tracing.EventSource+EventData*)" />
<Member Name="WriteEventWithRelatedActivityId(System.Int32,System.Guid,System.Object[])" />
<Member Name="WriteEventWithRelatedActivityIdCore(System.Int32,System.Guid*,System.Int32,System.Diagnostics.Tracing.EventSource+EventData*)" />
- <Member Name="get_Settings" />
+ <Member Name="get_Settings" />
</Type>
<Type Name="System.Diagnostics.Tracing.EventSource+EventData">
@@ -9979,8 +9983,8 @@
<Member MemberType="Field" Name="None" />
</Type>
- <Type Name="System.Diagnostics.Tracing.EventFieldAttribute">
- <Member Name="#ctor" />
+ <Type Name="System.Diagnostics.Tracing.EventFieldAttribute">
+ <Member Name="#ctor" />
<Member MemberType="Property" Name="Tags" />
<Member MemberType="Property" Name="Name" />
<Member MemberType="Property" Name="Format" />
@@ -9995,10 +9999,10 @@
<Member Name="get_Message" />
<Member Name="get_Opcode" />
<Member Name="get_Payload" />
- <Member Name="get_PayloadNames" />
+ <Member Name="get_PayloadNames" />
<Member Name="get_Task" />
<Member Name="get_Version" />
- <Member Name="get_Tags" />
+ <Member Name="get_Tags" />
<Member MemberType="Property" Name="ActivityId" />
<Member MemberType="Property" Name="EventName" />
<Member MemberType="Property" Name="RelatedActivityId" />
@@ -11667,10 +11671,10 @@
<Member Status="ImplRoot" Name="CreateUnwrapPromise&lt;TResult&gt;(System.Threading.Tasks.Task,System.Boolean)" />
<Member Status="ImplRoot" Name="NotifyDebuggerOfWaitCompletion" />
<Member Status="ImplRoot" Name="SetNotificationForWaitCompletion(System.Boolean)" />
- <!--Internal helper members exposed to be used by VS Debugger-->
- <Member Status="ImplRoot" Name="GetDelegateContinuationsForDebugger" />
- <Member Status="ImplRoot" Name="GetActiveTaskFromId(System.Int32)" />
- <Member Status="ImplRoot" Name="GetActiveTasks" />
+ <!--Internal helper members exposed to be used by VS Debugger-->
+ <Member Status="ImplRoot" Name="GetDelegateContinuationsForDebugger" />
+ <Member Status="ImplRoot" Name="GetActiveTaskFromId(System.Int32)" />
+ <Member Status="ImplRoot" Name="GetActiveTasks" />
</Type>
<Type Status="ImplRoot" Name="System.Threading.Tasks.SystemThreadingTasks_TaskDebugView">
<Member Status="ImplRoot" Name="#ctor(System.Threading.Tasks.Task)" />
diff --git a/src/mscorlib/mscorlib.shared.sources.props b/src/mscorlib/mscorlib.shared.sources.props
index e94fa2f0e1..e75b0df61d 100644
--- a/src/mscorlib/mscorlib.shared.sources.props
+++ b/src/mscorlib/mscorlib.shared.sources.props
@@ -41,6 +41,7 @@
<CompilerServicesSources Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\SpecialNameAttribute.cs" />
<CompilerServicesSources Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\SuppressMergeCheckAttribute.cs" />
<CompilerServicesSources Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\IsCopyConstructed.cs" />
+ <CompilerServicesSources Condition="'$(FeatureICastable)' == 'true'" Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\ICastable.cs" />
<CompilerServicesSources Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\SuppressIldasmAttribute.cs" />
<CompilerServicesSources Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\NativeCppClassAttribute.cs" />
<CompilerServicesSources Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\DecoratedNameAttribute.cs" />
diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/ICastable.cs b/src/mscorlib/src/System/Runtime/CompilerServices/ICastable.cs
new file mode 100644
index 0000000000..bbc53e5f62
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/CompilerServices/ICastable.cs
@@ -0,0 +1,63 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+
+
+//
+// Support for dynamic interface casting. Specifically implementing this interface on a type will allow the
+// type to support interfaces (for the purposes of casting and interface dispatch) that do not appear in its
+// interface map.
+//
+
+using System;
+
+namespace System.Runtime.CompilerServices
+{
+ public interface ICastable
+ {
+ // This is called if casting this object to the given interface type would otherwise fail. Casting
+ // here means the IL isinst and castclass instructions in the case where they are given an interface
+ // type as the target type.
+ //
+ // A return value of true indicates the cast is valid.
+ //
+ // If false is returned when this is called as part of a castclass then the usual InvalidCastException
+ // will be thrown unless an alternate exception is assigned to the castError output parameter. This
+ // parameter is ignored on successful casts or during the evaluation of an isinst (which returns null
+ // rather than throwing on error).
+ //
+ // No exception should be thrown from this method (it will cause unpredictable effects, including the
+ // possibility of an immediate failfast).
+ //
+ // The results of this call are not cached, so it is advisable to provide a performant implementation.
+ //
+ // The results of this call should be invariant for the same class, interface type pair. That is
+ // because this is the only guard placed before an interface invocation at runtime. If a type decides
+ // it no longer wants to implement a given interface it has no way to synchronize with callers that
+ // have already cached this relationship and can invoke directly via the interface pointer.
+ bool IsInstanceOfInterface(RuntimeTypeHandle interfaceType, out Exception castError);
+
+ // This is called as part of the interface dispatch mechanism when the dispatcher logic cannot find
+ // the given interface type in the interface map of this object.
+ //
+ // It allows the implementor to return an alternate class type which does implement the interface. The
+ // interface lookup shall be performed again on this type (failure to find the interface this time
+ // resulting in a fail fast) and the corresponding implemented method on that class called instead.
+ //
+ // Naturally, since the call is dispatched to a method on a class which does not match the type of the
+ // this pointer, extreme care must be taken in the implementation of the interface methods of this
+ // surrogate type.
+ //
+ // No exception should be thrown from this method (it will cause unpredictable effects, including the
+ // possibility of an immediate failfast).
+ //
+ // There is no error path defined here. By construction all interface dispatches will already have
+ // been verified via the castclass/isinst mechanism (and thus a call to IsInstanceOfInterface above)
+ // so this method is expected to succeed in all cases. The contract for interface dispatch does not
+ // include any errors from the infrastructure, of which this is a part.
+ //
+ // The results of this lookup are cached so computation of the result is not as perf-sensitive as
+ // IsInstanceOfInterface.
+ RuntimeTypeHandle GetImplType(RuntimeTypeHandle interfaceType);
+ }
+}
diff --git a/src/vm/amd64/asmconstants.h b/src/vm/amd64/asmconstants.h
index ed2e284a62..e356c9bfa9 100644
--- a/src/vm/amd64/asmconstants.h
+++ b/src/vm/amd64/asmconstants.h
@@ -290,15 +290,9 @@ ASMCONSTANTS_C_ASSERT(METHODTABLE_EQUIVALENCE_FLAGS
#define METHODTABLE_EQUIVALENCE_FLAGS 0x0
#endif
-#ifdef FEATURE_COMINTEROP
-#define METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS 0x40080000
+#define METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS (0x00080000 + 0x40000000 + 0x00400000)
ASMCONSTANTS_C_ASSERT(METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS
== MethodTable::enum_flag_NonTrivialInterfaceCast);
-#else
-#define METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS 0x00080000
-ASMCONSTANTS_C_ASSERT(METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS
- == MethodTable::enum_flag_NonTrivialInterfaceCast);
-#endif
#define MethodTable__enum_flag_ContainsPointers 0x01000000
ASMCONSTANTS_C_ASSERT(MethodTable__enum_flag_ContainsPointers
diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp
index de343848be..8e84eb0c6d 100644
--- a/src/vm/appdomain.cpp
+++ b/src/vm/appdomain.cpp
@@ -2979,6 +2979,10 @@ void SystemDomain::LoadBaseSystemClasses()
#endif // _DEBUG
#endif
+#ifdef FEATURE_ICASTABLE
+ g_pICastableInterface = MscorlibBinder::GetClass(CLASS__ICASTABLE);
+#endif // FEATURE_ICASTABLE
+
// Load a special marker method used to detect Constrained Execution Regions
// at jit time.
g_pPrepareConstrainedRegionsMethod = MscorlibBinder::GetMethod(METHOD__RUNTIME_HELPERS__PREPARE_CONSTRAINED_REGIONS);
diff --git a/src/vm/i386/asmconstants.h b/src/vm/i386/asmconstants.h
index 748569a8a9..530f9da34d 100644
--- a/src/vm/i386/asmconstants.h
+++ b/src/vm/i386/asmconstants.h
@@ -418,13 +418,8 @@ ASMCONSTANTS_C_ASSERT(ComPlusCallInfo__m_pRetThunk == offsetof(ComPlusCallInfo,
#endif // FEATURE_COMINTEROP
-#ifdef FEATURE_COMINTEROP
-#define NonTrivialInterfaceCastFlags 0x40080000
-ASMCONSTANTS_C_ASSERT(NonTrivialInterfaceCastFlags == MethodTable::public_enum_flag_NonTrivialInterfaceCast)
-#else
-#define NonTrivialInterfaceCastFlags 0x00080000
+#define NonTrivialInterfaceCastFlags (0x00080000 + 0x40000000 + 0x00400000)
ASMCONSTANTS_C_ASSERT(NonTrivialInterfaceCastFlags == MethodTable::public_enum_flag_NonTrivialInterfaceCast)
-#endif
#define ASM__VTABLE_SLOTS_PER_CHUNK 8
ASMCONSTANTS_C_ASSERT(ASM__VTABLE_SLOTS_PER_CHUNK == VTABLE_SLOTS_PER_CHUNK)
diff --git a/src/vm/jithelpers.cpp b/src/vm/jithelpers.cpp
index 71a36b0bab..d93228d744 100644
--- a/src/vm/jithelpers.cpp
+++ b/src/vm/jithelpers.cpp
@@ -2360,8 +2360,11 @@ TypeHandle::CastResult STDCALL ObjIsInstanceOfNoGC(Object *pObject, TypeHandle t
return TypeHandle::CanCast;
if (pMT->IsTransparentProxy() ||
- pMT->IsComObjectType() && toTypeHnd.IsInterface())
+ (toTypeHnd.IsInterface() && ( pMT->IsComObjectType() || pMT->IsICastable() ))
+ )
+ {
return TypeHandle::MaybeCast;
+ }
if (pMT->IsArray())
{
@@ -2439,6 +2442,35 @@ BOOL ObjIsInstanceOf(Object *pObject, TypeHandle toTypeHnd)
// allow an object of type T to be cast to Nullable<T> (they have the same representation)
fCast = TRUE;
}
+#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.
+ else if (toTypeHnd.IsInterface() && fromTypeHnd.GetMethodTable()->IsICastable())
+ {
+ // Make actuall call to obj.IsInstanceOfInterface(interfaceTypeObj, out exception)
+ OBJECTREF exception = NULL;
+ GCPROTECT_BEGIN(exception);
+ MethodTable *pFromTypeMT = fromTypeHnd.GetMethodTable();
+ MethodDesc *pIsInstanceOfMD = pFromTypeMT->GetMethodDescForInterfaceMethod(MscorlibBinder::GetMethod(METHOD__ICASTABLE__ISINSTANCEOF)); //GC triggers
+ OBJECTREF managedType = toTypeHnd.GetManagedClassObject(); //GC triggers
+
+ PREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC(pIsInstanceOfMD);
+
+ DECLARE_ARGHOLDER_ARRAY(args, 3);
+ args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(obj);
+ args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(managedType);
+ args[ARGNUM_2] = PTR_TO_ARGHOLDER(&exception);
+
+ CALL_MANAGED_METHOD(fCast, BOOL, args);
+ INDEBUG(managedType = NULL); // managedType isn't protected during the call
+
+ if (exception != NULL)
+ {
+ RealCOMPlusThrow(exception);
+ }
+ GCPROTECT_END(); //exception
+ }
+#endif // FEATURE_ICASTABLE
GCPROTECT_END();
@@ -5033,12 +5065,12 @@ HCIMPL1(void, IL_Throw, Object* obj)
// then do not clear the "_stackTrace" field of the exception object.
if (GetThread()->GetExceptionState()->IsRaisingForeignException())
{
- ((EXCEPTIONREF)oref)->SetStackTraceString(NULL);
+ ((EXCEPTIONREF)oref)->SetStackTraceString(NULL);
}
else
#endif // defined(FEATURE_EXCEPTIONDISPATCHINFO)
{
- ((EXCEPTIONREF)oref)->ClearStackTracePreservingRemoteStackTrace();
+ ((EXCEPTIONREF)oref)->ClearStackTracePreservingRemoteStackTrace();
}
}
@@ -5297,9 +5329,9 @@ void DoJITFailFast ()
#if defined(_TARGET_X86_)
__report_gsfailure();
#else // !defined(_TARGET_X86_)
- // On AMD64/IA64/ARM, we need to pass a stack cookie, which will be saved in the context record
- // that is used to raise the buffer-overrun exception by __report_gsfailure.
- __report_gsfailure((ULONG_PTR)0);
+ // On AMD64/IA64/ARM, we need to pass a stack cookie, which will be saved in the context record
+ // that is used to raise the buffer-overrun exception by __report_gsfailure.
+ __report_gsfailure((ULONG_PTR)0);
#endif // defined(_TARGET_X86_)
#else // FEATURE_PAL
if(ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, FailFast))
diff --git a/src/vm/metasig.h b/src/vm/metasig.h
index 03f7f501d5..6baf098ddb 100644
--- a/src/vm/metasig.h
+++ b/src/vm/metasig.h
@@ -347,9 +347,9 @@ DEFINE_METASIG_T(SM(FrameSecurityDescriptor_PMS_OutPMS_RuntimeMethodHandleIntern
C(FRAME_SECURITY_DESCRIPTOR) C(PERMISSION_SET) r(C(PERMISSION_SET)) g(METHOD_HANDLE_INTERNAL), F))
DEFINE_METASIG_T(SM(FrameSecurityDescriptor_RetInt, C(FRAME_SECURITY_DESCRIPTOR), i))
DEFINE_METASIG_T(SM(DynamicResolver_IPermission_PermissionToken_RuntimeMethodHandleInternal_RetBool, \
- C(DYNAMICRESOLVER) C(IPERMISSION) C(PERMISSION_TOKEN) g(METHOD_HANDLE_INTERNAL), F))
+ C(DYNAMICRESOLVER) C(IPERMISSION) C(PERMISSION_TOKEN) g(METHOD_HANDLE_INTERNAL), F))
DEFINE_METASIG_T(SM(DynamicResolver_PMS_OutPMS_RuntimeMethodHandleInternal_RetBool, \
- C(DYNAMICRESOLVER) C(PERMISSION_SET) r(C(PERMISSION_SET)) g(METHOD_HANDLE_INTERNAL), F))
+ C(DYNAMICRESOLVER) C(PERMISSION_SET) r(C(PERMISSION_SET)) g(METHOD_HANDLE_INTERNAL), F))
DEFINE_METASIG_T(SM(PermissionListSet_PMS_PMS_RetPermissionListSet, \
C(PERMISSION_LIST_SET) C(PERMISSION_SET) C(PERMISSION_SET), C(PERMISSION_LIST_SET)))
DEFINE_METASIG_T(SM(PMS_IntPtr_RuntimeMethodHandleInternal_Assembly_SecurityAction_RetVoid, C(PERMISSION_SET) I g(METHOD_HANDLE_INTERNAL) C(ASSEMBLY) g(SECURITY_ACTION), v))
@@ -675,6 +675,9 @@ DEFINE_METASIG(SM(RefObject_Object_Object_RetObject, r(j) j j, j))
DEFINE_METASIG_T(SM(RefCleanupWorkList_RetVoid, r(C(CLEANUP_WORK_LIST)), v))
DEFINE_METASIG_T(SM(RefCleanupWorkList_SafeHandle_RetIntPtr, r(C(CLEANUP_WORK_LIST)) C(SAFE_HANDLE), I))
+DEFINE_METASIG_T(IM(RuntimeTypeHandle_RefException_RetBool, g(RT_TYPE_HANDLE) r(C(EXCEPTION)), F))
+DEFINE_METASIG_T(IM(RuntimeTypeHandle_RetRuntimeTypeHandle, g(RT_TYPE_HANDLE), g(RT_TYPE_HANDLE)))
+
// Undefine macros in case we include the file again in the compilation unit
#undef DEFINE_METASIG
diff --git a/src/vm/methodtable.cpp b/src/vm/methodtable.cpp
index 0d95a9c884..644b9e60e0 100644
--- a/src/vm/methodtable.cpp
+++ b/src/vm/methodtable.cpp
@@ -633,6 +633,25 @@ void MethodTable::SetHasTypeEquivalence()
}
#endif
+#ifdef FEATURE_ICASTABLE
+void MethodTable::SetICastable()
+{
+ LIMITED_METHOD_CONTRACT;
+ SetFlag(enum_flag_ICastable);
+}
+#endif
+
+BOOL MethodTable::IsICastable()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+#ifdef FEATURE_ICASTABLE
+ return GetFlag(enum_flag_ICastable);
+#else
+ return FALSE;
+#endif
+}
+
+
#endif // !DACCESS_COMPILE
//==========================================================================================
@@ -776,6 +795,43 @@ PTR_MethodTable InterfaceInfo_t::GetApproxMethodTable(Module * pContainingModule
RETURN(pItfMD);
}
+#ifdef FEATURE_ICASTABLE
+ // In case of ICastable, instead of trying to find method implementation in the real object type
+ // we call pObj.GetValueInternal() and call GetMethodDescForInterfaceMethod() again with whatever type it returns.
+ // It allows objects that implement ICastable to mimic behavior of other types.
+ if (pServerMT->IsICastable() &&
+ !pItfMD->HasMethodInstantiation() &&
+ !TypeHandle(pServerMT).CanCastTo(ownerType)) // we need to make sure object doesn't implement this interface in a natural way
+ {
+ GCStress<cfg_any>::MaybeTrigger();
+
+ // Make call to obj.GetImplType(interfaceTypeObj)
+ MethodDesc *pGetImplTypeMD = pServerMT->GetMethodDescForInterfaceMethod(MscorlibBinder::GetMethod(METHOD__ICASTABLE__GETIMPLTYPE));
+ OBJECTREF ownerManagedType = ownerType.GetManagedClassObject(); //GC triggers
+
+ PREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC(pGetImplTypeMD);
+
+ DECLARE_ARGHOLDER_ARRAY(args, 2);
+ args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*pServer);
+ args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(ownerManagedType);
+
+ OBJECTREF impTypeObj = NULL;
+ CALL_MANAGED_METHOD_RETREF(impTypeObj, OBJECTREF, args);
+
+ INDEBUG(ownerManagedType = NULL); //ownerManagedType wasn't protected during the call
+ if (impTypeObj == NULL) // GetImplType returns default(RuntimeTypeHandle)
+ {
+ COMPlusThrow(kEntryPointNotFoundException);
+ }
+
+ ReflectClassBaseObject* resultTypeObj = ((ReflectClassBaseObject*)OBJECTREFToObject(impTypeObj));
+ TypeHandle resulTypeHnd = resultTypeObj->GetType();
+ MethodTable *pResultMT = resulTypeHnd.GetMethodTable();
+
+ RETURN(pResultMT->GetMethodDescForInterfaceMethod(ownerType, pItfMD));
+ }
+#endif
+
#ifdef FEATURE_COMINTEROP
if (pServerMT->IsComObjectType() && !pItfMD->HasMethodInstantiation())
{
diff --git a/src/vm/methodtable.h b/src/vm/methodtable.h
index 8a312132ee..8e6a59b6b3 100644
--- a/src/vm/methodtable.h
+++ b/src/vm/methodtable.h
@@ -856,6 +856,12 @@ public:
}
#endif // !FEATURE_COMINTEROP
+#ifdef FEATURE_ICASTABLE
+ void SetICastable();
+#endif
+
+ BOOL IsICastable(); // This type implements ICastable interface
+
#ifdef FEATURE_TYPEEQUIVALENCE
// type has opted into type equivalence or is instantiated by/derived from a type that is
BOOL HasTypeEquivalence()
@@ -3762,7 +3768,7 @@ private:
#ifdef FEATURE_REMOTING
enum_flag_ContextStatic = 0x00000040,
#endif
- enum_flag_UNUSED_ComponentSize_2 = 0x00000080,
+ enum_flag_HasRemotingVtsInfo = 0x00000080, // Optional data present indicating VTS methods and optional fields
enum_flag_HasVariance = 0x00000100, // This is an instantiated type some of whose type parameters are co or contra-variant
@@ -3843,7 +3849,7 @@ private:
enum_flag_IfInterfaceThenHasGuidInfo = 0x00200000, // Does the type has optional GuidInfo
#endif // FEATURE_COMINTEROP
- enum_flag_HasRemotingVtsInfo = 0x00400000, // Optional data present indicating VTS methods and optional fields
+ enum_flag_ICastable = 0x00400000, // class implements ICastable interface
enum_flag_HasIndirectParent = 0x00800000, // m_pParentMethodTable has double indirection
@@ -3859,18 +3865,15 @@ private:
enum_flag_Collectible = 0x10000000,
enum_flag_ContainsGenericVariables = 0x20000000, // we cache this flag to help detect these efficiently and
// to detect this condition when restoring
-#ifdef FEATURE_COMINTEROP
- enum_flag_ComObject = 0x40000000, // class is a com object
-#endif
+ enum_flag_ComObject = 0x40000000, // class is a com object
+
enum_flag_HasComponentSize = 0x80000000, // This is set if component size is used for flags.
-#ifdef FEATURE_COMINTEROP
// Types that require non-trivial interface cast have this bit set in the category
- enum_flag_NonTrivialInterfaceCast = 0x00080000 | enum_flag_ComObject,
-#else
- enum_flag_NonTrivialInterfaceCast = 0x00080000,
-#endif
+ enum_flag_NonTrivialInterfaceCast = enum_flag_Category_Array
+ | enum_flag_ComObject
+ | enum_flag_ICastable
}; // enum WFLAGS_HIGH_ENUM
diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp
index 949bba6e6f..b64772eb40 100644
--- a/src/vm/methodtablebuilder.cpp
+++ b/src/vm/methodtablebuilder.cpp
@@ -2116,6 +2116,13 @@ MethodTableBuilder::BuildMethodTableThrowing(
}
}
+#ifdef FEATURE_ICASTABLE
+ if (!IsValueClass() && g_pICastableInterface != NULL && pMT->CanCastToInterface(g_pICastableInterface))
+ {
+ pMT->SetICastable();
+ }
+#endif // FEATURE_ICASTABLE
+
// Grow the typedef ridmap in advance as we can't afford to
// fail once we set the resolve bit
pModule->EnsureTypeDefCanBeStored(bmtInternal->pType->GetTypeDefToken());
@@ -10066,6 +10073,26 @@ VOID MethodTableBuilder::ScanTypeForVtsInfo()
}
CONTRACTL_END;
+ //
+ // Do not mark System.String as needing vts info. The MethodTable bit used for VtsInfo
+ // is used for other purpose on System.String, and System.String does need VtsInfo anyway
+ // because of it is special-cased by the object cloner.
+ //
+ if (g_pStringClass == NULL)
+ {
+ LPCUTF8 name, nameSpace;
+
+ if (FAILED(GetMDImport()->GetNameOfTypeDef(GetCl(), &name, &nameSpace)))
+ {
+ BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT);
+ }
+
+ if (strcmp(name, g_StringName) == 0 && strcmp(nameSpace, g_SystemNS) == 0)
+ {
+ return;
+ }
+ }
+
DWORD i;
// Scan all the non-virtual, non-abstract, non-generic instance methods for
// one of the special custom attributes indicating a VTS event method.
diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h
index 7bbc9c2942..149d3582b1 100644
--- a/src/vm/mscorlib.h
+++ b/src/vm/mscorlib.h
@@ -2188,12 +2188,18 @@ DEFINE_CLASS(FRAME_SECURITY_DESCRIPTOR_WITH_RESOLVER, Security, FrameSecurityDes
#endif // FEATURE_COMPRESSEDSTACK
#ifdef FEATURE_COMINTEROP
-DEFINE_CLASS(ASYNC_TRACING_EVENT_ARGS, WindowsFoundationDiag, TracingStatusChangedEventArgs)
+DEFINE_CLASS(ASYNC_TRACING_EVENT_ARGS, WindowsFoundationDiag, TracingStatusChangedEventArgs)
DEFINE_CLASS(IASYNC_TRACING_EVENT_ARGS, WindowsFoundationDiag, ITracingStatusChangedEventArgs)
#endif // FEATURE_COMINTEROP
DEFINE_CLASS(MODULEBASE, Reflection, Module)
+#ifdef FEATURE_ICASTABLE
+DEFINE_CLASS(ICASTABLE, CompilerServices, ICastable)
+DEFINE_METHOD(ICASTABLE, ISINSTANCEOF, IsInstanceOfInterface, IM_RuntimeTypeHandle_RefException_RetBool)
+DEFINE_METHOD(ICASTABLE, GETIMPLTYPE, GetImplType, IM_RuntimeTypeHandle_RetRuntimeTypeHandle)
+#endif // FEATURE_ICASTABLE
+
#undef DEFINE_CLASS
#undef DEFINE_METHOD
#undef DEFINE_FIELD
diff --git a/src/vm/prestub.cpp b/src/vm/prestub.cpp
index a2e6fd3b37..79c34d27da 100644
--- a/src/vm/prestub.cpp
+++ b/src/vm/prestub.cpp
@@ -984,6 +984,26 @@ extern "C" PCODE STDCALL PreStubWorker(TransitionBlock * pTransitionBlock, Metho
{
pDispatchingMT = curobj->GetTrueMethodTable();
+#ifdef FEATURE_ICASTABLE
+ if (pDispatchingMT->IsICastable())
+ {
+ MethodTable *pMDMT = pMD->GetMethodTable();
+ TypeHandle objectType(pDispatchingMT);
+ TypeHandle methodType(pMDMT);
+
+ GCStress<cfg_any>::MaybeTrigger();
+ INDEBUG(curobj = NULL); // curobj is unprotected and CanCastTo() can trigger GC
+ if (!objectType.CanCastTo(methodType))
+ {
+ // Apperantly ICastable magic was involved when we chose this method to be called
+ // that's why we better stick to the MethodTable it belongs to, otherwise
+ // DoPrestub() will fail not being able to find implementation for pMD in pDispatchingMT.
+
+ pDispatchingMT = pMDMT;
+ }
+ }
+#endif // FEATURE_ICASTABLE
+
// For value types, the only virtual methods are interface implementations.
// Thus pDispatching == pMT because there
// is no inheritance in value types. Note the BoxedEntryPointStubs are shared
@@ -1958,13 +1978,14 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl
else
token = DispatchToken::CreateDispatchToken(slot);
- OBJECTREF pObj = pEMFrame->GetThis();
- if (pObj == NULL) {
+ OBJECTREF *protectedObj = pEMFrame->GetThisPtr();
+ _ASSERTE(protectedObj != NULL);
+ if (*protectedObj == NULL) {
COMPlusThrow(kNullReferenceException);
}
-
+
StubCallSite callSite(pIndirection, pEMFrame->GetReturnAddress());
- pCode = pMgr->ResolveWorker(&callSite, pObj, token, VirtualCallStubManager::SK_LOOKUP);
+ pCode = pMgr->ResolveWorker(&callSite, protectedObj, token, VirtualCallStubManager::SK_LOOKUP);
_ASSERTE(pCode != NULL);
}
else
diff --git a/src/vm/vars.cpp b/src/vm/vars.cpp
index 5275a92e16..21cafef0a3 100644
--- a/src/vm/vars.cpp
+++ b/src/vm/vars.cpp
@@ -94,6 +94,11 @@ GPTR_IMPL(MethodTable, g_pBaseCOMObject);
GPTR_IMPL(MethodTable, g_pBaseRuntimeClass);
#endif
+#ifdef FEATURE_ICASTABLE
+GPTR_IMPL(MethodTable, g_pICastableInterface);
+#endif // FEATURE_ICASTABLE
+
+
GPTR_IMPL(MethodDesc, g_pPrepareConstrainedRegionsMethod);
GPTR_IMPL(MethodDesc, g_pExecuteBackoutCodeHelperMethod);
diff --git a/src/vm/vars.hpp b/src/vm/vars.hpp
index c2128859c2..cfb4a93233 100644
--- a/src/vm/vars.hpp
+++ b/src/vm/vars.hpp
@@ -432,6 +432,10 @@ GPTR_DECL(MethodTable, g_pBaseCOMObject);
GPTR_DECL(MethodTable, g_pBaseRuntimeClass);
#endif
+#ifdef FEATURE_ICASTABLE
+GPTR_DECL(MethodTable, g_pICastableInterface);
+#endif // FEATURE_ICASTABLE
+
GPTR_DECL(MethodDesc, g_pPrepareConstrainedRegionsMethod);
GPTR_DECL(MethodDesc, g_pExecuteBackoutCodeHelperMethod);
diff --git a/src/vm/virtualcallstub.cpp b/src/vm/virtualcallstub.cpp
index d1ad6ec16d..c234e8c470 100644
--- a/src/vm/virtualcallstub.cpp
+++ b/src/vm/virtualcallstub.cpp
@@ -1204,6 +1204,10 @@ extern "C" PCODE STDCALL StubDispatchFixupWorker(TransitionBlock * pTransitionBl
MAKE_CURRENT_THREAD_AVAILABLE();
+#ifdef _DEBUG
+ Thread::ObjectRefFlush(CURRENT_THREAD);
+#endif
+
FrameWithCookie<StubDispatchFrame> frame(pTransitionBlock);
StubDispatchFrame * pSDFrame = &frame;
@@ -1280,12 +1284,13 @@ extern "C" PCODE STDCALL StubDispatchFixupWorker(TransitionBlock * pTransitionBl
else
token = DispatchToken::CreateDispatchToken(slot);
- OBJECTREF pObj = pSDFrame->GetThis();
- if (pObj == NULL) {
+ OBJECTREF *protectedObj = pSDFrame->GetThisPtr();
+ _ASSERTE(protectedObj != NULL);
+ if (*protectedObj == NULL) {
COMPlusThrow(kNullReferenceException);
}
- pTarget = pMgr->ResolveWorker(&callSite, pObj, token, VirtualCallStubManager::SK_LOOKUP);
+ pTarget = pMgr->ResolveWorker(&callSite, protectedObj, token, VirtualCallStubManager::SK_LOOKUP);
_ASSERTE(pTarget != NULL);
// Ready to return
@@ -1509,6 +1514,10 @@ PCODE VSD_ResolveWorker(TransitionBlock * pTransitionBlock,
MAKE_CURRENT_THREAD_AVAILABLE();
+#ifdef _DEBUG
+ Thread::ObjectRefFlush(CURRENT_THREAD);
+#endif
+
FrameWithCookie<StubDispatchFrame> frame(pTransitionBlock);
StubDispatchFrame * pSDFrame = &frame;
@@ -1516,7 +1525,9 @@ PCODE VSD_ResolveWorker(TransitionBlock * pTransitionBlock,
StubCallSite callSite(siteAddrForRegisterIndirect, returnAddress);
- OBJECTREF pObj = pSDFrame->GetThis();
+ OBJECTREF *protectedObj = pSDFrame->GetThisPtr();
+ _ASSERTE(protectedObj != NULL);
+ OBJECTREF pObj = *protectedObj;
PCODE target = NULL;
@@ -1588,7 +1599,7 @@ PCODE VSD_ResolveWorker(TransitionBlock * pTransitionBlock,
}
#endif
- target = pMgr->ResolveWorker(&callSite, pObj, token, stubKind);
+ target = pMgr->ResolveWorker(&callSite, protectedObj, token, stubKind);
GCPROTECT_END();
@@ -1624,7 +1635,7 @@ void VirtualCallStubManager::BackPatchWorkerStatic(PCODE returnAddress, TADDR si
}
PCODE VirtualCallStubManager::ResolveWorker(StubCallSite* pCallSite,
- OBJECTREF pObj,
+ OBJECTREF *protectedObj,
DispatchToken token,
StubKind stubKind)
{
@@ -1633,15 +1644,14 @@ PCODE VirtualCallStubManager::ResolveWorker(StubCallSite* pCallSite,
GC_TRIGGERS;
MODE_COOPERATIVE;
INJECT_FAULT(COMPlusThrowOM(););
- PRECONDITION(pObj != NULL);
+ PRECONDITION(protectedObj != NULL);
+ PRECONDITION(*protectedObj != NULL);
+ PRECONDITION(IsProtectedByGCFrame(protectedObj));
} CONTRACTL_END;
- MethodTable* objectType = pObj->GetMethodTable();
+ MethodTable* objectType = (*protectedObj)->GetMethodTable();
CONSISTENCY_CHECK(CheckPointer(objectType));
- // pObj is not protected. Clear it in debug builds to avoid accidental use.
- INDEBUG(pObj = NULL);
-
#ifdef STUB_LOGGING
if (g_dumpLogCounter != 0)
{
@@ -1772,11 +1782,12 @@ PCODE VirtualCallStubManager::ResolveWorker(StubCallSite* pCallSite,
if (target == NULL)
{
CONSISTENCY_CHECK(stub == CALL_STUB_EMPTY_ENTRY);
- patch = Resolver(objectType, token, &target);
+ patch = Resolver(objectType, token, protectedObj, &target);
#if defined(_DEBUG)
- if (!objectType->IsTransparentProxy() &&
- !objectType->IsComObjectType())
+ if ( !objectType->IsTransparentProxy() &&
+ !objectType->IsComObjectType() &&
+ !objectType->IsICastable())
{
CONSISTENCY_CHECK(!MethodTable::GetMethodDescForSlotAddress(target)->IsGenericMethodDefinition());
}
@@ -2065,7 +2076,8 @@ it means that the token is not resolvable.
BOOL
VirtualCallStubManager::Resolver(
MethodTable * pMT,
- DispatchToken token,
+ DispatchToken token,
+ OBJECTREF * protectedObj, // this one can actually be NULL, consider using pMT is you don't need the object itself
PCODE * ppTarget)
{
CONTRACTL {
@@ -2215,40 +2227,73 @@ VirtualCallStubManager::Resolver(
}
}
#ifdef FEATURE_COMINTEROP
- else if (IsInterfaceToken(token))
+ else if (pMT->IsComObjectType() && IsInterfaceToken(token))
{
- if (pMT->IsComObjectType())
- {
- MethodTable * pItfMT = GetTypeFromToken(token);
- implSlot = pItfMT->FindDispatchSlot(token.GetSlotNumber());
+ MethodTable * pItfMT = GetTypeFromToken(token);
+ implSlot = pItfMT->FindDispatchSlot(token.GetSlotNumber());
- if (pItfMT->HasInstantiation())
+ if (pItfMT->HasInstantiation())
+ {
+ DispatchSlot ds(implSlot);
+ MethodDesc * pTargetMD = ds.GetMethodDesc();
+ if (!pTargetMD->HasMethodInstantiation())
{
- DispatchSlot ds(implSlot);
- MethodDesc * pTargetMD = ds.GetMethodDesc();
- if (!pTargetMD->HasMethodInstantiation())
- {
- _ASSERTE(pItfMT->IsProjectedFromWinRT() || pItfMT->IsWinRTRedirectedInterface(TypeHandle::Interop_ManagedToNative));
+ _ASSERTE(pItfMT->IsProjectedFromWinRT() || pItfMT->IsWinRTRedirectedInterface(TypeHandle::Interop_ManagedToNative));
- MethodDesc *pInstMD = MethodDesc::FindOrCreateAssociatedMethodDesc(
- pTargetMD,
- pItfMT,
- FALSE, // forceBoxedEntryPoint
- Instantiation(), // methodInst
- FALSE, // allowInstParam
- TRUE); // forceRemotableMethod
+ MethodDesc *pInstMD = MethodDesc::FindOrCreateAssociatedMethodDesc(
+ pTargetMD,
+ pItfMT,
+ FALSE, // forceBoxedEntryPoint
+ Instantiation(), // methodInst
+ FALSE, // allowInstParam
+ TRUE); // forceRemotableMethod
- _ASSERTE(pInstMD->IsComPlusCall() || pInstMD->IsGenericComPlusCall());
+ _ASSERTE(pInstMD->IsComPlusCall() || pInstMD->IsGenericComPlusCall());
- *ppTarget = pInstMD->GetStableEntryPoint();
- return TRUE;
- }
+ *ppTarget = pInstMD->GetStableEntryPoint();
+ return TRUE;
}
-
- fShouldPatch = TRUE;
}
+
+ fShouldPatch = TRUE;
}
#endif // FEATURE_COMINTEROP
+#ifdef FEATURE_ICASTABLE
+ else if (pMT->IsICastable() && protectedObj != NULL && *protectedObj != NULL)
+ {
+ GCStress<cfg_any>::MaybeTrigger();
+
+ // In case of ICastable, instead of trying to find method implementation in the real object type
+ // we call pObj.GetValueInternal() and call Resolver() again with whatever type it returns.
+ // It allows objects that implement ICastable to mimic behavior of other types.
+ MethodTable * pTokenMT = GetTypeFromToken(token);
+
+ // Make call to obj.GetImplType(interfaceTypeObj)
+ MethodDesc *pGetImplTypeMD = pMT->GetMethodDescForInterfaceMethod(MscorlibBinder::GetMethod(METHOD__ICASTABLE__GETIMPLTYPE));
+ OBJECTREF tokenManagedType = pTokenMT->GetManagedClassObject(); //GC triggers
+
+ PREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC(pGetImplTypeMD);
+
+ DECLARE_ARGHOLDER_ARRAY(args, 2);
+ args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*protectedObj);
+ args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(tokenManagedType);
+
+ OBJECTREF impTypeObj = NULL;
+ CALL_MANAGED_METHOD_RETREF(impTypeObj, OBJECTREF, args);
+
+ INDEBUG(tokenManagedType = NULL); //tokenManagedType wasn't protected during the call
+ if (impTypeObj == NULL) // GetImplType returns default(RuntimeTypeHandle)
+ {
+ COMPlusThrow(kEntryPointNotFoundException);
+ }
+
+ ReflectClassBaseObject* resultTypeObj = ((ReflectClassBaseObject*)OBJECTREFToObject(impTypeObj));
+ TypeHandle resulTypeHnd = resultTypeObj->GetType();
+ MethodTable *pResultMT = resulTypeHnd.GetMethodTable();
+
+ return Resolver(pResultMT, token, protectedObj, ppTarget);
+ }
+#endif // FEATURE_ICASTABLE
if (implSlot.IsNull())
{
@@ -2586,7 +2631,10 @@ VirtualCallStubManager::GetTarget(
// No match, now do full resolve
BOOL fPatch;
- fPatch = Resolver(pMT, token, &target);
+
+ // TODO: passing NULL as protectedObj here can lead to incorrect behavior for ICastable objects
+ // We need to review if this is the case and refactor this code if we want ICastable to become officially supported
+ fPatch = Resolver(pMT, token, NULL, &target);
_ASSERTE(target != NULL);
#ifndef STUB_DISPATCH_PORTABLE
@@ -4162,7 +4210,9 @@ MethodDesc *VirtualCallStubManagerManager::Entry2MethodDesc(
CONSISTENCY_CHECK(!pMT->IsTransparentProxy());
PCODE target = NULL;
- VirtualCallStubManager::Resolver(pMT, token, &target);
+ // TODO: passing NULL as protectedObj here can lead to incorrect behavior for ICastable objects
+ // We need to review if this is the case and refactor this code if we want ICastable to become officially supported
+ VirtualCallStubManager::Resolver(pMT, token, NULL, &target);
return pMT->GetMethodDescForSlotAddress(target);
}
diff --git a/src/vm/virtualcallstub.h b/src/vm/virtualcallstub.h
index cc68d2fe01..f5bf0d9506 100644
--- a/src/vm/virtualcallstub.h
+++ b/src/vm/virtualcallstub.h
@@ -9,7 +9,6 @@
//
-
// See code:VirtualCallStubManager for details
//
// ============================================================================
@@ -494,11 +493,12 @@ private:
size_t token,
void *target);
- //Given a dispatch token and a method table, determine the
+ //Given a dispatch token, an object and a method table, determine the
//target address to go to. The return value (BOOL) states whether this address
//is cacheable or not.
static BOOL Resolver(MethodTable * pMT,
DispatchToken token,
+ OBJECTREF * protectedObj,
PCODE * ppTarget);
// This can be used to find a target without needing the ability to throw
@@ -554,7 +554,7 @@ private:
static void STDCALL BackPatchWorkerStatic(PCODE returnAddr, TADDR siteAddrForRegisterIndirect);
public:
- PCODE ResolveWorker(StubCallSite* pCallSite, OBJECTREF pObj, DispatchToken token, StubKind stubKind);
+ PCODE ResolveWorker(StubCallSite* pCallSite, OBJECTREF *protectedObj, DispatchToken token, StubKind stubKind);
void BackPatchWorker(StubCallSite* pCallSite);
//Change the callsite to point to stub
diff --git a/tests/src/Interop/ICastable/Castable.cs b/tests/src/Interop/ICastable/Castable.cs
new file mode 100644
index 0000000000..56a0efb14b
--- /dev/null
+++ b/tests/src/Interop/ICastable/Castable.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+public interface IRetArg<T>
+{
+ T ReturnArg(T t);
+}
+
+public interface IRetThis
+{
+ IRetThis ReturnThis();
+ Type GetMyType();
+}
+
+
+public interface IUnimplemented
+{
+ void UnimplementedMethod();
+}
+
+public interface IExtra
+{
+ int InnocentMethod();
+}
+
+public class CastableException : Exception {};
+
+
+public class RetArgImpl: IRetArg<string>
+{
+ public string ReturnArg(string t)
+ {
+ Console.WriteLine("ReturnArg has been called.");
+ return t;
+ }
+}
+
+public class GenRetArgImpl<T>: IRetArg<T>
+{
+ public T ReturnArg(T t)
+ {
+ Console.WriteLine("Generic ReturnArg has been called. My type is {0}", GetType());
+ return t;
+ }
+}
+
+
+public class RetThisImpl: IRetThis
+{
+ public IRetThis ReturnThis()
+ {
+ Console.WriteLine("RetThis has been called.");
+ return this;
+ }
+
+ public Type GetMyType()
+ {
+ Console.WriteLine("GetMyType has been called. My type is {0}", GetType());
+ return GetType();
+ }
+}
+
+
+public class Castable : ICastable, IExtra
+{
+ private Dictionary<Type, Type> _interface2impl;
+
+ public Castable(Dictionary<Type, Type> interface2impl)
+ {
+ _interface2impl = interface2impl;
+ }
+
+ public bool IsInstanceOfInterface(RuntimeTypeHandle interfaceType, out Exception castError)
+ {
+ Console.WriteLine("IsInstanceOfInterface has been called for type {0}", Type.GetTypeFromHandle(interfaceType));
+ if (_interface2impl == null)
+ {
+ castError = new CastableException();
+ return false;
+ }
+ castError = null;
+ return _interface2impl.ContainsKey(Type.GetTypeFromHandle(interfaceType));
+ }
+
+ public RuntimeTypeHandle GetImplType(RuntimeTypeHandle interfaceType)
+ {
+ Console.WriteLine("GetImplType has been called for type {0}", Type.GetTypeFromHandle(interfaceType));
+ return _interface2impl[Type.GetTypeFromHandle(interfaceType)].TypeHandle;
+ }
+
+ public int InnocentMethod()
+ {
+ Console.WriteLine("InnocentMethod has been called. My type is {0}", GetType());
+ return 3;
+ }
+}
+
+public class BadCastable : ICastable
+{
+ public bool IsInstanceOfInterface(RuntimeTypeHandle interfaceType, out Exception castError)
+ {
+ castError = null;
+ return true;
+ }
+
+ public RuntimeTypeHandle GetImplType(RuntimeTypeHandle interfaceType)
+ {
+ return default(RuntimeTypeHandle);
+ }
+}
+
+
+public class Program
+{
+ private static bool passed = true;
+
+ public static void Assert(bool value, string message)
+ {
+ if (!value)
+ {
+ Console.WriteLine("FAIL! " + message);
+ passed = false;
+ }
+ }
+
+ public static int Main()
+ {
+ //Console.WriteLine("Execution started. Attach debugger and press enter.");
+ //Console.ReadLine();
+
+ try
+ {
+ object implProxy = new Castable(
+ new Dictionary<Type, Type>()
+ {
+ { typeof(IRetArg<string>), typeof(RetArgImpl) },
+ { typeof(IRetArg<int>), typeof(GenRetArgImpl<int>) },
+ { typeof(IRetThis), typeof(RetThisImpl) },
+ { typeof(IExtra), null }, //we should never use it
+ }
+ );
+
+ // testing simple cases
+ Assert(implProxy is IRetThis, "implProxy should be castable to IRetThis via is");
+ Assert(!(implProxy is IUnimplemented), "implProxy should not be castable to IUnimplemented via is");
+ Assert((implProxy as IRetThis) != null, "implProxy should be castable to IRetThis is as");
+ Assert((implProxy as IUnimplemented) == null, "implProxy should not be castable to IUnimplemented is as");
+ var retThis = (IRetThis)implProxy;
+ Assert(object.ReferenceEquals(retThis.ReturnThis(), implProxy), "RetThis should return implProxy");
+ Assert(retThis.GetMyType() == typeof(Castable), "GetMyType should return typeof(Castable)");
+
+ Assert(!(implProxy is IUnimplemented), "implProxy should not be castable to IUnimplemented via is");
+ Assert((implProxy as IUnimplemented) == null, "implProxy should not be castable to IUnimplemented via as");
+
+
+ // testing generics
+ IRetArg<string> retArgStr = (IRetArg<string>)implProxy;
+ Assert(retArgStr.ReturnArg("hohoho") == "hohoho", "retArgStr.ReturnArg() should return arg");
+
+ IRetArg<int> retArgInt = (IRetArg<int>)implProxy;
+ Assert(retArgInt.ReturnArg(42) == 42, "retArgInt.ReturnArg() should return arg");
+
+
+ // testing Castable implemeting other interfaces
+ var extra = (IExtra)implProxy;
+ Assert(extra.InnocentMethod() == 3, "InnocentMethod() should be called on Castable and return 3");
+
+ // testing error handling
+ try
+ {
+ var _ = (IUnimplemented)implProxy;
+ Assert(false, "pProxy should not be castable to I1");
+ }
+ catch (InvalidCastException) {}
+
+ object nullCastable = new Castable(null);
+ try
+ {
+ var _ = (IRetThis)nullCastable;
+ Assert(false, "Exceptions should be thrown from IsInstanceOfInterface");
+ }
+ catch (CastableException) {}
+
+ object badCastable = new BadCastable();
+ try
+ {
+ var r = (IRetThis)badCastable;
+ r.ReturnThis();
+ Assert(false, "Exceptions should be thrown from ReturnThis()");
+ }
+ catch (EntryPointNotFoundException) {}
+
+ //delegate testing
+ Func<int> fInt = new Func<int>(extra.InnocentMethod);
+ Assert(fInt() == 3, "Delegate call to InnocentMethod() should return 3");
+
+ Func<IRetThis> func = new Func<IRetThis>(retThis.ReturnThis);
+ Assert(object.ReferenceEquals(func(), implProxy), "Delegate call to ReturnThis() should return this");
+ }
+ catch (Exception e)
+ {
+ Assert(false, e.ToString());
+ }
+
+ if (passed)
+ {
+ Console.WriteLine("Test PASSED!");
+ return 100;
+ }
+ else
+ {
+ Console.WriteLine("Test FAILED!");
+ return -1;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/tests/src/Interop/ICastable/Castable.csproj b/tests/src/Interop/ICastable/Castable.csproj
new file mode 100644
index 0000000000..dac6ca1774
--- /dev/null
+++ b/tests/src/Interop/ICastable/Castable.csproj
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>Castable</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <RestorePackages>true</RestorePackages>
+ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+ <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+ <LinkLocalMscorlib>true</LinkLocalMscorlib>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Castable.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ <None Include="app.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/ICastable/app.config b/tests/src/Interop/ICastable/app.config
new file mode 100644
index 0000000000..58c01219df
--- /dev/null
+++ b/tests/src/Interop/ICastable/app.config
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <runtime>
+ <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+ </assemblyBinding>
+ </runtime>
+</configuration> \ No newline at end of file
diff --git a/tests/src/Interop/ICastable/packages.config b/tests/src/Interop/ICastable/packages.config
new file mode 100644
index 0000000000..569e1bea86
--- /dev/null
+++ b/tests/src/Interop/ICastable/packages.config
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+</packages>
diff --git a/tests/src/dir.targets b/tests/src/dir.targets
index bb050594ac..cd45da5440 100644
--- a/tests/src/dir.targets
+++ b/tests/src/dir.targets
@@ -35,7 +35,8 @@
>
<ItemGroup>
<PossibleTargetFrameworks Include="$(_TargetFrameworkDirectories)" />
- <ReferencePath Include="%(PossibleTargetFrameworks.Identity)mscorlib.dll" />
+ <ReferencePath Condition="'$(LinkLocalMscorlib)' != 'true'" Include="%(PossibleTargetFrameworks.Identity)mscorlib.dll" />
+ <ReferencePath Condition="'$(LinkLocalMscorlib)' == 'true'" Include="$(ProjectDir)\..\binaries\Product\$(BuildOS).$(BuildArch).$(BuildType)\mscorlib.dll" />
</ItemGroup>
</Target>