Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Arzt <arzt.samuel@live.de>2017-10-13 19:53:53 +0300
committerJan Kotas <jkotas@microsoft.com>2017-10-13 19:53:53 +0300
commit4b2a9ec505e831b384acf8369cec5152690b2c8d (patch)
tree35c956bd0fb70eb5f3123bd610b92732ec817478 /src/ILVerify
parent41bfc1be1d7b151a7884b325e66657ed93f87ddf (diff)
[ILVerify] Implement tracking of 'this' pointer state (#4714)
* Implemented tracking of 'this' state. * Fixed specific instructions using unsafe pop. * Added test support for methods with special names. * Added documentation for test cases of special name methods. * Added test cases for this state tracking. * Fixed try / leave not propagating this state. Changed this state tracking to allow field access on uninitialized this.
Diffstat (limited to 'src/ILVerify')
-rw-r--r--src/ILVerify/README.md17
-rw-r--r--src/ILVerify/src/ILImporter.StackValue.cs15
-rw-r--r--src/ILVerify/src/ILImporter.Verify.cs155
-rw-r--r--src/ILVerify/src/Resources/Strings.resx18
-rw-r--r--src/ILVerify/src/VerifierError.cs12
-rw-r--r--src/ILVerify/tests/ILMethodTester.cs2
-rw-r--r--src/ILVerify/tests/ILTests/ReturnTests.il14
-rw-r--r--src/ILVerify/tests/ILTests/ThisStateTests.il140
-rw-r--r--src/ILVerify/tests/TestDataLoader.cs29
9 files changed, 323 insertions, 79 deletions
diff --git a/src/ILVerify/README.md b/src/ILVerify/README.md
index fc2a1b0c8..f9463c1d6 100644
--- a/src/ILVerify/README.md
+++ b/src/ILVerify/README.md
@@ -61,6 +61,23 @@ The method name must contain 2 '`_`' characters.
3. part: the expected [VerifierErrors](https://github.com/dotnet/corert/blob/master/src/ILVerify/src/VerifierError.cs) as string separated by '.'. We assert on these errors; the test fails if ILVerify does not report these errors.
E.g.: ```SimpleAdd_Invalid_ExpectedNumericType```
+
+### Methods with special names:
+
+In order to test methods with special names (e.g. '.ctor'), the specialname method is defined as usual and a separate empty method is added to the type:
+```
+special.[FriendlyName].[SpecialName]_[Valid | Invalid]_[ExpectedVerifierError1].[ExpectedVerifierError2]....[ExpectedVerifierErrorN]
+```
+
+The format of the special test method is equal to normal valid or invalid tests, except that the first part must contain 3 sub-parts separated by '`.`':
+ 1. part: the '`special`' prefix
+ 2. part: a friendly name
+ 3. part: the name of the specialname method to actually test
+
+Additionally the method signature of the special test method must be equal to the signature of the method that shall be tested.
+
+ E.g.: In order to test a specific invalid constructor method the specialname `.ctor` method is defined as usual, while an additional method ```'special.SimpleAdd..ctor_Invalid_StackUnexpected'``` is defined.
+
The methods are automatically fed into appropriate XUnit theories based on the naming convention. Methods not following this naming conventions are ignored by the test scaffolding system.
diff --git a/src/ILVerify/src/ILImporter.StackValue.cs b/src/ILVerify/src/ILImporter.StackValue.cs
index fe1148832..f7ce9e1bc 100644
--- a/src/ILVerify/src/ILImporter.StackValue.cs
+++ b/src/ILVerify/src/ILImporter.StackValue.cs
@@ -17,7 +17,8 @@ namespace Internal.IL
{
None = 0,
ReadOnly = 1 << 1,
- PermanentHome = 1 << 2
+ PermanentHome = 1 << 2,
+ ThisPtr = 1 << 3,
}
private StackValueFlags Flags;
@@ -35,14 +36,21 @@ namespace Internal.IL
public void SetIsReadOnly()
{
+ Debug.Assert(Kind == StackValueKind.ByRef);
Flags |= StackValueFlags.ReadOnly;
}
public void SetIsPermanentHome()
{
+ Debug.Assert(Kind == StackValueKind.ByRef);
Flags |= StackValueFlags.PermanentHome;
}
+ public void SetIsThisPtr()
+ {
+ Flags |= StackValueFlags.ThisPtr;
+ }
+
public bool IsReadOnly
{
get { return (Flags & StackValueFlags.ReadOnly) == StackValueFlags.ReadOnly; }
@@ -53,6 +61,11 @@ namespace Internal.IL
get { return (Flags & StackValueFlags.PermanentHome) == StackValueFlags.PermanentHome; }
}
+ public bool IsThisPtr
+ {
+ get { return (Flags & StackValueFlags.ThisPtr) == StackValueFlags.ThisPtr; }
+ }
+
public bool IsNullReference
{
get { return Kind == StackValueKind.ObjRef && Type == null; }
diff --git a/src/ILVerify/src/ILImporter.Verify.cs b/src/ILVerify/src/ILImporter.Verify.cs
index 32ca9b993..c45ab1541 100644
--- a/src/ILVerify/src/ILImporter.Verify.cs
+++ b/src/ILVerify/src/ILImporter.Verify.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using Internal.TypeSystem;
@@ -58,6 +57,7 @@ namespace Internal.IL
StackValue[] _stack = s_emptyStack;
int _stackTop = 0;
+ bool _isThisInitialized;
bool _trackObjCtorState;
class ExceptionRegion
@@ -97,6 +97,7 @@ namespace Internal.IL
public ImportState State = ImportState.Unmarked;
public StackValue[] EntryStack;
+ public bool IsThisInitialized = false;
public bool TryStart;
public bool FilterStart;
@@ -129,11 +130,16 @@ namespace Internal.IL
_stack[_stackTop++] = value;
}
- StackValue Pop()
+ StackValue Pop(bool allowUninitThis = false)
{
FatalCheck(_stackTop > 0, VerifierError.StackUnderflow);
- return _stack[--_stackTop];
+ var stackValue = _stack[--_stackTop];
+
+ if (!allowUninitThis)
+ Check(!_trackObjCtorState || !stackValue.IsThisPtr || _isThisInitialized, VerifierError.UninitStack, stackValue);
+
+ return stackValue;
}
public ILImporter(MethodDesc method, MethodIL methodIL)
@@ -172,6 +178,7 @@ namespace Internal.IL
_maxStack = _methodIL.MaxStack;
+ _isThisInitialized = false;
_trackObjCtorState = !_methodSignature.IsStatic && _method.IsConstructor && !method.OwningType.IsValueType;
_ilBytes = _methodIL.GetILBytes();
@@ -614,6 +621,12 @@ namespace Internal.IL
VerificationError(VerifierError.StackObjRef, value);
}
+ private void CheckIsNotPointer(TypeDesc type)
+ {
+ if (type.IsPointer)
+ VerificationError(VerifierError.UnmanagedPointer);
+ }
+
void CheckIsComparable(StackValue a, StackValue b, ILOpcode op)
{
if (!IsBinaryComparable(a, b, op))
@@ -701,6 +714,8 @@ namespace Internal.IL
void StartImportingBasicBlock(BasicBlock basicBlock)
{
+ _isThisInitialized = basicBlock.IsThisInitialized;
+
if (basicBlock.TryStart)
{
Check(basicBlock.EntryStack == null || basicBlock.EntryStack.Length == 0, VerifierError.TryNonEmptyStack);
@@ -713,9 +728,15 @@ namespace Internal.IL
continue;
if (r.ILRegion.Kind == ILExceptionRegionKind.Filter)
- MarkBasicBlock(_basicBlocks[r.ILRegion.FilterOffset]);
-
- MarkBasicBlock(_basicBlocks[r.ILRegion.HandlerOffset]);
+ {
+ var filterBlock = _basicBlocks[r.ILRegion.FilterOffset];
+ PropagateThisState(basicBlock, filterBlock);
+ MarkBasicBlock(filterBlock);
+ }
+
+ var handlerBlock = _basicBlocks[r.ILRegion.HandlerOffset];
+ PropagateThisState(basicBlock, handlerBlock);
+ MarkBasicBlock(handlerBlock);
}
}
@@ -807,15 +828,13 @@ namespace Internal.IL
if (!argument)
Check(_initLocals, VerifierError.InitLocals);
-#if false
- if (argument)
- {
- if (m_verTrackObjCtorInitState && !vstate->isThisInitialized() && x.IsThisPtr())
- x.SetUninitialisedObjRef();
- }
-#endif
+ CheckIsNotPointer(varType);
+
+ var stackValue = StackValue.CreateFromType(varType);
+ if (index == 0 && argument)
+ stackValue.SetIsThisPtr();
- Push(StackValue.CreateFromType(varType));
+ Push(stackValue);
}
void ImportStoreVar(int index, bool argument)
@@ -824,13 +843,8 @@ namespace Internal.IL
var value = Pop();
-#if false
- if (argument)
- {
- if (m_verTrackObjCtorInitState && !vstate->isThisInitialized() )
- Verify(!m_paramVerifyMap[num].IsThisPtr(), MVER_E_THIS_UNINIT_STORE); //"storing to uninit this ptr"
- }
-#endif
+ if (_trackObjCtorState && !_isThisInitialized)
+ Check(index != 0 || !argument, VerifierError.ThisUninitStore);
CheckIsAssignable(value, StackValue.CreateFromType(varType));
}
@@ -842,20 +856,22 @@ namespace Internal.IL
if (!argument)
Check(_initLocals, VerifierError.InitLocals);
-#if false
- if (argument)
+ Check(!varType.IsByRef, VerifierError.ByrefOfByref);
+
+ var stackValue = StackValue.CreateByRef(varType);
+ if (index == 0 && argument)
{
- if (m_verTrackObjCtorInitState && !vstate->isThisInitialized() )
- Verify(!tiRetVal.IsThisPtr(), MVER_E_THIS_UNINIT_STORE);
+ stackValue.SetIsThisPtr();
+
+ Check(!_trackObjCtorState || _isThisInitialized, VerifierError.ThisUninitStore);
}
-#endif
- Push(StackValue.CreateByRef(varType));
+ Push(stackValue);
}
void ImportDup()
{
- var value = Pop();
+ var value = Pop(allowUninitThis: true);
Push(value);
Push(value);
@@ -863,7 +879,7 @@ namespace Internal.IL
void ImportPop()
{
- Pop();
+ Pop(allowUninitThis: true);
}
void ImportJmp(int token)
@@ -967,7 +983,7 @@ namespace Internal.IL
{
for (int i = sig.Length - 1; i >= 0; i--)
{
- var actual = Pop();
+ var actual = Pop(allowUninitThis: true);
var declared = StackValue.CreateFromType(sig[i]);
CheckIsAssignable(actual, declared);
@@ -985,36 +1001,24 @@ namespace Internal.IL
else
if (methodType != null)
{
- var actualThis = Pop();
+ var actualThis = Pop(allowUninitThis: true);
var declaredThis = methodType.IsValueType ?
StackValue.CreateByRef(methodType) : StackValue.CreateObjRef(methodType);
-#if false
- // If this is a call to the base class .ctor, set thisPtr Init for
- // this block.
- if (mflags & CORINFO_FLG_CONSTRUCTOR)
+ // If this is a call to the base class .ctor, set thisPtr Init for this block.
+ if (method.IsConstructor)
{
- if (m_verTrackObjCtorInitState && tiThis.IsThisPtr()
- && verIsCallToInitThisPtr(getCurrentMethodClass(), methodClassHnd))
+ if (_trackObjCtorState && actualThis.IsThisPtr &&
+ (methodType == _thisType || methodType == _thisType.BaseType)) // Call to overloaded ctor or base ctor
{
- // do not allow double init
- Verify(vstate->thisInitialized == THISUNINIT
- || vstate->thisInitialized == THISEHREACHED, MVER_E_PATH_THIS);
-
- vstate->containsCtorCall = 1;
- vstate->setThisInitialized();
- vstate->thisInitializedThisBlock = true;
- tiThis.SetInitialisedObjRef();
+ _isThisInitialized = true;
}
else
{
- // We allow direct calls to value type constructors
- // NB: we have to check that the contents of tiThis is a value type, otherwise we could use a constrained
- // callvirt to illegally re-enter a .ctor on a value of reference type.
- VerifyAndReportFound(tiThis.IsByRef() && DereferenceByRef(tiThis).IsValueClass(), tiThis, MVER_E_CALL_CTOR);
+ // Allow direct calls to value type constructors
+ Check(actualThis.Kind == StackValueKind.ByRef && actualThis.Type.IsValueType, VerifierError.CallCtor);
}
}
-#endif
if (constrained != null)
{
@@ -1227,11 +1231,9 @@ namespace Internal.IL
void ImportReturn()
{
-#if false
// 'this' must be init before return
if (_trackObjCtorState)
- Verify(vstate->isThisPublishable(), MVER_E_THIS_UNINIT_RET);
-#endif
+ Check(_isThisInitialized, VerifierError.ThisUninitReturn);
// Check current region type
Check(_currentBasicBlock.FilterIndex == null, VerifierError.ReturnFromFilter);
@@ -1265,9 +1267,12 @@ namespace Internal.IL
void ImportFallthrough(BasicBlock next)
{
- if (!IsValidBranchTarget(_currentBasicBlock, next))
+ if (!IsValidBranchTarget(_currentBasicBlock, next) || _currentBasicBlock.ErrorCount > 0)
return;
+ PropagateThisState(_currentBasicBlock, next);
+
+ // Propagate stack across block bounds
StackValue[] entryStack = next.EntryStack;
if (entryStack != null)
@@ -1312,6 +1317,24 @@ namespace Internal.IL
MarkBasicBlock(next);
}
+ void PropagateThisState(BasicBlock current, BasicBlock next)
+ {
+ if (next.State == BasicBlock.ImportState.Unmarked)
+ next.IsThisInitialized = _isThisInitialized;
+ else
+ {
+ if (next.IsThisInitialized && !_isThisInitialized)
+ {
+ // Next block has 'this' initialized, but current state has not
+ // therefore next block must be reverified with 'this' uninitialized
+ if (next.State == BasicBlock.ImportState.WasVerified && next.ErrorCount == 0)
+ next.State = BasicBlock.ImportState.Unmarked;
+ }
+
+ next.IsThisInitialized = next.IsThisInitialized && _isThisInitialized;
+ }
+ }
+
void ImportSwitchJump(int jmpBase, int[] jmpDelta, BasicBlock fallthrough)
{
var value = Pop();
@@ -1465,14 +1488,14 @@ namespace Internal.IL
// Note that even if the field is static, we require that the this pointer
// satisfy the same constraints as a non-static field This happens to
// be simpler and seems reasonable
- var actualThis = Pop();
+ var actualThis = Pop(allowUninitThis: true);
if (actualThis.Kind == StackValueKind.ValueType)
actualThis = StackValue.CreateByRef(actualThis.Type);
var declaredThis = owningType.IsValueType ?
StackValue.CreateByRef(owningType) : StackValue.CreateObjRef(owningType);
- CheckIsAssignable(actualThis, declaredThis);
+ CheckIsAssignable(actualThis, declaredThis);
}
Push(StackValue.CreateFromType(field.FieldType));
@@ -1496,7 +1519,7 @@ namespace Internal.IL
// Note that even if the field is static, we require that the this pointer
// satisfy the same constraints as a non-static field This happens to
// be simpler and seems reasonable
- var actualThis = Pop();
+ var actualThis = Pop(allowUninitThis: true);
if (actualThis.Kind == StackValueKind.ValueType)
actualThis = StackValue.CreateByRef(actualThis.Type);
@@ -1531,7 +1554,7 @@ namespace Internal.IL
// Note that even if the field is static, we require that the this pointer
// satisfy the same constraints as a non-static field This happens to
// be simpler and seems reasonable
- var actualThis = Pop();
+ var actualThis = Pop(allowUninitThis: true);
if (actualThis.Kind == StackValueKind.ValueType)
actualThis = StackValue.CreateByRef(actualThis.Type);
@@ -1602,17 +1625,7 @@ namespace Internal.IL
CheckIsObjRef(value);
-#if false
- if (m_verTrackObjCtorInitState && !vstate->isThisInitialized())
- Verify(!tiRetVal.IsThisPtr(), MVER_E_STACK_UNINIT);
-
- while (vstate->stackLevel() > 0)
- {
- // vstate->pop();
- // throw is not a return so we don't need to be initialized
- vstate->popPossiblyUninit();
- }
-#endif
+ EmptyTheStack();
}
void ImportLoadString(int token)
@@ -1674,7 +1687,9 @@ namespace Internal.IL
{
EmptyTheStack();
+ PropagateThisState(_currentBasicBlock, target);
MarkBasicBlock(target);
+
// TODO
}
@@ -1905,7 +1920,7 @@ namespace Internal.IL
Check(_currentBasicBlock.FilterIndex.HasValue, VerifierError.Endfilter);
Check(_currentOffset == _exceptionRegions[_currentBasicBlock.FilterIndex.Value].ILRegion.HandlerOffset, VerifierError.Endfilter);
- var result = Pop();
+ var result = Pop(allowUninitThis: true);
Check(result.Kind == StackValueKind.Int32, VerifierError.StackUnexpected);
Check(_stackTop == 0, VerifierError.EndfilterStack);
}
diff --git a/src/ILVerify/src/Resources/Strings.resx b/src/ILVerify/src/Resources/Strings.resx
index f3f5ce7dc..065e2e5a1 100644
--- a/src/ILVerify/src/Resources/Strings.resx
+++ b/src/ILVerify/src/Resources/Strings.resx
@@ -138,9 +138,15 @@
<data name="BranchOutOfTry" xml:space="preserve">
<value>Branch out of try block.</value>
</data>
+ <data name="ByrefOfByref" xml:space="preserve">
+ <value>ByRef of ByRef.</value>
+ </data>
<data name="CallAbstract" xml:space="preserve">
<value>Call not allowed on abstract methods.</value>
</data>
+ <data name="CallCtor" xml:space="preserve">
+ <value>call to .ctor only allowed to initialize this pointer from within a .ctor. Try newobj.</value>
+ </data>
<data name="CallVirtOnStatic" xml:space="preserve">
<value>callvirt on static.</value>
</data>
@@ -279,6 +285,12 @@
<data name="TailRetVoid" xml:space="preserve">
<value>Void ret type expected for tail call.</value>
</data>
+ <data name="ThisUninitReturn" xml:space="preserve">
+ <value>Return from .ctor when this is uninitialized.</value>
+ </data>
+ <data name="ThisUninitStore" xml:space="preserve">
+ <value>Store into this when it is uninitialized.</value>
+ </data>
<data name="TokenResolve" xml:space="preserve">
<value>Unable to resolve token.</value>
</data>
@@ -288,6 +300,12 @@
<data name="Unaligned" xml:space="preserve">
<value>Missing ldind, stind, ldfld, stfld, ldobj, stobj, initblk, cpblk.</value>
</data>
+ <data name="UninitStack" xml:space="preserve">
+ <value>Uninitialized item on stack.</value>
+ </data>
+ <data name="UnmanagedPointer" xml:space="preserve">
+ <value>Unmanaged pointers are not a verifiable type.</value>
+ </data>
<data name="UnrecognizedArgumentNumber" xml:space="preserve">
<value>Unrecognized argument number.</value>
</data>
diff --git a/src/ILVerify/src/VerifierError.cs b/src/ILVerify/src/VerifierError.cs
index d76ae4aa8..0c599c5f5 100644
--- a/src/ILVerify/src/VerifierError.cs
+++ b/src/ILVerify/src/VerifierError.cs
@@ -80,8 +80,8 @@ namespace ILVerify
PathStackDepth, //"Stack depth differs depending on path."
//E_THIS "Instance variable (this) missing."
//E_THIS_UNINIT_EXCEP "Uninitialized this on entering a try block."
- //E_THIS_UNINIT_STORE "Store into this when it is uninitialized."
- //E_THIS_UNINIT_RET "Return from .ctor when this is uninitialized."
+ ThisUninitStore, // Store into this when it is uninitialized.
+ ThisUninitReturn, // Return from .ctor when this is uninitialized.
//E_THIS_UNINIT_V_RET "Return from .ctor before all fields are initialized."
//E_THIS_UNINIT_BR "Branch back when this is uninitialized."
LdftnCtor, //"ldftn/ldvirtftn not allowed on .ctor."
@@ -92,7 +92,7 @@ namespace ILVerify
StackOverflow, // Stack overflow.
StackUnderflow, // Stack underflow.
//E_STACK_EMPTY "Stack empty."
- //E_STACK_UNINIT "Uninitialized item on stack."
+ UninitStack, // Uninitialized item on stack.
ExpectedIntegerType, // Expected I, I4, or I8 on the stack.
ExpectedFloatType, // Expected R, R4, or R8 on the stack.
//E_STACK_NO_R_I8 "unexpected R, R4, R8, or I8 on the stack."
@@ -156,7 +156,7 @@ namespace ILVerify
//E_SIG_ELEM_PTR "ELEMENT_TYPE_PTR cannot be verified."
//E_SIG_VARARG "Unexpected vararg."
//E_SIG_VOID "Unexpected Void."
- //E_SIG_BYREF_BYREF "ByRef of ByRef"
+ ByrefOfByref, // ByRef of ByRef.
//E_CODE_SIZE_ZERO "Code size is zero."
//E_BAD_VARARG "Unrecognized use of vararg."
TailCall, // Missing call/callvirt/calli.
@@ -197,7 +197,7 @@ namespace ILVerify
CallVirtOnStatic, // callvirt on static.
InitLocals, // initlocals must be set for verifiable methods with one or more local variables.
//E_BR_TO_EXCEPTION "branch/leave to the beginning of a catch/filter handler"
- //E_CALL_CTOR "call to .ctor only allowed to initialize this pointer from within a .ctor. Try newobj."
+ CallCtor, // call to .ctor only allowed to initialize this pointer from within a .ctor. Try newobj.
////@GENERICSVER: new generics related error messages
ExpectedValClassObjRefVariable, // Value type, ObjRef type or variable type expected.
@@ -232,7 +232,7 @@ namespace ILVerify
//E_BACKWARD_BRANCH "Stack height at all points must be determinable in a single forward scan of IL."
//E_CALL_TO_VTYPE_BASE "Call to base type of valuetype."
//E_NEWOBJ_OF_ABSTRACT_CLASS "Cannot construct an instance of abstract class."
- //E_UNMANAGED_POINTER "Unmanaged pointers are not a verifiable type."
+ UnmanagedPointer, // Unmanaged pointers are not a verifiable type.
//E_LDFTN_NON_FINAL_VIRTUAL "Cannot LDFTN a non-final virtual method for delegate creation if target object is potentially not the same type as the method class."
//E_FIELD_OVERLAP "Accessing type with overlapping fields."
//E_THIS_MISMATCH "The 'this' parameter to the call must be the calling method's 'this' parameter."
diff --git a/src/ILVerify/tests/ILMethodTester.cs b/src/ILVerify/tests/ILMethodTester.cs
index 7ad7b6cac..1d5eeda16 100644
--- a/src/ILVerify/tests/ILMethodTester.cs
+++ b/src/ILVerify/tests/ILMethodTester.cs
@@ -62,7 +62,7 @@ namespace ILVerify.Tests
{
Assert.True(verifierErrors.Contains(item));
}
- }
+ }
}
private ILImporter ConstructILImporter(TestCase testCase)
diff --git a/src/ILVerify/tests/ILTests/ReturnTests.il b/src/ILVerify/tests/ILTests/ReturnTests.il
index 600f5b877..487dd3218 100644
--- a/src/ILVerify/tests/ILTests/ReturnTests.il
+++ b/src/ILVerify/tests/ILTests/ReturnTests.il
@@ -148,4 +148,18 @@
ldflda int32 Struct::instanceField
ret
}
+
+ .method public hidebysig instance void 'special.ReturnAfterBaseCtor..ctor_Valid'() cil managed { ret }
+ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
+ {
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+
+ .method public hidebysig instance void 'special.ReturnBeforeBaseCtor..ctor_Invalid_ThisUninitReturn'(int32) cil managed { ret }
+ .method public hidebysig specialname rtspecialname instance void .ctor(int32) cil managed
+ {
+ ret
+ }
}
diff --git a/src/ILVerify/tests/ILTests/ThisStateTests.il b/src/ILVerify/tests/ILTests/ThisStateTests.il
new file mode 100644
index 000000000..0c55f4451
--- /dev/null
+++ b/src/ILVerify/tests/ILTests/ThisStateTests.il
@@ -0,0 +1,140 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+.assembly extern System.Runtime
+{
+}
+
+.assembly ThisStateTests
+{
+}
+
+.class public auto ansi beforefieldinit FieldTestsType
+ extends [System.Runtime]System.Object
+{
+ .field private int32 instanceField
+ .field private static int32 staticField
+
+ .method public hidebysig instance void 'special.LoadFieldThisInit..ctor_Valid'() cil managed { ret }
+ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
+ {
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ldarg.0
+ ldfld int32 FieldTestsType::instanceField
+ pop
+ ret
+ }
+
+ .method public hidebysig instance void 'special.LoadFieldThisUninit..ctor_Valid'(int32) cil managed { ret }
+ .method public hidebysig specialname rtspecialname instance void .ctor(int32) cil managed
+ {
+ ldarg.0
+ dup
+ ldfld int32 FieldTestsType::instanceField
+ pop
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+
+ .method public hidebysig instance void 'special.LoadSFieldThisUninit..ctor_Valid'(float64) cil managed { ret }
+ .method public hidebysig specialname rtspecialname instance void .ctor(float64) cil managed
+ {
+ ldarg.0
+ ldsfld int32 FieldTestsType::staticField
+ pop
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+
+ .method public hidebysig instance void 'special.StoreFieldThisInit..ctor_Valid'(int64) cil managed { ret }
+ .method public hidebysig specialname rtspecialname instance void .ctor(int64) cil managed
+ {
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ldarg.0
+ ldc.i4.0
+ stfld int32 FieldTestsType::instanceField
+ ret
+ }
+
+ .method public hidebysig instance void 'special.StoreFieldThisUninit..ctor_Valid'(int32, int32) cil managed { ret }
+ .method public hidebysig specialname rtspecialname instance void .ctor(int32, int32) cil managed
+ {
+ ldarg.0
+ dup
+ ldc.i4.0
+ stfld int32 FieldTestsType::instanceField
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+
+ .method public hidebysig instance void 'special.StoreSFieldThisUninit..ctor_Valid'(native int) cil managed { ret }
+ .method public hidebysig specialname rtspecialname instance void .ctor(native int) cil managed
+ {
+ ldc.i4.0
+ stsfld int32 FieldTestsType::staticField
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+}
+
+.class public auto ansi beforefieldinit ThisStateTestsType
+ extends [System.Runtime]System.Object
+{
+ .method public hidebysig instance void 'special.ThrowThisUninit..ctor_Invalid_UninitStack'() cil managed { ret }
+ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
+ {
+ ldarg.0
+ throw
+ ret
+ }
+
+ .method public hidebysig instance void 'special.CallCtorOverload..ctor_Valid'(int32) cil managed { ret }
+ .method public hidebysig specialname rtspecialname instance void .ctor(int32) cil managed
+ {
+ ldarg.0
+ call instance void ThisStateTestsType::.ctor()
+ ret
+ }
+}
+
+.class public auto ansi beforefieldinit LoadStoreVarTestsType
+ extends [System.Runtime]System.Object
+{
+ .method public hidebysig instance void 'special.StoreLocUninit..ctor_Invalid_StackUninit'() cil managed { ret }
+ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
+ {
+ .locals init (
+ [0] int32
+ )
+
+ ldc.i4.0
+ stloc.0
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+
+ .method public hidebysig instance void 'special.StoreThisUninit..ctor_Invalid_ThisUninitStore'(int32) cil managed { ret }
+ .method public hidebysig specialname rtspecialname instance void .ctor(int32) cil managed
+ {
+ newobj instance void LoadStoreVarTestsType::.ctor()
+ starg 0
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+
+ .method public hidebysig instance void 'special.LoadAddrOfThisUninit..ctor_Invalid_ThisUninitStore'(int64) cil managed { ret }
+ .method public hidebysig specialname rtspecialname instance void .ctor(int64) cil managed
+ {
+ ldarga 0
+ pop
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+}
diff --git a/src/ILVerify/tests/TestDataLoader.cs b/src/ILVerify/tests/TestDataLoader.cs
index 680cd53fc..d594aea12 100644
--- a/src/ILVerify/tests/TestDataLoader.cs
+++ b/src/ILVerify/tests/TestDataLoader.cs
@@ -10,6 +10,7 @@ using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Text;
using Internal.IL;
+using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
using Newtonsoft.Json;
using Xunit;
@@ -31,6 +32,8 @@ namespace ILVerify.Tests
/// </summary>
public static string TESTASSEMBLYPATH = @"..\..\..\ILTests\";
+ private const string SPECIALTEST_PREFIX = "special.";
+
/// <summary>
/// Returns all methods that contain valid IL code based on the following naming convention:
/// [FriendlyName]_Valid
@@ -107,7 +110,8 @@ namespace ILVerify.Tests
if (!String.IsNullOrEmpty(methodName) && methodName.Contains("_"))
{
var mparams = methodName.Split('_');
- var newItem = methodSelector(mparams, methodHandle);
+ var specialMethodHandle = HandleSpecialTests(mparams, method);
+ var newItem = methodSelector(mparams, specialMethodHandle);
if (newItem != null)
{
@@ -123,6 +127,29 @@ namespace ILVerify.Tests
return retVal;
}
+ private static MethodDefinitionHandle HandleSpecialTests(string[] methodParams, EcmaMethod method)
+ {
+ if (!methodParams[0].StartsWith(SPECIALTEST_PREFIX))
+ return method.Handle;
+
+ // Cut off special prefix
+ var specialParams = methodParams[0].Substring(SPECIALTEST_PREFIX.Length);
+
+ // Get friendly name / special name
+ int delimiter = specialParams.IndexOf('.');
+ if (delimiter < 0)
+ return method.Handle;
+
+ var friendlyName = specialParams.Substring(0, delimiter);
+ var specialName = specialParams.Substring(delimiter + 1);
+
+ // Substitute method parameters with friendly name
+ methodParams[0] = friendlyName;
+
+ var specialMethodHandle = (EcmaMethod)method.OwningType.GetMethod(specialName, method.Signature);
+ return specialMethodHandle == null ? method.Handle : specialMethodHandle.Handle;
+ }
+
private static IEnumerable<string> GetAllTestDlls()
{
foreach (var item in System.IO.Directory.GetFiles(TESTASSEMBLYPATH))