Browse Source

Merge branch 'srm' of https://github.com/icsharpcode/ILSpy into ts-in-ui

pull/1198/head
Siegfried Pammer 7 years ago
parent
commit
e371881f4b
  1. 1
      BuildTools/appveyor-install.ps1
  2. 1
      BuildTools/update-assemblyinfo.ps1
  3. 81
      ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs
  4. 8
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  5. 18
      ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs
  6. 5
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs
  7. 29
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs
  8. 13
      ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs
  9. 91
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  10. 26
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  11. 72
      ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs
  12. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  13. 38
      ICSharpCode.Decompiler/IL/ILReader.cs
  14. 15
      ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs
  15. 3
      ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs
  16. 33
      ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs
  17. 5
      ICSharpCode.Decompiler/NRExtensions.cs
  18. 2
      ICSharpCode.Decompiler/TypeSystem/ComHelper.cs
  19. 3
      ICSharpCode.Decompiler/TypeSystem/IEntity.cs
  20. 5
      ICSharpCode.Decompiler/TypeSystem/IMethod.cs
  21. 4
      ICSharpCode.Decompiler/TypeSystem/IParameter.cs
  22. 4
      ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs
  23. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedEntity.cs
  24. 4
      ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs
  25. 4
      ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs
  26. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedMethod.cs
  27. 4
      ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs
  28. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedTypeParameter.cs
  29. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedParameter.cs
  30. 4
      ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs
  31. 4
      ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs
  32. 12
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs
  33. 14
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs
  34. 24
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs
  35. 12
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs
  36. 12
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs
  37. 12
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs
  38. 11
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs
  39. 6
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMember.cs
  40. 8
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs
  41. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs
  42. 43
      ICSharpCode.Decompiler/TypeSystem/InheritanceHelper.cs
  43. 173
      ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs
  44. 11
      ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs
  45. 540
      ICSharpCode.Decompiler/Util/ResXResourceWriter.cs
  46. 526
      ICSharpCode.Decompiler/Util/ResourcesFile.cs
  47. 109
      ILSpy.AddIn/AssemblyFileFinder.cs
  48. 3
      ILSpy.AddIn/Commands/OpenCodeItemCommand.cs
  49. 9
      ILSpy.AddIn/Commands/OpenILSpyCommand.cs
  50. 1
      ILSpy.AddIn/ILSpy.AddIn.csproj
  51. 11
      ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs
  52. 8
      ILSpy/Languages/CSharpLanguage.cs
  53. 47
      ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs
  54. 3
      appveyor.yml
  55. 99
      doc/Resources.txt

1
BuildTools/appveyor-install.ps1

@ -3,6 +3,7 @@ $ErrorActionPreference = "Stop" @@ -3,6 +3,7 @@ $ErrorActionPreference = "Stop"
$baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463";
$baseCommitRev = 1;
# make sure this list matches artifacts-only branches list in appveyor.yml!
$masterBranches = @("master", "3.2.x");
$globalAssemblyInfoTemplateFile = "ILSpy/Properties/AssemblyInfo.template.cs";

1
BuildTools/update-assemblyinfo.ps1

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
$baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463";
$baseCommitRev = 1;
# make sure this list matches artifacts-only branches list in appveyor.yml!
$masterBranches = @("master", "3.2.x");
$globalAssemblyInfoTemplateFile = "ILSpy/Properties/AssemblyInfo.template.cs";

81
ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs

@ -106,7 +106,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -106,7 +106,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
Assert.IsFalse(method.IsVirtual);
Assert.IsFalse(method.IsStatic);
Assert.AreEqual(0, method.Parameters.Count);
Assert.AreEqual(0, method.Attributes.Count);
Assert.AreEqual(0, method.GetAttributes().Count());
Assert.IsTrue(method.HasBody);
Assert.IsNull(method.AccessorOwner);
}
@ -117,7 +117,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -117,7 +117,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
ITypeDefinition testClass = GetTypeDefinition(typeof(DynamicTest));
Assert.AreEqual(SpecialType.Dynamic, testClass.Fields.Single(f => f.Name == "DynamicField").ReturnType);
Assert.AreEqual(SpecialType.Dynamic, testClass.Properties.Single().ReturnType);
Assert.AreEqual(0, testClass.Properties.Single().Attributes.Count);
Assert.AreEqual(0, testClass.Properties.Single().GetAttributes().Count());
}
[Test]
@ -153,7 +153,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -153,7 +153,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
ITypeDefinition testClass = GetTypeDefinition(typeof(DynamicTest));
IMethod m1 = testClass.Methods.Single(me => me.Name == "DynamicGenerics1");
Assert.AreEqual(0, m1.Parameters[0].Attributes.Count);
Assert.AreEqual(0, m1.Parameters[0].GetAttributes().Count());
}
[Test]
@ -550,7 +550,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -550,7 +550,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
[Test]
public void SerializableAttribute()
{
IAttribute attr = GetTypeDefinition(typeof(NonCustomAttributes)).Attributes.Single();
IAttribute attr = GetTypeDefinition(typeof(NonCustomAttributes)).GetAttributes().Single();
Assert.AreEqual("System.SerializableAttribute", attr.AttributeType.FullName);
}
@ -558,13 +558,13 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -558,13 +558,13 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
public void NonSerializedAttribute()
{
IField field = GetTypeDefinition(typeof(NonCustomAttributes)).Fields.Single(f => f.Name == "NonSerializedField");
Assert.AreEqual("System.NonSerializedAttribute", field.Attributes.Single().AttributeType.FullName);
Assert.AreEqual("System.NonSerializedAttribute", field.GetAttributes().Single().AttributeType.FullName);
}
[Test]
public void ExplicitStructLayoutAttribute()
{
IAttribute attr = GetTypeDefinition(typeof(ExplicitFieldLayoutStruct)).Attributes.Single();
IAttribute attr = GetTypeDefinition(typeof(ExplicitFieldLayoutStruct)).GetAttributes().Single();
Assert.AreEqual("System.Runtime.InteropServices.StructLayoutAttribute", attr.AttributeType.FullName);
var arg1 = attr.FixedArguments.Single();
Assert.AreEqual("System.Runtime.InteropServices.LayoutKind", arg1.Type.FullName);
@ -585,14 +585,14 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -585,14 +585,14 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
public void FieldOffsetAttribute()
{
IField field = GetTypeDefinition(typeof(ExplicitFieldLayoutStruct)).Fields.Single(f => f.Name == "Field0");
Assert.AreEqual("System.Runtime.InteropServices.FieldOffsetAttribute", field.Attributes.Single().AttributeType.FullName);
var arg = field.Attributes.Single().FixedArguments.Single();
Assert.AreEqual("System.Runtime.InteropServices.FieldOffsetAttribute", field.GetAttributes().Single().AttributeType.FullName);
var arg = field.GetAttributes().Single().FixedArguments.Single();
Assert.AreEqual("System.Int32", arg.Type.FullName);
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().FixedArguments.Single();
Assert.AreEqual("System.Runtime.InteropServices.FieldOffsetAttribute", field.GetAttributes().Single().AttributeType.FullName);
arg = field.GetAttributes().Single().FixedArguments.Single();
Assert.AreEqual("System.Int32", arg.Type.FullName);
Assert.AreEqual(100, arg.Value);
}
@ -601,7 +601,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -601,7 +601,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
public void DllImportAttribute()
{
IMethod method = GetTypeDefinition(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod");
IAttribute dllImport = method.Attributes.Single();
IAttribute dllImport = method.GetAttributes().Single();
Assert.AreEqual("System.Runtime.InteropServices.DllImportAttribute", dllImport.AttributeType.FullName);
Assert.AreEqual("unmanaged.dll", dllImport.FixedArguments[0].Value);
Assert.AreEqual((int)CharSet.Unicode, dllImport.NamedArguments.Single().Value);
@ -613,16 +613,17 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -613,16 +613,17 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
IParameter p = GetTypeDefinition(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod").Parameters.Single();
Assert.IsTrue(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(2, p.Attributes.Count);
Assert.AreEqual("System.Runtime.InteropServices.InAttribute", p.Attributes[0].AttributeType.FullName);
Assert.AreEqual("System.Runtime.InteropServices.OutAttribute", p.Attributes[1].AttributeType.FullName);
var attr = p.GetAttributes().ToList();
Assert.AreEqual(2, attr.Count);
Assert.AreEqual("System.Runtime.InteropServices.InAttribute", attr[0].AttributeType.FullName);
Assert.AreEqual("System.Runtime.InteropServices.OutAttribute", attr[1].AttributeType.FullName);
}
[Test]
public void MarshalAsAttributeOnMethod()
{
IMethod method = GetTypeDefinition(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod");
IAttribute marshalAs = method.ReturnTypeAttributes.Single();
IAttribute marshalAs = method.GetReturnTypeAttributes().Single();
Assert.AreEqual((int)UnmanagedType.Bool, marshalAs.FixedArguments.Single().Value);
}
@ -633,7 +634,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -633,7 +634,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
Assert.IsFalse(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsTrue(p.IsOut);
Assert.AreEqual(0, p.Attributes.Count);
Assert.AreEqual(0, p.GetAttributes().Count());
Assert.IsTrue(p.Type.Kind == TypeKind.ByReference);
}
@ -645,7 +646,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -645,7 +646,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.IsTrue(p.IsParams);
Assert.AreEqual(0, p.Attributes.Count);
Assert.AreEqual(0, p.GetAttributes().Count());
Assert.IsTrue(p.Type.Kind == TypeKind.Array);
}
@ -657,7 +658,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -657,7 +658,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.IsFalse(p.IsParams);
Assert.AreEqual(0, p.Attributes.Count);
Assert.AreEqual(0, p.GetAttributes().Count());
Assert.AreEqual(4, p.ConstantValue);
}
@ -670,7 +671,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -670,7 +671,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
Assert.IsFalse(p.IsOut);
Assert.IsFalse(p.IsParams);
// explicit optional parameter appears in type system if it's read from C#, but not when read from IL
//Assert.AreEqual(1, p.Attributes.Count);
//Assert.AreEqual(1, p.GetAttributes().Count());
}
[Test]
@ -681,7 +682,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -681,7 +682,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.IsFalse(p.IsParams);
Assert.AreEqual(0, p.Attributes.Count);
Assert.AreEqual(0, p.GetAttributes().Count());
Assert.AreEqual((int)StringComparison.OrdinalIgnoreCase, p.ConstantValue);
}
@ -693,7 +694,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -693,7 +694,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.IsFalse(p.IsParams);
Assert.AreEqual(0, p.Attributes.Count);
Assert.AreEqual(0, p.GetAttributes().Count());
Assert.IsNull(p.ConstantValue);
}
@ -781,7 +782,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -781,7 +782,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
ITypeDefinition type = GetTypeDefinition(typeof(IAssemblyEnum));
// [ComImport]
Assert.AreEqual(1, type.Attributes.Count(a => a.AttributeType.FullName == typeof(ComImportAttribute).FullName));
Assert.AreEqual(1, type.GetAttributes().Count(a => a.AttributeType.FullName == typeof(ComImportAttribute).FullName));
IMethod m = type.Methods.Single();
Assert.AreEqual("GetNextAssembly", m.Name);
@ -892,7 +893,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -892,7 +893,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
CustomAttributeTypedArgument<IType> GetParamsAttributeArgument(int index)
{
ITypeDefinition type = GetTypeDefinition(typeof(ParamsAttribute));
var arr = (AttributeArray)type.Attributes.Single().FixedArguments.Single().Value;
var arr = (AttributeArray)type.GetAttributes().Single().FixedArguments.Single().Value;
Assert.AreEqual(5, arr.Length);
return arr[index];
}
@ -942,7 +943,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -942,7 +943,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
ITypeDefinition type = GetTypeDefinition(typeof(ParamsAttribute));
IProperty prop = type.Properties.Single(p => p.Name == "Property");
var attr = prop.Attributes.Single();
var attr = prop.GetAttributes().Single();
Assert.AreEqual(type, attr.AttributeType);
var elements = (AttributeArray)attr.FixedArguments.Single().Value;
@ -959,15 +960,15 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -959,15 +960,15 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
ITypeDefinition type = GetTypeDefinition(typeof(ParamsAttribute));
IProperty prop = type.Properties.Single(p => p.Name == "Property");
Assert.AreEqual(0, prop.Getter.Attributes.Count);
Assert.AreEqual(1, prop.Getter.ReturnTypeAttributes.Count);
Assert.AreEqual(0, prop.Getter.GetAttributes().Count());
Assert.AreEqual(1, prop.Getter.GetReturnTypeAttributes().Count());
}
[Test]
public void DoubleAttribute_ImplicitNumericConversion()
{
ITypeDefinition type = GetTypeDefinition(typeof(DoubleAttribute));
var arg = type.Attributes.Single().FixedArguments.Single();
var arg = type.GetAttributes().Single().FixedArguments.Single();
Assert.AreEqual("System.Double", arg.Type.ReflectionName);
Assert.AreEqual(1.0, arg.Value);
}
@ -1397,7 +1398,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -1397,7 +1398,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
var f = type.GetFields().Single(x => x.Name == name);
Assert.IsTrue(f.IsConst);
Assert.AreEqual(expected, f.ConstantValue);
Assert.AreEqual(0, f.Attributes.Count);
Assert.AreEqual(0, f.GetAttributes().Count());
}
[Test]
@ -1518,33 +1519,33 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -1518,33 +1519,33 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
var type = GetTypeDefinition(typeof(ClassWithAttributesUsingNestedMembers));
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");
var typeTypeTestAttr = type.GetAttributes().Single(a => a.AttributeType.Name == "TypeTestAttribute");
Assert.AreEqual(42, typeTypeTestAttr.FixedArguments[0].Value);
Assert.AreEqual(inner, typeTypeTestAttr.FixedArguments[1].Value);
var typeMyAttr = type.Attributes.Single(a => a.AttributeType.Name == "MyAttribute");
var typeMyAttr = type.GetAttributes().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");
var propTypeTestAttr = prop.GetAttributes().Single(a => a.AttributeType.Name == "TypeTestAttribute");
Assert.AreEqual(42, propTypeTestAttr.FixedArguments[0].Value);
Assert.AreEqual(inner, propTypeTestAttr.FixedArguments[1].Value);
var propMyAttr = prop.Attributes.Single(a => a.AttributeType.Name == "MyAttribute");
var propMyAttr = prop.GetAttributes().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");
var innerTypeTestAttr = attributedInner.GetAttributes().Single(a => a.AttributeType.Name == "TypeTestAttribute");
Assert.AreEqual(42, innerTypeTestAttr.FixedArguments[0].Value);
Assert.AreEqual(inner, innerTypeTestAttr.FixedArguments[1].Value);
var innerMyAttr = attributedInner.Attributes.Single(a => a.AttributeType.Name == "MyAttribute");
var innerMyAttr = attributedInner.GetAttributes().Single(a => a.AttributeType.Name == "MyAttribute");
Assert.AreEqual(myAttribute, innerMyAttr.AttributeType);
var attributedInner2 = (ITypeDefinition)type.GetNestedTypes().Single(t => t.Name == "AttributedInner2");
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");
var inner2TypeTestAttr = attributedInner2.GetAttributes().Single(a => a.AttributeType.Name == "TypeTestAttribute");
Assert.AreEqual(43, inner2TypeTestAttr.FixedArguments[0].Value);
Assert.AreEqual(inner2, inner2TypeTestAttr.FixedArguments[1].Value);
var inner2MyAttr = attributedInner2.Attributes.Single(a => a.AttributeType.Name == "MyAttribute");
var inner2MyAttr = attributedInner2.GetAttributes().Single(a => a.AttributeType.Name == "MyAttribute");
Assert.AreEqual(myAttribute2, inner2MyAttr.AttributeType);
}
@ -1552,7 +1553,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -1552,7 +1553,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
public void ClassWithAttributeOnTypeParameter()
{
var tp = GetTypeDefinition(typeof(ClassWithAttributeOnTypeParameter<>)).TypeParameters.Single();
var attr = tp.Attributes.Single();
var attr = tp.GetAttributes().Single();
Assert.AreEqual("DoubleAttribute", attr.AttributeType.Name);
}
@ -1658,7 +1659,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -1658,7 +1659,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
public void Void_SerializableAttribute()
{
ITypeDefinition c = compilation.FindType(typeof(void)).GetDefinition();
var attr = c.Attributes.Single(a => a.AttributeType.FullName == "System.SerializableAttribute");
var attr = c.GetAttributes().Single(a => a.AttributeType.FullName == "System.SerializableAttribute");
Assert.AreEqual(0, attr.Constructor.Parameters.Count);
Assert.AreEqual(0, attr.FixedArguments.Length);
Assert.AreEqual(0, attr.NamedArguments.Length);
@ -1668,7 +1669,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -1668,7 +1669,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
public void Void_StructLayoutAttribute()
{
ITypeDefinition c = compilation.FindType(typeof(void)).GetDefinition();
var attr = c.Attributes.Single(a => a.AttributeType.FullName == "System.Runtime.InteropServices.StructLayoutAttribute");
var attr = c.GetAttributes().Single(a => a.AttributeType.FullName == "System.Runtime.InteropServices.StructLayoutAttribute");
Assert.AreEqual(1, attr.Constructor.Parameters.Count);
Assert.AreEqual(1, attr.FixedArguments.Length);
Assert.AreEqual(0, attr.FixedArguments[0].Value);
@ -1681,7 +1682,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -1681,7 +1682,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
public void Void_ComVisibleAttribute()
{
ITypeDefinition c = compilation.FindType(typeof(void)).GetDefinition();
var attr = c.Attributes.Single(a => a.AttributeType.FullName == "System.Runtime.InteropServices.ComVisibleAttribute");
var attr = c.GetAttributes().Single(a => a.AttributeType.FullName == "System.Runtime.InteropServices.ComVisibleAttribute");
Assert.AreEqual(1, attr.Constructor.Parameters.Count);
Assert.AreEqual(1, attr.FixedArguments.Length);
Assert.AreEqual(true, attr.FixedArguments[0].Value);

8
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -794,7 +794,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -794,7 +794,7 @@ namespace ICSharpCode.Decompiler.CSharp
EnumValueDisplayMode DetectBestEnumValueDisplayMode(ITypeDefinition typeDef, PEFile module)
{
if (typeDef.GetAttribute(KnownAttribute.Flags) != null)
if (typeDef.HasAttribute(KnownAttribute.Flags, inherit: false))
return EnumValueDisplayMode.All;
bool first = true;
long firstValue = 0, previousValue = 0;
@ -997,7 +997,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -997,7 +997,7 @@ namespace ICSharpCode.Decompiler.CSharp
void AddDefinesForConditionalAttributes(ILFunction function, DecompileRun decompileRun, ITypeResolveContext decompilationContext)
{
foreach (var call in function.Descendants.OfType<CallInstruction>()) {
var attr = call.Method.GetAttribute(KnownAttribute.Conditional);
var attr = call.Method.GetAttribute(KnownAttribute.Conditional, inherit: true);
var symbolName = attr?.FixedArguments.FirstOrDefault().Value as string;
if (symbolName == null || !decompileRun.DefinedSymbols.Add(symbolName))
continue;
@ -1014,12 +1014,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1014,12 +1014,12 @@ namespace ICSharpCode.Decompiler.CSharp
long initValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.ConstantValue, false);
enumDec.Initializer = typeSystemAstBuilder.ConvertConstantValue(decompilationContext.CurrentTypeDefinition.EnumUnderlyingType, field.ConstantValue);
if (enumDec.Initializer is PrimitiveExpression primitive
&& (decompilationContext.CurrentTypeDefinition.Attributes.Any(a => a.AttributeType.FullName == "System.FlagsAttribute")
&& (decompilationContext.CurrentTypeDefinition.HasAttribute(KnownAttribute.Flags)
|| (initValue > 9 && ((initValue & (initValue - 1)) == 0 || (initValue & (initValue + 1)) == 0))))
{
primitive.SetValue(initValue, $"0x{initValue:X}");
}
enumDec.Attributes.AddRange(field.Attributes.Select(a => new AttributeSection(typeSystemAstBuilder.ConvertAttribute(a))));
enumDec.Attributes.AddRange(field.GetAttributes().Select(a => new AttributeSection(typeSystemAstBuilder.ConvertAttribute(a))));
enumDec.AddAnnotation(new MemberResolveResult(null, field));
return enumDec;
}

18
ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs

@ -40,10 +40,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -40,10 +40,10 @@ namespace ICSharpCode.Decompiler.CSharp
switch (entity) {
case ITypeDefinition td:
namespaces.Add(td.Namespace);
HandleAttributes(td.Attributes, namespaces);
HandleAttributes(td.GetAttributes(), namespaces);
foreach (var typeParam in td.TypeParameters) {
HandleAttributes(typeParam.Attributes, namespaces);
HandleAttributes(typeParam.GetAttributes(), namespaces);
}
foreach (var baseType in td.DirectBaseTypes) {
@ -71,19 +71,19 @@ namespace ICSharpCode.Decompiler.CSharp @@ -71,19 +71,19 @@ namespace ICSharpCode.Decompiler.CSharp
}
break;
case IField field:
HandleAttributes(field.Attributes, namespaces);
HandleAttributes(field.GetAttributes(), namespaces);
CollectNamespacesForTypeReference(field.ReturnType, namespaces);
break;
case IMethod method:
HandleAttributes(method.Attributes, namespaces);
HandleAttributes(method.ReturnTypeAttributes, namespaces);
HandleAttributes(method.GetAttributes(), namespaces);
HandleAttributes(method.GetReturnTypeAttributes(), namespaces);
CollectNamespacesForTypeReference(method.ReturnType, namespaces);
foreach (var param in method.Parameters) {
HandleAttributes(param.Attributes, namespaces);
HandleAttributes(param.GetAttributes(), namespaces);
CollectNamespacesForTypeReference(param.Type, namespaces);
}
foreach (var typeParam in method.TypeParameters) {
HandleAttributes(typeParam.Attributes, namespaces);
HandleAttributes(typeParam.GetAttributes(), namespaces);
}
if (!method.MetadataToken.IsNil && method.HasBody) {
var reader = typeSystem.ModuleDefinition.Reader;
@ -93,12 +93,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -93,12 +93,12 @@ namespace ICSharpCode.Decompiler.CSharp
}
break;
case IProperty property:
HandleAttributes(property.Attributes, namespaces);
HandleAttributes(property.GetAttributes(), namespaces);
CollectNamespaces(property.Getter, typeSystem, namespaces);
CollectNamespaces(property.Setter, typeSystem, namespaces);
break;
case IEvent @event:
HandleAttributes(@event.Attributes, namespaces);
HandleAttributes(@event.GetAttributes(), namespaces);
CollectNamespaces(@event.AddAccessor, typeSystem, namespaces);
CollectNamespaces(@event.RemoveAccessor, typeSystem, namespaces);
break;

5
ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs

@ -150,8 +150,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -150,8 +150,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
get { return SymbolKind.Operator; }
}
IReadOnlyList<IAttribute> IEntity.Attributes {
get { return EmptyList<IAttribute>.Instance; }
IEnumerable<IAttribute> IEntity.GetAttributes()
{
return EmptyList<IAttribute>.Instance;
}
Accessibility IEntity.Accessibility {

29
ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs

@ -1741,7 +1741,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1741,7 +1741,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
void CheckForEnumerableInterface(ResolveResult expression, out IType collectionType, out IType enumeratorType, out IType elementType, out ResolveResult getEnumeratorInvocation)
{
bool? isGeneric;
elementType = GetElementTypeFromIEnumerable(expression.Type, compilation, false, out isGeneric);
elementType = expression.Type.GetElementTypeFromIEnumerable(compilation, false, out isGeneric);
if (isGeneric == true) {
ITypeDefinition enumerableOfT = compilation.FindType(KnownTypeCode.IEnumerableOfT).GetDefinition();
if (enumerableOfT != null)
@ -1765,33 +1765,6 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1765,33 +1765,6 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
getEnumeratorInvocation = ResolveMemberAccess(getEnumeratorInvocation, "GetEnumerator", EmptyList<IType>.Instance, NameLookupMode.InvocationTarget);
getEnumeratorInvocation = ResolveInvocation(getEnumeratorInvocation, new ResolveResult[0]);
}
static IType GetElementTypeFromIEnumerable(IType collectionType, ICompilation compilation, bool allowIEnumerator, out bool? isGeneric)
{
bool foundNonGenericIEnumerable = false;
foreach (IType baseType in collectionType.GetAllBaseTypes()) {
ITypeDefinition baseTypeDef = baseType.GetDefinition();
if (baseTypeDef != null) {
KnownTypeCode typeCode = baseTypeDef.KnownTypeCode;
if (typeCode == KnownTypeCode.IEnumerableOfT || (allowIEnumerator && typeCode == KnownTypeCode.IEnumeratorOfT)) {
ParameterizedType pt = baseType as ParameterizedType;
if (pt != null) {
isGeneric = true;
return pt.GetTypeArgument(0);
}
}
if (typeCode == KnownTypeCode.IEnumerable || (allowIEnumerator && typeCode == KnownTypeCode.IEnumerator))
foundNonGenericIEnumerable = true;
}
}
// System.Collections.IEnumerable found in type hierarchy -> Object is element type.
if (foundNonGenericIEnumerable) {
isGeneric = false;
return compilation.FindType(KnownTypeCode.Object);
}
isGeneric = null;
return SpecialType.UnknownType;
}
#endregion
#region GetExtensionMethods

13
ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs

@ -136,12 +136,6 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -136,12 +136,6 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
#region IMethod implementation
public IReadOnlyList<IAttribute> ReturnTypeAttributes {
get {
return baseMethod.ReturnTypeAttributes;
}
}
public IReadOnlyList<ITypeParameter> TypeParameters {
get {
return baseMethod.TypeParameters;
@ -243,11 +237,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -243,11 +237,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
}
}
public IReadOnlyList<IAttribute> Attributes {
get {
return baseMethod.Attributes;
}
}
IEnumerable<IAttribute> IEntity.GetAttributes() => baseMethod.GetAttributes();
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
public bool IsStatic {
get {

91
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -455,7 +455,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -455,7 +455,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public Attribute ConvertAttribute(IAttribute attribute)
{
Attribute attr = new Attribute();
attr.Type = ConvertType(attribute.AttributeType);
attr.Type = ConvertAttributeType(attribute.AttributeType);
SimpleType st = attr.Type as SimpleType;
MemberType mt = attr.Type as MemberType;
if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) {
@ -482,6 +482,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -482,6 +482,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
return attr;
}
private IEnumerable<AttributeSection> ConvertAttributes(IEnumerable<IAttribute> attibutes)
{
return attibutes.Select(a => new AttributeSection(ConvertAttribute(a)));
}
private IEnumerable<AttributeSection> ConvertAttributes(IEnumerable<IAttribute> attibutes, string target)
{
return attibutes.Select(a => new AttributeSection(ConvertAttribute(a)) {
AttributeTarget = target
});
}
#endregion
#region Convert Attribute Type
@ -495,6 +507,28 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -495,6 +507,28 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (type.Name.Length > 9 && type.Name.EndsWith("Attribute", StringComparison.Ordinal)) {
shortName = type.Name.Remove(type.Name.Length - 9);
}
if (AlwaysUseShortTypeNames) {
switch (astType) {
case SimpleType st:
st.Identifier = shortName;
break;
case MemberType mt:
mt.MemberName = shortName;
break;
}
} else if (resolver != null) {
ApplyShortAttributeNameIfPossible(type, astType, shortName);
}
if (AddTypeReferenceAnnotations)
astType.AddAnnotation(type);
if (AddResolveResultAnnotations)
astType.AddAnnotation(new TypeResolveResult(type));
return astType;
}
private void ApplyShortAttributeNameIfPossible(IType type, AstType astType, string shortName)
{
switch (astType) {
case SimpleType st:
ResolveResult shortRR = null;
@ -529,12 +563,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -529,12 +563,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
break;
}
if (AddTypeReferenceAnnotations)
astType.AddAnnotation(type);
if (AddResolveResultAnnotations)
astType.AddAnnotation(new TypeResolveResult(type));
return astType;
}
private bool IsAttributeType(IType type)
@ -792,8 +820,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -792,8 +820,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
bool IsFlagsEnum(ITypeDefinition type)
{
IType flagsAttributeType = type.Compilation.FindType(typeof(System.FlagsAttribute));
return type.GetAttribute(flagsAttributeType) != null;
return type.HasAttribute(KnownAttribute.Flags, inherit: false);
}
Expression ConvertEnumValue(IType type, long val)
@ -880,7 +907,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -880,7 +907,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.ParameterModifier = ParameterModifier.Params;
}
if (ShowAttributes) {
decl.Attributes.AddRange (parameter.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a))));
decl.Attributes.AddRange(ConvertAttributes(parameter.GetAttributes()));
}
if (parameter.Type.Kind == TypeKind.ByReference) {
// avoid 'out ref'
@ -997,7 +1024,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -997,7 +1024,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.ClassType = classType;
decl.Modifiers = modifiers;
if (ShowAttributes) {
decl.Attributes.AddRange (typeDefinition.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a))));
decl.Attributes.AddRange(ConvertAttributes(typeDefinition.GetAttributes()));
}
if (AddResolveResultAnnotations) {
decl.AddAnnotation(new TypeResolveResult(typeDefinition));
@ -1034,7 +1061,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1034,7 +1061,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
return decl;
}
DelegateDeclaration ConvertDelegate(IMethod invokeMethod, Modifiers modifiers)
{
ITypeDefinition d = invokeMethod.DeclaringTypeDefinition;
@ -1042,10 +1069,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1042,10 +1069,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
DelegateDeclaration decl = new DelegateDeclaration();
decl.Modifiers = modifiers & ~Modifiers.Sealed;
if (ShowAttributes) {
decl.Attributes.AddRange (d.Attributes.Select (a => new AttributeSection (ConvertAttribute (a))));
decl.Attributes.AddRange (invokeMethod.ReturnTypeAttributes.Select ((a) => new AttributeSection (ConvertAttribute (a)) {
AttributeTarget = "return"
}));
decl.Attributes.AddRange(ConvertAttributes(d.GetAttributes()));
decl.Attributes.AddRange(ConvertAttributes(invokeMethod.GetReturnTypeAttributes(), "return"));
}
if (AddResolveResultAnnotations) {
decl.AddAnnotation(new TypeResolveResult(d));
@ -1091,7 +1116,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1091,7 +1116,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.Modifiers = m;
}
if (ShowAttributes) {
decl.Attributes.AddRange (field.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a))));
decl.Attributes.AddRange(ConvertAttributes(field.GetAttributes()));
}
if (AddResolveResultAnnotations) {
decl.AddAnnotation(new MemberResolveResult(null, field));
@ -1115,7 +1140,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1115,7 +1140,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
}
Accessor ConvertAccessor(IMethod accessor, Accessibility ownerAccessibility, bool addParamterAttribute)
Accessor ConvertAccessor(IMethod accessor, Accessibility ownerAccessibility, bool addParameterAttribute)
{
if (accessor == null)
return Accessor.Null;
@ -1123,14 +1148,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1123,14 +1148,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (this.ShowAccessibility && accessor.Accessibility != ownerAccessibility)
decl.Modifiers = ModifierFromAccessibility(accessor.Accessibility);
if (ShowAttributes) {
decl.Attributes.AddRange (accessor.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a))));
decl.Attributes.AddRange (accessor.ReturnTypeAttributes.Select ((a) => new AttributeSection (ConvertAttribute (a)) {
AttributeTarget = "return"
}));
if (addParamterAttribute && accessor.Parameters.Count > 0) {
decl.Attributes.AddRange (accessor.Parameters.Last ().Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a)) {
AttributeTarget = "param"
}));
decl.Attributes.AddRange(ConvertAttributes(accessor.GetAttributes()));
decl.Attributes.AddRange(ConvertAttributes(accessor.GetReturnTypeAttributes(), "return"));
if (addParameterAttribute && accessor.Parameters.Count > 0) {
decl.Attributes.AddRange(ConvertAttributes(accessor.Parameters.Last().GetAttributes(), "param"));
}
}
if (AddResolveResultAnnotations) {
@ -1145,7 +1166,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1145,7 +1166,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
PropertyDeclaration decl = new PropertyDeclaration();
decl.Modifiers = GetMemberModifiers(property);
if (ShowAttributes) {
decl.Attributes.AddRange (property.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a))));
decl.Attributes.AddRange(ConvertAttributes(property.GetAttributes()));
}
if (AddResolveResultAnnotations) {
decl.AddAnnotation(new MemberResolveResult(null, property));
@ -1163,7 +1184,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1163,7 +1184,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
IndexerDeclaration decl = new IndexerDeclaration();
decl.Modifiers = GetMemberModifiers(indexer);
if (ShowAttributes) {
decl.Attributes.AddRange (indexer.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a))));
decl.Attributes.AddRange(ConvertAttributes(indexer.GetAttributes()));
}
if (AddResolveResultAnnotations) {
decl.AddAnnotation(new MemberResolveResult(null, indexer));
@ -1184,7 +1205,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1184,7 +1205,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
CustomEventDeclaration decl = new CustomEventDeclaration();
decl.Modifiers = GetMemberModifiers(ev);
if (ShowAttributes) {
decl.Attributes.AddRange (ev.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a))));
decl.Attributes.AddRange(ConvertAttributes(ev.GetAttributes()));
}
if (AddResolveResultAnnotations) {
decl.AddAnnotation(new MemberResolveResult(null, ev));
@ -1199,7 +1220,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1199,7 +1220,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
EventDeclaration decl = new EventDeclaration();
decl.Modifiers = GetMemberModifiers(ev);
if (ShowAttributes) {
decl.Attributes.AddRange (ev.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a))));
decl.Attributes.AddRange(ConvertAttributes(ev.GetAttributes()));
}
if (AddResolveResultAnnotations) {
decl.AddAnnotation(new MemberResolveResult(null, ev));
@ -1215,10 +1236,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1215,10 +1236,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
MethodDeclaration decl = new MethodDeclaration();
decl.Modifiers = GetMemberModifiers(method);
if (ShowAttributes) {
decl.Attributes.AddRange (method.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a))));
decl.Attributes.AddRange (method.ReturnTypeAttributes.Select ((a) => new AttributeSection (ConvertAttribute (a)) {
AttributeTarget = "return"
}));
decl.Attributes.AddRange(ConvertAttributes(method.GetAttributes()));
decl.Attributes.AddRange(ConvertAttributes(method.GetReturnTypeAttributes(), "return"));
}
if (AddResolveResultAnnotations) {
decl.AddAnnotation(new MemberResolveResult(null, method));
@ -1275,7 +1294,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1275,7 +1294,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
ConstructorDeclaration decl = new ConstructorDeclaration();
decl.Modifiers = GetMemberModifiers(ctor);
if (ShowAttributes)
decl.Attributes.AddRange (ctor.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a))));
decl.Attributes.AddRange(ConvertAttributes(ctor.GetAttributes()));
if (ctor.DeclaringTypeDefinition != null)
decl.Name = ctor.DeclaringTypeDefinition.Name;
foreach (IParameter p in ctor.Parameters) {
@ -1369,7 +1388,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1369,7 +1388,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.Variance = tp.Variance;
decl.Name = tp.Name;
if (ShowAttributes)
decl.Attributes.AddRange (tp.Attributes.Select ((a) => new AttributeSection (ConvertAttribute (a))));
decl.Attributes.AddRange(ConvertAttributes(tp.GetAttributes()));
return decl;
}

26
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -507,17 +507,17 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -507,17 +507,17 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
propertyDeclaration.Getter.Body = null;
propertyDeclaration.Setter.Body = null;
// Add C# 7.3 attributes on backing field:
var attributes = field.Attributes
.Where(a => !attributeTypesToRemoveFromAutoProperties.Any(t => t == a.AttributeType.FullName))
.Select(context.TypeSystemAstBuilder.ConvertAttribute).ToArray();
if (attributes.Length > 0) {
var section = new AttributeSection {
AttributeTarget = "field"
};
section.Attributes.AddRange(attributes);
propertyDeclaration.Attributes.Add(section);
}
// Add C# 7.3 attributes on backing field:
var attributes = field.GetAttributes()
.Where(a => !attributeTypesToRemoveFromAutoProperties.Contains(a.AttributeType.FullName))
.Select(context.TypeSystemAstBuilder.ConvertAttribute).ToArray();
if (attributes.Length > 0) {
var section = new AttributeSection {
AttributeTarget = "field"
};
section.Attributes.AddRange(attributes);
propertyDeclaration.Attributes.Add(section);
}
}
// Since the property instance is not changed, we can continue in the visitor as usual, so return null
return null;
@ -788,8 +788,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -788,8 +788,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
IField field = eventDef.DeclaringType.GetFields(f => f.Name == ev.Name, GetMemberOptions.IgnoreInheritedMembers).SingleOrDefault();
if (field != null) {
ed.AddAnnotation(field);
var attributes = field.Attributes
.Where(a => !attributeTypesToRemoveFromAutoEvents.Any(t => t == a.AttributeType.FullName))
var attributes = field.GetAttributes()
.Where(a => !attributeTypesToRemoveFromAutoEvents.Contains(a.AttributeType.FullName))
.Select(context.TypeSystemAstBuilder.ConvertAttribute).ToArray();
if (attributes.Length > 0) {
var section = new AttributeSection {

72
ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs

@ -21,7 +21,6 @@ using System.Collections; @@ -21,7 +21,6 @@ using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Resources;
using System.Threading.Tasks;
using System.Xml;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
@ -30,6 +29,7 @@ using ICSharpCode.Decompiler.CSharp.Transforms; @@ -30,6 +29,7 @@ using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using System.Threading;
using System.Text;
using System.Reflection.PortableExecutable;
using System.Reflection.Metadata;
using static ICSharpCode.Decompiler.Metadata.DotNetCorePathFinderExtensions;
@ -340,23 +340,40 @@ namespace ICSharpCode.Decompiler.CSharp @@ -340,23 +340,40 @@ namespace ICSharpCode.Decompiler.CSharp
Stream stream = r.TryOpenStream();
stream.Position = 0;
IEnumerable<DictionaryEntry> entries;
if (r.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) {
if (GetEntries(stream, out entries) && entries.All(e => e.Value is Stream)) {
foreach (var pair in entries) {
string fileName = Path.Combine(((string)pair.Key).Split('/').Select(p => CleanUpFileName(p)).ToArray());
string dirName = Path.GetDirectoryName(fileName);
if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName)) {
Directory.CreateDirectory(Path.Combine(targetDirectory, dirName));
bool decodedIntoIndividualFiles;
var individualResources = new List<Tuple<string, string>>();
try {
var resourcesFile = new ResourcesFile(stream);
if (resourcesFile.AllEntriesAreStreams()) {
foreach (var (name, value) in resourcesFile) {
string fileName = Path.Combine(name.Split('/').Select(p => CleanUpFileName(p)).ToArray());
string dirName = Path.GetDirectoryName(fileName);
if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName)) {
Directory.CreateDirectory(Path.Combine(targetDirectory, dirName));
}
Stream entryStream = (Stream)value;
entryStream.Position = 0;
individualResources.AddRange(
WriteResourceToFile(Path.Combine(targetDirectory, fileName), (string)name, entryStream));
}
Stream entryStream = (Stream)pair.Value;
entryStream.Position = 0;
WriteResourceToFile(Path.Combine(targetDirectory, fileName), (string)pair.Key, entryStream);
decodedIntoIndividualFiles = true;
} else {
decodedIntoIndividualFiles = false;
}
} catch (BadImageFormatException) {
decodedIntoIndividualFiles = false;
}
if (decodedIntoIndividualFiles) {
foreach (var entry in individualResources) {
yield return entry;
}
} else {
stream.Position = 0;
string fileName = GetFileNameForResource(Path.ChangeExtension(r.Name, ".resource"));
WriteResourceToFile(fileName, r.Name, stream);
string fileName = Path.ChangeExtension(GetFileNameForResource(r.Name), ".resource");
foreach (var entry in WriteResourceToFile(fileName, r.Name, stream)) {
yield return entry;
}
}
} else {
string fileName = GetFileNameForResource(r.Name);
@ -391,17 +408,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -391,17 +408,6 @@ namespace ICSharpCode.Decompiler.CSharp
}
return fileName;
}
bool GetEntries(Stream stream, out IEnumerable<DictionaryEntry> entries)
{
try {
entries = new ResourceSet(stream).Cast<DictionaryEntry>();
return true;
} catch (ArgumentException) {
entries = null;
return false;
}
}
#endregion
/// <summary>
@ -416,9 +422,21 @@ namespace ICSharpCode.Decompiler.CSharp @@ -416,9 +422,21 @@ namespace ICSharpCode.Decompiler.CSharp
if (pos > 0)
text = text.Substring(0, pos);
text = text.Trim();
foreach (char c in Path.GetInvalidFileNameChars())
text = text.Replace(c, '-');
return text;
// Whitelist allowed characters, replace everything else:
StringBuilder b = new StringBuilder(text.Length);
foreach (var c in text) {
if (char.IsLetterOrDigit(c) || c == '-' || c == '_')
b.Append(c);
else if (c == '.' && b.Length > 0 && b[b.Length - 1] != '.')
b.Append('.'); // allow dot, but never two in a row
else
b.Append('-');
if (b.Length >= 64)
break; // limit to 64 chars
}
if (b.Length == 0)
b.Append('-');
return b.ToString();
}
public static string GetPlatformName(Metadata.PEFile module)

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -372,6 +372,8 @@ @@ -372,6 +372,8 @@
<Compile Include="Util\GraphVizGraph.cs" />
<Compile Include="Util\KeyComparer.cs" />
<Compile Include="Util\LongDict.cs" />
<Compile Include="Util\ResourcesFile.cs" />
<Compile Include="Util\ResXResourceWriter.cs" />
<Compile Include="Util\UnicodeNewline.cs" />
<Compile Include="FlowAnalysis\ControlFlowNode.cs" />
<Compile Include="FlowAnalysis\DataFlowVisitor.cs" />

38
ICSharpCode.Decompiler/IL/ILReader.cs

@ -1116,21 +1116,47 @@ namespace ICSharpCode.Decompiler.IL @@ -1116,21 +1116,47 @@ namespace ICSharpCode.Decompiler.IL
return inst;
default:
Warn("Expected native int or pointer, but got " + inst.ResultType);
return inst;
return new Conv(inst, PrimitiveType.I, false, Sign.None);
}
}
ILInstruction PopFieldTarget(IField field)
{
return field.DeclaringType.IsReferenceType == true ? Pop(StackType.O) : PopPointer();
switch (field.DeclaringType.IsReferenceType) {
case true:
return Pop(StackType.O);
case false:
return PopPointer();
default:
// field in unresolved type
if (PeekStackType() == StackType.O)
return Pop();
else
return PopPointer();
}
}
/// <summary>
/// Like PopFieldTarget, but supports ldfld's special behavior for fields of temporary value types.
/// </summary>
ILInstruction PopLdFldTarget(IField field)
{
if (field.DeclaringType.IsReferenceType == true)
return Pop(StackType.O);
return PeekStackType() == StackType.O ? new AddressOf(Pop()) : PopPointer();
switch (field.DeclaringType.IsReferenceType) {
case true:
return Pop(StackType.O);
case false:
// field of value type: ldfld can handle temporaries
if (PeekStackType() == StackType.O)
return new AddressOf(Pop());
else
return PopPointer();
default:
// field in unresolved type
if (PeekStackType() == StackType.O)
return Pop(StackType.O);
else
return PopPointer();
}
}
private ILInstruction Return()

15
ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs

@ -25,7 +25,20 @@ namespace ICSharpCode.Decompiler.IL @@ -25,7 +25,20 @@ namespace ICSharpCode.Decompiler.IL
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(field.DeclaringType.IsReferenceType == true ? (target.ResultType == StackType.O) : (target.ResultType == StackType.I || target.ResultType == StackType.Ref));
switch (field.DeclaringType.IsReferenceType) {
case true:
Debug.Assert(target.ResultType == StackType.O,
"Class fields can only be accessed with an object on the stack");
break;
case false:
Debug.Assert(target.ResultType == StackType.I || target.ResultType == StackType.Ref,
"Struct fields can only be accessed with a pointer on the stack");
break;
case null:
// field of unresolved type
Debug.Assert(target.ResultType == StackType.O || target.ResultType == StackType.I || target.ResultType == StackType.Ref);
break;
}
}
}
}

3
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs

@ -23,6 +23,7 @@ using System.IO; @@ -23,6 +23,7 @@ using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using ICSharpCode.Decompiler.Util;
using LightJson.Serialization;
namespace ICSharpCode.Decompiler.Metadata
@ -44,7 +45,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -44,7 +45,7 @@ namespace ICSharpCode.Decompiler.Metadata
this.Version = parts[1];
this.Type = type;
this.Path = path;
this.RuntimeComponents = runtimeComponents ?? new string[0];
this.RuntimeComponents = runtimeComponents ?? Empty<string>.Array;
}
}

33
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs

@ -19,6 +19,8 @@ @@ -19,6 +19,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
@ -26,7 +28,11 @@ namespace ICSharpCode.Decompiler.Metadata @@ -26,7 +28,11 @@ namespace ICSharpCode.Decompiler.Metadata
{
public static class DotNetCorePathFinderExtensions
{
public static string DetectTargetFrameworkId(this PEReader assembly)
static readonly string RefPathPattern =
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<1>.NETFramework)[/\\]v(?<2>[^/\\]+)[/\\])" +
@"|(NuGetFallbackFolder[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\]ref[/\\])";
public static string DetectTargetFrameworkId(this PEReader assembly, string assemblyPath = null)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));
@ -44,6 +50,31 @@ namespace ICSharpCode.Decompiler.Metadata @@ -44,6 +50,31 @@ namespace ICSharpCode.Decompiler.Metadata
}
}
// Optionally try to detect target version through assembly path as a fallback (use case: reference assemblies)
if (assemblyPath != null) {
/*
* Detected path patterns (examples):
*
* - .NETFramework -> C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll
* - .NETCore -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll
* - .NETStandard -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll
*/
var pathMatch = Regex.Match(assemblyPath, RefPathPattern,
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
if (pathMatch.Success) {
var type = pathMatch.Groups[1].Value;
var version = pathMatch.Groups[2].Value;
if (type == ".NETFramework") {
return $".NETFramework,Version=v{version}";
} else if (type.Contains("netcore")) {
return $".NETCoreApp,Version=v{version}";
} else if (type.Contains("netstandard")) {
return $".NETStandard,Version=v{version}";
}
}
}
return string.Empty;
}
}

5
ICSharpCode.Decompiler/NRExtensions.cs

@ -44,10 +44,7 @@ namespace ICSharpCode.Decompiler @@ -44,10 +44,7 @@ namespace ICSharpCode.Decompiler
public static bool IsCompilerGenerated(this IEntity entity)
{
if (entity != null) {
foreach (IAttribute a in entity.Attributes) {
if (a.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")
return true;
}
return entity.HasAttribute(KnownAttribute.CompilerGenerated);
}
return false;
}

2
ICSharpCode.Decompiler/TypeSystem/ComHelper.cs

@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
return typeDefinition != null
&& typeDefinition.Kind == TypeKind.Interface
&& typeDefinition.GetAttributes(KnownAttribute.ComImport, inherit: false) != null;
&& typeDefinition.HasAttribute(KnownAttribute.ComImport, inherit: false);
}
/// <summary>

3
ICSharpCode.Decompiler/TypeSystem/IEntity.cs

@ -123,8 +123,9 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -123,8 +123,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// <summary>
/// Gets the attributes on this entity.
/// Does not include inherited attributes.
/// </summary>
IReadOnlyList<IAttribute> Attributes { get; }
IEnumerable<IAttribute> GetAttributes();
/// <summary>
/// Gets the accessibility of this entity.

5
ICSharpCode.Decompiler/TypeSystem/IMethod.cs

@ -79,7 +79,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -79,7 +79,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// <summary>
/// Gets the attributes associated with the return type. (e.g. [return: MarshalAs(...)])
/// </summary>
IReadOnlyList<IAttribute> ReturnTypeAttributes { get; }
/// <remarks>
/// Does not include inherited attributes.
/// </remarks>
IEnumerable<IAttribute> GetReturnTypeAttributes();
/// <summary>
/// Gets the type parameters of this method; or an empty list if the method is not generic.

4
ICSharpCode.Decompiler/TypeSystem/IParameter.cs

@ -63,9 +63,9 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -63,9 +63,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
public interface IParameter : IVariable
{
/// <summary>
/// Gets the list of attributes.
/// Gets the attributes on this parameter.
/// </summary>
IReadOnlyList<IAttribute> Attributes { get; }
IEnumerable<IAttribute> GetAttributes();
/// <summary>
/// Gets whether this parameter is a C# 'ref' parameter.

4
ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs

@ -82,9 +82,9 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -82,9 +82,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
new string Name { get; }
/// <summary>
/// Gets the list of attributes declared on this type parameter.
/// Gets the attributes declared on this type parameter.
/// </summary>
IReadOnlyList<IAttribute> Attributes { get; }
IEnumerable<IAttribute> GetAttributes();
/// <summary>
/// Gets the variance of this type parameter.

1
ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedEntity.cs

@ -59,6 +59,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -59,6 +59,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
public IReadOnlyList<IAttribute> Attributes { get; protected set; }
IEnumerable<IAttribute> IEntity.GetAttributes() => Attributes;
public bool IsStatic { get { return unresolved.IsStatic; } }
public bool IsAbstract { get { return unresolved.IsAbstract; } }

4
ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs

@ -71,8 +71,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -71,8 +71,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public int Index {
get { return index; }
}
public abstract IReadOnlyList<IAttribute> Attributes { get; }
public abstract IEnumerable<IAttribute> GetAttributes();
public VarianceModifier Variance {
get { return variance; }

4
ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs

@ -72,9 +72,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -72,9 +72,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
get { return owner; }
}
public IReadOnlyList<IAttribute> Attributes {
get { return attributes; }
}
public IEnumerable<IAttribute> GetAttributes() => attributes;
public bool IsRef {
get { return isRef; }

1
ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedMethod.cs

@ -170,6 +170,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -170,6 +170,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public IReadOnlyList<IParameter> Parameters { get; private set; }
public IReadOnlyList<IAttribute> ReturnTypeAttributes { get; private set; }
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => ReturnTypeAttributes;
public IReadOnlyList<ITypeParameter> TypeParameters { get; private set; }
public IReadOnlyList<IType> TypeArguments {

4
ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs

@ -105,7 +105,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -105,7 +105,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return LazyInit.GetOrSet(ref this.attributes, result);
}
}
public IEnumerable<IAttribute> GetAttributes() => Attributes;
public System.Reflection.Metadata.EntityHandle MetadataToken => parts[0].MetadataToken;
public SymbolKind SymbolKind {

2
ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultResolvedTypeParameter.cs

@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
this.attributes = attributes ?? EmptyList<IAttribute>.Instance;
}
public override IReadOnlyList<IAttribute> Attributes => attributes;
public override IEnumerable<IAttribute> GetAttributes() => attributes;
public override bool HasValueTypeConstraint => hasValueTypeConstraint;
public override bool HasReferenceTypeConstraint => hasReferenceTypeConstraint;

1
ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedParameter.cs

@ -229,6 +229,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -229,6 +229,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public IType Type { get; internal set; }
public string Name { get; internal set; }
public IReadOnlyList<IAttribute> Attributes { get; internal set; }
public IEnumerable<IAttribute> GetAttributes() => Attributes;
public bool IsRef { get; internal set; }
public bool IsOut { get; internal set; }
public bool IsParams { get; internal set; }

4
ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs

@ -141,9 +141,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -141,9 +141,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
get { return index; }
}
IReadOnlyList<IAttribute> ITypeParameter.Attributes {
get { return EmptyList<IAttribute>.Instance; }
}
IEnumerable<IAttribute> ITypeParameter.GetAttributes() =>EmptyList<IAttribute>.Instance;
SymbolKind ITypeParameter.OwnerType {
get { return ownerType; }

4
ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs

@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IAssembly IEntity.ParentAssembly => DeclaringType?.GetDefinition()?.ParentAssembly;
IReadOnlyList<IAttribute> IEntity.Attributes => EmptyList<IAttribute>.Instance;
IEnumerable<IAttribute> IEntity.GetAttributes() => EmptyList<IAttribute>.Instance;
public Accessibility Accessibility { get; set; } = Accessibility.Public;
@ -130,7 +130,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -130,7 +130,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override SymbolKind SymbolKind => symbolKind;
IReadOnlyList<IAttribute> IMethod.ReturnTypeAttributes => EmptyList<IAttribute>.Instance;
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => EmptyList<IAttribute>.Instance;
public IReadOnlyList<ITypeParameter> TypeParameters { get; set; } = EmptyList<ITypeParameter>.Instance;

12
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs

@ -38,7 +38,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -38,7 +38,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
readonly string name;
// lazy-loaded:
IAttribute[] customAttributes;
IType returnType;
internal MetadataEvent(MetadataAssembly assembly, EventDefinitionHandle handle)
@ -103,16 +102,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -103,16 +102,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
TypeParameterSubstitution IMember.Substitution => TypeParameterSubstitution.Identity;
#region Attributes
public IReadOnlyList<IAttribute> Attributes {
get {
var attr = LazyInit.VolatileRead(ref this.customAttributes);
if (attr != null)
return attr;
return LazyInit.GetOrSet(ref this.customAttributes, DecodeAttributes());
}
}
IAttribute[] DecodeAttributes()
public IEnumerable<IAttribute> GetAttributes()
{
var b = new AttributeListBuilder(assembly);
var metadata = assembly.metadata;

14
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs

@ -43,7 +43,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -43,7 +43,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IType type;
bool isVolatile; // initialized together with this.type
byte decimalConstant; // 0=no, 1=yes, 2=unknown
IAttribute[] customAttributes;
internal MetadataField(MetadataAssembly assembly, FieldDefinitionHandle handle)
{
@ -127,17 +126,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -127,17 +126,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public IType DeclaringType => DeclaringTypeDefinition;
public IAssembly ParentAssembly => assembly;
public ICompilation Compilation => assembly.Compilation;
public IReadOnlyList<IAttribute> Attributes {
get {
var attr = LazyInit.VolatileRead(ref this.customAttributes);
if (attr != null)
return attr;
return LazyInit.GetOrSet(ref this.customAttributes, DecodeAttributes());
}
}
IAttribute[] DecodeAttributes()
public IEnumerable<IAttribute> GetAttributes()
{
var b = new AttributeListBuilder(assembly);
var metadata = assembly.metadata;

24
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs

@ -45,8 +45,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -45,8 +45,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
// lazy-loaded fields:
ITypeDefinition declaringType;
string name;
IAttribute[] customAttributes;
IAttribute[] returnTypeAttributes;
IParameter[] parameters;
IType returnType;
@ -232,15 +230,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -232,15 +230,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public ICompilation Compilation => assembly.Compilation;
#region Attributes
public IReadOnlyList<IAttribute> Attributes {
get {
var attr = LazyInit.VolatileRead(ref this.customAttributes);
if (attr != null)
return attr;
return LazyInit.GetOrSet(ref this.customAttributes, DecodeAttributes());
}
}
IType FindInteropType(string name)
{
return assembly.Compilation.FindType(new TopLevelTypeName(
@ -248,7 +237,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -248,7 +237,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
));
}
IAttribute[] DecodeAttributes()
public IEnumerable<IAttribute> GetAttributes()
{
var b = new AttributeListBuilder(assembly);
@ -365,16 +354,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -365,16 +354,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
#endregion
#region Return type attributes
public IReadOnlyList<IAttribute> ReturnTypeAttributes {
get {
var attr = LazyInit.VolatileRead(ref this.returnTypeAttributes);
if (attr != null)
return attr;
return LazyInit.GetOrSet(ref this.returnTypeAttributes, DecodeReturnTypeAttributes());
}
}
private IAttribute[] DecodeReturnTypeAttributes()
public IEnumerable<IAttribute> GetReturnTypeAttributes()
{
var b = new AttributeListBuilder(assembly);
var metadata = assembly.metadata;

12
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs

@ -37,7 +37,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -37,7 +37,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
// lazy-loaded:
string name;
IAttribute[] customAttributes;
internal MetadataParameter(MetadataAssembly assembly, IParameterizedMember owner, IType type, ParameterHandle handle)
{
@ -53,16 +52,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -53,16 +52,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public EntityHandle MetadataToken => handle;
#region Attributes
public IReadOnlyList<IAttribute> Attributes {
get {
var attr = LazyInit.VolatileRead(ref this.customAttributes);
if (attr != null)
return attr;
return LazyInit.GetOrSet(ref this.customAttributes, DecodeAttributes());
}
}
IAttribute[] DecodeAttributes()
public IEnumerable<IAttribute> GetAttributes()
{
var b = new AttributeListBuilder(assembly);
var metadata = assembly.metadata;

12
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs

@ -42,7 +42,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -42,7 +42,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
readonly SymbolKind symbolKind;
// lazy-loaded:
IAttribute[] customAttributes;
volatile Accessibility cachedAccessiblity = InvalidAccessibility;
IParameter[] parameters;
IType returnType;
@ -144,16 +143,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -144,16 +143,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
TypeParameterSubstitution IMember.Substitution => TypeParameterSubstitution.Identity;
#region Attributes
public IReadOnlyList<IAttribute> Attributes {
get {
var attr = LazyInit.VolatileRead(ref this.customAttributes);
if (attr != null)
return attr;
return LazyInit.GetOrSet(ref this.customAttributes, DecodeAttributes());
}
}
IAttribute[] DecodeAttributes()
public IEnumerable<IAttribute> GetAttributes()
{
var b = new AttributeListBuilder(assembly);
var metadata = assembly.metadata;

12
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs

@ -51,7 +51,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -51,7 +51,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public bool HasExtensionMethods { get; }
// lazy-loaded:
IAttribute[] customAttributes;
IMember[] members;
IField[] fields;
IProperty[] properties;
@ -285,16 +284,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -285,16 +284,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public IAssembly ParentAssembly => assembly;
#region Type Attributes
public IReadOnlyList<IAttribute> Attributes {
get {
var attr = LazyInit.VolatileRead(ref this.customAttributes);
if (attr != null)
return attr;
return LazyInit.GetOrSet(ref this.customAttributes, DecodeAttributes());
}
}
IAttribute[] DecodeAttributes()
public IEnumerable<IAttribute> GetAttributes()
{
var b = new AttributeListBuilder(assembly);
var metadata = assembly.metadata;

11
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs

@ -98,16 +98,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -98,16 +98,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public GenericParameterHandle MetadataToken => handle;
public override IReadOnlyList<IAttribute> Attributes {
get {
var attr = LazyInit.VolatileRead(ref this.customAttributes);
if (attr != null)
return attr;
return LazyInit.GetOrSet(ref this.customAttributes, DecodeAttributes());
}
}
IAttribute[] DecodeAttributes()
public override IEnumerable<IAttribute> GetAttributes()
{
var metadata = assembly.metadata;
var gp = metadata.GetGenericParameter(handle);

6
ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMember.cs

@ -150,10 +150,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -150,10 +150,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public ITypeDefinition DeclaringTypeDefinition {
get { return baseMember.DeclaringTypeDefinition; }
}
public IReadOnlyList<IAttribute> Attributes {
get { return baseMember.Attributes; }
}
IEnumerable<IAttribute> IEntity.GetAttributes() => baseMember.GetAttributes();
public IEnumerable<IMember> ExplicitlyImplementedInterfaceMembers {
get {

8
ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs

@ -92,10 +92,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -92,10 +92,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public IReadOnlyList<IType> TypeArguments {
get { return this.Substitution.MethodTypeArguments ?? EmptyList<IType>.Instance; }
}
public IReadOnlyList<IAttribute> ReturnTypeAttributes {
get { return methodDefinition.ReturnTypeAttributes; }
}
public IEnumerable<IAttribute> GetReturnTypeAttributes() => methodDefinition.GetReturnTypeAttributes();
public IReadOnlyList<ITypeParameter> TypeParameters {
get {
@ -230,7 +228,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -230,7 +228,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
this.baseTp = baseTp;
}
public override IReadOnlyList<IAttribute> Attributes => baseTp.Attributes;
public override IEnumerable<IAttribute> GetAttributes() => baseTp.GetAttributes();
public override int GetHashCode()
{

2
ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
this.newOwner = newOwner;
}
IReadOnlyList<IAttribute> IParameter.Attributes => baseParameter.Attributes;
IEnumerable<IAttribute> IParameter.GetAttributes() => baseParameter.GetAttributes();
bool IParameter.IsRef => baseParameter.IsRef;
bool IParameter.IsOut => baseParameter.IsOut;
bool IParameter.IsParams => baseParameter.IsParams;

43
ICSharpCode.Decompiler/TypeSystem/InheritanceHelper.cs

@ -29,7 +29,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -29,7 +29,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
// TODO: maybe these should be extension methods?
// or even part of the interface itself? (would allow for easy caching)
#region GetBaseMember
/// <summary>
/// Gets the base member that has the same signature.
@ -58,16 +58,16 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -58,16 +58,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
yield return member;
}*/
}
// Remove generic specialization
var substitution = member.Substitution;
member = member.MemberDefinition;
if (member.DeclaringTypeDefinition == null) {
// For global methods, return empty list. (prevent SharpDevelop UDC crash 4524)
yield break;
}
IEnumerable<IType> allBaseTypes;
if (includeImplementedInterfaces) {
allBaseTypes = member.DeclaringTypeDefinition.GetAllBaseTypes();
@ -93,7 +93,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -93,7 +93,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
}
#endregion
#region GetDerivedMember
/// <summary>
/// Finds the member declared in 'derivedType' that has the same signature (could override) 'baseMember'.
@ -104,10 +104,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -104,10 +104,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
throw new ArgumentNullException("baseMember");
if (derivedType == null)
throw new ArgumentNullException("derivedType");
if (baseMember.Compilation != derivedType.Compilation)
throw new ArgumentException("baseMember and derivedType must be from the same compilation");
baseMember = baseMember.MemberDefinition;
bool includeInterfaces = baseMember.DeclaringTypeDefinition.Kind == TypeKind.Interface;
IMethod method = baseMember as IMethod;
@ -147,5 +147,34 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -147,5 +147,34 @@ namespace ICSharpCode.Decompiler.TypeSystem
return null;
}
#endregion
#region Attributes
internal static IEnumerable<IAttribute> GetAttributes(ITypeDefinition typeDef)
{
foreach (var baseType in typeDef.GetNonInterfaceBaseTypes().Reverse()) {
ITypeDefinition baseTypeDef = baseType.GetDefinition();
if (baseTypeDef == null)
continue;
foreach (var attr in baseTypeDef.GetAttributes()) {
yield return attr;
}
}
}
internal static IEnumerable<IAttribute> GetAttributes(IMember member)
{
HashSet<IMember> visitedMembers = new HashSet<IMember>();
do {
member = member.MemberDefinition; // it's sufficient to look at the definitions
if (!visitedMembers.Add(member)) {
// abort if we seem to be in an infinite loop (cyclic inheritance)
break;
}
foreach (var attr in member.GetAttributes()) {
yield return attr;
}
} while (member.IsOverride && (member = InheritanceHelper.GetBaseMember(member)) != null);
}
#endregion
}
}

173
ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

@ -420,181 +420,70 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -420,181 +420,70 @@ namespace ICSharpCode.Decompiler.TypeSystem
return null;
}
#endregion
#region ITypeReference.Resolve(ICompilation)
/// <summary>
/// Resolves a type reference in the compilation's main type resolve context.
/// Some type references require a more specific type resolve context and will not resolve using this method.
/// </summary>
/// <returns>
/// Returns the resolved type.
/// In case of an error, returns <see cref="SpecialType.UnknownType"/>.
/// Never returns null.
/// </returns>
public static IType Resolve (this ITypeReference reference, ICompilation compilation)
{
if (reference == null)
throw new ArgumentNullException ("reference");
if (compilation == null)
throw new ArgumentNullException ("compilation");
return reference.Resolve (compilation.TypeResolveContext);
}
#endregion
#region ITypeDefinition.GetAttribute
#region IEntity.GetAttribute
/// <summary>
/// Gets the attribute of the specified attribute type (or derived attribute types).
/// Gets whether the entity has an attribute of the specified attribute type (or derived attribute types).
/// </summary>
/// <param name="entity">The entity on which the attributes are declared.</param>
/// <param name="attributeType">The attribute type to look for.</param>
/// <param name="inherit">
/// Specifies whether attributes inherited from base classes and base members (if the given <paramref name="entity"/> in an <c>override</c>)
/// should be returned. The default is <c>true</c>.
/// Specifies whether attributes inherited from base classes and base members
/// (if the given <paramref name="entity"/> in an <c>override</c>)
/// should be returned.
/// </param>
/// <returns>
/// Returns the attribute that was found; or <c>null</c> if none was found.
/// 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.
/// </returns>
public static IAttribute GetAttribute(this IEntity entity, IType attributeType, bool inherit = true)
public static bool HasAttribute(this IEntity entity, KnownAttribute attrType, bool inherit=false)
{
return GetAttributes(entity, attributeType, inherit).FirstOrDefault();
return GetAttribute(entity, attrType, inherit) != null;
}
/// <summary>
/// Gets the attributes of the specified attribute type (or derived attribute types).
/// </summary>
/// <param name="entity">The entity on which the attributes are declared.</param>
/// <param name="attributeType">The attribute type to look for.</param>
/// <param name="inherit">
/// Specifies whether attributes inherited from base classes and base members (if the given <paramref name="entity"/> in an <c>override</c>)
/// should be returned. The default is <c>true</c>.
/// </param>
/// <returns>
/// 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.
/// </returns>
public static IEnumerable<IAttribute> GetAttributes(this IEntity entity, IType attributeType, bool inherit = true)
{
if (entity == null)
throw new ArgumentNullException("entity");
if (attributeType == null)
throw new ArgumentNullException("attributeType");
return GetAttributes(entity, attributeType.Equals, inherit);
}
/// <summary>
/// Gets the attribute of the specified attribute type (or derived attribute types).
/// </summary>
/// <param name="entity">The entity on which the attributes are declared.</param>
/// <param name="attributeType">The attribute type to look for.</param>
/// <param name="inherit">
/// Specifies whether attributes inherited from base classes and base members (if the given <paramref name="entity"/> in an <c>override</c>)
/// should be returned. The default is <c>true</c>.
/// Specifies whether attributes inherited from base classes and base members
/// (if the given <paramref name="entity"/> in an <c>override</c>)
/// should be returned.
/// </param>
/// <returns>
/// Returns the attribute that was found; or <c>null</c> if none was found.
/// 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.
/// </returns>
public static IAttribute GetAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit = true)
{
return GetAttributes(entity, attributeType, inherit).FirstOrDefault();
}
/// <summary>
/// Gets the attributes of the specified attribute type (or derived attribute types).
/// </summary>
/// <param name="entity">The entity on which the attributes are declared.</param>
/// <param name="attributeType">The attribute type to look for.</param>
/// <param name="inherit">
/// Specifies whether attributes inherited from base classes and base members (if the given <paramref name="entity"/> in an <c>override</c>)
/// should be returned. The default is <c>true</c>.
/// </param>
/// <returns>
/// 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.
/// </returns>
public static IEnumerable<IAttribute> GetAttributes(this IEntity entity, KnownAttribute attributeType, bool inherit = true)
public static IAttribute GetAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit=false)
{
if (entity == null)
throw new ArgumentNullException("entity");
return GetAttributes(entity, attrType => {
ITypeDefinition typeDef = attrType.GetDefinition();
return typeDef != null && typeDef.FullTypeName == attributeType.GetTypeName();
}, inherit);
return GetAttributes(entity, inherit).FirstOrDefault(a => a.AttributeType.IsKnownType(attributeType));
}
/// <summary>
/// Gets the attribute of the specified attribute type (or derived attribute types).
/// Gets the attributes on the entity.
/// </summary>
/// <param name="entity">The entity on which the attributes are declared.</param>
/// <param name="inherit">
/// Specifies whether attributes inherited from base classes and base members (if the given <paramref name="entity"/> in an <c>override</c>)
/// should be returned. The default is <c>true</c>.
/// Specifies whether attributes inherited from base classes and base members
/// (if the given <paramref name="entity"/> in an <c>override</c>)
/// should be returned.
/// </param>
/// <returns>
/// Returns the attribute that was found; or <c>null</c> if none was found.
/// 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.
/// 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.
/// </returns>
public static IEnumerable<IAttribute> GetAttributes(this IEntity entity, bool inherit = true)
{
if (entity == null)
throw new ArgumentNullException ("entity");
return GetAttributes(entity, a => true, inherit);
}
static IEnumerable<IAttribute> GetAttributes(IEntity entity, Predicate<IType> attributeTypePredicate, bool inherit)
{
if (!inherit) {
foreach (var attr in entity.Attributes) {
if (attributeTypePredicate(attr.AttributeType))
yield return attr;
public static IEnumerable<IAttribute> GetAttributes(this IEntity entity, bool inherit)
{
if (inherit) {
if (entity is ITypeDefinition td) {
return InheritanceHelper.GetAttributes(td);
} else if (entity is IMember m) {
return InheritanceHelper.GetAttributes(m);
} else {
throw new NotSupportedException("Unknown entity type");
}
yield break;
} else {
return entity.GetAttributes();
}
ITypeDefinition typeDef = entity as ITypeDefinition;
if (typeDef != null) {
foreach (var baseType in typeDef.GetNonInterfaceBaseTypes().Reverse()) {
ITypeDefinition baseTypeDef = baseType.GetDefinition();
if (baseTypeDef == null)
continue;
foreach (var attr in baseTypeDef.Attributes) {
if (attributeTypePredicate(attr.AttributeType))
yield return attr;
}
}
yield break;
}
IMember member = entity as IMember;
if (member != null) {
HashSet<IMember> visitedMembers = new HashSet<IMember>();
do {
member = member.MemberDefinition; // it's sufficient to look at the definitions
if (!visitedMembers.Add(member)) {
// abort if we seem to be in an infinite loop (cyclic inheritance)
break;
}
foreach (var attr in member.Attributes) {
if (attributeTypePredicate(attr.AttributeType))
yield return attr;
}
} while (member.IsOverride && (member = InheritanceHelper.GetBaseMember(member)) != null);
yield break;
}
throw new NotSupportedException("Unknown entity type");
}
#endregion
#region IsCompilerGenerated
public static bool IsCompilerGenereated(this IEntity entity)
{
if (entity == null)
throw new ArgumentNullException(nameof(entity));
return entity.GetAttribute(KnownAttribute.CompilerGenerated) != null;
}
#endregion

11
ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

@ -109,10 +109,9 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -109,10 +109,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
baseMethod.Specialize(substitution),
parameters.Skip(baseMethod.Parameters.Count - 1).Select(p => p.Type.AcceptVisitor(substitution)).ToList());
}
public IReadOnlyList<IAttribute> ReturnTypeAttributes {
get { return baseMethod.ReturnTypeAttributes; }
}
IEnumerable<IAttribute> IEntity.GetAttributes() => baseMethod.GetAttributes();
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
public IReadOnlyList<ITypeParameter> TypeParameters {
get { return baseMethod.TypeParameters; }
@ -225,10 +224,6 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -225,10 +224,6 @@ namespace ICSharpCode.Decompiler.TypeSystem
get { return baseMethod.ParentAssembly; }
}
public IReadOnlyList<IAttribute> Attributes {
get { return baseMethod.Attributes; }
}
public bool IsStatic {
get { return baseMethod.IsStatic; }
}

540
ICSharpCode.Decompiler/Util/ResXResourceWriter.cs

@ -0,0 +1,540 @@ @@ -0,0 +1,540 @@
// Copyright (c) 2018 Daniel Grunwald
// This file is based on the Mono implementation of ResXResourceWriter.
// It is modified to add support for "ResourceSerializedObject" values.
//
// 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.
//
// Copyright (c) 2004-2005 Novell, Inc.
//
// Authors:
// Duncan Mak duncan@ximian.com
// Gonzalo Paniagua Javier gonzalo@ximian.com
// Peter Bartok pbartok@novell.com
// Gary Barnett gary.barnett.mono@gmail.com
// includes code by Mike Krüger and Lluis Sanchez
using System.ComponentModel;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml;
using System;
namespace ICSharpCode.Decompiler.Util
{
#if INSIDE_SYSTEM_WEB
internal
#else
public
#endif
class ResXResourceWriter : IDisposable
{
#region Local Variables
private string filename;
private Stream stream;
private TextWriter textwriter;
private XmlTextWriter writer;
private bool written;
private string base_path;
#endregion // Local Variables
#region Static Fields
public static readonly string BinSerializedObjectMimeType = "application/x-microsoft.net.object.binary.base64";
public static readonly string ByteArraySerializedObjectMimeType = "application/x-microsoft.net.object.bytearray.base64";
public static readonly string DefaultSerializedObjectMimeType = BinSerializedObjectMimeType;
public static readonly string ResMimeType = "text/microsoft-resx";
public static readonly string ResourceSchema = schema;
public static readonly string SoapSerializedObjectMimeType = "application/x-microsoft.net.object.soap.base64";
public static readonly string Version = "2.0";
#endregion // Static Fields
#region Constructors & Destructor
public ResXResourceWriter(Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream");
if (!stream.CanWrite)
throw new ArgumentException("stream is not writable.", "stream");
this.stream = stream;
}
public ResXResourceWriter(TextWriter textWriter)
{
if (textWriter == null)
throw new ArgumentNullException("textWriter");
this.textwriter = textWriter;
}
public ResXResourceWriter(string fileName)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
this.filename = fileName;
}
~ResXResourceWriter()
{
Dispose(false);
}
#endregion // Constructors & Destructor
const string WinFormsAssemblyName = ", System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
const string ResXNullRefTypeName = "System.Resources.ResXNullRef" + WinFormsAssemblyName;
void InitWriter()
{
if (filename != null)
stream = File.Open(filename, FileMode.Create);
if (textwriter == null)
textwriter = new StreamWriter(stream, Encoding.UTF8);
writer = new XmlTextWriter(textwriter);
writer.Formatting = Formatting.Indented;
writer.WriteStartDocument();
writer.WriteStartElement("root");
writer.WriteRaw(schema);
WriteHeader("resmimetype", "text/microsoft-resx");
WriteHeader("version", "1.3");
WriteHeader("reader", "System.Resources.ResXResourceReader" + WinFormsAssemblyName);
WriteHeader("writer", "System.Resources.ResXResourceWriter" + WinFormsAssemblyName);
}
void WriteHeader(string name, string value)
{
writer.WriteStartElement("resheader");
writer.WriteAttributeString("name", name);
writer.WriteStartElement("value");
writer.WriteString(value);
writer.WriteEndElement();
writer.WriteEndElement();
}
void WriteNiceBase64(byte[] value, int offset, int length)
{
string b64;
StringBuilder sb;
int pos;
int inc;
string ins;
b64 = Convert.ToBase64String(value, offset, length);
// Wild guess; two extra newlines, and one newline/tab pair for every 80 chars
sb = new StringBuilder(b64, b64.Length + ((b64.Length + 160) / 80) * 3);
pos = 0;
inc = 80 + Environment.NewLine.Length + 1;
ins = Environment.NewLine + "\t";
while (pos < sb.Length) {
sb.Insert(pos, ins);
pos += inc;
}
sb.Insert(sb.Length, Environment.NewLine);
writer.WriteString(sb.ToString());
}
void WriteBytes(string name, Type type, byte[] value, int offset, int length, string comment)
{
writer.WriteStartElement("data");
writer.WriteAttributeString("name", name);
if (type != null) {
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
// byte[] should never get a mimetype, otherwise MS.NET won't be able
// to parse the data.
if (type != typeof(byte[]))
writer.WriteAttributeString("mimetype", ByteArraySerializedObjectMimeType);
writer.WriteStartElement("value");
WriteNiceBase64(value, offset, length);
} else {
writer.WriteAttributeString("mimetype", BinSerializedObjectMimeType);
writer.WriteStartElement("value");
writer.WriteBase64(value, offset, length);
}
writer.WriteEndElement();
if (!(comment == null || comment.Equals(String.Empty))) {
writer.WriteStartElement("comment");
writer.WriteString(comment);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
void WriteBytes(string name, Type type, byte[] value, string comment)
{
WriteBytes(name, type, value, 0, value.Length, comment);
}
void WriteString(string name, string value)
{
WriteString(name, value, null);
}
void WriteString(string name, string value, string type)
{
WriteString(name, value, type, String.Empty);
}
void WriteString(string name, string value, string type, string comment)
{
writer.WriteStartElement("data");
writer.WriteAttributeString("name", name);
if (type != null)
writer.WriteAttributeString("type", type);
writer.WriteStartElement("value");
writer.WriteString(value);
writer.WriteEndElement();
if (!(comment == null || comment.Equals(String.Empty))) {
writer.WriteStartElement("comment");
writer.WriteString(comment);
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteWhitespace("\n ");
}
public void AddResource(string name, byte[] value)
{
if (name == null)
throw new ArgumentNullException("name");
if (value == null)
throw new ArgumentNullException("value");
if (written)
throw new InvalidOperationException("The resource is already generated.");
if (writer == null)
InitWriter();
WriteBytes(name, value.GetType(), value, null);
}
public void AddResource(string name, object value)
{
AddResource(name, value, String.Empty);
}
private void AddResource(string name, object value, string comment)
{
if (value is string) {
AddResource(name, (string)value, comment);
return;
}
if (name == null)
throw new ArgumentNullException("name");
if (written)
throw new InvalidOperationException("The resource is already generated.");
if (writer == null)
InitWriter();
if (value is byte[]) {
WriteBytes(name, value.GetType(), (byte[])value, comment);
return;
}
if (value is ResourceSerializedObject rso) {
var bytes = rso.GetBytes();
WriteBytes(name, null, bytes, 0, bytes.Length, comment);
return;
}
if (value == null) {
// nulls written as ResXNullRef
WriteString(name, "", ResXNullRefTypeName, comment);
return;
}
if (value != null && !value.GetType().IsSerializable)
throw new InvalidOperationException(String.Format("The element '{0}' of type '{1}' is not serializable.", name, value.GetType().Name));
TypeConverter converter = TypeDescriptor.GetConverter(value);
if (converter != null && converter.CanConvertTo(typeof(string)) && converter.CanConvertFrom(typeof(string))) {
string str = (string)converter.ConvertToInvariantString(value);
WriteString(name, str, value.GetType().AssemblyQualifiedName, comment);
return;
}
if (converter != null && converter.CanConvertTo(typeof(byte[])) && converter.CanConvertFrom(typeof(byte[]))) {
byte[] b = (byte[])converter.ConvertTo(value, typeof(byte[]));
WriteBytes(name, value.GetType(), b, comment);
return;
}
MemoryStream ms = new MemoryStream();
BinaryFormatter fmt = new BinaryFormatter();
try {
fmt.Serialize(ms, value);
} catch (Exception e) {
throw new InvalidOperationException("Cannot add a " + value.GetType() +
"because it cannot be serialized: " +
e.Message);
}
WriteBytes(name, null, ms.GetBuffer(), 0, (int)ms.Length, comment);
ms.Close();
}
public void AddResource(string name, string value)
{
AddResource(name, value, string.Empty);
}
private void AddResource(string name, string value, string comment)
{
if (name == null)
throw new ArgumentNullException("name");
if (value == null)
throw new ArgumentNullException("value");
if (written)
throw new InvalidOperationException("The resource is already generated.");
if (writer == null)
InitWriter();
WriteString(name, value, null, comment);
}
public void AddMetadata(string name, string value)
{
if (name == null)
throw new ArgumentNullException("name");
if (value == null)
throw new ArgumentNullException("value");
if (written)
throw new InvalidOperationException("The resource is already generated.");
if (writer == null)
InitWriter();
writer.WriteStartElement("metadata");
writer.WriteAttributeString("name", name);
writer.WriteAttributeString("xml:space", "preserve");
writer.WriteElementString("value", value);
writer.WriteEndElement();
}
public void AddMetadata(string name, byte[] value)
{
if (name == null)
throw new ArgumentNullException("name");
if (value == null)
throw new ArgumentNullException("value");
if (written)
throw new InvalidOperationException("The resource is already generated.");
if (writer == null)
InitWriter();
writer.WriteStartElement("metadata");
writer.WriteAttributeString("name", name);
writer.WriteAttributeString("type", value.GetType().AssemblyQualifiedName);
writer.WriteStartElement("value");
WriteNiceBase64(value, 0, value.Length);
writer.WriteEndElement();
writer.WriteEndElement();
}
public void AddMetadata(string name, object value)
{
if (value is string) {
AddMetadata(name, (string)value);
return;
}
if (value is byte[]) {
AddMetadata(name, (byte[])value);
return;
}
if (name == null)
throw new ArgumentNullException("name");
if (value == null)
throw new ArgumentNullException("value");
if (!value.GetType().IsSerializable)
throw new InvalidOperationException(String.Format("The element '{0}' of type '{1}' is not serializable.", name, value.GetType().Name));
if (written)
throw new InvalidOperationException("The resource is already generated.");
if (writer == null)
InitWriter();
Type type = value.GetType();
TypeConverter converter = TypeDescriptor.GetConverter(value);
if (converter != null && converter.CanConvertTo(typeof(string)) && converter.CanConvertFrom(typeof(string))) {
string str = (string)converter.ConvertToInvariantString(value);
writer.WriteStartElement("metadata");
writer.WriteAttributeString("name", name);
if (type != null)
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
writer.WriteStartElement("value");
writer.WriteString(str);
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteWhitespace("\n ");
return;
}
if (converter != null && converter.CanConvertTo(typeof(byte[])) && converter.CanConvertFrom(typeof(byte[]))) {
byte[] b = (byte[])converter.ConvertTo(value, typeof(byte[]));
writer.WriteStartElement("metadata");
writer.WriteAttributeString("name", name);
if (type != null) {
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
writer.WriteAttributeString("mimetype", ByteArraySerializedObjectMimeType);
writer.WriteStartElement("value");
WriteNiceBase64(b, 0, b.Length);
} else {
writer.WriteAttributeString("mimetype", BinSerializedObjectMimeType);
writer.WriteStartElement("value");
writer.WriteBase64(b, 0, b.Length);
}
writer.WriteEndElement();
writer.WriteEndElement();
return;
}
MemoryStream ms = new MemoryStream();
BinaryFormatter fmt = new BinaryFormatter();
try {
fmt.Serialize(ms, value);
} catch (Exception e) {
throw new InvalidOperationException("Cannot add a " + value.GetType() +
"because it cannot be serialized: " +
e.Message);
}
writer.WriteStartElement("metadata");
writer.WriteAttributeString("name", name);
if (type != null) {
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
writer.WriteAttributeString("mimetype", ByteArraySerializedObjectMimeType);
writer.WriteStartElement("value");
WriteNiceBase64(ms.GetBuffer(), 0, ms.GetBuffer().Length);
} else {
writer.WriteAttributeString("mimetype", BinSerializedObjectMimeType);
writer.WriteStartElement("value");
writer.WriteBase64(ms.GetBuffer(), 0, ms.GetBuffer().Length);
}
writer.WriteEndElement();
writer.WriteEndElement();
ms.Close();
}
public void Close()
{
if (!written) {
Generate();
}
if (writer != null) {
writer.Close();
stream = null;
filename = null;
textwriter = null;
}
}
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Generate()
{
if (written)
throw new InvalidOperationException("The resource is already generated.");
written = true;
writer.WriteEndElement();
writer.Flush();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
Close();
}
static string schema = @"
<xsd:schema id='root' xmlns='' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:msdata='urn:schemas-microsoft-com:xml-msdata'>
<xsd:element name='root' msdata:IsDataSet='true'>
<xsd:complexType>
<xsd:choice maxOccurs='unbounded'>
<xsd:element name='data'>
<xsd:complexType>
<xsd:sequence>
<xsd:element name='value' type='xsd:string' minOccurs='0' msdata:Ordinal='1' />
<xsd:element name='comment' type='xsd:string' minOccurs='0' msdata:Ordinal='2' />
</xsd:sequence>
<xsd:attribute name='name' type='xsd:string' msdata:Ordinal='1' />
<xsd:attribute name='type' type='xsd:string' msdata:Ordinal='3' />
<xsd:attribute name='mimetype' type='xsd:string' msdata:Ordinal='4' />
</xsd:complexType>
</xsd:element>
<xsd:element name='resheader'>
<xsd:complexType>
<xsd:sequence>
<xsd:element name='value' type='xsd:string' minOccurs='0' msdata:Ordinal='1' />
</xsd:sequence>
<xsd:attribute name='name' type='xsd:string' use='required' />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
".Replace("'", "\"").Replace("\t", " ");
#region Public Properties
public string BasePath {
get { return base_path; }
set { base_path = value; }
}
#endregion
}
}

526
ICSharpCode.Decompiler/Util/ResourcesFile.cs

@ -0,0 +1,526 @@ @@ -0,0 +1,526 @@
// Copyright (c) 2018 Daniel Grunwald
// Based on the .NET Core ResourceReader; make available under the MIT license
// by the .NET Foundation.
//
// 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace ICSharpCode.Decompiler.Util
{
/// <summary>
/// .resources file.
/// </summary>
public class ResourcesFile : IEnumerable<KeyValuePair<string, object>>, IDisposable
{
sealed class MyBinaryReader : BinaryReader
{
public MyBinaryReader(Stream input, bool leaveOpen) : base(input, Encoding.UTF8, leaveOpen)
{
}
// upgrade from protected to public visibility
public new int Read7BitEncodedInt()
{
return base.Read7BitEncodedInt();
}
public void Seek(long pos, SeekOrigin origin)
{
BaseStream.Seek(pos, origin);
}
}
enum ResourceTypeCode
{
Null = 0,
String = 1,
Boolean = 2,
Char = 3,
Byte = 4,
SByte = 5,
Int16 = 6,
UInt16 = 7,
Int32 = 8,
UInt32 = 9,
Int64 = 10,
UInt64 = 11,
Single = 12,
Double = 13,
Decimal = 14,
DateTime = 0xF,
TimeSpan = 0x10,
LastPrimitive = 0x10,
ByteArray = 0x20,
Stream = 33,
StartOfUserTypes = 0x40
}
/// <summary>Holds the number used to identify resource files.</summary>
public const int MagicNumber = unchecked((int)0xBEEFCACE);
const int ResourceSetVersion = 2;
readonly MyBinaryReader reader;
readonly int version;
readonly int numResources;
readonly string[] typeTable;
readonly int[] namePositions;
readonly long fileStartPosition;
readonly long nameSectionPosition;
readonly long dataSectionPosition;
long[] startPositions;
/// <summary>
/// Creates a new ResourcesFile.
/// </summary>
/// <param name="stream">Input stream.</param>
/// <param name="leaveOpen">Whether the stream should be help open when the ResourcesFile is disposed.</param>
/// <remarks>
/// The stream is must be held open while the ResourcesFile is in use.
/// The stream must be seekable; any operation using the ResourcesFile will end up seeking the stream.
/// </remarks>
public ResourcesFile(Stream stream, bool leaveOpen = true)
{
fileStartPosition = stream.Position;
reader = new MyBinaryReader(stream, leaveOpen);
const string ResourcesHeaderCorrupted = "Resources header corrupted.";
// Read ResourceManager header
// Check for magic number
int magicNum = reader.ReadInt32();
if (magicNum != MagicNumber)
throw new BadImageFormatException("Not a .resources file - invalid magic number");
// Assuming this is ResourceManager header V1 or greater, hopefully
// after the version number there is a number of bytes to skip
// to bypass the rest of the ResMgr header. For V2 or greater, we
// use this to skip to the end of the header
int resMgrHeaderVersion = reader.ReadInt32();
int numBytesToSkip = reader.ReadInt32();
if (numBytesToSkip < 0 || resMgrHeaderVersion < 0) {
throw new BadImageFormatException(ResourcesHeaderCorrupted);
}
if (resMgrHeaderVersion > 1) {
reader.BaseStream.Seek(numBytesToSkip, SeekOrigin.Current);
} else {
// We don't care about numBytesToSkip; read the rest of the header
// readerType:
reader.ReadString();
// resourceSetType:
reader.ReadString();
}
// Read RuntimeResourceSet header
// Do file version check
version = reader.ReadInt32();
if (version != ResourceSetVersion && version != 1)
throw new BadImageFormatException($"Unsupported resource set version: {version}");
numResources = reader.ReadInt32();
if (numResources < 0) {
throw new BadImageFormatException(ResourcesHeaderCorrupted);
}
// Read type positions into type positions array.
// But delay initialize the type table.
int numTypes = reader.ReadInt32();
if (numTypes < 0) {
throw new BadImageFormatException(ResourcesHeaderCorrupted);
}
typeTable = new string[numTypes];
for (int i = 0; i < numTypes; i++) {
typeTable[i] = reader.ReadString();
}
// Prepare to read in the array of name hashes
// Note that the name hashes array is aligned to 8 bytes so
// we can use pointers into it on 64 bit machines. (4 bytes
// may be sufficient, but let's plan for the future)
// Skip over alignment stuff. All public .resources files
// should be aligned No need to verify the byte values.
long pos = reader.BaseStream.Position - fileStartPosition;
int alignBytes = unchecked((int)pos) & 7;
if (alignBytes != 0) {
for (int i = 0; i < 8 - alignBytes; i++) {
reader.ReadByte();
}
}
// Skip over the array of name hashes
try {
reader.Seek(checked(4 * numResources), SeekOrigin.Current);
} catch (OverflowException) {
throw new BadImageFormatException(ResourcesHeaderCorrupted);
}
// Read in the array of relative positions for all the names.
namePositions = new int[numResources];
for (int i = 0; i < numResources; i++) {
int namePosition = reader.ReadInt32();
if (namePosition < 0) {
throw new BadImageFormatException(ResourcesHeaderCorrupted);
}
namePositions[i] = namePosition;
}
// Read location of data section.
int dataSectionOffset = reader.ReadInt32();
if (dataSectionOffset < 0) {
throw new BadImageFormatException(ResourcesHeaderCorrupted);
}
// Store current location as start of name section
nameSectionPosition = reader.BaseStream.Position;
dataSectionPosition = fileStartPosition + dataSectionOffset;
// _nameSectionOffset should be <= _dataSectionOffset; if not, it's corrupt
if (dataSectionPosition < nameSectionPosition) {
throw new BadImageFormatException(ResourcesHeaderCorrupted);
}
}
public void Dispose()
{
reader.Dispose();
}
public int ResourceCount => numResources;
public string GetResourceName(int index)
{
return GetResourceName(index, out _);
}
int GetResourceDataOffset(int index)
{
GetResourceName(index, out int dataOffset);
return dataOffset;
}
string GetResourceName(int index, out int dataOffset)
{
long pos = nameSectionPosition + namePositions[index];
byte[] bytes;
lock (reader) {
reader.Seek(pos, SeekOrigin.Begin);
// Can't use reader.ReadString, since it's using UTF-8!
int byteLen = reader.Read7BitEncodedInt();
if (byteLen < 0) {
throw new BadImageFormatException("Resource name has negative length");
}
bytes = new byte[byteLen];
// We must read byteLen bytes, or we have a corrupted file.
// Use a blocking read in case the stream doesn't give us back
// everything immediately.
int count = byteLen;
while (count > 0) {
int n = reader.Read(bytes, byteLen - count, count);
if (n == 0)
throw new BadImageFormatException("End of stream within a resource name");
count -= n;
}
dataOffset = reader.ReadInt32();
if (dataOffset < 0) {
throw new BadImageFormatException("Negative data offset");
}
}
return Encoding.Unicode.GetString(bytes);
}
internal bool AllEntriesAreStreams()
{
if (version != 2)
return false;
lock (reader) {
for (int i = 0; i < numResources; i++) {
int dataOffset = GetResourceDataOffset(i);
reader.Seek(dataSectionPosition + dataOffset, SeekOrigin.Begin);
var typeCode = (ResourceTypeCode)reader.Read7BitEncodedInt();
if (typeCode != ResourceTypeCode.Stream)
return false;
}
}
return true;
}
object LoadObject(int dataOffset)
{
try {
lock (reader) {
if (version == 1) {
return LoadObjectV1(dataOffset);
} else {
return LoadObjectV2(dataOffset);
}
}
} catch (EndOfStreamException e) {
throw new BadImageFormatException("Invalid resource file", e);
}
}
string FindType(int typeIndex)
{
if (typeIndex < 0 || typeIndex >= typeTable.Length)
throw new BadImageFormatException("Type index out of bounds");
return typeTable[typeIndex];
}
// This takes a virtual offset into the data section and reads an Object
// from that location.
// Anyone who calls LoadObject should make sure they take a lock so
// no one can cause us to do a seek in here.
private object LoadObjectV1(int dataOffset)
{
Debug.Assert(System.Threading.Monitor.IsEntered(reader));
reader.Seek(dataSectionPosition + dataOffset, SeekOrigin.Begin);
int typeIndex = reader.Read7BitEncodedInt();
if (typeIndex == -1)
return null;
string typeName = FindType(typeIndex);
int comma = typeName.IndexOf(',');
if (comma > 0) {
// strip assembly name
typeName = typeName.Substring(0, comma);
}
switch (typeName) {
case "System.String":
return reader.ReadString();
case "System.Byte":
return reader.ReadByte();
case "System.SByte":
return reader.ReadSByte();
case "System.Int16":
return reader.ReadInt16();
case "System.UInt16":
return reader.ReadUInt16();
case "System.Int32":
return reader.ReadInt32();
case "System.UInt32":
return reader.ReadUInt32();
case "System.Int64":
return reader.ReadInt64();
case "System.UInt64":
return reader.ReadUInt64();
case "System.Single":
return reader.ReadSingle();
case "System.Double":
return reader.ReadDouble();
case "System.DateTime":
// Ideally we should use DateTime's ToBinary & FromBinary,
// but we can't for compatibility reasons.
return new DateTime(reader.ReadInt64());
case "System.TimeSpan":
return new TimeSpan(reader.ReadInt64());
case "System.Decimal":
int[] bits = new int[4];
for (int i = 0; i < bits.Length; i++)
bits[i] = reader.ReadInt32();
return new decimal(bits);
default:
return new ResourceSerializedObject(FindType(typeIndex), this, reader.BaseStream.Position);
}
}
private object LoadObjectV2(int dataOffset)
{
Debug.Assert(System.Threading.Monitor.IsEntered(reader));
reader.Seek(dataSectionPosition + dataOffset, SeekOrigin.Begin);
var typeCode = (ResourceTypeCode)reader.Read7BitEncodedInt();
switch (typeCode) {
case ResourceTypeCode.Null:
return null;
case ResourceTypeCode.String:
return reader.ReadString();
case ResourceTypeCode.Boolean:
return reader.ReadBoolean();
case ResourceTypeCode.Char:
return (char)reader.ReadUInt16();
case ResourceTypeCode.Byte:
return reader.ReadByte();
case ResourceTypeCode.SByte:
return reader.ReadSByte();
case ResourceTypeCode.Int16:
return reader.ReadInt16();
case ResourceTypeCode.UInt16:
return reader.ReadUInt16();
case ResourceTypeCode.Int32:
return reader.ReadInt32();
case ResourceTypeCode.UInt32:
return reader.ReadUInt32();
case ResourceTypeCode.Int64:
return reader.ReadInt64();
case ResourceTypeCode.UInt64:
return reader.ReadUInt64();
case ResourceTypeCode.Single:
return reader.ReadSingle();
case ResourceTypeCode.Double:
return reader.ReadDouble();
case ResourceTypeCode.Decimal:
return reader.ReadDecimal();
case ResourceTypeCode.DateTime:
// Use DateTime's ToBinary & FromBinary.
long data = reader.ReadInt64();
return DateTime.FromBinary(data);
case ResourceTypeCode.TimeSpan:
long ticks = reader.ReadInt64();
return new TimeSpan(ticks);
// Special types
case ResourceTypeCode.ByteArray: {
int len = reader.ReadInt32();
if (len < 0) {
throw new BadImageFormatException("Resource with negative length");
}
return reader.ReadBytes(len);
}
case ResourceTypeCode.Stream: {
int len = reader.ReadInt32();
if (len < 0) {
throw new BadImageFormatException("Resource with negative length");
}
byte[] bytes = reader.ReadBytes(len);
return new MemoryStream(bytes, writable: false);
}
default:
if (typeCode < ResourceTypeCode.StartOfUserTypes) {
throw new BadImageFormatException("Invalid typeCode");
}
return new ResourceSerializedObject(FindType(typeCode - ResourceTypeCode.StartOfUserTypes), this, reader.BaseStream.Position);
}
}
public object GetResourceValue(int index)
{
GetResourceName(index, out int dataOffset);
return LoadObject(dataOffset);
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
for (int i = 0; i < numResources; i++) {
string name = GetResourceName(i, out int dataOffset);
object val = LoadObject(dataOffset);
yield return new KeyValuePair<string, object>(name, val);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
long[] GetStartPositions()
{
long[] positions = LazyInit.VolatileRead(ref startPositions);
if (positions != null)
return positions;
lock (reader) {
// double-checked locking
positions = LazyInit.VolatileRead(ref startPositions);
if (positions != null)
return positions;
positions = new long[numResources * 2];
int outPos = 0;
for (int i = 0; i < numResources; i++) {
positions[outPos++] = nameSectionPosition + namePositions[i];
positions[outPos++] = dataSectionPosition + GetResourceDataOffset(i);
}
Array.Sort(positions);
return LazyInit.GetOrSet(ref startPositions, positions);
}
}
internal byte[] GetBytesForSerializedObject(long pos)
{
long[] positions = GetStartPositions();
int i = Array.BinarySearch(positions, pos);
if (i < 0) {
// 'pos' the the start position of the serialized object data
// This is the position after the type code, so it should not appear in the 'positions' array.
// Set i to the index of the next position after 'pos'.
i = ~i;
// Note: if 'pos' does exist in the array, that means the stream has length 0,
// so we keep the i that we found.
}
lock (reader) {
long endPos;
if (i == positions.Length) {
endPos = reader.BaseStream.Length;
} else {
endPos = positions[i];
}
int len = (int)(endPos - pos);
reader.Seek(pos, SeekOrigin.Begin);
return reader.ReadBytes(len);
}
}
}
public class ResourceSerializedObject
{
public string TypeName { get; }
readonly ResourcesFile file;
readonly long position;
internal ResourceSerializedObject(string typeName, ResourcesFile file, long position)
{
this.TypeName = typeName;
this.file = file;
this.position = position;
}
/// <summary>
/// Gets a stream that starts with the serialized object data.
/// </summary>
public Stream GetStream()
{
return new MemoryStream(file.GetBytesForSerializedObject(position), writable: false);
}
/// <summary>
/// Gets the serialized object data.
/// </summary>
public byte[] GetBytes()
{
return file.GetBytesForSerializedObject(position);
}
}
}

109
ILSpy.AddIn/AssemblyFileFinder.cs

@ -0,0 +1,109 @@ @@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.AddIn
{
public class AssemblyFileFinder
{
public static string FindAssemblyFile(AssemblyDefinition assemblyDefinition, string assemblyFile)
{
var assemblyName = assemblyDefinition.Name;
var detectedTargetFramework = DetectTargetFrameworkId(assemblyDefinition, assemblyFile);
if (string.IsNullOrEmpty(detectedTargetFramework)) {
// Without a target framework id it makes no sense to continue
return null;
}
var targetFramework = detectedTargetFramework.Split(new[] { ",Version=v" }, StringSplitOptions.None);
string file = null;
switch (targetFramework[0]) {
case ".NETCoreApp":
case ".NETStandard":
if (targetFramework.Length != 2)
return FindAssemblyFromGAC(assemblyDefinition);
var version = targetFramework[1].Length == 3 ? targetFramework[1] + ".0" : targetFramework[1];
var dotNetCorePathFinder = new DotNetCorePathFinder(assemblyFile, detectedTargetFramework, version);
file = dotNetCorePathFinder.TryResolveDotNetCore(Decompiler.Metadata.AssemblyNameReference.Parse(assemblyName.FullName));
if (file != null)
return file;
return FindAssemblyFromGAC(assemblyDefinition);
default:
return FindAssemblyFromGAC(assemblyDefinition);
}
}
static string FindAssemblyFromGAC(AssemblyDefinition assemblyDefinition)
{
return GacInterop.FindAssemblyInNetGac(Decompiler.Metadata.AssemblyNameReference.Parse(assemblyDefinition.Name.FullName));
}
static readonly string RefPathPattern = @"NuGetFallbackFolder[/\\][^/\\]+[/\\][^/\\]+[/\\]ref[/\\]";
public static bool IsReferenceAssembly(AssemblyDefinition assemblyDef, string assemblyFile)
{
if (assemblyDef.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.Runtime.CompilerServices.ReferenceAssemblyAttribute"))
return true;
// Try to detect reference assembly through specific path pattern
var refPathMatch = Regex.Match(assemblyFile, RefPathPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
return refPathMatch.Success;
}
static readonly string DetectTargetFrameworkIdRefPathPattern =
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<1>.NETFramework)[/\\]v(?<2>[^/\\]+)[/\\])" +
@"|(NuGetFallbackFolder[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\]ref[/\\])";
public static string DetectTargetFrameworkId(AssemblyDefinition assembly, string assemblyPath = null)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));
const string TargetFrameworkAttributeName = "System.Runtime.Versioning.TargetFrameworkAttribute";
foreach (var attribute in assembly.CustomAttributes) {
if (attribute.AttributeType.FullName != TargetFrameworkAttributeName)
continue;
if (attribute.HasConstructorArguments) {
if (attribute.ConstructorArguments[0].Value is string value)
return value;
}
}
// Optionally try to detect target version through assembly path as a fallback (use case: reference assemblies)
if (assemblyPath != null) {
/*
* Detected path patterns (examples):
*
* - .NETFramework -> C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll
* - .NETCore -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll
* - .NETStandard -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll
*/
var pathMatch = Regex.Match(assemblyPath, DetectTargetFrameworkIdRefPathPattern,
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
if (pathMatch.Success) {
var type = pathMatch.Groups[1].Value;
var version = pathMatch.Groups[2].Value;
if (type == ".NETFramework") {
return $".NETFramework,Version=v{version}";
} else if (type.Contains("netcore")) {
return $".NETCoreApp,Version=v{version}";
} else if (type.Contains("netstandard")) {
return $".NETStandard,Version=v{version}";
}
}
}
return string.Empty;
}
}
}

3
ILSpy.AddIn/Commands/OpenCodeItemCommand.cs

@ -106,8 +106,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -106,8 +106,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
owner.ShowMessage(OLEMSGICON.OLEMSGICON_WARNING,
"Symbol can't be opened. This might happen while project is loading.",
Environment.NewLine, invalidSymbolReference.AssemblyFile);
}
if (invalidSymbolReference.IsProjectReference) {
} else if (invalidSymbolReference.IsProjectReference) {
// Some project references don't have assemblies, maybe not compiled yet?
if (owner.ShowMessage(
OLEMSGBUTTON.OLEMSGBUTTON_YESNO, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, OLEMSGICON.OLEMSGICON_WARNING,

9
ILSpy.AddIn/Commands/OpenILSpyCommand.cs

@ -89,8 +89,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -89,8 +89,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
foreach (var reference in parentProject.MetadataReferences) {
using (var assemblyDef = AssemblyDefinition.ReadAssembly(reference.Display)) {
string assemblyName = assemblyDef.Name.Name;
if (IsReferenceAssembly(assemblyDef)) {
string resolvedAssemblyFile = GacInterop.FindAssemblyInNetGac(Decompiler.Metadata.AssemblyNameReference.Parse(assemblyDef.FullName));
if (AssemblyFileFinder.IsReferenceAssembly(assemblyDef, reference.Display)) {
string resolvedAssemblyFile = AssemblyFileFinder.FindAssemblyFile(assemblyDef, reference.Display);
dict.Add(assemblyName,
new DetectedReference(assemblyName, resolvedAssemblyFile, false));
} else {
@ -127,11 +127,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -127,11 +127,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
return null;
}
protected bool IsReferenceAssembly(AssemblyDefinition assemblyDef)
{
return assemblyDef.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.Runtime.CompilerServices.ReferenceAssemblyAttribute");
}
}
class OpenILSpyCommand : ILSpyCommand

1
ILSpy.AddIn/ILSpy.AddIn.csproj

@ -84,6 +84,7 @@ @@ -84,6 +84,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PkgCmdID.cs" />
<Compile Include="SyntaxNodeExtensions.cs" />
<Compile Include="AssemblyFileFinder.cs" />
<Compile Include="Utils.cs" />
</ItemGroup>

11
ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs

@ -5,11 +5,11 @@ using System; @@ -5,11 +5,11 @@ using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Resources;
using System.Threading;
using System.Xml.Linq;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Tests.Helpers;
using ICSharpCode.Decompiler.Util;
using NUnit.Framework;
namespace ILSpy.BamlDecompiler.Tests
@ -143,15 +143,16 @@ namespace ILSpy.BamlDecompiler.Tests @@ -143,15 +143,16 @@ namespace ILSpy.BamlDecompiler.Tests
{
if (res.ResourceType != ResourceType.Embedded) return null;
Stream s = res.TryOpenStream();
if (s == null) return null;
s.Position = 0;
ResourceReader reader;
ResourcesFile resources;
try {
reader = new ResourceReader(s);
resources = new ResourcesFile(s);
} catch (ArgumentException) {
return null;
}
foreach (DictionaryEntry entry in reader.Cast<DictionaryEntry>().OrderBy(e => e.Key.ToString())) {
if (entry.Key.ToString() == name) {
foreach (var entry in resources.OrderBy(e => e.Key)) {
if (entry.Key == name) {
if (entry.Value is Stream)
return (Stream)entry.Value;
if (entry.Value is byte[])

8
ILSpy/Languages/CSharpLanguage.cs

@ -17,12 +17,10 @@ @@ -17,12 +17,10 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Resources;
using System.Windows;
using System.Windows.Controls;
using ICSharpCode.Decompiler;
@ -388,11 +386,11 @@ namespace ICSharpCode.ILSpy @@ -388,11 +386,11 @@ namespace ICSharpCode.ILSpy
protected override IEnumerable<Tuple<string, string>> WriteResourceToFile(string fileName, string resourceName, Stream entryStream)
{
if (fileName.EndsWith(".resource", StringComparison.OrdinalIgnoreCase)) {
using (ResourceReader reader = new ResourceReader(entryStream))
fileName = Path.ChangeExtension(fileName, ".resx");
using (FileStream fs = new FileStream(Path.Combine(targetDirectory, fileName), FileMode.Create, FileAccess.Write))
using (ResXResourceWriter writer = new ResXResourceWriter(fs)) {
foreach (DictionaryEntry entry in reader) {
writer.AddResource((string)entry.Key, entry.Value);
foreach (var entry in new ResourcesFile(entryStream)) {
writer.AddResource(entry.Key, entry.Value);
}
}
return new[] { Tuple.Create("EmbeddedResource", fileName) };

47
ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs

@ -17,15 +17,12 @@ @@ -17,15 +17,12 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Resources;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.Controls;
using ICSharpCode.ILSpy.TextView;
@ -61,8 +58,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -61,8 +58,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
this.LazyLoading = true;
}
public override object Icon
{
public override object Icon {
get { return Images.ResourceResourcesFile; }
}
@ -71,46 +67,42 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -71,46 +67,42 @@ namespace ICSharpCode.ILSpy.TreeNodes
Stream s = Resource.TryOpenStream();
if (s == null) return;
s.Position = 0;
ResourceReader reader;
try {
reader = new ResourceReader(s);
}
catch (ArgumentException) {
return;
}
foreach (DictionaryEntry entry in reader.Cast<DictionaryEntry>().OrderBy(e => e.Key.ToString())) {
ProcessResourceEntry(entry);
foreach (var entry in new ResourcesFile(s)) {
ProcessResourceEntry(entry);
}
} catch (BadImageFormatException) {
// ignore errors
}
}
private void ProcessResourceEntry(DictionaryEntry entry)
private void ProcessResourceEntry(KeyValuePair<string, object> entry)
{
var keyString = entry.Key.ToString();
if (entry.Value is String) {
stringTableEntries.Add(new KeyValuePair<string, string>(keyString, (string)entry.Value));
stringTableEntries.Add(new KeyValuePair<string, string>(entry.Key, (string)entry.Value));
return;
}
if (entry.Value is byte[]) {
Children.Add(ResourceEntryNode.Create(keyString, new MemoryStream((byte[])entry.Value)));
Children.Add(ResourceEntryNode.Create(entry.Key, new MemoryStream((byte[])entry.Value)));
return;
}
var node = ResourceEntryNode.Create(keyString, entry.Value);
var node = ResourceEntryNode.Create(entry.Key, entry.Value);
if (node != null) {
Children.Add(node);
return;
}
string entryType = entry.Value.GetType().FullName;
if (entry.Value is System.Globalization.CultureInfo) {
otherEntries.Add(new SerializedObjectRepresentation(keyString, entryType, ((System.Globalization.CultureInfo)entry.Value).DisplayName));
if (entry.Value == null) {
otherEntries.Add(new SerializedObjectRepresentation(entry.Key, "null", ""));
} else if (entry.Value is ResourceSerializedObject so) {
otherEntries.Add(new SerializedObjectRepresentation(entry.Key, so.TypeName, "<serialized>"));
} else {
otherEntries.Add(new SerializedObjectRepresentation(keyString, entryType, entry.Value.ToString()));
otherEntries.Add(new SerializedObjectRepresentation(entry.Key, entry.Value.GetType().FullName, entry.Value.ToString()));
}
}
public override bool Save(DecompilerTextView textView)
{
Stream s = Resource.TryOpenStream();
@ -127,10 +119,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -127,10 +119,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
break;
case 2:
var reader = new ResourceReader(s);
using (var writer = new ResXResourceWriter(dlg.OpenFile())) {
foreach (DictionaryEntry entry in reader) {
writer.AddResource(entry.Key.ToString(), entry.Value);
foreach (var entry in new ResourcesFile(s)) {
writer.AddResource(entry.Key, entry.Value);
}
}
break;

3
appveyor.yml

@ -31,8 +31,7 @@ for: @@ -31,8 +31,7 @@ for:
- branches:
only:
- master
- 3.0.x
- 3.1.x
- 3.2.x
artifacts:
- path: ILSpy_binaries.zip
name: ILSpy %APPVEYOR_REPO_BRANCH% %ILSPY_VERSION_NUMBER% binaries

99
doc/Resources.txt

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
System.Resources.ResourceReader is unsuitable for deserializing resources in the ILSpy context,
because it tries to deserialize custom resource types, which fails if the types are in a non-GAC assembly.
So we are instead using our own "class ResourcesFile", which is based on the
.NET Core ResourceReader implementation.
struct ResourcesFileFormat {
int32 magicNum; [check == ResourceManager.MagicNumber]
// ResourceManager header:
int32 resMgrHeaderVersion; [check >= 0]
int32 numBytesToSkip; [check >= 0]
if (resMgrHeaderVersion <= 1) {
string readerType;
string resourceSetType;
} else {
byte _[numBytesToSkip];
}
// RuntimeResourceSet header:
int32 version; [check in (1, 2)]
int32 numResources; [check >=0]
int32 numTypes; [check >=0]
string typeName[numTypes];
.align 8;
int32 nameHashes[numResources];
int32 namePositions[numResources]; [check >= 0]
int32 dataSectionOffset; [check >= current position in file]
byte remainderOfFile[];
}
// normal strings in this file format are stored as:
struct string {
compressedint len;
byte value[len]; // interpret as UTF-8
}
// NameEntry #i is stored starting at remainderOfFile[namePositions[i]]
// (that is, namePositions is interpreted relative to the start of the remainderOfFile array)
struct NameEntry {
compressedint len;
byte name[len]; // interpret as UTF-16
int32 dataOffset; [check >= 0]
}
// Found at position ResourcesFileFormat.dataSectionOffset+NameEntry.dataOffset in the file.
struct ValueEntry {
if (version == 1) {
compressedint typeIndex;
if (typeIndex == -1) {
// no value stored; value is implicitly null
} else {
switch (typeName[typeIndex]) {
case string:
case int, uint, long, ulong, sbyte, byte, short, ushort:
case float:
case double:
T value; // value directly stored
case DateTime:
int64 value; // new DateTime(_store.ReadInt64())
case TimeSpan:
int64 value; // new TimeSpan(_store.ReadInt64())
case decimal:
int32 value[4];
default:
byte data[...]; // BinaryFormatter-serialized data using typeName[typeIndex]
}
}
} else if (version == 2) {
compressedint typeCode;
// note: unlike v1, no lookup into the typeName array!
switch (typeCode) {
case null:
// no value stored; value is implicitly null
case string:
case bool:
case int, uint, long, ulong, sbyte, byte, short, ushort:
T value;
case char:
uint16 value;
case float:
case double:
case decimal:
T value;
case DateTime:
int64 value; // DateTime.FromBinary(_store.ReadInt64())
case TimeSpan:
int64 value; // new TimeSpan(_store.ReadInt64())
case ResourceTypeCode.ByteArray:
int32 len;
byte value[len];
case ResourceTypeCode.Stream:
int32 len;
byte value[len];
case >= ResourceTypeCode.StartOfUserTypes:
byte data[...]; // BinaryFormatter-serialized data using typeName[typeCode - ResourceTypeCode.StartOfUserTypes]
}
}
}
Loading…
Cancel
Save