summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>2019-04-08 13:14:39 +0200
committerGitHub <noreply@github.com>2019-04-08 13:14:39 +0200
commit24c92a5939002ecbadefbc5f93c2c8cc371d8b72 (patch)
treee846057269d2774850af5baa75cb1b3edf7cf8aa /src
parent67e8c9ba99ea32339e2df59d9615a88851fa6dc7 (diff)
downloadcoreclr-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.rc1
-rw-r--r--src/dlls/mscorrc/resource.h1
-rw-r--r--src/vm/methodtable.cpp62
-rw-r--r--src/vm/methodtablebuilder.cpp12
-rw-r--r--src/vm/runtimehandles.cpp2
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();