Virt call unsafe accessor tests (#89220)
authorAaron Robinson <arobins@microsoft.com>
Fri, 21 Jul 2023 02:13:24 +0000 (19:13 -0700)
committerGitHub <noreply@github.com>
Fri, 21 Jul 2023 02:13:24 +0000 (19:13 -0700)
* UnsafeAccessors are limited to lookup on
the defined typed.

* Update documentation for attribute.

---------

Co-authored-by: vitek-karas <10670590+vitek-karas@users.noreply.github.com>
src/coreclr/vm/prestub.cpp
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/UnsafeAccessorAttribute.cs
src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs

index 014d06c..174e485 100644 (file)
@@ -1266,13 +1266,12 @@ namespace
 
         MethodDesc* targetMaybe = NULL;
 
-        // Following the iteration pattern found in MemberLoader::FindMethod().
-        // Reverse order is recommended - see comments in MemberLoader::FindMethod().
-        MethodTable::MethodIterator iter(targetType.AsMethodTable());
-        iter.MoveToEnd();
-        for (; iter.IsValid(); iter.Prev())
+        // Following a similar iteration pattern found in MemberLoader::FindMethod().
+        // However, we are only operating on the current type not walking the type hierarchy.
+        MethodTable::IntroducedMethodIterator iter(targetType.AsMethodTable());
+        for (; iter.IsValid(); iter.Next())
         {
-            MethodDesc* curr = iter.GetDeclMethodDesc();
+            MethodDesc* curr = iter.GetMethodDesc();
 
             // Check the target and current method match static/instance state.
             if (cxt.IsTargetStatic != (!!curr->IsStatic()))
index a1cff03..476cb27 100644 (file)
@@ -45,6 +45,8 @@ namespace System.Runtime.CompilerServices
     /// The runtime will try to find the matching method or field and forward the call
     /// to it. If the matching method or field is not found, the body of the <code>extern</code>
     /// method will throw <see cref="MissingFieldException" /> or <see cref="MissingMethodException" />.
+    /// Only the specific type defined will be examined for inaccessible members. The type hierarchy
+    /// is not walked looking for a match.
     ///
     /// For <see cref="UnsafeAccessorKind.Method"/>, <see cref="UnsafeAccessorKind.StaticMethod"/>,
     /// <see cref="UnsafeAccessorKind.Field"/>, and <see cref="UnsafeAccessorKind.StaticField"/>, the type of
index 5077691..28d88ca 100644 (file)
@@ -373,32 +373,56 @@ static unsafe class UnsafeAccessorsTests
         extern static string CallManagedMethod(UserDataClass d, delegate* unmanaged[Cdecl]<void> fptr);
     }
 
-    class InheritanceBase
+    abstract class InheritanceBase
     {
         private static string OnBase() => nameof(OnBase);
         private static string FieldOnBase = nameof(FieldOnBase);
+        protected abstract string Abstract();
+        protected virtual string Virtual() => $"{nameof(InheritanceBase)}.{nameof(Virtual)}";
+        protected virtual string NewVirtual() => $"{nameof(InheritanceBase)}.{nameof(NewVirtual)}";
+        protected virtual string BaseVirtual() => $"{nameof(InheritanceBase)}.{nameof(BaseVirtual)}";
     }
 
     class InheritanceDerived : InheritanceBase
     {
         private static string OnDerived() => nameof(OnDerived);
         private static string FieldOnDerived = nameof(FieldOnDerived);
+        protected override string Abstract() => $"{nameof(InheritanceDerived)}.{nameof(Abstract)}";
+        protected override string Virtual() => $"{nameof(InheritanceDerived)}.{nameof(Virtual)}";
+        protected new virtual string NewVirtual() => $"{nameof(InheritanceDerived)}.{nameof(NewVirtual)}";
     }
 
     [Fact]
+    [ActiveIssue("https://github.com/dotnet/runtime/issues/89212", TestRuntimes.Mono)]
     public static void Verify_InheritanceMethodResolution()
     {
         Console.WriteLine($"Running {nameof(Verify_InheritanceMethodResolution)}");
 
         var instance = new InheritanceDerived();
         Assert.Throws<MissingMethodException>(() => OnBase(instance));
+        Assert.Throws<MissingMethodException>(() => BaseVirtual(instance));
         Assert.Equal(nameof(OnDerived), OnDerived(instance));
+        Assert.Equal($"{nameof(InheritanceDerived)}.{nameof(Abstract)}", Abstract(instance));
+        Assert.Equal($"{nameof(InheritanceDerived)}.{nameof(Virtual)}", Virtual(instance));
+        Assert.Equal($"{nameof(InheritanceBase)}.{nameof(NewVirtual)}", NewVirtual(instance));
 
         [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = nameof(OnBase))]
         extern static string OnBase(InheritanceDerived i);
 
+        [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(BaseVirtual))]
+        extern static string BaseVirtual(InheritanceDerived target);
+
         [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = nameof(OnDerived))]
         extern static string OnDerived(InheritanceDerived i);
+
+        [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(Abstract))]
+        extern static string Abstract(InheritanceBase target);
+
+        [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(Virtual))]
+        extern static string Virtual(InheritanceBase target);
+
+        [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(NewVirtual))]
+        extern static string NewVirtual(InheritanceBase target);
     }
 
     [Fact]
@@ -436,7 +460,7 @@ static unsafe class UnsafeAccessorsTests
             () => FieldNotFound(null));
         AssertExtensions.ThrowsMissingMemberException<MissingFieldException>(
             isNativeAot ? null : UserDataClass.StaticFieldName,
-            () => 
+            () =>
             {
                 UserDataValue value = default;
                 FieldNotFoundStaticMismatch1(ref value);
@@ -489,6 +513,8 @@ static unsafe class UnsafeAccessorsTests
     [ActiveIssue("https://github.com/dotnet/runtime/issues/86040", TestRuntimes.Mono)]
     public static void Verify_InvalidTargetUnsafeAccessorAmbiguousMatch()
     {
+        Console.WriteLine($"Running {nameof(Verify_InvalidTargetUnsafeAccessorAmbiguousMatch)}");
+
         Assert.Throws<AmbiguousMatchException>(
             () => CallAmbiguousMethod(CallPrivateConstructorClass(), null));