diff options
author | Michal Strehovský <MichalStrehovsky@users.noreply.github.com> | 2019-04-08 13:14:39 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-08 13:14:39 +0200 |
commit | 24c92a5939002ecbadefbc5f93c2c8cc371d8b72 (patch) | |
tree | e846057269d2774850af5baa75cb1b3edf7cf8aa /src | |
parent | 67e8c9ba99ea32339e2df59d9615a88851fa6dc7 (diff) | |
download | coreclr-24c92a5939002ecbadefbc5f93c2c8cc371d8b72.tar.gz coreclr-24c92a5939002ecbadefbc5f93c2c8cc371d8b72.tar.bz2 coreclr-24c92a5939002ecbadefbc5f93c2c8cc371d8b72.zip |
Allow reabstraction of default interface methods (#23313)
Allow the runtime to load types with incomplete interface implementations. With this change, we allow (in pseudo-C#):
```csharp
interface IFoo { void Frob() { } }
interface IBar : IFoo { abstract void IFoo.Frob() }
class Fooer : IBar { }
```
Calling IFoo.Frob on an instance of `Fooer` will result in new exception being thrown because the default implementation of `IFoo.Frob` was re-abstracted by `IBar`.
Diffstat (limited to 'src')
-rw-r--r-- | src/dlls/mscorrc/mscorrc.rc | 1 | ||||
-rw-r--r-- | src/dlls/mscorrc/resource.h | 1 | ||||
-rw-r--r-- | src/vm/methodtable.cpp | 62 | ||||
-rw-r--r-- | src/vm/methodtablebuilder.cpp | 12 | ||||
-rw-r--r-- | src/vm/runtimehandles.cpp | 2 |
5 files changed, 71 insertions, 7 deletions
diff --git a/src/dlls/mscorrc/mscorrc.rc b/src/dlls/mscorrc/mscorrc.rc index 7acfb09ecb..c733f694ee 100644 --- a/src/dlls/mscorrc/mscorrc.rc +++ b/src/dlls/mscorrc/mscorrc.rc @@ -733,6 +733,7 @@ BEGIN IDS_CLASSLOAD_MI_FINAL_IMPL "Method implementation on an interface '%1' from assembly '%2' must be a final method." IDS_CLASSLOAD_AMBIGUOUS_OVERRIDE "Could not call method '%1' on interface '%2' with type '%3' from assembly '%4' because there are multiple incompatible interface methods overriding this method." IDS_CLASSLOAD_UNSUPPORTED_DISPATCH "Could not make constrained call to method '%1' on interface '%2' with type '%3' from assembly '%4'. Dispatch to default interface methods is not supported in this situation." + IDS_CLASSLOAD_METHOD_NOT_IMPLEMENTED "Could not call method '%1' on type '%2' with an instance of '%3' from assembly '%4' because there is no implementation for the method." IDS_CLASSLOAD_MISSINGMETHODRVA "Could not load type '%1' from assembly '%2' because the method '%3' has no implementation (no RVA)." SECURITY_E_INCOMPATIBLE_EVIDENCE "Assembly '%1' already loaded without additional security evidence." diff --git a/src/dlls/mscorrc/resource.h b/src/dlls/mscorrc/resource.h index cc9e0e81f5..f11aa12fec 100644 --- a/src/dlls/mscorrc/resource.h +++ b/src/dlls/mscorrc/resource.h @@ -462,6 +462,7 @@ #define IDS_CLASSLOAD_MI_FINAL_IMPL 0x1ac8 #define IDS_CLASSLOAD_AMBIGUOUS_OVERRIDE 0x1ac9 #define IDS_CLASSLOAD_UNSUPPORTED_DISPATCH 0x1aca +#define IDS_CLASSLOAD_METHOD_NOT_IMPLEMENTED 0x1acb #define BFA_INVALID_TOKEN_TYPE 0x2001 #define BFA_INVALID_TOKEN 0x2003 diff --git a/src/vm/methodtable.cpp b/src/vm/methodtable.cpp index 1603484ec6..34381fa2aa 100644 --- a/src/vm/methodtable.cpp +++ b/src/vm/methodtable.cpp @@ -6944,6 +6944,39 @@ BOOL MethodTable::FindDispatchEntry(UINT32 typeID, RETURN (FALSE); } +#ifndef DACCESS_COMPILE + +void ThrowExceptionForAbstractOverride( + MethodTable *pTargetClass, + MethodTable *pInterfaceMT, + MethodDesc *pInterfaceMD) +{ + LIMITED_METHOD_CONTRACT; + + SString assemblyName; + + pTargetClass->GetAssembly()->GetDisplayName(assemblyName); + + SString strInterfaceName; + TypeString::AppendType(strInterfaceName, TypeHandle(pInterfaceMT)); + + SString strMethodName; + TypeString::AppendMethod(strMethodName, pInterfaceMD, pInterfaceMD->GetMethodInstantiation()); + + SString strTargetClassName; + TypeString::AppendType(strTargetClassName, pTargetClass); + + COMPlusThrow( + kEntryPointNotFoundException, + IDS_CLASSLOAD_METHOD_NOT_IMPLEMENTED, + strMethodName, + strInterfaceName, + strTargetClassName, + assemblyName); +} + +#endif // !DACCESS_COMPILE + //========================================================================================== // Possible cases: // 1. Typed (interface) contract @@ -7063,15 +7096,32 @@ MethodTable::FindDispatchImpl( if (foundDefaultInterfaceImplementation) { - // Now, construct a DispatchSlot to return in *pImplSlot - DispatchSlot ds(pDefaultMethod->GetMethodEntryPoint()); - - if (pImplSlot != NULL) + // + // If the default implementation we found is abstract, we hit a reabstraction. + // + // interface IFoo { void Frob() { ... } } + // interface IBar { abstract void IFoo.Frob() } + // class Foo : IBar { /* IFoo.Frob not implemented here */ } + // + if (pDefaultMethod->IsAbstract()) { - *pImplSlot = ds; + if (throwOnConflict) + { + ThrowExceptionForAbstractOverride(this, pIfcMT, pIfcMD); + } } + else + { + // Now, construct a DispatchSlot to return in *pImplSlot + DispatchSlot ds(pDefaultMethod->GetMethodEntryPoint()); - RETURN(TRUE); + if (pImplSlot != NULL) + { + *pImplSlot = ds; + } + + RETURN(TRUE); + } } } diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp index b9460264c3..286cd74c0a 100644 --- a/src/vm/methodtablebuilder.cpp +++ b/src/vm/methodtablebuilder.cpp @@ -10772,6 +10772,18 @@ BOOL MethodTableBuilder::HasDefaultInterfaceImplementation(bmtRTType *pDeclType, if (!pDeclMD->IsAbstract()) return TRUE; + // If the method is an abstract MethodImpl, this is a reabstraction: + // + // interface IFoo { void Frob() { } } + // interface IBar : IFoo { abstract void IFoo.Frob() } + // + // We don't require these to have an implementation because they're final anyway. + if (pDeclMD->IsMethodImpl()) + { + assert(pDeclMD->IsFinal()); + return TRUE; + } + int targetSlot = pDeclMD->GetSlot(); // Iterate over all the interfaces this type implements diff --git a/src/vm/runtimehandles.cpp b/src/vm/runtimehandles.cpp index 9acb47f195..c25bcbf3fe 100644 --- a/src/vm/runtimehandles.cpp +++ b/src/vm/runtimehandles.cpp @@ -1165,7 +1165,7 @@ MethodDesc* QCALLTYPE RuntimeTypeHandle::GetInterfaceMethodImplementation(Enregi // with at least an abstract method. b19897_GetInterfaceMap_Abstract.exe tests this case. //@TODO:STUBDISPATCH: Don't need to track down the implementation, just the declaration, and this can //@TODO: be done faster - just need to make a function FindDispatchDecl. - DispatchSlot slot(typeHandle.GetMethodTable()->FindDispatchSlotForInterfaceMD(thOwnerOfMD, pMD, TRUE /* throwOnConflict */)); + DispatchSlot slot(typeHandle.GetMethodTable()->FindDispatchSlotForInterfaceMD(thOwnerOfMD, pMD, FALSE /* throwOnConflict */)); if (!slot.IsNull()) pResult = slot.GetMethodDesc(); |