From 5575cc060201ad462809f48db25a56c35e4eaca0 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Tue, 12 Nov 2019 14:51:55 -0800 Subject: [PATCH] Support custom modifier for method signature --- .../Collections/Generic/LowLevelStack.cs | 3 + .../src/TypeSystem/Common/MethodDesc.cs | 48 ++++++- .../TypeSystem/Ecma/EcmaSignatureParser.cs | 128 ++++++++++++++++-- .../ILTestAssembly/ILTestAssembly.ilproj | 1 + .../tests/ILTestAssembly/Signature.il | 96 +++++++++++++ .../tests/SignatureTests.cs | 53 ++++++++ .../tests/TypeSystem.Tests.csproj | 3 +- .../src/System.Private.TypeLoader.csproj | 3 + 8 files changed, 322 insertions(+), 13 deletions(-) create mode 100644 src/ILCompiler.TypeSystem/tests/ILTestAssembly/Signature.il create mode 100644 src/ILCompiler.TypeSystem/tests/SignatureTests.cs diff --git a/src/Common/src/System/Collections/Generic/LowLevelStack.cs b/src/Common/src/System/Collections/Generic/LowLevelStack.cs index b193036a262..f6967e1f42d 100644 --- a/src/Common/src/System/Collections/Generic/LowLevelStack.cs +++ b/src/Common/src/System/Collections/Generic/LowLevelStack.cs @@ -28,6 +28,9 @@ namespace System.Collections.Generic /// Data size is smaller because there will be minimal virtual function table. /// Code size is smaller because only functions called will be in the binary. /// +#if TYPE_LOADER_IMPLEMENTATION + [System.Runtime.CompilerServices.ForceDictionaryLookups] +#endif internal class LowLevelStack { protected T[] _items; diff --git a/src/Common/src/TypeSystem/Common/MethodDesc.cs b/src/Common/src/TypeSystem/Common/MethodDesc.cs index 7103433b0c0..3e204f8fa75 100644 --- a/src/Common/src/TypeSystem/Common/MethodDesc.cs +++ b/src/Common/src/TypeSystem/Common/MethodDesc.cs @@ -24,6 +24,19 @@ public enum MethodSignatureFlags Static = 0x0010, } + public enum EmbeddedSignatureDataKind + { + RequiredCustomModifier = 0, + OptionalCustomModifier = 1 + } + + public struct EmbeddedSignatureData + { + public string index; + public EmbeddedSignatureDataKind kind; + public TypeDesc type; + } + /// /// Represents the parameter types, the return type, and flags of a method. /// @@ -33,13 +46,15 @@ public sealed partial class MethodSignature : TypeSystemEntity internal int _genericParameterCount; internal TypeDesc _returnType; internal TypeDesc[] _parameters; + internal EmbeddedSignatureData[] _embeddedSignatureData; - public MethodSignature(MethodSignatureFlags flags, int genericParameterCount, TypeDesc returnType, TypeDesc[] parameters) + public MethodSignature(MethodSignatureFlags flags, int genericParameterCount, TypeDesc returnType, TypeDesc[] parameters, EmbeddedSignatureData[] embeddedSignatureData = null) { _flags = flags; _genericParameterCount = genericParameterCount; _returnType = returnType; _parameters = parameters; + _embeddedSignatureData = embeddedSignatureData; Debug.Assert(parameters != null, "Parameters must not be null"); } @@ -120,7 +135,32 @@ public bool Equals(MethodSignature otherSignature) return false; } - return true; + if (this._embeddedSignatureData == null && otherSignature._embeddedSignatureData == null) + { + return true; + } + + if (this._embeddedSignatureData != null && otherSignature._embeddedSignatureData != null) + { + if (this._embeddedSignatureData.Length != otherSignature._embeddedSignatureData.Length) + { + return false; + } + + for (int i = 0; i < this._embeddedSignatureData.Length; i++) + { + if (this._embeddedSignatureData[i].index != otherSignature._embeddedSignatureData[i].index) + return false; + if (this._embeddedSignatureData[i].kind != otherSignature._embeddedSignatureData[i].kind) + return false; + if (this._embeddedSignatureData[i].type != otherSignature._embeddedSignatureData[i].type) + return false; + } + + return true; + } + + return false; } public override bool Equals(object obj) @@ -174,6 +214,7 @@ public struct MethodSignatureBuilder private int _genericParameterCount; private TypeDesc _returnType; private TypeDesc[] _parameters; + private EmbeddedSignatureData[] _customModifiers; public MethodSignatureBuilder(MethodSignature template) { @@ -183,6 +224,7 @@ public MethodSignatureBuilder(MethodSignature template) _genericParameterCount = template._genericParameterCount; _returnType = template._returnType; _parameters = template._parameters; + _customModifiers = template._embeddedSignatureData; } public MethodSignatureFlags Flags @@ -237,7 +279,7 @@ public MethodSignature ToSignature() _returnType != _template._returnType || _parameters != _template._parameters) { - _template = new MethodSignature(_flags, _genericParameterCount, _returnType, _parameters); + _template = new MethodSignature(_flags, _genericParameterCount, _returnType, _parameters, _customModifiers); } return _template; diff --git a/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs b/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs index 74d60434792..85311c813ad 100644 --- a/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs +++ b/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs @@ -6,8 +6,10 @@ using System.Reflection.Metadata; using System.Runtime.InteropServices; using System.Diagnostics; +using System.Linq; using Internal.TypeSystem; +using System.Collections.Generic; namespace Internal.TypeSystem.Ecma { @@ -16,13 +18,20 @@ public struct EcmaSignatureParser private EcmaModule _module; private BlobReader _reader; - // TODO - // bool _hasModifiers; +#if TYPE_LOADER_IMPLEMENTATION + private LowLevelStack _indexStack; +#else + private Stack _indexStack; +#endif + private List _embeddedSignatureDataList; + public EcmaSignatureParser(EcmaModule module, BlobReader reader) { _module = module; _reader = reader; + _indexStack = null; + _embeddedSignatureDataList = null; } private TypeDesc GetWellKnownType(WellKnownType wellKnownType) @@ -31,6 +40,23 @@ private TypeDesc GetWellKnownType(WellKnownType wellKnownType) } private TypeDesc ParseType(SignatureTypeCode typeCode) + { + + if (_indexStack != null) + { + int was = _indexStack.Pop(); + _indexStack.Push(was + 1); + _indexStack.Push(0); + } + TypeDesc result = ParseTypeImpl(typeCode); + if (_indexStack != null) + { + _indexStack.Pop(); + } + return result; + } + + private TypeDesc ParseTypeImpl(SignatureTypeCode typeCode) { // Switch on the type. switch (typeCode) @@ -111,7 +137,7 @@ private TypeDesc ParseType(SignatureTypeCode typeCode) case SignatureTypeCode.TypedReference: return GetWellKnownType(WellKnownType.TypedReference); case SignatureTypeCode.FunctionPointer: - return _module.Context.GetFunctionPointerType(ParseMethodSignature()); + return _module.Context.GetFunctionPointerType(ParseMethodSignatureInternal(skipEmbeddedSignatureData: true)); default: throw new BadImageFormatException(); } @@ -119,15 +145,43 @@ private TypeDesc ParseType(SignatureTypeCode typeCode) private SignatureTypeCode ParseTypeCode(bool skipPinned = true) { - for (;;) + if (_indexStack != null) + { + int was = _indexStack.Pop(); + _indexStack.Push(was + 1); + _indexStack.Push(0); + } + SignatureTypeCode result = ParseTypeCodeImpl(skipPinned); + if (_indexStack != null) + { + _indexStack.Pop(); + } + return result; + } + + private SignatureTypeCode ParseTypeCodeImpl(bool skipPinned = true) + { + for (; ; ) { SignatureTypeCode typeCode = _reader.ReadSignatureTypeCode(); - // TODO: actually consume modopts - if (typeCode == SignatureTypeCode.RequiredModifier || - typeCode == SignatureTypeCode.OptionalModifier) + if (typeCode == SignatureTypeCode.RequiredModifier) + { + EntityHandle typeHandle = _reader.ReadTypeHandle(); + if (_embeddedSignatureDataList != null) + { + _embeddedSignatureDataList.Add(new EmbeddedSignatureData { index = string.Join(".", _indexStack), kind = EmbeddedSignatureDataKind.RequiredCustomModifier, type = _module.GetType(typeHandle) }); + } + continue; + } + + if (typeCode == SignatureTypeCode.OptionalModifier) { - _reader.ReadTypeHandle(); + EntityHandle typeHandle = _reader.ReadTypeHandle(); + if (_embeddedSignatureDataList != null) + { + _embeddedSignatureDataList.Add(new EmbeddedSignatureData { index = string.Join(".", _indexStack), kind = EmbeddedSignatureDataKind.OptionalCustomModifier, type = _module.GetType(typeHandle) }); + } continue; } @@ -143,6 +197,22 @@ private SignatureTypeCode ParseTypeCode(bool skipPinned = true) } public TypeDesc ParseType() + { + if (_indexStack != null) + { + int was = _indexStack.Pop(); + _indexStack.Push(was + 1); + _indexStack.Push(0); + } + TypeDesc result = ParseTypeImpl(); + if (_indexStack != null) + { + _indexStack.Pop(); + } + return result; + } + + private TypeDesc ParseTypeImpl() { return ParseType(ParseTypeCode()); } @@ -157,6 +227,43 @@ public bool IsFieldSignature } public MethodSignature ParseMethodSignature() + { + try + { +#if TYPE_LOADER_IMPLEMENTATION + _indexStack = new LowLevelStack(); +#else + _indexStack = new Stack(); +#endif + _indexStack.Push(0); + _embeddedSignatureDataList = new List(); + return ParseMethodSignatureInternal(skipEmbeddedSignatureData: false); + } + finally + { + _indexStack = null; + _embeddedSignatureDataList = null; + } + + } + + private MethodSignature ParseMethodSignatureInternal(bool skipEmbeddedSignatureData) + { + if (_indexStack != null) + { + int was = _indexStack.Pop(); + _indexStack.Push(was + 1); + _indexStack.Push(0); + } + MethodSignature result = ParseMethodSignatureImpl(skipEmbeddedSignatureData); + if (_indexStack != null) + { + _indexStack.Pop(); + } + return result; + } + + private MethodSignature ParseMethodSignatureImpl(bool skipEmbeddedSignatureData) { SignatureHeader header = _reader.ReadSignatureHeader(); @@ -198,7 +305,10 @@ public MethodSignature ParseMethodSignature() parameters = TypeDesc.EmptyTypes; } - return new MethodSignature(flags, arity, returnType, parameters); + EmbeddedSignatureData[] embeddedSignatureDataArray = (_embeddedSignatureDataList == null || _embeddedSignatureDataList.Count == 0 || skipEmbeddedSignatureData) ? null : _embeddedSignatureDataList.ToArray(); + + return new MethodSignature(flags, arity, returnType, parameters, embeddedSignatureDataArray); + } public PropertySignature ParsePropertySignature() diff --git a/src/ILCompiler.TypeSystem/tests/ILTestAssembly/ILTestAssembly.ilproj b/src/ILCompiler.TypeSystem/tests/ILTestAssembly/ILTestAssembly.ilproj index bce9574f966..a7c32cc839c 100644 --- a/src/ILCompiler.TypeSystem/tests/ILTestAssembly/ILTestAssembly.ilproj +++ b/src/ILCompiler.TypeSystem/tests/ILTestAssembly/ILTestAssembly.ilproj @@ -15,6 +15,7 @@ + diff --git a/src/ILCompiler.TypeSystem/tests/ILTestAssembly/Signature.il b/src/ILCompiler.TypeSystem/tests/ILTestAssembly/Signature.il new file mode 100644 index 00000000000..c9cc2d22377 --- /dev/null +++ b/src/ILCompiler.TypeSystem/tests/ILTestAssembly/Signature.il @@ -0,0 +1,96 @@ +.class private auto ansi beforefieldinit Atom + extends [CoreTestAssembly]System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [CoreTestAssembly]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method I::.ctor + +} // end of class Atom + +.class private auto ansi beforefieldinit A`1 + extends [CoreTestAssembly]System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [CoreTestAssembly]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method A`1::.ctor + +} // end of class A`1 + +.class private auto ansi beforefieldinit BaseClass`2 + extends [CoreTestAssembly]System.Object +{ + .method public hidebysig newslot virtual + instance void Method(!U u, + !T modopt (FooModifier) t) cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method BaseClass`2::Method + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [CoreTestAssembly]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method BaseClass`2::.ctor + +} // end of class BaseClass`2 + +.class private auto ansi beforefieldinit DerivedClass + extends class BaseClass`2,class Atom> +{ + .method public hidebysig virtual instance void + Method(class A`1 u, + class Atom modopt (FooModifier) t) cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method DerivedClass::Method + + .method public hidebysig virtual instance void + Method(class A`1 u, + class Atom t) cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method DerivedClass::Method + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void class BaseClass`2,class Atom>::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method DerivedClass::.ctor + +} // end of DerivedClass + +.class public FooModifier { } +.class public BarModifier { } diff --git a/src/ILCompiler.TypeSystem/tests/SignatureTests.cs b/src/ILCompiler.TypeSystem/tests/SignatureTests.cs new file mode 100644 index 00000000000..f64676cfbbd --- /dev/null +++ b/src/ILCompiler.TypeSystem/tests/SignatureTests.cs @@ -0,0 +1,53 @@ +// 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. + +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Internal.IL; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Xunit; + +namespace TypeSystemTests +{ + public class SignatureTests + { + private TestTypeSystemContext _context; + private ModuleDesc _testModule; + + public SignatureTests() + { + _context = new TestTypeSystemContext(TargetArchitecture.X64); + var systemModule = _context.CreateModuleForSimpleName("CoreTestAssembly"); + _context.SetSystemModule(systemModule); + + _testModule = _context.GetModuleForSimpleName("ILTestAssembly"); + } + + [Fact] + public void TestSignatureMatches() + { + MetadataType atomType = _testModule.GetType("", "Atom"); + MetadataType aType = _testModule.GetType("", "A`1"); + MetadataType aOfAtomType = aType.MakeInstantiatedType(new Instantiation(atomType)); + + + MetadataType baseClassType = _testModule.GetType("", "BaseClass`2"); + MethodDesc baseClassMethod = baseClassType.GetMethods().Single(m => string.Equals(m.Name, "Method")); + MethodSignature baseClassMethodSignature = baseClassMethod.Signature; + MethodSignatureBuilder matchingSignatureBuilder = new MethodSignatureBuilder(baseClassMethodSignature); + matchingSignatureBuilder[0] = aOfAtomType; + matchingSignatureBuilder[1] = atomType; + MethodSignature matchingSignature = matchingSignatureBuilder.ToSignature(); + + MetadataType derivedClassType = _testModule.GetType("", "DerivedClass"); + IEnumerable derivedClassMethods = derivedClassType.GetMethods().Where(m => string.Equals(m.Name, "Method")); + IEnumerable matches = derivedClassMethods.Select(m => matchingSignature.Equals(m.Signature)); + int matchCount = matches.Select(b => b ? 1 : 0).Sum(); + Assert.Equal(1, matchCount); + } + } +} diff --git a/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj b/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj index f1ef7772b06..d4b4ca90010 100644 --- a/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj +++ b/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj @@ -1,4 +1,4 @@ - + Library @@ -46,6 +46,7 @@ + diff --git a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj index edc3049c37d..61aa57683f6 100644 --- a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj +++ b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj @@ -116,6 +116,9 @@ Internal\TypeSystem\Ecma\CachingMetadataStringDecoder.cs + + Internal\TypeSystem\Ecma\LowLevelStack.cs + Internal\TypeSystem\Ecma\CustomAttributeTypeProvider.cs