From dcf60f83a0557b3c2fbb705a4fdb3bade0e14854 Mon Sep 17 00:00:00 2001 From: "Yi Zhang (CLR)" Date: Wed, 5 Apr 2017 21:04:25 -0700 Subject: [PATCH] Default Interface Method Prototype (#10505) * allow non-zero RVA on abstract interface method in ilasm * Revert "allow non-zero RVA on abstract interface method in ilasm" This reverts commit eecb8024e58f14a20e5e49359f38019f5768ac41. * add a test case and allow virtual non-abstract method in ilasm * allow non-abstract methods in the loader * fixup dispatch map * support for simple default interface method scenario * fix a bug with incorrect usage of MethodIterator skpping the first method. add a test case for overriding but it may not be what we want * add another simple test case for base class * allow private/internal methods in ilasm and add a explict impl test * update reference to mscorlib in il test * add shared generics and variance case * allow interface dispatch to return instantiating stubs with the right PARAM_TYPE calling conv * simple factoring and add a valuetype test case * add a test case for generic virtual methods * a bit more refactoring by moving the CALLCONV_PARAMTYPE logic inside getMethodSigInternal * support explicit methodimpl and remove implicit methodimpl test case * update test cases to give more clear output * Fix a bug where GetMethodDescForSlot chokes on interface MethodDesc without precode. This is accdentally discovered by methodimpl test case when iterating methods on a default interface method that has already been JITted * cleanup code after review and add a bit more comments * update comments * only use custom ILAsm for default interface methods tests - some tests are choking on CoreCLR ilasm for security related stuff * address comments and allow instance methods, and add a constraint value type call test scenario * disable the failing protected method scenario --- src/ilasm/assem.cpp | 1 - src/ilasm/assembler.cpp | 18 - src/vm/classcompat.cpp | 31 +- src/vm/jitinterface.cpp | 15 +- src/vm/jitinterface.h | 3 +- src/vm/method.cpp | 18 +- src/vm/method.hpp | 8 + src/vm/methodtable.cpp | 357 +++++++++++----- src/vm/methodtable.h | 8 + src/vm/methodtable.inl | 11 +- src/vm/methodtablebuilder.cpp | 267 ++++++------ tests/src/IL.targets | 5 +- .../constrainedcall/constrainedcall.cs | 185 +++++++++ .../constrainedcall/constrainedcall.il | 456 +++++++++++++++++++++ .../constrainedcall/constrainedcall.ilproj | 37 ++ .../genericmethods/genericmethods.cs | 96 +++++ .../genericmethods/genericmethods.il | 357 ++++++++++++++++ .../genericmethods/genericmethods.ilproj | 37 ++ .../methodimpl/methodimpl.cs | 83 ++++ .../methodimpl/methodimpl.il | 279 +++++++++++++ .../methodimpl/methodimpl.ilproj | 37 ++ .../sharedgenerics/sharedgenerics.cs | 68 +++ .../sharedgenerics/sharedgenerics.il | 275 +++++++++++++ .../sharedgenerics/sharedgenerics.ilproj | 37 ++ .../DefaultInterfaceMethods/simple/simple.cs | 124 ++++++ .../DefaultInterfaceMethods/simple/simple.il | 401 ++++++++++++++++++ .../DefaultInterfaceMethods/simple/simple.ilproj | 37 ++ .../valuetypes/valuetypes.cs | 94 +++++ .../valuetypes/valuetypes.il | 252 ++++++++++++ .../valuetypes/valuetypes.ilproj | 37 ++ 30 files changed, 3369 insertions(+), 265 deletions(-) create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.cs create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.il create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.ilproj create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/genericmethods/genericmethods.cs create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/genericmethods/genericmethods.il create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/genericmethods/genericmethods.ilproj create mode 100755 tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.cs create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.il create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.ilproj create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/sharedgenerics/sharedgenerics.cs create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/sharedgenerics/sharedgenerics.il create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/sharedgenerics/sharedgenerics.ilproj create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/simple/simple.cs create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/simple/simple.il create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/simple/simple.ilproj create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/valuetypes/valuetypes.cs create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/valuetypes/valuetypes.il create mode 100644 tests/src/Loader/classloader/DefaultInterfaceMethods/valuetypes/valuetypes.ilproj diff --git a/src/ilasm/assem.cpp b/src/ilasm/assem.cpp index 6416dfe..a3b8daf 100644 --- a/src/ilasm/assem.cpp +++ b/src/ilasm/assem.cpp @@ -339,7 +339,6 @@ BOOL Assembler::AddMethod(Method *pMethod) { char sz[1024]; sz[0] = 0; - if(fIsInterface && (!IsMdStatic(pMethod->m_Attr))) strcat_s(sz,1024," non-static declared in interface"); if(fIsImport) strcat_s(sz,1024," imported"); if(IsMdAbstract(pMethod->m_Attr)) strcat_s(sz,1024," abstract"); if(IsMdPinvokeImpl(pMethod->m_Attr)) strcat_s(sz,1024," pinvoke"); diff --git a/src/ilasm/assembler.cpp b/src/ilasm/assembler.cpp index be535ab..74190b7 100644 --- a/src/ilasm/assembler.cpp +++ b/src/ilasm/assembler.cpp @@ -705,24 +705,6 @@ void Assembler::StartMethod(__in __nullterminated char* name, BinStr* sig, CorMe { flags = (CorMethodAttr)(flags | mdSpecialName); if(IsTdInterface(m_pCurClass->m_Attr)) report->error("Instance constructor in interface\n"); - - } - if(!IsMdStatic(flags)) - { - if(IsTdInterface(m_pCurClass->m_Attr)) - { - if(!IsMdPublic(flags)) report->error("Non-public instance method in interface\n"); - if((!(IsMdVirtual(flags) && IsMdAbstract(flags)))) - { - if(OnErrGo) report->error("Non-virtual, non-abstract instance method in interface\n"); - else - { - report->warn("Non-virtual, non-abstract instance method in interface, set to such\n"); - flags = (CorMethodAttr)(flags |mdVirtual | mdAbstract); - } - } - - } } m_pCurMethod = new Method(this, m_pCurClass, name, sig, flags); } diff --git a/src/vm/classcompat.cpp b/src/vm/classcompat.cpp index 91004cd..031604b 100644 --- a/src/vm/classcompat.cpp +++ b/src/vm/classcompat.cpp @@ -2619,26 +2619,6 @@ VOID MethodTableBuilder::EnumerateClassMethods() } } - // Some interface checks. - if (IsInterface()) - { - if (IsMdVirtual(dwMemberAttrs)) - { - if (!IsMdAbstract(dwMemberAttrs)) - { - BuildMethodTableThrowException(BFA_VIRTUAL_NONAB_INT_METHOD); - } - } - else - { - // Instance field/method - if (!IsMdStatic(dwMemberAttrs)) - { - BuildMethodTableThrowException(BFA_NONVIRT_INST_INT_METHOD); - } - } - } - // No synchronized methods in ValueTypes if(fIsClassValueType && IsMiSynchronized(dwImplFlags)) { @@ -2804,17 +2784,20 @@ VOID MethodTableBuilder::EnumerateClassMethods() // If the interface is a standard managed interface then allocate space for an FCall method desc. Classification = mcFCall; } - else + else if (IsMdAbstract(dwMemberAttrs)) { // If COM interop is supported then all other interface MDs may be // accessed via COM interop mcComInterop MDs are BIG - // this is very often a waste of space + // @DIM_TODO - What if default interface method is called through COM interop? Classification = mcComInterop; } -#else // !FEATURE_COMINTEROP - // This codepath is used by remoting - Classification = mcIL; + else #endif // !FEATURE_COMINTEROP + { + // This codepath is used by remoting and default interface methods + Classification = mcIL; + } } else { diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp index 2fc5e09..ea10ca7 100644 --- a/src/vm/jitinterface.cpp +++ b/src/vm/jitinterface.cpp @@ -5615,7 +5615,7 @@ void CEEInfo::getCallInfo( pResult->classFlags = getClassAttribsInternal(pResolvedToken->hClass); pResult->methodFlags = getMethodAttribsInternal(pResult->hMethod); - getMethodSigInternal(pResult->hMethod, &pResult->sig, (pResult->hMethod == pResolvedToken->hMethod) ? pResolvedToken->hClass : NULL); + getMethodSigInternal(pResult->hMethod, &pResult->sig, (pResult->hMethod == pResolvedToken->hMethod) ? pResolvedToken->hClass : NULL, /* isCallSite = */ TRUE); if (flags & CORINFO_CALLINFO_VERIFICATION) { @@ -8356,7 +8356,8 @@ void CEEInfo::getMethodSigInternal( CORINFO_METHOD_HANDLE ftnHnd, CORINFO_SIG_INFO * sigRet, - CORINFO_CLASS_HANDLE owner) + CORINFO_CLASS_HANDLE owner, + BOOL isCallSite) { STANDARD_VM_CONTRACT; @@ -8382,7 +8383,15 @@ CEEInfo::getMethodSigInternal( // Shared generic methods and shared methods on generic structs take an extra argument representing their instantiation if (ftn->RequiresInstArg()) { - sigRet->callConv = (CorInfoCallConv) (sigRet->callConv | CORINFO_CALLCONV_PARAMTYPE); + // + // If we are making an interface call that is a default interface method, we need to lie to the JIT. + // The reason being that we already made sure target is always directly callable (through instantiation stubs), + // JIT should not generate shared generics aware call code and insert the secret argument again at the callsite. + // Otherwise we would end up with two secret generic dictionary arguments (since the stub also provides one). + // + BOOL isDefaultInterfaceMethodCallSite = isCallSite && ftn->IsDefaultInterfaceMethod(); + if (!isDefaultInterfaceMethodCallSite) + sigRet->callConv = (CorInfoCallConv) (sigRet->callConv | CORINFO_CALLCONV_PARAMTYPE); } // We want the calling convention bit to be consistant with the method attribute bit diff --git a/src/vm/jitinterface.h b/src/vm/jitinterface.h index e34b859..1dccdb2 100644 --- a/src/vm/jitinterface.h +++ b/src/vm/jitinterface.h @@ -713,7 +713,8 @@ public: void getMethodSigInternal ( CORINFO_METHOD_HANDLE ftnHnd, CORINFO_SIG_INFO* sigInfo, - CORINFO_CLASS_HANDLE owner = NULL + CORINFO_CLASS_HANDLE owner = NULL, + BOOL isCallSite = FALSE ); void getEHinfo( diff --git a/src/vm/method.cpp b/src/vm/method.cpp index 241a0cc..5ac5caf 100644 --- a/src/vm/method.cpp +++ b/src/vm/method.cpp @@ -1762,7 +1762,11 @@ BOOL MethodDesc::IsSharedByGenericMethodInstantiations() // Does this method require an extra MethodTable argument for instantiation information? // This is the case for // * per-inst static methods in shared-code instantiated generic classes (e.g. static void MyClass::m()) +// - there is no this pointer providing generic dictionary info // * shared-code instance methods in instantiated generic structs (e.g. void MyValueType::m()) +// - unboxed 'this' pointer in value-type instance methods don't have MethodTable pointer by definition +// * default interface method called via interface dispatch (e. g. IFoo.Foo calling into IFoo::Foo()) +// - this pointer is ambiguous as it can implement more than one IFoo BOOL MethodDesc::RequiresInstMethodTableArg() { LIMITED_METHOD_DAC_CONTRACT; @@ -1770,7 +1774,7 @@ BOOL MethodDesc::RequiresInstMethodTableArg() return IsSharedByGenericInstantiations() && !HasMethodInstantiation() && - (IsStatic() || GetMethodTable()->IsValueType()); + (IsStatic() || GetMethodTable()->IsValueType() || IsDefaultInterfaceMethod()); } //******************************************************************************* @@ -1792,7 +1796,7 @@ BOOL MethodDesc::RequiresInstArg() LIMITED_METHOD_DAC_CONTRACT; BOOL fRet = IsSharedByGenericInstantiations() && - (HasMethodInstantiation() || IsStatic() || GetMethodTable()->IsValueType()); + (HasMethodInstantiation() || IsStatic() || GetMethodTable()->IsValueType() || IsDefaultInterfaceMethod()); _ASSERT(fRet == (RequiresInstMethodTableArg() || RequiresInstMethodDescArg())); return fRet; @@ -1868,7 +1872,8 @@ BOOL MethodDesc::AcquiresInstMethodTableFromThis() { IsSharedByGenericInstantiations() && !HasMethodInstantiation() && !IsStatic() && - !GetMethodTable()->IsValueType(); + !GetMethodTable()->IsValueType() && + !IsDefaultInterfaceMethod(); } //******************************************************************************* @@ -2582,7 +2587,10 @@ BOOL MethodDesc::RequiresStableEntryPoint(BOOL fEstimateForChunk /*=FALSE*/) return TRUE; // TODO: Can we avoid early allocation of precodes for interfaces and cominterop? - if ((IsInterface() && !IsStatic()) || IsComPlusCall()) + // Only abstract virtual interface method need precode + // @DIM_TODO - We need to decide what is the right approach for precode for default + // interface methods + if ((IsInterface() && IsAbstract() && IsVirtual() && !IsStatic()) || IsComPlusCall()) return TRUE; } @@ -2678,7 +2686,7 @@ BOOL MethodDesc::MayHaveNativeCode() _ASSERTE(IsIL()); - if ((IsInterface() && !IsStatic()) || IsWrapperStub() || ContainsGenericVariables() || IsAbstract()) + if (IsWrapperStub() || ContainsGenericVariables() || IsAbstract()) { return FALSE; } diff --git a/src/vm/method.hpp b/src/vm/method.hpp index 71e838a..336260c 100644 --- a/src/vm/method.hpp +++ b/src/vm/method.hpp @@ -1087,6 +1087,14 @@ public: && GetSlot() < pMT->GetNumVirtuals(); } + // Is this a default interface method (virtual non-abstract instance method) + inline BOOL IsDefaultInterfaceMethod() + { + LIMITED_METHOD_CONTRACT; + + return (GetMethodTable()->IsInterface() && !IsStatic() && IsVirtual() && !IsAbstract()); + } + inline BOOL HasNonVtableSlot(); void SetHasNonVtableSlot() diff --git a/src/vm/methodtable.cpp b/src/vm/methodtable.cpp index 66a6a2e..474c611 100644 --- a/src/vm/methodtable.cpp +++ b/src/vm/methodtable.cpp @@ -1982,7 +1982,7 @@ MethodTable::Debug_DumpInterfaceMap( HRESULT hr; EX_TRY { - InterfaceMapIterator it(this, false); + InterfaceMapIterator it(this); while (it.Next()) { MethodTable *pInterfaceMT = it.GetInterface(); @@ -6850,6 +6850,12 @@ MethodTable::FindDispatchImpl( DispatchMapEntry e; if (!FindDispatchEntry(typeID, slotNumber, &e)) { + // Figure out the interface being called + MethodTable *pIfcMT = GetThread()->GetDomain()->LookupType(typeID); + + // Figure out which method of the interface the caller requested. + MethodDesc * pIfcMD = pIfcMT->GetMethodDescForSlot(slotNumber); + // A call to an array thru IList (or IEnumerable or ICollection) has to be handled specially. // These interfaces are "magic" (mostly due to working set concerned - they are created on demand internally // even though semantically, these are static interfaces.) @@ -6863,7 +6869,7 @@ MethodTable::FindDispatchImpl( // IList call thru an array. // Get the MT of IList or IReadOnlyList - MethodTable *pIfcMT = GetThread()->GetDomain()->LookupType(typeID); + // Quick sanity check if (!(pIfcMT->HasInstantiation())) @@ -6875,9 +6881,6 @@ MethodTable::FindDispatchImpl( // Get the type of T (as in IList) TypeHandle theT = pIfcMT->GetInstantiation()[0]; - // Figure out which method of IList the caller requested. - MethodDesc * pIfcMD = pIfcMT->GetMethodDescForSlot(slotNumber); - // Retrieve the corresponding method of SZArrayHelper. This is the guy that will actually execute. // This method will be an instantiation of a generic method. I.e. if the caller requested // IList.Meth(), he will actually be diverted to SZArrayHelper.Meth(). @@ -6894,11 +6897,34 @@ MethodTable::FindDispatchImpl( RETURN(TRUE); } + else + { + // + // See if we can find a default method from one of the implemented interfaces + // + MethodDesc *pDefaultMethod = NULL; + if (FindDefaultMethod( + pIfcMD, // the interface method being resolved + pIfcMT, // the interface being resolved + &pDefaultMethod)) + { + // Now, construct a DispatchSlot to return in *pImplSlot + DispatchSlot ds(pDefaultMethod->GetMethodEntryPoint()); + + if (pImplSlot != NULL) + { + *pImplSlot = ds; + } + + RETURN(TRUE); + } + } // This contract is not implemented by this class or any parent class. RETURN(FALSE); } + ///////////////////////////////// // 1.1. Update the typeID and slotNumber so that the full search can commense below typeID = TYPE_ID_THIS_CLASS; @@ -6916,6 +6942,148 @@ MethodTable::FindDispatchImpl( RETURN (TRUE); } +#ifndef DACCESS_COMPILE +BOOL MethodTable::FindDefaultMethod( + MethodDesc *pInterfaceMD, + MethodTable *pInterfaceMT, + MethodDesc **ppDefaultMethod +) +{ + CONTRACT(BOOL) { + INSTANCE_CHECK; + MODE_ANY; + THROWS; + GC_TRIGGERS; + PRECONDITION(CheckPointer(pInterfaceMD)); + PRECONDITION(CheckPointer(pInterfaceMT)); + PRECONDITION(CheckPointer(ppDefaultMethod)); + POSTCONDITION(!RETVAL || (*ppDefaultMethod) != nullptr); + } CONTRACT_END; + + // + // Find best candidate + // + InterfaceMapIterator it = this->IterateInterfaceMap(); + MethodTable *pBestCandidateMT = NULL; + MethodDesc *pBestCandidateMD = NULL; + + + // + // Walk interface from derived class to parent class + // + // @DIM_TODO - This is a first cut naive implementation for prototyping and flush out other + // implementation issues without worrying too much about ordering/checks + // Once CLR/C# team has agreed on the right order, we'll replace this with the real deal + // + MethodTable *pMT = this; + while (pMT != NULL) + { + MethodTable *pParentMT = pMT->GetParentMethodTable(); + unsigned dwParentInterfaces = 0; + if (pParentMT) + dwParentInterfaces = pParentMT->GetNumInterfaces(); + + // Scanning only current class only if the current class have more interface than parent + // (parent interface are laid out first in interface map) + if (pMT->GetNumInterfaces() > dwParentInterfaces) + { + // Only iterate the interfaceimpls on current class + MethodTable::InterfaceMapIterator it = pMT->IterateInterfaceMapFrom(dwParentInterfaces); + while (!it.Finished()) + { + MethodTable *pCurMT = it.GetInterface(); + + MethodDesc *pCurMD = NULL; + if (pCurMT == pInterfaceMT) + { + if (!pInterfaceMD->IsAbstract()) + { + // exact match + pCurMD = pInterfaceMD; + } + } + else if (pCurMT->CanCastToInterface(pInterfaceMT)) + { + if (pCurMT->HasSameTypeDefAs(pInterfaceMT)) + { + // Generic variance match + pCurMD = pInterfaceMD; + } + else + { + // + // Parent interface - search for an methodimpl for explicit override + // Implicit override in default interface methods are not allowed + // + MethodIterator methodIt(pCurMT); + for (; methodIt.IsValid(); methodIt.Next()) + { + MethodDesc *pMD = methodIt.GetMethodDesc(); + if (pMD->IsVirtual() && !pMD->IsAbstract() && pMD->IsMethodImpl()) + { + MethodImpl *pImpl = pMD->GetMethodImpl(); + MethodDesc **pImplMDs = pImpl->GetImplementedMDs(); + for (DWORD i = 0; i < pImpl->GetSize(); ++i) + { + if (pImplMDs[i] == pInterfaceMD) + { + pCurMD = pMD; + break; + } + } + } + } + } + } + + if (pCurMD != NULL) + { + // + // Found a match. But is it a more specific match (we want most specific interfaces) + // + if (pCurMD->HasClassOrMethodInstantiation()) + { + // Instantiate the MethodDesc + // We don't want generic dictionary from this pointer - we need pass secret type argument + // from instantiating stubs to resolve ambiguity + pCurMD = MethodDesc::FindOrCreateAssociatedMethodDesc( + pCurMD, + pCurMT, + FALSE, // forceBoxedEntryPoint + pCurMD->HasMethodInstantiation() ? + pCurMD->AsInstantiatedMethodDesc()->IMD_GetMethodInstantiation() : + Instantiation(), // for method themselves that are generic + FALSE, // allowInstParam + TRUE // forceRemoteableMethod + ); + } + + if (pBestCandidateMT == NULL || // first time + pCurMT->CanCastToInterface(pBestCandidateMT)) // Prefer "more specific"" interface + { + // This is a better match + pBestCandidateMT = pCurMT; + pBestCandidateMD = pCurMD; + } + } + + it.Next(); + } + } + + pMT = pParentMT; + } + + if (pBestCandidateMD != NULL) + { + *ppDefaultMethod = pBestCandidateMD; + RETURN(TRUE); + } + + RETURN(FALSE); +} +#endif // DACCESS_COMPILE + //========================================================================================== DispatchSlot MethodTable::FindDispatchSlot(UINT32 typeID, UINT32 slotNumber) { @@ -9473,79 +9641,79 @@ MethodTable::TryResolveConstraintMethodApprox( // we don't have enough exact type information at JIT time // even to decide whether we will be able to resolve to an unboxed entry point... // To cope with this case we always go via the helper function if there's any - // chance of this happening by checking for all interfaces which might possibly - // be compatible with the call (verification will have ensured that - // at least one of them will be) - - // Enumerate all potential interface instantiations - MethodTable::InterfaceMapIterator it = pCanonMT->IterateInterfaceMap(); - DWORD cPotentialMatchingInterfaces = 0; - while (it.Next()) +// chance of this happening by checking for all interfaces which might possibly +// be compatible with the call (verification will have ensured that +// at least one of them will be) + +// Enumerate all potential interface instantiations +MethodTable::InterfaceMapIterator it = pCanonMT->IterateInterfaceMap(); +DWORD cPotentialMatchingInterfaces = 0; +while (it.Next()) +{ + TypeHandle thPotentialInterfaceType(it.GetInterface()); + if (thPotentialInterfaceType.AsMethodTable()->GetCanonicalMethodTable() == + thInterfaceType.AsMethodTable()->GetCanonicalMethodTable()) + { + cPotentialMatchingInterfaces++; + pMD = pCanonMT->GetMethodDescForInterfaceMethod(thPotentialInterfaceType, pGenInterfaceMD); + + // See code:#TryResolveConstraintMethodApprox_DoNotReturnParentMethod + if ((pMD != NULL) && !pMD->GetMethodTable()->IsValueType()) { - TypeHandle thPotentialInterfaceType(it.GetInterface()); - if (thPotentialInterfaceType.AsMethodTable()->GetCanonicalMethodTable() == - thInterfaceType.AsMethodTable()->GetCanonicalMethodTable()) - { - cPotentialMatchingInterfaces++; - pMD = pCanonMT->GetMethodDescForInterfaceMethod(thPotentialInterfaceType, pGenInterfaceMD); - - // See code:#TryResolveConstraintMethodApprox_DoNotReturnParentMethod - if ((pMD != NULL) && !pMD->GetMethodTable()->IsValueType()) - { - LOG((LF_JIT, LL_INFO10000, "TryResolveConstraintMethodApprox: %s::%s not a value type method\n", - pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName)); - return NULL; - } - } + LOG((LF_JIT, LL_INFO10000, "TryResolveConstraintMethodApprox: %s::%s not a value type method\n", + pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName)); + return NULL; } - - _ASSERTE_MSG((cPotentialMatchingInterfaces != 0), - "At least one interface has to implement the method, otherwise there's a bug in JIT/verification."); + } +} - if (cPotentialMatchingInterfaces > 1) - { // We have more potentially matching interfaces - MethodTable * pInterfaceMT = thInterfaceType.GetMethodTable(); - _ASSERTE(pInterfaceMT->HasInstantiation()); - - BOOL fIsExactMethodResolved = FALSE; - - if (!pInterfaceMT->IsSharedByGenericInstantiations() && - !pInterfaceMT->IsGenericTypeDefinition() && - !this->IsSharedByGenericInstantiations() && - !this->IsGenericTypeDefinition()) - { // We have exact interface and type instantiations (no generic variables and __Canon used - // anywhere) - if (this->CanCastToInterface(pInterfaceMT)) - { - // We can resolve to exact method - pMD = this->GetMethodDescForInterfaceMethod(pInterfaceMT, pInterfaceMD); - _ASSERTE(pMD != NULL); - fIsExactMethodResolved = TRUE; - } - } - - if (!fIsExactMethodResolved) - { // We couldn't resolve the interface statically - _ASSERTE(pfForceUseRuntimeLookup != NULL); - // Notify the caller that it should use runtime lookup - // Note that we can leave pMD incorrect, because we will use runtime lookup - *pfForceUseRuntimeLookup = TRUE; - } +_ASSERTE_MSG((cPotentialMatchingInterfaces != 0), + "At least one interface has to implement the method, otherwise there's a bug in JIT/verification."); + +if (cPotentialMatchingInterfaces > 1) +{ // We have more potentially matching interfaces + MethodTable * pInterfaceMT = thInterfaceType.GetMethodTable(); + _ASSERTE(pInterfaceMT->HasInstantiation()); + + BOOL fIsExactMethodResolved = FALSE; + + if (!pInterfaceMT->IsSharedByGenericInstantiations() && + !pInterfaceMT->IsGenericTypeDefinition() && + !this->IsSharedByGenericInstantiations() && + !this->IsGenericTypeDefinition()) + { // We have exact interface and type instantiations (no generic variables and __Canon used + // anywhere) + if (this->CanCastToInterface(pInterfaceMT)) + { + // We can resolve to exact method + pMD = this->GetMethodDescForInterfaceMethod(pInterfaceMT, pInterfaceMD); + _ASSERTE(pMD != NULL); + fIsExactMethodResolved = TRUE; } - else + } + + if (!fIsExactMethodResolved) + { // We couldn't resolve the interface statically + _ASSERTE(pfForceUseRuntimeLookup != NULL); + // Notify the caller that it should use runtime lookup + // Note that we can leave pMD incorrect, because we will use runtime lookup + *pfForceUseRuntimeLookup = TRUE; + } +} +else +{ + // If we can resolve the interface exactly then do so (e.g. when doing the exact + // lookup at runtime, or when not sharing generic code). + if (pCanonMT->CanCastToInterface(thInterfaceType.GetMethodTable())) + { + pMD = pCanonMT->GetMethodDescForInterfaceMethod(thInterfaceType, pGenInterfaceMD); + if (pMD == NULL) { - // If we can resolve the interface exactly then do so (e.g. when doing the exact - // lookup at runtime, or when not sharing generic code). - if (pCanonMT->CanCastToInterface(thInterfaceType.GetMethodTable())) - { - pMD = pCanonMT->GetMethodDescForInterfaceMethod(thInterfaceType, pGenInterfaceMD); - if (pMD == NULL) - { - LOG((LF_JIT, LL_INFO10000, "TryResolveConstraintMethodApprox: failed to find method desc for interface method\n")); - } - } + LOG((LF_JIT, LL_INFO10000, "TryResolveConstraintMethodApprox: failed to find method desc for interface method\n")); } } +} + } else if (pGenInterfaceMD->IsVirtual()) { if (pGenInterfaceMD->HasNonVtableSlot() && pGenInterfaceMD->GetMethodTable()->IsValueType()) @@ -9566,33 +9734,36 @@ MethodTable::TryResolveConstraintMethodApprox( // methods on System.Object, i.e. when these are used as a constraint. pMD = NULL; } - + if (pMD == NULL) { // Fall back to VSD return NULL; } - - //#TryResolveConstraintMethodApprox_DoNotReturnParentMethod - // Only return a method if the value type itself declares the method, - // otherwise we might get a method from Object or System.ValueType - if (!pMD->GetMethodTable()->IsValueType()) - { // Fall back to VSD - return NULL; + + if (!pMD->GetMethodTable()->IsInterface()) + { + //#TryResolveConstraintMethodApprox_DoNotReturnParentMethod + // Only return a method if the value type itself declares the method + // otherwise we might get a method from Object or System.ValueType + if (!pMD->GetMethodTable()->IsValueType()) + { // Fall back to VSD + return NULL; + } + + // We've resolved the method, ignoring its generic method arguments + // If the method is a generic method then go and get the instantiated descriptor + pMD = MethodDesc::FindOrCreateAssociatedMethodDesc( + pMD, + this, + FALSE /* no BoxedEntryPointStub */, + pInterfaceMD->GetMethodInstantiation(), + FALSE /* no allowInstParam */); + + // FindOrCreateAssociatedMethodDesc won't return an BoxedEntryPointStub. + _ASSERTE(pMD != NULL); + _ASSERTE(!pMD->IsUnboxingStub()); } - - // We've resolved the method, ignoring its generic method arguments - // If the method is a generic method then go and get the instantiated descriptor - pMD = MethodDesc::FindOrCreateAssociatedMethodDesc( - pMD, - this, - FALSE /* no BoxedEntryPointStub */ , - pInterfaceMD->GetMethodInstantiation(), - FALSE /* no allowInstParam */ ); - - // FindOrCreateAssociatedMethodDesc won't return an BoxedEntryPointStub. - _ASSERTE(pMD != NULL); - _ASSERTE(!pMD->IsUnboxingStub()); - + return pMD; } // MethodTable::TryResolveConstraintMethodApprox diff --git a/src/vm/methodtable.h b/src/vm/methodtable.h index d3eb0ce..11296b0 100644 --- a/src/vm/methodtable.h +++ b/src/vm/methodtable.h @@ -2485,6 +2485,14 @@ public: UINT32 slotNumber, DispatchSlot * pImplSlot); + +#ifndef DACCESS_COMPILE + BOOL FindDefaultMethod( + MethodDesc *pInterfaceMD, + MethodTable *pObjectMT, + MethodDesc **ppDefaultMethod); +#endif // DACCESS_COMPILE + DispatchSlot FindDispatchSlot(UINT32 typeID, UINT32 slotNumber); DispatchSlot FindDispatchSlot(DispatchToken tok); diff --git a/src/vm/methodtable.inl b/src/vm/methodtable.inl index 9b72d24..f8a073f 100644 --- a/src/vm/methodtable.inl +++ b/src/vm/methodtable.inl @@ -650,12 +650,15 @@ inline MethodDesc* MethodTable::GetMethodDescForSlot(DWORD slot) PCODE pCode = GetRestoredSlot(slot); - // This is an optimization that we can take advantage of if we're trying - // to get the MethodDesc for an interface virtual, since their slots - // always point to the stub. + // This is an optimization that we can take advantage of if we're trying to get the MethodDesc + // for an interface virtual, since their slots usually point to stub. if (IsInterface() && slot < GetNumVirtuals()) { - return MethodDesc::GetMethodDescFromStubAddr(pCode); + // @DIM_TODO - This is not a reliable approach. Need to change MakeJitWorker to not stomp + // over slot and instead set the target of precode to the address. We may need the precode + // there anyway to handle other cases too (such as interop). + MethodDesc *pMD = MethodDesc::GetMethodDescFromStubAddr(pCode, /* fSpeculative = */ TRUE); + if (pMD != NULL) return pMD; } return MethodTable::GetMethodDescForSlotAddress(pCode); diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp index bd9dd24..b3aed49 100644 --- a/src/vm/methodtablebuilder.cpp +++ b/src/vm/methodtablebuilder.cpp @@ -1620,19 +1620,24 @@ MethodTableBuilder::BuildMethodTableThrowing( // Allocate MethodDescs (expects methods placed methods) AllocAndInitMethodDescs(); - // - // If we are a class, then there may be some unplaced vtable methods (which are by definition - // interface methods, otherwise they'd already have been placed). Place as many unplaced methods - // as possible, in the order preferred by interfaces. However, do not allow any duplicates - once - // a method has been placed, it cannot be placed again - if we are unable to neatly place an interface, - // create duplicate slots for it starting at dwCurrentDuplicateVtableSlot. Fill out the interface - // map for all interfaces as they are placed. - // - // If we are an interface, then all methods are already placed. Fill out the interface map for - // interfaces as they are placed. - // - if (!IsInterface()) + if (IsInterface()) { + ProcessMethodImpls(); + PlaceMethodImpls(); + } + else + { + // + // If we are a class, then there may be some unplaced vtable methods (which are by definition + // interface methods, otherwise they'd already have been placed). Place as many unplaced methods + // as possible, in the order preferred by interfaces. However, do not allow any duplicates - once + // a method has been placed, it cannot be placed again - if we are unable to neatly place an interface, + // create duplicate slots for it starting at dwCurrentDuplicateVtableSlot. Fill out the interface + // map for all interfaces as they are placed. + // + // If we are an interface, then all methods are already placed. Fill out the interface map for + // interfaces as they are placed. + // ComputeInterfaceMapEquivalenceSet(); PlaceInterfaceMethods(); @@ -2843,16 +2848,16 @@ MethodTableBuilder::EnumerateClassMethods() BuildMethodTableThrowException(BFA_NONVIRT_AB_METHOD); } } - else if(fIsClassInterface) - { - if (IsMdRTSpecialName(dwMemberAttrs) || IsMdVirtual(dwMemberAttrs)) - { - CONSISTENCY_CHECK(CheckPointer(strMethodName)); - if (strcmp(strMethodName, COR_CCTOR_METHOD_NAME)) - { - BuildMethodTableThrowException(BFA_NONAB_NONCCTOR_METHOD_ON_INT); - } - } + else if(fIsClassInterface) + { + if (IsMdRTSpecialName(dwMemberAttrs)) + { + CONSISTENCY_CHECK(CheckPointer(strMethodName)); + if (strcmp(strMethodName, COR_CCTOR_METHOD_NAME)) + { + BuildMethodTableThrowException(BFA_NONAB_NONCCTOR_METHOD_ON_INT); + } + } } // Virtual / not virtual @@ -2872,26 +2877,6 @@ MethodTableBuilder::EnumerateClassMethods() } } - // Some interface checks. - if (IsInterface()) - { - if (IsMdVirtual(dwMemberAttrs)) - { - if (!IsMdAbstract(dwMemberAttrs)) - { - BuildMethodTableThrowException(BFA_VIRTUAL_NONAB_INT_METHOD); - } - } - else - { - // Instance field/method - if (!IsMdStatic(dwMemberAttrs)) - { - BuildMethodTableThrowException(BFA_NONVIRT_INST_INT_METHOD); - } - } - } - // No synchronized methods in ValueTypes if(fIsClassValueType && IsMiSynchronized(dwImplFlags)) { @@ -3114,7 +3099,7 @@ MethodTableBuilder::EnumerateClassMethods() type = METHOD_TYPE_NORMAL; } else if (bmtGenerics->GetNumGenericArgs() != 0 && - (bmtGenerics->fSharedByGenericInstantiations || (!bmtProp->fIsRedirectedInterface && !GetHalfBakedClass()->IsProjectedFromWinRT()))) + (bmtGenerics->fSharedByGenericInstantiations || (!bmtProp->fIsRedirectedInterface && !GetHalfBakedClass()->IsProjectedFromWinRT()))) { // Methods in instantiated interfaces need nothing special - they are not visible from COM etc. // mcComInterop is only useful for unshared instantiated WinRT interfaces. If the interface is @@ -3126,7 +3111,7 @@ MethodTableBuilder::EnumerateClassMethods() // If the interface is a standard managed interface then allocate space for an FCall method desc. type = METHOD_TYPE_FCALL; } - else + else if (IsMdAbstract(dwMemberAttrs)) { // If COM interop is supported then all other interface MDs may be // accessed via COM interop. mcComInterop MDs have an additional @@ -3134,10 +3119,12 @@ MethodTableBuilder::EnumerateClassMethods() // allocated lazily when/if the MD actually gets used for interop. type = METHOD_TYPE_COMINTEROP; } -#else // !FEATURE_COMINTEROP - // This codepath is used by remoting - type = METHOD_TYPE_NORMAL; + else #endif // !FEATURE_COMINTEROP + { + // This codepath is used by remoting + type = METHOD_TYPE_NORMAL; + } } else { @@ -5712,49 +5699,60 @@ MethodTableBuilder::ProcessMethodImpls() } } - if (pDeclType == NULL) + if (IsInterface()) { - DWORD equivalenceSet = 0; - - for (DWORD i = 0; i < bmtInterface->dwInterfaceMapSize; i++) - { - bmtRTType * pCurItf = bmtInterface->pInterfaceMap[i].GetInterfaceType(); - // Type Equivalence is respected for this comparision as we just need to find an - // equivalent interface, the particular interface is unimportant - if (MetaSig::CompareTypeDefsUnderSubstitutions( - pCurItf->GetMethodTable(), pDeclMT, - &pCurItf->GetSubstitution(), pDeclSubst, - NULL)) - { - equivalenceSet = bmtInterface->pInterfaceMap[i].GetInterfaceEquivalenceSet(); - pItfEntry = &bmtInterface->pInterfaceMap[i]; - break; - } - } - - if (equivalenceSet == 0) + if (pDeclType == NULL) { // Interface is not implemented by this type. BuildMethodTableThrowException(IDS_CLASSLOAD_MI_NOTIMPLEMENTED, it.Token()); } - - // Interface is not implemented by this type exactly. We need to consider this MethodImpl on non exact interface matches, - // as the only match may be one of the non-exact matches - bmtMetaData->rgMethodImplTokens[m].fConsiderDuringInexactMethodImplProcessing = true; - bmtMetaData->rgMethodImplTokens[m].fThrowIfUnmatchedDuringInexactMethodImplProcessing = true; - bmtMetaData->rgMethodImplTokens[m].interfaceEquivalenceSet = equivalenceSet; - bmtMethod->dwNumberInexactMethodImplCandidates++; - continue; // Move on to other MethodImpls } else { - // This method impl may need to match other methods during inexact processing - if (pItfEntry->InEquivalenceSetWithMultipleEntries()) + if (pDeclType == NULL) { + DWORD equivalenceSet = 0; + + for (DWORD i = 0; i < bmtInterface->dwInterfaceMapSize; i++) + { + bmtRTType * pCurItf = bmtInterface->pInterfaceMap[i].GetInterfaceType(); + // Type Equivalence is respected for this comparision as we just need to find an + // equivalent interface, the particular interface is unimportant + if (MetaSig::CompareTypeDefsUnderSubstitutions( + pCurItf->GetMethodTable(), pDeclMT, + &pCurItf->GetSubstitution(), pDeclSubst, + NULL)) + { + equivalenceSet = bmtInterface->pInterfaceMap[i].GetInterfaceEquivalenceSet(); + pItfEntry = &bmtInterface->pInterfaceMap[i]; + break; + } + } + + if (equivalenceSet == 0) + { + // Interface is not implemented by this type. + BuildMethodTableThrowException(IDS_CLASSLOAD_MI_NOTIMPLEMENTED, it.Token()); + } + + // Interface is not implemented by this type exactly. We need to consider this MethodImpl on non exact interface matches, + // as the only match may be one of the non-exact matches bmtMetaData->rgMethodImplTokens[m].fConsiderDuringInexactMethodImplProcessing = true; - bmtMetaData->rgMethodImplTokens[m].fThrowIfUnmatchedDuringInexactMethodImplProcessing = false; - bmtMetaData->rgMethodImplTokens[m].interfaceEquivalenceSet = pItfEntry->GetInterfaceEquivalenceSet(); + bmtMetaData->rgMethodImplTokens[m].fThrowIfUnmatchedDuringInexactMethodImplProcessing = true; + bmtMetaData->rgMethodImplTokens[m].interfaceEquivalenceSet = equivalenceSet; bmtMethod->dwNumberInexactMethodImplCandidates++; + continue; // Move on to other MethodImpls + } + else + { + // This method impl may need to match other methods during inexact processing + if (pItfEntry->InEquivalenceSetWithMultipleEntries()) + { + bmtMetaData->rgMethodImplTokens[m].fConsiderDuringInexactMethodImplProcessing = true; + bmtMetaData->rgMethodImplTokens[m].fThrowIfUnmatchedDuringInexactMethodImplProcessing = false; + bmtMetaData->rgMethodImplTokens[m].interfaceEquivalenceSet = pItfEntry->GetInterfaceEquivalenceSet(); + bmtMethod->dwNumberInexactMethodImplCandidates++; + } } } @@ -5856,7 +5854,7 @@ MethodTableBuilder::ProcessMethodImpls() { // Method not found, throw. BuildMethodTableThrowException(IDS_CLASSLOAD_MI_DECLARATIONNOTFOUND, it.Token()); } - + if (!IsMdVirtual(declMethod.GetDeclAttrs())) { // Make sure the decl is virtual BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL, it.Token()); @@ -6190,35 +6188,62 @@ MethodTableBuilder::PlaceMethodImpls() BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MULTIPLEOVERRIDES, mdef); } - // Throws - PlaceLocalDeclaration(pCurDeclMethod, - pCurImplMethod, - slots, // Adds override to the slot and replaced arrays. - replaced, - &slotIndex); // Increments count + if (IsInterface()) + { + // We implement this slot, record it + slots[slotIndex] = pCurDeclMethod->GetSlotIndex(); + replaced[slotIndex] = pCurDeclMethod->GetMethodDesc(); + + // increment the counter + slotIndex++; + } + else + { + // Throws + PlaceLocalDeclaration( + pCurDeclMethod, + pCurImplMethod, + slots, // Adds override to the slot and replaced arrays. + replaced, + &slotIndex); // Increments count + } } else { bmtRTMethod * pCurDeclMethod = hDeclMethod.AsRTMethod(); - // Do not use pDecl->IsInterface here as that asks the method table and the MT may not yet be set up. - if(pCurDeclMethod->GetOwningType()->IsInterface()) + if (IsInterface()) { - // Throws - PlaceInterfaceDeclaration(pCurDeclMethod, - pCurImplMethod, - slots, - replaced, - &slotIndex); // Increments count + // We implement this slot, record it + slots[slotIndex] = pCurDeclMethod->GetSlotIndex(); + replaced[slotIndex] = pCurDeclMethod->GetMethodDesc(); + + // increment the counter + slotIndex++; } else { - // Throws - PlaceParentDeclaration(pCurDeclMethod, - pCurImplMethod, - slots, - replaced, - &slotIndex); // Increments count + // Do not use pDecl->IsInterface here as that asks the method table and the MT may not yet be set up. + if (pCurDeclMethod->GetOwningType()->IsInterface()) + { + // Throws + PlaceInterfaceDeclaration( + pCurDeclMethod, + pCurImplMethod, + slots, + replaced, + &slotIndex); // Increments count + } + else + { + // Throws + PlaceParentDeclaration( + pCurDeclMethod, + pCurImplMethod, + slots, + replaced, + &slotIndex); // Increments count + } } } @@ -6273,20 +6298,30 @@ MethodTableBuilder::WriteMethodImplData( // Set the size of the info the MethodImpl needs to keep track of. pImpl->SetSize(GetLoaderAllocator()->GetHighFrequencyHeap(), GetMemTracker(), cSlots); - // Gasp we do a bubble sort. Should change this to a qsort.. - for (DWORD i = 0; i < cSlots; i++) + if (!IsInterface()) { - for (DWORD j = i+1; j < cSlots; j++) + // If we are currently builting an interface, the slots here has no meaning and we can skip it + // Sort the two arrays in slot index order + for (DWORD i = 0; i < cSlots; i++) { - if (rgSlots[j] < rgSlots[i]) + int min = i; + for (DWORD j = i + 1; j < cSlots; j++) + { + if (rgSlots[j] < rgSlots[min]) + { + min = j; + } + } + + if (min != i) { MethodDesc * mTmp = rgDeclMD[i].GetValue(); - rgDeclMD[i].SetValue(rgDeclMD[j].GetValue()); - rgDeclMD[j].SetValue(mTmp); + rgDeclMD[i].SetValue(rgDeclMD[min].GetValue()); + rgDeclMD[min].SetValue(mTmp); DWORD sTmp = rgSlots[i]; - rgSlots[i] = rgSlots[j]; - rgSlots[j] = sTmp; + rgSlots[i] = rgSlots[min]; + rgSlots[min] = sTmp; } } } @@ -6560,8 +6595,8 @@ VOID MethodTableBuilder::ValidateInterfaceMethodConstraints() // If pTargetMT is null, this indicates that the target MethodDesc belongs // to the current type. Otherwise, the MethodDesc MUST be owned by a parent // of the type we're building. - BOOL fTargetIsOwnedByParent = !pTargetMD->GetMethodTablePtr()->IsNull(); - + BOOL fTargetIsOwnedByParent = !pTargetMD->GetMethodTablePtr()->IsNull(); + // If the method is owned by a parent, we need to use the parent's module, // and we must construct the substitution chain all the way up to the parent. const Substitution *pSubstTgt = NULL; @@ -10820,11 +10855,13 @@ void MethodTableBuilder::VerifyVirtualMethodsImplemented(MethodTable::MethodData MethodTable::MethodIterator it(hData); for (; it.IsValid() && it.IsVirtual(); it.Next()) { - if (it.GetTarget().IsNull()) - { - MethodDesc *pMD = it.GetDeclMethodDesc(); - BuildMethodTableThrowException(IDS_CLASSLOAD_NOTIMPLEMENTED, pMD->GetNameOnNonArrayClass()); - } + // @DIM_TODO - What is the right level of check if the interface itself does not have default implementation + // but a derived interface do + // if (it.GetTarget().IsNull()) + // { + // MethodDesc *pMD = it.GetDeclMethodDesc(); + // BuildMethodTableThrowException(IDS_CLASSLOAD_NOTIMPLEMENTED, pMD->GetNameOnNonArrayClass()); + // } } } } diff --git a/tests/src/IL.targets b/tests/src/IL.targets index d1075e5..88bb44e 100644 --- a/tests/src/IL.targets +++ b/tests/src/IL.targets @@ -24,9 +24,12 @@ <_IlasmSwitches Condition="'$(DebugType)' == 'Impl'">$(_IlasmSwitches) -DEBUG=IMPL <_IlasmSwitches Condition="'$(DebugType)' == 'PdbOnly'">$(_IlasmSwitches) -DEBUG=OPT <_IlasmSwitches Condition="'$(Optimize)' == 'True'">$(_IlasmSwitches) -OPTIMIZE + ilasm + + $(__BinDir)\$(ILAsmExe) - + diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.cs b/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.cs new file mode 100644 index 0000000..decd7bd --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; + +interface IFoo +{ + int Foo(int c); +} + +interface IAdd +{ + int Add(int c); +} + +// Only needed for writing IFoo.Foo code +class IFoo_Impl : IFoo +{ + public int Foo(int c) + { + IAdd adder = (IAdd) this; + return adder.Add(c); + } +} + +struct FooValue : IFoo, IAdd +{ + public int val; + + public int Foo(int c) + { + val +=c; + return val; + } + + public int Add(int c) + { + val +=c; + return val; + } +} + +interface IHorrible +{ + int GetLocalVal(); + void SetLocalVal(int val); + int Horrible(); +} + +// Only needed for the default interface implementation +class IHorrible_Impl : IHorrible +{ + public int GetLocalVal() { return 0; } + public void SetLocalVal(int val) {} + public int Horrible() + { + int val = GetLocalVal(); + val++; + SetLocalVal(val); + return val; + } +} + +struct HorribleCase : IHorrible>, IHorrible> +{ + int localVal; + public int GetLocalVal() { return localVal; } + public void SetLocalVal(int val) { localVal = val; } + int IHorrible>.Horrible() { return ++localVal; } + + // Remove + int IHorrible>.Horrible() { return ++localVal; } +} + +class HorribleTest +{ + public static int Horror(T t) where T:IHorrible + { + return t.Horrible() + t.Horrible(); + } + + public static void RunTest() + { + Test.Assert(Horror,IEnumerable>(new HorribleCase())) == 2, "Fail"); + Test.Assert(Horror,IList>(default(HorribleCase)) == 3, "Fail"); + } +} + +/* +interface IFoo +{ + int Foo(int c); +} + +interface IAdd +{ + int Add(int c); +} + +// Only needed for writing IFoo.Foo code +class IFoo_Impl : IFoo +{ + public int Foo(int c) + { + IAdd adder = (IAdd) this; + return adder.Add(c); + } +} + +struct FooValue : IFoo, IAdd +{ + public int val; + + public int Foo(int c) + { + val +=c; + return val; + } + + public int Add(int c) + { + val +=c; + return val; + } +} +*/ + +class SimpleConstraintTest +{ + public static int CallFoo_WithConstraints(ref T foo, int val) where T : IFoo + { + return foo.Foo(val); + } + + /* + public static int CallFoo_WithConstraints(ref T foo, int val) where T : IFoo + { + return foo.Foo(val); + } + */ + + public static void RunTest() + { + FooValue foo = new FooValue(); + foo.val = 10; + + Console.WriteLine("Calling CallFoo_WithConstraints on FooValue - expecting IFoo::Foo"); + Test.Assert(CallFoo_WithConstraints(ref foo, 10) == 20, "Calling CallFoo_WithConstraints on FooValue"); + + Test.Assert(foo.val == 10, "Expecting boxing on CallFoo_WithConstraints"); + } +} + +class Program +{ + public static int Main() + { + HorribleTest.RunTest(); + SimpleConstraintTest.RunTest(); + + return Test.Ret(); + } +} + +class Test +{ + private static bool Pass = true; + + public static int Ret() + { + return Pass? 100 : 101; + } + + public static void Assert(bool cond, string msg) + { + if (cond) + { + Console.WriteLine("PASS"); + } + else + { + Console.WriteLine("FAIL: " + msg); + Pass = false; + } + } +} + diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.il b/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.il new file mode 100644 index 0000000..ef39ba5 --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.il @@ -0,0 +1,456 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly constrainedcall +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) + + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module constrainedcall.exe +// MVID: {6171EA0F-1009-482D-8EF6-C944886D5D66} +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x01860000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class interface private abstract auto ansi IFoo +{ + .method public hidebysig newslot virtual + instance int32 Foo(int32 c) cil managed + { + // Code size 20 (0x14) + .maxstack 2 + .locals init (class IAdd V_0, + int32 V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: castclass IAdd + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldarg.1 + IL_000a: callvirt instance int32 IAdd::Add(int32) + IL_000f: stloc.1 + IL_0010: br.s IL_0012 + + IL_0012: ldloc.1 + IL_0013: ret + } // end of method IFoo::Foo + +} // end of class IFoo + +.class interface private abstract auto ansi IAdd +{ + .method public hidebysig newslot abstract virtual + instance int32 Add(int32 c) cil managed + { + } // end of method IAdd::Add + +} // end of class IAdd + +.class private sequential ansi sealed beforefieldinit FooValue + extends [mscorlib]System.ValueType + implements IFoo, + IAdd +{ + .field public int32 val + + .method public hidebysig newslot virtual final + instance int32 Add(int32 c) cil managed + { + // Code size 26 (0x1a) + .maxstack 3 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.0 + IL_0003: ldfld int32 FooValue::val + IL_0008: ldarg.1 + IL_0009: add + IL_000a: stfld int32 FooValue::val + IL_000f: ldarg.0 + IL_0010: ldfld int32 FooValue::val + IL_0015: stloc.0 + IL_0016: br.s IL_0018 + + IL_0018: ldloc.0 + IL_0019: ret + } // end of method FooValue::Add + +} // end of class FooValue + +.class interface private abstract auto ansi IHorrible`1 +{ + .method public hidebysig newslot abstract virtual + instance int32 GetLocalVal() cil managed + { + } // end of method IHorrible`1::GetLocalVal + + .method public hidebysig newslot abstract virtual + instance void SetLocalVal(int32 val) cil managed + { + } // end of method IHorrible`1::SetLocalVal + + .method public hidebysig newslot virtual + instance int32 Horrible() cil managed + { + // Code size 26 (0x1a) + .maxstack 2 + .locals init (int32 V_0, + int32 V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: callvirt instance int32 class IHorrible`1::GetLocalVal() + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldc.i4.1 + IL_000a: add + IL_000b: stloc.0 + IL_000c: ldarg.0 + IL_000d: ldloc.0 + IL_000e: callvirt instance void class IHorrible`1::SetLocalVal(int32) + IL_0013: nop + IL_0014: ldloc.0 + IL_0015: stloc.1 + IL_0016: br.s IL_0018 + + IL_0018: ldloc.1 + IL_0019: ret + } // end of method IHorrible`1::Horrible + +} // end of class IHorrible`1 + +.class private sequential ansi sealed beforefieldinit HorribleCase`1 + extends [mscorlib]System.ValueType + implements class IHorrible`1>, + class IHorrible`1> +{ + .field private int32 localVal + .method public hidebysig newslot virtual final + instance int32 GetLocalVal() cil managed + { + // Code size 12 (0xc) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldfld int32 valuetype HorribleCase`1::localVal + IL_0007: stloc.0 + IL_0008: br.s IL_000a + + IL_000a: ldloc.0 + IL_000b: ret + } // end of method HorribleCase`1::GetLocalVal + + .method public hidebysig newslot virtual final + instance void SetLocalVal(int32 val) cil managed + { + // Code size 9 (0x9) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.1 + IL_0003: stfld int32 valuetype HorribleCase`1::localVal + IL_0008: ret + } // end of method HorribleCase`1::SetLocalVal + + .method private hidebysig newslot virtual final + instance int32 'IHorrible>.Horrible'() cil managed + { + .override method instance int32 class IHorrible`1>::Horrible() + // Code size 23 (0x17) + .maxstack 3 + .locals init (int32 V_0, + int32 V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.0 + IL_0003: ldfld int32 valuetype HorribleCase`1::localVal + IL_0008: ldc.i4.1 + IL_0009: add + IL_000a: stloc.0 + IL_000b: ldloc.0 + IL_000c: stfld int32 valuetype HorribleCase`1::localVal + IL_0011: ldloc.0 + IL_0012: stloc.1 + IL_0013: br.s IL_0015 + + IL_0015: ldloc.1 + IL_0016: ret + } // end of method HorribleCase`1::'IHorrible>.Horrible' +} // end of class HorribleCase`1 + +.class private auto ansi beforefieldinit HorribleTest + extends [mscorlib]System.Object +{ + .method public hidebysig static int32 Horror<(class IHorrible`1) T,U>(!!T t) cil managed + { + // Code size 33 (0x21) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarga.s t + IL_0003: constrained. !!T + IL_0009: callvirt instance int32 class IHorrible`1::Horrible() + IL_000e: ldarga.s t + IL_0010: constrained. !!T + IL_0016: callvirt instance int32 class IHorrible`1::Horrible() + IL_001b: add + IL_001c: stloc.0 + IL_001d: br.s IL_001f + + IL_001f: ldloc.0 + IL_0020: ret + } // end of method HorribleTest::Horror + + .method public hidebysig static void RunTest() cil managed + { + // Code size 58 (0x3a) + .maxstack 2 + .locals init (valuetype HorribleCase`1 V_0) + IL_0000: nop + IL_0001: ldloca.s V_0 + IL_0003: initobj valuetype HorribleCase`1 + IL_0009: ldloc.0 + IL_000a: call int32 HorribleTest::Horror,class [mscorlib]System.Collections.Generic.IEnumerable`1>(!!0) + IL_000f: ldc.i4.3 + IL_0010: ceq + IL_0012: ldstr "Fail" + IL_0017: call void Test::Assert(bool, + string) + IL_001c: nop + IL_001d: ldloca.s V_0 + IL_001f: initobj valuetype HorribleCase`1 + IL_0025: ldloc.0 + IL_0026: call int32 HorribleTest::Horror,class [mscorlib]System.Collections.Generic.IList`1>(!!0) + IL_002b: ldc.i4.0 + IL_002c: ceq + IL_002e: ldstr "Fail" + IL_0033: call void Test::Assert(bool, + string) + IL_0038: nop + IL_0039: ret + } // end of method HorribleTest::RunTest + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method HorribleTest::.ctor + +} // end of class HorribleTest + +.class private auto ansi beforefieldinit SimpleConstraintTest + extends [mscorlib]System.Object +{ + .method public hidebysig static int32 CallFoo_WithConstraints<(IFoo) T>(!!T& foo, + int32 val) cil managed + { + // Code size 19 (0x13) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.1 + IL_0003: constrained. !!T + IL_0009: callvirt instance int32 IFoo::Foo(int32) + IL_000e: stloc.0 + IL_000f: br.s IL_0011 + + IL_0011: ldloc.0 + IL_0012: ret + } // end of method SimpleConstraintTest::CallFoo_WithConstraints + + .method public hidebysig static void RunTest() cil managed + { + // Code size 75 (0x4b) + .maxstack 2 + .locals init (valuetype FooValue V_0) + IL_0000: nop + IL_0001: ldloca.s V_0 + IL_0003: initobj FooValue + IL_0009: ldloca.s V_0 + IL_000b: ldc.i4.s 10 + IL_000d: stfld int32 FooValue::val + IL_0012: ldstr "Calling CallFoo_WithConstraints on FooValue - expe" + + "cting IFoo::Foo" + IL_0017: call void [mscorlib]System.Console::WriteLine(string) + IL_001c: nop + IL_001d: ldloca.s V_0 + IL_001f: ldc.i4.s 10 + IL_0021: call int32 SimpleConstraintTest::CallFoo_WithConstraints(!!0&, + int32) + IL_0026: ldc.i4.s 20 + IL_0028: ceq + IL_002a: ldstr "Calling CallFoo_WithConstraints on FooValue" + IL_002f: call void Test::Assert(bool, + string) + IL_0034: nop + IL_0035: ldloc.0 + IL_0036: ldfld int32 FooValue::val + IL_003b: ldc.i4.s 10 + IL_003d: ceq + IL_003f: ldstr "Expecting boxing on CallFoo_WithConstraints" + IL_0044: call void Test::Assert(bool, + string) + IL_0049: nop + IL_004a: ret + } // end of method SimpleConstraintTest::RunTest + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method SimpleConstraintTest::.ctor + +} // end of class SimpleConstraintTest + +.class private auto ansi beforefieldinit Program + extends [mscorlib]System.Object +{ + .method public hidebysig static int32 Main() cil managed + { + .entrypoint + // Code size 23 (0x17) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: call void HorribleTest::RunTest() + IL_0006: nop + IL_0007: call void SimpleConstraintTest::RunTest() + IL_000c: nop + IL_000d: call int32 Test::Ret() + IL_0012: stloc.0 + IL_0013: br.s IL_0015 + + IL_0015: ldloc.0 + IL_0016: ret + } // end of method Program::Main + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Program::.ctor + +} // end of class Program + +.class private auto ansi beforefieldinit Test + extends [mscorlib]System.Object +{ + .field private static bool Pass + .method public hidebysig static int32 Ret() cil managed + { + // Code size 19 (0x13) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldsfld bool Test::Pass + IL_0006: brtrue.s IL_000c + + IL_0008: ldc.i4.s 101 + IL_000a: br.s IL_000e + + IL_000c: ldc.i4.s 100 + IL_000e: stloc.0 + IL_000f: br.s IL_0011 + + IL_0011: ldloc.0 + IL_0012: ret + } // end of method Test::Ret + + .method public hidebysig static void Assert(bool cond, + string msg) cil managed + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: brfalse.s IL_0015 + + IL_0006: nop + IL_0007: ldstr "PASS" + IL_000c: call void [mscorlib]System.Console::WriteLine(string) + IL_0011: nop + IL_0012: nop + IL_0013: br.s IL_002e + + IL_0015: nop + IL_0016: ldstr "FAIL: " + IL_001b: ldarg.1 + IL_001c: call string [mscorlib]System.String::Concat(string, + string) + IL_0021: call void [mscorlib]System.Console::WriteLine(string) + IL_0026: nop + IL_0027: ldc.i4.0 + IL_0028: stsfld bool Test::Pass + IL_002d: nop + IL_002e: ret + } // end of method Test::Assert + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Test::.ctor + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldc.i4.1 + IL_0001: stsfld bool Test::Pass + IL_0006: ret + } // end of method Test::.cctor + +} // end of class Test + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** +// WARNING: Created Win32 resource file constrainedcall.res diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.ilproj b/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.ilproj new file mode 100644 index 0000000..e170b2b --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.ilproj @@ -0,0 +1,37 @@ + + + + + constrainedcall + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 7a9bfb7d + true + true + Exe + BuildAndRun + 0 + + True + + + + + False + + + + + + + + + + + + + diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/genericmethods/genericmethods.cs b/tests/src/Loader/classloader/DefaultInterfaceMethods/genericmethods/genericmethods.cs new file mode 100644 index 0000000..ca4936b --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/genericmethods/genericmethods.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +interface IFoo +{ + Type Foo(); +} + +interface IBar +{ + Type Bar1

(); + Type Bar2(); + void Bar3(out Type t, out Type u); +} + +class FooBar : IFoo, IBar +{ + public Type Foo() + { + Console.WriteLine("At IFoo::Foo: TypeOf(T) = {0}", typeof(T)); + return typeof(T); + } + + public Type Bar1

() + { + Console.WriteLine("At IBar::Foo

: TypeOf(P) = {0}", typeof(P)); + return typeof(P); + } + + public Type Bar2() + { + Console.WriteLine("At IBar::Bar2: TypeOf(K) = {0}", typeof(K)); + return typeof(K); + } + + public void Bar3(out Type t, out Type u) + { + Console.WriteLine("At IBar::Bar3: TypeOf(P) = {0}, TypeOf(K) = {1}", typeof(P), typeof(K)); + t = typeof(P); + u = typeof(K); + } +} + + +class Program +{ + static int Main(string[] args) + { + FooBar fooBar = new FooBar(); + IFoo foo = (IFoo) fooBar; + IBar bar = (IBar) fooBar; + + Console.WriteLine("Calling IFoo.Foo on FooBar - expecting IFoo::Foo() returning typeof(string)"); + Test.Assert(foo.Foo() == typeof(string), "Calling IFoo.Foo on FooBar"); + + Console.WriteLine("Calling IBar.Bar1 on FooBar - expecting bar.Bar1() returning typeof(string)"); + Test.Assert(bar.Bar1() == typeof(string), "Calling IBar.Bar1 on FooBar"); + + Console.WriteLine("Calling IBar.Bar2 on FooBar - expecting bar.Bar2() returning typeof(string[])"); + Test.Assert(bar.Bar2() == typeof(string[]), "Calling IBar.Bar2 on FooBar"); + + Type p, k; + Console.WriteLine("Calling IBar.Bar3 - expecting bar.Bar3() returning typeof(string), typeof(string[])"); + bar.Bar3(out p, out k); + Test.Assert(p == typeof(string) && k == typeof(string[]), "Calling IBar.Bar3"); + + return Test.Ret(); + } +} + +class Test +{ + private static bool Pass = true; + + public static int Ret() + { + return Pass ? 100 : 101; + } + + public static void Assert(bool cond, string msg) + { + if (cond) + { + Console.WriteLine("PASS"); + } + else + { + Console.WriteLine("FAIL: " + msg); + Pass = false; + } + } +} + diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/genericmethods/genericmethods.il b/tests/src/Loader/classloader/DefaultInterfaceMethods/genericmethods/genericmethods.il new file mode 100644 index 0000000..3a3204d --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/genericmethods/genericmethods.il @@ -0,0 +1,357 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly genericmethods +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) + + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module genericmethods.exe +// MVID: {62CAFB9A-4CDB-4A62-8A4F-DC7648609070} +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x01790000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class interface private abstract auto ansi IFoo +{ + .method public hidebysig newslot virtual + instance class [mscorlib]System.Type + Foo() cil managed + { + // Code size 37 (0x25) + .maxstack 2 + .locals init (class [mscorlib]System.Type V_0) + IL_0000: nop + IL_0001: ldstr "At IFoo::Foo: TypeOf(T) = {0}" + IL_0006: ldtoken !!T + IL_000b: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0010: call void [mscorlib]System.Console::WriteLine(string, + object) + IL_0015: nop + IL_0016: ldtoken !!T + IL_001b: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0020: stloc.0 + IL_0021: br.s IL_0023 + + IL_0023: ldloc.0 + IL_0024: ret + } // end of method IFoo::Foo + +} // end of class IFoo + +.class interface private abstract auto ansi IBar`1 +{ + .method public hidebysig newslot virtual + instance class [mscorlib]System.Type + Bar1

() cil managed + { + // Code size 37 (0x25) + .maxstack 2 + .locals init (class [mscorlib]System.Type V_0) + IL_0000: nop + IL_0001: ldstr "At IBar::Foo

: TypeOf(P) = {0}" + IL_0006: ldtoken !!P + IL_000b: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0010: call void [mscorlib]System.Console::WriteLine(string, + object) + IL_0015: nop + IL_0016: ldtoken !!P + IL_001b: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0020: stloc.0 + IL_0021: br.s IL_0023 + + IL_0023: ldloc.0 + IL_0024: ret + } // end of method IBar`1::Bar1 + + .method public hidebysig newslot virtual + instance class [mscorlib]System.Type + Bar2() cil managed + { + // Code size 37 (0x25) + .maxstack 2 + .locals init (class [mscorlib]System.Type V_0) + IL_0000: nop + IL_0001: ldstr "At IBar::Bar2: TypeOf(K) = {0}" + IL_0006: ldtoken !!K + IL_000b: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0010: call void [mscorlib]System.Console::WriteLine(string, + object) + IL_0015: nop + IL_0016: ldtoken !!K + IL_001b: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0020: stloc.0 + IL_0021: br.s IL_0023 + + IL_0023: ldloc.0 + IL_0024: ret + } // end of method IBar`1::Bar2 + + .method public hidebysig newslot virtual + instance void Bar3([out] class [mscorlib]System.Type& t, + [out] class [mscorlib]System.Type& u) cil managed + { + // Code size 57 (0x39) + .maxstack 8 + IL_0000: nop + IL_0001: ldstr "At IBar::Bar3: TypeOf(P) = {0}, TypeOf(K)" + + " = {1}" + IL_0006: ldtoken !!P + IL_000b: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0010: ldtoken !!K + IL_0015: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_001a: call void [mscorlib]System.Console::WriteLine(string, + object, + object) + IL_001f: nop + IL_0020: ldarg.1 + IL_0021: ldtoken !!P + IL_0026: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_002b: stind.ref + IL_002c: ldarg.2 + IL_002d: ldtoken !!K + IL_0032: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0037: stind.ref + IL_0038: ret + } // end of method IBar`1::Bar3 + +} // end of class IBar`1 + +.class private auto ansi beforefieldinit FooBar`1 + extends [mscorlib]System.Object + implements IFoo, + class IBar`1 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method FooBar`1::.ctor + +} // end of class FooBar`1 + +.class private auto ansi beforefieldinit Program + extends [mscorlib]System.Object +{ + .method private hidebysig static int32 + Main(string[] args) cil managed + { + .entrypoint + // Code size 223 (0xdf) + .maxstack 3 + .locals init (class FooBar`1 V_0, + class IFoo V_1, + class IBar`1 V_2, + class [mscorlib]System.Type V_3, + class [mscorlib]System.Type V_4, + int32 V_5) + IL_0000: nop + IL_0001: newobj instance void class FooBar`1::.ctor() + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: stloc.1 + IL_0009: ldloc.0 + IL_000a: stloc.2 + IL_000b: ldstr "Calling IFoo.Foo on FooBar - expec" + + "ting IFoo::Foo() returning typeof(string)" + IL_0010: call void [mscorlib]System.Console::WriteLine(string) + IL_0015: nop + IL_0016: ldloc.1 + IL_0017: callvirt instance class [mscorlib]System.Type IFoo::Foo() + IL_001c: ldtoken [mscorlib]System.String + IL_0021: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0026: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, + class [mscorlib]System.Type) + IL_002b: ldstr "Calling IFoo.Foo on FooBar" + IL_0030: call void Test::Assert(bool, + string) + IL_0035: nop + IL_0036: ldstr "Calling IBar.Bar1 on FooBar - expe" + + "cting bar.Bar1() returning typeof(string)" + IL_003b: call void [mscorlib]System.Console::WriteLine(string) + IL_0040: nop + IL_0041: ldloc.2 + IL_0042: callvirt instance class [mscorlib]System.Type class IBar`1::Bar1() + IL_0047: ldtoken [mscorlib]System.String + IL_004c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0051: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, + class [mscorlib]System.Type) + IL_0056: ldstr "Calling IBar.Bar1 on FooBar" + IL_005b: call void Test::Assert(bool, + string) + IL_0060: nop + IL_0061: ldstr "Calling IBar.Bar2 on FooBar - ex" + + "pecting bar.Bar2() returning typeof(string[])" + IL_0066: call void [mscorlib]System.Console::WriteLine(string) + IL_006b: nop + IL_006c: ldloc.2 + IL_006d: callvirt instance class [mscorlib]System.Type class IBar`1::Bar2() + IL_0072: ldtoken string[] + IL_0077: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_007c: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, + class [mscorlib]System.Type) + IL_0081: ldstr "Calling IBar.Bar2 on FooBar" + IL_0086: call void Test::Assert(bool, + string) + IL_008b: nop + IL_008c: ldstr "Calling IBar.Bar3 - expecting ba" + + "r.Bar3() returning typeof(string), typeof(string[])" + IL_0091: call void [mscorlib]System.Console::WriteLine(string) + IL_0096: nop + IL_0097: ldloc.2 + IL_0098: ldloca.s V_3 + IL_009a: ldloca.s V_4 + IL_009c: callvirt instance void class IBar`1::Bar3(class [mscorlib]System.Type&, + class [mscorlib]System.Type&) + IL_00a1: nop + IL_00a2: ldloc.3 + IL_00a3: ldtoken [mscorlib]System.String + IL_00a8: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_00ad: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, + class [mscorlib]System.Type) + IL_00b2: brfalse.s IL_00c7 + + IL_00b4: ldloc.s V_4 + IL_00b6: ldtoken string[] + IL_00bb: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_00c0: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, + class [mscorlib]System.Type) + IL_00c5: br.s IL_00c8 + + IL_00c7: ldc.i4.0 + IL_00c8: ldstr "Calling IBar.Bar3" + IL_00cd: call void Test::Assert(bool, + string) + IL_00d2: nop + IL_00d3: call int32 Test::Ret() + IL_00d8: stloc.s V_5 + IL_00da: br.s IL_00dc + + IL_00dc: ldloc.s V_5 + IL_00de: ret + } // end of method Program::Main + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Program::.ctor + +} // end of class Program + +.class private auto ansi beforefieldinit Test + extends [mscorlib]System.Object +{ + .field private static bool Pass + .method public hidebysig static int32 Ret() cil managed + { + // Code size 19 (0x13) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldsfld bool Test::Pass + IL_0006: brtrue.s IL_000c + + IL_0008: ldc.i4.s 101 + IL_000a: br.s IL_000e + + IL_000c: ldc.i4.s 100 + IL_000e: stloc.0 + IL_000f: br.s IL_0011 + + IL_0011: ldloc.0 + IL_0012: ret + } // end of method Test::Ret + + .method public hidebysig static void Assert(bool cond, + string msg) cil managed + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: brfalse.s IL_0015 + + IL_0006: nop + IL_0007: ldstr "PASS" + IL_000c: call void [mscorlib]System.Console::WriteLine(string) + IL_0011: nop + IL_0012: nop + IL_0013: br.s IL_002e + + IL_0015: nop + IL_0016: ldstr "FAIL: " + IL_001b: ldarg.1 + IL_001c: call string [mscorlib]System.String::Concat(string, + string) + IL_0021: call void [mscorlib]System.Console::WriteLine(string) + IL_0026: nop + IL_0027: ldc.i4.0 + IL_0028: stsfld bool Test::Pass + IL_002d: nop + IL_002e: ret + } // end of method Test::Assert + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Test::.ctor + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldc.i4.1 + IL_0001: stsfld bool Test::Pass + IL_0006: ret + } // end of method Test::.cctor + +} // end of class Test + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** +// WARNING: Created Win32 resource file genericmethods.res diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/genericmethods/genericmethods.ilproj b/tests/src/Loader/classloader/DefaultInterfaceMethods/genericmethods/genericmethods.ilproj new file mode 100644 index 0000000..909dbe9 --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/genericmethods/genericmethods.ilproj @@ -0,0 +1,37 @@ + + + + + genericmethods + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 7a9bfb7d + true + true + Exe + BuildAndRun + 0 + + True + + + + + False + + + + + + + + + + + + + diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.cs b/tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.cs new file mode 100755 index 0000000..8f4b5d1 --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.cs @@ -0,0 +1,83 @@ +using System; + +interface IFoo +{ + int Foo(int a); +} + +interface IBar +{ + int Bar(int b); +} + +interface IFooBar : IFoo, IBar +{ + int Foo(int a); +} + +class Temp : IFoo +{ + int IFoo.Foo(int a) + { + Console.WriteLine("At IFooBar::IFoo.Foo explicit methodimpl"); + return a + 30; + } +} + +class FooBar : IFooBar +{ + public int Foo(int a) + { + Console.WriteLine("At IFoo::Foo"); + return a+10; + } + + public int Bar(int b) + { + Console.WriteLine("At IBar::Bar"); + return b+20; + } +} + +class Program +{ + public static int Main() + { + FooBar fooBar = new FooBar(); + IFoo foo = (IFoo) fooBar; + IBar bar = (IBar) fooBar; + + Console.WriteLine("Calling IFoo.Foo on FooBar - expecting IFooBar::IFoo.Bar"); + Test.Assert(foo.Foo(10) == 40, "Calling IFoo.Foo on FooBar"); + + Console.WriteLine("Calling IBar.Bar on FooBar - expecting IBar::Bar"); + Test.Assert(bar.Bar(10) == 30, "Calling IBar.Bar on FooBar"); + + return Test.Ret(); + } +} + +class Test +{ + private static bool Pass = true; + + public static int Ret() + { + return Pass? 100 : 101; + } + + public static void Assert(bool cond, string msg) + { + if (cond) + { + Console.WriteLine("PASS"); + } + else + { + Console.WriteLine("FAIL: " + msg); + Pass = false; + } + } +} + + diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.il b/tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.il new file mode 100644 index 0000000..6ab45f0 --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.il @@ -0,0 +1,279 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly methodimpl +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) + + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module methodimpl.exe +// MVID: {F249A85F-3FD2-488F-B084-97B542AD68D6} +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x02FD0000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class interface private abstract auto ansi IFoo +{ + .method public hidebysig newslot virtual + instance int32 Foo(int32 a) cil managed + { + // Code size 21 (0x15) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At IFoo::Foo" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.s 10 + IL_000f: add + IL_0010: stloc.0 + IL_0011: br.s IL_0013 + + IL_0013: ldloc.0 + IL_0014: ret + } // end of method IFoo::Foo + +} // end of class IFoo + +.class interface private abstract auto ansi IBar +{ + .method public hidebysig newslot virtual + instance int32 Bar(int32 b) cil managed + { + // Code size 21 (0x15) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At IBar::Bar" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.s 20 + IL_000f: add + IL_0010: stloc.0 + IL_0011: br.s IL_0013 + + IL_0013: ldloc.0 + IL_0014: ret + } // end of method IBar::Bar + +} // end of class IBar + +.class interface private abstract auto ansi IFooBar + implements IFoo, + IBar +{ + .method private hidebysig newslot virtual final + instance int32 IFoo.Foo(int32 a) cil managed + { + .override IFoo::Foo + // Code size 21 (0x15) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At IFooBar::IFoo.Foo explicit methodimpl" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.s 30 + IL_000f: add + IL_0010: stloc.0 + IL_0011: br.s IL_0013 + + IL_0013: ldloc.0 + IL_0014: ret + } // end of method Temp::IFoo.Foo + +} // end of class IFooBar + +.class private auto ansi beforefieldinit FooBar + extends [mscorlib]System.Object + implements IFooBar, + IFoo, + IBar +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method FooBar::.ctor + +} // end of class FooBar + +.class private auto ansi beforefieldinit Program + extends [mscorlib]System.Object +{ + .method public hidebysig static int32 Main() cil managed + { + .entrypoint + // Code size 89 (0x59) + .maxstack 2 + .locals init (class FooBar V_0, + class IFoo V_1, + class IBar V_2, + int32 V_3) + IL_0000: nop + IL_0001: newobj instance void FooBar::.ctor() + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: stloc.1 + IL_0009: ldloc.0 + IL_000a: stloc.2 + IL_000b: ldstr "Calling IFoo.Foo on FooBar - expecting IFooBar::IF" + + "oo.Bar" + IL_0010: call void [mscorlib]System.Console::WriteLine(string) + IL_0015: nop + IL_0016: ldloc.1 + IL_0017: ldc.i4.s 10 + IL_0019: callvirt instance int32 IFoo::Foo(int32) + IL_001e: ldc.i4.s 40 + IL_0020: ceq + IL_0022: ldstr "Calling IFoo.Foo on FooBar" + IL_0027: call void Test::Assert(bool, + string) + IL_002c: nop + IL_002d: ldstr "Calling IBar.Bar on FooBar - expecting IBar::Bar" + IL_0032: call void [mscorlib]System.Console::WriteLine(string) + IL_0037: nop + IL_0038: ldloc.2 + IL_0039: ldc.i4.s 10 + IL_003b: callvirt instance int32 IBar::Bar(int32) + IL_0040: ldc.i4.s 30 + IL_0042: ceq + IL_0044: ldstr "Calling IBar.Bar on FooBar" + IL_0049: call void Test::Assert(bool, + string) + IL_004e: nop + IL_004f: call int32 Test::Ret() + IL_0054: stloc.3 + IL_0055: br.s IL_0057 + + IL_0057: ldloc.3 + IL_0058: ret + } // end of method Program::Main + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Program::.ctor + +} // end of class Program + +.class private auto ansi beforefieldinit Test + extends [mscorlib]System.Object +{ + .field private static bool Pass + .method public hidebysig static int32 Ret() cil managed + { + // Code size 19 (0x13) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldsfld bool Test::Pass + IL_0006: brtrue.s IL_000c + + IL_0008: ldc.i4.s 101 + IL_000a: br.s IL_000e + + IL_000c: ldc.i4.s 100 + IL_000e: stloc.0 + IL_000f: br.s IL_0011 + + IL_0011: ldloc.0 + IL_0012: ret + } // end of method Test::Ret + + .method public hidebysig static void Assert(bool cond, + string msg) cil managed + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: brfalse.s IL_0015 + + IL_0006: nop + IL_0007: ldstr "PASS" + IL_000c: call void [mscorlib]System.Console::WriteLine(string) + IL_0011: nop + IL_0012: nop + IL_0013: br.s IL_002e + + IL_0015: nop + IL_0016: ldstr "FAIL: " + IL_001b: ldarg.1 + IL_001c: call string [mscorlib]System.String::Concat(string, + string) + IL_0021: call void [mscorlib]System.Console::WriteLine(string) + IL_0026: nop + IL_0027: ldc.i4.0 + IL_0028: stsfld bool Test::Pass + IL_002d: nop + IL_002e: ret + } // end of method Test::Assert + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Test::.ctor + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldc.i4.1 + IL_0001: stsfld bool Test::Pass + IL_0006: ret + } // end of method Test::.cctor + +} // end of class Test + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** +// WARNING: Created Win32 resource file methodimpl.res diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.ilproj b/tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.ilproj new file mode 100644 index 0000000..16a1cad --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.ilproj @@ -0,0 +1,37 @@ + + + + + methodimpl + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 7a9bfb7d + true + true + Exe + BuildAndRun + 0 + + True + + + + + False + + + + + + + + + + + + + diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/sharedgenerics/sharedgenerics.cs b/tests/src/Loader/classloader/DefaultInterfaceMethods/sharedgenerics/sharedgenerics.cs new file mode 100644 index 0000000..93b214d --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/sharedgenerics/sharedgenerics.cs @@ -0,0 +1,68 @@ +using System; + +interface IFoo +{ + Type Foo(T a); +} + +interface IBar +{ + Type Bar(T b); +} + +class FooBar : IFoo, IBar +{ + public Type Foo(T a) + { + Console.WriteLine("At IFoo.Foo:Arg={0}, TypeOf(T)={1}", a.ToString(), typeof(T)); + return typeof(T); + } + + public Type Bar(U b) + { + Console.WriteLine("At IBar.Bar:Arg={0}, TypeOf(T)={1}", b.ToString(), typeof(U)); + return typeof(U); + } +} + +class Program +{ + public static int Main() + { + FooBar fooBar = new FooBar(); + IFoo foo = (IFoo) fooBar; + IBar bar = (IBar) fooBar; + + Console.WriteLine("Calling IFoo.Foo on FooBar - expecting default method IFoo.Foo"); + Test.Assert(foo.Foo("ABC") == typeof(string), "Calling IFoo.Foo on FooBar"); + + Console.WriteLine("Calling IBar.Foo on FooBar - expecting default method IBar.Foo"); + Test.Assert(bar.Bar(new string[] { "ABC" }) == typeof(object), "Calling IBar.Bar on FooBar"); + + return Test.Ret(); + } +} + +class Test +{ + private static bool Pass = true; + + public static int Ret() + { + return Pass? 100 : 101; + } + + public static void Assert(bool cond, string msg) + { + if (cond) + { + Console.WriteLine("PASS"); + } + else + { + Console.WriteLine("FAIL: " + msg); + Pass = false; + } + } +} + diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/sharedgenerics/sharedgenerics.il b/tests/src/Loader/classloader/DefaultInterfaceMethods/sharedgenerics/sharedgenerics.il new file mode 100644 index 0000000..8e9eaec --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/sharedgenerics/sharedgenerics.il @@ -0,0 +1,275 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly sharedgenerics +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) + + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module sharedgenerics.exe +// MVID: {0DEEC74C-30FE-495C-9653-12BE5220327A} +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x02D40000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class interface private abstract auto ansi IFoo`1 +{ + .method public hidebysig newslot virtual + instance class [mscorlib]System.Type + Foo(!T a) cil managed + { + // Code size 50 (0x32) + .maxstack 3 + .locals init (class [mscorlib]System.Type V_0) + IL_0000: nop + IL_0001: ldstr "At IFoo.Foo:Arg={0}, TypeOf(T)={1}" + IL_0006: ldarga.s a + IL_0008: constrained. !T + IL_000e: callvirt instance string [mscorlib]System.Object::ToString() + IL_0013: ldtoken !T + IL_0018: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_001d: call void [mscorlib]System.Console::WriteLine(string, + object, + object) + IL_0022: nop + IL_0023: ldtoken !T + IL_0028: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_002d: stloc.0 + IL_002e: br.s IL_0030 + + IL_0030: ldloc.0 + IL_0031: ret + } // end of method IFoo`1::Foo + +} // end of class IFoo`1 + +.class interface private abstract auto ansi IBar`1<- T> +{ + .method public hidebysig newslot virtual + instance class [mscorlib]System.Type + Bar(!T b) cil managed + { + // Code size 50 (0x32) + .maxstack 3 + .locals init (class [mscorlib]System.Type V_0) + IL_0000: nop + IL_0001: ldstr "At IBar.Bar:Arg={0}, TypeOf(T)={1}" + IL_0006: ldarga.s b + IL_0008: constrained. !T + IL_000e: callvirt instance string [mscorlib]System.Object::ToString() + IL_0013: ldtoken !T + IL_0018: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_001d: call void [mscorlib]System.Console::WriteLine(string, + object, + object) + IL_0022: nop + IL_0023: ldtoken !T + IL_0028: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_002d: stloc.0 + IL_002e: br.s IL_0030 + + IL_0030: ldloc.0 + IL_0031: ret + } // end of method IBar`1::Bar + +} // end of class IBar`1 + +.class private auto ansi beforefieldinit FooBar`2 + extends [mscorlib]System.Object + implements class IFoo`1, + class IBar`1 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method FooBar`2::.ctor + +} // end of class FooBar`2 + +.class private auto ansi beforefieldinit Program + extends [mscorlib]System.Object +{ + .method public hidebysig static int32 Main() cil managed + { + .entrypoint + // Code size 126 (0x7e) + .maxstack 5 + .locals init (class FooBar`2 V_0, + class IFoo`1 V_1, + class IBar`1 V_2, + int32 V_3) + IL_0000: nop + IL_0001: newobj instance void class FooBar`2::.ctor() + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: stloc.1 + IL_0009: ldloc.0 + IL_000a: stloc.2 + IL_000b: ldstr "Calling IFoo.Foo on FooBar" + + " - expecting default method IFoo.Foo" + IL_0010: call void [mscorlib]System.Console::WriteLine(string) + IL_0015: nop + IL_0016: ldloc.1 + IL_0017: ldstr "ABC" + IL_001c: callvirt instance class [mscorlib]System.Type class IFoo`1::Foo(!0) + IL_0021: ldtoken [mscorlib]System.String + IL_0026: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_002b: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, + class [mscorlib]System.Type) + IL_0030: ldstr "Calling IFoo.Foo on FooBar" + IL_0035: call void Test::Assert(bool, + string) + IL_003a: nop + IL_003b: ldstr "Calling IBar.Foo on FooBar - expecting default method IBar.Foo" + IL_0040: call void [mscorlib]System.Console::WriteLine(string) + IL_0045: nop + IL_0046: ldloc.2 + IL_0047: ldc.i4.1 + IL_0048: newarr [mscorlib]System.String + IL_004d: dup + IL_004e: ldc.i4.0 + IL_004f: ldstr "ABC" + IL_0054: stelem.ref + IL_0055: callvirt instance class [mscorlib]System.Type class IBar`1::Bar(!0) + IL_005a: ldtoken [mscorlib]System.Object + IL_005f: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0064: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, + class [mscorlib]System.Type) + IL_0069: ldstr "Calling IBar.Bar on FooBar" + IL_006e: call void Test::Assert(bool, + string) + IL_0073: nop + IL_0074: call int32 Test::Ret() + IL_0079: stloc.3 + IL_007a: br.s IL_007c + + IL_007c: ldloc.3 + IL_007d: ret + } // end of method Program::Main + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Program::.ctor + +} // end of class Program + +.class private auto ansi beforefieldinit Test + extends [mscorlib]System.Object +{ + .field private static bool Pass + .method public hidebysig static int32 Ret() cil managed + { + // Code size 19 (0x13) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldsfld bool Test::Pass + IL_0006: brtrue.s IL_000c + + IL_0008: ldc.i4.s 101 + IL_000a: br.s IL_000e + + IL_000c: ldc.i4.s 100 + IL_000e: stloc.0 + IL_000f: br.s IL_0011 + + IL_0011: ldloc.0 + IL_0012: ret + } // end of method Test::Ret + + .method public hidebysig static void Assert(bool cond, + string msg) cil managed + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: brfalse.s IL_0015 + + IL_0006: nop + IL_0007: ldstr "PASS" + IL_000c: call void [mscorlib]System.Console::WriteLine(string) + IL_0011: nop + IL_0012: nop + IL_0013: br.s IL_002e + + IL_0015: nop + IL_0016: ldstr "FAIL: " + IL_001b: ldarg.1 + IL_001c: call string [mscorlib]System.String::Concat(string, + string) + IL_0021: call void [mscorlib]System.Console::WriteLine(string) + IL_0026: nop + IL_0027: ldc.i4.0 + IL_0028: stsfld bool Test::Pass + IL_002d: nop + IL_002e: ret + } // end of method Test::Assert + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Test::.ctor + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldc.i4.1 + IL_0001: stsfld bool Test::Pass + IL_0006: ret + } // end of method Test::.cctor + +} // end of class Test + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** +// WARNING: Created Win32 resource file sharedgenerics.res diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/sharedgenerics/sharedgenerics.ilproj b/tests/src/Loader/classloader/DefaultInterfaceMethods/sharedgenerics/sharedgenerics.ilproj new file mode 100644 index 0000000..be40d20 --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/sharedgenerics/sharedgenerics.ilproj @@ -0,0 +1,37 @@ + + + + + sharedgenerics + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 7a9bfb7d + true + true + Exe + BuildAndRun + 0 + + True + + + + + False + + + + + + + + + + + + + diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/simple/simple.cs b/tests/src/Loader/classloader/DefaultInterfaceMethods/simple/simple.cs new file mode 100644 index 0000000..5936363 --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/simple/simple.cs @@ -0,0 +1,124 @@ +using System; + +interface IBlah +{ + int Blah(int c); +} + +// All methods go into IBlah +class IBlah_Impl +{ + public int Blah(int c) + { + Console.WriteLine("At IBlah.Blah"); + return c + Blah_Private_GetA() + Blah_Internal_GetB() + Blah_Protected_GetC(); + } + + private int Blah_Private_GetA() + { + Console.WriteLine("At IBlah.Blah_Private_GetA"); + return 1; + } + + internal int Blah_Internal_GetB() + { + Console.WriteLine("At IBlah.Blah_Internal_GetB"); + return 2; + } + + protected int Blah_Protected_GetC() + { + Console.WriteLine("At IBlah.Blah_Protected_GetC"); + return 3; + } +} + +interface IFoo +{ + int Foo(int a); +} + +interface IBar +{ + int Bar(int b); +} + +class Base : IBlah +{ + public int Blah(int c) + { + // Dummy + return 0; + } +} + +class FooBar : Base, IFoo, IBar +{ + public int Foo(int a) + { + Console.WriteLine("At IFoo.Foo"); + return a+1; + } + + public int Bar(int b) + { + Console.WriteLine("At IBar.Bar"); + return b+10; + } + + public int CallBlahProtected() + { + // change to IBlah.Blah_Protected_GetC(); + return CallBlahProtected(); + } +} + +class Program +{ + public static int Main() + { + FooBar fooBar = new FooBar(); + IFoo foo = (IFoo) fooBar; + IBar bar = (IBar) fooBar; + IBlah blah = (IBlah) fooBar; + + Console.WriteLine("Calling IFoo.Foo on FooBar - expecting default method on IFoo.Foo. "); + Test.Assert(foo.Foo(10) == 11, "Calling IFoo.Foo on FooBar"); + + Console.WriteLine("Calling IBar.Bar on FooBar - expecting default method on IBar.Bar. "); + Test.Assert(bar.Bar(10) == 20, "Calling IBar.Bar on FooBar"); + + Console.WriteLine("Calling IBlah.Blah on FooBar - expecting default method on IBlah.Blah from Base. "); + Test.Assert(blah.Blah(10) == 16, "Calling IBlah.Blah on FooBar"); + + // Doesn't work yet + // Console.WriteLine("Calling FooBar.CallBlahProtected - expecting protected methods on interface can be called"); + // Test.Assert(fooBar.CallBlahProtected() == 3, "Calling FooBar.CallBlahProtected"); + + return Test.Ret(); + } +} + +class Test +{ + private static bool Pass = true; + + public static int Ret() + { + return Pass? 100 : 101; + } + + public static void Assert(bool cond, string msg) + { + if (cond) + { + Console.WriteLine("PASS"); + } + else + { + Console.WriteLine("FAIL: " + msg); + Pass = false; + } + } +} + diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/simple/simple.il b/tests/src/Loader/classloader/DefaultInterfaceMethods/simple/simple.il new file mode 100644 index 0000000..c78aaf4 --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/simple/simple.il @@ -0,0 +1,401 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly simple +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) + + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module simple.exe +// MVID: {0B8FCFD0-673A-4DEB-90CC-B96749DE09C8} +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x01A80000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class interface private abstract auto ansi IBlah +{ + .method public hidebysig newslot virtual + instance int32 Blah(int32 c) cil managed + { + // Code size 39 (0x27) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At IBlah.Blah" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldarg.0 + IL_000e: call instance int32 IBlah::Blah_Private_GetA() + IL_0013: add + IL_0014: ldarg.0 + IL_0015: call instance int32 IBlah::Blah_Internal_GetB() + IL_001a: add + IL_001b: ldarg.0 + IL_001c: call instance int32 IBlah::Blah_Protected_GetC() + IL_0021: add + IL_0022: stloc.0 + IL_0023: br.s IL_0025 + + IL_0025: ldloc.0 + IL_0026: ret + } // end of method IBlah::Blah + + .method private hidebysig instance int32 + Blah_Private_GetA() cil managed + { + // Code size 18 (0x12) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At IBlah.Blah_Private_GetA" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldc.i4.1 + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + + IL_0010: ldloc.0 + IL_0011: ret + } // end of method IBlah_Impl::Blah_Private_GetA + + .method assembly hidebysig instance int32 + Blah_Internal_GetB() cil managed + { + // Code size 18 (0x12) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At IBlah.Blah_Internal_GetB" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldc.i4.2 + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + + IL_0010: ldloc.0 + IL_0011: ret + } // end of method IBlah_Impl::Blah_Internal_GetB + + .method family hidebysig instance int32 + Blah_Protected_GetC() cil managed + { + // Code size 18 (0x12) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At IBlah.Blah_Protected_GetC" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldc.i4.3 + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + + IL_0010: ldloc.0 + IL_0011: ret + } // end of method IBlah_Impl::Blah_Protected_GetC +} // end of class IBlah + +.class interface private abstract auto ansi IFoo +{ + .method public hidebysig newslot virtual + instance int32 Foo(int32 a) cil managed + { + // Code size 20 (0x14) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At IFoo.Foo" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.1 + IL_000e: add + IL_000f: stloc.0 + IL_0010: br.s IL_0012 + + IL_0012: ldloc.0 + IL_0013: ret + } // end of method IFoo::Foo + +} // end of class IFoo + +.class interface private abstract auto ansi IBar +{ + .method public hidebysig newslot virtual + instance int32 Bar(int32 b) cil managed + { + // Code size 21 (0x15) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "At IBar.Bar" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.1 + IL_000d: ldc.i4.s 10 + IL_000f: add + IL_0010: stloc.0 + IL_0011: br.s IL_0013 + + IL_0013: ldloc.0 + IL_0014: ret + } // end of method IBar::Bar + +} // end of class IBar + +.class private auto ansi beforefieldinit Base + extends [mscorlib]System.Object + implements IBlah +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Base::.ctor + +} // end of class Base + +.class private auto ansi beforefieldinit FooBar + extends Base + implements IFoo, + IBar +{ + .method public hidebysig instance int32 + CallBlahProtected() cil managed + { + // Code size 12 (0xc) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call instance int32 IBlah::Blah_Protected_GetC() + IL_0007: stloc.0 + IL_0008: br.s IL_000a + + IL_000a: ldloc.0 + IL_000b: ret + } // end of method FooBar::CallBlahProtected + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void Base::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method FooBar::.ctor + +} // end of class FooBar + +.class private auto ansi beforefieldinit Program + extends [mscorlib]System.Object +{ + .method public hidebysig static int32 Main() cil managed + { + .entrypoint + // Code size 158 (0x9e) + .maxstack 2 + .locals init (class FooBar V_0, + class IFoo V_1, + class IBar V_2, + class IBlah V_3, + int32 V_4) + IL_0000: nop + IL_0001: newobj instance void FooBar::.ctor() + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: stloc.1 + IL_0009: ldloc.0 + IL_000a: stloc.2 + IL_000b: ldloc.0 + IL_000c: stloc.3 + IL_000d: ldstr "Calling IFoo.Foo on FooBar - expecting default met" + + "hod on IFoo.Foo. " + IL_0012: call void [mscorlib]System.Console::WriteLine(string) + IL_0017: nop + IL_0018: ldloc.1 + IL_0019: ldc.i4.s 10 + IL_001b: callvirt instance int32 IFoo::Foo(int32) + IL_0020: ldc.i4.s 11 + IL_0022: ceq + IL_0024: ldstr "Calling IFoo.Foo on FooBar" + IL_0029: call void Test::Assert(bool, + string) + IL_002e: nop + IL_002f: ldstr "Calling IBar.Bar on FooBar - expecting default met" + + "hod on IBar.Bar. " + IL_0034: call void [mscorlib]System.Console::WriteLine(string) + IL_0039: nop + IL_003a: ldloc.2 + IL_003b: ldc.i4.s 10 + IL_003d: callvirt instance int32 IBar::Bar(int32) + IL_0042: ldc.i4.s 20 + IL_0044: ceq + IL_0046: ldstr "Calling IBar.Bar on FooBar" + IL_004b: call void Test::Assert(bool, + string) + IL_0050: nop + IL_0051: ldstr "Calling IBlah.Blah on FooBar - expecting default m" + + "ethod on IBlah.Blah from Base. " + IL_0056: call void [mscorlib]System.Console::WriteLine(string) + IL_005b: nop + IL_005c: ldloc.3 + IL_005d: ldc.i4.s 10 + IL_005f: callvirt instance int32 IBlah::Blah(int32) + IL_0064: ldc.i4.s 16 + IL_0066: ceq + IL_0068: ldstr "Calling IBlah.Blah on FooBar" + IL_006d: call void Test::Assert(bool, + string) + IL_0072: nop + IL_0073: ldstr "Calling FooBar.CallBlahProtected - expecting prote" + + "cted methods on interface can be called" + IL_0078: call void [mscorlib]System.Console::WriteLine(string) + IL_007d: nop + +/* THIS DOESN"T WORK YET + + IL_007e: ldloc.0 + IL_007f: callvirt instance int32 FooBar::CallBlahProtected() + IL_0084: ldc.i4.3 + IL_0085: ceq + IL_0087: ldstr "Calling FooBar.CallBlahProtected" + IL_008c: call void Test::Assert(bool, + string) + IL_0091: nop +*/ + IL_0092: call int32 Test::Ret() + IL_0097: stloc.s V_4 + IL_0099: br.s IL_009b + + IL_009b: ldloc.s V_4 + IL_009d: ret + } // end of method Program::Main + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Program::.ctor + +} // end of class Program + +.class private auto ansi beforefieldinit Test + extends [mscorlib]System.Object +{ + .field private static bool Pass + .method public hidebysig static int32 Ret() cil managed + { + // Code size 19 (0x13) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldsfld bool Test::Pass + IL_0006: brtrue.s IL_000c + + IL_0008: ldc.i4.s 101 + IL_000a: br.s IL_000e + + IL_000c: ldc.i4.s 100 + IL_000e: stloc.0 + IL_000f: br.s IL_0011 + + IL_0011: ldloc.0 + IL_0012: ret + } // end of method Test::Ret + + .method public hidebysig static void Assert(bool cond, + string msg) cil managed + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: brfalse.s IL_0015 + + IL_0006: nop + IL_0007: ldstr "PASS" + IL_000c: call void [mscorlib]System.Console::WriteLine(string) + IL_0011: nop + IL_0012: nop + IL_0013: br.s IL_002e + + IL_0015: nop + IL_0016: ldstr "FAIL: " + IL_001b: ldarg.1 + IL_001c: call string [mscorlib]System.String::Concat(string, + string) + IL_0021: call void [mscorlib]System.Console::WriteLine(string) + IL_0026: nop + IL_0027: ldc.i4.0 + IL_0028: stsfld bool Test::Pass + IL_002d: nop + IL_002e: ret + } // end of method Test::Assert + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Test::.ctor + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldc.i4.1 + IL_0001: stsfld bool Test::Pass + IL_0006: ret + } // end of method Test::.cctor + +} // end of class Test + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** +// WARNING: Created Win32 resource file simple.res diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/simple/simple.ilproj b/tests/src/Loader/classloader/DefaultInterfaceMethods/simple/simple.ilproj new file mode 100644 index 0000000..0bf910b --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/simple/simple.ilproj @@ -0,0 +1,37 @@ + + + + + simple + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 7a9bfb7d + true + true + Exe + BuildAndRun + 0 + + True + + + + + False + + + + + + + + + + + + + diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/valuetypes/valuetypes.cs b/tests/src/Loader/classloader/DefaultInterfaceMethods/valuetypes/valuetypes.cs new file mode 100644 index 0000000..0cf1be9 --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/valuetypes/valuetypes.cs @@ -0,0 +1,94 @@ +using System; + +interface IValue +{ + int GetValue(); + void SetValue(int a); + int Add(int a); +} + +// This class is only needed to spit out IL that assumes 'this' is an object (and therefore don't box) +struct FooBarStruct_ : IValue +{ + public int GetValue() + { + return 0; + } + + public void SetValue(int val) + { + } + + public int Add(int a) + { + // Force cast and boxing + IValue valueIntf = this as IValue; + int val = valueIntf.GetValue(); + val += a; + valueIntf.SetValue(val); + return val; + } +} + +struct FooBarStruct : IValue +{ + public int _val; + + public int GetValue() + { + return _val; + } + + public void SetValue(int val) + { + _val = val; + } + + public int Add(int a) + { + // Dummy + return 0; + } +} + +class Program +{ + public static int Main() + { + FooBarStruct fooBar = new FooBarStruct(); + + fooBar._val = 10; + + IValue foo = (IValue) fooBar; + + Console.WriteLine("Calling IFoo.Foo on FooBarStruct"); + Test.Assert(foo.Add(10) == 20, "Calling default method IValue.Add on FooBarStruct failed"); + Test.Assert(fooBar.GetValue() == 10, "FooBarStruct value should remain unchanged"); + + return Test.Ret(); + } +} + +class Test +{ + private static bool Pass = true; + + public static int Ret() + { + return Pass? 100 : 101; + } + + public static void Assert(bool cond, string msg) + { + if (cond) + { + Console.WriteLine("PASS"); + } + else + { + Console.WriteLine("FAIL: " + msg); + Pass = false; + } + } +} + diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/valuetypes/valuetypes.il b/tests/src/Loader/classloader/DefaultInterfaceMethods/valuetypes/valuetypes.il new file mode 100644 index 0000000..56c23f1 --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/valuetypes/valuetypes.il @@ -0,0 +1,252 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly valuetypes +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) + + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module valuetypes.exe +// MVID: {E191F723-B724-4D70-B3A8-CEA89FD033D3} +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x01170000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class interface private abstract auto ansi IValue +{ + .method public hidebysig newslot abstract virtual + instance int32 GetValue() cil managed + { + } // end of method IValue::GetValue + + .method public hidebysig newslot abstract virtual + instance void SetValue(int32 a) cil managed + { + } // end of method IValue::SetValue + + .method public hidebysig newslot virtual + instance int32 Add(int32 a) cil managed + { + // Code size 26 (0x1a) + .maxstack 2 + .locals init (int32 V_0, + int32 V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call instance int32 IValue::GetValue() + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldarg.1 + IL_000a: add + IL_000b: stloc.0 + IL_000c: ldarg.0 + IL_000d: ldloc.0 + IL_000e: call instance void IValue::SetValue(int32) + IL_0013: nop + IL_0014: ldloc.0 + IL_0015: stloc.1 + IL_0016: br.s IL_0018 + + IL_0018: ldloc.1 + IL_0019: ret + } // end of method IValue::Add + +} // end of class IValue + +.class private sequential ansi sealed beforefieldinit FooBarStruct + extends [mscorlib]System.ValueType + implements IValue +{ + .field public int32 _val + .method public hidebysig newslot virtual final + instance int32 GetValue() cil managed + { + // Code size 12 (0xc) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldfld int32 FooBarStruct::_val + IL_0007: stloc.0 + IL_0008: br.s IL_000a + + IL_000a: ldloc.0 + IL_000b: ret + } // end of method FooBarStruct::GetValue + + .method public hidebysig newslot virtual final + instance void SetValue(int32 val) cil managed + { + // Code size 9 (0x9) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.1 + IL_0003: stfld int32 FooBarStruct::_val + IL_0008: ret + } // end of method FooBarStruct::SetValue +} // end of class FooBarStruct + +.class private auto ansi beforefieldinit Program + extends [mscorlib]System.Object +{ + .method public hidebysig static int32 Main() cil managed + { + .entrypoint + .maxstack 2 + .locals init (valuetype FooBarStruct V_0, + class IValue V_1, + int32 V_2) + IL_0000: nop + IL_0001: ldloca.s V_0 + IL_0003: initobj FooBarStruct + IL_0009: ldloca.s V_0 + IL_000b: ldc.i4.s 10 + IL_000d: stfld int32 FooBarStruct::_val + IL_0012: ldloc.0 + IL_0013: box FooBarStruct + IL_0018: stloc.1 + IL_0019: ldstr "Calling IFoo.Foo on FooBarStruct" + IL_001e: call void [mscorlib]System.Console::WriteLine(string) + IL_0023: nop + IL_0024: ldloc.1 + IL_0025: ldc.i4.s 10 + IL_0027: callvirt instance int32 IValue::Add(int32) + IL_002c: ldc.i4.s 20 + IL_002e: ceq + IL_0030: ldstr "Calling default method IValue.Add on FooBarStruct " + + "failed" + IL_0035: call void Test::Assert(bool, + string) + IL_003a: nop + IL_003b: ldloca V_0 + callvirt instance int32 FooBarStruct::GetValue() + ldc.i4.s 10 + ceq + ldstr "FooBarStruct value should remain unchanged" + call void Test::Assert(bool, + string) + nop + call int32 Test::Ret() + ret + } // end of method Program::Main + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Program::.ctor + +} // end of class Program + +.class private auto ansi beforefieldinit Test + extends [mscorlib]System.Object +{ + .field private static bool Pass + .method public hidebysig static int32 Ret() cil managed + { + // Code size 19 (0x13) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldsfld bool Test::Pass + IL_0006: brtrue.s IL_000c + + IL_0008: ldc.i4.s 101 + IL_000a: br.s IL_000e + + IL_000c: ldc.i4.s 100 + IL_000e: stloc.0 + IL_000f: br.s IL_0011 + + IL_0011: ldloc.0 + IL_0012: ret + } // end of method Test::Ret + + .method public hidebysig static void Assert(bool cond, + string msg) cil managed + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: brfalse.s IL_0015 + + IL_0006: nop + IL_0007: ldstr "PASS" + IL_000c: call void [mscorlib]System.Console::WriteLine(string) + IL_0011: nop + IL_0012: nop + IL_0013: br.s IL_002e + + IL_0015: nop + IL_0016: ldstr "FAIL: " + IL_001b: ldarg.1 + IL_001c: call string [mscorlib]System.String::Concat(string, + string) + IL_0021: call void [mscorlib]System.Console::WriteLine(string) + IL_0026: nop + IL_0027: ldc.i4.0 + IL_0028: stsfld bool Test::Pass + IL_002d: nop + IL_002e: ret + } // end of method Test::Assert + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Test::.ctor + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldc.i4.1 + IL_0001: stsfld bool Test::Pass + IL_0006: ret + } // end of method Test::.cctor + +} // end of class Test + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** +// WARNING: Created Win32 resource file valuetypes.res diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/valuetypes/valuetypes.ilproj b/tests/src/Loader/classloader/DefaultInterfaceMethods/valuetypes/valuetypes.ilproj new file mode 100644 index 0000000..12973e7 --- /dev/null +++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/valuetypes/valuetypes.ilproj @@ -0,0 +1,37 @@ + + + + + valuetypes + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 7a9bfb7d + true + true + Exe + BuildAndRun + 0 + + True + + + + + False + + + + + + + + + + + + + -- 2.7.4