diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/CalliTests.il b/src/coreclr/tools/ILVerification.Tests/ILTests/CalliTests.il
new file mode 100644
index 00000000000000..cd0f85d74234da
--- /dev/null
+++ b/src/coreclr/tools/ILVerification.Tests/ILTests/CalliTests.il
@@ -0,0 +1,200 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+.assembly extern System.Runtime
+{
+}
+
+.assembly CalliTests
+{
+}
+
+// ILVerify reports every calli as Unverifiable, because the runtime does not track the lifetime of function
+// pointers, and then checks the call site.
+.class public auto ansi beforefieldinit CalliTests
+ extends [System.Runtime]System.Object
+{
+ .method private hidebysig static void StaticTarget(int32 x) cil managed
+ {
+ ret
+ }
+
+ .method private hidebysig static void ManagedTarget() cil managed
+ {
+ ret
+ }
+
+ .method private hidebysig static int32 ReturnsInt32() cil managed
+ {
+ ldc.i4.0
+ ret
+ }
+
+ .method private hidebysig instance void InstanceTarget(int32 x) cil managed
+ {
+ ret
+ }
+
+ .method public hidebysig static void Calli.StaticFromLdftn_Invalid_Unverifiable() cil managed
+ {
+ ldc.i4.1
+ ldftn void CalliTests::StaticTarget(int32)
+ calli void(int32)
+ ret
+ }
+
+ .method public hidebysig static int32 Calli.ReturnValue_Invalid_Unverifiable() cil managed
+ {
+ ldftn int32 CalliTests::ReturnsInt32()
+ calli int32()
+ ret
+ }
+
+ .method public hidebysig static void Calli.ErrorAfterCalli_Invalid_Unverifiable.StackUnderflow() cil managed
+ {
+ ldc.i4.1
+ ldftn void CalliTests::StaticTarget(int32)
+ calli void(int32)
+ pop
+ ret
+ }
+
+ .method public hidebysig static void Calli.FromArgument_Invalid_Unverifiable(method void *(int32) f) cil managed
+ {
+ ldc.i4.1
+ ldarg.0
+ calli void(int32)
+ ret
+ }
+
+ .method public hidebysig static void Calli.ArgumentMismatch_Invalid_Unverifiable.StackUnexpected(method void *(int32) f) cil managed
+ {
+ ldnull
+ ldarg.0
+ calli void(int32)
+ ret
+ }
+
+ .method public hidebysig static void Calli.MethodPointerNotNativeInt_Invalid_Unverifiable.StackMethod() cil managed
+ {
+ ldc.i4.1
+ ldc.i4.0
+ calli void(int32)
+ ret
+ }
+
+ .method public hidebysig instance void Calli.ImplicitThis_Invalid_Unverifiable(method instance void *(int32) f) cil managed
+ {
+ ldarg.0
+ ldc.i4.1
+ ldarg.1
+ calli instance void(int32)
+ ret
+ }
+
+ .method public hidebysig instance void Calli.ExplicitThis_Invalid_Unverifiable() cil managed
+ {
+ ldarg.0
+ ldc.i4.1
+ ldftn instance void CalliTests::InstanceTarget(int32)
+ calli instance explicit void(class CalliTests, int32)
+ ret
+ }
+
+ // A vararg call site with no extra arguments has no sentinel.
+ .method public hidebysig static void Calli.Vararg_Invalid_Unverifiable(method vararg void *(int32) f) cil managed
+ {
+ ldc.i4.1
+ ldarg.0
+ calli vararg void(int32)
+ ret
+ }
+
+ .method public hidebysig static void Calli.TailCall_Invalid_Unverifiable() cil managed
+ {
+ ldftn void CalliTests::ManagedTarget()
+ tail. calli void()
+ ret
+ }
+
+ .method public hidebysig static void Calli.TailCallByRefArgument_Invalid_Unverifiable.TailByRef(method void *(int32&) f) cil managed
+ {
+ .locals init (int32 V_0)
+ ldloca.s V_0
+ ldarg.0
+ tail. calli void(int32&)
+ ret
+ }
+
+ .method public hidebysig static void Calli.TailCallByRefThis_Invalid_Unverifiable.TailByRef(method instance void *() f) cil managed
+ {
+ .locals init (int32 V_0)
+ ldloca.s V_0
+ ldarg.0
+ tail. calli instance void()
+ ret
+ }
+
+ .method public hidebysig static void Calli.TailCallNotFollowedByRet_Invalid_Unverifiable.TailRet() cil managed
+ {
+ ldftn void CalliTests::ManagedTarget()
+ tail. calli void()
+ nop
+ ret
+ }
+
+ // The tail call's return checks are shared with call. A void method cannot tail-call a method returning a value;
+ // the value then left on the stack also makes the ret report ReturnVoid.
+ .method public hidebysig static void Calli.TailCallReturnsValue_Invalid_Unverifiable.TailRetVoid.ReturnVoid() cil managed
+ {
+ ldftn int32 CalliTests::ReturnsInt32()
+ tail. calli int32()
+ ret
+ }
+
+ .method public hidebysig static int32& Calli.ByRefReturn_Invalid_Unverifiable(method int32& *() f) cil managed
+ {
+ ldarg.0
+ calli int32&()
+ ret
+ }
+
+ // The calli operand is the TypeDef token of CalliTests (0x02000002), not a StandAloneSig token.
+ .method public hidebysig static void Calli.NotSignatureToken_Invalid_Unverifiable.ExpectedSignatureToken() cil managed
+ {
+ ldftn void CalliTests::ManagedTarget()
+ .emitbyte 0x29
+ .emitbyte 0x02
+ .emitbyte 0x00
+ .emitbyte 0x00
+ .emitbyte 0x02
+ ret
+ }
+
+ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
+ {
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+}
+
+.class public auto ansi beforefieldinit CalliConstructorTests
+ extends [System.Runtime]System.Object
+{
+ // Passing the uninitialized 'this' to a method pointer is not allowed (ECMA-335 III.1.8.1.4).
+ .method public hidebysig specialname rtspecialname instance void .ctor(method void *(object) f) cil managed
+ {
+ ldarg.0
+ ldarg.1
+ calli void(object)
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+
+ .method public hidebysig instance void 'special.CalliUninitThis..ctor_Invalid_Unverifiable.UninitStack'(method void *(object) f) cil managed
+ {
+ ret
+ }
+}
diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/CalliTests.ilproj b/src/coreclr/tools/ILVerification.Tests/ILTests/CalliTests.ilproj
new file mode 100644
index 00000000000000..356b4dcc778989
--- /dev/null
+++ b/src/coreclr/tools/ILVerification.Tests/ILTests/CalliTests.ilproj
@@ -0,0 +1,9 @@
+
+
+ $(MSBuildProjectName)
+
+
+
+
+
+
diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/TypedReferenceTests.il b/src/coreclr/tools/ILVerification.Tests/ILTests/TypedReferenceTests.il
new file mode 100644
index 00000000000000..6a510a76a09d38
--- /dev/null
+++ b/src/coreclr/tools/ILVerification.Tests/ILTests/TypedReferenceTests.il
@@ -0,0 +1,196 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+.assembly extern System.Runtime
+{
+}
+
+.assembly TypedReferenceTests
+{
+}
+
+.class public sequential ansi sealed beforefieldinit RefStruct
+ extends [System.Runtime]System.ValueType
+{
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( 01 00 00 00 )
+ .field public int32 Value
+}
+
+.class public auto ansi beforefieldinit TypedReferenceOther
+ extends [System.Runtime]System.Object
+{
+ .class nested private sequential ansi sealed beforefieldinit Hidden
+ extends [System.Runtime]System.ValueType
+ {
+ .field public int32 Value
+ }
+}
+
+.class public auto ansi beforefieldinit TypedReferenceTests
+ extends [System.Runtime]System.Object
+{
+ .method private hidebysig static void TakesTypedReference(typedref tr) cil managed
+ {
+ ret
+ }
+
+ // ---- mkrefany (ECMA-335 III.4.19) ----
+
+ .method public hidebysig static void Mkrefany.Local_Valid() cil managed
+ {
+ .locals init (int32 V_0)
+ ldloca.s V_0
+ mkrefany [System.Runtime]System.Int32
+ call void TypedReferenceTests::TakesTypedReference(typedref)
+ ret
+ }
+
+ .method public hidebysig static void Mkrefany.ErrorAfterMkrefany_Invalid_StackUnderflow() cil managed
+ {
+ .locals init (int32 V_0)
+ ldloca.s V_0
+ mkrefany [System.Runtime]System.Int32
+ pop
+ pop
+ ret
+ }
+
+ // Correct CIL, but verification requires a managed pointer.
+ .method public hidebysig static void Mkrefany.NativeIntPointer_Invalid_StackByRef() cil managed
+ {
+ ldc.i4.0
+ conv.i
+ mkrefany [System.Runtime]System.Int32
+ pop
+ ret
+ }
+
+ .method public hidebysig static void Mkrefany.TypeMismatch_Invalid_StackUnexpected() cil managed
+ {
+ .locals init (int32 V_0)
+ ldloca.s V_0
+ mkrefany [System.Runtime]System.Int64
+ pop
+ ret
+ }
+
+ // The pointer must point to exactly the type of the class token, not to a derived type.
+ .method public hidebysig static void Mkrefany.ReferenceTypeMismatch_Invalid_StackUnexpected() cil managed
+ {
+ .locals init (string V_0)
+ ldloca.s V_0
+ mkrefany [System.Runtime]System.Object
+ pop
+ ret
+ }
+
+ .method public hidebysig static void Mkrefany.ReadOnlyPointer_Invalid_ReadOnlyInMkrefany() cil managed
+ {
+ ldc.i4.1
+ newarr [System.Runtime]System.Int32
+ ldc.i4.0
+ readonly. ldelema [System.Runtime]System.Int32
+ mkrefany [System.Runtime]System.Int32
+ pop
+ ret
+ }
+
+ // The ECMA-335 augments allow a managed pointer to a byref-like type, so mkrefany accepts a byref-like class.
+ .method public hidebysig static void Mkrefany.TypedReference_Valid() cil managed
+ {
+ .locals init (typedref V_0)
+ ldloca.s V_0
+ mkrefany [System.Runtime]System.TypedReference
+ pop
+ ret
+ }
+
+ .method public hidebysig static void Mkrefany.ByRefLikeClass_Valid() cil managed
+ {
+ .locals init (valuetype RefStruct V_0)
+ ldloca.s V_0
+ mkrefany RefStruct
+ pop
+ ret
+ }
+
+ .method public hidebysig static void Mkrefany.InaccessibleType_Invalid_TypeAccess() cil managed
+ {
+ .locals init (valuetype TypedReferenceOther/Hidden V_0)
+ ldloca.s V_0
+ mkrefany TypedReferenceOther/Hidden
+ pop
+ ret
+ }
+
+ // ---- refanyval (ECMA-335 III.4.23) ----
+
+ .method public hidebysig static void Refanyval.ReadThroughAddress_Valid(typedref tr) cil managed
+ {
+ ldarg.0
+ refanyval [System.Runtime]System.Int32
+ ldind.i4
+ pop
+ ret
+ }
+
+ .method public hidebysig static void Refanyval.NotTypedReference_Invalid_StackUnexpected() cil managed
+ {
+ ldc.i4.0
+ refanyval [System.Runtime]System.Int32
+ pop
+ ret
+ }
+
+ .method public hidebysig static void Refanyval.ErrorAfterRefanyval_Invalid_StackUnderflow(typedref tr) cil managed
+ {
+ ldarg.0
+ refanyval [System.Runtime]System.Int32
+ pop
+ pop
+ ret
+ }
+
+ // The address in a typed reference can point to a local of a caller.
+ .method public hidebysig static int32& Refanyval.ReturnAddress_Invalid_ReturnPtrToStack(typedref tr) cil managed
+ {
+ ldarg.0
+ refanyval [System.Runtime]System.Int32
+ ret
+ }
+
+ // ---- refanytype (ECMA-335 III.4.22) ----
+
+ .method public hidebysig static void Refanytype.TypeFromHandle_Valid(typedref tr) cil managed
+ {
+ ldarg.0
+ refanytype
+ call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
+ pop
+ ret
+ }
+
+ .method public hidebysig static void Refanytype.NotTypedReference_Invalid_StackUnexpected() cil managed
+ {
+ ldc.i4.0
+ refanytype
+ pop
+ ret
+ }
+
+ .method public hidebysig static void Refanytype.ErrorAfterRefanytype_Invalid_StackUnderflow(typedref tr) cil managed
+ {
+ ldarg.0
+ refanytype
+ pop
+ pop
+ ret
+ }
+
+ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
+ {
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+}
diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/TypedReferenceTests.ilproj b/src/coreclr/tools/ILVerification.Tests/ILTests/TypedReferenceTests.ilproj
new file mode 100644
index 00000000000000..356b4dcc778989
--- /dev/null
+++ b/src/coreclr/tools/ILVerification.Tests/ILTests/TypedReferenceTests.ilproj
@@ -0,0 +1,9 @@
+
+
+ $(MSBuildProjectName)
+
+
+
+
+
+
diff --git a/src/coreclr/tools/ILVerification/ILImporter.Verify.cs b/src/coreclr/tools/ILVerification/ILImporter.Verify.cs
index 4cac413209b443..7770c8ffba7b29 100644
--- a/src/coreclr/tools/ILVerification/ILImporter.Verify.cs
+++ b/src/coreclr/tools/ILVerification/ILImporter.Verify.cs
@@ -1773,31 +1773,7 @@ void ImportCall(ILOpcode opcode, int token)
// special checks for tailcalls
if (tailCall)
- {
- TypeDesc callerReturnType = _methodSignature.ReturnType;
-
- if (returnType.IsVoid || callerReturnType.IsVoid)
- {
- Check(returnType.IsVoid && callerReturnType.IsVoid, VerifierError.TailRetVoid);
- }
- // else
- // if (returnType.IsValueType || callerReturnType.IsValueType)
- // {
- // TODO: Check exact match
- // }
- else
- {
- var retStackType = StackValue.CreateFromType(returnType);
- var callerRetStackType = StackValue.CreateFromType(callerReturnType);
- Check(IsAssignable(retStackType, callerRetStackType), VerifierError.TailRetType, retStackType, callerRetStackType);
- }
-
- // for tailcall, stack must be empty
- Check(_stackTop == 0, VerifierError.TailStackEmpty);
-
- // The instruction following a tail.call shall be a ret
- Check(_currentOffset < _ilBytes.Length && (ILOpcode)_ilBytes[_currentOffset] == ILOpcode.ret, VerifierError.TailRet);
- }
+ CheckTailCallReturn(returnType);
// now push on the result
if (opcode == ILOpcode.newobj)
@@ -1829,9 +1805,81 @@ void ImportCall(ILOpcode opcode, int token)
}
}
+ void CheckTailCallReturn(TypeDesc returnType)
+ {
+ TypeDesc callerReturnType = _methodSignature.ReturnType;
+
+ if (returnType.IsVoid || callerReturnType.IsVoid)
+ {
+ Check(returnType.IsVoid && callerReturnType.IsVoid, VerifierError.TailRetVoid);
+ }
+ // else
+ // if (returnType.IsValueType || callerReturnType.IsValueType)
+ // {
+ // TODO: Check exact match
+ // }
+ else
+ {
+ var retStackType = StackValue.CreateFromType(returnType);
+ var callerRetStackType = StackValue.CreateFromType(callerReturnType);
+ Check(IsAssignable(retStackType, callerRetStackType), VerifierError.TailRetType, retStackType, callerRetStackType);
+ }
+
+ // for tailcall, stack must be empty
+ Check(_stackTop == 0, VerifierError.TailStackEmpty);
+
+ // The instruction following a tail.call shall be a ret
+ Check(_currentOffset < _ilBytes.Length && (ILOpcode)_ilBytes[_currentOffset] == ILOpcode.ret, VerifierError.TailRet);
+ }
+
void ImportCalli(int token)
{
- throw new NotImplementedException($"{nameof(ImportCalli)} not implemented");
+ // calli is always reported as unverifiable. The runtime does not track the lifetime of function
+ // pointers, so the target may be code that has been unloaded. The call site is still checked, and
+ // verification goes on.
+ Unverifiable();
+
+ bool tailCall = HasPendingPrefix(Prefix.Tail);
+ ClearPendingPrefix(Prefix.Tail);
+
+ object tokenObj = ResolveToken(token);
+ FatalCheck(tokenObj is MethodSignature, VerifierError.ExpectedSignatureToken);
+ var sig = (MethodSignature)tokenObj;
+
+ var ftn = Pop();
+ Check(ftn.Kind == StackValueKind.NativeInt, VerifierError.StackMethod, ftn);
+
+ // An instance call site that does not list the type of 'this' takes one argument more than it declares.
+ bool implicitThis = !sig.IsStatic && !sig.IsExplicitThis;
+ var args = new StackValue[sig.Length + (implicitThis ? 1 : 0)];
+ for (int i = args.Length - 1; i >= 0; i--)
+ args[i] = Pop();
+
+ int firstParameter = args.Length - sig.Length;
+ for (int i = 0; i < sig.Length; i++)
+ {
+ var declared = StackValue.CreateFromType(sig[i]);
+ CheckIsAssignable(args[firstParameter + i], declared);
+
+ if (tailCall)
+ Check(!IsByRefLike(declared), VerifierError.TailByRef, declared);
+ }
+
+ if (implicitThis && tailCall)
+ Check(!IsByRefLike(args[0]), VerifierError.TailByRef, args[0]);
+
+ if (tailCall)
+ CheckTailCallReturn(sig.ReturnType);
+
+ if (!sig.ReturnType.IsVoid)
+ {
+ var returnValue = StackValue.CreateFromType(sig.ReturnType);
+
+ if (returnValue.Kind == StackValueKind.ByRef)
+ returnValue.SetIsPermanentHome();
+
+ Push(returnValue);
+ }
}
void ImportLdFtn(int token, ILOpcode opCode)
@@ -2918,7 +2966,7 @@ void ReportInvalidExceptionRegion()
}
//
- // Deprecated
+ // Variable argument lists and typed references
//
void ImportArgList()
@@ -2928,17 +2976,43 @@ void ImportArgList()
void ImportRefAnyType()
{
- throw new PlatformNotSupportedException("TypedReference not supported in .NET Core");
+ var typedReference = Pop();
+ CheckIsAssignable(typedReference, StackValue.CreateValueType(GetWellKnownType(WellKnownType.TypedReference)));
+
+ Push(StackValue.CreateValueType(GetWellKnownType(WellKnownType.RuntimeTypeHandle)));
}
void ImportMkRefAny(int token)
{
- throw new PlatformNotSupportedException("TypedReference not supported in .NET Core");
+ var type = ResolveTypeToken(token);
+
+ var ptr = Pop();
+
+ // ECMA-335 III.4.19: verification requires a managed pointer to an instance of the class.
+ // The class may be byref-like: the ECMA-335 augments allow a managed pointer to a byref-like type.
+ if (Check(ptr.Kind == StackValueKind.ByRef, VerifierError.StackByRef, ptr))
+ {
+ // III.1.8.1.2.2: a controlled-mutability managed pointer cannot be used with mkrefany.
+ Check(!ptr.IsReadOnly, VerifierError.ReadOnlyInMkrefany);
+
+ if (ptr.Type != type)
+ VerificationError(VerifierError.StackUnexpected, ptr, StackValue.CreateByRef(type));
+ }
+
+ Check(_method.OwningType.CanAccess(type), VerifierError.TypeAccess);
+
+ Push(StackValue.CreateValueType(GetWellKnownType(WellKnownType.TypedReference)));
}
void ImportRefAnyVal(int token)
{
- throw new PlatformNotSupportedException("TypedReference not supported in .NET Core");
+ var type = ResolveTypeToken(token);
+
+ var typedReference = Pop();
+ CheckIsAssignable(typedReference, StackValue.CreateValueType(GetWellKnownType(WellKnownType.TypedReference)));
+
+ // The address can point to a local of a caller, so it does not have a permanent home.
+ Push(StackValue.CreateByRef(type));
}
}
}
diff --git a/src/coreclr/tools/ILVerification/Strings.resx b/src/coreclr/tools/ILVerification/Strings.resx
index 66b0abac4723ba..8e000e803b0002 100644
--- a/src/coreclr/tools/ILVerification/Strings.resx
+++ b/src/coreclr/tools/ILVerification/Strings.resx
@@ -231,6 +231,9 @@
Expected numeric type on the stack.
+
+ Expected signature token.
+
Expected static field.
@@ -336,6 +339,9 @@
Illegal write to readonly ByRef.
+
+ A readonly ByRef cannot be used with mkrefany.
+
The readonly prefix may only be applied to calls to array methods returning ByRefs.
diff --git a/src/coreclr/tools/ILVerification/VerifierError.cs b/src/coreclr/tools/ILVerification/VerifierError.cs
index 87491f2741b387..40c33f2d6b5bd5 100644
--- a/src/coreclr/tools/ILVerification/VerifierError.cs
+++ b/src/coreclr/tools/ILVerification/VerifierError.cs
@@ -142,5 +142,7 @@ public enum VerifierError
EHClauseOrder, // An exception handling clause nested in a try must precede that try's clause.
EHClauseFilterEnd, // A filter must have exactly one endfilter as its last instruction.
UnalignedAlignment, // Alignment specified for 'unaligned.' must be 1, 2, or 4.
+ ExpectedSignatureToken, // Expected signature token.
+ ReadOnlyInMkrefany, // A readonly ByRef cannot be used with mkrefany.
}
}