From 9a74f018b542f178ab678bd4f8324188781e77ee Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 5 Jun 2018 00:38:08 +0200 Subject: [PATCH] Decode metadata signatures directly into IType; without going through ITypeReference. --- .../CSharp/CSharpDecompiler.cs | 3 +- .../CSharp/RequiredNamespaceCollector.cs | 4 +- .../Documentation/XmlDocKeyProvider.cs | 4 +- .../ICSharpCode.Decompiler.csproj | 3 + ICSharpCode.Decompiler/IL/ILReader.cs | 43 ++- .../Transforms/TransformArrayInitializers.cs | 32 +- .../Metadata/MetadataExtensions.cs | 19 +- .../Metadata/MetadataResolver.cs | 2 +- ICSharpCode.Decompiler/SRMExtensions.cs | 41 ++- .../TypeSystem/ApplyAttributeTypeVisitor.cs | 202 ++++++++++++ .../TypeSystem/DecompilerTypeSystem.cs | 86 ++--- .../TypeSystem/IAssembly.cs | 2 +- .../TypeSystem/IDecompilerTypeSystem.cs | 5 +- .../DefaultUnresolvedAssembly.cs | 6 +- .../Implementation/MetadataTypeReference.cs | 259 +++++++++++++++ .../Implementation/TypeSpecification.cs | 168 +--------- .../TypeSystem/MetadataLoader.cs | 295 ++++++++---------- .../SpecializingDecompilerTypeSystem.cs | 41 ++- .../TypeSystem/TypeProvider.cs | 166 ++++++++++ ILSpy/Languages/CSharpLanguage.cs | 39 +-- .../Analyzer/ScopedWhereUsedAnalyzer.cs | 2 +- 21 files changed, 942 insertions(+), 480 deletions(-) create mode 100644 ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeReference.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 53e57c88b..150ae6665 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -985,7 +985,8 @@ namespace ICSharpCode.Decompiler.CSharp // Field data as specified in II.16.3.2 of ECMA-335 6th edition: // .data I_X = int32(123) // .field public static int32 _x at I_X - var message = string.Format(" Not supported: data({0}) ", BitConverter.ToString(fieldDefinition.GetInitialValue(typeSystem.ModuleDefinition.Reader)).Replace('-', ' ')); + var initVal = fieldDefinition.GetInitialValue(typeSystem.ModuleDefinition.Reader); + var message = string.Format(" Not supported: data({0}) ", BitConverter.ToString(initVal.ReadBytes(initVal.RemainingBytes)).Replace('-', ' ')); ((FieldDeclaration)fieldDecl).Variables.Single().AddChild(new Comment(message, CommentType.MultiLine), Roles.Comment); } return fieldDecl; diff --git a/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs b/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs index f50b32dca..eab00bde4 100644 --- a/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs +++ b/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs @@ -156,9 +156,9 @@ namespace ICSharpCode.Decompiler.CSharp var metadata = reader.GetMetadataReader(); if (!method.LocalSignature.IsNil) { - var localSignature = metadata.GetStandaloneSignature(method.LocalSignature).DecodeLocalSignature(TypeReferenceSignatureDecoder.Instance, default); + var localSignature = typeSystem.DecodeLocalSignature(method.LocalSignature); foreach (var type in localSignature) - CollectNamespacesForTypeReference(typeSystem.ResolveFromSignature(type), namespaces); + CollectNamespacesForTypeReference(type, namespaces); } foreach (var region in method.ExceptionRegions) { diff --git a/ICSharpCode.Decompiler/Documentation/XmlDocKeyProvider.cs b/ICSharpCode.Decompiler/Documentation/XmlDocKeyProvider.cs index a39c030ef..e3bb19e53 100644 --- a/ICSharpCode.Decompiler/Documentation/XmlDocKeyProvider.cs +++ b/ICSharpCode.Decompiler/Documentation/XmlDocKeyProvider.cs @@ -198,12 +198,12 @@ namespace ICSharpCode.Decompiler.Documentation public string GetModifiedType(string modifier, string unmodifiedType, bool isRequired) { - throw new NotImplementedException(); + return unmodifiedType; } public string GetPinnedType(string elementType) { - throw new NotImplementedException(); + return elementType; } public string GetPointerType(string elementType) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index e87d87739..bfa6aa47d 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -338,11 +338,14 @@ + + + diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index e45d0695a..273a74c89 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -54,7 +54,6 @@ namespace ICSharpCode.Decompiler.IL IMethod method; MethodBodyBlock body; Metadata.IDebugInfoProvider debugInfo; - MethodSignature methodSignature; StackType methodReturnStackType; BlobReader reader; ImmutableStack currentStack; @@ -78,14 +77,13 @@ namespace ICSharpCode.Decompiler.IL this.metadata = module.Metadata; this.method = typeSystem.ResolveAsMethod(methodDefinitionHandle); var methodDefinition = metadata.GetMethodDefinition(methodDefinitionHandle); - this.methodSignature = methodDefinition.DecodeSignature(TypeSystem.Implementation.TypeReferenceSignatureDecoder.Instance, default); this.body = body; this.reader = body.GetILReader(); this.debugInfo = module.DebugInfo; this.currentStack = ImmutableStack.Empty; this.unionFind = new UnionFind(); this.stackMismatchPairs = new List<(ILVariable, ILVariable)>(); - this.methodReturnStackType = typeSystem.ResolveFromSignature(TypeSystem.Implementation.DynamicTypeReference.Create(methodSignature.ReturnType, methodDefinition.GetCustomAttributes(), metadata)).GetStackType(); + this.methodReturnStackType = method.ReturnType.GetStackType(); InitParameterVariables(); localVariables = InitLocalVariables(); if (body.LocalVariablesInitialized) { @@ -126,12 +124,10 @@ namespace ICSharpCode.Decompiler.IL ILVariable[] InitLocalVariables() { if (body.LocalSignature.IsNil) return Empty.Array; - var standaloneSignature = metadata.GetStandaloneSignature(body.LocalSignature); - Debug.Assert(standaloneSignature.GetKind() == StandaloneSignatureKind.LocalVariables); - var variableTypes = standaloneSignature.DecodeLocalSignature(TypeSystem.Implementation.TypeReferenceSignatureDecoder.Instance, default); + var variableTypes = typeSystem.DecodeLocalSignature(body.LocalSignature); var localVariables = new ILVariable[variableTypes.Length]; foreach (var (index, type) in variableTypes.WithIndex()) { - localVariables[index] = CreateILVariable(index, typeSystem.ResolveFromSignature(type)); + localVariables[index] = CreateILVariable(index, type); } return localVariables; } @@ -144,14 +140,16 @@ namespace ICSharpCode.Decompiler.IL if (method.Parameters.LastOrDefault()?.Type == SpecialType.ArgList) popCount--; parameterVariables = new ILVariable[popCount]; - int paramIndex = 0; int offset = 0; - if (methodSignature.Header.IsInstance) { + int paramIndex = 0; + int offset = 0; + if (!method.IsStatic) { offset = 1; parameterVariables[paramIndex++] = CreateILVariable(-1, method.DeclaringType, "this"); } while (paramIndex < parameterVariables.Length) { - var type = typeSystem.ResolveFromSignature(methodSignature.ParameterTypes[paramIndex - offset]); - parameterVariables[paramIndex] = CreateILVariable(paramIndex - offset, type, method.Parameters[paramIndex - offset].Name); + IType type = method.Parameters[paramIndex - offset].Type; + string name = method.Parameters[paramIndex - offset].Name; + parameterVariables[paramIndex] = CreateILVariable(paramIndex - offset, type, name); paramIndex++; } Debug.Assert(paramIndex == parameterVariables.Length); @@ -181,16 +179,16 @@ namespace ICSharpCode.Decompiler.IL ILVariable CreateILVariable(int index, IType parameterType, string name) { - ITypeDefinition def = parameterType.GetDefinition(); - if (def != null && index < 0 && def.IsReferenceType == false) { - parameterType = new ByReferenceType(parameterType); - } Debug.Assert(!parameterType.IsUnbound()); if (parameterType.IsUnbound()) { // parameter types should not be unbound, the only known cause for these is a Cecil bug: Debug.Assert(index < 0); // cecil bug occurs only for "this" parameterType = new ParameterizedType(parameterType.GetDefinition(), parameterType.TypeArguments); } + ITypeDefinition def = parameterType.GetDefinition(); + if (def != null && index < 0 && def.IsReferenceType == false) { + parameterType = new ByReferenceType(parameterType); + } var ilVar = new ILVariable(VariableKind.Parameter, parameterType, index); Debug.Assert(ilVar.StoreCount == 1); // count the initial store when the method is called with an argument if (index < 0) @@ -1289,21 +1287,18 @@ namespace ICSharpCode.Decompiler.IL ILInstruction DecodeCallIndirect() { - var standaloneSignature = metadata.GetStandaloneSignature((StandaloneSignatureHandle)ReadAndDecodeMetadataToken()); - Debug.Assert(standaloneSignature.GetKind() == StandaloneSignatureKind.Method); - var signature = standaloneSignature.DecodeMethodSignature(TypeSystem.Implementation.TypeReferenceSignatureDecoder.Instance, default); + var signatureHandle = (StandaloneSignatureHandle)ReadAndDecodeMetadataToken(); + var signature = typeSystem.DecodeMethodSignature(signatureHandle); var functionPointer = Pop(StackType.I); Debug.Assert(!signature.Header.IsInstance); - var parameterTypes = new IType[signature.ParameterTypes.Length]; - var arguments = new ILInstruction[parameterTypes.Length]; + var arguments = new ILInstruction[signature.ParameterTypes.Length]; for (int i = signature.ParameterTypes.Length - 1; i >= 0; i--) { - parameterTypes[i] = typeSystem.ResolveFromSignature(signature.ParameterTypes[i]); - arguments[i] = Pop(parameterTypes[i].GetStackType()); + arguments[i] = Pop(signature.ParameterTypes[i].GetStackType()); } var call = new CallIndirect( signature.Header.CallingConvention, - typeSystem.ResolveFromSignature(signature.ReturnType), - parameterTypes.ToImmutableArray(), + signature.ReturnType, + signature.ParameterTypes, arguments, functionPointer ); diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs index b64697f67..d7bc19fc8 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs @@ -19,8 +19,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection.Metadata; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms @@ -337,7 +337,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (MatchInitializeArrayCall(body.Instructions[pos], out var method, out var v2, out var field) && array == v2) { if (field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA)) { var valuesList = new List(); - if (DecodeArrayInitializer(arrayType, array, field.GetInitialValue(context.TypeSystem.ModuleDefinition.Reader), arrayLength, valuesList)) { + var initialValue = field.GetInitialValue(context.TypeSystem.ModuleDefinition.Reader); + if (DecodeArrayInitializer(arrayType, array, initialValue, arrayLength, valuesList)) { values = valuesList.ToArray(); foundPos = pos; return true; @@ -349,30 +350,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } - static bool DecodeArrayInitializer(IType type, ILVariable array, byte[] initialValue, int[] arrayLength, List output) + static bool DecodeArrayInitializer(IType type, ILVariable array, BlobReader initialValue, int[] arrayLength, List output) { TypeCode typeCode = ReflectionHelper.GetTypeCode(type); switch (typeCode) { case TypeCode.Boolean: case TypeCode.Byte: - return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => new LdcI4((int)d[i])); + return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadByte())); case TypeCode.SByte: - return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => new LdcI4((int)unchecked((sbyte)d[i]))); + return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadSByte())); case TypeCode.Int16: - return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => new LdcI4((int)BitConverter.ToInt16(d, i))); + return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadInt16())); case TypeCode.Char: case TypeCode.UInt16: - return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => new LdcI4((int)BitConverter.ToUInt16(d, i))); + return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadUInt16())); case TypeCode.Int32: case TypeCode.UInt32: - return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => new LdcI4(BitConverter.ToInt32(d, i))); + return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadInt32())); case TypeCode.Int64: case TypeCode.UInt64: - return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => new LdcI8(BitConverter.ToInt64(d, i))); + return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI8(r.ReadInt64())); case TypeCode.Single: - return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => new LdcF4(BitConverter.ToSingle(d, i))); + return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcF4(r.ReadSingle())); case TypeCode.Double: - return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => new LdcF8(BitConverter.ToDouble(d, i))); + return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcF8(r.ReadDouble())); case TypeCode.Object: case TypeCode.Empty: var typeDef = type.GetDefinition(); @@ -384,15 +385,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - static bool DecodeArrayInitializer(byte[] initialValue, ILVariable array, int[] arrayLength, List output, TypeCode elementType, IType type, Func decoder) + delegate ILInstruction ValueDecoder(ref BlobReader reader); + + static bool DecodeArrayInitializer(BlobReader initialValue, ILVariable array, int[] arrayLength, + List output, TypeCode elementType, IType type, ValueDecoder decoder) { int elementSize = ElementSizeOf(elementType); var totalLength = arrayLength.Aggregate(1, (t, l) => t * l); - if (initialValue.Length < (totalLength * elementSize)) + if (initialValue.RemainingBytes < (totalLength * elementSize)) return false; for (int i = 0; i < totalLength; i++) { - output.Add(decoder(initialValue, i * elementSize)); + output.Add(decoder(ref initialValue)); int next = i; for (int j = arrayLength.Length - 1; j >= 0; j--) { output.Add(new LdcI4(next % arrayLength[j])); diff --git a/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs b/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs index b6fbf6df3..a178c1b77 100644 --- a/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs +++ b/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs @@ -170,7 +170,16 @@ namespace ICSharpCode.Decompiler.Metadata return type.GetDefaultMemberName(reader, out var attr); } - static readonly ITypeResolveContext minimalCorlibContext = new SimpleTypeResolveContext(MinimalCorlib.Instance.CreateCompilation()); + internal static readonly TypeProvider minimalCorlibTypeProvider = + new TypeProvider(MinimalCorlib.Instance.CreateCompilation().MainAssembly); + + /// + /// An attribute type provider that can be used to decode attribute signatures + /// that only mention built-in types. + /// + public static ICustomAttributeTypeProvider MinimalAttributeTypeProvider { + get => minimalCorlibTypeProvider; + } public static string GetDefaultMemberName(this TypeDefinitionHandle type, MetadataReader reader, out CustomAttributeHandle defaultMemberAttribute) { @@ -178,8 +187,8 @@ namespace ICSharpCode.Decompiler.Metadata foreach (var h in td.GetCustomAttributes()) { var ca = reader.GetCustomAttribute(h); - if (ca.GetAttributeType(reader).ToString() == "System.Reflection.DefaultMemberAttribute") { - var decodedValues = ca.DecodeValue(new TypeSystemAttributeTypeProvider(minimalCorlibContext)); + if (ca.GetAttributeType(reader).IsTopLevelType(reader, "System.Reflection", "DefaultMemberAttribute")) { + var decodedValues = ca.DecodeValue(minimalCorlibTypeProvider); if (decodedValues.FixedArguments.Length == 1 && decodedValues.FixedArguments[0].Value is string value) { defaultMemberAttribute = h; return value; @@ -190,7 +199,7 @@ namespace ICSharpCode.Decompiler.Metadata defaultMemberAttribute = default(CustomAttributeHandle); return null; } - + public static bool HasOverrides(this MethodDefinitionHandle handle, MetadataReader reader) { for (int row = 1; row <= reader.GetTableRowCount(TableIndex.MethodImpl); row++) { @@ -214,7 +223,7 @@ namespace ICSharpCode.Decompiler.Metadata return false; } - public static PrimitiveTypeCode ToPrimtiveTypeCode(this KnownTypeCode typeCode) + public static PrimitiveTypeCode ToPrimitiveTypeCode(this KnownTypeCode typeCode) { switch (typeCode) { case KnownTypeCode.Object: diff --git a/ICSharpCode.Decompiler/Metadata/MetadataResolver.cs b/ICSharpCode.Decompiler/Metadata/MetadataResolver.cs index 3efc4c159..81bf49425 100644 --- a/ICSharpCode.Decompiler/Metadata/MetadataResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/MetadataResolver.cs @@ -360,7 +360,7 @@ namespace ICSharpCode.Decompiler.Metadata for (; i < totalParameterCount; i++) { if (!IsSameCompressedInteger(ref a, ref b, out typeCode)) return false; - // + // varargs sentinel if (typeCode == 65) break; if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode)) diff --git a/ICSharpCode.Decompiler/SRMExtensions.cs b/ICSharpCode.Decompiler/SRMExtensions.cs index a971e4731..11992b89a 100644 --- a/ICSharpCode.Decompiler/SRMExtensions.cs +++ b/ICSharpCode.Decompiler/SRMExtensions.cs @@ -183,6 +183,16 @@ namespace ICSharpCode.Decompiler } } + public static bool IsTopLevelType(this EntityHandle handle, MetadataReader reader, string namespaceName, string name, int typeParameterCount = 0) + { + return GetFullTypeName(handle, reader) == new TopLevelTypeName(namespaceName, name, typeParameterCount); + } + + public static bool IsAttributeType(this CustomAttribute attr, MetadataReader reader, string namespaceName, string name) + { + return attr.GetAttributeType(reader).IsTopLevelType(reader, namespaceName, name); + } + public static FullTypeName GetFullTypeName(this TypeSpecificationHandle handle, MetadataReader reader) { if (handle.IsNil) @@ -362,24 +372,13 @@ namespace ICSharpCode.Decompiler } #endregion - public static byte[] GetInitialValue(this FieldDefinition field, PEReader pefile) + public static unsafe BlobReader GetInitialValue(this FieldDefinition field, PEReader pefile) { if (!field.HasFlag(FieldAttributes.HasFieldRVA) || field.GetRelativeVirtualAddress() == 0) - return Empty.Array; + return default; int rva = field.GetRelativeVirtualAddress(); int size = field.DecodeSignature(new FieldValueSizeDecoder(), default); - var headers = pefile.PEHeaders; - int index = headers.GetContainingSectionIndex(rva); - var sectionHeader = headers.SectionHeaders[index]; - var sectionData = pefile.GetEntireImage(); - int totalOffset = rva + sectionHeader.PointerToRawData - sectionHeader.VirtualAddress; - var reader = sectionData.GetReader(); - reader.Offset += totalOffset; - int offset = field.GetOffset(); - if (offset > 0) - reader.Offset += offset; - return reader.ReadBytes(size); - + return pefile.GetSectionData(rva).GetReader(0, size); } class FieldValueSizeDecoder : ISignatureTypeProvider @@ -387,14 +386,14 @@ namespace ICSharpCode.Decompiler public int GetArrayType(int elementType, ArrayShape shape) => elementType; public int GetByReferenceType(int elementType) => elementType; public int GetFunctionPointerType(MethodSignature signature) => IntPtr.Size; - public int GetGenericInstantiation(int genericType, ImmutableArray typeArguments) => throw new NotSupportedException(); - public int GetGenericMethodParameter(Unit genericContext, int index) => throw new NotSupportedException(); - public int GetGenericTypeParameter(Unit genericContext, int index) => throw new NotSupportedException(); + public int GetGenericInstantiation(int genericType, ImmutableArray typeArguments) => genericType; + public int GetGenericMethodParameter(Unit genericContext, int index) => 0; + public int GetGenericTypeParameter(Unit genericContext, int index) => 0; public int GetModifiedType(int modifier, int unmodifiedType, bool isRequired) => unmodifiedType; public int GetPinnedType(int elementType) => elementType; public int GetPointerType(int elementType) => elementType; - public int GetPrimitiveType(PrimitiveTypeCode typeCode) + public int GetPrimitiveType(PrimitiveTypeCode typeCode) { switch (typeCode) { case PrimitiveTypeCode.Boolean: @@ -421,7 +420,7 @@ namespace ICSharpCode.Decompiler } } - public int GetSZArrayType(int elementType) => throw new NotSupportedException(); + public int GetSZArrayType(int elementType) => 0; public int GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) { @@ -431,12 +430,12 @@ namespace ICSharpCode.Decompiler public int GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) { - throw new NotImplementedException(); + return 0; } public int GetTypeFromSpecification(MetadataReader reader, Unit genericContext, TypeSpecificationHandle handle, byte rawTypeKind) { - throw new NotImplementedException(); + return 0; } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs new file mode 100644 index 000000000..bdf5b423c --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs @@ -0,0 +1,202 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.Decompiler.Util; +using SRM = System.Reflection.Metadata; + +namespace ICSharpCode.Decompiler.TypeSystem +{ + /// + /// Options for converting builtin + /// + [Flags] + enum TypeAttributeOptions + { + None = 0, + Dynamic = 1, + Tuple = 2, + Default = Dynamic | Tuple + } + + /// + /// Introduces 'dynamic' and tuple types based on attribute values. + /// + class ApplyAttributeTypeVisitor : TypeVisitor + { + public static IType ApplyAttributesToType( + IType inputType, + ICompilation compilation, + SRM.CustomAttributeHandleCollection? attributes, + SRM.MetadataReader metadata, + TypeAttributeOptions options) + { + if (options == TypeAttributeOptions.None) { + return inputType; + } + bool hasDynamicAttribute = false; + bool[] dynamicAttributeData = null; + bool useTupleTypes = (options & TypeAttributeOptions.Tuple) != 0; + string[] tupleElementNames = null; + if (attributes != null) { + foreach (var attrHandle in attributes.Value) { + var attr = metadata.GetCustomAttribute(attrHandle); + var attrType = attr.GetAttributeType(metadata); + if ((options & TypeAttributeOptions.Dynamic) != 0 + && attrType.IsTopLevelType(metadata, "System.Runtime.CompilerServices", "DynamicAttribute")) { + hasDynamicAttribute = true; + var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider); + if (ctor.FixedArguments.Length == 1) { + var arg = ctor.FixedArguments[0]; + if (arg.Value is ImmutableArray> values + && values.All(v => v.Value is bool)) { + dynamicAttributeData = values.SelectArray(v => (bool)v.Value); + } + } + } else if (useTupleTypes && attrType.IsTopLevelType(metadata, "System.Runtime.CompilerServices", "TupleElementNamesAttribute")) { + var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider); + if (ctor.FixedArguments.Length == 1) { + var arg = ctor.FixedArguments[0]; + if (arg.Value is ImmutableArray> values + && values.All(v => v.Value is string || v.Value == null)) { + tupleElementNames = values.SelectArray(v => (string)v.Value); + } + } + } + } + } + if (hasDynamicAttribute || useTupleTypes) { + return inputType.AcceptVisitor(new ApplyAttributeTypeVisitor( + compilation, hasDynamicAttribute, dynamicAttributeData, useTupleTypes, tupleElementNames + )); + } else { + return inputType; + } + } + + readonly ICompilation compilation; + readonly bool hasDynamicAttribute; + readonly bool[] dynamicAttributeData; + readonly bool useTupleTypes; + readonly string[] tupleElementNames; + int dynamicTypeIndex = 0; + int tupleTypeIndex = 0; + + public ApplyAttributeTypeVisitor(ICompilation compilation, bool hasDynamicAttribute, bool[] dynamicAttributeData, bool useTupleTypes, string[] tupleElementNames) + { + this.compilation = compilation ?? throw new ArgumentNullException(nameof(compilation)); + this.hasDynamicAttribute = hasDynamicAttribute; + this.dynamicAttributeData = dynamicAttributeData; + this.useTupleTypes = useTupleTypes; + this.tupleElementNames = tupleElementNames; + } + + public override IType VisitPointerType(PointerType type) + { + dynamicTypeIndex++; + return base.VisitPointerType(type); + } + + public override IType VisitArrayType(ArrayType type) + { + dynamicTypeIndex++; + return base.VisitArrayType(type); + } + + public override IType VisitByReferenceType(ByReferenceType type) + { + dynamicTypeIndex++; + return base.VisitByReferenceType(type); + } + + public override IType VisitParameterizedType(ParameterizedType type) + { + if (useTupleTypes && TupleType.IsTupleCompatible(type, out int tupleCardinality)) { + if (tupleCardinality > 1) { + var valueTupleAssembly = type.GetDefinition()?.ParentAssembly; + ImmutableArray elementNames = default; + if (tupleElementNames != null && tupleTypeIndex < tupleElementNames.Length) { + string[] extractedValues = new string[tupleCardinality]; + Array.Copy(tupleElementNames, tupleTypeIndex, extractedValues, 0, + Math.Min(tupleCardinality, tupleElementNames.Length - tupleTypeIndex)); + elementNames = ImmutableArray.CreateRange(extractedValues); + } + tupleTypeIndex += tupleCardinality; + var elementTypes = ImmutableArray.CreateBuilder(tupleCardinality); + do { + int normalArgCount = Math.Min(type.TypeArguments.Count, TupleType.RestPosition - 1); + for (int i = 0; i < normalArgCount; i++) { + dynamicTypeIndex++; + elementTypes.Add(type.TypeArguments[i].AcceptVisitor(this)); + } + if (type.TypeArguments.Count == TupleType.RestPosition) { + type = type.TypeArguments.Last() as ParameterizedType; + dynamicTypeIndex++; + if (type != null && TupleType.IsTupleCompatible(type, out int nestedCardinality)) { + tupleTypeIndex += nestedCardinality; + } else { + Debug.Fail("TRest should be another value tuple"); + type = null; + } + } else { + type = null; + } + } while (type != null); + Debug.Assert(elementTypes.Count == tupleCardinality); + return new TupleType( + compilation, + elementTypes.ToImmutable(), + elementNames, + valueTupleAssembly + ); + } else { + // C# doesn't have syntax for tuples of cardinality <= 1 + tupleTypeIndex += tupleCardinality; + } + } + // Visit generic type and type arguments. + // Like base implementation, except that it increments typeIndex. + var genericType = type.GenericType.AcceptVisitor(this); + bool changed = type.GenericType != genericType; + var arguments = new IType[type.TypeArguments.Count]; + for (int i = 0; i < type.TypeArguments.Count; i++) { + dynamicTypeIndex++; + arguments[i] = type.TypeArguments[i].AcceptVisitor(this); + changed = changed || arguments[i] != type.TypeArguments[i]; + } + if (!changed) + return type; + return new ParameterizedType(genericType, arguments); + } + + public override IType VisitTypeDefinition(ITypeDefinition type) + { + if (type.KnownTypeCode == KnownTypeCode.Object && hasDynamicAttribute) { + if (dynamicAttributeData == null || dynamicTypeIndex >= dynamicAttributeData.Length) + return SpecialType.Dynamic; + if (dynamicAttributeData[dynamicTypeIndex]) + return SpecialType.Dynamic; + return type; + } + return type; + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index fe65b2c92..09aa9b294 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -7,6 +7,7 @@ using ICSharpCode.Decompiler.Util; using static ICSharpCode.Decompiler.Metadata.MetadataExtensions; using System.Diagnostics; +using System.Collections.Immutable; namespace ICSharpCode.Decompiler.TypeSystem { @@ -21,12 +22,7 @@ namespace ICSharpCode.Decompiler.TypeSystem readonly Metadata.PEFile moduleDefinition; readonly ICompilation compilation; readonly ITypeResolveContext context; - - /// - /// CecilLoader used for converting cecil type references to ITypeReference. - /// May only be accessed within lock(typeReferenceCecilLoader). - /// - readonly MetadataLoader typeReferenceCecilLoader; + readonly TypeAttributeOptions typeAttributeOptions; Dictionary fieldLookupCache = new Dictionary(); Dictionary propertyLookupCache = new Dictionary(); @@ -44,19 +40,18 @@ namespace ICSharpCode.Decompiler.TypeSystem if (settings == null) throw new ArgumentNullException(nameof(settings)); this.moduleDefinition = moduleDefinition; - typeReferenceCecilLoader = new MetadataLoader { - UseDynamicType = settings.Dynamic, - UseTupleTypes = settings.TupleTypes, - }; - MetadataLoader cecilLoader = new MetadataLoader { + typeAttributeOptions = TypeAttributeOptions.None; + if (settings.Dynamic) + typeAttributeOptions |= TypeAttributeOptions.Dynamic; + if (settings.TupleTypes) + typeAttributeOptions |= TypeAttributeOptions.Tuple; + MetadataLoader loader = new MetadataLoader { IncludeInternalMembers = true, - LazyLoad = true, ShortenInterfaceImplNames = false, UseDynamicType = settings.Dynamic, UseTupleTypes = settings.TupleTypes, }; - typeReferenceCecilLoader.SetCurrentModule(moduleDefinition); - IUnresolvedAssembly mainAssembly = cecilLoader.LoadModule(moduleDefinition); + IUnresolvedAssembly mainAssembly = loader.LoadModule(moduleDefinition); // Load referenced assemblies and type-forwarder references. // This is necessary to make .NET Core/PCL binaries work better. var referencedAssemblies = new List(); @@ -68,7 +63,7 @@ namespace ICSharpCode.Decompiler.TypeSystem continue; var asm = moduleDefinition.AssemblyResolver.Resolve(asmRef); if (asm != null) { - IUnresolvedAssembly unresolvedAsm = cecilLoader.LoadModule(asm); + IUnresolvedAssembly unresolvedAsm = loader.LoadModule(asm); referencedAssemblies.Add(unresolvedAsm); moduleLookup.Add(unresolvedAsm, asm); var metadata = asm.Metadata; @@ -111,12 +106,7 @@ namespace ICSharpCode.Decompiler.TypeSystem return null; return file; } - - public IType ResolveFromSignature(ITypeReference typeReference) - { - return typeReference.Resolve(context); - } - + public IMember ResolveAsMember(SRM.EntityHandle memberReference) { switch (memberReference.Kind) { @@ -147,14 +137,37 @@ namespace ICSharpCode.Decompiler.TypeSystem #region Resolve Type public IType ResolveAsType(SRM.EntityHandle typeReference) { - if (typeReference.IsNil) - return SpecialType.UnknownType; - ITypeReference typeRef; - lock (typeReferenceCecilLoader) - typeRef = typeReferenceCecilLoader.ReadTypeReference(typeReference); - return typeRef.Resolve(context); + return MetadataTypeReference.Resolve( + typeReference, + moduleDefinition.Metadata, + context, + typeAttributes: null, + typeAttributeOptions + ); } #endregion + + public SRM.MethodSignature DecodeMethodSignature(SRM.StandaloneSignatureHandle handle) + { + var standaloneSignature = moduleDefinition.Metadata.GetStandaloneSignature(handle); + if (standaloneSignature.GetKind() != SRM.StandaloneSignatureKind.Method) + throw new InvalidOperationException("Expected Method signature"); + return standaloneSignature.DecodeMethodSignature( + new TypeProvider(compilation.MainAssembly), + context + ); + } + + public ImmutableArray DecodeLocalSignature(SRM.StandaloneSignatureHandle handle) + { + var standaloneSignature = moduleDefinition.Metadata.GetStandaloneSignature(handle); + if (standaloneSignature.GetKind() != SRM.StandaloneSignatureKind.LocalVariables) + throw new InvalidOperationException("Expected Local signature"); + return standaloneSignature.DecodeLocalSignature( + new TypeProvider(compilation.MainAssembly), + context + ); + } #region Resolve Field public IField ResolveAsField(SRM.EntityHandle fieldReference) @@ -171,9 +184,10 @@ namespace ICSharpCode.Decompiler.TypeSystem ITypeReference returnType; switch (fieldReference.Kind) { case SRM.HandleKind.FieldDefinition: - var fieldDef = metadata.GetFieldDefinition((SRM.FieldDefinitionHandle)fieldReference); + var fieldDefHandle = (SRM.FieldDefinitionHandle)fieldReference; + var fieldDef = metadata.GetFieldDefinition(fieldDefHandle); declaringType = ResolveAsType(fieldDef.GetDeclaringType()); - returnType = DynamicTypeReference.Create(fieldDef.DecodeSignature(TypeReferenceSignatureDecoder.Instance, default), fieldDef.GetCustomAttributes(), metadata); + returnType = new FieldTypeReference(fieldDefHandle, metadata, typeAttributeOptions); var declaringTypeDefinition = declaringType.GetDefinition(); if (declaringTypeDefinition == null) field = CreateFakeField(declaringType, metadata.GetString(fieldDef.Name), returnType); @@ -412,14 +426,12 @@ namespace ICSharpCode.Decompiler.TypeSystem m.ReturnType = signature.ReturnType; m.IsStatic = !signature.Header.IsInstance; - lock (typeReferenceCecilLoader) { - var metadata = moduleDefinition.Metadata; - for (int i = 0; i < signature.GenericParameterCount; i++) { - m.TypeParameters.Add(new DefaultUnresolvedTypeParameter(SymbolKind.Method, i, "")); - } - for (int i = 0; i < signature.ParameterTypes.Length; i++) { - m.Parameters.Add(new DefaultUnresolvedParameter(signature.ParameterTypes[i], "")); - } + var metadata = moduleDefinition.Metadata; + for (int i = 0; i < signature.GenericParameterCount; i++) { + m.TypeParameters.Add(new DefaultUnresolvedTypeParameter(SymbolKind.Method, i, "")); + } + for (int i = 0; i < signature.ParameterTypes.Length; i++) { + m.Parameters.Add(new DefaultUnresolvedParameter(signature.ParameterTypes[i], "")); } return new ResolvedFakeMethod(m, context.WithCurrentTypeDefinition(declaringType.GetDefinition()), declaringType); } diff --git a/ICSharpCode.Decompiler/TypeSystem/IAssembly.cs b/ICSharpCode.Decompiler/TypeSystem/IAssembly.cs index ab259a7e8..30975b443 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IAssembly.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IAssembly.cs @@ -122,6 +122,6 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// Gets the type definition from the metadata token, or null if not found. /// - ITypeDefinition ResolveTypeDefToken(EntityHandle token); + ITypeDefinition ResolveTypeDefToken(TypeDefinitionHandle token); } } diff --git a/ICSharpCode.Decompiler/TypeSystem/IDecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/IDecompilerTypeSystem.cs index f1865c4f5..714a500ce 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IDecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IDecompilerTypeSystem.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Collections.Immutable; using System.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; @@ -28,7 +29,9 @@ namespace ICSharpCode.Decompiler.TypeSystem { ICompilation Compilation { get; } - IType ResolveFromSignature(ITypeReference typeReference); + MethodSignature DecodeMethodSignature(StandaloneSignatureHandle standaloneSignatureHandle); + ImmutableArray DecodeLocalSignature(StandaloneSignatureHandle standaloneSignatureHandle); + IType ResolveAsType(EntityHandle typeReference); IField ResolveAsField(EntityHandle fieldReference); IMethod ResolveAsMethod(EntityHandle methodReference); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs index 12ae00679..151f75da0 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs @@ -297,10 +297,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return lookup; } - internal IUnresolvedTypeDefinition GetTypeDefByToken(System.Reflection.Metadata.EntityHandle token) + internal IUnresolvedTypeDefinition GetTypeDefByToken(System.Reflection.Metadata.TypeDefinitionHandle token) { - if (token.Kind != System.Reflection.Metadata.HandleKind.TypeDefinition) - throw new ArgumentException("Token must be typedef-token."); var lookup = LazyInit.VolatileRead(ref allTypesByMetadata); if (lookup == null) { lookup = LazyInit.GetOrSet(ref allTypesByMetadata, BuildMetadataLookup()); @@ -445,7 +443,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } - public ITypeDefinition ResolveTypeDefToken(System.Reflection.Metadata.EntityHandle token) + public ITypeDefinition ResolveTypeDefToken(System.Reflection.Metadata.TypeDefinitionHandle token) { var td = unresolvedAssembly.GetTypeDefByToken(token); if (td != null) diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeReference.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeReference.cs new file mode 100644 index 000000000..0af15894a --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeReference.cs @@ -0,0 +1,259 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using SRM = System.Reflection.Metadata; +using System.Diagnostics; +using System; +using ICSharpCode.Decompiler.Util; +using System.Linq; +using System.Collections.Immutable; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + sealed class MetadataTypeReference : ITypeReference + { + readonly SRM.EntityHandle type; + readonly SRM.MetadataReader metadata; + readonly SRM.CustomAttributeHandleCollection? typeAttributes; + readonly TypeAttributeOptions attributeOptions; + + public MetadataTypeReference(SRM.EntityHandle type, + SRM.MetadataReader metadata, + SRM.CustomAttributeHandleCollection? typeAttributes = null, + TypeAttributeOptions attributeOptions = TypeAttributeOptions.Default) + { + this.type = type; + this.metadata = metadata; + this.typeAttributes = typeAttributes; + this.attributeOptions = attributeOptions; + } + + public IType Resolve(ITypeResolveContext context) + { + return Resolve(type, metadata, context, + typeAttributes, attributeOptions); + } + + public static IType Resolve(SRM.EntityHandle type, + SRM.MetadataReader metadata, + ITypeResolveContext context, + SRM.CustomAttributeHandleCollection? typeAttributes = null, + TypeAttributeOptions attributeOptions = TypeAttributeOptions.Default) + { + if (type.IsNil) + return SpecialType.UnknownType; + var tp = new TypeProvider(context.CurrentAssembly); + IType ty; + switch (type.Kind) { + case SRM.HandleKind.TypeDefinition: + ty = tp.GetTypeFromDefinition(metadata, (SRM.TypeDefinitionHandle)type, 0); + break; + case SRM.HandleKind.TypeReference: + ty = tp.GetTypeFromReference(metadata, (SRM.TypeReferenceHandle)type, 0); + break; + case SRM.HandleKind.TypeSpecification: + var typeSpec = metadata.GetTypeSpecification((SRM.TypeSpecificationHandle)type); + ty = typeSpec.DecodeSignature(tp, context); + break; + default: + Debug.Fail("Not a type handle"); + ty = SpecialType.UnknownType; + break; + } + ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, context.Compilation, + typeAttributes, metadata, attributeOptions); + return ty; + } + } + + sealed class FieldTypeReference : ITypeReference + { + readonly SRM.FieldDefinitionHandle fieldHandle; + readonly SRM.MetadataReader metadata; + readonly TypeAttributeOptions attributeOptions; + + public FieldTypeReference(SRM.FieldDefinitionHandle fieldHandle, + SRM.MetadataReader metadata, + TypeAttributeOptions attributeOptions = TypeAttributeOptions.Default) + { + this.fieldHandle = fieldHandle; + this.metadata = metadata; + this.attributeOptions = attributeOptions; + } + + IType ITypeReference.Resolve(ITypeResolveContext context) + { + return Resolve(fieldHandle, metadata, context, attributeOptions); + } + + public static IType Resolve(SRM.FieldDefinitionHandle fieldHandle, + SRM.MetadataReader metadata, + ITypeResolveContext context, + TypeAttributeOptions attributeOptions = TypeAttributeOptions.Default) + { + var fieldDef = metadata.GetFieldDefinition(fieldHandle); + IType ty = fieldDef.DecodeSignature(new TypeProvider(context.CurrentAssembly), context); + ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, context.Compilation, + fieldDef.GetCustomAttributes(), metadata, attributeOptions); + return ty; + } + } + + /// + /// Represents an unresolved method signature. + /// + sealed class UnresolvedMethodSignature + { + readonly SRM.EntityHandle handle; + readonly SRM.MetadataReader metadata; + readonly TypeAttributeOptions attributeOptions; + + public UnresolvedMethodSignature(SRM.MethodDefinitionHandle handle, SRM.MetadataReader metadata, + TypeAttributeOptions attributeOptions = TypeAttributeOptions.Default) + { + this.handle = handle; + this.metadata = metadata; + this.attributeOptions = attributeOptions; + } + + public UnresolvedMethodSignature(SRM.PropertyDefinitionHandle handle, SRM.MetadataReader metadata, + TypeAttributeOptions attributeOptions = TypeAttributeOptions.Default) + { + this.handle = handle; + this.metadata = metadata; + this.attributeOptions = attributeOptions; + } + + public SRM.MethodSignature Resolve(ITypeResolveContext context) + { + return (SRM.MethodSignature)context.Compilation.CacheManager.GetOrAddShared( + this, key => { + Debug.Assert(key == this); + switch (handle.Kind) { + case SRM.HandleKind.MethodDefinition: + return GetSignature( + metadata.GetMethodDefinition((SRM.MethodDefinitionHandle)handle), + metadata, context + ); + case SRM.HandleKind.PropertyDefinition: + return GetSignature( + metadata.GetPropertyDefinition((SRM.PropertyDefinitionHandle)handle), + metadata, context + ); + default: + throw new InvalidOperationException(); + } + } + ); + } + + public static SRM.MethodSignature GetSignature(SRM.MethodDefinition methodDef, + SRM.MetadataReader metadata, ITypeResolveContext context, + TypeAttributeOptions attributeOptions = TypeAttributeOptions.Default) + { + var typeProvider = new TypeProvider(context.CurrentAssembly); + var signature = methodDef.DecodeSignature(typeProvider, context); + return ApplyAttributes(signature, methodDef.GetParameters(), context.Compilation, metadata, attributeOptions); + } + + public static SRM.MethodSignature GetSignature(SRM.PropertyDefinition propertyDef, + SRM.MetadataReader metadata, ITypeResolveContext context, + TypeAttributeOptions attributeOptions = TypeAttributeOptions.Default) + { + var typeProvider = new TypeProvider(context.CurrentAssembly); + var signature = propertyDef.DecodeSignature(typeProvider, context); + var accessors = propertyDef.GetAccessors(); + SRM.ParameterHandleCollection? parameterHandles = null; + if (!accessors.Getter.IsNil) { + var getter = metadata.GetMethodDefinition(accessors.Getter); + parameterHandles = getter.GetParameters(); + } else { + if (!accessors.Setter.IsNil) { + var setter = metadata.GetMethodDefinition(accessors.Setter); + parameterHandles = setter.GetParameters(); + } + } + return ApplyAttributes(signature, parameterHandles, context.Compilation, metadata, attributeOptions); + } + + static SRM.MethodSignature ApplyAttributes(SRM.MethodSignature signature, SRM.ParameterHandleCollection? parameterHandles, ICompilation compilation, SRM.MetadataReader metadata, TypeAttributeOptions attributeOptions) + { + SRM.CustomAttributeHandleCollection? returnTypeAttributes = null; + var parameterAttributes = new SRM.CustomAttributeHandleCollection?[signature.ParameterTypes.Length]; + if (parameterHandles != null) { + foreach (var parameterHandle in parameterHandles) { + var par = metadata.GetParameter(parameterHandle); + if (par.SequenceNumber == 0) { + returnTypeAttributes = par.GetCustomAttributes(); + } else if (par.SequenceNumber <= parameterAttributes.Length) { + parameterAttributes[par.SequenceNumber - 1] = par.GetCustomAttributes(); + } + } + } + IType returnType = ApplyAttributeTypeVisitor.ApplyAttributesToType( + signature.ReturnType, compilation, returnTypeAttributes, metadata, attributeOptions + ); + var parameterTypes = signature.ParameterTypes.SelectWithIndex( + (i, p) => ApplyAttributeTypeVisitor.ApplyAttributesToType( + p, compilation, parameterAttributes[i], metadata, attributeOptions + ) + ).ToImmutableArray(); + return new SRM.MethodSignature( + signature.Header, returnType, + signature.RequiredParameterCount, signature.GenericParameterCount, + parameterTypes + ); + } + } + + sealed class SignatureParameterTypeReference : ITypeReference + { + readonly UnresolvedMethodSignature unresolvedSig; + readonly int index; + + public SignatureParameterTypeReference(UnresolvedMethodSignature unresolvedSig, int index) + { + this.unresolvedSig = unresolvedSig; + this.index = index; + } + + public IType Resolve(ITypeResolveContext context) + { + var sig = unresolvedSig.Resolve(context); + if (index < sig.ParameterTypes.Length) + return sig.ParameterTypes[index]; + else + return SpecialType.UnknownType; + } + } + + sealed class SignatureReturnTypeReference : ITypeReference + { + readonly UnresolvedMethodSignature unresolvedSig; + + public SignatureReturnTypeReference(UnresolvedMethodSignature unresolvedSig) + { + this.unresolvedSig = unresolvedSig; + } + + public IType Resolve(ITypeResolveContext context) + { + return unresolvedSig.Resolve(context).ReturnType; + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeSpecification.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeSpecification.cs index 4479ce47b..55d0df833 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeSpecification.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeSpecification.cs @@ -23,6 +23,7 @@ using SRM = System.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; using System.Collections.Generic; +using System.Diagnostics; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { @@ -81,120 +82,37 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return ElementType.Resolve(context); } } - - public sealed class DynamicTypeReference : TypeVisitor, ITypeReference + + sealed class TypeDefTokenTypeReference : ITypeReference { - readonly ITypeReference reference; - readonly bool[] dynamicInfo; - int typeIndex; - - static readonly ITypeResolveContext minimalCorlibContext = new SimpleTypeResolveContext(MinimalCorlib.Instance.CreateCompilation()); - - public static ITypeReference Create(ITypeReference reference, SRM.CustomAttributeHandleCollection? customAttributes, SRM.MetadataReader metadata) - { - if (HasDynamicAttribute(customAttributes, metadata, out var dynamicInfo)) - return new DynamicTypeReference(reference, dynamicInfo); - return reference; - } - - public static bool HasDynamicAttribute(SRM.CustomAttributeHandleCollection? attributes, SRM.MetadataReader metadata, out bool[] mapping) - { - mapping = null; - if (attributes == null) - return false; - - foreach (var handle in attributes) { - var a = metadata.GetCustomAttribute(handle); - var type = a.GetAttributeType(metadata); - if (type.GetFullTypeName(metadata).ToString() == "System.Runtime.CompilerServices.DynamicAttribute") { - var ctor = a.DecodeValue(new TypeSystemAttributeTypeProvider(minimalCorlibContext)); - if (ctor.FixedArguments.Length == 1) { - var arg = ctor.FixedArguments[0]; - if (arg.Type.ReflectionName == "System.Boolean[]" && arg.Value is ImmutableArray> values) { - mapping = values.SelectArray(v => (bool)v.Value); - return true; - } - } - return true; - } - } - return false; - } + readonly SRM.TypeDefinitionHandle token; - DynamicTypeReference(ITypeReference reference, bool[] dynamicInfo) + public TypeDefTokenTypeReference(SRM.TypeDefinitionHandle token) { - this.reference = reference; - this.dynamicInfo = dynamicInfo; + this.token = token; } public IType Resolve(ITypeResolveContext context) { - return reference.Resolve(context).AcceptVisitor(this); - } - - public override IType VisitPointerType(PointerType type) - { - typeIndex++; - return base.VisitPointerType(type); - } - - public override IType VisitArrayType(ArrayType type) - { - typeIndex++; - return base.VisitArrayType(type); - } - - public override IType VisitByReferenceType(ByReferenceType type) - { - typeIndex++; - return base.VisitByReferenceType(type); - } - - public override IType VisitParameterizedType(ParameterizedType type) - { - var genericType = type.GenericType.AcceptVisitor(this); - bool changed = type.GenericType != genericType; - var arguments = new IType[type.TypeArguments.Count]; - for (int i = 0; i < type.TypeArguments.Count; i++) { - typeIndex++; - arguments[i] = type.TypeArguments[i].AcceptVisitor(this); - changed = changed || arguments[i] != type.TypeArguments[i]; - } - if (!changed) - return type; - return new ParameterizedType(genericType, arguments); - } - - public override IType VisitTypeDefinition(ITypeDefinition type) - { - if (type.KnownTypeCode == KnownTypeCode.Object) { - if (dynamicInfo == null || typeIndex >= dynamicInfo.Length) - return SpecialType.Dynamic; - if (dynamicInfo[typeIndex]) - return SpecialType.Dynamic; - return type; - } - return type; + ITypeDefinition td = context.CurrentAssembly.ResolveTypeDefToken(token); + if (td != null) + return td; + return SpecialType.UnknownType; } } - sealed class TypeDefTokenTypeReference : ITypeReference + sealed class TypeSpecTypeReference : ITypeReference { - readonly SRM.EntityHandle token; + readonly SRM.TypeSpecification typeSpec; - public TypeDefTokenTypeReference(SRM.EntityHandle token) + public TypeSpecTypeReference(SRM.TypeSpecification typeSpec) { - if (token.Kind != SRM.HandleKind.TypeDefinition) - throw new ArgumentException(nameof(token), "must be TypeDef token"); - this.token = token; + this.typeSpec = typeSpec; } public IType Resolve(ITypeResolveContext context) { - ITypeDefinition td = context.CurrentAssembly.ResolveTypeDefToken(token); - if (td != null) - return td; - return SpecialType.UnknownType; + return typeSpec.DecodeSignature(new TypeProvider(context.CurrentAssembly), context); } } @@ -279,60 +197,4 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation .DecodeSignature(this, default); } } - - public class TypeSystemAttributeTypeProvider : SRM.ICustomAttributeTypeProvider - { - readonly ITypeResolveContext context; - - public static TypeSystemAttributeTypeProvider CreateDefault() => new TypeSystemAttributeTypeProvider(new SimpleTypeResolveContext(MinimalCorlib.Instance.CreateCompilation())); - - public TypeSystemAttributeTypeProvider(ITypeResolveContext context) - { - this.context = context; - } - - public IType GetPrimitiveType(SRM.PrimitiveTypeCode typeCode) - { - return context.Compilation.FindType(typeCode.ToKnownTypeCode()); - } - - public IType GetSystemType() - { - return context.Compilation.FindType(KnownTypeCode.Type); - } - - public IType GetSZArrayType(IType elementType) - { - return new ArrayType(context.Compilation, elementType); - } - - public IType GetTypeFromDefinition(SRM.MetadataReader reader, SRM.TypeDefinitionHandle handle, byte rawTypeKind) - { - var type = reader.GetTypeDefinition(handle); - return new DefaultUnresolvedTypeDefinition(type.GetFullTypeName(reader).ToString()).Resolve(context); - } - - public IType GetTypeFromReference(SRM.MetadataReader reader, SRM.TypeReferenceHandle handle, byte rawTypeKind) - { - return new DefaultUnresolvedTypeDefinition(handle.GetFullTypeName(reader).ToString()).Resolve(context); - } - - public IType GetTypeFromSerializedName(string name) - { - return new GetClassTypeReference(new FullTypeName(name)).Resolve(context); - } - - public SRM.PrimitiveTypeCode GetUnderlyingEnumType(IType type) - { - var def = type.GetEnumUnderlyingType().GetDefinition(); - if (def == null) - throw new InvalidOperationException(); - return def.KnownTypeCode.ToPrimtiveTypeCode(); - } - - public bool IsSystemType(IType type) - { - return type.IsKnownType(KnownTypeCode.Type); - } - } } diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataLoader.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataLoader.cs index e4b5441a3..4c7deadc5 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataLoader.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataLoader.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Linq; @@ -91,19 +92,8 @@ namespace ICSharpCode.Decompiler.TypeSystem /// for every delay-loading operation. /// If you access the Cecil objects directly in your application, you may need to take the same lock. /// - public bool LazyLoad { get; set; } - - /// - /// This delegate gets executed whenever an entity was loaded. - /// - /// - /// This callback may be to build a dictionary that maps between - /// entities and cecil objects. - /// Warning: if delay-loading is used and the type system is accessed by multiple threads, - /// the callback may be invoked concurrently on multiple threads. - /// - public Action OnEntityLoaded { get; set; } - + public bool LazyLoad { get; private set; } = true; + bool shortenInterfaceImplNames = true; /// @@ -140,8 +130,9 @@ namespace ICSharpCode.Decompiler.TypeSystem // use a shared typeSystemTranslationTable this.IncludeInternalMembers = loader.IncludeInternalMembers; this.LazyLoad = loader.LazyLoad; - this.OnEntityLoaded = loader.OnEntityLoaded; this.ShortenInterfaceImplNames = loader.ShortenInterfaceImplNames; + this.UseDynamicType = loader.UseDynamicType; + this.UseTupleTypes = loader.UseTupleTypes; this.currentModule = loader.currentModule; this.currentMetadata = loader.currentMetadata; this.currentAssembly = loader.currentAssembly; @@ -222,7 +213,6 @@ namespace ICSharpCode.Decompiler.TypeSystem if (this.LazyLoad) { var t = new LazySRMTypeDefinition(cecilLoaderCloneForLazyLoading, module, h); currentAssembly.AddTypeDefinition(t); - RegisterCecilObject(t); } else { var t = CreateTopLevelTypeDefinition(h, td); cecilTypeDefs.Add(h); @@ -295,32 +285,24 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// Reads a type reference. /// - /// The Cecil type reference that should be converted into + /// The metadata type token that should be converted into /// a type system type reference. - /// Attributes associated with the Cecil type reference. - /// This is used to support the 'dynamic' type. + /// Attributes associated with the type. + /// This is used to support the 'dynamic' type, and for tuple element names. + /// public ITypeReference ReadTypeReference(EntityHandle type, CustomAttributeHandleCollection? typeAttributes = null) { - ITypeReference CreateTypeReference(TypeReferenceHandle handle) - { - var t = currentMetadata.GetTypeReference(handle); - var asmref = handle.GetDeclaringAssembly(currentMetadata); - if (asmref.IsNil) - return new GetClassTypeReference(handle.GetFullTypeName(currentMetadata), DefaultAssemblyReference.CurrentAssembly); - var asm = currentMetadata.GetAssemblyReference(asmref); - return new GetClassTypeReference(handle.GetFullTypeName(currentMetadata), new DefaultAssemblyReference(currentMetadata.GetString(asm.Name))); - } - - switch (type.Kind) { - case HandleKind.TypeSpecification: - return DynamicTypeReference.Create(currentMetadata.GetTypeSpecification((TypeSpecificationHandle)type) - .DecodeSignature(TypeReferenceSignatureDecoder.Instance, default), typeAttributes, currentMetadata); - case HandleKind.TypeReference: - return CreateTypeReference((TypeReferenceHandle)type); - case HandleKind.TypeDefinition: - return new TypeDefTokenTypeReference(type); - default: - throw new NotSupportedException(); + return new MetadataTypeReference(type, currentMetadata, typeAttributes, TypeAttributeOptions); + } + + private TypeAttributeOptions TypeAttributeOptions { + get { + TypeAttributeOptions options = TypeAttributeOptions.None; + if (UseDynamicType) + options |= TypeAttributeOptions.Dynamic; + if (UseTupleTypes) + options |= TypeAttributeOptions.Tuple; + return options; } } #endregion @@ -808,7 +790,6 @@ namespace ICSharpCode.Decompiler.TypeSystem InitMembers(typeDefinition, td, td.Members); td.ApplyInterningProvider(interningProvider); td.Freeze(); - RegisterCecilObject(td); } void InitBaseTypes(TypeDefinitionHandle handle, IList baseTypes) @@ -986,10 +967,10 @@ namespace ICSharpCode.Decompiler.TypeSystem { foreach (var h in typeDefinition.GetCustomAttributes()) { var a = reader.GetCustomAttribute(h); - var type = a.GetAttributeType(reader).GetFullTypeName(reader); - if (type.ToString() != typeof(System.Reflection.DefaultMemberAttribute).FullName) + var type = a.GetAttributeType(reader); + if (!type.IsTopLevelType(reader, "System.Reflection", "DefaultMemberAttribute")) continue; - var value = a.DecodeValue(new TypeSystemAttributeTypeProvider(minimalCorlibContext)); + var value = a.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider); if (value.FixedArguments.Length == 1 && value.FixedArguments[0].Value is string name) return name; } @@ -998,14 +979,14 @@ namespace ICSharpCode.Decompiler.TypeSystem static bool IsAccessor(MethodSemanticsAttributes semantics) { - return semantics > (MethodSemanticsAttributes)0 && semantics != MethodSemanticsAttributes.Other; + return semantics != 0 && semantics != MethodSemanticsAttributes.Other; } #endregion #region Lazy-Loaded Type Definition sealed class LazySRMTypeDefinition : AbstractUnresolvedEntity, IUnresolvedTypeDefinition { - // loader + cecilTypeDef, used for lazy-loading; and set to null after lazy loading is complete + // loader + cecilTypeDef, used for lazy-loading readonly MetadataLoader loader; readonly Metadata.PEFile module; readonly MetadataReader metadata; @@ -1224,31 +1205,11 @@ namespace ICSharpCode.Decompiler.TypeSystem AddAttributes(method, m.Attributes, m.ReturnTypeAttributes); TranslateModifiers(handle, m); - var declaringType = currentMetadata.GetTypeDefinition(method.GetDeclaringType()); - var reader = currentMetadata.GetBlobReader(method.Signature); - var signature = method.DecodeSignature(TypeReferenceSignatureDecoder.Instance, default); - - var parameters = method.GetParameters().ToArray(); - m.ReturnType = HandleReturnType(parameters.FirstOrDefault(), signature.ReturnType); - - int j = 0; - if (signature.RequiredParameterCount > parameters.Length) { - foreach (var parameterType in signature.ParameterTypes) { - m.Parameters.Add(new DefaultUnresolvedParameter(DynamicTypeReference.Create(parameterType, null, currentMetadata), string.Empty)); - } - } else { - foreach (var p in parameters) { - var par = currentMetadata.GetParameter(p); - if (par.SequenceNumber > 0) { - m.Parameters.Add(ReadParameter(par, signature.ParameterTypes[j])); - j++; - } - } - } - - if (signature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - m.Parameters.Add(new DefaultUnresolvedParameter(SpecialType.ArgList, string.Empty)); - } + var signature = method.DecodeSignature(TypeCodeProvider.Instance, default); + var unresolvedSig = new UnresolvedMethodSignature(handle, currentMetadata, TypeAttributeOptions); + var (retType, parameters) = TranslateSignature(signature, unresolvedSig, method.GetParameters()); + m.ReturnType = retType; + m.Parameters.AddRange(parameters); // mark as extension method if the attribute is set if ((method.Attributes & MethodAttributes.Static) == MethodAttributes.Static && HasExtensionAttribute(currentMetadata, method.GetCustomAttributes())) { @@ -1297,24 +1258,79 @@ namespace ICSharpCode.Decompiler.TypeSystem return m; } - ITypeReference HandleReturnType(ParameterHandle parameterHandle, ITypeReference returnType) + private (ITypeReference, List) TranslateSignature( + MethodSignature signature, + UnresolvedMethodSignature unresolvedSig, ParameterHandleCollection? parameterHandleCollection) { - CustomAttributeHandleCollection? attributes = null; - if (!parameterHandle.IsNil) { - var par = currentMetadata.GetParameter(parameterHandle); - if (par.SequenceNumber == 0) { - attributes = par.GetCustomAttributes(); + var returnType = new SignatureReturnTypeReference(unresolvedSig); + var parameters = new List(); + int i = 0; + if (parameterHandleCollection != null) { + foreach (var p in parameterHandleCollection) { + var par = currentMetadata.GetParameter(p); + if (par.SequenceNumber > 0 && i < signature.RequiredParameterCount) { + Debug.Assert(par.SequenceNumber - 1 == i); + var paramTypeRef = new SignatureParameterTypeReference(unresolvedSig, i); + parameters.Add(ReadParameter(par, paramTypeRef, signature.ParameterTypes[i])); + i++; + } } } - return DynamicTypeReference.Create(returnType, attributes, currentMetadata); + Debug.Assert(i == parameters.Count); + while (i < signature.RequiredParameterCount) { + parameters.Add(new DefaultUnresolvedParameter( + new SignatureParameterTypeReference(unresolvedSig, i), + name: string.Empty + )); + i++; + } + + if (signature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { + parameters.Add(new DefaultUnresolvedParameter(SpecialType.ArgList, string.Empty)); + } + return (returnType, parameters); } - static bool HasExtensionAttribute(MetadataReader currentModule, CustomAttributeHandleCollection attributes) + /// + /// Allows decoding a signature without decoding the types, + /// e.g. for counting the number of parameters in a method signature. + /// + sealed class TypeCodeProvider : ISignatureTypeProvider + { + public static readonly TypeCodeProvider Instance = new TypeCodeProvider(); + + public SignatureTypeCode GetArrayType(SignatureTypeCode elementType, ArrayShape shape) => SignatureTypeCode.Array; + public SignatureTypeCode GetByReferenceType(SignatureTypeCode elementType) => SignatureTypeCode.ByReference; + public SignatureTypeCode GetFunctionPointerType(MethodSignature signature) => SignatureTypeCode.FunctionPointer; + public SignatureTypeCode GetGenericInstantiation(SignatureTypeCode genericType, ImmutableArray typeArguments) => SignatureTypeCode.GenericTypeInstance; + public SignatureTypeCode GetGenericMethodParameter(Unit genericContext, int index) => SignatureTypeCode.GenericMethodParameter; + public SignatureTypeCode GetGenericTypeParameter(Unit genericContext, int index) => SignatureTypeCode.GenericTypeParameter; + + public SignatureTypeCode GetModifiedType(SignatureTypeCode modifier, SignatureTypeCode unmodifiedType, bool isRequired) + { + // skip modifiers + return unmodifiedType; + } + + public SignatureTypeCode GetPinnedType(SignatureTypeCode elementType) => SignatureTypeCode.Pinned; + public SignatureTypeCode GetPointerType(SignatureTypeCode elementType) => SignatureTypeCode.Pointer; + public SignatureTypeCode GetPrimitiveType(PrimitiveTypeCode typeCode) => (SignatureTypeCode)typeCode; + public SignatureTypeCode GetSZArrayType(SignatureTypeCode elementType) => SignatureTypeCode.SZArray; + + public SignatureTypeCode GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) + => SignatureTypeCode.TypeHandle; + public SignatureTypeCode GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) + => SignatureTypeCode.TypeHandle; + public SignatureTypeCode GetTypeFromSpecification(MetadataReader reader, Unit genericContext, TypeSpecificationHandle handle, byte rawTypeKind) + => SignatureTypeCode.TypeHandle; + } + + static bool HasExtensionAttribute(MetadataReader metadata, CustomAttributeHandleCollection attributes) { foreach (var h in attributes) { - var attr = currentModule.GetCustomAttribute(h); - var type = attr.GetAttributeType(currentModule); - if (type.GetFullTypeName(currentModule).ToString() == "System.Runtime.CompilerServices.ExtensionAttribute") + var attr = metadata.GetCustomAttribute(h); + var type = attr.GetAttributeType(metadata); + if (type.IsTopLevelType(metadata, "System.Runtime.CompilerServices", "ExtensionAttribute")) return true; } return false; @@ -1376,11 +1392,11 @@ namespace ICSharpCode.Decompiler.TypeSystem #endregion #region Read Parameter - public IUnresolvedParameter ReadParameter(Parameter parameter, ITypeReference type) + public IUnresolvedParameter ReadParameter(Parameter parameter, ITypeReference typeRef, SignatureTypeCode type) { - var p = new DefaultUnresolvedParameter(DynamicTypeReference.Create(type, parameter.GetCustomAttributes(), currentMetadata), interningProvider.Intern(currentMetadata.GetString(parameter.Name))); + var p = new DefaultUnresolvedParameter(typeRef, interningProvider.Intern(currentMetadata.GetString(parameter.Name))); - if (type is ByReferenceTypeReference) { + if (type == SignatureTypeCode.ByReference) { if ((parameter.Attributes & ParameterAttributes.In) == 0 && (parameter.Attributes & ParameterAttributes.Out) != 0) p.IsOut = true; else @@ -1393,16 +1409,16 @@ namespace ICSharpCode.Decompiler.TypeSystem if (!constantHandle.IsNil) { var constant = currentMetadata.GetConstant(constantHandle); var blobReader = currentMetadata.GetBlobReader(constant.Value); - p.DefaultValue = CreateSimpleConstantValue(type, blobReader.ReadConstant(constant.TypeCode)); + p.DefaultValue = CreateSimpleConstantValue(typeRef, blobReader.ReadConstant(constant.TypeCode)); } else { - p.DefaultValue = CreateSimpleConstantValue(type, null); + p.DefaultValue = CreateSimpleConstantValue(typeRef, null); } } - if (type is ArrayTypeReference) { + if (type == SignatureTypeCode.SZArray) { foreach (CustomAttributeHandle h in parameter.GetCustomAttributes()) { var att = currentMetadata.GetCustomAttribute(h); - if (att.GetAttributeType(currentMetadata).GetFullTypeName(currentMetadata).ToString() == typeof(ParamArrayAttribute).FullName) { + if (att.IsAttributeType(currentMetadata, "System", "ParamArrayAttribute")) { p.IsParams = true; break; } @@ -1426,55 +1442,28 @@ namespace ICSharpCode.Decompiler.TypeSystem decimal? TryDecodeDecimalConstantAttribute(CustomAttributeHandle handle) { var attribute = currentMetadata.GetCustomAttribute(handle); - - ITypeReference attributeType = ReadTypeReference(attribute.GetAttributeType(currentMetadata)); - MethodSignature signature; - switch (attribute.Constructor.Kind) { - case HandleKind.MethodDefinition: - var md = currentMetadata.GetMethodDefinition((MethodDefinitionHandle)attribute.Constructor); - signature = md.DecodeSignature(TypeReferenceSignatureDecoder.Instance, default); - break; - case HandleKind.MemberReference: - var mr = currentMetadata.GetMemberReference((MemberReferenceHandle)attribute.Constructor); - Debug.Assert(mr.GetKind() == MemberReferenceKind.Method); - signature = mr.DecodeMethodSignature(TypeReferenceSignatureDecoder.Instance, default); - break; - default: - throw new NotSupportedException(); - } - - if (signature.RequiredParameterCount != 5) + var attrValue = attribute.DecodeValue(minimalCorlibTypeProvider); + if (attrValue.FixedArguments.Length != 5) return null; - - var reader = new Implementation.BlobReader(currentMetadata.GetBlobBytes(attribute.Value), null); - if (reader.ReadUInt16() != 0x0001) { - Debug.WriteLine("Unknown blob prolog"); - return null; - } - // DecimalConstantAttribute has the arguments (byte scale, byte sign, uint hi, uint mid, uint low) or (byte scale, byte sign, int hi, int mid, int low) // Both of these invoke the Decimal constructor (int lo, int mid, int hi, bool isNegative, byte scale) with explicit argument conversions if required. - var ctorArgs = new object[signature.RequiredParameterCount]; - for (int i = 0; i < ctorArgs.Length; i++) { - switch (signature.ParameterTypes[i].Resolve(minimalCorlibContext).FullName) { - case "System.Byte": - ctorArgs[i] = reader.ReadByte(); - break; - case "System.Int32": - ctorArgs[i] = reader.ReadInt32(); - break; - case "System.UInt32": - ctorArgs[i] = unchecked((int)reader.ReadUInt32()); - break; - default: - return null; + if (!(attrValue.FixedArguments[0].Value is byte scale && attrValue.FixedArguments[1].Value is byte sign)) + return null; + unchecked { + if (attrValue.FixedArguments[2].Value is uint hi + && attrValue.FixedArguments[3].Value is uint mid + && attrValue.FixedArguments[4].Value is uint lo) { + return new decimal((int)lo, (int)mid, (int)hi, sign != 0, scale); } } - - if (!ctorArgs.Select(a => a.GetType()).SequenceEqual(new[] { typeof(byte), typeof(byte), typeof(int), typeof(int), typeof(int) })) - return null; - - return new decimal((int)ctorArgs[4], (int)ctorArgs[3], (int)ctorArgs[2], (byte)ctorArgs[1] != 0, (byte)ctorArgs[0]); + { + if (attrValue.FixedArguments[2].Value is int hi + && attrValue.FixedArguments[3].Value is int mid + && attrValue.FixedArguments[4].Value is int lo) { + return new decimal(lo, mid, hi, sign != 0, scale); + } + } + return null; } public IUnresolvedField ReadField(FieldDefinitionHandle handle, IUnresolvedTypeDefinition parentType) @@ -1489,7 +1478,7 @@ namespace ICSharpCode.Decompiler.TypeSystem f.Accessibility = GetAccessibility(field.Attributes); f.IsReadOnly = (field.Attributes & FieldAttributes.InitOnly) == FieldAttributes.InitOnly; f.IsStatic = (field.Attributes & FieldAttributes.Static) == FieldAttributes.Static; - f.ReturnType = DynamicTypeReference.Create(field.DecodeSignature(TypeReferenceSignatureDecoder.Instance, default), field.GetCustomAttributes(), currentMetadata); + f.ReturnType = new FieldTypeReference(handle, currentMetadata, TypeAttributeOptions); var constantHandle = field.GetDefaultValue(); if (!constantHandle.IsNil) { var constant = currentMetadata.GetConstant(constantHandle); @@ -1599,37 +1588,23 @@ namespace ICSharpCode.Decompiler.TypeSystem if (!accessors.Getter.IsNil && !accessors.Setter.IsNil) p.Accessibility = MergePropertyAccessibility(GetAccessibility(currentMetadata.GetMethodDefinition(accessors.Getter).Attributes), GetAccessibility(currentMetadata.GetMethodDefinition(accessors.Setter).Attributes)); - var signature = property.DecodeSignature(TypeReferenceSignatureDecoder.Instance, default); p.Getter = ReadMethod(accessors.Getter, parentType, SymbolKind.Accessor, p); p.Setter = ReadMethod(accessors.Setter, parentType, SymbolKind.Accessor, p); - var parameterHandles = Empty.Array; + ParameterHandleCollection? parameterHandles = null; if (!accessors.Getter.IsNil) { var getter = currentMetadata.GetMethodDefinition(accessors.Getter); - parameterHandles = getter.GetParameters().ToArray(); - } else { - if (!accessors.Setter.IsNil) { - var setter = currentMetadata.GetMethodDefinition(accessors.Setter); - parameterHandles = setter.GetParameters().ToArray(); - } + parameterHandles = getter.GetParameters(); + } else if (!accessors.Setter.IsNil) { + var setter = currentMetadata.GetMethodDefinition(accessors.Setter); + parameterHandles = setter.GetParameters(); } - p.ReturnType = HandleReturnType(parameterHandles.FirstOrDefault(), signature.ReturnType); - - int i = 0; - if (signature.RequiredParameterCount > parameterHandles.Length) { - foreach (var parameterType in signature.ParameterTypes) { - p.Parameters.Add(new DefaultUnresolvedParameter(DynamicTypeReference.Create(parameterType, null, currentMetadata), string.Empty)); - } - } else { - foreach (var h in parameterHandles) { - var par = currentMetadata.GetParameter(h); - if (par.SequenceNumber > 0 && i < signature.ParameterTypes.Length) { - p.Parameters.Add(ReadParameter(par, signature.ParameterTypes[i])); - i++; - } - } - } + var signature = property.DecodeSignature(TypeCodeProvider.Instance, default); + var unresolvedSig = new UnresolvedMethodSignature(handle, currentMetadata, TypeAttributeOptions); + var (retType, parameters) = TranslateSignature(signature, unresolvedSig, parameterHandles); + p.ReturnType = retType; + p.Parameters.AddRange(parameters); AddAttributes(property, p); var accessor = p.Getter ?? p.Setter; @@ -1691,14 +1666,6 @@ namespace ICSharpCode.Decompiler.TypeSystem member.MetadataToken = cecilDefinition; member.ApplyInterningProvider(interningProvider); member.Freeze(); - RegisterCecilObject(member); - } - #endregion - - #region Type system translation table - void RegisterCecilObject(IUnresolvedEntity typeSystemObject) - { - OnEntityLoaded?.Invoke(typeSystemObject); } #endregion } diff --git a/ICSharpCode.Decompiler/TypeSystem/SpecializingDecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/SpecializingDecompilerTypeSystem.cs index 68c76762b..b5d78a80a 100644 --- a/ICSharpCode.Decompiler/TypeSystem/SpecializingDecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/SpecializingDecompilerTypeSystem.cs @@ -17,6 +17,8 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Immutable; +using System.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.TypeSystem @@ -53,18 +55,13 @@ namespace ICSharpCode.Decompiler.TypeSystem public TypeParameterSubstitution Substitution { get { return substitution; } } - - public IType ResolveFromSignature(ITypeReference typeReference) - { - return context.ResolveFromSignature(typeReference).AcceptVisitor(substitution); - } - - public IType ResolveAsType(System.Reflection.Metadata.EntityHandle typeReference) + + public IType ResolveAsType(EntityHandle typeReference) { return context.ResolveAsType(typeReference).AcceptVisitor(substitution); } - public IField ResolveAsField(System.Reflection.Metadata.EntityHandle fieldReference) + public IField ResolveAsField(EntityHandle fieldReference) { IField field = context.ResolveAsField(fieldReference); if (field != null) @@ -72,7 +69,7 @@ namespace ICSharpCode.Decompiler.TypeSystem return field; } - public IMethod ResolveAsMethod(System.Reflection.Metadata.EntityHandle methodReference) + public IMethod ResolveAsMethod(EntityHandle methodReference) { IMethod method = context.ResolveAsMethod(methodReference); if (method != null) @@ -88,12 +85,12 @@ namespace ICSharpCode.Decompiler.TypeSystem return context.GetSpecializingTypeSystem(newSubstitution); } - public System.Reflection.Metadata.MetadataReader GetMetadata() + public MetadataReader GetMetadata() { return context.GetMetadata(); } - public IMember ResolveAsMember(System.Reflection.Metadata.EntityHandle memberReference) + public IMember ResolveAsMember(EntityHandle memberReference) { IMember member = context.ResolveAsMember(memberReference); if (member != null) @@ -105,5 +102,27 @@ namespace ICSharpCode.Decompiler.TypeSystem { return context.GetModuleDefinition(assembly); } + + MethodSignature IDecompilerTypeSystem.DecodeMethodSignature(StandaloneSignatureHandle standaloneSignatureHandle) + { + var sig = context.DecodeMethodSignature(standaloneSignatureHandle); + return new MethodSignature( + sig.Header, + sig.ReturnType.AcceptVisitor(substitution), + sig.RequiredParameterCount, + sig.GenericParameterCount, + ImmutableArray.CreateRange( + sig.ParameterTypes, t => t.AcceptVisitor(substitution) + ) + ); + } + + ImmutableArray IDecompilerTypeSystem.DecodeLocalSignature(StandaloneSignatureHandle standaloneSignatureHandle) + { + var sig = context.DecodeLocalSignature(standaloneSignatureHandle); + return ImmutableArray.CreateRange( + sig, t => t.AcceptVisitor(substitution) + ); + } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs new file mode 100644 index 000000000..93a33d89b --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs @@ -0,0 +1,166 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Immutable; +using System.Reflection.Metadata.Ecma335; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem.Implementation; +using SRM = System.Reflection.Metadata; + +namespace ICSharpCode.Decompiler.TypeSystem +{ + /// + /// Allows decoding signatures using decompiler types. + /// + class TypeProvider : SRM.ISignatureTypeProvider, SRM.ICustomAttributeTypeProvider + { + readonly IAssembly assembly; + readonly ICompilation compilation; + + public TypeProvider(IAssembly assembly) + { + this.assembly = assembly; + this.compilation = assembly.Compilation; + } + + public IType GetArrayType(IType elementType, SRM.ArrayShape shape) + { + return new ArrayType(compilation, elementType, shape.Rank); + } + + public IType GetByReferenceType(IType elementType) + { + return new ByReferenceType(elementType); + } + + public IType GetFunctionPointerType(SRM.MethodSignature signature) + { + return compilation.FindType(KnownTypeCode.IntPtr); + } + + public IType GetGenericInstantiation(IType genericType, ImmutableArray typeArguments) + { + return new ParameterizedType(genericType, typeArguments); + } + + public IType GetGenericMethodParameter(ITypeResolveContext genericContext, int index) + { + // Note: returning type parameter, never type argument. + // Otherwise we risk screwing up the counting for dynamicTypeIndex. + IMethod method = genericContext.CurrentMember as IMethod; + if (method != null && index < method.TypeParameters.Count) { + return method.TypeParameters[index]; + } + return DummyTypeParameter.GetMethodTypeParameter(index); + } + + public IType GetGenericTypeParameter(ITypeResolveContext genericContext, int index) + { + ITypeDefinition typeDef = genericContext.CurrentTypeDefinition; + if (typeDef != null && index < typeDef.TypeParameters.Count) { + return typeDef.TypeParameters[index]; + } + return DummyTypeParameter.GetClassTypeParameter(index); + } + + public IType GetModifiedType(IType modifier, IType unmodifiedType, bool isRequired) + { + return unmodifiedType; + } + + public IType GetPinnedType(IType elementType) + { + return new PinnedType(elementType); + } + + public IType GetPointerType(IType elementType) + { + return new PointerType(elementType); + } + + public IType GetPrimitiveType(SRM.PrimitiveTypeCode typeCode) + { + return compilation.FindType(typeCode.ToKnownTypeCode()); + } + + public IType GetSystemType() + { + return compilation.FindType(KnownTypeCode.Type); + } + + public IType GetSZArrayType(IType elementType) + { + return new ArrayType(compilation, elementType); + } + + public IType GetTypeFromDefinition(SRM.MetadataReader reader, SRM.TypeDefinitionHandle handle, byte rawTypeKind) + { + ITypeDefinition td = assembly.ResolveTypeDefToken(handle); + if (td != null) + return td; + return SpecialType.UnknownType; + } + + public IType GetTypeFromReference(SRM.MetadataReader reader, SRM.TypeReferenceHandle handle, byte rawTypeKind) + { + var asmref = handle.GetDeclaringAssembly(reader); + IAssemblyReference nrAsmRef; + if (asmref.IsNil) + nrAsmRef = DefaultAssemblyReference.CurrentAssembly; + else + nrAsmRef = new DefaultAssemblyReference(reader.GetString(reader.GetAssemblyReference(asmref).Name)); + bool? isReferenceType = null; + switch (reader.ResolveSignatureTypeKind(handle, rawTypeKind)) { + case SRM.SignatureTypeKind.ValueType: + isReferenceType = false; + break; + case SRM.SignatureTypeKind.Class: + isReferenceType = true; + break; + } + var gctr = new GetClassTypeReference(handle.GetFullTypeName(reader), nrAsmRef, isReferenceType); + return gctr.Resolve(new SimpleTypeResolveContext(assembly)); + } + + public IType GetTypeFromSerializedName(string name) + { + // TODO: aren't we missing support for assembly-qualified names? + return new GetClassTypeReference(new FullTypeName(name)) + .Resolve(new SimpleTypeResolveContext(assembly)); + } + + public IType GetTypeFromSpecification(SRM.MetadataReader reader, ITypeResolveContext genericContext, SRM.TypeSpecificationHandle handle, byte rawTypeKind) + { + return reader.GetTypeSpecification(handle).DecodeSignature(this, genericContext); + } + + public SRM.PrimitiveTypeCode GetUnderlyingEnumType(IType type) + { + var def = type.GetEnumUnderlyingType().GetDefinition(); + if (def == null) + throw new InvalidOperationException(); + return def.KnownTypeCode.ToPrimitiveTypeCode(); + } + + public bool IsSystemType(IType type) + { + return type.IsKnownType(KnownTypeCode.Type); + } + } +} diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 509f45564..9c820d0a2 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -586,7 +586,7 @@ namespace ICSharpCode.ILSpy var st = new SyntaxTree(); st.AddChild(astType, Roles.Type); - st.AcceptVisitor(new InsertDynamicTypeVisitor(metadata, customAttributes)); + //st.AcceptVisitor(new InsertDynamicTypeVisitor(metadata, customAttributes)); st.FirstChild.AcceptVisitor(new CSharpOutputVisitor(w, TypeToStringFormattingOptions)); return w.ToString(); } @@ -788,41 +788,4 @@ namespace ICSharpCode.ILSpy return info; } } - - class InsertDynamicTypeVisitor : DepthFirstAstVisitor - { - bool isDynamic; - bool[] mapping; - int typeIndex; - - public InsertDynamicTypeVisitor(MetadataReader metadata, CustomAttributeHandleCollection? customAttributes) - { - isDynamic = DynamicTypeReference.HasDynamicAttribute(customAttributes, metadata, out mapping); - } - - public override void VisitComposedType(ComposedType composedType) - { - typeIndex++; - base.VisitComposedType(composedType); - } - - public override void VisitPrimitiveType(PrimitiveType primitiveType) - { - if (isDynamic && primitiveType.KnownTypeCode == KnownTypeCode.Object && (mapping == null || typeIndex >= mapping.Length || mapping[typeIndex])) { - primitiveType.ReplaceWith(new SimpleType("dynamic")); - } else { - base.VisitPrimitiveType(primitiveType); - } - } - - public override void VisitMemberType(MemberType memberType) - { - base.VisitMemberType(memberType); - } - - public override void VisitSimpleType(SimpleType simpleType) - { - base.VisitSimpleType(simpleType); - } - } } diff --git a/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs b/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs index 5cdc3a109..24533731d 100644 --- a/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs +++ b/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs @@ -38,7 +38,6 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer readonly bool provideTypeSystem; TypeDefinitionHandle typeScopeHandle; TypeDefinition typeScope; - static readonly TypeSystemAttributeTypeProvider typeProvider = TypeSystemAttributeTypeProvider.CreateDefault(); readonly Accessibility memberAccessibility = Accessibility.Public; Accessibility typeAccessibility = Accessibility.Public; @@ -305,6 +304,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer string typeScopeNamespace = metadata.GetString(typeScope.Namespace); string typeScopeName = metadata.GetString(typeScope.Name); + var typeProvider = Decompiler.Metadata.MetadataExtensions.MinimalAttributeTypeProvider; var attributes = metadata.CustomAttributes.Select(h => metadata.GetCustomAttribute(h)).Where(ca => ca.GetAttributeType(metadata).GetFullTypeName(metadata).ToString() == "System.Runtime.CompilerServices.InternalsVisibleToAttribute"); var friendAssemblies = new HashSet(); foreach (var attribute in attributes) {