From ee3012281d7622a25ba43ab667c53521e161ff6c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 26 Jun 2018 23:19:58 +0200 Subject: [PATCH] Implement MetadataEvent and re-work the representation of attribute arguments. --- .../TypeSystem/TypeSystemLoaderTests.cs | 164 ++++++++---------- .../CSharp/CSharpDecompiler.cs | 53 +++--- .../CSharp/RequiredNamespaceCollector.cs | 26 ++- .../CSharp/Syntax/TypeSystemAstBuilder.cs | 12 +- .../ICSharpCode.Decompiler.csproj | 2 + ICSharpCode.Decompiler/Metadata/Dom.cs | 34 ++++ .../Metadata/MethodSemanticsLookup.cs | 100 +++++++++++ ICSharpCode.Decompiler/SRMExtensions.cs | 7 + ICSharpCode.Decompiler/SRMHacks.cs | 2 +- .../TypeSystem/ApplyAttributeTypeVisitor.cs | 2 +- .../TypeSystem/ComHelper.cs | 16 +- .../TypeSystem/DecompilerTypeSystem.cs | 4 + .../TypeSystem/IAttribute.cs | 6 +- .../Implementation/AttributeListBuilder.cs | 155 +++++++++++------ .../Implementation/CustomAttribute.cs | 69 +++++--- .../Implementation/DefaultAttribute.cs | 43 +++-- .../Implementation/DefaultResolvedEvent.cs | 9 +- .../DefaultUnresolvedAssembly.cs | 4 +- .../DefaultUnresolvedAttribute.cs | 37 ++-- .../Implementation/KnownAttributes.cs | 48 ++++- .../Implementation/MetadataEvent.cs | 147 ++++++++++++++++ .../Implementation/MetadataMethod.cs | 60 ++++--- .../Implementation/MetadataParameter.cs | 15 +- .../Implementation/MetadataProperty.cs | 23 +-- .../Implementation/MetadataTypeDefinition.cs | 41 +++-- .../Implementation/ResolvedAttributeBlob.cs | 34 ++-- .../Implementation/SpecializedEvent.cs | 13 ++ .../UnresolvedSecurityDeclarationBlob.cs | 3 +- .../TypeSystem/MetadataAssembly.cs | 93 +++++++++- .../TypeSystem/TypeSystemExtensions.cs | 10 +- 30 files changed, 854 insertions(+), 378 deletions(-) create mode 100644 ICSharpCode.Decompiler/Metadata/MethodSemanticsLookup.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs diff --git a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs index f42a18298..a2b597c46 100644 --- a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs +++ b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs @@ -18,8 +18,10 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; +using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -32,6 +34,8 @@ using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.TypeSystem { + using AttributeArray = ImmutableArray>; + [TestFixture] public class TypeSystemLoaderTests { @@ -87,7 +91,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem Assert.IsFalse(c.IsAbstract); Assert.IsFalse(c.IsSealed); Assert.IsFalse(c.IsStatic); - Assert.IsFalse(c.IsShadowing); + //Assert.IsFalse(c.IsShadowing); } [Test] @@ -158,17 +162,16 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { var attributes = compilation.MainAssembly.AssemblyAttributes; var typeTest = attributes.Single(a => a.AttributeType.FullName == typeof(TypeTestAttribute).FullName); - Assert.AreEqual(3, typeTest.PositionalArguments.Count); + Assert.AreEqual(3, typeTest.FixedArguments.Length); // first argument is (int)42 - Assert.AreEqual(42, (int)typeTest.PositionalArguments[0].ConstantValue); + Assert.AreEqual(42, (int)typeTest.FixedArguments[0].Value); // second argument is typeof(System.Action<>) - TypeOfResolveResult rt = (TypeOfResolveResult)typeTest.PositionalArguments[1]; - Assert.IsFalse(rt.ReferencedType is ParameterizedType); // rt must not be constructed - it's just an unbound type - Assert.AreEqual("System.Action", rt.ReferencedType.FullName); - Assert.AreEqual(1, rt.ReferencedType.TypeParameterCount); + var ty = (IType)typeTest.FixedArguments[1].Value; + Assert.IsFalse(ty is ParameterizedType); // rt must not be constructed - it's just an unbound type + Assert.AreEqual("System.Action", ty.FullName); + Assert.AreEqual(1, ty.TypeParameterCount); // third argument is typeof(IDictionary>) - rt = (TypeOfResolveResult)typeTest.PositionalArguments[2]; - ParameterizedType crt = (ParameterizedType)rt.ReferencedType; + var crt = (ParameterizedType)typeTest.FixedArguments[2].Value; Assert.AreEqual("System.Collections.Generic.IDictionary", crt.FullName); Assert.AreEqual("System.String", crt.TypeArguments[0].FullName); // we know the name for TestAttribute, but not necessarily the namespace, as NUnit is not in the compilation @@ -185,9 +188,9 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { var attributes = compilation.MainAssembly.AssemblyAttributes; var forwardAttribute = attributes.Single(a => a.AttributeType.FullName == typeof(TypeForwardedToAttribute).FullName); - Assert.AreEqual(1, forwardAttribute.PositionalArguments.Count); - TypeOfResolveResult rt = (TypeOfResolveResult)forwardAttribute.PositionalArguments[0]; - Assert.AreEqual("System.Func`2", rt.ReferencedType.ReflectionName); + Assert.AreEqual(1, forwardAttribute.FixedArguments.Length); + var rt = (IType)forwardAttribute.FixedArguments[0].Value; + Assert.AreEqual("System.Func`2", rt.ReflectionName); } [Test] @@ -564,19 +567,19 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IAttribute attr = GetTypeDefinition(typeof(ExplicitFieldLayoutStruct)).Attributes.Single(); Assert.AreEqual("System.Runtime.InteropServices.StructLayoutAttribute", attr.AttributeType.FullName); - ResolveResult arg1 = attr.PositionalArguments.Single(); + var arg1 = attr.FixedArguments.Single(); Assert.AreEqual("System.Runtime.InteropServices.LayoutKind", arg1.Type.FullName); - Assert.AreEqual((int)LayoutKind.Explicit, arg1.ConstantValue); + Assert.AreEqual((int)LayoutKind.Explicit, arg1.Value); var arg2 = attr.NamedArguments[0]; - Assert.AreEqual("CharSet", arg2.Key.Name); - Assert.AreEqual("System.Runtime.InteropServices.CharSet", arg2.Value.Type.FullName); - Assert.AreEqual((int)CharSet.Unicode, arg2.Value.ConstantValue); + Assert.AreEqual("CharSet", arg2.Name); + Assert.AreEqual("System.Runtime.InteropServices.CharSet", arg2.Type.FullName); + Assert.AreEqual((int)CharSet.Unicode, arg2.Value); var arg3 = attr.NamedArguments[1]; - Assert.AreEqual("Pack", arg3.Key.Name); - Assert.AreEqual("System.Int32", arg3.Value.Type.FullName); - Assert.AreEqual(8, arg3.Value.ConstantValue); + Assert.AreEqual("Pack", arg3.Name); + Assert.AreEqual("System.Int32", arg3.Type.FullName); + Assert.AreEqual(8, arg3.Value); } [Test] @@ -584,15 +587,15 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IField field = GetTypeDefinition(typeof(ExplicitFieldLayoutStruct)).Fields.Single(f => f.Name == "Field0"); Assert.AreEqual("System.Runtime.InteropServices.FieldOffsetAttribute", field.Attributes.Single().AttributeType.FullName); - ResolveResult arg = field.Attributes.Single().PositionalArguments.Single(); + var arg = field.Attributes.Single().FixedArguments.Single(); Assert.AreEqual("System.Int32", arg.Type.FullName); - Assert.AreEqual(0, arg.ConstantValue); + Assert.AreEqual(0, arg.Value); field = GetTypeDefinition(typeof(ExplicitFieldLayoutStruct)).Fields.Single(f => f.Name == "Field100"); Assert.AreEqual("System.Runtime.InteropServices.FieldOffsetAttribute", field.Attributes.Single().AttributeType.FullName); - arg = field.Attributes.Single().PositionalArguments.Single(); + arg = field.Attributes.Single().FixedArguments.Single(); Assert.AreEqual("System.Int32", arg.Type.FullName); - Assert.AreEqual(100, arg.ConstantValue); + Assert.AreEqual(100, arg.Value); } [Test] @@ -601,8 +604,8 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem IMethod method = GetTypeDefinition(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod"); IAttribute dllImport = method.Attributes.Single(); Assert.AreEqual("System.Runtime.InteropServices.DllImportAttribute", dllImport.AttributeType.FullName); - Assert.AreEqual("unmanaged.dll", dllImport.PositionalArguments[0].ConstantValue); - Assert.AreEqual((int)CharSet.Unicode, dllImport.NamedArguments.Single().Value.ConstantValue); + Assert.AreEqual("unmanaged.dll", dllImport.FixedArguments[0].Value); + Assert.AreEqual((int)CharSet.Unicode, dllImport.NamedArguments.Single().Value); } [Test] @@ -621,7 +624,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IMethod method = GetTypeDefinition(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod"); IAttribute marshalAs = method.ReturnTypeAttributes.Single(); - Assert.AreEqual((int)UnmanagedType.Bool, marshalAs.PositionalArguments.Single().ConstantValue); + Assert.AreEqual((int)UnmanagedType.Bool, marshalAs.FixedArguments.Single().Value); } [Test] @@ -887,65 +890,52 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem Assert.AreEqual("ICSharpCode.Decompiler.Tests.TypeSystem.OuterGeneric`1[[`0]]", p.Type.ReflectionName); } - ResolveResult GetParamsAttributeArgument(int index) + CustomAttributeTypedArgument GetParamsAttributeArgument(int index) { ITypeDefinition type = GetTypeDefinition(typeof(ParamsAttribute)); - var arr = (ArrayCreateResolveResult)type.Attributes.Single().PositionalArguments.Single(); - Assert.AreEqual(5, arr.InitializerElements.Count); - Assert.AreEqual(1, arr.SizeArguments.Count); - Assert.AreEqual(5, arr.SizeArguments[0].ConstantValue); - return arr.InitializerElements[index]; - } - - ResolveResult Unbox(ResolveResult resolveResult) - { - ConversionResolveResult crr = (ConversionResolveResult)resolveResult; - Assert.AreEqual(TypeKind.Class, crr.Type.Kind); - Assert.AreEqual("System.Object", crr.Type.FullName); - Assert.AreEqual(Conversion.BoxingConversion, crr.Conversion); - return crr.Input; + var arr = (AttributeArray)type.Attributes.Single().FixedArguments.Single().Value; + Assert.AreEqual(5, arr.Length); + return arr[index]; } - + [Test] public void ParamsAttribute_Integer() { - ResolveResult rr = Unbox(GetParamsAttributeArgument(0)); - Assert.AreEqual("System.Int32", rr.Type.FullName); - Assert.AreEqual(1, rr.ConstantValue); + var arg = GetParamsAttributeArgument(0); + Assert.AreEqual("System.Int32", arg.Type.FullName); + Assert.AreEqual(1, arg.Value); } [Test] public void ParamsAttribute_Enum() { - ResolveResult rr = Unbox(GetParamsAttributeArgument(1)); - Assert.AreEqual("System.StringComparison", rr.Type.FullName); - Assert.AreEqual((int)StringComparison.CurrentCulture, rr.ConstantValue); + var arg = GetParamsAttributeArgument(1); + Assert.AreEqual("System.StringComparison", arg.Type.FullName); + Assert.AreEqual((int)StringComparison.CurrentCulture, arg.Value); } [Test] public void ParamsAttribute_NullReference() { - ResolveResult rr = GetParamsAttributeArgument(2); - Assert.AreEqual("System.Object", rr.Type.FullName); - Assert.IsTrue(rr.IsCompileTimeConstant); - Assert.IsNull(rr.ConstantValue); + var arg = GetParamsAttributeArgument(2); + //Assert.AreEqual("System.Object", arg.Type.FullName); + Assert.IsNull(arg.Value); } [Test] public void ParamsAttribute_Double() { - ResolveResult rr = Unbox(GetParamsAttributeArgument(3)); - Assert.AreEqual("System.Double", rr.Type.FullName); - Assert.AreEqual(4.0, rr.ConstantValue); + var arg = GetParamsAttributeArgument(3); + Assert.AreEqual("System.Double", arg.Type.FullName); + Assert.AreEqual(4.0, arg.Value); } [Test] public void ParamsAttribute_String() { - ConversionResolveResult rr = (ConversionResolveResult)GetParamsAttributeArgument(4); - Assert.AreEqual("System.Object", rr.Type.FullName); - Assert.AreEqual("System.String", rr.Input.Type.FullName); - Assert.AreEqual("Test", rr.Input.ConstantValue); + var arg = GetParamsAttributeArgument(4); + Assert.AreEqual("System.String", arg.Type.FullName); + Assert.AreEqual("Test", arg.Value); } [Test] @@ -956,13 +946,13 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem var attr = prop.Attributes.Single(); Assert.AreEqual(type, attr.AttributeType); - var normalArguments = ((ArrayCreateResolveResult)attr.PositionalArguments.Single()).InitializerElements; - Assert.AreEqual(0, normalArguments.Count); + var elements = (AttributeArray)attr.FixedArguments.Single().Value; + Assert.AreEqual(0, elements.Length); var namedArg = attr.NamedArguments.Single(); - Assert.AreEqual(prop, namedArg.Key); - var arrayElements = ((ArrayCreateResolveResult)namedArg.Value).InitializerElements; - Assert.AreEqual(2, arrayElements.Count); + Assert.AreEqual(prop.Name, namedArg.Name); + var arrayElements = (AttributeArray)namedArg.Value; + Assert.AreEqual(2, arrayElements.Length); } [Test] @@ -978,9 +968,9 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem public void DoubleAttribute_ImplicitNumericConversion() { ITypeDefinition type = GetTypeDefinition(typeof(DoubleAttribute)); - var arg = type.Attributes.Single().PositionalArguments.ElementAt(0); + var arg = type.Attributes.Single().FixedArguments.Single(); Assert.AreEqual("System.Double", arg.Type.ReflectionName); - Assert.AreEqual(1.0, arg.ConstantValue); + Assert.AreEqual(1.0, arg.Value); } [Test, Ignore("interface impls need redesign")] @@ -1488,25 +1478,22 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem var inner = type.GetNestedTypes().Single(t => t.Name == "Inner"); var myAttribute = type.GetNestedTypes().Single(t => t.Name == "MyAttribute"); var typeTypeTestAttr = type.Attributes.Single(a => a.AttributeType.Name == "TypeTestAttribute"); - Assert.AreEqual(42, typeTypeTestAttr.PositionalArguments[0].ConstantValue); - Assert.IsInstanceOf(typeTypeTestAttr.PositionalArguments[1]); - Assert.AreEqual(inner, ((TypeOfResolveResult)typeTypeTestAttr.PositionalArguments[1]).ReferencedType); + Assert.AreEqual(42, typeTypeTestAttr.FixedArguments[0].Value); + Assert.AreEqual(inner, typeTypeTestAttr.FixedArguments[1].Value); var typeMyAttr = type.Attributes.Single(a => a.AttributeType.Name == "MyAttribute"); Assert.AreEqual(myAttribute, typeMyAttr.AttributeType); var prop = type.GetProperties().Single(p => p.Name == "P"); var propTypeTestAttr = prop.Attributes.Single(a => a.AttributeType.Name == "TypeTestAttribute"); - Assert.AreEqual(42, propTypeTestAttr.PositionalArguments[0].ConstantValue); - Assert.IsInstanceOf(propTypeTestAttr.PositionalArguments[1]); - Assert.AreEqual(inner, ((TypeOfResolveResult)propTypeTestAttr.PositionalArguments[1]).ReferencedType); + Assert.AreEqual(42, propTypeTestAttr.FixedArguments[0].Value); + Assert.AreEqual(inner, propTypeTestAttr.FixedArguments[1].Value); var propMyAttr = prop.Attributes.Single(a => a.AttributeType.Name == "MyAttribute"); Assert.AreEqual(myAttribute, propMyAttr.AttributeType); var attributedInner = (ITypeDefinition)type.GetNestedTypes().Single(t => t.Name == "AttributedInner"); var innerTypeTestAttr = attributedInner.Attributes.Single(a => a.AttributeType.Name == "TypeTestAttribute"); - Assert.AreEqual(42, innerTypeTestAttr.PositionalArguments[0].ConstantValue); - Assert.IsInstanceOf(innerTypeTestAttr.PositionalArguments[1]); - Assert.AreEqual(inner, ((TypeOfResolveResult)innerTypeTestAttr.PositionalArguments[1]).ReferencedType); + Assert.AreEqual(42, innerTypeTestAttr.FixedArguments[0].Value); + Assert.AreEqual(inner, innerTypeTestAttr.FixedArguments[1].Value); var innerMyAttr = attributedInner.Attributes.Single(a => a.AttributeType.Name == "MyAttribute"); Assert.AreEqual(myAttribute, innerMyAttr.AttributeType); @@ -1514,9 +1501,8 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem var inner2 = attributedInner2.GetNestedTypes().Single(t => t.Name == "Inner"); var myAttribute2 = attributedInner2.GetNestedTypes().Single(t => t.Name == "MyAttribute"); var inner2TypeTestAttr = attributedInner2.Attributes.Single(a => a.AttributeType.Name == "TypeTestAttribute"); - Assert.AreEqual(43, inner2TypeTestAttr.PositionalArguments[0].ConstantValue); - Assert.IsInstanceOf(inner2TypeTestAttr.PositionalArguments[1]); - Assert.AreEqual(inner2, ((TypeOfResolveResult)inner2TypeTestAttr.PositionalArguments[1]).ReferencedType); + Assert.AreEqual(43, inner2TypeTestAttr.FixedArguments[0].Value); + Assert.AreEqual(inner2, inner2TypeTestAttr.FixedArguments[1].Value); var inner2MyAttr = attributedInner2.Attributes.Single(a => a.AttributeType.Name == "MyAttribute"); Assert.AreEqual(myAttribute2, inner2MyAttr.AttributeType); } @@ -1633,8 +1619,8 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem ITypeDefinition c = compilation.FindType(typeof(void)).GetDefinition(); var attr = c.Attributes.Single(a => a.AttributeType.FullName == "System.SerializableAttribute"); Assert.AreEqual(0, attr.Constructor.Parameters.Count); - Assert.AreEqual(0, attr.PositionalArguments.Count); - Assert.AreEqual(0, attr.NamedArguments.Count); + Assert.AreEqual(0, attr.FixedArguments.Length); + Assert.AreEqual(0, attr.NamedArguments.Length); } [Test] @@ -1643,11 +1629,11 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem ITypeDefinition c = compilation.FindType(typeof(void)).GetDefinition(); var attr = c.Attributes.Single(a => a.AttributeType.FullName == "System.Runtime.InteropServices.StructLayoutAttribute"); Assert.AreEqual(1, attr.Constructor.Parameters.Count); - Assert.AreEqual(1, attr.PositionalArguments.Count); - Assert.AreEqual(0, attr.PositionalArguments[0].ConstantValue); - Assert.AreEqual(1, attr.NamedArguments.Count); - Assert.AreEqual("System.Runtime.InteropServices.StructLayoutAttribute.Size", attr.NamedArguments[0].Key.FullName); - Assert.AreEqual(1, attr.NamedArguments[0].Value.ConstantValue); + Assert.AreEqual(1, attr.FixedArguments.Length); + Assert.AreEqual(0, attr.FixedArguments[0].Value); + Assert.AreEqual(1, attr.NamedArguments.Length); + Assert.AreEqual("Size", attr.NamedArguments[0].Name); + Assert.AreEqual(1, attr.NamedArguments[0].Value); } [Test] @@ -1656,9 +1642,9 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem ITypeDefinition c = compilation.FindType(typeof(void)).GetDefinition(); var attr = c.Attributes.Single(a => a.AttributeType.FullName == "System.Runtime.InteropServices.ComVisibleAttribute"); Assert.AreEqual(1, attr.Constructor.Parameters.Count); - Assert.AreEqual(1, attr.PositionalArguments.Count); - Assert.AreEqual(true, attr.PositionalArguments[0].ConstantValue); - Assert.AreEqual(0, attr.NamedArguments.Count); + Assert.AreEqual(1, attr.FixedArguments.Length); + Assert.AreEqual(true, attr.FixedArguments[0].Value); + Assert.AreEqual(0, attr.NamedArguments.Length); } [Test] diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 150ae6665..436bfb077 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -371,18 +371,17 @@ namespace ICSharpCode.Decompiler.CSharp void DoDecompileModuleAndAssemblyAttributes(DecompileRun decompileRun, ITypeResolveContext decompilationContext, SyntaxTree syntaxTree) { foreach (var a in typeSystem.Compilation.MainAssembly.AssemblyAttributes) { - decompileRun.Namespaces.Add(a.AttributeType.Namespace); - if (a.AttributeType.FullName == typeof(System.Runtime.CompilerServices.TypeForwardedToAttribute).FullName) { - decompileRun.Namespaces.Add(((TypeOfResolveResult)a.PositionalArguments[0]).ReferencedType.Namespace); - } else { - decompileRun.Namespaces.AddRange(a.PositionalArguments.Select(pa => pa.Type.Namespace)); - decompileRun.Namespaces.AddRange(a.NamedArguments.Select(na => na.Value.Type.Namespace)); - } var astBuilder = CreateAstBuilder(decompilationContext); var attrSection = new AttributeSection(astBuilder.ConvertAttribute(a)); attrSection.AttributeTarget = "assembly"; syntaxTree.AddChild(attrSection, SyntaxTree.MemberRole); } + foreach (var a in typeSystem.Compilation.MainAssembly.ModuleAttributes) { + var astBuilder = CreateAstBuilder(decompilationContext); + var attrSection = new AttributeSection(astBuilder.ConvertAttribute(a)); + attrSection.AttributeTarget = "module"; + syntaxTree.AddChild(attrSection, SyntaxTree.MemberRole); + } } void DoDecompileTypes(IEnumerable types, DecompileRun decompileRun, ITypeResolveContext decompilationContext, SyntaxTree syntaxTree) @@ -738,16 +737,16 @@ namespace ICSharpCode.Decompiler.CSharp } if (typeDecl.Members.OfType().Any(idx => idx.PrivateImplementationType.IsNull)) { // Remove the [DefaultMember] attribute if the class contains indexers - RemoveAttribute(typeDecl, new TopLevelTypeName("System.Reflection", "DefaultMemberAttribute")); + RemoveAttribute(typeDecl, KnownAttribute.DefaultMember); } if (settings.IntroduceRefAndReadonlyModifiersOnStructs && typeDecl.ClassType == ClassType.Struct) { - if (RemoveAttribute(typeDecl, new TopLevelTypeName("System.Runtime.CompilerServices", "IsByRefLikeAttribute"))) { + if (RemoveAttribute(typeDecl, KnownAttribute.IsByRefLike)) { typeDecl.Modifiers |= Modifiers.Ref; } - if (RemoveAttribute(typeDecl, new TopLevelTypeName("System.Runtime.CompilerServices", "IsReadOnlyAttribute"))) { + if (RemoveAttribute(typeDecl, KnownAttribute.IsReadOnly)) { typeDecl.Modifiers |= Modifiers.Readonly; } - if (FindAttribute(typeDecl, new TopLevelTypeName("System", "ObsoleteAttribute"), out var attr)) { + if (FindAttribute(typeDecl, KnownAttribute.Obsolete, out var attr)) { if (obsoleteAttributePattern.IsMatch(attr)) { if (attr.Parent is Syntax.AttributeSection section && section.Attributes.Count == 1) section.Remove(); @@ -880,28 +879,28 @@ namespace ICSharpCode.Decompiler.CSharp if (!body.Descendants.Any(d => d is YieldReturnStatement || d is YieldBreakStatement)) { body.Add(new YieldBreakStatement()); } - RemoveAttribute(entityDecl, new TopLevelTypeName("System.Runtime.CompilerServices", "IteratorStateMachineAttribute")); + RemoveAttribute(entityDecl, KnownAttribute.IteratorStateMachine); if (function.StateMachineCompiledWithMono) { - RemoveAttribute(entityDecl, new TopLevelTypeName("System.Diagnostics", "DebuggerHiddenAttribute")); + RemoveAttribute(entityDecl, KnownAttribute.DebuggerHidden); } } if (function.IsAsync) { entityDecl.Modifiers |= Modifiers.Async; - RemoveAttribute(entityDecl, new TopLevelTypeName("System.Runtime.CompilerServices", "AsyncStateMachineAttribute")); - RemoveAttribute(entityDecl, new TopLevelTypeName("System.Diagnostics", "DebuggerStepThroughAttribute")); + RemoveAttribute(entityDecl, KnownAttribute.AsyncStateMachine); + RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough); } } catch (Exception innerException) when (!(innerException is OperationCanceledException)) { throw new DecompilerException(typeSystem.ModuleDefinition, (MethodDefinitionHandle)method.MetadataToken, innerException); } } - bool RemoveAttribute(EntityDeclaration entityDecl, FullTypeName attrName) + bool RemoveAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType) { bool found = false; foreach (var section in entityDecl.Attributes) { foreach (var attr in section.Attributes) { var symbol = attr.Type.GetSymbol(); - if (symbol is ITypeDefinition td && td.FullTypeName == attrName) { + if (symbol is ITypeDefinition td && td.FullTypeName == attributeType.GetTypeName()) { attr.Remove(); found = true; } @@ -913,13 +912,13 @@ namespace ICSharpCode.Decompiler.CSharp return found; } - bool FindAttribute(EntityDeclaration entityDecl, FullTypeName attrName, out Syntax.Attribute attribute) + bool FindAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType, out Syntax.Attribute attribute) { attribute = null; foreach (var section in entityDecl.Attributes) { foreach (var attr in section.Attributes) { var symbol = attr.Type.GetSymbol(); - if (symbol is ITypeDefinition td && td.FullTypeName == attrName) { + if (symbol is ITypeDefinition td && td.FullTypeName == attributeType.GetTypeName()) { attribute = attr; return true; } @@ -931,8 +930,8 @@ namespace ICSharpCode.Decompiler.CSharp void AddDefinesForConditionalAttributes(ILFunction function, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { foreach (var call in function.Descendants.OfType()) { - var attr = call.Method.GetAttribute(new TopLevelTypeName("System.Diagnostics", nameof(ConditionalAttribute))); - var symbolName = attr?.PositionalArguments.FirstOrDefault()?.ConstantValue as string; + var attr = call.Method.GetAttribute(KnownAttribute.Conditional); + var symbolName = attr?.FixedArguments.FirstOrDefault().Value as string; if (symbolName == null || !decompileRun.DefinedSymbols.Add(symbolName)) continue; syntaxTree.InsertChildAfter(null, new PreProcessorDirective(PreProcessorDirectiveType.Define, symbolName), Roles.PreProcessorDirective); @@ -977,7 +976,7 @@ namespace ICSharpCode.Decompiler.CSharp fixedFieldDecl.Variables.Add(new FixedVariableInitializer(field.Name, new PrimitiveExpression(elementCount))); fixedFieldDecl.Variables.Single().CopyAnnotationsFrom(((FieldDeclaration)fieldDecl).Variables.Single()); fixedFieldDecl.CopyAnnotationsFrom(fieldDecl); - RemoveAttribute(fixedFieldDecl, fixedBufferAttributeTypeName); + RemoveAttribute(fixedFieldDecl, KnownAttribute.FixedBuffer); return fixedFieldDecl; } var fieldDefinition = typeSystem.GetMetadata().GetFieldDefinition((FieldDefinitionHandle)field.MetadataToken); @@ -992,16 +991,14 @@ namespace ICSharpCode.Decompiler.CSharp return fieldDecl; } - static readonly FullTypeName fixedBufferAttributeTypeName = new TopLevelTypeName("System.Runtime.CompilerServices", "FixedBufferAttribute"); - internal static bool IsFixedField(IField field, out IType type, out int elementCount) { type = null; elementCount = 0; - IAttribute attr = field.GetAttribute(fixedBufferAttributeTypeName, inherit: false); - if (attr != null && attr.PositionalArguments.Count == 2) { - if (attr.PositionalArguments[0] is TypeOfResolveResult trr && attr.PositionalArguments[1].ConstantValue is int length) { - type = trr.ReferencedType; + IAttribute attr = field.GetAttribute(KnownAttribute.FixedBuffer, inherit: false); + if (attr != null && attr.FixedArguments.Length == 2) { + if (attr.FixedArguments[0].Value is IType trr && attr.FixedArguments[1].Value is int length) { + type = trr; elementCount = length; return true; } diff --git a/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs b/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs index 8cf6daaed..73267de30 100644 --- a/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs +++ b/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs @@ -24,6 +24,8 @@ namespace ICSharpCode.Decompiler.CSharp foreach (var type in typeSystem.MainAssembly.TypeDefinitions) { CollectNamespaces(type, typeSystem, namespaces); } + HandleAttributes(typeSystem.MainAssembly.AssemblyAttributes, namespaces); + HandleAttributes(typeSystem.MainAssembly.ModuleAttributes, namespaces); } public static void CollectNamespaces(IEntity entity, DecompilerTypeSystem typeSystem, HashSet namespaces, bool scanningFullType = false) @@ -138,19 +140,27 @@ namespace ICSharpCode.Decompiler.CSharp } } - static void HandleAttributes(IEnumerable attributes, HashSet namespaces) + public static void HandleAttributes(IEnumerable attributes, HashSet namespaces) { foreach (var attr in attributes) { namespaces.Add(attr.AttributeType.Namespace); - foreach (var arg in attr.PositionalArguments) { - namespaces.Add(arg.Type.Namespace); - if (arg is TypeOfResolveResult torr) - namespaces.Add(torr.ReferencedType.Namespace); + foreach (var arg in attr.FixedArguments) { + HandleAttributeValue(arg.Type, arg.Value, namespaces); } foreach (var arg in attr.NamedArguments) { - namespaces.Add(arg.Value.Type.Namespace); - if (arg.Value is TypeOfResolveResult torr) - namespaces.Add(torr.ReferencedType.Namespace); + HandleAttributeValue(arg.Type, arg.Value, namespaces); + } + } + } + + static void HandleAttributeValue(IType type, object value, HashSet namespaces) + { + CollectNamespacesForTypeReference(type, namespaces); + if (value is IType typeofType) + CollectNamespacesForTypeReference(typeofType, namespaces); + if (value is ImmutableArray> arr) { + foreach (var element in arr) { + HandleAttributeValue(element.Type, element.Value, namespaces); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 3addb4931..4bd99e846 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -434,15 +434,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } else if (mt != null && mt.MemberName.EndsWith("Attribute", StringComparison.Ordinal)) { mt.MemberName = mt.MemberName.Substring(0, mt.MemberName.Length - 9); } - foreach (ResolveResult arg in attribute.PositionalArguments) { - attr.Arguments.Add(ConvertConstantValue(arg)); + foreach (var arg in attribute.FixedArguments) { + attr.Arguments.Add(ConvertConstantValue(arg.Type, arg.Value)); } - if (attribute.NamedArguments.Count > 0) { + if (attribute.NamedArguments.Length > 0) { InitializedObjectResolveResult targetResult = new InitializedObjectResolveResult(attribute.AttributeType); - foreach (var pair in attribute.NamedArguments) { - NamedExpression namedArgument = new NamedExpression(pair.Key.Name, ConvertConstantValue(pair.Value)); + foreach (var namedArg in attribute.NamedArguments) { + NamedExpression namedArgument = new NamedExpression(namedArg.Name, ConvertConstantValue(namedArg.Type, namedArg.Value)); if (AddResolveResultAnnotations) { - namedArgument.AddAnnotation(new MemberResolveResult(targetResult, pair.Key)); + //namedArgument.AddAnnotation(new MemberResolveResult(targetResult, pair.Key)); } attr.Arguments.Add(namedArgument); } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 5fe8d9ef2..9665971b4 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -292,6 +292,7 @@ + @@ -348,6 +349,7 @@ + diff --git a/ICSharpCode.Decompiler/Metadata/Dom.cs b/ICSharpCode.Decompiler/Metadata/Dom.cs index 7f5c5ef7d..3b2bd620e 100644 --- a/ICSharpCode.Decompiler/Metadata/Dom.cs +++ b/ICSharpCode.Decompiler/Metadata/Dom.cs @@ -194,6 +194,40 @@ namespace ICSharpCode.Decompiler.Metadata return default; } + Dictionary typeForwarderLookup; + + /// + /// Finds the type forwarder with the specified name. + /// + public ExportedTypeHandle GetTypeForwarder(FullTypeName typeName) + { + var lookup = LazyInit.VolatileRead(ref typeForwarderLookup); + if (lookup == null) { + lookup = new Dictionary(); + foreach (var handle in Metadata.ExportedTypes) { + var td = Metadata.GetExportedType(handle); + lookup[td.GetFullTypeName(Metadata)] = handle; + } + lookup = LazyInit.GetOrSet(ref typeForwarderLookup, lookup); + } + if (lookup.TryGetValue(typeName, out var resultHandle)) + return resultHandle; + else + return default; + } + + MethodSemanticsLookup methodSemanticsLookup; + + internal MethodSemanticsLookup MethodSemanticsLookup { + get { + var r = LazyInit.VolatileRead(ref methodSemanticsLookup); + if (r != null) + return r; + else + return LazyInit.GetOrSet(ref methodSemanticsLookup, new MethodSemanticsLookup(Metadata)); + } + } + IAssembly TypeSystem.IAssemblyReference.Resolve(ITypeResolveContext context) { return new MetadataAssembly(context.Compilation, this, TypeSystemOptions.Default); diff --git a/ICSharpCode.Decompiler/Metadata/MethodSemanticsLookup.cs b/ICSharpCode.Decompiler/Metadata/MethodSemanticsLookup.cs new file mode 100644 index 000000000..cd2220970 --- /dev/null +++ b/ICSharpCode.Decompiler/Metadata/MethodSemanticsLookup.cs @@ -0,0 +1,100 @@ +// 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; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.Metadata +{ + /// + /// Lookup structure that, for an accessor, can find the associated property/event. + /// + class MethodSemanticsLookup + { + const MethodSemanticsAttributes csharpAccessors = + MethodSemanticsAttributes.Getter | MethodSemanticsAttributes.Setter + | MethodSemanticsAttributes.Adder | MethodSemanticsAttributes.Remover; + + readonly struct Entry : IComparable + { + public readonly MethodSemanticsAttributes Semantics; + public readonly int MethodRowNumber; + public MethodDefinitionHandle Method => MetadataTokens.MethodDefinitionHandle(MethodRowNumber); + public readonly EntityHandle Association; + + public Entry(MethodSemanticsAttributes semantics, MethodDefinitionHandle method, EntityHandle association) + { + Semantics = semantics; + MethodRowNumber = MetadataTokens.GetRowNumber(method); + Association = association; + } + + public int CompareTo(Entry other) + { + return MethodRowNumber.CompareTo(other.MethodRowNumber); + } + } + + // entries, sorted by MethodRowNumber + readonly List entries; + + public MethodSemanticsLookup(MetadataReader metadata, MethodSemanticsAttributes filter = csharpAccessors) + { + if ((filter & MethodSemanticsAttributes.Other) != 0) { + throw new NotSupportedException("SRM doesn't provide access to 'other' accessors"); + } + entries = new List(metadata.GetTableRowCount(TableIndex.MethodSemantics)); + foreach (var propHandle in metadata.PropertyDefinitions) { + var prop = metadata.GetPropertyDefinition(propHandle); + var accessors = prop.GetAccessors(); + AddEntry(MethodSemanticsAttributes.Getter, accessors.Getter, propHandle); + AddEntry(MethodSemanticsAttributes.Setter, accessors.Setter, propHandle); + } + foreach (var eventHandle in metadata.EventDefinitions) { + var ev = metadata.GetEventDefinition(eventHandle); + var accessors = ev.GetAccessors(); + AddEntry(MethodSemanticsAttributes.Adder, accessors.Adder, eventHandle); + AddEntry(MethodSemanticsAttributes.Remover, accessors.Remover, eventHandle); + AddEntry(MethodSemanticsAttributes.Raiser, accessors.Raiser, eventHandle); + } + entries.Sort(); + + void AddEntry(MethodSemanticsAttributes semantics, MethodDefinitionHandle method, EntityHandle association) + { + if ((semantics & filter) == 0 || method.IsNil) + return; + entries.Add(new Entry(semantics, method, association)); + } + } + + public (EntityHandle, MethodSemanticsAttributes) GetSemantics(MethodDefinitionHandle method) + { + int pos = entries.BinarySearch(new Entry(0, method, default(EntityHandle))); + if (pos >= 0) { + return (entries[pos].Association, entries[pos].Semantics); + } else { + return (default(EntityHandle), 0); + } + } + } +} diff --git a/ICSharpCode.Decompiler/SRMExtensions.cs b/ICSharpCode.Decompiler/SRMExtensions.cs index 1eb2df690..25207e7d4 100644 --- a/ICSharpCode.Decompiler/SRMExtensions.cs +++ b/ICSharpCode.Decompiler/SRMExtensions.cs @@ -227,6 +227,13 @@ namespace ICSharpCode.Decompiler } } + public static FullTypeName GetFullTypeName(this ExportedType type, MetadataReader metadata) + { + string ns = type.Namespace.IsNil ? "" : metadata.GetString(type.Namespace); + string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(metadata.GetString(type.Name), out int typeParameterCount); + return new TopLevelTypeName(ns, name, typeParameterCount); + } + public static TType DecodeSignature(this EventDefinition ev, MetadataReader reader, ISignatureTypeProvider provider, TGenericContext genericContext) { switch (ev.Type.Kind) { diff --git a/ICSharpCode.Decompiler/SRMHacks.cs b/ICSharpCode.Decompiler/SRMHacks.cs index f5370a369..972df3f7c 100644 --- a/ICSharpCode.Decompiler/SRMHacks.cs +++ b/ICSharpCode.Decompiler/SRMHacks.cs @@ -44,7 +44,7 @@ namespace ICSharpCode.Decompiler } } - return resultBuilder.ToImmutable(); + return resultBuilder.MoveToImmutable(); } /* diff --git a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs index fa9c5f2ac..608d83137 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs @@ -152,7 +152,7 @@ namespace ICSharpCode.Decompiler.TypeSystem Debug.Assert(elementTypes.Count == tupleCardinality); return new TupleType( compilation, - elementTypes.ToImmutable(), + elementTypes.MoveToImmutable(), elementNames, valueTupleAssembly ); diff --git a/ICSharpCode.Decompiler/TypeSystem/ComHelper.cs b/ICSharpCode.Decompiler/TypeSystem/ComHelper.cs index 2be326cfe..5c646dba2 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ComHelper.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ComHelper.cs @@ -26,11 +26,6 @@ namespace ICSharpCode.Decompiler.TypeSystem /// public static class ComHelper { - static bool IsComAttribute(IAttribute attribute, string name) - { - return attribute.AttributeType.Name == name && attribute.AttributeType.Namespace == "System.Runtime.InteropServices"; - } - /// /// Gets whether the specified type is imported from COM. /// @@ -38,7 +33,7 @@ namespace ICSharpCode.Decompiler.TypeSystem { return typeDefinition != null && typeDefinition.Kind == TypeKind.Interface - && typeDefinition.Attributes.Any(a => IsComAttribute(a, "ComImportAttribute")); + && typeDefinition.GetAttributes(KnownAttribute.ComImport, inherit: false) != null; } /// @@ -50,11 +45,10 @@ namespace ICSharpCode.Decompiler.TypeSystem { if (typeDefinition == null) return SpecialType.UnknownType; - var coClassAttribute = typeDefinition.Attributes.FirstOrDefault(a => IsComAttribute(a, "CoClassAttribute")); - if (coClassAttribute != null && coClassAttribute.PositionalArguments.Count == 1) { - var rr = coClassAttribute.PositionalArguments[0] as TypeOfResolveResult; - if (rr != null) - return rr.ReferencedType; + var coClassAttribute = typeDefinition.GetAttribute(KnownAttribute.CoClass, inherit: false); + if (coClassAttribute != null && coClassAttribute.FixedArguments.Length == 1) { + if (coClassAttribute.FixedArguments[0].Value is IType ty) + return ty; } return SpecialType.UnknownType; } diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index 146b5101c..ad9db6bad 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -58,6 +58,10 @@ namespace ICSharpCode.Decompiler.TypeSystem /// ExtensionMethods = 4, /// + /// Only load the public API into the type system. + /// + OnlyPublicAPI = 8, + /// /// Default settings: all features enabled. /// Default = Dynamic | Tuple | ExtensionMethods diff --git a/ICSharpCode.Decompiler/TypeSystem/IAttribute.cs b/ICSharpCode.Decompiler/TypeSystem/IAttribute.cs index ef6b940e7..504ccf61c 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IAttribute.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IAttribute.cs @@ -17,6 +17,8 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.Collections.Immutable; +using System.Reflection.Metadata; using ICSharpCode.Decompiler.Semantics; namespace ICSharpCode.Decompiler.TypeSystem @@ -53,11 +55,11 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// Gets the positional arguments. /// - IReadOnlyList PositionalArguments { get; } + ImmutableArray> FixedArguments { get; } /// /// Gets the named arguments passed to the attribute. /// - IReadOnlyList> NamedArguments { get; } + ImmutableArray> NamedArguments { get; } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs index 4d51afb13..1951cd4fa 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs @@ -26,6 +26,7 @@ using System.Text; using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Semantics; using System.Runtime.InteropServices; +using System.Linq; namespace ICSharpCode.Decompiler.TypeSystem.Implementation { @@ -67,33 +68,26 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation /// public void Add(KnownAttribute type, KnownTypeCode argType, object argValue) { - Add(type, new ConstantResolveResult(assembly.Compilation.FindType(argType), argValue)); + Add(type, ImmutableArray.Create(new CustomAttributeTypedArgument(assembly.Compilation.FindType(argType), argValue))); } /// - /// Construct a builtin attribute. + /// Construct a builtin attribute with a single positional argument of known type. /// - public void Add(KnownAttribute type, params ResolveResult[] positionalArguments) - { - Add(new DefaultAttribute(assembly.GetAttributeType(type), positionalArguments)); - } - - internal KeyValuePair MakeNamedArg(IType attrType, string name, KnownTypeCode valueType, object value) - { - return MakeNamedArg(attrType, name, assembly.Compilation.FindType(valueType), value); - } - - internal KeyValuePair MakeNamedArg(IType attrType, string name, IType valueType, object value) + public void Add(KnownAttribute type, TopLevelTypeName argType, object argValue) { - var rr = new ConstantResolveResult(valueType, value); - return Implementation.CustomAttribute.MakeNamedArg(assembly.Compilation, attrType, name, rr); + Add(type, ImmutableArray.Create(new CustomAttributeTypedArgument(assembly.Compilation.FindType(argType), argValue))); } - internal KeyValuePair MakeNamedArg(IType attrType, string name, bool value) + /// + /// Construct a builtin attribute. + /// + public void Add(KnownAttribute type, ImmutableArray> fixedArguments) { - return MakeNamedArg(attrType, name, assembly.Compilation.FindType(KnownTypeCode.Boolean), value); + Add(new DefaultAttribute(assembly.GetAttributeType(type), fixedArguments, + ImmutableArray.Create>())); } - + #region MarshalAsAttribute (ConvertMarshalInfo) internal void AddMarshalInfo(BlobHandle marshalInfo) { @@ -106,34 +100,30 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation IAttribute ConvertMarshalInfo(SRM.BlobReader marshalInfo) { - IType marshalAsAttributeType = assembly.GetAttributeType(KnownAttribute.MarshalAs); + var b = new AttributeBuilder(assembly, KnownAttribute.MarshalAs); IType unmanagedTypeType = assembly.Compilation.FindType(new TopLevelTypeName(InteropServices, nameof(UnmanagedType))); int type = marshalInfo.ReadByte(); - var positionalArguments = new ResolveResult[] { - new ConstantResolveResult(unmanagedTypeType, type) - }; - var namedArguments = new List>(); - + b.AddFixedArg(unmanagedTypeType, type); + int size; switch (type) { case 0x1e: // FixedArray if (!marshalInfo.TryReadCompressedInteger(out size)) size = 0; - namedArguments.Add(MakeNamedArg(marshalAsAttributeType, "SizeConst", KnownTypeCode.Int32, size)); + b.AddNamedArg("SizeConst", KnownTypeCode.Int32, size); if (marshalInfo.RemainingBytes > 0) { type = marshalInfo.ReadByte(); if (type != 0x66) // None - namedArguments.Add(MakeNamedArg(marshalAsAttributeType, "ArraySubType", unmanagedTypeType, type)); + b.AddNamedArg("ArraySubType", unmanagedTypeType, type); } break; case 0x1d: // SafeArray if (marshalInfo.RemainingBytes > 0) { VarEnum varType = (VarEnum)marshalInfo.ReadByte(); if (varType != VarEnum.VT_EMPTY) { - var varEnumType = assembly.Compilation.FindType(new TopLevelTypeName(InteropServices, nameof(VarEnum))); - namedArguments.Add(MakeNamedArg(marshalAsAttributeType, - "SafeArraySubType", varEnumType, (int)varType)); + var varEnumType = new TopLevelTypeName(InteropServices, nameof(VarEnum)); + b.AddNamedArg("SafeArraySubType", varEnumType, (int)varType); } } break; @@ -144,19 +134,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation type = 0x66; // Cecil uses NativeType.None as default. } if (type != 0x50) { // Max - namedArguments.Add(MakeNamedArg(marshalAsAttributeType, - "ArraySubType", unmanagedTypeType, type)); + b.AddNamedArg("ArraySubType", unmanagedTypeType, type); } int sizeParameterIndex = marshalInfo.TryReadCompressedInteger(out int value) ? value : -1; size = marshalInfo.TryReadCompressedInteger(out value) ? value : -1; int sizeParameterMultiplier = marshalInfo.TryReadCompressedInteger(out value) ? value : -1; if (size >= 0) { - namedArguments.Add(MakeNamedArg(marshalAsAttributeType, - "SizeConst", KnownTypeCode.Int32, size)); + b.AddNamedArg("SizeConst", KnownTypeCode.Int32, size); } if (sizeParameterMultiplier != 0 && sizeParameterIndex >= 0) { - namedArguments.Add(MakeNamedArg(marshalAsAttributeType, - "SizeParamIndex", KnownTypeCode.Int16, (short)sizeParameterIndex)); + b.AddNamedArg("SizeParamIndex", KnownTypeCode.Int16, (short)sizeParameterIndex); } break; case 0x2c: // CustomMarshaler @@ -165,21 +152,18 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation string managedType = marshalInfo.ReadSerializedString(); string cookie = marshalInfo.ReadSerializedString(); if (managedType != null) { - namedArguments.Add(MakeNamedArg(marshalAsAttributeType, - "MarshalType", KnownTypeCode.String, managedType)); + b.AddNamedArg("MarshalType", KnownTypeCode.String, managedType); } if (!string.IsNullOrEmpty(cookie)) { - namedArguments.Add(MakeNamedArg(marshalAsAttributeType, - "MarshalCookie", KnownTypeCode.String, cookie)); + b.AddNamedArg("MarshalCookie", KnownTypeCode.String, cookie); } break; case 0x17: // FixedSysString - namedArguments.Add(MakeNamedArg(marshalAsAttributeType, - "SizeConst", KnownTypeCode.Int32, marshalInfo.ReadCompressedInteger())); + b.AddNamedArg("SizeConst", KnownTypeCode.Int32, marshalInfo.ReadCompressedInteger()); break; } - return new DefaultAttribute(marshalAsAttributeType, positionalArguments, namedArguments); + return b.Build(); } #endregion @@ -239,7 +223,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public void AddSecurityAttributes(DeclarativeSecurityAttribute secDecl) { var securityActionType = assembly.Compilation.FindType(new TopLevelTypeName("System.Security.Permissions", "SecurityAction")); - var securityAction = new ConstantResolveResult(securityActionType, (int)secDecl.Action); + var securityAction = new CustomAttributeTypedArgument(securityActionType, (int)secDecl.Action); var metadata = assembly.metadata; var reader = metadata.GetBlobReader(secDecl.PermissionSet); if (reader.ReadByte() == '.') { @@ -255,20 +239,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } - private void ReadXmlSecurityAttribute(ref SRM.BlobReader reader, ConstantResolveResult securityAction) + private void ReadXmlSecurityAttribute(ref SRM.BlobReader reader, CustomAttributeTypedArgument securityAction) { string xml = reader.ReadUTF16(reader.RemainingBytes); - var permissionSetAttributeType = assembly.GetAttributeType(KnownAttribute.PermissionSet); - Add(new DefaultAttribute( - permissionSetAttributeType, - positionalArguments: new ResolveResult[] { securityAction }, - namedArguments: new[] { - MakeNamedArg(permissionSetAttributeType, "XML", assembly.Compilation.FindType(KnownTypeCode.String), xml) - } - )); + var b = new AttributeBuilder(assembly, KnownAttribute.PermissionSet); + b.AddFixedArg(securityAction); + b.AddNamedArg("XML", KnownTypeCode.String, xml); } - private IAttribute ReadBinarySecurityAttribute(ref SRM.BlobReader reader, ResolveResult securityActionRR) + private IAttribute ReadBinarySecurityAttribute(ref SRM.BlobReader reader, CustomAttributeTypedArgument securityAction) { string attributeTypeName = reader.ReadSerializedString(); IType attributeType = assembly.Compilation.FindType(new FullTypeName(attributeTypeName)); @@ -282,8 +261,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return new DefaultAttribute( attributeType, - positionalArguments: new ResolveResult[] { securityActionRR }, - namedArguments: CustomAttribute.ConvertNamedArguments(assembly.Compilation, attributeType, namedArgs)); + fixedArguments: ImmutableArray.Create(securityAction), + namedArguments: namedArgs); } #endregion @@ -295,4 +274,70 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return attributes.ToArray(); } } + + struct AttributeBuilder + { + readonly ICompilation compilation; + readonly IType attributeType; + ImmutableArray>.Builder fixedArgs; + ImmutableArray>.Builder namedArgs; + + public AttributeBuilder(MetadataAssembly assembly, KnownAttribute attributeType) + : this(assembly, assembly.GetAttributeType(attributeType)) + { + } + + public AttributeBuilder(MetadataAssembly assembly, IType attributeType) + { + this.compilation = assembly.Compilation; + this.attributeType = attributeType; + this.fixedArgs = ImmutableArray.CreateBuilder>(); + this.namedArgs = ImmutableArray.CreateBuilder>(); + } + + public void AddFixedArg(CustomAttributeTypedArgument arg) + { + fixedArgs.Add(arg); + } + + public void AddFixedArg(KnownTypeCode type, object value) + { + AddFixedArg(compilation.FindType(type), value); + } + + public void AddFixedArg(TopLevelTypeName type, object value) + { + AddFixedArg(compilation.FindType(type), value); + } + + public void AddFixedArg(IType type, object value) + { + fixedArgs.Add(new CustomAttributeTypedArgument(type, value)); + } + + public void AddNamedArg(string name, KnownTypeCode type, object value) + { + AddNamedArg(name, compilation.FindType(type), value); + } + + public void AddNamedArg(string name, TopLevelTypeName type, object value) + { + AddNamedArg(name, compilation.FindType(type), value); + } + + public void AddNamedArg(string name, IType type, object value) + { + CustomAttributeNamedArgumentKind kind; + if (attributeType.GetFields(f => f.Name == name, GetMemberOptions.ReturnMemberDefinitions).Any()) + kind = CustomAttributeNamedArgumentKind.Field; + else + kind = CustomAttributeNamedArgumentKind.Property; + namedArgs.Add(new CustomAttributeNamedArgument(name, kind, type, value)); + } + + public IAttribute Build() + { + return new DefaultAttribute(attributeType, fixedArgs.ToImmutable(), namedArgs.ToImmutable()); + } + } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/CustomAttribute.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/CustomAttribute.cs index db99e81c5..9897639af 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/CustomAttribute.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/CustomAttribute.cs @@ -21,7 +21,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using System.Text; +using System.Reflection.Metadata; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Util; using SRM = System.Reflection.Metadata; @@ -38,8 +38,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IMethod Constructor { get; } // lazy-loaded: - IReadOnlyList positionalArguments; - IReadOnlyList> namedArguments; + SRM.CustomAttributeValue value; + bool valueDecoded; internal CustomAttribute(MetadataAssembly assembly, IMethod attrCtor, SRM.CustomAttributeHandle handle) { @@ -53,34 +53,30 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IType AttributeType => Constructor.DeclaringType; - public IReadOnlyList PositionalArguments { + public ImmutableArray> FixedArguments { get { - var args = LazyInit.VolatileRead(ref this.positionalArguments); - if (args != null) - return args; DecodeValue(); - return this.positionalArguments; + return value.FixedArguments; } } - public IReadOnlyList> NamedArguments { + public ImmutableArray> NamedArguments { get { - var namedArgs = LazyInit.VolatileRead(ref this.namedArguments); - if (namedArgs != null) - return namedArgs; DecodeValue(); - return this.namedArguments; + return value.NamedArguments; } } void DecodeValue() { - var metadata = assembly.metadata; - var attr = metadata.GetCustomAttribute(handle); - var attrVal = attr.DecodeValue(assembly.TypeProvider); - LazyInit.GetOrSet(ref this.positionalArguments, ConvertArguments(attrVal.FixedArguments)); - LazyInit.GetOrSet(ref this.namedArguments, - ConvertNamedArguments(assembly.Compilation, AttributeType, attrVal.NamedArguments)); + lock (this) { + if (!valueDecoded) { + var metadata = assembly.metadata; + var attr = metadata.GetCustomAttribute(handle); + value = attr.DecodeValue(assembly.TypeProvider); + valueDecoded = true; + } + } } internal static KeyValuePair MakeNamedArg(ICompilation compilation, IType attrType, string name, ResolveResult rr) @@ -101,21 +97,46 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return new KeyValuePair(field, rr); } + internal static KeyValuePair MakeNamedArg(ICompilation compilation, IType attrType, SRM.CustomAttributeNamedArgumentKind kind, string name, ResolveResult rr) + { + if (kind == CustomAttributeNamedArgumentKind.Field) { + var field = attrType.GetFields(f => f.Name == name).FirstOrDefault(); + if (field != null) { + return new KeyValuePair(field, rr); + } + } + if (kind == CustomAttributeNamedArgumentKind.Property) { + var prop = attrType.GetProperties(f => f.Name == name).FirstOrDefault(); + if (prop != null) { + return new KeyValuePair(prop, rr); + } + } + var fakeField = new FakeField(compilation) { + DeclaringType = attrType, + Name = name, + ReturnType = rr.Type + }; + return new KeyValuePair(fakeField, rr); + } + internal static IReadOnlyList> ConvertNamedArguments( ICompilation compilation, IType attributeType, ImmutableArray> namedArgs) { var arr = new KeyValuePair[namedArgs.Length]; for (int i = 0; i < arr.Length; i++) { var namedArg = namedArgs[i]; - arr[i] = MakeNamedArg(compilation, attributeType, namedArg.Name, ConvertArgument(namedArg.Type, namedArg.Value)); + arr[i] = MakeNamedArg(compilation, attributeType, namedArg.Kind, namedArg.Name, + ConvertArgument(compilation, namedArg.Type, namedArg.Value)); } return arr; } - private static ResolveResult ConvertArgument(IType type, object value) + private static ResolveResult ConvertArgument(ICompilation compilation, IType type, object value) { if (value is ImmutableArray> arr) { - return new ArrayCreateResolveResult(type, null, ConvertArguments(arr)); + var arrSize = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), arr.Length); + return new ArrayCreateResolveResult(type, new[] { arrSize }, + ConvertArguments(compilation, type, arr)); } else if (value is IType valueType) { return new TypeOfResolveResult(type, valueType); } else { @@ -123,9 +144,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } - private static IReadOnlyList ConvertArguments(ImmutableArray> arr) + private static IReadOnlyList ConvertArguments(ICompilation compilation, IType type, ImmutableArray> arr) { - return arr.SelectArray(arg => ConvertArgument(arg.Type, arg.Value)); + return arr.SelectArray(arg => ConvertArgument(compilation, type ?? arg.Type, arg.Value)); } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultAttribute.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultAttribute.cs index b34c9af73..e5bef0a92 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultAttribute.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultAttribute.cs @@ -18,7 +18,9 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; +using System.Reflection.Metadata; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Util; @@ -30,30 +32,33 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public class DefaultAttribute : IAttribute { readonly IType attributeType; - readonly IReadOnlyList positionalArguments; - readonly IReadOnlyList> namedArguments; volatile IMethod constructor; - - public DefaultAttribute(IType attributeType, IReadOnlyList positionalArguments = null, - IReadOnlyList> namedArguments = null) + + public ImmutableArray> FixedArguments { get; } + public ImmutableArray> NamedArguments { get; } + + public DefaultAttribute(IType attributeType, + ImmutableArray> fixedArguments, + ImmutableArray> namedArguments) { if (attributeType == null) throw new ArgumentNullException("attributeType"); this.attributeType = attributeType; - this.positionalArguments = positionalArguments ?? EmptyList.Instance; - this.namedArguments = namedArguments ?? EmptyList>.Instance; + this.FixedArguments = fixedArguments; + this.NamedArguments = namedArguments; } - - public DefaultAttribute(IMethod constructor, IReadOnlyList positionalArguments = null, - IReadOnlyList> namedArguments = null) + + public DefaultAttribute(IMethod constructor, + ImmutableArray> fixedArguments, + ImmutableArray> namedArguments) { if (constructor == null) throw new ArgumentNullException("constructor"); this.constructor = constructor; this.attributeType = constructor.DeclaringType ?? SpecialType.UnknownType; - this.positionalArguments = positionalArguments ?? EmptyList.Instance; - this.namedArguments = namedArguments ?? EmptyList>.Instance; - if (this.positionalArguments.Count != constructor.Parameters.Count) { + this.FixedArguments = fixedArguments; + this.NamedArguments = namedArguments; + if (fixedArguments.Length != constructor.Parameters.Count) { throw new ArgumentException("Positional argument count must match the constructor's parameter count"); } } @@ -66,8 +71,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation get { IMethod ctor = this.constructor; if (ctor == null) { - foreach (IMethod candidate in this.AttributeType.GetConstructors(m => m.Parameters.Count == positionalArguments.Count)) { - if (candidate.Parameters.Select(p => p.Type).SequenceEqual(this.PositionalArguments.Select(a => a.Type))) { + foreach (IMethod candidate in this.AttributeType.GetConstructors(m => m.Parameters.Count == FixedArguments.Length)) { + if (candidate.Parameters.Select(p => p.Type).SequenceEqual(this.FixedArguments.Select(a => a.Type))) { ctor = candidate; break; } @@ -77,13 +82,5 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return ctor; } } - - public IReadOnlyList PositionalArguments { - get { return positionalArguments; } - } - - public IReadOnlyList> NamedArguments { - get { return namedArguments; } - } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedEvent.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedEvent.cs index f305ed62b..33b3ec4e0 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedEvent.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedEvent.cs @@ -59,14 +59,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override IMember Specialize(TypeParameterSubstitution substitution) { - if (TypeParameterSubstitution.Identity.Equals(substitution) - || DeclaringType.TypeParameterCount == 0) - { - return this; - } - if (substitution.MethodTypeArguments != null && substitution.MethodTypeArguments.Count > 0) - substitution = new TypeParameterSubstitution(substitution.ClassTypeArguments, EmptyList.Instance); - return new SpecializedEvent(this, substitution); + return SpecializedEvent.Create(this, substitution); } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs index 5962458e8..218eefe31 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs @@ -385,8 +385,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation from attr in this.AssemblyAttributes where attr.AttributeType.Name == "InternalsVisibleToAttribute" && attr.AttributeType.Namespace == "System.Runtime.CompilerServices" - && attr.PositionalArguments.Count == 1 - select GetShortName(attr.PositionalArguments.Single().ConstantValue as string) + && attr.FixedArguments.Length == 1 + select GetShortName(attr.FixedArguments.Single().Value as string) ).ToArray(); } return internalsVisibleTo; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAttribute.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAttribute.cs index 718cb06d8..9c4bd53e5 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAttribute.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAttribute.cs @@ -18,6 +18,8 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Reflection.Metadata; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Util; @@ -186,7 +188,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation readonly IReadOnlyList positionalArguments; // cannot use ProjectedList because KeyValuePair is value type - IReadOnlyList> namedArguments; + //IReadOnlyList> namedArguments; IMethod constructor; volatile bool constructorResolved; @@ -231,33 +233,18 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return null; } - public IReadOnlyList PositionalArguments { - get { return positionalArguments; } - } - - public IReadOnlyList> NamedArguments { - get { - var namedArgs = LazyInit.VolatileRead(ref this.namedArguments); - if (namedArgs != null) { - return namedArgs; - } else { - var newNamedArgs = new List>(); - foreach (var pair in unresolved.NamedArguments) { - IMember member = pair.Key.Resolve(context); - if (member != null) { - ResolveResult val = pair.Value.Resolve(context); - newNamedArgs.Add(new KeyValuePair(member, val)); - } - } - return LazyInit.GetOrSet(ref this.namedArguments, newNamedArgs); - } - } - } - public ICompilation Compilation { get { return context.Compilation; } } - + + IType IAttribute.AttributeType => throw new NotImplementedException(); + + IMethod IAttribute.Constructor => throw new NotImplementedException(); + + ImmutableArray> IAttribute.FixedArguments => throw new NotImplementedException(); + + ImmutableArray> IAttribute.NamedArguments => throw new NotImplementedException(); + public override string ToString() { if (positionalArguments.Count == 0) diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs index 80684db82..1bc609682 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs @@ -1,14 +1,30 @@ -using System; -using System.Collections.Generic; +// Copyright (c) 2010-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.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Text; -namespace ICSharpCode.Decompiler.TypeSystem.Implementation +namespace ICSharpCode.Decompiler.TypeSystem { - enum KnownAttribute + public enum KnownAttribute { /// /// Not a known attribute @@ -22,21 +38,32 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation Extension, Dynamic, TupleElementNames, + Conditional, + Obsolete, + IsReadOnly, + DebuggerHidden, + DebuggerStepThrough, // Assembly attributes: AssemblyVersion, InternalsVisibleTo, + TypeForwardedTo, // Type attributes: Serializable, ComImport, + CoClass, StructLayout, DefaultMember, + IsByRefLike, + IteratorStateMachine, + AsyncStateMachine, // Field attributes: FieldOffset, NonSerialized, DecimalConstant, + FixedBuffer, // Method attributes: DllImport, @@ -65,18 +92,29 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation new TopLevelTypeName("System.Runtime.CompilerServices", nameof(ExtensionAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(DynamicAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(TupleElementNamesAttribute)), + new TopLevelTypeName("System.Diagnostics", nameof(ConditionalAttribute)), + new TopLevelTypeName("System", nameof(ObsoleteAttribute)), + new TopLevelTypeName("System.Runtime.CompilerServices", "IsReadOnlyAttribute"), + new TopLevelTypeName("System.Diagnostics", nameof(DebuggerHiddenAttribute)), + new TopLevelTypeName("System.Diagnostics", nameof(DebuggerStepThroughAttribute)), // Assembly attributes: new TopLevelTypeName("System.Reflection", nameof(AssemblyVersionAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(InternalsVisibleToAttribute)), + new TopLevelTypeName("System.Runtime.CompilerServices", nameof(TypeForwardedToAttribute)), // Type attributes: new TopLevelTypeName("System", nameof(SerializableAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(ComImportAttribute)), + new TopLevelTypeName("System.Runtime.InteropServices", nameof(CoClassAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(StructLayoutAttribute)), new TopLevelTypeName("System.Reflection", nameof(DefaultMemberAttribute)), + new TopLevelTypeName("System.Runtime.CompilerServices", "IsByRefLikeAttribute"), + new TopLevelTypeName("System.Reflection", nameof(IteratorStateMachineAttribute)), + new TopLevelTypeName("System.Reflection", nameof(AsyncStateMachineAttribute)), // Field attributes: new TopLevelTypeName("System.Runtime.InteropServices", nameof(FieldOffsetAttribute)), new TopLevelTypeName("System", nameof(NonSerializedAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(DecimalConstantAttribute)), + new TopLevelTypeName("System.Runtime.CompilerServices", nameof(FixedBufferAttribute)), // Method attributes: new TopLevelTypeName("System.Runtime.InteropServices", nameof(DllImportAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(PreserveSigAttribute)), diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs new file mode 100644 index 000000000..6b00b6d47 --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs @@ -0,0 +1,147 @@ +// 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 ICSharpCode.Decompiler.Semantics; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + sealed class MetadataEvent : IEvent + { + readonly MetadataAssembly assembly; + readonly EventDefinitionHandle handle; + readonly EventAccessors accessors; + readonly string name; + + // lazy-loaded: + IAttribute[] customAttributes; + IType returnType; + + internal MetadataEvent(MetadataAssembly assembly, EventDefinitionHandle handle) + { + Debug.Assert(assembly != null); + Debug.Assert(!handle.IsNil); + this.assembly = assembly; + this.handle = handle; + + var metadata = assembly.metadata; + var ev = metadata.GetEventDefinition(handle); + accessors = ev.GetAccessors(); + name = metadata.GetString(ev.Name); + } + + public EntityHandle MetadataToken => handle; + public string Name => name; + + SymbolKind ISymbol.SymbolKind => SymbolKind.Event; + + public bool CanAdd => !accessors.Adder.IsNil; + public bool CanRemove => !accessors.Remover.IsNil; + public bool CanInvoke => !accessors.Raiser.IsNil; + public IMethod AddAccessor => assembly.GetDefinition(accessors.Adder); + public IMethod RemoveAccessor => assembly.GetDefinition(accessors.Remover); + public IMethod InvokeAccessor => assembly.GetDefinition(accessors.Raiser); + IMethod AnyAccessor => assembly.GetDefinition(accessors.GetAny()); + + #region Signature (ReturnType + Parameters) + public IType ReturnType { + get { + var returnType = LazyInit.VolatileRead(ref this.returnType); + if (returnType != null) + return returnType; + var metadata = assembly.metadata; + var ev = metadata.GetEventDefinition(handle); + var context = new GenericContext(DeclaringTypeDefinition?.TypeParameters); + returnType = assembly.ResolveType(ev.Type, context, ev.GetCustomAttributes()); + return LazyInit.GetOrSet(ref this.returnType, returnType); + } + } + #endregion + + public bool IsExplicitInterfaceImplementation => AnyAccessor?.IsExplicitInterfaceImplementation ?? false; + public IEnumerable ImplementedInterfaceMembers => GetInterfaceMembersFromAccessor(AnyAccessor); + + internal static IEnumerable GetInterfaceMembersFromAccessor(IMethod method) + { + if (method == null) + return EmptyList.Instance; + return method.ImplementedInterfaceMembers.Select(m => ((IMethod)m).AccessorOwner).Where(m => m != null); + } + + public ITypeDefinition DeclaringTypeDefinition => AnyAccessor?.DeclaringTypeDefinition; + public IType DeclaringType => AnyAccessor?.DeclaringType; + IMember IMember.MemberDefinition => this; + TypeParameterSubstitution IMember.Substitution => TypeParameterSubstitution.Identity; + + #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()); + } + } + + IAttribute[] DecodeAttributes() + { + var b = new AttributeListBuilder(assembly); + var metadata = assembly.metadata; + var eventDef = metadata.GetEventDefinition(handle); + b.Add(eventDef.GetCustomAttributes()); + return b.Build(); + } + #endregion + + public Accessibility Accessibility => AnyAccessor?.Accessibility ?? Accessibility.None; + public bool IsStatic => AnyAccessor?.IsStatic ?? false; + public bool IsAbstract => AnyAccessor?.IsAbstract ?? false; + public bool IsSealed => AnyAccessor?.IsSealed ?? false; + public bool IsVirtual => AnyAccessor?.IsVirtual ?? false; + public bool IsOverride => AnyAccessor?.IsOverride ?? false; + public bool IsOverridable => AnyAccessor?.IsOverridable ?? false; + + bool IEntity.IsShadowing => AnyAccessor?.IsShadowing ?? false; + + public IAssembly ParentAssembly => assembly; + public ICompilation Compilation => assembly.Compilation; + + 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 this == obj; + } + + public IMember Specialize(TypeParameterSubstitution substitution) + { + return SpecializedEvent.Create(this, substitution); + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs index 363fe2024..860900446 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs @@ -39,6 +39,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation readonly MethodAttributes attributes; readonly SymbolKind symbolKind; readonly ITypeParameter[] typeParameters; + readonly EntityHandle accessorOwner; public bool IsExtensionMethod { get; } // lazy-loaded fields: @@ -61,7 +62,11 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.attributes = def.Attributes; this.symbolKind = SymbolKind.Method; - if ((attributes & (MethodAttributes.SpecialName | MethodAttributes.RTSpecialName)) != 0) { + var (accessorOwner, semanticsAttribute) = assembly.PEFile.MethodSemanticsLookup.GetSemantics(handle); + if (semanticsAttribute != 0) { + this.symbolKind = SymbolKind.Accessor; + this.accessorOwner = accessorOwner; + } else if ((attributes & (MethodAttributes.SpecialName | MethodAttributes.RTSpecialName)) != 0) { string name = this.Name; if (name == ".cctor" || name == ".ctor") this.symbolKind = SymbolKind.Constructor; @@ -104,7 +109,18 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public bool HasBody => assembly.metadata.GetMethodDefinition(handle).HasBody(); - public IMember AccessorOwner => throw new NotImplementedException(); + public IMember AccessorOwner { + get { + if (accessorOwner.IsNil) + return null; + if (accessorOwner.Kind == HandleKind.PropertyDefinition) + return assembly.GetDefinition((PropertyDefinitionHandle)accessorOwner); + else if (accessorOwner.Kind == HandleKind.EventDefinition) + return assembly.GetDefinition((EventDefinitionHandle)accessorOwner); + else + return null; + } + } #region Signature (ReturnType + Parameters) public IReadOnlyList Parameters { @@ -231,18 +247,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation #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 dllImport = new AttributeBuilder(assembly, KnownAttribute.DllImport); + dllImport.AddFixedArg(KnownTypeCode.String, + metadata.GetString(metadata.GetModuleReference(info.Module).Name)); var importAttrs = info.Attributes; if ((importAttrs & MethodImportAttributes.BestFitMappingDisable) == MethodImportAttributes.BestFitMappingDisable) - namedArgs.Add(b.MakeNamedArg(dllImportType, "BestFitMapping", false)); + dllImport.AddNamedArg("BestFitMapping", KnownTypeCode.Boolean, false); if ((importAttrs & MethodImportAttributes.BestFitMappingEnable) == MethodImportAttributes.BestFitMappingEnable) - namedArgs.Add(b.MakeNamedArg(dllImportType, "BestFitMapping", true)); + dllImport.AddNamedArg("BestFitMapping", KnownTypeCode.Boolean, true); CallingConvention callingConvention; switch (info.Attributes & MethodImportAttributes.CallingConventionMask) { @@ -270,7 +283,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } if (callingConvention != CallingConvention.Winapi) { var callingConventionType = FindInteropType(nameof(CallingConvention)); - namedArgs.Add(b.MakeNamedArg(dllImportType, "CallingConvention", callingConventionType, (int)callingConvention)); + dllImport.AddNamedArg("CallingConvention", callingConventionType, (int)callingConvention); } CharSet charSet = CharSet.None; @@ -287,33 +300,32 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } if (charSet != CharSet.None) { var charSetType = FindInteropType(nameof(CharSet)); - namedArgs.Add(b.MakeNamedArg(dllImportType, "CharSet", charSetType, (int)charSet)); + dllImport.AddNamedArg("CharSet", charSetType, (int)charSet); } if (!info.Name.IsNil && info.Name != def.Name) { - namedArgs.Add(b.MakeNamedArg(dllImportType, - "EntryPoint", KnownTypeCode.String, metadata.GetString(info.Name))); + dllImport.AddNamedArg("EntryPoint", KnownTypeCode.String, metadata.GetString(info.Name)); } if ((info.Attributes & MethodImportAttributes.ExactSpelling) == MethodImportAttributes.ExactSpelling) { - namedArgs.Add(b.MakeNamedArg(dllImportType, "ExactSpelling", true)); + dllImport.AddNamedArg("ExactSpelling", KnownTypeCode.Boolean, true); } if ((implAttributes & MethodImplAttributes.PreserveSig) == MethodImplAttributes.PreserveSig) { implAttributes &= ~MethodImplAttributes.PreserveSig; } else { - namedArgs.Add(b.MakeNamedArg(dllImportType, "PreserveSig", true)); + dllImport.AddNamedArg("PreserveSig", KnownTypeCode.Boolean, true); } if ((info.Attributes & MethodImportAttributes.SetLastError) == MethodImportAttributes.SetLastError) - namedArgs.Add(b.MakeNamedArg(dllImportType, "SetLastError", true)); + dllImport.AddNamedArg("SetLastError", KnownTypeCode.Boolean, true); if ((info.Attributes & MethodImportAttributes.ThrowOnUnmappableCharDisable) == MethodImportAttributes.ThrowOnUnmappableCharDisable) - namedArgs.Add(b.MakeNamedArg(dllImportType, "ThrowOnUnmappableChar", false)); + dllImport.AddNamedArg("ThrowOnUnmappableChar", KnownTypeCode.Boolean, false); if ((info.Attributes & MethodImportAttributes.ThrowOnUnmappableCharEnable) == MethodImportAttributes.ThrowOnUnmappableCharEnable) - namedArgs.Add(b.MakeNamedArg(dllImportType, "ThrowOnUnmappableChar", true)); + dllImport.AddNamedArg("ThrowOnUnmappableChar", KnownTypeCode.Boolean, true); - b.Add(new DefaultAttribute(dllImportType, positionalArguments, namedArgs)); + b.Add(dllImport.Build()); } #endregion @@ -326,10 +338,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation #region MethodImplAttribute if (implAttributes != 0) { - b.Add(KnownAttribute.MethodImpl, new ConstantResolveResult( - Compilation.FindType(new TopLevelTypeName("System.Runtime.CompilerServices", nameof(MethodImplOptions))), + b.Add(KnownAttribute.MethodImpl, + new TopLevelTypeName("System.Runtime.CompilerServices", nameof(MethodImplOptions)), (int)implAttributes - )); + ); } #endregion @@ -392,7 +404,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation 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 IsVirtual => (attributes & (MethodAttributes.Abstract | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final)) == (MethodAttributes.Virtual | MethodAttributes.NewSlot); public bool IsOverride => (attributes & (MethodAttributes.NewSlot | MethodAttributes.Virtual)) == MethodAttributes.Virtual; public bool IsOverridable => (attributes & (MethodAttributes.Abstract | MethodAttributes.Virtual)) != 0 diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs index 4b80d1c22..dbf523768 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs @@ -84,7 +84,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation 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 IsOptional => (attributes & ParameterAttributes.HasDefault) != 0; + public bool IsOptional => (attributes & ParameterAttributes.Optional) != 0; public bool IsParams { get { @@ -109,7 +109,18 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation bool IVariable.IsConst => false; - public object ConstantValue => throw new NotImplementedException(); + public object ConstantValue { + get { + var metadata = assembly.metadata; + var propertyDef = metadata.GetParameter(handle); + var constantHandle = propertyDef.GetDefaultValue(); + if (constantHandle.IsNil) + return null; + var constant = metadata.GetConstant(constantHandle); + var blobReader = metadata.GetBlobReader(constant.Value); + return blobReader.ReadConstant(constant.TypeCode); + } + } SymbolKind ISymbol.SymbolKind => SymbolKind.Parameter; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs index bf918ded4..114edfce1 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs @@ -75,6 +75,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IMethod Getter => assembly.GetDefinition(getterHandle); public IMethod Setter => assembly.GetDefinition(setterHandle); + IMethod AnyAccessor => assembly.GetDefinition(getterHandle.IsNil ? setterHandle : getterHandle); public bool IsIndexer => symbolKind == SymbolKind.Indexer; public SymbolKind SymbolKind => symbolKind; @@ -118,8 +119,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } #endregion - public bool IsExplicitInterfaceImplementation => (Getter ?? Setter)?.IsExplicitInterfaceImplementation ?? false; - public IEnumerable ImplementedInterfaceMembers => GetInterfaceMembersFromAccessor(Getter ?? Setter); + public bool IsExplicitInterfaceImplementation => AnyAccessor?.IsExplicitInterfaceImplementation ?? false; + public IEnumerable ImplementedInterfaceMembers => GetInterfaceMembersFromAccessor(AnyAccessor); internal static IEnumerable GetInterfaceMembersFromAccessor(IMethod method) { @@ -128,8 +129,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return method.ImplementedInterfaceMembers.Select(m => ((IMethod)m).AccessorOwner).Where(m => m != null); } - public ITypeDefinition DeclaringTypeDefinition => (Getter ?? Setter)?.DeclaringTypeDefinition; - public IType DeclaringType => (Getter ?? Setter)?.DeclaringType; + public ITypeDefinition DeclaringTypeDefinition => AnyAccessor?.DeclaringTypeDefinition; + public IType DeclaringType => AnyAccessor?.DeclaringType; IMember IMember.MemberDefinition => this; TypeParameterSubstitution IMember.Substitution => TypeParameterSubstitution.Identity; @@ -202,14 +203,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } #endregion - public bool IsStatic => (Getter ?? Setter)?.IsStatic ?? false; - public bool IsAbstract => (Getter ?? Setter)?.IsAbstract ?? false; - public bool IsSealed => (Getter ?? Setter)?.IsSealed ?? false; - public bool IsVirtual => (Getter ?? Setter)?.IsVirtual ?? false; - public bool IsOverride => (Getter ?? Setter)?.IsOverride ?? false; - public bool IsOverridable => (Getter ?? Setter)?.IsOverridable ?? false; + public bool IsStatic => AnyAccessor?.IsStatic ?? false; + public bool IsAbstract => AnyAccessor?.IsAbstract ?? false; + public bool IsSealed => AnyAccessor?.IsSealed ?? false; + public bool IsVirtual => AnyAccessor?.IsVirtual ?? false; + public bool IsOverride => AnyAccessor?.IsOverride ?? false; + public bool IsOverridable => AnyAccessor?.IsOverridable ?? false; - bool IEntity.IsShadowing => (Getter ?? Setter)?.IsShadowing ?? false; + bool IEntity.IsShadowing => AnyAccessor?.IsShadowing ?? false; public IAssembly ParentAssembly => assembly; public ICompilation Compilation => assembly.Compilation; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs index 0756a0ed3..2ac98d47b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs @@ -168,7 +168,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation var propertyCollection = metadata.GetTypeDefinition(handle).GetProperties(); var propertyList = new List(propertyCollection.Count); foreach (PropertyDefinitionHandle h in propertyCollection) { - propertyList.Add(assembly.GetDefinition(h)); + var property = metadata.GetPropertyDefinition(h); + var accessors = property.GetAccessors(); + bool getterVisible = !accessors.Getter.IsNil && assembly.IsVisible(metadata.GetMethodDefinition(accessors.Getter).Attributes); + bool setterVisible = !accessors.Setter.IsNil && assembly.IsVisible(metadata.GetMethodDefinition(accessors.Setter).Attributes); + if (getterVisible || setterVisible) { + propertyList.Add(assembly.GetDefinition(h)); + } } return LazyInit.GetOrSet(ref this.properties, propertyList.ToArray()); } @@ -183,7 +189,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation var eventCollection = metadata.GetTypeDefinition(handle).GetEvents(); var eventList = new List(eventCollection.Count); foreach (EventDefinitionHandle h in eventCollection) { - eventList.Add(assembly.GetDefinition(h)); + var ev = metadata.GetEventDefinition(h); + var accessors = ev.GetAccessors(); + if (accessors.Adder.IsNil) + continue; + var addMethod = metadata.GetMethodDefinition(accessors.Adder); + if (assembly.IsVisible(addMethod.Attributes)) { + eventList.Add(assembly.GetDefinition(h)); + } } return LazyInit.GetOrSet(ref this.events, eventList.ToArray()); } @@ -311,29 +324,21 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation var layout = typeDefinition.GetLayout(); LayoutKind defaultLayoutKind = Kind == TypeKind.Struct ? LayoutKind.Sequential : LayoutKind.Auto; if (layoutKind != defaultLayoutKind || charSet != CharSet.Ansi || layout.PackingSize > 0 || layout.Size > 0) { - var structLayoutAttributeType = Compilation.FindType(KnownAttribute.StructLayout); - var layoutKindType = Compilation.FindType(new TopLevelTypeName("System.Runtime.InteropServices", "LayoutKind")); - var positionalArguments = new ResolveResult[] { - new ConstantResolveResult(layoutKindType, (int)layoutKind) - }; - var namedArguments = new List>(3); + var structLayout = new AttributeBuilder(assembly, KnownAttribute.StructLayout); + structLayout.AddFixedArg( + new TopLevelTypeName("System.Runtime.InteropServices", "LayoutKind"), + (int)layoutKind); if (charSet != CharSet.Ansi) { var charSetType = Compilation.FindType(new TopLevelTypeName("System.Runtime.InteropServices", "CharSet")); - namedArguments.Add(b.MakeNamedArg(structLayoutAttributeType, - "CharSet", charSetType, (int)charSet)); + structLayout.AddNamedArg("CharSet", charSetType, (int)charSet); } if (layout.PackingSize > 0) { - namedArguments.Add(b.MakeNamedArg(structLayoutAttributeType, - "Pack", KnownTypeCode.Int32, (int)layout.PackingSize)); + structLayout.AddNamedArg("Pack", KnownTypeCode.Int32, (int)layout.PackingSize); } if (layout.Size > 0) { - namedArguments.Add(b.MakeNamedArg(structLayoutAttributeType, - "Size", KnownTypeCode.Int32, (int)layout.Size)); + structLayout.AddNamedArg("Size", KnownTypeCode.Int32, (int)layout.Size); } - b.Add(new DefaultAttribute( - structLayoutAttributeType, - positionalArguments, namedArguments - )); + b.Add(structLayout.Build()); } #endregion diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/ResolvedAttributeBlob.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/ResolvedAttributeBlob.cs index 03914dac6..e8feacfb3 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/ResolvedAttributeBlob.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/ResolvedAttributeBlob.cs @@ -26,7 +26,9 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; +using System.Reflection.Metadata; using System.Threading; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Util; @@ -74,7 +76,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return constructor; } } - + + IType IAttribute.AttributeType => throw new NotImplementedException(); + + IMethod IAttribute.Constructor => throw new NotImplementedException(); + + ImmutableArray> IAttribute.FixedArguments => throw new NotImplementedException(); + + ImmutableArray> IAttribute.NamedArguments => throw new NotImplementedException(); + IMethod ResolveConstructor() { var parameterTypes = ctorParameterTypes.Resolve(context); @@ -92,28 +102,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return null; } - public IReadOnlyList PositionalArguments { - get { - var result = LazyInit.VolatileRead(ref this.positionalArguments); - if (result != null) { - return result; - } - DecodeBlob(); - return positionalArguments; - } - } - - public IReadOnlyList> NamedArguments { - get { - var result = LazyInit.VolatileRead(ref this.namedArguments); - if (result != null) { - return result; - } - DecodeBlob(); - return namedArguments; - } - } - public override string ToString() { return "[" + attributeType.ToString() + "(...)]"; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedEvent.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedEvent.cs index 5e68861dc..d40a42b2b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedEvent.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedEvent.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using ICSharpCode.Decompiler.Util; + namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// @@ -23,6 +25,17 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation /// public class SpecializedEvent : SpecializedMember, IEvent { + public static IEvent Create(IEvent ev, TypeParameterSubstitution substitution) + { + if (TypeParameterSubstitution.Identity.Equals(substitution) + || ev.DeclaringType.TypeParameterCount == 0) { + return ev; + } + if (substitution.MethodTypeArguments != null && substitution.MethodTypeArguments.Count > 0) + substitution = new TypeParameterSubstitution(substitution.ClassTypeArguments, EmptyList.Instance); + return new SpecializedEvent(ev, substitution); + } + readonly IEvent eventDefinition; public SpecializedEvent(IEvent eventDefinition, TypeParameterSubstitution substitution) diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/UnresolvedSecurityDeclarationBlob.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/UnresolvedSecurityDeclarationBlob.cs index 19f67e321..0a4396d34 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/UnresolvedSecurityDeclarationBlob.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/UnresolvedSecurityDeclarationBlob.cs @@ -108,10 +108,11 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation namedArgs.Add(namedArg); } + throw new NotImplementedException(); /* attributes[i] = new DefaultAttribute( attributeType, positionalArguments: new ResolveResult[] { securityActionRR }, - namedArguments: namedArgs); + namedArguments: namedArgs);*/ } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs index ed3053b16..452a227cc 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs @@ -51,6 +51,7 @@ namespace ICSharpCode.Decompiler.TypeSystem readonly MetadataField[] fieldDefs; readonly MetadataMethod[] methodDefs; readonly MetadataProperty[] propertyDefs; + readonly MetadataEvent[] eventDefs; internal MetadataAssembly(ICompilation compilation, Metadata.PEFile peFile, TypeSystemOptions options) { @@ -77,6 +78,7 @@ namespace ICSharpCode.Decompiler.TypeSystem this.fieldDefs = new MetadataField[metadata.FieldDefinitions.Count + 1]; this.methodDefs = new MetadataMethod[metadata.MethodDefinitions.Count + 1]; this.propertyDefs = new MetadataProperty[metadata.PropertyDefinitions.Count + 1]; + this.eventDefs = new MetadataEvent[metadata.EventDefinitions.Count + 1]; } internal string GetString(StringHandle name) @@ -99,6 +101,13 @@ namespace ICSharpCode.Decompiler.TypeSystem public ITypeDefinition GetTypeDefinition(TopLevelTypeName topLevelTypeName) { var typeDefHandle = PEFile.GetTypeDefinition(topLevelTypeName); + if (typeDefHandle.IsNil) { + var forwarderHandle = PEFile.GetTypeForwarder(topLevelTypeName); + if (!forwarderHandle.IsNil) { + var forwarder = metadata.GetExportedType(forwarderHandle); + return ResolveForwardedType(forwarder).GetDefinition(); + } + } return GetDefinition(typeDefHandle); } #endregion @@ -109,7 +118,7 @@ namespace ICSharpCode.Decompiler.TypeSystem if (this == assembly) return true; foreach (string shortName in GetInternalsVisibleTo()) { - if (assembly.AssemblyName == shortName) + if (string.Equals(assembly.AssemblyName, shortName, StringComparison.OrdinalIgnoreCase)) return true; } return false; @@ -212,7 +221,14 @@ namespace ICSharpCode.Decompiler.TypeSystem public IEvent GetDefinition(EventDefinitionHandle handle) { - throw new NotImplementedException(); + int row = MetadataTokens.GetRowNumber(handle); + if (row >= methodDefs.Length) + return null; + var ev = LazyInit.VolatileRead(ref eventDefs[row]); + if (ev != null || handle.IsNil) + return ev; + ev = new MetadataEvent(this, handle); + return LazyInit.GetOrSet(ref eventDefs[row], ev); } #endregion @@ -431,6 +447,8 @@ namespace ICSharpCode.Decompiler.TypeSystem if (assembly.Version != null) { b.Add(KnownAttribute.AssemblyVersion, KnownTypeCode.String, assembly.Version.ToString()); } + + AddTypeForwarderAttributes(ref b); } return LazyInit.GetOrSet(ref this.assemblyAttributes, b.Build()); } @@ -446,9 +464,57 @@ namespace ICSharpCode.Decompiler.TypeSystem return attrs; var b = new AttributeListBuilder(this); b.Add(metadata.GetCustomAttributes(Handle.ModuleDefinition)); + if (!metadata.IsAssembly) { + AddTypeForwarderAttributes(ref b); + } return LazyInit.GetOrSet(ref this.moduleAttributes, b.Build()); } } + + void AddTypeForwarderAttributes(ref AttributeListBuilder b) + { + foreach (ExportedTypeHandle t in metadata.ExportedTypes) { + var type = metadata.GetExportedType(t); + if (type.IsForwarder) { + b.Add(KnownAttribute.TypeForwardedTo, KnownTypeCode.Type, ResolveForwardedType(type)); + } + } + } + + IType ResolveForwardedType(ExportedType forwarder) + { + IAssembly assembly; + switch (forwarder.Implementation.Kind) { + case HandleKind.AssemblyFile: + assembly = this; + break; + case HandleKind.ExportedType: + throw new NotImplementedException(); + case HandleKind.AssemblyReference: + var asmRef = metadata.GetAssemblyReference((AssemblyReferenceHandle)forwarder.Implementation); + string shortName = metadata.GetString(asmRef.Name); + assembly = null; + foreach (var asm in Compilation.Assemblies) { + if (string.Equals(asm.AssemblyName, shortName, StringComparison.OrdinalIgnoreCase)) { + assembly = asm; + break; + } + } + break; + default: + throw new NotSupportedException(); + } + var typeName = forwarder.GetFullTypeName(metadata); + using (var busyLock = BusyManager.Enter(this)) { + if (busyLock.Success) { + var td = assembly.GetTypeDefinition(typeName); + if (td != null) { + return td; + } + } + } + return new UnknownType(typeName); + } #endregion #region Attribute Helpers @@ -479,15 +545,32 @@ namespace ICSharpCode.Decompiler.TypeSystem var attr = LazyInit.VolatileRead(ref knownAttributes[(int)type]); if (attr != null) return attr; - attr = new DefaultAttribute(GetAttributeType(type)); + attr = new DefaultAttribute(GetAttributeType(type), + ImmutableArray.Create>(), + ImmutableArray.Create>()); return LazyInit.GetOrSet(ref knownAttributes[(int)type], attr); } #endregion #region Visibility Filter - internal bool IsVisible(FieldAttributes attributes) + internal bool IncludeInternalMembers => (options & TypeSystemOptions.OnlyPublicAPI) == 0; + + internal bool IsVisible(FieldAttributes att) { - return true; + att &= FieldAttributes.FieldAccessMask; + return IncludeInternalMembers + || att == FieldAttributes.Public + || att == FieldAttributes.Family + || att == FieldAttributes.FamORAssem; + } + + internal bool IsVisible(MethodAttributes att) + { + att &= MethodAttributes.MemberAccessMask; + return IncludeInternalMembers + || att == MethodAttributes.Public + || att == MethodAttributes.Family + || att == MethodAttributes.FamORAssem; } #endregion } diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index 2295b2641..e24a8f910 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -498,7 +498,7 @@ namespace ICSharpCode.Decompiler.TypeSystem /// If inherit is true, an from the entity itself will be returned if possible; /// and the base entity will only be searched if none exists. /// - public static IAttribute GetAttribute(this IEntity entity, FullTypeName attributeType, bool inherit = true) + public static IAttribute GetAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit = true) { return GetAttributes(entity, attributeType, inherit).FirstOrDefault(); } @@ -516,13 +516,13 @@ namespace ICSharpCode.Decompiler.TypeSystem /// Returns the list of attributes that were found. /// If inherit is true, attributes from the entity itself are returned first; followed by attributes inherited from the base entity. /// - public static IEnumerable GetAttributes(this IEntity entity, FullTypeName attributeType, bool inherit = true) + public static IEnumerable GetAttributes(this IEntity entity, KnownAttribute attributeType, bool inherit = true) { if (entity == null) throw new ArgumentNullException("entity"); return GetAttributes(entity, attrType => { ITypeDefinition typeDef = attrType.GetDefinition(); - return typeDef != null && typeDef.FullTypeName == attributeType; + return typeDef != null && typeDef.FullTypeName == attributeType.GetTypeName(); }, inherit); } @@ -589,14 +589,12 @@ namespace ICSharpCode.Decompiler.TypeSystem #endregion #region IsCompilerGenerated - static readonly FullTypeName compilerGeneratedAttributeTypeName = new FullTypeName("System.Runtime.CompilerServices.CompilerGeneratedAttribute"); - public static bool IsCompilerGenereated(this IEntity entity) { if (entity == null) throw new ArgumentNullException(nameof(entity)); - return entity.GetAttribute(compilerGeneratedAttributeTypeName) != null; + return entity.GetAttribute(KnownAttribute.CompilerGenerated) != null; } #endregion