diff options
author | Samuel Arzt <arzt.samuel@live.de> | 2017-10-13 19:53:53 +0300 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2017-10-13 19:53:53 +0300 |
commit | 4b2a9ec505e831b384acf8369cec5152690b2c8d (patch) | |
tree | 35c956bd0fb70eb5f3123bd610b92732ec817478 /src/ILVerify | |
parent | 41bfc1be1d7b151a7884b325e66657ed93f87ddf (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.md | 17 | ||||
-rw-r--r-- | src/ILVerify/src/ILImporter.StackValue.cs | 15 | ||||
-rw-r--r-- | src/ILVerify/src/ILImporter.Verify.cs | 155 | ||||
-rw-r--r-- | src/ILVerify/src/Resources/Strings.resx | 18 | ||||
-rw-r--r-- | src/ILVerify/src/VerifierError.cs | 12 | ||||
-rw-r--r-- | src/ILVerify/tests/ILMethodTester.cs | 2 | ||||
-rw-r--r-- | src/ILVerify/tests/ILTests/ReturnTests.il | 14 | ||||
-rw-r--r-- | src/ILVerify/tests/ILTests/ThisStateTests.il | 140 | ||||
-rw-r--r-- | src/ILVerify/tests/TestDataLoader.cs | 29 |
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)) |