From 87dc394abab1a15385aeea9ab9d2d7203c51097a Mon Sep 17 00:00:00 2001 From: Pkuyo Date: Mon, 8 Jun 2026 00:22:00 +0800 Subject: [PATCH 1/3] Implement base type verification and add related tests --- .../ILTests/BaseTypeTests.il | 57 +++++++++++ .../ILTests/BaseTypeTests.ilproj | 9 ++ src/coreclr/tools/ILVerification/Strings.resx | 3 + .../tools/ILVerification/TypeVerifier.cs | 97 ++++++++++++++++++- .../tools/ILVerification/VerifierError.cs | 1 + 5 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.il create mode 100644 src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.ilproj diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.il b/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.il new file mode 100644 index 00000000000000..7f0cb63d9581c8 --- /dev/null +++ b/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.il @@ -0,0 +1,57 @@ +// 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 BaseTypeTests +{ +} + +.class public auto ansi beforefieldinit ObjectTypeSpecBase_InvalidType_InvalidBaseType + extends object +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} + +.class interface public auto ansi abstract NilBaseInterface_ValidType_Valid +{ +} + +.class public auto ansi beforefieldinit GenericBase`1 + extends [System.Runtime]System.Object +{ +} + +.class public auto ansi beforefieldinit GenericClassTypeSpecBase_ValidType_Valid + extends class GenericBase`1 +{ +} + +.class public sequential ansi sealed beforefieldinit ValueTypeBase + extends [System.Runtime]System.ValueType +{ +} + +.class public auto ansi beforefieldinit ValueTypeBase_InvalidType_InvalidBaseType + extends valuetype ValueTypeBase +{ +} + +.class public sequential ansi sealed beforefieldinit GenericValueTypeBase`1 + extends [System.Runtime]System.ValueType +{ +} + +.class public auto ansi beforefieldinit GenericValueTypeSpecBase_InvalidType_InvalidBaseType + extends valuetype GenericValueTypeBase`1 +{ +} diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.ilproj b/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.ilproj new file mode 100644 index 00000000000000..356b4dcc778989 --- /dev/null +++ b/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.ilproj @@ -0,0 +1,9 @@ + + + $(MSBuildProjectName) + + + + + + diff --git a/src/coreclr/tools/ILVerification/Strings.resx b/src/coreclr/tools/ILVerification/Strings.resx index 02be5d1e00ff0c..fd31723fb735cb 100644 --- a/src/coreclr/tools/ILVerification/Strings.resx +++ b/src/coreclr/tools/ILVerification/Strings.resx @@ -453,4 +453,7 @@ Stack must be empty before localloc, except for the size item. + + Type '{0}' has invalid base type '{1}'. + diff --git a/src/coreclr/tools/ILVerification/TypeVerifier.cs b/src/coreclr/tools/ILVerification/TypeVerifier.cs index 4caec0c170c7eb..80f3d173a287af 100644 --- a/src/coreclr/tools/ILVerification/TypeVerifier.cs +++ b/src/coreclr/tools/ILVerification/TypeVerifier.cs @@ -39,9 +39,79 @@ public TypeVerifier(EcmaModule module, TypeDefinitionHandle typeDefinitionHandle public void Verify() { + VerifyBaseType(); VerifyInterfaces(); } + private void VerifyBaseType() + { + TypeDefinition typeDefinition = _module.MetadataReader.GetTypeDefinition(_typeDefinitionHandle); + EcmaType type = _module.GetType(_typeDefinitionHandle); + EntityHandle baseType = typeDefinition.BaseType; + if (baseType.IsNil) + { + if (!type.IsObject && !type.IsModuleType && !type.IsInterface) + { + VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); + } + } + else if (baseType.Kind == HandleKind.TypeSpecification) + { + if (!IsValidBaseTypeSpecification((TypeSpecificationHandle)baseType)) + { + VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); + } + + } + else if (IsValueType(baseType)) + { + VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); + } + } + + private bool IsValueType(EntityHandle typeHandle) + { + return _module.GetType(typeHandle).IsValueType; + } + + private bool IsValidBaseTypeSpecification(TypeSpecificationHandle typeSpecificationHandle) + { + try + { + TypeSpecification typeSpecification = _module.MetadataReader.GetTypeSpecification(typeSpecificationHandle); + BlobReader signatureReader = _module.MetadataReader.GetBlobReader(typeSpecification.Signature); + + if (signatureReader.ReadSignatureTypeCode() != SignatureTypeCode.GenericTypeInstance) + { + return false; + } + + int genericTypeKind = signatureReader.ReadCompressedInteger(); + if (genericTypeKind != (int)SignatureTypeKind.Class) + { + return false; + } + + EntityHandle genericTypeHandle = signatureReader.ReadTypeHandle(); + if (genericTypeHandle.Kind != HandleKind.TypeDefinition && + genericTypeHandle.Kind != HandleKind.TypeReference) + { + return false; + } + + if (IsValueType(genericTypeHandle)) + { + return false; + } + + return true; + } + catch (BadImageFormatException) + { + return false; + } + } + public void VerifyInterfaces() { TypeDefinition typeDefinition = _module.MetadataReader.GetTypeDefinition(_typeDefinitionHandle); @@ -117,10 +187,11 @@ private string Format(TypeDesc type) { if (_verifierOptions.IncludeMetadataTokensInErrorMessages) { + // type can be an InstantiatedType, so use the TypeDef to get the metadata token. TypeDesc typeDesc = type.GetTypeDefinition(); EcmaModule module = (EcmaModule)((MetadataType)typeDesc).Module; - return string.Format("{0}([{1}]0x{2:X8})", type, module, module.MetadataReader.GetToken(((EcmaType)type).Handle)); + return string.Format("{0}([{1}]0x{2:X8})", type, module, module.MetadataReader.GetToken(((EcmaType)typeDesc).Handle)); } else { @@ -128,6 +199,30 @@ private string Format(TypeDesc type) } } + private string Format(EntityHandle handle) + { + if (handle.IsNil) + { + return "nil"; + } + + if (handle.Kind == HandleKind.TypeDefinition || + handle.Kind == HandleKind.TypeReference || + handle.Kind == HandleKind.TypeSpecification) + { + return Format(_module.GetType(handle)); + } + + if (_verifierOptions.IncludeMetadataTokensInErrorMessages) + { + return string.Format("{0}([{1}]0x{2:X8})", handle.Kind, _module, _module.MetadataReader.GetToken(handle)); + } + else + { + return handle.Kind.ToString(); + } + } + private string Format(TypeDesc interfaceTypeDesc, EcmaModule module, InterfaceImplementation interfaceImplementation) { if (_verifierOptions.IncludeMetadataTokensInErrorMessages) diff --git a/src/coreclr/tools/ILVerification/VerifierError.cs b/src/coreclr/tools/ILVerification/VerifierError.cs index bc4821fbedb7d4..c9d4e8e18c8d55 100644 --- a/src/coreclr/tools/ILVerification/VerifierError.cs +++ b/src/coreclr/tools/ILVerification/VerifierError.cs @@ -190,5 +190,6 @@ public enum VerifierError InterfaceImplHasDuplicate, // InterfaceImpl has a duplicate InterfaceMethodNotImplemented, // Class implements interface but not method LocallocStackNotEmpty, // localloc requires that stack must be empty, except for 'size' argument + InvalidBaseType, // Type has an invalid base type. } } From 065706206a8ae6e505a02665f237f2d528984bad Mon Sep 17 00:00:00 2001 From: Pkuyo Date: Wed, 10 Jun 2026 01:37:42 +0800 Subject: [PATCH 2/3] Add type specification validation and related tests --- .../Common/TypeSystem/Ecma/EcmaModule.cs | 20 ++ .../ILTests/AccessTests.il | 2 +- .../ILTests/BaseTypeTests.il | 15 ++ .../ILTests/CastingTests.il | 4 +- .../ILTests/LoadStoreIndirectTests.il | 2 +- .../tools/ILVerification/TypeVerifier.cs | 79 ++++---- .../PreserveBaseOverridesAttibuteTesting.il | 32 +-- .../SignatureTests.cs | 185 ++++++++++++++++++ 8 files changed, 277 insertions(+), 62 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs index 312d33f2fd5eb3..f8b90686118dc2 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs @@ -570,6 +570,9 @@ private object ResolveTypeSpecification(TypeSpecificationHandle handle) TypeSpecification typeSpecification = _metadataReader.GetTypeSpecification(handle); BlobReader signatureReader = _metadataReader.GetBlobReader(typeSpecification.Signature); + + ValidateTypeSpecificationSignature(signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader, NotFoundBehavior.ReturnResolutionFailure); TypeDesc parsedType = parser.ParseType(); @@ -579,6 +582,23 @@ private object ResolveTypeSpecification(TypeSpecificationHandle handle) return parsedType; } + private static void ValidateTypeSpecificationSignature(BlobReader signatureReader) + { + switch (signatureReader.ReadSignatureTypeCode()) + { + case SignatureTypeCode.Pointer: + case SignatureTypeCode.FunctionPointer: + case SignatureTypeCode.Array: + case SignatureTypeCode.SZArray: + case SignatureTypeCode.GenericTypeInstance: + case SignatureTypeCode.GenericTypeParameter: + case SignatureTypeCode.GenericMethodParameter: + return; + } + + ThrowHelper.ThrowBadImageFormatException(); + } + private object ResolveMemberReference(MemberReferenceHandle handle) { MemberReference memberReference = _metadataReader.GetMemberReference(handle); diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/AccessTests.il b/src/coreclr/tools/ILVerification.Tests/ILTests/AccessTests.il index 1139e289e03719..4132dd2c34a9d7 100644 --- a/src/coreclr/tools/ILVerification.Tests/ILTests/AccessTests.il +++ b/src/coreclr/tools/ILVerification.Tests/ILTests/AccessTests.il @@ -264,7 +264,7 @@ .method public hidebysig instance void Call.GenericMethodWithPrivateNestedClass_Invalid_MethodAccess() cil managed { - call void class SimpleClass::GenericMethod() + call void SimpleClass::GenericMethod() ret } diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.il b/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.il index 7f0cb63d9581c8..de23cc5e6bcee2 100644 --- a/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.il +++ b/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.il @@ -22,6 +22,16 @@ } } +.class public auto ansi beforefieldinit ArrayTypeSpecBase_InvalidType_InvalidBaseType + extends int32[] +{ +} + +.class public auto ansi beforefieldinit PointerTypeSpecBase_InvalidType_InvalidBaseType + extends int32* +{ +} + .class interface public auto ansi abstract NilBaseInterface_ValidType_Valid { } @@ -36,6 +46,11 @@ { } +.class public auto ansi beforefieldinit GenericOpenClassTypeSpecBase_ValidType_Valid`1 + extends class GenericBase`1 +{ +} + .class public sequential ansi sealed beforefieldinit ValueTypeBase extends [System.Runtime]System.ValueType { diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/CastingTests.il b/src/coreclr/tools/ILVerification.Tests/ILTests/CastingTests.il index 65de357f4be137..0ba5d66814c063 100644 --- a/src/coreclr/tools/ILVerification.Tests/ILTests/CastingTests.il +++ b/src/coreclr/tools/ILVerification.Tests/ILTests/CastingTests.il @@ -343,10 +343,10 @@ ret } - .method public static void Casting.BoxByRefInt_Invalid_BoxByRef.ExpectedValClassObjRefVariable(int32& i) cil managed + .method public static void Casting.BoxIntPointer_Invalid_UnmanagedPointer.ExpectedValClassObjRefVariable(int32* i) cil managed { ldarg.0 - box int32& + box int32* pop ret } diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/LoadStoreIndirectTests.il b/src/coreclr/tools/ILVerification.Tests/ILTests/LoadStoreIndirectTests.il index d8ca59cd5f1c76..67f99c03a97fcf 100644 --- a/src/coreclr/tools/ILVerification.Tests/ILTests/LoadStoreIndirectTests.il +++ b/src/coreclr/tools/ILVerification.Tests/ILTests/LoadStoreIndirectTests.il @@ -140,7 +140,7 @@ ldloca.s V_0 ldstr "Hello" - stobj string + stobj [System.Runtime]System.String ret } diff --git a/src/coreclr/tools/ILVerification/TypeVerifier.cs b/src/coreclr/tools/ILVerification/TypeVerifier.cs index 80f3d173a287af..e3f2531a77c240 100644 --- a/src/coreclr/tools/ILVerification/TypeVerifier.cs +++ b/src/coreclr/tools/ILVerification/TypeVerifier.cs @@ -39,11 +39,17 @@ public TypeVerifier(EcmaModule module, TypeDefinitionHandle typeDefinitionHandle public void Verify() { - VerifyBaseType(); + // Once the base type metadata is invalid, later checks can force the type system to + // resolve the same bad base token again and produce a duplicate token resolution error. + if (!VerifyBaseType()) + { + return; + } + VerifyInterfaces(); } - private void VerifyBaseType() + private bool VerifyBaseType() { TypeDefinition typeDefinition = _module.MetadataReader.GetTypeDefinition(_typeDefinitionHandle); EcmaType type = _module.GetType(_typeDefinitionHandle); @@ -53,6 +59,7 @@ private void VerifyBaseType() if (!type.IsObject && !type.IsModuleType && !type.IsInterface) { VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); + return false; } } else if (baseType.Kind == HandleKind.TypeSpecification) @@ -60,46 +67,35 @@ private void VerifyBaseType() if (!IsValidBaseTypeSpecification((TypeSpecificationHandle)baseType)) { VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); + return false; } } - else if (IsValueType(baseType)) + else if (_module.GetType(baseType).IsValueType) { VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); + return false; } - } - private bool IsValueType(EntityHandle typeHandle) - { - return _module.GetType(typeHandle).IsValueType; + return true; } private bool IsValidBaseTypeSpecification(TypeSpecificationHandle typeSpecificationHandle) { try { - TypeSpecification typeSpecification = _module.MetadataReader.GetTypeSpecification(typeSpecificationHandle); - BlobReader signatureReader = _module.MetadataReader.GetBlobReader(typeSpecification.Signature); - - if (signatureReader.ReadSignatureTypeCode() != SignatureTypeCode.GenericTypeInstance) - { - return false; - } - - int genericTypeKind = signatureReader.ReadCompressedInteger(); - if (genericTypeKind != (int)SignatureTypeKind.Class) - { - return false; - } + TypeDesc baseType = _module.GetType(typeSpecificationHandle); - EntityHandle genericTypeHandle = signatureReader.ReadTypeHandle(); - if (genericTypeHandle.Kind != HandleKind.TypeDefinition && - genericTypeHandle.Kind != HandleKind.TypeReference) + // Arrays, pointers, and generic variables are valid TypeSpec forms in other + // metadata and IL token contexts, so GetType must continue to resolve them. A + // BaseType TypeSpec is narrower: it has to name a constructed generic class, + // which resolves to InstantiatedType. + if (baseType is not InstantiatedType) { return false; } - if (IsValueType(genericTypeHandle)) + if (baseType.IsValueType) { return false; } @@ -110,6 +106,10 @@ private bool IsValidBaseTypeSpecification(TypeSpecificationHandle typeSpecificat { return false; } + catch (TypeSystemException) + { + return false; + } } public void VerifyInterfaces() @@ -185,18 +185,14 @@ public void VerifyInterfaces() private string Format(TypeDesc type) { - if (_verifierOptions.IncludeMetadataTokensInErrorMessages) - { - // type can be an InstantiatedType, so use the TypeDef to get the metadata token. - TypeDesc typeDesc = type.GetTypeDefinition(); - EcmaModule module = (EcmaModule)((MetadataType)typeDesc).Module; - - return string.Format("{0}([{1}]0x{2:X8})", type, module, module.MetadataReader.GetToken(((EcmaType)typeDesc).Handle)); - } - else + TypeDesc typeDefinition = type.GetTypeDefinition(); + if (_verifierOptions.IncludeMetadataTokensInErrorMessages && typeDefinition is EcmaType ecmaType) { - return type.ToString(); + EcmaModule module = (EcmaModule)ecmaType.Module; + return string.Format("{0}([{1}]0x{2:X8})", type, module, module.MetadataReader.GetToken(ecmaType.Handle)); } + + return type.ToString(); } private string Format(EntityHandle handle) @@ -206,21 +202,20 @@ private string Format(EntityHandle handle) return "nil"; } - if (handle.Kind == HandleKind.TypeDefinition || - handle.Kind == HandleKind.TypeReference || - handle.Kind == HandleKind.TypeSpecification) + try { return Format(_module.GetType(handle)); } - - if (_verifierOptions.IncludeMetadataTokensInErrorMessages) + catch (BadImageFormatException) { - return string.Format("{0}([{1}]0x{2:X8})", handle.Kind, _module, _module.MetadataReader.GetToken(handle)); } - else + catch (TypeSystemException) { - return handle.Kind.ToString(); } + + return _verifierOptions.IncludeMetadataTokensInErrorMessages ? + string.Format("{0}([{1}]0x{2:X8})", handle.Kind, _module, _module.MetadataReader.GetToken(handle)) : + handle.Kind.ToString(); } private string Format(TypeDesc interfaceTypeDesc, EcmaModule module, InterfaceImplementation interfaceImplementation) diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/ILTestAssembly/PreserveBaseOverridesAttibuteTesting.il b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/ILTestAssembly/PreserveBaseOverridesAttibuteTesting.il index 54bc08c49083b3..881963bbe9776a 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/ILTestAssembly/PreserveBaseOverridesAttibuteTesting.il +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/ILTestAssembly/PreserveBaseOverridesAttibuteTesting.il @@ -60,7 +60,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func2() { - .override method instance string class A.T1::Func1() + .override method instance string A.T1::Func1() ldstr "T2" ret } @@ -81,7 +81,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string class A.T1::Func1() + .override method instance string A.T1::Func1() ldstr "T3" ret } @@ -92,7 +92,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string class A.T2Exp::Func2() + .override method instance string A.T2Exp::Func2() ldstr "T3" ret } @@ -126,7 +126,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string class A.T1::Func1() + .override method instance string A.T1::Func1() ldstr "T3" ret } @@ -138,7 +138,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string class A.T2Imp::Func1() + .override method instance string A.T2Imp::Func1() ldstr "T3" ret } @@ -173,7 +173,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func2() { - .override method instance string class B.T1::Func1() + .override method instance string B.T1::Func1() ldstr "T2" ret } @@ -185,7 +185,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string class B.T1::Func1() + .override method instance string B.T1::Func1() ldstr "T3" ret } @@ -197,7 +197,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string class B.T2::Func2() + .override method instance string B.T2::Func2() ldstr "T3" ret } @@ -244,7 +244,7 @@ .method public hidebysig newslot virtual instance string Func2() { .custom instance void [CoreTestAssembly]System.Runtime.CompilerServices.PreserveBaseOverridesAttribute::.ctor() = (01 00 00 00) - .override method instance string class C.T1::Func1() + .override method instance string C.T1::Func1() ldstr "T2" ret } @@ -256,7 +256,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string class C.T1::Func1() + .override method instance string C.T1::Func1() ldstr "T3" ret } @@ -268,7 +268,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string class C.T2::Func2() + .override method instance string C.T2::Func2() ldstr "T3" ret } @@ -326,7 +326,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string class D.T1::Func1() + .override method instance string D.T1::Func1() ldstr "T3" ret } @@ -338,7 +338,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string class D.T2::Func1() + .override method instance string D.T2::Func1() ldstr "T3" ret } @@ -384,7 +384,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string class E.T1::Func1() + .override method instance string E.T1::Func1() ldstr "T3" ret } @@ -396,7 +396,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string class E.T2::Func1() + .override method instance string E.T2::Func1() ldstr "T3" ret } @@ -1272,4 +1272,4 @@ ret } } -*/ \ No newline at end of file +*/ diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/SignatureTests.cs b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/SignatureTests.cs index 2909c2e553c8c3..7348b8b15c8b10 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/SignatureTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/SignatureTests.cs @@ -1,10 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; using System.Text; using Internal.IL; @@ -22,6 +26,9 @@ public class SignatureTests private TestTypeSystemContext _context; private ModuleDesc _testModule; + private const byte ElementTypeValueType = 0x11; + private const byte ElementTypeClass = 0x12; + public SignatureTests(ITestOutputHelper output) { _output = output; @@ -50,6 +57,184 @@ private static string GetModOptMethodSignatureInfo(MethodSignature signature) return sb.ToString(); } + private static EcmaModule CreateModuleWithTypeSpecification(Action buildSignature) + { + // Create a minimal in-memory assembly with exactly one TypeSpec row. + MetadataBuilder metadataBuilder = new MetadataBuilder(); + StringHandle assemblyName = metadataBuilder.GetOrAddString("TypeSpecTest"); + + metadataBuilder.AddModule(0, metadataBuilder.GetOrAddString("TypeSpecTest.dll"), metadataBuilder.GetOrAddGuid(new Guid("F3C03C57-397E-4A33-B670-CB4EE2C88AF7")), default(GuidHandle), default(GuidHandle)); + metadataBuilder.AddAssembly(assemblyName, new Version(1, 0, 0, 0), default(StringHandle), default(BlobHandle), default(AssemblyFlags), AssemblyHashAlgorithm.None); + metadataBuilder.AddTypeDefinition( + default(TypeAttributes), + default(StringHandle), + metadataBuilder.GetOrAddString(""), + default(EntityHandle), + MetadataTokens.FieldDefinitionHandle(1), + MetadataTokens.MethodDefinitionHandle(1)); + + BlobBuilder signature = new BlobBuilder(); + buildSignature(metadataBuilder, signature); + metadataBuilder.AddTypeSpecification(metadataBuilder.GetOrAddBlob(signature)); + + BlobBuilder peBlob = new BlobBuilder(); + ManagedPEBuilder peBuilder = new ManagedPEBuilder(PEHeaderBuilder.CreateLibraryHeader(), new MetadataRootBuilder(metadataBuilder), new BlobBuilder()); + peBuilder.Serialize(peBlob); + + MemoryStream peStream = new MemoryStream(); + peBlob.WriteContentTo(peStream); + peStream.Position = 0; + + TestTypeSystemContext context = new TestTypeSystemContext(TargetArchitecture.X64); + ModuleDesc systemModule = context.CreateModuleForSimpleName("CoreTestAssembly"); + context.SetSystemModule(systemModule); + return (EcmaModule)context.CreateModuleForSimpleName("TypeSpecTest", peStream); + } + + private static AssemblyReferenceHandle AddCoreTestAssemblyReference(MetadataBuilder metadataBuilder) + { + return metadataBuilder.AddAssemblyReference( + metadataBuilder.GetOrAddString("CoreTestAssembly"), + new Version(0, 0, 0, 0), + default(StringHandle), + default(BlobHandle), + default(AssemblyFlags), + default(BlobHandle)); + } + + private static TypeReferenceHandle AddCoreTestAssemblyTypeReference(MetadataBuilder metadataBuilder, string ns, string name) + { + AssemblyReferenceHandle coreTestAssembly = AddCoreTestAssemblyReference(metadataBuilder); + return metadataBuilder.AddTypeReference(coreTestAssembly, metadataBuilder.GetOrAddString(ns), metadataBuilder.GetOrAddString(name)); + } + + private static void WriteTypeDefOrRefEncoded(BlobBuilder signature, EntityHandle handle) + { + int tag = handle.Kind switch + { + HandleKind.TypeDefinition => 0, + HandleKind.TypeReference => 1, + HandleKind.TypeSpecification => 2, + _ => throw new BadImageFormatException() + }; + + signature.WriteCompressedInteger((MetadataTokens.GetRowNumber(handle) << 2) | tag); + } + + [Theory] + [InlineData(SignatureTypeCode.Void)] + [InlineData(SignatureTypeCode.Boolean)] + [InlineData(SignatureTypeCode.Char)] + [InlineData(SignatureTypeCode.SByte)] + [InlineData(SignatureTypeCode.Byte)] + [InlineData(SignatureTypeCode.Int16)] + [InlineData(SignatureTypeCode.UInt16)] + [InlineData(SignatureTypeCode.Int32)] + [InlineData(SignatureTypeCode.UInt32)] + [InlineData(SignatureTypeCode.Int64)] + [InlineData(SignatureTypeCode.UInt64)] + [InlineData(SignatureTypeCode.Single)] + [InlineData(SignatureTypeCode.Double)] + [InlineData(SignatureTypeCode.String)] + [InlineData(SignatureTypeCode.ByReference)] + [InlineData(SignatureTypeCode.TypedReference)] + [InlineData(SignatureTypeCode.IntPtr)] + [InlineData(SignatureTypeCode.UIntPtr)] + [InlineData(SignatureTypeCode.Object)] + public void TestInvalidTopLevelTypeSpecification(SignatureTypeCode typeCode) + { + // Primitive and special element types cannot be used as + // the top-level signature in a TypeSpec row. + EcmaModule module = CreateModuleWithTypeSpecification((_, signature) => + { + signature.WriteByte((byte)typeCode); + if (typeCode == SignatureTypeCode.ByReference) + signature.WriteByte((byte)SignatureTypeCode.Int32); + }); + + Assert.Throws(() => module.GetType(MetadataTokens.TypeSpecificationHandle(1))); + } + + [Theory] + [InlineData(ElementTypeClass, "Object")] + [InlineData(ElementTypeValueType, "Int32")] + public void TestInvalidTopLevelTypeHandleTypeSpecification(byte elementType, string typeName) + { + // CLASS and VALUETYPE cannot be used as tokens in a top-level TypeSpec signature. + EcmaModule module = CreateModuleWithTypeSpecification((metadataBuilder, signature) => + { + TypeReferenceHandle typeRef = AddCoreTestAssemblyTypeReference(metadataBuilder, "System", typeName); + signature.WriteByte(elementType); + WriteTypeDefOrRefEncoded(signature, typeRef); + }); + + Assert.Throws(() => module.GetType(MetadataTokens.TypeSpecificationHandle(1))); + } + + [Theory] + [InlineData(SignatureTypeCode.GenericTypeParameter)] + [InlineData(SignatureTypeCode.GenericMethodParameter)] + public void TestTopLevelGenericVariableTypeSpecificationResolves(SignatureTypeCode typeCode) + { + EcmaModule module = CreateModuleWithTypeSpecification((_, signature) => + { + signature.WriteByte((byte)typeCode); + signature.WriteCompressedInteger(0); + }); + + Assert.NotNull(module.GetType(MetadataTokens.TypeSpecificationHandle(1))); + } + + [Theory] + [InlineData(SignatureTypeCode.Pointer)] + [InlineData(SignatureTypeCode.FunctionPointer)] + [InlineData(SignatureTypeCode.Array)] + [InlineData(SignatureTypeCode.SZArray)] + [InlineData(SignatureTypeCode.GenericTypeInstance)] + public void TestValidTopLevelTypeSpecification(SignatureTypeCode typeCode) + { + EcmaModule module = CreateModuleWithTypeSpecification((metadataBuilder, signature) => + { + signature.WriteByte((byte)typeCode); + + // Use the smallest valid payload for each root so this test only exercises + // top-level TypeSpec validation. + switch (typeCode) + { + case SignatureTypeCode.Pointer: + signature.WriteByte((byte)SignatureTypeCode.Void); + break; + + case SignatureTypeCode.FunctionPointer: + signature.WriteByte((byte)SignatureCallingConvention.Default); + signature.WriteCompressedInteger(0); + signature.WriteByte((byte)SignatureTypeCode.Void); + break; + + case SignatureTypeCode.Array: + signature.WriteByte((byte)SignatureTypeCode.Object); + signature.WriteCompressedInteger(1); + signature.WriteCompressedInteger(0); + signature.WriteCompressedInteger(0); + break; + + case SignatureTypeCode.SZArray: + signature.WriteByte((byte)SignatureTypeCode.Object); + break; + + case SignatureTypeCode.GenericTypeInstance: + TypeReferenceHandle genericTypeRef = AddCoreTestAssemblyTypeReference(metadataBuilder, "GenericTypes", "GenericClass`1"); + signature.WriteByte(ElementTypeClass); + WriteTypeDefOrRefEncoded(signature, genericTypeRef); + signature.WriteCompressedInteger(1); + signature.WriteByte((byte)SignatureTypeCode.Object); + break; + } + }); + + Assert.NotNull(module.GetType(MetadataTokens.TypeSpecificationHandle(1))); + } + [Fact] public void TestSignatureMatches2ModOptsAtStartOfSig() { From a7ece370e34f4a94c39f572a9c4d0b9938187498 Mon Sep 17 00:00:00 2001 From: Pkuyo Date: Wed, 10 Jun 2026 12:35:23 +0800 Subject: [PATCH 3/3] Gate TypeSpec validation under ILVERIFICATION --- .../Common/TypeSystem/Ecma/EcmaModule.cs | 4 + .../ILVerification/ILVerification.projitems | 1 + .../tools/ILVerification/TypeVerifier.cs | 51 ++--- .../PreserveBaseOverridesAttibuteTesting.il | 30 +-- .../SignatureTests.cs | 185 ------------------ 5 files changed, 37 insertions(+), 234 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs index f8b90686118dc2..ea770a28a8c61b 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs @@ -571,7 +571,9 @@ private object ResolveTypeSpecification(TypeSpecificationHandle handle) BlobReader signatureReader = _metadataReader.GetBlobReader(typeSpecification.Signature); +#if ILVERIFICATION ValidateTypeSpecificationSignature(signatureReader); +#endif EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader, NotFoundBehavior.ReturnResolutionFailure); @@ -582,6 +584,7 @@ private object ResolveTypeSpecification(TypeSpecificationHandle handle) return parsedType; } +#if ILVERIFICATION private static void ValidateTypeSpecificationSignature(BlobReader signatureReader) { switch (signatureReader.ReadSignatureTypeCode()) @@ -598,6 +601,7 @@ private static void ValidateTypeSpecificationSignature(BlobReader signatureReade ThrowHelper.ThrowBadImageFormatException(); } +#endif private object ResolveMemberReference(MemberReferenceHandle handle) { diff --git a/src/coreclr/tools/ILVerification/ILVerification.projitems b/src/coreclr/tools/ILVerification/ILVerification.projitems index c301f2da1d298e..05c12be9f40b4d 100644 --- a/src/coreclr/tools/ILVerification/ILVerification.projitems +++ b/src/coreclr/tools/ILVerification/ILVerification.projitems @@ -13,6 +13,7 @@ + ILVERIFICATION;$(DefineConstants) $(MSBuildThisFileDirectory)..\Common\ diff --git a/src/coreclr/tools/ILVerification/TypeVerifier.cs b/src/coreclr/tools/ILVerification/TypeVerifier.cs index e3f2531a77c240..b6a5cd0f8a6a9b 100644 --- a/src/coreclr/tools/ILVerification/TypeVerifier.cs +++ b/src/coreclr/tools/ILVerification/TypeVerifier.cs @@ -62,54 +62,37 @@ private bool VerifyBaseType() return false; } } - else if (baseType.Kind == HandleKind.TypeSpecification) + else { - if (!IsValidBaseTypeSpecification((TypeSpecificationHandle)baseType)) + TypeDesc resolvedBaseType; + try + { + resolvedBaseType = _module.GetType(baseType); + } + catch (BadImageFormatException) + { + VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); + return false; + } + catch (TypeSystemException) { VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); return false; } - - } - else if (_module.GetType(baseType).IsValueType) - { - VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); - return false; - } - - return true; - } - - private bool IsValidBaseTypeSpecification(TypeSpecificationHandle typeSpecificationHandle) - { - try - { - TypeDesc baseType = _module.GetType(typeSpecificationHandle); // Arrays, pointers, and generic variables are valid TypeSpec forms in other // metadata and IL token contexts, so GetType must continue to resolve them. A // BaseType TypeSpec is narrower: it has to name a constructed generic class, // which resolves to InstantiatedType. - if (baseType is not InstantiatedType) - { - return false; - } - - if (baseType.IsValueType) + if (resolvedBaseType.IsValueType || + (baseType.Kind == HandleKind.TypeSpecification && resolvedBaseType is not InstantiatedType)) { + VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); return false; } - - return true; - } - catch (BadImageFormatException) - { - return false; - } - catch (TypeSystemException) - { - return false; } + + return true; } public void VerifyInterfaces() diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/ILTestAssembly/PreserveBaseOverridesAttibuteTesting.il b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/ILTestAssembly/PreserveBaseOverridesAttibuteTesting.il index 881963bbe9776a..418bbc0c048b2f 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/ILTestAssembly/PreserveBaseOverridesAttibuteTesting.il +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/ILTestAssembly/PreserveBaseOverridesAttibuteTesting.il @@ -60,7 +60,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func2() { - .override method instance string A.T1::Func1() + .override method instance string class A.T1::Func1() ldstr "T2" ret } @@ -81,7 +81,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string A.T1::Func1() + .override method instance string class A.T1::Func1() ldstr "T3" ret } @@ -92,7 +92,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string A.T2Exp::Func2() + .override method instance string class A.T2Exp::Func2() ldstr "T3" ret } @@ -126,7 +126,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string A.T1::Func1() + .override method instance string class A.T1::Func1() ldstr "T3" ret } @@ -138,7 +138,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string A.T2Imp::Func1() + .override method instance string class A.T2Imp::Func1() ldstr "T3" ret } @@ -173,7 +173,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func2() { - .override method instance string B.T1::Func1() + .override method instance string class B.T1::Func1() ldstr "T2" ret } @@ -185,7 +185,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string B.T1::Func1() + .override method instance string class B.T1::Func1() ldstr "T3" ret } @@ -197,7 +197,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string B.T2::Func2() + .override method instance string class B.T2::Func2() ldstr "T3" ret } @@ -244,7 +244,7 @@ .method public hidebysig newslot virtual instance string Func2() { .custom instance void [CoreTestAssembly]System.Runtime.CompilerServices.PreserveBaseOverridesAttribute::.ctor() = (01 00 00 00) - .override method instance string C.T1::Func1() + .override method instance string class C.T1::Func1() ldstr "T2" ret } @@ -256,7 +256,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string C.T1::Func1() + .override method instance string class C.T1::Func1() ldstr "T3" ret } @@ -268,7 +268,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string C.T2::Func2() + .override method instance string class C.T2::Func2() ldstr "T3" ret } @@ -326,7 +326,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string D.T1::Func1() + .override method instance string class D.T1::Func1() ldstr "T3" ret } @@ -338,7 +338,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string D.T2::Func1() + .override method instance string class D.T2::Func1() ldstr "T3" ret } @@ -384,7 +384,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string E.T1::Func1() + .override method instance string class E.T1::Func1() ldstr "T3" ret } @@ -396,7 +396,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance string Func3() { - .override method instance string E.T2::Func1() + .override method instance string class E.T2::Func1() ldstr "T3" ret } diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/SignatureTests.cs b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/SignatureTests.cs index 7348b8b15c8b10..2909c2e553c8c3 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/SignatureTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/SignatureTests.cs @@ -1,14 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; -using System.Reflection.PortableExecutable; using System.Text; using Internal.IL; @@ -26,9 +22,6 @@ public class SignatureTests private TestTypeSystemContext _context; private ModuleDesc _testModule; - private const byte ElementTypeValueType = 0x11; - private const byte ElementTypeClass = 0x12; - public SignatureTests(ITestOutputHelper output) { _output = output; @@ -57,184 +50,6 @@ private static string GetModOptMethodSignatureInfo(MethodSignature signature) return sb.ToString(); } - private static EcmaModule CreateModuleWithTypeSpecification(Action buildSignature) - { - // Create a minimal in-memory assembly with exactly one TypeSpec row. - MetadataBuilder metadataBuilder = new MetadataBuilder(); - StringHandle assemblyName = metadataBuilder.GetOrAddString("TypeSpecTest"); - - metadataBuilder.AddModule(0, metadataBuilder.GetOrAddString("TypeSpecTest.dll"), metadataBuilder.GetOrAddGuid(new Guid("F3C03C57-397E-4A33-B670-CB4EE2C88AF7")), default(GuidHandle), default(GuidHandle)); - metadataBuilder.AddAssembly(assemblyName, new Version(1, 0, 0, 0), default(StringHandle), default(BlobHandle), default(AssemblyFlags), AssemblyHashAlgorithm.None); - metadataBuilder.AddTypeDefinition( - default(TypeAttributes), - default(StringHandle), - metadataBuilder.GetOrAddString(""), - default(EntityHandle), - MetadataTokens.FieldDefinitionHandle(1), - MetadataTokens.MethodDefinitionHandle(1)); - - BlobBuilder signature = new BlobBuilder(); - buildSignature(metadataBuilder, signature); - metadataBuilder.AddTypeSpecification(metadataBuilder.GetOrAddBlob(signature)); - - BlobBuilder peBlob = new BlobBuilder(); - ManagedPEBuilder peBuilder = new ManagedPEBuilder(PEHeaderBuilder.CreateLibraryHeader(), new MetadataRootBuilder(metadataBuilder), new BlobBuilder()); - peBuilder.Serialize(peBlob); - - MemoryStream peStream = new MemoryStream(); - peBlob.WriteContentTo(peStream); - peStream.Position = 0; - - TestTypeSystemContext context = new TestTypeSystemContext(TargetArchitecture.X64); - ModuleDesc systemModule = context.CreateModuleForSimpleName("CoreTestAssembly"); - context.SetSystemModule(systemModule); - return (EcmaModule)context.CreateModuleForSimpleName("TypeSpecTest", peStream); - } - - private static AssemblyReferenceHandle AddCoreTestAssemblyReference(MetadataBuilder metadataBuilder) - { - return metadataBuilder.AddAssemblyReference( - metadataBuilder.GetOrAddString("CoreTestAssembly"), - new Version(0, 0, 0, 0), - default(StringHandle), - default(BlobHandle), - default(AssemblyFlags), - default(BlobHandle)); - } - - private static TypeReferenceHandle AddCoreTestAssemblyTypeReference(MetadataBuilder metadataBuilder, string ns, string name) - { - AssemblyReferenceHandle coreTestAssembly = AddCoreTestAssemblyReference(metadataBuilder); - return metadataBuilder.AddTypeReference(coreTestAssembly, metadataBuilder.GetOrAddString(ns), metadataBuilder.GetOrAddString(name)); - } - - private static void WriteTypeDefOrRefEncoded(BlobBuilder signature, EntityHandle handle) - { - int tag = handle.Kind switch - { - HandleKind.TypeDefinition => 0, - HandleKind.TypeReference => 1, - HandleKind.TypeSpecification => 2, - _ => throw new BadImageFormatException() - }; - - signature.WriteCompressedInteger((MetadataTokens.GetRowNumber(handle) << 2) | tag); - } - - [Theory] - [InlineData(SignatureTypeCode.Void)] - [InlineData(SignatureTypeCode.Boolean)] - [InlineData(SignatureTypeCode.Char)] - [InlineData(SignatureTypeCode.SByte)] - [InlineData(SignatureTypeCode.Byte)] - [InlineData(SignatureTypeCode.Int16)] - [InlineData(SignatureTypeCode.UInt16)] - [InlineData(SignatureTypeCode.Int32)] - [InlineData(SignatureTypeCode.UInt32)] - [InlineData(SignatureTypeCode.Int64)] - [InlineData(SignatureTypeCode.UInt64)] - [InlineData(SignatureTypeCode.Single)] - [InlineData(SignatureTypeCode.Double)] - [InlineData(SignatureTypeCode.String)] - [InlineData(SignatureTypeCode.ByReference)] - [InlineData(SignatureTypeCode.TypedReference)] - [InlineData(SignatureTypeCode.IntPtr)] - [InlineData(SignatureTypeCode.UIntPtr)] - [InlineData(SignatureTypeCode.Object)] - public void TestInvalidTopLevelTypeSpecification(SignatureTypeCode typeCode) - { - // Primitive and special element types cannot be used as - // the top-level signature in a TypeSpec row. - EcmaModule module = CreateModuleWithTypeSpecification((_, signature) => - { - signature.WriteByte((byte)typeCode); - if (typeCode == SignatureTypeCode.ByReference) - signature.WriteByte((byte)SignatureTypeCode.Int32); - }); - - Assert.Throws(() => module.GetType(MetadataTokens.TypeSpecificationHandle(1))); - } - - [Theory] - [InlineData(ElementTypeClass, "Object")] - [InlineData(ElementTypeValueType, "Int32")] - public void TestInvalidTopLevelTypeHandleTypeSpecification(byte elementType, string typeName) - { - // CLASS and VALUETYPE cannot be used as tokens in a top-level TypeSpec signature. - EcmaModule module = CreateModuleWithTypeSpecification((metadataBuilder, signature) => - { - TypeReferenceHandle typeRef = AddCoreTestAssemblyTypeReference(metadataBuilder, "System", typeName); - signature.WriteByte(elementType); - WriteTypeDefOrRefEncoded(signature, typeRef); - }); - - Assert.Throws(() => module.GetType(MetadataTokens.TypeSpecificationHandle(1))); - } - - [Theory] - [InlineData(SignatureTypeCode.GenericTypeParameter)] - [InlineData(SignatureTypeCode.GenericMethodParameter)] - public void TestTopLevelGenericVariableTypeSpecificationResolves(SignatureTypeCode typeCode) - { - EcmaModule module = CreateModuleWithTypeSpecification((_, signature) => - { - signature.WriteByte((byte)typeCode); - signature.WriteCompressedInteger(0); - }); - - Assert.NotNull(module.GetType(MetadataTokens.TypeSpecificationHandle(1))); - } - - [Theory] - [InlineData(SignatureTypeCode.Pointer)] - [InlineData(SignatureTypeCode.FunctionPointer)] - [InlineData(SignatureTypeCode.Array)] - [InlineData(SignatureTypeCode.SZArray)] - [InlineData(SignatureTypeCode.GenericTypeInstance)] - public void TestValidTopLevelTypeSpecification(SignatureTypeCode typeCode) - { - EcmaModule module = CreateModuleWithTypeSpecification((metadataBuilder, signature) => - { - signature.WriteByte((byte)typeCode); - - // Use the smallest valid payload for each root so this test only exercises - // top-level TypeSpec validation. - switch (typeCode) - { - case SignatureTypeCode.Pointer: - signature.WriteByte((byte)SignatureTypeCode.Void); - break; - - case SignatureTypeCode.FunctionPointer: - signature.WriteByte((byte)SignatureCallingConvention.Default); - signature.WriteCompressedInteger(0); - signature.WriteByte((byte)SignatureTypeCode.Void); - break; - - case SignatureTypeCode.Array: - signature.WriteByte((byte)SignatureTypeCode.Object); - signature.WriteCompressedInteger(1); - signature.WriteCompressedInteger(0); - signature.WriteCompressedInteger(0); - break; - - case SignatureTypeCode.SZArray: - signature.WriteByte((byte)SignatureTypeCode.Object); - break; - - case SignatureTypeCode.GenericTypeInstance: - TypeReferenceHandle genericTypeRef = AddCoreTestAssemblyTypeReference(metadataBuilder, "GenericTypes", "GenericClass`1"); - signature.WriteByte(ElementTypeClass); - WriteTypeDefOrRefEncoded(signature, genericTypeRef); - signature.WriteCompressedInteger(1); - signature.WriteByte((byte)SignatureTypeCode.Object); - break; - } - }); - - Assert.NotNull(module.GetType(MetadataTokens.TypeSpecificationHandle(1))); - } - [Fact] public void TestSignatureMatches2ModOptsAtStartOfSig() {