diff --git a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs index 2af568fbe..0119f4ba8 100644 --- a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs +++ b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs @@ -1359,6 +1359,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem var f = type.GetFields().Single(x => x.Name == name); Assert.IsTrue(f.IsConst); Assert.AreEqual(expected, f.ConstantValue); + Assert.AreEqual(0, f.Attributes.Count); } [Test] @@ -1613,6 +1614,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { ITypeDefinition c = compilation.FindType(typeof(void)).GetDefinition(); Assert.IsNotNull(c, "System.Void not found"); + Assert.AreEqual(TypeKind.Void, c.Kind); Assert.AreEqual(0, c.GetMethods().Count()); Assert.AreEqual(0, c.GetProperties().Count()); Assert.AreEqual(0, c.GetEvents().Count()); diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 7bad20aca..f323f9a29 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -293,7 +293,7 @@ namespace ICSharpCode.Decompiler.Disassembler } WriteSecurityDeclarations(module, methodDefinition.GetDeclarativeSecurityAttributes()); - if (handle.HasBody(metadata)) { + if (methodDefinition.HasBody()) { methodBodyDisassembler.Disassemble(module, handle); } var declaringType = metadata.GetTypeDefinition(methodDefinition.GetDeclaringType()); diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index b4afb1b72..779af3792 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -345,7 +345,9 @@ + + diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index be97cf60f..975769c3c 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -366,10 +366,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow { var typeSystem = context.TypeSystem; var metadata = typeSystem.GetMetadata(); - if (method.IsNil || !method.HasBody(metadata)) + if (method.IsNil) throw new SymbolicAnalysisFailedException(); var methodDef = metadata.GetMethodDefinition(method); + if (!methodDef.HasBody()) + throw new SymbolicAnalysisFailedException(); var sdtp = typeSystem as SpecializingDecompilerTypeSystem; if (sdtp != null) { diff --git a/ICSharpCode.Decompiler/SRMExtensions.cs b/ICSharpCode.Decompiler/SRMExtensions.cs index ca301aeff..1eb2df690 100644 --- a/ICSharpCode.Decompiler/SRMExtensions.cs +++ b/ICSharpCode.Decompiler/SRMExtensions.cs @@ -92,13 +92,7 @@ namespace ICSharpCode.Decompiler } return false; } - - public static bool HasBody(this MethodDefinitionHandle handle, MetadataReader reader) - { - var methodDefinition = reader.GetMethodDefinition(handle); - return methodDefinition.HasBody(); - } - + public static bool HasBody(this MethodDefinition methodDefinition) { const MethodAttributes noBodyAttrs = MethodAttributes.Abstract | MethodAttributes.PinvokeImpl; diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index 7e3984ed3..146b5101c 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -237,7 +237,7 @@ namespace ICSharpCode.Decompiler.TypeSystem #region Resolve Field public IField ResolveAsField(SRM.EntityHandle fieldReference) { - if (fieldReference.IsNil) + /*if (fieldReference.IsNil) throw new ArgumentNullException(nameof(fieldReference)); if (fieldReference.Kind != SRM.HandleKind.FieldDefinition && fieldReference.Kind != SRM.HandleKind.MemberReference) throw new ArgumentException("HandleKind must be either FieldDefinition or MemberReference", nameof(fieldReference)); @@ -248,21 +248,9 @@ namespace ICSharpCode.Decompiler.TypeSystem switch (fieldReference.Kind) { case SRM.HandleKind.FieldDefinition: var fieldDefHandle = (SRM.FieldDefinitionHandle)fieldReference; - var fieldDef = metadata.GetFieldDefinition(fieldDefHandle); - declaringType = ResolveDeclaringType(fieldDef.GetDeclaringType()); - var declaringTypeDefinition = declaringType.GetDefinition(); - if (declaringTypeDefinition != null) { - field = declaringTypeDefinition.GetFields(f => f.MetadataToken == fieldReference, GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); - } else { - field = null; - } + field = mainAssembly.GetDefinition(fieldDefHandle); if (field == null) { - field = new FakeField(compilation) { - DeclaringType = declaringType, - Name = metadata.GetString(fieldDef.Name), - ReturnType = FieldTypeReference.Resolve(fieldDefHandle, metadata, context, typeSystemOptions), - IsStatic = (fieldDef.Attributes & System.Reflection.FieldAttributes.Static) != 0, - }; + throw new NotImplementedException(); } break; case SRM.HandleKind.MemberReference: @@ -281,9 +269,11 @@ namespace ICSharpCode.Decompiler.TypeSystem fieldLookupCache.Add(fieldReference, field); } return field; - } + }*/ + throw new NotImplementedException(); } + /* IField FindNonGenericField(SRM.MetadataReader metadata, SRM.MemberReferenceHandle memberRefHandle, IType declaringType) { var memberRef = metadata.GetMemberReference(memberRefHandle); @@ -303,196 +293,15 @@ namespace ICSharpCode.Decompiler.TypeSystem ReturnType = returnType }; } + */ #endregion #region Resolve Method public IMethod ResolveAsMethod(SRM.EntityHandle methodReference) { - if (methodReference.IsNil) - throw new ArgumentNullException(nameof(methodReference)); - if (methodReference.Kind != SRM.HandleKind.MethodDefinition && methodReference.Kind != SRM.HandleKind.MemberReference && methodReference.Kind != SRM.HandleKind.MethodSpecification) - throw new ArgumentException("HandleKind must be either a MethodDefinition, MemberReference or MethodSpecification", nameof(methodReference)); - lock (methodLookupCache) { - if (!methodLookupCache.TryGetValue(methodReference, out IMethod method)) { - var metadata = moduleDefinition.Metadata; - switch (methodReference.Kind) { - case SRM.HandleKind.MethodDefinition: - method = ResolveMethodDefinition(metadata, (SRM.MethodDefinitionHandle)methodReference); - break; - case SRM.HandleKind.MemberReference: - method = ResolveMethodReference(metadata, (SRM.MemberReferenceHandle)methodReference); - break; - case SRM.HandleKind.MethodSpecification: - var methodSpec = metadata.GetMethodSpecification((SRM.MethodSpecificationHandle)methodReference); - var methodTypeArgs = methodSpec.DecodeSignature(mainAssembly.TypeProvider, new GenericContext(context)); - if (methodSpec.Method.Kind == SRM.HandleKind.MethodDefinition) { - // generic instance of a methoddef (=generic method in non-generic class in current assembly) - method = ResolveMethodDefinition(metadata, (SRM.MethodDefinitionHandle)methodSpec.Method); - method = method.Specialize(new TypeParameterSubstitution(null, methodTypeArgs)); - } else { - method = ResolveMethodReference(metadata, (SRM.MemberReferenceHandle)methodSpec.Method, methodTypeArgs); - } - break; - default: - throw new NotSupportedException(); - } - methodLookupCache.Add(methodReference, method); - } - return method; - } - } - - IMethod ResolveMethodDefinition(SRM.MetadataReader metadata, SRM.MethodDefinitionHandle methodDefHandle, bool expandVarArgs = true) - { - var method = mainAssembly.GetDefinition(methodDefHandle); - if (method == null) { - throw new NotImplementedException(); - } - if (expandVarArgs && method.Parameters.LastOrDefault()?.Type.Kind == TypeKind.ArgList) { - method = new VarArgInstanceMethod(method, EmptyList.Instance); - } - return method; + return MainAssembly.ResolveMethod(methodReference); } - /// - /// Resolves a method reference. - /// - /// - /// Class type arguments are provided by the declaring type stored in the memberRef. - /// Method type arguments are provided by the caller. - /// - IMethod ResolveMethodReference(SRM.MetadataReader metadata, SRM.MemberReferenceHandle memberRefHandle, IReadOnlyList methodTypeArguments = null) - { - var memberRef = metadata.GetMemberReference(memberRefHandle); - Debug.Assert(memberRef.GetKind() == SRM.MemberReferenceKind.Method); - SRM.MethodSignature signature; - IReadOnlyList classTypeArguments = null; - IMethod method; - if (memberRef.Parent.Kind == SRM.HandleKind.MethodDefinition) { - method = ResolveMethodDefinition(metadata, (SRM.MethodDefinitionHandle)memberRef.Parent, expandVarArgs: false); - signature = memberRef.DecodeMethodSignature(mainAssembly.TypeProvider, new GenericContext(context)); - } else { - var declaringType = ResolveDeclaringType(memberRef.Parent); - var declaringTypeDefinition = declaringType.GetDefinition(); - if (declaringType.TypeArguments.Count > 0) { - classTypeArguments = declaringType.TypeArguments; - } - // Note: declaringType might be parameterized, but the signature is for the original method definition. - // We'll have to search the member directly on declaringTypeDefinition. - string name = metadata.GetString(memberRef.Name); - signature = memberRef.DecodeMethodSignature(mainAssembly.TypeProvider, - new GenericContext(declaringTypeDefinition?.TypeParameters)); - if (declaringTypeDefinition != null) { - // Find the set of overloads to search: - IEnumerable methods; - if (name == ".ctor") { - methods = declaringTypeDefinition.GetConstructors(); - } else if (name == ".cctor") { - methods = declaringTypeDefinition.Methods.Where(m => m.IsConstructor && m.IsStatic); - } else { - methods = declaringTypeDefinition.GetMethods(m => m.Name == name, GetMemberOptions.IgnoreInheritedMembers) - .Concat(declaringTypeDefinition.GetAccessors(m => m.Name == name, GetMemberOptions.IgnoreInheritedMembers)); - } - // Determine the expected parameters from the signature: - ImmutableArray parameterTypes; - if (signature.Header.CallingConvention == SRM.SignatureCallingConvention.VarArgs) { - parameterTypes = signature.ParameterTypes - .Take(signature.RequiredParameterCount) - .Concat(new[] { SpecialType.ArgList }) - .ToImmutableArray(); - } else { - parameterTypes = signature.ParameterTypes; - } - // Search for the matching method: - method = null; - foreach (var m in methods) { - if (m.TypeParameters.Count != signature.GenericParameterCount) - continue; - if (CompareSignatures(m.Parameters, parameterTypes) && CompareTypes(m.ReturnType, signature.ReturnType)) { - method = m; - break; - } - } - } else { - method = null; - } - if (method == null) { - method = CreateFakeMethod(declaringType, name, signature); - } - } - if (classTypeArguments != null || methodTypeArguments != null) { - method = method.Specialize(new TypeParameterSubstitution(classTypeArguments, methodTypeArguments)); - } - if (signature.Header.CallingConvention == SRM.SignatureCallingConvention.VarArgs) { - method = new VarArgInstanceMethod(method, signature.ParameterTypes.Skip(signature.RequiredParameterCount)); - } - return method; - } - - static readonly NormalizeTypeVisitor normalizeTypeVisitor = new NormalizeTypeVisitor { - ReplaceClassTypeParametersWithDummy = true, - ReplaceMethodTypeParametersWithDummy = true, - }; - - static bool CompareTypes(IType a, IType b) - { - IType type1 = a.AcceptVisitor(normalizeTypeVisitor); - IType type2 = b.AcceptVisitor(normalizeTypeVisitor); - return type1.Equals(type2); - } - - static bool IsVarArgMethod(IMethod method) - { - return method.Parameters.Count > 0 && method.Parameters[method.Parameters.Count - 1].Type.Kind == TypeKind.ArgList; - } - - static bool CompareSignatures(IReadOnlyList parameters, ImmutableArray parameterTypes) - { - if (parameterTypes.Length != parameters.Count) - return false; - for (int i = 0; i < parameterTypes.Length; i++) { - if (!CompareTypes(parameterTypes[i], parameters[i].Type)) - return false; - } - return true; - } - - /// - /// Create a dummy IMethod from the specified MethodReference - /// - IMethod CreateFakeMethod(IType declaringType, string name, SRM.MethodSignature signature) - { - SymbolKind symbolKind = SymbolKind.Method; - if (name == ".ctor" || name == ".cctor") - symbolKind = SymbolKind.Constructor; - var m = new FakeMethod(compilation, symbolKind); - m.DeclaringType = declaringType; - m.Name = name; - m.ReturnType = signature.ReturnType; - m.IsStatic = !signature.Header.IsInstance; - - var metadata = moduleDefinition.Metadata; - TypeParameterSubstitution substitution = null; - if (signature.GenericParameterCount > 0) { - var typeParameters = new List(); - for (int i = 0; i < signature.GenericParameterCount; i++) { - typeParameters.Add(new DefaultTypeParameter(m, i)); - } - m.TypeParameters = typeParameters; - substitution = new TypeParameterSubstitution(null, typeParameters); - } - var parameters = new List(); - for (int i = 0; i < signature.RequiredParameterCount; i++) { - var type = signature.ParameterTypes[i]; - if (substitution != null) { - // replace the dummy method type parameters with the owned instances we just created - type = type.AcceptVisitor(substitution); - } - parameters.Add(new DefaultParameter(type, "")); - } - m.Parameters = parameters; - return m; - } #endregion #region Resolve Property diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs index 0319742f8..4d51afb13 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs @@ -89,6 +89,11 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return Implementation.CustomAttribute.MakeNamedArg(assembly.Compilation, attrType, name, rr); } + internal KeyValuePair MakeNamedArg(IType attrType, string name, bool value) + { + return MakeNamedArg(attrType, name, assembly.Compilation.FindType(KnownTypeCode.Boolean), value); + } + #region MarshalAsAttribute (ConvertMarshalInfo) internal void AddMarshalInfo(BlobHandle marshalInfo) { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs index 8f7215fa7..edd5f75ba 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs @@ -38,6 +38,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation NonSerialized, // Method attributes: + DllImport, + PreserveSig, + MethodImpl, // Parameter attributes: ParamArray, @@ -70,6 +73,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation // Field attributes: new TopLevelTypeName("System.Runtime.InteropServices", nameof(FieldOffsetAttribute)), new TopLevelTypeName("System", nameof(NonSerializedAttribute)), + // Method attributes: + new TopLevelTypeName("System.Runtime.InteropServices", nameof(DllImportAttribute)), + new TopLevelTypeName("System.Runtime.InteropServices", nameof(PreserveSigAttribute)), + new TopLevelTypeName("System.Runtime.CompilerServices", nameof(MethodImplAttribute)), // Parameter attributes: new TopLevelTypeName("System", nameof(ParamArrayAttribute)), // Marshalling attributes: @@ -88,5 +95,5 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation { return compilation.FindType(attrType.GetTypeName()); } - + } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs index c9e09f532..5a9e9d940 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs @@ -22,9 +22,7 @@ using System.Diagnostics; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; -using System.Runtime.InteropServices; using System.Threading; -using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -32,7 +30,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation /// /// Field definition backed by System.Reflection.Metadata /// - class MetadataField : IField + sealed class MetadataField : IField { readonly MetadataAssembly assembly; readonly FieldDefinitionHandle handle; @@ -149,7 +147,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public string FullName => $"{DeclaringType?.FullName}.{Name}"; public string ReflectionName => $"{DeclaringType?.ReflectionName}.{Name}"; - public string Namespace => DeclaringType?.Namespace; + public string Namespace => DeclaringType?.Namespace ?? string.Empty; public bool IsVolatile { get { @@ -179,6 +177,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation Volatile.Write(ref this.isVolatile, true); ty = mod.ElementType; } + ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, + fieldDef.GetCustomAttributes(), metadata, assembly.TypeSystemOptions); return LazyInit.GetOrSet(ref this.type, ty); } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs new file mode 100644 index 000000000..50ca1fed4 --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs @@ -0,0 +1,416 @@ +// 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.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Semantics; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + sealed class MetadataMethod : IMethod + { + readonly MetadataAssembly assembly; + readonly MethodDefinitionHandle handle; + + // eagerly loaded fields: + readonly MethodAttributes attributes; + readonly SymbolKind symbolKind; + readonly ITypeParameter[] typeParameters; + public bool IsExtensionMethod { get; } + + // lazy-loaded fields: + ITypeDefinition declaringType; + string name; + IAttribute[] customAttributes; + IAttribute[] returnTypeAttributes; + IParameter[] parameters; + IType returnType; + + internal MetadataMethod(MetadataAssembly assembly, MethodDefinitionHandle handle) + { + Debug.Assert(assembly != null); + Debug.Assert(!handle.IsNil); + this.assembly = assembly; + this.handle = handle; + var metadata = assembly.metadata; + var def = metadata.GetMethodDefinition(handle); + this.attributes = def.Attributes; + + this.symbolKind = SymbolKind.Method; + if ((attributes & (MethodAttributes.SpecialName | MethodAttributes.RTSpecialName)) != 0) { + string name = this.Name; + if (name == ".cctor" || name == ".ctor") + this.symbolKind = SymbolKind.Constructor; + else if (name.StartsWith("op_", StringComparison.Ordinal)) + this.symbolKind = SymbolKind.Operator; + } + this.typeParameters = MetadataTypeParameter.Create(assembly, this, def.GetGenericParameters()); + this.IsExtensionMethod = (attributes & MethodAttributes.Static) == MethodAttributes.Static + && (assembly.TypeSystemOptions & TypeSystemOptions.ExtensionMethods) == TypeSystemOptions.ExtensionMethods + && def.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.Extension); + } + + public EntityHandle MetadataToken => handle; + + public override string ToString() + { + return $"{MetadataTokens.GetToken(handle):X8} {DeclaringType?.ReflectionName}.{Name}"; + } + + public string Name { + get { + string name = LazyInit.VolatileRead(ref this.name); + if (name != null) + return name; + var metadata = assembly.metadata; + var methodDef = metadata.GetMethodDefinition(handle); + return LazyInit.GetOrSet(ref this.name, metadata.GetString(methodDef.Name)); + } + } + + public IReadOnlyList TypeParameters => typeParameters; + IReadOnlyList IMethod.TypeArguments => typeParameters; + + public SymbolKind SymbolKind => symbolKind; + public bool IsConstructor => symbolKind == SymbolKind.Constructor; + public bool IsDestructor => symbolKind == SymbolKind.Destructor; + public bool IsOperator => symbolKind == SymbolKind.Operator; + public bool IsAccessor => symbolKind == SymbolKind.Accessor; + + public bool HasBody => assembly.metadata.GetMethodDefinition(handle).HasBody(); + + + public IMember AccessorOwner => throw new NotImplementedException(); + + #region Signature (ReturnType + Parameters) + public IReadOnlyList Parameters { + get { + var parameters = LazyInit.VolatileRead(ref this.parameters); + if (parameters != null) + return parameters; + DecodeSignature(); + return this.parameters; + } + } + + public IType ReturnType { + get { + var returnType = LazyInit.VolatileRead(ref this.returnType); + if (returnType != null) + return returnType; + DecodeSignature(); + return this.returnType; + } + } + + private void DecodeSignature() + { + var metadata = assembly.metadata; + var methodDef = metadata.GetMethodDefinition(handle); + var genericContext = new GenericContext(DeclaringType.TypeParameters, this.TypeParameters); + var signature = methodDef.DecodeSignature(assembly.TypeProvider, genericContext); + int i = 0; + CustomAttributeHandleCollection? returnTypeAttributes = null; + IParameter[] parameters = new IParameter[signature.RequiredParameterCount + + (signature.Header.CallingConvention == SignatureCallingConvention.VarArgs ? 1 : 0)]; + foreach (var parameterHandle in methodDef.GetParameters()) { + var par = metadata.GetParameter(parameterHandle); + if (par.SequenceNumber == 0) { + // "parameter" holds return type attributes + returnTypeAttributes = par.GetCustomAttributes(); + } else if (par.SequenceNumber > 0 && i < signature.RequiredParameterCount) { + Debug.Assert(par.SequenceNumber - 1 == i); + var parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( + signature.ParameterTypes[i], Compilation, + par.GetCustomAttributes(), metadata, assembly.TypeSystemOptions); + parameters[i] = new MetadataParameter(assembly, this, parameterType, parameterHandle); + i++; + } + } + while (i < signature.RequiredParameterCount) { + var parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( + signature.ParameterTypes[i], Compilation, null, metadata, assembly.TypeSystemOptions); + parameters[i] = new DefaultParameter(parameterType, name: string.Empty, owner: this, + isRef: parameterType.Kind == TypeKind.ByReference); + i++; + } + if (signature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { + parameters[i] = new DefaultParameter(SpecialType.ArgList, name: string.Empty, owner: this); + i++; + } + Debug.Assert(i == parameters.Length); + var returnType = ApplyAttributeTypeVisitor.ApplyAttributesToType(signature.ReturnType, + Compilation, returnTypeAttributes, metadata, assembly.TypeSystemOptions); + LazyInit.GetOrSet(ref this.returnType, returnType); + LazyInit.GetOrSet(ref this.parameters, parameters); + } + #endregion + + public IReadOnlyList ImplementedInterfaceMembers => throw new NotImplementedException(); + + public bool IsExplicitInterfaceImplementation => throw new NotImplementedException(); + + IMember IMember.MemberDefinition => this; + IMethod IMethod.ReducedFrom => this; + TypeParameterSubstitution IMember.Substitution => TypeParameterSubstitution.Identity; + + public ITypeDefinition DeclaringTypeDefinition { + get { + var declType = LazyInit.VolatileRead(ref this.declaringType); + if (declType != null) { + return declType; + } else { + var def = assembly.metadata.GetMethodDefinition(handle); + return LazyInit.GetOrSet(ref this.declaringType, + assembly.GetDefinition(def.GetDeclaringType())); + } + } + } + + public IType DeclaringType => DeclaringTypeDefinition; + + public IAssembly ParentAssembly => assembly; + public ICompilation Compilation => assembly.Compilation; + + #region Attributes + public IReadOnlyList Attributes { + get { + var attr = LazyInit.VolatileRead(ref this.customAttributes); + if (attr != null) + return attr; + return LazyInit.GetOrSet(ref this.customAttributes, DecodeAttributes()); + } + } + + IType FindInteropType(string name) + { + return assembly.Compilation.FindType(new TopLevelTypeName( + "System.Runtime.InteropServices", name, 0 + )); + } + + IAttribute[] DecodeAttributes() + { + var b = new AttributeListBuilder(assembly); + + var metadata = assembly.metadata; + var def = metadata.GetMethodDefinition(handle); + MethodImplAttributes implAttributes = def.ImplAttributes & ~MethodImplAttributes.CodeTypeMask; + + #region DllImportAttribute + var info = def.GetImport(); + if ((attributes & MethodAttributes.PinvokeImpl) == MethodAttributes.PinvokeImpl && !info.Module.IsNil) { + var dllImportType = assembly.GetAttributeType(KnownAttribute.DllImport); + var positionalArguments = new ResolveResult[] { + new ConstantResolveResult(assembly.Compilation.FindType(KnownTypeCode.String), + metadata.GetString(metadata.GetModuleReference(info.Module).Name)) + }; + var namedArgs = new List>(); + + var importAttrs = info.Attributes; + if ((importAttrs & MethodImportAttributes.BestFitMappingDisable) == MethodImportAttributes.BestFitMappingDisable) + namedArgs.Add(b.MakeNamedArg(dllImportType, "BestFitMapping", false)); + if ((importAttrs & MethodImportAttributes.BestFitMappingEnable) == MethodImportAttributes.BestFitMappingEnable) + namedArgs.Add(b.MakeNamedArg(dllImportType, "BestFitMapping", true)); + + CallingConvention callingConvention; + switch (info.Attributes & MethodImportAttributes.CallingConventionMask) { + case 0: + Debug.WriteLine($"P/Invoke calling convention not set on: {this}"); + callingConvention = 0; + break; + case MethodImportAttributes.CallingConventionCDecl: + callingConvention = CallingConvention.Cdecl; + break; + case MethodImportAttributes.CallingConventionFastCall: + callingConvention = CallingConvention.FastCall; + break; + case MethodImportAttributes.CallingConventionStdCall: + callingConvention = CallingConvention.StdCall; + break; + case MethodImportAttributes.CallingConventionThisCall: + callingConvention = CallingConvention.ThisCall; + break; + case MethodImportAttributes.CallingConventionWinApi: + callingConvention = CallingConvention.Winapi; + break; + default: + throw new NotSupportedException("unknown calling convention"); + } + if (callingConvention != CallingConvention.Winapi) { + var callingConventionType = FindInteropType(nameof(CallingConvention)); + namedArgs.Add(b.MakeNamedArg(dllImportType, "CallingConvention", callingConventionType, (int)callingConvention)); + } + + CharSet charSet = CharSet.None; + switch (info.Attributes & MethodImportAttributes.CharSetMask) { + case MethodImportAttributes.CharSetAnsi: + charSet = CharSet.Ansi; + break; + case MethodImportAttributes.CharSetAuto: + charSet = CharSet.Auto; + break; + case MethodImportAttributes.CharSetUnicode: + charSet = CharSet.Unicode; + break; + } + if (charSet != CharSet.None) { + var charSetType = FindInteropType(nameof(CharSet)); + namedArgs.Add(b.MakeNamedArg(dllImportType, "CharSet", charSetType, (int)charSet)); + } + + if (!info.Name.IsNil && info.Name != def.Name) { + namedArgs.Add(b.MakeNamedArg(dllImportType, + "EntryPoint", KnownTypeCode.String, metadata.GetString(info.Name))); + } + + if ((info.Attributes & MethodImportAttributes.ExactSpelling) == MethodImportAttributes.ExactSpelling) { + namedArgs.Add(b.MakeNamedArg(dllImportType, "ExactSpelling", true)); + } + + if ((implAttributes & MethodImplAttributes.PreserveSig) == MethodImplAttributes.PreserveSig) { + implAttributes &= ~MethodImplAttributes.PreserveSig; + } else { + namedArgs.Add(b.MakeNamedArg(dllImportType, "PreserveSig", true)); + } + + if ((info.Attributes & MethodImportAttributes.SetLastError) == MethodImportAttributes.SetLastError) + namedArgs.Add(b.MakeNamedArg(dllImportType, "SetLastError", true)); + + if ((info.Attributes & MethodImportAttributes.ThrowOnUnmappableCharDisable) == MethodImportAttributes.ThrowOnUnmappableCharDisable) + namedArgs.Add(b.MakeNamedArg(dllImportType, "ThrowOnUnmappableChar", false)); + if ((info.Attributes & MethodImportAttributes.ThrowOnUnmappableCharEnable) == MethodImportAttributes.ThrowOnUnmappableCharEnable) + namedArgs.Add(b.MakeNamedArg(dllImportType, "ThrowOnUnmappableChar", true)); + + b.Add(new DefaultAttribute(dllImportType, positionalArguments, namedArgs)); + } + #endregion + + #region PreserveSigAttribute + if (implAttributes == MethodImplAttributes.PreserveSig) { + b.Add(KnownAttribute.PreserveSig); + implAttributes = 0; + } + #endregion + + #region MethodImplAttribute + if (implAttributes != 0) { + b.Add(KnownAttribute.MethodImpl, new ConstantResolveResult( + Compilation.FindType(new TopLevelTypeName("System.Runtime.CompilerServices", nameof(MethodImplOptions))), + (int)implAttributes + )); + } + #endregion + + b.Add(def.GetCustomAttributes()); + b.AddSecurityAttributes(def.GetDeclarativeSecurityAttributes()); + + return b.Build(); + } + #endregion + + #region Return type attributes + public IReadOnlyList ReturnTypeAttributes { + get { + var attr = LazyInit.VolatileRead(ref this.returnTypeAttributes); + if (attr != null) + return attr; + return LazyInit.GetOrSet(ref this.returnTypeAttributes, DecodeReturnTypeAttributes()); + } + } + + private IAttribute[] DecodeReturnTypeAttributes() + { + var b = new AttributeListBuilder(assembly); + var metadata = assembly.metadata; + var methodDefinition = metadata.GetMethodDefinition(handle); + var parameters = methodDefinition.GetParameters(); + if (parameters.Count > 0) { + var retParam = metadata.GetParameter(parameters.First()); + if (retParam.SequenceNumber == 0) { + b.AddMarshalInfo(retParam.GetMarshallingDescriptor()); + b.Add(retParam.GetCustomAttributes()); + } + } + return b.Build(); + } + #endregion + + public Accessibility Accessibility => GetAccessibility(attributes); + + internal static Accessibility GetAccessibility(MethodAttributes attr) + { + switch (attr & MethodAttributes.MemberAccessMask) { + case MethodAttributes.Public: + return Accessibility.Public; + case MethodAttributes.Assembly: + return Accessibility.Internal; + case MethodAttributes.Private: + return Accessibility.Private; + case MethodAttributes.Family: + return Accessibility.Protected; + case MethodAttributes.FamANDAssem: + return Accessibility.ProtectedAndInternal; + case MethodAttributes.FamORAssem: + return Accessibility.ProtectedOrInternal; + default: + return Accessibility.None; + } + } + + public bool IsStatic => (attributes & MethodAttributes.Static) != 0; + public bool IsAbstract => (attributes & MethodAttributes.Abstract) != 0; + public bool IsSealed => (attributes & (MethodAttributes.Abstract | MethodAttributes.Final | MethodAttributes.NewSlot | MethodAttributes.Static)) == MethodAttributes.Final; + public bool IsVirtual => (attributes & (MethodAttributes.Abstract | MethodAttributes.Virtual | MethodAttributes.NewSlot)) == (MethodAttributes.Virtual | MethodAttributes.NewSlot); + public bool IsOverride => (attributes & (MethodAttributes.NewSlot | MethodAttributes.Static)) == 0; + public bool IsOverridable + => (attributes & (MethodAttributes.Abstract | MethodAttributes.Virtual)) != 0 + && (attributes & MethodAttributes.Final) == 0; + + bool IEntity.IsShadowing => throw new NotImplementedException(); + + public string FullName => $"{DeclaringType?.FullName}.{Name}"; + public string ReflectionName => $"{DeclaringType?.ReflectionName}.{Name}"; + public string Namespace => DeclaringType?.Namespace ?? string.Empty; + + bool IMember.Equals(IMember obj, TypeVisitor typeNormalization) + { + return obj == this; + } + + public IMethod Specialize(TypeParameterSubstitution substitution) + { + return SpecializedMethod.Create(this, substitution); + } + + IMember IMember.Specialize(TypeParameterSubstitution substitution) + { + return SpecializedMethod.Create(this, substitution); + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs new file mode 100644 index 000000000..35a35de9a --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs @@ -0,0 +1,73 @@ +// 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.Generic; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Text; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + sealed class MetadataParameter : IParameter + { + readonly MetadataAssembly assembly; + readonly ParameterHandle handle; + readonly ParameterAttributes attributes; + + public IType Type { get; } + public IParameterizedMember Owner { get; } + + internal MetadataParameter(MetadataAssembly assembly, IParameterizedMember owner, IType type, ParameterHandle handle) + { + this.assembly = assembly; + this.Owner = owner; + this.Type = type; + this.handle = handle; + + var param = assembly.metadata.GetParameter(handle); + this.attributes = param.Attributes; + } + + public EntityHandle MetadataToken => handle; + + public IReadOnlyList Attributes => throw new NotImplementedException(); + + const ParameterAttributes inOut = ParameterAttributes.In | ParameterAttributes.Out; + public bool IsRef => Type.Kind == TypeKind.ByReference && (attributes & inOut) != ParameterAttributes.Out; + public bool IsOut => Type.Kind == TypeKind.ByReference && (attributes & inOut) == ParameterAttributes.Out; + + public bool IsParams => throw new NotImplementedException(); + + public bool IsOptional => (attributes & ParameterAttributes.HasDefault) != 0; + + public string Name => throw new NotImplementedException(); + + bool IVariable.IsConst => false; + + public object ConstantValue => throw new NotImplementedException(); + + SymbolKind ISymbol.SymbolKind => SymbolKind.Parameter; + + public override string ToString() + { + return $"{MetadataTokens.GetToken(handle):X8} {DefaultParameter.ToString(this)}"; + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs index 1546bf93f..2f243e97b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs @@ -82,7 +82,11 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.Kind = TypeKind.Enum; this.EnumUnderlyingType = assembly.Compilation.FindType(underlyingType.ToKnownTypeCode()); } else if (td.IsValueType(metadata)) { - this.Kind = TypeKind.Struct; + if (KnownTypeCode == KnownTypeCode.Void) { + this.Kind = TypeKind.Void; + } else { + this.Kind = TypeKind.Struct; + } } else if (td.IsDelegate(metadata)) { this.Kind = TypeKind.Delegate; } else { @@ -97,8 +101,24 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return $"{MetadataTokens.GetToken(handle):X8} {fullTypeName}"; } - public IReadOnlyList NestedTypes => throw new NotImplementedException(); + ITypeDefinition[] nestedTypes; + + public IReadOnlyList NestedTypes { + get { + var nestedTypes = LazyInit.VolatileRead(ref this.nestedTypes); + if (nestedTypes != null) + return nestedTypes; + var metadata = assembly.metadata; + var nestedTypeCollection = metadata.GetTypeDefinition(handle).GetNestedTypes(); + var nestedTypeList = new List(nestedTypeCollection.Length); + foreach (TypeDefinitionHandle h in nestedTypeCollection) { + nestedTypeList.Add(assembly.GetDefinition(h)); + } + return LazyInit.GetOrSet(ref this.nestedTypes, nestedTypeList.ToArray()); + } + } + #region Members IMember[] members; public IReadOnlyList Members { @@ -132,13 +152,57 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } - public IEnumerable Methods => throw new NotImplementedException(); + IProperty[] properties; + + public IEnumerable Properties { + get { + var properties = LazyInit.VolatileRead(ref this.properties); + if (properties != null) + return properties; + var metadata = assembly.metadata; + var propertyCollection = metadata.GetTypeDefinition(handle).GetProperties(); + var propertyList = new List(propertyCollection.Count); + foreach (PropertyDefinitionHandle h in propertyCollection) { + propertyList.Add(assembly.GetDefinition(h)); + } + return LazyInit.GetOrSet(ref this.properties, propertyList.ToArray()); + } + } - public IEnumerable Properties => throw new NotImplementedException(); + IEvent[] events; - public IEnumerable Events => throw new NotImplementedException(); + public IEnumerable Events { + get { + var events = LazyInit.VolatileRead(ref this.events); + if (events != null) + return events; + var metadata = assembly.metadata; + var eventCollection = metadata.GetTypeDefinition(handle).GetEvents(); + var eventList = new List(eventCollection.Count); + foreach (EventDefinitionHandle h in eventCollection) { + eventList.Add(assembly.GetDefinition(h)); + } + return LazyInit.GetOrSet(ref this.events, eventList.ToArray()); + } + } + IMethod[] methods; + public IEnumerable Methods { + get { + var methods = LazyInit.VolatileRead(ref this.methods); + if (methods != null) + return methods; + var metadata = assembly.metadata; + var methodsCollection = metadata.GetTypeDefinition(handle).GetMethods(); + var methodsList = new List(methodsCollection.Count); + foreach (MethodDefinitionHandle h in methodsCollection) { + methodsList.Add(assembly.GetDefinition(h)); + } + return LazyInit.GetOrSet(ref this.methods, methodsList.ToArray()); + } + } + #endregion public IType DeclaringType => DeclaringTypeDefinition; @@ -161,7 +225,28 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation IReadOnlyList IType.TypeArguments => TypeParameters; - public IEnumerable DirectBaseTypes => throw new NotImplementedException(); + List directBaseTypes; + + public IEnumerable DirectBaseTypes { + get { + var baseTypes = LazyInit.VolatileRead(ref this.directBaseTypes); + if (baseTypes != null) + return baseTypes; + var metadata = assembly.metadata; + var td = metadata.GetTypeDefinition(handle); + var context = new GenericContext(TypeParameters); + var interfaceImplCollection = td.GetInterfaceImplementations(); + baseTypes = new List(1 + interfaceImplCollection.Count); + if (!td.BaseType.IsNil) { + baseTypes.Add(assembly.ResolveType(td.BaseType, context)); + } + foreach (var h in interfaceImplCollection) { + var iface = metadata.GetInterfaceImplementation(h); + baseTypes.Add(assembly.ResolveType(iface.Interface, context, iface.GetCustomAttributes())); + } + return LazyInit.GetOrSet(ref this.directBaseTypes, baseTypes); + } + } public EntityHandle MetadataToken => handle; @@ -285,7 +370,17 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public ICompilation Compilation => assembly.Compilation; - public string FullName => throw new NotImplementedException(); + public string FullName { + get { + if (DeclaringType != null) + return DeclaringType.FullName + "." + Name; + else if (!string.IsNullOrEmpty(this.Namespace)) + return this.Namespace + "." + Name; + else + return Name; + } + } + public string ReflectionName => fullTypeName.ReflectionName; public string Namespace => fullTypeName.TopLevelTypeName.Namespace; @@ -344,6 +439,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IEnumerable GetMethods(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { + if (Kind == TypeKind.Void) + return EmptyList.Instance; if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFiltered(this.Methods, ExtensionMethods.And(m => !m.IsConstructor, filter)); } else { @@ -353,11 +450,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IEnumerable GetMethods(IReadOnlyList typeArguments, Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { + if (Kind == TypeKind.Void) + return EmptyList.Instance; return GetMembersHelper.GetMethods(this, typeArguments, filter, options); } public IEnumerable GetConstructors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.IgnoreInheritedMembers) { + if (Kind == TypeKind.Void) + return EmptyList.Instance; if (ComHelper.IsComImport(this)) { IType coClass = ComHelper.GetCoClass(this); using (var busyLock = BusyManager.Enter(this)) { @@ -377,6 +478,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IEnumerable GetProperties(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { + if (Kind == TypeKind.Void) + return EmptyList.Instance; if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFiltered(this.Properties, filter); } else { @@ -386,6 +489,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IEnumerable GetFields(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { + if (Kind == TypeKind.Void) + return EmptyList.Instance; if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFiltered(this.Fields, filter); } else { @@ -395,6 +500,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IEnumerable GetEvents(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { + if (Kind == TypeKind.Void) + return EmptyList.Instance; if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFiltered(this.Events, filter); } else { @@ -404,6 +511,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IEnumerable GetMembers(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { + if (Kind == TypeKind.Void) + return EmptyList.Instance; if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFiltered(this.Members, filter); } else { @@ -413,6 +522,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IEnumerable GetAccessors(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { + if (Kind == TypeKind.Void) + return EmptyList.Instance; if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) { return GetFilteredAccessors(filter); } else { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs index 100870cb6..6da3f7594 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; @@ -36,23 +37,25 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation IReadOnlyList customAttributes; IReadOnlyList constraints; - public static IReadOnlyList Create(MetadataAssembly assembly, IEntity owner, GenericParameterHandleCollection handles) + public static ITypeParameter[] Create(MetadataAssembly assembly, IEntity owner, GenericParameterHandleCollection handles) { if (handles.Count == 0) - return EmptyList.Instance; + return Empty.Array; var tps = new ITypeParameter[handles.Count]; int i = 0; foreach (var handle in handles) { - tps[i] = Create(assembly, owner, handle); + tps[i] = Create(assembly, owner, i, handle); + i++; } return tps; } - public static MetadataTypeParameter Create(MetadataAssembly assembly, IEntity owner, GenericParameterHandle handle) + public static MetadataTypeParameter Create(MetadataAssembly assembly, IEntity owner, int index, GenericParameterHandle handle) { var metadata = assembly.metadata; var gp = metadata.GetGenericParameter(handle); - return new MetadataTypeParameter(assembly, owner, gp.Index, assembly.GetString(gp.Name), handle, gp.Attributes); + Debug.Assert(gp.Index == index); + return new MetadataTypeParameter(assembly, owner, index, assembly.GetString(gp.Name), handle, gp.Attributes); } private MetadataTypeParameter(MetadataAssembly assembly, IEntity owner, int index, string name, diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs index 7abb95089..298ee55b8 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection; @@ -48,6 +49,7 @@ namespace ICSharpCode.Decompiler.TypeSystem readonly MetadataNamespace rootNamespace; readonly MetadataTypeDefinition[] typeDefs; readonly MetadataField[] fieldDefs; + readonly MetadataMethod[] methodDefs; internal MetadataAssembly(ICompilation compilation, Metadata.PEFile peFile, TypeSystemOptions options) { @@ -67,9 +69,12 @@ namespace ICSharpCode.Decompiler.TypeSystem this.AssemblyName = metadata.GetString(moddef.Name); this.FullAssemblyName = this.AssemblyName; } + this.rootNamespace = new MetadataNamespace(this, null, string.Empty, metadata.GetNamespaceDefinitionRoot()); + // create arrays for resolved entities, indexed by row index this.typeDefs = new MetadataTypeDefinition[metadata.TypeDefinitions.Count + 1]; this.fieldDefs = new MetadataField[metadata.FieldDefinitions.Count + 1]; + this.methodDefs = new MetadataMethod[metadata.MethodDefinitions.Count + 1]; } internal string GetString(StringHandle name) @@ -181,7 +186,14 @@ namespace ICSharpCode.Decompiler.TypeSystem public IMethod GetDefinition(MethodDefinitionHandle handle) { - throw new NotImplementedException(); + int row = MetadataTokens.GetRowNumber(handle); + if (row >= methodDefs.Length) + return null; + var method = LazyInit.VolatileRead(ref methodDefs[row]); + if (method != null || handle.IsNil) + return method; + method = new MetadataMethod(this, handle); + return LazyInit.GetOrSet(ref methodDefs[row], method); } public IProperty GetDefinition(PropertyDefinitionHandle handle) @@ -195,16 +207,199 @@ namespace ICSharpCode.Decompiler.TypeSystem } #endregion - public IMethod ResolveMethod(EntityHandle methodRefDefSpec, GenericContext context = default) + #region Resolve Type + public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, CustomAttributeHandleCollection? typeAttributes = null) { - throw new NotImplementedException(); + return ResolveType(typeRefDefSpec, context, options, typeAttributes); + } + + public IType ResolveType(SRM.EntityHandle typeRefDefSpec, GenericContext context, TypeSystemOptions customOptions, CustomAttributeHandleCollection? typeAttributes = null) + { + return MetadataTypeReference.Resolve(typeRefDefSpec, metadata, TypeProvider, context, customOptions, typeAttributes); + } + + IType ResolveDeclaringType(SRM.EntityHandle declaringTypeReference, GenericContext context) + { + // resolve without substituting dynamic/tuple types + return ResolveType(declaringTypeReference, context, + options & ~(TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple)); + } + #endregion + + #region ResolveMethod + public IMethod ResolveMethod(EntityHandle methodReference, GenericContext context = default) + { + if (methodReference.IsNil) + throw new ArgumentNullException(nameof(methodReference)); + switch (methodReference.Kind) { + case SRM.HandleKind.MethodDefinition: + return ResolveMethodDefinition(metadata, (SRM.MethodDefinitionHandle)methodReference, expandVarArgs: true); + case SRM.HandleKind.MemberReference: + return ResolveMethodReference(metadata, (SRM.MemberReferenceHandle)methodReference, context); + case SRM.HandleKind.MethodSpecification: + var methodSpec = metadata.GetMethodSpecification((SRM.MethodSpecificationHandle)methodReference); + var methodTypeArgs = methodSpec.DecodeSignature(TypeProvider, context); + IMethod method; + if (methodSpec.Method.Kind == SRM.HandleKind.MethodDefinition) { + // generic instance of a methoddef (=generic method in non-generic class in current assembly) + method = ResolveMethodDefinition(metadata, (SRM.MethodDefinitionHandle)methodSpec.Method, expandVarArgs: true); + method = method.Specialize(new TypeParameterSubstitution(context.ClassTypeParameters, methodTypeArgs)); + } else { + method = ResolveMethodReference(metadata, (SRM.MemberReferenceHandle)methodSpec.Method, context, methodTypeArgs); + } + return method; + default: + throw new ArgumentException("HandleKind must be either a MethodDefinition, MemberReference or MethodSpecification", nameof(methodReference)); + } + } + + IMethod ResolveMethodDefinition(SRM.MetadataReader metadata, SRM.MethodDefinitionHandle methodDefHandle, bool expandVarArgs) + { + var method = GetDefinition(methodDefHandle); + if (method == null) { + throw new NotImplementedException(); + } + if (expandVarArgs && method.Parameters.LastOrDefault()?.Type.Kind == TypeKind.ArgList) { + method = new VarArgInstanceMethod(method, EmptyList.Instance); + } + return method; + } + + /// + /// Resolves a method reference. + /// + /// + /// Class type arguments are provided by the declaring type stored in the memberRef. + /// Method type arguments are provided by the caller. + /// + IMethod ResolveMethodReference(SRM.MetadataReader metadata, SRM.MemberReferenceHandle memberRefHandle, GenericContext context, IReadOnlyList methodTypeArguments = null) + { + var memberRef = metadata.GetMemberReference(memberRefHandle); + Debug.Assert(memberRef.GetKind() == SRM.MemberReferenceKind.Method); + SRM.MethodSignature signature; + IReadOnlyList classTypeArguments = null; + IMethod method; + if (memberRef.Parent.Kind == SRM.HandleKind.MethodDefinition) { + method = ResolveMethodDefinition(metadata, (SRM.MethodDefinitionHandle)memberRef.Parent, expandVarArgs: false); + signature = memberRef.DecodeMethodSignature(TypeProvider, context); + } else { + var declaringType = ResolveDeclaringType(memberRef.Parent, context); + var declaringTypeDefinition = declaringType.GetDefinition(); + if (declaringType.TypeArguments.Count > 0) { + classTypeArguments = declaringType.TypeArguments; + } + // Note: declaringType might be parameterized, but the signature is for the original method definition. + // We'll have to search the member directly on declaringTypeDefinition. + string name = metadata.GetString(memberRef.Name); + signature = memberRef.DecodeMethodSignature(TypeProvider, + new GenericContext(declaringTypeDefinition?.TypeParameters)); + if (declaringTypeDefinition != null) { + // Find the set of overloads to search: + IEnumerable methods; + if (name == ".ctor") { + methods = declaringTypeDefinition.GetConstructors(); + } else if (name == ".cctor") { + methods = declaringTypeDefinition.Methods.Where(m => m.IsConstructor && m.IsStatic); + } else { + methods = declaringTypeDefinition.GetMethods(m => m.Name == name, GetMemberOptions.IgnoreInheritedMembers) + .Concat(declaringTypeDefinition.GetAccessors(m => m.Name == name, GetMemberOptions.IgnoreInheritedMembers)); + } + // Determine the expected parameters from the signature: + ImmutableArray parameterTypes; + if (signature.Header.CallingConvention == SRM.SignatureCallingConvention.VarArgs) { + parameterTypes = signature.ParameterTypes + .Take(signature.RequiredParameterCount) + .Concat(new[] { SpecialType.ArgList }) + .ToImmutableArray(); + } else { + parameterTypes = signature.ParameterTypes; + } + // Search for the matching method: + method = null; + foreach (var m in methods) { + if (m.TypeParameters.Count != signature.GenericParameterCount) + continue; + if (CompareSignatures(m.Parameters, parameterTypes) && CompareTypes(m.ReturnType, signature.ReturnType)) { + method = m; + break; + } + } + } else { + method = null; + } + if (method == null) { + method = CreateFakeMethod(declaringType, name, signature); + } + } + if (classTypeArguments != null || methodTypeArguments != null) { + method = method.Specialize(new TypeParameterSubstitution(classTypeArguments, methodTypeArguments)); + } + if (signature.Header.CallingConvention == SRM.SignatureCallingConvention.VarArgs) { + method = new VarArgInstanceMethod(method, signature.ParameterTypes.Skip(signature.RequiredParameterCount)); + } + return method; } - public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context = default, CustomAttributeHandleCollection? typeAttributes = null) + static readonly NormalizeTypeVisitor normalizeTypeVisitor = new NormalizeTypeVisitor { + ReplaceClassTypeParametersWithDummy = true, + ReplaceMethodTypeParametersWithDummy = true, + }; + + static bool CompareTypes(IType a, IType b) { - return MetadataTypeReference.Resolve(typeRefDefSpec, metadata, TypeProvider, context, options, typeAttributes); + IType type1 = a.AcceptVisitor(normalizeTypeVisitor); + IType type2 = b.AcceptVisitor(normalizeTypeVisitor); + return type1.Equals(type2); } + static bool CompareSignatures(IReadOnlyList parameters, ImmutableArray parameterTypes) + { + if (parameterTypes.Length != parameters.Count) + return false; + for (int i = 0; i < parameterTypes.Length; i++) { + if (!CompareTypes(parameterTypes[i], parameters[i].Type)) + return false; + } + return true; + } + + /// + /// Create a dummy IMethod from the specified MethodReference + /// + IMethod CreateFakeMethod(IType declaringType, string name, SRM.MethodSignature signature) + { + SymbolKind symbolKind = SymbolKind.Method; + if (name == ".ctor" || name == ".cctor") + symbolKind = SymbolKind.Constructor; + var m = new FakeMethod(Compilation, symbolKind); + m.DeclaringType = declaringType; + m.Name = name; + m.ReturnType = signature.ReturnType; + m.IsStatic = !signature.Header.IsInstance; + + TypeParameterSubstitution substitution = null; + if (signature.GenericParameterCount > 0) { + var typeParameters = new List(); + for (int i = 0; i < signature.GenericParameterCount; i++) { + typeParameters.Add(new DefaultTypeParameter(m, i)); + } + m.TypeParameters = typeParameters; + substitution = new TypeParameterSubstitution(null, typeParameters); + } + var parameters = new List(); + for (int i = 0; i < signature.RequiredParameterCount; i++) { + var type = signature.ParameterTypes[i]; + if (substitution != null) { + // replace the dummy method type parameters with the owned instances we just created + type = type.AcceptVisitor(substitution); + } + parameters.Add(new DefaultParameter(type, "")); + } + m.Parameters = parameters; + return m; + } + #endregion + #region Module / Assembly attributes IAttribute[] assemblyAttributes; IAttribute[] moduleAttributes; diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs index e987bafa2..5b0fc90ed 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs @@ -144,8 +144,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public IType GetTypeFromSerializedName(string name) { - // TODO: aren't we missing support for assembly-qualified names? - return new GetClassTypeReference(new FullTypeName(name)) + return ReflectionHelper.ParseReflectionName(name) .Resolve(assembly != null ? new SimpleTypeResolveContext(assembly) : new SimpleTypeResolveContext(compilation)); }