diff options
-rw-r--r-- | CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/debug/daccess/nidump.cpp | 6 | ||||
-rw-r--r-- | src/inc/dacvars.h | 4 | ||||
-rw-r--r-- | src/mscorlib/model.xml | 58 | ||||
-rw-r--r-- | src/mscorlib/mscorlib.shared.sources.props | 1 | ||||
-rw-r--r-- | src/mscorlib/src/System/Runtime/CompilerServices/ICastable.cs | 63 | ||||
-rw-r--r-- | src/vm/amd64/asmconstants.h | 8 | ||||
-rw-r--r-- | src/vm/appdomain.cpp | 4 | ||||
-rw-r--r-- | src/vm/i386/asmconstants.h | 7 | ||||
-rw-r--r-- | src/vm/jithelpers.cpp | 44 | ||||
-rw-r--r-- | src/vm/metasig.h | 7 | ||||
-rw-r--r-- | src/vm/methodtable.cpp | 56 | ||||
-rw-r--r-- | src/vm/methodtable.h | 23 | ||||
-rw-r--r-- | src/vm/methodtablebuilder.cpp | 27 | ||||
-rw-r--r-- | src/vm/mscorlib.h | 8 | ||||
-rw-r--r-- | src/vm/prestub.cpp | 29 | ||||
-rw-r--r-- | src/vm/vars.cpp | 5 | ||||
-rw-r--r-- | src/vm/vars.hpp | 4 | ||||
-rw-r--r-- | src/vm/virtualcallstub.cpp | 132 | ||||
-rw-r--r-- | src/vm/virtualcallstub.h | 6 | ||||
-rw-r--r-- | tests/src/Interop/ICastable/Castable.cs | 219 | ||||
-rw-r--r-- | tests/src/Interop/ICastable/Castable.csproj | 42 | ||||
-rw-r--r-- | tests/src/Interop/ICastable/app.config | 7 | ||||
-rw-r--r-- | tests/src/Interop/ICastable/packages.config | 3 | ||||
-rw-r--r-- | tests/src/dir.targets | 3 |
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<TResult>(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> |