diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..63fd71df8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,112 @@ +root = true + +[*] +charset = utf-8 +#end_of_line = +indent_size = 4 +indent_style = tab +tab_width = 4 + +[*.json] + +[app.config] + +[*.yml] +indent_size = 2 +indent_style = space + +[*.{proj,csproj,vbproj,props,targets,resx,vsixmanifest}] +indent_size = 2 +indent_style = space + +[app.manifest] +indent_size = 2 +indent_style = space + +[*.xml] + +[*.xaml] +indent_style = space + +[*.{cs,vb}] +insert_final_newline = true + +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = true +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:none +dotnet_style_predefined_type_for_member_access = true:none +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_require_accessibility_modifiers = never:info + +[*.cs] +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = false +csharp_indent_labels = flush_left +csharp_indent_switch_labels = false +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = none +csharp_new_line_between_query_expression_clauses = true +csharp_prefer_braces = false +csharp_prefer_simple_default_expression = true:suggestion +#csharp_preferred_modifier_order = +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = +csharp_space_between_square_brackets = false +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_deconstructed_variable_declaration = false:none +csharp_style_expression_bodied_accessors = true:suggestion +csharp_style_expression_bodied_constructors = true:suggestion +csharp_style_expression_bodied_indexers = true:suggestion +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_expression_bodied_operators = true:suggestion +csharp_style_expression_bodied_properties = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_var_elsewhere = true:suggestion +csharp_style_var_for_built_in_types = false:none +csharp_style_var_when_type_is_apparent = true:suggestion + +[*.vb] +#visual_basic_preferred_modifier_order = diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..1071e16bc --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,78 @@ +name: GitHub CI +on: + push: + paths: + - '.github/workflows/**' + - 'Examples/**' + - 'src/**' + - '!**/*.md' + branches: + - master + pull_request: + paths: + - 'Examples/**' + - 'src/**' + - '!**/*.md' + branches: + - master + release: + types: released + +env: + CI_REQ_DOTNET_SDK_VER: 6.0.100 + +jobs: + build-windows: + name: Build (Windows) + runs-on: windows-latest + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{env.CI_REQ_DOTNET_SDK_VER}} + + - name: Build + shell: pwsh + run: | + $msbuildPath = Split-Path (& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.Component.MSBuild -find MSBuild\Current\Bin\amd64\MSBuild.exe | Select-Object -First 1) -Parent + $env:PATH = $msbuildPath + ';' + $env:PATH + .\build.ps1 + + - name: upload-artifact doesn't support wildcards + if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') + shell: pwsh + run: | + New-Item -ItemType Directory nuget_files > $null + Copy-Item src\bin\Release\*.*nupkg nuget_files + + - uses: actions/upload-artifact@v4 + if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') + with: + name: nupkg + path: nuget_files + if-no-files-found: error + + - name: Upload to nuget.org if it's a new release + if: startsWith(github.ref, 'refs/tags/') + shell: pwsh + run: | + Invoke-WebRequest -Uri https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -UseBasicParsing -OutFile nuget.exe + Get-ChildItem src\bin\Release\dnlib.*.nupkg | ForEach-Object { .\nuget.exe push $_.FullName -ApiKey ${{secrets.NUGET_APIKEY}} -NonInteractive -Source https://api.nuget.org/v3/index.json } + + # Make sure it builds on Linux too + build-ubuntu: + name: Build (Ubuntu) + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{env.CI_REQ_DOTNET_SDK_VER}} + + - name: Build + shell: pwsh + run: ./build.ps1 diff --git a/.gitignore b/.gitignore index 85f65d4d2..f6adf54ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,4 @@ -*~ -*/obj/ -*.csproj.user -*.sdf -*.opensdf -*.suo -/Debug/ -/Release/ -/Examples/bin/ -*.tmp_proj +.vs/ +.vscode/ +bin/ +obj/ diff --git a/Examples/Example1.cs b/Examples/Example1.cs index c866bec13..e2b5dd3e5 100644 --- a/Examples/Example1.cs +++ b/Examples/Example1.cs @@ -1,4 +1,4 @@ -using System; +using System; using dnlib.DotNet; namespace dnlib.Examples { @@ -8,17 +8,17 @@ namespace dnlib.Examples { public class Example1 { public static void Run() { // Load mscorlib.dll - string filename = typeof(void).Module.FullyQualifiedName; - ModuleDefMD mod = ModuleDefMD.Load(filename); + var filename = typeof(void).Module.FullyQualifiedName; + var mod = ModuleDefMD.Load(filename); int totalNumTypes = 0; // mod.Types only returns non-nested types. // mod.GetTypes() returns all types, including nested types. - foreach (TypeDef type in mod.GetTypes()) { + foreach (var type in mod.GetTypes()) { totalNumTypes++; Console.WriteLine(); Console.WriteLine("Type: {0}", type.FullName); - if (type.BaseType != null) + if (type.BaseType is not null) Console.WriteLine(" Base type: {0}", type.BaseType.FullName); Console.WriteLine(" Methods: {0}", type.Methods.Count); @@ -29,7 +29,7 @@ public static void Run() { if (type.Interfaces.Count > 0) { Console.WriteLine(" Interfaces:"); - foreach (InterfaceImpl iface in type.Interfaces) + foreach (var iface in type.Interfaces) Console.WriteLine(" {0}", iface.Interface.FullName); } } diff --git a/Examples/Example2.cs b/Examples/Example2.cs index 476cde7df..97926d504 100644 --- a/Examples/Example2.cs +++ b/Examples/Example2.cs @@ -1,4 +1,4 @@ -using dnlib.DotNet; +using dnlib.DotNet; using dnlib.DotNet.Emit; namespace dnlib.Examples { @@ -7,10 +7,10 @@ public class Example2 { // and then save the assembly to disk. public static void Run() { // Open the current module - ModuleDefMD mod = ModuleDefMD.Load(typeof(Example2).Module); + var mod = ModuleDefMD.Load(typeof(Example2).Module); // Create a new public class that derives from System.Object - TypeDef type1 = new TypeDefUser("My.Namespace", "MyType", + var type1 = new TypeDefUser("My.Namespace", "MyType", mod.CorLibTypes.Object.TypeDefOrRef); type1.Attributes = TypeAttributes.Public | TypeAttributes.AutoLayout | TypeAttributes.Class | TypeAttributes.AnsiClass; @@ -19,7 +19,7 @@ public static void Run() { mod.Types.Add(type1); // Create a public static System.Int32 field called MyField - FieldDef field1 = new FieldDefUser("MyField", + var field1 = new FieldDefUser("MyField", new FieldSig(mod.CorLibTypes.Int32), FieldAttributes.Public | FieldAttributes.Static); // Add it to the type we created earlier @@ -27,22 +27,22 @@ public static void Run() { // Add a static method that adds both inputs and the static field // and returns the result - MethodImplAttributes methImplFlags = MethodImplAttributes.IL | MethodImplAttributes.Managed; - MethodAttributes methFlags = MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.ReuseSlot; - MethodDef meth1 = new MethodDefUser("MyMethod", + var methImplFlags = MethodImplAttributes.IL | MethodImplAttributes.Managed; + var methFlags = MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.ReuseSlot; + var meth1 = new MethodDefUser("MyMethod", MethodSig.CreateStatic(mod.CorLibTypes.Int32, mod.CorLibTypes.Int32, mod.CorLibTypes.Int32), methImplFlags, methFlags); type1.Methods.Add(meth1); // Create the CIL method body - CilBody body = new CilBody(); + var body = new CilBody(); meth1.Body = body; // Name the 1st and 2nd args a and b, respectively meth1.ParamDefs.Add(new ParamDefUser("a", 1)); meth1.ParamDefs.Add(new ParamDefUser("b", 2)); // Create a local. We don't really need it but let's add one anyway - Local local1 = new Local(mod.CorLibTypes.Int32); + var local1 = new Local(mod.CorLibTypes.Int32); body.Variables.Add(local1); // Add the instructions, and use the useless local diff --git a/Examples/Example3.cs b/Examples/Example3.cs index 62688233c..30e919d74 100644 --- a/Examples/Example3.cs +++ b/Examples/Example3.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text; using dnlib.DotNet; using dnlib.DotNet.Emit; @@ -23,12 +23,12 @@ public class Example3 { public static void Run() { // Create a new module. The string passed in is the name of the module, // not the file name. - ModuleDef mod = new ModuleDefUser("MyModule.exe"); + var mod = new ModuleDefUser("MyModule.exe"); // It's a console application mod.Kind = ModuleKind.Console; // Add the module to an assembly - AssemblyDef asm = new AssemblyDefUser("MyAssembly", new Version(1, 2, 3, 4), null, null); + var asm = new AssemblyDefUser("MyAssembly", new Version(1, 2, 3, 4), null, null); asm.Modules.Add(mod); // Add a .NET resource @@ -37,14 +37,14 @@ public static void Run() { ManifestResourceAttributes.Private)); // Add the startup type. It derives from System.Object. - TypeDef startUpType = new TypeDefUser("My.Namespace", "Startup", mod.CorLibTypes.Object.TypeDefOrRef); + var startUpType = new TypeDefUser("My.Namespace", "Startup", mod.CorLibTypes.Object.TypeDefOrRef); startUpType.Attributes = TypeAttributes.NotPublic | TypeAttributes.AutoLayout | TypeAttributes.Class | TypeAttributes.AnsiClass; // Add the type to the module mod.Types.Add(startUpType); // Create the entry point method - MethodDef entryPoint = new MethodDefUser("Main", + var entryPoint = new MethodDefUser("Main", MethodSig.CreateStatic(mod.CorLibTypes.Int32, new SZArraySig(mod.CorLibTypes.String))); entryPoint.Attributes = MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.ReuseSlot; @@ -57,14 +57,14 @@ public static void Run() { mod.EntryPoint = entryPoint; // Create a TypeRef to System.Console - TypeRef consoleRef = new TypeRefUser(mod, "System", "Console", mod.CorLibTypes.AssemblyRef); + var consoleRef = new TypeRefUser(mod, "System", "Console", mod.CorLibTypes.AssemblyRef); // Create a method ref to 'System.Void System.Console::WriteLine(System.String)' - MemberRef consoleWrite1 = new MemberRefUser(mod, "WriteLine", + var consoleWrite1 = new MemberRefUser(mod, "WriteLine", MethodSig.CreateStatic(mod.CorLibTypes.Void, mod.CorLibTypes.String), consoleRef); // Add a CIL method body to the entry point method - CilBody epBody = new CilBody(); + var epBody = new CilBody(); entryPoint.Body = epBody; epBody.Instructions.Add(OpCodes.Ldstr.ToInstruction("Hello World!")); epBody.Instructions.Add(OpCodes.Call.ToInstruction(consoleWrite1)); diff --git a/Examples/Example4.cs b/Examples/Example4.cs index 04f6de693..a2dd34005 100644 --- a/Examples/Example4.cs +++ b/Examples/Example4.cs @@ -1,4 +1,4 @@ -using System; +using System; using dnlib.DotNet; using dnlib.DotNet.Emit; diff --git a/Examples/Example5.cs b/Examples/Example5.cs index edb26243f..8116fe53f 100644 --- a/Examples/Example5.cs +++ b/Examples/Example5.cs @@ -1,8 +1,6 @@ -using System; +using System; using System.IO; using dnlib.DotNet; -using dnlib.PE; -using dnlib.IO; namespace dnlib.Examples { /// @@ -16,7 +14,7 @@ public static void Run() { var mod = ModuleDefMD.Load(typeof(int).Module); // Get PE image interface - var peImage = mod.MetaData.PEImage; + var peImage = mod.Metadata.PEImage; // Print some info Console.WriteLine("Machine: {0}", peImage.ImageNTHeaders.FileHeader.Machine); @@ -26,13 +24,13 @@ public static void Run() { for (int i = 0; i < peImage.ImageSectionHeaders.Count; i++) { var section = peImage.ImageSectionHeaders[i]; - // Create a stream for the whole section - var stream = peImage.CreateStream(section.VirtualAddress, section.SizeOfRawData); + // Create a reader for the whole section + var reader = peImage.CreateReader(section.VirtualAddress, section.SizeOfRawData); // Write the data to disk var fileName = string.Format(sectionFileName, i); Console.WriteLine("Dumping section {0} to file {1}", section.DisplayName, fileName); - File.WriteAllBytes(fileName, stream.ReadAllBytes()); + File.WriteAllBytes(fileName, reader.ToArray()); } } } diff --git a/Examples/Example6.cs b/Examples/Example6.cs index 00b196a0b..d92add484 100644 --- a/Examples/Example6.cs +++ b/Examples/Example6.cs @@ -1,5 +1,4 @@ -using System; -using System.IO; +using System; using dnlib.DotNet; using dnlib.DotNet.MD; using dnlib.DotNet.Writer; @@ -8,15 +7,13 @@ namespace dnlib.Examples { /// - /// This example shows how to create a module writer listener that gets notified of various + /// This example shows how to add a module writer listener that gets notified of various /// events. This listener just adds a new PE section to the image and prints the new RIDs. /// It also shows how to add some dummy .NET heaps, and simple obfuscation that will break /// most libraries that open .NET assemblies. /// - public class Example6 : IModuleWriterListener { - public static void Run() { - new Example6().DoIt(); - } + public class Example6 { + public static void Run() => new Example6().DoIt(); void DoIt() { string destFileName = @"c:\output.dll"; @@ -28,7 +25,7 @@ void DoIt() { var opts = new ModuleWriterOptions(mod); // Add a listener that gets notified during the writing process - opts.Listener = this; + opts.WriterEvent += OnWriterEvent; // This is normally 16 but setting it to a value less than 14 will fool some // apps into thinking that there's no .NET metadata available @@ -36,15 +33,18 @@ void DoIt() { // Add extra data. This will break most libraries that open .NET assemblies. // Any value can be written here. - opts.MetaDataOptions.TablesHeapOptions.ExtraData = 0x12345678; + opts.MetadataOptions.TablesHeapOptions.ExtraData = 0x12345678; // Add a few dummy heaps - opts.MetaDataOptions.OtherHeaps.Add(new MyHeap("#US ")); - opts.MetaDataOptions.OtherHeaps.Add(new MyHeap("#Strings ")); - opts.MetaDataOptions.OtherHeaps.Add(new MyHeap("#Strimgs")); - opts.MetaDataOptions.OtherHeaps.Add(new MyHeap("#GU1D")); - opts.MetaDataOptions.OtherHeapsEnd.Add(new MyHeap("#US ")); - opts.MetaDataOptions.OtherHeapsEnd.Add(new MyHeap("#Strings ")); + opts.MetadataOptions.CustomHeaps.Add(new MyHeap("#US ")); + opts.MetadataOptions.CustomHeaps.Add(new MyHeap("#Strings ")); + opts.MetadataOptions.CustomHeaps.Add(new MyHeap("#Strimgs")); + opts.MetadataOptions.CustomHeaps.Add(new MyHeap("#GU1D")); + opts.MetadataOptions.CustomHeaps.Add(new MyHeap("#US ")); + opts.MetadataOptions.CustomHeaps.Add(new MyHeap("#Strings ")); + opts.MetadataOptions.MetadataHeapsAdded += (s, e) => { + // You could sort the heaps here + }; // Write the module. The listener will get notified, see OnWriterEvent() below mod.Write(destFileName, opts); @@ -59,56 +59,37 @@ class MyHeap : IHeap { // This is the data. I chose 10 bytes, but any non-zero value can be used byte[] heapData = new byte[10]; - public MyHeap(string name) { - this.name = name; - } + public MyHeap(string name) => this.name = name; // The rest of the code is just for implementing the required interface - public string Name { - get { return name; } - } - - public bool IsEmpty { - get { return false; } - } + public string Name => name; + public bool IsEmpty => false; public void SetReadOnly() { } - public FileOffset FileOffset { - get { return offset; } - } - - public RVA RVA { - get { return rva; } - } + public FileOffset FileOffset => offset; + public RVA RVA => rva; public void SetOffset(FileOffset offset, RVA rva) { this.offset = offset; this.rva = rva; } - public uint GetFileLength() { - return (uint)heapData.Length; - } - - public uint GetVirtualSize() { - return GetFileLength(); - } - - public void WriteTo(BinaryWriter writer) { - writer.Write(heapData); - } + public uint GetFileLength() => (uint)heapData.Length; + public uint GetVirtualSize() => GetFileLength(); + public uint CalculateAlignment() => 0; + public void WriteTo(DataWriter writer) => writer.WriteBytes(heapData); } // Gets notified during module writing - public void OnWriterEvent(ModuleWriterBase writer, ModuleWriterEvent evt) { - switch (evt) { + void OnWriterEvent(object sender, ModuleWriterEventArgs e) { + switch (e.Event) { case ModuleWriterEvent.PESectionsCreated: // Add a PE section var sect1 = new PESection(".dummy", 0x40000040); - writer.Sections.Add(sect1); + e.Writer.AddSection(sect1); // Let's add data sect1.Add(new ByteArrayChunk(new byte[123]), 4); sect1.Add(new ByteArrayChunk(new byte[10]), 4); @@ -117,15 +98,15 @@ public void OnWriterEvent(ModuleWriterBase writer, ModuleWriterEvent evt) { case ModuleWriterEvent.MDEndCreateTables: // All types, methods etc have gotten their new RIDs. Let's print the new values Console.WriteLine("Old -> new type and method tokens"); - foreach (var type in writer.Module.GetTypes()) { + foreach (var type in e.Writer.Module.GetTypes()) { Console.WriteLine("TYPE: {0:X8} -> {1:X8} {2}", type.MDToken.Raw, - new MDToken(Table.TypeDef, writer.MetaData.GetRid(type)).Raw, + new MDToken(Table.TypeDef, e.Writer.Metadata.GetRid(type)).Raw, type.FullName); foreach (var method in type.Methods) Console.WriteLine(" METH: {0:X8} -> {1:X8} {2}", method.MDToken.Raw, - new MDToken(Table.Method, writer.MetaData.GetRid(method)).Raw, + new MDToken(Table.Method, e.Writer.Metadata.GetRid(method)).Raw, method.FullName); } break; diff --git a/Examples/Example7.cs b/Examples/Example7.cs new file mode 100644 index 000000000..32c699885 --- /dev/null +++ b/Examples/Example7.cs @@ -0,0 +1,69 @@ +using System; +using System.IO; +using System.Linq; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace dnlib.Examples { + public class Example7 { + public static void Run() => new Example7().DoIt(); + + void DoIt() { + var test1 = new OpCode( + "test1", 0xf0, 0x00, OperandType.InlineNone, FlowControl.Next, StackBehaviour.Push0, StackBehaviour.Pop0); + var test2 = new OpCode( + "test2", 0xf0, 0x01, OperandType.InlineBrTarget, FlowControl.Branch, StackBehaviour.Push0, StackBehaviour.Pop0); + var test3 = new OpCode( + "test3", 0xf0, 0x02, OperandType.InlineString, FlowControl.Next, StackBehaviour.Push1, StackBehaviour.Pop0); + + var ctx = new ModuleContext(); + + ctx.RegisterExperimentalOpCode(test1); + ctx.RegisterExperimentalOpCode(test2); + ctx.RegisterExperimentalOpCode(test3); + + var mod = ModuleDefMD.Load(typeof(Example7).Module, ctx); + var body = mod.Types.Single(x => x.Name == nameof(Example7)).Methods.Single(x => x.Name == nameof(CustomCil)).Body; + + Console.WriteLine("Original:"); + foreach (var insn in body.Instructions) + Console.WriteLine("{0} (0x{1:X4})", insn, insn.OpCode.Value); + Console.WriteLine(); + + var code = body.Instructions; + + code.Clear(); + + var label = OpCodes.Nop.ToInstruction(); + + code.Add(test1.ToInstruction()); + code.Add(OpCodes.Nop.ToInstruction()); + code.Add(label); + code.Add(OpCodes.Ret.ToInstruction()); + code.Add(test2.ToInstruction(label)); + code.Add(test3.ToInstruction("foo")); + + Console.WriteLine("Modified:"); + foreach (var insn in body.Instructions) + Console.WriteLine("{0} (0x{1:X4})", insn, insn.OpCode.Value); + Console.WriteLine(); + + using (var stream = new MemoryStream()) { + mod.Write(stream); + + stream.Position = 0; + + mod = ModuleDefMD.Load(stream, ctx); + body = mod.Types.Single(x => x.Name == nameof(Example7)).Methods.Single(x => x.Name == nameof(CustomCil)).Body; + + Console.WriteLine("Roundtripped:"); + foreach (var insn in body.Instructions) + Console.WriteLine("{0} (0x{1:X4})", insn, insn.OpCode.Value); + Console.WriteLine(); + } + } + + void CustomCil() { + } + } +} diff --git a/Examples/Examples.csproj b/Examples/Examples.csproj index 1773222b6..3bd7f6613 100644 --- a/Examples/Examples.csproj +++ b/Examples/Examples.csproj @@ -1,59 +1,18 @@ - - + + - Debug - x86 - 8.0.30703 - 2.0 - {F27E72B5-C4BD-40BF-AD19-4C8A99B55872} + net6.0;net45 Exe - Properties - dnlib.Examples - dnlib.Examples - v2.0 - 512 - - - x86 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - x86 - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + false + latest + - - - - - - - - + + - - {FDFC1237-143F-4919-8318-4926901F4639} - dnlib - + - - - \ No newline at end of file + + diff --git a/Examples/Program.cs b/Examples/Program.cs index 434e24627..e22415ad6 100644 --- a/Examples/Program.cs +++ b/Examples/Program.cs @@ -1,4 +1,4 @@ -namespace dnlib.Examples { +namespace dnlib.Examples { class Program { static void Main(string[] args) { // Just uncomment whatever you want to debug @@ -7,7 +7,8 @@ static void Main(string[] args) { // Example3.Run(); // Example4.Run(); // Example5.Run(); - Example6.Run(); +// Example6.Run(); + Example7.Run(); } } } diff --git a/Examples/Properties/AssemblyInfo.cs b/Examples/Properties/AssemblyInfo.cs deleted file mode 100644 index 556b997b6..000000000 --- a/Examples/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("dnlib.Examples")] -[assembly: AssemblyDescription("dnlib examples")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("dnlib.Examples")] -[assembly: AssemblyCopyright("Copyright (C) 2012-2014 de4dot@gmail.com")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/LICENSE.txt b/LICENSE.txt index 1d6e3873d..74858b859 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,16 +1,4 @@ -dnlib: .NET assembly library -https://github.com/0xd4d/dnlib - -Copyright (C) 2012-2015 de4dot@gmail.com - -Contributors ------------- - -Ki, "yck1509 ", https://github.com/yck1509 -kiootic, "kiootic ", https://github.com/kiootic - -MIT LICENSE ------------ +Copyright (C) 2012-2019 de4dot@gmail.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 8f517429f..0cee42416 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,74 @@ -.NET module/assembly reader/writer library written for [de4dot](https://github.com/0xd4d/de4dot/). +# dnlib [![NuGet](https://img.shields.io/nuget/v/dnlib.svg)](https://www.nuget.org/packages/dnlib/) [![](https://github.com/0xd4d/dnlib/workflows/GitHub%20CI/badge.svg)](https://github.com/0xd4d/dnlib/actions) - -dnlib was created because de4dot needed a robust .NET assembly library that -could handle all types of obfuscated assemblies. de4dot used to use Mono.Cecil -but since Mono.Cecil can't handle obfuscated assemblies, doesn't fully support -mixed mode assemblies, doesn't read .NET assemblies the same way the [CLR](http://en.wikipedia.org/wiki/Common_Language_Runtime) does -and many other missing features de4dot needed, dnlib was a necessity. The API -is similar because it made porting de4dot to dnlib a lot easier. - -For another application using dnlib, see [ConfuserEx](https://github.com/yck1509/ConfuserEx/) -(a .NET obfuscator). It uses many of the more advanced features of dnlib. Have -a look at its writer code which gets executed during the assembly writing -process. - -Compiling ---------- - -You must have Visual Studio 2008 or later. The solution file was created by -Visual Studio 2010, so if you use VS2008, open the solution file and change the -version number so VS2008 can read it. - -Examples --------- - -All examples use C#, but since it's a .NET library, you can use any .NET -language (eg. VB.NET). - -See the Examples project for several examples. +.NET module/assembly reader/writer library Opening a .NET assembly/module ------------------------------ -First of all, the important namespaces are `dnlib.DotNet` and -`dnlib.DotNet.Emit`. `dnlib.DotNet.Emit` is only needed if you intend to -read/write method bodies. All the examples below assume you have the -appropriate using statements at the top of each source file: +First of all, the important namespaces are `dnlib.DotNet` and `dnlib.DotNet.Emit`. `dnlib.DotNet.Emit` is only needed if you intend to read/write method bodies. All the examples below assume you have the appropriate using statements at the top of each source file: -```csharp +```C# using dnlib.DotNet; using dnlib.DotNet.Emit; ``` -ModuleDefMD is the class that is created when you open a .NET module. It has -several `Load()` methods that will create a ModuleDefMD instance. If it's not a -.NET module/assembly, a `BadImageFormatException` will be thrown. +ModuleDefMD is the class that is created when you open a .NET module. It has several `Load()` methods that will create a ModuleDefMD instance. If it's not a .NET module/assembly, a `BadImageFormatException` will be thrown. Read a .NET module from a file: -```csharp - ModuleDefMD module = ModuleDefMD.Load(@"C:\path\to\file.exe"); +```C# + // Create a default assembly resolver and type resolver and pass it to Load(). + // If it's a .NET Core assembly, you'll need to disable GAC loading and add + // .NET Core reference assembly search paths. + ModuleContext modCtx = ModuleDef.CreateModuleContext(); + ModuleDefMD module = ModuleDefMD.Load(@"C:\path\to\file.exe", modCtx); ``` Read a .NET module from a byte array: -```csharp +```C# byte[] data = System.IO.File.ReadAllBytes(@"C:\path\of\file.dll"); - ModuleDefMD module = ModuleDefMD.Load(data); + // See comment above about the assembly resolver + ModuleContext modCtx = ModuleDef.CreateModuleContext(); + ModuleDefMD module = ModuleDefMD.Load(data, modCtx); ``` -You can also pass in a Stream instance, an address in memory (HINSTANCE) or -even a System.Reflection.Module instance: +You can also pass in a Stream instance, an address in memory (HINSTANCE) or even a System.Reflection.Module instance: -```csharp +```C# System.Reflection.Module reflectionModule = typeof(void).Module; // Get mscorlib.dll's module - ModuleDefMD module = ModuleDefMD.Load(reflectionModule); + // See comment above about the assembly resolver + ModuleContext modCtx = ModuleDef.CreateModuleContext(); + ModuleDefMD module = ModuleDefMD.Load(reflectionModule, modCtx); ``` To get the assembly, use its Assembly property: -```csharp +```C# AssemblyDef asm = module.Assembly; Console.WriteLine("Assembly: {0}", asm); ``` +If it's an obfuscated Unity/Mono assembly, you need to create a `ModuleCreationOptions` instance and write `CLRRuntimeReaderKind.Mono` to `ModuleCreationOptions.Runtime` and pass in this `ModuleCreationOptions` instance to one of the `ModuleDefMD.Load(...)` methods. + Saving a .NET assembly/module ----------------------------- Use `module.Write()`. It can save the assembly to a file or a Stream. -```csharp +```C# module.Write(@"C:\saved-assembly.dll"); ``` If it's a C++/CLI assembly, you should use `NativeWrite()` -```csharp +```C# module.NativeWrite(@"C:\saved-assembly.dll"); ``` To detect it at runtime, use this code: -```csharp +```C# if (module.IsILOnly) { // This assembly has only IL code, and no native code (eg. it's a C# or VB assembly) module.Write(@"C:\saved-assembly.dll"); @@ -104,28 +82,14 @@ To detect it at runtime, use this code: PDB files --------- -Right after opening the module, call one of its `LoadPdb()` methods. You can -also pass in a `ModuleCreationOptions` to `ModuleDefMD.Load()` and if one of -the PDB options is enabled, the PDB file will be opened before `Load()` -returns. +PDB files are read from disk by default. You can change this behaviour by creating a `ModuleCreationOptions` and passing it in to the code that creates a module. -```csharp - var mod = ModuleDefMD.Load(@"C:\myfile.dll"); - mod.LoadPdb(); // Will load C:\myfile.pdb if it exists -``` +To save a PDB file, create a `ModuleWriterOptions` / `NativeModuleWriterOptions` and set its `WritePdb` property to `true`. By default, it will create a PDB file with the same name as the output assembly but with a `.pdb` extension. You can override this by writing the PDB file name to `PdbFileName` or writing your own stream to `PdbStream`. If `PdbStream` is initialized, `PdbFileName` should also be initialized because the name of the PDB file will be written to the PE file. -To save a PDB file, create a `ModuleWriterOptions` / -`NativeModuleWriterOptions` and set its `WritePdb` property to `true`. By -default, it will create a PDB file with the same name as the output assembly -but with a `.pdb` extension. You can override this by writing the PDB file -name to `PdbFileName` or writing your own stream to `PdbStream`. If -`PdbStream` is initialized, `PdbFileName` should also be initialized because -the name of the PDB file will be written to the PE file. Another more -advanced property is `CreatePdbSymbolWriter` which returns a `ISymbolWriter2` -instance that dnlib will use. - -```csharp - var mod = ModuleDefMD.Load(@"C:\myfile.dll"); +```C# + // Create a default assembly resolver and type resolver + ModuleContext modCtx = ModuleDef.CreateModuleContext(); + var mod = ModuleDefMD.Load(@"C:\myfile.dll", modCtx); // ... var wopts = new dnlib.DotNet.Writer.ModuleWriterOptions(mod); wopts.WritePdb = true; @@ -133,17 +97,25 @@ instance that dnlib will use. mod.Write(@"C:\out.dll", wopts); ``` -There exist two different types of PDB readers, one is using the Microsoft -COM PDB API available in diasymreader.dll (for Windows only), and the other -one, which is now the default implementation, is a managed PDB reader. The PDB -writer currently only uses the COM PDB API so will only work on Windows. +dnlib supports Windows PDBs, portable PDBs and embedded portable PDBs. -Strong name sign an assembly ----------------------------- +Windows PDBs +------------ + +It's only possible to write Windows PDBs on Windows (portable PDBs can be written on any OS). dnlib has a managed Windows PDB reader that supports all OSes. + +There are two *native* Windows PDB reader and writer implementations, the old `diasymreader.dll` that ships with .NET Framework and `Microsoft.DiaSymReader.Native` which has been updated with more features and bug fixes. + +dnlib will use `Microsoft.DiaSymReader.Native` if it exists and fall back to `diasymreader.dll` if needed. `PdbReaderOptions` and `PdbWriterOptions` can be used to disable one of them. + +`Microsoft.DiaSymReader.Native` is a NuGet package with 32-bit and 64-bit native DLLs. You have to add a reference to this NuGet package if you want dnlib to use it. dnlib doesn't add a reference to it. + +Strong name signing an assembly +------------------------------- Use the following code to strong name sign the assembly when saving it: -```csharp +```C# using dnlib.DotNet.Writer; ... // Open or create an assembly @@ -165,12 +137,11 @@ Use the following code to strong name sign the assembly when saving it: Enhanced strong name signing an assembly ---------------------------------------- -See this [MSDN article](http://msdn.microsoft.com/en-us/library/hh415055.aspx) -for info on enhanced strong naming. +See this [MSDN article](http://msdn.microsoft.com/en-us/library/hh415055.aspx) for info on enhanced strong naming. Enhanced strong name signing without key migration: -```csharp +```C# using dnlib.DotNet.Writer; ... // Open or create an assembly @@ -192,7 +163,7 @@ Enhanced strong name signing without key migration: Enhanced strong name signing with key migration: -```csharp +```C# using dnlib.DotNet.Writer; ... // Open or create an assembly @@ -215,59 +186,86 @@ Enhanced strong name signing with key migration: mod.Write(@"C:\out\file.dll", opts); ``` +Exporting managed methods (DllExport) +------------------------------------- + +dnlib supports exporting managed methods so the managed DLL file can be loaded by native code and then executed. .NET Framework supports this feature, but there's no guarantee that other CLRs (eg. .NET Core or Mono/Unity) support this feature. +In case of .NET Core please be aware that `ijwhost.dll` has to be loaded prior to calling your exported method and that ijwhost currently (as of .NET Core 3.0) does not work if the calling app is self-contained. + +The `MethodDef` class has an `ExportInfo` property. If it gets initialized, the method gets exported when saving the module. At most 65536 (2^16) methods can be exported. This is a PE file limitation, not a dnlib limitation. + +Exported methods should not be generic. + +The method's calling convention should be changed to eg. stdcall, or cdecl, by adding an optional modifier to `MethodDef.MethodSig.RetType`. It must be a `System.Runtime.CompilerServices.CallConvCdecl`, `System.Runtime.CompilerServices.CallConvStdcall`, `System.Runtime.CompilerServices.CallConvThiscall`, or a `System.Runtime.CompilerServices.CallConvFastcall`, eg.: + +```C# +var type = method.MethodSig.RetType; +type = new CModOptSig(module.CorLibTypes.GetTypeRef("System.Runtime.CompilerServices", "CallConvCdecl"), type); +method.MethodSig.RetType = type; +``` + +Requirements: + +- The assembly platform must be x86, x64, IA-64 or ARM (ARM64 isn't supported at the moment). AnyCPU assemblies are not supported. This is as simple as changing (if needed) `ModuleWriterOptions.PEHeadersOptions.Machine` when saving the file. x86 files should set `32-bit required` flag and clear `32-bit preferred` flag in the COR20 header. +- `ModuleWriterOptions.Cor20HeaderOptions.Flags`: The `IL Only` bit must be cleared. +- It must be a DLL file (see `ModuleWriterOptions.PEHeadersOptions.Characteristics`). The file will fail to load at runtime if it's an EXE file. + +NOTE: VS' debugger crashes if there's a `DebuggableAttribute` attribute and if the first ctor arg is 0x107. The workaround is to clear the `EnableEditAndContinue` bit: + +```C# +var ca = module.Assembly.CustomAttributes.Find("System.Diagnostics.DebuggableAttribute"); +if (ca is not null && ca.ConstructorArguments.Count == 1) { + var arg = ca.ConstructorArguments[0]; + // VS' debugger crashes if value == 0x107, so clear EnC bit + if (arg.Type.FullName == "System.Diagnostics.DebuggableAttribute/DebuggingModes" && arg.Value is int value && value == 0x107) { + arg.Value = value & ~(int)DebuggableAttribute.DebuggingModes.EnableEditAndContinue; + ca.ConstructorArguments[0] = arg; + } +} +``` + +See the following issues: [#271](https://github.com/0xd4d/dnlib/issues/271), [#172](https://github.com/0xd4d/dnlib/issues/172) + Type classes ------------ -The metadata has three type tables: `TypeRef`, `TypeDef`, and `TypeSpec`. The -classes dnlib use are called the same. These three classes all implement -`ITypeDefOrRef`. +The metadata has three type tables: `TypeRef`, `TypeDef`, and `TypeSpec`. The classes dnlib use are called the same. These three classes all implement `ITypeDefOrRef`. -There's also type signature classes. The base class is `TypeSig`. You'll find -`TypeSig`s in method signatures (return type and parameter types) and locals. -The `TypeSpec` class also has a `TypeSig` property. +There's also type signature classes. The base class is `TypeSig`. You'll find `TypeSig`s in method signatures (return type and parameter types) and locals. The `TypeSpec` class also has a `TypeSig` property. All of these types implement `IType`. `TypeRef` is a reference to a type in (usually) another assembly. -`TypeDef` is a type definition and it's a type defined in some module. This -class does *not* derive from `TypeRef`. :) +`TypeDef` is a type definition and it's a type defined in some module. This class does *not* derive from `TypeRef`. :) `TypeSpec` can be a generic type, an array type, etc. -`TypeSig` is the base class of all type signatures (found in method sigs and -locals). It has a `Next` property that points to the next `TypeSig`. Eg. a -Byte[] would first contain a `SZArraySig`, and its `Next` property would point -to Byte signature. +`TypeSig` is the base class of all type signatures (found in method sigs and locals). It has a `Next` property that points to the next `TypeSig`. Eg. a Byte[] would first contain a `SZArraySig`, and its `Next` property would point to Byte signature. -`CorLibTypeSig` is a simple corlib type. You don't create these directly. Use -eg. `module.CorLibTypes.Int32` to get a System.Int32 type signature. +`CorLibTypeSig` is a simple corlib type. You don't create these directly. Use eg. `module.CorLibTypes.Int32` to get a System.Int32 type signature. `ValueTypeSig` is used when the next class is a value type. `ClassSig` is used when the next class is a reference type. -`GenericInstSig` is a generic instance type. It has a reference to the generic -type (a `TypeDef` or a `TypeRef`) and the generic arguments. +`GenericInstSig` is a generic instance type. It has a reference to the generic type (a `TypeDef` or a `TypeRef`) and the generic arguments. `PtrSig` is a pointer sig. `ByRefSig` is a by reference type. -`ArraySig` is a multi-dimensional array type. Most likely when you create an -array, you should use `SZArraySig`, and *not* `ArraySig`. +`ArraySig` is a multi-dimensional array type. Most likely when you create an array, you should use `SZArraySig`, and *not* `ArraySig`. -`SZArraySig` is a single dimension, zero lower bound array. In C#, a `byte[]` -is a `SZArraySig`, and *not* an `ArraySig`. +`SZArraySig` is a single dimension, zero lower bound array. In C#, a `byte[]` is a `SZArraySig`, and *not* an `ArraySig`. `GenericVar` is a generic type variable. `GenericMVar` is a generic method variable. -Some examples if you're not used to the way type signatures are represented -in metadata: +Some examples if you're not used to the way type signatures are represented in metadata: -```csharp +```C# ModuleDef mod = ....; // Create a byte[] @@ -288,10 +286,9 @@ in metadata: SZArraySig array5 = new SZArraySig(new ClassSig(stream)); ``` -Sometimes you must convert an `ITypeDefOrRef` (`TypeRef`, `TypeDef`, or -`TypeSpec`) to/from a `TypeSig`. There's extension methods you can use: +Sometimes you must convert an `ITypeDefOrRef` (`TypeRef`, `TypeDef`, or `TypeSpec`) to/from a `TypeSig`. There's extension methods you can use: -```csharp +```C# // array5 is defined above ITypeDefOrRef type1 = array5.ToTypeDefOrRef(); TypeSig type2 = type1.ToTypeSig(); @@ -300,22 +297,9 @@ Sometimes you must convert an `ITypeDefOrRef` (`TypeRef`, `TypeDef`, or Naming conventions of metadata table classes -------------------------------------------- -For most tables in the metadata, there's a corresponding dnlib class with the -exact same or a similar name. Eg. the metadata has a `TypeDef` table, and dnlib -has a `TypeDef` class. Some tables don't have a class because they're -referenced by other classes, and that information is part of some other class. -Eg. the `TypeDef` class contains all its properties and events, even though the -`TypeDef` table has no property or event column. - -For each of these table classes, there's an abstract base class, and two sub -classes. These sub classes are named the same as the base class but ends in -either `MD` (for classes created from the metadata) or `User` (for classes -created by the user). Eg. `TypeDef` is the base class, and it has two sub -classes `TypeDefMD` which is auto-created from metadata, and `TypeRefUser` -which is created by the user when adding user types. Most of the XyzMD classes -are internal and can't be referenced directly by the user. They're created by -`ModuleDefMD` (which is the only public `MD` class). All XyzUser classes are -public. +For most tables in the metadata, there's a corresponding dnlib class with the exact same or a similar name. Eg. the metadata has a `TypeDef` table, and dnlib has a `TypeDef` class. Some tables don't have a class because they're referenced by other classes, and that information is part of some other class. Eg. the `TypeDef` class contains all its properties and events, even though the `TypeDef` table has no property or event column. + +For each of these table classes, there's an abstract base class, and two sub classes. These sub classes are named the same as the base class but ends in either `MD` (for classes created from the metadata) or `User` (for classes created by the user). Eg. `TypeDef` is the base class, and it has two sub classes `TypeDefMD` which is auto-created from metadata, and `TypeRefUser` which is created by the user when adding user types. Most of the XyzMD classes are internal and can't be referenced directly by the user. They're created by `ModuleDefMD` (which is the only public `MD` class). All XyzUser classes are public. Metadata table classes ---------------------- @@ -332,23 +316,19 @@ Here's a list of the most common metadata table classes `GenericParam` is a generic parameter (owned by a `MethodDef` or a `TypeDef`) -`MemberRef` is what you create if you need a field reference or a method -reference. +`MemberRef` is what you create if you need a field reference or a method reference. -`MethodDef` is a method definition. It usually has a `CilBody` with CIL -instructions. Owned by a `TypeDef`. +`MethodDef` is a method definition. It usually has a `CilBody` with CIL instructions. Owned by a `TypeDef`. `MethodSpec` is a instantiated generic method. -`ModuleDef` is the base module class. When you read an existing module, a -`ModuleDefMD` is created. +`ModuleDef` is the base module class. When you read an existing module, a `ModuleDefMD` is created. `ModuleRef` is a module reference. `PropertyDef` is a property definition. Owned by a `TypeDef`. -`TypeDef` is a type definition. It contains a lot of interesting stuff, -including methods, fields, properties, etc. +`TypeDef` is a type definition. It contains a lot of interesting stuff, including methods, fields, properties, etc. `TypeRef` is a type reference. Usually to a type in another assembly. @@ -357,31 +337,23 @@ including methods, fields, properties, etc. Method classes -------------- -The following are the method classes: `MethodDef`, `MemberRef` (method ref) and -`MethodSpec`. They all implement `IMethod`. +The following are the method classes: `MethodDef`, `MemberRef` (method ref) and `MethodSpec`. They all implement `IMethod`. Field classes ------------- -The following are the field classes: `FieldDef` and `MemberRef` (field ref). -They both implement `IField`. +The following are the field classes: `FieldDef` and `MemberRef` (field ref). They both implement `IField`. Comparing types, methods, fields, etc ------------------------------------- -dnlib has a `SigComparer` class that can compare any type with any other type. -Any method with any other method, etc. It also has several pre-created -`IEqualityComparer` classes (eg. `TypeEqualityComparer`, -`FieldEqualityComparer`, etc) which you can use if you intend to eg. use a type -as a key in a `Dictionary`. +dnlib has a `SigComparer` class that can compare any type with any other type. Any method with any other method, etc. It also has several pre-created `IEqualityComparer` classes (eg. `TypeEqualityComparer`, `FieldEqualityComparer`, etc) which you can use if you intend to eg. use a type as a key in a `Dictionary`. -The `SigComparer` class can also compare types with `System.Type`, methods with -`System.Reflection.MethodBase`, etc. +The `SigComparer` class can also compare types with `System.Type`, methods with `System.Reflection.MethodBase`, etc. -It has many options you can set, see `SigComparerOptions`. The default options -is usually good enough, though. +It has many options you can set, see `SigComparerOptions`. The default options is usually good enough, though. -```csharp +```C# // Compare two types TypeRef type1 = ...; TypeDef type2 = ...; @@ -404,12 +376,9 @@ It has many `Equals()` and `GetHashCode()` overloads. .NET Resources -------------- -There's three types of .NET resource, and they all derive from the common base -class `Resource`. `ModuleDef.Resources` is a list of all resources the module -owns. +There's three types of .NET resource, and they all derive from the common base class `Resource`. `ModuleDef.Resources` is a list of all resources the module owns. -`EmbeddedResource` is a resource that has data embedded in the owner module. -This is the most common type of resource and it's probably what you want. +`EmbeddedResource` is a resource that has data embedded in the owner module. This is the most common type of resource and it's probably what you want. `AssemblyLinkedResource` is a reference to a resource in another assembly. @@ -418,74 +387,62 @@ This is the most common type of resource and it's probably what you want. Win32 resources --------------- -`ModuleDef.Win32Resources` can be null or a `Win32Resources` instance. You can -add/remove any Win32 resource blob. dnlib doesn't try to parse these blobs. +`ModuleDef.Win32Resources` can be null or a `Win32Resources` instance. You can add/remove any Win32 resource blob. dnlib doesn't try to parse these blobs. Parsing method bodies --------------------- -This is usually only needed if you have decrypted a method body. If it's a -standard method body, you can use `MethodBodyReader.Create()`. If it's similar -to a standard method body, you can derive a class from `MethodBodyReaderBase` -and override the necessary methods. +This is usually only needed if you have decrypted a method body. If it's a standard method body, you can use `MethodBodyReader.Create()`. If it's similar to a standard method body, you can derive a class from `MethodBodyReaderBase` and override the necessary methods. Resolving references -------------------- -`TypeRef.Resolve()` and `MemberRef.Resolve()` both use -`module.Context.Resolver` to resolve the type, field or method. The custom -attribute parser code may also resolve type references. +`TypeRef.Resolve()` and `MemberRef.Resolve()` both use `module.Context.Resolver` to resolve the type, field or method. The custom attribute parser code may also resolve type references. -If you call Resolve() or read custom attributes, you should initialize -module.Context to a `ModuleContext`. It should normally be shared between all -modules you open. +If you call `Resolve()` or read custom attributes, you should initialize module.Context to a `ModuleContext`. It should normally be shared between all modules you open. + +```C# + // You should pass this context to ModuleDefMD.Load(), but you can also write + // it to `module.Context` + ModuleContext modCtx = ModuleDef.CreateModuleContext(); + // It creates the default assembly resolver + AssemblyResolver asmResolver = (AssemblyResolver)modCtx.AssemblyResolver; -```csharp - AssemblyResolver asmResolver = new AssemblyResolver(); - ModuleContext modCtx = new ModuleContext(asmResolver); - - // All resolved assemblies will also get this same modCtx - asmResolver.DefaultModuleContext = modCtx; - // Enable the TypeDef cache for all assemblies that are loaded // by the assembly resolver. Only enable it if all auto-loaded // assemblies are read-only. asmResolver.EnableTypeDefCache = true; ``` -All assemblies that you yourself open should be added to the assembly resolver -cache. +All assemblies that you yourself open should be added to the assembly resolver cache. -```csharp +```C# ModuleDefMD mod = ModuleDefMD.Load(...); mod.Context = modCtx; // Use the previously created (and shared) context - mod.Context.AssemblyResolver.AddToCache(mod); + // This code assumes you're using the default assembly resolver + ((AssemblyResolver)mod.Context.AssemblyResolver).AddToCache(mod); ``` Resolving types, methods, etc from metadata tokens -------------------------------------------------- -`ModuleDefMD` has several `ResolveXXX()` methods, eg. `ResolveTypeDef()`, -`ResolveMethod()`, etc. +`ModuleDefMD` has several `ResolveXXX()` methods, eg. `ResolveTypeDef()`, `ResolveMethod()`, etc. Creating mscorlib type references --------------------------------- -Every module has a `CorLibTypes` property. It has references to a few of the -simplest types such as all integer types, floating point types, Object, String, -etc. If you need a type that's not there, you must create it yourself, eg.: +Every module has a `CorLibTypes` property. It has references to a few of the simplest types such as all integer types, floating point types, Object, String, etc. If you need a type that's not there, you must create it yourself, eg.: -```csharp +```C# TypeRef consoleRef = new TypeRefUser(mod, "System", "Console", mod.CorLibTypes.AssemblyRef); ``` Importing runtime types, methods, fields ---------------------------------------- -To import a `System.Type`, `System.Reflection.MethodInfo`, -`System.Reflection.FieldInfo`, etc into a module, use the `Importer` class. +To import a `System.Type`, `System.Reflection.MethodInfo`, `System.Reflection.FieldInfo`, etc into a module, use the `Importer` class. -```csharp +```C# Importer importer = new Importer(mod); ITypeDefOrRef consoleRef = importer.Import(typeof(System.Console)); IMethod writeLine = importer.Import(typeof(System.Console).GetMethod("WriteLine")); @@ -493,44 +450,40 @@ To import a `System.Type`, `System.Reflection.MethodInfo`, You can also use it to import types, methods etc from another `ModuleDef`. -All imported types, methods etc will be references to the original assembly. -I.e., it won't add the imported `TypeDef` to the target module. It will just -create a `TypeRef` to it. +All imported types, methods etc will be references to the original assembly. I.e., it won't add the imported `TypeDef` to the target module. It will just create a `TypeRef` to it. Using decrypted methods ----------------------- -If `ModuleDefMD.MethodDecrypter` is initialized, `ModuleDefMD` will call it and -check whether the method has been decrypted. If it has, it calls -`IMethodDecrypter.GetMethodBody()` which you should implement. Return the new -`MethodBody`. `GetMethodBody()` should usually call `MethodBodyReader.Create()` -which does the actual parsing of the CIL code. +If `ModuleDefMD.MethodDecrypter` is initialized, `ModuleDefMD` will call it and check whether the method has been decrypted. If it has, it calls `IMethodDecrypter.GetMethodBody()` which you should implement. Return the new `MethodBody`. `GetMethodBody()` should usually call `MethodBodyReader.Create()` which does the actual parsing of the CIL code. -It's also possible to override `ModuleDefMD.ReadUserString()`. This method is -called by the CIL parser when it finds a `Ldstr` instruction. If -`ModuleDefMD.StringDecrypter` is not null, its `ReadUserString()` method is -called with the string token. Return the decrypted string or null if it should -be read from the `#US` heap. +It's also possible to override `ModuleDefMD.ReadUserString()`. This method is called by the CIL parser when it finds a `Ldstr` instruction. If `ModuleDefMD.StringDecrypter` is not null, its `ReadUserString()` method is called with the string token. Return the decrypted string or null if it should be read from the `#US` heap. Low level access to the metadata -------------------------------- The low level classes are in the `dnlib.DotNet.MD` namespace. -Open an existing .NET module/assembly and you get a ModuleDefMD. It has several -properties, eg. `StringsStream` is the #Strings stream. +Open an existing .NET module/assembly and you get a ModuleDefMD. It has several properties, eg. `StringsStream` is the #Strings stream. -The `MetaData` property gives you full access to the metadata. +The `Metadata` property gives you full access to the metadata. To get a list of all valid TypeDef rids (row IDs), use this code: -```csharp +```C# using dnlib.DotNet.MD; // ... ModuleDefMD mod = ModuleDefMD.Load(...); - RidList typeDefRids = mod.MetaData.GetTypeDefRidList(); + RidList typeDefRids = mod.Metadata.GetTypeDefRidList(); for (int i = 0; i < typeDefRids.Count; i++) Console.WriteLine("rid: {0}", typeDefRids[i]); ``` -You don't need to create a `ModuleDefMD`, though. See `DotNetFile`. +You don't need to create a `ModuleDefMD`, though. See `MetadataFactory`. + +Credits +------- + +Big thanks to [Ki](https://github.com/yck1509) for writing the managed Windows PDB reader! + +[List of all contributors](https://github.com/0xd4d/dnlib/graphs/contributors) diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 000000000..11e20bc0b --- /dev/null +++ b/build.ps1 @@ -0,0 +1,36 @@ +param([switch]$NoMsbuild) + +$ErrorActionPreference = 'Stop' + +$configuration = 'Release' + +# +# dotnet build isn't used because it can't build net35 tfms +# + +$env:NoTargetFrameworkNet35 = '' + +$useMsbuild = $IsWindows -or $IsWindows -eq $null +if ($NoMsbuild) { + $useMsbuild = $false +} + +if (!$useMsbuild) { + # There are currently no net35 reference assemblies on nuget + $env:NoTargetFrameworkNet35 = 'true' +} + +if ($useMsbuild) { + msbuild -v:m -restore -t:Build -p:Configuration=$configuration + if ($LASTEXITCODE) { exit $LASTEXITCODE } + msbuild -v:m -t:Pack -p:Configuration=$configuration src/dnlib.csproj + if ($LASTEXITCODE) { exit $LASTEXITCODE } +} +else { + dotnet build -v:m -c $configuration + if ($LASTEXITCODE) { exit $LASTEXITCODE } + dotnet pack -v:m -c $configuration src/dnlib.csproj + if ($LASTEXITCODE) { exit $LASTEXITCODE } +} + +$env:NoTargetFrameworkNet35 = '' diff --git a/dnlib.sln b/dnlib.sln index e8dc7640b..cc58351cd 100644 --- a/dnlib.sln +++ b/dnlib.sln @@ -1,42 +1,31 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dnlib", "src\dnlib.csproj", "{FDFC1237-143F-4919-8318-4926901F4639}" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29318.209 +MinimumVisualStudioVersion = 16.0.29318.209 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dnlib", "src\dnlib.csproj", "{FDFC1237-143F-4919-8318-4926901F4639}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Examples", "Examples\Examples.csproj", "{F27E72B5-C4BD-40BF-AD19-4C8A99B55872}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples", "Examples\Examples.csproj", "{F27E72B5-C4BD-40BF-AD19-4C8A99B55872}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {FDFC1237-143F-4919-8318-4926901F4639}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FDFC1237-143F-4919-8318-4926901F4639}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FDFC1237-143F-4919-8318-4926901F4639}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {FDFC1237-143F-4919-8318-4926901F4639}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {FDFC1237-143F-4919-8318-4926901F4639}.Debug|x86.ActiveCfg = Debug|Any CPU {FDFC1237-143F-4919-8318-4926901F4639}.Release|Any CPU.ActiveCfg = Release|Any CPU {FDFC1237-143F-4919-8318-4926901F4639}.Release|Any CPU.Build.0 = Release|Any CPU - {FDFC1237-143F-4919-8318-4926901F4639}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {FDFC1237-143F-4919-8318-4926901F4639}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {FDFC1237-143F-4919-8318-4926901F4639}.Release|x86.ActiveCfg = Release|Any CPU - {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Debug|Any CPU.ActiveCfg = Debug|x86 - {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Debug|x86.ActiveCfg = Debug|x86 - {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Debug|x86.Build.0 = Debug|x86 - {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Release|Any CPU.ActiveCfg = Release|x86 - {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Release|Mixed Platforms.Build.0 = Release|x86 - {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Release|x86.ActiveCfg = Release|x86 - {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Release|x86.Build.0 = Release|x86 + {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F27E72B5-C4BD-40BF-AD19-4C8A99B55872}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {49902A67-E85B-44E8-9C4F-26F8854AA894} + EndGlobalSection EndGlobal diff --git a/src/DefaultDllImportSearchPathsAttribute.cs b/src/DefaultDllImportSearchPathsAttribute.cs new file mode 100644 index 000000000..ad4b10d2a --- /dev/null +++ b/src/DefaultDllImportSearchPathsAttribute.cs @@ -0,0 +1,23 @@ +// dnlib: See LICENSE.txt for more info + +#if NET35 +namespace System.Runtime.InteropServices { + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Method, AllowMultiple = false)] + sealed class DefaultDllImportSearchPathsAttribute : Attribute { + public DefaultDllImportSearchPathsAttribute(DllImportSearchPath paths) => _paths = paths; + public DllImportSearchPath Paths => _paths; + internal DllImportSearchPath _paths; + } + + [Flags] + enum DllImportSearchPath { + LegacyBehavior = 0, + AssemblyDirectory = 2, + UseDllDirectoryForDependencies = 0x100, + ApplicationDirectory = 0x200, + UserDirectories = 0x400, + System32 = 0x800, + SafeDirectories = 0x1000, + } +} +#endif diff --git a/src/DotNet/AccessChecker.cs b/src/DotNet/AccessChecker.cs deleted file mode 100644 index 3f3758b71..000000000 --- a/src/DotNet/AccessChecker.cs +++ /dev/null @@ -1,610 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Collections.Generic; - -namespace dnlib.DotNet { - /// - /// Checks whether a type has access to some other target type, method or field - /// according to the target's visibility. - /// - public struct AccessChecker { - TypeDef userType; - List userTypeEnclosingTypes; - bool enclosingTypesInitialized; - Dictionary baseTypes; - bool baseTypesInitialized; - - [Flags] - enum CheckTypeAccess { - /// - /// Can't access the type - /// - None = 0, - - /// - /// Normal access to the type and its members. Type + member must be public, internal - /// or protected (for sub classes) to access the member. - /// - Normal = 1, - - /// - /// Full access to the type, even if the type is private. If clear, the type - /// must be public, internal or protected (for sub classes). - /// - FullTypeAccess = 2, - - /// - /// Full access to the type's members (types, fields, methods), even if the - /// members are private. If clear, the members must be public, internal - /// or protected (for sub classes) - /// - FullMemberAccess = 4, - - /// - /// Full access to the type and its members - /// - Full = Normal | FullTypeAccess | FullMemberAccess, - } - - /// - /// Gets/sets the user type which is accessing the target type, field or method - /// - public TypeDef UserType { - get { return userType; } - set { - if (userType == value) - return; - userType = value; - enclosingTypesInitialized = false; - baseTypesInitialized = false; - if (userTypeEnclosingTypes != null) - userTypeEnclosingTypes.Clear(); - if (baseTypes != null) - baseTypes.Clear(); - } - } - - /// - /// Constructor - /// - /// The type accessing the target type, field or method - public AccessChecker(TypeDef userType) { - this.userType = userType; - this.userTypeEnclosingTypes = null; - this.baseTypes = null; - this.enclosingTypesInitialized = false; - this.baseTypesInitialized = false; - } - - /// - /// Checks whether it can access a method or a field - /// - /// Operand - /// true if it has access to it, false if not, and null - /// if we can't determine it (eg. we couldn't resolve a type or input was null) - public bool? CanAccess(object op) { - var md = op as MethodDef; - if (md != null) - return CanAccess(md); - - var mr = op as MemberRef; - if (mr != null) - return CanAccess(mr); - - var fd = op as FieldDef; - if (fd != null) - return CanAccess(fd); - - var ms = op as MethodSpec; - if (ms != null) - return CanAccess(ms); - - var tr = op as TypeRef; - if (tr != null) - return CanAccess(tr.Resolve()); - - var td = op as TypeDef; - if (td != null) - return CanAccess(td); - - var ts = op as TypeSpec; - if (ts != null) - return CanAccess(ts); - - return null; - } - - /// - /// Checks whether it can access a - /// - /// The type - /// true if it has access to it, false if not, and null - /// if we can't determine it (eg. we couldn't resolve a type or input was null) - public bool? CanAccess(TypeRef tr) { - if (tr == null) - return null; - return CanAccess(tr.Resolve()); - } - - /// - /// Checks whether it can access a - /// - /// The type - /// true if it has access to it, false if not, and null - /// if we can't determine it (eg. we couldn't resolve a type or input was null) - public bool? CanAccess(TypeDef td) { - var access = GetTypeAccess(td, null); - if (access == null) - return null; - return (access.Value & CheckTypeAccess.Normal) != 0; - } - - /// - /// Returns the access we have to . If is - /// enclosing this type, we have private access to it and all its members. If its - /// declaring type encloses us, we have private access to it, but only normal access - /// to its members. Else, we only have normal access to it and its members. If we inherit - /// it, we have protected access to it and its members. - /// - /// The type - /// Generic instance of or null if none - CheckTypeAccess? GetTypeAccess(TypeDef td, GenericInstSig git) { - if (td == null) - return null; - if (userType == td) - return CheckTypeAccess.Full; - - // If this is our nested type, we have private access to it itself, but normal - // access to its members. - if (td.DeclaringType == userType) - return CheckTypeAccess.Normal | CheckTypeAccess.FullTypeAccess; - - // If we're not a nested type, td can't be our enclosing type - if (userType.DeclaringType == null) - return GetTypeAccess2(td, git); - - // Can't be an enclosing type if they're not in the same module - if (userType.Module != td.Module) - return GetTypeAccess2(td, git); - - var tdEncTypes = GetEnclosingTypes(td, true); - var ourEncTypes = InitializeOurEnclosingTypes(); - int maxChecks = Math.Min(tdEncTypes.Count, ourEncTypes.Count); - int commonIndex; - for (commonIndex = 0; commonIndex < maxChecks; commonIndex++) { - if (tdEncTypes[commonIndex] != ourEncTypes[commonIndex]) - break; - } - - // If td encloses us, then we have access to td and all its members even if - // they're private. - if (commonIndex == tdEncTypes.Count) - return CheckTypeAccess.Full; - - // If there are no common enclosing types, only check the visibility. - if (commonIndex == 0) - return GetTypeAccess2(td, git); - - // If td's declaring type encloses this, then we have full access to td even if - // it's private, but only normal access to its members. - if (commonIndex + 1 == tdEncTypes.Count) - return CheckTypeAccess.Normal | CheckTypeAccess.FullTypeAccess; - - // Normal visibility checks starting from type after common enclosing type. - // Note that we have full access to it so we don't need to check its access, - // so start from the next one. - for (int i = commonIndex + 1; i < tdEncTypes.Count; i++) { - if (!IsVisible(tdEncTypes[i], null)) - return CheckTypeAccess.None; - } - return CheckTypeAccess.Normal; - } - - CheckTypeAccess GetTypeAccess2(TypeDef td, GenericInstSig git) { - while (td != null) { - var declType = td.DeclaringType; - if (userType != declType && !IsVisible(td, git)) - return CheckTypeAccess.None; - td = declType; - git = null; - } - return CheckTypeAccess.Normal; - } - - /// - /// Checks whether is visible to us without checking whether they - /// have any common enclosing types. - /// - /// Type - /// Generic instance of or null if none - bool IsVisible(TypeDef td, GenericInstSig git) { - if (td == null) - return false; - if (td == userType) - return true; - - switch (td.Visibility) { - case TypeAttributes.NotPublic: - return IsSameAssemblyOrFriendAssembly(td.Module); - - case TypeAttributes.Public: - return true; - - case TypeAttributes.NestedPublic: - return true; - - case TypeAttributes.NestedPrivate: - return false; - - case TypeAttributes.NestedFamily: - return CheckFamily(td, git); - - case TypeAttributes.NestedAssembly: - return IsSameAssemblyOrFriendAssembly(td.Module); - - case TypeAttributes.NestedFamANDAssem: - return IsSameAssemblyOrFriendAssembly(td.Module) && - CheckFamily(td, git); - - case TypeAttributes.NestedFamORAssem: - return IsSameAssemblyOrFriendAssembly(td.Module) || - CheckFamily(td, git); - - default: - return false; - } - } - - bool IsSameAssemblyOrFriendAssembly(ModuleDef module) { - if (module == null) - return false; - var userModule = userType.Module; - if (userModule == null) - return false; - if (userModule == module) - return true; - var userAsm = userModule.Assembly; - var modAsm = module.Assembly; - if (IsSameAssembly(userAsm, modAsm)) - return true; - if (userAsm != null && userAsm.IsFriendAssemblyOf(modAsm)) - return true; - - return false; - } - - static bool IsSameAssembly(IAssembly asm1, IAssembly asm2) { - if (asm1 == null || asm2 == null) - return false; - if (asm1 == asm2) - return true; - return new AssemblyNameComparer(AssemblyNameComparerFlags.All).Equals(asm1, asm2); - } - - /// - /// Checks whether has access to . - /// is Family, FamANDAssem, or FamORAssem. - /// - /// Type - /// Generic instance of or null if none - bool CheckFamily(TypeDef td, GenericInstSig git) { - if (td == null) - return false; - InitializeBaseTypes(); - - if (baseTypes.ContainsKey(git == null ? (IType)td : git)) - return true; - - // td is Family, FamANDAssem, or FamORAssem. If we derive from its enclosing type, - // we have access to it. - var td2 = td.DeclaringType; - if (td2 != null && baseTypes.ContainsKey(td2)) - return true; - - // If one of our enclosing types derive from it, we also have access to it - var userDeclType = userType.DeclaringType; - if (userDeclType != null) - return new AccessChecker(userDeclType).CheckFamily(td, git); - - return false; - } - - void InitializeBaseTypes() { - if (baseTypesInitialized) - return; - if (baseTypes == null) - baseTypes = new Dictionary(TypeEqualityComparer.Instance); - baseTypesInitialized = true; - - ITypeDefOrRef baseType = userType; - while (baseType != null) { - baseTypes[baseType] = true; - baseType = baseType.GetBaseType(); - } - } - - List InitializeOurEnclosingTypes() { - if (!enclosingTypesInitialized) { - userTypeEnclosingTypes = GetEnclosingTypes(userType, true); - enclosingTypesInitialized = true; - } - return userTypeEnclosingTypes; - } - - /// - /// Returns a list of all enclosing types, in order of non-enclosed to most enclosed type - /// - /// Type - /// true if should be included - /// A list of all enclosing types - static List GetEnclosingTypes(TypeDef td, bool includeInput) { - var list = new List(); - if (includeInput && td != null) - list.Add(td); - while (td != null) { - var dt = td.DeclaringType; - if (dt == null) - break; - if (list.Contains(dt)) - break; - list.Add(dt); - td = dt; - } - list.Reverse(); - return list; - } - - /// - /// Checks whether it can access a - /// - /// The field - /// true if it has access to it, false if not, and null - /// if we can't determine it (eg. we couldn't resolve a type or input was null) - public bool? CanAccess(FieldDef fd) { - return CanAccess(fd, null); - } - - bool? CanAccess(FieldDef fd, GenericInstSig git) { - if (fd == null) - return null; - var access = GetTypeAccess(fd.DeclaringType, git); - if (access == null) - return null; - var acc = access.Value; - if ((acc & CheckTypeAccess.Normal) == 0) - return false; - if ((acc & CheckTypeAccess.FullMemberAccess) != 0) - return true; - - return IsVisible(fd, git); - } - - bool IsVisible(FieldDef fd, GenericInstSig git) { - if (fd == null) - return false; - var fdDeclaringType = fd.DeclaringType; - if (fdDeclaringType == null) - return false; - if (userType == fdDeclaringType) - return true; - - switch (fd.Access) { - case FieldAttributes.PrivateScope: - // Private scope aka compiler controlled fields/methods can only be accessed - // by a Field/Method token. This means they must be in the same module. - return userType.Module == fdDeclaringType.Module; - - case FieldAttributes.Private: - return false; - - case FieldAttributes.FamANDAssem: - return IsSameAssemblyOrFriendAssembly(fdDeclaringType.Module) && - CheckFamily(fdDeclaringType, git); - - case FieldAttributes.Assembly: - return IsSameAssemblyOrFriendAssembly(fdDeclaringType.Module); - - case FieldAttributes.Family: - return CheckFamily(fdDeclaringType, git); - - case FieldAttributes.FamORAssem: - return IsSameAssemblyOrFriendAssembly(fdDeclaringType.Module) || - CheckFamily(fdDeclaringType, git); - - case FieldAttributes.Public: - return true; - - default: - return false; - } - } - - /// - /// Checks whether it can access a - /// - /// The method - /// true if it has access to it, false if not, and null - /// if we can't determine it (eg. we couldn't resolve a type or input was null) - public bool? CanAccess(MethodDef md) { - return CanAccess(md, (GenericInstSig)null); - } - - bool? CanAccess(MethodDef md, GenericInstSig git) { - if (md == null) - return null; - var access = GetTypeAccess(md.DeclaringType, git); - if (access == null) - return null; - var acc = access.Value; - if ((acc & CheckTypeAccess.Normal) == 0) - return false; - if ((acc & CheckTypeAccess.FullMemberAccess) != 0) - return true; - - return IsVisible(md, git); - } - - bool IsVisible(MethodDef md, GenericInstSig git) { - if (md == null) - return false; - var mdDeclaringType = md.DeclaringType; - if (mdDeclaringType == null) - return false; - if (userType == mdDeclaringType) - return true; - - switch (md.Access) { - case MethodAttributes.PrivateScope: - // Private scope aka compiler controlled fields/methods can only be accessed - // by a Field/Method token. This means they must be in the same module. - return userType.Module == mdDeclaringType.Module; - - case MethodAttributes.Private: - return false; - - case MethodAttributes.FamANDAssem: - return IsSameAssemblyOrFriendAssembly(mdDeclaringType.Module) && - CheckFamily(mdDeclaringType, git); - - case MethodAttributes.Assembly: - return IsSameAssemblyOrFriendAssembly(mdDeclaringType.Module); - - case MethodAttributes.Family: - return CheckFamily(mdDeclaringType, git); - - case MethodAttributes.FamORAssem: - return IsSameAssemblyOrFriendAssembly(mdDeclaringType.Module) || - CheckFamily(mdDeclaringType, git); - - case MethodAttributes.Public: - return true; - - default: - return false; - } - } - - /// - /// Checks whether it can access a - /// - /// The member reference - /// true if it has access to it, false if not, and null - /// if we can't determine it (eg. we couldn't resolve a type or input was null) - public bool? CanAccess(MemberRef mr) { - if (mr == null) - return null; - - var parent = mr.Class; - - var td = parent as TypeDef; - if (td != null) - return CanAccess(td, mr); - - var tr = parent as TypeRef; - if (tr != null) - return CanAccess(tr.Resolve(), mr); - - var ts = parent as TypeSpec; - if (ts != null) - return CanAccess(ts.ResolveTypeDef(), ts.TryGetGenericInstSig(), mr); - - var md = parent as MethodDef; - if (md != null) - return CanAccess(md, mr); - - var mod = parent as ModuleRef; - if (mod != null) - return CanAccess(mod, mr); - - return null; - } - - bool? CanAccess(TypeDef td, MemberRef mr) { - return CanAccess(td, null, mr); - } - - bool? CanAccess(TypeDef td, GenericInstSig git, MemberRef mr) { - if (mr == null || td == null) - return null; - - if (mr.MethodSig != null) { - var md = td.FindMethodCheckBaseType(mr.Name, mr.MethodSig); - if (md == null) { - // Assume that it's an array type if it's one of these methods - if (mr.Name == "Get" || mr.Name == "Set" || mr.Name == "Address" || mr.Name == MethodDef.InstanceConstructorName) - return true; - return null; - } - return CanAccess(md, git); - } - - if (mr.FieldSig != null) - return CanAccess(td.FindFieldCheckBaseType(mr.Name, mr.FieldSig), git); - - return null; - } - - bool? CanAccess(MethodDef md, MemberRef mr) { - if (mr == null || md == null) - return null; - return CanAccess(md); - } - - bool? CanAccess(ModuleRef mod, MemberRef mr) { - if (mr == null || mod == null || mod.Module == null) - return null; - - var userModule = userType.Module; - if (userModule == null) - return null; - var userAsm = userModule.Assembly; - if (!IsSameAssembly(userAsm, mod.Module.Assembly)) - return false; - if (userAsm == null) - return false; - var otherMod = userAsm.FindModule(mod.Name); - if (otherMod == null) - return false; - return CanAccess(otherMod.GlobalType, mr); - } - - /// - /// Checks whether it can access a - /// - /// The type spec - /// true if it has access to it, false if not, and null - /// if we can't determine it (eg. we couldn't resolve a type or input was null) - public bool? CanAccess(TypeSpec ts) { - return CanAccess(ts.ResolveTypeDef()); - } - - /// - /// Checks whether it can access a - /// - /// The method spec - /// true if it has access to it, false if not, and null - /// if we can't determine it (eg. we couldn't resolve a type or input was null) - public bool? CanAccess(MethodSpec ms) { - if (ms == null) - return null; - - var mdr = ms.Method; - - var md = mdr as MethodDef; - if (md != null) - return CanAccess(md); - - var mr = mdr as MemberRef; - if (mr != null) - return CanAccess(mr); - - return null; - } - - /// - public override string ToString() { - return string.Format("{0}", userType); - } - } -} diff --git a/src/DotNet/AllTypesHelper.cs b/src/DotNet/AllTypesHelper.cs index 1d0429ba8..2dcac1f1e 100644 --- a/src/DotNet/AllTypesHelper.cs +++ b/src/DotNet/AllTypesHelper.cs @@ -1,13 +1,12 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; -using dnlib.Threading; +using System.Collections.Generic; namespace dnlib.DotNet { /// /// Returns types without getting stuck in an infinite loop /// - public struct AllTypesHelper { + readonly struct AllTypesHelper { /// /// Gets a list of all types and nested types /// @@ -15,8 +14,8 @@ public struct AllTypesHelper { public static IEnumerable Types(IEnumerable types) { var visited = new Dictionary(); var stack = new Stack>(); - if (types != null) - stack.Push(types.GetSafeEnumerable().GetEnumerator()); + if (types is not null) + stack.Push(types.GetEnumerator()); while (stack.Count > 0) { var enumerator = stack.Pop(); while (enumerator.MoveNext()) { @@ -27,7 +26,7 @@ public static IEnumerable Types(IEnumerable types) { yield return type; if (type.NestedTypes.Count > 0) { stack.Push(enumerator); - enumerator = type.NestedTypes.GetSafeEnumerable().GetEnumerator(); + enumerator = type.NestedTypes.GetEnumerator(); } } } diff --git a/src/DotNet/AssemblyAttributes.cs b/src/DotNet/AssemblyAttributes.cs index 7d2b2dbec..c4a687f43 100644 --- a/src/DotNet/AssemblyAttributes.cs +++ b/src/DotNet/AssemblyAttributes.cs @@ -27,6 +27,8 @@ public enum AssemblyAttributes : uint { PA_AMD64 = 0x0040, /// Processor Architecture: ARM (PE32) PA_ARM = 0x0050, + /// Processor Architecture: ARM64 (PE32+) + PA_ARM64 = 0x0060, /// applies to any platform but cannot run on any (e.g. reference assembly), should not have "specified" set PA_NoPlatform = 0x0070, /// Propagate PA flags to AssemblyRef record diff --git a/src/DotNet/AssemblyDef.cs b/src/DotNet/AssemblyDef.cs index bf7636313..109b196d8 100644 --- a/src/DotNet/AssemblyDef.cs +++ b/src/DotNet/AssemblyDef.cs @@ -7,51 +7,42 @@ using dnlib.Utils; using dnlib.DotNet.MD; using dnlib.DotNet.Writer; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif +using System.Text.RegularExpressions; +using dnlib.DotNet.Pdb; +using System.Collections.Generic; +using System.Diagnostics; namespace dnlib.DotNet { /// /// A high-level representation of a row in the Assembly table /// - public abstract class AssemblyDef : IHasCustomAttribute, IHasDeclSecurity, IAssembly, IListListener, ITypeDefFinder, IDnlibDef { + public abstract class AssemblyDef : IHasCustomAttribute, IHasDeclSecurity, IHasCustomDebugInformation, IAssembly, IListListener, ITypeDefFinder, IDnlibDef { /// /// The row id in its table /// protected uint rid; /// - public MDToken MDToken { - get { return new MDToken(Table.Assembly, rid); } - } + public MDToken MDToken => new MDToken(Table.Assembly, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 14; } - } + public int HasCustomAttributeTag => 14; /// - public int HasDeclSecurityTag { - get { return 2; } - } + public int HasDeclSecurityTag => 2; /// /// From column Assembly.HashAlgId /// public AssemblyHashAlgorithm HashAlgorithm { - get { return hashAlgorithm; } - set { hashAlgorithm = value; } + get => hashAlgorithm; + set => hashAlgorithm = value; } /// protected AssemblyHashAlgorithm hashAlgorithm; @@ -62,12 +53,8 @@ public AssemblyHashAlgorithm HashAlgorithm { /// /// If is null public Version Version { - get { return version; } - set { - if (value == null) - throw new ArgumentNullException("value"); - version = value; - } + get => version; + set => version = value ?? throw new ArgumentNullException(nameof(value)); } /// protected Version version; @@ -76,8 +63,8 @@ public Version Version { /// From column Assembly.Flags /// public AssemblyAttributes Attributes { - get { return (AssemblyAttributes)attributes; } - set { attributes = (int)value; } + get => (AssemblyAttributes)attributes; + set => attributes = (int)value; } /// Attributes protected int attributes; @@ -87,8 +74,8 @@ public AssemblyAttributes Attributes { /// /// An empty is created if the caller writes null public PublicKey PublicKey { - get { return publicKey; } - set { publicKey = value ?? new PublicKey(); } + get => publicKey; + set => publicKey = value ?? new PublicKey(); } /// protected PublicKey publicKey; @@ -96,16 +83,14 @@ public PublicKey PublicKey { /// /// Gets the public key token which is calculated from /// - public PublicKeyToken PublicKeyToken { - get { return publicKey.Token; } - } + public PublicKeyToken PublicKeyToken => publicKey.Token; /// /// From column Assembly.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -114,48 +99,41 @@ public UTF8String Name { /// From column Assembly.Locale /// public UTF8String Culture { - get { return culture; } - set { culture = value; } + get => culture; + set => culture = value; } /// Name protected UTF8String culture; /// - public ThreadSafe.IList DeclSecurities { + public IList DeclSecurities { get { - if (declSecurities == null) + if (declSecurities is null) InitializeDeclSecurities(); return declSecurities; } } /// - protected ThreadSafe.IList declSecurities; + protected IList declSecurities; /// Initializes - protected virtual void InitializeDeclSecurities() { - Interlocked.CompareExchange(ref declSecurities, ThreadSafeListCreator.Create(), null); - } + protected virtual void InitializeDeclSecurities() => + Interlocked.CompareExchange(ref declSecurities, new List(), null); /// - public PublicKeyBase PublicKeyOrToken { - get { return publicKey; } - } + public PublicKeyBase PublicKeyOrToken => publicKey; /// - public string FullName { - get { return GetFullNameWithPublicKeyToken(); } - } + public string FullName => GetFullNameWithPublicKeyToken(); /// - public string FullNameToken { - get { return GetFullNameWithPublicKeyToken(); } - } + public string FullNameToken => GetFullNameWithPublicKeyToken(); /// /// Gets all modules. The first module is always the . /// - public ThreadSafe.IList Modules { + public IList Modules { get { - if (modules == null) + if (modules is null) InitializeModules(); return modules; } @@ -163,16 +141,15 @@ public ThreadSafe.IList Modules { /// protected LazyList modules; /// Initializes - protected virtual void InitializeModules() { + protected virtual void InitializeModules() => Interlocked.CompareExchange(ref modules, new LazyList(this), null); - } /// /// Gets all custom attributes /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -180,34 +157,47 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } - } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + + /// + public int HasCustomDebugInformationTag => 14; /// - public bool HasDeclSecurities { - get { return DeclSecurities.Count > 0; } + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); + /// + public bool HasDeclSecurities => DeclSecurities.Count > 0; /// /// true if is not empty /// - public bool HasModules { - get { return Modules.Count > 0; } - } + public bool HasModules => Modules.Count > 0; /// /// Gets the manifest (main) module. This is always the first module in . /// null is returned if is empty. /// - public ModuleDef ManifestModule { - get { return Modules.Get(0, null); } - } + public ModuleDef ManifestModule => Modules.Count == 0 ? null : Modules[0]; /// /// Modify property: = @@ -215,17 +205,8 @@ public ModuleDef ManifestModule { /// /// Value to AND /// Value to OR - void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - newVal = (origVal & (int)andMask) | (int)orMask; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else + void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) => attributes = (attributes & (int)andMask) | (int)orMask; -#endif - } /// /// Set or clear flags in @@ -234,149 +215,120 @@ void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) { /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, AssemblyAttributes flags) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else if (set) attributes |= (int)flags; else attributes &= ~(int)flags; -#endif } /// /// Gets/sets the bit /// public bool HasPublicKey { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PublicKey) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.PublicKey); } + get => ((AssemblyAttributes)attributes & AssemblyAttributes.PublicKey) != 0; + set => ModifyAttributes(value, AssemblyAttributes.PublicKey); } /// /// Gets/sets the processor architecture /// public AssemblyAttributes ProcessorArchitecture { - get { return (AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask; } - set { ModifyAttributes(~AssemblyAttributes.PA_Mask, value & AssemblyAttributes.PA_Mask); } + get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask; + set => ModifyAttributes(~AssemblyAttributes.PA_Mask, value & AssemblyAttributes.PA_Mask); } /// /// Gets/sets the processor architecture /// public AssemblyAttributes ProcessorArchitectureFull { - get { return (AssemblyAttributes)attributes & AssemblyAttributes.PA_FullMask; } - set { ModifyAttributes(~AssemblyAttributes.PA_FullMask, value & AssemblyAttributes.PA_FullMask); } + get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_FullMask; + set => ModifyAttributes(~AssemblyAttributes.PA_FullMask, value & AssemblyAttributes.PA_FullMask); } /// /// true if unspecified processor architecture /// - public bool IsProcessorArchitectureNone { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_None; } - } + public bool IsProcessorArchitectureNone => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_None; /// /// true if neutral (PE32) architecture /// - public bool IsProcessorArchitectureMSIL { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_MSIL; } - } + public bool IsProcessorArchitectureMSIL => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_MSIL; /// /// true if x86 (PE32) architecture /// - public bool IsProcessorArchitectureX86 { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_x86; } - } + public bool IsProcessorArchitectureX86 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_x86; /// /// true if IA-64 (PE32+) architecture /// - public bool IsProcessorArchitectureIA64 { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_IA64; } - } + public bool IsProcessorArchitectureIA64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_IA64; /// /// true if x64 (PE32+) architecture /// - public bool IsProcessorArchitectureX64 { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_AMD64; } - } + public bool IsProcessorArchitectureX64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_AMD64; /// /// true if ARM (PE32) architecture /// - public bool IsProcessorArchitectureARM { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_ARM; } - } + public bool IsProcessorArchitectureARM => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_ARM; /// /// true if eg. reference assembly (not runnable) /// - public bool IsProcessorArchitectureNoPlatform { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_NoPlatform; } - } + public bool IsProcessorArchitectureNoPlatform => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_NoPlatform; /// /// Gets/sets the bit /// public bool IsProcessorArchitectureSpecified { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Specified) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.PA_Specified); } + get => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Specified) != 0; + set => ModifyAttributes(value, AssemblyAttributes.PA_Specified); } /// /// Gets/sets the bit /// public bool EnableJITcompileTracking { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.EnableJITcompileTracking) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.EnableJITcompileTracking); } + get => ((AssemblyAttributes)attributes & AssemblyAttributes.EnableJITcompileTracking) != 0; + set => ModifyAttributes(value, AssemblyAttributes.EnableJITcompileTracking); } /// /// Gets/sets the bit /// public bool DisableJITcompileOptimizer { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.DisableJITcompileOptimizer) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.DisableJITcompileOptimizer); } + get => ((AssemblyAttributes)attributes & AssemblyAttributes.DisableJITcompileOptimizer) != 0; + set => ModifyAttributes(value, AssemblyAttributes.DisableJITcompileOptimizer); } /// /// Gets/sets the bit /// public bool IsRetargetable { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.Retargetable) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.Retargetable); } + get => ((AssemblyAttributes)attributes & AssemblyAttributes.Retargetable) != 0; + set => ModifyAttributes(value, AssemblyAttributes.Retargetable); } /// /// Gets/sets the content type /// public AssemblyAttributes ContentType { - get { return (AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask; } - set { ModifyAttributes(~AssemblyAttributes.ContentType_Mask, value & AssemblyAttributes.ContentType_Mask); } + get => (AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask; + set => ModifyAttributes(~AssemblyAttributes.ContentType_Mask, value & AssemblyAttributes.ContentType_Mask); } /// /// true if content type is Default /// - public bool IsContentTypeDefault { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_Default; } - } + public bool IsContentTypeDefault => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_Default; /// /// true if content type is WindowsRuntime /// - public bool IsContentTypeWindowsRuntime { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime; } - } + public bool IsContentTypeWindowsRuntime => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime; /// /// Finds a module in this assembly @@ -384,8 +336,11 @@ public bool IsContentTypeWindowsRuntime { /// Name of module /// A instance or null if it wasn't found. public ModuleDef FindModule(UTF8String name) { - foreach (var module in Modules.GetSafeEnumerable()) { - if (module == null) + var modules = Modules; + int count = modules.Count; + for (int i = 0; i < count; i++) { + var module = modules[i]; + if (module is null) continue; if (UTF8String.CaseInsensitiveEquals(module.Name, name)) return module; @@ -393,17 +348,6 @@ public ModuleDef FindModule(UTF8String name) { return null; } - /// - /// Creates an instance from a file - /// - /// File name of an existing .NET assembly - /// A new instance - /// If is null - /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) - public static AssemblyDef Load(string fileName) { - return Load(fileName, (ModuleCreationOptions)null); - } - /// /// Creates an instance from a file /// @@ -412,9 +356,8 @@ public static AssemblyDef Load(string fileName) { /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) - public static AssemblyDef Load(string fileName, ModuleContext context) { - return Load(fileName, new ModuleCreationOptions(context)); - } + public static AssemblyDef Load(string fileName, ModuleContext context) => + Load(fileName, new ModuleCreationOptions(context)); /// /// Creates an instance from a file @@ -424,35 +367,24 @@ public static AssemblyDef Load(string fileName, ModuleContext context) { /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) - public static AssemblyDef Load(string fileName, ModuleCreationOptions options) { - if (fileName == null) - throw new ArgumentNullException("fileName"); + public static AssemblyDef Load(string fileName, ModuleCreationOptions options = null) { + if (fileName is null) + throw new ArgumentNullException(nameof(fileName)); ModuleDef module = null; try { module = ModuleDefMD.Load(fileName, options); var asm = module.Assembly; - if (asm == null) - throw new BadImageFormatException(string.Format("{0} is only a .NET module, not a .NET assembly. Use ModuleDef.Load().", fileName)); + if (asm is null) + throw new BadImageFormatException($"{fileName} is only a .NET module, not a .NET assembly. Use ModuleDef.Load()."); return asm; } catch { - if (module != null) + if (module is not null) module.Dispose(); throw; } } - /// - /// Creates an instance from a byte[] - /// - /// Contents of a .NET assembly - /// A new instance - /// If is null - /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) - public static AssemblyDef Load(byte[] data) { - return Load(data, (ModuleCreationOptions)null); - } - /// /// Creates an instance from a byte[] /// @@ -461,9 +393,8 @@ public static AssemblyDef Load(byte[] data) { /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) - public static AssemblyDef Load(byte[] data, ModuleContext context) { - return Load(data, new ModuleCreationOptions(context)); - } + public static AssemblyDef Load(byte[] data, ModuleContext context) => + Load(data, new ModuleCreationOptions(context)); /// /// Creates an instance from a byte[] @@ -473,35 +404,24 @@ public static AssemblyDef Load(byte[] data, ModuleContext context) { /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) - public static AssemblyDef Load(byte[] data, ModuleCreationOptions options) { - if (data == null) - throw new ArgumentNullException("data"); + public static AssemblyDef Load(byte[] data, ModuleCreationOptions options = null) { + if (data is null) + throw new ArgumentNullException(nameof(data)); ModuleDef module = null; try { module = ModuleDefMD.Load(data, options); var asm = module.Assembly; - if (asm == null) - throw new BadImageFormatException(string.Format("{0} is only a .NET module, not a .NET assembly. Use ModuleDef.Load().", module.ToString())); + if (asm is null) + throw new BadImageFormatException($"{module.ToString()} is only a .NET module, not a .NET assembly. Use ModuleDef.Load()."); return asm; } catch { - if (module != null) + if (module is not null) module.Dispose(); throw; } } - /// - /// Creates an instance from a memory location - /// - /// Address of a .NET assembly - /// A new instance - /// If is null - /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) - public static AssemblyDef Load(IntPtr addr) { - return Load(addr, (ModuleCreationOptions)null); - } - /// /// Creates an instance from a memory location /// @@ -510,9 +430,8 @@ public static AssemblyDef Load(IntPtr addr) { /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) - public static AssemblyDef Load(IntPtr addr, ModuleContext context) { - return Load(addr, new ModuleCreationOptions(context)); - } + public static AssemblyDef Load(IntPtr addr, ModuleContext context) => + Load(addr, new ModuleCreationOptions(context)); /// /// Creates an instance from a memory location @@ -522,37 +441,24 @@ public static AssemblyDef Load(IntPtr addr, ModuleContext context) { /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) - public static AssemblyDef Load(IntPtr addr, ModuleCreationOptions options) { + public static AssemblyDef Load(IntPtr addr, ModuleCreationOptions options = null) { if (addr == IntPtr.Zero) - throw new ArgumentNullException("addr"); + throw new ArgumentNullException(nameof(addr)); ModuleDef module = null; try { module = ModuleDefMD.Load(addr, options); var asm = module.Assembly; - if (asm == null) - throw new BadImageFormatException(string.Format("{0} (addr: {1:X8}) is only a .NET module, not a .NET assembly. Use ModuleDef.Load().", module.ToString(), addr.ToInt64())); + if (asm is null) + throw new BadImageFormatException($"{module.ToString()} (addr: {addr.ToInt64():X8}) is only a .NET module, not a .NET assembly. Use ModuleDef.Load()."); return asm; } catch { - if (module != null) + if (module is not null) module.Dispose(); throw; } } - /// - /// Creates an instance from a stream - /// - /// This will read all bytes from the stream and call . - /// It's better to use one of the other Load() methods. - /// The stream - /// A new instance - /// If is null - /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) - public static AssemblyDef Load(Stream stream) { - return Load(stream, (ModuleCreationOptions)null); - } - /// /// Creates an instance from a stream /// @@ -563,9 +469,8 @@ public static AssemblyDef Load(Stream stream) { /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) - public static AssemblyDef Load(Stream stream, ModuleContext context) { - return Load(stream, new ModuleCreationOptions(context)); - } + public static AssemblyDef Load(Stream stream, ModuleContext context) => + Load(stream, new ModuleCreationOptions(context)); /// /// Creates an instance from a stream @@ -577,19 +482,19 @@ public static AssemblyDef Load(Stream stream, ModuleContext context) { /// A new instance /// If is null /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module) - public static AssemblyDef Load(Stream stream, ModuleCreationOptions options) { - if (stream == null) - throw new ArgumentNullException("stream"); + public static AssemblyDef Load(Stream stream, ModuleCreationOptions options = null) { + if (stream is null) + throw new ArgumentNullException(nameof(stream)); ModuleDef module = null; try { module = ModuleDefMD.Load(stream, options); var asm = module.Assembly; - if (asm == null) - throw new BadImageFormatException(string.Format("{0} is only a .NET module, not a .NET assembly. Use ModuleDef.Load().", module.ToString())); + if (asm is null) + throw new BadImageFormatException($"{module.ToString()} is only a .NET module, not a .NET assembly. Use ModuleDef.Load()."); return asm; } catch { - if (module != null) + if (module is not null) module.Dispose(); throw; } @@ -598,20 +503,12 @@ public static AssemblyDef Load(Stream stream, ModuleCreationOptions options) { /// /// Gets the assembly name with the public key /// - public string GetFullNameWithPublicKey() { - return GetFullName(publicKey); - } + public string GetFullNameWithPublicKey() => FullNameFactory.AssemblyFullName(this, false); /// /// Gets the assembly name with the public key token /// - public string GetFullNameWithPublicKeyToken() { - return GetFullName(publicKey.Token); - } - - string GetFullName(PublicKeyBase pkBase) { - return Utils.GetAssemblyNameString(name, version, culture, pkBase, Attributes); - } + public string GetFullNameWithPublicKeyToken() => FullNameFactory.AssemblyFullName(this, true); /// /// Finds a . For speed, enable @@ -623,11 +520,14 @@ string GetFullName(PublicKeyBase pkBase) { /// are separated by a / character. /// An existing or null if it wasn't found. public TypeDef Find(string fullName, bool isReflectionName) { - foreach (var module in Modules.GetSafeEnumerable()) { - if (module == null) + var modules = Modules; + int count = modules.Count; + for (int i = 0; i < count; i++) { + var module = modules[i]; + if (module is null) continue; var type = module.Find(fullName, isReflectionName); - if (type != null) + if (type is not null) return type; } return null; @@ -641,56 +541,41 @@ public TypeDef Find(string fullName, bool isReflectionName) { /// The type ref /// An existing or null if it wasn't found. public TypeDef Find(TypeRef typeRef) { - foreach (var module in Modules.GetSafeEnumerable()) { - if (module == null) + var modules = Modules; + int count = modules.Count; + for (int i = 0; i < count; i++) { + var module = modules[i]; + if (module is null) continue; var type = module.Find(typeRef); - if (type != null) + if (type is not null) return type; } return null; } - /// - /// Writes the assembly to a file on disk. If the file exists, it will be truncated. - /// - /// Filename - public void Write(string filename) { - Write(filename, null); - } - /// /// Writes the assembly to a file on disk. If the file exists, it will be truncated. /// /// Filename /// Writer options - public void Write(string filename, ModuleWriterOptions options) { + public void Write(string filename, ModuleWriterOptions options = null) => ManifestModule.Write(filename, options); - } - - /// - /// Writes the assembly to a stream. - /// - /// Destination stream - public void Write(Stream dest) { - Write(dest, null); - } /// /// Writes the assembly to a stream. /// /// Destination stream /// Writer options - public void Write(Stream dest, ModuleWriterOptions options) { + public void Write(Stream dest, ModuleWriterOptions options = null) => ManifestModule.Write(dest, options); - } /// /// Checks whether this assembly is a friend assembly of /// /// Target assembly public bool IsFriendAssemblyOf(AssemblyDef targetAsm) { - if (targetAsm == null) + if (targetAsm is null) return false; if (this == targetAsm) return true; @@ -703,7 +588,7 @@ public bool IsFriendAssemblyOf(AssemblyDef targetAsm) { foreach (var ca in targetAsm.CustomAttributes.FindAll("System.Runtime.CompilerServices.InternalsVisibleToAttribute")) { if (ca.ConstructorArguments.Count != 1) continue; - var arg = ca.ConstructorArguments.Get(0, default(CAArgument)); + var arg = ca.ConstructorArguments.Count == 0 ? default : ca.ConstructorArguments[0]; if (arg.Type.GetElementType() != ElementType.String) continue; var asmName = arg.Value as UTF8String; @@ -736,23 +621,20 @@ public bool IsFriendAssemblyOf(AssemblyDef targetAsm) { /// Signature public key public void UpdateOrCreateAssemblySignatureKeyAttribute(StrongNamePublicKey identityPubKey, StrongNameKey identityKey, StrongNamePublicKey signaturePubKey) { var manifestModule = ManifestModule; - if (manifestModule == null) + if (manifestModule is null) return; // Remove all existing attributes - var ca = CustomAttributes.ExecuteLocked(null, (tsList, arg) => { - CustomAttribute foundCa = null; - for (int i = 0; i < tsList.Count_NoLock(); i++) { - var caTmp = tsList.Get_NoLock(i); - if (caTmp.TypeFullName != "System.Reflection.AssemblySignatureKeyAttribute") - continue; - tsList.RemoveAt_NoLock(i); - i--; - if (foundCa == null) - foundCa = caTmp; - } - return foundCa; - }); + CustomAttribute ca = null; + for (int i = 0; i < CustomAttributes.Count; i++) { + var caTmp = CustomAttributes[i]; + if (caTmp.TypeFullName != "System.Reflection.AssemblySignatureKeyAttribute") + continue; + CustomAttributes.RemoveAt(i); + i--; + if (ca is null) + ca = caTmp; + } if (IsValidAssemblySignatureKeyAttribute(ca)) ca.NamedArguments.Clear(); @@ -766,16 +648,15 @@ public void UpdateOrCreateAssemblySignatureKeyAttribute(StrongNamePublicKey iden } bool IsValidAssemblySignatureKeyAttribute(CustomAttribute ca) { -#if THREAD_SAFE - return false; -#else - if (ca == null) + if (dnlib.Settings.IsThreadSafe) + return false; + if (ca is null) return false; var ctor = ca.Constructor; - if (ctor == null) + if (ctor is null) return false; var sig = ctor.MethodSig; - if (sig == null || sig.Params.Count != 2) + if (sig is null || sig.Params.Count != 2) return false; if (sig.Params[0].GetElementType() != ElementType.String) return false; @@ -784,7 +665,6 @@ bool IsValidAssemblySignatureKeyAttribute(CustomAttribute ca) { if (ca.ConstructorArguments.Count != 2) return false; return true; -#endif } CustomAttribute CreateAssemblySignatureKeyAttribute() { @@ -798,28 +678,44 @@ CustomAttribute CreateAssemblySignatureKeyAttribute() { return ca; } + /// + /// Gets the original System.Runtime.Versioning.TargetFrameworkAttribute custom attribute information if possible. + /// It reads this from the original metadata and doesn't use . + /// Returns false if the custom attribute isn't present or if it is invalid. + /// + /// Framework name + /// Version + /// Profile + /// + public virtual bool TryGetOriginalTargetFrameworkAttribute(out string framework, out Version version, out string profile) { + framework = null; + version = null; + profile = null; + return false; + } + /// void IListListener.OnLazyAdd(int index, ref ModuleDef module) { - if (module == null) + if (module is null) return; #if DEBUG - if (module.Assembly == null) - throw new InvalidOperationException("Module.Assembly == null"); + if (module.Assembly is null) + throw new InvalidOperationException("Module.Assembly is null"); #endif } /// void IListListener.OnAdd(int index, ModuleDef module) { - if (module == null) + if (module is null) return; - if (module.Assembly != null) + if (module.Assembly is not null) throw new InvalidOperationException("Module already has an assembly. Remove it from that assembly before adding it to this assembly."); module.Assembly = this; } /// void IListListener.OnRemove(int index, ModuleDef module) { - if (module != null) + if (module is not null) module.Assembly = null; } @@ -829,16 +725,14 @@ void IListListener.OnResize(int index) { /// void IListListener.OnClear() { - foreach (var module in Modules.GetEnumerable_NoLock()) { - if (module != null) + foreach (var module in modules.GetEnumerable_NoLock()) { + if (module is not null) module.Assembly = null; } } /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } /// @@ -891,18 +785,16 @@ public AssemblyDefUser(UTF8String name, Version version, PublicKey publicKey) /// Locale /// If any of the args is invalid public AssemblyDefUser(UTF8String name, Version version, PublicKey publicKey, UTF8String locale) { - if ((object)name == null) - throw new ArgumentNullException("name"); - if (version == null) - throw new ArgumentNullException("version"); - if ((object)locale == null) - throw new ArgumentNullException("locale"); - this.modules = new LazyList(this); + if (name is null) + throw new ArgumentNullException(nameof(name)); + if (locale is null) + throw new ArgumentNullException(nameof(locale)); + modules = new LazyList(this); this.name = name; - this.version = version; + this.version = version ?? throw new ArgumentNullException(nameof(version)); this.publicKey = publicKey ?? new PublicKey(); - this.culture = locale; - this.attributes = (int)AssemblyAttributes.None; + culture = locale; + attributes = (int)AssemblyAttributes.None; } /// @@ -912,8 +804,8 @@ public AssemblyDefUser(UTF8String name, Version version, PublicKey publicKey, UT /// If is null public AssemblyDefUser(AssemblyName asmName) : this(new AssemblyNameInfo(asmName)) { - this.hashAlgorithm = (AssemblyHashAlgorithm)asmName.HashAlgorithm; - this.attributes = (int)asmName.Flags; + hashAlgorithm = (AssemblyHashAlgorithm)asmName.HashAlgorithm; + attributes = (int)asmName.Flags; } /// @@ -922,15 +814,15 @@ public AssemblyDefUser(AssemblyName asmName) /// Assembly name info /// If is null public AssemblyDefUser(IAssembly asmName) { - if (asmName == null) - throw new ArgumentNullException("asmName"); - this.modules = new LazyList(this); - this.name = asmName.Name; - this.version = asmName.Version ?? new Version(0, 0, 0, 0); - this.publicKey = asmName.PublicKeyOrToken as PublicKey ?? new PublicKey(); - this.culture = asmName.Culture; - this.attributes = (int)AssemblyAttributes.None; - this.hashAlgorithm = AssemblyHashAlgorithm.SHA1; + if (asmName is null) + throw new ArgumentNullException(nameof(asmName)); + modules = new LazyList(this); + name = asmName.Name; + version = asmName.Version ?? new Version(0, 0, 0, 0); + publicKey = asmName.PublicKeyOrToken as PublicKey ?? new PublicKey(); + culture = asmName.Culture; + attributes = (int)AssemblyAttributes.None; + hashAlgorithm = AssemblyHashAlgorithm.SHA1; } } @@ -944,27 +836,25 @@ sealed class AssemblyDefMD : AssemblyDef, IMDTokenProviderMD { readonly uint origRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override void InitializeDeclSecurities() { - var list = readerModule.MetaData.GetDeclSecurityRidList(Table.Assembly, origRid); - var tmp = new LazyList((int)list.Length, list, (list2, index) => readerModule.ResolveDeclSecurity(((RidList)list2)[index])); + var list = readerModule.Metadata.GetDeclSecurityRidList(Table.Assembly, origRid); + var tmp = new LazyList(list.Count, list, (list2, index) => readerModule.ResolveDeclSecurity(list2[index])); Interlocked.CompareExchange(ref declSecurities, tmp, null); } /// protected override void InitializeModules() { var list = readerModule.GetModuleRidList(); - var tmp = new LazyList((int)list.Length + 1, this, list, (list2, index) => { + var tmp = new LazyList(list.Count + 1, this, list, (list2, index) => { ModuleDef module; if (index == 0) module = readerModule; else - module = readerModule.ReadModule(((RidList)list2)[index - 1], this); - if (module == null) + module = readerModule.ReadModule(list2[index - 1], this); + if (module is null) module = new ModuleDefUser("INVALID", Guid.NewGuid()); module.Assembly = this; return module; @@ -974,11 +864,175 @@ protected override void InitializeModules() { /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.Assembly, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Assembly, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + + /// + public override bool TryGetOriginalTargetFrameworkAttribute(out string framework, out Version version, out string profile) { + if (!hasInitdTFA) + InitializeTargetFrameworkAttribute(); + framework = tfaFramework; + version = tfaVersion; + profile = tfaProfile; + return tfaReturnValue; + } + volatile bool hasInitdTFA; + string tfaFramework; + Version tfaVersion; + string tfaProfile; + bool tfaReturnValue; + + void InitializeTargetFrameworkAttribute() { + if (hasInitdTFA) + return; + + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Assembly, origRid); + var gpContext = new GenericParamContext(); + for (int i = 0; i < list.Count; i++) { + var caRid = list[i]; + if (!readerModule.TablesStream.TryReadCustomAttributeRow(caRid, out var caRow)) + continue; + + // Prevent a possible infinite loop. + // This can happen when a custom AssemblyResolver calls this function when resolving an AssemblyRef. + // If this function encounters a custom attribute whose type is a MemberRef with a TypeSpec class, + // the MemberRef constructor called by ResolveCustomAttributeType() will call ResolveTypeDef() which + // will make it back to the AssemblyResolver and back to this function creating an infinite loop. + // The TargetFrameworkAttribute will never be generic so we can just skip parsing any generic attributes. + if (!CodedToken.CustomAttributeType.Decode(caRow.Type, out MDToken token)) + continue; + if (token.Table == Table.MemberRef && + (!readerModule.TablesStream.TryReadMemberRefRow(token.Rid, out var mrRow) || + readerModule.ResolveMemberRefParent(mrRow.Class, gpContext) is TypeSpec ts && + ts.TypeSig is GenericInstSig)) + continue; + + var caType = readerModule.ResolveCustomAttributeType(caRow.Type, gpContext); + if (!TryGetName(caType, out var ns, out var name)) + continue; + if (ns != nameSystemRuntimeVersioning || name != nameTargetFrameworkAttribute) + continue; + var ca = CustomAttributeReader.Read(readerModule, caType, caRow.Value, gpContext); + if (ca is null || ca.ConstructorArguments.Count != 1) + continue; + var s = ca.ConstructorArguments[0].Value as UTF8String; + if (s is null) + continue; + if (TryCreateTargetFrameworkInfo(s, out var tmpFramework, out var tmpVersion, out var tmpProfile)) { + tfaFramework = tmpFramework; + tfaVersion = tmpVersion; + tfaProfile = tmpProfile; + tfaReturnValue = true; + break; + } + } + + hasInitdTFA = true; + } + static readonly UTF8String nameSystemRuntimeVersioning = new UTF8String("System.Runtime.Versioning"); + static readonly UTF8String nameTargetFrameworkAttribute = new UTF8String("TargetFrameworkAttribute"); + + static bool TryGetName(ICustomAttributeType caType, out UTF8String ns, out UTF8String name) { + ITypeDefOrRef type; + if (caType is MemberRef mr) + type = mr.DeclaringType; + else + type = (caType as MethodDef)?.DeclaringType; + if (type is TypeRef tr) { + ns = tr.Namespace; + name = tr.Name; + return true; + } + if (type is TypeDef td) { + ns = td.Namespace; + name = td.Name; + return true; + } + ns = null; + name = null; + return false; + } + + static bool TryCreateTargetFrameworkInfo(string attrString, out string framework, out Version version, out string profile) { + framework = null; + version = null; + profile = null; + + // See corclr/src/mscorlib/src/System/Runtime/Versioning/BinaryCompatibility.cs + var values = attrString.Split(new char[] { ',' }); + if (values.Length < 2 || values.Length > 3) + return false; + var frameworkRes = values[0].Trim(); + if (frameworkRes.Length == 0) + return false; + + Version versionRes = null; + string profileRes = null; + for (int i = 1; i < values.Length; i++) { + var kvp = values[i].Split('='); + if (kvp.Length != 2) + return false; + + var key = kvp[0].Trim(); + var value = kvp[1].Trim(); + + if (key.Equals("Version", StringComparison.OrdinalIgnoreCase)) { + if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase)) + value = value.Substring(1); + if (!TryParse(value, out versionRes)) + return false; + versionRes = new Version(versionRes.Major, versionRes.Minor, versionRes.Build == -1 ? 0 : versionRes.Build, 0); + } + else if (key.Equals("Profile", StringComparison.OrdinalIgnoreCase)) { + if (!string.IsNullOrEmpty(value)) + profileRes = value; + } + } + if (versionRes is null) + return false; + + framework = frameworkRes; + version = versionRes; + profile = profileRes; + return true; + } + + static int ParseInt32(string s) => int.TryParse(s, out int res) ? res : 0; + + static bool TryParse(string s, out Version version) { + Match m; + + m = Regex.Match(s, @"^(\d+)\.(\d+)$"); + if (m.Groups.Count == 3) { + version = new Version(ParseInt32(m.Groups[1].Value), ParseInt32(m.Groups[2].Value)); + return true; + } + + m = Regex.Match(s, @"^(\d+)\.(\d+)\.(\d+)$"); + if (m.Groups.Count == 4) { + version = new Version(ParseInt32(m.Groups[1].Value), ParseInt32(m.Groups[2].Value), ParseInt32(m.Groups[3].Value)); + return true; + } + + m = Regex.Match(s, @"^(\d+)\.(\d+)\.(\d+)\.(\d+)$"); + if (m.Groups.Count == 5) { + version = new Version(ParseInt32(m.Groups[1].Value), ParseInt32(m.Groups[2].Value), ParseInt32(m.Groups[3].Value), ParseInt32(m.Groups[4].Value)); + return true; + } + + version = null; + return false; + } + /// /// Constructor /// @@ -988,21 +1042,24 @@ protected override void InitializeCustomAttributes() { /// If is invalid public AssemblyDefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.AssemblyTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("Assembly rid {0} does not exist", rid)); + throw new BadImageFormatException($"Assembly rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; if (rid != 1) - this.modules = new LazyList(this); - uint publicKey, name; - uint culture = readerModule.TablesStream.ReadAssemblyRow(origRid, out this.hashAlgorithm, out this.version, out this.attributes, out publicKey, out name); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.culture = readerModule.StringsStream.ReadNoNull(culture); - this.publicKey = new PublicKey(readerModule.BlobStream.Read(publicKey)); + modules = new LazyList(this); + bool b = readerModule.TablesStream.TryReadAssemblyRow(origRid, out var row); + Debug.Assert(b); + hashAlgorithm = (AssemblyHashAlgorithm)row.HashAlgId; + version = new Version(row.MajorVersion, row.MinorVersion, row.BuildNumber, row.RevisionNumber); + attributes = (int)row.Flags; + name = readerModule.StringsStream.ReadNoNull(row.Name); + culture = readerModule.StringsStream.ReadNoNull(row.Locale); + publicKey = new PublicKey(readerModule.BlobStream.Read(row.PublicKey)); } } } diff --git a/src/DotNet/AssemblyHash.cs b/src/DotNet/AssemblyHash.cs index 9f01522c9..22de5f3eb 100644 --- a/src/DotNet/AssemblyHash.cs +++ b/src/DotNet/AssemblyHash.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.IO; using System.Security.Cryptography; @@ -8,7 +8,7 @@ namespace dnlib.DotNet { /// /// Hashes some data according to a /// - struct AssemblyHash : IDisposable { + readonly struct AssemblyHash : IDisposable { readonly HashAlgorithm hasher; /// @@ -17,42 +17,18 @@ struct AssemblyHash : IDisposable { /// If is an unsupported hash algorithm, then /// will be used as the hash algorithm. /// The algorithm to use - public AssemblyHash(AssemblyHashAlgorithm hashAlgo) { - switch (hashAlgo) { - case AssemblyHashAlgorithm.MD5: - hasher = MD5.Create(); - break; - - case AssemblyHashAlgorithm.None: - case AssemblyHashAlgorithm.MD2: - case AssemblyHashAlgorithm.MD4: - case AssemblyHashAlgorithm.SHA1: - case AssemblyHashAlgorithm.MAC: - case AssemblyHashAlgorithm.SSL3_SHAMD5: - case AssemblyHashAlgorithm.HMAC: - case AssemblyHashAlgorithm.TLS1PRF: - case AssemblyHashAlgorithm.HASH_REPLACE_OWF: - default: - hasher = SHA1.Create(); - break; - - case AssemblyHashAlgorithm.SHA_256: - hasher = SHA256.Create(); - break; - - case AssemblyHashAlgorithm.SHA_384: - hasher = SHA384.Create(); - break; - - case AssemblyHashAlgorithm.SHA_512: - hasher = SHA512.Create(); - break; - } - } + public AssemblyHash(AssemblyHashAlgorithm hashAlgo) => + hasher = hashAlgo switch { + AssemblyHashAlgorithm.MD5 => MD5.Create(), + AssemblyHashAlgorithm.SHA_256 => SHA256.Create(), + AssemblyHashAlgorithm.SHA_384 => SHA384.Create(), + AssemblyHashAlgorithm.SHA_512 => SHA512.Create(), + _ => SHA1.Create(), + }; /// public void Dispose() { - if (hasher != null) + if (hasher is not null) ((IDisposable)hasher).Dispose(); } @@ -65,7 +41,7 @@ public void Dispose() { /// The algorithm to use /// Hashed data or null if was null public static byte[] Hash(byte[] data, AssemblyHashAlgorithm hashAlgo) { - if (data == null) + if (data is null) return null; using (var asmHash = new AssemblyHash(hashAlgo)) { @@ -78,9 +54,7 @@ public static byte[] Hash(byte[] data, AssemblyHashAlgorithm hashAlgo) { /// Hash data /// /// Data - public void Hash(byte[] data) { - Hash(data, 0, data.Length); - } + public void Hash(byte[] data) => Hash(data, 0, data.Length); /// /// Hash data @@ -113,7 +87,7 @@ public void Hash(Stream stream, uint length, byte[] buffer) { /// Computes the hash /// public byte[] ComputeHash() { - hasher.TransformFinalBlock(new byte[0], 0, 0); + hasher.TransformFinalBlock(Array2.Empty(), 0, 0); return hasher.Hash; } @@ -125,10 +99,10 @@ public byte[] ComputeHash() { /// The data /// A new instance public static PublicKeyToken CreatePublicKeyToken(byte[] publicKeyData) { - if (publicKeyData == null) + if (publicKeyData is null) return new PublicKeyToken(); var hash = Hash(publicKeyData, AssemblyHashAlgorithm.SHA1); - byte[] pkt = new byte[8]; + var pkt = new byte[8]; for (int i = 0; i < pkt.Length && i < hash.Length; i++) pkt[i] = hash[hash.Length - i - 1]; return new PublicKeyToken(pkt); diff --git a/src/DotNet/AssemblyHashAlgorithm.cs b/src/DotNet/AssemblyHashAlgorithm.cs index b1b02853c..8c5913cc5 100644 --- a/src/DotNet/AssemblyHashAlgorithm.cs +++ b/src/DotNet/AssemblyHashAlgorithm.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// Any ALG_CLASS_HASH type in WinCrypt.h can be used by Microsoft's CLI implementation /// @@ -34,22 +34,21 @@ public enum AssemblyHashAlgorithm : uint { } public static partial class Extensions { - internal static string GetName(this AssemblyHashAlgorithm hashAlg) { - switch (hashAlg) { - case AssemblyHashAlgorithm.MD2: return null; - case AssemblyHashAlgorithm.MD4: return null; - case AssemblyHashAlgorithm.MD5: return "MD5"; - case AssemblyHashAlgorithm.SHA1: return "SHA1"; - case AssemblyHashAlgorithm.MAC: return null; - case AssemblyHashAlgorithm.SSL3_SHAMD5: return null; - case AssemblyHashAlgorithm.HMAC: return null; - case AssemblyHashAlgorithm.TLS1PRF: return null; - case AssemblyHashAlgorithm.HASH_REPLACE_OWF: return null; - case AssemblyHashAlgorithm.SHA_256: return "SHA256"; - case AssemblyHashAlgorithm.SHA_384: return "SHA384"; - case AssemblyHashAlgorithm.SHA_512: return "SHA512"; - default: return null; - } - } + internal static string GetName(this AssemblyHashAlgorithm hashAlg) => + hashAlg switch { + AssemblyHashAlgorithm.MD2 => null, + AssemblyHashAlgorithm.MD4 => null, + AssemblyHashAlgorithm.MD5 => "MD5", + AssemblyHashAlgorithm.SHA1 => "SHA1", + AssemblyHashAlgorithm.MAC => null, + AssemblyHashAlgorithm.SSL3_SHAMD5 => null, + AssemblyHashAlgorithm.HMAC => null, + AssemblyHashAlgorithm.TLS1PRF => null, + AssemblyHashAlgorithm.HASH_REPLACE_OWF => null, + AssemblyHashAlgorithm.SHA_256 => "SHA256", + AssemblyHashAlgorithm.SHA_384 => "SHA384", + AssemblyHashAlgorithm.SHA_512 => "SHA512", + _ => null, + }; } } diff --git a/src/DotNet/AssemblyNameComparer.cs b/src/DotNet/AssemblyNameComparer.cs index ed6f6535f..4c4a07826 100644 --- a/src/DotNet/AssemblyNameComparer.cs +++ b/src/DotNet/AssemblyNameComparer.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; namespace dnlib.DotNet { @@ -43,66 +43,54 @@ public enum AssemblyNameComparerFlags { /// /// Compares two assembly names /// - public struct AssemblyNameComparer : IEqualityComparer { + public readonly struct AssemblyNameComparer : IEqualityComparer { /// /// Compares the name, version, public key token, culture and content type /// - public static AssemblyNameComparer CompareAll = new AssemblyNameComparer(AssemblyNameComparerFlags.All); + public static readonly AssemblyNameComparer CompareAll = new AssemblyNameComparer(AssemblyNameComparerFlags.All); /// /// Compares only the name and the public key token /// - public static AssemblyNameComparer NameAndPublicKeyTokenOnly = new AssemblyNameComparer(AssemblyNameComparerFlags.Name | AssemblyNameComparerFlags.PublicKeyToken); + public static readonly AssemblyNameComparer NameAndPublicKeyTokenOnly = new AssemblyNameComparer(AssemblyNameComparerFlags.Name | AssemblyNameComparerFlags.PublicKeyToken); /// /// Compares only the name /// - public static AssemblyNameComparer NameOnly = new AssemblyNameComparer(AssemblyNameComparerFlags.Name); + public static readonly AssemblyNameComparer NameOnly = new AssemblyNameComparer(AssemblyNameComparerFlags.Name); readonly AssemblyNameComparerFlags flags; /// /// Gets the bit /// - public bool CompareName { - get { return (flags & AssemblyNameComparerFlags.Name) != 0; } - } + public bool CompareName => (flags & AssemblyNameComparerFlags.Name) != 0; /// /// Gets the bit /// - public bool CompareVersion { - get { return (flags & AssemblyNameComparerFlags.Version) != 0; } - } + public bool CompareVersion => (flags & AssemblyNameComparerFlags.Version) != 0; /// /// Gets the bit /// - public bool ComparePublicKeyToken { - get { return (flags & AssemblyNameComparerFlags.PublicKeyToken) != 0; } - } + public bool ComparePublicKeyToken => (flags & AssemblyNameComparerFlags.PublicKeyToken) != 0; /// /// Gets the bit /// - public bool CompareCulture { - get { return (flags & AssemblyNameComparerFlags.Culture) != 0; } - } + public bool CompareCulture => (flags & AssemblyNameComparerFlags.Culture) != 0; /// /// Gets the bit /// - public bool CompareContentType { - get { return (flags & AssemblyNameComparerFlags.ContentType) != 0; } - } + public bool CompareContentType => (flags & AssemblyNameComparerFlags.ContentType) != 0; /// /// Constructor /// /// Comparison flags - public AssemblyNameComparer(AssemblyNameComparerFlags flags) { - this.flags = flags; - } + public AssemblyNameComparer(AssemblyNameComparerFlags flags) => this.flags = flags; /// /// Compares two assembly names @@ -113,9 +101,9 @@ public AssemblyNameComparer(AssemblyNameComparerFlags flags) { public int CompareTo(IAssembly a, IAssembly b) { if (a == b) return 0; - if (a == null) + if (a is null) return -1; - if (b == null) + if (b is null) return 1; int v; @@ -140,9 +128,7 @@ public int CompareTo(IAssembly a, IAssembly b) { /// First /// Second /// true if equal, false otherwise - public bool Equals(IAssembly a, IAssembly b) { - return CompareTo(a, b) == 0; - } + public bool Equals(IAssembly a, IAssembly b) => CompareTo(a, b) == 0; /// /// Figures out which of two assembly names is closer to another assembly name @@ -155,9 +141,9 @@ public bool Equals(IAssembly a, IAssembly b) { public int CompareClosest(IAssembly requested, IAssembly a, IAssembly b) { if (a == b) return 0; - if (a == null) + if (a is null) return !CompareName ? 1 : UTF8String.CaseInsensitiveEquals(requested.Name, b.Name) ? 1 : 0; - if (b == null) + if (b is null) return !CompareName ? 0 : UTF8String.CaseInsensitiveEquals(requested.Name, a.Name) ? 0 : 1; // Compare the most important parts first: @@ -251,7 +237,7 @@ public int CompareClosest(IAssembly requested, IAssembly a, IAssembly b) { /// Assembly name /// The hash code public int GetHashCode(IAssembly a) { - if (a == null) + if (a is null) return 0; int hash = 0; diff --git a/src/DotNet/AssemblyNameInfo.cs b/src/DotNet/AssemblyNameInfo.cs index 1d1512ec5..9e76d400f 100644 --- a/src/DotNet/AssemblyNameInfo.cs +++ b/src/DotNet/AssemblyNameInfo.cs @@ -19,86 +19,59 @@ public sealed class AssemblyNameInfo : IAssembly { /// Gets/sets the /// public AssemblyHashAlgorithm HashAlgId { - get { return hashAlgId; } - set { hashAlgId = value; } + get => hashAlgId; + set => hashAlgId = value; } /// /// Gets/sets the or null if none specified /// public Version Version { - get { return version; } - set { version = value; } - } - - /// - /// Gets/sets the - /// - [Obsolete("Use Attributes property instead", false)] - public AssemblyAttributes Flags { - get { return flags; } - set { flags = value; } + get => version; + set => version = value; } /// /// Gets/sets the /// public AssemblyAttributes Attributes { - get { return flags; } - set { flags = value; } + get => flags; + set => flags = value; } /// /// Gets/sets the public key or token /// public PublicKeyBase PublicKeyOrToken { - get { return publicKeyOrToken; } - set { publicKeyOrToken = value; } + get => publicKeyOrToken; + set => publicKeyOrToken = value; } /// /// Gets/sets the name /// public UTF8String Name { - get { return name; } - set { name = value; } - } - - /// - /// Gets/sets the culture or null if none specified - /// - [Obsolete("Use Culture property instead", false)] - public UTF8String Locale { - get { return Culture; } - set { Culture = value; } + get => name; + set => name = value; } /// /// Gets/sets the culture or null if none specified /// public UTF8String Culture { - get { return culture; } - set { culture = value; } + get => culture; + set => culture = value; } /// /// Gets the full name of the assembly /// - public string FullName { - get { return FullNameToken; } - } + public string FullName => FullNameToken; /// /// Gets the full name of the assembly but use a public key token /// - public string FullNameToken { - get { - var pk = publicKeyOrToken; - if (pk is PublicKey) - pk = (pk as PublicKey).Token; - return Utils.GetAssemblyNameString(name, version, culture, pk, flags); - } - } + public string FullNameToken => FullNameFactory.AssemblyFullName(this, true); /// /// Modify property: = @@ -106,9 +79,7 @@ public string FullNameToken { /// /// Value to AND /// Value to OR - void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) { - Attributes = (Attributes & andMask) | orMask; - } + void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) => Attributes = (Attributes & andMask) | orMask; /// /// Set or clear flags in @@ -127,128 +98,110 @@ void ModifyAttributes(bool set, AssemblyAttributes flags) { /// Gets/sets the bit /// public bool HasPublicKey { - get { return (Attributes & AssemblyAttributes.PublicKey) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.PublicKey); } + get => (Attributes & AssemblyAttributes.PublicKey) != 0; + set => ModifyAttributes(value, AssemblyAttributes.PublicKey); } /// /// Gets/sets the processor architecture /// public AssemblyAttributes ProcessorArchitecture { - get { return Attributes & AssemblyAttributes.PA_Mask; } - set { ModifyAttributes(~AssemblyAttributes.PA_Mask, value & AssemblyAttributes.PA_Mask); } + get => Attributes & AssemblyAttributes.PA_Mask; + set => ModifyAttributes(~AssemblyAttributes.PA_Mask, value & AssemblyAttributes.PA_Mask); } /// /// Gets/sets the processor architecture /// public AssemblyAttributes ProcessorArchitectureFull { - get { return Attributes & AssemblyAttributes.PA_FullMask; } - set { ModifyAttributes(~AssemblyAttributes.PA_FullMask, value & AssemblyAttributes.PA_FullMask); } + get => Attributes & AssemblyAttributes.PA_FullMask; + set => ModifyAttributes(~AssemblyAttributes.PA_FullMask, value & AssemblyAttributes.PA_FullMask); } /// /// true if unspecified processor architecture /// - public bool IsProcessorArchitectureNone { - get { return (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_None; } - } + public bool IsProcessorArchitectureNone => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_None; /// /// true if neutral (PE32) architecture /// - public bool IsProcessorArchitectureMSIL { - get { return (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_MSIL; } - } + public bool IsProcessorArchitectureMSIL => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_MSIL; /// /// true if x86 (PE32) architecture /// - public bool IsProcessorArchitectureX86 { - get { return (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_x86; } - } + public bool IsProcessorArchitectureX86 => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_x86; /// /// true if IA-64 (PE32+) architecture /// - public bool IsProcessorArchitectureIA64 { - get { return (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_IA64; } - } + public bool IsProcessorArchitectureIA64 => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_IA64; /// /// true if x64 (PE32+) architecture /// - public bool IsProcessorArchitectureX64 { - get { return (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_AMD64; } - } + public bool IsProcessorArchitectureX64 => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_AMD64; /// /// true if ARM (PE32) architecture /// - public bool IsProcessorArchitectureARM { - get { return (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_ARM; } - } + public bool IsProcessorArchitectureARM => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_ARM; /// /// true if eg. reference assembly (not runnable) /// - public bool IsProcessorArchitectureNoPlatform { - get { return (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_NoPlatform; } - } + public bool IsProcessorArchitectureNoPlatform => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_NoPlatform; /// /// Gets/sets the bit /// public bool IsProcessorArchitectureSpecified { - get { return (Attributes & AssemblyAttributes.PA_Specified) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.PA_Specified); } + get => (Attributes & AssemblyAttributes.PA_Specified) != 0; + set => ModifyAttributes(value, AssemblyAttributes.PA_Specified); } /// /// Gets/sets the bit /// public bool EnableJITcompileTracking { - get { return (Attributes & AssemblyAttributes.EnableJITcompileTracking) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.EnableJITcompileTracking); } + get => (Attributes & AssemblyAttributes.EnableJITcompileTracking) != 0; + set => ModifyAttributes(value, AssemblyAttributes.EnableJITcompileTracking); } /// /// Gets/sets the bit /// public bool DisableJITcompileOptimizer { - get { return (Attributes & AssemblyAttributes.DisableJITcompileOptimizer) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.DisableJITcompileOptimizer); } + get => (Attributes & AssemblyAttributes.DisableJITcompileOptimizer) != 0; + set => ModifyAttributes(value, AssemblyAttributes.DisableJITcompileOptimizer); } /// /// Gets/sets the bit /// public bool IsRetargetable { - get { return (Attributes & AssemblyAttributes.Retargetable) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.Retargetable); } + get => (Attributes & AssemblyAttributes.Retargetable) != 0; + set => ModifyAttributes(value, AssemblyAttributes.Retargetable); } /// /// Gets/sets the content type /// public AssemblyAttributes ContentType { - get { return Attributes & AssemblyAttributes.ContentType_Mask; } - set { ModifyAttributes(~AssemblyAttributes.ContentType_Mask, value & AssemblyAttributes.ContentType_Mask); } + get => Attributes & AssemblyAttributes.ContentType_Mask; + set => ModifyAttributes(~AssemblyAttributes.ContentType_Mask, value & AssemblyAttributes.ContentType_Mask); } /// /// true if content type is Default /// - public bool IsContentTypeDefault { - get { return (Attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_Default; } - } + public bool IsContentTypeDefault => (Attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_Default; /// /// true if content type is WindowsRuntime /// - public bool IsContentTypeWindowsRuntime { - get { return (Attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime; } - } + public bool IsContentTypeWindowsRuntime => (Attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime; /// /// Default constructor @@ -269,15 +222,15 @@ public AssemblyNameInfo(string asmFullName) /// /// The assembly public AssemblyNameInfo(IAssembly asm) { - if (asm == null) + if (asm is null) return; var asmDef = asm as AssemblyDef; - this.hashAlgId = asmDef == null ? 0 : asmDef.HashAlgorithm; - this.version = asm.Version ?? new Version(0, 0, 0, 0); - this.flags = asm.Attributes; - this.publicKeyOrToken = asm.PublicKeyOrToken; - this.name = UTF8String.IsNullOrEmpty(asm.Name) ? UTF8String.Empty : asm.Name; - this.culture = UTF8String.IsNullOrEmpty(asm.Culture) ? UTF8String.Empty : asm.Culture; + hashAlgId = asmDef is null ? 0 : asmDef.HashAlgorithm; + version = asm.Version ?? new Version(0, 0, 0, 0); + flags = asm.Attributes; + publicKeyOrToken = asm.PublicKeyOrToken; + name = UTF8String.IsNullOrEmpty(asm.Name) ? UTF8String.Empty : asm.Name; + culture = UTF8String.IsNullOrEmpty(asm.Culture) ? UTF8String.Empty : asm.Culture; } /// @@ -285,20 +238,18 @@ public AssemblyNameInfo(IAssembly asm) { /// /// Assembly name info public AssemblyNameInfo(AssemblyName asmName) { - if (asmName == null) + if (asmName is null) return; - this.hashAlgId = (AssemblyHashAlgorithm)asmName.HashAlgorithm; - this.version = asmName.Version ?? new Version(0, 0, 0, 0); - this.flags = (AssemblyAttributes)asmName.Flags; - this.publicKeyOrToken = (PublicKeyBase)PublicKeyBase.CreatePublicKey(asmName.GetPublicKey()) ?? + hashAlgId = (AssemblyHashAlgorithm)asmName.HashAlgorithm; + version = asmName.Version ?? new Version(0, 0, 0, 0); + flags = (AssemblyAttributes)asmName.Flags; + publicKeyOrToken = (PublicKeyBase)PublicKeyBase.CreatePublicKey(asmName.GetPublicKey()) ?? PublicKeyBase.CreatePublicKeyToken(asmName.GetPublicKeyToken()); - this.name = asmName.Name ?? string.Empty; - this.culture = asmName.CultureInfo != null && asmName.CultureInfo.Name != null ? asmName.CultureInfo.Name : string.Empty; + name = asmName.Name ?? string.Empty; + culture = asmName.CultureInfo is not null && asmName.CultureInfo.Name is not null ? asmName.CultureInfo.Name : string.Empty; } /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } } diff --git a/src/DotNet/AssemblyRef.cs b/src/DotNet/AssemblyRef.cs index d92379f1f..f025cfffb 100644 --- a/src/DotNet/AssemblyRef.cs +++ b/src/DotNet/AssemblyRef.cs @@ -1,15 +1,18 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Reflection; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// /// A high-level representation of a row in the AssemblyRef table /// - public abstract class AssemblyRef : IHasCustomAttribute, IImplementation, IResolutionScope, IAssembly, IScope { + public abstract class AssemblyRef : IHasCustomAttribute, IImplementation, IResolutionScope, IHasCustomDebugInformation, IAssembly, IScope { /// /// An assembly ref that can be used to indicate that it references the current assembly /// when the current assembly is not known (eg. a type string without any assembly info @@ -23,40 +26,28 @@ public abstract class AssemblyRef : IHasCustomAttribute, IImplementation, IResol protected uint rid; /// - public MDToken MDToken { - get { return new MDToken(Table.AssemblyRef, rid); } - } + public MDToken MDToken => new MDToken(Table.AssemblyRef, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 15; } - } + public int HasCustomAttributeTag => 15; /// - public int ImplementationTag { - get { return 1; } - } + public int ImplementationTag => 1; /// - public int ResolutionScopeTag { - get { return 2; } - } + public int ResolutionScopeTag => 2; /// - public ScopeType ScopeType { - get { return ScopeType.AssemblyRef; } - } + public ScopeType ScopeType => ScopeType.AssemblyRef; /// - public string ScopeName { - get { return FullName; } - } + public string ScopeName => FullName; /// /// From columns AssemblyRef.MajorVersion, AssemblyRef.MinorVersion, @@ -64,12 +55,8 @@ public string ScopeName { /// /// If is null public Version Version { - get { return version; } - set { - if (value == null) - throw new ArgumentNullException("value"); - version = value; - } + get => version; + set => version = value ?? throw new ArgumentNullException(nameof(value)); } /// protected Version version; @@ -78,8 +65,8 @@ public Version Version { /// From column AssemblyRef.Flags /// public AssemblyAttributes Attributes { - get { return (AssemblyAttributes)attributes; } - set { attributes = (int)value; } + get => (AssemblyAttributes)attributes; + set => attributes = (int)value; } /// Attributes protected int attributes; @@ -89,12 +76,8 @@ public AssemblyAttributes Attributes { /// /// If is null public PublicKeyBase PublicKeyOrToken { - get { return publicKeyOrToken; } - set { - if (value == null) - throw new ArgumentNullException("value"); - publicKeyOrToken = value; - } + get => publicKeyOrToken; + set => publicKeyOrToken = value ?? throw new ArgumentNullException(nameof(value)); } /// protected PublicKeyBase publicKeyOrToken; @@ -103,8 +86,8 @@ public PublicKeyBase PublicKeyOrToken { /// From column AssemblyRef.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -113,8 +96,8 @@ public UTF8String Name { /// From column AssemblyRef.Locale /// public UTF8String Culture { - get { return culture; } - set { culture = value; } + get => culture; + set => culture = value; } /// Culture protected UTF8String culture; @@ -123,8 +106,8 @@ public UTF8String Culture { /// From column AssemblyRef.HashValue /// public byte[] Hash { - get { return hashValue; } - set { hashValue = value; } + get => hashValue; + set => hashValue = value; } /// protected byte[] hashValue; @@ -134,7 +117,7 @@ public byte[] Hash { /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -142,33 +125,46 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } - } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 15; /// - public string FullName { - get { return FullNameToken; } + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); + + /// + public string FullName => FullNameToken; /// /// Same as , except that it uses the PublicKey if available. /// - public string RealFullName { - get { return Utils.GetAssemblyNameString(name, version, culture, publicKeyOrToken, Attributes); } - } + public string RealFullName => FullNameFactory.AssemblyFullName(this, false); /// /// Gets the full name of the assembly but use a public key token /// - public string FullNameToken { - get { return Utils.GetAssemblyNameString(name, version, culture, PublicKeyBase.ToPublicKeyToken(publicKeyOrToken), Attributes); } - } + public string FullNameToken => FullNameFactory.AssemblyFullName(this, true); /// /// Modify property: = @@ -176,17 +172,8 @@ public string FullNameToken { /// /// Value to AND /// Value to OR - void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - newVal = (origVal & (int)andMask) | (int)orMask; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else + void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) => attributes = (attributes & (int)andMask) | (int)orMask; -#endif - } /// /// Set or clear flags in @@ -195,154 +182,123 @@ void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) { /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, AssemblyAttributes flags) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else if (set) attributes |= (int)flags; else attributes &= ~(int)flags; -#endif } /// /// Gets/sets the bit /// public bool HasPublicKey { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PublicKey) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.PublicKey); } + get => ((AssemblyAttributes)attributes & AssemblyAttributes.PublicKey) != 0; + set => ModifyAttributes(value, AssemblyAttributes.PublicKey); } /// /// Gets/sets the processor architecture /// public AssemblyAttributes ProcessorArchitecture { - get { return (AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask; } - set { ModifyAttributes(~AssemblyAttributes.PA_Mask, value & AssemblyAttributes.PA_Mask); } + get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask; + set => ModifyAttributes(~AssemblyAttributes.PA_Mask, value & AssemblyAttributes.PA_Mask); } /// /// Gets/sets the processor architecture /// public AssemblyAttributes ProcessorArchitectureFull { - get { return (AssemblyAttributes)attributes & AssemblyAttributes.PA_FullMask; } - set { ModifyAttributes(~AssemblyAttributes.PA_FullMask, value & AssemblyAttributes.PA_FullMask); } + get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_FullMask; + set => ModifyAttributes(~AssemblyAttributes.PA_FullMask, value & AssemblyAttributes.PA_FullMask); } /// /// true if unspecified processor architecture /// - public bool IsProcessorArchitectureNone { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_None; } - } + public bool IsProcessorArchitectureNone => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_None; /// /// true if neutral (PE32) architecture /// - public bool IsProcessorArchitectureMSIL { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_MSIL; } - } + public bool IsProcessorArchitectureMSIL => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_MSIL; /// /// true if x86 (PE32) architecture /// - public bool IsProcessorArchitectureX86 { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_x86; } - } + public bool IsProcessorArchitectureX86 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_x86; /// /// true if IA-64 (PE32+) architecture /// - public bool IsProcessorArchitectureIA64 { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_IA64; } - } + public bool IsProcessorArchitectureIA64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_IA64; /// /// true if x64 (PE32+) architecture /// - public bool IsProcessorArchitectureX64 { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_AMD64; } - } + public bool IsProcessorArchitectureX64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_AMD64; /// /// true if ARM (PE32) architecture /// - public bool IsProcessorArchitectureARM { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_ARM; } - } + public bool IsProcessorArchitectureARM => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_ARM; /// /// true if eg. reference assembly (not runnable) /// - public bool IsProcessorArchitectureNoPlatform { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_NoPlatform; } - } + public bool IsProcessorArchitectureNoPlatform => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_NoPlatform; /// /// Gets/sets the bit /// public bool IsProcessorArchitectureSpecified { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Specified) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.PA_Specified); } + get => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Specified) != 0; + set => ModifyAttributes(value, AssemblyAttributes.PA_Specified); } /// /// Gets/sets the bit /// public bool EnableJITcompileTracking { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.EnableJITcompileTracking) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.EnableJITcompileTracking); } + get => ((AssemblyAttributes)attributes & AssemblyAttributes.EnableJITcompileTracking) != 0; + set => ModifyAttributes(value, AssemblyAttributes.EnableJITcompileTracking); } /// /// Gets/sets the bit /// public bool DisableJITcompileOptimizer { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.DisableJITcompileOptimizer) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.DisableJITcompileOptimizer); } + get => ((AssemblyAttributes)attributes & AssemblyAttributes.DisableJITcompileOptimizer) != 0; + set => ModifyAttributes(value, AssemblyAttributes.DisableJITcompileOptimizer); } /// /// Gets/sets the bit /// public bool IsRetargetable { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.Retargetable) != 0; } - set { ModifyAttributes(value, AssemblyAttributes.Retargetable); } + get => ((AssemblyAttributes)attributes & AssemblyAttributes.Retargetable) != 0; + set => ModifyAttributes(value, AssemblyAttributes.Retargetable); } /// /// Gets/sets the content type /// public AssemblyAttributes ContentType { - get { return (AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask; } - set { ModifyAttributes(~AssemblyAttributes.ContentType_Mask, value & AssemblyAttributes.ContentType_Mask); } + get => (AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask; + set => ModifyAttributes(~AssemblyAttributes.ContentType_Mask, value & AssemblyAttributes.ContentType_Mask); } /// /// true if content type is Default /// - public bool IsContentTypeDefault { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_Default; } - } + public bool IsContentTypeDefault => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_Default; /// /// true if content type is WindowsRuntime /// - public bool IsContentTypeWindowsRuntime { - get { return ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime; } - } + public bool IsContentTypeWindowsRuntime => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime; /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } /// @@ -352,30 +308,22 @@ public class AssemblyRefUser : AssemblyRef { /// /// Creates a reference to CLR 1.0's mscorlib /// - public static AssemblyRefUser CreateMscorlibReferenceCLR10() { - return new AssemblyRefUser("mscorlib", new Version(1, 0, 3300, 0), new PublicKeyToken("b77a5c561934e089")); - } + public static AssemblyRefUser CreateMscorlibReferenceCLR10() => new AssemblyRefUser("mscorlib", new Version(1, 0, 3300, 0), new PublicKeyToken("b77a5c561934e089")); /// /// Creates a reference to CLR 1.1's mscorlib /// - public static AssemblyRefUser CreateMscorlibReferenceCLR11() { - return new AssemblyRefUser("mscorlib", new Version(1, 0, 5000, 0), new PublicKeyToken("b77a5c561934e089")); - } + public static AssemblyRefUser CreateMscorlibReferenceCLR11() => new AssemblyRefUser("mscorlib", new Version(1, 0, 5000, 0), new PublicKeyToken("b77a5c561934e089")); /// /// Creates a reference to CLR 2.0's mscorlib /// - public static AssemblyRefUser CreateMscorlibReferenceCLR20() { - return new AssemblyRefUser("mscorlib", new Version(2, 0, 0, 0), new PublicKeyToken("b77a5c561934e089")); - } + public static AssemblyRefUser CreateMscorlibReferenceCLR20() => new AssemblyRefUser("mscorlib", new Version(2, 0, 0, 0), new PublicKeyToken("b77a5c561934e089")); /// /// Creates a reference to CLR 4.0's mscorlib /// - public static AssemblyRefUser CreateMscorlibReferenceCLR40() { - return new AssemblyRefUser("mscorlib", new Version(4, 0, 0, 0), new PublicKeyToken("b77a5c561934e089")); - } + public static AssemblyRefUser CreateMscorlibReferenceCLR40() => new AssemblyRefUser("mscorlib", new Version(4, 0, 0, 0), new PublicKeyToken("b77a5c561934e089")); /// /// Default constructor @@ -423,17 +371,15 @@ public AssemblyRefUser(UTF8String name, Version version, PublicKeyBase publicKey /// Locale /// If any of the args is invalid public AssemblyRefUser(UTF8String name, Version version, PublicKeyBase publicKey, UTF8String locale) { - if ((object)name == null) - throw new ArgumentNullException("name"); - if (version == null) - throw new ArgumentNullException("version"); - if ((object)locale == null) - throw new ArgumentNullException("locale"); + if (name is null) + throw new ArgumentNullException(nameof(name)); + if (locale is null) + throw new ArgumentNullException(nameof(locale)); this.name = name; - this.version = version; - this.publicKeyOrToken = publicKey; - this.culture = locale; - this.attributes = (int)(publicKey is PublicKey ? AssemblyAttributes.PublicKey : AssemblyAttributes.None); + this.version = version ?? throw new ArgumentNullException(nameof(version)); + publicKeyOrToken = publicKey; + culture = locale; + attributes = (int)(publicKey is PublicKey ? AssemblyAttributes.PublicKey : AssemblyAttributes.None); } /// @@ -442,23 +388,21 @@ public AssemblyRefUser(UTF8String name, Version version, PublicKeyBase publicKey /// Assembly name info /// If is null public AssemblyRefUser(AssemblyName asmName) - : this(new AssemblyNameInfo(asmName)) { - this.attributes = (int)asmName.Flags; - } + : this(new AssemblyNameInfo(asmName)) => attributes = (int)asmName.Flags; /// /// Constructor /// /// Assembly public AssemblyRefUser(IAssembly assembly) { - if (assembly == null) + if (assembly is null) throw new ArgumentNullException("asmName"); - this.version = assembly.Version ?? new Version(0, 0, 0, 0); - this.publicKeyOrToken = assembly.PublicKeyOrToken; - this.name = UTF8String.IsNullOrEmpty(assembly.Name) ? UTF8String.Empty : assembly.Name; - this.culture = assembly.Culture; - this.attributes = (int)((publicKeyOrToken is PublicKey ? AssemblyAttributes.PublicKey : AssemblyAttributes.None) | assembly.ContentType); + version = assembly.Version ?? new Version(0, 0, 0, 0); + publicKeyOrToken = assembly.PublicKeyOrToken; + name = UTF8String.IsNullOrEmpty(assembly.Name) ? UTF8String.Empty : assembly.Name; + culture = assembly.Culture; + attributes = (int)((publicKeyOrToken is PublicKey ? AssemblyAttributes.PublicKey : AssemblyAttributes.None) | assembly.ContentType); } } @@ -472,17 +416,22 @@ sealed class AssemblyRefMD : AssemblyRef, IMDTokenProviderMD { readonly uint origRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.AssemblyRef, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.AssemblyRef, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -492,24 +441,26 @@ protected override void InitializeCustomAttributes() { /// If is invalid public AssemblyRefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.AssemblyRefTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("AssemblyRef rid {0} does not exist", rid)); + throw new BadImageFormatException($"AssemblyRef rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint publicKeyOrToken, name, culture; - uint hashValue = readerModule.TablesStream.ReadAssemblyRefRow(origRid, out this.version, out this.attributes, out publicKeyOrToken, out name, out culture); - var pkData = readerModule.BlobStream.Read(publicKeyOrToken); - if ((this.attributes & (uint)AssemblyAttributes.PublicKey) != 0) - this.publicKeyOrToken = new PublicKey(pkData); + bool b = readerModule.TablesStream.TryReadAssemblyRefRow(origRid, out var row); + Debug.Assert(b); + version = new Version(row.MajorVersion, row.MinorVersion, row.BuildNumber, row.RevisionNumber); + attributes = (int)row.Flags; + var pkData = readerModule.BlobStream.Read(row.PublicKeyOrToken); + if ((attributes & (uint)AssemblyAttributes.PublicKey) != 0) + publicKeyOrToken = new PublicKey(pkData); else - this.publicKeyOrToken = new PublicKeyToken(pkData); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.culture = readerModule.StringsStream.ReadNoNull(culture); - this.hashValue = readerModule.BlobStream.Read(hashValue); + publicKeyOrToken = new PublicKeyToken(pkData); + name = readerModule.StringsStream.ReadNoNull(row.Name); + culture = readerModule.StringsStream.ReadNoNull(row.Locale); + hashValue = readerModule.BlobStream.Read(row.HashValue); } } } diff --git a/src/DotNet/AssemblyResolver.cs b/src/DotNet/AssemblyResolver.cs index b46c70c78..791399d1c 100644 --- a/src/DotNet/AssemblyResolver.cs +++ b/src/DotNet/AssemblyResolver.cs @@ -1,18 +1,12 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.IO; -using System.Text.RegularExpressions; +using System.Linq; using System.Xml; using dnlib.Threading; -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif - namespace dnlib.DotNet { /// /// Resolves assemblies @@ -25,45 +19,47 @@ public class AssemblyResolver : IAssemblyResolver { static readonly string[] winMDAssemblyExtensions = new string[] { ".winmd" }; static readonly List gacInfos; - static readonly List extraMonoPaths; + static readonly string[] extraMonoPaths; static readonly string[] monoVerDirs = new string[] { - "4.5", "4.0", - "3.5", "3.0", "2.0", + // The "-api" dirs are reference assembly dirs. + "4.5", @"4.5\Facades", "4.5-api", @"4.5-api\Facades", "4.0", "4.0-api", + "3.5", "3.5-api", "3.0", "3.0-api", "2.0", "2.0-api", "1.1", "1.0", }; ModuleContext defaultModuleContext; - readonly Dictionary> moduleSearchPaths = new Dictionary>(); - readonly Dictionary cachedAssemblies = new Dictionary(StringComparer.Ordinal); - readonly ThreadSafe.IList preSearchPaths = ThreadSafeListCreator.Create(); - readonly ThreadSafe.IList postSearchPaths = ThreadSafeListCreator.Create(); + readonly Dictionary> moduleSearchPaths = new Dictionary>(); + readonly Dictionary cachedAssemblies = new Dictionary(StringComparer.OrdinalIgnoreCase); + readonly List preSearchPaths = new List(); + readonly List postSearchPaths = new List(); bool findExactMatch; bool enableFrameworkRedirect; - bool enableTypeDefCache; + bool enableTypeDefCache = true; + bool useGac = true; #if THREAD_SAFE readonly Lock theLock = Lock.Create(); #endif sealed class GacInfo { - public readonly int version; - public readonly string path; - public readonly string prefix; - public readonly IList subDirs; + public readonly int Version; + public readonly string Path; + public readonly string Prefix; + public readonly string[] SubDirs; - public GacInfo(int version, string prefix, string path, IList subDirs) { - this.version = version; - this.prefix = prefix; - this.path = path; - this.subDirs = subDirs; + public GacInfo(int version, string prefix, string path, string[] subDirs) { + Version = version; + Prefix = prefix; + Path = path; + SubDirs = subDirs; } } static AssemblyResolver() { gacInfos = new List(); - if (Type.GetType("Mono.Runtime") != null) { + if (Type.GetType("Mono.Runtime") is not null) { var dirs = new Dictionary(StringComparer.OrdinalIgnoreCase); - extraMonoPaths = new List(); + var extraMonoPathsList = new List(); foreach (var prefix in FindMonoPrefixes()) { var dir = Path.Combine(Path.Combine(Path.Combine(prefix, "lib"), "mono"), "gac"); if (dirs.ContainsKey(dir)) @@ -78,26 +74,30 @@ static AssemblyResolver() { dir = Path.GetDirectoryName(dir); foreach (var verDir in monoVerDirs) { - var dir2 = Path.Combine(dir, verDir); + var dir2 = dir; + foreach (var d in verDir.Split(new char[] { '\\' })) + dir2 = Path.Combine(dir2, d); if (Directory.Exists(dir2)) - extraMonoPaths.Add(dir2); + extraMonoPathsList.Add(dir2); } } var paths = Environment.GetEnvironmentVariable("MONO_PATH"); - if (paths != null) { - foreach (var path in paths.Split(Path.PathSeparator)) { + if (paths is not null) { + foreach (var tmp in paths.Split(Path.PathSeparator)) { + var path = tmp.Trim(); if (path != string.Empty && Directory.Exists(path)) - extraMonoPaths.Add(path); + extraMonoPathsList.Add(path); } } + extraMonoPaths = extraMonoPathsList.ToArray(); } else { var windir = Environment.GetEnvironmentVariable("WINDIR"); if (!string.IsNullOrEmpty(windir)) { string path; - // .NET 1.x and 2.x + // .NET Framework 1.x and 2.x path = Path.Combine(windir, "assembly"); if (Directory.Exists(path)) { gacInfos.Add(new GacInfo(2, "", path, new string[] { @@ -105,7 +105,7 @@ static AssemblyResolver() { })); } - // .NET 4.x + // .NET Framework 4.x path = Path.Combine(Path.Combine(windir, "Microsoft.NET"), "assembly"); if (Directory.Exists(path)) { gacInfos.Add(new GacInfo(4, "v4.0_", path, new string[] { @@ -128,7 +128,8 @@ static IEnumerable FindMonoPrefixes() { var prefixes = Environment.GetEnvironmentVariable("MONO_GAC_PREFIX"); if (!string.IsNullOrEmpty(prefixes)) { - foreach (var prefix in prefixes.Split(Path.PathSeparator)) { + foreach (var tmp in prefixes.Split(Path.PathSeparator)) { + var prefix = tmp.Trim(); if (prefix != string.Empty) yield return prefix; } @@ -139,8 +140,8 @@ static IEnumerable FindMonoPrefixes() { /// Gets/sets the default /// public ModuleContext DefaultModuleContext { - get { return defaultModuleContext; } - set { defaultModuleContext = value; } + get => defaultModuleContext; + set => defaultModuleContext = value; } /// @@ -149,75 +150,68 @@ public ModuleContext DefaultModuleContext { /// assembly that is closest to the requested assembly. /// public bool FindExactMatch { - get { return findExactMatch; } - set { findExactMatch = value; } + get => findExactMatch; + set => findExactMatch = value; } /// /// true if resolved .NET framework assemblies can be redirected to the source - /// module's framework assembly version. Eg. if a resolved .NET 3.5 assembly can be - /// redirected to a .NET 4.0 assembly if the source module is a .NET 4.0 assembly. This is + /// module's framework assembly version. Eg. if a resolved .NET Framework 3.5 assembly can be + /// redirected to a .NET Framework 4.0 assembly if the source module is a .NET Framework 4.0 assembly. This is /// ignored if is true. /// public bool EnableFrameworkRedirect { - get { return enableFrameworkRedirect; } - set { enableFrameworkRedirect = value; } + get => enableFrameworkRedirect; + set => enableFrameworkRedirect = value; } /// /// If true, all modules in newly resolved assemblies will have their - /// property set to true. + /// property set to true. This is + /// enabled by default since these modules shouldn't be modified by the user. /// public bool EnableTypeDefCache { - get { return enableTypeDefCache; } - set { enableTypeDefCache = value; } + get => enableTypeDefCache; + set => enableTypeDefCache = value; } /// - /// Gets paths searched before trying the standard locations + /// true to search the Global Assembly Cache. Default value is true. /// - public ThreadSafe.IList PreSearchPaths { - get { return preSearchPaths; } + public bool UseGAC { + get => useGac; + set => useGac = value; } /// - /// Gets paths searched after trying the standard locations + /// Gets paths searched before trying the standard locations /// - public ThreadSafe.IList PostSearchPaths { - get { return postSearchPaths; } - } + public IList PreSearchPaths => preSearchPaths; /// - /// Default constructor + /// Gets paths searched after trying the standard locations /// - public AssemblyResolver() - : this(null, true) { - } + public IList PostSearchPaths => postSearchPaths; /// - /// Constructor + /// Default constructor /// - /// Module context for all resolved assemblies - public AssemblyResolver(ModuleContext defaultModuleContext) - : this(defaultModuleContext, true) { + public AssemblyResolver() + : this(null) { } /// /// Constructor /// /// Module context for all resolved assemblies - /// If true, add other common assembly search - /// paths, not just the module search paths and the GAC. - public AssemblyResolver(ModuleContext defaultModuleContext, bool addOtherSearchPaths) { + public AssemblyResolver(ModuleContext defaultModuleContext) { this.defaultModuleContext = defaultModuleContext; - this.enableFrameworkRedirect = true; - if (addOtherSearchPaths) - AddOtherSearchPaths(postSearchPaths); + enableFrameworkRedirect = true; } /// public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) { - if (assembly == null) + if (assembly is null) return null; if (EnableFrameworkRedirect && !FindExactMatch) @@ -226,8 +220,8 @@ public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif - AssemblyDef resolvedAssembly = Resolve2(assembly, sourceModule); - if (resolvedAssembly == null) { + var resolvedAssembly = Resolve2(assembly, sourceModule); + if (resolvedAssembly is null) { string asmName = UTF8String.ToSystemStringOrEmpty(assembly.Name); string asmNameTrimmed = asmName.Trim(); if (asmName != asmNameTrimmed) { @@ -241,7 +235,7 @@ public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) { } } - if (resolvedAssembly == null) { + if (resolvedAssembly is null) { // Make sure we don't search for this assembly again. This speeds up callers who // keep asking for this assembly when trying to resolve many different TypeRefs cachedAssemblies[GetAssemblyNameKey(assembly)] = null; @@ -250,15 +244,17 @@ public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) { var key1 = GetAssemblyNameKey(resolvedAssembly); var key2 = GetAssemblyNameKey(assembly); - AssemblyDef asm1, asm2; - cachedAssemblies.TryGetValue(key1, out asm1); - cachedAssemblies.TryGetValue(key2, out asm2); + cachedAssemblies.TryGetValue(key1, out var asm1); + cachedAssemblies.TryGetValue(key2, out var asm2); if (asm1 != resolvedAssembly && asm2 != resolvedAssembly) { // This assembly was just resolved if (enableTypeDefCache) { - foreach (var module in resolvedAssembly.Modules.GetSafeEnumerable()) { - if (module != null) + var modules = resolvedAssembly.Modules; + int count = modules.Count; + for (int i = 0; i < count; i++) { + var module = modules[i]; + if (module is not null) module.EnableTypeDefFindCache = true; } } @@ -278,7 +274,7 @@ public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) { // Dupe assembly. Don't insert it. var dupeModule = resolvedAssembly.ManifestModule; - if (dupeModule != null) + if (dupeModule is not null) dupeModule.Dispose(); return asm1 ?? asm2; #if THREAD_SAFE @@ -286,16 +282,30 @@ public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) { #endif } - /// + /// + /// Add a module's assembly to the assembly cache + /// + /// The module whose assembly should be cached + /// true if 's assembly is cached, false + /// if it's not cached because some other assembly with the exact same full name has + /// already been cached or if or its assembly is null. + public bool AddToCache(ModuleDef module) => module is not null && AddToCache(module.Assembly); + + /// + /// Add an assembly to the assembly cache + /// + /// The assembly + /// true if is cached, false if it's not + /// cached because some other assembly with the exact same full name has already been + /// cached or if is null. public bool AddToCache(AssemblyDef asm) { - if (asm == null) + if (asm is null) return false; var asmKey = GetAssemblyNameKey(asm); - AssemblyDef cachedAsm; #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif - if (cachedAssemblies.TryGetValue(asmKey, out cachedAsm) && cachedAsm != null) + if (cachedAssemblies.TryGetValue(asmKey, out var cachedAsm) && cachedAsm is not null) return asm == cachedAsm; cachedAssemblies[asmKey] = asm; return true; @@ -304,21 +314,42 @@ public bool AddToCache(AssemblyDef asm) { #endif } - /// + /// + /// Removes a module's assembly from the cache + /// + /// The module + /// true if its assembly was removed, false if it wasn't removed + /// since it wasn't in the cache, it has no assembly, or was + /// null + public bool Remove(ModuleDef module) => module is not null && Remove(module.Assembly); + + /// + /// Removes the assembly from the cache + /// + /// The assembly + /// true if it was removed, false if it wasn't removed since it + /// wasn't in the cache or if was null public bool Remove(AssemblyDef asm) { - if (asm == null) + if (asm is null) return false; var asmKey = GetAssemblyNameKey(asm); #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif + if (asm.ManifestModule is { } module) + moduleSearchPaths.Remove(module); return cachedAssemblies.Remove(asmKey); #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif } - /// + /// + /// Clears the cache and calls on each cached module. + /// Use to remove any assemblies you added yourself + /// using before calling this method if you don't want + /// them disposed. + /// public void Clear() { List asms; #if THREAD_SAFE @@ -326,36 +357,51 @@ public void Clear() { #endif asms = new List(cachedAssemblies.Values); cachedAssemblies.Clear(); + moduleSearchPaths.Clear(); #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif foreach (var asm in asms) { - if (asm == null) + if (asm is null) continue; - foreach (var mod in asm.Modules.GetSafeEnumerable()) + foreach (var mod in asm.Modules) mod.Dispose(); } } + /// + /// Gets the cached assemblies in this resolver. + /// + /// The cached assemblies. + public IEnumerable GetCachedAssemblies() { + AssemblyDef[] assemblies; +#if THREAD_SAFE + theLock.EnterReadLock(); try { +#endif + assemblies = cachedAssemblies.Values.ToArray(); +#if THREAD_SAFE + } finally { theLock.ExitReadLock(); } +#endif + return assemblies; + } + static string GetAssemblyNameKey(IAssembly asmName) { // Make sure the name contains PublicKeyToken= and not PublicKey= - return asmName.FullNameToken.ToUpperInvariant(); + return asmName.FullNameToken; } AssemblyDef Resolve2(IAssembly assembly, ModuleDef sourceModule) { - AssemblyDef resolvedAssembly; - - if (cachedAssemblies.TryGetValue(GetAssemblyNameKey(assembly), out resolvedAssembly)) + if (cachedAssemblies.TryGetValue(GetAssemblyNameKey(assembly), out var resolvedAssembly)) return resolvedAssembly; var moduleContext = defaultModuleContext; - if (moduleContext == null && sourceModule != null) + if (moduleContext is null && sourceModule is not null) moduleContext = sourceModule.Context; resolvedAssembly = FindExactAssembly(assembly, PreFindAssemblies(assembly, sourceModule, true), moduleContext) ?? FindExactAssembly(assembly, FindAssemblies(assembly, sourceModule, true), moduleContext) ?? FindExactAssembly(assembly, PostFindAssemblies(assembly, sourceModule, true), moduleContext); - if (resolvedAssembly != null) + if (resolvedAssembly is not null) return resolvedAssembly; if (!findExactMatch) { @@ -377,15 +423,15 @@ AssemblyDef Resolve2(IAssembly assembly, ModuleDef sourceModule) { /// An instance or null if an exact match /// couldn't be found. AssemblyDef FindExactAssembly(IAssembly assembly, IEnumerable paths, ModuleContext moduleContext) { - if (paths == null) + if (paths is null) return null; var asmComparer = AssemblyNameComparer.CompareAll; - foreach (var path in paths.GetSafeEnumerable()) { + foreach (var path in paths) { ModuleDefMD mod = null; try { mod = ModuleDefMD.Load(path, moduleContext); var asm = mod.Assembly; - if (asm != null && asmComparer.Equals(assembly, asm)) { + if (asm is not null && asmComparer.Equals(assembly, asm)) { mod = null; return asm; } @@ -393,7 +439,7 @@ AssemblyDef FindExactAssembly(IAssembly assembly, IEnumerable paths, Mod catch { } finally { - if (mod != null) + if (mod is not null) mod.Dispose(); } } @@ -408,8 +454,9 @@ AssemblyDef FindExactAssembly(IAssembly assembly, IEnumerable paths, Mod AssemblyDef FindClosestAssembly(IAssembly assembly) { AssemblyDef closest = null; var asmComparer = AssemblyNameComparer.CompareAll; - foreach (var asm in cachedAssemblies.Values) { - if (asm == null) + foreach (var kv in cachedAssemblies) { + var asm = kv.Value; + if (asm is null) continue; if (asmComparer.CompareClosest(assembly, closest, asm) == 1) closest = asm; @@ -418,18 +465,18 @@ AssemblyDef FindClosestAssembly(IAssembly assembly) { } AssemblyDef FindClosestAssembly(IAssembly assembly, AssemblyDef closest, IEnumerable paths, ModuleContext moduleContext) { - if (paths == null) + if (paths is null) return closest; var asmComparer = AssemblyNameComparer.CompareAll; - foreach (var path in paths.GetSafeEnumerable()) { + foreach (var path in paths) { ModuleDefMD mod = null; try { mod = ModuleDefMD.Load(path, moduleContext); var asm = mod.Assembly; - if (asm != null && asmComparer.CompareClosest(assembly, closest, asm) == 1) { - if (!IsCached(closest) && closest != null) { + if (asm is not null && asmComparer.CompareClosest(assembly, closest, asm) == 1) { + if (!IsCached(closest) && closest is not null) { var closeMod = closest.ManifestModule; - if (closeMod != null) + if (closeMod is not null) closeMod.Dispose(); } closest = asm; @@ -439,7 +486,7 @@ AssemblyDef FindClosestAssembly(IAssembly assembly, AssemblyDef closest, IEnumer catch { } finally { - if (mod != null) + if (mod is not null) mod.Dispose(); } } @@ -452,20 +499,26 @@ AssemblyDef FindClosestAssembly(IAssembly assembly, AssemblyDef closest, IEnumer /// /// Assembly to check bool IsCached(AssemblyDef asm) { - if (asm == null) + if (asm is null) return false; - AssemblyDef cachedAsm; - return cachedAssemblies.TryGetValue(GetAssemblyNameKey(asm), out cachedAsm) && + return cachedAssemblies.TryGetValue(GetAssemblyNameKey(asm), out var cachedAsm) && cachedAsm == asm; } IEnumerable FindAssemblies2(IAssembly assembly, IEnumerable paths) { - if (paths != null) { + if (paths is not null) { var asmSimpleName = UTF8String.ToSystemStringOrEmpty(assembly.Name); var exts = assembly.IsContentTypeWindowsRuntime ? winMDAssemblyExtensions : assemblyExtensions; foreach (var ext in exts) { - foreach (var path in paths.GetSafeEnumerable()) { - var fullPath = Path.Combine(path, asmSimpleName + ext); + foreach (var path in paths) { + string fullPath; + try { + fullPath = Path.Combine(path, asmSimpleName + ext); + } + catch (ArgumentException) { + // Invalid path chars + yield break; + } if (File.Exists(fullPath)) yield return fullPath; } @@ -506,13 +559,22 @@ protected virtual IEnumerable PostFindAssemblies(IAssembly assembly, Mod /// null or an enumerable of full paths to try protected virtual IEnumerable FindAssemblies(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) { if (assembly.IsContentTypeWindowsRuntime) { - var path = Path.Combine(Path.Combine(Environment.SystemDirectory, "WinMetadata"), assembly.Name + ".winmd"); + string path; + try { + path = Path.Combine(Path.Combine(Environment.SystemDirectory, "WinMetadata"), assembly.Name + ".winmd"); + } + catch (ArgumentException) { + // Invalid path chars + path = null; + } if (File.Exists(path)) yield return path; } else { - foreach (var path in FindAssembliesGac(assembly, sourceModule, matchExactly)) - yield return path; + if (UseGAC) { + foreach (var path in FindAssembliesGac(assembly, sourceModule, matchExactly)) + yield return path; + } } foreach (var path in FindAssembliesModuleSearchPaths(assembly, sourceModule, matchExactly)) yield return path; @@ -525,14 +587,14 @@ IEnumerable FindAssembliesGac(IAssembly assembly, ModuleDef sourceModule } IEnumerable GetGacInfos(ModuleDef sourceModule) { - int version = sourceModule == null ? int.MinValue : sourceModule.IsClr40 ? 4 : 2; - // Try the correct GAC first (eg. GAC4 if it's a .NET 4 assembly) + int version = sourceModule is null ? int.MinValue : sourceModule.IsClr40 ? 4 : 2; + // Try the correct GAC first (eg. GAC4 if it's a .NET Framework 4 assembly) foreach (var gacInfo in gacInfos) { - if (gacInfo.version == version) + if (gacInfo.Version == version) yield return gacInfo; } foreach (var gacInfo in gacInfos) { - if (gacInfo.version != version) + if (gacInfo.Version != version) yield return gacInfo; } } @@ -542,16 +604,23 @@ IEnumerable FindAssembliesGacExactly(IAssembly assembly, ModuleDef sourc foreach (var path in FindAssembliesGacExactly(gacInfo, assembly, sourceModule)) yield return path; } - if (extraMonoPaths != null) { + if (extraMonoPaths is not null) { foreach (var path in GetExtraMonoPaths(assembly, sourceModule)) yield return path; } } static IEnumerable GetExtraMonoPaths(IAssembly assembly, ModuleDef sourceModule) { - if (extraMonoPaths != null) { + if (extraMonoPaths is not null) { foreach (var dir in extraMonoPaths) { - var file = Path.Combine(dir, assembly.Name + ".dll"); + string file; + try { + file = Path.Combine(dir, assembly.Name + ".dll"); + } + catch (ArgumentException) { + // Invalid path chars + break; + } if (File.Exists(file)) yield return file; } @@ -560,14 +629,23 @@ static IEnumerable GetExtraMonoPaths(IAssembly assembly, ModuleDef sourc IEnumerable FindAssembliesGacExactly(GacInfo gacInfo, IAssembly assembly, ModuleDef sourceModule) { var pkt = PublicKeyBase.ToPublicKeyToken(assembly.PublicKeyOrToken); - if (gacInfo != null && pkt != null) { + if (gacInfo is not null && pkt is not null) { string pktString = pkt.ToString(); string verString = Utils.CreateVersionWithNoUndefinedValues(assembly.Version).ToString(); + var cultureString = UTF8String.ToSystemStringOrEmpty(assembly.Culture); + if (cultureString.Equals("neutral", StringComparison.OrdinalIgnoreCase)) + cultureString = string.Empty; var asmSimpleName = UTF8String.ToSystemStringOrEmpty(assembly.Name); - foreach (var subDir in gacInfo.subDirs) { - var baseDir = Path.Combine(gacInfo.path, subDir); - baseDir = Path.Combine(baseDir, asmSimpleName); - baseDir = Path.Combine(baseDir, string.Format("{0}{1}__{2}", gacInfo.prefix, verString, pktString)); + foreach (var subDir in gacInfo.SubDirs) { + var baseDir = Path.Combine(gacInfo.Path, subDir); + try { + baseDir = Path.Combine(baseDir, asmSimpleName); + } + catch (ArgumentException) { + // Invalid path chars + break; + } + baseDir = Path.Combine(baseDir, $"{gacInfo.Prefix}{verString}_{cultureString}_{pktString}"); var pathName = Path.Combine(baseDir, asmSimpleName + ".dll"); if (File.Exists(pathName)) yield return pathName; @@ -580,18 +658,24 @@ IEnumerable FindAssembliesGacAny(IAssembly assembly, ModuleDef sourceMod foreach (var path in FindAssembliesGacAny(gacInfo, assembly, sourceModule)) yield return path; } - if (extraMonoPaths != null) { + if (extraMonoPaths is not null) { foreach (var path in GetExtraMonoPaths(assembly, sourceModule)) yield return path; } } IEnumerable FindAssembliesGacAny(GacInfo gacInfo, IAssembly assembly, ModuleDef sourceModule) { - if (gacInfo != null) { + if (gacInfo is not null) { var asmSimpleName = UTF8String.ToSystemStringOrEmpty(assembly.Name); - foreach (var subDir in gacInfo.subDirs) { - var baseDir = Path.Combine(gacInfo.path, subDir); - baseDir = Path.Combine(baseDir, asmSimpleName); + foreach (var subDir in gacInfo.SubDirs) { + var baseDir = Path.Combine(gacInfo.Path, subDir); + try { + baseDir = Path.Combine(baseDir, asmSimpleName); + } + catch (ArgumentException) { + // Invalid path chars + break; + } foreach (var dir in GetDirs(baseDir)) { var pathName = Path.Combine(dir, asmSimpleName + ".dll"); if (File.Exists(pathName)) @@ -602,6 +686,8 @@ IEnumerable FindAssembliesGacAny(GacInfo gacInfo, IAssembly assembly, Mo } IEnumerable GetDirs(string baseDir) { + if (!Directory.Exists(baseDir)) + return Array2.Empty(); var dirs = new List(); try { foreach (var di in new DirectoryInfo(baseDir).GetDirectories()) @@ -617,13 +703,19 @@ IEnumerable FindAssembliesModuleSearchPaths(IAssembly assembly, ModuleDe var searchPaths = GetSearchPaths(sourceModule); var exts = assembly.IsContentTypeWindowsRuntime ? winMDAssemblyExtensions : assemblyExtensions; foreach (var ext in exts) { - foreach (var path in searchPaths.GetSafeEnumerable()) { + foreach (var path in searchPaths) { for (int i = 0; i < 2; i++) { string path2; - if (i == 0) - path2 = Path.Combine(path, asmSimpleName + ext); - else - path2 = Path.Combine(Path.Combine(path, asmSimpleName), asmSimpleName + ext); + try { + if (i == 0) + path2 = Path.Combine(path, asmSimpleName + ext); + else + path2 = Path.Combine(Path.Combine(path, asmSimpleName), asmSimpleName + ext); + } + catch (ArgumentException) { + // Invalid path chars + yield break; + } if (File.Exists(path2)) yield return path2; } @@ -637,11 +729,10 @@ IEnumerable FindAssembliesModuleSearchPaths(IAssembly assembly, ModuleDe /// The module or null if unknown /// A list of all search paths to use for this module IEnumerable GetSearchPaths(ModuleDef module) { - ModuleDef keyModule = module; - if (keyModule == null) + var keyModule = module; + if (keyModule is null) keyModule = nullModule; - IList searchPaths; - if (moduleSearchPaths.TryGetValue(keyModule, out searchPaths)) + if (moduleSearchPaths.TryGetValue(keyModule, out var searchPaths)) return searchPaths; moduleSearchPaths[keyModule] = searchPaths = new List(GetModuleSearchPaths(module)); return searchPaths; @@ -654,9 +745,7 @@ IEnumerable GetSearchPaths(ModuleDef module) { /// /// The module or null if unknown /// A list of search paths - protected virtual IEnumerable GetModuleSearchPaths(ModuleDef module) { - return GetModulePrivateSearchPaths(module); - } + protected virtual IEnumerable GetModuleSearchPaths(ModuleDef module) => GetModulePrivateSearchPaths(module); /// /// Gets all private assembly search paths as found in the module's .config file. @@ -664,30 +753,33 @@ protected virtual IEnumerable GetModuleSearchPaths(ModuleDef module) { /// The module or null if unknown /// A list of search paths protected IEnumerable GetModulePrivateSearchPaths(ModuleDef module) { - if (module == null) - return new string[0]; + if (module is null) + return Array2.Empty(); var asm = module.Assembly; - if (asm == null) - return new string[0]; + if (asm is null) + return Array2.Empty(); module = asm.ManifestModule; - if (module == null) - return new string[0]; // Should never happen + if (module is null) + return Array2.Empty(); // Should never happen string baseDir = null; try { var imageName = module.Location; if (imageName != string.Empty) { - baseDir = Directory.GetParent(imageName).FullName; - var configName = imageName + ".config"; - if (File.Exists(configName)) - return GetPrivatePaths(baseDir, configName); + var directoryInfo = Directory.GetParent(imageName); + if (directoryInfo is not null) { + baseDir = directoryInfo.FullName; + var configName = imageName + ".config"; + if (File.Exists(configName)) + return GetPrivatePaths(baseDir, configName); + } } } catch { } - if (baseDir != null) + if (baseDir is not null) return new List { baseDir }; - return new string[0]; + return Array2.Empty(); } IEnumerable GetPrivatePaths(string baseDir, string configFileName) { @@ -702,7 +794,7 @@ IEnumerable GetPrivatePaths(string baseDir, string configFileName) { doc.Load(XmlReader.Create(xmlStream)); foreach (var tmp in doc.GetElementsByTagName("probing")) { var probingElem = tmp as XmlElement; - if (probingElem == null) + if (probingElem is null) continue; var privatePath = probingElem.GetAttribute("privatePath"); if (string.IsNullOrEmpty(privatePath)) @@ -727,143 +819,5 @@ IEnumerable GetPrivatePaths(string baseDir, string configFileName) { return searchPaths; } - - /// - /// Add other common search paths - /// - /// A list that gets updated with the new paths - protected static void AddOtherSearchPaths(IList paths) { - var dirPF = Environment.GetEnvironmentVariable("ProgramFiles"); - AddOtherAssemblySearchPaths(paths, dirPF); - var dirPFx86 = Environment.GetEnvironmentVariable("ProgramFiles(x86)"); - if (!StringComparer.OrdinalIgnoreCase.Equals(dirPF, dirPFx86)) - AddOtherAssemblySearchPaths(paths, dirPFx86); - - var windir = Environment.GetEnvironmentVariable("WINDIR"); - if (!string.IsNullOrEmpty(windir)) { - AddIfExists(paths, windir, @"Microsoft.NET\Framework\v1.1.4322"); - AddIfExists(paths, windir, @"Microsoft.NET\Framework\v1.0.3705"); - } - } - - static void AddOtherAssemblySearchPaths(IList paths, string path) { - if (string.IsNullOrEmpty(path)) - return; - AddSilverlightDirs(paths, Path.Combine(path, @"Microsoft Silverlight")); - AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v2.0\Libraries\Client"); - AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v2.0\Libraries\Server"); - AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v2.0\Reference Assemblies"); - AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v3.0\Libraries\Client"); - AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v3.0\Libraries\Server"); - AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v4.0\Libraries\Client"); - AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v4.0\Libraries\Server"); - AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v5.0\Libraries\Client"); - AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v5.0\Libraries\Server"); - AddIfExists(paths, path, @"Microsoft.NET\SDK\CompactFramework\v2.0\WindowsCE"); - AddIfExists(paths, path, @"Microsoft.NET\SDK\CompactFramework\v3.5\WindowsCE"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETCore\v4.5.1"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETCore\v4.5"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETMicroFramework\v3.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETMicroFramework\v4.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETMicroFramework\v4.1"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETMicroFramework\v4.2"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETMicroFramework\v4.3"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETPortable\v4.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETPortable\v4.6"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\v3.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\v3.5"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\Silverlight\v3.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\Silverlight\v4.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\Silverlight\v5.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\WindowsPhone\v8.1"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\WindowsPhoneApp\v8.1"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETCore\3.259.4.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETCore\3.259.3.1"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETCore\3.78.4.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETCore\3.78.3.1"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETCore\3.7.4.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETCore\3.3.1.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETFramework\v2.0\2.3.0.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.3.0.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.3.1.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.4.0.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETPortable\2.3.5.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETPortable\2.3.5.1"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETPortable\3.47.4.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\2.0\Runtime\v2.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\2.0\Runtime\v4.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\3.0\Runtime\.NETPortable"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\3.0\Runtime\v2.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\3.0\Runtime\v4.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\WindowsPowerShell\v1.0"); - AddIfExists(paths, path, @"Reference Assemblies\Microsoft\WindowsPowerShell\3.0"); - AddIfExists(paths, path, @"Microsoft Visual Studio .NET\Common7\IDE\PublicAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio .NET\Common7\IDE\PrivateAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio .NET 2003\Common7\IDE\PublicAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio .NET 2003\Common7\IDE\PrivateAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio 8\Common7\IDE\PublicAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio 11.0\Common7\IDE\PublicAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio 11.0\Common7\IDE\PrivateAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio 12.0\Common7\IDE\PublicAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio 12.0\Common7\IDE\PrivateAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio 14.0\Common7\IDE\PublicAssemblies"); - AddIfExists(paths, path, @"Microsoft Visual Studio 14.0\Common7\IDE\PrivateAssemblies"); - AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v2.0\References\Windows\x86"); - AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v2.0\References\Xbox360"); - AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v3.0\References\Windows\x86"); - AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v3.0\References\Xbox360"); - AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v3.0\References\Zune"); - AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v3.1\References\Windows\x86"); - AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v3.1\References\Xbox360"); - AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v3.1\References\Zune"); - AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86"); - AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v4.0\References\Xbox360"); - AddIfExists(paths, path, @"Windows CE Tools\wce500\Windows Mobile 5.0 Pocket PC SDK\Designtimereferences"); - AddIfExists(paths, path, @"Windows CE Tools\wce500\Windows Mobile 5.0 Smartphone SDK\Designtimereferences"); - AddIfExists(paths, path, @"Windows Mobile 5.0 SDK R2\Managed Libraries"); - AddIfExists(paths, path, @"Windows Mobile 6 SDK\Managed Libraries"); - AddIfExists(paths, path, @"Windows Mobile 6.5.3 DTK\Managed Libraries"); - AddIfExists(paths, path, @"Microsoft SQL Server\90\SDK\Assemblies"); - AddIfExists(paths, path, @"Microsoft SQL Server\100\SDK\Assemblies"); - AddIfExists(paths, path, @"Microsoft SQL Server\110\SDK\Assemblies"); - AddIfExists(paths, path, @"Microsoft SQL Server\120\SDK\Assemblies"); - AddIfExists(paths, path, @"Microsoft ASP.NET\ASP.NET MVC 2\Assemblies"); - AddIfExists(paths, path, @"Microsoft ASP.NET\ASP.NET MVC 3\Assemblies"); - AddIfExists(paths, path, @"Microsoft ASP.NET\ASP.NET MVC 4\Assemblies"); - AddIfExists(paths, path, @"Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies"); - AddIfExists(paths, path, @"Microsoft ASP.NET\ASP.NET Web Pages\v2.0\Assemblies"); - AddIfExists(paths, path, @"Microsoft SDKs\F#\3.0\Framework\v4.0"); - } - - static void AddSilverlightDirs(IList paths, string basePath) { - try { - var di = new DirectoryInfo(basePath); - foreach (var dir in di.GetDirectories()) { - if (Regex.IsMatch(dir.Name, @"^\d+(?:\.\d+){3}$")) - AddIfExists(paths, basePath, dir.Name); - } - } - catch { - } - } - - static void AddIfExists(IList paths, string basePath, string extraPath) { - var path = Path.Combine(basePath, extraPath); - if (Directory.Exists(path)) - paths.Add(path); - } } } diff --git a/src/DotNet/CallingConvention.cs b/src/DotNet/CallingConvention.cs index 1ff69a423..da2d6557c 100644 --- a/src/DotNet/CallingConvention.cs +++ b/src/DotNet/CallingConvention.cs @@ -26,7 +26,7 @@ public enum CallingConvention : byte { LocalSig = 0x7, /// Property = 0x8, - /// + /// Unmanaged calling convention encoded as modopts Unmanaged = 0x9, /// generic method instantiation GenericInst = 0xA, diff --git a/src/DotNet/CallingConventionSig.cs b/src/DotNet/CallingConventionSig.cs index 7f825f6a1..98c182d1e 100644 --- a/src/DotNet/CallingConventionSig.cs +++ b/src/DotNet/CallingConventionSig.cs @@ -1,13 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif +using System.Collections.Generic; /* All signature classes: @@ -37,99 +30,75 @@ public abstract class CallingConventionSig : IContainsGenericParameter { /// Gets/sets the extra data found after the signature /// public byte[] ExtraData { - get { return extraData; } - set { extraData = value; } + get => extraData; + set => extraData = value; } /// /// Returns true if is set /// - public bool IsDefault { - get { return (callingConvention & CallingConvention.Mask) == CallingConvention.Default; } - } + public bool IsDefault => (callingConvention & CallingConvention.Mask) == CallingConvention.Default; /// /// Returns true if is set /// - public bool IsC { - get { return (callingConvention & CallingConvention.Mask) == CallingConvention.C; } - } + public bool IsC => (callingConvention & CallingConvention.Mask) == CallingConvention.C; /// /// Returns true if is set /// - public bool IsStdCall { - get { return (callingConvention & CallingConvention.Mask) == CallingConvention.StdCall; } - } + public bool IsStdCall => (callingConvention & CallingConvention.Mask) == CallingConvention.StdCall; /// /// Returns true if is set /// - public bool IsThisCall { - get { return (callingConvention & CallingConvention.Mask) == CallingConvention.ThisCall; } - } + public bool IsThisCall => (callingConvention & CallingConvention.Mask) == CallingConvention.ThisCall; /// /// Returns true if is set /// - public bool IsFastCall { - get { return (callingConvention & CallingConvention.Mask) == CallingConvention.FastCall; } - } + public bool IsFastCall => (callingConvention & CallingConvention.Mask) == CallingConvention.FastCall; /// /// Returns true if is set /// - public bool IsVarArg { - get { return (callingConvention & CallingConvention.Mask) == CallingConvention.VarArg; } - } + public bool IsVarArg => (callingConvention & CallingConvention.Mask) == CallingConvention.VarArg; /// /// Returns true if is set /// - public bool IsField { - get { return (callingConvention & CallingConvention.Mask) == CallingConvention.Field; } - } + public bool IsField => (callingConvention & CallingConvention.Mask) == CallingConvention.Field; /// /// Returns true if is set /// - public bool IsLocalSig { - get { return (callingConvention & CallingConvention.Mask) == CallingConvention.LocalSig; } - } + public bool IsLocalSig => (callingConvention & CallingConvention.Mask) == CallingConvention.LocalSig; /// /// Returns true if is set /// - public bool IsProperty { - get { return (callingConvention & CallingConvention.Mask) == CallingConvention.Property; } - } + public bool IsProperty => (callingConvention & CallingConvention.Mask) == CallingConvention.Property; /// /// Returns true if is set /// - public bool IsUnmanaged { - get { return (callingConvention & CallingConvention.Mask) == CallingConvention.Unmanaged; } - } + public bool IsUnmanaged => (callingConvention & CallingConvention.Mask) == CallingConvention.Unmanaged; /// /// Returns true if is set /// - public bool IsGenericInst { - get { return (callingConvention & CallingConvention.Mask) == CallingConvention.GenericInst; } - } + public bool IsGenericInst => (callingConvention & CallingConvention.Mask) == CallingConvention.GenericInst; /// /// Returns true if is set /// - public bool IsNativeVarArg { - get { return (callingConvention & CallingConvention.Mask) == CallingConvention.NativeVarArg; } - } + public bool IsNativeVarArg => (callingConvention & CallingConvention.Mask) == CallingConvention.NativeVarArg; /// /// Gets/sets the bit /// public bool Generic { - get { return (callingConvention & CallingConvention.Generic) != 0; } + get => (callingConvention & CallingConvention.Generic) != 0; set { if (value) callingConvention |= CallingConvention.Generic; @@ -142,7 +111,7 @@ public bool Generic { /// Gets/sets the bit /// public bool HasThis { - get { return (callingConvention & CallingConvention.HasThis) != 0; } + get => (callingConvention & CallingConvention.HasThis) != 0; set { if (value) callingConvention |= CallingConvention.HasThis; @@ -155,7 +124,7 @@ public bool HasThis { /// Gets/sets the bit /// public bool ExplicitThis { - get { return (callingConvention & CallingConvention.ExplicitThis) != 0; } + get => (callingConvention & CallingConvention.ExplicitThis) != 0; set { if (value) callingConvention |= CallingConvention.ExplicitThis; @@ -168,7 +137,7 @@ public bool ExplicitThis { /// Gets/sets the bit /// public bool ReservedByCLR { - get { return (callingConvention & CallingConvention.ReservedByCLR) != 0; } + get => (callingConvention & CallingConvention.ReservedByCLR) != 0; set { if (value) callingConvention |= CallingConvention.ReservedByCLR; @@ -180,17 +149,13 @@ public bool ReservedByCLR { /// /// true if there's an implicit this parameter /// - public bool ImplicitThis { - get { return HasThis && !ExplicitThis; } - } + public bool ImplicitThis => HasThis && !ExplicitThis; /// /// true if this contains a /// or a . /// - public bool ContainsGenericParameter { - get { return TypeHelper.ContainsGenericParameter(this); } - } + public bool ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); /// /// Default constructor @@ -202,16 +167,12 @@ protected CallingConventionSig() { /// Constructor /// /// The calling convention - protected CallingConventionSig(CallingConvention callingConvention) { - this.callingConvention = callingConvention; - } + protected CallingConventionSig(CallingConvention callingConvention) => this.callingConvention = callingConvention; /// /// Gets the calling convention /// - public CallingConvention GetCallingConvention() { - return callingConvention; - } + public CallingConvention GetCallingConvention() => callingConvention; } /// @@ -224,23 +185,21 @@ public sealed class FieldSig : CallingConventionSig { /// Gets/sets the field type /// public TypeSig Type { - get { return type; } - set { type = value; } + get => type; + set => type = value; } /// /// Default constructor /// - public FieldSig() { - this.callingConvention = CallingConvention.Field; - } + public FieldSig() => callingConvention = CallingConvention.Field; /// /// Constructor /// /// Field type public FieldSig(TypeSig type) { - this.callingConvention = CallingConvention.Field; + callingConvention = CallingConvention.Field; this.type = type; } @@ -257,14 +216,10 @@ internal FieldSig(CallingConvention callingConvention, TypeSig type) { /// /// Clone this /// - public FieldSig Clone() { - return new FieldSig(callingConvention, type); - } + public FieldSig Clone() => new FieldSig(callingConvention, type); /// - public override string ToString() { - return FullNameCreator.FullName(type == null ? null : type, false); - } + public override string ToString() => FullNameFactory.FullName(type, false, null, null, null, null); } /// @@ -274,50 +229,48 @@ public abstract class MethodBaseSig : CallingConventionSig { /// protected TypeSig retType; /// - protected ThreadSafe.IList parameters; + protected IList parameters; /// protected uint genParamCount; /// - protected ThreadSafe.IList paramsAfterSentinel; + protected IList paramsAfterSentinel; /// /// Gets/sets the calling convention /// public CallingConvention CallingConvention { - get { return callingConvention; } - set { callingConvention = value; } + get => callingConvention; + set => callingConvention = value; } /// /// Gets/sets the return type /// public TypeSig RetType { - get { return retType; } - set { retType = value; } + get => retType; + set => retType = value; } /// /// Gets the parameters. This is never null /// - public ThreadSafe.IList Params { - get { return parameters; } - } + public IList Params => parameters; /// /// Gets/sets the generic param count /// public uint GenParamCount { - get { return genParamCount; } - set { genParamCount = value; } + get => genParamCount; + set => genParamCount = value; } /// /// Gets the parameters that are present after the sentinel. Note that this is null /// if there's no sentinel. It can still be empty even if it's not null. /// - public ThreadSafe.IList ParamsAfterSentinel { - get { return paramsAfterSentinel; } - set { paramsAfterSentinel = value; } + public IList ParamsAfterSentinel { + get => paramsAfterSentinel; + set => paramsAfterSentinel = value; } } @@ -332,26 +285,22 @@ public sealed class MethodSig : MethodBaseSig { /// and it's a hint to the module writer if it tries to re-use the same token. /// public uint OriginalToken { - get { return origToken; } - set { origToken = value; } + get => origToken; + set => origToken = value; } /// /// Creates a static MethodSig /// /// Return type - public static MethodSig CreateStatic(TypeSig retType) { - return new MethodSig(CallingConvention.Default, 0, retType); - } + public static MethodSig CreateStatic(TypeSig retType) => new MethodSig(CallingConvention.Default, 0, retType); /// /// Creates a static MethodSig /// /// Return type /// Arg type #1 - public static MethodSig CreateStatic(TypeSig retType, TypeSig argType1) { - return new MethodSig(CallingConvention.Default, 0, retType, argType1); - } + public static MethodSig CreateStatic(TypeSig retType, TypeSig argType1) => new MethodSig(CallingConvention.Default, 0, retType, argType1); /// /// Creates a static MethodSig @@ -359,9 +308,7 @@ public static MethodSig CreateStatic(TypeSig retType, TypeSig argType1) { /// Return type /// Arg type #1 /// Arg type #2 - public static MethodSig CreateStatic(TypeSig retType, TypeSig argType1, TypeSig argType2) { - return new MethodSig(CallingConvention.Default, 0, retType, argType1, argType2); - } + public static MethodSig CreateStatic(TypeSig retType, TypeSig argType1, TypeSig argType2) => new MethodSig(CallingConvention.Default, 0, retType, argType1, argType2); /// /// Creates a static MethodSig @@ -370,35 +317,27 @@ public static MethodSig CreateStatic(TypeSig retType, TypeSig argType1, TypeSig /// Arg type #1 /// Arg type #2 /// Arg type #3 - public static MethodSig CreateStatic(TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) { - return new MethodSig(CallingConvention.Default, 0, retType, argType1, argType2, argType3); - } + public static MethodSig CreateStatic(TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) => new MethodSig(CallingConvention.Default, 0, retType, argType1, argType2, argType3); /// /// Creates a static MethodSig /// /// Return type /// Argument types - public static MethodSig CreateStatic(TypeSig retType, params TypeSig[] argTypes) { - return new MethodSig(CallingConvention.Default, 0, retType, argTypes); - } + public static MethodSig CreateStatic(TypeSig retType, params TypeSig[] argTypes) => new MethodSig(CallingConvention.Default, 0, retType, argTypes); /// /// Creates an instance MethodSig /// /// Return type - public static MethodSig CreateInstance(TypeSig retType) { - return new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType); - } + public static MethodSig CreateInstance(TypeSig retType) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType); /// /// Creates an instance MethodSig /// /// Return type /// Arg type #1 - public static MethodSig CreateInstance(TypeSig retType, TypeSig argType1) { - return new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType, argType1); - } + public static MethodSig CreateInstance(TypeSig retType, TypeSig argType1) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType, argType1); /// /// Creates an instance MethodSig @@ -406,9 +345,7 @@ public static MethodSig CreateInstance(TypeSig retType, TypeSig argType1) { /// Return type /// Arg type #1 /// Arg type #2 - public static MethodSig CreateInstance(TypeSig retType, TypeSig argType1, TypeSig argType2) { - return new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType, argType1, argType2); - } + public static MethodSig CreateInstance(TypeSig retType, TypeSig argType1, TypeSig argType2) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType, argType1, argType2); /// /// Creates an instance MethodSig @@ -417,27 +354,21 @@ public static MethodSig CreateInstance(TypeSig retType, TypeSig argType1, TypeSi /// Arg type #1 /// Arg type #2 /// Arg type #3 - public static MethodSig CreateInstance(TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) { - return new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType, argType1, argType2, argType3); - } + public static MethodSig CreateInstance(TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType, argType1, argType2, argType3); /// /// Creates an instance MethodSig /// /// Return type /// Argument types - public static MethodSig CreateInstance(TypeSig retType, params TypeSig[] argTypes) { - return new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType, argTypes); - } + public static MethodSig CreateInstance(TypeSig retType, params TypeSig[] argTypes) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType, argTypes); /// /// Creates a static generic MethodSig /// /// Number of generic parameters /// Return type - public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType) { - return new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType); - } + public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType) => new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType); /// /// Creates a static generic MethodSig @@ -445,9 +376,7 @@ public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType) /// Number of generic parameters /// Return type /// Arg type #1 - public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, TypeSig argType1) { - return new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType, argType1); - } + public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, TypeSig argType1) => new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType, argType1); /// /// Creates a static generic MethodSig @@ -456,9 +385,7 @@ public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, /// Return type /// Arg type #1 /// Arg type #2 - public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2) { - return new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType, argType1, argType2); - } + public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2) => new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType, argType1, argType2); /// /// Creates a static generic MethodSig @@ -468,9 +395,7 @@ public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, /// Arg type #1 /// Arg type #2 /// Arg type #3 - public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) { - return new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType, argType1, argType2, argType3); - } + public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) => new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType, argType1, argType2, argType3); /// /// Creates a static generic MethodSig @@ -478,18 +403,14 @@ public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, /// Number of generic parameters /// Return type /// Argument types - public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, params TypeSig[] argTypes) { - return new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType, argTypes); - } + public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, params TypeSig[] argTypes) => new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType, argTypes); /// /// Creates an instance generic MethodSig /// /// Number of generic parameters /// Return type - public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType) { - return new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType); - } + public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType); /// /// Creates an instance generic MethodSig @@ -497,9 +418,7 @@ public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retTyp /// Number of generic parameters /// Return type /// Arg type #1 - public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType, TypeSig argType1) { - return new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType, argType1); - } + public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType, TypeSig argType1) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType, argType1); /// /// Creates an instance generic MethodSig @@ -508,9 +427,7 @@ public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retTyp /// Return type /// Arg type #1 /// Arg type #2 - public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2) { - return new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType, argType1, argType2); - } + public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType, argType1, argType2); /// /// Creates an instance generic MethodSig @@ -520,9 +437,7 @@ public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retTyp /// Arg type #1 /// Arg type #2 /// Arg type #3 - public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) { - return new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType, argType1, argType2, argType3); - } + public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType, argType1, argType2, argType3); /// /// Creates an instance generic MethodSig @@ -530,16 +445,12 @@ public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retTyp /// Number of generic parameters /// Return type /// Argument types - public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType, params TypeSig[] argTypes) { - return new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType, argTypes); - } + public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType, params TypeSig[] argTypes) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType, argTypes); /// /// Default constructor /// - public MethodSig() { - this.parameters = ThreadSafeListCreator.Create(); - } + public MethodSig() => parameters = new List(); /// /// Constructor @@ -547,7 +458,7 @@ public MethodSig() { /// Calling convention public MethodSig(CallingConvention callingConvention) { this.callingConvention = callingConvention; - this.parameters = ThreadSafeListCreator.Create(); + parameters = new List(); } /// @@ -558,7 +469,7 @@ public MethodSig(CallingConvention callingConvention) { public MethodSig(CallingConvention callingConvention, uint genParamCount) { this.callingConvention = callingConvention; this.genParamCount = genParamCount; - this.parameters = ThreadSafeListCreator.Create(); + parameters = new List(); } /// @@ -571,7 +482,7 @@ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSi this.callingConvention = callingConvention; this.genParamCount = genParamCount; this.retType = retType; - this.parameters = ThreadSafeListCreator.Create(); + parameters = new List(); } /// @@ -585,7 +496,7 @@ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSi this.callingConvention = callingConvention; this.genParamCount = genParamCount; this.retType = retType; - this.parameters = ThreadSafeListCreator.Create(argType1); + parameters = new List { argType1 }; } /// @@ -600,7 +511,7 @@ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSi this.callingConvention = callingConvention; this.genParamCount = genParamCount; this.retType = retType; - this.parameters = ThreadSafeListCreator.Create(argType1, argType2); + parameters = new List { argType1, argType2 }; } /// @@ -616,7 +527,7 @@ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSi this.callingConvention = callingConvention; this.genParamCount = genParamCount; this.retType = retType; - this.parameters = ThreadSafeListCreator.Create(argType1, argType2, argType3); + parameters = new List { argType1, argType2, argType3 }; } /// @@ -630,7 +541,7 @@ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSi this.callingConvention = callingConvention; this.genParamCount = genParamCount; this.retType = retType; - this.parameters = ThreadSafeListCreator.Create(argTypes); + parameters = new List(argTypes); } /// @@ -644,7 +555,7 @@ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSi this.callingConvention = callingConvention; this.genParamCount = genParamCount; this.retType = retType; - this.parameters = ThreadSafeListCreator.Create(argTypes); + parameters = new List(argTypes); } /// @@ -659,21 +570,17 @@ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSi this.callingConvention = callingConvention; this.genParamCount = genParamCount; this.retType = retType; - this.parameters = ThreadSafeListCreator.Create(argTypes); - this.paramsAfterSentinel = paramsAfterSentinel == null ? null : ThreadSafeListCreator.Create(paramsAfterSentinel); + parameters = new List(argTypes); + this.paramsAfterSentinel = paramsAfterSentinel is null ? null : new List(paramsAfterSentinel); } /// /// Clone this /// - public MethodSig Clone() { - return new MethodSig(callingConvention, genParamCount, retType, parameters, paramsAfterSentinel); - } + public MethodSig Clone() => new MethodSig(callingConvention, genParamCount, retType, parameters, paramsAfterSentinel); /// - public override string ToString() { - return FullNameCreator.MethodSigFullName(this); - } + public override string ToString() => FullNameFactory.MethodBaseSigFullName(this, null); } /// @@ -684,18 +591,14 @@ public sealed class PropertySig : MethodBaseSig { /// Creates a static PropertySig /// /// Return type - public static PropertySig CreateStatic(TypeSig retType) { - return new PropertySig(false, retType); - } + public static PropertySig CreateStatic(TypeSig retType) => new PropertySig(false, retType); /// /// Creates a static PropertySig /// /// Return type /// Arg type #1 - public static PropertySig CreateStatic(TypeSig retType, TypeSig argType1) { - return new PropertySig(false, retType, argType1); - } + public static PropertySig CreateStatic(TypeSig retType, TypeSig argType1) => new PropertySig(false, retType, argType1); /// /// Creates a static PropertySig @@ -703,9 +606,7 @@ public static PropertySig CreateStatic(TypeSig retType, TypeSig argType1) { /// Return type /// Arg type #1 /// Arg type #2 - public static PropertySig CreateStatic(TypeSig retType, TypeSig argType1, TypeSig argType2) { - return new PropertySig(false, retType, argType1, argType2); - } + public static PropertySig CreateStatic(TypeSig retType, TypeSig argType1, TypeSig argType2) => new PropertySig(false, retType, argType1, argType2); /// /// Creates a static PropertySig @@ -714,35 +615,27 @@ public static PropertySig CreateStatic(TypeSig retType, TypeSig argType1, TypeSi /// Arg type #1 /// Arg type #2 /// Arg type #3 - public static PropertySig CreateStatic(TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) { - return new PropertySig(false, retType, argType1, argType2, argType3); - } + public static PropertySig CreateStatic(TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) => new PropertySig(false, retType, argType1, argType2, argType3); /// /// Creates a static PropertySig /// /// Return type /// Argument types - public static PropertySig CreateStatic(TypeSig retType, params TypeSig[] argTypes) { - return new PropertySig(false, retType, argTypes); - } + public static PropertySig CreateStatic(TypeSig retType, params TypeSig[] argTypes) => new PropertySig(false, retType, argTypes); /// /// Creates an instance PropertySig /// /// Return type - public static PropertySig CreateInstance(TypeSig retType) { - return new PropertySig(true, retType); - } + public static PropertySig CreateInstance(TypeSig retType) => new PropertySig(true, retType); /// /// Creates an instance PropertySig /// /// Return type /// Arg type #1 - public static PropertySig CreateInstance(TypeSig retType, TypeSig argType1) { - return new PropertySig(true, retType, argType1); - } + public static PropertySig CreateInstance(TypeSig retType, TypeSig argType1) => new PropertySig(true, retType, argType1); /// /// Creates an instance PropertySig @@ -750,9 +643,7 @@ public static PropertySig CreateInstance(TypeSig retType, TypeSig argType1) { /// Return type /// Arg type #1 /// Arg type #2 - public static PropertySig CreateInstance(TypeSig retType, TypeSig argType1, TypeSig argType2) { - return new PropertySig(true, retType, argType1, argType2); - } + public static PropertySig CreateInstance(TypeSig retType, TypeSig argType1, TypeSig argType2) => new PropertySig(true, retType, argType1, argType2); /// /// Creates an instance PropertySig @@ -761,25 +652,21 @@ public static PropertySig CreateInstance(TypeSig retType, TypeSig argType1, Type /// Arg type #1 /// Arg type #2 /// Arg type #3 - public static PropertySig CreateInstance(TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) { - return new PropertySig(true, retType, argType1, argType2, argType3); - } + public static PropertySig CreateInstance(TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) => new PropertySig(true, retType, argType1, argType2, argType3); /// /// Creates an instance PropertySig /// /// Return type /// Argument types - public static PropertySig CreateInstance(TypeSig retType, params TypeSig[] argTypes) { - return new PropertySig(true, retType, argTypes); - } + public static PropertySig CreateInstance(TypeSig retType, params TypeSig[] argTypes) => new PropertySig(true, retType, argTypes); /// /// Default constructor /// public PropertySig() { - this.callingConvention = CallingConvention.Property; - this.parameters = ThreadSafeListCreator.Create(); + callingConvention = CallingConvention.Property; + parameters = new List(); } /// @@ -788,7 +675,7 @@ public PropertySig() { /// Calling convention (must have Property set) internal PropertySig(CallingConvention callingConvention) { this.callingConvention = callingConvention; - this.parameters = ThreadSafeListCreator.Create(); + parameters = new List(); } /// @@ -796,8 +683,8 @@ internal PropertySig(CallingConvention callingConvention) { /// /// true if instance, false if static public PropertySig(bool hasThis) { - this.callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0); - this.parameters = ThreadSafeListCreator.Create(); + callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0); + parameters = new List(); } /// @@ -806,9 +693,9 @@ public PropertySig(bool hasThis) { /// true if instance, false if static /// Return type public PropertySig(bool hasThis, TypeSig retType) { - this.callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0); + callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0); this.retType = retType; - this.parameters = ThreadSafeListCreator.Create(); + parameters = new List(); } /// @@ -818,9 +705,9 @@ public PropertySig(bool hasThis, TypeSig retType) { /// Return type /// Arg type #1 public PropertySig(bool hasThis, TypeSig retType, TypeSig argType1) { - this.callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0); + callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0); this.retType = retType; - this.parameters = ThreadSafeListCreator.Create(argType1); + parameters = new List { argType1 }; } /// @@ -831,9 +718,9 @@ public PropertySig(bool hasThis, TypeSig retType, TypeSig argType1) { /// Arg type #1 /// Arg type #2 public PropertySig(bool hasThis, TypeSig retType, TypeSig argType1, TypeSig argType2) { - this.callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0); + callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0); this.retType = retType; - this.parameters = ThreadSafeListCreator.Create(argType1, argType2); + parameters = new List { argType1, argType2 }; } /// @@ -845,9 +732,9 @@ public PropertySig(bool hasThis, TypeSig retType, TypeSig argType1, TypeSig argT /// Arg type #2 /// Arg type #3 public PropertySig(bool hasThis, TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) { - this.callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0); + callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0); this.retType = retType; - this.parameters = ThreadSafeListCreator.Create(argType1, argType2, argType3); + parameters = new List { argType1, argType2, argType3 }; } /// @@ -857,9 +744,9 @@ public PropertySig(bool hasThis, TypeSig retType, TypeSig argType1, TypeSig argT /// Return type /// Argument types public PropertySig(bool hasThis, TypeSig retType, params TypeSig[] argTypes) { - this.callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0); + callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0); this.retType = retType; - this.parameters = ThreadSafeListCreator.Create(argTypes); + parameters = new List(argTypes); } /// @@ -874,42 +761,36 @@ internal PropertySig(CallingConvention callingConvention, uint genParamCount, Ty this.callingConvention = callingConvention; this.genParamCount = genParamCount; this.retType = retType; - this.parameters = ThreadSafeListCreator.Create(argTypes); - this.paramsAfterSentinel = paramsAfterSentinel == null ? null : ThreadSafeListCreator.Create(paramsAfterSentinel); + parameters = new List(argTypes); + this.paramsAfterSentinel = paramsAfterSentinel is null ? null : new List(paramsAfterSentinel); } /// /// Clone this /// - public PropertySig Clone() { - return new PropertySig(callingConvention, genParamCount, retType, parameters, paramsAfterSentinel); - } + public PropertySig Clone() => new PropertySig(callingConvention, genParamCount, retType, parameters, paramsAfterSentinel); /// - public override string ToString() { - return FullNameCreator.PropertySigFullName(this); - } + public override string ToString() => FullNameFactory.MethodBaseSigFullName(this, null); } /// /// A local variables signature /// public sealed class LocalSig : CallingConventionSig { - readonly ThreadSafe.IList locals; + readonly IList locals; /// /// All local types. This is never null. /// - public ThreadSafe.IList Locals { - get { return locals; } - } + public IList Locals => locals; /// /// Default constructor /// public LocalSig() { - this.callingConvention = CallingConvention.LocalSig; - this.locals = ThreadSafeListCreator.Create(); + callingConvention = CallingConvention.LocalSig; + locals = new List(); } /// @@ -919,7 +800,7 @@ public LocalSig() { /// Number of locals internal LocalSig(CallingConvention callingConvention, uint count) { this.callingConvention = callingConvention; - this.locals = ThreadSafeListCreator.Create((int)count); + locals = new List((int)count); } /// @@ -927,8 +808,8 @@ internal LocalSig(CallingConvention callingConvention, uint count) { /// /// Local type #1 public LocalSig(TypeSig local1) { - this.callingConvention = CallingConvention.LocalSig; - this.locals = ThreadSafeListCreator.Create(local1); + callingConvention = CallingConvention.LocalSig; + locals = new List { local1 }; } /// @@ -937,8 +818,8 @@ public LocalSig(TypeSig local1) { /// Local type #1 /// Local type #2 public LocalSig(TypeSig local1, TypeSig local2) { - this.callingConvention = CallingConvention.LocalSig; - this.locals = ThreadSafeListCreator.Create(local1, local2); + callingConvention = CallingConvention.LocalSig; + locals = new List { local1, local2 }; } /// @@ -948,8 +829,8 @@ public LocalSig(TypeSig local1, TypeSig local2) { /// Local type #2 /// Local type #3 public LocalSig(TypeSig local1, TypeSig local2, TypeSig local3) { - this.callingConvention = CallingConvention.LocalSig; - this.locals = ThreadSafeListCreator.Create(local1, local2, local3); + callingConvention = CallingConvention.LocalSig; + locals = new List { local1, local2, local3 }; } /// @@ -957,8 +838,8 @@ public LocalSig(TypeSig local1, TypeSig local2, TypeSig local3) { /// /// All locals public LocalSig(params TypeSig[] locals) { - this.callingConvention = CallingConvention.LocalSig; - this.locals = ThreadSafeListCreator.Create(locals); + callingConvention = CallingConvention.LocalSig; + this.locals = new List(locals); } /// @@ -966,8 +847,8 @@ public LocalSig(params TypeSig[] locals) { /// /// All locals public LocalSig(IList locals) { - this.callingConvention = CallingConvention.LocalSig; - this.locals = ThreadSafeListCreator.Create(locals); + callingConvention = CallingConvention.LocalSig; + this.locals = new List(locals); } /// @@ -976,37 +857,33 @@ public LocalSig(IList locals) { /// All locals (this instance now owns it) /// Dummy internal LocalSig(IList locals, bool dummy) { - this.callingConvention = CallingConvention.LocalSig; - this.locals = ThreadSafeListCreator.MakeThreadSafe(locals); + callingConvention = CallingConvention.LocalSig; + this.locals = locals; } /// /// Clone this /// - public LocalSig Clone() { - return new LocalSig(locals); - } + public LocalSig Clone() => new LocalSig(locals); } /// /// An instantiated generic method signature /// public sealed class GenericInstMethodSig : CallingConventionSig { - readonly ThreadSafe.IList genericArgs; + readonly IList genericArgs; /// /// Gets the generic arguments (must be instantiated types, i.e., closed types) /// - public ThreadSafe.IList GenericArguments { - get { return genericArgs; } - } + public IList GenericArguments => genericArgs; /// /// Default constructor /// public GenericInstMethodSig() { - this.callingConvention = CallingConvention.GenericInst; - this.genericArgs = ThreadSafeListCreator.Create(); + callingConvention = CallingConvention.GenericInst; + genericArgs = new List(); } /// @@ -1016,7 +893,7 @@ public GenericInstMethodSig() { /// Number of generic args internal GenericInstMethodSig(CallingConvention callingConvention, uint size) { this.callingConvention = callingConvention; - this.genericArgs = ThreadSafeListCreator.Create((int)size); + genericArgs = new List((int)size); } /// @@ -1024,8 +901,8 @@ internal GenericInstMethodSig(CallingConvention callingConvention, uint size) { /// /// Generic arg #1 public GenericInstMethodSig(TypeSig arg1) { - this.callingConvention = CallingConvention.GenericInst; - this.genericArgs = ThreadSafeListCreator.Create(arg1); + callingConvention = CallingConvention.GenericInst; + genericArgs = new List { arg1 }; } /// @@ -1034,8 +911,8 @@ public GenericInstMethodSig(TypeSig arg1) { /// Generic arg #1 /// Generic arg #2 public GenericInstMethodSig(TypeSig arg1, TypeSig arg2) { - this.callingConvention = CallingConvention.GenericInst; - this.genericArgs = ThreadSafeListCreator.Create(arg1, arg2); + callingConvention = CallingConvention.GenericInst; + genericArgs = new List { arg1, arg2 }; } /// @@ -1045,8 +922,8 @@ public GenericInstMethodSig(TypeSig arg1, TypeSig arg2) { /// Generic arg #2 /// Generic arg #3 public GenericInstMethodSig(TypeSig arg1, TypeSig arg2, TypeSig arg3) { - this.callingConvention = CallingConvention.GenericInst; - this.genericArgs = ThreadSafeListCreator.Create(arg1, arg2, arg3); + callingConvention = CallingConvention.GenericInst; + genericArgs = new List { arg1, arg2, arg3 }; } /// @@ -1054,8 +931,8 @@ public GenericInstMethodSig(TypeSig arg1, TypeSig arg2, TypeSig arg3) { /// /// Generic args public GenericInstMethodSig(params TypeSig[] args) { - this.callingConvention = CallingConvention.GenericInst; - this.genericArgs = ThreadSafeListCreator.Create(args); + callingConvention = CallingConvention.GenericInst; + genericArgs = new List(args); } /// @@ -1063,16 +940,14 @@ public GenericInstMethodSig(params TypeSig[] args) { /// /// Generic args public GenericInstMethodSig(IList args) { - this.callingConvention = CallingConvention.GenericInst; - this.genericArgs = ThreadSafeListCreator.Create(args); + callingConvention = CallingConvention.GenericInst; + genericArgs = new List(args); } /// /// Clone this /// - public GenericInstMethodSig Clone() { - return new GenericInstMethodSig(genericArgs); - } + public GenericInstMethodSig Clone() => new GenericInstMethodSig(genericArgs); } public static partial class Extensions { @@ -1081,72 +956,56 @@ public static partial class Extensions { /// /// this /// Field type or null if none - public static TypeSig GetFieldType(this FieldSig sig) { - return sig == null ? null : sig.Type; - } + public static TypeSig GetFieldType(this FieldSig sig) => sig?.Type; /// /// Gets the return type /// /// this /// Return type or null if none - public static TypeSig GetRetType(this MethodBaseSig sig) { - return sig == null ? null : sig.RetType; - } + public static TypeSig GetRetType(this MethodBaseSig sig) => sig?.RetType; /// /// Gets the parameters /// /// this /// The parameters - public static IList GetParams(this MethodBaseSig sig) { - return sig == null ? ThreadSafeListCreator.Create() : sig.Params; - } + public static IList GetParams(this MethodBaseSig sig) => sig?.Params ?? new List(); /// /// Gets the parameter count /// /// this /// Parameter count - public static int GetParamCount(this MethodBaseSig sig) { - return sig == null ? 0 : sig.Params.Count; - } + public static int GetParamCount(this MethodBaseSig sig) => sig?.Params.Count ?? 0; /// /// Gets the generic parameter count /// /// this /// Generic parameter count - public static uint GetGenParamCount(this MethodBaseSig sig) { - return sig == null ? 0 : sig.GenParamCount; - } + public static uint GetGenParamCount(this MethodBaseSig sig) => sig?.GenParamCount ?? 0; /// /// Gets the parameters after the sentinel /// /// this /// Parameters after sentinel or null if none - public static IList GetParamsAfterSentinel(this MethodBaseSig sig) { - return sig == null ? null : sig.ParamsAfterSentinel; - } + public static IList GetParamsAfterSentinel(this MethodBaseSig sig) => sig?.ParamsAfterSentinel; /// /// Gets the locals /// /// this /// All locals - public static IList GetLocals(this LocalSig sig) { - return sig == null ? ThreadSafeListCreator.Create() : sig.Locals; - } + public static IList GetLocals(this LocalSig sig) => sig?.Locals ?? new List(); /// /// Gets the generic arguments /// /// this /// All generic arguments - public static IList GetGenericArguments(this GenericInstMethodSig sig) { - return sig == null ? ThreadSafeListCreator.Create() : sig.GenericArguments; - } + public static IList GetGenericArguments(this GenericInstMethodSig sig) => sig?.GenericArguments ?? new List(); /// /// Gets the property @@ -1154,8 +1013,6 @@ public static IList GetGenericArguments(this GenericInstMethodSig sig) /// this /// The type's property or /// false if input isnull - public static bool GetIsDefault(this CallingConventionSig sig) { - return sig == null ? false : sig.IsDefault; - } + public static bool GetIsDefault(this CallingConventionSig sig) => sig?.IsDefault ?? false; } } diff --git a/src/DotNet/ClassLayout.cs b/src/DotNet/ClassLayout.cs index 8c93448c0..e11c69832 100644 --- a/src/DotNet/ClassLayout.cs +++ b/src/DotNet/ClassLayout.cs @@ -1,6 +1,7 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Diagnostics; using dnlib.DotNet.MD; namespace dnlib.DotNet { @@ -14,22 +15,20 @@ public abstract class ClassLayout : IMDTokenProvider { protected uint rid; /// - public MDToken MDToken { - get { return new MDToken(Table.ClassLayout, rid); } - } + public MDToken MDToken => new MDToken(Table.ClassLayout, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// /// From column ClassLayout.PackingSize /// public ushort PackingSize { - get { return packingSize; } - set { packingSize = value; } + get => packingSize; + set => packingSize = value; } /// protected ushort packingSize; @@ -38,8 +37,8 @@ public ushort PackingSize { /// From column ClassLayout.ClassSize /// public uint ClassSize { - get { return classSize; } - set { classSize = value; } + get => classSize; + set => classSize = value; } /// protected uint classSize; @@ -70,15 +69,10 @@ public ClassLayoutUser(ushort packingSize, uint classSize) { /// Created from a row in the ClassLayout table /// sealed class ClassLayoutMD : ClassLayout, IMDTokenProviderMD { - /// The module where this instance is located - readonly ModuleDefMD readerModule; - readonly uint origRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// /// Constructor @@ -89,15 +83,17 @@ public uint OrigRid { /// If is invalid public ClassLayoutMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.ClassLayoutTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("ClassLayout rid {0} does not exist", rid)); + throw new BadImageFormatException($"ClassLayout rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; - this.readerModule = readerModule; - this.classSize = readerModule.TablesStream.ReadClassLayoutRow(origRid, out this.packingSize); + bool b = readerModule.TablesStream.TryReadClassLayoutRow(origRid, out var row); + Debug.Assert(b); + classSize = row.ClassSize; + packingSize = row.PackingSize; } } } diff --git a/src/DotNet/Constant.cs b/src/DotNet/Constant.cs index b9e28e4e2..9d100067d 100644 --- a/src/DotNet/Constant.cs +++ b/src/DotNet/Constant.cs @@ -1,8 +1,9 @@ // dnlib: See LICENSE.txt for more info using System; -using System.Text; +using System.Diagnostics; using dnlib.DotNet.MD; +using dnlib.IO; namespace dnlib.DotNet { /// @@ -15,22 +16,20 @@ public abstract class Constant : IMDTokenProvider { protected uint rid; /// - public MDToken MDToken { - get { return new MDToken(Table.Constant, rid); } - } + public MDToken MDToken => new MDToken(Table.Constant, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// /// From column Constant.Type /// public ElementType Type { - get { return type; } - set { type = value; } + get => type; + set => type = value; } /// protected ElementType type; @@ -39,8 +38,8 @@ public ElementType Type { /// From column Constant.Value /// public object Value { - get { return value; } - set { this.value = value; } + get => value; + set => this.value = value; } /// protected object value; @@ -61,7 +60,7 @@ public ConstantUser() { /// /// Value public ConstantUser(object value) { - this.type = GetElementType(value); + type = GetElementType(value); this.value = value; } @@ -76,24 +75,24 @@ public ConstantUser(object value, ElementType type) { } static ElementType GetElementType(object value) { - if (value == null) + if (value is null) return ElementType.Class; - switch (System.Type.GetTypeCode(value.GetType())) { - case TypeCode.Boolean: return ElementType.Boolean; - case TypeCode.Char: return ElementType.Char; - case TypeCode.SByte: return ElementType.I1; - case TypeCode.Byte: return ElementType.U1; - case TypeCode.Int16: return ElementType.I2; - case TypeCode.UInt16: return ElementType.U2; - case TypeCode.Int32: return ElementType.I4; - case TypeCode.UInt32: return ElementType.U4; - case TypeCode.Int64: return ElementType.I8; - case TypeCode.UInt64: return ElementType.U8; - case TypeCode.Single: return ElementType.R4; - case TypeCode.Double: return ElementType.R8; - case TypeCode.String: return ElementType.String; - default: return ElementType.Void; - } + return System.Type.GetTypeCode(value.GetType()) switch { + TypeCode.Boolean => ElementType.Boolean, + TypeCode.Char => ElementType.Char, + TypeCode.SByte => ElementType.I1, + TypeCode.Byte => ElementType.U1, + TypeCode.Int16 => ElementType.I2, + TypeCode.UInt16 => ElementType.U2, + TypeCode.Int32 => ElementType.I4, + TypeCode.UInt32 => ElementType.U4, + TypeCode.Int64 => ElementType.I8, + TypeCode.UInt64 => ElementType.U8, + TypeCode.Single => ElementType.R4, + TypeCode.Double => ElementType.R8, + TypeCode.String => ElementType.String, + _ => ElementType.Void, + }; } } @@ -101,15 +100,10 @@ static ElementType GetElementType(object value) { /// Created from a row in the Constant table /// sealed class ConstantMD : Constant, IMDTokenProviderMD { - /// The module where this instance is located - readonly ModuleDefMD readerModule; - readonly uint origRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// /// Constructor @@ -120,84 +114,84 @@ public uint OrigRid { /// If is invalid public ConstantMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.ConstantTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("Constant rid {0} does not exist", rid)); + throw new BadImageFormatException($"Constant rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; - this.readerModule = readerModule; - uint value = readerModule.TablesStream.ReadConstantRow(origRid, out this.type); - this.value = GetValue(this.type, readerModule.BlobStream.ReadNoNull(value)); + bool b = readerModule.TablesStream.TryReadConstantRow(origRid, out var row); + Debug.Assert(b); + type = (ElementType)row.Type; + var reader = readerModule.BlobStream.CreateReader(row.Value); + value = GetValue(type, ref reader); } - static object GetValue(ElementType etype, byte[] data) { + static object GetValue(ElementType etype, ref DataReader reader) { switch (etype) { case ElementType.Boolean: - if (data == null || data.Length < 1) + if (reader.Length < 1) return false; - return BitConverter.ToBoolean(data, 0); + return reader.ReadBoolean(); case ElementType.Char: - if (data == null || data.Length < 2) + if (reader.Length < 2) return (char)0; - return BitConverter.ToChar(data, 0); + return reader.ReadChar(); case ElementType.I1: - if (data == null || data.Length < 1) + if (reader.Length < 1) return (sbyte)0; - return (sbyte)data[0]; + return reader.ReadSByte(); case ElementType.U1: - if (data == null || data.Length < 1) + if (reader.Length < 1) return (byte)0; - return data[0]; + return reader.ReadByte(); case ElementType.I2: - if (data == null || data.Length < 2) + if (reader.Length < 2) return (short)0; - return BitConverter.ToInt16(data, 0); + return reader.ReadInt16(); case ElementType.U2: - if (data == null || data.Length < 2) + if (reader.Length < 2) return (ushort)0; - return BitConverter.ToUInt16(data, 0); + return reader.ReadUInt16(); case ElementType.I4: - if (data == null || data.Length < 4) + if (reader.Length < 4) return (int)0; - return BitConverter.ToInt32(data, 0); + return reader.ReadInt32(); case ElementType.U4: - if (data == null || data.Length < 4) + if (reader.Length < 4) return (uint)0; - return BitConverter.ToUInt32(data, 0); + return reader.ReadUInt32(); case ElementType.I8: - if (data == null || data.Length < 8) + if (reader.Length < 8) return (long)0; - return BitConverter.ToInt64(data, 0); + return reader.ReadInt64(); case ElementType.U8: - if (data == null || data.Length < 8) + if (reader.Length < 8) return (ulong)0; - return BitConverter.ToUInt64(data, 0); + return reader.ReadUInt64(); case ElementType.R4: - if (data == null || data.Length < 4) + if (reader.Length < 4) return (float)0; - return BitConverter.ToSingle(data, 0); + return reader.ReadSingle(); case ElementType.R8: - if (data == null || data.Length < 8) + if (reader.Length < 8) return (double)0; - return BitConverter.ToDouble(data, 0); + return reader.ReadDouble(); case ElementType.String: - if (data == null) - return string.Empty; - return Encoding.Unicode.GetString(data, 0, data.Length / 2 * 2); + return reader.ReadUtf16String((int)(reader.BytesLeft / 2)); case ElementType.Class: return null; diff --git a/src/DotNet/CorLibTypes.cs b/src/DotNet/CorLibTypes.cs index a5fc6948d..8a2f598d9 100644 --- a/src/DotNet/CorLibTypes.cs +++ b/src/DotNet/CorLibTypes.cs @@ -27,99 +27,61 @@ public sealed class CorLibTypes : ICorLibTypes { readonly AssemblyRef corLibAssemblyRef; /// - public CorLibTypeSig Void { - get { return typeVoid; } - } + public CorLibTypeSig Void => typeVoid; /// - public CorLibTypeSig Boolean { - get { return typeBoolean; } - } + public CorLibTypeSig Boolean => typeBoolean; /// - public CorLibTypeSig Char { - get { return typeChar; } - } + public CorLibTypeSig Char => typeChar; /// - public CorLibTypeSig SByte { - get { return typeSByte; } - } + public CorLibTypeSig SByte => typeSByte; /// - public CorLibTypeSig Byte { - get { return typeByte; } - } + public CorLibTypeSig Byte => typeByte; /// - public CorLibTypeSig Int16 { - get { return typeInt16; } - } + public CorLibTypeSig Int16 => typeInt16; /// - public CorLibTypeSig UInt16 { - get { return typeUInt16; } - } + public CorLibTypeSig UInt16 => typeUInt16; /// - public CorLibTypeSig Int32 { - get { return typeInt32; } - } + public CorLibTypeSig Int32 => typeInt32; /// - public CorLibTypeSig UInt32 { - get { return typeUInt32; } - } + public CorLibTypeSig UInt32 => typeUInt32; /// - public CorLibTypeSig Int64 { - get { return typeInt64; } - } + public CorLibTypeSig Int64 => typeInt64; /// - public CorLibTypeSig UInt64 { - get { return typeUInt64; } - } + public CorLibTypeSig UInt64 => typeUInt64; /// - public CorLibTypeSig Single { - get { return typeSingle; } - } + public CorLibTypeSig Single => typeSingle; /// - public CorLibTypeSig Double { - get { return typeDouble; } - } + public CorLibTypeSig Double => typeDouble; /// - public CorLibTypeSig String { - get { return typeString; } - } + public CorLibTypeSig String => typeString; /// - public CorLibTypeSig TypedReference { - get { return typeTypedReference; } - } + public CorLibTypeSig TypedReference => typeTypedReference; /// - public CorLibTypeSig IntPtr { - get { return typeIntPtr; } - } + public CorLibTypeSig IntPtr => typeIntPtr; /// - public CorLibTypeSig UIntPtr { - get { return typeUIntPtr; } - } + public CorLibTypeSig UIntPtr => typeUIntPtr; /// - public CorLibTypeSig Object { - get { return typeObject; } - } + public CorLibTypeSig Object => typeObject; /// - public AssemblyRef AssemblyRef { - get { return corLibAssemblyRef; } - } + public AssemblyRef AssemblyRef => corLibAssemblyRef; /// /// Constructor @@ -141,9 +103,7 @@ public CorLibTypes(ModuleDef module, AssemblyRef corLibAssemblyRef) { Initialize(); } - AssemblyRef CreateCorLibAssemblyRef() { - return module.UpdateRowId(AssemblyRefUser.CreateMscorlibReferenceCLR20()); - } + AssemblyRef CreateCorLibAssemblyRef() => module.UpdateRowId(AssemblyRefUser.CreateMscorlibReferenceCLR20()); void Initialize() { bool isCorLib = module.Assembly.IsCorLib(); @@ -171,15 +131,13 @@ ITypeDefOrRef CreateCorLibTypeRef(bool isCorLib, string name) { var tr = new TypeRefUser(module, "System", name, corLibAssemblyRef); if (isCorLib) { var td = module.Find(tr); - if (td != null) + if (td is not null) return td; } return module.UpdateRowId(tr); } /// - public TypeRef GetTypeRef(string @namespace, string name) { - return module.UpdateRowId(new TypeRefUser(module, @namespace, name, corLibAssemblyRef)); - } + public TypeRef GetTypeRef(string @namespace, string name) => module.UpdateRowId(new TypeRefUser(module, @namespace, name, corLibAssemblyRef)); } } diff --git a/src/DotNet/CpuArch.cs b/src/DotNet/CpuArch.cs new file mode 100644 index 000000000..2e0c27a5d --- /dev/null +++ b/src/DotNet/CpuArch.cs @@ -0,0 +1,427 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using dnlib.DotNet.Writer; +using dnlib.IO; +using dnlib.PE; + +namespace dnlib.DotNet { + enum StubType { + Export, + EntryPoint, + } + + abstract class CpuArch { + // To support a new CPU arch, the easiest way is to check coreclr/src/ilasm/writer.cpp or + // coreclr/src/dlls/mscorpe/stubs.h, eg. ExportStubAMD64Template, ExportStubX86Template, + // ExportStubARMTemplate, ExportStubIA64Template, or use ilasm to generate a file with + // exports and check the stub + static readonly X86CpuArch x86CpuArch = new X86CpuArch(); + static readonly X64CpuArch x64CpuArch = new X64CpuArch(); + static readonly ItaniumCpuArch itaniumCpuArch = new ItaniumCpuArch(); + static readonly ArmCpuArch armCpuArch = new ArmCpuArch(); + + /// + /// Gets the required alignment for the stubs, must be a power of 2 + /// + /// Stub type + /// + public abstract uint GetStubAlignment(StubType stubType); + + /// + /// Gets the size of a stub, it doesn't have to be a multiple of + /// + /// Stub type + /// + public abstract uint GetStubSize(StubType stubType); + + /// + /// Gets the offset of the code (entry point) relative to the start of the stub + /// + /// Stub type + /// + public abstract uint GetStubCodeOffset(StubType stubType); + + public static bool TryGetCpuArch(Machine machine, out CpuArch cpuArch) { + switch (machine) { + case Machine.I386: + case Machine.I386_Native_Apple: + case Machine.I386_Native_FreeBSD: + case Machine.I386_Native_Linux: + case Machine.I386_Native_NetBSD: + case Machine.I386_Native_Sun: + cpuArch = x86CpuArch; + return true; + + case Machine.AMD64: + case Machine.AMD64_Native_Apple: + case Machine.AMD64_Native_FreeBSD: + case Machine.AMD64_Native_Linux: + case Machine.AMD64_Native_NetBSD: + case Machine.AMD64_Native_Sun: + cpuArch = x64CpuArch; + return true; + + case Machine.IA64: + cpuArch = itaniumCpuArch; + return true; + + case Machine.ARMNT: + case Machine.ARMNT_Native_Apple: + case Machine.ARMNT_Native_FreeBSD: + case Machine.ARMNT_Native_Linux: + case Machine.ARMNT_Native_NetBSD: + case Machine.ARMNT_Native_Sun: + cpuArch = armCpuArch; + return true; + + case Machine.ARM64: + case Machine.ARM64_Native_Apple: + case Machine.ARM64_Native_FreeBSD: + case Machine.ARM64_Native_Linux: + case Machine.ARM64_Native_NetBSD: + case Machine.ARM64_Native_Sun: + //TODO: Support ARM64 + goto default; + + default: + cpuArch = null; + return false; + } + } + + /// + /// Gets the RVA of the func field that the stub jumps to + /// + /// Reader, positioned at the stub func + /// PE image + /// Updated with RVA of func field + /// + public bool TryGetExportedRvaFromStub(ref DataReader reader, IPEImage peImage, out uint funcRva) => + TryGetExportedRvaFromStubCore(ref reader, peImage, out funcRva); + + protected abstract bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva); + + /// + /// Writes stub relocs, if needed + /// + /// Stub type + /// Reloc directory + /// The chunk where this stub will be written to + /// Offset of this stub in + public abstract void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset); + + /// + /// Writes the stub that jumps to the managed function + /// + /// Stub type + /// Writer + /// Image base + /// RVA of this stub + /// RVA of a pointer-sized field that contains the absolute address of the managed function + public abstract void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva); + } + + sealed class X86CpuArch : CpuArch { + public override uint GetStubAlignment(StubType stubType) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + return 4; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override uint GetStubSize(StubType stubType) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + return 2/*padding*/ + 6; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override uint GetStubCodeOffset(StubType stubType) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + return 2/*padding*/; + default: + throw new ArgumentOutOfRangeException(); + } + } + + protected override bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva) { + funcRva = 0; + + // FF25xxxxxxxx jmp DWORD PTR [xxxxxxxx] + if (reader.ReadUInt16() != 0x25FF) + return false; + funcRva = reader.ReadUInt32() - (uint)peImage.ImageNTHeaders.OptionalHeader.ImageBase; + return true; + } + + public override void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + relocDirectory.Add(chunk, stubOffset + 4); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + writer.WriteUInt16(0);// padding + writer.WriteUInt16(0x25FF); + writer.WriteUInt32((uint)imageBase + managedFuncRva); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + + sealed class X64CpuArch : CpuArch { + public override uint GetStubAlignment(StubType stubType) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + return 4; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override uint GetStubSize(StubType stubType) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + return 2/*padding*/ + 12; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override uint GetStubCodeOffset(StubType stubType) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + return 2/*padding*/; + default: + throw new ArgumentOutOfRangeException(); + } + } + + protected override bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva) { + funcRva = 0; + + // 48A1xxxxxxxxxxxxxxxx movabs rax,[xxxxxxxxxxxxxxxx] + // FFE0 jmp rax + if (reader.ReadUInt16() != 0xA148) + return false; + ulong absAddr = reader.ReadUInt64(); + if (reader.ReadUInt16() != 0xE0FF) + return false; + ulong rva = absAddr - peImage.ImageNTHeaders.OptionalHeader.ImageBase; + if (rva > uint.MaxValue) + return false; + funcRva = (uint)rva; + return true; + } + + public override void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + relocDirectory.Add(chunk, stubOffset + 4); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + writer.WriteUInt16(0);// padding + writer.WriteUInt16(0xA148); + writer.WriteUInt64(imageBase + managedFuncRva); + writer.WriteUInt16(0xE0FF); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + + sealed class ItaniumCpuArch : CpuArch { + public override uint GetStubAlignment(StubType stubType) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + return 16; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override uint GetStubSize(StubType stubType) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + return 0x30; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override uint GetStubCodeOffset(StubType stubType) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + return 0x20; + default: + throw new ArgumentOutOfRangeException(); + } + } + + protected override bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva) { + funcRva = 0; + + // From ExportStubIA64Template in coreclr/src/ilasm/writer.cpp + // + // ld8 r9 = [gp] ;; + // ld8 r10 = [r9],8 + // nop.i ;; + // ld8 gp = [r9] + // mov b6 = r10 + // br.cond.sptk.few b6 + // + // 0x0B, 0x48, 0x00, 0x02, 0x18, 0x10, 0xA0, 0x40, + // 0x24, 0x30, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, + // 0x10, 0x08, 0x00, 0x12, 0x18, 0x10, 0x60, 0x50, + // 0x04, 0x80, 0x03, 0x00, 0x60, 0x00, 0x80, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,//address of the template + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //address of VTFixup slot + ulong addrTemplate = reader.ReadUInt64(); + ulong absAddr = reader.ReadUInt64(); + reader.Position = (uint)peImage.ToFileOffset((RVA)(addrTemplate - peImage.ImageNTHeaders.OptionalHeader.ImageBase)); + if (reader.ReadUInt64() != 0x40A010180200480BUL) + return false; + if (reader.ReadUInt64() != 0x0004000000283024UL) + return false; + if (reader.ReadUInt64() != 0x5060101812000810UL) + return false; + if (reader.ReadUInt64() != 0x0080006000038004UL) + return false; + + ulong rva = absAddr - peImage.ImageNTHeaders.OptionalHeader.ImageBase; + if (rva > uint.MaxValue) + return false; + funcRva = (uint)rva; + return true; + } + + public override void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + relocDirectory.Add(chunk, stubOffset + 0x20); + relocDirectory.Add(chunk, stubOffset + 0x28); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + writer.WriteUInt64(0x40A010180200480BUL); + writer.WriteUInt64(0x0004000000283024UL); + writer.WriteUInt64(0x5060101812000810UL); + writer.WriteUInt64(0x0080006000038004UL); + writer.WriteUInt64(imageBase + stubRva); + writer.WriteUInt64(imageBase + managedFuncRva); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + + sealed class ArmCpuArch : CpuArch { + public override uint GetStubAlignment(StubType stubType) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + return 4; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override uint GetStubSize(StubType stubType) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + return 8; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override uint GetStubCodeOffset(StubType stubType) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + return 0; + default: + throw new ArgumentOutOfRangeException(); + } + } + + protected override bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva) { + funcRva = 0; + + // DFF800F0 ldr.w pc,[pc] + // xxxxxxxx + if (reader.ReadUInt32() != 0xF000F8DF) + return false; + funcRva = reader.ReadUInt32() - (uint)peImage.ImageNTHeaders.OptionalHeader.ImageBase; + return true; + } + + public override void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + relocDirectory.Add(chunk, stubOffset + 4); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva) { + switch (stubType) { + case StubType.Export: + case StubType.EntryPoint: + writer.WriteUInt32(0xF000F8DF); + writer.WriteUInt32((uint)imageBase + managedFuncRva); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } +} diff --git a/src/DotNet/CustomAttribute.cs b/src/DotNet/CustomAttribute.cs index f3bacf34e..377fdf82e 100644 --- a/src/DotNet/CustomAttribute.cs +++ b/src/DotNet/CustomAttribute.cs @@ -2,14 +2,6 @@ using System; using System.Collections.Generic; -using dnlib.Threading; -using dnlib.IO; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif namespace dnlib.DotNet { /// @@ -18,96 +10,98 @@ namespace dnlib.DotNet { public sealed class CustomAttribute : ICustomAttribute { ICustomAttributeType ctor; byte[] rawData; - readonly ThreadSafe.IList arguments; - readonly ThreadSafe.IList namedArguments; - IBinaryReader blobReader; + readonly IList arguments; + readonly IList namedArguments; + uint caBlobOffset; /// /// Gets/sets the custom attribute constructor /// public ICustomAttributeType Constructor { - get { return ctor; } - set { ctor = value; } + get => ctor; + set => ctor = value; } /// /// Gets the attribute type /// - public ITypeDefOrRef AttributeType { - get { - var cat = ctor; - return cat == null ? null : cat.DeclaringType; - } - } + public ITypeDefOrRef AttributeType => ctor?.DeclaringType; /// /// Gets the full name of the attribute type /// public string TypeFullName { get { - var mrCtor = ctor as MemberRef; - if (mrCtor != null) + if (ctor is MemberRef mrCtor) return mrCtor.GetDeclaringTypeFullName() ?? string.Empty; - var mdCtor = ctor as MethodDef; - if (mdCtor != null) { + if (ctor is MethodDef mdCtor) { var declType = mdCtor.DeclaringType; - if (declType != null) + if (declType is not null) return declType.FullName; } return string.Empty; } - } - + } + + /// + /// Gets the name of the attribute type + /// + internal string TypeName { + get { + if (ctor is MemberRef mrCtor) + return mrCtor.GetDeclaringTypeName() ?? string.Empty; + + if (ctor is MethodDef mdCtor) { + var declType = mdCtor.DeclaringType; + if (declType is not null) + return declType.Name; + } + + return string.Empty; + } + } + /// /// true if the raw custom attribute blob hasn't been parsed /// - public bool IsRawBlob { - get { return rawData != null; } - } + public bool IsRawBlob => rawData is not null; /// /// Gets the raw custom attribute blob or null if the CA was successfully parsed. /// - public byte[] RawData { - get { return rawData; } - } + public byte[] RawData => rawData; /// /// Gets all constructor arguments /// - public ThreadSafe.IList ConstructorArguments { - get { return arguments; } - } + public IList ConstructorArguments => arguments; /// /// true if is not empty /// - public bool HasConstructorArguments { - get { return arguments.Count > 0; } - } + public bool HasConstructorArguments => arguments.Count > 0; /// /// Gets all named arguments (field and property values) /// - public ThreadSafe.IList NamedArguments { - get { return namedArguments; } - } + public IList NamedArguments => namedArguments; /// /// true if is not empty /// - public bool HasNamedArguments { - get { return namedArguments.Count > 0; } - } + public bool HasNamedArguments => namedArguments.Count > 0; /// /// Gets all s that are field arguments /// public IEnumerable Fields { get { - foreach (var namedArg in namedArguments.GetSafeEnumerable()) { + var namedArguments = this.namedArguments; + int count = namedArguments.Count; + for (int i = 0; i < count; i++) { + var namedArg = namedArguments[i]; if (namedArg.IsField) yield return namedArg; } @@ -119,29 +113,35 @@ public IEnumerable Fields { /// public IEnumerable Properties { get { - foreach (var namedArg in namedArguments.GetSafeEnumerable()) { + var namedArguments = this.namedArguments; + int count = namedArguments.Count; + for (int i = 0; i < count; i++) { + var namedArg = namedArguments[i]; if (namedArg.IsProperty) yield return namedArg; } } } + /// + /// Gets the #Blob offset or 0 if unknown + /// + public uint BlobOffset => caBlobOffset; + /// /// Constructor /// /// Custom attribute constructor /// Raw custom attribute blob public CustomAttribute(ICustomAttributeType ctor, byte[] rawData) - : this(ctor, null, null, null) { - this.rawData = rawData; - } + : this(ctor, null, null, 0) => this.rawData = rawData; /// /// Constructor /// /// Custom attribute constructor public CustomAttribute(ICustomAttributeType ctor) - : this(ctor, null, null, null) { + : this(ctor, null, null, 0) { } /// @@ -169,7 +169,7 @@ public CustomAttribute(ICustomAttributeType ctor, IEnumerable n /// Constructor arguments or null if none /// Named arguments or null if none public CustomAttribute(ICustomAttributeType ctor, IEnumerable arguments, IEnumerable namedArguments) - : this(ctor, arguments, namedArguments, null) { + : this(ctor, arguments, namedArguments, 0) { } /// @@ -178,12 +178,12 @@ public CustomAttribute(ICustomAttributeType ctor, IEnumerable argume /// Custom attribute constructor /// Constructor arguments or null if none /// Named arguments or null if none - /// A reader that returns the original custom attribute blob data - public CustomAttribute(ICustomAttributeType ctor, IEnumerable arguments, IEnumerable namedArguments, IBinaryReader blobReader) { + /// Original custom attribute #Blob offset or 0 + public CustomAttribute(ICustomAttributeType ctor, IEnumerable arguments, IEnumerable namedArguments, uint caBlobOffset) { this.ctor = ctor; - this.arguments = arguments == null ? ThreadSafeListCreator.Create() : ThreadSafeListCreator.Create(arguments); - this.namedArguments = namedArguments == null ? ThreadSafeListCreator.Create() : ThreadSafeListCreator.Create(namedArguments); - this.blobReader = blobReader; + this.arguments = arguments is null ? new List() : new List(arguments); + this.namedArguments = namedArguments is null ? new List() : new List(namedArguments); + this.caBlobOffset = caBlobOffset; } /// @@ -192,12 +192,12 @@ public CustomAttribute(ICustomAttributeType ctor, IEnumerable argume /// Custom attribute constructor /// Constructor arguments. The list is now owned by this instance. /// Named arguments. The list is now owned by this instance. - /// A reader that returns the original custom attribute blob data - internal CustomAttribute(ICustomAttributeType ctor, List arguments, List namedArguments, IBinaryReader blobReader) { + /// Original custom attribute #Blob offset or 0 + internal CustomAttribute(ICustomAttributeType ctor, List arguments, List namedArguments, uint caBlobOffset) { this.ctor = ctor; - this.arguments = arguments == null ? ThreadSafeListCreator.Create() : ThreadSafeListCreator.MakeThreadSafe(arguments); - this.namedArguments = namedArguments == null ? ThreadSafeListCreator.Create() : ThreadSafeListCreator.MakeThreadSafe(namedArguments); - this.blobReader = blobReader; + this.arguments = arguments ?? new List(); + this.namedArguments = namedArguments ?? new List(); + this.caBlobOffset = caBlobOffset; } /// @@ -205,36 +205,28 @@ internal CustomAttribute(ICustomAttributeType ctor, List arguments, /// /// Name of field /// A instance or null if not found - public CANamedArgument GetField(string name) { - return GetNamedArgument(name, true); - } + public CANamedArgument GetField(string name) => GetNamedArgument(name, true); /// /// Gets the field named /// /// Name of field /// A instance or null if not found - public CANamedArgument GetField(UTF8String name) { - return GetNamedArgument(name, true); - } + public CANamedArgument GetField(UTF8String name) => GetNamedArgument(name, true); /// /// Gets the property named /// /// Name of property /// A instance or null if not found - public CANamedArgument GetProperty(string name) { - return GetNamedArgument(name, false); - } + public CANamedArgument GetProperty(string name) => GetNamedArgument(name, false); /// /// Gets the property named /// /// Name of property /// A instance or null if not found - public CANamedArgument GetProperty(UTF8String name) { - return GetNamedArgument(name, false); - } + public CANamedArgument GetProperty(UTF8String name) => GetNamedArgument(name, false); /// /// Gets the property/field named @@ -243,7 +235,10 @@ public CANamedArgument GetProperty(UTF8String name) { /// true if it's a field, false if it's a property /// A instance or null if not found public CANamedArgument GetNamedArgument(string name, bool isField) { - foreach (var namedArg in namedArguments.GetSafeEnumerable()) { + var namedArguments = this.namedArguments; + int count = namedArguments.Count; + for (int i = 0; i < count; i++) { + var namedArg = namedArguments[i]; if (namedArg.IsField == isField && UTF8String.ToSystemStringOrEmpty(namedArg.Name) == name) return namedArg; } @@ -257,46 +252,18 @@ public CANamedArgument GetNamedArgument(string name, bool isField) { /// true if it's a field, false if it's a property /// A instance or null if not found public CANamedArgument GetNamedArgument(UTF8String name, bool isField) { - foreach (var namedArg in namedArguments.GetSafeEnumerable()) { + var namedArguments = this.namedArguments; + int count = namedArguments.Count; + for (int i = 0; i < count; i++) { + var namedArg = namedArguments[i]; if (namedArg.IsField == isField && UTF8String.Equals(namedArg.Name, name)) return namedArg; } return null; } - /// - /// Gets the binary custom attribute data that was used to create this instance. - /// - /// Blob of this custom attribute - public byte[] GetBlob() { - if (rawData != null) - return rawData; - if (blob != null) - return blob; -#if THREAD_SAFE - if (blobReader != null) { - lock (this) { -#endif - if (blobReader != null) { - blob = blobReader.ReadAllBytes(); - blobReader.Dispose(); - blobReader = null; - return blob; - } -#if THREAD_SAFE - } - } -#endif - if (blob != null) - return blob; - return blob = new byte[0]; - } - byte[] blob; - /// - public override string ToString() { - return TypeFullName; - } + public override string ToString() => TypeFullName; } /// @@ -310,16 +277,16 @@ public struct CAArgument : ICloneable { /// Gets/sets the argument type /// public TypeSig Type { - get { return type; } - set { type = value; } + readonly get => type; + set => type = value; } /// /// Gets/sets the argument value /// public object Value { - get { return value; } - set { this.value = value; } + readonly get => value; + set => this.value = value; } /// @@ -328,7 +295,7 @@ public object Value { /// Argument type public CAArgument(TypeSig type) { this.type = type; - this.value = null; + value = null; } /// @@ -341,34 +308,31 @@ public CAArgument(TypeSig type, object value) { this.value = value; } - object ICloneable.Clone() { - return Clone(); - } + readonly object ICloneable.Clone() => Clone(); /// /// Clones this instance and any s and s /// referenced from this instance. /// /// - public CAArgument Clone() { + public readonly CAArgument Clone() { var value = this.value; if (value is CAArgument) value = ((CAArgument)value).Clone(); - else if (value is IList) { - var args = (IList)value; - var newArgs = ThreadSafeListCreator.Create(args.Count); - foreach (var arg in args.GetSafeEnumerable()) + else if (value is IList args) { + var newArgs = new List(args.Count); + int count = args.Count; + for (int i = 0; i < count; i++) { + var arg = args[i]; newArgs.Add(arg.Clone()); + } value = newArgs; } return new CAArgument(type, value); } /// - public override string ToString() { - object v = value; - return string.Format("{0} ({1})", v == null ? "null" : v, type); - } + public override readonly string ToString() => $"{value ?? "null"} ({type})"; } /// @@ -384,56 +348,56 @@ public sealed class CANamedArgument : ICloneable { /// true if it's a field /// public bool IsField { - get { return isField; } - set { isField = value; } + get => isField; + set => isField = value; } /// /// true if it's a property /// public bool IsProperty { - get { return !isField; } - set { isField = !value; } + get => !isField; + set => isField = !value; } /// /// Gets/sets the field/property type /// public TypeSig Type { - get { return type; } - set { type = value; } + get => type; + set => type = value; } /// /// Gets/sets the property/field name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// /// Gets/sets the argument /// public CAArgument Argument { - get { return argument; } - set { argument = value; } + get => argument; + set => argument = value; } /// /// Gets/sets the argument type /// public TypeSig ArgumentType { - get { return argument.Type; } - set { argument.Type = value; } + get => argument.Type; + set => argument.Type = value; } /// /// Gets/sets the argument value /// public object Value { - get { return argument.Value; } - set { argument.Value = value; } + get => argument.Value; + set => argument.Value = value; } /// @@ -446,9 +410,7 @@ public CANamedArgument() { /// Constructor /// /// true if field, false if property - public CANamedArgument(bool isField) { - this.isField = isField; - } + public CANamedArgument(bool isField) => this.isField = isField; /// /// Constructor @@ -486,22 +448,15 @@ public CANamedArgument(bool isField, TypeSig type, UTF8String name, CAArgument a this.argument = argument; } - object ICloneable.Clone() { - return Clone(); - } + object ICloneable.Clone() => Clone(); /// /// Clones this instance and any s referenced from this instance. /// /// - public CANamedArgument Clone() { - return new CANamedArgument(isField, type, name, argument.Clone()); - } + public CANamedArgument Clone() => new CANamedArgument(isField, type, name, argument.Clone()); /// - public override string ToString() { - object v = Value; - return string.Format("({0}) {1} {2} = {3} ({4})", isField ? "field" : "property", type, name, v == null ? "null" : v, ArgumentType); - } + public override string ToString() => $"({(isField ? "field" : "property")}) {type} {name} = {Value ?? "null"} ({ArgumentType})"; } } diff --git a/src/DotNet/CustomAttributeCollection.cs b/src/DotNet/CustomAttributeCollection.cs index a18c333a0..07fba4659 100644 --- a/src/DotNet/CustomAttributeCollection.cs +++ b/src/DotNet/CustomAttributeCollection.cs @@ -1,14 +1,14 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; +using System.Collections.Generic; using dnlib.Utils; -using dnlib.Threading; +using System; namespace dnlib.DotNet { /// /// Stores s /// - public class CustomAttributeCollection : LazyList { + public class CustomAttributeCollection : LazyList { /// /// Default constructor /// @@ -21,7 +21,7 @@ public CustomAttributeCollection() { /// Initial length of the list /// Context passed to /// Delegate instance that returns original values - public CustomAttributeCollection(int length, object context, MFunc readOriginalValue) + public CustomAttributeCollection(int length, object context, Func readOriginalValue) : base(length, context, readOriginalValue) { } @@ -30,19 +30,21 @@ public CustomAttributeCollection(int length, object context, MFunc /// Full name of custom attribute type /// true if the custom attribute type is present, false otherwise - public bool IsDefined(string fullName) { - return Find(fullName) != null; - } + public bool IsDefined(string fullName) => Find(fullName) is not null; /// /// Removes all custom attributes of a certain type /// /// Full name of custom attribute type that should be removed public void RemoveAll(string fullName) { - this.IterateAllReverse((tsList, index, value) => { - if (value.TypeFullName == fullName) - RemoveAt_NoLock(index); - }); + if (fullName == null) + return; + + for (int i = Count - 1; i >= 0; i--) { + var ca = this[i]; + if (ca is not null && fullName.EndsWith(ca.TypeName, StringComparison.Ordinal) && ca.TypeFullName == fullName) + RemoveAt(i); + } } /// @@ -51,8 +53,11 @@ public void RemoveAll(string fullName) { /// Full name of custom attribute type /// A or null if it wasn't found public CustomAttribute Find(string fullName) { - foreach (var ca in this.GetSafeEnumerable()) { - if (ca != null && ca.TypeFullName == fullName) + if (fullName == null) + return null; + + foreach (var ca in this) { + if (ca is not null && fullName.EndsWith(ca.TypeName, StringComparison.Ordinal) && ca.TypeFullName == fullName) return ca; } @@ -65,8 +70,11 @@ public CustomAttribute Find(string fullName) { /// Full name of custom attribute type /// All s of the requested type public IEnumerable FindAll(string fullName) { - foreach (var ca in this.GetSafeEnumerable()) { - if (ca != null && ca.TypeFullName == fullName) + if (fullName == null) + yield break; + + foreach (var ca in this) { + if (ca is not null && fullName.EndsWith(ca.TypeName, StringComparison.Ordinal) && ca.TypeFullName == fullName) yield return ca; } } @@ -76,9 +84,7 @@ public IEnumerable FindAll(string fullName) { /// /// Custom attribute type /// The first found or null if none found - public CustomAttribute Find(IType attrType) { - return Find(attrType, 0); - } + public CustomAttribute Find(IType attrType) => Find(attrType, 0); /// /// Finds a custom attribute @@ -88,7 +94,7 @@ public CustomAttribute Find(IType attrType) { /// The first found or null if none found public CustomAttribute Find(IType attrType, SigComparerOptions options) { var comparer = new SigComparer(options); - foreach (var ca in this.GetSafeEnumerable()) { + foreach (var ca in this) { if (comparer.Equals(ca.AttributeType, attrType)) return ca; } @@ -100,9 +106,7 @@ public CustomAttribute Find(IType attrType, SigComparerOptions options) { /// /// Custom attribute type /// All s of the requested type - public IEnumerable FindAll(IType attrType) { - return FindAll(attrType, 0); - } + public IEnumerable FindAll(IType attrType) => FindAll(attrType, 0); /// /// Finds all custom attributes of a certain type @@ -112,7 +116,7 @@ public IEnumerable FindAll(IType attrType) { /// All s of the requested type public IEnumerable FindAll(IType attrType, SigComparerOptions options) { var comparer = new SigComparer(options); - foreach (var ca in this.GetSafeEnumerable()) { + foreach (var ca in this) { if (comparer.Equals(ca.AttributeType, attrType)) yield return ca; } diff --git a/src/DotNet/CustomAttributeReader.cs b/src/DotNet/CustomAttributeReader.cs index 0ea158d49..85cd4ccd1 100644 --- a/src/DotNet/CustomAttributeReader.cs +++ b/src/DotNet/CustomAttributeReader.cs @@ -1,10 +1,10 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.IO; +using System.Runtime.Serialization; using dnlib.IO; -using dnlib.Threading; namespace dnlib.DotNet { /// @@ -18,29 +18,29 @@ sealed class CAAssemblyRefFinder : IAssemblyRefFinder { /// Constructor /// /// The module to search first - public CAAssemblyRefFinder(ModuleDef module) { - this.module = module; - } + public CAAssemblyRefFinder(ModuleDef module) => this.module = module; /// public AssemblyRef FindAssemblyRef(TypeRef nonNestedTypeRef) { var modAsm = module.Assembly; - if (modAsm != null) { + if (modAsm is not null) { var type = modAsm.Find(nonNestedTypeRef); - if (type != null) + // If the user added a new type with the same name as a corelib type, don't return it, + // only return the type if it is this module's original type. + if (type is TypeDefMD td && td.ReaderModule == module) return module.UpdateRowId(new AssemblyRefUser(modAsm)); } - else if (module.Find(nonNestedTypeRef) != null) + else if (module.Find(nonNestedTypeRef) is not null) return AssemblyRef.CurrentAssembly; var corLibAsm = module.Context.AssemblyResolver.Resolve(module.CorLibTypes.AssemblyRef, module); - if (corLibAsm != null) { + if (corLibAsm is not null) { var type = corLibAsm.Find(nonNestedTypeRef); - if (type != null) + if (type is not null) return module.CorLibTypes.AssemblyRef; } - if (modAsm != null) + if (modAsm is not null) return module.UpdateRowId(new AssemblyRefUser(modAsm)); return AssemblyRef.CurrentAssembly; } @@ -73,19 +73,28 @@ public CABlobParserException(string message) public CABlobParserException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Constructor + /// + /// + /// + protected CABlobParserException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } } /// /// Reads custom attributes from the #Blob stream /// - public struct CustomAttributeReader : IDisposable { + public struct CustomAttributeReader { readonly ModuleDef module; - readonly IBinaryReader reader; + DataReader reader; + readonly uint caBlobOffset; readonly GenericParamContext gpContext; GenericArguments genericArguments; RecursionCounter recursionCounter; bool verifyReadAllBytes; - readonly bool ownReader; /// /// Reads a custom attribute @@ -94,9 +103,7 @@ public struct CustomAttributeReader : IDisposable { /// Custom attribute constructor /// Offset of custom attribute in the #Blob stream /// A new instance - public static CustomAttribute Read(ModuleDefMD readerModule, ICustomAttributeType ctor, uint offset) { - return Read(readerModule, ctor, offset, new GenericParamContext()); - } + public static CustomAttribute Read(ModuleDefMD readerModule, ICustomAttributeType ctor, uint offset) => Read(readerModule, ctor, offset, new GenericParamContext()); /// /// Reads a custom attribute @@ -107,24 +114,21 @@ public static CustomAttribute Read(ModuleDefMD readerModule, ICustomAttributeTyp /// Generic parameter context /// A new instance public static CustomAttribute Read(ModuleDefMD readerModule, ICustomAttributeType ctor, uint offset, GenericParamContext gpContext) { - using (var reader = new CustomAttributeReader(readerModule, offset, gpContext)) { - try { - if (ctor == null) - return reader.CreateRaw(ctor); - return reader.Read(ctor); - } - catch (CABlobParserException) { - return reader.CreateRaw(ctor); - } - catch (IOException) { - return reader.CreateRaw(ctor); - } + var caReader = new CustomAttributeReader(readerModule, offset, gpContext); + try { + if (ctor is null) + return caReader.CreateRaw(ctor); + return caReader.Read(ctor); + } + catch (CABlobParserException) { + return caReader.CreateRaw(ctor); + } + catch (IOException) { + return caReader.CreateRaw(ctor); } } - CustomAttribute CreateRaw(ICustomAttributeType ctor) { - return new CustomAttribute(ctor, GetRawBlob()); - } + CustomAttribute CreateRaw(ICustomAttributeType ctor) => new CustomAttribute(ctor, GetRawBlob()); /// /// Reads a custom attribute @@ -133,20 +137,18 @@ CustomAttribute CreateRaw(ICustomAttributeType ctor) { /// CA blob /// Custom attribute constructor /// A new instance - public static CustomAttribute Read(ModuleDef module, byte[] caBlob, ICustomAttributeType ctor) { - return Read(module, MemoryImageStream.Create(caBlob), ctor, new GenericParamContext()); - } + public static CustomAttribute Read(ModuleDef module, byte[] caBlob, ICustomAttributeType ctor) => + Read(module, ByteArrayDataReaderFactory.CreateReader(caBlob), ctor, new GenericParamContext()); /// /// Reads a custom attribute /// /// Owner module - /// A stream positioned at the the first byte of the CA blob + /// A reader positioned at the the first byte of the CA blob /// Custom attribute constructor /// A new instance - public static CustomAttribute Read(ModuleDef module, IBinaryReader stream, ICustomAttributeType ctor) { - return Read(module, stream, ctor, new GenericParamContext()); - } + public static CustomAttribute Read(ModuleDef module, DataReader reader, ICustomAttributeType ctor) => + Read(module, ref reader, ctor, new GenericParamContext()); /// /// Reads a custom attribute @@ -156,47 +158,61 @@ public static CustomAttribute Read(ModuleDef module, IBinaryReader stream, ICust /// Custom attribute constructor /// Generic parameter context /// A new instance - public static CustomAttribute Read(ModuleDef module, byte[] caBlob, ICustomAttributeType ctor, GenericParamContext gpContext) { - return Read(module, MemoryImageStream.Create(caBlob), ctor, gpContext); - } + public static CustomAttribute Read(ModuleDef module, byte[] caBlob, ICustomAttributeType ctor, GenericParamContext gpContext) => + Read(module, ByteArrayDataReaderFactory.CreateReader(caBlob), ctor, gpContext); /// /// Reads a custom attribute /// /// Owner module - /// A stream positioned at the the first byte of the CA blob + /// A stream positioned at the the first byte of the CA blob /// Custom attribute constructor /// Generic parameter context /// A new instance - public static CustomAttribute Read(ModuleDef module, IBinaryReader stream, ICustomAttributeType ctor, GenericParamContext gpContext) { - using (var reader = new CustomAttributeReader(module, stream, gpContext)) { - try { - if (stream == null || ctor == null) - return reader.CreateRaw(ctor); - return reader.Read(ctor); - } - catch (CABlobParserException) { - return reader.CreateRaw(ctor); - } - catch (IOException) { - return reader.CreateRaw(ctor); - } + public static CustomAttribute Read(ModuleDef module, DataReader reader, ICustomAttributeType ctor, GenericParamContext gpContext) => + Read(module, ref reader, ctor, gpContext); + + /// + /// Reads a custom attribute + /// + /// Owner module + /// A stream positioned at the the first byte of the CA blob + /// Custom attribute constructor + /// Generic parameter context + /// A new instance + static CustomAttribute Read(ModuleDef module, ref DataReader reader, ICustomAttributeType ctor, GenericParamContext gpContext) { + var caReader = new CustomAttributeReader(module, ref reader, gpContext); + CustomAttribute ca; + try { + if (ctor is null) + ca = caReader.CreateRaw(ctor); + else + ca = caReader.Read(ctor); + } + catch (CABlobParserException) { + ca = caReader.CreateRaw(ctor); + } + catch (IOException) { + ca = caReader.CreateRaw(ctor); } + return ca; } /// /// Reads custom attribute named arguments /// /// Owner module - /// A stream positioned at the the first byte of the CA blob - /// Number of named arguments to read from + /// A reader positioned at the the first byte of the CA blob + /// Number of named arguments to read from /// Generic parameter context /// A list of s or null if some error /// occurred. - internal static List ReadNamedArguments(ModuleDef module, IBinaryReader stream, int numNamedArgs, GenericParamContext gpContext) { + internal static List ReadNamedArguments(ModuleDef module, ref DataReader reader, int numNamedArgs, GenericParamContext gpContext) { try { - using (var reader = new CustomAttributeReader(module, stream, false, gpContext)) - return reader.ReadNamedArguments(numNamedArgs); + var caReader = new CustomAttributeReader(module, ref reader, gpContext); + var namedArgs = caReader.ReadNamedArguments(numNamedArgs); + reader.CurrentOffset = caReader.reader.CurrentOffset; + return namedArgs; } catch (CABlobParserException) { return null; @@ -207,63 +223,46 @@ internal static List ReadNamedArguments(ModuleDef module, IBina } CustomAttributeReader(ModuleDefMD readerModule, uint offset, GenericParamContext gpContext) { - this.module = readerModule; - this.reader = readerModule.BlobStream.CreateStream(offset); - this.ownReader = true; - this.genericArguments = null; - this.recursionCounter = new RecursionCounter(); - this.verifyReadAllBytes = false; + module = readerModule; + caBlobOffset = offset; + reader = readerModule.BlobStream.CreateReader(offset); + genericArguments = null; + recursionCounter = new RecursionCounter(); + verifyReadAllBytes = false; this.gpContext = gpContext; } - CustomAttributeReader(ModuleDef module, IBinaryReader reader, GenericParamContext gpContext) { + CustomAttributeReader(ModuleDef module, ref DataReader reader, GenericParamContext gpContext) { this.module = module; + caBlobOffset = 0; this.reader = reader; - this.ownReader = false; - this.genericArguments = null; - this.recursionCounter = new RecursionCounter(); - this.verifyReadAllBytes = false; + genericArguments = null; + recursionCounter = new RecursionCounter(); + verifyReadAllBytes = false; this.gpContext = gpContext; } - CustomAttributeReader(ModuleDef module, IBinaryReader reader, bool ownRerader, GenericParamContext gpContext) { - this.module = module; - this.reader = reader; - this.ownReader = ownRerader; - this.genericArguments = null; - this.recursionCounter = new RecursionCounter(); - this.verifyReadAllBytes = false; - this.gpContext = gpContext; - } - - byte[] GetRawBlob() { - return reader.ReadAllBytes(); - } + byte[] GetRawBlob() => reader.ToArray(); CustomAttribute Read(ICustomAttributeType ctor) { - var methodSig = ctor == null ? null : ctor.MethodSig; - if (methodSig == null) + var methodSig = ctor?.MethodSig; + if (methodSig is null) throw new CABlobParserException("ctor is null or not a method"); - var mrCtor = ctor as MemberRef; - if (mrCtor != null) { - var owner = mrCtor.Class as TypeSpec; - if (owner != null) { - var gis = owner.TypeSig as GenericInstSig; - if (gis != null) { - genericArguments = new GenericArguments(); - genericArguments.PushTypeArgs(gis.GenericArguments); - } - } + if (ctor is MemberRef mrCtor && mrCtor.Class is TypeSpec owner && owner.TypeSig is GenericInstSig gis) { + genericArguments = new GenericArguments(); + genericArguments.PushTypeArgs(gis.GenericArguments); } - bool isEmpty = methodSig.Params.Count == 0 && reader.Position == reader.Length; + var methodSigParams = methodSig.Params; + bool isEmpty = methodSigParams.Count == 0 && reader.Position == reader.Length; if (!isEmpty && reader.ReadUInt16() != 1) throw new CABlobParserException("Invalid CA blob prolog"); - var ctorArgs = new List(methodSig.Params.Count); - foreach (var arg in methodSig.Params.GetSafeEnumerable()) - ctorArgs.Add(ReadFixedArg(FixTypeSig(arg))); + var ctorArgs = new List(methodSigParams.Count); + int count = methodSigParams.Count; + for (int i = 0; i < count; i++) + ctorArgs.Add(ReadFixedArg(FixTypeSig(methodSigParams[i]))); // Some tools don't write the next ushort if there are no named arguments. int numNamedArgs = reader.Position == reader.Length ? 0 : reader.ReadUInt16(); @@ -274,19 +273,12 @@ CustomAttribute Read(ICustomAttributeType ctor) { if (verifyReadAllBytes && reader.Position != reader.Length) throw new CABlobParserException("Not all CA blob bytes were read"); - return new CustomAttribute(ctor, ctorArgs, namedArgs, CloneBlobReader(reader)); - } - - static IBinaryReader CloneBlobReader(IBinaryReader reader) { - if (reader == null) - return null; - var imgStream = reader as IImageStream; - if (imgStream != null) - return imgStream.Clone(); - return MemoryImageStream.Create(reader.ReadAllBytes()); + return new CustomAttribute(ctor, ctorArgs, namedArgs, caBlobOffset); } List ReadNamedArguments(int numNamedArgs) { + if ((uint)numNamedArgs >= 0x4000_0000 || numNamedArgs * 4 > reader.BytesLeft) + return null; var namedArgs = new List(numNamedArgs); for (int i = 0; i < numNamedArgs; i++) { if (reader.Position == reader.Length) @@ -296,12 +288,10 @@ List ReadNamedArguments(int numNamedArgs) { return namedArgs; } - TypeSig FixTypeSig(TypeSig type) { - return SubstituteGenericParameter(type.RemoveModifiers()).RemoveModifiers(); - } + TypeSig FixTypeSig(TypeSig type) => SubstituteGenericParameter(type.RemoveModifiers()).RemoveModifiers(); TypeSig SubstituteGenericParameter(TypeSig type) { - if (genericArguments == null) + if (genericArguments is null) return type; return genericArguments.Resolve(type); } @@ -309,12 +299,11 @@ TypeSig SubstituteGenericParameter(TypeSig type) { CAArgument ReadFixedArg(TypeSig argType) { if (!recursionCounter.Increment()) throw new CABlobParserException("Too much recursion"); - if (argType == null) + if (argType is null) throw new CABlobParserException("null argType"); CAArgument result; - var arrayType = argType as SZArraySig; - if (arrayType != null) + if (argType is SZArraySig arrayType) result = ReadArrayArgument(arrayType); else result = ReadElem(argType); @@ -324,11 +313,10 @@ CAArgument ReadFixedArg(TypeSig argType) { } CAArgument ReadElem(TypeSig argType) { - if (argType == null) + if (argType is null) throw new CABlobParserException("null argType"); - TypeSig realArgType; - var value = ReadValue((SerializationType)argType.ElementType, argType, out realArgType); - if (realArgType == null) + var value = ReadValue((SerializationType)argType.ElementType, argType, out var realArgType); + if (realArgType is null) throw new CABlobParserException("Invalid arg type"); // One example when this is true is when prop/field type is object and @@ -352,7 +340,7 @@ object ReadValue(SerializationType etype, TypeSig argType, out TypeSig realArgTy case SerializationType.Char: realArgType = module.CorLibTypes.Char; - result = (char)reader.ReadUInt16(); + result = reader.ReadChar(); break; case SerializationType.I1: @@ -412,7 +400,7 @@ object ReadValue(SerializationType etype, TypeSig argType, out TypeSig realArgTy // It's ET.ValueType if it's eg. a ctor enum arg type case (SerializationType)ElementType.ValueType: - if (argType == null) + if (argType is null) throw new CABlobParserException("Invalid element type"); realArgType = argType; result = ReadEnumValue(GetEnumUnderlyingType(argType)); @@ -423,18 +411,16 @@ object ReadValue(SerializationType etype, TypeSig argType, out TypeSig realArgTy case SerializationType.TaggedObject: realArgType = ReadFieldOrPropType(); var arraySig = realArgType as SZArraySig; - if (arraySig != null) + if (arraySig is not null) result = ReadArrayArgument(arraySig); - else { - TypeSig tmpType; - result = ReadValue((SerializationType)realArgType.ElementType, realArgType, out tmpType); - } + else + result = ReadValue((SerializationType)realArgType.ElementType, realArgType, out var tmpType); break; // It's ET.Class if it's eg. a ctor System.Type arg type case (SerializationType)ElementType.Class: var tdr = argType as TypeDefOrRefSig; - if (tdr != null && tdr.DefinitionAssembly.IsCorLib() && tdr.Namespace == "System") { + if (tdr is not null && tdr.DefinitionAssembly.IsCorLib() && tdr.Namespace == "System") { if (tdr.TypeName == "Type") { result = ReadValue(SerializationType.Type, tdr, out realArgType); break; @@ -473,11 +459,10 @@ object ReadValue(SerializationType etype, TypeSig argType, out TypeSig realArgTy } object ReadEnumValue(TypeSig underlyingType) { - if (underlyingType != null) { + if (underlyingType is not null) { if (underlyingType.ElementType < ElementType.Boolean || underlyingType.ElementType > ElementType.U8) throw new CABlobParserException("Invalid enum underlying type"); - TypeSig realArgType; - return ReadValue((SerializationType)underlyingType.ElementType, underlyingType, out realArgType); + return ReadValue((SerializationType)underlyingType.ElementType, underlyingType, out var realArgType); } // We couldn't resolve the type ref. It should be an enum, but we don't know for sure. @@ -490,11 +475,11 @@ object ReadEnumValue(TypeSig underlyingType) { TypeSig ReadType(bool canReturnNull) { var name = ReadUTF8String(); - if (canReturnNull && (object)name == null) + if (canReturnNull && name is null) return null; var asmRefFinder = new CAAssemblyRefFinder(module); var type = TypeNameParser.ParseAsTypeSigReflection(module, UTF8String.ToSystemStringOrEmpty(name), asmRefFinder, gpContext); - if (type == null) + if (type is null) throw new CABlobParserException("Could not parse type"); return type; } @@ -506,10 +491,10 @@ TypeSig ReadType(bool canReturnNull) { /// The underlying type or null if we couldn't resolve the type ref /// If is not an enum or null static TypeSig GetEnumUnderlyingType(TypeSig type) { - if (type == null) + if (type is null) throw new CABlobParserException("null enum type"); var td = GetTypeDef(type); - if (td == null) + if (td is null) return null; if (!td.IsEnum) throw new CABlobParserException("Not an enum"); @@ -524,14 +509,13 @@ static TypeSig GetEnumUnderlyingType(TypeSig type) { /// A or null if we couldn't resolve the /// or if is a type spec static TypeDef GetTypeDef(TypeSig type) { - var tdr = type as TypeDefOrRefSig; - if (tdr != null) { + if (type is TypeDefOrRefSig tdr) { var td = tdr.TypeDef; - if (td != null) + if (td is not null) return td; var tr = tdr.TypeRef; - if (tr != null) + if (tr is not null) return tr.Resolve(); } @@ -546,10 +530,10 @@ CAArgument ReadArrayArgument(SZArraySig arrayType) { int arrayCount = reader.ReadInt32(); if (arrayCount == -1) { // -1 if it's null } - else if (arrayCount < 0) + else if (arrayCount < 0 || arrayCount > reader.BytesLeft) throw new CABlobParserException("Array is too big"); else { - var array = ThreadSafeListCreator.Create(arrayCount); + var array = new List(arrayCount); arg.Value = array; for (int i = 0; i < arrayCount; i++) array.Add(ReadFixedArg(FixTypeSig(arrayType.Next))); @@ -560,14 +544,12 @@ CAArgument ReadArrayArgument(SZArraySig arrayType) { } CANamedArgument ReadNamedArgument() { - bool isField; - switch ((SerializationType)reader.ReadByte()) { - case SerializationType.Property:isField = false; break; - case SerializationType.Field: isField = true; break; - default: throw new CABlobParserException("Named argument is not a field/property"); - } - - TypeSig fieldPropType = ReadFieldOrPropType(); + var isField = (SerializationType)reader.ReadByte() switch { + SerializationType.Property => false, + SerializationType.Field => true, + _ => throw new CABlobParserException("Named argument is not a field/property"), + }; + var fieldPropType = ReadFieldOrPropType(); var name = ReadUTF8String(); var argument = ReadFixedArg(fieldPropType); @@ -577,27 +559,26 @@ CANamedArgument ReadNamedArgument() { TypeSig ReadFieldOrPropType() { if (!recursionCounter.Increment()) throw new CABlobParserException("Too much recursion"); - TypeSig result; - switch ((SerializationType)reader.ReadByte()) { - case SerializationType.Boolean: result = module.CorLibTypes.Boolean; break; - case SerializationType.Char: result = module.CorLibTypes.Char; break; - case SerializationType.I1: result = module.CorLibTypes.SByte; break; - case SerializationType.U1: result = module.CorLibTypes.Byte; break; - case SerializationType.I2: result = module.CorLibTypes.Int16; break; - case SerializationType.U2: result = module.CorLibTypes.UInt16; break; - case SerializationType.I4: result = module.CorLibTypes.Int32; break; - case SerializationType.U4: result = module.CorLibTypes.UInt32; break; - case SerializationType.I8: result = module.CorLibTypes.Int64; break; - case SerializationType.U8: result = module.CorLibTypes.UInt64; break; - case SerializationType.R4: result = module.CorLibTypes.Single; break; - case SerializationType.R8: result = module.CorLibTypes.Double; break; - case SerializationType.String: result = module.CorLibTypes.String; break; - case SerializationType.SZArray: result = new SZArraySig(ReadFieldOrPropType()); break; - case SerializationType.Type: result = new ClassSig(module.CorLibTypes.GetTypeRef("System", "Type")); break; - case SerializationType.TaggedObject: result = module.CorLibTypes.Object; break; - case SerializationType.Enum: result = ReadType(false); break; - default: throw new CABlobParserException("Invalid type"); - } + var result = (SerializationType)reader.ReadByte() switch { + SerializationType.Boolean => module.CorLibTypes.Boolean, + SerializationType.Char => module.CorLibTypes.Char, + SerializationType.I1 => module.CorLibTypes.SByte, + SerializationType.U1 => module.CorLibTypes.Byte, + SerializationType.I2 => module.CorLibTypes.Int16, + SerializationType.U2 => module.CorLibTypes.UInt16, + SerializationType.I4 => module.CorLibTypes.Int32, + SerializationType.U4 => module.CorLibTypes.UInt32, + SerializationType.I8 => module.CorLibTypes.Int64, + SerializationType.U8 => module.CorLibTypes.UInt64, + SerializationType.R4 => module.CorLibTypes.Single, + SerializationType.R8 => module.CorLibTypes.Double, + SerializationType.String => module.CorLibTypes.String, + SerializationType.SZArray => new SZArraySig(ReadFieldOrPropType()), + SerializationType.Type => new ClassSig(module.CorLibTypes.GetTypeRef("System", "Type")), + SerializationType.TaggedObject => module.CorLibTypes.Object, + SerializationType.Enum => ReadType(false), + _ => throw new CABlobParserException("Invalid type"), + }; recursionCounter.Decrement(); return result; } @@ -606,18 +587,11 @@ UTF8String ReadUTF8String() { if (reader.ReadByte() == 0xFF) return null; reader.Position--; - uint len; - if (!reader.ReadCompressedUInt32(out len)) + if (!reader.TryReadCompressedUInt32(out uint len)) throw new CABlobParserException("Could not read compressed UInt32"); if (len == 0) return UTF8String.Empty; return new UTF8String(reader.ReadBytes((int)len)); } - - /// - public void Dispose() { - if (ownReader && reader != null) - reader.Dispose(); - } } } diff --git a/src/DotNet/DeclSecurity.cs b/src/DotNet/DeclSecurity.cs index 905387062..ff9a12763 100644 --- a/src/DotNet/DeclSecurity.cs +++ b/src/DotNet/DeclSecurity.cs @@ -5,47 +5,37 @@ using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif +using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// /// A high-level representation of a row in the DeclSecurity table /// [DebuggerDisplay("{Action} Count={SecurityAttributes.Count}")] - public abstract class DeclSecurity : IHasCustomAttribute { + public abstract class DeclSecurity : IHasCustomAttribute, IHasCustomDebugInformation { /// /// The row id in its table /// protected uint rid; /// - public MDToken MDToken { - get { return new MDToken(Table.DeclSecurity, rid); } - } + public MDToken MDToken => new MDToken(Table.DeclSecurity, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 8; } - } + public int HasCustomAttributeTag => 8; /// /// From column DeclSecurity.Action /// public SecurityAction Action { - get { return action; } - set { action = value; } + get => action; + set => action = value; } /// protected SecurityAction action; @@ -53,26 +43,25 @@ public SecurityAction Action { /// /// From column DeclSecurity.PermissionSet /// - public ThreadSafe.IList SecurityAttributes { + public IList SecurityAttributes { get { - if (securityAttributes == null) + if (securityAttributes is null) InitializeSecurityAttributes(); return securityAttributes; } } /// - protected ThreadSafe.IList securityAttributes; + protected IList securityAttributes; /// Initializes - protected virtual void InitializeSecurityAttributes() { - Interlocked.CompareExchange(ref securityAttributes, ThreadSafeListCreator.Create(), null); - } + protected virtual void InitializeSecurityAttributes() => + Interlocked.CompareExchange(ref securityAttributes, new List(), null); /// /// Gets all custom attributes /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -80,21 +69,38 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 8; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// /// true if is not empty /// - public bool HasSecurityAttributes { - get { return SecurityAttributes.Count > 0; } - } + public bool HasSecurityAttributes => SecurityAttributes.Count > 0; /// /// Gets the blob data or null if there's none @@ -103,23 +109,21 @@ public bool HasSecurityAttributes { public abstract byte[] GetBlob(); /// - /// Returns the .NET 1.x XML string or null if it's not a .NET 1.x format + /// Returns the .NET Framework 1.x XML string or null if it's not a .NET Framework 1.x format /// /// - public string GetNet1xXmlString() { - return GetNet1xXmlStringInternal(SecurityAttributes); - } + public string GetNet1xXmlString() => GetNet1xXmlStringInternal(SecurityAttributes); internal static string GetNet1xXmlStringInternal(IList secAttrs) { - if (secAttrs == null || secAttrs.Count != 1) + if (secAttrs is null || secAttrs.Count != 1) return null; var sa = secAttrs[0]; - if (sa == null || sa.TypeFullName != "System.Security.Permissions.PermissionSetAttribute") + if (sa is null || sa.TypeFullName != "System.Security.Permissions.PermissionSetAttribute") return null; if (sa.NamedArguments.Count != 1) return null; var na = sa.NamedArguments[0]; - if (na == null || !na.IsProperty || na.Name != "XML") + if (na is null || !na.IsProperty || na.Name != "XML") return null; if (na.ArgumentType.GetElementType() != ElementType.String) return null; @@ -127,10 +131,9 @@ internal static string GetNet1xXmlStringInternal(IList secAtt if (arg.Type.GetElementType() != ElementType.String) return null; var utf8 = arg.Value as UTF8String; - if ((object)utf8 != null) + if (utf8 is not null) return utf8; - var s = arg.Value as string; - if (s != null) + if (arg.Value is string s) return s; return null; } @@ -153,13 +156,11 @@ public DeclSecurityUser() { /// The security attributes (now owned by this) public DeclSecurityUser(SecurityAction action, IList securityAttrs) { this.action = action; - this.securityAttributes = ThreadSafeListCreator.MakeThreadSafe(securityAttrs); + securityAttributes = securityAttrs; } /// - public override byte[] GetBlob() { - return null; - } + public override byte[] GetBlob() => null; } /// @@ -173,9 +174,7 @@ sealed class DeclSecurityMD : DeclSecurity, IMDTokenProviderMD { readonly uint permissionSet; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override void InitializeSecurityAttributes() { @@ -186,11 +185,19 @@ protected override void InitializeSecurityAttributes() { /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.DeclSecurity, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.DeclSecurity, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + var gpContext = new GenericParamContext(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), gpContext, list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -200,20 +207,21 @@ protected override void InitializeCustomAttributes() { /// If is invalid public DeclSecurityMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.DeclSecurityTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("DeclSecurity rid {0} does not exist", rid)); + throw new BadImageFormatException($"DeclSecurity rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - this.permissionSet = readerModule.TablesStream.ReadDeclSecurityRow(origRid, out this.action); + bool b = readerModule.TablesStream.TryReadDeclSecurityRow(origRid, out var row); + Debug.Assert(b); + permissionSet = row.PermissionSet; + action = (SecurityAction)row.Action; } /// - public override byte[] GetBlob() { - return readerModule.BlobStream.Read(permissionSet); - } + public override byte[] GetBlob() => readerModule.BlobStream.Read(permissionSet); } } diff --git a/src/DotNet/DeclSecurityReader.cs b/src/DotNet/DeclSecurityReader.cs index 6b9c975c2..6215237d9 100644 --- a/src/DotNet/DeclSecurityReader.cs +++ b/src/DotNet/DeclSecurityReader.cs @@ -1,22 +1,15 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; -using System.Text; +using System.Collections.Generic; using dnlib.IO; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif namespace dnlib.DotNet { /// /// Reads DeclSecurity blobs /// - public struct DeclSecurityReader : IDisposable { - readonly IBinaryReader reader; + public struct DeclSecurityReader { + DataReader reader; readonly ModuleDef module; readonly GenericParamContext gpContext; @@ -26,9 +19,7 @@ public struct DeclSecurityReader : IDisposable { /// Module that will own the returned list /// #Blob offset of DeclSecurity signature /// A list of s - public static ThreadSafe.IList Read(ModuleDefMD module, uint sig) { - return Read(module, module.BlobStream.CreateStream(sig), new GenericParamContext()); - } + public static IList Read(ModuleDefMD module, uint sig) => Read(module, module.BlobStream.CreateReader(sig), new GenericParamContext()); /// /// Reads a DeclSecurity blob @@ -37,9 +28,7 @@ public static ThreadSafe.IList Read(ModuleDefMD module, uint /// #Blob offset of DeclSecurity signature /// Generic parameter context /// A list of s - public static ThreadSafe.IList Read(ModuleDefMD module, uint sig, GenericParamContext gpContext) { - return Read(module, module.BlobStream.CreateStream(sig), gpContext); - } + public static IList Read(ModuleDefMD module, uint sig, GenericParamContext gpContext) => Read(module, module.BlobStream.CreateReader(sig), gpContext); /// /// Reads a DeclSecurity blob @@ -47,9 +36,7 @@ public static ThreadSafe.IList Read(ModuleDefMD module, uint /// Module that will own the returned list /// DeclSecurity blob /// A list of s - public static ThreadSafe.IList Read(ModuleDef module, byte[] blob) { - return Read(module, MemoryImageStream.Create(blob), new GenericParamContext()); - } + public static IList Read(ModuleDef module, byte[] blob) => Read(module, ByteArrayDataReaderFactory.CreateReader(blob), new GenericParamContext()); /// /// Reads a DeclSecurity blob @@ -58,9 +45,7 @@ public static ThreadSafe.IList Read(ModuleDef module, byte[] /// DeclSecurity blob /// Generic parameter context/// /// A list of s - public static ThreadSafe.IList Read(ModuleDef module, byte[] blob, GenericParamContext gpContext) { - return Read(module, MemoryImageStream.Create(blob), gpContext); - } + public static IList Read(ModuleDef module, byte[] blob, GenericParamContext gpContext) => Read(module, ByteArrayDataReaderFactory.CreateReader(blob), gpContext); /// /// Reads a DeclSecurity blob @@ -68,9 +53,7 @@ public static ThreadSafe.IList Read(ModuleDef module, byte[] /// Module that will own the returned list /// DeclSecurity stream that will be owned by us /// A list of s - public static ThreadSafe.IList Read(ModuleDef module, IBinaryReader signature) { - return Read(module, signature, new GenericParamContext()); - } + public static IList Read(ModuleDef module, DataReader signature) => Read(module, signature, new GenericParamContext()); /// /// Reads a DeclSecurity blob @@ -79,21 +62,21 @@ public static ThreadSafe.IList Read(ModuleDef module, IBinary /// DeclSecurity stream that will be owned by us /// Generic parameter context /// A list of s - public static ThreadSafe.IList Read(ModuleDef module, IBinaryReader signature, GenericParamContext gpContext) { - using (var reader = new DeclSecurityReader(module, signature, gpContext)) - return reader.Read(); + public static IList Read(ModuleDef module, DataReader signature, GenericParamContext gpContext) { + var reader = new DeclSecurityReader(module, signature, gpContext); + return reader.Read(); } - DeclSecurityReader(ModuleDef module, IBinaryReader reader, GenericParamContext gpContext) { + DeclSecurityReader(ModuleDef module, DataReader reader, GenericParamContext gpContext) { this.reader = reader; this.module = module; this.gpContext = gpContext; } - ThreadSafe.IList Read() { + IList Read() { try { if (reader.Position >= reader.Length) - return ThreadSafeListCreator.Create(); + return new List(); if (reader.ReadByte() == '.') return ReadBinaryFormat(); @@ -101,26 +84,26 @@ ThreadSafe.IList Read() { return ReadXmlFormat(); } catch { - return ThreadSafeListCreator.Create(); + return new List(); } } /// - /// Reads the new (.NET 2.0+) DeclSecurity blob format + /// Reads the new (.NET Framework 2.0+) DeclSecurity blob format /// /// - ThreadSafe.IList ReadBinaryFormat() { + IList ReadBinaryFormat() { int numAttrs = (int)reader.ReadCompressedUInt32(); - var list = ThreadSafeListCreator.Create(numAttrs); + var list = new List(numAttrs); for (int i = 0; i < numAttrs; i++) { var name = ReadUTF8String(); // Use CA search rules. Some tools don't write the fully qualified name. var attrRef = TypeNameParser.ParseReflection(module, UTF8String.ToSystemStringOrEmpty(name), new CAAssemblyRefFinder(module), gpContext); - int blobLength = (int)reader.ReadCompressedUInt32(); + /*int blobLength = (int)*/reader.ReadCompressedUInt32(); int numNamedArgs = (int)reader.ReadCompressedUInt32(); - var namedArgs = CustomAttributeReader.ReadNamedArguments(module, reader, numNamedArgs, gpContext); - if (namedArgs == null) + var namedArgs = CustomAttributeReader.ReadNamedArguments(module, ref reader, numNamedArgs, gpContext); + if (namedArgs is null) throw new ApplicationException("Could not read named arguments"); list.Add(new SecurityAttribute(attrRef, namedArgs)); } @@ -129,24 +112,18 @@ ThreadSafe.IList ReadBinaryFormat() { } /// - /// Reads the old (.NET 1.x) DeclSecurity blob format + /// Reads the old (.NET Framework 1.x) DeclSecurity blob format /// /// - ThreadSafe.IList ReadXmlFormat() { - var xml = Encoding.Unicode.GetString(reader.ReadAllBytes()); + IList ReadXmlFormat() { + var xml = reader.ReadUtf16String((int)reader.Length / 2); var sa = SecurityAttribute.CreateFromXml(module, xml); - return ThreadSafeListCreator.Create(sa); + return new List { sa }; } UTF8String ReadUTF8String() { uint len = reader.ReadCompressedUInt32(); return len == 0 ? UTF8String.Empty : new UTF8String(reader.ReadBytes((int)len)); } - - /// - public void Dispose() { - if (reader != null) - reader.Dispose(); - } } } diff --git a/src/DotNet/ElementType.cs b/src/DotNet/ElementType.cs index 341ca74a8..df727e8c0 100644 --- a/src/DotNet/ElementType.cs +++ b/src/DotNet/ElementType.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// See CorHdr.h/CorElementType /// @@ -111,22 +111,13 @@ public static bool IsPrimitive(this ElementType etype) { } } - /// - /// Returns the size of the element type in bytes or -1 if it's unknown - /// - /// Element type - /// - public static int GetPrimitiveSize(this ElementType etype) { - return GetPrimitiveSize(etype, -1); - } - /// /// Returns the size of the element type in bytes or -1 if it's unknown /// /// Element type /// Size of a pointer /// - public static int GetPrimitiveSize(this ElementType etype, int ptrSize) { + public static int GetPrimitiveSize(this ElementType etype, int ptrSize = -1) { switch (etype) { case ElementType.Boolean: case ElementType.I1: diff --git a/src/DotNet/Emit/Code.cs b/src/DotNet/Emit/Code.cs index 59a6fedc8..9491639cd 100644 --- a/src/DotNet/Emit/Code.cs +++ b/src/DotNet/Emit/Code.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet.Emit { +namespace dnlib.DotNet.Emit { /// /// A CIL opcode. If the high byte is 0 or if it's , it's a 1-byte opcode, /// else it's a two-byte opcode and the highest byte is the first byte of the opcode. @@ -173,7 +173,7 @@ public enum Code : ushort { Neg = 0x0065, Newarr = 0x008D, Newobj = 0x0073, -// No = 0xFE19, // Not supported by MS' CLI and must be parsed as an opcode without an operand + No = 0xFE19, Nop = 0x0000, Not = 0x0066, Or = 0x0060, @@ -240,18 +240,52 @@ public enum Code : ushort { } public static partial class Extensions { + /// + /// Determines whether a is experimental + /// + /// The code + /// true if the is experimental; otherwise, false + public static bool IsExperimental(this Code code) { + byte hi = (byte)((ushort)code >> 8); + + return hi >= 0xF0 && hi <= 0xFB; + } + /// /// Converts a to an /// /// The code /// A or null if it's invalid public static OpCode ToOpCode(this Code code) { - int hi = (ushort)code >> 8; - int lo = (byte)code; + byte hi = (byte)((ushort)code >> 8); + byte lo = (byte)code; + if (hi == 0) + return OpCodes.OneByteOpCodes[lo]; + if (hi == 0xFE) + return OpCodes.TwoByteOpCodes[lo]; + if (code == Code.UNKNOWN1) + return OpCodes.UNKNOWN1; + if (code == Code.UNKNOWN2) + return OpCodes.UNKNOWN2; + return null; + } + + /// + /// Converts a to an , using a module context to look + /// up potential experimental opcodes + /// + /// The code + /// The module context + /// A or null if it's invalid + public static OpCode ToOpCode(this Code code, ModuleContext context) { + byte hi = (byte)((ushort)code >> 8); + byte lo = (byte)code; if (hi == 0) return OpCodes.OneByteOpCodes[lo]; if (hi == 0xFE) return OpCodes.TwoByteOpCodes[lo]; + if (context.GetExperimentalOpCode(hi, lo) is OpCode op) + return op; if (code == Code.UNKNOWN1) return OpCodes.UNKNOWN1; if (code == Code.UNKNOWN2) diff --git a/src/DotNet/Emit/DynamicMethodBodyReader.cs b/src/DotNet/Emit/DynamicMethodBodyReader.cs index cbe8435ee..a90ff326c 100644 --- a/src/DotNet/Emit/DynamicMethodBodyReader.cs +++ b/src/DotNet/Emit/DynamicMethodBodyReader.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; @@ -9,12 +9,30 @@ using dnlib.IO; namespace dnlib.DotNet.Emit { + /// + /// options + /// + [Flags] + public enum DynamicMethodBodyReaderOptions { + /// + /// No option is enabled + /// + None = 0, + + /// + /// Some fields/methods have an unknown declaring type and don't have a context with + /// that information. If this is enabled, the reader will try to guess it but it doesn't + /// always work. If you get an , try enabling this option. + /// + UnknownDeclaringType = 0x00000001, + } + /// /// Reads code from a DynamicMethod /// public class DynamicMethodBodyReader : MethodBodyReaderBase, ISignatureReaderHelper { static readonly ReflectionFieldInfo rtdmOwnerFieldInfo = new ReflectionFieldInfo("m_owner"); - static readonly ReflectionFieldInfo dmResolverFieldInfo = new ReflectionFieldInfo("m_resolver"); + static readonly ReflectionFieldInfo dmResolverFieldInfo = new ReflectionFieldInfo("m_resolver", "_resolver"); static readonly ReflectionFieldInfo rslvCodeFieldInfo = new ReflectionFieldInfo("m_code"); static readonly ReflectionFieldInfo rslvDynamicScopeFieldInfo = new ReflectionFieldInfo("m_scope"); static readonly ReflectionFieldInfo rslvMethodFieldInfo = new ReflectionFieldInfo("m_method"); @@ -37,6 +55,8 @@ public class DynamicMethodBodyReader : MethodBodyReaderBase, ISignatureReaderHel static readonly ReflectionFieldInfo ehEndFinallyFieldInfo = new ReflectionFieldInfo("m_endFinally"); static readonly ReflectionFieldInfo vamMethodFieldInfo = new ReflectionFieldInfo("m_method"); static readonly ReflectionFieldInfo vamDynamicMethodFieldInfo = new ReflectionFieldInfo("m_dynamicMethod"); + static readonly ReflectionFieldInfo dmDynamicILInfoFieldInfo = new ReflectionFieldInfo("m_DynamicILInfo", "_dynamicILInfo"); + static readonly ReflectionFieldInfo dynILInfoMaxStackFieldInfo = new ReflectionFieldInfo("m_maxStackSize"); readonly ModuleDef module; readonly Importer importer; @@ -44,18 +64,19 @@ public class DynamicMethodBodyReader : MethodBodyReaderBase, ISignatureReaderHel readonly MethodDef method; readonly int codeSize; readonly int maxStack; + readonly bool initLocals; readonly List tokens; readonly IList ehInfos; readonly byte[] ehHeader; + readonly string methodName; + readonly DynamicMethodBodyReaderOptions options; class ReflectionFieldInfo { SR.FieldInfo fieldInfo; readonly string fieldName1; readonly string fieldName2; - public ReflectionFieldInfo(string fieldName) { - this.fieldName1 = fieldName; - } + public ReflectionFieldInfo(string fieldName) => fieldName1 = fieldName; public ReflectionFieldInfo(string fieldName1, string fieldName2) { this.fieldName1 = fieldName1; @@ -63,26 +84,26 @@ public ReflectionFieldInfo(string fieldName1, string fieldName2) { } public object Read(object instance) { - if (fieldInfo == null) + if (fieldInfo is null) InitializeField(instance.GetType()); - if (fieldInfo == null) - throw new Exception(string.Format("Couldn't find field '{0}' or '{1}'", fieldName1, fieldName2)); + if (fieldInfo is null) + throw new Exception($"Couldn't find field '{fieldName1}' or '{fieldName2}'"); return fieldInfo.GetValue(instance); } public bool Exists(object instance) { InitializeField(instance.GetType()); - return fieldInfo != null; + return fieldInfo is not null; } void InitializeField(Type type) { - if (fieldInfo != null) + if (fieldInfo is not null) return; - var flags = SR.BindingFlags.Instance | SR.BindingFlags.Public | SR.BindingFlags.NonPublic; + const SR.BindingFlags flags = SR.BindingFlags.Instance | SR.BindingFlags.Public | SR.BindingFlags.NonPublic; fieldInfo = type.GetField(fieldName1, flags); - if (fieldInfo == null && fieldName2 != null) + if (fieldInfo is null && fieldName2 is not null) fieldInfo = type.GetField(fieldName2, flags); } } @@ -106,62 +127,97 @@ public DynamicMethodBodyReader(ModuleDef module, object obj) /// created by DynamicMethod.CreateDelegate(), a DynamicMethod instance, a RTDynamicMethod /// instance or a DynamicResolver instance. /// Generic parameter context - public DynamicMethodBodyReader(ModuleDef module, object obj, GenericParamContext gpContext) { + public DynamicMethodBodyReader(ModuleDef module, object obj, GenericParamContext gpContext) + : this(module, obj, new Importer(module, ImporterOptions.TryToUseDefs, gpContext), DynamicMethodBodyReaderOptions.None) { + } + + /// + /// Constructor + /// + /// Module that will own the method body + /// This can be one of several supported types: the delegate instance + /// created by DynamicMethod.CreateDelegate(), a DynamicMethod instance, a RTDynamicMethod + /// instance or a DynamicResolver instance. + /// Importer + public DynamicMethodBodyReader(ModuleDef module, object obj, Importer importer) + : this(module, obj, importer, DynamicMethodBodyReaderOptions.None) { + } + + /// + /// Constructor + /// + /// Module that will own the method body + /// This can be one of several supported types: the delegate instance + /// created by DynamicMethod.CreateDelegate(), a DynamicMethod instance, a RTDynamicMethod + /// instance or a DynamicResolver instance. + /// Importer + /// Options + public DynamicMethodBodyReader(ModuleDef module, object obj, Importer importer, DynamicMethodBodyReaderOptions options) + : base(module.Context) { this.module = module; - this.importer = new Importer(module, ImporterOptions.TryToUseDefs, gpContext); - this.gpContext = gpContext; + this.importer = importer; + this.options = options; + gpContext = importer.gpContext; + methodName = null; - if (obj == null) - throw new ArgumentNullException("obj"); + if (obj is null) + throw new ArgumentNullException(nameof(obj)); - var del = obj as Delegate; - if (del != null) { + if (obj is Delegate del) { obj = del.Method; - if (obj == null) - throw new Exception("Delegate.Method == null"); + if (obj is null) + throw new Exception("Delegate.Method is null"); } if (obj.GetType().ToString() == "System.Reflection.Emit.DynamicMethod+RTDynamicMethod") { obj = rtdmOwnerFieldInfo.Read(obj) as DynamicMethod; - if (obj == null) + if (obj is null) throw new Exception("RTDynamicMethod.m_owner is null or invalid"); } - if (obj is DynamicMethod) { - obj = dmResolverFieldInfo.Read(obj); - if (obj == null) + if (obj is DynamicMethod dynMethod) { + methodName = dynMethod.Name; + obj = dmResolverFieldInfo.Read(obj) ?? dmDynamicILInfoFieldInfo.Read(obj);; + if (obj is null) throw new Exception("No resolver found"); } - if (obj.GetType().ToString() != "System.Reflection.Emit.DynamicResolver") - throw new Exception("Couldn't find DynamicResolver"); + string objTypeName = obj.GetType().ToString(); + bool isILInfo = objTypeName == "System.Reflection.Emit.DynamicILInfo"; + if (objTypeName != "System.Reflection.Emit.DynamicResolver" && !isILInfo) + throw new Exception("Couldn't find DynamicResolver or DynamicILInfo"); var code = rslvCodeFieldInfo.Read(obj) as byte[]; - if (code == null) + if (code is null) throw new Exception("No code"); codeSize = code.Length; - var delMethod = rslvMethodFieldInfo.Read(obj) as SR.MethodBase; - if (delMethod == null) + var delMethod = rslvMethodFieldInfo.Read(obj) as DynamicMethod; + if (delMethod is null) throw new Exception("No method"); - maxStack = (int)rslvMaxStackFieldInfo.Read(obj); + initLocals = delMethod.InitLocals; + maxStack = isILInfo ? (int)dynILInfoMaxStackFieldInfo.Read(obj) : (int)rslvMaxStackFieldInfo.Read(obj); var scope = rslvDynamicScopeFieldInfo.Read(obj); - if (scope == null) + if (scope is null) throw new Exception("No scope"); var tokensList = scopeTokensFieldInfo.Read(scope) as System.Collections.IList; - if (tokensList == null) + if (tokensList is null) throw new Exception("No tokens"); tokens = new List(tokensList.Count); for (int i = 0; i < tokensList.Count; i++) tokens.Add(tokensList[i]); - ehInfos = (IList)rslvExceptionsFieldInfo.Read(obj); - ehHeader = rslvExceptionHeaderFieldInfo.Read(obj) as byte[]; + if (isILInfo) + ehHeader = rslvExceptionsFieldInfo.Read(obj) as byte[]; + else { + ehInfos = (IList)rslvExceptionsFieldInfo.Read(obj); + ehHeader = rslvExceptionHeaderFieldInfo.Read(obj) as byte[]; + } UpdateLocals(rslvLocalsFieldInfo.Read(obj) as byte[]); - this.reader = MemoryImageStream.Create(code); - this.method = CreateMethodDef(delMethod); - this.parameters = this.method.Parameters; + reader = ByteArrayDataReaderFactory.CreateReader(code); + method = CreateMethodDef(delMethod); + parameters = method.Parameters; } class ExceptionInfo { @@ -176,12 +232,14 @@ class ExceptionInfo { } static List CreateExceptionInfos(IList ehInfos) { - if (ehInfos == null) + if (ehInfos is null) return new List(); var infos = new List(ehInfos.Count); - foreach (var ehInfo in ehInfos) { + int count = ehInfos.Count; + for (int i = 0; i < count; i++) { + var ehInfo = ehInfos[i]; var eh = new ExceptionInfo { CatchAddr = (int[])ehCatchAddrFieldInfo.Read(ehInfo), CatchClass = (Type[])ehCatchClassFieldInfo.Read(ehInfo), @@ -199,15 +257,17 @@ static List CreateExceptionInfos(IList ehInfos) { } void UpdateLocals(byte[] localsSig) { - if (localsSig == null || localsSig.Length == 0) + if (localsSig is null || localsSig.Length == 0) return; var sig = SignatureReader.ReadSig(this, module.CorLibTypes, localsSig, gpContext) as LocalSig; - if (sig == null) + if (sig is null) return; - foreach (var local in sig.Locals) - locals.Add(new Local(local)); + var sigLocals = sig.Locals; + int count = sigLocals.Count; + for (int i = 0; i < count; i++) + locals.Add(new Local(sigLocals[i])); } MethodDef CreateMethodDef(SR.MethodBase delMethod) { @@ -231,8 +291,7 @@ MethodDef CreateMethodDef(SR.MethodBase delMethod) { } TypeSig GetReturnType(SR.MethodBase mb) { - var mi = mb as SR.MethodInfo; - if (mi != null) + if (mb is SR.MethodInfo mi) return importer.ImportAsTypeSig(mi.ReturnType); return module.CorLibTypes.Void; } @@ -256,26 +315,30 @@ public bool Read() { } void CreateExceptionHandlers() { - if (ehHeader != null) { + if (ehHeader is not null) { + if (ehHeader.Length < 4) + return; var reader = new BinaryReader(new MemoryStream(ehHeader)); - byte b = (byte)reader.ReadByte(); + byte b = reader.ReadByte(); if ((b & 0x40) == 0) { // DynamicResolver only checks bit 6 // Calculate num ehs exactly the same way that DynamicResolver does int numHandlers = (ushort)((reader.ReadByte() - 2) / 12); - reader.ReadInt16(); + reader.ReadUInt16(); for (int i = 0; i < numHandlers; i++) { + if (reader.BaseStream.Position + 12 > reader.BaseStream.Length) + break; var eh = new ExceptionHandler(); - eh.HandlerType = (ExceptionHandlerType)reader.ReadInt16(); + eh.HandlerType = (ExceptionHandlerType)reader.ReadUInt16(); int offs = reader.ReadUInt16(); eh.TryStart = GetInstructionThrow((uint)offs); - eh.TryEnd = GetInstruction((uint)(reader.ReadSByte() + offs)); + eh.TryEnd = GetInstruction((uint)(reader.ReadByte() + offs)); offs = reader.ReadUInt16(); eh.HandlerStart = GetInstructionThrow((uint)offs); - eh.HandlerEnd = GetInstruction((uint)(reader.ReadSByte() + offs)); + eh.HandlerEnd = GetInstruction((uint)(reader.ReadByte() + offs)); - if (eh.HandlerType == ExceptionHandlerType.Catch) + if (eh.IsCatch) eh.CatchType = ReadToken(reader.ReadUInt32()) as ITypeDefOrRef; - else if (eh.HandlerType == ExceptionHandlerType.Filter) + else if (eh.IsFilter) eh.FilterStart = GetInstruction(reader.ReadUInt32()); else reader.ReadUInt32(); @@ -287,18 +350,20 @@ void CreateExceptionHandlers() { reader.BaseStream.Position--; int numHandlers = (ushort)(((reader.ReadUInt32() >> 8) - 4) / 24); for (int i = 0; i < numHandlers; i++) { + if (reader.BaseStream.Position + 24 > reader.BaseStream.Length) + break; var eh = new ExceptionHandler(); - eh.HandlerType = (ExceptionHandlerType)reader.ReadInt32(); - int offs = reader.ReadInt32(); + eh.HandlerType = (ExceptionHandlerType)reader.ReadUInt32(); + var offs = reader.ReadUInt32(); eh.TryStart = GetInstructionThrow((uint)offs); - eh.TryEnd = GetInstruction((uint)(reader.ReadInt32() + offs)); - offs = reader.ReadInt32(); + eh.TryEnd = GetInstruction((uint)(reader.ReadUInt32() + offs)); + offs = reader.ReadUInt32(); eh.HandlerStart = GetInstructionThrow((uint)offs); - eh.HandlerEnd = GetInstruction((uint)(reader.ReadInt32() + offs)); + eh.HandlerEnd = GetInstruction((uint)(reader.ReadUInt32() + offs)); - if (eh.HandlerType == ExceptionHandlerType.Catch) + if (eh.IsCatch) eh.CatchType = ReadToken(reader.ReadUInt32()) as ITypeDefOrRef; - else if (eh.HandlerType == ExceptionHandlerType.Filter) + else if (eh.IsFilter) eh.FilterStart = GetInstruction(reader.ReadUInt32()); else reader.ReadUInt32(); @@ -307,7 +372,7 @@ void CreateExceptionHandlers() { } } } - else if (ehInfos != null) { + else if (ehInfos is not null) { foreach (var ehInfo in CreateExceptionInfos(ehInfos)) { var tryStart = GetInstructionThrow((uint)ehInfo.StartAddr); var tryEnd = GetInstruction((uint)ehInfo.EndAddr); @@ -316,7 +381,7 @@ void CreateExceptionHandlers() { var eh = new ExceptionHandler(); eh.HandlerType = (ExceptionHandlerType)ehInfo.Type[i]; eh.TryStart = tryStart; - eh.TryEnd = eh.HandlerType == ExceptionHandlerType.Finally ? endFinally : tryEnd; + eh.TryEnd = eh.IsFinally ? endFinally : tryEnd; eh.FilterStart = null; // not supported by DynamicMethod.ILGenerator eh.HandlerStart = GetInstructionThrow((uint)ehInfo.CatchAddr[i]); eh.HandlerEnd = GetInstruction((uint)ehInfo.CatchEndAddr[i]); @@ -332,45 +397,33 @@ void CreateExceptionHandlers() { /// /// A new instance public MethodDef GetMethod() { - bool initLocals = true; var cilBody = new CilBody(initLocals, instructions, exceptionHandlers, locals); cilBody.MaxStack = (ushort)Math.Min(maxStack, ushort.MaxValue); instructions = null; exceptionHandlers = null; locals = null; method.Body = cilBody; + method.Name = methodName; return method; } /// - protected override IField ReadInlineField(Instruction instr) { - return ReadToken(reader.ReadUInt32()) as IField; - } + protected override IField ReadInlineField(Instruction instr) => ReadToken(reader.ReadUInt32()) as IField; /// - protected override IMethod ReadInlineMethod(Instruction instr) { - return ReadToken(reader.ReadUInt32()) as IMethod; - } + protected override IMethod ReadInlineMethod(Instruction instr) => ReadToken(reader.ReadUInt32()) as IMethod; /// - protected override MethodSig ReadInlineSig(Instruction instr) { - return ReadToken(reader.ReadUInt32()) as MethodSig; - } + protected override MethodSig ReadInlineSig(Instruction instr) => ReadToken(reader.ReadUInt32()) as MethodSig; /// - protected override string ReadInlineString(Instruction instr) { - return ReadToken(reader.ReadUInt32()) as string ?? string.Empty; - } + protected override string ReadInlineString(Instruction instr) => ReadToken(reader.ReadUInt32()) as string ?? string.Empty; /// - protected override ITokenOperand ReadInlineTok(Instruction instr) { - return ReadToken(reader.ReadUInt32()) as ITokenOperand; - } + protected override ITokenOperand ReadInlineTok(Instruction instr) => ReadToken(reader.ReadUInt32()) as ITokenOperand; /// - protected override ITypeDefOrRef ReadInlineType(Instruction instr) { - return ReadToken(reader.ReadUInt32()) as ITypeDefOrRef; - } + protected override ITypeDefOrRef ReadInlineType(Instruction instr) => ReadToken(reader.ReadUInt32()) as ITypeDefOrRef; object ReadToken(uint token) { uint rid = token & 0x00FFFFFF; @@ -398,11 +451,18 @@ object ReadToken(uint token) { IMethod ImportMethod(uint rid) { var obj = Resolve(rid); - if (obj == null) + if (obj is null) return null; - if (obj is RuntimeMethodHandle) - return importer.Import(SR.MethodBase.GetMethodFromHandle((RuntimeMethodHandle)obj)); + if (obj is RuntimeMethodHandle) { + if ((options & DynamicMethodBodyReaderOptions.UnknownDeclaringType) != 0) { + // Sometimes it's a generic type but obj != `GenericMethodInfo`, so pass in 'default' and the + // runtime will try to figure out the declaring type. https://github.com/0xd4d/dnlib/issues/298 + return importer.Import(SR.MethodBase.GetMethodFromHandle((RuntimeMethodHandle)obj, default)); + } + else + return importer.Import(SR.MethodBase.GetMethodFromHandle((RuntimeMethodHandle)obj)); + } if (obj.GetType().ToString() == "System.Reflection.Emit.GenericMethodInfo") { var context = (RuntimeTypeHandle)gmiContextFieldInfo.Read(obj); @@ -417,8 +477,7 @@ IMethod ImportMethod(uint rid) { obj = method; } - var dm = obj as DynamicMethod; - if (dm != null) + if (obj is DynamicMethod dm) throw new Exception("DynamicMethod calls another DynamicMethod"); return null; @@ -426,13 +485,13 @@ IMethod ImportMethod(uint rid) { SR.MethodInfo GetVarArgMethod(object obj) { if (vamDynamicMethodFieldInfo.Exists(obj)) { - // .NET 4.0+ + // .NET Framework 4.0+ var method = vamMethodFieldInfo.Read(obj) as SR.MethodInfo; var dynMethod = vamDynamicMethodFieldInfo.Read(obj) as DynamicMethod; return dynMethod ?? method; } else { - // .NET 2.0 + // .NET Framework 2.0 // This is either a DynamicMethod or a MethodInfo return vamMethodFieldInfo.Read(obj) as SR.MethodInfo; } @@ -440,11 +499,18 @@ SR.MethodInfo GetVarArgMethod(object obj) { IField ImportField(uint rid) { var obj = Resolve(rid); - if (obj == null) + if (obj is null) return null; - if (obj is RuntimeFieldHandle) - return importer.Import(SR.FieldInfo.GetFieldFromHandle((RuntimeFieldHandle)obj)); + if (obj is RuntimeFieldHandle) { + if ((options & DynamicMethodBodyReaderOptions.UnknownDeclaringType) != 0) { + // Sometimes it's a generic type but obj != `GenericFieldInfo`, so pass in 'default' and the + // runtime will try to figure out the declaring type. https://github.com/0xd4d/dnlib/issues/298 + return importer.Import(SR.FieldInfo.GetFieldFromHandle((RuntimeFieldHandle)obj, default)); + } + else + return importer.Import(SR.FieldInfo.GetFieldFromHandle((RuntimeFieldHandle)obj)); + } if (obj.GetType().ToString() == "System.Reflection.Emit.GenericFieldInfo") { var context = (RuntimeTypeHandle)gfiContextFieldInfo.Read(obj); @@ -465,7 +531,7 @@ ITypeDefOrRef ImportType(uint rid) { CallingConventionSig ImportSignature(uint rid) { var sig = Resolve(rid) as byte[]; - if (sig == null) + if (sig is null) return null; return SignatureReader.ReadSig(this, module.CorLibTypes, sig, gpContext); @@ -477,22 +543,27 @@ object Resolve(uint index) { return tokens[(int)index]; } + /// + public override void RestoreMethod(MethodDef method) { + base.RestoreMethod(method); + + var body = method.Body; + body.InitLocals = initLocals; + body.MaxStack = (ushort)Math.Min(maxStack, ushort.MaxValue); + } + ITypeDefOrRef ISignatureReaderHelper.ResolveTypeDefOrRef(uint codedToken, GenericParamContext gpContext) { - uint token; - if (!CodedToken.TypeDefOrRef.Decode(codedToken, out token)) + if (!CodedToken.TypeDefOrRef.Decode(codedToken, out uint token)) return null; - uint rid = MDToken.ToRID(token); switch (MDToken.ToTable(token)) { case Table.TypeDef: case Table.TypeRef: case Table.TypeSpec: - return ImportType(rid); + return module.ResolveToken(token) as ITypeDefOrRef; } return null; } - TypeSig ISignatureReaderHelper.ConvertRTInternalAddress(IntPtr address) { - return importer.ImportAsTypeSig(MethodTableToTypeConverter.Convert(address)); - } + TypeSig ISignatureReaderHelper.ConvertRTInternalAddress(IntPtr address) => importer.ImportAsTypeSig(MethodTableToTypeConverter.Convert(address)); } } diff --git a/src/DotNet/Emit/ExceptionHandler.cs b/src/DotNet/Emit/ExceptionHandler.cs index b4ecb1cd9..e98f6bc3d 100644 --- a/src/DotNet/Emit/ExceptionHandler.cs +++ b/src/DotNet/Emit/ExceptionHandler.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet.Emit { +namespace dnlib.DotNet.Emit { /// /// A CIL method exception handler /// @@ -34,7 +34,7 @@ public sealed class ExceptionHandler { public Instruction HandlerEnd; /// - /// The catch type if is + /// The catch type if is /// public ITypeDefOrRef CatchType; @@ -43,6 +43,26 @@ public sealed class ExceptionHandler { /// public ExceptionHandlerType HandlerType; + /// + /// Checks if it's a `catch` handler + /// + public bool IsCatch => ((uint)HandlerType & 7) == (uint)ExceptionHandlerType.Catch; + + /// + /// Checks if it's a `filter` handler + /// + public bool IsFilter => (HandlerType & ExceptionHandlerType.Filter) != 0; + + /// + /// Checks if it's a `finally` handler + /// + public bool IsFinally => (HandlerType & ExceptionHandlerType.Finally) != 0; + + /// + /// Checks if it's a `fault` handler + /// + public bool IsFault => (HandlerType & ExceptionHandlerType.Fault) != 0; + /// /// Default constructor /// @@ -53,8 +73,6 @@ public ExceptionHandler() { /// Constructor /// /// Exception clause type - public ExceptionHandler(ExceptionHandlerType handlerType) { - this.HandlerType = handlerType; - } + public ExceptionHandler(ExceptionHandlerType handlerType) => HandlerType = handlerType; } } diff --git a/src/DotNet/Emit/ExceptionHandlerType.cs b/src/DotNet/Emit/ExceptionHandlerType.cs index 57622149f..d5b80ef1a 100644 --- a/src/DotNet/Emit/ExceptionHandlerType.cs +++ b/src/DotNet/Emit/ExceptionHandlerType.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.DotNet.Emit { /// diff --git a/src/DotNet/Emit/Extensions.cs b/src/DotNet/Emit/Extensions.cs index 31f88b3d2..fcbb4f39c 100644 --- a/src/DotNet/Emit/Extensions.cs +++ b/src/DotNet/Emit/Extensions.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet.Emit { +namespace dnlib.DotNet.Emit { /// /// Extension methods /// diff --git a/src/DotNet/Emit/FlowControl.cs b/src/DotNet/Emit/FlowControl.cs index a01204541..d29b86fde 100644 --- a/src/DotNet/Emit/FlowControl.cs +++ b/src/DotNet/Emit/FlowControl.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet.Emit { +namespace dnlib.DotNet.Emit { /// /// CIL opcode flow control /// diff --git a/src/DotNet/Emit/Instruction.cs b/src/DotNet/Emit/Instruction.cs index ba8829cd4..3013665e7 100644 --- a/src/DotNet/Emit/Instruction.cs +++ b/src/DotNet/Emit/Instruction.cs @@ -1,9 +1,8 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using dnlib.DotNet.Pdb; -using dnlib.Threading; namespace dnlib.DotNet.Emit { /// @@ -40,9 +39,7 @@ public Instruction() { /// Constructor /// /// Opcode - public Instruction(OpCode opCode) { - this.OpCode = opCode; - } + public Instruction(OpCode opCode) => OpCode = opCode; /// /// Constructor @@ -50,8 +47,8 @@ public Instruction(OpCode opCode) { /// Opcode /// The operand public Instruction(OpCode opCode, object operand) { - this.OpCode = opCode; - this.Operand = operand; + OpCode = opCode; + Operand = operand; } /// @@ -61,7 +58,7 @@ public Instruction(OpCode opCode, object operand) { /// A new instance public static Instruction Create(OpCode opCode) { if (opCode.OperandType != OperandType.InlineNone) - throw new ArgumentException("Must be a no-operand opcode", "opCode"); + throw new ArgumentException("Must be a no-operand opcode", nameof(opCode)); return new Instruction(opCode); } @@ -73,7 +70,7 @@ public static Instruction Create(OpCode opCode) { /// A new instance public static Instruction Create(OpCode opCode, byte value) { if (opCode.Code != Code.Unaligned) - throw new ArgumentException("Opcode does not have a byte operand", "opCode"); + throw new ArgumentException("Opcode does not have a byte operand", nameof(opCode)); return new Instruction(opCode, value); } @@ -85,7 +82,7 @@ public static Instruction Create(OpCode opCode, byte value) { /// A new instance public static Instruction Create(OpCode opCode, sbyte value) { if (opCode.Code != Code.Ldc_I4_S) - throw new ArgumentException("Opcode does not have a sbyte operand", "opCode"); + throw new ArgumentException("Opcode does not have a sbyte operand", nameof(opCode)); return new Instruction(opCode, value); } @@ -97,7 +94,7 @@ public static Instruction Create(OpCode opCode, sbyte value) { /// A new instance public static Instruction Create(OpCode opCode, int value) { if (opCode.OperandType != OperandType.InlineI) - throw new ArgumentException("Opcode does not have an int32 operand", "opCode"); + throw new ArgumentException("Opcode does not have an int32 operand", nameof(opCode)); return new Instruction(opCode, value); } @@ -109,7 +106,7 @@ public static Instruction Create(OpCode opCode, int value) { /// A new instance public static Instruction Create(OpCode opCode, long value) { if (opCode.OperandType != OperandType.InlineI8) - throw new ArgumentException("Opcode does not have an int64 operand", "opCode"); + throw new ArgumentException("Opcode does not have an int64 operand", nameof(opCode)); return new Instruction(opCode, value); } @@ -121,7 +118,7 @@ public static Instruction Create(OpCode opCode, long value) { /// A new instance public static Instruction Create(OpCode opCode, float value) { if (opCode.OperandType != OperandType.ShortInlineR) - throw new ArgumentException("Opcode does not have a real4 operand", "opCode"); + throw new ArgumentException("Opcode does not have a real4 operand", nameof(opCode)); return new Instruction(opCode, value); } @@ -133,7 +130,7 @@ public static Instruction Create(OpCode opCode, float value) { /// A new instance public static Instruction Create(OpCode opCode, double value) { if (opCode.OperandType != OperandType.InlineR) - throw new ArgumentException("Opcode does not have a real8 operand", "opCode"); + throw new ArgumentException("Opcode does not have a real8 operand", nameof(opCode)); return new Instruction(opCode, value); } @@ -145,7 +142,7 @@ public static Instruction Create(OpCode opCode, double value) { /// A new instance public static Instruction Create(OpCode opCode, string s) { if (opCode.OperandType != OperandType.InlineString) - throw new ArgumentException("Opcode does not have a string operand", "opCode"); + throw new ArgumentException("Opcode does not have a string operand", nameof(opCode)); return new Instruction(opCode, s); } @@ -157,7 +154,7 @@ public static Instruction Create(OpCode opCode, string s) { /// A new instance public static Instruction Create(OpCode opCode, Instruction target) { if (opCode.OperandType != OperandType.ShortInlineBrTarget && opCode.OperandType != OperandType.InlineBrTarget) - throw new ArgumentException("Opcode does not have an instruction operand", "opCode"); + throw new ArgumentException("Opcode does not have an instruction operand", nameof(opCode)); return new Instruction(opCode, target); } @@ -169,8 +166,8 @@ public static Instruction Create(OpCode opCode, Instruction target) { /// A new instance public static Instruction Create(OpCode opCode, IList targets) { if (opCode.OperandType != OperandType.InlineSwitch) - throw new ArgumentException("Opcode does not have a targets array operand", "opCode"); - return new Instruction(opCode, ThreadSafeListCreator.MakeThreadSafe(targets)); + throw new ArgumentException("Opcode does not have a targets array operand", nameof(opCode)); + return new Instruction(opCode, targets); } /// @@ -181,7 +178,7 @@ public static Instruction Create(OpCode opCode, IList targets) { /// A new instance public static Instruction Create(OpCode opCode, ITypeDefOrRef type) { if (opCode.OperandType != OperandType.InlineType && opCode.OperandType != OperandType.InlineTok) - throw new ArgumentException("Opcode does not have a type operand", "opCode"); + throw new ArgumentException("Opcode does not have a type operand", nameof(opCode)); return new Instruction(opCode, type); } @@ -191,9 +188,7 @@ public static Instruction Create(OpCode opCode, ITypeDefOrRef type) { /// The opcode /// The type /// A new instance - public static Instruction Create(OpCode opCode, CorLibTypeSig type) { - return Create(opCode, type.TypeDefOrRef); - } + public static Instruction Create(OpCode opCode, CorLibTypeSig type) => Create(opCode, type.TypeDefOrRef); /// /// Creates a new instruction with a method/field operand @@ -203,7 +198,7 @@ public static Instruction Create(OpCode opCode, CorLibTypeSig type) { /// A new instance public static Instruction Create(OpCode opCode, MemberRef mr) { if (opCode.OperandType != OperandType.InlineField && opCode.OperandType != OperandType.InlineMethod && opCode.OperandType != OperandType.InlineTok) - throw new ArgumentException("Opcode does not have a field operand", "opCode"); + throw new ArgumentException("Opcode does not have a field operand", nameof(opCode)); return new Instruction(opCode, mr); } @@ -215,7 +210,7 @@ public static Instruction Create(OpCode opCode, MemberRef mr) { /// A new instance public static Instruction Create(OpCode opCode, IField field) { if (opCode.OperandType != OperandType.InlineField && opCode.OperandType != OperandType.InlineTok) - throw new ArgumentException("Opcode does not have a field operand", "opCode"); + throw new ArgumentException("Opcode does not have a field operand", nameof(opCode)); return new Instruction(opCode, field); } @@ -227,7 +222,7 @@ public static Instruction Create(OpCode opCode, IField field) { /// A new instance public static Instruction Create(OpCode opCode, IMethod method) { if (opCode.OperandType != OperandType.InlineMethod && opCode.OperandType != OperandType.InlineTok) - throw new ArgumentException("Opcode does not have a method operand", "opCode"); + throw new ArgumentException("Opcode does not have a method operand", nameof(opCode)); return new Instruction(opCode, method); } @@ -239,7 +234,7 @@ public static Instruction Create(OpCode opCode, IMethod method) { /// A new instance public static Instruction Create(OpCode opCode, ITokenOperand token) { if (opCode.OperandType != OperandType.InlineTok) - throw new ArgumentException("Opcode does not have a token operand", "opCode"); + throw new ArgumentException("Opcode does not have a token operand", nameof(opCode)); return new Instruction(opCode, token); } @@ -251,7 +246,7 @@ public static Instruction Create(OpCode opCode, ITokenOperand token) { /// A new instance public static Instruction Create(OpCode opCode, MethodSig methodSig) { if (opCode.OperandType != OperandType.InlineSig) - throw new ArgumentException("Opcode does not have a method sig operand", "opCode"); + throw new ArgumentException("Opcode does not have a method sig operand", nameof(opCode)); return new Instruction(opCode, methodSig); } @@ -263,7 +258,7 @@ public static Instruction Create(OpCode opCode, MethodSig methodSig) { /// A new instance public static Instruction Create(OpCode opCode, Parameter parameter) { if (opCode.OperandType != OperandType.ShortInlineVar && opCode.OperandType != OperandType.InlineVar) - throw new ArgumentException("Opcode does not have a method parameter operand", "opCode"); + throw new ArgumentException("Opcode does not have a method parameter operand", nameof(opCode)); return new Instruction(opCode, parameter); } @@ -275,7 +270,7 @@ public static Instruction Create(OpCode opCode, Parameter parameter) { /// A new instance public static Instruction Create(OpCode opCode, Local local) { if (opCode.OperandType != OperandType.ShortInlineVar && opCode.OperandType != OperandType.InlineVar) - throw new ArgumentException("Opcode does not have a method local operand", "opCode"); + throw new ArgumentException("Opcode does not have a method local operand", nameof(opCode)); return new Instruction(opCode, local); } @@ -331,7 +326,7 @@ public int GetSize() { case OperandType.InlineSwitch: var targets = Operand as IList; - return opCode.Size + 4 + (targets == null ? 0 : targets.Count * 4); + return opCode.Size + 4 + (targets is null ? 0 : targets.Count * 4); case OperandType.InlineVar: return opCode.Size + 2; @@ -343,17 +338,13 @@ public int GetSize() { } } - static bool IsSystemVoid(TypeSig type) { - return type.RemovePinnedAndModifiers().GetElementType() == ElementType.Void; - } + static bool IsSystemVoid(TypeSig type) => type.RemovePinnedAndModifiers().GetElementType() == ElementType.Void; /// /// Updates with the new stack size /// /// Current stack size - public void UpdateStack(ref int stack) { - UpdateStack(ref stack, false); - } + public void UpdateStack(ref int stack) => UpdateStack(ref stack, false); /// /// Updates with the new stack size @@ -362,8 +353,7 @@ public void UpdateStack(ref int stack) { /// true if the method has a return value, /// false otherwise public void UpdateStack(ref int stack, bool methodHasReturnValue) { - int pushes, pops; - CalculateStackUsage(methodHasReturnValue, out pushes, out pops); + CalculateStackUsage(methodHasReturnValue, out int pushes, out int pops); if (pops == -1) stack = 0; else @@ -376,9 +366,7 @@ public void UpdateStack(ref int stack, bool methodHasReturnValue) { /// Updated with number of stack pushes /// Updated with number of stack pops or -1 if the stack should /// be cleared. - public void CalculateStackUsage(out int pushes, out int pops) { - CalculateStackUsage(false, out pushes, out pops); - } + public void CalculateStackUsage(out int pushes, out int pops) => CalculateStackUsage(false, out pushes, out pops); /// /// Calculates stack usage @@ -390,51 +378,45 @@ public void CalculateStackUsage(out int pushes, out int pops) { public void CalculateStackUsage(bool methodHasReturnValue, out int pushes, out int pops) { var opCode = OpCode; if (opCode.FlowControl == FlowControl.Call) - CalculateStackUsageCall(opCode, out pushes, out pops); + CalculateStackUsageCall(opCode.Code, out pushes, out pops); else CalculateStackUsageNonCall(opCode, methodHasReturnValue, out pushes, out pops); } - void CalculateStackUsageCall(OpCode opCode, out int pushes, out int pops) { + void CalculateStackUsageCall(Code code, out int pushes, out int pops) { pushes = 0; pops = 0; // It doesn't push or pop anything. The stack should be empty when JMP is executed. - if (opCode.Code == Code.Jmp) + if (code == Code.Jmp) return; MethodSig sig; var op = Operand; - var method = op as IMethod; - if (method != null) + if (op is IMethod method) sig = method.MethodSig; else sig = op as MethodSig; // calli instruction - if (sig == null) + if (sig is null) return; bool implicitThis = sig.ImplicitThis; - if (!IsSystemVoid(sig.RetType) || (opCode.Code == Code.Newobj && sig.HasThis)) + if (!IsSystemVoid(sig.RetType) || (code == Code.Newobj && sig.HasThis)) pushes++; pops += sig.Params.Count; var paramsAfterSentinel = sig.ParamsAfterSentinel; - if (paramsAfterSentinel != null) + if (paramsAfterSentinel is not null) pops += paramsAfterSentinel.Count; - if (implicitThis && opCode.Code != Code.Newobj) + if (implicitThis && code != Code.Newobj) pops++; - if (opCode.Code == Code.Calli) + if (code == Code.Calli) pops++; } void CalculateStackUsageNonCall(OpCode opCode, bool hasReturnValue, out int pushes, out int pops) { - StackBehaviour stackBehavior; - - pushes = 0; - pops = 0; - - stackBehavior = opCode.StackBehaviourPush; - switch (stackBehavior) { + switch (opCode.StackBehaviourPush) { case StackBehaviour.Push0: + pushes = 0; break; case StackBehaviour.Push1: @@ -443,27 +425,28 @@ void CalculateStackUsageNonCall(OpCode opCode, bool hasReturnValue, out int push case StackBehaviour.Pushr4: case StackBehaviour.Pushr8: case StackBehaviour.Pushref: - pushes++; + pushes = 1; break; case StackBehaviour.Push1_push1: - pushes += 2; + pushes = 2; break; case StackBehaviour.Varpush: // only call, calli, callvirt which are handled elsewhere default: + pushes = 0; break; } - stackBehavior = opCode.StackBehaviourPop; - switch (stackBehavior) { + switch (opCode.StackBehaviourPop) { case StackBehaviour.Pop0: + pops = 0; break; case StackBehaviour.Pop1: case StackBehaviour.Popi: case StackBehaviour.Popref: - pops++; + pops = 1; break; case StackBehaviour.Pop1_pop1: @@ -474,7 +457,7 @@ void CalculateStackUsageNonCall(OpCode opCode, bool hasReturnValue, out int push case StackBehaviour.Popi_popr8: case StackBehaviour.Popref_pop1: case StackBehaviour.Popref_popi: - pops += 2; + pops = 2; break; case StackBehaviour.Popi_popi_popi: @@ -484,7 +467,7 @@ void CalculateStackUsageNonCall(OpCode opCode, bool hasReturnValue, out int push case StackBehaviour.Popref_popi_popr8: case StackBehaviour.Popref_popi_popref: case StackBehaviour.Popref_popi_pop1: - pops += 3; + pops = 3; break; case StackBehaviour.PopAll: @@ -493,10 +476,13 @@ void CalculateStackUsageNonCall(OpCode opCode, bool hasReturnValue, out int push case StackBehaviour.Varpop: // call, calli, callvirt, newobj (all handled elsewhere), and ret if (hasReturnValue) - pops++; + pops = 1; + else + pops = 0; break; default: + pops = 0; break; } } @@ -504,30 +490,22 @@ void CalculateStackUsageNonCall(OpCode opCode, bool hasReturnValue, out int push /// /// Checks whether it's one of the leave instructions /// - public bool IsLeave() { - return OpCode == OpCodes.Leave || OpCode == OpCodes.Leave_S; - } + public bool IsLeave() => OpCode == OpCodes.Leave || OpCode == OpCodes.Leave_S; /// /// Checks whether it's one of the br instructions /// - public bool IsBr() { - return OpCode == OpCodes.Br || OpCode == OpCodes.Br_S; - } + public bool IsBr() => OpCode == OpCodes.Br || OpCode == OpCodes.Br_S; /// /// Checks whether it's one of the brfalse instructions /// - public bool IsBrfalse() { - return OpCode == OpCodes.Brfalse || OpCode == OpCodes.Brfalse_S; - } + public bool IsBrfalse() => OpCode == OpCodes.Brfalse || OpCode == OpCodes.Brfalse_S; /// /// Checks whether it's one of the brtrue instructions /// - public bool IsBrtrue() { - return OpCode == OpCodes.Brtrue || OpCode == OpCodes.Brtrue_S; - } + public bool IsBrtrue() => OpCode == OpCodes.Brtrue || OpCode == OpCodes.Brtrue_S; /// /// Checks whether it's one of the conditional branch instructions (bcc, brtrue, brfalse) @@ -594,24 +572,22 @@ public bool IsLdcI4() { /// The integer value /// isn't one of the /// ldc.i4 opcodes - public int GetLdcI4Value() { - switch (OpCode.Code) { - case Code.Ldc_I4_M1:return -1; - case Code.Ldc_I4_0: return 0; - case Code.Ldc_I4_1: return 1; - case Code.Ldc_I4_2: return 2; - case Code.Ldc_I4_3: return 3; - case Code.Ldc_I4_4: return 4; - case Code.Ldc_I4_5: return 5; - case Code.Ldc_I4_6: return 6; - case Code.Ldc_I4_7: return 7; - case Code.Ldc_I4_8: return 8; - case Code.Ldc_I4_S: return (sbyte)Operand; - case Code.Ldc_I4: return (int)Operand; - default: - throw new InvalidOperationException(string.Format("Not a ldc.i4 instruction: {0}", this)); - } - } + public int GetLdcI4Value() => + OpCode.Code switch { + Code.Ldc_I4_M1 => -1, + Code.Ldc_I4_0 => 0, + Code.Ldc_I4_1 => 1, + Code.Ldc_I4_2 => 2, + Code.Ldc_I4_3 => 3, + Code.Ldc_I4_4 => 4, + Code.Ldc_I4_5 => 5, + Code.Ldc_I4_6 => 6, + Code.Ldc_I4_7 => 7, + Code.Ldc_I4_8 => 8, + Code.Ldc_I4_S => (sbyte)Operand, + Code.Ldc_I4 => (int)Operand, + _ => throw new InvalidOperationException($"Not a ldc.i4 instruction: {this}"), + }; /// /// Checks whether it's one of the ldarg instructions, but does not check @@ -680,11 +656,10 @@ public bool IsStloc() { } /// - /// Returns the local if it's a ldloc or stloc instruction. It does not - /// return the local if it's a ldloca instruction. + /// Returns the local if it's a ldloc, stloc or ldloca instruction /// /// The locals - /// The local or null if it's not a ldloc or stloc + /// The local or null if it's not a ldloc, stloc or ldloca /// instruction or if the local doesn't exist. public Local GetLocal(IList locals) { int index; @@ -694,6 +669,8 @@ public Local GetLocal(IList locals) { case Code.Ldloc_S: case Code.Stloc: case Code.Stloc_S: + case Code.Ldloca: + case Code.Ldloca_S: return Operand as Local; case Code.Ldloc_0: @@ -714,7 +691,9 @@ public Local GetLocal(IList locals) { return null; } - return locals.Get(index, null); + if ((uint)index < (uint)locals.Count) + return locals[index]; + return null; } /// @@ -735,7 +714,7 @@ public int GetParameterIndex() { case Code.Ldarg: case Code.Ldarg_S: var parameter = Operand as Parameter; - if (parameter != null) + if (parameter is not null) return parameter.Index; break; } @@ -749,7 +728,10 @@ public int GetParameterIndex() { /// All parameters /// A parameter or null if it doesn't exist public Parameter GetParameter(IList parameters) { - return parameters.Get(GetParameterIndex(), null); + int i = GetParameterIndex(); + if ((uint)i < (uint)parameters.Count) + return parameters[i]; + return null; } /// @@ -759,33 +741,58 @@ public Parameter GetParameter(IList parameters) { /// Declaring type (only needed if it's an instance method) /// The type or null if it doesn't exist public TypeSig GetArgumentType(MethodSig methodSig, ITypeDefOrRef declaringType) { - if (methodSig == null) + if (methodSig is null) return null; int index = GetParameterIndex(); - if (index == 0 && methodSig.ImplicitThis) - return declaringType.ToTypeSig(); //TODO: Should be ByRef if value type + if (index == 0 && methodSig.ImplicitThis) { + if (declaringType is null) + return null; + TypeSig declSig; + bool isValueType; + if (declaringType is TypeSpec spec) { + declSig = spec.TypeSig; + isValueType = declSig.IsValueType; + } + else { + // Consistent with ParameterList.UpdateThisParameterType + var td = declaringType.ResolveTypeDef(); + if (td is null) + return declaringType.ToTypeSig(); + isValueType = td.IsValueType; + ClassOrValueTypeSig cvSig = isValueType ? new ValueTypeSig(td) : new ClassSig(td); + if (td.HasGenericParameters) { + int gpCount = td.GenericParameters.Count; + var genArgs = new List(gpCount); + for (int i = 0; i < gpCount; i++) + genArgs.Add(new GenericVar(i, td)); + declSig = new GenericInstSig(cvSig, genArgs); + } + else + declSig = cvSig; + } + return isValueType ? new ByRefSig(declSig) : declSig; + } if (methodSig.ImplicitThis) index--; - return methodSig.Params.Get(index, null); + if ((uint)index < (uint)methodSig.Params.Count) + return methodSig.Params[index]; + return null; } /// /// Clone this instance. The and fields /// are shared by this instance and the created instance. /// - public Instruction Clone() { - return new Instruction { + public Instruction Clone() => + new Instruction { Offset = Offset, OpCode = OpCode, Operand = Operand, SequencePoint = SequencePoint, }; - } /// - public override string ToString() { - return InstructionPrinter.ToString(this); - } + public override string ToString() => InstructionPrinter.ToString(this); } static partial class Extensions { @@ -794,35 +801,27 @@ static partial class Extensions { /// /// this /// - public static OpCode GetOpCode(this Instruction self) { - return self == null ? OpCodes.UNKNOWN1 : self.OpCode; - } + public static OpCode GetOpCode(this Instruction self) => self?.OpCode ?? OpCodes.UNKNOWN1; /// /// Gets the operand or null if is null /// /// this /// - public static object GetOperand(this Instruction self) { - return self == null ? null : self.Operand; - } + public static object GetOperand(this Instruction self) => self?.Operand; /// /// Gets the offset or 0 if is null /// /// this /// - public static uint GetOffset(this Instruction self) { - return self == null ? 0 : self.Offset; - } + public static uint GetOffset(this Instruction self) => self?.Offset ?? 0; /// /// Gets the sequence point or null if is null /// /// this /// - public static dnlib.DotNet.Pdb.SequencePoint GetSequencePoint(this Instruction self) { - return self == null ? null : self.SequencePoint; - } + public static dnlib.DotNet.Pdb.SequencePoint GetSequencePoint(this Instruction self) => self?.SequencePoint; } } diff --git a/src/DotNet/Emit/InstructionPrinter.cs b/src/DotNet/Emit/InstructionPrinter.cs index ddeb722a3..5e1830d84 100644 --- a/src/DotNet/Emit/InstructionPrinter.cs +++ b/src/DotNet/Emit/InstructionPrinter.cs @@ -1,8 +1,7 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; +using System.Collections.Generic; using System.Text; -using dnlib.Threading; namespace dnlib.DotNet.Emit { /// @@ -15,12 +14,12 @@ public static class InstructionPrinter { /// The instruction /// The result public static string ToString(Instruction instr) { - if (instr == null) + if (instr is null) return string.Empty; var sb = new StringBuilder(); - sb.Append(string.Format("IL_{0:X4}: ", instr.Offset)); + sb.Append($"IL_{instr.Offset:X4}: "); sb.Append(instr.OpCode.Name); AddOperandString(sb, instr, " "); @@ -43,9 +42,7 @@ public static string GetOperandString(Instruction instr) { /// /// Place result here /// The instruction - public static void AddOperandString(StringBuilder sb, Instruction instr) { - AddOperandString(sb, instr, string.Empty); - } + public static void AddOperandString(StringBuilder sb, Instruction instr) => AddOperandString(sb, instr, string.Empty); /// /// Add an instruction's operand to @@ -70,7 +67,7 @@ public static void AddOperandString(StringBuilder sb, Instruction instr, string sb.Append(extra); if (op is IFullName) sb.Append((op as IFullName).FullName); - else if (op != null) + else if (op is not null) sb.Append(op.ToString()); else sb.Append("null"); @@ -81,12 +78,12 @@ public static void AddOperandString(StringBuilder sb, Instruction instr, string case OperandType.InlineR: case OperandType.ShortInlineI: case OperandType.ShortInlineR: - sb.Append(string.Format("{0}{1}", extra, op)); + sb.Append($"{extra}{op}"); break; case OperandType.InlineSig: sb.Append(extra); - sb.Append(FullNameCreator.MethodFullName(null, (UTF8String)null, op as MethodSig)); + FullNameFactory.MethodFullNameSB(null, (UTF8String)null, op as MethodSig, null, null, null, sb); break; case OperandType.InlineString: @@ -96,14 +93,14 @@ public static void AddOperandString(StringBuilder sb, Instruction instr, string case OperandType.InlineSwitch: var targets = op as IList; - if (targets == null) + if (targets is null) sb.Append("null"); else { sb.Append('('); for (int i = 0; i < targets.Count; i++) { if (i != 0) sb.Append(','); - AddInstructionTarget(sb, targets.Get(i, null)); + AddInstructionTarget(sb, targets[i]); } sb.Append(')'); } @@ -112,7 +109,7 @@ public static void AddOperandString(StringBuilder sb, Instruction instr, string case OperandType.InlineVar: case OperandType.ShortInlineVar: sb.Append(extra); - if (op == null) + if (op is null) sb.Append("null"); else sb.Append(op.ToString()); @@ -126,14 +123,14 @@ public static void AddOperandString(StringBuilder sb, Instruction instr, string } static void AddInstructionTarget(StringBuilder sb, Instruction targetInstr) { - if (targetInstr == null) + if (targetInstr is null) sb.Append("null"); else - sb.Append(string.Format("IL_{0:X4}", targetInstr.Offset)); + sb.Append($"IL_{targetInstr.Offset:X4}"); } static void EscapeString(StringBuilder sb, string s, bool addQuotes) { - if (s == null) { + if (s is null) { sb.Append("null"); return; } @@ -152,7 +149,7 @@ static void EscapeString(StringBuilder sb, string s, bool addQuotes) { case '\t': sb.Append(@"\t"); break; case '\v': sb.Append(@"\v"); break; default: - sb.Append(string.Format(@"\u{0:X4}", (int)c)); + sb.Append($@"\u{(int)c:X4}"); break; } } diff --git a/src/DotNet/Emit/InvalidMethodException.cs b/src/DotNet/Emit/InvalidMethodException.cs index bd903180e..8538ac113 100644 --- a/src/DotNet/Emit/InvalidMethodException.cs +++ b/src/DotNet/Emit/InvalidMethodException.cs @@ -1,6 +1,7 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; +using System.Runtime.Serialization; namespace dnlib.DotNet.Emit { /// @@ -30,5 +31,14 @@ public InvalidMethodException(string msg) public InvalidMethodException(string msg, Exception innerException) : base(msg, innerException) { } + + /// + /// Constructor + /// + /// + /// + protected InvalidMethodException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } } } diff --git a/src/DotNet/Emit/LocalList.cs b/src/DotNet/Emit/LocalList.cs index 14d237cb8..700f557d3 100644 --- a/src/DotNet/Emit/LocalList.cs +++ b/src/DotNet/Emit/LocalList.cs @@ -1,62 +1,51 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; using dnlib.Utils; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif +using dnlib.DotNet.Pdb; namespace dnlib.DotNet.Emit { /// /// A collection of s /// [DebuggerDisplay("Count = {Count}")] - public sealed class LocalList : IListListener, ThreadSafe.IList { + [DebuggerTypeProxy(typeof(LocalList_CollectionDebugView))] + public sealed class LocalList : IListListener, IList { readonly LazyList locals; /// /// Gets the number of locals /// - public int Count { - get { return locals.Count; } - } + public int Count => locals.Count; /// /// Gets the list of locals /// - public ThreadSafe.IList Locals { - get { return locals; } - } + public IList Locals => locals; /// /// Gets the N'th local /// /// The local index public Local this[int index] { - get { return locals[index]; } - set { locals[index] = value; } + get => locals[index]; + set => locals[index] = value; } /// /// Default constructor /// - public LocalList() { - this.locals = new LazyList(this); - } + public LocalList() => locals = new LazyList(this); /// /// Constructor /// /// All locals that will be owned by this instance - public LocalList(IEnumerable locals) { + public LocalList(IList locals) { this.locals = new LazyList(this); - foreach (var local in locals.GetSafeEnumerable()) - this.locals.Add(local); + for (int i = 0; i < locals.Count; i++) + this.locals.Add(locals[i]); } /// @@ -74,18 +63,14 @@ void IListListener.OnLazyAdd(int index, ref Local value) { } /// - void IListListener.OnAdd(int index, Local value) { - value.Index = index; - } + void IListListener.OnAdd(int index, Local value) => value.Index = index; /// - void IListListener.OnRemove(int index, Local value) { - value.Index = -1; - } + void IListListener.OnRemove(int index, Local value) => value.Index = -1; /// void IListListener.OnResize(int index) { - for (int i = index; i < locals.Count_NoLock(); i++) + for (int i = index; i < locals.Count_NoLock; i++) locals.Get_NoLock(i).Index = i; } @@ -96,129 +81,35 @@ void IListListener.OnClear() { } /// - public int IndexOf(Local item) { - return locals.IndexOf(item); - } - - /// - public void Insert(int index, Local item) { - locals.Insert(index, item); - } - - /// - public void RemoveAt(int index) { - locals.RemoveAt(index); - } - - void ICollection.Add(Local item) { - locals.Add(item); - } - - /// - public void Clear() { - locals.Clear(); - } - - /// - public bool Contains(Local item) { - return locals.Contains(item); - } - - /// - public void CopyTo(Local[] array, int arrayIndex) { - locals.CopyTo(array, arrayIndex); - } - - /// - public bool IsReadOnly { - get { return false; } - } - - /// - public bool Remove(Local item) { - return locals.Remove(item); - } - - /// - public IEnumerator GetEnumerator() { - return locals.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return ((IEnumerable)this).GetEnumerator(); - } - -#if THREAD_SAFE - /// - public int IndexOf_NoLock(Local item) { - return locals.IndexOf_NoLock(item); - } + public int IndexOf(Local item) => locals.IndexOf(item); /// - public void Insert_NoLock(int index, Local item) { - locals.Insert_NoLock(index, item); - } + public void Insert(int index, Local item) => locals.Insert(index, item); /// - public void RemoveAt_NoLock(int index) { - locals.RemoveAt_NoLock(index); - } + public void RemoveAt(int index) => locals.RemoveAt(index); - /// - public Local Get_NoLock(int index) { - return locals.Get_NoLock(index); - } + void ICollection.Add(Local item) => locals.Add(item); /// - public void Set_NoLock(int index, Local value) { - locals.Set_NoLock(index, value); - } + public void Clear() => locals.Clear(); /// - public void Add_NoLock(Local item) { - locals.Add_NoLock(item); - } + public bool Contains(Local item) => locals.Contains(item); /// - public void Clear_NoLock() { - locals.Clear_NoLock(); - } + public void CopyTo(Local[] array, int arrayIndex) => locals.CopyTo(array, arrayIndex); /// - public bool Contains_NoLock(Local item) { - return locals.Contains_NoLock(item); - } + public bool IsReadOnly => false; /// - public void CopyTo_NoLock(Local[] array, int arrayIndex) { - locals.CopyTo_NoLock(array, arrayIndex); - } + public bool Remove(Local item) => locals.Remove(item); /// - public int Count_NoLock { - get { return locals.Count_NoLock; } - } - - /// - public bool IsReadOnly_NoLock { - get { return locals.IsReadOnly_NoLock; } - } - - /// - public bool Remove_NoLock(Local item) { - return locals.Remove_NoLock(item); - } - - /// - public IEnumerator GetEnumerator_NoLock() { - return locals.GetEnumerator_NoLock(); - } - - /// - public TRetType ExecuteLocked(TArgType arg, ExecuteLockedDelegate handler) { - return locals.ExecuteLocked(arg, (tsList, arg2) => handler(this, arg2)); - } -#endif + public LazyList.Enumerator GetEnumerator() => locals.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => locals.GetEnumerator(); + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => ((IEnumerable)this).GetEnumerator(); } /// @@ -228,54 +119,76 @@ public sealed class Local : IVariable { TypeSig typeSig; int index; string name; - int pdbAttributes; + PdbLocalAttributes attributes; /// /// Gets/sets the type of the local /// public TypeSig Type { - get { return typeSig; } - set { typeSig = value; } + get => typeSig; + set => typeSig = value; } /// /// Local index /// public int Index { - get { return index; } - internal set { index = value; } + get => index; + internal set => index = value; } /// - /// Gets/sets the name + /// Gets the name. This property is obsolete, use to get/set the name stored in the PDB file. /// public string Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; + } + + /// + /// Gets the attributes. This property is obsolete, use to get/set the attributes stored in the PDB file. + /// + public PdbLocalAttributes Attributes { + get => attributes; + set => attributes = value; } + internal void SetName(string name) => this.name = name; + internal void SetAttributes(PdbLocalAttributes attributes) => this.attributes = attributes; + /// - /// Gets/sets the PDB attributes. This seems to be a CorSymVarFlag enumeration. - /// It's VAR_IS_COMP_GEN (1) if it's a compiler-generated local. + /// Constructor /// - public int PdbAttributes { - get { return pdbAttributes; } - set { pdbAttributes = value; } + /// The type + public Local(TypeSig typeSig) => this.typeSig = typeSig; + + /// + /// Constructor + /// + /// The type + /// Name of local + public Local(TypeSig typeSig, string name) { + this.typeSig = typeSig; + this.name = name; } /// /// Constructor /// /// The type - public Local(TypeSig typeSig) { + /// Name of local + /// Index, should only be used if you don't add it to the locals list + public Local(TypeSig typeSig, string name, int index) { this.typeSig = typeSig; + this.name = name; + this.index = index; } /// public override string ToString() { var n = name; if (string.IsNullOrEmpty(n)) - return string.Format("V_{0}", Index); + return $"V_{Index}"; return n; } } diff --git a/src/DotNet/Emit/MethodBody.cs b/src/DotNet/Emit/MethodBody.cs index 3792e4c71..d21b39fa7 100644 --- a/src/DotNet/Emit/MethodBody.cs +++ b/src/DotNet/Emit/MethodBody.cs @@ -1,15 +1,8 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; +using System.Collections.Generic; using dnlib.DotNet.Pdb; using dnlib.PE; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif namespace dnlib.DotNet.Emit { /// @@ -28,8 +21,8 @@ public sealed class NativeMethodBody : MethodBody { /// Gets/sets the RVA of the native method body /// public RVA RVA { - get { return rva; } - set { rva = value; } + get => rva; + set => rva = value; } /// @@ -42,9 +35,7 @@ public NativeMethodBody() { /// Constructor /// /// RVA of method body - public NativeMethodBody(RVA rva) { - this.rva = rva; - } + public NativeMethodBody(RVA rva) => this.rva = rva; } /// @@ -56,10 +47,9 @@ public sealed class CilBody : MethodBody { byte headerSize; ushort maxStack; uint localVarSigTok; - readonly ThreadSafe.IList instructions; - readonly ThreadSafe.IList exceptionHandlers; + readonly IList instructions; + readonly IList exceptionHandlers; readonly LocalList localList; - PdbScope pdbScope; /// /// Size of a small header @@ -70,16 +60,16 @@ public sealed class CilBody : MethodBody { /// Gets/sets a flag indicating whether the original max stack value should be used. /// public bool KeepOldMaxStack { - get { return keepOldMaxStack; } - set { keepOldMaxStack = value; } + get => keepOldMaxStack; + set => keepOldMaxStack = value; } /// /// Gets/sets the init locals flag. This is only valid if the method has any locals. /// public bool InitLocals { - get { return initLocals; } - set { initLocals = value; } + get => initLocals; + set => initLocals = value; } /// @@ -87,106 +77,95 @@ public bool InitLocals { /// the header. /// public byte HeaderSize { - get { return headerSize; } - set { headerSize = value; } + get => headerSize; + set => headerSize = value; } /// /// true if it was a small body header ( is 1) /// - public bool IsSmallHeader { - get { return headerSize == SMALL_HEADER_SIZE; } - } + public bool IsSmallHeader => headerSize == SMALL_HEADER_SIZE; /// /// true if it was a big body header /// - public bool IsBigHeader { - get { return headerSize != SMALL_HEADER_SIZE; } - } + public bool IsBigHeader => headerSize != SMALL_HEADER_SIZE; /// /// Gets/sets max stack value from the fat method header. /// public ushort MaxStack { - get { return maxStack; } - set { maxStack = value; } + get => maxStack; + set => maxStack = value; } /// /// Gets/sets the locals metadata token /// public uint LocalVarSigTok { - get { return localVarSigTok; } - set { localVarSigTok = value; } + get => localVarSigTok; + set => localVarSigTok = value; } /// /// true if is not empty /// - public bool HasInstructions { - get { return instructions.Count > 0; } - } + public bool HasInstructions => instructions.Count > 0; /// /// Gets the instructions /// - public ThreadSafe.IList Instructions { - get { return instructions; } - } + public IList Instructions => instructions; /// /// true if is not empty /// - public bool HasExceptionHandlers { - get { return exceptionHandlers.Count > 0; } - } + public bool HasExceptionHandlers => exceptionHandlers.Count > 0; /// /// Gets the exception handlers /// - public ThreadSafe.IList ExceptionHandlers { - get { return exceptionHandlers; } - } + public IList ExceptionHandlers => exceptionHandlers; /// /// true if is not empty /// - public bool HasVariables { - get { return localList.Count > 0; } - } + public bool HasVariables => localList.Count > 0; /// /// Gets the locals /// - public LocalList Variables {// Only called Variables for compat w/ older code. Locals is a better and more accurate name - get { return localList; } - } + public LocalList Variables => localList; /// - /// Gets/sets the PDB scope. This is null if no PDB has been loaded or if there's - /// no PDB scope for this method. + /// Gets/sets the PDB method. This is null if no PDB has been loaded or if there's + /// no PDB info for this method. /// - public PdbScope Scope { - get { return pdbScope; } - set { pdbScope = value; } + public PdbMethod PdbMethod { + get => pdbMethod; + set => pdbMethod = value; } + PdbMethod pdbMethod; /// - /// true if is not null + /// true if is not null /// - public bool HasScope { - get { return pdbScope != null; } - } + public bool HasPdbMethod => PdbMethod is not null; + + /// + /// Gets the total size of the body in the PE file, including header, IL bytes, and exception handlers. + /// This property returns 0 if the size is unknown. + /// + internal uint MetadataBodySize { get; set; } /// /// Default constructor /// public CilBody() { - this.initLocals = true; - this.instructions = ThreadSafeListCreator.Create(); - this.exceptionHandlers = ThreadSafeListCreator.Create(); - this.localList = new LocalList(); + initLocals = true; + instructions = new List(); + exceptionHandlers = new List(); + localList = new LocalList(); } /// @@ -198,9 +177,9 @@ public CilBody() { /// All locals. This instance will own the locals in the list. public CilBody(bool initLocals, IList instructions, IList exceptionHandlers, IList locals) { this.initLocals = initLocals; - this.instructions = ThreadSafeListCreator.MakeThreadSafe(instructions); - this.exceptionHandlers = ThreadSafeListCreator.MakeThreadSafe(exceptionHandlers); - this.localList = new LocalList(locals); + this.instructions = instructions; + this.exceptionHandlers = exceptionHandlers; + localList = new LocalList(locals); } /// @@ -209,39 +188,29 @@ public CilBody(bool initLocals, IList instructions, IList /// All method parameters, including the hidden 'this' parameter /// if it's an instance method. Use . - public void SimplifyMacros(IList parameters) { - instructions.SimplifyMacros(localList, parameters); - } + public void SimplifyMacros(IList parameters) => instructions.SimplifyMacros(localList, parameters); /// /// Optimizes instructions by using the shorter form if possible. Eg. Ldc_I4 1 /// will be replaced with Ldc_I4_1. /// - public void OptimizeMacros() { - instructions.OptimizeMacros(); - } + public void OptimizeMacros() => instructions.OptimizeMacros(); /// /// Short branch instructions are converted to the long form, eg. Beq_S is /// converted to Beq. /// - public void SimplifyBranches() { - instructions.SimplifyBranches(); - } + public void SimplifyBranches() => instructions.SimplifyBranches(); /// /// Optimizes branches by using the smallest possible branch /// - public void OptimizeBranches() { - instructions.OptimizeBranches(); - } + public void OptimizeBranches() => instructions.OptimizeBranches(); /// /// Updates each instruction's offset /// /// Total size in bytes of all instructions - public uint UpdateInstructionOffsets() { - return instructions.UpdateInstructionOffsets(); - } + public uint UpdateInstructionOffsets() => instructions.UpdateInstructionOffsets(); } } diff --git a/src/DotNet/Emit/MethodBodyReader.cs b/src/DotNet/Emit/MethodBodyReader.cs index bb1ebf2ff..232c73e6d 100644 --- a/src/DotNet/Emit/MethodBodyReader.cs +++ b/src/DotNet/Emit/MethodBodyReader.cs @@ -30,9 +30,8 @@ public static partial class Extensions { /// An object /// The metadata token /// A or null if is invalid - public static IMDTokenProvider ResolveToken(this IInstructionOperandResolver self, uint token) { - return self.ResolveToken(token, new GenericParamContext()); - } + public static IMDTokenProvider ResolveToken(this IInstructionOperandResolver self, uint token) => + self.ResolveToken(token, new GenericParamContext()); } /// @@ -46,7 +45,9 @@ public sealed class MethodBodyReader : MethodBodyReaderBase { ushort maxStack; uint codeSize; uint localVarSigTok; - IBinaryReader exceptionsReader; + uint startOfHeader; + uint totalBodySize; + DataReader? exceptionsReader; readonly GenericParamContext gpContext; /// @@ -56,9 +57,8 @@ public sealed class MethodBodyReader : MethodBodyReaderBase { /// The operand resolver /// A reader positioned at the start of a .NET method body /// Use parameters from this method - public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, IBinaryReader reader, MethodDef method) { - return CreateCilBody(opResolver, reader, null, method.Parameters, new GenericParamContext()); - } + public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader reader, MethodDef method) => + CreateCilBody(opResolver, reader, null, method.Parameters, new GenericParamContext()); /// /// Creates a CIL method body or returns an empty one if doesn't @@ -68,9 +68,8 @@ public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, IBin /// A reader positioned at the start of a .NET method body /// Use parameters from this method /// Generic parameter context - public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, IBinaryReader reader, MethodDef method, GenericParamContext gpContext) { - return CreateCilBody(opResolver, reader, null, method.Parameters, gpContext); - } + public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader reader, MethodDef method, GenericParamContext gpContext) => + CreateCilBody(opResolver, reader, null, method.Parameters, gpContext); /// /// Creates a CIL method body or returns an empty one if doesn't @@ -79,9 +78,8 @@ public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, IBin /// The operand resolver /// A reader positioned at the start of a .NET method body /// Method parameters - public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, IBinaryReader reader, IList parameters) { - return CreateCilBody(opResolver, reader, null, parameters, new GenericParamContext()); - } + public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader reader, IList parameters) => + CreateCilBody(opResolver, reader, null, parameters, new GenericParamContext()); /// /// Creates a CIL method body or returns an empty one if doesn't @@ -91,9 +89,20 @@ public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, IBin /// A reader positioned at the start of a .NET method body /// Method parameters /// Generic parameter context - public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, IBinaryReader reader, IList parameters, GenericParamContext gpContext) { - return CreateCilBody(opResolver, reader, null, parameters, gpContext); - } + public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader reader, IList parameters, GenericParamContext gpContext) => + CreateCilBody(opResolver, reader, null, parameters, gpContext); + + /// + /// Creates a CIL method body or returns an empty one if doesn't + /// point to the start of a valid CIL method body. + /// + /// The operand resolver + /// A reader positioned at the start of a .NET method body + /// Method parameters + /// Generic parameter context + /// The module context + public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader reader, IList parameters, GenericParamContext gpContext, ModuleContext context) => + CreateCilBody(opResolver, reader, null, parameters, gpContext, context); /// /// Creates a CIL method body or returns an empty one if is not @@ -104,9 +113,8 @@ public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, IBin /// Exceptions or null if all exception handlers are in /// /// Method parameters - public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList parameters) { - return CreateCilBody(opResolver, MemoryImageStream.Create(code), exceptions == null ? null : MemoryImageStream.Create(exceptions), parameters, new GenericParamContext()); - } + public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList parameters) => + CreateCilBody(opResolver, ByteArrayDataReaderFactory.CreateReader(code), exceptions is null ? (DataReader?)null : ByteArrayDataReaderFactory.CreateReader(exceptions), parameters, new GenericParamContext()); /// /// Creates a CIL method body or returns an empty one if is not @@ -118,9 +126,8 @@ public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte /// /// Method parameters /// Generic parameter context - public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList parameters, GenericParamContext gpContext) { - return CreateCilBody(opResolver, MemoryImageStream.Create(code), exceptions == null ? null : MemoryImageStream.Create(exceptions), parameters, gpContext); - } + public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList parameters, GenericParamContext gpContext) => + CreateCilBody(opResolver, ByteArrayDataReaderFactory.CreateReader(code), exceptions is null ? (DataReader?)null : ByteArrayDataReaderFactory.CreateReader(exceptions), parameters, gpContext); /// /// Creates a CIL method body or returns an empty one if doesn't @@ -131,9 +138,8 @@ public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte /// Exception handler reader or null if exceptions aren't /// present or if contains the exception handlers /// Method parameters - public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, IBinaryReader codeReader, IBinaryReader ehReader, IList parameters) { - return CreateCilBody(opResolver, codeReader, ehReader, parameters, new GenericParamContext()); - } + public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader codeReader, DataReader? ehReader, IList parameters) => + CreateCilBody(opResolver, codeReader, ehReader, parameters, new GenericParamContext()); /// /// Creates a CIL method body or returns an empty one if doesn't @@ -145,8 +151,22 @@ public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, IBin /// present or if contains the exception handlers /// Method parameters /// Generic parameter context - public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, IBinaryReader codeReader, IBinaryReader ehReader, IList parameters, GenericParamContext gpContext) { - var mbReader = new MethodBodyReader(opResolver, codeReader, ehReader, parameters, gpContext); + public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader codeReader, DataReader? ehReader, IList parameters, GenericParamContext gpContext) => + CreateCilBody(opResolver, codeReader, ehReader, parameters, gpContext, null); + + /// + /// Creates a CIL method body or returns an empty one if doesn't + /// point to the start of a valid CIL method body. + /// + /// The operand resolver + /// A reader positioned at the start of a .NET method body + /// Exception handler reader or null if exceptions aren't + /// present or if contains the exception handlers + /// Method parameters + /// Generic parameter context + /// The module context + public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, DataReader codeReader, DataReader? ehReader, IList parameters, GenericParamContext gpContext, ModuleContext context) { + var mbReader = new MethodBodyReader(opResolver, codeReader, ehReader, parameters, gpContext, context); if (!mbReader.Read()) return new CilBody(); return mbReader.CreateCilBody(); @@ -165,9 +185,25 @@ public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, IBin /// Max stack /// Code size /// Local variable signature token or 0 if none - public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList parameters, ushort flags, ushort maxStack, uint codeSize, uint localVarSigTok) { - return CreateCilBody(opResolver, code, exceptions, parameters, flags, maxStack, codeSize, localVarSigTok, new GenericParamContext()); - } + public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList parameters, ushort flags, ushort maxStack, uint codeSize, uint localVarSigTok) => + CreateCilBody(opResolver, code, exceptions, parameters, flags, maxStack, codeSize, localVarSigTok, new GenericParamContext()); + + /// + /// Creates a CIL method body or returns an empty one if is not + /// a valid CIL method body. + /// + /// The operand resolver + /// All code + /// Exceptions or null if all exception handlers are in + /// + /// Method parameters + /// Method header flags, eg. 2 if tiny method + /// Max stack + /// Code size + /// Local variable signature token or 0 if none + /// Generic parameter context + public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList parameters, ushort flags, ushort maxStack, uint codeSize, uint localVarSigTok, GenericParamContext gpContext) => + CreateCilBody(opResolver, code, exceptions, parameters, flags, maxStack, codeSize, localVarSigTok, gpContext, null); /// /// Creates a CIL method body or returns an empty one if is not @@ -183,10 +219,11 @@ public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte /// Code size /// Local variable signature token or 0 if none /// Generic parameter context - public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList parameters, ushort flags, ushort maxStack, uint codeSize, uint localVarSigTok, GenericParamContext gpContext) { - var codeReader = MemoryImageStream.Create(code); - var ehReader = exceptions == null ? null : MemoryImageStream.Create(exceptions); - var mbReader = new MethodBodyReader(opResolver, codeReader, ehReader, parameters, gpContext); + /// The module context + public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte[] code, byte[] exceptions, IList parameters, ushort flags, ushort maxStack, uint codeSize, uint localVarSigTok, GenericParamContext gpContext, ModuleContext context) { + var codeReader = ByteArrayDataReaderFactory.CreateReader(code); + var ehReader = exceptions is null ? (DataReader?)null : ByteArrayDataReaderFactory.CreateReader(exceptions); + var mbReader = new MethodBodyReader(opResolver, codeReader, ehReader, parameters, gpContext, context); mbReader.SetHeader(flags, maxStack, codeSize, localVarSigTok); if (!mbReader.Read()) return new CilBody(); @@ -199,7 +236,7 @@ public static CilBody CreateCilBody(IInstructionOperandResolver opResolver, byte /// The operand resolver /// A reader positioned at the start of a .NET method body /// Use parameters from this method - public MethodBodyReader(IInstructionOperandResolver opResolver, IBinaryReader reader, MethodDef method) + public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader reader, MethodDef method) : this(opResolver, reader, null, method.Parameters, new GenericParamContext()) { } @@ -210,7 +247,7 @@ public MethodBodyReader(IInstructionOperandResolver opResolver, IBinaryReader re /// A reader positioned at the start of a .NET method body /// Use parameters from this method /// Generic parameter context - public MethodBodyReader(IInstructionOperandResolver opResolver, IBinaryReader reader, MethodDef method, GenericParamContext gpContext) + public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader reader, MethodDef method, GenericParamContext gpContext) : this(opResolver, reader, null, method.Parameters, gpContext) { } @@ -220,7 +257,7 @@ public MethodBodyReader(IInstructionOperandResolver opResolver, IBinaryReader re /// The operand resolver /// A reader positioned at the start of a .NET method body /// Method parameters - public MethodBodyReader(IInstructionOperandResolver opResolver, IBinaryReader reader, IList parameters) + public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader reader, IList parameters) : this(opResolver, reader, null, parameters, new GenericParamContext()) { } @@ -231,7 +268,7 @@ public MethodBodyReader(IInstructionOperandResolver opResolver, IBinaryReader re /// A reader positioned at the start of a .NET method body /// Method parameters /// Generic parameter context - public MethodBodyReader(IInstructionOperandResolver opResolver, IBinaryReader reader, IList parameters, GenericParamContext gpContext) + public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader reader, IList parameters, GenericParamContext gpContext) : this(opResolver, reader, null, parameters, gpContext) { } @@ -243,7 +280,7 @@ public MethodBodyReader(IInstructionOperandResolver opResolver, IBinaryReader re /// Exception handler reader or null if exceptions aren't /// present or if contains the exception handlers /// Method parameters - public MethodBodyReader(IInstructionOperandResolver opResolver, IBinaryReader codeReader, IBinaryReader ehReader, IList parameters) + public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader codeReader, DataReader? ehReader, IList parameters) : this(opResolver, codeReader, ehReader, parameters, new GenericParamContext()) { } @@ -256,11 +293,26 @@ public MethodBodyReader(IInstructionOperandResolver opResolver, IBinaryReader co /// present or if contains the exception handlers /// Method parameters /// Generic parameter context - public MethodBodyReader(IInstructionOperandResolver opResolver, IBinaryReader codeReader, IBinaryReader ehReader, IList parameters, GenericParamContext gpContext) - : base(codeReader, parameters) { + public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader codeReader, DataReader? ehReader, IList parameters, GenericParamContext gpContext) + : this(opResolver, codeReader, ehReader, parameters, gpContext, null) { + } + + /// + /// Constructor + /// + /// The operand resolver + /// A reader positioned at the start of a .NET method body + /// Exception handler reader or null if exceptions aren't + /// present or if contains the exception handlers + /// Method parameters + /// Generic parameter context + /// Module context + public MethodBodyReader(IInstructionOperandResolver opResolver, DataReader codeReader, DataReader? ehReader, IList parameters, GenericParamContext gpContext, ModuleContext context) + : base(codeReader, parameters, context) { this.opResolver = opResolver; - this.exceptionsReader = ehReader; + exceptionsReader = ehReader; this.gpContext = gpContext; + startOfHeader = uint.MaxValue; } /// @@ -271,7 +323,7 @@ public MethodBodyReader(IInstructionOperandResolver opResolver, IBinaryReader co /// Code size /// Local variable signature token void SetHeader(ushort flags, ushort maxStack, uint codeSize, uint localVarSigTok) { - this.hasReadHeader = true; + hasReadHeader = true; this.flags = flags; this.maxStack = maxStack; this.codeSize = codeSize; @@ -288,7 +340,7 @@ public bool Read() { return false; SetLocals(ReadLocals()); ReadInstructions(); - ReadExceptionHandlers(); + ReadExceptionHandlers(out totalBodySize); return true; } catch (InvalidMethodException) { @@ -307,6 +359,7 @@ bool ReadHeader() { return true; hasReadHeader = true; + startOfHeader = reader.Position; byte b = reader.ReadByte(); switch (b & 7) { case 2: @@ -329,7 +382,7 @@ bool ReadHeader() { // The CLR allows the code to start inside the method header. But if it does, // the CLR doesn't read any exceptions. - reader.Position += -12 + headerSize * 4; + reader.Position = reader.Position - 12 + headerSize * 4U; if (headerSize < 3) flags &= 0xFFF7; headerSize *= 4; @@ -339,7 +392,7 @@ bool ReadHeader() { return false; } - if (reader.Position + codeSize < reader.Position || reader.Position + codeSize > reader.Length) + if ((ulong)reader.Position + codeSize > reader.Length) return false; return true; @@ -351,10 +404,10 @@ bool ReadHeader() { /// All locals or null if there are none IList ReadLocals() { var standAloneSig = opResolver.ResolveToken(localVarSigTok, gpContext) as StandAloneSig; - if (standAloneSig == null) + if (standAloneSig is null) return null; var localSig = standAloneSig.LocalSig; - if (localSig == null) + if (localSig is null) return null; return localSig.Locals; } @@ -362,77 +415,72 @@ IList ReadLocals() { /// /// Reads all instructions /// - void ReadInstructions() { - ReadInstructionsNumBytes(codeSize); - } + void ReadInstructions() => ReadInstructionsNumBytes(codeSize); /// - protected override IField ReadInlineField(Instruction instr) { - return opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as IField; - } + protected override IField ReadInlineField(Instruction instr) => opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as IField; /// - protected override IMethod ReadInlineMethod(Instruction instr) { - return opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as IMethod; - } + protected override IMethod ReadInlineMethod(Instruction instr) => opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as IMethod; /// protected override MethodSig ReadInlineSig(Instruction instr) { var standAloneSig = opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as StandAloneSig; - if (standAloneSig == null) + if (standAloneSig is null) return null; var sig = standAloneSig.MethodSig; - if (sig != null) + if (sig is not null) sig.OriginalToken = standAloneSig.MDToken.Raw; return sig; } /// - protected override string ReadInlineString(Instruction instr) { - return opResolver.ReadUserString(reader.ReadUInt32()) ?? string.Empty; - } + protected override string ReadInlineString(Instruction instr) => opResolver.ReadUserString(reader.ReadUInt32()) ?? string.Empty; /// - protected override ITokenOperand ReadInlineTok(Instruction instr) { - return opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as ITokenOperand; - } + protected override ITokenOperand ReadInlineTok(Instruction instr) => opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as ITokenOperand; /// - protected override ITypeDefOrRef ReadInlineType(Instruction instr) { - return opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef; - } + protected override ITypeDefOrRef ReadInlineType(Instruction instr) => opResolver.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef; /// /// Reads all exception handlers /// - void ReadExceptionHandlers() { - if ((flags & 8) == 0) + void ReadExceptionHandlers(out uint totalBodySize) { + if ((flags & 8) == 0) { + totalBodySize = startOfHeader == uint.MaxValue ? 0 : reader.Position - startOfHeader; return; - IBinaryReader ehReader; - if (exceptionsReader != null) - ehReader = exceptionsReader; + } + bool canSaveTotalBodySize; + DataReader ehReader; + if (exceptionsReader is not null) { + canSaveTotalBodySize = false; + ehReader = exceptionsReader.Value; + } else { + canSaveTotalBodySize = true; ehReader = reader; - ehReader.Position = (ehReader.Position + 3) & ~3; + ehReader.Position = (ehReader.Position + 3) & ~3U; } // Only read the first one. Any others aren't used. byte b = ehReader.ReadByte(); - if ((b & 0x3F) != 1) + if ((b & 0x3F) != 1) { + totalBodySize = startOfHeader == uint.MaxValue ? 0 : reader.Position - startOfHeader; return; // Not exception handler clauses + } if ((b & 0x40) != 0) - ReadFatExceptionHandlers(ehReader); + ReadFatExceptionHandlers(ref ehReader); else - ReadSmallExceptionHandlers(ehReader); - } - - static ushort GetNumberOfExceptionHandlers(uint num) { - // The CLR truncates the count so num handlers is always <= FFFFh. - return (ushort)num; + ReadSmallExceptionHandlers(ref ehReader); + if (canSaveTotalBodySize) + totalBodySize = startOfHeader == uint.MaxValue ? 0 : ehReader.Position - startOfHeader; + else + totalBodySize = 0; } - void ReadFatExceptionHandlers(IBinaryReader ehReader) { + void ReadFatExceptionHandlers(ref DataReader ehReader) { ehReader.Position--; - int num = GetNumberOfExceptionHandlers((ehReader.ReadUInt32() >> 8) / 24); + int num = (int)((ehReader.ReadUInt32() >> 8) / 24); for (int i = 0; i < num; i++) { var eh = new ExceptionHandler((ExceptionHandlerType)ehReader.ReadUInt32()); uint offs = ehReader.ReadUInt32(); @@ -441,9 +489,9 @@ void ReadFatExceptionHandlers(IBinaryReader ehReader) { offs = ehReader.ReadUInt32(); eh.HandlerStart = GetInstruction(offs); eh.HandlerEnd = GetInstruction(offs + ehReader.ReadUInt32()); - if (eh.HandlerType == ExceptionHandlerType.Catch) + if (eh.IsCatch) eh.CatchType = opResolver.ResolveToken(ehReader.ReadUInt32(), gpContext) as ITypeDefOrRef; - else if (eh.HandlerType == ExceptionHandlerType.Filter) + else if (eh.IsFilter) eh.FilterStart = GetInstruction(ehReader.ReadUInt32()); else ehReader.ReadUInt32(); @@ -451,8 +499,8 @@ void ReadFatExceptionHandlers(IBinaryReader ehReader) { } } - void ReadSmallExceptionHandlers(IBinaryReader ehReader) { - int num = GetNumberOfExceptionHandlers((uint)ehReader.ReadByte() / 12); + void ReadSmallExceptionHandlers(ref DataReader ehReader) { + int num = (int)((uint)ehReader.ReadByte() / 12); ehReader.Position += 2; for (int i = 0; i < num; i++) { var eh = new ExceptionHandler((ExceptionHandlerType)ehReader.ReadUInt16()); @@ -462,9 +510,9 @@ void ReadSmallExceptionHandlers(IBinaryReader ehReader) { offs = ehReader.ReadUInt16(); eh.HandlerStart = GetInstruction(offs); eh.HandlerEnd = GetInstruction(offs + ehReader.ReadByte()); - if (eh.HandlerType == ExceptionHandlerType.Catch) + if (eh.IsCatch) eh.CatchType = opResolver.ResolveToken(ehReader.ReadUInt32(), gpContext) as ITypeDefOrRef; - else if (eh.HandlerType == ExceptionHandlerType.Filter) + else if (eh.IsFilter) eh.FilterStart = GetInstruction(ehReader.ReadUInt32()); else ehReader.ReadUInt32(); @@ -484,6 +532,7 @@ public CilBody CreateCilBody() { cilBody.HeaderSize = headerSize; cilBody.MaxStack = maxStack; cilBody.LocalVarSigTok = localVarSigTok; + cilBody.MetadataBodySize = totalBodySize; instructions = null; exceptionHandlers = null; locals = null; diff --git a/src/DotNet/Emit/MethodBodyReaderBase.cs b/src/DotNet/Emit/MethodBodyReaderBase.cs index ac7ececfe..fd81be924 100644 --- a/src/DotNet/Emit/MethodBodyReaderBase.cs +++ b/src/DotNet/Emit/MethodBodyReaderBase.cs @@ -1,9 +1,8 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using dnlib.IO; -using dnlib.Threading; namespace dnlib.DotNet.Emit { /// @@ -11,7 +10,7 @@ namespace dnlib.DotNet.Emit { /// public abstract class MethodBodyReaderBase { /// The method reader - protected IBinaryReader reader; + protected DataReader reader; /// All parameters protected IList parameters; /// All locals @@ -22,37 +21,30 @@ public abstract class MethodBodyReaderBase { protected IList exceptionHandlers = new List(); uint currentOffset; /// First byte after the end of the code - protected long codeEndOffs; + protected uint codeEndOffs; /// Start offset of method - protected long codeStartOffs; + protected uint codeStartOffs; + readonly ModuleContext context; /// /// Gets all parameters /// - public IList Parameters { - get { return parameters; } - } + public IList Parameters => parameters; /// /// Gets all locals /// - public IList Locals { - get { return locals; } - } + public IList Locals => locals; /// /// Gets all instructions /// - public IList Instructions { - get { return instructions; } - } + public IList Instructions => instructions; /// /// Gets all exception handlers /// - public IList ExceptionHandlers { - get { return exceptionHandlers; } - } + public IList ExceptionHandlers => exceptionHandlers; /// /// Constructor @@ -60,11 +52,19 @@ public IList ExceptionHandlers { protected MethodBodyReaderBase() { } + /// + /// Constructor + /// + /// The module context + protected MethodBodyReaderBase(ModuleContext context) { + this.context = context; + } + /// /// Constructor /// /// The reader - protected MethodBodyReaderBase(IBinaryReader reader) + protected MethodBodyReaderBase(DataReader reader) : this(reader, null) { } @@ -73,9 +73,20 @@ protected MethodBodyReaderBase(IBinaryReader reader) /// /// The reader /// Method parameters or null if they're not known yet - protected MethodBodyReaderBase(IBinaryReader reader, IList parameters) { + protected MethodBodyReaderBase(DataReader reader, IList parameters) + : this(reader, parameters, null) { + } + + /// + /// Constructor + /// + /// The reader + /// Method parameters or null if they're not known yet + /// The module context + protected MethodBodyReaderBase(DataReader reader, IList parameters, ModuleContext context) { this.reader = reader; this.parameters = parameters; + this.context = context; } /// @@ -83,11 +94,13 @@ protected MethodBodyReaderBase(IBinaryReader reader, IList parameters /// /// A list of types of all locals or null if none protected void SetLocals(IList newLocals) { + var locals = this.locals; locals.Clear(); - if (newLocals == null) + if (newLocals is null) return; - foreach (var typeSig in newLocals.GetSafeEnumerable()) - locals.Add(new Local(typeSig)); + int count = newLocals.Count; + for (int i = 0; i < count; i++) + locals.Add(new Local(newLocals[i])); } /// @@ -95,11 +108,13 @@ protected void SetLocals(IList newLocals) { /// /// A list of types of all locals or null if none protected void SetLocals(IList newLocals) { + var locals = this.locals; locals.Clear(); - if (newLocals == null) + if (newLocals is null) return; - foreach (var local in newLocals.GetSafeEnumerable()) - locals.Add(new Local(local.Type)); + int count = newLocals.Count; + for (int i = 0; i < count; i++) + locals.Add(new Local(newLocals[i].Type)); } /// @@ -109,8 +124,9 @@ protected void SetLocals(IList newLocals) { protected void ReadInstructions(int numInstrs) { codeStartOffs = reader.Position; codeEndOffs = reader.Length; // We don't know the end pos so use the last one - instructions = new List(numInstrs); + this.instructions = new List(numInstrs); currentOffset = 0; + var instructions = this.instructions; for (int i = 0; i < numInstrs && reader.Position < codeEndOffs; i++) instructions.Add(ReadOneInstruction()); FixBranches(); @@ -126,8 +142,9 @@ protected void ReadInstructionsNumBytes(uint codeSize) { if (codeEndOffs < codeStartOffs || codeEndOffs > reader.Length) throw new InvalidMethodException("Invalid code size"); - instructions = new List(); //TODO: Estimate number of instructions based on codeSize + this.instructions = new List(); //TODO: Estimate number of instructions based on codeSize currentOffset = 0; + var instructions = this.instructions; while (reader.Position < codeEndOffs) instructions.Add(ReadOneInstruction()); reader.Position = codeEndOffs; @@ -139,7 +156,10 @@ protected void ReadInstructionsNumBytes(uint codeSize) { /// instead of an offset. /// void FixBranches() { - foreach (var instr in instructions) { + var instructions = this.instructions; + int count = instructions.Count; + for (int i = 0; i < count; i++) { + var instr = instructions[i]; switch (instr.OpCode.OperandType) { case OperandType.InlineBrTarget: case OperandType.ShortInlineBrTarget: @@ -149,8 +169,8 @@ void FixBranches() { case OperandType.InlineSwitch: var uintTargets = (IList)instr.Operand; var targets = new Instruction[uintTargets.Count]; - for (int i = 0; i < uintTargets.Count; i++) - targets[i] = GetInstruction(uintTargets[i]); + for (int j = 0; j < uintTargets.Count; j++) + targets[j] = GetInstruction(uintTargets[j]); instr.Operand = targets; break; } @@ -164,6 +184,7 @@ void FixBranches() { /// The instruction or null if there's no instruction at . protected Instruction GetInstruction(uint offset) { // The instructions are sorted and all Offset fields are correct. Do a binary search. + var instructions = this.instructions; int lo = 0, hi = instructions.Count - 1; while (lo <= hi && hi != -1) { int i = (lo + hi) / 2; @@ -187,9 +208,9 @@ protected Instruction GetInstruction(uint offset) { /// protected Instruction GetInstructionThrow(uint offset) { var instr = GetInstruction(offset); - if (instr != null) + if (instr is not null) return instr; - throw new InvalidOperationException(string.Format("There's no instruction @ {0:X4}", offset)); + throw new InvalidOperationException($"There's no instruction @ {offset:X4}"); } /// @@ -217,47 +238,50 @@ Instruction ReadOneInstruction() { /// OpCode ReadOpCode() { var op = reader.ReadByte(); - if (op != 0xFE) - return OpCodes.OneByteOpCodes[op]; - return OpCodes.TwoByteOpCodes[reader.ReadByte()]; + if (op == 0xFE) + return OpCodes.TwoByteOpCodes[reader.ReadByte()]; + if (op >= 0xF0 && op <= 0xFB && context is not null && reader.BytesLeft >= 1) { + if (context.GetExperimentalOpCode(op, reader.ReadByte()) is OpCode opCode) + return opCode; + else + reader.Position--; + } + return OpCodes.OneByteOpCodes[op]; } /// /// Reads the instruction operand (if any) /// /// The instruction - object ReadOperand(Instruction instr) { - switch (instr.OpCode.OperandType) { - case OperandType.InlineBrTarget: return ReadInlineBrTarget(instr); - case OperandType.InlineField: return ReadInlineField(instr); - case OperandType.InlineI: return ReadInlineI(instr); - case OperandType.InlineI8: return ReadInlineI8(instr); - case OperandType.InlineMethod: return ReadInlineMethod(instr); - case OperandType.InlineNone: return ReadInlineNone(instr); - case OperandType.InlinePhi: return ReadInlinePhi(instr); - case OperandType.InlineR: return ReadInlineR(instr); - case OperandType.InlineSig: return ReadInlineSig(instr); - case OperandType.InlineString: return ReadInlineString(instr); - case OperandType.InlineSwitch: return ReadInlineSwitch(instr); - case OperandType.InlineTok: return ReadInlineTok(instr); - case OperandType.InlineType: return ReadInlineType(instr); - case OperandType.InlineVar: return ReadInlineVar(instr); - case OperandType.ShortInlineBrTarget: return ReadShortInlineBrTarget(instr); - case OperandType.ShortInlineI: return ReadShortInlineI(instr); - case OperandType.ShortInlineR: return ReadShortInlineR(instr); - case OperandType.ShortInlineVar: return ReadShortInlineVar(instr); - default: throw new InvalidOperationException("Invalid OpCode.OperandType"); - } - } + object ReadOperand(Instruction instr) => + instr.OpCode.OperandType switch { + OperandType.InlineBrTarget => ReadInlineBrTarget(instr), + OperandType.InlineField => ReadInlineField(instr), + OperandType.InlineI => ReadInlineI(instr), + OperandType.InlineI8 => ReadInlineI8(instr), + OperandType.InlineMethod => ReadInlineMethod(instr), + OperandType.InlineNone => ReadInlineNone(instr), + OperandType.InlinePhi => ReadInlinePhi(instr), + OperandType.InlineR => ReadInlineR(instr), + OperandType.InlineSig => ReadInlineSig(instr), + OperandType.InlineString => ReadInlineString(instr), + OperandType.InlineSwitch => ReadInlineSwitch(instr), + OperandType.InlineTok => ReadInlineTok(instr), + OperandType.InlineType => ReadInlineType(instr), + OperandType.InlineVar => ReadInlineVar(instr), + OperandType.ShortInlineBrTarget => ReadShortInlineBrTarget(instr), + OperandType.ShortInlineI => ReadShortInlineI(instr), + OperandType.ShortInlineR => ReadShortInlineR(instr), + OperandType.ShortInlineVar => ReadShortInlineVar(instr), + _ => throw new InvalidOperationException("Invalid OpCode.OperandType"), + }; /// /// Reads a operand /// /// The current instruction /// The operand - protected virtual uint ReadInlineBrTarget(Instruction instr) { - return instr.Offset + (uint)instr.GetSize() + reader.ReadUInt32(); - } + protected virtual uint ReadInlineBrTarget(Instruction instr) => instr.Offset + (uint)instr.GetSize() + reader.ReadUInt32(); /// /// Reads a operand @@ -271,18 +295,14 @@ protected virtual uint ReadInlineBrTarget(Instruction instr) { /// /// The current instruction /// The operand - protected virtual int ReadInlineI(Instruction instr) { - return reader.ReadInt32(); - } + protected virtual int ReadInlineI(Instruction instr) => reader.ReadInt32(); /// /// Reads a operand /// /// The current instruction /// The operand - protected virtual long ReadInlineI8(Instruction instr) { - return reader.ReadInt64(); - } + protected virtual long ReadInlineI8(Instruction instr) => reader.ReadInt64(); /// /// Reads a operand @@ -296,27 +316,21 @@ protected virtual long ReadInlineI8(Instruction instr) { /// /// The current instruction /// The operand - protected virtual object ReadInlineNone(Instruction instr) { - return null; - } + protected virtual object ReadInlineNone(Instruction instr) => null; /// /// Reads a operand /// /// The current instruction /// The operand - protected virtual object ReadInlinePhi(Instruction instr) { - return null; - } + protected virtual object ReadInlinePhi(Instruction instr) => null; /// /// Reads a operand /// /// The current instruction /// The operand - protected virtual double ReadInlineR(Instruction instr) { - return reader.ReadDouble(); - } + protected virtual double ReadInlineR(Instruction instr) => reader.ReadDouble(); /// /// Reads a operand @@ -342,7 +356,7 @@ protected virtual IList ReadInlineSwitch(Instruction instr) { long offsetAfterInstr = (long)instr.Offset + (long)instr.OpCode.Size + 4L + (long)num * 4; if (offsetAfterInstr > uint.MaxValue || codeStartOffs + offsetAfterInstr > codeEndOffs) { reader.Position = codeEndOffs; - return new uint[0]; + return Array2.Empty(); } var targets = new uint[num]; @@ -382,27 +396,21 @@ protected virtual IVariable ReadInlineVar(Instruction instr) { /// /// The current instruction /// The operand - protected virtual Parameter ReadInlineVarArg(Instruction instr) { - return GetParameter(reader.ReadUInt16()); - } + protected virtual Parameter ReadInlineVarArg(Instruction instr) => GetParameter(reader.ReadUInt16()); /// /// Reads a (a local) operand /// /// The current instruction /// The operand - protected virtual Local ReadInlineVarLocal(Instruction instr) { - return GetLocal(reader.ReadUInt16()); - } + protected virtual Local ReadInlineVarLocal(Instruction instr) => GetLocal(reader.ReadUInt16()); /// /// Reads a operand /// /// The current instruction /// The operand - protected virtual uint ReadShortInlineBrTarget(Instruction instr) { - return instr.Offset + (uint)instr.GetSize() + (uint)reader.ReadSByte(); - } + protected virtual uint ReadShortInlineBrTarget(Instruction instr) => instr.Offset + (uint)instr.GetSize() + (uint)reader.ReadSByte(); /// /// Reads a operand @@ -420,9 +428,7 @@ protected virtual object ReadShortInlineI(Instruction instr) { /// /// The current instruction /// The operand - protected virtual float ReadShortInlineR(Instruction instr) { - return reader.ReadSingle(); - } + protected virtual float ReadShortInlineR(Instruction instr) => reader.ReadSingle(); /// /// Reads a operand @@ -440,18 +446,14 @@ protected virtual IVariable ReadShortInlineVar(Instruction instr) { /// /// The current instruction /// The operand - protected virtual Parameter ReadShortInlineVarArg(Instruction instr) { - return GetParameter(reader.ReadByte()); - } + protected virtual Parameter ReadShortInlineVarArg(Instruction instr) => GetParameter(reader.ReadByte()); /// /// Reads a (a local) operand /// /// The current instruction /// The operand - protected virtual Local ReadShortInlineVarLocal(Instruction instr) { - return GetLocal(reader.ReadByte()); - } + protected virtual Local ReadShortInlineVarLocal(Instruction instr) => GetLocal(reader.ReadByte()); /// /// Returns true if it's one of the ldarg/starg instructions that have an operand @@ -477,7 +479,10 @@ protected static bool IsArgOperandInstruction(Instruction instr) { /// A parameter index /// A or null if is invalid protected Parameter GetParameter(int index) { - return parameters.Get(index, null); + var parameters = this.parameters; + if ((uint)index < (uint)parameters.Count) + return parameters[index]; + return null; } /// @@ -486,7 +491,10 @@ protected Parameter GetParameter(int index) { /// A local index /// A or null if is invalid protected Local GetLocal(int index) { - return locals.Get(index, null); + var locals = this.locals; + if ((uint)index < (uint)locals.Count) + return locals[index]; + return null; } /// @@ -505,8 +513,8 @@ protected bool Add(ExceptionHandler eh) { if (handlerEnd <= handlerStart) return false; - if (eh.HandlerType == ExceptionHandlerType.Filter) { - if (eh.FilterStart == null) + if (eh.IsFilter) { + if (eh.FilterStart is null) return false; if (eh.FilterStart.Offset >= handlerStart) return false; @@ -534,8 +542,9 @@ protected bool Add(ExceptionHandler eh) { /// at the end of the method. /// The instruction offset uint GetOffset(Instruction instr) { - if (instr != null) + if (instr is not null) return instr.Offset; + var instructions = this.instructions; if (instructions.Count == 0) return 0; return instructions[instructions.Count - 1].Offset; @@ -551,21 +560,27 @@ public virtual void RestoreMethod(MethodDef method) { var body = method.Body; body.Variables.Clear(); - if (locals != null) { - foreach (var local in locals) - body.Variables.Add(local); + var locals = this.locals; + if (locals is not null) { + int count = locals.Count; + for (int i = 0; i < count; i++) + body.Variables.Add(locals[i]); } body.Instructions.Clear(); - if (instructions != null) { - foreach (var instr in instructions) - body.Instructions.Add(instr); + var instructions = this.instructions; + if (instructions is not null) { + int count = instructions.Count; + for (int i = 0; i < count; i++) + body.Instructions.Add(instructions[i]); } body.ExceptionHandlers.Clear(); - if (exceptionHandlers != null) { - foreach (var eh in exceptionHandlers) - body.ExceptionHandlers.Add(eh); + var exceptionHandlers = this.exceptionHandlers; + if (exceptionHandlers is not null) { + int count = exceptionHandlers.Count; + for (int i = 0; i < count; i++) + body.ExceptionHandlers.Add(exceptionHandlers[i]); } } } diff --git a/src/DotNet/Emit/MethodTableToTypeConverter.cs b/src/DotNet/Emit/MethodTableToTypeConverter.cs index a805e4961..38c05f398 100644 --- a/src/DotNet/Emit/MethodTableToTypeConverter.cs +++ b/src/DotNet/Emit/MethodTableToTypeConverter.cs @@ -1,11 +1,10 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using SR = System.Reflection; -using dnlib.Threading; namespace dnlib.DotNet.Emit { /// @@ -24,15 +23,20 @@ static class MethodTableToTypeConverter { static readonly Dictionary addrToType = new Dictionary(); static ModuleBuilder moduleBuilder; static int numNewTypes; -#if THREAD_SAFE - static readonly Lock theLock = Lock.Create(); -#endif + static object lockObj = new object(); static MethodTableToTypeConverter() { - if (ptrFieldInfo == null) { + if (ptrFieldInfo is null) { +#if NETSTANDARD || NETCOREAPP + var asmb = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("DynAsm"), AssemblyBuilderAccess.Run); +#else var asmb = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("DynAsm"), AssemblyBuilderAccess.Run); +#endif moduleBuilder = asmb.DefineDynamicModule("DynMod"); } + + // In .NET 8+, ILGenerator is an abstract class and the actual ILGenerator implementation is found in RuntimeILGenerator. + localSignatureFieldInfo ??= Type.GetType("System.Reflection.Emit.RuntimeILGenerator")?.GetField("m_localSignature", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); } /// @@ -41,30 +45,25 @@ static MethodTableToTypeConverter() { /// Address of type /// The or null public static Type Convert(IntPtr address) { - Type type; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - if (addrToType.TryGetValue(address, out type)) - return type; + lock (lockObj) { + if (addrToType.TryGetValue(address, out var type)) + return type; - type = GetTypeNET20(address) ?? GetTypeUsingTypeBuilder(address); - addrToType[address] = type; - return type; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + type = GetTypeNET20(address) ?? GetTypeUsingTypeBuilder(address); + addrToType[address] = type; + return type; + } } static Type GetTypeUsingTypeBuilder(IntPtr address) { - if (moduleBuilder == null) + if (moduleBuilder is null) return null; var tb = moduleBuilder.DefineType(GetNextTypeName()); - var mb = tb.DefineMethod(METHOD_NAME, SR.MethodAttributes.Static, typeof(void), new Type[0]); + var mb = tb.DefineMethod(METHOD_NAME, SR.MethodAttributes.Static, typeof(void), Array2.Empty()); try { - if (setMethodBodyMethodInfo != null) + if (setMethodBodyMethodInfo is not null) return GetTypeNET45(tb, mb, address); else return GetTypeNET40(tb, mb, address); @@ -75,18 +74,22 @@ static Type GetTypeUsingTypeBuilder(IntPtr address) { } } - // .NET 4.5 and later have the documented SetMethodBody() method. + // .NET Framework 4.5 and later have the documented SetMethodBody() method. static Type GetTypeNET45(TypeBuilder tb, MethodBuilder mb, IntPtr address) { - byte[] code = new byte[1] { 0x2A }; + var code = new byte[1] { 0x2A }; int maxStack = 8; - byte[] locals = GetLocalSignature(address); + var locals = GetLocalSignature(address); setMethodBodyMethodInfo.Invoke(mb, new object[5] { code, maxStack, locals, null, null }); - - var createdMethod = tb.CreateType().GetMethod(METHOD_NAME, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); +#if NETSTANDARD + var type = tb.CreateTypeInfo(); +#else + var type = tb.CreateType(); +#endif + var createdMethod = type.GetMethod(METHOD_NAME, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); return createdMethod.GetMethodBody().LocalVariables[0].LocalType; } - // This code works with .NET 4.0+ but will throw an exception if .NET 2.0 is used + // This code works with .NET Framework 4.0+ but will throw an exception if .NET Framework 2.0 is used // ("operation could destabilize the runtime") static Type GetTypeNET40(TypeBuilder tb, MethodBuilder mb, IntPtr address) { var ilg = mb.GetILGenerator(); @@ -100,23 +103,25 @@ static Type GetTypeNET40(TypeBuilder tb, MethodBuilder mb, IntPtr address) { sigDoneFieldInfo.SetValue(sigHelper, true); currSigFieldInfo.SetValue(sigHelper, locals.Length); signatureFieldInfo.SetValue(sigHelper, locals); - - var createdMethod = tb.CreateType().GetMethod(METHOD_NAME, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); +#if NETSTANDARD + var type = tb.CreateTypeInfo(); +#else + var type = tb.CreateType(); +#endif + var createdMethod = type.GetMethod(METHOD_NAME, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); return createdMethod.GetMethodBody().LocalVariables[0].LocalType; } - // .NET 2.0 - 3.5 + // .NET Framework 2.0 - 3.5 static Type GetTypeNET20(IntPtr address) { - if (ptrFieldInfo == null) + if (ptrFieldInfo is null) return null; object th = new RuntimeTypeHandle(); ptrFieldInfo.SetValue(th, address); return Type.GetTypeFromHandle((RuntimeTypeHandle)th); } - static string GetNextTypeName() { - return string.Format("Type{0}", numNewTypes++); - } + static string GetNextTypeName() => $"Type{numNewTypes++}"; static byte[] GetLocalSignature(IntPtr mtAddr) { ulong mtValue = (ulong)mtAddr.ToInt64(); diff --git a/src/DotNet/Emit/MethodUtils.cs b/src/DotNet/Emit/MethodUtils.cs index a02f35480..5a2024d30 100644 --- a/src/DotNet/Emit/MethodUtils.cs +++ b/src/DotNet/Emit/MethodUtils.cs @@ -1,7 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; -using dnlib.Threading; +using System.Collections.Generic; namespace dnlib.DotNet.Emit { /// @@ -17,7 +16,9 @@ public static class MethodUtils { /// All method parameters, including the hidden 'this' parameter /// if it's an instance method. Use . public static void SimplifyMacros(this IList instructions, IList locals, IList parameters) { - foreach (var instr in instructions.GetSafeEnumerable()) { + int count = instructions.Count; + for (int i = 0; i < count; i++) { + var instr = instructions[i]; switch (instr.OpCode.Code) { case Code.Beq_S: instr.OpCode = OpCodes.Beq; @@ -218,9 +219,11 @@ public static void SimplifyMacros(this IList instructions, IList(IList list, int index) { - if (list == null) - return default(T); - return list.Get(index, default(T)); + if (list is null) + return default; + if ((uint)index < (uint)list.Count) + return list[index]; + return default; } /// @@ -229,14 +232,16 @@ static T ReadList(IList list, int index) { /// /// All instructions public static void OptimizeMacros(this IList instructions) { - foreach (var instr in instructions.GetSafeEnumerable()) { + int count = instructions.Count; + for (int i = 0; i < count; i++) { + var instr = instructions[i]; Parameter arg; Local local; switch (instr.OpCode.Code) { case Code.Ldarg: case Code.Ldarg_S: arg = instr.Operand as Parameter; - if (arg == null) + if (arg is null) break; if (arg.Index == 0) { instr.OpCode = OpCodes.Ldarg_0; @@ -260,7 +265,7 @@ public static void OptimizeMacros(this IList instructions) { case Code.Ldarga: arg = instr.Operand as Parameter; - if (arg == null) + if (arg is null) break; if (byte.MinValue <= arg.Index && arg.Index <= byte.MaxValue) instr.OpCode = OpCodes.Ldarga_S; @@ -338,7 +343,7 @@ public static void OptimizeMacros(this IList instructions) { case Code.Ldloc: case Code.Ldloc_S: local = instr.Operand as Local; - if (local == null) + if (local is null) break; if (local.Index == 0) { instr.OpCode = OpCodes.Ldloc_0; @@ -362,7 +367,7 @@ public static void OptimizeMacros(this IList instructions) { case Code.Ldloca: local = instr.Operand as Local; - if (local == null) + if (local is null) break; if (byte.MinValue <= local.Index && local.Index <= byte.MaxValue) instr.OpCode = OpCodes.Ldloca_S; @@ -370,7 +375,7 @@ public static void OptimizeMacros(this IList instructions) { case Code.Starg: arg = instr.Operand as Parameter; - if (arg == null) + if (arg is null) break; if (byte.MinValue <= arg.Index && arg.Index <= byte.MaxValue) instr.OpCode = OpCodes.Starg_S; @@ -379,7 +384,7 @@ public static void OptimizeMacros(this IList instructions) { case Code.Stloc: case Code.Stloc_S: local = instr.Operand as Local; - if (local == null) + if (local is null) break; if (local.Index == 0) { instr.OpCode = OpCodes.Stloc_0; @@ -412,7 +417,9 @@ public static void OptimizeMacros(this IList instructions) { /// /// All instructions public static void SimplifyBranches(this IList instructions) { - foreach (var instr in instructions.GetSafeEnumerable()) { + int count = instructions.Count; + for (int i = 0; i < count; i++) { + var instr = instructions[i]; switch (instr.OpCode.Code) { case Code.Beq_S: instr.OpCode = OpCodes.Beq; break; case Code.Bge_S: instr.OpCode = OpCodes.Bge; break; @@ -441,7 +448,9 @@ public static void OptimizeBranches(this IList instructions) { UpdateInstructionOffsets(instructions); bool modified = false; - foreach (var instr in instructions.GetSafeEnumerable()) { + int count = instructions.Count; + for (int i = 0; i < count; i++) { + var instr = instructions[i]; OpCode shortOpCode; switch (instr.OpCode.Code) { case Code.Beq: shortOpCode = OpCodes.Beq_S; break; @@ -461,7 +470,7 @@ public static void OptimizeBranches(this IList instructions) { default: continue; } var targetInstr = instr.Operand as Instruction; - if (targetInstr == null) + if (targetInstr is null) continue; int afterShortInstr; @@ -495,7 +504,9 @@ public static void OptimizeBranches(this IList instructions) { /// Total size in bytes of all instructions public static uint UpdateInstructionOffsets(this IList instructions) { uint offset = 0; - foreach (var instr in instructions.GetSafeEnumerable()) { + int count = instructions.Count; + for (int i = 0; i < count; i++) { + var instr = instructions[i]; instr.Offset = offset; offset += (uint)instr.GetSize(); } diff --git a/src/DotNet/Emit/OpCode.cs b/src/DotNet/Emit/OpCode.cs index f72769ed8..d5e315f62 100644 --- a/src/DotNet/Emit/OpCode.cs +++ b/src/DotNet/Emit/OpCode.cs @@ -1,7 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; -using dnlib.Threading; +using System.Collections.Generic; namespace dnlib.DotNet.Emit { /// @@ -36,214 +35,179 @@ public sealed class OpCode { /// /// Push stack behavior /// - public readonly StackBehaviour StackBehaviourPush; // UK spelling for compatibility with Reflection + public readonly StackBehaviour StackBehaviourPush; /// /// Pop stack behavior /// - public readonly StackBehaviour StackBehaviourPop; // UK spelling for compatibility with Reflection + public readonly StackBehaviour StackBehaviourPop; /// /// Gets the value which is compatible with /// - public short Value { - get { return (short)Code; } - } + public short Value => (short)Code; /// /// Gets the size of the opcode. It's either 1 or 2 bytes. /// - public int Size { - get { return Code < (Code)0x100 || Code == Code.UNKNOWN1 ? 1 : 2; } + public int Size => Code < (Code)0x100 || Code == Code.UNKNOWN1 ? 1 : 2; + + /// + /// Constructs an experimental opcode. + /// + public OpCode(string name, byte first, byte second, OperandType operandType, FlowControl flowControl, StackBehaviour push, StackBehaviour pop) + : this(name, (Code)((first << 8) | second), operandType, flowControl, OpCodeType.Experimental, push, pop, true) { } - internal OpCode(string name, Code code, OperandType operandType, FlowControl flowControl, OpCodeType opCodeType, StackBehaviour push, StackBehaviour pop) { - this.Name = name; - this.Code = code; - this.OperandType = operandType; - this.FlowControl = flowControl; - this.OpCodeType = opCodeType; - this.StackBehaviourPush = push; - this.StackBehaviourPop = pop; - if (((ushort)code >> 8) == 0) - OpCodes.OneByteOpCodes[(byte)code] = this; - else if (((ushort)code >> 8) == 0xFE) - OpCodes.TwoByteOpCodes[(byte)code] = this; + internal OpCode(string name, Code code, OperandType operandType, FlowControl flowControl, OpCodeType opCodeType, StackBehaviour push, StackBehaviour pop, bool experimental = false) { + Name = name; + Code = code; + OperandType = operandType; + FlowControl = flowControl; + OpCodeType = opCodeType; + StackBehaviourPush = push; + StackBehaviourPop = pop; + if (!experimental) { + if (((ushort)code >> 8) == 0) + OpCodes.OneByteOpCodes[(byte)code] = this; + else if (((ushort)code >> 8) == 0xFE) + OpCodes.TwoByteOpCodes[(byte)code] = this; + } } /// /// Creates a new instruction with no operand /// /// A new instance - public Instruction ToInstruction() { - return Instruction.Create(this); - } + public Instruction ToInstruction() => Instruction.Create(this); /// /// Creates a new instruction with a operand /// /// The value /// A new instance - public Instruction ToInstruction(byte value) { - return Instruction.Create(this, value); - } + public Instruction ToInstruction(byte value) => Instruction.Create(this, value); /// /// Creates a new instruction with a operand /// /// The value /// A new instance - public Instruction ToInstruction(sbyte value) { - return Instruction.Create(this, value); - } + public Instruction ToInstruction(sbyte value) => Instruction.Create(this, value); /// /// Creates a new instruction with an operand /// /// The value /// A new instance - public Instruction ToInstruction(int value) { - return Instruction.Create(this, value); - } + public Instruction ToInstruction(int value) => Instruction.Create(this, value); /// /// Creates a new instruction with a operand /// /// The value /// A new instance - public Instruction ToInstruction(long value) { - return Instruction.Create(this, value); - } + public Instruction ToInstruction(long value) => Instruction.Create(this, value); /// /// Creates a new instruction with a operand /// /// The value /// A new instance - public Instruction ToInstruction(float value) { - return Instruction.Create(this, value); - } + public Instruction ToInstruction(float value) => Instruction.Create(this, value); /// /// Creates a new instruction with a operand /// /// The value /// A new instance - public Instruction ToInstruction(double value) { - return Instruction.Create(this, value); - } + public Instruction ToInstruction(double value) => Instruction.Create(this, value); /// /// Creates a new instruction with a string operand /// /// The string /// A new instance - public Instruction ToInstruction(string s) { - return Instruction.Create(this, s); - } + public Instruction ToInstruction(string s) => Instruction.Create(this, s); /// /// Creates a new instruction with an instruction target operand /// /// Target instruction /// A new instance - public Instruction ToInstruction(Instruction target) { - return Instruction.Create(this, target); - } + public Instruction ToInstruction(Instruction target) => Instruction.Create(this, target); /// /// Creates a new instruction with an instruction target list operand /// /// The targets /// A new instance - public Instruction ToInstruction(IList targets) { - return Instruction.Create(this, ThreadSafeListCreator.MakeThreadSafe(targets)); - } + public Instruction ToInstruction(IList targets) => Instruction.Create(this, targets); /// /// Creates a new instruction with a type operand /// /// The type /// A new instance - public Instruction ToInstruction(ITypeDefOrRef type) { - return Instruction.Create(this, type); - } + public Instruction ToInstruction(ITypeDefOrRef type) => Instruction.Create(this, type); /// /// Creates a new instruction with a type operand /// /// The type /// A new instance - public Instruction ToInstruction(CorLibTypeSig type) { - return Instruction.Create(this, type.TypeDefOrRef); - } + public Instruction ToInstruction(CorLibTypeSig type) => Instruction.Create(this, type.TypeDefOrRef); /// /// Creates a new instruction with a method/field operand /// /// The method/field /// A new instance - public Instruction ToInstruction(MemberRef mr) { - return Instruction.Create(this, mr); - } + public Instruction ToInstruction(MemberRef mr) => Instruction.Create(this, mr); /// /// Creates a new instruction with a field operand /// /// The field /// A new instance - public Instruction ToInstruction(IField field) { - return Instruction.Create(this, field); - } + public Instruction ToInstruction(IField field) => Instruction.Create(this, field); /// /// Creates a new instruction with a method operand /// /// The method /// A new instance - public Instruction ToInstruction(IMethod method) { - return Instruction.Create(this, method); - } + public Instruction ToInstruction(IMethod method) => Instruction.Create(this, method); /// /// Creates a new instruction with a token operand /// /// The token /// A new instance - public Instruction ToInstruction(ITokenOperand token) { - return Instruction.Create(this, token); - } + public Instruction ToInstruction(ITokenOperand token) => Instruction.Create(this, token); /// /// Creates a new instruction with a method signature operand /// /// The method signature /// A new instance - public Instruction ToInstruction(MethodSig methodSig) { - return Instruction.Create(this, methodSig); - } + public Instruction ToInstruction(MethodSig methodSig) => Instruction.Create(this, methodSig); /// /// Creates a new instruction with a method parameter operand /// /// The method parameter /// A new instance - public Instruction ToInstruction(Parameter parameter) { - return Instruction.Create(this, parameter); - } + public Instruction ToInstruction(Parameter parameter) => Instruction.Create(this, parameter); /// /// Creates a new instruction with a method local operand /// /// The method local /// A new instance - public Instruction ToInstruction(Local local) { - return Instruction.Create(this, local); - } + public Instruction ToInstruction(Local local) => Instruction.Create(this, local); /// - public override string ToString() { - return Name; - } + public override string ToString() => Name; } } diff --git a/src/DotNet/Emit/OpCodeType.cs b/src/DotNet/Emit/OpCodeType.cs index 473ad433d..6af8b86f2 100644 --- a/src/DotNet/Emit/OpCodeType.cs +++ b/src/DotNet/Emit/OpCodeType.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet.Emit { +namespace dnlib.DotNet.Emit { /// /// CIL opcode type /// @@ -17,5 +17,7 @@ public enum OpCodeType : byte { Prefix, /// Primitive, + /// + Experimental, } } diff --git a/src/DotNet/Emit/OpCodes.cs b/src/DotNet/Emit/OpCodes.cs index bfcda86ca..639f9c4b1 100644 --- a/src/DotNet/Emit/OpCodes.cs +++ b/src/DotNet/Emit/OpCodes.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet.Emit { +namespace dnlib.DotNet.Emit { /// /// Contains all valid CIL opcodes /// @@ -240,7 +240,7 @@ public static class OpCodes { public static readonly OpCode Constrained = new OpCode("constrained.", Code.Constrained, OperandType.InlineType, FlowControl.Meta, OpCodeType.Prefix, StackBehaviour.Push0, StackBehaviour.Pop0); public static readonly OpCode Cpblk = new OpCode("cpblk", Code.Cpblk, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi_popi_popi); public static readonly OpCode Initblk = new OpCode("initblk", Code.Initblk, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Popi_popi_popi); -// public static readonly OpCode No = new OpCode("no.", Code.No, OperandType.ShortInlineI, FlowControl.Meta, OpCodeType.Prefix, StackBehaviour.Push0, StackBehaviour.Pop0); + public static readonly OpCode No = new OpCode("no.", Code.No, OperandType.ShortInlineI, FlowControl.Meta, OpCodeType.Prefix, StackBehaviour.Push0, StackBehaviour.Pop0); public static readonly OpCode Rethrow = new OpCode("rethrow", Code.Rethrow, OperandType.InlineNone, FlowControl.Throw, OpCodeType.Objmodel, StackBehaviour.Push0, StackBehaviour.Pop0); public static readonly OpCode Sizeof = new OpCode("sizeof", Code.Sizeof, OperandType.InlineType, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop0); public static readonly OpCode Refanytype = new OpCode("refanytype", Code.Refanytype, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Pushi, StackBehaviour.Pop1); @@ -251,11 +251,11 @@ static OpCodes() { // The OpCode ctor copies itself to one of these arrays. Whatever are still null // are unsupported opcodes. Set them all to UNKNOWN1 or UNKNOWN2. for (int i = 0; i < OneByteOpCodes.Length; i++) { - if (OneByteOpCodes[i] == null) + if (OneByteOpCodes[i] is null) OneByteOpCodes[i] = UNKNOWN1; } for (int i = 0; i < TwoByteOpCodes.Length; i++) { - if (TwoByteOpCodes[i] == null) + if (TwoByteOpCodes[i] is null) TwoByteOpCodes[i] = UNKNOWN2; } } diff --git a/src/DotNet/Emit/OperandType.cs b/src/DotNet/Emit/OperandType.cs index 1fcd6ee9d..f4fe42fa0 100644 --- a/src/DotNet/Emit/OperandType.cs +++ b/src/DotNet/Emit/OperandType.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using dnlib.DotNet.MD; +using dnlib.DotNet.MD; namespace dnlib.DotNet.Emit { /// diff --git a/src/DotNet/Emit/StackBehaviour.cs b/src/DotNet/Emit/StackBehaviour.cs index ccb9e926a..37e323135 100644 --- a/src/DotNet/Emit/StackBehaviour.cs +++ b/src/DotNet/Emit/StackBehaviour.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet.Emit { +namespace dnlib.DotNet.Emit { /// /// CIL opcode stack behavior /// diff --git a/src/DotNet/EventAttributes.cs b/src/DotNet/EventAttributes.cs index dc01c73c1..abe9ac861 100644 --- a/src/DotNet/EventAttributes.cs +++ b/src/DotNet/EventAttributes.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.DotNet { /// diff --git a/src/DotNet/EventDef.cs b/src/DotNet/EventDef.cs index 45b65afec..b299136d7 100644 --- a/src/DotNet/EventDef.cs +++ b/src/DotNet/EventDef.cs @@ -1,21 +1,18 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; using dnlib.Threading; -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif - namespace dnlib.DotNet { /// /// A high-level representation of a row in the Event table /// - public abstract class EventDef : IHasCustomAttribute, IHasSemantic, IFullName, IMemberDef { + public abstract class EventDef : IHasCustomAttribute, IHasSemantic, IHasCustomDebugInformation, IFullName, IMemberDef { /// /// The row id in its table /// @@ -26,32 +23,26 @@ public abstract class EventDef : IHasCustomAttribute, IHasSemantic, IFullName, I #endif /// - public MDToken MDToken { - get { return new MDToken(Table.Event, rid); } - } + public MDToken MDToken => new MDToken(Table.Event, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 10; } - } + public int HasCustomAttributeTag => 10; /// - public int HasSemanticTag { - get { return 0; } - } + public int HasSemanticTag => 0; /// /// From column Event.EventFlags /// public EventAttributes Attributes { - get { return (EventAttributes)attributes; } - set { attributes = (int)value; } + get => (EventAttributes)attributes; + set => attributes = (int)value; } /// protected int attributes; @@ -60,8 +51,8 @@ public EventAttributes Attributes { /// From column Event.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -70,8 +61,8 @@ public UTF8String Name { /// From column Event.EventType /// public ITypeDefOrRef EventType { - get { return eventType; } - set { eventType = value; } + get => eventType; + set => eventType = value; } /// protected ITypeDefOrRef eventType; @@ -81,7 +72,7 @@ public ITypeDefOrRef EventType { /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -89,21 +80,42 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); + + /// + public int HasCustomDebugInformationTag => 10; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// /// Gets/sets the adder method /// public MethodDef AddMethod { get { - if (otherMethods == null) + if (otherMethods is null) InitializeEventMethods(); return addMethod; } set { - if (otherMethods == null) + if (otherMethods is null) InitializeEventMethods(); addMethod = value; } @@ -114,12 +126,12 @@ public MethodDef AddMethod { /// public MethodDef InvokeMethod { get { - if (otherMethods == null) + if (otherMethods is null) InitializeEventMethods(); return invokeMethod; } set { - if (otherMethods == null) + if (otherMethods is null) InitializeEventMethods(); invokeMethod = value; } @@ -130,12 +142,12 @@ public MethodDef InvokeMethod { /// public MethodDef RemoveMethod { get { - if (otherMethods == null) + if (otherMethods is null) InitializeEventMethods(); return removeMethod; } set { - if (otherMethods == null) + if (otherMethods is null) InitializeEventMethods(); removeMethod = value; } @@ -144,9 +156,9 @@ public MethodDef RemoveMethod { /// /// Gets the other methods /// - public ThreadSafe.IList OtherMethods { + public IList OtherMethods { get { - if (otherMethods == null) + if (otherMethods is null) InitializeEventMethods(); return otherMethods; } @@ -156,7 +168,7 @@ void InitializeEventMethods() { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif - if (otherMethods == null) + if (otherMethods is null) InitializeEventMethods_NoLock(); #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } @@ -167,9 +179,8 @@ void InitializeEventMethods() { /// Initializes , , /// and . /// - protected virtual void InitializeEventMethods_NoLock() { - otherMethods = ThreadSafeListCreator.Create(); - } + protected virtual void InitializeEventMethods_NoLock() => + otherMethods = new List(); /// protected MethodDef addMethod; @@ -178,58 +189,47 @@ protected virtual void InitializeEventMethods_NoLock() { /// protected MethodDef removeMethod; /// - protected ThreadSafe.IList otherMethods; + protected IList otherMethods; /// Reset , , , - protected void ResetMethods() { - otherMethods = null; - } + protected void ResetMethods() => otherMethods = null; /// /// true if there are no methods attached to this event /// - public bool IsEmpty { - get { - // The first property access initializes the other fields we access here - return AddMethod == null && - removeMethod == null && - invokeMethod == null && - otherMethods.Count == 0; - } - } + public bool IsEmpty => + // The first property access initializes the other fields we access here + AddMethod is null && + removeMethod is null && + invokeMethod is null && + otherMethods.Count == 0; /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } - } + public bool HasCustomAttributes => CustomAttributes.Count > 0; /// /// true if is not empty /// - public bool HasOtherMethods { - get { return OtherMethods.Count > 0; } - } + public bool HasOtherMethods => OtherMethods.Count > 0; /// /// Gets/sets the declaring type (owner type) /// public TypeDef DeclaringType { - get { return declaringType2; } + get => declaringType2; set { var currentDeclaringType = DeclaringType2; if (currentDeclaringType == value) return; - if (currentDeclaringType != null) + if (currentDeclaringType is not null) currentDeclaringType.Events.Remove(this); // Will set DeclaringType2 = null - if (value != null) + if (value is not null) value.Events.Add(this); // Will set DeclaringType2 = value } } /// - ITypeDefOrRef IMemberRef.DeclaringType { - get { return declaringType2; } - } + ITypeDefOrRef IMemberRef.DeclaringType => declaringType2; /// /// Called by and should normally not be called by any user @@ -237,81 +237,33 @@ ITypeDefOrRef IMemberRef.DeclaringType { /// declaring type without inserting it in the declaring type's method list. /// public TypeDef DeclaringType2 { - get { return declaringType2; } - set { declaringType2 = value; } + get => declaringType2; + set => declaringType2 = value; } /// protected TypeDef declaringType2; /// - public ModuleDef Module { - get { - var dt = declaringType2; - return dt == null ? null : dt.Module; - } - } + public ModuleDef Module => declaringType2?.Module; /// /// Gets the full name of the event /// - public string FullName { - get { - var dt = declaringType2; - return FullNameCreator.EventFullName(dt == null ? null : dt.FullName, name, eventType); - } - } - - bool IIsTypeOrMethod.IsType { - get { return false; } - } - - bool IIsTypeOrMethod.IsMethod { - get { return false; } - } - - bool IMemberRef.IsField { - get { return false; } - } - - bool IMemberRef.IsTypeSpec { - get { return false; } - } - - bool IMemberRef.IsTypeRef { - get { return false; } - } - - bool IMemberRef.IsTypeDef { - get { return false; } - } - - bool IMemberRef.IsMethodSpec { - get { return false; } - } - - bool IMemberRef.IsMethodDef { - get { return false; } - } - - bool IMemberRef.IsMemberRef { - get { return false; } - } - - bool IMemberRef.IsFieldDef { - get { return false; } - } - - bool IMemberRef.IsPropertyDef { - get { return false; } - } - - bool IMemberRef.IsEventDef { - get { return true; } - } - - bool IMemberRef.IsGenericParam { - get { return false; } - } + public string FullName => FullNameFactory.EventFullName(declaringType2?.FullName, name, eventType, null, null); + + bool IIsTypeOrMethod.IsType => false; + bool IIsTypeOrMethod.IsMethod => false; + bool IMemberRef.IsField => false; + bool IMemberRef.IsTypeSpec => false; + bool IMemberRef.IsTypeRef => false; + bool IMemberRef.IsTypeDef => false; + bool IMemberRef.IsMethodSpec => false; + bool IMemberRef.IsMethodDef => false; + bool IMemberRef.IsMemberRef => false; + bool IMemberRef.IsFieldDef => false; + bool IMemberRef.IsPropertyDef => false; + bool IMemberRef.IsEventDef => true; + bool IMemberRef.IsGenericParam => false; /// /// Set or clear flags in @@ -320,43 +272,30 @@ bool IMemberRef.IsGenericParam { /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, EventAttributes flags) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else if (set) attributes |= (int)flags; else attributes &= ~(int)flags; -#endif } /// /// Gets/sets the bit /// public bool IsSpecialName { - get { return ((EventAttributes)attributes & EventAttributes.SpecialName) != 0; } - set { ModifyAttributes(value, EventAttributes.SpecialName); } + get => ((EventAttributes)attributes & EventAttributes.SpecialName) != 0; + set => ModifyAttributes(value, EventAttributes.SpecialName); } /// /// Gets/sets the bit /// public bool IsRuntimeSpecialName { - get { return ((EventAttributes)attributes & EventAttributes.RTSpecialName) != 0; } - set { ModifyAttributes(value, EventAttributes.RTSpecialName); } + get => ((EventAttributes)attributes & EventAttributes.RTSpecialName) != 0; + set => ModifyAttributes(value, EventAttributes.RTSpecialName); } /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } /// @@ -394,8 +333,8 @@ public EventDefUser(UTF8String name, ITypeDefOrRef type) /// Flags public EventDefUser(UTF8String name, ITypeDefOrRef type, EventAttributes flags) { this.name = name; - this.eventType = type; - this.attributes = (int)flags; + eventType = type; + attributes = (int)flags; } } @@ -409,17 +348,22 @@ sealed class EventDefMD : EventDef, IMDTokenProviderMD { readonly uint origRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.Event, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Event, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(declaringType2), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -429,19 +373,20 @@ protected override void InitializeCustomAttributes() { /// If is invalid public EventDefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.EventTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("Event rid {0} does not exist", rid)); + throw new BadImageFormatException($"Event rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint name; - uint eventType = readerModule.TablesStream.ReadEventRow(origRid, out this.attributes, out name); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.declaringType2 = readerModule.GetOwnerType(this); - this.eventType = readerModule.ResolveTypeDefOrRef(eventType, new GenericParamContext(declaringType2)); + bool b = readerModule.TablesStream.TryReadEventRow(origRid, out var row); + Debug.Assert(b); + attributes = row.EventFlags; + name = readerModule.StringsStream.ReadNoNull(row.Name); + declaringType2 = readerModule.GetOwnerType(this); + eventType = readerModule.ResolveTypeDefOrRef(row.EventType, new GenericParamContext(declaringType2)); } internal EventDefMD InitializeAll() { @@ -459,10 +404,10 @@ internal EventDefMD InitializeAll() { /// protected override void InitializeEventMethods_NoLock() { - ThreadSafe.IList newOtherMethods; + IList newOtherMethods; var dt = declaringType2 as TypeDefMD; - if (dt == null) - newOtherMethods = ThreadSafeListCreator.Create(); + if (dt is null) + newOtherMethods = new List(); else dt.InitializeEvent(this, out addMethod, out invokeMethod, out removeMethod, out newOtherMethods); otherMethods = newOtherMethods; diff --git a/src/DotNet/ExportedType.cs b/src/DotNet/ExportedType.cs index b812a709d..4cccc504d 100644 --- a/src/DotNet/ExportedType.cs +++ b/src/DotNet/ExportedType.cs @@ -1,15 +1,17 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; using dnlib.Threading; namespace dnlib.DotNet { /// /// A high-level representation of a row in the ExportedType table /// - public abstract class ExportedType : IHasCustomAttribute, IImplementation, IType { + public abstract class ExportedType : IHasCustomAttribute, IImplementation, IHasCustomDebugInformation, IType { /// /// The row id in its table /// @@ -25,32 +27,26 @@ public abstract class ExportedType : IHasCustomAttribute, IImplementation, IType protected ModuleDef module; /// - public MDToken MDToken { - get { return new MDToken(Table.ExportedType, rid); } - } + public MDToken MDToken => new MDToken(Table.ExportedType, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 17; } - } + public int HasCustomAttributeTag => 17; /// - public int ImplementationTag { - get { return 2; } - } + public int ImplementationTag => 2; /// /// Gets all custom attributes /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -58,118 +54,105 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 17; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// public bool IsValueType { get { var td = Resolve(); - return td != null && td.IsValueType; + return td is not null && td.IsValueType; } } /// - public bool IsPrimitive { - get { return this.IsPrimitive(); } - } + public bool IsPrimitive => this.IsPrimitive(); /// - string IType.TypeName { - get { return FullNameCreator.Name(this, false); } - } + string IType.TypeName => TypeName; /// public UTF8String Name { - get { return typeName; } - set { typeName = value; } + get => typeName; + set => typeName = value; } /// - public string ReflectionName { - get { return FullNameCreator.Name(this, true); } - } + public string ReflectionName => FullNameFactory.Name(this, true, null); /// - public string Namespace { - get { return FullNameCreator.Namespace(this, false); } - } + public string Namespace => TypeNamespace; /// - public string ReflectionNamespace { - get { return FullNameCreator.Namespace(this, true); } - } + public string ReflectionNamespace => FullNameFactory.Namespace(this, true, null); /// - public string FullName { - get { return FullNameCreator.FullName(this, false); } - } + public string FullName => FullNameFactory.FullName(this, false, null, null); /// - public string ReflectionFullName { - get { return FullNameCreator.FullName(this, true); } - } + public string ReflectionFullName => FullNameFactory.FullName(this, true, null, null); /// - public string AssemblyQualifiedName { - get { return FullNameCreator.AssemblyQualifiedName(this); } - } + public string AssemblyQualifiedName => FullNameFactory.AssemblyQualifiedName(this, null, null); /// - public IAssembly DefinitionAssembly { - get { return FullNameCreator.DefinitionAssembly(this); } - } + public IAssembly DefinitionAssembly => FullNameFactory.DefinitionAssembly(this); /// - public IScope Scope { - get { return FullNameCreator.Scope(this); } - } + public IScope Scope => FullNameFactory.Scope(this); /// - public ITypeDefOrRef ScopeType { - get { return FullNameCreator.ScopeType(this); } - } + public ITypeDefOrRef ScopeType => FullNameFactory.ScopeType(this); /// /// Always returns false since a does not contain any /// or . /// - public bool ContainsGenericParameter { - get { return false; } - } + public bool ContainsGenericParameter => false; /// - public ModuleDef Module { - get { return module; } - } + public ModuleDef Module => module; /// - bool IIsTypeOrMethod.IsMethod { - get { return false; } - } + bool IIsTypeOrMethod.IsMethod => false; /// - bool IIsTypeOrMethod.IsType { - get { return true; } - } + bool IIsTypeOrMethod.IsType => true; /// - int IGenericParameterProvider.NumberOfGenericParameters { - get { return 0; } - } + int IGenericParameterProvider.NumberOfGenericParameters => 0; /// /// From column ExportedType.Flags /// public TypeAttributes Attributes { - get { return (TypeAttributes)attributes; } - set { attributes = (int)value; } + get => (TypeAttributes)attributes; + set => attributes = (int)value; } /// Attributes protected int attributes; @@ -178,8 +161,8 @@ public TypeAttributes Attributes { /// From column ExportedType.TypeDefId /// public uint TypeDefId { - get { return typeDefId; } - set { typeDefId = value; } + get => typeDefId; + set => typeDefId = value; } /// protected uint typeDefId; @@ -188,8 +171,8 @@ public uint TypeDefId { /// From column ExportedType.TypeName /// public UTF8String TypeName { - get { return typeName; } - set { typeName = value; } + get => typeName; + set => typeName = value; } /// protected UTF8String typeName; @@ -198,8 +181,8 @@ public UTF8String TypeName { /// From column ExportedType.TypeNamespace /// public UTF8String TypeNamespace { - get { return typeNamespace; } - set { typeNamespace = value; } + get => typeNamespace; + set => typeNamespace = value; } /// protected UTF8String typeNamespace; @@ -243,16 +226,12 @@ void InitializeImplementation() { } /// Called to initialize - protected virtual IImplementation GetImplementation_NoLock() { - return null; - } + protected virtual IImplementation GetImplementation_NoLock() => null; /// /// true if it's nested within another /// - public bool IsNested { - get { return DeclaringType != null; } - } + public bool IsNested => DeclaringType is not null; /// /// Gets the declaring type, if any @@ -271,17 +250,8 @@ public ExportedType DeclaringType { /// /// Value to AND /// Value to OR - void ModifyAttributes(TypeAttributes andMask, TypeAttributes orMask) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - newVal = (origVal & (int)andMask) | (int)orMask; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else + void ModifyAttributes(TypeAttributes andMask, TypeAttributes orMask) => attributes = (attributes & (int)andMask) | (int)orMask; -#endif - } /// /// Set or clear flags in @@ -290,246 +260,210 @@ void ModifyAttributes(TypeAttributes andMask, TypeAttributes orMask) { /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, TypeAttributes flags) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else if (set) attributes |= (int)flags; else attributes &= ~(int)flags; -#endif } /// /// Gets/sets the visibility /// public TypeAttributes Visibility { - get { return (TypeAttributes)attributes & TypeAttributes.VisibilityMask; } - set { ModifyAttributes(~TypeAttributes.VisibilityMask, value & TypeAttributes.VisibilityMask); } + get => (TypeAttributes)attributes & TypeAttributes.VisibilityMask; + set => ModifyAttributes(~TypeAttributes.VisibilityMask, value & TypeAttributes.VisibilityMask); } /// /// true if is set /// - public bool IsNotPublic { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NotPublic; } - } + public bool IsNotPublic => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NotPublic; /// /// true if is set /// - public bool IsPublic { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public; } - } + public bool IsPublic => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public; /// /// true if is set /// - public bool IsNestedPublic { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic; } - } + public bool IsNestedPublic => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic; /// /// true if is set /// - public bool IsNestedPrivate { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate; } - } + public bool IsNestedPrivate => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate; /// /// true if is set /// - public bool IsNestedFamily { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily; } - } + public bool IsNestedFamily => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily; /// /// true if is set /// - public bool IsNestedAssembly { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedAssembly; } - } + public bool IsNestedAssembly => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedAssembly; /// /// true if is set /// - public bool IsNestedFamilyAndAssembly { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamANDAssem; } - } + public bool IsNestedFamilyAndAssembly => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamANDAssem; /// /// true if is set /// - public bool IsNestedFamilyOrAssembly { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem; } - } + public bool IsNestedFamilyOrAssembly => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem; /// /// Gets/sets the layout /// public TypeAttributes Layout { - get { return (TypeAttributes)attributes & TypeAttributes.LayoutMask; } - set { ModifyAttributes(~TypeAttributes.LayoutMask, value & TypeAttributes.LayoutMask); } + get => (TypeAttributes)attributes & TypeAttributes.LayoutMask; + set => ModifyAttributes(~TypeAttributes.LayoutMask, value & TypeAttributes.LayoutMask); } /// /// true if is set /// - public bool IsAutoLayout { - get { return ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.AutoLayout; } - } + public bool IsAutoLayout => ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.AutoLayout; /// /// true if is set /// - public bool IsSequentialLayout { - get { return ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.SequentialLayout; } - } + public bool IsSequentialLayout => ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.SequentialLayout; /// /// true if is set /// - public bool IsExplicitLayout { - get { return ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.ExplicitLayout; } - } + public bool IsExplicitLayout => ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.ExplicitLayout; + + /// + /// true if is set + /// + public bool IsExtendedLayout => ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.ExtendedLayout; /// /// Gets/sets the bit /// public bool IsInterface { - get { return ((TypeAttributes)attributes & TypeAttributes.Interface) != 0; } - set { ModifyAttributes(value, TypeAttributes.Interface); } + get => ((TypeAttributes)attributes & TypeAttributes.Interface) != 0; + set => ModifyAttributes(value, TypeAttributes.Interface); } /// /// Gets/sets the bit /// public bool IsClass { - get { return ((TypeAttributes)attributes & TypeAttributes.Interface) == 0; } - set { ModifyAttributes(!value, TypeAttributes.Interface); } + get => ((TypeAttributes)attributes & TypeAttributes.Interface) == 0; + set => ModifyAttributes(!value, TypeAttributes.Interface); } /// /// Gets/sets the bit /// public bool IsAbstract { - get { return ((TypeAttributes)attributes & TypeAttributes.Abstract) != 0; } - set { ModifyAttributes(value, TypeAttributes.Abstract); } + get => ((TypeAttributes)attributes & TypeAttributes.Abstract) != 0; + set => ModifyAttributes(value, TypeAttributes.Abstract); } /// /// Gets/sets the bit /// public bool IsSealed { - get { return ((TypeAttributes)attributes & TypeAttributes.Sealed) != 0; } - set { ModifyAttributes(value, TypeAttributes.Sealed); } + get => ((TypeAttributes)attributes & TypeAttributes.Sealed) != 0; + set => ModifyAttributes(value, TypeAttributes.Sealed); } /// /// Gets/sets the bit /// public bool IsSpecialName { - get { return ((TypeAttributes)attributes & TypeAttributes.SpecialName) != 0; } - set { ModifyAttributes(value, TypeAttributes.SpecialName); } + get => ((TypeAttributes)attributes & TypeAttributes.SpecialName) != 0; + set => ModifyAttributes(value, TypeAttributes.SpecialName); } /// /// Gets/sets the bit /// public bool IsImport { - get { return ((TypeAttributes)attributes & TypeAttributes.Import) != 0; } - set { ModifyAttributes(value, TypeAttributes.Import); } + get => ((TypeAttributes)attributes & TypeAttributes.Import) != 0; + set => ModifyAttributes(value, TypeAttributes.Import); } /// /// Gets/sets the bit /// public bool IsSerializable { - get { return ((TypeAttributes)attributes & TypeAttributes.Serializable) != 0; } - set { ModifyAttributes(value, TypeAttributes.Serializable); } + get => ((TypeAttributes)attributes & TypeAttributes.Serializable) != 0; + set => ModifyAttributes(value, TypeAttributes.Serializable); } /// /// Gets/sets the bit /// public bool IsWindowsRuntime { - get { return ((TypeAttributes)attributes & TypeAttributes.WindowsRuntime) != 0; } - set { ModifyAttributes(value, TypeAttributes.WindowsRuntime); } + get => ((TypeAttributes)attributes & TypeAttributes.WindowsRuntime) != 0; + set => ModifyAttributes(value, TypeAttributes.WindowsRuntime); } /// /// Gets/sets the string format /// public TypeAttributes StringFormat { - get { return (TypeAttributes)attributes & TypeAttributes.StringFormatMask; } - set { ModifyAttributes(~TypeAttributes.StringFormatMask, value & TypeAttributes.StringFormatMask); } + get => (TypeAttributes)attributes & TypeAttributes.StringFormatMask; + set => ModifyAttributes(~TypeAttributes.StringFormatMask, value & TypeAttributes.StringFormatMask); } /// /// true if is set /// - public bool IsAnsiClass { - get { return ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.AnsiClass; } - } + public bool IsAnsiClass => ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.AnsiClass; /// /// true if is set /// - public bool IsUnicodeClass { - get { return ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.UnicodeClass; } - } + public bool IsUnicodeClass => ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.UnicodeClass; /// /// true if is set /// - public bool IsAutoClass { - get { return ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.AutoClass; } - } + public bool IsAutoClass => ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.AutoClass; /// /// true if is set /// - public bool IsCustomFormatClass { - get { return ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.CustomFormatClass; } - } + public bool IsCustomFormatClass => ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.CustomFormatClass; /// /// Gets/sets the bit /// public bool IsBeforeFieldInit { - get { return ((TypeAttributes)attributes & TypeAttributes.BeforeFieldInit) != 0; } - set { ModifyAttributes(value, TypeAttributes.BeforeFieldInit); } + get => ((TypeAttributes)attributes & TypeAttributes.BeforeFieldInit) != 0; + set => ModifyAttributes(value, TypeAttributes.BeforeFieldInit); } /// /// Gets/sets the bit. See also /// public bool IsForwarder { - get { return ((TypeAttributes)attributes & TypeAttributes.Forwarder) != 0; } - set { ModifyAttributes(value, TypeAttributes.Forwarder); } + get => ((TypeAttributes)attributes & TypeAttributes.Forwarder) != 0; + set => ModifyAttributes(value, TypeAttributes.Forwarder); } /// /// Gets/sets the bit /// public bool IsRuntimeSpecialName { - get { return ((TypeAttributes)attributes & TypeAttributes.RTSpecialName) != 0; } - set { ModifyAttributes(value, TypeAttributes.RTSpecialName); } + get => ((TypeAttributes)attributes & TypeAttributes.RTSpecialName) != 0; + set => ModifyAttributes(value, TypeAttributes.RTSpecialName); } /// /// Gets/sets the bit /// public bool HasSecurity { - get { return ((TypeAttributes)attributes & TypeAttributes.HasSecurity) != 0; } - set { ModifyAttributes(value, TypeAttributes.HasSecurity); } + get => ((TypeAttributes)attributes & TypeAttributes.HasSecurity) != 0; + set => ModifyAttributes(value, TypeAttributes.HasSecurity); } const int MAX_LOOP_ITERS = 50; @@ -539,14 +473,14 @@ public bool HasSecurity { /// public bool MovedToAnotherAssembly { get { - ExportedType et = this; + var et = this; for (int i = 0; i < MAX_LOOP_ITERS; i++) { var impl = et.Implementation; if (impl is AssemblyRef) return et.IsForwarder; et = impl as ExportedType; - if (et == null) + if (et is null) break; } return false; @@ -557,9 +491,7 @@ public bool MovedToAnotherAssembly { /// Resolves the type /// /// A instance or null if it couldn't be resolved - public TypeDef Resolve() { - return Resolve(null); - } + public TypeDef Resolve() => Resolve(null); /// /// Resolves the type @@ -567,7 +499,7 @@ public TypeDef Resolve() { /// Source module or null /// A instance or null if it couldn't be resolved public TypeDef Resolve(ModuleDef sourceModule) { - if (module == null) + if (module is null) return null; return Resolve(sourceModule, this); @@ -575,15 +507,15 @@ public TypeDef Resolve(ModuleDef sourceModule) { static TypeDef Resolve(ModuleDef sourceModule, ExportedType et) { for (int i = 0; i < MAX_LOOP_ITERS; i++) { - if (et == null || et.module == null) + if (et is null || et.module is null) break; var resolver = et.module.Context.AssemblyResolver; var etAsm = resolver.Resolve(et.DefinitionAssembly, sourceModule ?? et.module); - if (etAsm == null) + if (etAsm is null) break; var td = etAsm.Find(et.FullName, false); - if (td != null) + if (td is not null) return td; et = FindExportedType(etAsm, et); @@ -593,8 +525,14 @@ static TypeDef Resolve(ModuleDef sourceModule, ExportedType et) { } static ExportedType FindExportedType(AssemblyDef asm, ExportedType et) { - foreach (var mod in asm.Modules.GetSafeEnumerable()) { - foreach (var et2 in mod.ExportedTypes.GetSafeEnumerable()) { + var modules = asm.Modules; + int count = modules.Count; + for (int i = 0; i < count; i++) { + var mod = modules[i]; + var exportedTypes = mod.ExportedTypes; + int count2 = exportedTypes.Count; + for (int j = 0; j < count2; j++) { + var et2 = exportedTypes[j]; if (new SigComparer(SigComparerOptions.DontCompareTypeScope).Equals(et, et2)) return et2; } @@ -609,9 +547,9 @@ static ExportedType FindExportedType(AssemblyDef asm, ExportedType et) { /// If the type couldn't be resolved public TypeDef ResolveThrow() { var type = Resolve(); - if (type != null) + if (type is not null) return type; - throw new TypeResolveException(string.Format("Could not resolve type: {0} ({1})", this, DefinitionAssembly)); + throw new TypeResolveException($"Could not resolve type: {this} ({DefinitionAssembly})"); } /// @@ -622,13 +560,12 @@ public TypeRef ToTypeRef() { TypeRef result = null, prev = null; var mod = module; IImplementation impl = this; - for (int i = 0; i < MAX_LOOP_ITERS && impl != null; i++) { - var et = impl as ExportedType; - if (et != null) { + for (int i = 0; i < MAX_LOOP_ITERS && impl is not null; i++) { + if (impl is ExportedType et) { var newTr = mod.UpdateRowId(new TypeRefUser(mod, et.TypeNamespace, et.TypeName)); - if (result == null) + if (result is null) result = newTr; - if (prev != null) + if (prev is not null) prev.ResolutionScope = newTr; prev = newTr; @@ -636,15 +573,13 @@ public TypeRef ToTypeRef() { continue; } - var asmRef = impl as AssemblyRef; - if (asmRef != null) { + if (impl is AssemblyRef asmRef) { // prev is never null when we're here prev.ResolutionScope = asmRef; return result; } - var file = impl as FileDef; - if (file != null) { + if (impl is FileDef file) { // prev is never null when we're here prev.ResolutionScope = FindModule(mod, file); return result; @@ -656,20 +591,18 @@ public TypeRef ToTypeRef() { } static ModuleDef FindModule(ModuleDef module, FileDef file) { - if (module == null || file == null) + if (module is null || file is null) return null; if (UTF8String.CaseInsensitiveEquals(module.Name, file.Name)) return module; var asm = module.Assembly; - if (asm == null) + if (asm is null) return null; return asm.FindModule(file.Name); } /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } /// @@ -680,9 +613,7 @@ public class ExportedTypeUser : ExportedType { /// Constructor /// /// Owner module - public ExportedTypeUser(ModuleDef module) { - this.module = module; - } + public ExportedTypeUser(ModuleDef module) => this.module = module; /// /// Constructor @@ -698,9 +629,9 @@ public ExportedTypeUser(ModuleDef module, uint typeDefId, UTF8String typeNamespa this.typeDefId = typeDefId; this.typeName = typeName; this.typeNamespace = typeNamespace; - this.attributes = (int)flags; + attributes = (int)flags; this.implementation = implementation; - this.implementation_isInitialized = true; + implementation_isInitialized = true; } } @@ -715,22 +646,26 @@ sealed class ExportedTypeMD : ExportedType, IMDTokenProviderMD { readonly uint implementationRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.ExportedType, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.ExportedType, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } /// - protected override IImplementation GetImplementation_NoLock() { - return readerModule.ResolveImplementation(implementationRid); + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); } + /// + protected override IImplementation GetImplementation_NoLock() => + readerModule.ResolveImplementation(implementationRid); + /// /// Constructor /// @@ -740,19 +675,21 @@ protected override IImplementation GetImplementation_NoLock() { /// If is invalid public ExportedTypeMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.ExportedTypeTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("ExportedType rid {0} does not exist", rid)); + throw new BadImageFormatException($"ExportedType rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - this.module = readerModule; - uint name, @namespace; - this.implementationRid = readerModule.TablesStream.ReadExportedTypeRow(origRid, out this.attributes, out this.typeDefId, out name, out @namespace); - this.typeName = readerModule.StringsStream.ReadNoNull(name); - this.typeNamespace = readerModule.StringsStream.ReadNoNull(@namespace); + module = readerModule; + bool b = readerModule.TablesStream.TryReadExportedTypeRow(origRid, out var row); + implementationRid = row.Implementation; + attributes = (int)row.Flags; + typeDefId = row.TypeDefId; + typeName = readerModule.StringsStream.ReadNoNull(row.TypeName); + typeNamespace = readerModule.StringsStream.ReadNoNull(row.TypeNamespace); } } } diff --git a/src/DotNet/Extensions.cs b/src/DotNet/Extensions.cs index 4ed09f1bc..2d5da47b6 100644 --- a/src/DotNet/Extensions.cs +++ b/src/DotNet/Extensions.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// Extension methods /// diff --git a/src/DotNet/FieldDef.cs b/src/DotNet/FieldDef.cs index 358126a26..1e72ac34b 100644 --- a/src/DotNet/FieldDef.cs +++ b/src/DotNet/FieldDef.cs @@ -1,8 +1,11 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; using dnlib.PE; using dnlib.Threading; @@ -10,7 +13,7 @@ namespace dnlib.DotNet { /// /// A high-level representation of a row in the Field table /// - public abstract class FieldDef : IHasConstant, IHasCustomAttribute, IHasFieldMarshal, IMemberForwarded, IField, ITokenOperand, IMemberDef { + public abstract class FieldDef : IHasConstant, IHasCustomAttribute, IHasFieldMarshal, IMemberForwarded, IHasCustomDebugInformation, IField, ITokenOperand, IMemberDef { /// /// The row id in its table /// @@ -21,42 +24,32 @@ public abstract class FieldDef : IHasConstant, IHasCustomAttribute, IHasFieldMar #endif /// - public MDToken MDToken { - get { return new MDToken(Table.Field, rid); } - } + public MDToken MDToken => new MDToken(Table.Field, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasConstantTag { - get { return 0; } - } + public int HasConstantTag => 0; /// - public int HasCustomAttributeTag { - get { return 1; } - } + public int HasCustomAttributeTag => 1; /// - public int HasFieldMarshalTag { - get { return 0; } - } + public int HasFieldMarshalTag => 0; /// - public int MemberForwardedTag { - get { return 0; } - } + public int MemberForwardedTag => 0; /// /// Gets all custom attributes /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -64,16 +57,37 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); + + /// + public int HasCustomDebugInformationTag => 1; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// /// From column Field.Flags /// public FieldAttributes Attributes { - get { return (FieldAttributes)attributes; } - set { attributes = (int)value; } + get => (FieldAttributes)attributes; + set => attributes = (int)value; } /// Attributes protected int attributes; @@ -82,8 +96,8 @@ public FieldAttributes Attributes { /// From column Field.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -92,8 +106,8 @@ public UTF8String Name { /// From column Field.Signature /// public CallingConventionSig Signature { - get { return signature; } - set { signature = value; } + get => signature; + set => signature = value; } /// protected CallingConventionSig signature; @@ -137,9 +151,7 @@ void InitializeFieldOffset() { } /// Called to initialize - protected virtual uint? GetFieldOffset_NoLock() { - return null; - } + protected virtual uint? GetFieldOffset_NoLock() => null; /// public MarshalType MarshalType { @@ -178,14 +190,11 @@ void InitializeMarshalType() { } /// Called to initialize - protected virtual MarshalType GetMarshalType_NoLock() { - return null; - } + protected virtual MarshalType GetMarshalType_NoLock() => null; /// Reset - protected void ResetMarshalType() { + protected void ResetMarshalType() => marshalType_isInitialized = false; - } /// /// Gets/sets the field RVA @@ -226,14 +235,10 @@ void InitializeRVA() { } /// Called to initialize - protected virtual RVA GetRVA_NoLock() { - return 0; - } + protected virtual RVA GetRVA_NoLock() => 0; /// Reset - protected void ResetRVA() { - rva_isInitialized = false; - } + protected void ResetRVA() => rva_isInitialized = false; /// /// Gets/sets the initial value. Be sure to set to true if @@ -275,14 +280,10 @@ void InitializeInitialValue() { } /// Called to initialize - protected virtual byte[] GetInitialValue_NoLock() { - return null; - } + protected virtual byte[] GetInitialValue_NoLock() => null; /// Reset - protected void ResetInitialValue() { - initialValue_isInitialized = false; - } + protected void ResetInitialValue() => initialValue_isInitialized = false; /// public ImplMap ImplMap { @@ -321,9 +322,7 @@ void InitializeImplMap() { } /// Called to initialize - protected virtual ImplMap GetImplMap_NoLock() { - return null; - } + protected virtual ImplMap GetImplMap_NoLock() => null; /// public Constant Constant { @@ -362,45 +361,35 @@ void InitializeConstant() { } /// Called to initialize - protected virtual Constant GetConstant_NoLock() { - return null; - } + protected virtual Constant GetConstant_NoLock() => null; /// Reset - protected void ResetConstant() { - constant_isInitialized = false; - } + protected void ResetConstant() => constant_isInitialized = false; /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } - } + public bool HasCustomAttributes => CustomAttributes.Count > 0; /// - public bool HasImplMap { - get { return ImplMap != null; } - } + public bool HasImplMap => ImplMap is not null; /// /// Gets/sets the declaring type (owner type) /// public TypeDef DeclaringType { - get { return declaringType2; } + get => declaringType2; set { var currentDeclaringType = DeclaringType2; if (currentDeclaringType == value) return; - if (currentDeclaringType != null) + if (currentDeclaringType is not null) currentDeclaringType.Fields.Remove(this); // Will set DeclaringType2 = null - if (value != null) + if (value is not null) value.Fields.Add(this); // Will set DeclaringType2 = value } } /// - ITypeDefOrRef IMemberRef.DeclaringType { - get { return declaringType2; } - } + ITypeDefOrRef IMemberRef.DeclaringType => declaringType2; /// /// Called by and should normally not be called by any user @@ -408,8 +397,8 @@ ITypeDefOrRef IMemberRef.DeclaringType { /// declaring type without inserting it in the declaring type's method list. /// public TypeDef DeclaringType2 { - get { return declaringType2; } - set { declaringType2 = value; } + get => declaringType2; + set => declaringType2 = value; } /// protected TypeDef declaringType2; @@ -418,83 +407,36 @@ public TypeDef DeclaringType2 { /// Gets/sets the /// public FieldSig FieldSig { - get { return signature as FieldSig; } - set { signature = value; } + get => signature as FieldSig; + set => signature = value; } /// - public ModuleDef Module { - get { - var dt = declaringType2; - return dt == null ? null : dt.Module; - } - } - - bool IIsTypeOrMethod.IsType { - get { return false; } - } - - bool IIsTypeOrMethod.IsMethod { - get { return false; } - } - - bool IMemberRef.IsField { - get { return true; } - } - - bool IMemberRef.IsTypeSpec { - get { return false; } - } - - bool IMemberRef.IsTypeRef { - get { return false; } - } - - bool IMemberRef.IsTypeDef { - get { return false; } - } - - bool IMemberRef.IsMethodSpec { - get { return false; } - } - - bool IMemberRef.IsMethodDef { - get { return false; } - } - - bool IMemberRef.IsMemberRef { - get { return false; } - } - - bool IMemberRef.IsFieldDef { - get { return true; } - } - - bool IMemberRef.IsPropertyDef { - get { return false; } - } - - bool IMemberRef.IsEventDef { - get { return false; } - } - - bool IMemberRef.IsGenericParam { - get { return false; } - } + public ModuleDef Module => declaringType2?.Module; + + bool IIsTypeOrMethod.IsType => false; + bool IIsTypeOrMethod.IsMethod => false; + bool IMemberRef.IsField => true; + bool IMemberRef.IsTypeSpec => false; + bool IMemberRef.IsTypeRef => false; + bool IMemberRef.IsTypeDef => false; + bool IMemberRef.IsMethodSpec => false; + bool IMemberRef.IsMethodDef => false; + bool IMemberRef.IsMemberRef => false; + bool IMemberRef.IsFieldDef => true; + bool IMemberRef.IsPropertyDef => false; + bool IMemberRef.IsEventDef => false; + bool IMemberRef.IsGenericParam => false; /// /// true if is not null /// - public bool HasLayoutInfo { - get { return FieldOffset != null; } - } + public bool HasLayoutInfo => FieldOffset is not null; /// /// true if is not null /// - public bool HasConstant { - get { return Constant != null; } - } + public bool HasConstant => Constant is not null; /// /// Gets the constant element type or if there's no constant @@ -502,25 +444,23 @@ public bool HasConstant { public ElementType ElementType { get { var c = Constant; - return c == null ? ElementType.End : c.Type; + return c is null ? ElementType.End : c.Type; } } /// /// true if is not null /// - public bool HasMarshalType { - get { return MarshalType != null; } - } + public bool HasMarshalType => MarshalType is not null; /// /// Gets/sets the field type /// public TypeSig FieldType { - get { return FieldSig.GetFieldType(); } + get => FieldSig.GetFieldType(); set { var sig = FieldSig; - if (sig != null) + if (sig is not null) sig.Type = value; } } @@ -531,17 +471,8 @@ public TypeSig FieldType { /// /// Value to AND /// Value to OR - void ModifyAttributes(FieldAttributes andMask, FieldAttributes orMask) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - newVal = (origVal & (int)andMask) | (int)orMask; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else + void ModifyAttributes(FieldAttributes andMask, FieldAttributes orMask) => attributes = (attributes & (int)andMask) | (int)orMask; -#endif - } /// /// Set or clear flags in @@ -550,183 +481,150 @@ void ModifyAttributes(FieldAttributes andMask, FieldAttributes orMask) { /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, FieldAttributes flags) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else if (set) attributes |= (int)flags; else attributes &= ~(int)flags; -#endif } /// /// Gets/sets the field access /// public FieldAttributes Access { - get { return (FieldAttributes)attributes & FieldAttributes.FieldAccessMask; } - set { ModifyAttributes(~FieldAttributes.FieldAccessMask, value & FieldAttributes.FieldAccessMask); } + get => (FieldAttributes)attributes & FieldAttributes.FieldAccessMask; + set => ModifyAttributes(~FieldAttributes.FieldAccessMask, value & FieldAttributes.FieldAccessMask); } /// /// true if is set /// - public bool IsCompilerControlled { - get { return IsPrivateScope; } - } + public bool IsCompilerControlled => IsPrivateScope; /// /// true if is set /// - public bool IsPrivateScope { - get { return ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.PrivateScope; } - } + public bool IsPrivateScope => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.PrivateScope; /// /// true if is set /// - public bool IsPrivate { - get { return ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Private; } - } + public bool IsPrivate => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Private; /// /// true if is set /// - public bool IsFamilyAndAssembly { - get { return ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamANDAssem; } - } + public bool IsFamilyAndAssembly => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamANDAssem; /// /// true if is set /// - public bool IsAssembly { - get { return ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Assembly; } - } + public bool IsAssembly => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Assembly; /// /// true if is set /// - public bool IsFamily { - get { return ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Family; } - } + public bool IsFamily => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Family; /// /// true if is set /// - public bool IsFamilyOrAssembly { - get { return ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamORAssem; } - } + public bool IsFamilyOrAssembly => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamORAssem; /// /// true if is set /// - public bool IsPublic { - get { return ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public; } - } + public bool IsPublic => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public; /// /// Gets/sets the bit /// public bool IsStatic { - get { return ((FieldAttributes)attributes & FieldAttributes.Static) != 0; } - set { ModifyAttributes(value, FieldAttributes.Static); } + get => ((FieldAttributes)attributes & FieldAttributes.Static) != 0; + set => ModifyAttributes(value, FieldAttributes.Static); } /// /// Gets/sets the bit /// public bool IsInitOnly { - get { return ((FieldAttributes)attributes & FieldAttributes.InitOnly) != 0; } - set { ModifyAttributes(value, FieldAttributes.InitOnly); } + get => ((FieldAttributes)attributes & FieldAttributes.InitOnly) != 0; + set => ModifyAttributes(value, FieldAttributes.InitOnly); } /// /// Gets/sets the bit /// public bool IsLiteral { - get { return ((FieldAttributes)attributes & FieldAttributes.Literal) != 0; } - set { ModifyAttributes(value, FieldAttributes.Literal); } + get => ((FieldAttributes)attributes & FieldAttributes.Literal) != 0; + set => ModifyAttributes(value, FieldAttributes.Literal); } /// /// Gets/sets the bit /// public bool IsNotSerialized { - get { return ((FieldAttributes)attributes & FieldAttributes.NotSerialized) != 0; } - set { ModifyAttributes(value, FieldAttributes.NotSerialized); } + get => ((FieldAttributes)attributes & FieldAttributes.NotSerialized) != 0; + set => ModifyAttributes(value, FieldAttributes.NotSerialized); } /// /// Gets/sets the bit /// public bool IsSpecialName { - get { return ((FieldAttributes)attributes & FieldAttributes.SpecialName) != 0; } - set { ModifyAttributes(value, FieldAttributes.SpecialName); } + get => ((FieldAttributes)attributes & FieldAttributes.SpecialName) != 0; + set => ModifyAttributes(value, FieldAttributes.SpecialName); } /// /// Gets/sets the bit /// public bool IsPinvokeImpl { - get { return ((FieldAttributes)attributes & FieldAttributes.PinvokeImpl) != 0; } - set { ModifyAttributes(value, FieldAttributes.PinvokeImpl); } + get => ((FieldAttributes)attributes & FieldAttributes.PinvokeImpl) != 0; + set => ModifyAttributes(value, FieldAttributes.PinvokeImpl); } /// /// Gets/sets the bit /// public bool IsRuntimeSpecialName { - get { return ((FieldAttributes)attributes & FieldAttributes.RTSpecialName) != 0; } - set { ModifyAttributes(value, FieldAttributes.RTSpecialName); } + get => ((FieldAttributes)attributes & FieldAttributes.RTSpecialName) != 0; + set => ModifyAttributes(value, FieldAttributes.RTSpecialName); } /// /// Gets/sets the bit /// public bool HasFieldMarshal { - get { return ((FieldAttributes)attributes & FieldAttributes.HasFieldMarshal) != 0; } - set { ModifyAttributes(value, FieldAttributes.HasFieldMarshal); } + get => ((FieldAttributes)attributes & FieldAttributes.HasFieldMarshal) != 0; + set => ModifyAttributes(value, FieldAttributes.HasFieldMarshal); } /// /// Gets/sets the bit /// public bool HasDefault { - get { return ((FieldAttributes)attributes & FieldAttributes.HasDefault) != 0; } - set { ModifyAttributes(value, FieldAttributes.HasDefault); } + get => ((FieldAttributes)attributes & FieldAttributes.HasDefault) != 0; + set => ModifyAttributes(value, FieldAttributes.HasDefault); } /// /// Gets/sets the bit /// public bool HasFieldRVA { - get { return ((FieldAttributes)attributes & FieldAttributes.HasFieldRVA) != 0; } - set { ModifyAttributes(value, FieldAttributes.HasFieldRVA); } + get => ((FieldAttributes)attributes & FieldAttributes.HasFieldRVA) != 0; + set => ModifyAttributes(value, FieldAttributes.HasFieldRVA); } /// /// Returns the full name of this field /// - public string FullName { - get { - var dt = declaringType2; - return FullNameCreator.FieldFullName(dt == null ? null : dt.FullName, name, FieldSig); - } - } + public string FullName => FullNameFactory.FieldFullName(declaringType2?.FullName, name, FieldSig, null, null); /// /// Gets the size of this field in bytes or 0 if unknown. /// public uint GetFieldSize() { - uint size; - if (!GetFieldSize(out size)) + if (!GetFieldSize(out uint size)) return 0; return size; } @@ -736,9 +634,7 @@ public uint GetFieldSize() { /// /// Updated with size /// true if is valid, false otherwise - public bool GetFieldSize(out uint size) { - return GetFieldSize(declaringType2, FieldSig, out size); - } + public bool GetFieldSize(out uint size) => GetFieldSize(declaringType2, FieldSig, out size); /// /// Gets the size of this field in bytes or 0 if unknown. @@ -747,9 +643,7 @@ public bool GetFieldSize(out uint size) { /// The field signature of this /// Updated with size /// true if is valid, false otherwise - protected bool GetFieldSize(TypeDef declaringType, FieldSig fieldSig, out uint size) { - return GetFieldSize(declaringType, fieldSig, GetPointerSize(declaringType), out size); - } + protected bool GetFieldSize(TypeDef declaringType, FieldSig fieldSig, out uint size) => GetFieldSize(declaringType, fieldSig, GetPointerSize(declaringType), out size); /// /// Gets the size of this field in bytes or 0 if unknown. @@ -761,7 +655,7 @@ protected bool GetFieldSize(TypeDef declaringType, FieldSig fieldSig, out uint s /// true if is valid, false otherwise protected bool GetFieldSize(TypeDef declaringType, FieldSig fieldSig, int ptrSize, out uint size) { size = 0; - if (fieldSig == null) + if (fieldSig is null) return false; return GetClassSize(declaringType, fieldSig.Type, ptrSize, out size); } @@ -769,7 +663,7 @@ protected bool GetFieldSize(TypeDef declaringType, FieldSig fieldSig, int ptrSiz bool GetClassSize(TypeDef declaringType, TypeSig ts, int ptrSize, out uint size) { size = 0; ts = ts.RemovePinnedAndModifiers(); - if (ts == null) + if (ts is null) return false; int size2 = ts.ElementType.GetPrimitiveSize(ptrSize); @@ -779,33 +673,31 @@ bool GetClassSize(TypeDef declaringType, TypeSig ts, int ptrSize, out uint size) } var tdrs = ts as TypeDefOrRefSig; - if (tdrs == null) + if (tdrs is null) return false; var td = tdrs.TypeDef; - if (td != null) + if (td is not null) return TypeDef.GetClassSize(td, out size); var tr = tdrs.TypeRef; - if (tr != null) + if (tr is not null) return TypeDef.GetClassSize(tr.Resolve(), out size); return false; } int GetPointerSize(TypeDef declaringType) { - if (declaringType == null) + if (declaringType is null) return 4; var module = declaringType.Module; - if (module == null) + if (module is null) return 4; return module.GetPointerSize(); } /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } /// @@ -859,51 +751,53 @@ sealed class FieldDefMD : FieldDef, IMDTokenProviderMD { readonly FieldAttributes origAttributes; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.Field, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Field, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } /// - protected override uint? GetFieldOffset_NoLock() { - return readerModule.TablesStream.ReadFieldLayoutRow2(readerModule.MetaData.GetFieldLayoutRid(origRid)); + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(declaringType2), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); } /// - protected override MarshalType GetMarshalType_NoLock() { - return readerModule.ReadMarshalType(Table.Field, origRid, new GenericParamContext(declaringType2)); + protected override uint? GetFieldOffset_NoLock() { + if (readerModule.TablesStream.TryReadFieldLayoutRow(readerModule.Metadata.GetFieldLayoutRid(origRid), out var row)) + return row.OffSet; + return null; } + /// + protected override MarshalType GetMarshalType_NoLock() => + readerModule.ReadMarshalType(Table.Field, origRid, new GenericParamContext(declaringType2)); + /// protected override RVA GetRVA_NoLock() { - RVA rva2; - GetFieldRVA_NoLock(out rva2); + GetFieldRVA_NoLock(out var rva2); return rva2; } /// protected override byte[] GetInitialValue_NoLock() { - RVA rva2; - if (!GetFieldRVA_NoLock(out rva2)) + if (!GetFieldRVA_NoLock(out var rva2)) return null; return ReadInitialValue_NoLock(rva2); } /// - protected override ImplMap GetImplMap_NoLock() { - return readerModule.ResolveImplMap(readerModule.MetaData.GetImplMapRid(Table.Field, origRid)); - } + protected override ImplMap GetImplMap_NoLock() => + readerModule.ResolveImplMap(readerModule.Metadata.GetImplMapRid(Table.Field, origRid)); /// - protected override Constant GetConstant_NoLock() { - return readerModule.ResolveConstant(readerModule.MetaData.GetConstantRid(Table.Field, origRid)); - } + protected override Constant GetConstant_NoLock() => + readerModule.ResolveConstant(readerModule.Metadata.GetConstantRid(Table.Field, origRid)); /// /// Constructor @@ -914,20 +808,21 @@ protected override Constant GetConstant_NoLock() { /// If is invalid public FieldDefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.FieldTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("Field rid {0} does not exist", rid)); + throw new BadImageFormatException($"Field rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint name; - uint signature = readerModule.TablesStream.ReadFieldRow(origRid, out this.attributes, out name); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.origAttributes = (FieldAttributes)attributes; - this.declaringType2 = readerModule.GetOwnerType(this); - this.signature = readerModule.ReadSignature(signature, new GenericParamContext(declaringType2)); + bool b = readerModule.TablesStream.TryReadFieldRow(origRid, out var row); + Debug.Assert(b); + name = readerModule.StringsStream.ReadNoNull(row.Name); + attributes = row.Flags; + origAttributes = (FieldAttributes)attributes; + declaringType2 = readerModule.GetOwnerType(this); + signature = readerModule.ReadSignature(row.Signature, new GenericParamContext(declaringType2)); } internal FieldDefMD InitializeAll() { @@ -950,12 +845,16 @@ bool GetFieldRVA_NoLock(out RVA rva) { rva = 0; return false; } - return readerModule.TablesStream.ReadFieldRVARow(readerModule.MetaData.GetFieldRVARid(origRid), out rva); + if (!readerModule.TablesStream.TryReadFieldRVARow(readerModule.Metadata.GetFieldRVARid(origRid), out var row)) { + rva = 0; + return false; + } + rva = (RVA)row.RVA; + return true; } byte[] ReadInitialValue_NoLock(RVA rva) { - uint size; - if (!GetFieldSize(declaringType2, signature as FieldSig, out size)) + if (!GetFieldSize(declaringType2, signature as FieldSig, out uint size)) return null; if (size >= int.MaxValue) return null; diff --git a/src/DotNet/FileAttributes.cs b/src/DotNet/FileAttributes.cs index 6fc1de139..3fab691ad 100644 --- a/src/DotNet/FileAttributes.cs +++ b/src/DotNet/FileAttributes.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.DotNet { /// @@ -9,8 +9,8 @@ namespace dnlib.DotNet { [Flags] public enum FileAttributes : uint { /// This is not a resource file - ContainsMetaData = 0x0000, + ContainsMetadata = 0x0000, /// This is a resource file or other non-metadata-containing file - ContainsNoMetaData = 0x0001, + ContainsNoMetadata = 0x0001, } } diff --git a/src/DotNet/FileDef.cs b/src/DotNet/FileDef.cs index 6681dff6a..9f65542b0 100644 --- a/src/DotNet/FileDef.cs +++ b/src/DotNet/FileDef.cs @@ -1,46 +1,43 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// /// A high-level representation of a row in the File table /// - public abstract class FileDef : IHasCustomAttribute, IImplementation, IManagedEntryPoint { + public abstract class FileDef : IHasCustomAttribute, IImplementation, IHasCustomDebugInformation, IManagedEntryPoint { /// /// The row id in its table /// protected uint rid; /// - public MDToken MDToken { - get { return new MDToken(Table.File, rid); } - } + public MDToken MDToken => new MDToken(Table.File, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 16; } - } + public int HasCustomAttributeTag => 16; /// - public int ImplementationTag { - get { return 0; } - } + public int ImplementationTag => 0; /// /// From column File.Flags /// public FileAttributes Flags { - get { return (FileAttributes)attributes; } - set { attributes = (int)value; } + get => (FileAttributes)attributes; + set => attributes = (int)value; } /// Attributes protected int attributes; @@ -49,8 +46,8 @@ public FileAttributes Flags { /// From column File.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -59,8 +56,8 @@ public UTF8String Name { /// From column File.HashValue /// public byte[] HashValue { - get { return hashValue; } - set { hashValue = value; } + get => hashValue; + set => hashValue = value; } /// protected byte[] hashValue; @@ -70,7 +67,7 @@ public byte[] HashValue { /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -78,14 +75,33 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 16; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// /// Set or clear flags in @@ -94,48 +110,33 @@ public bool HasCustomAttributes { /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, FileAttributes flags) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else if (set) attributes |= (int)flags; else attributes &= ~(int)flags; -#endif } /// - /// Gets/sets the bit + /// Gets/sets the bit /// - public bool ContainsMetaData { - get { return ((FileAttributes)attributes & FileAttributes.ContainsNoMetaData) == 0; } - set { ModifyAttributes(!value, FileAttributes.ContainsNoMetaData); } + public bool ContainsMetadata { + get => ((FileAttributes)attributes & FileAttributes.ContainsNoMetadata) == 0; + set => ModifyAttributes(!value, FileAttributes.ContainsNoMetadata); } /// - /// Gets/sets the bit + /// Gets/sets the bit /// - public bool ContainsNoMetaData { - get { return ((FileAttributes)attributes & FileAttributes.ContainsNoMetaData) != 0; } - set { ModifyAttributes(value, FileAttributes.ContainsNoMetaData); } + public bool ContainsNoMetadata { + get => ((FileAttributes)attributes & FileAttributes.ContainsNoMetadata) != 0; + set => ModifyAttributes(value, FileAttributes.ContainsNoMetadata); } /// - public string FullName { - get { return UTF8String.ToSystemStringOrEmpty(name); } - } + public string FullName => UTF8String.ToSystemStringOrEmpty(name); /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } /// @@ -156,7 +157,7 @@ public FileDefUser() { /// File hash public FileDefUser(UTF8String name, FileAttributes flags, byte[] hashValue) { this.name = name; - this.attributes = (int)flags; + attributes = (int)flags; this.hashValue = hashValue; } } @@ -171,17 +172,22 @@ sealed class FileDefMD : FileDef, IMDTokenProviderMD { readonly uint origRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.File, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.File, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -191,18 +197,19 @@ protected override void InitializeCustomAttributes() { /// If is invalid public FileDefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.FileTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("File rid {0} does not exist", rid)); + throw new BadImageFormatException($"File rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint name; - uint hashValue = readerModule.TablesStream.ReadFileRow(origRid, out this.attributes, out name); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.hashValue = readerModule.BlobStream.Read(hashValue); + bool b = readerModule.TablesStream.TryReadFileRow(origRid, out var row); + Debug.Assert(b); + attributes = (int)row.Flags; + name = readerModule.StringsStream.ReadNoNull(row.Name); + hashValue = readerModule.BlobStream.Read(row.HashValue); } } } diff --git a/src/DotNet/FrameworkRedirect.cs b/src/DotNet/FrameworkRedirect.cs index 602214b38..7208342fd 100644 --- a/src/DotNet/FrameworkRedirect.cs +++ b/src/DotNet/FrameworkRedirect.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; @@ -11,7 +11,7 @@ public static class FrameworkRedirect { static readonly Dictionary frmRedir2; static readonly Dictionary frmRedir4; - struct FrameworkRedirectInfo { + readonly struct FrameworkRedirectInfo { public readonly PublicKeyToken publicKeyToken; public readonly Version redirectVersion; @@ -295,29 +295,95 @@ static void InitFrameworkRedirectV4() { } /// - /// Redirects a .NET framework assembly from an older version to the correct version + /// Redirects a .NET Framework assembly from an older version to the correct version /// loaded at runtime. /// /// Current assembly reference that might get updated /// Module using the assembly reference public static void ApplyFrameworkRedirect(ref IAssembly assembly, ModuleDef sourceModule) { - if (sourceModule == null) - return; - if (!Utils.LocaleEquals(assembly.Culture, "")) - return; - if (!sourceModule.IsClr20 && !sourceModule.IsClr40) - return; + if (TryApplyFrameworkRedirectCore(assembly, sourceModule, out var redirectedAssembly)) + assembly = redirectedAssembly; + } + + /// + /// Tries to redirect a .NET Framework assembly from an older version to the correct version + /// loaded at runtime. + /// + /// Assembly reference + /// Module using the assembly reference + /// Updated with the redirected assembly if successful + /// + public static bool TryApplyFrameworkRedirect(IAssembly assembly, ModuleDef sourceModule, out IAssembly redirectedAssembly) => + TryApplyFrameworkRedirectCore(assembly, sourceModule, out redirectedAssembly); + + static bool TryApplyFrameworkRedirectCore(IAssembly assembly, ModuleDef sourceModule, out IAssembly redirectedAssembly) { + if (sourceModule is not null) { + if (sourceModule.IsClr40) + return TryApplyFrameworkRedirect(assembly, frmRedir4, out redirectedAssembly); + if (sourceModule.IsClr20) + return TryApplyFrameworkRedirect(assembly, frmRedir2, out redirectedAssembly); + } - FrameworkRedirectInfo redirect; - if (!(sourceModule.IsClr20 ? frmRedir2 : frmRedir4).TryGetValue(assembly.Name, out redirect)) - return; + redirectedAssembly = null; + return false; + } + + /// + /// Redirects a .NET Framework 2.0-3.5 assembly from an older version to the correct version + /// loaded at runtime. + /// + /// Current assembly reference that might get updated + public static void ApplyFrameworkRedirectV2(ref IAssembly assembly) { + if (TryApplyFrameworkRedirect(assembly, frmRedir2, out var redirectedAssembly)) + assembly = redirectedAssembly; + } + + /// + /// Redirects a .NET Framework 4.0+ assembly from an older version to the correct version + /// loaded at runtime. + /// + /// Current assembly reference that might get updated + public static void ApplyFrameworkRedirectV4(ref IAssembly assembly) { + if (TryApplyFrameworkRedirect(assembly, frmRedir4, out var redirectedAssembly)) + assembly = redirectedAssembly; + } + + /// + /// Tries to redirect a .NET Framework 2.0-3.5 assembly from an older version to the correct version + /// loaded at runtime. + /// + /// Assembly reference + /// Updated with the redirected assembly if successful + /// + public static bool TryApplyFrameworkRedirectV2(IAssembly assembly, out IAssembly redirectedAssembly) => + TryApplyFrameworkRedirect(assembly, frmRedir2, out redirectedAssembly); + + /// + /// Tries to redirect a .NET Framework 4.0+ assembly from an older version to the correct version + /// loaded at runtime. + /// + /// Assembly reference + /// Updated with the redirected assembly if successful + /// + public static bool TryApplyFrameworkRedirectV4(IAssembly assembly, out IAssembly redirectedAssembly) => + TryApplyFrameworkRedirect(assembly, frmRedir4, out redirectedAssembly); + + static bool TryApplyFrameworkRedirect(IAssembly assembly, Dictionary frmRedir, out IAssembly redirectedAssembly) { + redirectedAssembly = null; + if (!Utils.LocaleEquals(assembly.Culture, "")) + return false; + if (!frmRedir.TryGetValue(assembly.Name, out var redirect)) + return false; if (PublicKeyBase.TokenCompareTo(assembly.PublicKeyOrToken, redirect.publicKeyToken) != 0) - return; - if (Utils.CompareTo(assembly.Version, redirect.redirectVersion) == 0) - return; + return false; - assembly = new AssemblyNameInfo(assembly); - assembly.Version = redirect.redirectVersion; + if (Utils.CompareTo(assembly.Version, redirect.redirectVersion) == 0) + redirectedAssembly = assembly; + else { + redirectedAssembly = new AssemblyNameInfo(assembly); + redirectedAssembly.Version = redirect.redirectVersion; + } + return true; } } } diff --git a/src/DotNet/FullNameCreator.cs b/src/DotNet/FullNameFactory.cs similarity index 57% rename from src/DotNet/FullNameCreator.cs rename to src/DotNet/FullNameFactory.cs index a2fa73245..5590bb745 100644 --- a/src/DotNet/FullNameCreator.cs +++ b/src/DotNet/FullNameFactory.cs @@ -1,14 +1,13 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; +using System.Collections.Generic; using System.Text; -using dnlib.Threading; namespace dnlib.DotNet { /// - /// Helps create a name + /// Helps create a name /// - public interface IFullNameCreatorHelper { + public interface IFullNameFactoryHelper { /// /// Checks whether the assembly name should be included when printing /// the full type name. The assembly name isn't required in custom attributes @@ -24,77 +23,186 @@ public interface IFullNameCreatorHelper { /// /// Creates type names, method names, etc. /// - public struct FullNameCreator { + public struct FullNameFactory { + const uint MaxArrayRank = 100; + const uint MaxMethodGenParamCount = 200; const string RECURSION_ERROR_RESULT_STRING = "<<>>"; const string NULLVALUE = "<<>>"; readonly StringBuilder sb; readonly bool isReflection; - readonly IFullNameCreatorHelper helper; + readonly IFullNameFactoryHelper helper; GenericArguments genericArguments; RecursionCounter recursionCounter; /// /// Checks whether the assembly name should be included when printing the full name. - /// See for more info. + /// See for more info. /// /// Owner module /// The type (TypeDef, TypeRef or ExportedType) /// or null /// true if the assembly name must be included, false otherwise - public static bool MustUseAssemblyName(ModuleDef module, IType type) { - var td = type as TypeDef; - if (td != null) + public static bool MustUseAssemblyName(ModuleDef module, IType type) => MustUseAssemblyName(module, type, true); + + /// + /// Checks whether the assembly name should be included when printing the full name. + /// See for more info. + /// + /// Owner module + /// The type (TypeDef, TypeRef or ExportedType) + /// or null + /// If false, don't add an assembly name if it's a type in , + /// if true, don't add an assembly name if it's a type in or the corlib. + /// true if the assembly name must be included, false otherwise + public static bool MustUseAssemblyName(ModuleDef module, IType type, bool allowCorlib) { + if (type is TypeDef td) return td.Module != module; var tr = type as TypeRef; - if (tr == null) + if (tr is null) return true; if (tr.ResolutionScope == AssemblyRef.CurrentAssembly) return false; - if (!tr.DefinitionAssembly.IsCorLib()) + if (allowCorlib) { + if (!tr.DefinitionAssembly.IsCorLib()) + return true; + // If it's present in this module, but it's a corlib type, then we will need the + // assembly name. + return module.Find(tr) is not null; + } + else return true; - // If it's present in this module, but it's a corlib type, then we will need the - // assembly name. - return module.Find(tr) != null; + } + + /// + /// Returns the full name of a + /// + /// The TypeRef + /// Set if output should be compatible with reflection + /// Helps print the name + /// String builder to use or null + /// The full name + public static string FullName(IType type, bool isReflection, IFullNameFactoryHelper helper, StringBuilder sb) => + FullNameSB(type, isReflection, helper, sb).ToString(); + + /// + /// Returns the full name of a + /// + /// The TypeRef + /// Set if output should be compatible with reflection + /// Helps print the name + /// String builder to use or null + /// The full name + public static StringBuilder FullNameSB(IType type, bool isReflection, IFullNameFactoryHelper helper, StringBuilder sb) { + if (type is TypeDef td) + return FullNameSB(td, isReflection, helper, sb); + if (type is TypeRef tr) + return FullNameSB(tr, isReflection, helper, sb); + if (type is TypeSpec ts) + return FullNameSB(ts, isReflection, helper, sb); + if (type is TypeSig sig) + return FullNameSB(sig, isReflection, helper, null, null, sb); + if (type is ExportedType et) + return FullNameSB(et, isReflection, helper, sb); + return sb ?? new StringBuilder(); + } + + /// + /// Returns the name of a + /// + /// The TypeRef + /// Set if output should be compatible with reflection + /// String builder to use or null + /// The full name + public static string Name(IType type, bool isReflection, StringBuilder sb) => + NameSB(type, isReflection, sb).ToString(); + + /// + /// Returns the name of a + /// + /// The TypeRef + /// Set if output should be compatible with reflection + /// String builder to use or null + /// The full name + public static StringBuilder NameSB(IType type, bool isReflection, StringBuilder sb) { + if (type is TypeDef td) + return NameSB(td, isReflection, sb); + if (type is TypeRef tr) + return NameSB(tr, isReflection, sb); + if (type is TypeSpec ts) + return NameSB(ts, isReflection, sb); + if (type is TypeSig sig) + return NameSB(sig, isReflection, sb); + if (type is ExportedType et) + return NameSB(et, isReflection, sb); + return sb ?? new StringBuilder(); + } + + /// + /// Returns the namespace of a + /// + /// The TypeRef + /// Set if output should be compatible with reflection + /// String builder to use or null + /// The full name + public static string Namespace(IType type, bool isReflection, StringBuilder sb) => + NamespaceSB(type, isReflection, sb).ToString(); + + /// + /// Returns the namespace of a + /// + /// The TypeRef + /// Set if output should be compatible with reflection + /// String builder to use or null + /// The full name + public static StringBuilder NamespaceSB(IType type, bool isReflection, StringBuilder sb) { + if (type is TypeDef td) + return NamespaceSB(td, isReflection, sb); + if (type is TypeRef tr) + return NamespaceSB(tr, isReflection, sb); + if (type is TypeSpec ts) + return NamespaceSB(ts, isReflection, sb); + if (type is TypeSig sig) + return NamespaceSB(sig, isReflection, sb); + if (type is ExportedType et) + return NamespaceSB(et, isReflection, sb); + return sb ?? new StringBuilder(); } /// /// Returns the assembly qualified full name of a /// /// The IType + /// Helps print the name + /// String builder to use or null /// The assembly qualified full name - public static string AssemblyQualifiedName(IType type) { - return AssemblyQualifiedName(type, null); - } + public static string AssemblyQualifiedName(IType type, IFullNameFactoryHelper helper = null, StringBuilder sb = null) => + AssemblyQualifiedNameSB(type, helper, sb).ToString(); /// /// Returns the assembly qualified full name of a /// /// The IType /// Helps print the name + /// String builder to use or null /// The assembly qualified full name - public static string AssemblyQualifiedName(IType type, IFullNameCreatorHelper helper) { - var td = type as TypeDef; - if (td != null) - return AssemblyQualifiedName(td, helper); + public static StringBuilder AssemblyQualifiedNameSB(IType type, IFullNameFactoryHelper helper, StringBuilder sb) { + if (type is TypeDef td) + return AssemblyQualifiedNameSB(td, helper, sb); - var tr = type as TypeRef; - if (tr != null) - return AssemblyQualifiedName(tr, helper); + if (type is TypeRef tr) + return AssemblyQualifiedNameSB(tr, helper, sb); - var ts = type as TypeSpec; - if (ts != null) - return AssemblyQualifiedName(ts, helper); + if (type is TypeSpec ts) + return AssemblyQualifiedNameSB(ts, helper, sb); - var sig = type as TypeSig; - if (sig != null) - return AssemblyQualifiedName(sig, helper); + if (type is TypeSig sig) + return AssemblyQualifiedNameSB(sig, helper, sb); - var et = type as ExportedType; - if (et != null) - return AssemblyQualifiedName(et, helper); + if (type is ExportedType et) + return AssemblyQualifiedNameSB(et, helper, sb); - return string.Empty; + return sb ?? new StringBuilder(); } /// @@ -103,10 +211,11 @@ public static string AssemblyQualifiedName(IType type, IFullNameCreatorHelper he /// Declaring type full name or null if none /// Name of property /// Property signature + /// Type generic arguments or null if none + /// String builder to use or null /// Property full name - public static string PropertyFullName(string declaringType, UTF8String name, CallingConventionSig propertySig) { - return PropertyFullName(declaringType, name, propertySig, null); - } + public static string PropertyFullName(string declaringType, UTF8String name, CallingConventionSig propertySig, IList typeGenArgs = null, StringBuilder sb = null) => + PropertyFullNameSB(declaringType, name, propertySig, typeGenArgs, sb).ToString(); /// /// Returns the full name of a property @@ -115,28 +224,30 @@ public static string PropertyFullName(string declaringType, UTF8String name, Cal /// Name of property /// Property signature /// Type generic arguments or null if none + /// String builder to use or null /// Property full name - public static string PropertyFullName(string declaringType, UTF8String name, CallingConventionSig propertySig, IList typeGenArgs) { - var fnc = new FullNameCreator(false, null); - if (typeGenArgs != null) { + public static StringBuilder PropertyFullNameSB(string declaringType, UTF8String name, CallingConventionSig propertySig, IList typeGenArgs, StringBuilder sb) { + var fnc = new FullNameFactory(false, null, sb); + if (typeGenArgs is not null) { fnc.genericArguments = new GenericArguments(); fnc.genericArguments.PushTypeArgs(typeGenArgs); } fnc.CreatePropertyFullName(declaringType, name, propertySig); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// - /// Returns the full name of an event + /// Returns the full name of a property /// /// Declaring type full name or null if none - /// Name of event + /// Name of property /// Event type - /// Event full name - public static string EventFullName(string declaringType, UTF8String name, ITypeDefOrRef typeDefOrRef) { - return EventFullName(declaringType, name, typeDefOrRef, null); - } + /// Type generic arguments or null if none + /// String builder to use or null + /// Property full name + public static string EventFullName(string declaringType, UTF8String name, ITypeDefOrRef typeDefOrRef, IList typeGenArgs = null, StringBuilder sb = null) => + EventFullNameSB(declaringType, name, typeDefOrRef, typeGenArgs, sb).ToString(); /// /// Returns the full name of a property @@ -145,27 +256,17 @@ public static string EventFullName(string declaringType, UTF8String name, ITypeD /// Name of property /// Event type /// Type generic arguments or null if none + /// String builder to use or null /// Property full name - public static string EventFullName(string declaringType, UTF8String name, ITypeDefOrRef typeDefOrRef, IList typeGenArgs) { - var fnc = new FullNameCreator(false, null); - if (typeGenArgs != null) { + public static StringBuilder EventFullNameSB(string declaringType, UTF8String name, ITypeDefOrRef typeDefOrRef, IList typeGenArgs, StringBuilder sb) { + var fnc = new FullNameFactory(false, null, sb); + if (typeGenArgs is not null) { fnc.genericArguments = new GenericArguments(); fnc.genericArguments.PushTypeArgs(typeGenArgs); } fnc.CreateEventFullName(declaringType, name, typeDefOrRef); - return fnc.Result; - } - - /// - /// Returns the full name of a field - /// - /// Declaring type full name or null if none - /// Name of field - /// Field signature - /// Field full name - public static string FieldFullName(string declaringType, UTF8String name, FieldSig fieldSig) { - return FieldFullName(declaringType, UTF8String.ToSystemString(name), fieldSig, null); + return fnc.sb ?? new StringBuilder(); } /// @@ -175,21 +276,10 @@ public static string FieldFullName(string declaringType, UTF8String name, FieldS /// Name of field /// Field signature /// Type generic arguments or null if none + /// String builder to use or null /// Field full name - public static string FieldFullName(string declaringType, UTF8String name, FieldSig fieldSig, IList typeGenArgs) { - return FieldFullName(declaringType, UTF8String.ToSystemString(name), fieldSig, typeGenArgs); - } - - /// - /// Returns the full name of a field - /// - /// Declaring type full name or null if none - /// Name of field - /// Field signature - /// Field full name - public static string FieldFullName(string declaringType, string name, FieldSig fieldSig) { - return FieldFullName(declaringType, name, fieldSig, null); - } + public static string FieldFullName(string declaringType, string name, FieldSig fieldSig, IList typeGenArgs = null, StringBuilder sb = null) => + FieldFullNameSB(declaringType, name, fieldSig, typeGenArgs, sb).ToString(); /// /// Returns the full name of a field @@ -198,75 +288,17 @@ public static string FieldFullName(string declaringType, string name, FieldSig f /// Name of field /// Field signature /// Type generic arguments or null if none + /// String builder to use or null /// Field full name - public static string FieldFullName(string declaringType, string name, FieldSig fieldSig, IList typeGenArgs) { - var fnc = new FullNameCreator(false, null); - if (typeGenArgs != null) { + public static StringBuilder FieldFullNameSB(string declaringType, string name, FieldSig fieldSig, IList typeGenArgs, StringBuilder sb) { + var fnc = new FullNameFactory(false, null, sb); + if (typeGenArgs is not null) { fnc.genericArguments = new GenericArguments(); fnc.genericArguments.PushTypeArgs(typeGenArgs); } fnc.CreateFieldFullName(declaringType, name, fieldSig); - return fnc.Result; - } - - /// - /// Returns the full name of a method - /// - /// Declaring type full name or null if none - /// Name of method or null if none - /// Method signature - /// Method full name - public static string MethodFullName(string declaringType, UTF8String name, MethodSig methodSig) { - return MethodFullName(declaringType, UTF8String.ToSystemString(name), methodSig, null, null, null); - } - - /// - /// Returns the full name of a method - /// - /// Declaring type full name or null if none - /// Name of method or null if none - /// Method signature - /// Type generic arguments or null if none - /// Method full name - public static string MethodFullName(string declaringType, UTF8String name, MethodSig methodSig, IList typeGenArgs) { - return MethodFullName(declaringType, UTF8String.ToSystemString(name), methodSig, typeGenArgs, null, null); - } - - /// - /// Returns the full name of a method - /// - /// Declaring type full name or null if none - /// Name of method or null if none - /// Method signature - /// Type generic arguments or null if none - /// Method generic arguments or null if none - /// Method full name - public static string MethodFullName(string declaringType, UTF8String name, MethodSig methodSig, IList typeGenArgs, IList methodGenArgs) { - return MethodFullName(declaringType, UTF8String.ToSystemString(name), methodSig, typeGenArgs, methodGenArgs, null); - } - - /// - /// Returns the full name of a method - /// - /// Declaring type full name or null if none - /// Name of method or null if none - /// Method signature - /// Method full name - public static string MethodFullName(string declaringType, string name, MethodSig methodSig) { - return MethodFullName(declaringType, name, methodSig, null, null, null); - } - - /// - /// Returns the full name of a method - /// - /// Declaring type full name or null if none - /// Name of method or null if none - /// Method signature - /// Type generic arguments or null if none - /// Method full name - public static string MethodFullName(string declaringType, string name, MethodSig methodSig, IList typeGenArgs) { - return MethodFullName(declaringType, name, methodSig, typeGenArgs, null, null); + return fnc.sb ?? new StringBuilder(); } /// @@ -277,10 +309,11 @@ public static string MethodFullName(string declaringType, string name, MethodSig /// Method signature /// Type generic arguments or null if none /// Method generic arguments or null if none + /// Generic parameter owner method or null + /// String builder to use or null /// Method full name - public static string MethodFullName(string declaringType, string name, MethodSig methodSig, IList typeGenArgs, IList methodGenArgs) { - return MethodFullName(declaringType, name, methodSig, typeGenArgs, methodGenArgs, null); - } + public static string MethodFullName(string declaringType, string name, MethodSig methodSig, IList typeGenArgs = null, IList methodGenArgs = null, MethodDef gppMethod = null, StringBuilder sb = null) => + MethodFullNameSB(declaringType, name, methodSig, typeGenArgs, methodGenArgs, gppMethod, sb).ToString(); /// /// Returns the full name of a method @@ -291,46 +324,39 @@ public static string MethodFullName(string declaringType, string name, MethodSig /// Type generic arguments or null if none /// Method generic arguments or null if none /// Generic parameter owner method or null + /// String builder to use or null /// Method full name - public static string MethodFullName(string declaringType, string name, MethodSig methodSig, IList typeGenArgs, IList methodGenArgs, MethodDef gppMethod) { - var fnc = new FullNameCreator(false, null); - if (typeGenArgs != null || methodGenArgs != null) + public static StringBuilder MethodFullNameSB(string declaringType, string name, MethodSig methodSig, IList typeGenArgs, IList methodGenArgs, MethodDef gppMethod, StringBuilder sb) { + var fnc = new FullNameFactory(false, null, sb); + if (typeGenArgs is not null || methodGenArgs is not null) fnc.genericArguments = new GenericArguments(); - if (typeGenArgs != null) + if (typeGenArgs is not null) fnc.genericArguments.PushTypeArgs(typeGenArgs); - if (methodGenArgs != null) + if (methodGenArgs is not null) fnc.genericArguments.PushMethodArgs(methodGenArgs); fnc.CreateMethodFullName(declaringType, name, methodSig, gppMethod); - return fnc.Result; - } - - /// - /// Returns the full name of a method sig - /// - /// Method sig - /// Method sig full name - public static string MethodSigFullName(MethodSig methodSig) { - return MethodBaseSigFullName(methodSig); + return fnc.sb ?? new StringBuilder(); } /// /// Returns the full name of a property sig /// - /// Property sig + /// Property sig + /// String builder to use or null /// Property sig full name - public static string PropertySigFullName(PropertySig propertySig) { - return MethodBaseSigFullName(propertySig); - } + public static string MethodBaseSigFullName(MethodBaseSig sig, StringBuilder sb = null) => + MethodBaseSigFullNameSB(sig, sb).ToString(); /// /// Returns the full name of a property sig /// /// Property sig + /// String builder to use or null /// Property sig full name - public static string MethodBaseSigFullName(MethodBaseSig sig) { - var fnc = new FullNameCreator(false, null); + public static StringBuilder MethodBaseSigFullNameSB(MethodBaseSig sig, StringBuilder sb) { + var fnc = new FullNameFactory(false, null, sb); fnc.CreateMethodFullName(null, null, sig, null); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// @@ -340,11 +366,24 @@ public static string MethodBaseSigFullName(MethodBaseSig sig) { /// Name or null /// Method sig /// Owner method or null + /// String builder to use or null + /// Sig full name + public static string MethodBaseSigFullName(string declType, string name, MethodBaseSig sig, MethodDef gppMethod, StringBuilder sb = null) => + MethodBaseSigFullNameSB(declType, name, sig, gppMethod, sb).ToString(); + + /// + /// Returns the full name of a sig + /// + /// Declaring type or null + /// Name or null + /// Method sig + /// Owner method or null + /// String builder to use or null /// Sig full name - public static string MethodBaseSigFullName(string declType, string name, MethodBaseSig sig, MethodDef gppMethod) { - var fnc = new FullNameCreator(false, null); + public static StringBuilder MethodBaseSigFullNameSB(string declType, string name, MethodBaseSig sig, MethodDef gppMethod, StringBuilder sb) { + var fnc = new FullNameFactory(false, null, sb); fnc.CreateMethodFullName(declType, name, sig, gppMethod); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// @@ -352,11 +391,22 @@ public static string MethodBaseSigFullName(string declType, string name, MethodB /// /// The TypeRef /// Set if output should be compatible with reflection + /// String builder to use or null /// The namespace - public static string Namespace(TypeRef typeRef, bool isReflection) { - var fnc = new FullNameCreator(isReflection, null); - fnc.CreateNamespace(typeRef); - return fnc.Result; + public static string Namespace(TypeRef typeRef, bool isReflection, StringBuilder sb = null) => + NamespaceSB(typeRef, isReflection, sb).ToString(); + + /// + /// Returns the namespace of a + /// + /// The TypeRef + /// Set if output should be compatible with reflection + /// String builder to use or null + /// The namespace + public static StringBuilder NamespaceSB(TypeRef typeRef, bool isReflection, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, null, sb); + fnc.CreateNamespace(typeRef, true); + return fnc.sb ?? new StringBuilder(); } /// @@ -364,11 +414,22 @@ public static string Namespace(TypeRef typeRef, bool isReflection) { /// /// The TypeRef /// Set if output should be compatible with reflection + /// String builder to use or null + /// The name + public static string Name(TypeRef typeRef, bool isReflection, StringBuilder sb = null) => + NameSB(typeRef, isReflection, sb).ToString(); + + /// + /// Returns the name of a + /// + /// The TypeRef + /// Set if output should be compatible with reflection + /// String builder to use or null /// The name - public static string Name(TypeRef typeRef, bool isReflection) { - var fnc = new FullNameCreator(isReflection, null); + public static StringBuilder NameSB(TypeRef typeRef, bool isReflection, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, null, sb); fnc.CreateName(typeRef); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// @@ -376,10 +437,11 @@ public static string Name(TypeRef typeRef, bool isReflection) { /// /// The TypeRef /// Set if output should be compatible with reflection + /// Helps print the name + /// String builder to use or null /// The full name - public static string FullName(TypeRef typeRef, bool isReflection) { - return FullName(typeRef, isReflection, null); - } + public static string FullName(TypeRef typeRef, bool isReflection, IFullNameFactoryHelper helper = null, StringBuilder sb = null) => + FullNameSB(typeRef, isReflection, helper, sb).ToString(); /// /// Returns the full name of a @@ -387,32 +449,35 @@ public static string FullName(TypeRef typeRef, bool isReflection) { /// The TypeRef /// Set if output should be compatible with reflection /// Helps print the name + /// String builder to use or null /// The full name - public static string FullName(TypeRef typeRef, bool isReflection, IFullNameCreatorHelper helper) { - var fnc = new FullNameCreator(isReflection, helper); + public static StringBuilder FullNameSB(TypeRef typeRef, bool isReflection, IFullNameFactoryHelper helper, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, helper, sb); fnc.CreateFullName(typeRef); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// /// Returns the assembly qualified full name of a /// /// The TypeRef + /// Helps print the name + /// String builder to use or null /// The assembly qualified full name - public static string AssemblyQualifiedName(TypeRef typeRef) { - return AssemblyQualifiedName(typeRef, null); - } + public static string AssemblyQualifiedName(TypeRef typeRef, IFullNameFactoryHelper helper = null, StringBuilder sb = null) => + AssemblyQualifiedNameSB(typeRef, helper, sb).ToString(); /// /// Returns the assembly qualified full name of a /// /// The TypeRef /// Helps print the name + /// String builder to use or null /// The assembly qualified full name - public static string AssemblyQualifiedName(TypeRef typeRef, IFullNameCreatorHelper helper) { - var fnc = new FullNameCreator(true, helper); + public static StringBuilder AssemblyQualifiedNameSB(TypeRef typeRef, IFullNameFactoryHelper helper, StringBuilder sb) { + var fnc = new FullNameFactory(true, helper, sb); fnc.CreateAssemblyQualifiedName(typeRef); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// @@ -420,38 +485,46 @@ public static string AssemblyQualifiedName(TypeRef typeRef, IFullNameCreatorHelp /// /// The TypeRef /// A or null if none found - public static IAssembly DefinitionAssembly(TypeRef typeRef) { - return new FullNameCreator().GetDefinitionAssembly(typeRef); - } + public static IAssembly DefinitionAssembly(TypeRef typeRef) => + new FullNameFactory().GetDefinitionAssembly(typeRef); /// /// Gets the scope /// /// The TypeRef /// The or null if none found - public static IScope Scope(TypeRef typeRef) { - return new FullNameCreator().GetScope(typeRef); - } + public static IScope Scope(TypeRef typeRef) => + new FullNameFactory().GetScope(typeRef); /// /// Returns the owner module. The type was created from metadata in this module. /// /// The TypeRef /// A or null if none found - public static ModuleDef OwnerModule(TypeRef typeRef) { - return new FullNameCreator().GetOwnerModule(typeRef); - } + public static ModuleDef OwnerModule(TypeRef typeRef) => + new FullNameFactory().GetOwnerModule(typeRef); /// /// Returns the namespace of a /// /// The TypeDef /// Set if output should be compatible with reflection + /// String builder to use or null /// The namespace - public static string Namespace(TypeDef typeDef, bool isReflection) { - var fnc = new FullNameCreator(isReflection, null); - fnc.CreateNamespace(typeDef); - return fnc.Result; + public static string Namespace(TypeDef typeDef, bool isReflection, StringBuilder sb = null) => + NamespaceSB(typeDef, isReflection, sb).ToString(); + + /// + /// Returns the namespace of a + /// + /// The TypeDef + /// Set if output should be compatible with reflection + /// String builder to use or null + /// The namespace + public static StringBuilder NamespaceSB(TypeDef typeDef, bool isReflection, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, null, sb); + fnc.CreateNamespace(typeDef, true); + return fnc.sb ?? new StringBuilder(); } /// @@ -459,11 +532,22 @@ public static string Namespace(TypeDef typeDef, bool isReflection) { /// /// The TypeDef /// Set if output should be compatible with reflection + /// String builder to use or null /// The name - public static string Name(TypeDef typeDef, bool isReflection) { - var fnc = new FullNameCreator(isReflection, null); + public static string Name(TypeDef typeDef, bool isReflection, StringBuilder sb = null) => + NameSB(typeDef, isReflection, sb).ToString(); + + /// + /// Returns the name of a + /// + /// The TypeDef + /// Set if output should be compatible with reflection + /// String builder to use or null + /// The name + public static StringBuilder NameSB(TypeDef typeDef, bool isReflection, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, null, sb); fnc.CreateName(typeDef); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// @@ -471,10 +555,11 @@ public static string Name(TypeDef typeDef, bool isReflection) { /// /// The TypeDef /// Set if output should be compatible with reflection + /// Helps print the name + /// String builder to use or null /// The full name - public static string FullName(TypeDef typeDef, bool isReflection) { - return FullName(typeDef, isReflection, null); - } + public static string FullName(TypeDef typeDef, bool isReflection, IFullNameFactoryHelper helper = null, StringBuilder sb = null) => + FullNameSB(typeDef, isReflection, helper, sb).ToString(); /// /// Returns the full name of a @@ -482,32 +567,35 @@ public static string FullName(TypeDef typeDef, bool isReflection) { /// The TypeDef /// Set if output should be compatible with reflection /// Helps print the name + /// String builder to use or null /// The full name - public static string FullName(TypeDef typeDef, bool isReflection, IFullNameCreatorHelper helper) { - var fnc = new FullNameCreator(isReflection, helper); + public static StringBuilder FullNameSB(TypeDef typeDef, bool isReflection, IFullNameFactoryHelper helper, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, helper, sb); fnc.CreateFullName(typeDef); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// /// Returns the assembly qualified full name of a /// /// The TypeDef + /// Helps print the name + /// String builder to use or null /// The assembly qualified full name - public static string AssemblyQualifiedName(TypeDef typeDef) { - return AssemblyQualifiedName(typeDef, null); - } + public static string AssemblyQualifiedName(TypeDef typeDef, IFullNameFactoryHelper helper = null, StringBuilder sb = null) => + AssemblyQualifiedNameSB(typeDef, helper, sb).ToString(); /// /// Returns the assembly qualified full name of a /// /// The TypeDef /// Helps print the name + /// String builder to use or null /// The assembly qualified full name - public static string AssemblyQualifiedName(TypeDef typeDef, IFullNameCreatorHelper helper) { - var fnc = new FullNameCreator(true, helper); + public static StringBuilder AssemblyQualifiedNameSB(TypeDef typeDef, IFullNameFactoryHelper helper, StringBuilder sb) { + var fnc = new FullNameFactory(true, helper, sb); fnc.CreateAssemblyQualifiedName(typeDef); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// @@ -515,29 +603,38 @@ public static string AssemblyQualifiedName(TypeDef typeDef, IFullNameCreatorHelp /// /// The TypeDef /// A or null if none found - public static IAssembly DefinitionAssembly(TypeDef typeDef) { - return new FullNameCreator().GetDefinitionAssembly(typeDef); - } + public static IAssembly DefinitionAssembly(TypeDef typeDef) => + new FullNameFactory().GetDefinitionAssembly(typeDef); /// /// Returns the owner module. The type was created from metadata in this module. /// /// The TypeDef /// A or null if none found - public static ModuleDef OwnerModule(TypeDef typeDef) { - return new FullNameCreator().GetOwnerModule(typeDef); - } + public static ModuleDef OwnerModule(TypeDef typeDef) => + new FullNameFactory().GetOwnerModule(typeDef); + + /// + /// Returns the namespace of a + /// + /// The TypeSpec + /// Set if output should be compatible with reflection + /// String builder to use or null + /// The namespace + public static string Namespace(TypeSpec typeSpec, bool isReflection, StringBuilder sb = null) => + NamespaceSB(typeSpec, isReflection, sb).ToString(); /// /// Returns the namespace of a /// /// The TypeSpec /// Set if output should be compatible with reflection + /// String builder to use or null /// The namespace - public static string Namespace(TypeSpec typeSpec, bool isReflection) { - var fnc = new FullNameCreator(isReflection, null); - fnc.CreateNamespace(typeSpec); - return fnc.Result; + public static StringBuilder NamespaceSB(TypeSpec typeSpec, bool isReflection, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, null, sb); + fnc.CreateNamespace(typeSpec, true); + return fnc.sb ?? new StringBuilder(); } /// @@ -545,11 +642,22 @@ public static string Namespace(TypeSpec typeSpec, bool isReflection) { /// /// The TypeSpec /// Set if output should be compatible with reflection + /// String builder to use or null + /// The name + public static string Name(TypeSpec typeSpec, bool isReflection, StringBuilder sb = null) => + NameSB(typeSpec, isReflection, sb).ToString(); + + /// + /// Returns the name of a + /// + /// The TypeSpec + /// Set if output should be compatible with reflection + /// String builder to use or null /// The name - public static string Name(TypeSpec typeSpec, bool isReflection) { - var fnc = new FullNameCreator(isReflection, null); + public static StringBuilder NameSB(TypeSpec typeSpec, bool isReflection, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, null, sb); fnc.CreateName(typeSpec); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// @@ -557,10 +665,11 @@ public static string Name(TypeSpec typeSpec, bool isReflection) { /// /// The TypeSpec /// Set if output should be compatible with reflection + /// Helps print the name + /// String builder to use or null /// The full name - public static string FullName(TypeSpec typeSpec, bool isReflection) { - return FullName(typeSpec, isReflection, null); - } + public static string FullName(TypeSpec typeSpec, bool isReflection, IFullNameFactoryHelper helper = null, StringBuilder sb = null) => + FullNameSB(typeSpec, isReflection, helper, sb).ToString(); /// /// Returns the full name of a @@ -568,32 +677,35 @@ public static string FullName(TypeSpec typeSpec, bool isReflection) { /// The TypeSpec /// Set if output should be compatible with reflection /// Helps print the name + /// String builder to use or null /// The full name - public static string FullName(TypeSpec typeSpec, bool isReflection, IFullNameCreatorHelper helper) { - var fnc = new FullNameCreator(isReflection, helper); + public static StringBuilder FullNameSB(TypeSpec typeSpec, bool isReflection, IFullNameFactoryHelper helper, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, helper, sb); fnc.CreateFullName(typeSpec); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// /// Returns the assembly qualified full name of a /// /// The TypeSpec + /// Helps print the name + /// String builder to use or null /// The assembly qualified full name - public static string AssemblyQualifiedName(TypeSpec typeSpec) { - return AssemblyQualifiedName(typeSpec, null); - } + public static string AssemblyQualifiedName(TypeSpec typeSpec, IFullNameFactoryHelper helper = null, StringBuilder sb = null) => + AssemblyQualifiedNameSB(typeSpec, helper, sb).ToString(); /// /// Returns the assembly qualified full name of a /// /// The TypeSpec /// Helps print the name + /// String builder to use or null /// The assembly qualified full name - public static string AssemblyQualifiedName(TypeSpec typeSpec, IFullNameCreatorHelper helper) { - var fnc = new FullNameCreator(true, helper); + public static StringBuilder AssemblyQualifiedNameSB(TypeSpec typeSpec, IFullNameFactoryHelper helper, StringBuilder sb) { + var fnc = new FullNameFactory(true, helper, sb); fnc.CreateAssemblyQualifiedName(typeSpec); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// @@ -601,80 +713,77 @@ public static string AssemblyQualifiedName(TypeSpec typeSpec, IFullNameCreatorHe /// /// The TypeSpec /// A or null if none found - public static IAssembly DefinitionAssembly(TypeSpec typeSpec) { - return new FullNameCreator().GetDefinitionAssembly(typeSpec); - } + public static IAssembly DefinitionAssembly(TypeSpec typeSpec) => + new FullNameFactory().GetDefinitionAssembly(typeSpec); /// /// Gets the scope type /// /// The TypeSpec /// The scope type or null if none found - public static ITypeDefOrRef ScopeType(TypeSpec typeSpec) { - return new FullNameCreator().GetScopeType(typeSpec); - } + public static ITypeDefOrRef ScopeType(TypeSpec typeSpec) => + new FullNameFactory().GetScopeType(typeSpec); /// /// Gets the scope /// /// The TypeSpec /// The or null if none found - public static IScope Scope(TypeSpec typeSpec) { - return new FullNameCreator().GetScope(typeSpec); - } + public static IScope Scope(TypeSpec typeSpec) => + new FullNameFactory().GetScope(typeSpec); /// /// Returns the owner module. The type was created from metadata in this module. /// /// The TypeSpec /// A or null if none found - public static ModuleDef OwnerModule(TypeSpec typeSpec) { - return new FullNameCreator().GetOwnerModule(typeSpec); - } + public static ModuleDef OwnerModule(TypeSpec typeSpec) => + new FullNameFactory().GetOwnerModule(typeSpec); /// /// Returns the namespace of a /// /// The type sig /// Set if output should be compatible with reflection + /// String builder to use or null /// The namespace - public static string Namespace(TypeSig typeSig, bool isReflection) { - var fnc = new FullNameCreator(isReflection, null); - fnc.CreateNamespace(typeSig); - return fnc.Result; - } + public static string Namespace(TypeSig typeSig, bool isReflection, StringBuilder sb = null) => + NamespaceSB(typeSig, isReflection, sb).ToString(); /// - /// Returns the name of a + /// Returns the namespace of a /// /// The type sig /// Set if output should be compatible with reflection - /// The name - public static string Name(TypeSig typeSig, bool isReflection) { - var fnc = new FullNameCreator(isReflection, null); - fnc.CreateName(typeSig); - return fnc.Result; + /// String builder to use or null + /// The namespace + public static StringBuilder NamespaceSB(TypeSig typeSig, bool isReflection, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, null, sb); + fnc.CreateNamespace(typeSig, true); + return fnc.sb ?? new StringBuilder(); } /// - /// Returns the full name of a + /// Returns the name of a /// /// The type sig /// Set if output should be compatible with reflection - /// The full name - public static string FullName(TypeSig typeSig, bool isReflection) { - return FullName(typeSig, isReflection, null, null, null); - } + /// String builder to use or null + /// The name + public static string Name(TypeSig typeSig, bool isReflection, StringBuilder sb = null) => + NameSB(typeSig, isReflection, sb).ToString(); /// - /// Returns the full name of a + /// Returns the name of a /// /// The type sig /// Set if output should be compatible with reflection - /// Helps print the name - /// The full name - public static string FullName(TypeSig typeSig, bool isReflection, IFullNameCreatorHelper helper) { - return FullName(typeSig, isReflection, helper, null, null); + /// String builder to use or null + /// The name + public static StringBuilder NameSB(TypeSig typeSig, bool isReflection, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, null, sb); + fnc.CreateName(typeSig); + return fnc.sb ?? new StringBuilder(); } /// @@ -682,12 +791,13 @@ public static string FullName(TypeSig typeSig, bool isReflection, IFullNameCreat /// /// The type sig /// Set if output should be compatible with reflection + /// Helps print the name /// Type generic args or null if none /// Method generic args or null if none + /// String builder to use or null /// The full name - public static string FullName(TypeSig typeSig, bool isReflection, IList typeGenArgs, IList methodGenArgs) { - return FullName(typeSig, isReflection, null, typeGenArgs, methodGenArgs); - } + public static string FullName(TypeSig typeSig, bool isReflection, IFullNameFactoryHelper helper = null, IList typeGenArgs = null, IList methodGenArgs = null, StringBuilder sb = null) => + FullNameSB(typeSig, isReflection, helper, typeGenArgs, methodGenArgs, sb).ToString(); /// /// Returns the full name of a @@ -697,38 +807,41 @@ public static string FullName(TypeSig typeSig, bool isReflection, IList /// Helps print the name /// Type generic args or null if none /// Method generic args or null if none + /// String builder to use or null /// The full name - public static string FullName(TypeSig typeSig, bool isReflection, IFullNameCreatorHelper helper, IList typeGenArgs, IList methodGenArgs) { - var fnc = new FullNameCreator(isReflection, helper); - if (typeGenArgs != null || methodGenArgs != null) + public static StringBuilder FullNameSB(TypeSig typeSig, bool isReflection, IFullNameFactoryHelper helper, IList typeGenArgs, IList methodGenArgs, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, helper, sb); + if (typeGenArgs is not null || methodGenArgs is not null) fnc.genericArguments = new GenericArguments(); - if (typeGenArgs != null) + if (typeGenArgs is not null) fnc.genericArguments.PushTypeArgs(typeGenArgs); - if (methodGenArgs != null) + if (methodGenArgs is not null) fnc.genericArguments.PushMethodArgs(methodGenArgs); fnc.CreateFullName(typeSig); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// /// Returns the assembly qualified full name of a /// /// The TypeSig + /// Helps print the name + /// String builder to use or null /// The assembly qualified full name - public static string AssemblyQualifiedName(TypeSig typeSig) { - return AssemblyQualifiedName(typeSig, null); - } + public static string AssemblyQualifiedName(TypeSig typeSig, IFullNameFactoryHelper helper = null, StringBuilder sb = null) => + AssemblyQualifiedNameSB(typeSig, helper, sb).ToString(); /// /// Returns the assembly qualified full name of a /// /// The TypeSig /// Helps print the name + /// String builder to use or null /// The assembly qualified full name - public static string AssemblyQualifiedName(TypeSig typeSig, IFullNameCreatorHelper helper) { - var fnc = new FullNameCreator(true, helper); + public static StringBuilder AssemblyQualifiedNameSB(TypeSig typeSig, IFullNameFactoryHelper helper, StringBuilder sb) { + var fnc = new FullNameFactory(true, helper, sb); fnc.CreateAssemblyQualifiedName(typeSig); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// @@ -736,47 +849,54 @@ public static string AssemblyQualifiedName(TypeSig typeSig, IFullNameCreatorHelp /// /// The TypeSig /// A or null if none found - public static IAssembly DefinitionAssembly(TypeSig typeSig) { - return new FullNameCreator().GetDefinitionAssembly(typeSig); - } + public static IAssembly DefinitionAssembly(TypeSig typeSig) => + new FullNameFactory().GetDefinitionAssembly(typeSig); /// /// Gets the scope /// /// The TypeSig /// The or null if none found - public static IScope Scope(TypeSig typeSig) { - return new FullNameCreator().GetScope(typeSig); - } + public static IScope Scope(TypeSig typeSig) => + new FullNameFactory().GetScope(typeSig); /// /// Gets the scope type /// /// The TypeSig /// The scope type or null if none found - public static ITypeDefOrRef ScopeType(TypeSig typeSig) { - return new FullNameCreator().GetScopeType(typeSig); - } + public static ITypeDefOrRef ScopeType(TypeSig typeSig) => + new FullNameFactory().GetScopeType(typeSig); /// /// Returns the owner module. The type was created from metadata in this module. /// /// The TypeSig /// A or null if none found - public static ModuleDef OwnerModule(TypeSig typeSig) { - return new FullNameCreator().GetOwnerModule(typeSig); - } + public static ModuleDef OwnerModule(TypeSig typeSig) => + new FullNameFactory().GetOwnerModule(typeSig); /// /// Returns the namespace of a /// /// The ExportedType /// Set if output should be compatible with reflection + /// String builder to use or null /// The namespace - public static string Namespace(ExportedType exportedType, bool isReflection) { - var fnc = new FullNameCreator(isReflection, null); - fnc.CreateNamespace(exportedType); - return fnc.Result; + public static string Namespace(ExportedType exportedType, bool isReflection, StringBuilder sb = null) => + NamespaceSB(exportedType, isReflection, sb).ToString(); + + /// + /// Returns the namespace of a + /// + /// The ExportedType + /// Set if output should be compatible with reflection + /// String builder to use or null + /// The namespace + public static StringBuilder NamespaceSB(ExportedType exportedType, bool isReflection, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, null, sb); + fnc.CreateNamespace(exportedType, true); + return fnc.sb ?? new StringBuilder(); } /// @@ -784,11 +904,22 @@ public static string Namespace(ExportedType exportedType, bool isReflection) { /// /// The ExportedType /// Set if output should be compatible with reflection + /// String builder to use or null /// The name - public static string Name(ExportedType exportedType, bool isReflection) { - var fnc = new FullNameCreator(isReflection, null); + public static string Name(ExportedType exportedType, bool isReflection, StringBuilder sb = null) => + NameSB(exportedType, isReflection, sb).ToString(); + + /// + /// Returns the name of a + /// + /// The ExportedType + /// Set if output should be compatible with reflection + /// String builder to use or null + /// The name + public static StringBuilder NameSB(ExportedType exportedType, bool isReflection, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, null, sb); fnc.CreateName(exportedType); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// @@ -796,10 +927,11 @@ public static string Name(ExportedType exportedType, bool isReflection) { /// /// The ExportedType /// Set if output should be compatible with reflection + /// Helps print the name + /// String builder to use or null /// The full name - public static string FullName(ExportedType exportedType, bool isReflection) { - return FullName(exportedType, isReflection, null); - } + public static string FullName(ExportedType exportedType, bool isReflection, IFullNameFactoryHelper helper = null, StringBuilder sb = null) => + FullNameSB(exportedType, isReflection, helper, sb).ToString(); /// /// Returns the full name of a @@ -807,32 +939,35 @@ public static string FullName(ExportedType exportedType, bool isReflection) { /// The ExportedType /// Set if output should be compatible with reflection /// Helps print the name + /// String builder to use or null /// The full name - public static string FullName(ExportedType exportedType, bool isReflection, IFullNameCreatorHelper helper) { - var fnc = new FullNameCreator(isReflection, helper); + public static StringBuilder FullNameSB(ExportedType exportedType, bool isReflection, IFullNameFactoryHelper helper, StringBuilder sb) { + var fnc = new FullNameFactory(isReflection, helper, sb); fnc.CreateFullName(exportedType); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// /// Returns the assembly qualified full name of a /// /// The ExportedType + /// Helps print the name + /// String builder to use or null /// The assembly qualified full name - public static string AssemblyQualifiedName(ExportedType exportedType) { - return AssemblyQualifiedName(exportedType, null); - } + public static string AssemblyQualifiedName(ExportedType exportedType, IFullNameFactoryHelper helper = null, StringBuilder sb = null) => + AssemblyQualifiedNameSB(exportedType, helper, sb).ToString(); /// /// Returns the assembly qualified full name of a /// /// The ExportedType /// Helps print the name + /// String builder to use or null /// The assembly qualified full name - public static string AssemblyQualifiedName(ExportedType exportedType, IFullNameCreatorHelper helper) { - var fnc = new FullNameCreator(true, helper); + public static StringBuilder AssemblyQualifiedNameSB(ExportedType exportedType, IFullNameFactoryHelper helper, StringBuilder sb) { + var fnc = new FullNameFactory(true, helper, sb); fnc.CreateAssemblyQualifiedName(exportedType); - return fnc.Result; + return fnc.sb ?? new StringBuilder(); } /// @@ -840,51 +975,68 @@ public static string AssemblyQualifiedName(ExportedType exportedType, IFullNameC /// /// The ExportedType /// A or null if none found - public static IAssembly DefinitionAssembly(ExportedType exportedType) { - return new FullNameCreator().GetDefinitionAssembly(exportedType); - } + public static IAssembly DefinitionAssembly(ExportedType exportedType) => + new FullNameFactory().GetDefinitionAssembly(exportedType); /// /// Gets the scope type /// /// The ExportedType /// The scope type or null if none found - public static ITypeDefOrRef ScopeType(ExportedType exportedType) { - return new FullNameCreator().GetScopeType(exportedType); - } + public static ITypeDefOrRef ScopeType(ExportedType exportedType) => + new FullNameFactory().GetScopeType(exportedType); /// /// Gets the scope /// /// The ExportedType /// The or null if none found - public static IScope Scope(ExportedType exportedType) { - return new FullNameCreator().GetScope(exportedType); - } + public static IScope Scope(ExportedType exportedType) => + new FullNameFactory().GetScope(exportedType); /// /// Returns the owner module. The type was created from metadata in this module. /// /// The ExportedType /// A or null if none found - public static ModuleDef OwnerModule(ExportedType exportedType) { - return new FullNameCreator().GetOwnerModule(exportedType); - } + public static ModuleDef OwnerModule(ExportedType exportedType) => + new FullNameFactory().GetOwnerModule(exportedType); - string Result { - get { return sb == null ? null : sb.ToString(); } + /// + /// Returns the full assembly name of a + /// + /// The IAssembly + /// true to use public key token in name even if a public key is available + /// String builder to use or null + /// The full assembly name + public static string AssemblyFullName(IAssembly assembly, bool withToken, StringBuilder sb = null) => + AssemblyFullNameSB(assembly, withToken, sb).ToString(); + + /// + /// Returns the full assembly name of a + /// + /// The IAssembly + /// true to use public key token in name even if a public key is available + /// String builder to use or null + /// The full assembly name + public static StringBuilder AssemblyFullNameSB(IAssembly assembly, bool withToken, StringBuilder sb = null) { + var fnc = new FullNameFactory(false, null, sb); + fnc.CreateAssemblyFullName(assembly, withToken); + return fnc.sb ?? new StringBuilder(); } - FullNameCreator(bool isReflection, IFullNameCreatorHelper helper) { - this.sb = new StringBuilder(); + string Result => sb?.ToString(); + + FullNameFactory(bool isReflection, IFullNameFactoryHelper helper, StringBuilder sb) { + this.sb = sb ?? new StringBuilder(); this.isReflection = isReflection; this.helper = helper; - this.genericArguments = null; - this.recursionCounter = new RecursionCounter(); + genericArguments = null; + recursionCounter = new RecursionCounter(); } bool MustUseAssemblyName(IType type) { - if (helper == null) + if (helper is null) return true; return helper.MustUseAssemblyName(GetDefinitionType(type)); } @@ -893,17 +1045,14 @@ IType GetDefinitionType(IType type) { if (!recursionCounter.Increment()) return type; - TypeSpec ts = type as TypeSpec; - if (ts != null) + if (type is TypeSpec ts) type = ts.TypeSig; - TypeSig sig = type as TypeSig; - if (sig != null) { - TypeDefOrRefSig tdr; + if (type is TypeSig sig) { GenericInstSig gis; - if ((tdr = sig as TypeDefOrRefSig) != null) + if (sig is TypeDefOrRefSig tdr) type = GetDefinitionType(tdr.TypeDefOrRef); - else if ((gis = sig as GenericInstSig) != null) + else if ((gis = sig as GenericInstSig) is not null) type = GetDefinitionType(gis.GenericType); else type = GetDefinitionType(sig.Next); @@ -924,13 +1073,13 @@ void CreateFullName(ITypeDefOrRef typeDefOrRef) { sb.Append(NULLVALUE); } - void CreateNamespace(ITypeDefOrRef typeDefOrRef) { + void CreateNamespace(ITypeDefOrRef typeDefOrRef, bool onlyNamespace) { if (typeDefOrRef is TypeRef) - CreateNamespace((TypeRef)typeDefOrRef); + CreateNamespace((TypeRef)typeDefOrRef, onlyNamespace); else if (typeDefOrRef is TypeDef) - CreateNamespace((TypeDef)typeDefOrRef); + CreateNamespace((TypeDef)typeDefOrRef, onlyNamespace); else if (typeDefOrRef is TypeSpec) - CreateNamespace((TypeSpec)typeDefOrRef); + CreateNamespace((TypeSpec)typeDefOrRef, onlyNamespace); else sb.Append(NULLVALUE); } @@ -958,7 +1107,7 @@ void CreateAssemblyQualifiedName(ITypeDefOrRef typeDefOrRef) { } void CreateAssemblyQualifiedName(TypeRef typeRef) { - if (typeRef == null) { + if (typeRef is null) { sb.Append(NULLVALUE); return; } @@ -968,14 +1117,16 @@ void CreateAssemblyQualifiedName(TypeRef typeRef) { } CreateFullName(typeRef); - if (MustUseAssemblyName(typeRef)) - AddAssemblyName(GetDefinitionAssembly(typeRef)); + if (MustUseAssemblyName(typeRef)) { + sb.Append(", "); + CreateAssemblyFullName(GetDefinitionAssembly(typeRef), true); + } recursionCounter.Decrement(); } void CreateFullName(TypeRef typeRef) { - if (typeRef == null) { + if (typeRef is null) { sb.Append(NULLVALUE); return; } @@ -984,29 +1135,28 @@ void CreateFullName(TypeRef typeRef) { return; } - var declaringTypeRef = typeRef.ResolutionScope as TypeRef; - if (declaringTypeRef != null) { + if (typeRef.ResolutionScope is TypeRef declaringTypeRef) { CreateFullName(declaringTypeRef); AddNestedTypeSeparator(); } - if (AddNamespace(typeRef.Namespace)) + if (AddNamespace(typeRef.Namespace, false)) sb.Append('.'); AddName(typeRef.Name); recursionCounter.Decrement(); } - void CreateNamespace(TypeRef typeRef) { - if (typeRef == null) { + void CreateNamespace(TypeRef typeRef, bool onlyNamespace) { + if (typeRef is null) { sb.Append(NULLVALUE); return; } - AddNamespace(typeRef.Namespace); + AddNamespace(typeRef.Namespace, onlyNamespace); } void CreateName(TypeRef typeRef) { - if (typeRef == null) { + if (typeRef is null) { sb.Append(NULLVALUE); return; } @@ -1014,7 +1164,7 @@ void CreateName(TypeRef typeRef) { } void CreateAssemblyQualifiedName(TypeDef typeDef) { - if (typeDef == null) { + if (typeDef is null) { sb.Append(NULLVALUE); return; } @@ -1024,14 +1174,16 @@ void CreateAssemblyQualifiedName(TypeDef typeDef) { } CreateFullName(typeDef); - if (MustUseAssemblyName(typeDef)) - AddAssemblyName(GetDefinitionAssembly(typeDef)); + if (MustUseAssemblyName(typeDef)) { + sb.Append(", "); + CreateAssemblyFullName(GetDefinitionAssembly(typeDef), true); + } recursionCounter.Decrement(); } void CreateFullName(TypeDef typeDef) { - if (typeDef == null) { + if (typeDef is null) { sb.Append(NULLVALUE); return; } @@ -1041,28 +1193,28 @@ void CreateFullName(TypeDef typeDef) { } var declaringTypeDef = typeDef.DeclaringType; - if (declaringTypeDef != null) { + if (declaringTypeDef is not null) { CreateFullName(declaringTypeDef); AddNestedTypeSeparator(); } - if (AddNamespace(typeDef.Namespace)) + if (AddNamespace(typeDef.Namespace, false)) sb.Append('.'); AddName(typeDef.Name); recursionCounter.Decrement(); } - void CreateNamespace(TypeDef typeDef) { - if (typeDef == null) { + void CreateNamespace(TypeDef typeDef, bool onlyNamespace) { + if (typeDef is null) { sb.Append(NULLVALUE); return; } - AddNamespace(typeDef.Namespace); + AddNamespace(typeDef.Namespace, onlyNamespace); } void CreateName(TypeDef typeDef) { - if (typeDef == null) { + if (typeDef is null) { sb.Append(NULLVALUE); return; } @@ -1070,7 +1222,7 @@ void CreateName(TypeDef typeDef) { } void CreateAssemblyQualifiedName(TypeSpec typeSpec) { - if (typeSpec == null) { + if (typeSpec is null) { sb.Append(NULLVALUE); return; } @@ -1078,23 +1230,23 @@ void CreateAssemblyQualifiedName(TypeSpec typeSpec) { } void CreateFullName(TypeSpec typeSpec) { - if (typeSpec == null) { + if (typeSpec is null) { sb.Append(NULLVALUE); return; } CreateFullName(typeSpec.TypeSig); } - void CreateNamespace(TypeSpec typeSpec) { - if (typeSpec == null) { + void CreateNamespace(TypeSpec typeSpec, bool onlyNamespace) { + if (typeSpec is null) { sb.Append(NULLVALUE); return; } - CreateNamespace(typeSpec.TypeSig); + CreateNamespace(typeSpec.TypeSig, onlyNamespace); } void CreateName(TypeSpec typeSpec) { - if (typeSpec == null) { + if (typeSpec is null) { sb.Append(NULLVALUE); return; } @@ -1103,7 +1255,7 @@ void CreateName(TypeSpec typeSpec) { void CreateAssemblyQualifiedName(TypeSig typeSig) { - if (typeSig == null) { + if (typeSig is null) { sb.Append(NULLVALUE); return; } @@ -1113,26 +1265,20 @@ void CreateAssemblyQualifiedName(TypeSig typeSig) { } CreateFullName(typeSig); - if (MustUseAssemblyName(typeSig)) - AddAssemblyName(GetDefinitionAssembly(typeSig)); + if (MustUseAssemblyName(typeSig)) { + sb.Append(", "); + CreateAssemblyFullName(GetDefinitionAssembly(typeSig), true); + } recursionCounter.Decrement(); } - void CreateFullName(TypeSig typeSig) { - CreateTypeSigName(typeSig, TYPESIG_NAMESPACE | TYPESIG_NAME); - } - - void CreateNamespace(TypeSig typeSig) { - CreateTypeSigName(typeSig, TYPESIG_NAMESPACE); - } - - void CreateName(TypeSig typeSig) { - CreateTypeSigName(typeSig, TYPESIG_NAME); - } + void CreateFullName(TypeSig typeSig) => CreateTypeSigName(typeSig, TYPESIG_NAMESPACE | TYPESIG_NAME); + void CreateNamespace(TypeSig typeSig, bool onlyNamespace) => CreateTypeSigName(typeSig, TYPESIG_NAMESPACE | (onlyNamespace ? TYPESIG_ONLY_NAMESPACE : 0)); + void CreateName(TypeSig typeSig) => CreateTypeSigName(typeSig, TYPESIG_NAME); TypeSig ReplaceGenericArg(TypeSig typeSig) { - if (genericArguments == null) + if (genericArguments is null) return typeSig; var newTypeSig = genericArguments.Resolve(typeSig); if (newTypeSig != typeSig) @@ -1142,8 +1288,9 @@ TypeSig ReplaceGenericArg(TypeSig typeSig) { const int TYPESIG_NAMESPACE = 1; const int TYPESIG_NAME = 2; + const int TYPESIG_ONLY_NAMESPACE = 4; void CreateTypeSigName(TypeSig typeSig, int flags) { - if (typeSig == null) { + if (typeSig is null) { sb.Append(NULLVALUE); return; } @@ -1181,7 +1328,7 @@ void CreateTypeSigName(TypeSig typeSig, int flags) { if (createNamespace && createName) CreateFullName(((TypeDefOrRefSig)typeSig).TypeDefOrRef); else if (createNamespace) - CreateNamespace(((TypeDefOrRefSig)typeSig).TypeDefOrRef); + CreateNamespace(((TypeDefOrRefSig)typeSig).TypeDefOrRef, (flags & TYPESIG_ONLY_NAMESPACE) != 0); else if (createName) CreateName(((TypeDefOrRefSig)typeSig).TypeDefOrRef); break; @@ -1204,6 +1351,8 @@ void CreateTypeSigName(TypeSig typeSig, int flags) { var arraySig = (ArraySig)typeSig; sb.Append('['); uint rank = arraySig.Rank; + if (rank > MaxArrayRank) + rank = MaxArrayRank; if (rank == 0) sb.Append(""); // Not allowed else if (rank == 1) @@ -1214,8 +1363,8 @@ void CreateTypeSigName(TypeSig typeSig, int flags) { if (!isReflection) { const int NO_LOWER = int.MinValue; const uint NO_SIZE = uint.MaxValue; - int lower = arraySig.LowerBounds.Get(i, NO_LOWER); - uint size = arraySig.Sizes.Get(i, NO_SIZE); + int lower = i < arraySig.LowerBounds.Count ? arraySig.LowerBounds[i] : NO_LOWER; + uint size = i < arraySig.Sizes.Count ? arraySig.Sizes[i] : NO_SIZE; if (lower != NO_LOWER) { sb.Append(lower); sb.Append(".."); @@ -1292,7 +1441,9 @@ void CreateTypeSigName(TypeSig typeSig, int flags) { if (isReflection) { sb.Append('['); int i = -1; - foreach (var genArg in typeGenArgs.GetSafeEnumerable()) { + int count = typeGenArgs.Count; + for (int j = 0; j < count; j++) { + var genArg = typeGenArgs[j]; i++; if (i != 0) sb.Append(','); @@ -1305,11 +1456,7 @@ void CreateTypeSigName(TypeSig typeSig, int flags) { if (mustWriteAssembly) { sb.Append(", "); - var asm = GetDefinitionAssembly(genArg); - if (asm == null) - sb.Append(NULLVALUE); - else - sb.Append(EscapeAssemblyName(GetAssemblyName(asm))); + CreateAssemblyFullName(GetDefinitionAssembly(genArg), true, true); sb.Append(']'); } } @@ -1318,7 +1465,9 @@ void CreateTypeSigName(TypeSig typeSig, int flags) { else { sb.Append('<'); int i = -1; - foreach (var genArg in typeGenArgs.GetSafeEnumerable()) { + int count = typeGenArgs.Count; + for (int j = 0; j < count; j++) { + var genArg = typeGenArgs[j]; i++; if (i != 0) sb.Append(','); @@ -1334,7 +1483,7 @@ void CreateTypeSigName(TypeSig typeSig, int flags) { if (createName) { var gs = (GenericSig)typeSig; var gp = gs.GenericParam; - if (gp == null || !AddName(gp.Name)) { + if (gp is null || !AddName(gp.Name)) { sb.Append(gs.IsMethodVar ? "!!" : "!"); sb.Append(gs.Number); } @@ -1365,7 +1514,7 @@ void CreateTypeSigName(TypeSig typeSig, int flags) { } void CreateAssemblyQualifiedName(ExportedType exportedType) { - if (exportedType == null) { + if (exportedType is null) { sb.Append(NULLVALUE); return; } @@ -1375,14 +1524,16 @@ void CreateAssemblyQualifiedName(ExportedType exportedType) { } CreateFullName(exportedType); - if (MustUseAssemblyName(exportedType)) - AddAssemblyName(GetDefinitionAssembly(exportedType)); + if (MustUseAssemblyName(exportedType)) { + sb.Append(", "); + CreateAssemblyFullName(GetDefinitionAssembly(exportedType), true); + } recursionCounter.Decrement(); } void CreateFullName(ExportedType exportedType) { - if (exportedType == null) { + if (exportedType is null) { sb.Append(NULLVALUE); return; } @@ -1391,49 +1542,42 @@ void CreateFullName(ExportedType exportedType) { return; } - var declaringExportedType = exportedType.Implementation as ExportedType; - if (declaringExportedType != null) { + if (exportedType.Implementation is ExportedType declaringExportedType) { CreateFullName(declaringExportedType); AddNestedTypeSeparator(); } - if (AddNamespace(exportedType.TypeNamespace)) + if (AddNamespace(exportedType.TypeNamespace, false)) sb.Append('.'); AddName(exportedType.TypeName); recursionCounter.Decrement(); } - void CreateNamespace(ExportedType exportedType) { - if (exportedType == null) { + void CreateNamespace(ExportedType exportedType, bool onlyNamespace) { + if (exportedType is null) { sb.Append(NULLVALUE); return; } - AddNamespace(exportedType.TypeNamespace); + AddNamespace(exportedType.TypeNamespace, onlyNamespace); } void CreateName(ExportedType exportedType) { - if (exportedType == null) { + if (exportedType is null) { sb.Append(NULLVALUE); return; } AddName(exportedType.TypeName); } - static string GetAssemblyName(IAssembly assembly) { - var pk = assembly.PublicKeyOrToken; - if (pk is PublicKey) - pk = ((PublicKey)pk).Token; - return Utils.GetAssemblyNameString(EscapeAssemblyName(assembly.Name), assembly.Version, assembly.Culture, pk, assembly.Attributes); - } - - static string EscapeAssemblyName(UTF8String asmSimplName) { - return EscapeAssemblyName(UTF8String.ToSystemString(asmSimplName)); - } + static string EscapeAssemblyName(UTF8String asmSimpleName) => + EscapeAssemblyName(UTF8String.ToSystemString(asmSimpleName)); - static string EscapeAssemblyName(string asmSimplName) { - var sb = new StringBuilder(asmSimplName.Length); - foreach (var c in asmSimplName) { + static string EscapeAssemblyName(string asmSimpleName) { + if (asmSimpleName.IndexOf(']') < 0) + return asmSimpleName; + var sb = new StringBuilder(asmSimpleName.Length); + foreach (var c in asmSimpleName) { if (c == ']') sb.Append('\\'); sb.Append(c); @@ -1448,10 +1592,14 @@ void AddNestedTypeSeparator() { sb.Append('/'); } - bool AddNamespace(UTF8String @namespace) { + bool AddNamespace(UTF8String @namespace, bool onlyNamespace) { if (UTF8String.IsNullOrEmpty(@namespace)) return false; - AddIdentifier(@namespace.String); + // If it's reflection and only namespace (instead of full name), it's not escaped + if (onlyNamespace && isReflection) + sb.Append(@namespace.String); + else + AddIdentifier(@namespace.String); return true; } @@ -1462,16 +1610,43 @@ bool AddName(UTF8String name) { return true; } - void AddAssemblyName(IAssembly assembly) { - sb.Append(", "); - if (assembly == null) + void CreateAssemblyFullName(IAssembly assembly, bool useToken, bool escapeClosingBracket = false) { + if (assembly is null) { sb.Append(NULLVALUE); - else { - var pkt = assembly.PublicKeyOrToken; - if (pkt is PublicKey) - pkt = ((PublicKey)pkt).Token; - sb.Append(Utils.GetAssemblyNameString(assembly.Name, assembly.Version, assembly.Culture, pkt, assembly.Attributes)); + return; + } + + foreach (var c in UTF8String.ToSystemStringOrEmpty(assembly.Name)) { + if (c == ',' || c == '=' || (escapeClosingBracket && c == ']')) + sb.Append('\\'); + sb.Append(c); + } + + if (assembly.Version is not null) { + sb.Append(", Version="); + sb.Append(Utils.CreateVersionWithNoUndefinedValues(assembly.Version)); } + + if (assembly.Culture is not null) { + sb.Append(", Culture="); + if (UTF8String.IsNullOrEmpty(assembly.Culture)) + sb.Append("neutral"); + else + sb.Append(escapeClosingBracket ? EscapeAssemblyName(assembly.Culture) : assembly.Culture.String); + } + + var publicKey = assembly.PublicKeyOrToken; + if (useToken) + publicKey = PublicKeyBase.ToPublicKeyToken(publicKey); + + sb.Append(", "); + sb.Append(publicKey is null || publicKey is PublicKeyToken ? "PublicKeyToken=" : "PublicKey="); + sb.Append(publicKey is null ? "null" : publicKey.ToString()); + + if (assembly.IsRetargetable) + sb.Append(", Retargetable=Yes"); + if (assembly.IsContentTypeWindowsRuntime) + sb.Append(", ContentType=WindowsRuntime"); } void AddIdentifier(string id) { @@ -1497,78 +1672,66 @@ void AddIdentifier(string id) { } IAssembly GetDefinitionAssembly(ITypeDefOrRef typeDefOrRef) { - var tr = typeDefOrRef as TypeRef; - if (tr != null) + if (typeDefOrRef is TypeRef tr) return GetDefinitionAssembly(tr); - var td = typeDefOrRef as TypeDef; - if (td != null) + if (typeDefOrRef is TypeDef td) return GetDefinitionAssembly(td); - var ts = typeDefOrRef as TypeSpec; - if (ts != null) + if (typeDefOrRef is TypeSpec ts) return GetDefinitionAssembly(ts); return null; } IScope GetScope(ITypeDefOrRef typeDefOrRef) { - var tr = typeDefOrRef as TypeRef; - if (tr != null) + if (typeDefOrRef is TypeRef tr) return GetScope(tr); - var td = typeDefOrRef as TypeDef; - if (td != null) + if (typeDefOrRef is TypeDef td) return td.Scope; - var ts = typeDefOrRef as TypeSpec; - if (ts != null) + if (typeDefOrRef is TypeSpec ts) return GetScope(ts); return null; } ITypeDefOrRef GetScopeType(ITypeDefOrRef typeDefOrRef) { - var tr = typeDefOrRef as TypeRef; - if (tr != null) + if (typeDefOrRef is TypeRef tr) return tr; - var td = typeDefOrRef as TypeDef; - if (td != null) + if (typeDefOrRef is TypeDef td) return td; - var ts = typeDefOrRef as TypeSpec; - if (ts != null) + if (typeDefOrRef is TypeSpec ts) return GetScopeType(ts); return null; } ModuleDef GetOwnerModule(ITypeDefOrRef typeDefOrRef) { - var tr = typeDefOrRef as TypeRef; - if (tr != null) + if (typeDefOrRef is TypeRef tr) return GetOwnerModule(tr); - var td = typeDefOrRef as TypeDef; - if (td != null) + if (typeDefOrRef is TypeDef td) return GetOwnerModule(td); - var ts = typeDefOrRef as TypeSpec; - if (ts != null) + if (typeDefOrRef is TypeSpec ts) return GetOwnerModule(ts); return null; } IAssembly GetDefinitionAssembly(TypeRef typeRef) { - if (typeRef == null) + if (typeRef is null) return null; if (!recursionCounter.Increment()) return null; IAssembly result; var scope = typeRef.ResolutionScope; - if (scope == null) + if (scope is null) result = null; //TODO: Check ownerModule's ExportedType table else if (scope is TypeRef) result = GetDefinitionAssembly((TypeRef)scope); @@ -1576,7 +1739,7 @@ IAssembly GetDefinitionAssembly(TypeRef typeRef) { result = (AssemblyRef)scope; else if (scope is ModuleRef) { var ownerModule = GetOwnerModule(typeRef); - result = ownerModule == null ? null : ownerModule.Assembly; + result = ownerModule?.Assembly; } else if (scope is ModuleDef) result = ((ModuleDef)scope).Assembly; @@ -1588,7 +1751,7 @@ IAssembly GetDefinitionAssembly(TypeRef typeRef) { } IScope GetScope(TypeRef typeRef) { - if (typeRef == null) + if (typeRef is null) return null; if (!recursionCounter.Increment()) return null; @@ -1599,15 +1762,15 @@ IScope GetScope(TypeRef typeRef) { ModuleDef modDef; var scope = typeRef.ResolutionScope; - if (scope == null) + if (scope is null) result = null; //TODO: Check ownerModule's ExportedType table - else if ((tr = scope as TypeRef) != null) + else if ((tr = scope as TypeRef) is not null) result = GetScope(tr); - else if ((asmRef = scope as AssemblyRef) != null) + else if ((asmRef = scope as AssemblyRef) is not null) result = asmRef; - else if ((modRef = scope as ModuleRef) != null) + else if ((modRef = scope as ModuleRef) is not null) result = modRef; - else if ((modDef = scope as ModuleDef) != null) + else if ((modDef = scope as ModuleDef) is not null) result = modDef; else result = null; // Should never be reached @@ -1617,24 +1780,21 @@ IScope GetScope(TypeRef typeRef) { } ModuleDef GetOwnerModule(TypeRef typeRef) { - if (typeRef == null) + if (typeRef is null) return null; return typeRef.Module; } - IAssembly GetDefinitionAssembly(TypeDef typeDef) { - var ownerModule = GetOwnerModule(typeDef); - return ownerModule == null ? null : ownerModule.Assembly; - } + IAssembly GetDefinitionAssembly(TypeDef typeDef) => GetOwnerModule(typeDef)?.Assembly; ModuleDef GetOwnerModule(TypeDef typeDef) { - if (typeDef == null) + if (typeDef is null) return null; ModuleDef result = null; for (int i = recursionCounter.Counter; i < RecursionCounter.MAX_RECURSION_COUNT; i++) { var declaringType = typeDef.DeclaringType; - if (declaringType == null) { + if (declaringType is null) { result = typeDef.Module2; break; } @@ -1645,31 +1805,31 @@ ModuleDef GetOwnerModule(TypeDef typeDef) { } IAssembly GetDefinitionAssembly(TypeSpec typeSpec) { - if (typeSpec == null) + if (typeSpec is null) return null; return GetDefinitionAssembly(typeSpec.TypeSig); } IScope GetScope(TypeSpec typeSpec) { - if (typeSpec == null) + if (typeSpec is null) return null; return GetScope(typeSpec.TypeSig); } ITypeDefOrRef GetScopeType(TypeSpec typeSpec) { - if (typeSpec == null) + if (typeSpec is null) return null; return GetScopeType(typeSpec.TypeSig); } ModuleDef GetOwnerModule(TypeSpec typeSpec) { - if (typeSpec == null) + if (typeSpec is null) return null; return GetOwnerModule(typeSpec.TypeSig); } IAssembly GetDefinitionAssembly(TypeSig typeSig) { - if (typeSig == null) + if (typeSig is null) return null; if (!recursionCounter.Increment()) return null; @@ -1717,7 +1877,7 @@ IAssembly GetDefinitionAssembly(TypeSig typeSig) { case ElementType.GenericInst: var genericInstSig = (GenericInstSig)typeSig; var genericType = genericInstSig.GenericType; - result = GetDefinitionAssembly(genericType == null ? null : genericType.TypeDefOrRef); + result = GetDefinitionAssembly(genericType?.TypeDefOrRef); break; case ElementType.Var: @@ -1738,7 +1898,7 @@ IAssembly GetDefinitionAssembly(TypeSig typeSig) { } ITypeDefOrRef GetScopeType(TypeSig typeSig) { - if (typeSig == null) + if (typeSig is null) return null; if (!recursionCounter.Increment()) return null; @@ -1784,9 +1944,7 @@ ITypeDefOrRef GetScopeType(TypeSig typeSig) { break; case ElementType.GenericInst: - var genericInstSig = (GenericInstSig)typeSig; - var genericType = genericInstSig.GenericType; - result = GetScopeType(genericType == null ? null : genericType.TypeDefOrRef); + result = GetScopeType(((GenericInstSig)typeSig).GenericType?.TypeDefOrRef); break; case ElementType.Var: @@ -1807,7 +1965,7 @@ ITypeDefOrRef GetScopeType(TypeSig typeSig) { } IScope GetScope(TypeSig typeSig) { - if (typeSig == null) + if (typeSig is null) return null; if (!recursionCounter.Increment()) return null; @@ -1853,9 +2011,7 @@ IScope GetScope(TypeSig typeSig) { break; case ElementType.GenericInst: - var genericInstSig = (GenericInstSig)typeSig; - var genericType = genericInstSig.GenericType; - result = GetScope(genericType == null ? null : genericType.TypeDefOrRef); + result = GetScope(((GenericInstSig)typeSig).GenericType?.TypeDefOrRef); break; case ElementType.Var: @@ -1876,7 +2032,7 @@ IScope GetScope(TypeSig typeSig) { } ModuleDef GetOwnerModule(TypeSig typeSig) { - if (typeSig == null) + if (typeSig is null) return null; if (!recursionCounter.Increment()) return null; @@ -1922,9 +2078,7 @@ ModuleDef GetOwnerModule(TypeSig typeSig) { break; case ElementType.GenericInst: - var genericInstSig = (GenericInstSig)typeSig; - var genericType = genericInstSig.GenericType; - result = GetOwnerModule(genericType == null ? null : genericType.TypeDefOrRef); + result = GetOwnerModule(((GenericInstSig)typeSig).GenericType?.TypeDefOrRef); break; case ElementType.Var: @@ -1945,22 +2099,21 @@ ModuleDef GetOwnerModule(TypeSig typeSig) { } IAssembly GetDefinitionAssembly(ExportedType exportedType) { - if (exportedType == null) + if (exportedType is null) return null; if (!recursionCounter.Increment()) return null; IAssembly result; - ExportedType et; AssemblyRef asmRef; var scope = exportedType.Implementation; - if ((et = scope as ExportedType) != null) + if (scope is ExportedType et) result = GetDefinitionAssembly(et); - else if ((asmRef = scope as AssemblyRef) != null) + else if ((asmRef = scope as AssemblyRef) is not null) result = asmRef; else if (scope is FileDef) { var ownerModule = GetOwnerModule(exportedType); - result = ownerModule == null ? null : ownerModule.Assembly; + result = ownerModule?.Assembly; } else result = null; @@ -1969,30 +2122,27 @@ IAssembly GetDefinitionAssembly(ExportedType exportedType) { return result; } - ITypeDefOrRef GetScopeType(ExportedType exportedType) { - return null; - } + ITypeDefOrRef GetScopeType(ExportedType exportedType) => null; IScope GetScope(ExportedType exportedType) { - if (exportedType == null) + if (exportedType is null) return null; if (!recursionCounter.Increment()) return null; IScope result; - ExportedType et; AssemblyRef asmRef; FileDef file; var scope = exportedType.Implementation; - if ((et = scope as ExportedType) != null) + if (scope is ExportedType et) result = GetScope(et); - else if ((asmRef = scope as AssemblyRef) != null) + else if ((asmRef = scope as AssemblyRef) is not null) result = asmRef; - else if ((file = scope as FileDef) != null) { + else if ((file = scope as FileDef) is not null) { var ownerModule = GetOwnerModule(exportedType); //TODO: Not all modules' names are equal to the name in FileDef.Name var modRef = new ModuleRefUser(ownerModule, file.Name); - if (ownerModule != null) + if (ownerModule is not null) ownerModule.UpdateRowId(modRef); result = modRef; } @@ -2004,41 +2154,43 @@ IScope GetScope(ExportedType exportedType) { } ModuleDef GetOwnerModule(ExportedType exportedType) { - if (exportedType == null) + if (exportedType is null) return null; return exportedType.Module; } void CreateFieldFullName(string declaringType, string name, FieldSig fieldSig) { - CreateFullName(fieldSig == null ? null : fieldSig.Type); + CreateFullName(fieldSig?.Type); sb.Append(' '); - if (declaringType != null) { + if (declaringType is not null) { sb.Append(declaringType); sb.Append("::"); } - if (name != null) + if (name is not null) sb.Append(name); } void CreateMethodFullName(string declaringType, string name, MethodBaseSig methodSig, MethodDef gppMethod) { - if (methodSig == null) { + if (methodSig is null) { sb.Append(NULLVALUE); return; } CreateFullName(methodSig.RetType); sb.Append(' '); - if (declaringType != null) { + if (declaringType is not null) { sb.Append(declaringType); sb.Append("::"); } - if (name != null) + if (name is not null) sb.Append(name); if (methodSig.Generic) { sb.Append('<'); uint genParamCount = methodSig.GenParamCount; + if (genParamCount > MaxMethodGenParamCount) + genParamCount = MaxMethodGenParamCount; for (uint i = 0; i < genParamCount; i++) { if (i != 0) sb.Append(','); @@ -2052,8 +2204,8 @@ void CreateMethodFullName(string declaringType, string name, MethodBaseSig metho sb.Append(')'); } - int PrintMethodArgList(IEnumerable args, bool hasPrintedArgs, bool isAfterSentinel) { - if (args == null) + int PrintMethodArgList(IList args, bool hasPrintedArgs, bool isAfterSentinel) { + if (args is null) return 0; if (isAfterSentinel) { if (hasPrintedArgs) @@ -2062,7 +2214,9 @@ int PrintMethodArgList(IEnumerable args, bool hasPrintedArgs, bool isAf hasPrintedArgs = true; } int count = 0; - foreach (var arg in args.GetSafeEnumerable()) { + int argsCount = args.Count; + for (int i = 0; i < argsCount; i++) { + var arg = args[i]; count++; if (hasPrintedArgs) sb.Append(','); @@ -2072,14 +2226,13 @@ int PrintMethodArgList(IEnumerable args, bool hasPrintedArgs, bool isAf return count; } - void CreatePropertyFullName(string declaringType, UTF8String name, CallingConventionSig propertySig) { + void CreatePropertyFullName(string declaringType, UTF8String name, CallingConventionSig propertySig) => CreateMethodFullName(declaringType, UTF8String.ToSystemString(name), propertySig as MethodBaseSig, null); - } void CreateEventFullName(string declaringType, UTF8String name, ITypeDefOrRef typeDefOrRef) { CreateFullName(typeDefOrRef); sb.Append(' '); - if (declaringType != null) { + if (declaringType is not null) { sb.Append(declaringType); sb.Append("::"); } @@ -2088,8 +2241,6 @@ void CreateEventFullName(string declaringType, UTF8String name, ITypeDefOrRef ty } /// - public override string ToString() { - return Result; - } + public override string ToString() => Result; } } diff --git a/src/DotNet/GenericArguments.cs b/src/DotNet/GenericArguments.cs index 92a76c7f3..866b5f766 100644 --- a/src/DotNet/GenericArguments.cs +++ b/src/DotNet/GenericArguments.cs @@ -1,9 +1,9 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; +using System.Collections.Generic; namespace dnlib.DotNet { - struct GenericArgumentsStack { + readonly struct GenericArgumentsStack { readonly List> argsStack; readonly bool isTypeVar; @@ -12,7 +12,7 @@ struct GenericArgumentsStack { /// /// true if it's for generic types, false if generic methods public GenericArgumentsStack(bool isTypeVar) { - this.argsStack = new List>(); + argsStack = new List>(); this.isTypeVar = isTypeVar; } @@ -20,9 +20,7 @@ public GenericArgumentsStack(bool isTypeVar) { /// Pushes generic arguments /// /// The generic arguments - public void Push(IList args) { - argsStack.Add(args); - } + public void Push(IList args) => argsStack.Add(args); /// /// Pops generic arguments @@ -48,7 +46,7 @@ public TypeSig Resolve(uint number) { return null; var typeSig = args[(int)number]; var gvar = typeSig as GenericSig; - if (gvar == null || gvar.IsTypeVar != isTypeVar) + if (gvar is null || gvar.IsTypeVar != isTypeVar) return typeSig; result = gvar; number = gvar.Number; @@ -60,7 +58,7 @@ public TypeSig Resolve(uint number) { /// /// Replaces generic type/method var with its generic argument /// - public sealed class GenericArguments { + sealed class GenericArguments { GenericArgumentsStack typeArgsStack = new GenericArgumentsStack(true); GenericArgumentsStack methodArgsStack = new GenericArgumentsStack(false); @@ -68,33 +66,25 @@ public sealed class GenericArguments { /// Pushes generic arguments /// /// The generic arguments - public void PushTypeArgs(IList typeArgs) { - typeArgsStack.Push(typeArgs); - } + public void PushTypeArgs(IList typeArgs) => typeArgsStack.Push(typeArgs); /// /// Pops generic arguments /// /// The popped generic arguments - public IList PopTypeArgs() { - return typeArgsStack.Pop(); - } + public IList PopTypeArgs() => typeArgsStack.Pop(); /// /// Pushes generic arguments /// /// The generic arguments - public void PushMethodArgs(IList methodArgs) { - methodArgsStack.Push(methodArgs); - } + public void PushMethodArgs(IList methodArgs) => methodArgsStack.Push(methodArgs); /// /// Pops generic arguments /// /// The popped generic arguments - public IList PopMethodArgs() { - return methodArgsStack.Pop(); - } + public IList PopMethodArgs() => methodArgsStack.Pop(); /// /// Replaces a generic type/method var with its generic argument (if any). If @@ -105,23 +95,21 @@ public IList PopMethodArgs() { /// New which is never null unless /// is null public TypeSig Resolve(TypeSig typeSig) { - if (typeSig == null) + if (typeSig is null) return null; var sig = typeSig; - var genericMVar = sig as GenericMVar; - if (genericMVar != null) { + if (sig is GenericMVar genericMVar) { var newSig = methodArgsStack.Resolve(genericMVar.Number); - if (newSig == null || newSig == sig) + if (newSig is null || newSig == sig) return sig; return newSig; } - var genericVar = sig as GenericVar; - if (genericVar != null) { + if (sig is GenericVar genericVar) { var newSig = typeArgsStack.Resolve(genericVar.Number); - if (newSig == null || newSig == sig) + if (newSig is null || newSig == sig) return sig; return newSig; } diff --git a/src/DotNet/GenericParam.cs b/src/DotNet/GenericParam.cs index 5df8bfe36..942654bd7 100644 --- a/src/DotNet/GenericParam.cs +++ b/src/DotNet/GenericParam.cs @@ -1,51 +1,42 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Diagnostics; using System.Threading; using dnlib.Utils; using dnlib.DotNet.MD; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif +using dnlib.DotNet.Pdb; +using System.Collections.Generic; namespace dnlib.DotNet { /// /// A high-level representation of a row in the GenericParam table /// [DebuggerDisplay("{Name.String}")] - public abstract class GenericParam : IHasCustomAttribute, IMemberDef, IListListener { + public abstract class GenericParam : IHasCustomAttribute, IHasCustomDebugInformation, IMemberDef, IListListener { /// /// The row id in its table /// protected uint rid; /// - public MDToken MDToken { - get { return new MDToken(Table.GenericParam, rid); } - } + public MDToken MDToken => new MDToken(Table.GenericParam, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 19; } - } + public int HasCustomAttributeTag => 19; /// /// Gets the owner type/method /// public ITypeOrMethodDef Owner { - get { return owner; } - internal set { owner = value; } + get => owner; + internal set => owner = value; } /// protected ITypeOrMethodDef owner; @@ -54,29 +45,23 @@ public ITypeOrMethodDef Owner { /// Gets the declaring type or null if none or if is /// not a /// - public TypeDef DeclaringType { - get { return owner as TypeDef; } - } + public TypeDef DeclaringType => owner as TypeDef; /// - ITypeDefOrRef IMemberRef.DeclaringType { - get { return owner as TypeDef; } - } + ITypeDefOrRef IMemberRef.DeclaringType => owner as TypeDef; /// /// Gets the declaring method or null if none or if is /// not a /// - public MethodDef DeclaringMethod { - get { return owner as MethodDef; } - } + public MethodDef DeclaringMethod => owner as MethodDef; /// /// From column GenericParam.Number /// public ushort Number { - get { return number; } - set { number = value; } + get => number; + set => number = value; } /// protected ushort number; @@ -85,8 +70,8 @@ public ushort Number { /// From column GenericParam.Flags /// public GenericParamAttributes Flags { - get { return (GenericParamAttributes)attributes; } - set { attributes = (int)value; } + get => (GenericParamAttributes)attributes; + set => attributes = (int)value; } /// Attributes protected int attributes; @@ -95,8 +80,8 @@ public GenericParamAttributes Flags { /// From column GenericParam.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -105,8 +90,8 @@ public UTF8String Name { /// From column GenericParam.Kind (v1.1 only) /// public ITypeDefOrRef Kind { - get { return kind; } - set { kind = value; } + get => kind; + set => kind = value; } /// protected ITypeDefOrRef kind; @@ -114,9 +99,9 @@ public ITypeDefOrRef Kind { /// /// Gets the generic param constraints /// - public ThreadSafe.IList GenericParamConstraints { + public IList GenericParamConstraints { get { - if (genericParamConstraints == null) + if (genericParamConstraints is null) InitializeGenericParamConstraints(); return genericParamConstraints; } @@ -124,16 +109,15 @@ public ThreadSafe.IList GenericParamConstraints { /// protected LazyList genericParamConstraints; /// Initializes - protected virtual void InitializeGenericParamConstraints() { + protected virtual void InitializeGenericParamConstraints() => Interlocked.CompareExchange(ref genericParamConstraints, new LazyList(this), null); - } /// /// Gets all custom attributes /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -141,86 +125,58 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } - } - - /// - /// true if is not empty - /// - public bool HasGenericParamConstraints { - get { return GenericParamConstraints.Count > 0; } - } + public bool HasCustomAttributes => CustomAttributes.Count > 0; /// - public ModuleDef Module { - get { - var dt = owner; - return dt == null ? null : dt.Module; - } - } + public int HasCustomDebugInformationTag => 19; /// - public string FullName { - get { return UTF8String.ToSystemStringOrEmpty(name); } - } + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; - bool IIsTypeOrMethod.IsType { - get { return false; } - } - - bool IIsTypeOrMethod.IsMethod { - get { return false; } - } - - bool IMemberRef.IsField { - get { return false; } - } - - bool IMemberRef.IsTypeSpec { - get { return false; } - } - - bool IMemberRef.IsTypeRef { - get { return false; } - } - - bool IMemberRef.IsTypeDef { - get { return false; } - } - - bool IMemberRef.IsMethodSpec { - get { return false; } - } - - bool IMemberRef.IsMethodDef { - get { return false; } - } - - bool IMemberRef.IsMemberRef { - get { return false; } - } - - bool IMemberRef.IsFieldDef { - get { return false; } + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); - bool IMemberRef.IsPropertyDef { - get { return false; } - } + /// + /// true if is not empty + /// + public bool HasGenericParamConstraints => GenericParamConstraints.Count > 0; - bool IMemberRef.IsEventDef { - get { return false; } - } + /// + public ModuleDef Module => owner?.Module; - bool IMemberRef.IsGenericParam { - get { return true; } - } + /// + public string FullName => UTF8String.ToSystemStringOrEmpty(name); + + bool IIsTypeOrMethod.IsType => false; + bool IIsTypeOrMethod.IsMethod => false; + bool IMemberRef.IsField => false; + bool IMemberRef.IsTypeSpec => false; + bool IMemberRef.IsTypeRef => false; + bool IMemberRef.IsTypeDef => false; + bool IMemberRef.IsMethodSpec => false; + bool IMemberRef.IsMethodDef => false; + bool IMemberRef.IsMemberRef => false; + bool IMemberRef.IsFieldDef => false; + bool IMemberRef.IsPropertyDef => false; + bool IMemberRef.IsEventDef => false; + bool IMemberRef.IsGenericParam => true; /// /// Modify property: = @@ -228,17 +184,8 @@ bool IMemberRef.IsGenericParam { /// /// Value to AND /// Value to OR - void ModifyAttributes(GenericParamAttributes andMask, GenericParamAttributes orMask) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - newVal = (origVal & (int)andMask) | (int)orMask; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else + void ModifyAttributes(GenericParamAttributes andMask, GenericParamAttributes orMask) => attributes = (attributes & (int)andMask) | (int)orMask; -#endif - } /// /// Set or clear flags in @@ -247,96 +194,83 @@ void ModifyAttributes(GenericParamAttributes andMask, GenericParamAttributes orM /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, GenericParamAttributes flags) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else if (set) attributes |= (int)flags; else attributes &= ~(int)flags; -#endif } /// /// Gets/sets variance (non, contra, co) /// public GenericParamAttributes Variance { - get { return (GenericParamAttributes)attributes & GenericParamAttributes.VarianceMask; } - set { ModifyAttributes(~GenericParamAttributes.VarianceMask, value & GenericParamAttributes.VarianceMask); } + get => (GenericParamAttributes)attributes & GenericParamAttributes.VarianceMask; + set => ModifyAttributes(~GenericParamAttributes.VarianceMask, value & GenericParamAttributes.VarianceMask); } /// /// true if is set /// - public bool IsNonVariant { - get { return Variance == GenericParamAttributes.NonVariant; } - } + public bool IsNonVariant => Variance == GenericParamAttributes.NonVariant; /// /// true if is set /// - public bool IsCovariant { - get { return Variance == GenericParamAttributes.Covariant; } - } + public bool IsCovariant => Variance == GenericParamAttributes.Covariant; /// /// true if is set /// - public bool IsContravariant { - get { return Variance == GenericParamAttributes.Contravariant; } - } + public bool IsContravariant => Variance == GenericParamAttributes.Contravariant; /// /// Gets/sets the special constraint /// public GenericParamAttributes SpecialConstraint { - get { return (GenericParamAttributes)attributes & GenericParamAttributes.SpecialConstraintMask; } - set { ModifyAttributes(~GenericParamAttributes.SpecialConstraintMask, value & GenericParamAttributes.SpecialConstraintMask); } + get => (GenericParamAttributes)attributes & GenericParamAttributes.SpecialConstraintMask; + set => ModifyAttributes(~GenericParamAttributes.SpecialConstraintMask, value & GenericParamAttributes.SpecialConstraintMask); } /// /// true if there are no special constraints /// - public bool HasNoSpecialConstraint { - get { return ((GenericParamAttributes)attributes & GenericParamAttributes.SpecialConstraintMask) == GenericParamAttributes.NoSpecialConstraint; } - } + public bool HasNoSpecialConstraint => ((GenericParamAttributes)attributes & GenericParamAttributes.SpecialConstraintMask) == GenericParamAttributes.NoSpecialConstraint; /// /// Gets/sets the bit /// public bool HasReferenceTypeConstraint { - get { return ((GenericParamAttributes)attributes & GenericParamAttributes.ReferenceTypeConstraint) != 0; } - set { ModifyAttributes(value, GenericParamAttributes.ReferenceTypeConstraint); } + get => ((GenericParamAttributes)attributes & GenericParamAttributes.ReferenceTypeConstraint) != 0; + set => ModifyAttributes(value, GenericParamAttributes.ReferenceTypeConstraint); } /// /// Gets/sets the bit /// public bool HasNotNullableValueTypeConstraint { - get { return ((GenericParamAttributes)attributes & GenericParamAttributes.NotNullableValueTypeConstraint) != 0; } - set { ModifyAttributes(value, GenericParamAttributes.NotNullableValueTypeConstraint); } + get => ((GenericParamAttributes)attributes & GenericParamAttributes.NotNullableValueTypeConstraint) != 0; + set => ModifyAttributes(value, GenericParamAttributes.NotNullableValueTypeConstraint); } /// /// Gets/sets the bit /// public bool HasDefaultConstructorConstraint { - get { return ((GenericParamAttributes)attributes & GenericParamAttributes.DefaultConstructorConstraint) != 0; } - set { ModifyAttributes(value, GenericParamAttributes.DefaultConstructorConstraint); } + get => ((GenericParamAttributes)attributes & GenericParamAttributes.DefaultConstructorConstraint) != 0; + set => ModifyAttributes(value, GenericParamAttributes.DefaultConstructorConstraint); } - /// - void IListListener.OnLazyAdd(int index, ref GenericParamConstraint value) { - OnLazyAdd2(index, ref value); + /// + /// Gets/sets the bit + /// + public bool AllowsByRefLike { + get => ((GenericParamAttributes)attributes & GenericParamAttributes.AllowByRefLike) != 0; + set => ModifyAttributes(value, GenericParamAttributes.AllowByRefLike); } + /// + void IListListener.OnLazyAdd(int index, ref GenericParamConstraint value) => OnLazyAdd2(index, ref value); + internal virtual void OnLazyAdd2(int index, ref GenericParamConstraint value) { #if DEBUG if (value.Owner != this) @@ -346,15 +280,13 @@ internal virtual void OnLazyAdd2(int index, ref GenericParamConstraint value) { /// void IListListener.OnAdd(int index, GenericParamConstraint value) { - if (value.Owner != null) + if (value.Owner is not null) throw new InvalidOperationException("Generic param constraint is already owned by another generic param. Set Owner to null first."); value.Owner = this; } /// - void IListListener.OnRemove(int index, GenericParamConstraint value) { - value.Owner = null; - } + void IListListener.OnRemove(int index, GenericParamConstraint value) => value.Owner = null; /// void IListListener.OnResize(int index) { @@ -362,7 +294,7 @@ void IListListener.OnResize(int index) { /// void IListListener.OnClear() { - foreach (var gpc in GenericParamConstraints.GetEnumerable_NoLock()) + foreach (var gpc in genericParamConstraints.GetEnumerable_NoLock()) gpc.Owner = null; } @@ -370,10 +302,10 @@ void IListListener.OnClear() { public override string ToString() { var o = owner; if (o is TypeDef) - return string.Format("!{0}", number); + return $"!{number}"; if (o is MethodDef) - return string.Format("!!{0}", number); - return string.Format("??{0}", number); + return $"!!{number}"; + return $"??{number}"; } } @@ -411,9 +343,9 @@ public GenericParamUser(ushort number, GenericParamAttributes flags) /// Flags /// Name public GenericParamUser(ushort number, GenericParamAttributes flags, UTF8String name) { - this.genericParamConstraints = new LazyList(this); + genericParamConstraints = new LazyList(this); this.number = number; - this.attributes = (int)flags; + attributes = (int)flags; this.name = name; } } @@ -428,27 +360,31 @@ sealed class GenericParamMD : GenericParam, IMDTokenProviderMD { readonly uint origRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.GenericParam, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.GenericParam, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), GetGenericParamContext(owner), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// protected override void InitializeGenericParamConstraints() { - var list = readerModule.MetaData.GetGenericParamConstraintRidList(origRid); - var tmp = new LazyList((int)list.Length, this, list, (list2, index) => readerModule.ResolveGenericParamConstraint(((RidList)list2)[index], GetGenericParamContext(owner))); + var list = readerModule.Metadata.GetGenericParamConstraintRidList(origRid); + var tmp = new LazyList(list.Count, this, list, (list2, index) => readerModule.ResolveGenericParamConstraint(list2[index], GetGenericParamContext(owner))); Interlocked.CompareExchange(ref genericParamConstraints, tmp, null); } static GenericParamContext GetGenericParamContext(ITypeOrMethodDef tmOwner) { - var md = tmOwner as MethodDef; - if (md != null) + if (tmOwner is MethodDef md) return GenericParamContext.Create(md); return new GenericParamContext(tmOwner as TypeDef); } @@ -462,20 +398,22 @@ static GenericParamContext GetGenericParamContext(ITypeOrMethodDef tmOwner) { /// If is invalid public GenericParamMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.GenericParamTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("GenericParam rid {0} does not exist", rid)); + throw new BadImageFormatException($"GenericParam rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint name; - uint kind = readerModule.TablesStream.ReadGenericParamRow(origRid, out this.number, out this.attributes, out name); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.owner = readerModule.GetOwner(this); - if (kind != 0) - this.kind = readerModule.ResolveTypeDefOrRef(kind, GetGenericParamContext(owner)); + bool b = readerModule.TablesStream.TryReadGenericParamRow(origRid, out var row); + Debug.Assert(b); + number = row.Number; + attributes = row.Flags; + name = readerModule.StringsStream.ReadNoNull(row.Name); + owner = readerModule.GetOwner(this); + if (row.Kind != 0) + kind = readerModule.ResolveTypeDefOrRef(row.Kind, GetGenericParamContext(owner)); } internal GenericParamMD InitializeAll() { diff --git a/src/DotNet/GenericParamAttributes.cs b/src/DotNet/GenericParamAttributes.cs index 2b14fba24..420bf828b 100644 --- a/src/DotNet/GenericParamAttributes.cs +++ b/src/DotNet/GenericParamAttributes.cs @@ -18,7 +18,7 @@ public enum GenericParamAttributes : ushort { Contravariant = 0x0002, /// - SpecialConstraintMask = 0x001C, + SpecialConstraintMask = 0x003C, /// NoSpecialConstraint = 0x0000, /// type argument must be a reference type @@ -27,5 +27,7 @@ public enum GenericParamAttributes : ushort { NotNullableValueTypeConstraint = 0x0008, /// type argument must have a public default constructor DefaultConstructorConstraint = 0x0010, + /// type argument can be ByRefLike + AllowByRefLike = 0x0020, } } diff --git a/src/DotNet/GenericParamConstraint.cs b/src/DotNet/GenericParamConstraint.cs index 4d20a9a05..1611a4bf1 100644 --- a/src/DotNet/GenericParamConstraint.cs +++ b/src/DotNet/GenericParamConstraint.cs @@ -1,41 +1,40 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// /// A high-level representation of a row in the GenericParamConstraint table /// - public abstract class GenericParamConstraint : IHasCustomAttribute, IContainsGenericParameter { + public abstract class GenericParamConstraint : IHasCustomAttribute, IHasCustomDebugInformation, IContainsGenericParameter { /// /// The row id in its table /// protected uint rid; /// - public MDToken MDToken { - get { return new MDToken(Table.GenericParamConstraint, rid); } - } + public MDToken MDToken => new MDToken(Table.GenericParamConstraint, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 20; } - } + public int HasCustomAttributeTag => 20; /// /// Gets the owner generic param /// public GenericParam Owner { - get { return owner; } - internal set { owner = value; } + get => owner; + internal set => owner = value; } /// protected GenericParam owner; @@ -44,8 +43,8 @@ public GenericParam Owner { /// From column GenericParamConstraint.Constraint /// public ITypeDefOrRef Constraint { - get { return constraint; } - set { constraint = value; } + get => constraint; + set => constraint = value; } /// protected ITypeDefOrRef constraint; @@ -55,7 +54,7 @@ public ITypeDefOrRef Constraint { /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -63,18 +62,35 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } - } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 20; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; - bool IContainsGenericParameter.ContainsGenericParameter { - get { return TypeHelper.ContainsGenericParameter(this); } + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); + + bool IContainsGenericParameter.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); } /// @@ -91,32 +107,38 @@ public GenericParamConstraintUser() { /// Constructor /// /// The constraint - public GenericParamConstraintUser(ITypeDefOrRef constraint) { - this.constraint = constraint; - } + public GenericParamConstraintUser(ITypeDefOrRef constraint) => this.constraint = constraint; } /// /// Created from a row in the GenericParamConstraint table /// - sealed class GenericParamConstraintMD : GenericParamConstraint, IMDTokenProviderMD { + sealed class GenericParamConstraintMD : GenericParamConstraint, IMDTokenProviderMD, IContainsGenericParameter2 { /// The module where this instance is located readonly ModuleDefMD readerModule; readonly uint origRid; + readonly GenericParamContext gpContext; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; + + bool IContainsGenericParameter2.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.GenericParamConstraint, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.GenericParamConstraint, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), gpContext, list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -127,17 +149,19 @@ protected override void InitializeCustomAttributes() { /// If is invalid public GenericParamConstraintMD(ModuleDefMD readerModule, uint rid, GenericParamContext gpContext) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.GenericParamConstraintTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("GenericParamConstraint rid {0} does not exist", rid)); + throw new BadImageFormatException($"GenericParamConstraint rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint constraint = readerModule.TablesStream.ReadGenericParamConstraintRow2(origRid); - this.constraint = readerModule.ResolveTypeDefOrRef(constraint, gpContext); - this.owner = readerModule.GetOwner(this); + this.gpContext = gpContext; + bool b = readerModule.TablesStream.TryReadGenericParamConstraintRow(origRid, out var row); + Debug.Assert(b); + constraint = readerModule.ResolveTypeDefOrRef(row.Constraint, gpContext); + owner = readerModule.GetOwner(this); } internal GenericParamConstraintMD InitializeAll() { diff --git a/src/DotNet/GenericParamContext.cs b/src/DotNet/GenericParamContext.cs index 7f2e35f59..07b3304ca 100644 --- a/src/DotNet/GenericParamContext.cs +++ b/src/DotNet/GenericParamContext.cs @@ -1,10 +1,10 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info namespace dnlib.DotNet { /// /// Generic parameter context /// - public struct GenericParamContext { + public readonly struct GenericParamContext { /// /// Type context /// @@ -18,9 +18,7 @@ public struct GenericParamContext { /// /// true if and are both null /// - public bool IsEmpty { - get { return Type == null && Method == null; } - } + public bool IsEmpty => Type is null && Method is null; /// /// Creates a new instance and initializes the @@ -30,7 +28,7 @@ public bool IsEmpty { /// Method /// A new instance public static GenericParamContext Create(MethodDef method) { - if (method == null) + if (method is null) return new GenericParamContext(); return new GenericParamContext(method.DeclaringType, method); } @@ -42,17 +40,15 @@ public static GenericParamContext Create(MethodDef method) { /// /// Type /// A new instance - public static GenericParamContext Create(TypeDef type) { - return new GenericParamContext(type); - } + public static GenericParamContext Create(TypeDef type) => new GenericParamContext(type); /// /// Constructor /// /// Type context public GenericParamContext(TypeDef type) { - this.Type = type; - this.Method = null; + Type = type; + Method = null; } /// @@ -62,8 +58,8 @@ public GenericParamContext(TypeDef type) { /// /// Method context public GenericParamContext(MethodDef method) { - this.Type = null; - this.Method = method; + Type = null; + Method = method; } /// @@ -72,8 +68,8 @@ public GenericParamContext(MethodDef method) { /// Type context /// Method context public GenericParamContext(TypeDef type, MethodDef method) { - this.Type = type; - this.Method = method; + Type = type; + Method = method; } } } diff --git a/src/DotNet/IAssemblyResolver.cs b/src/DotNet/IAssemblyResolver.cs index 2824e6e77..2d3eee45f 100644 --- a/src/DotNet/IAssemblyResolver.cs +++ b/src/DotNet/IAssemblyResolver.cs @@ -1,6 +1,5 @@ // dnlib: See LICENSE.txt for more info -using System; using System.Reflection; namespace dnlib.DotNet { @@ -16,58 +15,9 @@ public interface IAssemblyResolver { /// An instance owned by the assembly resolver or /// null if the assembly couldn't be found. AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule); - - /// - /// Add an assembly to the assembly cache - /// - /// The assembly - /// true if is cached, false if it's not - /// cached because some other assembly with the exact same full name has already been - /// cached or if is null. - bool AddToCache(AssemblyDef asm); - - /// - /// Removes the assembly from the cache - /// - /// The assembly - /// true if it was removed, false if it wasn't removed since it - /// wasn't in the cache or if was null - bool Remove(AssemblyDef asm); - - /// - /// Clears the cache and calls on each cached module. - /// Use to remove any assemblies you added yourself - /// using before calling this method if you don't want - /// them disposed. - /// - void Clear(); } public static partial class Extensions { - /// - /// Add a module's assembly to the assembly cache - /// - /// this - /// The module whose assembly should be cached - /// true if 's assembly is cached, false - /// if it's not cached because some other assembly with the exact same full name has - /// already been cached or if or its assembly is null. - public static bool AddToCache(this IAssemblyResolver self, ModuleDef module) { - return module != null && self.AddToCache(module.Assembly); - } - - /// - /// Removes a module's assembly from the cache - /// - /// this - /// The module - /// true if its assembly was removed, false if it wasn't removed - /// since it wasn't in the cache, it has no assembly, or was - /// null - public static bool Remove(this IAssemblyResolver self, ModuleDef module) { - return module != null && self.Remove(module.Assembly); - } - /// /// Finds and returns an /// @@ -77,7 +27,7 @@ public static bool Remove(this IAssemblyResolver self, ModuleDef module) { /// An instance owned by the assembly resolver or /// null if the assembly couldn't be found. public static AssemblyDef Resolve(this IAssemblyResolver self, AssemblyName assembly, ModuleDef sourceModule) { - if (assembly == null) + if (assembly is null) return null; return self.Resolve(new AssemblyNameInfo(assembly), sourceModule); } @@ -91,7 +41,7 @@ public static AssemblyDef Resolve(this IAssemblyResolver self, AssemblyName asse /// An instance owned by the assembly resolver or /// null if the assembly couldn't be found. public static AssemblyDef Resolve(this IAssemblyResolver self, string asmFullName, ModuleDef sourceModule) { - if (asmFullName == null) + if (asmFullName is null) return null; return self.Resolve(new AssemblyNameInfo(asmFullName), sourceModule); } @@ -105,12 +55,12 @@ public static AssemblyDef Resolve(this IAssemblyResolver self, string asmFullNam /// An instance owned by the assembly resolver /// If the assembly couldn't be found. public static AssemblyDef ResolveThrow(this IAssemblyResolver self, IAssembly assembly, ModuleDef sourceModule) { - if (assembly == null) + if (assembly is null) return null; var asm = self.Resolve(assembly, sourceModule); - if (asm != null) + if (asm is not null) return asm; - throw new AssemblyResolveException(string.Format("Could not resolve assembly: {0}", assembly)); + throw new AssemblyResolveException($"Could not resolve assembly: {assembly}"); } /// @@ -122,12 +72,12 @@ public static AssemblyDef ResolveThrow(this IAssemblyResolver self, IAssembly as /// An instance owned by the assembly resolver /// If the assembly couldn't be found. public static AssemblyDef ResolveThrow(this IAssemblyResolver self, AssemblyName assembly, ModuleDef sourceModule) { - if (assembly == null) + if (assembly is null) return null; var asm = self.Resolve(new AssemblyNameInfo(assembly), sourceModule); - if (asm != null) + if (asm is not null) return asm; - throw new AssemblyResolveException(string.Format("Could not resolve assembly: {0}", assembly)); + throw new AssemblyResolveException($"Could not resolve assembly: {assembly}"); } /// @@ -139,12 +89,12 @@ public static AssemblyDef ResolveThrow(this IAssemblyResolver self, AssemblyName /// An instance owned by the assembly resolver /// If the assembly couldn't be found. public static AssemblyDef ResolveThrow(this IAssemblyResolver self, string asmFullName, ModuleDef sourceModule) { - if (asmFullName == null) + if (asmFullName is null) return null; var asm = self.Resolve(new AssemblyNameInfo(asmFullName), sourceModule); - if (asm != null) + if (asm is not null) return asm; - throw new AssemblyResolveException(string.Format("Could not resolve assembly: {0}", asmFullName)); + throw new AssemblyResolveException($"Could not resolve assembly: {asmFullName}"); } } } diff --git a/src/DotNet/ICodedToken.cs b/src/DotNet/ICodedToken.cs index 921d742ef..e4b5ca57b 100644 --- a/src/DotNet/ICodedToken.cs +++ b/src/DotNet/ICodedToken.cs @@ -1,12 +1,8 @@ // dnlib: See LICENSE.txt for more info using System; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif +using System.Collections.Generic; +using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// @@ -153,15 +149,35 @@ public interface IAssembly : IFullName { public static partial class Extensions { /// /// Checks whether appears to be the core library (eg. - /// mscorlib, System.Runtime or corefx) + /// mscorlib, System.Runtime or corefx). + /// + /// If is a reference to a private corlib (eg. System.Private.CoreLib), + /// this method returns false unless is an + /// whose manifest (first) module defines System.Object. This check is performed in + /// the constructor and the result can be found in . + /// + /// Note that this method also returns true if it appears to be a 'public' corlib, + /// eg. mscorlib, etc, even if it internally possibly references a private corlib. /// /// The assembly public static bool IsCorLib(this IAssembly asm) { + if (asm is AssemblyDef asmDef) { + var manifestModule = asmDef.ManifestModule; + if (manifestModule is not null) { + var isCorModule = manifestModule.IsCoreLibraryModule; + if (isCorModule is not null) + return isCorModule.Value; + } + } + string asmName; - return asm != null && + return asm is not null && UTF8String.IsNullOrEmpty(asm.Culture) && ((asmName = UTF8String.ToSystemStringOrEmpty(asm.Name)).Equals("mscorlib", StringComparison.OrdinalIgnoreCase) || asmName.Equals("System.Runtime", StringComparison.OrdinalIgnoreCase) || + // This name could change but since CoreCLR is used a lot, it's worth supporting + asmName.Equals("System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) || + asmName.Equals("netstandard", StringComparison.OrdinalIgnoreCase) || asmName.Equals("corefx", StringComparison.OrdinalIgnoreCase)); } @@ -171,7 +187,7 @@ public static bool IsCorLib(this IAssembly asm) { /// The assembly /// A new instance public static AssemblyRef ToAssemblyRef(this IAssembly asm) { - if (asm == null) + if (asm is null) return null; // Always create a new one, even if it happens to be an AssemblyRef return new AssemblyRefUser(asm.Name, asm.Version, asm.PublicKeyOrToken, asm.Culture) { Attributes = asm.Attributes }; @@ -181,44 +197,32 @@ public static AssemblyRef ToAssemblyRef(this IAssembly asm) { /// Converts to a /// /// The type - /// A instance or null if - /// is invalid - public static TypeSig ToTypeSig(this ITypeDefOrRef type) { - return ToTypeSig(type, true); - } - - /// - /// Converts to a - /// - /// The type - /// true if we should try to figure out whether + /// true if we should try to resolve in order to figure out whether /// is a /// A instance or null if /// is invalid - public static TypeSig ToTypeSig(this ITypeDefOrRef type, bool checkValueType) { - if (type == null) + public static TypeSig ToTypeSig(this ITypeDefOrRef type, bool resolveToCheckValueType = true) { + if (type is null) return null; var module = type.Module; - if (module != null) { + if (module is not null) { var corLibType = module.CorLibTypes.GetCorLibTypeSig(type); - if (corLibType != null) + if (corLibType is not null) return corLibType; } var td = type as TypeDef; - if (td != null) - return CreateClassOrValueType(type, checkValueType ? td.IsValueType : false); + if (td is not null) + return CreateClassOrValueType(type, td.IsValueType); - var tr = type as TypeRef; - if (tr != null) { - if (checkValueType) + if (type is TypeRef tr) { + if (resolveToCheckValueType) td = tr.Resolve(); - return CreateClassOrValueType(type, td == null ? false : td.IsValueType); + return CreateClassOrValueType(type, td is null ? false : td.IsValueType); } - var ts = type as TypeSpec; - if (ts != null) + if (type is TypeSpec ts) return ts.TypeSig; return null; @@ -238,7 +242,7 @@ static TypeSig CreateClassOrValueType(ITypeDefOrRef type, bool isValueType) { /// public static TypeDefOrRefSig TryGetTypeDefOrRefSig(this ITypeDefOrRef type) { var ts = type as TypeSpec; - return ts == null ? null : ts.TypeSig.RemovePinnedAndModifiers() as TypeDefOrRefSig; + return ts is null ? null : ts.TypeSig.RemovePinnedAndModifiers() as TypeDefOrRefSig; } /// @@ -249,7 +253,7 @@ public static TypeDefOrRefSig TryGetTypeDefOrRefSig(this ITypeDefOrRef type) { /// public static ClassOrValueTypeSig TryGetClassOrValueTypeSig(this ITypeDefOrRef type) { var ts = type as TypeSpec; - return ts == null ? null : ts.TypeSig.RemovePinnedAndModifiers() as ClassOrValueTypeSig; + return ts is null ? null : ts.TypeSig.RemovePinnedAndModifiers() as ClassOrValueTypeSig; } /// @@ -260,7 +264,7 @@ public static ClassOrValueTypeSig TryGetClassOrValueTypeSig(this ITypeDefOrRef t /// public static ValueTypeSig TryGetValueTypeSig(this ITypeDefOrRef type) { var ts = type as TypeSpec; - return ts == null ? null : ts.TypeSig.RemovePinnedAndModifiers() as ValueTypeSig; + return ts is null ? null : ts.TypeSig.RemovePinnedAndModifiers() as ValueTypeSig; } /// @@ -271,7 +275,7 @@ public static ValueTypeSig TryGetValueTypeSig(this ITypeDefOrRef type) { /// public static ClassSig TryGetClassSig(this ITypeDefOrRef type) { var ts = type as TypeSpec; - return ts == null ? null : ts.TypeSig.RemovePinnedAndModifiers() as ClassSig; + return ts is null ? null : ts.TypeSig.RemovePinnedAndModifiers() as ClassSig; } /// @@ -282,7 +286,7 @@ public static ClassSig TryGetClassSig(this ITypeDefOrRef type) { /// public static GenericSig TryGetGenericSig(this ITypeDefOrRef type) { var ts = type as TypeSpec; - return ts == null ? null : ts.TypeSig.RemovePinnedAndModifiers() as GenericSig; + return ts is null ? null : ts.TypeSig.RemovePinnedAndModifiers() as GenericSig; } /// @@ -293,7 +297,7 @@ public static GenericSig TryGetGenericSig(this ITypeDefOrRef type) { /// public static GenericVar TryGetGenericVar(this ITypeDefOrRef type) { var ts = type as TypeSpec; - return ts == null ? null : ts.TypeSig.RemovePinnedAndModifiers() as GenericVar; + return ts is null ? null : ts.TypeSig.RemovePinnedAndModifiers() as GenericVar; } /// @@ -304,7 +308,7 @@ public static GenericVar TryGetGenericVar(this ITypeDefOrRef type) { /// public static GenericMVar TryGetGenericMVar(this ITypeDefOrRef type) { var ts = type as TypeSpec; - return ts == null ? null : ts.TypeSig.RemovePinnedAndModifiers() as GenericMVar; + return ts is null ? null : ts.TypeSig.RemovePinnedAndModifiers() as GenericMVar; } /// @@ -315,7 +319,7 @@ public static GenericMVar TryGetGenericMVar(this ITypeDefOrRef type) { /// public static GenericInstSig TryGetGenericInstSig(this ITypeDefOrRef type) { var ts = type as TypeSpec; - return ts == null ? null : ts.TypeSig.RemovePinnedAndModifiers() as GenericInstSig; + return ts is null ? null : ts.TypeSig.RemovePinnedAndModifiers() as GenericInstSig; } /// @@ -326,7 +330,7 @@ public static GenericInstSig TryGetGenericInstSig(this ITypeDefOrRef type) { /// public static PtrSig TryGetPtrSig(this ITypeDefOrRef type) { var ts = type as TypeSpec; - return ts == null ? null : ts.TypeSig.RemovePinnedAndModifiers() as PtrSig; + return ts is null ? null : ts.TypeSig.RemovePinnedAndModifiers() as PtrSig; } /// @@ -337,7 +341,7 @@ public static PtrSig TryGetPtrSig(this ITypeDefOrRef type) { /// public static ByRefSig TryGetByRefSig(this ITypeDefOrRef type) { var ts = type as TypeSpec; - return ts == null ? null : ts.TypeSig.RemovePinnedAndModifiers() as ByRefSig; + return ts is null ? null : ts.TypeSig.RemovePinnedAndModifiers() as ByRefSig; } /// @@ -348,7 +352,7 @@ public static ByRefSig TryGetByRefSig(this ITypeDefOrRef type) { /// public static ArraySig TryGetArraySig(this ITypeDefOrRef type) { var ts = type as TypeSpec; - return ts == null ? null : ts.TypeSig.RemovePinnedAndModifiers() as ArraySig; + return ts is null ? null : ts.TypeSig.RemovePinnedAndModifiers() as ArraySig; } /// @@ -359,7 +363,7 @@ public static ArraySig TryGetArraySig(this ITypeDefOrRef type) { /// public static SZArraySig TryGetSZArraySig(this ITypeDefOrRef type) { var ts = type as TypeSpec; - return ts == null ? null : ts.TypeSig.RemovePinnedAndModifiers() as SZArraySig; + return ts is null ? null : ts.TypeSig.RemovePinnedAndModifiers() as SZArraySig; } /// @@ -368,19 +372,7 @@ public static SZArraySig TryGetSZArraySig(this ITypeDefOrRef type) { /// /// The type /// The base type or null if there's no base type - public static ITypeDefOrRef GetBaseTypeThrow(this ITypeDefOrRef tdr) { - return tdr.GetBaseType(true); - } - - /// - /// Returns the base type of - /// - /// The type - /// The base type or null if there's no base type, or if - /// we couldn't resolve a - public static ITypeDefOrRef GetBaseType(this ITypeDefOrRef tdr) { - return tdr.GetBaseType(false); - } + public static ITypeDefOrRef GetBaseTypeThrow(this ITypeDefOrRef tdr) => tdr.GetBaseType(true); /// /// Returns the base type of @@ -392,39 +384,33 @@ public static ITypeDefOrRef GetBaseType(this ITypeDefOrRef tdr) { /// The base type or null if there's no base type, or if /// is true and we couldn't resolve /// a - public static ITypeDefOrRef GetBaseType(this ITypeDefOrRef tdr, bool throwOnResolveFailure) { - var td = tdr as TypeDef; - if (td != null) + public static ITypeDefOrRef GetBaseType(this ITypeDefOrRef tdr, bool throwOnResolveFailure = false) { + if (tdr is TypeDef td) return td.BaseType; - var tr = tdr as TypeRef; - if (tr != null) { + if (tdr is TypeRef tr) { td = throwOnResolveFailure ? tr.ResolveThrow() : tr.Resolve(); - return td == null ? null : td.BaseType; + return td?.BaseType; } var ts = tdr as TypeSpec; - if (ts == null) + if (ts is null) return null; var git = ts.TypeSig.ToGenericInstSig(); - if (git != null) { - var genType = git.GenericType; - tdr = genType == null ? null : genType.TypeDefOrRef; - } - else { - var sig = ts.TypeSig.ToTypeDefOrRefSig(); - tdr = sig == null ? null : sig.TypeDefOrRef; - } + if (git is not null) + tdr = git.GenericType?.TypeDefOrRef; + else + tdr = ts.TypeSig.ToTypeDefOrRefSig()?.TypeDefOrRef; td = tdr as TypeDef; - if (td != null) + if (td is not null) return td.BaseType; tr = tdr as TypeRef; - if (tr != null) { + if (tr is not null) { td = throwOnResolveFailure ? tr.ResolveThrow() : tr.Resolve(); - return td == null ? null : td.BaseType; + return td?.BaseType; } return null; @@ -437,24 +423,22 @@ public static ITypeDefOrRef GetBaseType(this ITypeDefOrRef tdr, bool throwOnReso /// A or null if input was null or if we /// couldn't resolve the reference. public static TypeDef ResolveTypeDef(this ITypeDefOrRef tdr) { - var td = tdr as TypeDef; - if (td != null) + if (tdr is TypeDef td) return td; - var tr = tdr as TypeRef; - if (tr != null) + if (tdr is TypeRef tr) return tr.Resolve(); - if (tdr == null) + if (tdr is null) return null; tdr = tdr.ScopeType; td = tdr as TypeDef; - if (td != null) + if (td is not null) return td; tr = tdr as TypeRef; - if (tr != null) + if (tr is not null) return tr.Resolve(); return null; @@ -467,27 +451,25 @@ public static TypeDef ResolveTypeDef(this ITypeDefOrRef tdr) { /// A instance. /// If the type couldn't be resolved public static TypeDef ResolveTypeDefThrow(this ITypeDefOrRef tdr) { - var td = tdr as TypeDef; - if (td != null) + if (tdr is TypeDef td) return td; - var tr = tdr as TypeRef; - if (tr != null) + if (tdr is TypeRef tr) return tr.ResolveThrow(); - if (tdr == null) + if (tdr is null) throw new TypeResolveException("Can't resolve a null pointer"); tdr = tdr.ScopeType; td = tdr as TypeDef; - if (td != null) + if (td is not null) return td; tr = tdr as TypeRef; - if (tr != null) + if (tr is not null) return tr.ResolveThrow(); - throw new TypeResolveException(string.Format("Could not resolve type: {0} ({1})", tdr, tdr == null ? null : tdr.DefinitionAssembly)); + throw new TypeResolveException($"Could not resolve type: {tdr} ({tdr?.DefinitionAssembly})"); } /// @@ -499,12 +481,10 @@ public static TypeDef ResolveTypeDefThrow(this ITypeDefOrRef tdr) { /// null or if it wasn't possible to resolve it (the field doesn't exist or its /// assembly couldn't be loaded) public static FieldDef ResolveFieldDef(this IField field) { - var fd = field as FieldDef; - if (fd != null) + if (field is FieldDef fd) return fd; - var mr = field as MemberRef; - if (mr != null) + if (field is MemberRef mr) return mr.ResolveField(); return null; @@ -517,15 +497,13 @@ public static FieldDef ResolveFieldDef(this IField field) { /// Field to resolve /// The public static FieldDef ResolveFieldDefThrow(this IField field) { - var fd = field as FieldDef; - if (fd != null) + if (field is FieldDef fd) return fd; - var mr = field as MemberRef; - if (mr != null) + if (field is MemberRef mr) return mr.ResolveFieldThrow(); - throw new MemberRefResolveException(string.Format("Could not resolve field: {0}", field)); + throw new MemberRefResolveException($"Could not resolve field: {field}"); } /// @@ -539,22 +517,19 @@ public static FieldDef ResolveFieldDefThrow(this IField field) { /// null or if it wasn't possible to resolve it (the method doesn't exist or its /// assembly couldn't be loaded) public static MethodDef ResolveMethodDef(this IMethod method) { - var md = method as MethodDef; - if (md != null) + if (method is MethodDef md) return md; - var mr = method as MemberRef; - if (mr != null) + if (method is MemberRef mr) return mr.ResolveMethod(); - var ms = method as MethodSpec; - if (ms != null) { + if (method is MethodSpec ms) { md = ms.Method as MethodDef; - if (md != null) + if (md is not null) return md; mr = ms.Method as MemberRef; - if (mr != null) + if (mr is not null) return mr.ResolveMethod(); } @@ -570,26 +545,23 @@ public static MethodDef ResolveMethodDef(this IMethod method) { /// Method to resolve /// The public static MethodDef ResolveMethodDefThrow(this IMethod method) { - var md = method as MethodDef; - if (md != null) + if (method is MethodDef md) return md; - var mr = method as MemberRef; - if (mr != null) + if (method is MemberRef mr) return mr.ResolveMethodThrow(); - var ms = method as MethodSpec; - if (ms != null) { + if (method is MethodSpec ms) { md = ms.Method as MethodDef; - if (md != null) + if (md is not null) return md; mr = ms.Method as MemberRef; - if (mr != null) + if (mr is not null) return mr.ResolveMethodThrow(); } - throw new MemberRefResolveException(string.Format("Could not resolve method: {0}", method)); + throw new MemberRefResolveException($"Could not resolve method: {method}"); } /// @@ -598,27 +570,50 @@ public static MethodDef ResolveMethodDefThrow(this IMethod method) { /// Member reference /// static internal IAssembly GetDefinitionAssembly(this MemberRef mr) { - if (mr == null) + if (mr is null) return null; var parent = mr.Class; - var tdr = parent as ITypeDefOrRef; - if (tdr != null) + if (parent is ITypeDefOrRef tdr) return tdr.DefinitionAssembly; - if (parent is ModuleRef) { - var mod = mr.Module; - return mod == null ? null : mod.Assembly; - } + if (parent is ModuleRef) + return mr.Module?.Assembly; - var md = parent as MethodDef; - if (md != null) { - var declType = md.DeclaringType; - return declType == null ? null : declType.DefinitionAssembly; - } + if (parent is MethodDef md) + return md.DeclaringType?.DefinitionAssembly; return null; } + + /// + /// Gets the normal visible parameters, doesn't include the hidden 'this' parameter + /// + /// this + /// The normal visible parameters + public static IList GetParams(this IMethod method) => method?.MethodSig.GetParams(); + + /// + /// Gets the normal visible parameter count, doesn't include the hidden 'this' parameter + /// + /// this + /// Normal visible parameter count + public static int GetParamCount(this IMethod method) => method?.MethodSig.GetParamCount() ?? 0; + + /// + /// Checks whether any normal visible parameter exists, doesn't include the hidden 'this' parameter + /// + /// this + /// true if there's at least one normal visible parameter + public static bool HasParams(this IMethod method) => method.GetParamCount() > 0; + + /// + /// Gets a normal visible parameter, doesn't include the hidden 'this' parameter + /// + /// this + /// Normal visible parameter index + /// + public static TypeSig GetParam(this IMethod method, int index) => method?.MethodSig.GetParams() is { } types && index >= 0 && index < types.Count ? types[index] : null; } /// @@ -915,7 +910,7 @@ public interface IHasDeclSecurity : ICodedToken, IHasCustomAttribute, IFullName /// /// Gets the permission sets /// - ThreadSafe.IList DeclSecurities { get; } + IList DeclSecurities { get; } /// /// true if is not empty @@ -1006,7 +1001,7 @@ public interface IResolutionScope : ICodedToken, IHasCustomAttribute, IFullName /// /// TypeOrMethodDef coded token interface /// - public interface ITypeOrMethodDef : ICodedToken, IHasCustomAttribute, IHasDeclSecurity, IMemberRefParent, IFullName, IMemberRef, IGenericParameterProvider { + public interface ITypeOrMethodDef : ICodedToken, IHasCustomAttribute, IHasDeclSecurity, IMemberRefParent, IFullName, IMemberRef, IGenericParameterProvider, IMemberDef { /// /// The coded token tag /// @@ -1015,7 +1010,7 @@ public interface ITypeOrMethodDef : ICodedToken, IHasCustomAttribute, IHasDeclSe /// /// Gets the generic parameters /// - ThreadSafe.IList GenericParameters { get; } + IList GenericParameters { get; } /// /// true if is not empty @@ -1023,19 +1018,38 @@ public interface ITypeOrMethodDef : ICodedToken, IHasCustomAttribute, IHasDeclSe bool HasGenericParameters { get; } } + /// + /// HasCustomDebugInformation interface + /// + public interface IHasCustomDebugInformation { + /// + /// The custom debug information tag + /// + int HasCustomDebugInformationTag { get; } + + /// + /// Gets the custom debug infos + /// + IList CustomDebugInfos { get; } + + /// + /// true if is not empty + /// + bool HasCustomDebugInfos { get; } + } + public static partial class Extensions { /// /// Converts a to a /// /// The sig public static ITypeDefOrRef ToTypeDefOrRef(this TypeSig sig) { - if (sig == null) + if (sig is null) return null; - var tdrSig = sig as TypeDefOrRefSig; - if (tdrSig != null) + if (sig is TypeDefOrRefSig tdrSig) return tdrSig.TypeDefOrRef; var module = sig.Module; - if (module == null) + if (module is null) return new TypeSpecUser(sig); return module.UpdateRowId(new TypeSpecUser(sig)); } @@ -1046,33 +1060,30 @@ public static ITypeDefOrRef ToTypeDefOrRef(this TypeSig sig) { /// Type /// internal static bool IsPrimitive(this IType tdr) { - if (tdr == null) + if (tdr is null) + return false; + if (!tdr.DefinitionAssembly.IsCorLib()) return false; - switch (tdr.Name) { - case "Boolean": - case "Char": - case "SByte": - case "Byte": - case "Int16": - case "UInt16": - case "Int32": - case "UInt32": - case "Int64": - case "UInt64": - case "Single": - case "Double": - case "IntPtr": - case "UIntPtr": - break; + switch (tdr.FullName) { + case "System.Boolean": + case "System.Char": + case "System.SByte": + case "System.Byte": + case "System.Int16": + case "System.UInt16": + case "System.Int32": + case "System.UInt32": + case "System.Int64": + case "System.UInt64": + case "System.Single": + case "System.Double": + case "System.IntPtr": + case "System.UIntPtr": + return true; default: return false; } - - if (tdr.Namespace != "System") - return false; - - return tdr.DefinitionAssembly.IsCorLib(); } } } diff --git a/src/DotNet/ICorLibTypes.cs b/src/DotNet/ICorLibTypes.cs index a69739041..ab924df4b 100644 --- a/src/DotNet/ICorLibTypes.cs +++ b/src/DotNet/ICorLibTypes.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// Access to .NET core library's simple types /// @@ -119,17 +119,15 @@ public static partial class Extensions { public static CorLibTypeSig GetCorLibTypeSig(this ICorLibTypes self, ITypeDefOrRef type) { CorLibTypeSig corLibType; - TypeDef td; - if ((td = type as TypeDef) != null && - td.DeclaringType == null && - (corLibType = self.GetCorLibTypeSig(td.Namespace, td.Name, td.DefinitionAssembly)) != null) { + if (type is TypeDef td && + td.DeclaringType is null && + (corLibType = self.GetCorLibTypeSig(td.Namespace, td.Name, td.DefinitionAssembly)) is not null) { return corLibType; } - TypeRef tr; - if ((tr = type as TypeRef) != null && + if (type is TypeRef tr && !(tr.ResolutionScope is TypeRef) && - (corLibType = self.GetCorLibTypeSig(tr.Namespace, tr.Name, tr.DefinitionAssembly)) != null) { + (corLibType = self.GetCorLibTypeSig(tr.Namespace, tr.Name, tr.DefinitionAssembly)) is not null) { return corLibType; } @@ -145,9 +143,8 @@ public static CorLibTypeSig GetCorLibTypeSig(this ICorLibTypes self, ITypeDefOrR /// Name /// Definition assembly /// A or null if it didn't match any primitive type - public static CorLibTypeSig GetCorLibTypeSig(this ICorLibTypes self, UTF8String @namespace, UTF8String name, IAssembly defAsm) { - return self.GetCorLibTypeSig(UTF8String.ToSystemStringOrEmpty(@namespace), UTF8String.ToSystemStringOrEmpty(name), defAsm); - } + public static CorLibTypeSig GetCorLibTypeSig(this ICorLibTypes self, UTF8String @namespace, UTF8String name, IAssembly defAsm) => + self.GetCorLibTypeSig(UTF8String.ToSystemStringOrEmpty(@namespace), UTF8String.ToSystemStringOrEmpty(name), defAsm); /// /// Gets a if and @@ -161,29 +158,29 @@ public static CorLibTypeSig GetCorLibTypeSig(this ICorLibTypes self, UTF8String public static CorLibTypeSig GetCorLibTypeSig(this ICorLibTypes self, string @namespace, string name, IAssembly defAsm) { if (@namespace != "System") return null; - if (defAsm == null || !defAsm.IsCorLib()) + if (defAsm is null || !defAsm.IsCorLib()) return null; - switch (name) { - case "Void": return self.Void; - case "Boolean": return self.Boolean; - case "Char": return self.Char; - case "SByte": return self.SByte; - case "Byte": return self.Byte; - case "Int16": return self.Int16; - case "UInt16": return self.UInt16; - case "Int32": return self.Int32; - case "UInt32": return self.UInt32; - case "Int64": return self.Int64; - case "UInt64": return self.UInt64; - case "Single": return self.Single; - case "Double": return self.Double; - case "String": return self.String; - case "TypedReference": return self.TypedReference; - case "IntPtr": return self.IntPtr; - case "UIntPtr": return self.UIntPtr; - case "Object": return self.Object; - } - return null; + return name switch { + "Void" => self.Void, + "Boolean" => self.Boolean, + "Char" => self.Char, + "SByte" => self.SByte, + "Byte" => self.Byte, + "Int16" => self.Int16, + "UInt16" => self.UInt16, + "Int32" => self.Int32, + "UInt32" => self.UInt32, + "Int64" => self.Int64, + "UInt64" => self.UInt64, + "Single" => self.Single, + "Double" => self.Double, + "String" => self.String, + "TypedReference" => self.TypedReference, + "IntPtr" => self.IntPtr, + "UIntPtr" => self.UIntPtr, + "Object" => self.Object, + _ => null, + }; } } } diff --git a/src/DotNet/ICustomAttribute.cs b/src/DotNet/ICustomAttribute.cs index ac1609ab6..e80b4aa36 100644 --- a/src/DotNet/ICustomAttribute.cs +++ b/src/DotNet/ICustomAttribute.cs @@ -1,13 +1,7 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System.Collections.Generic; -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif - namespace dnlib.DotNet { /// /// Custom attribute interface. Implemented by and @@ -27,7 +21,7 @@ public interface ICustomAttribute { /// /// Gets all named arguments (field and property values) /// - ThreadSafe.IList NamedArguments { get; } + IList NamedArguments { get; } /// /// true if is not empty diff --git a/src/DotNet/IDecrypters.cs b/src/DotNet/IDecrypters.cs index 96e66a5de..dc90367f7 100644 --- a/src/DotNet/IDecrypters.cs +++ b/src/DotNet/IDecrypters.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; +using System.Collections.Generic; using dnlib.PE; using dnlib.DotNet.Emit; diff --git a/src/DotNet/ILogger.cs b/src/DotNet/ILogger.cs index 6ab6b32bb..32952e4f0 100644 --- a/src/DotNet/ILogger.cs +++ b/src/DotNet/ILogger.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Reflection; using dnlib.DotNet.Writer; @@ -65,9 +65,8 @@ public static partial class Extensions { /// this /// Sender or null /// Message - public static void Error(this ILogger logger, object sender, string message) { + public static void Error(this ILogger logger, object sender, string message) => logger.Log(sender, LoggerEvent.Error, "{0}", message); - } /// /// Log an error message @@ -76,9 +75,8 @@ public static void Error(this ILogger logger, object sender, string message) { /// Sender or null /// Message /// Message arg #1 - public static void Error(this ILogger logger, object sender, string message, object arg1) { + public static void Error(this ILogger logger, object sender, string message, object arg1) => logger.Log(sender, LoggerEvent.Error, message, arg1); - } /// /// Log an error message @@ -88,9 +86,8 @@ public static void Error(this ILogger logger, object sender, string message, obj /// Message /// Message arg #1 /// Message arg #2 - public static void Error(this ILogger logger, object sender, string message, object arg1, object arg2) { + public static void Error(this ILogger logger, object sender, string message, object arg1, object arg2) => logger.Log(sender, LoggerEvent.Error, message, arg1, arg2); - } /// /// Log an error message @@ -101,9 +98,8 @@ public static void Error(this ILogger logger, object sender, string message, obj /// Message arg #1 /// Message arg #2 /// Message arg #3 - public static void Error(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) { + public static void Error(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) => logger.Log(sender, LoggerEvent.Error, message, arg1, arg2, arg3); - } /// /// Log an error message @@ -115,9 +111,8 @@ public static void Error(this ILogger logger, object sender, string message, obj /// Message arg #2 /// Message arg #3 /// Message arg #4 - public static void Error(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) { + public static void Error(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) => logger.Log(sender, LoggerEvent.Error, message, arg1, arg2, arg3, arg4); - } /// /// Log an error message @@ -126,9 +121,8 @@ public static void Error(this ILogger logger, object sender, string message, obj /// Sender or null /// Message /// Message arguments - public static void Error(this ILogger logger, object sender, string message, params object[] args) { + public static void Error(this ILogger logger, object sender, string message, params object[] args) => logger.Log(sender, LoggerEvent.Error, message, args); - } /// /// Log a warning message @@ -136,9 +130,8 @@ public static void Error(this ILogger logger, object sender, string message, par /// this /// Sender or null /// Message - public static void Warning(this ILogger logger, object sender, string message) { + public static void Warning(this ILogger logger, object sender, string message) => logger.Log(sender, LoggerEvent.Warning, "{0}", message); - } /// /// Log a warning message @@ -147,9 +140,8 @@ public static void Warning(this ILogger logger, object sender, string message) { /// Sender or null /// Message /// Message arg #1 - public static void Warning(this ILogger logger, object sender, string message, object arg1) { + public static void Warning(this ILogger logger, object sender, string message, object arg1) => logger.Log(sender, LoggerEvent.Warning, message, arg1); - } /// /// Log a warning message @@ -159,9 +151,8 @@ public static void Warning(this ILogger logger, object sender, string message, o /// Message /// Message arg #1 /// Message arg #2 - public static void Warning(this ILogger logger, object sender, string message, object arg1, object arg2) { + public static void Warning(this ILogger logger, object sender, string message, object arg1, object arg2) => logger.Log(sender, LoggerEvent.Warning, message, arg1, arg2); - } /// /// Log a warning message @@ -172,9 +163,7 @@ public static void Warning(this ILogger logger, object sender, string message, o /// Message arg #1 /// Message arg #2 /// Message arg #3 - public static void Warning(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) { - logger.Log(sender, LoggerEvent.Warning, message, arg1, arg2, arg3); - } + public static void Warning(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) => logger.Log(sender, LoggerEvent.Warning, message, arg1, arg2, arg3); /// /// Log a warning message @@ -186,9 +175,8 @@ public static void Warning(this ILogger logger, object sender, string message, o /// Message arg #2 /// Message arg #3 /// Message arg #4 - public static void Warning(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) { + public static void Warning(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) => logger.Log(sender, LoggerEvent.Warning, message, arg1, arg2, arg3, arg4); - } /// /// Log a warning message @@ -197,9 +185,8 @@ public static void Warning(this ILogger logger, object sender, string message, o /// Sender or null /// Message /// Message arguments - public static void Warning(this ILogger logger, object sender, string message, params object[] args) { + public static void Warning(this ILogger logger, object sender, string message, params object[] args) => logger.Log(sender, LoggerEvent.Warning, message, args); - } /// /// Log an info message @@ -207,9 +194,8 @@ public static void Warning(this ILogger logger, object sender, string message, p /// this /// Sender or null /// Message - public static void Info(this ILogger logger, object sender, string message) { + public static void Info(this ILogger logger, object sender, string message) => logger.Log(sender, LoggerEvent.Info, "{0}", message); - } /// /// Log an info message @@ -218,9 +204,8 @@ public static void Info(this ILogger logger, object sender, string message) { /// Sender or null /// Message /// Message arg #1 - public static void Info(this ILogger logger, object sender, string message, object arg1) { + public static void Info(this ILogger logger, object sender, string message, object arg1) => logger.Log(sender, LoggerEvent.Info, message, arg1); - } /// /// Log an info message @@ -230,9 +215,8 @@ public static void Info(this ILogger logger, object sender, string message, obje /// Message /// Message arg #1 /// Message arg #2 - public static void Info(this ILogger logger, object sender, string message, object arg1, object arg2) { + public static void Info(this ILogger logger, object sender, string message, object arg1, object arg2) => logger.Log(sender, LoggerEvent.Info, message, arg1, arg2); - } /// /// Log an info message @@ -243,9 +227,8 @@ public static void Info(this ILogger logger, object sender, string message, obje /// Message arg #1 /// Message arg #2 /// Message arg #3 - public static void Info(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) { + public static void Info(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) => logger.Log(sender, LoggerEvent.Info, message, arg1, arg2, arg3); - } /// /// Log an info message @@ -257,9 +240,8 @@ public static void Info(this ILogger logger, object sender, string message, obje /// Message arg #2 /// Message arg #3 /// Message arg #4 - public static void Info(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) { + public static void Info(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) => logger.Log(sender, LoggerEvent.Info, message, arg1, arg2, arg3, arg4); - } /// /// Log an info message @@ -268,9 +250,8 @@ public static void Info(this ILogger logger, object sender, string message, obje /// Sender or null /// Message /// Message arguments - public static void Info(this ILogger logger, object sender, string message, params object[] args) { + public static void Info(this ILogger logger, object sender, string message, params object[] args) => logger.Log(sender, LoggerEvent.Info, message, args); - } /// /// Log a verbose message @@ -278,9 +259,8 @@ public static void Info(this ILogger logger, object sender, string message, para /// this /// Sender or null /// Message - public static void Verbose(this ILogger logger, object sender, string message) { + public static void Verbose(this ILogger logger, object sender, string message) => logger.Log(sender, LoggerEvent.Verbose, "{0}", message); - } /// /// Log a verbose message @@ -289,9 +269,8 @@ public static void Verbose(this ILogger logger, object sender, string message) { /// Sender or null /// Message /// Message arg #1 - public static void Verbose(this ILogger logger, object sender, string message, object arg1) { + public static void Verbose(this ILogger logger, object sender, string message, object arg1) => logger.Log(sender, LoggerEvent.Verbose, message, arg1); - } /// /// Log a verbose message @@ -301,9 +280,8 @@ public static void Verbose(this ILogger logger, object sender, string message, o /// Message /// Message arg #1 /// Message arg #2 - public static void Verbose(this ILogger logger, object sender, string message, object arg1, object arg2) { + public static void Verbose(this ILogger logger, object sender, string message, object arg1, object arg2) => logger.Log(sender, LoggerEvent.Verbose, message, arg1, arg2); - } /// /// Log a verbose message @@ -314,9 +292,8 @@ public static void Verbose(this ILogger logger, object sender, string message, o /// Message arg #1 /// Message arg #2 /// Message arg #3 - public static void Verbose(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) { + public static void Verbose(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) => logger.Log(sender, LoggerEvent.Verbose, message, arg1, arg2, arg3); - } /// /// Log a verbose message @@ -328,9 +305,8 @@ public static void Verbose(this ILogger logger, object sender, string message, o /// Message arg #2 /// Message arg #3 /// Message arg #4 - public static void Verbose(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) { + public static void Verbose(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) => logger.Log(sender, LoggerEvent.Verbose, message, arg1, arg2, arg3, arg4); - } /// /// Log a verbose message @@ -339,9 +315,8 @@ public static void Verbose(this ILogger logger, object sender, string message, o /// Sender or null /// Message /// Message arguments - public static void Verbose(this ILogger logger, object sender, string message, params object[] args) { + public static void Verbose(this ILogger logger, object sender, string message, params object[] args) => logger.Log(sender, LoggerEvent.Verbose, message, args); - } /// /// Log a very verbose message @@ -349,9 +324,8 @@ public static void Verbose(this ILogger logger, object sender, string message, p /// this /// Sender or null /// Message - public static void VeryVerbose(this ILogger logger, object sender, string message) { + public static void VeryVerbose(this ILogger logger, object sender, string message) => logger.Log(sender, LoggerEvent.VeryVerbose, "{0}", message); - } /// /// Log a very verbose message @@ -360,9 +334,8 @@ public static void VeryVerbose(this ILogger logger, object sender, string messag /// Sender or null /// Message /// Message arg #1 - public static void VeryVerbose(this ILogger logger, object sender, string message, object arg1) { + public static void VeryVerbose(this ILogger logger, object sender, string message, object arg1) => logger.Log(sender, LoggerEvent.VeryVerbose, message, arg1); - } /// /// Log a very verbose message @@ -372,9 +345,8 @@ public static void VeryVerbose(this ILogger logger, object sender, string messag /// Message /// Message arg #1 /// Message arg #2 - public static void VeryVerbose(this ILogger logger, object sender, string message, object arg1, object arg2) { + public static void VeryVerbose(this ILogger logger, object sender, string message, object arg1, object arg2) => logger.Log(sender, LoggerEvent.VeryVerbose, message, arg1, arg2); - } /// /// Log a very verbose message @@ -385,9 +357,8 @@ public static void VeryVerbose(this ILogger logger, object sender, string messag /// Message arg #1 /// Message arg #2 /// Message arg #3 - public static void VeryVerbose(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) { + public static void VeryVerbose(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3) => logger.Log(sender, LoggerEvent.VeryVerbose, message, arg1, arg2, arg3); - } /// /// Log a very verbose message @@ -399,9 +370,8 @@ public static void VeryVerbose(this ILogger logger, object sender, string messag /// Message arg #2 /// Message arg #3 /// Message arg #4 - public static void VeryVerbose(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) { + public static void VeryVerbose(this ILogger logger, object sender, string message, object arg1, object arg2, object arg3, object arg4) => logger.Log(sender, LoggerEvent.VeryVerbose, message, arg1, arg2, arg3, arg4); - } /// /// Log a very verbose message @@ -410,9 +380,8 @@ public static void VeryVerbose(this ILogger logger, object sender, string messag /// Sender or null /// Message /// Message arguments - public static void VeryVerbose(this ILogger logger, object sender, string message, params object[] args) { + public static void VeryVerbose(this ILogger logger, object sender, string message, params object[] args) => logger.Log(sender, LoggerEvent.VeryVerbose, message, args); - } } /// @@ -441,24 +410,24 @@ public sealed class DummyLogger : ILogger { /// errors. It must have a public constructor that takes a as the only /// argument. public DummyLogger(Type exceptionToThrow) { - if (exceptionToThrow != null) { + if (exceptionToThrow is not null) { if (!exceptionToThrow.IsSubclassOf(typeof(Exception))) - throw new ArgumentException(string.Format("Not a System.Exception sub class: {0}", exceptionToThrow.GetType())); + throw new ArgumentException($"Not a System.Exception sub class: {exceptionToThrow.GetType()}"); ctor = exceptionToThrow.GetConstructor(new Type[] { typeof(string) }); - if (ctor == null) - throw new ArgumentException(string.Format("Exception type {0} doesn't have a public constructor that takes a string as the only argument", exceptionToThrow.GetType())); + if (ctor is null) + throw new ArgumentException($"Exception type {exceptionToThrow.GetType()} doesn't have a public constructor that takes a string as the only argument"); } } /// public void Log(object sender, LoggerEvent loggerEvent, string format, params object[] args) { - if (loggerEvent == LoggerEvent.Error && ctor != null) + if (loggerEvent == LoggerEvent.Error && ctor is not null) throw (Exception)ctor.Invoke(new object[] { string.Format(format, args) }); } /// public bool IgnoresEvent(LoggerEvent loggerEvent) { - if (ctor == null) + if (ctor is null) return true; return loggerEvent != LoggerEvent.Error; } diff --git a/src/DotNet/IResolver.cs b/src/DotNet/IResolver.cs index 89e986446..136e2dc6e 100644 --- a/src/DotNet/IResolver.cs +++ b/src/DotNet/IResolver.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// Resolves types, methods, fields /// @@ -40,9 +40,7 @@ public static partial class Extensions { /// this /// The type /// A instance or null if it couldn't be resolved - public static TypeDef Resolve(this ITypeResolver self, TypeRef typeRef) { - return self.Resolve(typeRef, null); - } + public static TypeDef Resolve(this ITypeResolver self, TypeRef typeRef) => self.Resolve(typeRef, null); /// /// Resolves a type @@ -51,9 +49,7 @@ public static TypeDef Resolve(this ITypeResolver self, TypeRef typeRef) { /// The type /// A instance /// If the type couldn't be resolved - public static TypeDef ResolveThrow(this ITypeResolver self, TypeRef typeRef) { - return self.ResolveThrow(typeRef, null); - } + public static TypeDef ResolveThrow(this ITypeResolver self, TypeRef typeRef) => self.ResolveThrow(typeRef, null); /// /// Resolves a type @@ -65,9 +61,9 @@ public static TypeDef ResolveThrow(this ITypeResolver self, TypeRef typeRef) { /// If the type couldn't be resolved public static TypeDef ResolveThrow(this ITypeResolver self, TypeRef typeRef, ModuleDef sourceModule) { var type = self.Resolve(typeRef, sourceModule); - if (type != null) + if (type is not null) return type; - throw new TypeResolveException(string.Format("Could not resolve type: {0} ({1})", typeRef, typeRef == null ? null : typeRef.DefinitionAssembly)); + throw new TypeResolveException($"Could not resolve type: {typeRef} ({typeRef?.DefinitionAssembly})"); } /// @@ -79,9 +75,9 @@ public static TypeDef ResolveThrow(this ITypeResolver self, TypeRef typeRef, Mod /// If the method/field couldn't be resolved public static IMemberForwarded ResolveThrow(this IMemberRefResolver self, MemberRef memberRef) { var memberDef = self.Resolve(memberRef); - if (memberDef != null) + if (memberDef is not null) return memberDef; - throw new MemberRefResolveException(string.Format("Could not resolve method/field: {0} ({1})", memberRef, memberRef == null ? null : memberRef.GetDefinitionAssembly())); + throw new MemberRefResolveException($"Could not resolve method/field: {memberRef} ({memberRef?.GetDefinitionAssembly()})"); } /// @@ -90,9 +86,7 @@ public static IMemberForwarded ResolveThrow(this IMemberRefResolver self, Member /// this /// A field reference /// A instance or null if it couldn't be resolved. - public static FieldDef ResolveField(this IMemberRefResolver self, MemberRef memberRef) { - return self.Resolve(memberRef) as FieldDef; - } + public static FieldDef ResolveField(this IMemberRefResolver self, MemberRef memberRef) => self.Resolve(memberRef) as FieldDef; /// /// Resolves a field @@ -102,10 +96,9 @@ public static FieldDef ResolveField(this IMemberRefResolver self, MemberRef memb /// A instance or null if it couldn't be resolved. /// If the field couldn't be resolved public static FieldDef ResolveFieldThrow(this IMemberRefResolver self, MemberRef memberRef) { - var field = self.Resolve(memberRef) as FieldDef; - if (field != null) + if (self.Resolve(memberRef) is FieldDef field) return field; - throw new MemberRefResolveException(string.Format("Could not resolve field: {0} ({1})", memberRef, memberRef == null ? null : memberRef.GetDefinitionAssembly())); + throw new MemberRefResolveException($"Could not resolve field: {memberRef} ({memberRef?.GetDefinitionAssembly()})"); } /// @@ -114,9 +107,7 @@ public static FieldDef ResolveFieldThrow(this IMemberRefResolver self, MemberRef /// this /// A method reference /// A instance or null if it couldn't be resolved. - public static MethodDef ResolveMethod(this IMemberRefResolver self, MemberRef memberRef) { - return self.Resolve(memberRef) as MethodDef; - } + public static MethodDef ResolveMethod(this IMemberRefResolver self, MemberRef memberRef) => self.Resolve(memberRef) as MethodDef; /// /// Resolves a method @@ -126,10 +117,9 @@ public static MethodDef ResolveMethod(this IMemberRefResolver self, MemberRef me /// A instance or null if it couldn't be resolved. /// If the method couldn't be resolved public static MethodDef ResolveMethodThrow(this IMemberRefResolver self, MemberRef memberRef) { - var method = self.Resolve(memberRef) as MethodDef; - if (method != null) + if (self.Resolve(memberRef) is MethodDef method) return method; - throw new MemberRefResolveException(string.Format("Could not resolve method: {0} ({1})", memberRef, memberRef == null ? null : memberRef.GetDefinitionAssembly())); + throw new MemberRefResolveException($"Could not resolve method: {memberRef} ({memberRef?.GetDefinitionAssembly()})"); } } } diff --git a/src/DotNet/ITokenResolver.cs b/src/DotNet/ITokenResolver.cs index 2d3e0c834..495aaa3cd 100644 --- a/src/DotNet/ITokenResolver.cs +++ b/src/DotNet/ITokenResolver.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info namespace dnlib.DotNet { /// @@ -21,8 +21,6 @@ public static partial class Extensions { /// This /// The metadata token /// A or null if is invalid - public static IMDTokenProvider ResolveToken(this ITokenResolver self, uint token) { - return self.ResolveToken(token, new GenericParamContext()); - } + public static IMDTokenProvider ResolveToken(this ITokenResolver self, uint token) => self.ResolveToken(token, new GenericParamContext()); } } diff --git a/src/DotNet/IType.cs b/src/DotNet/IType.cs index 03cd92424..7a1dda756 100644 --- a/src/DotNet/IType.cs +++ b/src/DotNet/IType.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// Interface to get the full name of a type /// @@ -80,6 +80,9 @@ public interface IContainsGenericParameter { /// bool ContainsGenericParameter { get; } } + interface IContainsGenericParameter2 { + bool ContainsGenericParameter { get; } + } public static partial class Extensions { /// @@ -89,15 +92,15 @@ public static partial class Extensions { /// this /// The scope type public static ITypeDefOrRef GetNonNestedTypeRefScope(this IType type) { - if (type == null) + if (type is null) return null; var scopeType = type.ScopeType; var tr = scopeType as TypeRef; - if (tr == null) + if (tr is null) return scopeType; for (int i = 0; i < 100; i++) { var dt = tr.ResolutionScope as TypeRef; - if (dt == null) + if (dt is null) return tr; tr = dt; } diff --git a/src/DotNet/ITypeDefFinder.cs b/src/DotNet/ITypeDefFinder.cs index 0a232b1a6..ab0b69ca2 100644 --- a/src/DotNet/ITypeDefFinder.cs +++ b/src/DotNet/ITypeDefFinder.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// Finds s /// @@ -35,9 +35,9 @@ public static partial class Extensions { /// If type couldn't be found public static TypeDef FindThrow(this ITypeDefFinder self, TypeRef typeRef) { var type = self.Find(typeRef); - if (type != null) + if (type is not null) return type; - throw new TypeResolveException(string.Format("Could not find type: {0}", typeRef)); + throw new TypeResolveException($"Could not find type: {typeRef}"); } /// @@ -52,9 +52,9 @@ public static TypeDef FindThrow(this ITypeDefFinder self, TypeRef typeRef) { /// If type couldn't be found public static TypeDef FindThrow(this ITypeDefFinder self, string fullName, bool isReflectionName) { var type = self.Find(fullName, isReflectionName); - if (type != null) + if (type is not null) return type; - throw new TypeResolveException(string.Format("Could not find type: {0}", fullName)); + throw new TypeResolveException($"Could not find type: {fullName}"); } /// @@ -63,9 +63,7 @@ public static TypeDef FindThrow(this ITypeDefFinder self, string fullName, bool /// this /// Full name of the type (no assembly information). Nested types are separated by / /// An existing or null if it wasn't found. - public static TypeDef FindNormal(this ITypeDefFinder self, string fullName) { - return self.Find(fullName, false); - } + public static TypeDef FindNormal(this ITypeDefFinder self, string fullName) => self.Find(fullName, false); /// /// Finds a @@ -76,9 +74,9 @@ public static TypeDef FindNormal(this ITypeDefFinder self, string fullName) { /// If type couldn't be found public static TypeDef FindNormalThrow(this ITypeDefFinder self, string fullName) { var type = self.Find(fullName, false); - if (type != null) + if (type is not null) return type; - throw new TypeResolveException(string.Format("Could not find type: {0}", fullName)); + throw new TypeResolveException($"Could not find type: {fullName}"); } /// @@ -87,9 +85,7 @@ public static TypeDef FindNormalThrow(this ITypeDefFinder self, string fullName) /// this /// Full name of the type (no assembly information). Nested types are separated by + /// An existing or null if it wasn't found. - public static TypeDef FindReflection(this ITypeDefFinder self, string fullName) { - return self.Find(fullName, true); - } + public static TypeDef FindReflection(this ITypeDefFinder self, string fullName) => self.Find(fullName, true); /// /// Finds a @@ -100,9 +96,9 @@ public static TypeDef FindReflection(this ITypeDefFinder self, string fullName) /// If type couldn't be found public static TypeDef FindReflectionThrow(this ITypeDefFinder self, string fullName) { var type = self.Find(fullName, true); - if (type != null) + if (type is not null) return type; - throw new TypeResolveException(string.Format("Could not find type: {0}", fullName)); + throw new TypeResolveException($"Could not find type: {fullName}"); } /// @@ -112,9 +108,7 @@ public static TypeDef FindReflectionThrow(this ITypeDefFinder self, string fullN /// this /// The type ref /// true if the exists, false otherwise - public static bool TypeExists(this ITypeDefFinder self, TypeRef typeRef) { - return self.Find(typeRef) != null; - } + public static bool TypeExists(this ITypeDefFinder self, TypeRef typeRef) => self.Find(typeRef) is not null; /// /// Checks whether a exists @@ -125,9 +119,7 @@ public static bool TypeExists(this ITypeDefFinder self, TypeRef typeRef) { /// type names are separated by a + character. If false, nested type names /// are separated by a / character. /// true if the exists, false otherwise - public static bool TypeExists(this ITypeDefFinder self, string fullName, bool isReflectionName) { - return self.Find(fullName, isReflectionName) != null; - } + public static bool TypeExists(this ITypeDefFinder self, string fullName, bool isReflectionName) => self.Find(fullName, isReflectionName) is not null; /// /// Checks whether a exists @@ -135,9 +127,7 @@ public static bool TypeExists(this ITypeDefFinder self, string fullName, bool is /// this /// Full name of the type (no assembly information). Nested types are separated by / /// true if the exists, false otherwise - public static bool TypeExistsNormal(this ITypeDefFinder self, string fullName) { - return self.Find(fullName, false) != null; - } + public static bool TypeExistsNormal(this ITypeDefFinder self, string fullName) => self.Find(fullName, false) is not null; /// /// Checks whether a exists @@ -145,8 +135,6 @@ public static bool TypeExistsNormal(this ITypeDefFinder self, string fullName) { /// this /// Full name of the type (no assembly information). Nested types are separated by + /// true if the exists, false otherwise - public static bool TypeExistsReflection(this ITypeDefFinder self, string fullName) { - return self.Find(fullName, true) != null; - } + public static bool TypeExistsReflection(this ITypeDefFinder self, string fullName) => self.Find(fullName, true) is not null; } } diff --git a/src/DotNet/IVariable.cs b/src/DotNet/IVariable.cs index a80d9d11e..0cb4fb1e3 100644 --- a/src/DotNet/IVariable.cs +++ b/src/DotNet/IVariable.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// Interface to access a local or a parameter /// diff --git a/src/DotNet/ImplMap.cs b/src/DotNet/ImplMap.cs index 60d24926b..480ec7a00 100644 --- a/src/DotNet/ImplMap.cs +++ b/src/DotNet/ImplMap.cs @@ -2,7 +2,8 @@ using System; using System.Diagnostics; -using System.Threading; +using System.IO; +using System.Runtime.InteropServices; using dnlib.DotNet.MD; namespace dnlib.DotNet { @@ -17,22 +18,20 @@ public abstract class ImplMap : IMDTokenProvider { protected uint rid; /// - public MDToken MDToken { - get { return new MDToken(Table.ImplMap, rid); } - } + public MDToken MDToken => new MDToken(Table.ImplMap, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// /// From column ImplMap.MappingFlags /// public PInvokeAttributes Attributes { - get { return (PInvokeAttributes)attributes; } - set { attributes = (int)value; } + get => (PInvokeAttributes)attributes; + set => attributes = (int)value; } /// Attributes protected int attributes; @@ -41,8 +40,8 @@ public PInvokeAttributes Attributes { /// From column ImplMap.ImportName /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -51,8 +50,8 @@ public UTF8String Name { /// From column ImplMap.ImportScope /// public ModuleRef Module { - get { return module; } - set { module = value; } + get => module; + set => module = value; } /// protected ModuleRef module; @@ -63,17 +62,8 @@ public ModuleRef Module { /// /// Value to AND /// Value to OR - void ModifyAttributes(PInvokeAttributes andMask, PInvokeAttributes orMask) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - newVal = (origVal & (int)andMask) | (int)orMask; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else + void ModifyAttributes(PInvokeAttributes andMask, PInvokeAttributes orMask) => attributes = (attributes & (int)andMask) | (int)orMask; -#endif - } /// /// Set or clear flags in @@ -82,175 +72,134 @@ void ModifyAttributes(PInvokeAttributes andMask, PInvokeAttributes orMask) { /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, PInvokeAttributes flags) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else if (set) attributes |= (int)flags; else attributes &= ~(int)flags; -#endif } /// /// Gets/sets the bit /// public bool IsNoMangle { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.NoMangle) != 0; } - set { ModifyAttributes(value, PInvokeAttributes.NoMangle); } + get => ((PInvokeAttributes)attributes & PInvokeAttributes.NoMangle) != 0; + set => ModifyAttributes(value, PInvokeAttributes.NoMangle); } /// /// Gets/sets the char set /// public PInvokeAttributes CharSet { - get { return (PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask; } - set { ModifyAttributes(~PInvokeAttributes.CharSetMask, value & PInvokeAttributes.CharSetMask); } + get => (PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask; + set => ModifyAttributes(~PInvokeAttributes.CharSetMask, value & PInvokeAttributes.CharSetMask); } /// /// true if is set /// - public bool IsCharSetNotSpec { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetNotSpec; } - } + public bool IsCharSetNotSpec => ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetNotSpec; /// /// true if is set /// - public bool IsCharSetAnsi { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetAnsi; } - } + public bool IsCharSetAnsi => ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetAnsi; /// /// true if is set /// - public bool IsCharSetUnicode { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetUnicode; } - } + public bool IsCharSetUnicode => ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetUnicode; /// /// true if is set /// - public bool IsCharSetAuto { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetAuto; } - } + public bool IsCharSetAuto => ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetAuto; /// /// Gets/sets best fit /// public PInvokeAttributes BestFit { - get { return (PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask; } - set { ModifyAttributes(~PInvokeAttributes.BestFitMask, value & PInvokeAttributes.BestFitMask); } + get => (PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask; + set => ModifyAttributes(~PInvokeAttributes.BestFitMask, value & PInvokeAttributes.BestFitMask); } /// /// true if is set /// - public bool IsBestFitUseAssem { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitUseAssem; } - } + public bool IsBestFitUseAssem => ((PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitUseAssem; /// /// true if is set /// - public bool IsBestFitEnabled { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitEnabled; } - } + public bool IsBestFitEnabled => ((PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitEnabled; /// /// true if is set /// - public bool IsBestFitDisabled { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitDisabled; } - } + public bool IsBestFitDisabled => ((PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitDisabled; /// /// Gets/sets throw on unmappable char /// public PInvokeAttributes ThrowOnUnmappableChar { - get { return (PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask; } - set { ModifyAttributes(~PInvokeAttributes.ThrowOnUnmappableCharMask, value & PInvokeAttributes.ThrowOnUnmappableCharMask); } + get => (PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask; + set => ModifyAttributes(~PInvokeAttributes.ThrowOnUnmappableCharMask, value & PInvokeAttributes.ThrowOnUnmappableCharMask); } /// /// true if is set /// - public bool IsThrowOnUnmappableCharUseAssem { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharUseAssem; } - } + public bool IsThrowOnUnmappableCharUseAssem => ((PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharUseAssem; /// /// true if is set /// - public bool IsThrowOnUnmappableCharEnabled { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharEnabled; } - } + public bool IsThrowOnUnmappableCharEnabled => ((PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharEnabled; /// /// true if is set /// - public bool IsThrowOnUnmappableCharDisabled { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharDisabled; } - } + public bool IsThrowOnUnmappableCharDisabled => ((PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharDisabled; /// /// Gets/sets the bit /// public bool SupportsLastError { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.SupportsLastError) != 0; } - set { ModifyAttributes(value, PInvokeAttributes.SupportsLastError); } + get => ((PInvokeAttributes)attributes & PInvokeAttributes.SupportsLastError) != 0; + set => ModifyAttributes(value, PInvokeAttributes.SupportsLastError); } /// /// Gets/sets calling convention /// public PInvokeAttributes CallConv { - get { return (PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask; } - set { ModifyAttributes(~PInvokeAttributes.CallConvMask, value & PInvokeAttributes.CallConvMask); } + get => (PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask; + set => ModifyAttributes(~PInvokeAttributes.CallConvMask, value & PInvokeAttributes.CallConvMask); } /// /// true if is set /// - public bool IsCallConvWinapi { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvWinapi; } - } + public bool IsCallConvWinapi => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvWinapi; /// /// true if is set /// - public bool IsCallConvCdecl { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvCdecl; } - } + public bool IsCallConvCdecl => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvCdecl; /// /// true if is set /// - public bool IsCallConvStdcall { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvStdcall; } - } + public bool IsCallConvStdcall => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvStdcall; /// /// true if is set /// - public bool IsCallConvThiscall { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvThiscall; } - } + public bool IsCallConvThiscall => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvThiscall; /// /// true if is set /// - public bool IsCallConvFastcall { - get { return ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvFastcall; } - } + public bool IsCallConvFastcall => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvFastcall; /// /// Checks whether this is a certain P/Invoke method @@ -258,20 +207,40 @@ public bool IsCallConvFastcall { /// Name of the DLL /// Name of the function within the DLL /// true if it's the specified P/Invoke method, else false - public bool IsPinvokeMethod(string dllName, string funcName) { + public bool IsPinvokeMethod(string dllName, string funcName) => IsPinvokeMethod(dllName, funcName, IsWindows()); + + /// + /// Checks whether this is a certain P/Invoke method + /// + /// Name of the DLL + /// Name of the function within the DLL + /// Treat as Windows + /// true if it's the specified P/Invoke method, else false + public bool IsPinvokeMethod(string dllName, string funcName, bool treatAsWindows) { if (name != funcName) return false; var mod = module; - if (mod == null) + if (mod is null) return false; - return GetDllName(dllName).Equals(GetDllName(mod.Name), StringComparison.OrdinalIgnoreCase); + return GetDllName(dllName, treatAsWindows).Equals(GetDllName(mod.Name, treatAsWindows), StringComparison.OrdinalIgnoreCase); } - static string GetDllName(string dllName) { + static string GetDllName(string dllName, bool treatAsWindows) { + if (treatAsWindows) + dllName = dllName.TrimEnd(trimChars); if (dllName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) return dllName.Substring(0, dllName.Length - 4); return dllName; } + + static bool IsWindows() => +#if NETSTANDARD || NETCOREAPP + RuntimeInformation.IsOSPlatform(OSPlatform.Windows); +#else + Path.DirectorySeparatorChar == '\\' || Path.AltDirectorySeparatorChar == '\\'; +#endif + + static readonly char[] trimChars = { ' ' }; } /// @@ -291,9 +260,9 @@ public ImplMapUser() { /// Name /// Flags public ImplMapUser(ModuleRef scope, UTF8String name, PInvokeAttributes flags) { - this.module = scope; + module = scope; this.name = name; - this.attributes = (int)flags; + attributes = (int)flags; } } @@ -301,15 +270,10 @@ public ImplMapUser(ModuleRef scope, UTF8String name, PInvokeAttributes flags) { /// Created from a row in the ImplMap table /// sealed class ImplMapMD : ImplMap, IMDTokenProviderMD { - /// The module where this instance is located - readonly ModuleDefMD readerModule; - readonly uint origRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// /// Constructor @@ -320,18 +284,18 @@ public uint OrigRid { /// If is invalid public ImplMapMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.ImplMapTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("ImplMap rid {0} does not exist", rid)); + throw new BadImageFormatException($"ImplMap rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; - this.readerModule = readerModule; - uint name; - uint scope = readerModule.TablesStream.ReadImplMapRow(origRid, out this.attributes, out name); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.module = readerModule.ResolveModuleRef(scope); + bool b = readerModule.TablesStream.TryReadImplMapRow(origRid, out var row); + Debug.Assert(b); + attributes = row.MappingFlags; + name = readerModule.StringsStream.ReadNoNull(row.ImportName); + module = readerModule.ResolveModuleRef(row.ImportScope); } } } diff --git a/src/DotNet/Importer.cs b/src/DotNet/Importer.cs index be0585d99..de37e66b6 100644 --- a/src/DotNet/Importer.cs +++ b/src/DotNet/Importer.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Reflection; -using dnlib.Threading; namespace dnlib.DotNet { /// @@ -38,6 +37,11 @@ public enum ImporterOptions { /// TryToUseDefs = TryToUseTypeDefs | TryToUseMethodDefs | TryToUseFieldDefs, + /// + /// Use already existing s whenever possible + /// + TryToUseExistingAssemblyRefs = 8, + /// /// Don't set this flag. For internal use only. /// @@ -45,59 +49,64 @@ public enum ImporterOptions { } /// - /// Imports s, s, s - /// and s as references + /// Re-maps entities that were renamed in the target module /// - public struct Importer { - readonly ModuleDef module; - readonly GenericParamContext gpContext; - RecursionCounter recursionCounter; - ImporterOptions options; + public abstract class ImportMapper { + /// + /// Matches source to the one that is already present in the target module under a different name. + /// + /// referenced by the entity that is being imported. + /// matching or null if there's no match. + public virtual ITypeDefOrRef Map(ITypeDefOrRef source) => null; /// - /// Gets/sets the bit + /// Matches source to the one that is already present in the target module under a different name. /// - public bool TryToUseTypeDefs { - get { return (options & ImporterOptions.TryToUseTypeDefs) != 0; } - set { - if (value) - options |= ImporterOptions.TryToUseTypeDefs; - else - options &= ~ImporterOptions.TryToUseTypeDefs; - } - } + /// referenced by the entity that is being imported. + /// matching or null if there's no match. + public virtual IField Map(FieldDef source) => null; /// - /// Gets/sets the bit + /// Matches source to the one that is already present in the target module under a different name. /// - public bool TryToUseMethodDefs { - get { return (options & ImporterOptions.TryToUseMethodDefs) != 0; } - set { - if (value) - options |= ImporterOptions.TryToUseMethodDefs; - else - options &= ~ImporterOptions.TryToUseMethodDefs; - } - } + /// referenced by the entity that is being imported. + /// matching or null if there's no match. + public virtual IMethod Map(MethodDef source) => null; /// - /// Gets/sets the bit + /// Matches source to the one that is already present in the target module under a different name. /// - public bool TryToUseFieldDefs { - get { return (options & ImporterOptions.TryToUseFieldDefs) != 0; } - set { - if (value) - options |= ImporterOptions.TryToUseFieldDefs; - else - options &= ~ImporterOptions.TryToUseFieldDefs; - } - } + /// referenced by the entity that is being imported. + /// matching or null if there's no match. + public virtual MemberRef Map(MemberRef source) => null; /// - /// Gets/sets the bit + /// Overrides default behavior of + /// May be used to use reference assemblies for resolution, for example. /// + /// to create for. is non-generic type or generic type without generic arguments. + /// or null to use default 's type resolution + public virtual ITypeDefOrRef Map(Type source) => null; + } + + /// + /// Imports s, s, s + /// and s as references + /// + public struct Importer { + readonly ModuleDef module; + internal readonly GenericParamContext gpContext; + readonly ImportMapper mapper; + RecursionCounter recursionCounter; + ImporterOptions options; + + bool TryToUseTypeDefs => (options & ImporterOptions.TryToUseTypeDefs) != 0; + bool TryToUseMethodDefs => (options & ImporterOptions.TryToUseMethodDefs) != 0; + bool TryToUseFieldDefs => (options & ImporterOptions.TryToUseFieldDefs) != 0; + bool TryToUseExistingAssemblyRefs => (options & ImporterOptions.TryToUseExistingAssemblyRefs) != 0; + bool FixSignature { - get { return (options & ImporterOptions.FixSignature) != 0; } + get => (options & ImporterOptions.FixSignature) != 0; set { if (value) options |= ImporterOptions.FixSignature; @@ -111,7 +120,7 @@ bool FixSignature { /// /// The module that will own all references public Importer(ModuleDef module) - : this(module, 0, new GenericParamContext()) { + : this(module, 0, new GenericParamContext(), null) { } /// @@ -120,7 +129,7 @@ public Importer(ModuleDef module) /// The module that will own all references /// Generic parameter context public Importer(ModuleDef module, GenericParamContext gpContext) - : this(module, 0, gpContext) { + : this(module, 0, gpContext, null) { } /// @@ -129,7 +138,17 @@ public Importer(ModuleDef module, GenericParamContext gpContext) /// The module that will own all references /// Importer options public Importer(ModuleDef module, ImporterOptions options) - : this(module, options, new GenericParamContext()) { + : this(module, options, new GenericParamContext(), null) { + } + + /// + /// Constructor + /// + /// The module that will own all references + /// Importer options + /// Generic parameter context + public Importer(ModuleDef module, ImporterOptions options, GenericParamContext gpContext) + : this(module, options, gpContext, null) { } /// @@ -138,21 +157,29 @@ public Importer(ModuleDef module, ImporterOptions options) /// The module that will own all references /// Importer options /// Generic parameter context - public Importer(ModuleDef module, ImporterOptions options, GenericParamContext gpContext) { + /// Mapper for renamed entities + public Importer(ModuleDef module, ImporterOptions options, GenericParamContext gpContext, ImportMapper mapper) { this.module = module; - this.recursionCounter = new RecursionCounter(); + recursionCounter = new RecursionCounter(); this.options = options; this.gpContext = gpContext; + this.mapper = mapper; } /// - /// Imports a as a + /// Imports a as a . /// /// The type /// The imported type or null if is invalid - public ITypeDefOrRef Import(Type type) { - return module.UpdateRowId(ImportAsTypeSig(type).ToTypeDefOrRef()); - } + public ITypeDefOrRef Import(Type type) => module.UpdateRowId(ImportAsTypeSig(type).ToTypeDefOrRef()); + + /// + /// Imports a as a . See also + /// + /// The type + /// + [Obsolete("Use 'Import(Type)' instead.")] + public ITypeDefOrRef ImportDeclaringType(Type type) => Import(type); /// /// Imports a as a @@ -161,23 +188,21 @@ public ITypeDefOrRef Import(Type type) { /// A list of all required modifiers or null /// A list of all optional modifiers or null /// The imported type or null if is invalid - public ITypeDefOrRef Import(Type type, IList requiredModifiers, IList optionalModifiers) { - return module.UpdateRowId(ImportAsTypeSig(type, requiredModifiers, optionalModifiers).ToTypeDefOrRef()); - } + public ITypeDefOrRef Import(Type type, IList requiredModifiers, IList optionalModifiers) => + module.UpdateRowId(ImportAsTypeSig(type, requiredModifiers, optionalModifiers).ToTypeDefOrRef()); /// /// Imports a as a /// /// The type /// The imported type or null if is invalid - public TypeSig ImportAsTypeSig(Type type) { - return ImportAsTypeSig(type, false); - } + public TypeSig ImportAsTypeSig(Type type) => ImportAsTypeSig(type, null, false); - TypeSig ImportAsTypeSig(Type type, bool treatAsGenericInst) { - if (type == null) + TypeSig ImportAsTypeSig(Type type, Type declaringType, bool? treatAsGenericInst = null) { + if (type is null) return null; - switch (treatAsGenericInst ? ElementType.GenericInst : type.GetElementType2()) { + bool treatAsGenericInst2 = treatAsGenericInst ?? declaringType.MustTreatTypeAsGenericInstType(type); + switch (treatAsGenericInst2 ? ElementType.GenericInst : type.GetElementType2()) { case ElementType.Void: return module.CorLibTypes.Void; case ElementType.Boolean: return module.CorLibTypes.Boolean; case ElementType.Char: return module.CorLibTypes.Char; @@ -195,11 +220,11 @@ TypeSig ImportAsTypeSig(Type type, bool treatAsGenericInst) { case ElementType.TypedByRef:return module.CorLibTypes.TypedReference; case ElementType.U: return module.CorLibTypes.UIntPtr; case ElementType.Object: return module.CorLibTypes.Object; - case ElementType.Ptr: return new PtrSig(ImportAsTypeSig(type.GetElementType(), treatAsGenericInst)); - case ElementType.ByRef: return new ByRefSig(ImportAsTypeSig(type.GetElementType(), treatAsGenericInst)); - case ElementType.SZArray: return new SZArraySig(ImportAsTypeSig(type.GetElementType(), treatAsGenericInst)); - case ElementType.ValueType: return new ValueTypeSig(CreateTypeRef(type)); - case ElementType.Class: return new ClassSig(CreateTypeRef(type)); + case ElementType.Ptr: return new PtrSig(ImportAsTypeSig(type.GetElementType(), declaringType)); + case ElementType.ByRef: return new ByRefSig(ImportAsTypeSig(type.GetElementType(), declaringType)); + case ElementType.SZArray: return new SZArraySig(ImportAsTypeSig(type.GetElementType(), declaringType)); + case ElementType.ValueType: return new ValueTypeSig(CreateTypeDefOrRef(type)); + case ElementType.Class: return new ClassSig(CreateTypeDefOrRef(type)); case ElementType.Var: return new GenericVar((uint)type.GenericParameterPosition, gpContext.Type); case ElementType.MVar: return new GenericMVar((uint)type.GenericParameterPosition, gpContext.Method); @@ -208,14 +233,17 @@ TypeSig ImportAsTypeSig(Type type, bool treatAsGenericInst) { return module.CorLibTypes.IntPtr; case ElementType.Array: - FixSignature = true; // We don't know sizes and lower bounds - return new ArraySig(ImportAsTypeSig(type.GetElementType(), treatAsGenericInst), (uint)type.GetArrayRank()); + // We don't know sizes and lower bounds. Assume it's `0..` + var lowerBounds = new int[type.GetArrayRank()]; + var sizes = Array2.Empty(); + FixSignature = true; + return new ArraySig(ImportAsTypeSig(type.GetElementType(), declaringType), (uint)type.GetArrayRank(), sizes, lowerBounds); case ElementType.GenericInst: var typeGenArgs = type.GetGenericArguments(); - var git = new GenericInstSig(ImportAsTypeSig(type.GetGenericTypeDefinition()) as ClassOrValueTypeSig, (uint)typeGenArgs.Length); + var git = new GenericInstSig(ImportAsTypeSig(type.GetGenericTypeDefinition(), null, false) as ClassOrValueTypeSig, (uint)typeGenArgs.Length); foreach (var ga in typeGenArgs) - git.GenericArguments.Add(ImportAsTypeSig(ga)); + git.GenericArguments.Add(ImportAsTypeSig(ga, declaringType)); return git; case ElementType.Sentinel: @@ -234,28 +262,28 @@ TypeSig ImportAsTypeSig(Type type, bool treatAsGenericInst) { } ITypeDefOrRef TryResolve(TypeRef tr) { - if (!TryToUseTypeDefs || tr == null) + if (!TryToUseTypeDefs || tr is null) return tr; if (!IsThisModule(tr)) return tr; var td = tr.Resolve(); - if (td == null || td.Module != module) + if (td is null || td.Module != module) return tr; return td; } IMethodDefOrRef TryResolveMethod(IMethodDefOrRef mdr) { - if (!TryToUseMethodDefs || mdr == null) + if (!TryToUseMethodDefs || mdr is null) return mdr; var mr = mdr as MemberRef; - if (mr == null) + if (mr is null) return mdr; if (!mr.IsMethodRef) return mr; var declType = GetDeclaringType(mr); - if (declType == null) + if (declType is null) return mr; if (declType.Module != module) return mr; @@ -263,14 +291,14 @@ IMethodDefOrRef TryResolveMethod(IMethodDefOrRef mdr) { } IField TryResolveField(MemberRef mr) { - if (!TryToUseFieldDefs || mr == null) + if (!TryToUseFieldDefs || mr is null) return mr; if (!mr.IsFieldRef) return mr; var declType = GetDeclaringType(mr); - if (declType == null) + if (declType is null) return mr; if (declType.Module != module) return mr; @@ -278,15 +306,14 @@ IField TryResolveField(MemberRef mr) { } TypeDef GetDeclaringType(MemberRef mr) { - if (mr == null) + if (mr is null) return null; - var td = mr.Class as TypeDef; - if (td != null) + if (mr.Class is TypeDef td) return td; td = TryResolve(mr.Class as TypeRef) as TypeDef; - if (td != null) + if (td is not null) return td; var modRef = mr.Class as ModuleRef; @@ -297,33 +324,31 @@ TypeDef GetDeclaringType(MemberRef mr) { } bool IsThisModule(TypeRef tr) { - if (tr == null) + if (tr is null) return false; - var scopeType = tr.ScopeType.GetNonNestedTypeRefScope() as TypeRef; - if (scopeType == null) + var scopeType = tr.GetNonNestedTypeRefScope() as TypeRef; + if (scopeType is null) return false; if (module == scopeType.ResolutionScope) return true; - var modRef = scopeType.ResolutionScope as ModuleRef; - if (modRef != null) + if (scopeType.ResolutionScope is ModuleRef modRef) return IsThisModule(modRef); var asmRef = scopeType.ResolutionScope as AssemblyRef; return Equals(module.Assembly, asmRef); } - bool IsThisModule(ModuleRef modRef) { - return modRef != null && - module.Name == modRef.Name && - Equals(module.Assembly, modRef.DefinitionAssembly); - } + bool IsThisModule(ModuleRef modRef) => + modRef is not null && + module.Name == modRef.Name && + Equals(module.Assembly, modRef.DefinitionAssembly); static bool Equals(IAssembly a, IAssembly b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; return Utils.Equals(a.Version, b.Version) && PublicKeyBase.TokenEquals(a.PublicKeyOrToken, b.PublicKeyOrToken) && @@ -331,22 +356,34 @@ static bool Equals(IAssembly a, IAssembly b) { UTF8String.CaseInsensitiveEquals(a.Culture, b.Culture); } - ITypeDefOrRef CreateTypeRef(Type type) { - return TryResolve(CreateTypeRef2(type)); + ITypeDefOrRef CreateTypeDefOrRef(Type type) { + var tdr = mapper?.Map(type); + if (tdr is TypeSpec) + throw new InvalidOperationException(); + if (tdr is TypeDef td) + return td; + if (tdr is TypeRef tr) + return TryResolve(tr); + + if (TryToUseTypeDefs && IsThisModule(type.Module) && module.ResolveToken(type.MetadataToken) is TypeDef def) + return def; + + return TryResolve(CreateTypeRef(type)); } - TypeRef CreateTypeRef2(Type type) { + TypeRef CreateTypeRef(Type type) { if (!type.IsNested) - return module.UpdateRowId(new TypeRefUser(module, type.Namespace ?? string.Empty, type.Name ?? string.Empty, CreateScopeReference(type))); - return module.UpdateRowId(new TypeRefUser(module, string.Empty, type.Name ?? string.Empty, CreateTypeRef2(type.DeclaringType))); + return module.UpdateRowId(new TypeRefUser(module, type.Namespace ?? string.Empty, ReflectionExtensions.Unescape(type.Name) ?? string.Empty, CreateScopeReference(type))); + type.GetTypeNamespaceAndName_TypeDefOrRef(out var @namespace, out var name); + return module.UpdateRowId(new TypeRefUser(module, @namespace ?? string.Empty, name ?? string.Empty, CreateTypeRef(type.DeclaringType))); } IResolutionScope CreateScopeReference(Type type) { - if (type == null) + if (type is null) return null; var asmName = type.Assembly.GetName(); var modAsm = module.Assembly; - if (modAsm != null) { + if (modAsm is not null) { if (UTF8String.ToSystemStringOrEmpty(modAsm.Name).Equals(asmName.Name, StringComparison.OrdinalIgnoreCase)) { if (UTF8String.ToSystemStringOrEmpty(module.Name).Equals(type.Module.ScopeName, StringComparison.OrdinalIgnoreCase)) return module; @@ -354,9 +391,11 @@ IResolutionScope CreateScopeReference(Type type) { } } var pkt = asmName.GetPublicKeyToken(); - if (pkt == null || pkt.Length == 0) + if (pkt is null || pkt.Length == 0) pkt = null; - return module.UpdateRowId(new AssemblyRefUser(asmName.Name, asmName.Version, PublicKeyBase.CreatePublicKeyToken(pkt), asmName.CultureInfo.Name)); + if (TryToUseExistingAssemblyRefs && module.GetAssemblyRef(asmName.Name) is AssemblyRef asmRef) + return asmRef; + return module.UpdateRowId(new AssemblyRefUser(asmName.Name, asmName.Version, PublicKeyBase.CreatePublicKeyToken(pkt), asmName.CultureInfo?.Name ?? string.Empty)); } /// @@ -366,39 +405,36 @@ IResolutionScope CreateScopeReference(Type type) { /// A list of all required modifiers or null /// A list of all optional modifiers or null /// The imported type or null if is invalid - public TypeSig ImportAsTypeSig(Type type, IList requiredModifiers, IList optionalModifiers) { - return ImportAsTypeSig(type, requiredModifiers, optionalModifiers, false); - } + public TypeSig ImportAsTypeSig(Type type, IList requiredModifiers, IList optionalModifiers) => + ImportAsTypeSig(type, requiredModifiers, optionalModifiers, null); - TypeSig ImportAsTypeSig(Type type, IList requiredModifiers, IList optionalModifiers, bool treatAsGenericInst) { - if (type == null) + TypeSig ImportAsTypeSig(Type type, IList requiredModifiers, IList optionalModifiers, Type declaringType) { + if (type is null) return null; if (IsEmpty(requiredModifiers) && IsEmpty(optionalModifiers)) - return ImportAsTypeSig(type, treatAsGenericInst); + return ImportAsTypeSig(type, declaringType); FixSignature = true; // Order of modifiers is unknown - var ts = ImportAsTypeSig(type, treatAsGenericInst); + var ts = ImportAsTypeSig(type, declaringType); // We don't know the original order of the modifiers. // Assume all required modifiers are closer to the real type. // Assume all modifiers should be applied in the same order as in the lists. - if (requiredModifiers != null) { - foreach (var modifier in requiredModifiers.GetSafeEnumerable()) + if (requiredModifiers is not null) { + foreach (var modifier in requiredModifiers) ts = new CModReqdSig(Import(modifier), ts); } - if (optionalModifiers != null) { - foreach (var modifier in optionalModifiers.GetSafeEnumerable()) + if (optionalModifiers is not null) { + foreach (var modifier in optionalModifiers) ts = new CModOptSig(Import(modifier), ts); } return ts; } - static bool IsEmpty(IList list) { - return list == null || list.Count == 0; - } + static bool IsEmpty(IList list) => list is null || list.Count == 0; /// /// Imports a as a . This will be either @@ -407,9 +443,7 @@ static bool IsEmpty(IList list) { /// The method /// The imported method or null if is invalid /// or if we failed to import the method - public IMethod Import(MethodBase methodBase) { - return Import(methodBase, false); - } + public IMethod Import(MethodBase methodBase) => Import(methodBase, false); /// /// Imports a as a . This will be either @@ -425,14 +459,20 @@ public IMethod Import(MethodBase methodBase, bool forceFixSignature) { return ImportInternal(methodBase, forceFixSignature); } - IMethod ImportInternal(MethodBase methodBase) { - return ImportInternal(methodBase, false); - } + IMethod ImportInternal(MethodBase methodBase) => ImportInternal(methodBase, false); IMethod ImportInternal(MethodBase methodBase, bool forceFixSignature) { - if (methodBase == null) + if (methodBase is null) return null; + if (TryToUseMethodDefs && IsThisModule(methodBase.Module) && + !methodBase.IsGenericMethod && (methodBase.DeclaringType is null || !methodBase.DeclaringType.IsGenericType) && + module.ResolveToken(methodBase.MetadataToken) is MethodDef md) { + // In same module and method and declaring type are both non-generic, directly resolve method definition. + // Obfuscator may rename many methods into same name then TryResolveMethod will return inconsistent method. + return md; + } + if (forceFixSignature) { //TODO: } @@ -447,6 +487,8 @@ IMethod ImportInternal(MethodBase methodBase, bool forceFixSignature) { method = ImportInternal(origMethod) as IMethodDefOrRef; method = TryResolveMethod(method); + if (methodBase.ContainsGenericParameters) + return method; // Declaring type is instantiated but method itself is not var gim = CreateGenericInstMethodSig(methodBase); var methodSpec = module.UpdateRowId(new MethodSpecUser(method, gim)); @@ -457,13 +499,13 @@ IMethod ImportInternal(MethodBase methodBase, bool forceFixSignature) { } else { IMemberRefParent parent; - if (methodBase.DeclaringType == null) { + if (methodBase.DeclaringType is null) { // It's the global type. We can reference it with a ModuleRef token. parent = GetModuleParent(methodBase.Module); } else parent = Import(methodBase.DeclaringType); - if (parent == null) + if (parent is null) return null; MethodBase origMethod; @@ -489,11 +531,12 @@ IMethod ImportInternal(MethodBase methodBase, bool forceFixSignature) { } } + bool IsThisModule(Module module2) => UTF8String.ToSystemStringOrEmpty(module.Name).Equals(module2.ScopeName, StringComparison.OrdinalIgnoreCase) && IsThisAssembly(module2); + MethodSig CreateMethodSig(MethodBase mb) { var sig = new MethodSig(GetCallingConvention(mb)); - var mi = mb as MethodInfo; - if (mi != null) + if (mb is MethodInfo mi) sig.RetType = ImportAsTypeSig(mi.ReturnParameter, mb.DeclaringType); else sig.RetType = module.CorLibTypes.Void; @@ -507,9 +550,8 @@ MethodSig CreateMethodSig(MethodBase mb) { return sig; } - TypeSig ImportAsTypeSig(ParameterInfo p, Type declaringType) { - return ImportAsTypeSig(p.ParameterType, p.GetRequiredCustomModifiers(), p.GetOptionalCustomModifiers(), declaringType.MustTreatTypeAsGenericInstType(p.ParameterType)); - } + TypeSig ImportAsTypeSig(ParameterInfo p, Type declaringType) => + ImportAsTypeSig(p.ParameterType, p.GetRequiredCustomModifiers(), p.GetOptionalCustomModifiers(), declaringType); CallingConvention GetCallingConvention(MethodBase mb) { CallingConvention cc = 0; @@ -550,24 +592,24 @@ GenericInstMethodSig CreateGenericInstMethodSig(MethodBase mb) { } IMemberRefParent GetModuleParent(Module module2) { - // If we have no assembly, assume this is a netmodule in the same assembly as module - var modAsm = module.Assembly; - bool isSameAssembly = modAsm == null || - UTF8String.ToSystemStringOrEmpty(modAsm.Name).Equals(module2.Assembly.GetName().Name, StringComparison.OrdinalIgnoreCase); - if (!isSameAssembly) + if (!IsThisAssembly(module2)) return null; return module.UpdateRowId(new ModuleRefUser(module, module.Name)); } + bool IsThisAssembly(Module module2) { + // If we have no assembly, assume this is a netmodule in the same assembly as module + var modAsm = module.Assembly; + return modAsm is null || UTF8String.ToSystemStringOrEmpty(modAsm.Name).Equals(module2.Assembly.GetName().Name, StringComparison.OrdinalIgnoreCase); + } + /// /// Imports a as a /// /// The field /// The imported field or null if is invalid /// or if we failed to import the field - public IField Import(FieldInfo fieldInfo) { - return Import(fieldInfo, false); - } + public IField Import(FieldInfo fieldInfo) => Import(fieldInfo, false); /// /// Imports a as a @@ -579,21 +621,29 @@ public IField Import(FieldInfo fieldInfo) { /// or if we failed to import the field public IField Import(FieldInfo fieldInfo, bool forceFixSignature) { FixSignature = false; - if (fieldInfo == null) + if (fieldInfo is null) return null; + if (TryToUseFieldDefs && IsThisModule(fieldInfo.Module) && + (fieldInfo.DeclaringType is null || !fieldInfo.DeclaringType.IsGenericType) && + module.ResolveToken(fieldInfo.MetadataToken) is FieldDef fd) { + // In same module and declaring type is non-generic, directly resolve field definition. + // Obfuscator may rename many fields into same name then TryResolveField will return inconsistent field. + return fd; + } + if (forceFixSignature) { //TODO: } IMemberRefParent parent; - if (fieldInfo.DeclaringType == null) { + if (fieldInfo.DeclaringType is null) { // It's the global type. We can reference it with a ModuleRef token. parent = GetModuleParent(fieldInfo.Module); } else parent = Import(fieldInfo.DeclaringType); - if (parent == null) + if (parent is null) return null; FieldInfo origField; @@ -606,26 +656,10 @@ public IField Import(FieldInfo fieldInfo, bool forceFixSignature) { origField = fieldInfo; } - MemberRef fieldRef; - if (origField.FieldType.ContainsGenericParameters) { - var origDeclType = origField.DeclaringType; - var asm = module.Context.AssemblyResolver.Resolve(origDeclType.Module.Assembly.GetName(), module); - if (asm == null || asm.FullName != origDeclType.Assembly.FullName) - throw new Exception("Couldn't resolve the correct assembly"); - var mod = asm.FindModule(origDeclType.Module.Name) as ModuleDefMD; - if (mod == null) - throw new Exception("Couldn't resolve the correct module"); - var fieldDef = mod.ResolveField((uint)(origField.MetadataToken & 0x00FFFFFF)); - if (fieldDef == null) - throw new Exception("Couldn't resolve the correct field"); - - var fieldSig = new FieldSig(Import(fieldDef.FieldSig.GetFieldType())); - fieldRef = module.UpdateRowId(new MemberRefUser(module, fieldInfo.Name, fieldSig, parent)); - } - else { - var fieldSig = new FieldSig(ImportAsTypeSig(fieldInfo.FieldType)); - fieldRef = module.UpdateRowId(new MemberRefUser(module, fieldInfo.Name, fieldSig, parent)); - } + var fieldSig = new FieldSig(ImportAsTypeSig(origField.FieldType, + origField.GetRequiredCustomModifiers(), origField.GetOptionalCustomModifiers(), origField.DeclaringType)); + var fieldRef = module.UpdateRowId(new MemberRefUser(module, fieldInfo.Name, fieldSig, parent)); + var field = TryResolveField(fieldRef); if (FixSignature && !forceFixSignature) { //TODO: @@ -639,7 +673,7 @@ public IField Import(FieldInfo fieldInfo, bool forceFixSignature) { /// The type /// The imported type or null public IType Import(IType type) { - if (type == null) + if (type is null) return null; if (!recursionCounter.Increment()) return null; @@ -650,13 +684,13 @@ public IType Import(IType type) { TypeSpec ts; TypeSig sig; - if ((td = type as TypeDef) != null) + if ((td = type as TypeDef) is not null) result = Import(td); - else if ((tr = type as TypeRef) != null) + else if ((tr = type as TypeRef) is not null) result = Import(tr); - else if ((ts = type as TypeSpec) != null) + else if ((ts = type as TypeSpec) is not null) result = Import(ts); - else if ((sig = type as TypeSig) != null) + else if ((sig = type as TypeSig) is not null) result = Import(sig); else result = null; @@ -671,22 +705,25 @@ public IType Import(IType type) { /// The type /// The imported type or null public ITypeDefOrRef Import(TypeDef type) { - if (type == null) + if (type is null) return null; if (TryToUseTypeDefs && type.Module == module) return type; + var mapped = mapper?.Map(type); + if (mapped is not null) + return mapped; return Import2(type); } TypeRef Import2(TypeDef type) { - if (type == null) + if (type is null) return null; if (!recursionCounter.Increment()) return null; TypeRef result; var declType = type.DeclaringType; - if (declType != null) + if (declType is not null) result = module.UpdateRowId(new TypeRefUser(module, type.Namespace, type.Name, Import2(declType))); else result = module.UpdateRowId(new TypeRefUser(module, type.Namespace, type.Name, CreateScopeReference(type.DefinitionAssembly, type.Module))); @@ -696,10 +733,10 @@ TypeRef Import2(TypeDef type) { } IResolutionScope CreateScopeReference(IAssembly defAsm, ModuleDef defMod) { - if (defAsm == null) + if (defAsm is null) return null; var modAsm = module.Assembly; - if (defMod != null && defAsm != null && modAsm != null) { + if (defMod is not null && defAsm is not null && modAsm is not null) { if (UTF8String.CaseInsensitiveEquals(modAsm.Name, defAsm.Name)) { if (UTF8String.CaseInsensitiveEquals(module.Name, defMod.Name)) return module; @@ -709,6 +746,8 @@ IResolutionScope CreateScopeReference(IAssembly defAsm, ModuleDef defMod) { var pkt = PublicKeyBase.ToPublicKeyToken(defAsm.PublicKeyOrToken); if (PublicKeyBase.IsNullOrEmpty2(pkt)) pkt = null; + if (TryToUseExistingAssemblyRefs && module.GetAssemblyRef(defAsm.Name) is AssemblyRef asmRef) + return asmRef; return module.UpdateRowId(new AssemblyRefUser(defAsm.Name, defAsm.Version, pkt, defAsm.Culture) { Attributes = defAsm.Attributes & ~AssemblyAttributes.PublicKey }); } @@ -718,18 +757,22 @@ IResolutionScope CreateScopeReference(IAssembly defAsm, ModuleDef defMod) { /// The type /// The imported type or null public ITypeDefOrRef Import(TypeRef type) { + var mapped = mapper?.Map(type); + if (mapped is not null) + return mapped; + return TryResolve(Import2(type)); } TypeRef Import2(TypeRef type) { - if (type == null) + if (type is null) return null; if (!recursionCounter.Increment()) return null; TypeRef result; var declaringType = type.DeclaringType; - if (declaringType != null) + if (declaringType is not null) result = module.UpdateRowId(new TypeRefUser(module, type.Namespace, type.Name, Import2(declaringType))); else result = module.UpdateRowId(new TypeRefUser(module, type.Namespace, type.Name, CreateScopeReference(type.DefinitionAssembly, type.Module))); @@ -744,7 +787,7 @@ TypeRef Import2(TypeRef type) { /// The type /// The imported type or null public TypeSpec Import(TypeSpec type) { - if (type == null) + if (type is null) return null; return module.UpdateRowId(new TypeSpecUser(Import(type.TypeSig))); } @@ -755,7 +798,7 @@ public TypeSpec Import(TypeSpec type) { /// The type /// The imported type or null public TypeSig Import(TypeSig type) { - if (type == null) + if (type is null) return null; if (!recursionCounter.Increment()) return null; @@ -805,7 +848,7 @@ public TypeSig Import(TypeSig type) { case ElementType.GenericInst: var gis = (GenericInstSig)type; var genArgs = new List(gis.GenericArguments.Count); - foreach (var ga in gis.GenericArguments.GetSafeEnumerable()) + foreach (var ga in gis.GenericArguments) genArgs.Add(Import(ga)); result = new GenericInstSig(Import(gis.GenericType) as ClassOrValueTypeSig, genArgs); break; @@ -822,13 +865,16 @@ public TypeSig Import(TypeSig type) { return result; } - ITypeDefOrRef Import(ITypeDefOrRef type) { - return (ITypeDefOrRef)Import((IType)type); - } + /// + /// Imports a + /// + /// The type + /// The imported type or null + public ITypeDefOrRef Import(ITypeDefOrRef type) => (ITypeDefOrRef)Import((IType)type); TypeSig CreateClassOrValueType(ITypeDefOrRef type, bool isValueType) { var corLibType = module.CorLibTypes.GetCorLibTypeSig(type); - if (corLibType != null) + if (corLibType is not null) return corLibType; if (isValueType) @@ -842,7 +888,7 @@ TypeSig CreateClassOrValueType(ITypeDefOrRef type, bool isValueType) { /// The sig /// The imported sig or null if input is invalid public CallingConventionSig Import(CallingConventionSig sig) { - if (sig == null) + if (sig is null) return null; if (!recursionCounter.Increment()) return null; @@ -872,7 +918,7 @@ public CallingConventionSig Import(CallingConventionSig sig) { /// The sig /// The imported sig or null if input is invalid public FieldSig Import(FieldSig sig) { - if (sig == null) + if (sig is null) return null; if (!recursionCounter.Increment()) return null; @@ -889,12 +935,12 @@ public FieldSig Import(FieldSig sig) { /// The sig /// The imported sig or null if input is invalid public MethodSig Import(MethodSig sig) { - if (sig == null) + if (sig is null) return null; if (!recursionCounter.Increment()) return null; - MethodSig result = Import(new MethodSig(sig.GetCallingConvention()), sig); + var result = Import(new MethodSig(sig.GetCallingConvention()), sig); recursionCounter.Decrement(); return result; @@ -902,12 +948,12 @@ public MethodSig Import(MethodSig sig) { T Import(T sig, T old) where T : MethodBaseSig { sig.RetType = Import(old.RetType); - foreach (var p in old.Params.GetSafeEnumerable()) + foreach (var p in old.Params) sig.Params.Add(Import(p)); sig.GenParamCount = old.GenParamCount; var paramsAfterSentinel = sig.ParamsAfterSentinel; - if (paramsAfterSentinel != null) { - foreach (var p in old.ParamsAfterSentinel.GetSafeEnumerable()) + if (paramsAfterSentinel is not null) { + foreach (var p in old.ParamsAfterSentinel) paramsAfterSentinel.Add(Import(p)); } return sig; @@ -919,12 +965,12 @@ T Import(T sig, T old) where T : MethodBaseSig { /// The sig /// The imported sig or null if input is invalid public PropertySig Import(PropertySig sig) { - if (sig == null) + if (sig is null) return null; if (!recursionCounter.Increment()) return null; - PropertySig result = Import(new PropertySig(sig.GetCallingConvention()), sig); + var result = Import(new PropertySig(sig.GetCallingConvention()), sig); recursionCounter.Decrement(); return result; @@ -936,13 +982,13 @@ public PropertySig Import(PropertySig sig) { /// The sig /// The imported sig or null if input is invalid public LocalSig Import(LocalSig sig) { - if (sig == null) + if (sig is null) return null; if (!recursionCounter.Increment()) return null; - LocalSig result = new LocalSig(sig.GetCallingConvention(), (uint)sig.Locals.Count); - foreach (var l in sig.Locals.GetSafeEnumerable()) + var result = new LocalSig(sig.GetCallingConvention(), (uint)sig.Locals.Count); + foreach (var l in sig.Locals) result.Locals.Add(Import(l)); recursionCounter.Decrement(); @@ -955,13 +1001,13 @@ public LocalSig Import(LocalSig sig) { /// The sig /// The imported sig or null if input is invalid public GenericInstMethodSig Import(GenericInstMethodSig sig) { - if (sig == null) + if (sig is null) return null; if (!recursionCounter.Increment()) return null; - GenericInstMethodSig result = new GenericInstMethodSig(sig.GetCallingConvention(), (uint)sig.GenericArguments.Count); - foreach (var l in sig.GenericArguments.GetSafeEnumerable()) + var result = new GenericInstMethodSig(sig.GetCallingConvention(), (uint)sig.GenericArguments.Count); + foreach (var l in sig.GenericArguments) result.GenericArguments.Add(Import(l)); recursionCounter.Decrement(); @@ -974,7 +1020,7 @@ public GenericInstMethodSig Import(GenericInstMethodSig sig) { /// The field /// The imported type or null if is invalid public IField Import(IField field) { - if (field == null) + if (field is null) return null; if (!recursionCounter.Increment()) return null; @@ -983,9 +1029,9 @@ public IField Import(IField field) { MemberRef mr; FieldDef fd; - if ((fd = field as FieldDef) != null) + if ((fd = field as FieldDef) is not null) result = Import(fd); - else if ((mr = field as MemberRef) != null) + else if ((mr = field as MemberRef) is not null) result = Import(mr); else result = null; @@ -1000,7 +1046,7 @@ public IField Import(IField field) { /// The method /// The imported method or null if is invalid public IMethod Import(IMethod method) { - if (method == null) + if (method is null) return null; if (!recursionCounter.Increment()) return null; @@ -1010,11 +1056,11 @@ public IMethod Import(IMethod method) { MethodSpec ms; MemberRef mr; - if ((md = method as MethodDef) != null) + if ((md = method as MethodDef) is not null) result = Import(md); - else if ((ms = method as MethodSpec) != null) + else if ((ms = method as MethodSpec) is not null) result = Import(ms); - else if ((mr = method as MemberRef) != null) + else if ((mr = method as MemberRef) is not null) result = Import(mr); else result = null; @@ -1029,12 +1075,17 @@ public IMethod Import(IMethod method) { /// The field /// The imported type or null if is invalid public IField Import(FieldDef field) { - if (field == null) + if (field is null) return null; if (TryToUseFieldDefs && field.Module == module) return field; if (!recursionCounter.Increment()) return null; + var mapped = mapper?.Map(field); + if (mapped is not null) { + recursionCounter.Decrement(); + return mapped; + } MemberRef result = module.UpdateRowId(new MemberRefUser(module, field.Name)); result.Signature = Import(field.Signature); @@ -1045,12 +1096,10 @@ public IField Import(FieldDef field) { } IMemberRefParent ImportParent(TypeDef type) { - if (type == null) + if (type is null) return null; - if (type.IsGlobalModuleType) { - var om = type.Module; - return module.UpdateRowId(new ModuleRefUser(module, om == null ? null : om.Name)); - } + if (type.IsGlobalModuleType) + return module.UpdateRowId(new ModuleRefUser(module, type.Module?.Name)); return Import(type); } @@ -1060,12 +1109,17 @@ IMemberRefParent ImportParent(TypeDef type) { /// The method /// The imported method or null if is invalid public IMethod Import(MethodDef method) { - if (method == null) + if (method is null) return null; if (TryToUseMethodDefs && method.Module == module) return method; if (!recursionCounter.Increment()) return null; + var mapped = mapper?.Map(method); + if (mapped is not null) { + recursionCounter.Decrement(); + return mapped; + } MemberRef result = module.UpdateRowId(new MemberRefUser(module, method.Name)); result.Signature = Import(method.Signature); @@ -1081,7 +1135,7 @@ public IMethod Import(MethodDef method) { /// The method /// The imported method or null if is invalid public MethodSpec Import(MethodSpec method) { - if (method == null) + if (method is null) return null; if (!recursionCounter.Increment()) return null; @@ -1099,15 +1153,20 @@ public MethodSpec Import(MethodSpec method) { /// The member ref /// The imported member ref or null if is invalid public MemberRef Import(MemberRef memberRef) { - if (memberRef == null) + if (memberRef is null) return null; if (!recursionCounter.Increment()) return null; + var mapped = mapper?.Map(memberRef); + if (mapped is not null) { + recursionCounter.Decrement(); + return mapped; + } MemberRef result = module.UpdateRowId(new MemberRefUser(module, memberRef.Name)); result.Signature = Import(memberRef.Signature); result.Class = Import(memberRef.Class); - if (result.Class == null) // Will be null if memberRef.Class is null or a MethodDef + if (result.Class is null) // Will be null if memberRef.Class is null or a MethodDef result = null; recursionCounter.Decrement(); @@ -1115,24 +1174,18 @@ public MemberRef Import(MemberRef memberRef) { } IMemberRefParent Import(IMemberRefParent parent) { - var tdr = parent as ITypeDefOrRef; - if (tdr != null) { - var td = tdr as TypeDef; - if (td != null && td.IsGlobalModuleType) { - var om = td.Module; - return module.UpdateRowId(new ModuleRefUser(module, om == null ? null : om.Name)); - } + if (parent is ITypeDefOrRef tdr) { + if (tdr is TypeDef td && td.IsGlobalModuleType) + return module.UpdateRowId(new ModuleRefUser(module, td.Module?.Name)); return Import(tdr); } - var modRef = parent as ModuleRef; - if (modRef != null) + if (parent is ModuleRef modRef) return module.UpdateRowId(new ModuleRefUser(module, modRef.Name)); - var method = parent as MethodDef; - if (method != null) { + if (parent is MethodDef method) { var dt = method.DeclaringType; - return dt == null || dt.Module != module ? null : method; + return dt is null || dt.Module != module ? null : method; } return null; diff --git a/src/DotNet/InterfaceImpl.cs b/src/DotNet/InterfaceImpl.cs index ec3aaded0..a326c7a9c 100644 --- a/src/DotNet/InterfaceImpl.cs +++ b/src/DotNet/InterfaceImpl.cs @@ -1,43 +1,41 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// /// A high-level representation of a row in the InterfaceImpl table /// [DebuggerDisplay("{Interface}")] - public abstract class InterfaceImpl : IHasCustomAttribute, IContainsGenericParameter { + public abstract class InterfaceImpl : IHasCustomAttribute, IContainsGenericParameter, IHasCustomDebugInformation { /// /// The row id in its table /// protected uint rid; /// - public MDToken MDToken { - get { return new MDToken(Table.InterfaceImpl, rid); } - } + public MDToken MDToken => new MDToken(Table.InterfaceImpl, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 5; } - } + public int HasCustomAttributeTag => 5; /// /// From column InterfaceImpl.Interface /// public ITypeDefOrRef Interface { - get { return @interface; } - set { @interface = value; } + get => @interface; + set => @interface = value; } /// protected ITypeDefOrRef @interface; @@ -47,7 +45,7 @@ public ITypeDefOrRef Interface { /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -55,18 +53,35 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } - } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 5; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; - bool IContainsGenericParameter.ContainsGenericParameter { - get { return TypeHelper.ContainsGenericParameter(this); } + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); + + bool IContainsGenericParameter.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); } /// @@ -83,32 +98,38 @@ public InterfaceImplUser() { /// Constructor /// /// The interface the type implements - public InterfaceImplUser(ITypeDefOrRef @interface) { - this.@interface = @interface; - } + public InterfaceImplUser(ITypeDefOrRef @interface) => this.@interface = @interface; } /// /// Created from a row in the InterfaceImpl table /// - sealed class InterfaceImplMD : InterfaceImpl, IMDTokenProviderMD { + sealed class InterfaceImplMD : InterfaceImpl, IMDTokenProviderMD, IContainsGenericParameter2 { /// The module where this instance is located readonly ModuleDefMD readerModule; readonly uint origRid; + readonly GenericParamContext gpContext; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; + + bool IContainsGenericParameter2.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.InterfaceImpl, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.InterfaceImpl, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), gpContext, list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -119,16 +140,18 @@ protected override void InitializeCustomAttributes() { /// If is invalid public InterfaceImplMD(ModuleDefMD readerModule, uint rid, GenericParamContext gpContext) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.InterfaceImplTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("InterfaceImpl rid {0} does not exist", rid)); + throw new BadImageFormatException($"InterfaceImpl rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint @interface = readerModule.TablesStream.ReadInterfaceImplRow2(origRid); - this.@interface = readerModule.ResolveTypeDefOrRef(@interface, gpContext); + this.gpContext = gpContext; + bool b = readerModule.TablesStream.TryReadInterfaceImplRow(origRid, out var row); + Debug.Assert(b); + @interface = readerModule.ResolveTypeDefOrRef(row.Interface, gpContext); } } } diff --git a/src/DotNet/MD/BlobStream.cs b/src/DotNet/MD/BlobStream.cs index 1d324d605..984ab0af7 100644 --- a/src/DotNet/MD/BlobStream.cs +++ b/src/DotNet/MD/BlobStream.cs @@ -1,21 +1,20 @@ // dnlib: See LICENSE.txt for more info -using dnlib.IO; +using System; +using dnlib.IO; namespace dnlib.DotNet.MD { /// /// Represents the #Blob stream /// public sealed class BlobStream : HeapStream { - static readonly byte[] noData = new byte[0]; - /// public BlobStream() { } /// - public BlobStream(IImageStream imageStream, StreamHeader streamHeader) - : base(imageStream, streamHeader) { + public BlobStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader) + : base(mdReaderFactory, metadataBaseOffset, streamHeader) { } /// @@ -27,18 +26,10 @@ public byte[] Read(uint offset) { // The CLR has a special check for offset 0. It always interprets it as // 0-length data, even if that first byte isn't 0 at all. if (offset == 0) - return noData; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - IImageStream reader; - int size = GetReader_NoLock(offset, out reader); - if (size < 0) + return Array2.Empty(); + if (!TryCreateReader(offset, out var reader)) return null; - return reader.ReadBytes(size); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + return reader.ToArray(); } /// @@ -47,41 +38,36 @@ public byte[] Read(uint offset) { /// /// Offset of data /// The data - public byte[] ReadNoNull(uint offset) { - return Read(offset) ?? noData; - } + public byte[] ReadNoNull(uint offset) => Read(offset) ?? Array2.Empty(); /// - /// Creates a new sub stream of the #Blob stream that can access one blob + /// Creates a reader that can access a blob /// /// Offset of blob /// A new stream - public IImageStream CreateStream(uint offset) { -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - IImageStream reader; - int size = GetReader_NoLock(offset, out reader); - if (size < 0) - return MemoryImageStream.CreateEmpty(); - return reader.Create((FileOffset)reader.Position, size); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + public DataReader CreateReader(uint offset) { + if (TryCreateReader(offset, out var reader)) + return reader; + return default; } - int GetReader_NoLock(uint offset, out IImageStream reader) { - reader = null; + /// + /// Creates a reader that can access a blob or returns false on failure + /// + /// Offset of blob + /// Updated with the reader + /// + public bool TryCreateReader(uint offset, out DataReader reader) { + reader = dataReader; if (!IsValidOffset(offset)) - return -1; - reader = GetReader_NoLock(offset); - uint length; - if (!reader.ReadCompressedUInt32(out length)) - return -1; - if (reader.Position + length < length || reader.Position + length > reader.Length) - return -1; - - return (int)length; // length <= 0x1FFFFFFF so this cast does not make it negative + return false; + reader.Position = offset; + if (!reader.TryReadCompressedUInt32(out uint length)) + return false; + if (!reader.CanRead(length)) + return false; + reader = reader.Slice(reader.Position, length); + return true; } } } diff --git a/src/DotNet/MD/CodedToken.cs b/src/DotNet/MD/CodedToken.cs index 792db014b..a77f4f452 100644 --- a/src/DotNet/MD/CodedToken.cs +++ b/src/DotNet/MD/CodedToken.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.DotNet.MD { /// @@ -64,8 +64,8 @@ public sealed class CodedToken { }); /// CustomAttributeType coded token - public static readonly CodedToken CustomAttributeType = new CodedToken(3, new Table[4] { - 0, 0, Table.Method, Table.MemberRef, + public static readonly CodedToken CustomAttributeType = new CodedToken(3, new Table[5] { + 0, 0, Table.Method, Table.MemberRef, 0, }); /// ResolutionScope coded token @@ -78,6 +78,17 @@ public sealed class CodedToken { Table.TypeDef, Table.Method, }); + /// HasCustomDebugInformation coded token + public static readonly CodedToken HasCustomDebugInformation = new CodedToken(5, new Table[27] { + Table.Method, Table.Field, Table.TypeRef, Table.TypeDef, + Table.Param, Table.InterfaceImpl, Table.MemberRef, Table.Module, + Table.DeclSecurity, Table.Property, Table.Event, Table.StandAloneSig, + Table.ModuleRef, Table.TypeSpec, Table.Assembly, Table.AssemblyRef, + Table.File, Table.ExportedType, Table.ManifestResource, Table.GenericParam, + Table.GenericParamConstraint, Table.MethodSpec, Table.Document, Table.LocalScope, + Table.LocalVariable, Table.LocalConstant, Table.ImportScope, + }); + readonly Table[] tableTypes; readonly int bits; readonly int mask; @@ -85,16 +96,12 @@ public sealed class CodedToken { /// /// Returns all types of tables /// - public Table[] TableTypes { - get { return tableTypes; } - } + public Table[] TableTypes => tableTypes; /// /// Returns the number of bits that is used to encode table type /// - public int Bits { - get { return bits; } - } + public int Bits => bits; /// /// Constructor @@ -103,7 +110,7 @@ public int Bits { /// All table types internal CodedToken(int bits, Table[] tableTypes) { this.bits = bits; - this.mask = (1 << bits) - 1; + mask = (1 << bits) - 1; this.tableTypes = tableTypes; } @@ -113,9 +120,7 @@ internal CodedToken(int bits, Table[] tableTypes) { /// The token /// Coded token /// - public uint Encode(MDToken token) { - return Encode(token.Raw); - } + public uint Encode(MDToken token) => Encode(token.Raw); /// /// Encodes a token @@ -124,8 +129,7 @@ public uint Encode(MDToken token) { /// Coded token /// public uint Encode(uint token) { - uint codedToken; - Encode(token, out codedToken); + Encode(token, out uint codedToken); return codedToken; } @@ -135,9 +139,7 @@ public uint Encode(uint token) { /// The token /// Coded token /// true if successful - public bool Encode(MDToken token, out uint codedToken) { - return Encode(token.Raw, out codedToken); - } + public bool Encode(MDToken token, out uint codedToken) => Encode(token.Raw, out codedToken); /// /// Encodes a token @@ -164,8 +166,7 @@ public bool Encode(uint token, out uint codedToken) { /// Decoded token or 0 on failure /// public MDToken Decode2(uint codedToken) { - uint token; - Decode(codedToken, out token); + Decode(codedToken, out uint token); return new MDToken(token); } @@ -176,8 +177,7 @@ public MDToken Decode2(uint codedToken) { /// Decoded token or 0 on failure /// public uint Decode(uint codedToken) { - uint token; - Decode(codedToken, out token); + Decode(codedToken, out uint token); return token; } @@ -188,8 +188,7 @@ public uint Decode(uint codedToken) { /// Decoded token /// true if successful public bool Decode(uint codedToken, out MDToken token) { - uint decodedToken; - bool result = Decode(codedToken, out decodedToken); + bool result = Decode(codedToken, out uint decodedToken); token = new MDToken(decodedToken); return result; } diff --git a/src/DotNet/MD/ColumnInfo.cs b/src/DotNet/MD/ColumnInfo.cs index cab756e4d..02c7b6bf4 100644 --- a/src/DotNet/MD/ColumnInfo.cs +++ b/src/DotNet/MD/ColumnInfo.cs @@ -1,8 +1,8 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Diagnostics; -using System.IO; +using dnlib.DotNet.Writer; using dnlib.IO; namespace dnlib.DotNet.MD { @@ -20,39 +20,33 @@ public sealed class ColumnInfo { /// /// Gets the column index /// - public int Index { - get { return index; } - } + public int Index => index; /// /// Returns the column offset within the table row /// public int Offset { - get { return offset; } - internal set { offset = (byte)value; } + get => offset; + internal set => offset = (byte)value; } /// /// Returns the column size /// public int Size { - get { return size; } - internal set { size = (byte)value; } + get => size; + internal set => size = (byte)value; } /// /// Returns the column name /// - public string Name { - get { return name; } - } + public string Name => name; /// /// Returns the ColumnSize enum value /// - public ColumnSize ColumnSize { - get { return columnSize; } - } + public ColumnSize ColumnSize => columnSize; /// /// Constructor @@ -87,13 +81,17 @@ public ColumnInfo(byte index, string name, ColumnSize columnSize, byte offset, b /// /// A reader positioned on this column /// The column value - public uint Read(IBinaryReader reader) { - switch (size) { - case 1: return reader.ReadByte(); - case 2: return reader.ReadUInt16(); - case 4: return reader.ReadUInt32(); - default: throw new InvalidOperationException("Invalid column size"); - } + public uint Read(ref DataReader reader) => + size switch { + 1 => reader.ReadByte(), + 2 => reader.ReadUInt16(), + 4 => reader.ReadUInt32(), + _ => throw new InvalidOperationException("Invalid column size"), + }; + + internal uint Unsafe_Read24(ref DataReader reader) { + Debug.Assert(size == 2 || size == 4); + return size == 2 ? reader.Unsafe_ReadUInt16() : reader.Unsafe_ReadUInt32(); } /// @@ -101,13 +99,21 @@ public uint Read(IBinaryReader reader) { /// /// The writer position on this column /// The column value - public void Write(BinaryWriter writer, uint value) { + public void Write(DataWriter writer, uint value) { switch (size) { - case 1: writer.Write((byte)value); break; - case 2: writer.Write((ushort)value); break; - case 4: writer.Write(value); break; + case 1: writer.WriteByte((byte)value); break; + case 2: writer.WriteUInt16((ushort)value); break; + case 4: writer.WriteUInt32(value); break; default: throw new InvalidOperationException("Invalid column size"); } } + + internal void Write24(DataWriter writer, uint value) { + Debug.Assert(size == 2 || size == 4); + if (size == 2) + writer.WriteUInt16((ushort)value); + else + writer.WriteUInt32(value); + } } } diff --git a/src/DotNet/MD/ColumnSize.cs b/src/DotNet/MD/ColumnSize.cs index 4f7ef6d26..7784c6630 100644 --- a/src/DotNet/MD/ColumnSize.cs +++ b/src/DotNet/MD/ColumnSize.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet.MD { +namespace dnlib.DotNet.MD { /// /// MD table column size /// @@ -95,6 +95,22 @@ public enum ColumnSize : byte { MethodSpec, /// RID into GenericParamConstraint table GenericParamConstraint, + /// RID into Document table + Document = 0x30, + /// RID into MethodDebugInformation table + MethodDebugInformation, + /// RID into LocalScope table + LocalScope, + /// RID into LocalVariable table + LocalVariable, + /// RID into LocalConstant table + LocalConstant, + /// RID into ImportScope table + ImportScope, + /// RID into StateMachineMethod table + StateMachineMethod, + /// RID into CustomDebugInformation table + CustomDebugInformation, /// 8-bit byte Byte = 0x40, /// 16-bit signed int @@ -137,5 +153,7 @@ public enum ColumnSize : byte { ResolutionScope, /// TypeOrMethodDef encoded token TypeOrMethodDef, + /// HasCustomDebugInformation encoded token + HasCustomDebugInformation, } } diff --git a/src/DotNet/MD/ComImageFlags.cs b/src/DotNet/MD/ComImageFlags.cs index 181eccd96..0ddef31ed 100644 --- a/src/DotNet/MD/ComImageFlags.cs +++ b/src/DotNet/MD/ComImageFlags.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.DotNet.MD { /// @@ -16,7 +16,7 @@ public enum ComImageFlags : uint { /// /// See COMIMAGE_FLAGS_32BITREQUIRED in the Windows SDK /// - _32BitRequired = 2, + Bit32Required = 2, /// /// Set if a native header exists (COMIMAGE_FLAGS_IL_LIBRARY) @@ -41,6 +41,6 @@ public enum ComImageFlags : uint { /// /// See COMIMAGE_FLAGS_32BITPREFERRED in the Windows SDK /// - _32BitPreferred = 0x20000, + Bit32Preferred = 0x20000, } } diff --git a/src/DotNet/MD/CompressedMetaData.cs b/src/DotNet/MD/CompressedMetaData.cs deleted file mode 100644 index 0be9d94e9..000000000 --- a/src/DotNet/MD/CompressedMetaData.cs +++ /dev/null @@ -1,296 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Collections.Generic; -using System.IO; -using dnlib.IO; -using dnlib.PE; -using dnlib.Threading; - -namespace dnlib.DotNet.MD { - /// - /// Used when a #~ stream is present in the metadata - /// - sealed class CompressedMetaData : MetaData { - /// - public override bool IsCompressed { - get { return true; } - } - - /// - public CompressedMetaData(IPEImage peImage, ImageCor20Header cor20Header, MetaDataHeader mdHeader) - : base(peImage, cor20Header, mdHeader) { - } - - static CompressedMetaData() { - var windir = Environment.GetEnvironmentVariable("WINDIR"); - if (!string.IsNullOrEmpty(windir)) { - var baseDir = Path.Combine(windir, "assembly"); - nativeImages40 = Path.Combine(baseDir, "NativeImages_v4.0.30319"); - } - } - - static string nativeImages40; - static HotHeapVersion GetHotHeapVersion(string fileName, string version) { - // Some .NET 2.0 assemblies are stored in the 4.0 GAC. The version is not easily - // detectable from the data in the image so check the path. - if (nativeImages40 != null && fileName != null && fileName.StartsWith(nativeImages40, StringComparison.OrdinalIgnoreCase)) - return HotHeapVersion.CLR40; - - if (version.StartsWith(MDHeaderRuntimeVersion.MS_CLR_20_PREFIX)) - return HotHeapVersion.CLR20; - if (version.StartsWith(MDHeaderRuntimeVersion.MS_CLR_40_PREFIX)) - return HotHeapVersion.CLR40; - - return HotHeapVersion.CLR40; - } - - /// - protected override void InitializeInternal() { - var hotHeapVersion = GetHotHeapVersion(peImage.FileName, mdHeader.VersionString); - - IImageStream imageStream = null, fullStream = null; - DotNetStream dns = null; - List hotStreams = null; - HotStream hotStream = null; - var newAllStreams = new List(allStreams); - try { - var mdRva = cor20Header.MetaData.VirtualAddress; - for (int i = mdHeader.StreamHeaders.Count - 1; i >= 0; i--) { - var sh = mdHeader.StreamHeaders[i]; - var rva = mdRva + sh.Offset; - var fileOffset = peImage.ToFileOffset(rva); - imageStream = peImage.CreateStream(fileOffset, sh.StreamSize); - switch (sh.Name) { - case "#Strings": - if (stringsStream == null) { - stringsStream = new StringsStream(imageStream, sh); - imageStream = null; - newAllStreams.Add(stringsStream); - continue; - } - break; - - case "#US": - if (usStream == null) { - usStream = new USStream(imageStream, sh); - imageStream = null; - newAllStreams.Add(usStream); - continue; - } - break; - - case "#Blob": - if (blobStream == null) { - blobStream = new BlobStream(imageStream, sh); - imageStream = null; - newAllStreams.Add(blobStream); - continue; - } - break; - - case "#GUID": - if (guidStream == null) { - guidStream = new GuidStream(imageStream, sh); - imageStream = null; - newAllStreams.Add(guidStream); - continue; - } - break; - - case "#~": - if (tablesStream == null) { - tablesStream = new TablesStream(imageStream, sh); - imageStream = null; - newAllStreams.Add(tablesStream); - continue; - } - break; - - case "#!": - if (hotStreams == null) - hotStreams = new List(); - fullStream = peImage.CreateFullStream(); - hotStream = HotStream.Create(hotHeapVersion, imageStream, sh, fullStream, fileOffset); - fullStream = null; - hotStreams.Add(hotStream); - newAllStreams.Add(hotStream); - hotStream = null; - imageStream = null; - continue; - } - dns = new DotNetStream(imageStream, sh); - imageStream = null; - newAllStreams.Add(dns); - dns = null; - } - } - finally { - if (imageStream != null) - imageStream.Dispose(); - if (fullStream != null) - fullStream.Dispose(); - if (dns != null) - dns.Dispose(); - if (hotStream != null) - hotStream.Dispose(); - newAllStreams.Reverse(); - allStreams = ThreadSafeListCreator.MakeThreadSafe(newAllStreams); - } - - if (tablesStream == null) - throw new BadImageFormatException("Missing MD stream"); - - if (hotStreams != null) { - hotStreams.Reverse(); - InitializeHotStreams(hotStreams); - } - - tablesStream.Initialize(peImage); - } - - int GetPointerSize() { - return peImage.ImageNTHeaders.OptionalHeader.Magic == 0x10B ? 4 : 8; - } - - void InitializeHotStreams(IList hotStreams) { - if (hotStreams == null || hotStreams.Count == 0) - return; - - // If this is a 32-bit image, make sure that we emulate this by masking - // all base offsets to 32 bits. - long offsetMask = GetPointerSize() == 8 ? -1L : uint.MaxValue; - - // It's always the last one found that is used - var hotTable = hotStreams[hotStreams.Count - 1].HotTableStream; - if (hotTable != null) { - hotTable.Initialize(offsetMask); - tablesStream.HotTableStream = hotTable; - } - - HotHeapStream hotStrings = null, hotBlob = null, hotGuid = null, hotUs = null; - for (int i = hotStreams.Count - 1; i >= 0; i--) { - var hotStream = hotStreams[i]; - var hotHeapStreams = hotStream.HotHeapStreams; - if (hotHeapStreams == null) - continue; - - // It's always the last one found that is used - for (int j = hotHeapStreams.Count - 1; j >= 0; j--) { - var hotHeap = hotHeapStreams[j]; - switch (hotHeap.HeapType) { - case HeapType.Strings: - if (hotStrings == null) { - hotHeap.Initialize(offsetMask); - hotStrings = hotHeap; - } - break; - - case HeapType.Guid: - if (hotGuid == null) { - hotHeap.Initialize(offsetMask); - hotGuid = hotHeap; - } - break; - - case HeapType.Blob: - if (hotBlob == null) { - hotHeap.Initialize(offsetMask); - hotBlob = hotHeap; - } - break; - - case HeapType.US: - if (hotUs == null) { - hotHeap.Initialize(offsetMask); - hotUs = hotHeap; - } - break; - } - } - } - InitializeNonExistentHeaps(); - stringsStream.HotHeapStream = hotStrings; - guidStream.HotHeapStream = hotGuid; - blobStream.HotHeapStream = hotBlob; - usStream.HotHeapStream = hotUs; - } - - /// - public override RidList GetFieldRidList(uint typeDefRid) { - return GetRidList(tablesStream.TypeDefTable, typeDefRid, 4, tablesStream.FieldTable); - } - - /// - public override RidList GetMethodRidList(uint typeDefRid) { - return GetRidList(tablesStream.TypeDefTable, typeDefRid, 5, tablesStream.MethodTable); - } - - /// - public override RidList GetParamRidList(uint methodRid) { - return GetRidList(tablesStream.MethodTable, methodRid, 5, tablesStream.ParamTable); - } - - /// - public override RidList GetEventRidList(uint eventMapRid) { - return GetRidList(tablesStream.EventMapTable, eventMapRid, 1, tablesStream.EventTable); - } - - /// - public override RidList GetPropertyRidList(uint propertyMapRid) { - return GetRidList(tablesStream.PropertyMapTable, propertyMapRid, 1, tablesStream.PropertyTable); - } - - /// - /// Gets a rid list (eg. field list) - /// - /// Source table, eg. TypeDef - /// Row ID in - /// Column index in , eg. 4 for TypeDef.FieldList - /// Destination table, eg. Field - /// A new instance - RidList GetRidList(MDTable tableSource, uint tableSourceRid, int colIndex, MDTable tableDest) { - var column = tableSource.TableInfo.Columns[colIndex]; - uint startRid, nextListRid; - bool hasNext; -#if THREAD_SAFE - tablesStream.theLock.EnterWriteLock(); try { -#endif - if (!tablesStream.ReadColumn_NoLock(tableSource, tableSourceRid, column, out startRid)) - return RidList.Empty; - hasNext = tablesStream.ReadColumn_NoLock(tableSource, tableSourceRid + 1, column, out nextListRid); -#if THREAD_SAFE - } finally { tablesStream.theLock.ExitWriteLock(); } -#endif - uint lastRid = tableDest.Rows + 1; - if (startRid == 0 || startRid >= lastRid) - return RidList.Empty; - uint endRid = hasNext ? nextListRid : lastRid; - if (endRid < startRid) - endRid = startRid; - if (endRid > lastRid) - endRid = lastRid; - return new ContiguousRidList(startRid, endRid - startRid); - } - - /// - protected override uint BinarySearch_NoLock(MDTable tableSource, int keyColIndex, uint key) { - var keyColumn = tableSource.TableInfo.Columns[keyColIndex]; - uint ridLo = 1, ridHi = tableSource.Rows; - while (ridLo <= ridHi) { - uint rid = (ridLo + ridHi) / 2; - uint key2; - if (!tablesStream.ReadColumn_NoLock(tableSource, rid, keyColumn, out key2)) - break; // Never happens since rid is valid - if (key == key2) - return rid; - if (key2 > key) - ridHi = rid - 1; - else - ridLo = rid + 1; - } - - return 0; - } - } -} diff --git a/src/DotNet/MD/CompressedMetadata.cs b/src/DotNet/MD/CompressedMetadata.cs new file mode 100644 index 000000000..680620558 --- /dev/null +++ b/src/DotNet/MD/CompressedMetadata.cs @@ -0,0 +1,178 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Collections.Generic; +using dnlib.IO; +using dnlib.PE; + +namespace dnlib.DotNet.MD { + /// + /// Used when a #~ stream is present in the metadata + /// + sealed class CompressedMetadata : MetadataBase { + readonly CLRRuntimeReaderKind runtime; + + /// + public override bool IsCompressed => true; + + /// + public CompressedMetadata(IPEImage peImage, ImageCor20Header cor20Header, MetadataHeader mdHeader, CLRRuntimeReaderKind runtime) + : base(peImage, cor20Header, mdHeader) { + this.runtime = runtime; + } + + /// + internal CompressedMetadata(MetadataHeader mdHeader, bool isStandalonePortablePdb, CLRRuntimeReaderKind runtime) + : base(mdHeader, isStandalonePortablePdb) { + this.runtime = runtime; + } + + /// + protected override void InitializeInternal(DataReaderFactory mdReaderFactory, uint metadataBaseOffset) { + DotNetStream dns = null; + var newAllStreams = new List(allStreams); + bool forceAllBig = false; + try { + for (int i = mdHeader.StreamHeaders.Count - 1; i >= 0; i--) { + var sh = mdHeader.StreamHeaders[i]; + switch (sh.Name) { + case "#Strings": + if (stringsStream is null) { + stringsStream = new StringsStream(mdReaderFactory, metadataBaseOffset, sh); + newAllStreams.Add(stringsStream); + continue; + } + break; + + case "#US": + if (usStream is null) { + usStream = new USStream(mdReaderFactory, metadataBaseOffset, sh); + newAllStreams.Add(usStream); + continue; + } + break; + + case "#Blob": + if (blobStream is null) { + blobStream = new BlobStream(mdReaderFactory, metadataBaseOffset, sh); + newAllStreams.Add(blobStream); + continue; + } + break; + + case "#GUID": + if (guidStream is null) { + guidStream = new GuidStream(mdReaderFactory, metadataBaseOffset, sh); + newAllStreams.Add(guidStream); + continue; + } + break; + + case "#~": + if (tablesStream is null) { + tablesStream = new TablesStream(mdReaderFactory, metadataBaseOffset, sh, runtime); + newAllStreams.Add(tablesStream); + continue; + } + break; + + case "#Pdb": + if (isStandalonePortablePdb && pdbStream is null) { + pdbStream = new PdbStream(mdReaderFactory, metadataBaseOffset, sh); + newAllStreams.Add(pdbStream); + continue; + } + break; + + case "#JTD": + if (runtime == CLRRuntimeReaderKind.Mono) { + forceAllBig = true; + continue; + } + break; + } + dns = new CustomDotNetStream(mdReaderFactory, metadataBaseOffset, sh); + newAllStreams.Add(dns); + dns = null; + } + } + finally { + dns?.Dispose(); + newAllStreams.Reverse(); + allStreams = newAllStreams; + } + + if (tablesStream is null) + throw new BadImageFormatException("Missing MD stream"); + + if (pdbStream is not null) + tablesStream.Initialize(pdbStream.TypeSystemTableRows, forceAllBig); + else + tablesStream.Initialize(null, forceAllBig); + } + + /// + public override RidList GetFieldRidList(uint typeDefRid) => GetRidList(tablesStream.TypeDefTable, typeDefRid, 4, tablesStream.FieldTable); + + /// + public override RidList GetMethodRidList(uint typeDefRid) => GetRidList(tablesStream.TypeDefTable, typeDefRid, 5, tablesStream.MethodTable); + + /// + public override RidList GetParamRidList(uint methodRid) => GetRidList(tablesStream.MethodTable, methodRid, 5, tablesStream.ParamTable); + + /// + public override RidList GetEventRidList(uint eventMapRid) => GetRidList(tablesStream.EventMapTable, eventMapRid, 1, tablesStream.EventTable); + + /// + public override RidList GetPropertyRidList(uint propertyMapRid) => GetRidList(tablesStream.PropertyMapTable, propertyMapRid, 1, tablesStream.PropertyTable); + + /// + public override RidList GetLocalVariableRidList(uint localScopeRid) => GetRidList(tablesStream.LocalScopeTable, localScopeRid, 2, tablesStream.LocalVariableTable); + + /// + public override RidList GetLocalConstantRidList(uint localScopeRid) => GetRidList(tablesStream.LocalScopeTable, localScopeRid, 3, tablesStream.LocalConstantTable); + + /// + /// Gets a rid list (eg. field list) + /// + /// Source table, eg. TypeDef + /// Row ID in + /// Column index in , eg. 4 for TypeDef.FieldList + /// Destination table, eg. Field + /// A new instance + RidList GetRidList(MDTable tableSource, uint tableSourceRid, int colIndex, MDTable tableDest) { + var column = tableSource.TableInfo.Columns[colIndex]; + if (!tablesStream.TryReadColumn24(tableSource, tableSourceRid, column, out uint startRid)) + return RidList.Empty; + bool hasNext = tablesStream.TryReadColumn24(tableSource, tableSourceRid + 1, column, out uint nextListRid); + uint lastRid = tableDest.Rows + 1; + if (startRid == 0 || startRid >= lastRid) + return RidList.Empty; + uint endRid = !hasNext || (nextListRid == 0 && tableSourceRid + 1 == tableSource.Rows && tableDest.Rows == 0xFFFF) ? lastRid : nextListRid; + if (endRid < startRid) + endRid = startRid; + if (endRid > lastRid) + endRid = lastRid; + return RidList.Create(startRid, endRid - startRid); + } + + /// + protected override uint BinarySearch(MDTable tableSource, int keyColIndex, uint key) { + var keyColumn = tableSource.TableInfo.Columns[keyColIndex]; + uint ridLo = 1, ridHi = tableSource.Rows; + while (ridLo <= ridHi) { + uint rid = (ridLo + ridHi) / 2; + if (!tablesStream.TryReadColumn24(tableSource, rid, keyColumn, out uint key2)) + break; // Never happens since rid is valid + if (key == key2) + return rid; + if (key2 > key) + ridHi = rid - 1; + else + ridLo = rid + 1; + } + + return 0; + } + } +} diff --git a/src/DotNet/MD/CustomDotNetStream.cs b/src/DotNet/MD/CustomDotNetStream.cs new file mode 100644 index 000000000..d7a914261 --- /dev/null +++ b/src/DotNet/MD/CustomDotNetStream.cs @@ -0,0 +1,25 @@ +// dnlib: See LICENSE.txt for more info + +using dnlib.IO; + +namespace dnlib.DotNet.MD { + /// + /// A custom .NET metadata stream + /// + public class CustomDotNetStream : DotNetStream { + /// + /// Constructor + /// + public CustomDotNetStream() { } + + /// + /// Constructor + /// + /// Data reader factory + /// Offset of metadata + /// The stream header + public CustomDotNetStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader) + : base(mdReaderFactory, metadataBaseOffset, streamHeader) { + } + } +} diff --git a/src/DotNet/MD/DotNetStream.cs b/src/DotNet/MD/DotNetStream.cs index 83c753716..b6e6f3cf1 100644 --- a/src/DotNet/MD/DotNetStream.cs +++ b/src/DotNet/MD/DotNetStream.cs @@ -3,81 +3,94 @@ using System; using System.Diagnostics; using dnlib.IO; -using dnlib.Threading; namespace dnlib.DotNet.MD { /// /// .NET metadata stream /// - [DebuggerDisplay("{imageStream.Length} {streamHeader.Name}")] - public class DotNetStream : IFileSection, IDisposable { + [DebuggerDisplay("{dataReader.Length} {streamHeader.Name}")] + public abstract class DotNetStream : IFileSection, IDisposable { /// - /// Reader that can access the whole stream + /// Reader that can access the whole stream. + /// + /// NOTE: Always copy this field to a local variable before using it since it must be thread safe. /// - protected IImageStream imageStream; + protected DataReader dataReader; /// /// null if it wasn't present in the file /// StreamHeader streamHeader; + DataReaderFactory mdReaderFactory; + uint metadataBaseOffset; + /// - public FileOffset StartOffset { - get { return imageStream.FileOffset; } - } + public FileOffset StartOffset => (FileOffset)dataReader.StartOffset; /// - public FileOffset EndOffset { - get { return imageStream.FileOffset + imageStream.Length; } - } + public FileOffset EndOffset => (FileOffset)dataReader.EndOffset; /// - /// Gets the length of the internal .NET blob stream + /// Gets the length of this stream in the metadata /// - public long ImageStreamLength { - get { return imageStream.Length; } - } + public uint StreamLength => dataReader.Length; /// /// Gets the stream header /// - public StreamHeader StreamHeader { - get { return streamHeader; } - } + public StreamHeader StreamHeader => streamHeader; /// /// Gets the name of the stream /// - public string Name { - get { return streamHeader == null ? string.Empty : streamHeader.Name; } - } + public string Name => streamHeader is null ? string.Empty : streamHeader.Name; /// - /// Returns a cloned of the internal .NET blob stream. + /// Gets a data reader that can read the full stream /// - /// A new instance - public IImageStream GetClonedImageStream() { - return imageStream.Clone(); - } + /// + public DataReader CreateReader() => dataReader; /// /// Default constructor /// - public DotNetStream() { - this.imageStream = MemoryImageStream.CreateEmpty(); - this.streamHeader = null; + protected DotNetStream() { + streamHeader = null; + dataReader = default; } /// /// Constructor /// - /// Stream data + /// Data reader factory + /// Offset of metadata /// The stream header - public DotNetStream(IImageStream imageStream, StreamHeader streamHeader) { - this.imageStream = imageStream; + protected DotNetStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader) { + this.mdReaderFactory = mdReaderFactory; + mdReaderFactory.DataReaderInvalidated += DataReaderFactory_DataReaderInvalidated; + this.mdReaderFactory = mdReaderFactory; + this.metadataBaseOffset = metadataBaseOffset; this.streamHeader = streamHeader; + RecreateReader(mdReaderFactory, metadataBaseOffset, streamHeader, notifyThisClass: false); } + void DataReaderFactory_DataReaderInvalidated(object sender, EventArgs e) => RecreateReader(mdReaderFactory, metadataBaseOffset, streamHeader, notifyThisClass: true); + + void RecreateReader(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader, bool notifyThisClass) { + if (mdReaderFactory is null || streamHeader is null) + dataReader = default; + else + dataReader = mdReaderFactory.CreateReader(metadataBaseOffset + streamHeader.Offset, streamHeader.StreamSize); + if (notifyThisClass) + OnReaderRecreated(); + } + + /// + /// Called after gets recreated + /// + protected virtual void OnReaderRecreated() { } + /// public void Dispose() { Dispose(true); @@ -90,11 +103,11 @@ public void Dispose() { /// true if called by protected virtual void Dispose(bool disposing) { if (disposing) { - var ims = imageStream; - if (ims != null) - ims.Dispose(); - imageStream = null; + var mdReaderFactory = this.mdReaderFactory; + if (mdReaderFactory is not null) + mdReaderFactory.DataReaderInvalidated -= DataReaderFactory_DataReaderInvalidated; streamHeader = null; + this.mdReaderFactory = null; } } @@ -103,18 +116,14 @@ protected virtual void Dispose(bool disposing) { /// /// The index /// true if the index is valid - public virtual bool IsValidIndex(uint index) { - return IsValidOffset(index); - } + public virtual bool IsValidIndex(uint index) => IsValidOffset(index); /// /// Check whether an offset is within the stream /// /// Stream offset /// true if the offset is valid - public bool IsValidOffset(uint offset) { - return offset == 0 || offset < imageStream.Length; - } + public bool IsValidOffset(uint offset) => offset == 0 || offset < dataReader.Length; /// /// Check whether an offset is within the stream @@ -125,7 +134,7 @@ public bool IsValidOffset(uint offset) { public bool IsValidOffset(uint offset, int size) { if (size == 0) return IsValidOffset(offset); - return size > 0 && (long)offset + (uint)size <= imageStream.Length; + return size > 0 && (ulong)offset + (uint)size <= dataReader.Length; } } @@ -133,51 +142,13 @@ public bool IsValidOffset(uint offset, int size) { /// Base class of #US, #Strings, #Blob, and #GUID classes /// public abstract class HeapStream : DotNetStream { - HotHeapStream hotHeapStream; -#if THREAD_SAFE - internal readonly Lock theLock = Lock.Create(); -#endif - - /// - /// Gets/sets the instance - /// - internal HotHeapStream HotHeapStream { - set { hotHeapStream = value; } - } - /// protected HeapStream() { } /// - protected HeapStream(IImageStream imageStream, StreamHeader streamHeader) - : base(imageStream, streamHeader) { - } - - /// - /// Gets the heap reader and initializes its position - /// - /// Offset in the heap. If it's the #GUID heap, this should - /// be the offset of the GUID, not its index - /// The heap reader - protected IImageStream GetReader_NoLock(uint offset) { - var stream = hotHeapStream == null ? null : hotHeapStream.GetBlobReader(offset); - if (stream == null) { - stream = imageStream; - stream.Position = offset; - } - return stream; - } - - /// - protected override void Dispose(bool disposing) { - if (disposing) { - var hhs = hotHeapStream; - if (hhs != null) - hhs.Dispose(); - hotHeapStream = null; - } - base.Dispose(disposing); + protected HeapStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader) + : base(mdReaderFactory, metadataBaseOffset, streamHeader) { } } } diff --git a/src/DotNet/MD/DotNetTableSizes.cs b/src/DotNet/MD/DotNetTableSizes.cs index 197505d2f..26ea002c7 100644 --- a/src/DotNet/MD/DotNetTableSizes.cs +++ b/src/DotNet/MD/DotNetTableSizes.cs @@ -1,75 +1,93 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; namespace dnlib.DotNet.MD { /// /// Initializes .NET table row sizes /// - sealed class DotNetTableSizes { + public sealed class DotNetTableSizes { bool bigStrings; bool bigGuid; bool bigBlob; - IList rowCounts; + bool forceAllBig; TableInfo[] tableInfos; + internal static bool IsSystemTable(Table table) => table < Table.Document; + + /// + /// Initializes the table sizes + /// + /// true if #Strings size >= 0x10000 + /// true if #GUID size >= 0x10000 + /// true if #Blob size >= 0x10000 + /// Count of rows in each table + /// Count of rows in each table (debug tables) + public void InitializeSizes(bool bigStrings, bool bigGuid, bool bigBlob, IList systemRowCounts, IList debugRowCounts) => + InitializeSizes(bigStrings, bigGuid, bigBlob, systemRowCounts, debugRowCounts, false); + /// /// Initializes the table sizes /// /// true if #Strings size >= 0x10000 /// true if #GUID size >= 0x10000 /// true if #Blob size >= 0x10000 - /// Count of rows in each table - public void InitializeSizes(bool bigStrings, bool bigGuid, bool bigBlob, IList rowCounts) { - this.bigStrings = bigStrings; - this.bigGuid = bigGuid; - this.bigBlob = bigBlob; - this.rowCounts = rowCounts; + /// Count of rows in each table + /// Count of rows in each table (debug tables) + /// Force all columns to 4 bytes instead of 2 or 4 bytes + internal void InitializeSizes(bool bigStrings, bool bigGuid, bool bigBlob, IList systemRowCounts, IList debugRowCounts, bool forceAllBig) { + this.bigStrings = bigStrings || forceAllBig; + this.bigGuid = bigGuid || forceAllBig; + this.bigBlob = bigBlob || forceAllBig; + this.forceAllBig = forceAllBig; foreach (var tableInfo in tableInfos) { + var rowCounts = IsSystemTable(tableInfo.Table) ? systemRowCounts : debugRowCounts; int colOffset = 0; foreach (var colInfo in tableInfo.Columns) { colInfo.Offset = colOffset; - var colSize = GetSize(colInfo.ColumnSize); + var colSize = GetSize(colInfo.ColumnSize, rowCounts); colInfo.Size = colSize; - colOffset += colSize + (colSize & 1); + colOffset += colSize; } tableInfo.RowSize = colOffset; } } - int GetSize(ColumnSize columnSize) { - if (ColumnSize.Module <= columnSize && columnSize <= ColumnSize.GenericParamConstraint) { + int GetSize(ColumnSize columnSize, IList rowCounts) { + if (ColumnSize.Module <= columnSize && columnSize <= ColumnSize.CustomDebugInformation) { int table = (int)(columnSize - ColumnSize.Module); - return rowCounts[table] > 0xFFFF ? 4 : 2; + uint count = table >= rowCounts.Count ? 0 : rowCounts[table]; + return forceAllBig || count > 0xFFFF ? 4 : 2; } - else if (ColumnSize.TypeDefOrRef <= columnSize && columnSize <= ColumnSize.TypeOrMethodDef) { - CodedToken info; - switch (columnSize) { - case ColumnSize.TypeDefOrRef: info = CodedToken.TypeDefOrRef; break; - case ColumnSize.HasConstant: info = CodedToken.HasConstant; break; - case ColumnSize.HasCustomAttribute: info = CodedToken.HasCustomAttribute; break; - case ColumnSize.HasFieldMarshal: info = CodedToken.HasFieldMarshal; break; - case ColumnSize.HasDeclSecurity: info = CodedToken.HasDeclSecurity; break; - case ColumnSize.MemberRefParent: info = CodedToken.MemberRefParent; break; - case ColumnSize.HasSemantic: info = CodedToken.HasSemantic; break; - case ColumnSize.MethodDefOrRef: info = CodedToken.MethodDefOrRef; break; - case ColumnSize.MemberForwarded: info = CodedToken.MemberForwarded; break; - case ColumnSize.Implementation: info = CodedToken.Implementation; break; - case ColumnSize.CustomAttributeType:info = CodedToken.CustomAttributeType; break; - case ColumnSize.ResolutionScope: info = CodedToken.ResolutionScope; break; - case ColumnSize.TypeOrMethodDef: info = CodedToken.TypeOrMethodDef; break; - default: throw new InvalidOperationException(string.Format("Invalid ColumnSize: {0}", columnSize)); - } + else if (ColumnSize.TypeDefOrRef <= columnSize && columnSize <= ColumnSize.HasCustomDebugInformation) { + var info = columnSize switch { + ColumnSize.TypeDefOrRef => CodedToken.TypeDefOrRef, + ColumnSize.HasConstant => CodedToken.HasConstant, + ColumnSize.HasCustomAttribute => CodedToken.HasCustomAttribute, + ColumnSize.HasFieldMarshal => CodedToken.HasFieldMarshal, + ColumnSize.HasDeclSecurity => CodedToken.HasDeclSecurity, + ColumnSize.MemberRefParent => CodedToken.MemberRefParent, + ColumnSize.HasSemantic => CodedToken.HasSemantic, + ColumnSize.MethodDefOrRef => CodedToken.MethodDefOrRef, + ColumnSize.MemberForwarded => CodedToken.MemberForwarded, + ColumnSize.Implementation => CodedToken.Implementation, + ColumnSize.CustomAttributeType => CodedToken.CustomAttributeType, + ColumnSize.ResolutionScope => CodedToken.ResolutionScope, + ColumnSize.TypeOrMethodDef => CodedToken.TypeOrMethodDef, + ColumnSize.HasCustomDebugInformation => CodedToken.HasCustomDebugInformation, + _ => throw new InvalidOperationException($"Invalid ColumnSize: {columnSize}"), + }; uint maxRows = 0; foreach (var tableType in info.TableTypes) { - var tableRows = rowCounts[(int)tableType]; + int index = (int)tableType; + var tableRows = index >= rowCounts.Count ? 0 : rowCounts[index]; if (tableRows > maxRows) maxRows = tableRows; } // Can't overflow since maxRows <= 0x00FFFFFF and info.Bits < 8 uint finalRows = maxRows << info.Bits; - return finalRows > 0xFFFF ? 4 : 2; + return forceAllBig || finalRows > 0xFFFF ? 4 : 2; } else { switch (columnSize) { @@ -78,12 +96,12 @@ int GetSize(ColumnSize columnSize) { case ColumnSize.UInt16: return 2; case ColumnSize.Int32: return 4; case ColumnSize.UInt32: return 4; - case ColumnSize.Strings:return bigStrings ? 4 : 2; - case ColumnSize.GUID: return bigGuid ? 4 : 2; - case ColumnSize.Blob: return bigBlob ? 4 : 2; + case ColumnSize.Strings:return forceAllBig || bigStrings ? 4 : 2; + case ColumnSize.GUID: return forceAllBig || bigGuid ? 4 : 2; + case ColumnSize.Blob: return forceAllBig || bigBlob ? 4 : 2; } } - throw new InvalidOperationException(string.Format("Invalid ColumnSize: {0}", columnSize)); + throw new InvalidOperationException($"Invalid ColumnSize: {columnSize}"); } /// @@ -92,10 +110,10 @@ int GetSize(ColumnSize columnSize) { /// Major table version /// Minor table version /// All table infos (not completely initialized) - public TableInfo[] CreateTables(byte majorVersion, byte minorVersion) { - int maxPresentTables; - return CreateTables(majorVersion, minorVersion, out maxPresentTables); - } + public TableInfo[] CreateTables(byte majorVersion, byte minorVersion) => + CreateTables(majorVersion, minorVersion, out int maxPresentTables); + + internal const int normalMaxTables = (int)Table.CustomDebugInformation + 1; /// /// Creates the table infos @@ -105,10 +123,9 @@ public TableInfo[] CreateTables(byte majorVersion, byte minorVersion) { /// Initialized to max present tables (eg. 42 or 45) /// All table infos (not completely initialized) public TableInfo[] CreateTables(byte majorVersion, byte minorVersion, out int maxPresentTables) { - // The three extra generics tables aren't used by CLR 1.x - maxPresentTables = (majorVersion == 1 && minorVersion == 0) ? (int)Table.NestedClass + 1 : (int)Table.GenericParamConstraint + 1; + maxPresentTables = (majorVersion == 1 && minorVersion == 0) ? (int)Table.NestedClass + 1 : normalMaxTables; - var tableInfos = new TableInfo[(int)Table.GenericParamConstraint + 1]; + var tableInfos = new TableInfo[normalMaxTables]; tableInfos[(int)Table.Module] = new TableInfo(Table.Module, "Module", new ColumnInfo[] { new ColumnInfo(0, "Generation", ColumnSize.UInt16), @@ -168,8 +185,9 @@ public TableInfo[] CreateTables(byte majorVersion, byte minorVersion, out int ma }); tableInfos[(int)Table.Constant] = new TableInfo(Table.Constant, "Constant", new ColumnInfo[] { new ColumnInfo(0, "Type", ColumnSize.Byte), - new ColumnInfo(1, "Parent", ColumnSize.HasConstant), - new ColumnInfo(2, "Value", ColumnSize.Blob), + new ColumnInfo(1, "Padding", ColumnSize.Byte), + new ColumnInfo(2, "Parent", ColumnSize.HasConstant), + new ColumnInfo(3, "Value", ColumnSize.Blob), }); tableInfos[(int)Table.CustomAttribute] = new TableInfo(Table.CustomAttribute, "CustomAttribute", new ColumnInfo[] { new ColumnInfo(0, "Parent", ColumnSize.HasCustomAttribute), @@ -341,6 +359,49 @@ public TableInfo[] CreateTables(byte majorVersion, byte minorVersion, out int ma new ColumnInfo(0, "Owner", ColumnSize.GenericParam), new ColumnInfo(1, "Constraint", ColumnSize.TypeDefOrRef), }); + tableInfos[0x2D] = new TableInfo((Table)0x2D, string.Empty, new ColumnInfo[] { }); + tableInfos[0x2E] = new TableInfo((Table)0x2E, string.Empty, new ColumnInfo[] { }); + tableInfos[0x2F] = new TableInfo((Table)0x2F, string.Empty, new ColumnInfo[] { }); + tableInfos[(int)Table.Document] = new TableInfo(Table.Document, "Document", new ColumnInfo[] { + new ColumnInfo(0, "Name", ColumnSize.Blob), + new ColumnInfo(1, "HashAlgorithm", ColumnSize.GUID), + new ColumnInfo(2, "Hash", ColumnSize.Blob), + new ColumnInfo(3, "Language", ColumnSize.GUID), + }); + tableInfos[(int)Table.MethodDebugInformation] = new TableInfo(Table.MethodDebugInformation, "MethodDebugInformation", new ColumnInfo[] { + new ColumnInfo(0, "Document", ColumnSize.Document), + new ColumnInfo(1, "SequencePoints", ColumnSize.Blob), + }); + tableInfos[(int)Table.LocalScope] = new TableInfo(Table.LocalScope, "LocalScope", new ColumnInfo[] { + new ColumnInfo(0, "Method", ColumnSize.Method), + new ColumnInfo(1, "ImportScope", ColumnSize.ImportScope), + new ColumnInfo(2, "VariableList", ColumnSize.LocalVariable), + new ColumnInfo(3, "ConstantList", ColumnSize.LocalConstant), + new ColumnInfo(4, "StartOffset", ColumnSize.UInt32), + new ColumnInfo(5, "Length", ColumnSize.UInt32), + }); + tableInfos[(int)Table.LocalVariable] = new TableInfo(Table.LocalVariable, "LocalVariable", new ColumnInfo[] { + new ColumnInfo(0, "Attributes", ColumnSize.UInt16), + new ColumnInfo(1, "Index", ColumnSize.UInt16), + new ColumnInfo(2, "Name", ColumnSize.Strings), + }); + tableInfos[(int)Table.LocalConstant] = new TableInfo(Table.LocalConstant, "LocalConstant", new ColumnInfo[] { + new ColumnInfo(0, "Name", ColumnSize.Strings), + new ColumnInfo(1, "Signature", ColumnSize.Blob), + }); + tableInfos[(int)Table.ImportScope] = new TableInfo(Table.ImportScope, "ImportScope", new ColumnInfo[] { + new ColumnInfo(0, "Parent", ColumnSize.ImportScope), + new ColumnInfo(1, "Imports", ColumnSize.Blob), + }); + tableInfos[(int)Table.StateMachineMethod] = new TableInfo(Table.StateMachineMethod, "StateMachineMethod", new ColumnInfo[] { + new ColumnInfo(0, "MoveNextMethod", ColumnSize.Method), + new ColumnInfo(1, "KickoffMethod", ColumnSize.Method), + }); + tableInfos[(int)Table.CustomDebugInformation] = new TableInfo(Table.CustomDebugInformation, "CustomDebugInformation", new ColumnInfo[] { + new ColumnInfo(0, "Parent", ColumnSize.HasCustomDebugInformation), + new ColumnInfo(1, "Kind", ColumnSize.GUID), + new ColumnInfo(2, "Value", ColumnSize.Blob), + }); return this.tableInfos = tableInfos; } } diff --git a/src/DotNet/MD/ENCMetaData.cs b/src/DotNet/MD/ENCMetadata.cs similarity index 51% rename from src/DotNet/MD/ENCMetaData.cs rename to src/DotNet/MD/ENCMetadata.cs index 465e5501f..5e8645f1e 100644 --- a/src/DotNet/MD/ENCMetaData.cs +++ b/src/DotNet/MD/ENCMetadata.cs @@ -1,7 +1,8 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; +using System.Diagnostics; using dnlib.IO; using dnlib.PE; using dnlib.Threading; @@ -10,97 +11,178 @@ namespace dnlib.DotNet.MD { /// /// Used when a #- stream is present in the metadata /// - sealed class ENCMetaData : MetaData { + sealed class ENCMetadata : MetadataBase { static readonly UTF8String DeletedName = "_Deleted"; bool hasMethodPtr, hasFieldPtr, hasParamPtr, hasEventPtr, hasPropertyPtr; - bool hasDeletedRows; + bool hasDeletedFields; + bool hasDeletedNonFields; + readonly CLRRuntimeReaderKind runtime; readonly Dictionary sortedTables = new Dictionary(); #if THREAD_SAFE readonly Lock theLock = Lock.Create(); #endif /// - public override bool IsCompressed { - get { return false; } - } + public override bool IsCompressed => false; /// - public ENCMetaData(IPEImage peImage, ImageCor20Header cor20Header, MetaDataHeader mdHeader) + public ENCMetadata(IPEImage peImage, ImageCor20Header cor20Header, MetadataHeader mdHeader, CLRRuntimeReaderKind runtime) : base(peImage, cor20Header, mdHeader) { + this.runtime = runtime; } /// - protected override void InitializeInternal() { - IImageStream imageStream = null; + internal ENCMetadata(MetadataHeader mdHeader, bool isStandalonePortablePdb, CLRRuntimeReaderKind runtime) + : base(mdHeader, isStandalonePortablePdb) { + this.runtime = runtime; + } + + /// + protected override void InitializeInternal(DataReaderFactory mdReaderFactory, uint metadataBaseOffset) { DotNetStream dns = null; + bool forceAllBig = false; try { - var mdRva = cor20Header.MetaData.VirtualAddress; - foreach (var sh in mdHeader.StreamHeaders) { - var rva = mdRva + sh.Offset; - imageStream = peImage.CreateStream(rva, sh.StreamSize); - switch (sh.Name.ToUpperInvariant()) { - case "#STRINGS": - if (stringsStream == null) { - stringsStream = new StringsStream(imageStream, sh); - imageStream = null; - allStreams.Add(stringsStream); - continue; - } - break; - - case "#US": - if (usStream == null) { - usStream = new USStream(imageStream, sh); - imageStream = null; - allStreams.Add(usStream); - continue; - } - break; - - case "#BLOB": - if (blobStream == null) { - blobStream = new BlobStream(imageStream, sh); - imageStream = null; - allStreams.Add(blobStream); + if (runtime == CLRRuntimeReaderKind.Mono) { + var newAllStreams = new List(allStreams); + for (int i = mdHeader.StreamHeaders.Count - 1; i >= 0; i--) { + var sh = mdHeader.StreamHeaders[i]; + switch (sh.Name) { + case "#Strings": + if (stringsStream is null) { + stringsStream = new StringsStream(mdReaderFactory, metadataBaseOffset, sh); + newAllStreams.Add(stringsStream); + continue; + } + break; + + case "#US": + if (usStream is null) { + usStream = new USStream(mdReaderFactory, metadataBaseOffset, sh); + newAllStreams.Add(usStream); + continue; + } + break; + + case "#Blob": + if (blobStream is null) { + blobStream = new BlobStream(mdReaderFactory, metadataBaseOffset, sh); + newAllStreams.Add(blobStream); + continue; + } + break; + + case "#GUID": + if (guidStream is null) { + guidStream = new GuidStream(mdReaderFactory, metadataBaseOffset, sh); + newAllStreams.Add(guidStream); + continue; + } + break; + + case "#~": + case "#-": + if (tablesStream is null) { + tablesStream = new TablesStream(mdReaderFactory, metadataBaseOffset, sh, runtime); + newAllStreams.Add(tablesStream); + continue; + } + break; + + case "#Pdb": + if (isStandalonePortablePdb && pdbStream is null) { + pdbStream = new PdbStream(mdReaderFactory, metadataBaseOffset, sh); + newAllStreams.Add(pdbStream); + continue; + } + break; + + case "#JTD": + forceAllBig = true; continue; } - break; - - case "#GUID": - if (guidStream == null) { - guidStream = new GuidStream(imageStream, sh); - imageStream = null; - allStreams.Add(guidStream); - continue; - } - break; - - case "#~": // Only if #Schema is used - case "#-": - if (tablesStream == null) { - tablesStream = new TablesStream(imageStream, sh); - imageStream = null; - allStreams.Add(tablesStream); + dns = new CustomDotNetStream(mdReaderFactory, metadataBaseOffset, sh); + newAllStreams.Add(dns); + dns = null; + } + newAllStreams.Reverse(); + allStreams = newAllStreams; + } + else { + Debug.Assert(runtime == CLRRuntimeReaderKind.CLR); + foreach (var sh in mdHeader.StreamHeaders) { + switch (sh.Name.ToUpperInvariant()) { + case "#STRINGS": + if (stringsStream is null) { + stringsStream = new StringsStream(mdReaderFactory, metadataBaseOffset, sh); + allStreams.Add(stringsStream); + continue; + } + break; + + case "#US": + if (usStream is null) { + usStream = new USStream(mdReaderFactory, metadataBaseOffset, sh); + allStreams.Add(usStream); + continue; + } + break; + + case "#BLOB": + if (blobStream is null) { + blobStream = new BlobStream(mdReaderFactory, metadataBaseOffset, sh); + allStreams.Add(blobStream); + continue; + } + break; + + case "#GUID": + if (guidStream is null) { + guidStream = new GuidStream(mdReaderFactory, metadataBaseOffset, sh); + allStreams.Add(guidStream); + continue; + } + break; + + case "#~": // Only if #Schema is used + case "#-": + if (tablesStream is null) { + tablesStream = new TablesStream(mdReaderFactory, metadataBaseOffset, sh, runtime); + allStreams.Add(tablesStream); + continue; + } + break; + + case "#PDB": + // Case sensitive comparison since it's a stream that's not read by the CLR, + // only by other libraries eg. System.Reflection.Metadata. + if (isStandalonePortablePdb && pdbStream is null && sh.Name == "#Pdb") { + pdbStream = new PdbStream(mdReaderFactory, metadataBaseOffset, sh); + allStreams.Add(pdbStream); + continue; + } + break; + + case "#JTD": + forceAllBig = true; continue; } - break; + dns = new CustomDotNetStream(mdReaderFactory, metadataBaseOffset, sh); + allStreams.Add(dns); + dns = null; } - dns = new DotNetStream(imageStream, sh); - imageStream = null; - allStreams.Add(dns); - dns = null; } } finally { - if (imageStream != null) - imageStream.Dispose(); - if (dns != null) - dns.Dispose(); + dns?.Dispose(); } - if (tablesStream == null) + if (tablesStream is null) throw new BadImageFormatException("Missing MD stream"); - tablesStream.Initialize(peImage); + + if (pdbStream is not null) + tablesStream.Initialize(pdbStream.TypeSystemTableRows, forceAllBig); + else + tablesStream.Initialize(null, forceAllBig); // The pointer tables are used iff row count != 0 hasFieldPtr = !tablesStream.FieldPtrTable.IsEmpty; @@ -108,38 +190,51 @@ protected override void InitializeInternal() { hasParamPtr = !tablesStream.ParamPtrTable.IsEmpty; hasEventPtr = !tablesStream.EventPtrTable.IsEmpty; hasPropertyPtr = !tablesStream.PropertyPtrTable.IsEmpty; - hasDeletedRows = tablesStream.HasDelete; + + switch (runtime) { + case CLRRuntimeReaderKind.CLR: + hasDeletedFields = tablesStream.HasDelete; + hasDeletedNonFields = tablesStream.HasDelete; + break; + + case CLRRuntimeReaderKind.Mono: + hasDeletedFields = true; + hasDeletedNonFields = false; + break; + + default: + throw new InvalidOperationException(); + } } /// public override RidList GetTypeDefRidList() { - if (!hasDeletedRows) + if (!hasDeletedNonFields) return base.GetTypeDefRidList(); uint rows = tablesStream.TypeDefTable.Rows; - var list = new RandomRidList((int)rows); + var list = new List((int)rows); for (uint rid = 1; rid <= rows; rid++) { - var row = tablesStream.ReadTypeDefRow(rid); - if (row == null) + if (!tablesStream.TryReadTypeDefRow(rid, out var row)) continue; // Should never happen since rid is valid // RTSpecialName is ignored by the CLR. It's only the name that indicates // whether it's been deleted. - if (stringsStream.ReadNoNull(row.Name).StartsWith(DeletedName)) + // It's not possible to delete the global type () + if (rid != 1 && stringsStream.ReadNoNull(row.Name).StartsWith(DeletedName)) continue; // ignore this deleted row list.Add(rid); } - return list; + return RidList.Create(list); } /// public override RidList GetExportedTypeRidList() { - if (!hasDeletedRows) + if (!hasDeletedNonFields) return base.GetExportedTypeRidList(); uint rows = tablesStream.ExportedTypeTable.Rows; - var list = new RandomRidList((int)rows); + var list = new List((int)rows); for (uint rid = 1; rid <= rows; rid++) { - var row = tablesStream.ReadExportedTypeRow(rid); - if (row == null) + if (!tablesStream.TryReadExportedTypeRow(rid, out var row)) continue; // Should never happen since rid is valid // RTSpecialName is ignored by the CLR. It's only the name that indicates @@ -148,7 +243,7 @@ public override RidList GetExportedTypeRidList() { continue; // ignore this deleted row list.Add(rid); } - return list; + return RidList.Create(list); } /// @@ -159,8 +254,7 @@ public override RidList GetExportedTypeRidList() { uint ToFieldRid(uint listRid) { if (!hasFieldPtr) return listRid; - uint listValue; - return tablesStream.ReadColumn(tablesStream.FieldPtrTable, listRid, 0, out listValue) ? listValue : 0; + return tablesStream.TryReadColumn24(tablesStream.FieldPtrTable, listRid, 0, out uint listValue) ? listValue : 0; } /// @@ -171,8 +265,7 @@ uint ToFieldRid(uint listRid) { uint ToMethodRid(uint listRid) { if (!hasMethodPtr) return listRid; - uint listValue; - return tablesStream.ReadColumn(tablesStream.MethodPtrTable, listRid, 0, out listValue) ? listValue : 0; + return tablesStream.TryReadColumn24(tablesStream.MethodPtrTable, listRid, 0, out uint listValue) ? listValue : 0; } /// @@ -183,8 +276,7 @@ uint ToMethodRid(uint listRid) { uint ToParamRid(uint listRid) { if (!hasParamPtr) return listRid; - uint listValue; - return tablesStream.ReadColumn(tablesStream.ParamPtrTable, listRid, 0, out listValue) ? listValue : 0; + return tablesStream.TryReadColumn24(tablesStream.ParamPtrTable, listRid, 0, out uint listValue) ? listValue : 0; } /// @@ -195,8 +287,7 @@ uint ToParamRid(uint listRid) { uint ToEventRid(uint listRid) { if (!hasEventPtr) return listRid; - uint listValue; - return tablesStream.ReadColumn(tablesStream.EventPtrTable, listRid, 0, out listValue) ? listValue : 0; + return tablesStream.TryReadColumn24(tablesStream.EventPtrTable, listRid, 0, out uint listValue) ? listValue : 0; } /// @@ -207,54 +298,59 @@ uint ToEventRid(uint listRid) { uint ToPropertyRid(uint listRid) { if (!hasPropertyPtr) return listRid; - uint listValue; - return tablesStream.ReadColumn(tablesStream.PropertyPtrTable, listRid, 0, out listValue) ? listValue : 0; + return tablesStream.TryReadColumn24(tablesStream.PropertyPtrTable, listRid, 0, out uint listValue) ? listValue : 0; } /// public override RidList GetFieldRidList(uint typeDefRid) { var list = GetRidList(tablesStream.TypeDefTable, typeDefRid, 4, tablesStream.FieldTable); - if (list.Length == 0 || (!hasFieldPtr && !hasDeletedRows)) + if (list.Count == 0 || (!hasFieldPtr && !hasDeletedFields)) return list; var destTable = tablesStream.FieldTable; - var newList = new RandomRidList((int)list.Length); - for (uint i = 0; i < list.Length; i++) { + var newList = new List(list.Count); + for (int i = 0; i < list.Count; i++) { var rid = ToFieldRid(list[i]); if (destTable.IsInvalidRID(rid)) continue; - if (hasDeletedRows) { + if (hasDeletedFields) { // It's a deleted row if RTSpecialName is set and name is "_Deleted" - var row = tablesStream.ReadFieldRow(rid); - if (row == null) + if (!tablesStream.TryReadFieldRow(rid, out var row)) continue; // Should never happen since rid is valid - if ((row.Flags & (uint)FieldAttributes.RTSpecialName) != 0) { - if (stringsStream.ReadNoNull(row.Name).StartsWith(DeletedName)) - continue; // ignore this deleted row + if (runtime == CLRRuntimeReaderKind.CLR) { + if ((row.Flags & (uint)FieldAttributes.RTSpecialName) != 0) { + if (stringsStream.ReadNoNull(row.Name).StartsWith(DeletedName)) + continue; // ignore this deleted row + } + } + else { + if ((row.Flags & (uint)(FieldAttributes.SpecialName | FieldAttributes.RTSpecialName)) == (uint)(FieldAttributes.SpecialName | FieldAttributes.RTSpecialName)) { + if (stringsStream.ReadNoNull(row.Name) == DeletedName) + continue; // ignore this deleted row + } } } // It's a valid non-deleted rid so add it newList.Add(rid); } - return newList; + return RidList.Create(newList); } /// public override RidList GetMethodRidList(uint typeDefRid) { var list = GetRidList(tablesStream.TypeDefTable, typeDefRid, 5, tablesStream.MethodTable); - if (list.Length == 0 || (!hasMethodPtr && !hasDeletedRows)) + if (list.Count == 0 || (!hasMethodPtr && !hasDeletedNonFields)) return list; var destTable = tablesStream.MethodTable; - var newList = new RandomRidList((int)list.Length); - for (uint i = 0; i < list.Length; i++) { + var newList = new List(list.Count); + for (int i = 0; i < list.Count; i++) { var rid = ToMethodRid(list[i]); if (destTable.IsInvalidRID(rid)) continue; - if (hasDeletedRows) { + if (hasDeletedNonFields) { // It's a deleted row if RTSpecialName is set and name is "_Deleted" - var row = tablesStream.ReadMethodRow(rid); - if (row == null) + if (!tablesStream.TryReadMethodRow(rid, out var row)) continue; // Should never happen since rid is valid if ((row.Flags & (uint)MethodAttributes.RTSpecialName) != 0) { if (stringsStream.ReadNoNull(row.Name).StartsWith(DeletedName)) @@ -264,42 +360,41 @@ public override RidList GetMethodRidList(uint typeDefRid) { // It's a valid non-deleted rid so add it newList.Add(rid); } - return newList; + return RidList.Create(newList); } /// public override RidList GetParamRidList(uint methodRid) { var list = GetRidList(tablesStream.MethodTable, methodRid, 5, tablesStream.ParamTable); - if (list.Length == 0 || !hasParamPtr) + if (list.Count == 0 || !hasParamPtr) return list; var destTable = tablesStream.ParamTable; - var newList = new RandomRidList((int)list.Length); - for (uint i = 0; i < list.Length; i++) { + var newList = new List(list.Count); + for (int i = 0; i < list.Count; i++) { var rid = ToParamRid(list[i]); if (destTable.IsInvalidRID(rid)) continue; newList.Add(rid); } - return newList; + return RidList.Create(newList); } /// public override RidList GetEventRidList(uint eventMapRid) { var list = GetRidList(tablesStream.EventMapTable, eventMapRid, 1, tablesStream.EventTable); - if (list.Length == 0 || (!hasEventPtr && !hasDeletedRows)) + if (list.Count == 0 || (!hasEventPtr && !hasDeletedNonFields)) return list; var destTable = tablesStream.EventTable; - var newList = new RandomRidList((int)list.Length); - for (uint i = 0; i < list.Length; i++) { + var newList = new List(list.Count); + for (int i = 0; i < list.Count; i++) { var rid = ToEventRid(list[i]); if (destTable.IsInvalidRID(rid)) continue; - if (hasDeletedRows) { + if (hasDeletedNonFields) { // It's a deleted row if RTSpecialName is set and name is "_Deleted" - var row = tablesStream.ReadEventRow(rid); - if (row == null) + if (!tablesStream.TryReadEventRow(rid, out var row)) continue; // Should never happen since rid is valid if ((row.EventFlags & (uint)EventAttributes.RTSpecialName) != 0) { if (stringsStream.ReadNoNull(row.Name).StartsWith(DeletedName)) @@ -309,25 +404,24 @@ public override RidList GetEventRidList(uint eventMapRid) { // It's a valid non-deleted rid so add it newList.Add(rid); } - return newList; + return RidList.Create(newList); } /// public override RidList GetPropertyRidList(uint propertyMapRid) { var list = GetRidList(tablesStream.PropertyMapTable, propertyMapRid, 1, tablesStream.PropertyTable); - if (list.Length == 0 || (!hasPropertyPtr && !hasDeletedRows)) + if (list.Count == 0 || (!hasPropertyPtr && !hasDeletedNonFields)) return list; var destTable = tablesStream.PropertyTable; - var newList = new RandomRidList((int)list.Length); - for (uint i = 0; i < list.Length; i++) { + var newList = new List(list.Count); + for (int i = 0; i < list.Count; i++) { var rid = ToPropertyRid(list[i]); if (destTable.IsInvalidRID(rid)) continue; - if (hasDeletedRows) { + if (hasDeletedNonFields) { // It's a deleted row if RTSpecialName is set and name is "_Deleted" - var row = tablesStream.ReadPropertyRow(rid); - if (row == null) + if (!tablesStream.TryReadPropertyRow(rid, out var row)) continue; // Should never happen since rid is valid if ((row.PropFlags & (uint)PropertyAttributes.RTSpecialName) != 0) { if (stringsStream.ReadNoNull(row.Name).StartsWith(DeletedName)) @@ -337,9 +431,15 @@ public override RidList GetPropertyRidList(uint propertyMapRid) { // It's a valid non-deleted rid so add it newList.Add(rid); } - return newList; + return RidList.Create(newList); } + /// + public override RidList GetLocalVariableRidList(uint localScopeRid) => GetRidList(tablesStream.LocalScopeTable, localScopeRid, 2, tablesStream.LocalVariableTable); + + /// + public override RidList GetLocalConstantRidList(uint localScopeRid) => GetRidList(tablesStream.LocalScopeTable, localScopeRid, 3, tablesStream.LocalConstantTable); + /// /// Gets a rid list (eg. field list) /// @@ -350,17 +450,9 @@ public override RidList GetPropertyRidList(uint propertyMapRid) { /// A new instance RidList GetRidList(MDTable tableSource, uint tableSourceRid, int colIndex, MDTable tableDest) { var column = tableSource.TableInfo.Columns[colIndex]; - uint startRid, nextListRid; - bool hasNext; -#if THREAD_SAFE - tablesStream.theLock.EnterWriteLock(); try { -#endif - if (!tablesStream.ReadColumn_NoLock(tableSource, tableSourceRid, column, out startRid)) + if (!tablesStream.TryReadColumn24(tableSource, tableSourceRid, column, out uint startRid)) return RidList.Empty; - hasNext = tablesStream.ReadColumn_NoLock(tableSource, tableSourceRid + 1, column, out nextListRid); -#if THREAD_SAFE - } finally { tablesStream.theLock.ExitWriteLock(); } -#endif + bool hasNext = tablesStream.TryReadColumn24(tableSource, tableSourceRid + 1, column, out uint nextListRid); uint lastRid = tableDest.Rows + 1; if (startRid == 0 || startRid >= lastRid) return RidList.Empty; @@ -369,17 +461,16 @@ RidList GetRidList(MDTable tableSource, uint tableSourceRid, int colIndex, MDTab endRid = startRid; if (endRid > lastRid) endRid = lastRid; - return new ContiguousRidList(startRid, endRid - startRid); + return RidList.Create(startRid, endRid - startRid); } /// - protected override uint BinarySearch_NoLock(MDTable tableSource, int keyColIndex, uint key) { + protected override uint BinarySearch(MDTable tableSource, int keyColIndex, uint key) { var keyColumn = tableSource.TableInfo.Columns[keyColIndex]; uint ridLo = 1, ridHi = tableSource.Rows; while (ridLo <= ridHi) { uint rid = (ridLo + ridHi) / 2; - uint key2; - if (!tablesStream.ReadColumn_NoLock(tableSource, rid, keyColumn, out key2)) + if (!tablesStream.TryReadColumn24(tableSource, rid, keyColumn, out uint key2)) break; // Never happens since rid is valid if (key == key2) return rid; @@ -390,26 +481,17 @@ protected override uint BinarySearch_NoLock(MDTable tableSource, int keyColIndex } if (tableSource.Table == Table.GenericParam && !tablesStream.IsSorted(tableSource)) - return LinearSearch_NoLock(tableSource, keyColIndex, key); + return LinearSearch(tableSource, keyColIndex, key); return 0; } - /// - /// Linear searches the table (O(n)) for a rid whose key column at index - /// is equal to . - /// - /// Table to search - /// Key column index - /// Key - /// The rid of the found row, or 0 if none found - uint LinearSearch_NoLock(MDTable tableSource, int keyColIndex, uint key) { - if (tableSource == null) + uint LinearSearch(MDTable tableSource, int keyColIndex, uint key) { + if (tableSource is null) return 0; var keyColumn = tableSource.TableInfo.Columns[keyColIndex]; for (uint rid = 1; rid <= tableSource.Rows; rid++) { - uint key2; - if (!tablesStream.ReadColumn_NoLock(tableSource, rid, keyColumn, out key2)) + if (!tablesStream.TryReadColumn24(tableSource, rid, keyColumn, out uint key2)) break; // Never happens since rid is valid if (key == key2) return rid; diff --git a/src/DotNet/MD/GuidStream.cs b/src/DotNet/MD/GuidStream.cs index dbbfca061..0127a46b2 100644 --- a/src/DotNet/MD/GuidStream.cs +++ b/src/DotNet/MD/GuidStream.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using dnlib.IO; namespace dnlib.DotNet.MD { @@ -13,14 +13,12 @@ public GuidStream() { } /// - public GuidStream(IImageStream imageStream, StreamHeader streamHeader) - : base(imageStream, streamHeader) { + public GuidStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader) + : base(mdReaderFactory, metadataBaseOffset, streamHeader) { } /// - public override bool IsValidIndex(uint index) { - return index == 0 || (index <= 0x10000000 && IsValidOffset((index - 1) * 16, 16)); - } + public override bool IsValidIndex(uint index) => index == 0 || (index <= 0x10000000 && IsValidOffset((index - 1) * 16, 16)); /// /// Read a @@ -30,14 +28,9 @@ public override bool IsValidIndex(uint index) { public Guid? Read(uint index) { if (index == 0 || !IsValidIndex(index)) return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock((index - 1) * 16); - return new Guid(reader.ReadBytes(16)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + var reader = dataReader; + reader.Position = (index - 1) * 16; + return reader.ReadGuid(); } } } diff --git a/src/DotNet/MD/HeapType.cs b/src/DotNet/MD/HeapType.cs index c185d1599..cf3f3ed1b 100644 --- a/src/DotNet/MD/HeapType.cs +++ b/src/DotNet/MD/HeapType.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info namespace dnlib.DotNet.MD { /// diff --git a/src/DotNet/MD/HotHeapStream.cs b/src/DotNet/MD/HotHeapStream.cs deleted file mode 100644 index 2d085dacd..000000000 --- a/src/DotNet/MD/HotHeapStream.cs +++ /dev/null @@ -1,220 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.IO; -using System.Runtime.ExceptionServices; -using System.Security; -using dnlib.IO; - -namespace dnlib.DotNet.MD { - /// - /// Stores some/all heap data - /// - abstract class HotHeapStream : IDisposable { - readonly HeapType heapType; - protected readonly IImageStream reader; - protected readonly long baseOffset; - protected bool invalid; - protected long posData; - protected long posIndexes; - protected long posRids; - protected uint numRids; - protected long offsetMask; - - /// - /// Gets the heap type - /// - public HeapType HeapType { - get { return heapType; } - } - - /// - /// Constructor - /// - /// Heap type - /// Data stream - /// Offset in of start of data - protected HotHeapStream(HeapType heapType, IImageStream reader, long baseOffset) { - this.heapType = heapType; - this.reader = reader; - this.baseOffset = baseOffset; - } - - /// - /// Must be called once after creating it so it can initialize - /// - /// Offset mask (0xFFFFFFFF or 0xFFFFFFFFFFFFFFFF) - public abstract void Initialize(long mask); - - /// - /// Returns a stream that can access a blob - /// - /// Offset in the original heap. If it's the #GUID heap, it should - /// be (index - 1) * 16 - /// The reader (owned by us) or null if the data isn't present - public IImageStream GetBlobReader(uint originalHeapOffset) { - long dataOffset; - if (GetBlobOffset(originalHeapOffset, out dataOffset)) { - reader.Position = dataOffset; - return reader; - } - return null; - } - - /// - /// Returns the offset in of some data - /// - /// Original heap offset - /// Updated with offset in of data - /// true if data is present, false if data is not present - protected abstract bool GetBlobOffset(uint originalHeapOffset, out long dataOffset); - - /// - /// Binary searches the rids table for - /// - /// Original heap offset - /// The rids table index or if not found - protected uint BinarySearch(uint originalHeapOffset) { - uint lo = 0, hi = numRids - 1; - while (lo <= hi && hi != uint.MaxValue) { - uint index = (lo + hi) / 2; - uint val = reader.ReadUInt32At(posRids + index * 4); - if (originalHeapOffset == val) - return index; - if (originalHeapOffset > val) - lo = index + 1; - else - hi = index - 1; - } - return uint.MaxValue; - } - - /// - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose method - /// - /// true if called by - protected virtual void Dispose(bool disposing) { - if (disposing) { - if (reader != null) - reader.Dispose(); - } - } - } - - /// - /// Hot heap stream (CLR 2.0) - /// - sealed class HotHeapStreamCLR20 : HotHeapStream { - /// - /// Constructor - /// - /// Heap type - /// Data stream - /// Offset in of start of data - public HotHeapStreamCLR20(HeapType heapType, IImageStream reader, long baseOffset) - : base(heapType, reader, baseOffset) { - } - - /// - [HandleProcessCorruptedStateExceptions, SecurityCritical] // Req'd on .NET 4.0 - public override void Initialize(long mask) { - try { - offsetMask = mask; - reader.Position = baseOffset; - posData = (baseOffset - reader.ReadInt32()) & mask; - posIndexes = (baseOffset - (reader.ReadInt32() & ~3)) & mask; - uint ridsOffset = reader.ReadUInt32(); - numRids = ridsOffset / 4; - posRids = (baseOffset - numRids * 4) & mask; - } - // Ignore exceptions. The CLR only reads these values when needed. Assume - // that this was invalid data that the CLR will never read anyway. - catch (AccessViolationException) { - invalid = true; - } - catch (IOException) { - invalid = true; - } - } - - /// - protected override bool GetBlobOffset(uint originalHeapOffset, out long dataOffset) { - if (invalid) { - dataOffset = 0; - return false; - } - uint index = BinarySearch(originalHeapOffset); - if (index == uint.MaxValue) { - dataOffset = 0; - return false; - } - - if (index == 0) - dataOffset = posData; - else - dataOffset = posData + reader.ReadUInt32At((posIndexes + (index - 1) * 4) & offsetMask); - dataOffset &= offsetMask; - return true; - } - } - - /// - /// Hot heap stream (CLR 4.0) - /// - sealed class HotHeapStreamCLR40 : HotHeapStream { - /// - /// Constructor - /// - /// Heap type - /// Data stream - /// Offset in of start of data - public HotHeapStreamCLR40(HeapType heapType, IImageStream reader, long baseOffset) - : base(heapType, reader, baseOffset) { - } - - /// - [HandleProcessCorruptedStateExceptions, SecurityCritical] // Req'd on .NET 4.0 - public override void Initialize(long mask) { - try { - offsetMask = mask; - reader.Position = baseOffset; - uint ridsOffset = reader.ReadUInt32(); - numRids = ridsOffset / 4; - posRids = (baseOffset - ridsOffset) & mask; - posIndexes = (baseOffset - reader.ReadInt32()) & mask; - posData = (baseOffset - reader.ReadInt32()) & mask; - } - // Ignore exceptions. The CLR only reads these values when needed. Assume - // that this was invalid data that the CLR will never read anyway. - catch (AccessViolationException) { - invalid = true; - } - catch (IOException) { - invalid = true; - } - } - - /// - protected override bool GetBlobOffset(uint originalHeapOffset, out long dataOffset) { - if (invalid) { - dataOffset = 0; - return false; - } - uint index = BinarySearch(originalHeapOffset); - if (index == uint.MaxValue) { - dataOffset = 0; - return false; - } - - dataOffset = posData + reader.ReadUInt32At((posIndexes + index * 4) & offsetMask); - dataOffset &= offsetMask; - return true; - } - } -} diff --git a/src/DotNet/MD/HotHeapVersion.cs b/src/DotNet/MD/HotHeapVersion.cs deleted file mode 100644 index 5bc11a3e9..000000000 --- a/src/DotNet/MD/HotHeapVersion.cs +++ /dev/null @@ -1,18 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -namespace dnlib.DotNet.MD { - /// - /// Hot heap version - /// - public enum HotHeapVersion { - /// - /// CLR 2.0 (.NET 2.0 - 3.5) - /// - CLR20, - - /// - /// CLR 4.0 (.NET 4.0 - 4.5) - /// - CLR40, - } -} diff --git a/src/DotNet/MD/HotStream.cs b/src/DotNet/MD/HotStream.cs deleted file mode 100644 index 9d84cd90f..000000000 --- a/src/DotNet/MD/HotStream.cs +++ /dev/null @@ -1,306 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.ExceptionServices; -using System.Security; -using System.Threading; -using dnlib.IO; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif - -namespace dnlib.DotNet.MD { - /// - /// Represents the (undocumented) #! stream. The CLR only uses this stream if the - /// normal compressed tables stream (#~) is used. - /// - abstract class HotStream : DotNetStream { - protected readonly IImageStream fullStream; - protected readonly long baseOffset; - protected readonly long endOffset; - protected HotTableStream hotTableStream; - protected ThreadSafe.IList hotHeapStreams; - - /// - /// Gets the or null if there's none - /// - public HotTableStream HotTableStream { - get { - if (hotTableStream == null) { - var newHts = CreateHotTableStream(); - if (Interlocked.CompareExchange(ref hotTableStream, newHts, null) != null) - newHts.Dispose(); - } - return hotTableStream; - } - } - - /// - /// Gets all s - /// - public IList HotHeapStreams { - get { - if (hotHeapStreams == null) { - var newHhs = CreateHotHeapStreams(); - if (Interlocked.CompareExchange(ref hotHeapStreams, newHhs, null) != null) { - foreach (var hhs in newHhs) - hhs.Dispose(); - } - } - return hotHeapStreams; - } - } - - /// - /// Creates a instance - /// - /// Hot heap version - /// Heap stream - /// Stream header info - /// Stream for the full PE file - /// Offset in where the data starts - /// A instance or null if - /// is invalid - public static HotStream Create(HotHeapVersion version, IImageStream imageStream, StreamHeader streamHeader, IImageStream fullStream, FileOffset baseOffset) { - switch (version) { - case HotHeapVersion.CLR20: return new HotStreamCLR20(imageStream, streamHeader, fullStream, baseOffset); - case HotHeapVersion.CLR40: return new HotStreamCLR40(imageStream, streamHeader, fullStream, baseOffset); - default: return null; - } - } - - /// - /// Constructor - /// - /// Heap stream - /// Stream header info - /// Stream for the full PE file - /// Offset in where the data starts - protected HotStream(IImageStream imageStream, StreamHeader streamHeader, IImageStream fullStream, FileOffset baseOffset) - : base(imageStream, streamHeader) { - this.fullStream = fullStream; - this.baseOffset = (long)baseOffset; - this.endOffset = (long)baseOffset + imageStream.Length; - } - - [HandleProcessCorruptedStateExceptions, SecurityCritical] // Req'd on .NET 4.0 - HotTableStream CreateHotTableStream() { - try { - return CreateHotTableStreamImpl(); - } - catch (AccessViolationException) { - return null; - } - catch (IOException) { - return null; - } - } - - [HandleProcessCorruptedStateExceptions, SecurityCritical] // Req'd on .NET 4.0 - ThreadSafe.IList CreateHotHeapStreams() { - try { - return CreateHotHeapStreams2(); - } - catch (AccessViolationException) { - return null; - } - catch (IOException) { - return null; - } - } - - ThreadSafe.IList CreateHotHeapStreams2() { - var list = ThreadSafeListCreator.Create(); - try { - long dirBaseOffs = GetHotHeapDirectoryBaseOffset(); - for (long offs = dirBaseOffs; offs + 8 <= endOffset - 8; offs += 8) { - fullStream.Position = offs; - HeapType heapType; - long hotHeapOffset; - ReadHotHeapDirectory(fullStream, dirBaseOffs, out heapType, out hotHeapOffset); - - IImageStream dataStream = null; - HotHeapStream hotHeapStream = null; - try { - dataStream = fullStream.Clone(); - list.Add(hotHeapStream = CreateHotHeapStream(heapType, dataStream, hotHeapOffset)); - dataStream = null; - hotHeapStream = null; - } - catch { - if (hotHeapStream != null) - hotHeapStream.Dispose(); - if (dataStream != null) - dataStream.Dispose(); - throw; - } - } - } - catch { - foreach (var h in list) - h.Dispose(); - throw; - } - return list; - } - - /// - /// Reads a hot heap directory - /// - /// Reader stream - /// Pool directory base offset - /// Updated with heap type - /// Updated with heap offset - protected abstract void ReadHotHeapDirectory(IImageStream reader, long dirBaseOffs, out HeapType heapType, out long hotHeapOffs); - - /// - /// Creates a - /// - /// Heap type - /// Data stream - /// Offset in of start of data - /// A new instance - protected abstract HotHeapStream CreateHotHeapStream(HeapType heapType, IImageStream stream, long baseOffset); - - /// - /// Creates the - /// - /// A new instance or null if it doesn't exist - protected abstract HotTableStream CreateHotTableStreamImpl(); - - /// - /// Gets the offset of the hot table directory in - /// - protected long GetHotTableBaseOffset() { - // All bits in this dword are used - return endOffset - 8 - HotTableStream.HOT_HEAP_DIR_SIZE - fullStream.ReadUInt32At(endOffset - 8); - } - - /// - /// Gets the offset of the hot pool directory in - /// - protected abstract long GetHotHeapDirectoryBaseOffset(); - - /// - protected override void Dispose(bool disposing) { - if (disposing) { - IDisposable id = fullStream; - if (id != null) - id.Dispose(); - id = hotTableStream; - if (id != null) - id.Dispose(); - var hhs = hotHeapStreams; - if (hhs != null) { - foreach (var hs in hhs) { - if (hs != null) - hs.Dispose(); - } - } - } - base.Dispose(disposing); - } - } - - /// - /// Represents the #! stream. Should be used if the module uses CLR 2.0 (.NET 2.0 - 3.5) - /// - sealed class HotStreamCLR20 : HotStream { - /// - /// Constructor - /// - /// Heap stream - /// Stream header info - /// Stream for the full PE file - /// Offset in where the data starts - public HotStreamCLR20(IImageStream imageStream, StreamHeader streamHeader, IImageStream fullStream, FileOffset baseOffset) - : base(imageStream, streamHeader, fullStream, baseOffset) { - } - - /// - protected override HotTableStream CreateHotTableStreamImpl() { - IImageStream stream = null; - try { - stream = fullStream.Clone(); - return new HotTableStreamCLR20(stream, GetHotTableBaseOffset()); - } - catch { - if (stream != null) - stream.Dispose(); - throw; - } - } - - /// - protected override long GetHotHeapDirectoryBaseOffset() { - // Lower 2 bits are ignored - return endOffset - 8 - (fullStream.ReadUInt32At(endOffset - 8 + 4) & ~3); - } - - /// - protected override void ReadHotHeapDirectory(IImageStream reader, long dirBaseOffs, out HeapType heapType, out long hotHeapOffs) { - heapType = (HeapType)reader.ReadUInt32(); - // Lower 2 bits are ignored - hotHeapOffs = dirBaseOffs - (reader.ReadUInt32() & ~3); - } - - /// - protected override HotHeapStream CreateHotHeapStream(HeapType heapType, IImageStream stream, long baseOffset) { - return new HotHeapStreamCLR20(heapType, stream, baseOffset); - } - } - - /// - /// Represents the #! stream. Should be used if the module uses CLR 4.0 (.NET 4.0 - 4.5) - /// - sealed class HotStreamCLR40 : HotStream { - /// - /// Constructor - /// - /// Heap stream - /// Stream header info - /// Stream for the full PE file - /// Offset in where the data starts - public HotStreamCLR40(IImageStream imageStream, StreamHeader streamHeader, IImageStream fullStream, FileOffset baseOffset) - : base(imageStream, streamHeader, fullStream, baseOffset) { - } - - /// - protected override HotTableStream CreateHotTableStreamImpl() { - IImageStream stream = null; - try { - stream = fullStream.Clone(); - return new HotTableStreamCLR40(stream, GetHotTableBaseOffset()); - } - catch { - if (stream != null) - stream.Dispose(); - throw; - } - } - - /// - protected override long GetHotHeapDirectoryBaseOffset() { - // All bits are used - return endOffset - 8 - fullStream.ReadUInt32At(endOffset - 8 + 4); - } - - /// - protected override void ReadHotHeapDirectory(IImageStream reader, long dirBaseOffs, out HeapType heapType, out long hotHeapOffs) { - heapType = (HeapType)reader.ReadUInt32(); - // All bits are used - hotHeapOffs = dirBaseOffs - reader.ReadUInt32(); - } - - /// - protected override HotHeapStream CreateHotHeapStream(HeapType heapType, IImageStream stream, long baseOffset) { - return new HotHeapStreamCLR40(heapType, stream, baseOffset); - } - } -} diff --git a/src/DotNet/MD/HotTableStream.cs b/src/DotNet/MD/HotTableStream.cs deleted file mode 100644 index a49d73854..000000000 --- a/src/DotNet/MD/HotTableStream.cs +++ /dev/null @@ -1,276 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.IO; -using System.Runtime.ExceptionServices; -using System.Security; -using dnlib.IO; - -namespace dnlib.DotNet.MD { - /// - /// Stores some/all rows of a table - /// - abstract class HotTableStream : IDisposable { - protected const int MAX_TABLES = (int)Table.GenericParamConstraint + 1; - internal const uint HOT_HEAP_DIR_SIZE = 4 + MAX_TABLES * 4; - - protected readonly IImageStream fullStream; - protected readonly long baseOffset; - - /// - /// Constructor - /// - /// Data stream - /// Offset in of start of the - /// hot table directory header - protected HotTableStream(IImageStream fullStream, long baseOffset) { - this.fullStream = fullStream; - this.baseOffset = baseOffset; - } - - /// - /// Must be called once after creating it so it can initialize - /// - /// Offset mask (0xFFFFFFFF or 0xFFFFFFFFFFFFFFFF) - public abstract void Initialize(long mask); - - /// - /// Returns a reader positioned at a table row - /// - /// Table - /// A valid row ID (i.e., >= 1 and <= number of rows) - /// The reader (owned by us) or null if the row isn't present - public IImageStream GetTableReader(MDTable table, uint rid) { - long offset; - if (GetRowOffset(table, rid, out offset)) { - fullStream.Position = offset; - return fullStream; - } - - return null; - } - - /// - /// Returns the offset (in ) of a row - /// - /// Table - /// A valid row ID (i.e., >= 1 and <= number of rows) - /// Updated with the offset - /// true if the row exists, false if the row doesn't exist - protected abstract bool GetRowOffset(MDTable table, uint rid, out long offset); - - /// - /// Add offsets - /// - /// Mask - /// Base offset - /// Displacement - /// Returns 0 if is 0, else returns - /// the sum of and masked - /// by - protected static long AddOffsets(long mask, long baseOffset, long displ) { - if (displ == 0) - return 0; - return (baseOffset + displ) & mask; - } - - /// - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose method - /// - /// true if called by - protected virtual void Dispose(bool disposing) { - if (disposing) { - var fs = fullStream; - if (fs != null) - fs.Dispose(); - } - } - } - - /// - /// Hot table stream (CLR 2.0) - /// - sealed class HotTableStreamCLR20 : HotTableStream { - TableHeader[] tableHeaders; - - class TableHeader { - public uint numRows; - public long posTable1; - public long posTable2; - public long posData; - public int shift; - public uint mask; - } - - /// - /// Constructor - /// - /// Data stream - /// Offset in of start of the - /// hot table directory header - public HotTableStreamCLR20(IImageStream fullStream, long baseOffset) - : base(fullStream, baseOffset) { - } - - /// - [HandleProcessCorruptedStateExceptions, SecurityCritical] // Req'd on .NET 4.0 - public override void Initialize(long mask) { - tableHeaders = new TableHeader[MAX_TABLES]; - for (int i = 0; i < tableHeaders.Length; i++) { - fullStream.Position = baseOffset + 4 + i * 4; - int headerOffs = fullStream.ReadInt32(); - if (headerOffs == 0) - continue; - var headerBaseOffs = (baseOffset + headerOffs) & mask; - fullStream.Position = headerBaseOffs; - try { - var header = new TableHeader { - numRows = fullStream.ReadUInt32(), - posTable1 = AddOffsets(mask, headerBaseOffs, fullStream.ReadInt32()), - posTable2 = (headerBaseOffs + fullStream.ReadInt32()) & mask, - posData = (headerBaseOffs + fullStream.ReadInt32()) & mask, - shift = fullStream.ReadUInt16(), - }; - header.mask = (1U << header.shift) - 1; - tableHeaders[i] = header; - } - // Ignore exceptions. The CLR only reads these values when needed. Assume - // that this was invalid data that the CLR will never read anyway. - catch (AccessViolationException) { - } - catch (IOException) { - } - } - } - - /// - protected override bool GetRowOffset(MDTable table, uint rid, out long offset) { - offset = 0; - if ((uint)table.Table >= (uint)tableHeaders.Length) - return false; - var header = tableHeaders[(int)table.Table]; - if (header == null) - return false; - - // Check whether the whole table is in memory - if (header.posTable1 == 0) { - offset = header.posData + (rid - 1) * table.RowSize; - return true; - } - - fullStream.Position = header.posTable1 + (rid & header.mask) * 2; - int index = fullStream.ReadUInt16(); - int stop = fullStream.ReadUInt16(); - fullStream.Position = header.posTable2 + index; - byte highBits = (byte)(rid >> header.shift); - while (index < stop) { - if (fullStream.ReadByte() == highBits) { - offset = header.posData + index * table.RowSize; - return true; - } - index++; - } - - offset = 0; - return false; - } - } - - /// - /// Hot table stream (CLR 4.0) - /// - sealed class HotTableStreamCLR40 : HotTableStream { - TableHeader[] tableHeaders; - - class TableHeader { - public uint numRows; - public long posTable1; - public long posTable2; - public long posIndexes; - public long posData; - public int shift; - public uint mask; - } - - /// - /// Constructor - /// - /// Data stream - /// Offset in of start of the - /// hot table directory header - public HotTableStreamCLR40(IImageStream fullStream, long baseOffset) - : base(fullStream, baseOffset) { - } - - /// - [HandleProcessCorruptedStateExceptions, SecurityCritical] // Req'd on .NET 4.0 - public override void Initialize(long mask) { - tableHeaders = new TableHeader[MAX_TABLES]; - for (int i = 0; i < tableHeaders.Length; i++) { - fullStream.Position = baseOffset + 4 + i * 4; - int headerOffs = fullStream.ReadInt32(); - if (headerOffs == 0) - continue; - var headerBaseOffs = (baseOffset + headerOffs) & mask; - fullStream.Position = headerBaseOffs; - try { - var header = new TableHeader { - numRows = fullStream.ReadUInt32(), - posTable1 = AddOffsets(mask, headerBaseOffs, fullStream.ReadInt32()), - posTable2 = (headerBaseOffs + fullStream.ReadInt32()) & mask, - posIndexes = (headerBaseOffs + fullStream.ReadInt32()) & mask, - posData = (headerBaseOffs + fullStream.ReadInt32()) & mask, - shift = fullStream.ReadUInt16(), - }; - header.mask = (1U << header.shift) - 1; - tableHeaders[i] = header; - } - // Ignore exceptions. The CLR only reads these values when needed. Assume - // that this was invalid data that the CLR will never read anyway. - catch (AccessViolationException) { - } - catch (IOException) { - } - } - } - - /// - protected override bool GetRowOffset(MDTable table, uint rid, out long offset) { - offset = 0; - if ((uint)table.Table >= (uint)tableHeaders.Length) - return false; - var header = tableHeaders[(int)table.Table]; - if (header == null) - return false; - - // Check whether the whole table is in memory - if (header.posTable1 == 0) { - offset = header.posData + (rid - 1) * table.RowSize; - return true; - } - - fullStream.Position = header.posTable1 + (rid & header.mask) * 2; - int index = fullStream.ReadUInt16(); - int stop = fullStream.ReadUInt16(); - fullStream.Position = header.posTable2 + index; - byte highBits = (byte)(rid >> header.shift); - while (index < stop) { - if (fullStream.ReadByte() == highBits) { - index = fullStream.ReadUInt16At(header.posIndexes + index * 2); - offset = header.posData + index * table.RowSize; - return true; - } - index++; - } - - offset = 0; - return false; - } - } -} diff --git a/src/DotNet/MD/IRowReaders.cs b/src/DotNet/MD/IRowReaders.cs index 474e89209..acb89bc04 100644 --- a/src/DotNet/MD/IRowReaders.cs +++ b/src/DotNet/MD/IRowReaders.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet.MD { +namespace dnlib.DotNet.MD { /// /// Reads metadata table columns /// @@ -21,13 +21,13 @@ public interface IColumnReader { /// Reads table rows /// /// Raw row - public interface IRowReader where TRow : class, IRawRow { + public interface IRowReader where TRow : struct { /// - /// Reads a table row + /// Reads a table row or returns false if the row should be read from the original table /// /// Row id - /// The table row or null if its row should be read from the original - /// table - TRow ReadRow(uint rid); + /// The row + /// + bool TryReadRow(uint rid, out TRow row); } } diff --git a/src/DotNet/MD/ImageCor20Header.cs b/src/DotNet/MD/ImageCor20Header.cs index 5f8019963..61774662a 100644 --- a/src/DotNet/MD/ImageCor20Header.cs +++ b/src/DotNet/MD/ImageCor20Header.cs @@ -12,7 +12,7 @@ public sealed class ImageCor20Header : FileSection { readonly uint cb; readonly ushort majorRuntimeVersion; readonly ushort minorRuntimeVersion; - readonly ImageDataDirectory metaData; + readonly ImageDataDirectory metadata; readonly ComImageFlags flags; readonly uint entryPointToken_or_RVA; readonly ImageDataDirectory resources; @@ -25,93 +25,67 @@ public sealed class ImageCor20Header : FileSection { /// /// Returns true if it has a native header /// - public bool HasNativeHeader { - get { return (flags & ComImageFlags.ILLibrary) != 0; } - } + public bool HasNativeHeader => (flags & ComImageFlags.ILLibrary) != 0; /// /// Returns the IMAGE_COR20_HEADER.cb field /// - public uint CB { - get { return cb; } - } + public uint CB => cb; /// /// Returns the IMAGE_COR20_HEADER.MajorRuntimeVersion field /// - public ushort MajorRuntimeVersion { - get { return majorRuntimeVersion; } - } + public ushort MajorRuntimeVersion => majorRuntimeVersion; /// /// Returns the IMAGE_COR20_HEADER.MinorRuntimeVersion field /// - public ushort MinorRuntimeVersion { - get { return minorRuntimeVersion; } - } + public ushort MinorRuntimeVersion => minorRuntimeVersion; /// - /// Returns the IMAGE_COR20_HEADER.MetaData field + /// Returns the IMAGE_COR20_HEADER.Metadata field /// - public ImageDataDirectory MetaData { - get { return metaData; } - } + public ImageDataDirectory Metadata => metadata; /// /// Returns the IMAGE_COR20_HEADER.Flags field /// - public ComImageFlags Flags { - get { return flags; } - } + public ComImageFlags Flags => flags; /// /// Returns the IMAGE_COR20_HEADER.EntryPointToken/EntryPointTokenRVA field /// - public uint EntryPointToken_or_RVA { - get { return entryPointToken_or_RVA; } - } + public uint EntryPointToken_or_RVA => entryPointToken_or_RVA; /// /// Returns the IMAGE_COR20_HEADER.Resources field /// - public ImageDataDirectory Resources { - get { return resources; } - } + public ImageDataDirectory Resources => resources; /// /// Returns the IMAGE_COR20_HEADER.StrongNameSignature field /// - public ImageDataDirectory StrongNameSignature { - get { return strongNameSignature; } - } + public ImageDataDirectory StrongNameSignature => strongNameSignature; /// /// Returns the IMAGE_COR20_HEADER.CodeManagerTable field /// - public ImageDataDirectory CodeManagerTable { - get { return codeManagerTable; } - } + public ImageDataDirectory CodeManagerTable => codeManagerTable; /// /// Returns the IMAGE_COR20_HEADER.VTableFixups field /// - public ImageDataDirectory VTableFixups { - get { return vtableFixups; } - } + public ImageDataDirectory VTableFixups => vtableFixups; /// /// Returns the IMAGE_COR20_HEADER.ExportAddressTableJumps field /// - public ImageDataDirectory ExportAddressTableJumps { - get { return exportAddressTableJumps; } - } + public ImageDataDirectory ExportAddressTableJumps => exportAddressTableJumps; /// /// Returns the IMAGE_COR20_HEADER.ManagedNativeHeader field /// - public ImageDataDirectory ManagedNativeHeader { - get { return managedNativeHeader; } - } + public ImageDataDirectory ManagedNativeHeader => managedNativeHeader; /// /// Constructor @@ -119,23 +93,23 @@ public ImageDataDirectory ManagedNativeHeader { /// PE file reader pointing to the start of this section /// Verify section /// Thrown if verification fails - public ImageCor20Header(IImageStream reader, bool verify) { - SetStartOffset(reader); - this.cb = reader.ReadUInt32(); - if (verify && this.cb < 0x48) + public ImageCor20Header(ref DataReader reader, bool verify) { + SetStartOffset(ref reader); + cb = reader.ReadUInt32(); + if (verify && cb < 0x48) throw new BadImageFormatException("Invalid IMAGE_COR20_HEADER.cb value"); - this.majorRuntimeVersion = reader.ReadUInt16(); - this.minorRuntimeVersion = reader.ReadUInt16(); - this.metaData = new ImageDataDirectory(reader, verify); - this.flags = (ComImageFlags)reader.ReadUInt32(); - this.entryPointToken_or_RVA = reader.ReadUInt32(); - this.resources = new ImageDataDirectory(reader, verify); - this.strongNameSignature = new ImageDataDirectory(reader, verify); - this.codeManagerTable = new ImageDataDirectory(reader, verify); - this.vtableFixups = new ImageDataDirectory(reader, verify); - this.exportAddressTableJumps = new ImageDataDirectory(reader, verify); - this.managedNativeHeader = new ImageDataDirectory(reader, verify); - SetEndoffset(reader); + majorRuntimeVersion = reader.ReadUInt16(); + minorRuntimeVersion = reader.ReadUInt16(); + metadata = new ImageDataDirectory(ref reader, verify); + flags = (ComImageFlags)reader.ReadUInt32(); + entryPointToken_or_RVA = reader.ReadUInt32(); + resources = new ImageDataDirectory(ref reader, verify); + strongNameSignature = new ImageDataDirectory(ref reader, verify); + codeManagerTable = new ImageDataDirectory(ref reader, verify); + vtableFixups = new ImageDataDirectory(ref reader, verify); + exportAddressTableJumps = new ImageDataDirectory(ref reader, verify); + managedNativeHeader = new ImageDataDirectory(ref reader, verify); + SetEndoffset(ref reader); } } } diff --git a/src/DotNet/MD/MDHeaderRuntimeVersion.cs b/src/DotNet/MD/MDHeaderRuntimeVersion.cs index 8b04f347e..ab16a77a4 100644 --- a/src/DotNet/MD/MDHeaderRuntimeVersion.cs +++ b/src/DotNet/MD/MDHeaderRuntimeVersion.cs @@ -1,42 +1,42 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet.MD { +namespace dnlib.DotNet.MD { /// /// Version strings found in the meta data header /// public static class MDHeaderRuntimeVersion { /// - /// MS CLR 1.0 version string (.NET 1.0) + /// MS CLR 1.0 version string (.NET Framework 1.0) /// public const string MS_CLR_10 = "v1.0.3705"; /// - /// MS CLR 1.0 version string (.NET 1.0). This is an incorrect version that shouldn't be used. + /// MS CLR 1.0 version string (.NET Framework 1.0). This is an incorrect version that shouldn't be used. /// public const string MS_CLR_10_X86RETAIL = "v1.x86ret"; /// - /// MS CLR 1.0 version string (.NET 1.0). This is an incorrect version that shouldn't be used. + /// MS CLR 1.0 version string (.NET Framework 1.0). This is an incorrect version that shouldn't be used. /// public const string MS_CLR_10_RETAIL = "retail"; /// - /// MS CLR 1.0 version string (.NET 1.0). This is an incorrect version that shouldn't be used. + /// MS CLR 1.0 version string (.NET Framework 1.0). This is an incorrect version that shouldn't be used. /// public const string MS_CLR_10_COMPLUS = "COMPLUS"; /// - /// MS CLR 1.1 version string (.NET 1.1) + /// MS CLR 1.1 version string (.NET Framework 1.1) /// public const string MS_CLR_11 = "v1.1.4322"; /// - /// MS CLR 2.0 version string (.NET 2.0-3.5) + /// MS CLR 2.0 version string (.NET Framework 2.0-3.5) /// public const string MS_CLR_20 = "v2.0.50727"; /// - /// MS CLR 4.0 version string (.NET 4.0-4.5) + /// MS CLR 4.0 version string (.NET Framework 4.0-4.5) /// public const string MS_CLR_40 = "v4.0.30319"; @@ -74,5 +74,10 @@ public static class MDHeaderRuntimeVersion { /// ECMA 2005 version string /// public const string ECMA_2005 = "Standard CLI 2005"; + + /// + /// Portable PDB v1.0 + /// + public const string PORTABLE_PDB_V1_0 = "PDB v1.0"; } } diff --git a/src/DotNet/MD/MDStreamFlags.cs b/src/DotNet/MD/MDStreamFlags.cs index 6393efac8..92c938f4c 100644 --- a/src/DotNet/MD/MDStreamFlags.cs +++ b/src/DotNet/MD/MDStreamFlags.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.DotNet.MD { /// diff --git a/src/DotNet/MD/MDTable.cs b/src/DotNet/MD/MDTable.cs index 9d97122a5..a7bcdf68b 100644 --- a/src/DotNet/MD/MDTable.cs +++ b/src/DotNet/MD/MDTable.cs @@ -9,90 +9,60 @@ namespace dnlib.DotNet.MD { /// /// A MD table (eg. Method table) /// - [DebuggerDisplay("DL:{imageStream.Length} R:{numRows} RS:{tableInfo.RowSize} C:{Count} {tableInfo.Name}")] + [DebuggerDisplay("DL:{dataReader.Length} R:{numRows} RS:{tableInfo.RowSize} C:{Count} {tableInfo.Name}")] public sealed class MDTable : IDisposable, IFileSection { readonly Table table; uint numRows; TableInfo tableInfo; - IImageStream imageStream; + DataReader dataReader; // Fix for VS2015 expression evaluator: "The debugger is unable to evaluate this expression" - int Count { - get { return tableInfo.Columns.Count; } - } + int Count => tableInfo.Columns.Length; /// - public FileOffset StartOffset { - get { return imageStream.FileOffset; } - } + public FileOffset StartOffset => (FileOffset)dataReader.StartOffset; /// - public FileOffset EndOffset { - get { return imageStream.FileOffset + imageStream.Length; } - } + public FileOffset EndOffset => (FileOffset)dataReader.EndOffset; /// /// Gets the table /// - public Table Table { - get { return table; } - } + public Table Table => table; /// /// Gets the name of this table /// - public string Name { - get { return tableInfo.Name; } - } + public string Name => tableInfo.Name; /// /// Returns total number of rows /// - public uint Rows { - get { return numRows; } - } + public uint Rows => numRows; /// /// Gets the total size in bytes of one row in this table /// - public uint RowSize { - get { return (uint)tableInfo.RowSize; } - } + public uint RowSize => (uint)tableInfo.RowSize; /// /// Returns all the columns /// - public IList Columns { - get { return tableInfo.Columns; } - } + public IList Columns => tableInfo.Columns; /// /// Returns true if there are no valid rows /// - public bool IsEmpty { - get { return numRows == 0; } - } + public bool IsEmpty => numRows == 0; /// /// Returns info about this table /// - public TableInfo TableInfo { - get { return tableInfo; } - } + public TableInfo TableInfo => tableInfo; - /// - /// The stream that can access all the rows in this table - /// - internal IImageStream ImageStream { - get { return imageStream; } - set { - var ims = imageStream; - if (ims == value) - return; - if (ims != null) - ims.Dispose(); - imageStream = value; - } + internal DataReader DataReader { + get => dataReader; + set => dataReader = value; } /// @@ -105,36 +75,48 @@ internal MDTable(Table table, uint numRows, TableInfo tableInfo) { this.table = table; this.numRows = numRows; this.tableInfo = tableInfo; - } - internal IImageStream CloneImageStream() { - return imageStream.Clone(); - } + var columns = tableInfo.Columns; + int length = columns.Length; + if (length > 0) Column0 = columns[0]; + if (length > 1) Column1 = columns[1]; + if (length > 2) Column2 = columns[2]; + if (length > 3) Column3 = columns[3]; + if (length > 4) Column4 = columns[4]; + if (length > 5) Column5 = columns[5]; + if (length > 6) Column6 = columns[6]; + if (length > 7) Column7 = columns[7]; + if (length > 8) Column8 = columns[8]; + } + + // So we don't have to call IList indexer + internal readonly ColumnInfo Column0; + internal readonly ColumnInfo Column1; + internal readonly ColumnInfo Column2; + internal readonly ColumnInfo Column3; + internal readonly ColumnInfo Column4; + internal readonly ColumnInfo Column5; + internal readonly ColumnInfo Column6; + internal readonly ColumnInfo Column7; + internal readonly ColumnInfo Column8; /// /// Checks whether the row exists /// /// Row ID - public bool IsValidRID(uint rid) { - return rid != 0 && rid <= numRows; - } + public bool IsValidRID(uint rid) => rid != 0 && rid <= numRows; /// /// Checks whether the row does not exist /// /// Row ID - public bool IsInvalidRID(uint rid) { - return rid == 0 || rid > numRows; - } + public bool IsInvalidRID(uint rid) => rid == 0 || rid > numRows; /// public void Dispose() { - var ims = imageStream; - if (ims != null) - ims.Dispose(); numRows = 0; tableInfo = null; - imageStream = null; + dataReader = default; } } } diff --git a/src/DotNet/MD/MetaDataCreator.cs b/src/DotNet/MD/MetaDataCreator.cs deleted file mode 100644 index 735379423..000000000 --- a/src/DotNet/MD/MetaDataCreator.cs +++ /dev/null @@ -1,203 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Collections.Generic; -using dnlib.IO; -using dnlib.PE; - -namespace dnlib.DotNet.MD { - /// - /// Low level access to a .NET file's metadata - /// - public static class MetaDataCreator { - enum MetaDataType { - Unknown, - Compressed, // #~ (normal) - ENC, // #- (edit and continue) - } - - /// - /// Create a instance - /// - /// The file to load - /// A new instance - internal static MetaData Load(string fileName) { - IPEImage peImage = null; - try { - return Load(peImage = new PEImage(fileName)); - } - catch { - if (peImage != null) - peImage.Dispose(); - throw; - } - } - - /// - /// Create a instance - /// - /// The .NET file data - /// A new instance - internal static MetaData Load(byte[] data) { - IPEImage peImage = null; - try { - return Load(peImage = new PEImage(data)); - } - catch { - if (peImage != null) - peImage.Dispose(); - throw; - } - } - - /// - /// Create a instance - /// - /// Address of a .NET file in memory - /// A new instance - internal static MetaData Load(IntPtr addr) { - IPEImage peImage = null; - - // We don't know what layout it is. Memory is more common so try that first. - try { - return Load(peImage = new PEImage(addr, ImageLayout.Memory, true)); - } - catch { - if (peImage != null) - peImage.Dispose(); - peImage = null; - } - - try { - return Load(peImage = new PEImage(addr, ImageLayout.File, true)); - } - catch { - if (peImage != null) - peImage.Dispose(); - throw; - } - } - - /// - /// Create a instance - /// - /// Address of a .NET file in memory - /// Image layout of the file in memory - /// A new instance - internal static MetaData Load(IntPtr addr, ImageLayout imageLayout) { - IPEImage peImage = null; - try { - return Load(peImage = new PEImage(addr, imageLayout, true)); - } - catch { - if (peImage != null) - peImage.Dispose(); - throw; - } - } - - /// - /// Create a instance - /// - /// The PE image - /// A new instance - internal static MetaData Load(IPEImage peImage) { - return Create(peImage, true); - } - - /// - /// Create a instance - /// - /// The PE image - /// A new instance - public static IMetaData CreateMetaData(IPEImage peImage) { - return Create(peImage, true); - } - - /// - /// Create a instance - /// - /// The PE image - /// true if we should verify that it's a .NET PE file - /// A new instance - public static IMetaData CreateMetaData(IPEImage peImage, bool verify) { - return Create(peImage, verify); - } - - /// - /// Create a instance - /// - /// The PE image - /// true if we should verify that it's a .NET PE file - /// A new instance - internal static MetaData Create(IPEImage peImage, bool verify) { - IImageStream cor20HeaderStream = null, mdHeaderStream = null; - MetaData md = null; - try { - var dotNetDir = peImage.ImageNTHeaders.OptionalHeader.DataDirectories[14]; - if (dotNetDir.VirtualAddress == 0) - throw new BadImageFormatException(".NET data directory RVA is 0"); - if (dotNetDir.Size < 0x48) - throw new BadImageFormatException(".NET data directory size < 0x48"); - var cor20Header = new ImageCor20Header(cor20HeaderStream = peImage.CreateStream(dotNetDir.VirtualAddress, 0x48), verify); - if (cor20Header.MetaData.VirtualAddress == 0) - throw new BadImageFormatException(".NET MetaData RVA is 0"); - if (cor20Header.MetaData.Size < 16) - throw new BadImageFormatException(".NET MetaData size is too small"); - var mdSize = cor20Header.MetaData.Size; - var mdRva = cor20Header.MetaData.VirtualAddress; - var mdHeader = new MetaDataHeader(mdHeaderStream = peImage.CreateStream(mdRva, mdSize), verify); - if (verify) { - foreach (var sh in mdHeader.StreamHeaders) { - if (sh.Offset + sh.StreamSize < sh.Offset || sh.Offset + sh.StreamSize > mdSize) - throw new BadImageFormatException("Invalid stream header"); - } - } - - switch (GetMetaDataType(mdHeader.StreamHeaders)) { - case MetaDataType.Compressed: - md = new CompressedMetaData(peImage, cor20Header, mdHeader); - break; - - case MetaDataType.ENC: - md = new ENCMetaData(peImage, cor20Header, mdHeader); - break; - - default: - throw new BadImageFormatException("No #~ or #- stream found"); - } - md.Initialize(); - - return md; - } - catch { - if (md != null) - md.Dispose(); - throw; - } - finally { - if (cor20HeaderStream != null) - cor20HeaderStream.Dispose(); - if (mdHeaderStream != null) - mdHeaderStream.Dispose(); - } - } - - static MetaDataType GetMetaDataType(IList streamHeaders) { - MetaDataType? mdType = null; - foreach (var sh in streamHeaders) { - if (mdType == null) { - if (sh.Name == "#~") - mdType = MetaDataType.Compressed; - else if (sh.Name == "#-") - mdType = MetaDataType.ENC; - } - if (sh.Name == "#Schema") - mdType = MetaDataType.ENC; - } - if (mdType == null) - return MetaDataType.Unknown; - return mdType.Value; - } - } -} diff --git a/src/DotNet/MD/MetaDataHeader.cs b/src/DotNet/MD/MetaDataHeader.cs deleted file mode 100644 index 54bd5d6c5..000000000 --- a/src/DotNet/MD/MetaDataHeader.cs +++ /dev/null @@ -1,147 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Collections.Generic; -using System.Text; -using dnlib.IO; - -namespace dnlib.DotNet.MD { - /// - /// Represents the .NET metadata header - /// - /// IMAGE_COR20_HEADER.MetaData points to this header - public sealed class MetaDataHeader : FileSection { - readonly uint signature; - readonly ushort majorVersion; - readonly ushort minorVersion; - readonly uint reserved1; - readonly uint stringLength; - readonly string versionString; - readonly FileOffset offset2ndPart; - readonly StorageFlags flags; - readonly byte reserved2; - readonly ushort streams; - readonly IList streamHeaders; - - /// - /// Returns the signature (should be 0x424A5342) - /// - public uint Signature { - get { return signature; } - } - - /// - /// Returns the major version - /// - public ushort MajorVersion { - get { return majorVersion; } - } - - /// - /// Returns the minor version - /// - public ushort MinorVersion { - get { return minorVersion; } - } - - /// - /// Returns the reserved dword (pointer to extra header data) - /// - public uint Reserved1 { - get { return reserved1; } - } - - /// - /// Returns the version string length value - /// - public uint StringLength { - get { return stringLength; } - } - - /// - /// Returns the version string - /// - public string VersionString { - get { return versionString; } - } - - /// - /// Returns the offset of STORAGEHEADER - /// - public FileOffset StorageHeaderOffset { - get { return offset2ndPart; } - } - - /// - /// Returns the flags (reserved) - /// - public StorageFlags Flags { - get { return flags; } - } - - /// - /// Returns the reserved byte (padding) - /// - public byte Reserved2 { - get { return reserved2; } - } - - /// - /// Returns the number of streams - /// - public ushort Streams { - get { return streams; } - } - - /// - /// Returns all stream headers - /// - public IList StreamHeaders { - get { return streamHeaders; } - } - - /// - /// Constructor - /// - /// PE file reader pointing to the start of this section - /// Verify section - /// Thrown if verification fails - public MetaDataHeader(IImageStream reader, bool verify) { - SetStartOffset(reader); - this.signature = reader.ReadUInt32(); - if (verify && this.signature != 0x424A5342) - throw new BadImageFormatException("Invalid MetaData header signature"); - this.majorVersion = reader.ReadUInt16(); - this.minorVersion = reader.ReadUInt16(); - if (verify && !((majorVersion == 1 && minorVersion == 1) || (majorVersion == 0 && minorVersion >= 19))) - throw new BadImageFormatException(string.Format("Unknown MetaData header version: {0}.{1}", majorVersion, minorVersion)); - this.reserved1 = reader.ReadUInt32(); - this.stringLength = reader.ReadUInt32(); - this.versionString = ReadString(reader, stringLength); - this.offset2ndPart = reader.FileOffset + reader.Position; - this.flags = (StorageFlags)reader.ReadByte(); - this.reserved2 = reader.ReadByte(); - this.streams = reader.ReadUInt16(); - this.streamHeaders = new StreamHeader[streams]; - for (int i = 0; i < streamHeaders.Count; i++) - streamHeaders[i] = new StreamHeader(reader, verify); - SetEndoffset(reader); - } - - static string ReadString(IImageStream reader, uint maxLength) { - long endPos = reader.Position + maxLength; - if (endPos < reader.Position || endPos > reader.Length) - throw new BadImageFormatException("Invalid MD version string"); - byte[] utf8Bytes = new byte[maxLength]; - uint i; - for (i = 0; i < maxLength; i++) { - byte b = reader.ReadByte(); - if (b == 0) - break; - utf8Bytes[i] = b; - } - reader.Position = endPos; - return Encoding.UTF8.GetString(utf8Bytes, 0, (int)i); - } - } -} diff --git a/src/DotNet/MD/IMetaData.cs b/src/DotNet/MD/Metadata.cs similarity index 66% rename from src/DotNet/MD/IMetaData.cs rename to src/DotNet/MD/Metadata.cs index 41d0ec65d..6d2501198 100644 --- a/src/DotNet/MD/IMetaData.cs +++ b/src/DotNet/MD/Metadata.cs @@ -1,139 +1,140 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; using dnlib.PE; -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif - namespace dnlib.DotNet.MD { /// - /// Interface to access the .NET metadata + /// Reads .NET metadata /// - public interface IMetaData : IDisposable { + public abstract class Metadata : IDisposable { /// /// true if the compressed (normal) metadata is used, false if the non-compressed /// (Edit N' Continue) metadata is used. This can be false even if the table stream /// is #~ but that's very uncommon. /// - bool IsCompressed { get; } + public abstract bool IsCompressed { get; } /// - /// Gets the .NET header + /// true if this is standalone Portable PDB metadata /// - ImageCor20Header ImageCor20Header { get; } + public abstract bool IsStandalonePortablePdb { get; } /// - /// Gets the major version number found in the MetaData header + /// Gets the .NET header /// - ushort MajorVersion { get; } + public abstract ImageCor20Header ImageCor20Header { get; } /// - /// Gets the minor version number found in the MetaData header + /// Gets the version found in the metadata header. The major version number is in the high 16 bits + /// and the lower version number is in the low 16 bits. /// - ushort MinorVersion { get; } + public abstract uint Version { get; } /// - /// Gets the version string found in the MetaData header + /// Gets the version string found in the metadata header /// - string VersionString { get; } + public abstract string VersionString { get; } /// /// Gets the /// - IPEImage PEImage { get; } + public abstract IPEImage PEImage { get; } /// /// Gets the metadata header /// - MetaDataHeader MetaDataHeader { get; } + public abstract MetadataHeader MetadataHeader { get; } /// /// Returns the #Strings stream or a default empty one if it's not present /// - StringsStream StringsStream { get; } + public abstract StringsStream StringsStream { get; } /// /// Returns the #US stream or a default empty one if it's not present /// - USStream USStream { get; } + public abstract USStream USStream { get; } /// /// Returns the #Blob stream or a default empty one if it's not present /// - BlobStream BlobStream { get; } + public abstract BlobStream BlobStream { get; } /// /// Returns the #GUID stream or a default empty one if it's not present /// - GuidStream GuidStream { get; } + public abstract GuidStream GuidStream { get; } /// /// Returns the #~ or #- tables stream /// - TablesStream TablesStream { get; } + public abstract TablesStream TablesStream { get; } + + /// + /// Returns the #Pdb stream or null if it's not a standalone portable PDB file + /// + public abstract PdbStream PdbStream { get; } /// /// Gets all streams /// - ThreadSafe.IList AllStreams { get; } + public abstract IList AllStreams { get; } /// /// Gets a list of all the valid TypeDef rids. It's usually every rid in the /// TypeDef table, but could be less if a type has been deleted. /// - RidList GetTypeDefRidList(); + public abstract RidList GetTypeDefRidList(); /// /// Gets a list of all the valid ExportedType rids. It's usually every rid in the /// ExportedType table, but could be less if a type has been deleted. /// - RidList GetExportedTypeRidList(); + public abstract RidList GetExportedTypeRidList(); /// /// Gets the Field rid list /// /// TypeDef rid /// A new instance - RidList GetFieldRidList(uint typeDefRid); + public abstract RidList GetFieldRidList(uint typeDefRid); /// /// Gets the Method rid list /// /// TypeDef rid /// A new instance - RidList GetMethodRidList(uint typeDefRid); + public abstract RidList GetMethodRidList(uint typeDefRid); /// /// Gets the Param rid list /// /// Method rid /// A new instance - RidList GetParamRidList(uint methodRid); + public abstract RidList GetParamRidList(uint methodRid); /// /// Gets the Event rid list /// /// EventMap rid /// A new instance - RidList GetEventRidList(uint eventMapRid); + public abstract RidList GetEventRidList(uint eventMapRid); /// /// Gets the Property rid list /// /// PropertyMap rid /// A new instance - RidList GetPropertyRidList(uint propertyMapRid); + public abstract RidList GetPropertyRidList(uint propertyMapRid); /// /// Finds all InterfaceImpl rids owned by /// /// Owner TypeDef rid /// A instance containing the valid InterfaceImpl rids - RidList GetInterfaceImplRidList(uint typeDefRid); + public abstract RidList GetInterfaceImplRidList(uint typeDefRid); /// /// Finds all GenericParam rids owned by in table @@ -141,14 +142,14 @@ public interface IMetaData : IDisposable { /// A TypeOrMethodDef table /// Owner rid /// A instance containing the valid GenericParam rids - RidList GetGenericParamRidList(Table table, uint rid); + public abstract RidList GetGenericParamRidList(Table table, uint rid); /// /// Finds all GenericParamConstraint rids owned by /// /// Owner GenericParam rid /// A instance containing the valid GenericParamConstraint rids - RidList GetGenericParamConstraintRidList(uint genericParamRid); + public abstract RidList GetGenericParamConstraintRidList(uint genericParamRid); /// /// Finds all CustomAttribute rids owned by in table @@ -156,7 +157,7 @@ public interface IMetaData : IDisposable { /// A HasCustomAttribute table /// Owner rid /// A instance containing the valid CustomAttribute rids - RidList GetCustomAttributeRidList(Table table, uint rid); + public abstract RidList GetCustomAttributeRidList(Table table, uint rid); /// /// Finds all DeclSecurity rids owned by in table @@ -164,7 +165,7 @@ public interface IMetaData : IDisposable { /// A HasDeclSecurity table /// Owner rid /// A instance containing the valid DeclSecurity rids - RidList GetDeclSecurityRidList(Table table, uint rid); + public abstract RidList GetDeclSecurityRidList(Table table, uint rid); /// /// Finds all MethodSemantics rids owned by in table @@ -172,14 +173,14 @@ public interface IMetaData : IDisposable { /// A HasSemantic table /// Owner rid /// A instance containing the valid MethodSemantics rids - RidList GetMethodSemanticsRidList(Table table, uint rid); + public abstract RidList GetMethodSemanticsRidList(Table table, uint rid); /// /// Finds all MethodImpl rids owned by /// /// Owner TypeDef rid /// A instance containing the valid MethodImpl rids - RidList GetMethodImplRidList(uint typeDefRid); + public abstract RidList GetMethodImplRidList(uint typeDefRid); /// /// Finds a ClassLayout rid @@ -187,7 +188,7 @@ public interface IMetaData : IDisposable { /// Owner TypeDef rid /// The ClassLayout rid or 0 if is invalid /// or if it has no row in the ClassLayout table. - uint GetClassLayoutRid(uint typeDefRid); + public abstract uint GetClassLayoutRid(uint typeDefRid); /// /// Finds a FieldLayout rid @@ -195,7 +196,7 @@ public interface IMetaData : IDisposable { /// Owner Field rid /// The FieldLayout rid or 0 if is invalid /// or if it has no row in the FieldLayout table. - uint GetFieldLayoutRid(uint fieldRid); + public abstract uint GetFieldLayoutRid(uint fieldRid); /// /// Finds a FieldMarshal rid @@ -204,7 +205,7 @@ public interface IMetaData : IDisposable { /// Owner rid /// The FieldMarshal rid or 0 if is invalid /// or if it has no row in the FieldMarshal table. - uint GetFieldMarshalRid(Table table, uint rid); + public abstract uint GetFieldMarshalRid(Table table, uint rid); /// /// Finds a FieldRVA rid @@ -212,7 +213,7 @@ public interface IMetaData : IDisposable { /// Owner Field rid /// The FieldRVA rid or 0 if is invalid /// or if it has no row in the FieldRVA table. - uint GetFieldRVARid(uint fieldRid); + public abstract uint GetFieldRVARid(uint fieldRid); /// /// Finds an ImplMap rid @@ -221,7 +222,7 @@ public interface IMetaData : IDisposable { /// Owner rid /// The ImplMap rid or 0 if is invalid /// or if it has no row in the ImplMap table. - uint GetImplMapRid(Table table, uint rid); + public abstract uint GetImplMapRid(Table table, uint rid); /// /// Finds a NestedClass rid @@ -229,7 +230,7 @@ public interface IMetaData : IDisposable { /// Owner TypeDef rid /// The NestedClass rid or 0 if is invalid /// or if it has no row in the NestedClass table. - uint GetNestedClassRid(uint typeDefRid); + public abstract uint GetNestedClassRid(uint typeDefRid); /// /// Finds an EventMap rid @@ -237,7 +238,7 @@ public interface IMetaData : IDisposable { /// Owner TypeDef rid /// The EventMap rid or 0 if is invalid /// or if it has no row in the EventMap table. - uint GetEventMapRid(uint typeDefRid); + public abstract uint GetEventMapRid(uint typeDefRid); /// /// Finds a PropertyMap rid @@ -245,7 +246,7 @@ public interface IMetaData : IDisposable { /// Owner TypeDef rid /// The PropertyMap rid or 0 if is invalid /// or if it has no row in the PropertyMap table. - uint GetPropertyMapRid(uint typeDefRid); + public abstract uint GetPropertyMapRid(uint typeDefRid); /// /// Finds a Constant rid @@ -254,7 +255,7 @@ public interface IMetaData : IDisposable { /// Owner rid /// The Constant rid or 0 if is invalid /// or if it has no row in the Constant table. - uint GetConstantRid(Table table, uint rid); + public abstract uint GetConstantRid(Table table, uint rid); /// /// Returns the owner TypeDef rid @@ -262,7 +263,7 @@ public interface IMetaData : IDisposable { /// A Field rid /// The owner TypeDef rid or 0 if is invalid /// or if it has no owner. - uint GetOwnerTypeOfField(uint fieldRid); + public abstract uint GetOwnerTypeOfField(uint fieldRid); /// /// Returns the owner TypeDef rid @@ -270,7 +271,7 @@ public interface IMetaData : IDisposable { /// A Method rid /// The owner TypeDef rid or 0 if is invalid /// or if it has no owner. - uint GetOwnerTypeOfMethod(uint methodRid); + public abstract uint GetOwnerTypeOfMethod(uint methodRid); /// /// Returns the owner TypeDef rid @@ -278,7 +279,7 @@ public interface IMetaData : IDisposable { /// A Event rid /// The owner TypeDef rid or 0 if is invalid /// or if it has no owner. - uint GetOwnerTypeOfEvent(uint eventRid); + public abstract uint GetOwnerTypeOfEvent(uint eventRid); /// /// Returns the owner TypeDef rid @@ -286,7 +287,7 @@ public interface IMetaData : IDisposable { /// A Property rid /// The owner TypeDef rid or 0 if is invalid /// or if it has no owner. - uint GetOwnerTypeOfProperty(uint propertyRid); + public abstract uint GetOwnerTypeOfProperty(uint propertyRid); /// /// Returns the owner TypeOrMethodDef rid @@ -294,7 +295,7 @@ public interface IMetaData : IDisposable { /// A GenericParam rid /// The owner TypeOrMethodDef rid or 0 if is /// invalid or if it has no owner. - uint GetOwnerOfGenericParam(uint gpRid); + public abstract uint GetOwnerOfGenericParam(uint gpRid); /// /// Returns the owner GenericParam rid @@ -302,7 +303,7 @@ public interface IMetaData : IDisposable { /// A GenericParamConstraint rid /// The owner GenericParam rid or 0 if is /// invalid or if it has no owner. - uint GetOwnerOfGenericParamConstraint(uint gpcRid); + public abstract uint GetOwnerOfGenericParamConstraint(uint gpcRid); /// /// Returns the owner Method rid @@ -310,20 +311,61 @@ public interface IMetaData : IDisposable { /// A Param rid /// The owner Method rid or 0 if is invalid /// or if it has no owner. - uint GetOwnerOfParam(uint paramRid); + public abstract uint GetOwnerOfParam(uint paramRid); /// /// Gets a list of all nested classes owned by /// /// A TypeDef rid /// A new instance - RidList GetNestedClassRidList(uint typeDefRid); + public abstract RidList GetNestedClassRidList(uint typeDefRid); /// /// Gets a list of all non-nested classes. A type is a non-nested type if /// returns an empty list. /// /// A new instance - RidList GetNonNestedClassRidList(); + public abstract RidList GetNonNestedClassRidList(); + + /// + /// Finds all LocalScope rids owned by + /// + /// Owner Method rid + /// A instance containing the valid LocalScope rids + public abstract RidList GetLocalScopeRidList(uint methodRid); + + /// + /// Finds all LocalVariable rids owned by + /// + /// Owner LocalScope rid + /// A instance containing the valid LocalVariable rids + public abstract RidList GetLocalVariableRidList(uint localScopeRid); + + /// + /// Finds all LocalConstant rids owned by + /// + /// Owner LocalScope rid + /// A instance containing the valid LocalConstant rids + public abstract RidList GetLocalConstantRidList(uint localScopeRid); + + /// + /// Gets the StateMachineMethod rid or 0 if it's not a state machine method + /// + /// Owner Method rid + /// + public abstract uint GetStateMachineMethodRid(uint methodRid); + + /// + /// Finds all CustomDebugInformation rids owned by in table + /// + /// A HasCustomDebugInformation table + /// Owner rid + /// A instance containing the valid CustomDebugInformation rids + public abstract RidList GetCustomDebugInformationRidList(Table table, uint rid); + + /// + /// Disposes of this instance + /// + public abstract void Dispose(); } } diff --git a/src/DotNet/MD/MetaData.cs b/src/DotNet/MD/MetadataBase.cs similarity index 63% rename from src/DotNet/MD/MetaData.cs rename to src/DotNet/MD/MetadataBase.cs index fd5b03eb8..9e6e11530 100644 --- a/src/DotNet/MD/MetaData.cs +++ b/src/DotNet/MD/MetadataBase.cs @@ -1,23 +1,18 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Threading; +using dnlib.IO; using dnlib.PE; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif namespace dnlib.DotNet.MD { /// /// Common base class for #~ and #- metadata classes /// - abstract class MetaData : IMetaData { + abstract class MetadataBase : Metadata { /// /// The PE image /// @@ -31,7 +26,7 @@ abstract class MetaData : IMetaData { /// /// The MD header /// - protected MetaDataHeader mdHeader; + protected MetadataHeader mdHeader; /// /// The #Strings stream @@ -58,10 +53,19 @@ abstract class MetaData : IMetaData { /// protected TablesStream tablesStream; + /// + /// The #Pdb stream + /// + protected PdbStream pdbStream; + /// /// All the streams that are present in the PE image /// - protected ThreadSafe.IList allStreams; + protected IList allStreams; + + public override bool IsStandalonePortablePdb => isStandalonePortablePdb; + /// true if this is standalone Portable PDB metadata + protected readonly bool isStandalonePortablePdb; uint[] fieldRidToTypeDefRid; uint[] methodRidToTypeDefRid; @@ -70,8 +74,10 @@ abstract class MetaData : IMetaData { uint[] gpRidToOwnerRid; uint[] gpcRidToOwnerRid; uint[] paramRidToOwnerRid; - Dictionary typeDefRidToNestedClasses; - RandomRidList nonNestedTypes; + Dictionary> typeDefRidToNestedClasses; + StrongBox nonNestedTypes; + + DataReaderFactory mdReaderFactoryToDisposeLater; /// /// Sorts a table by key column @@ -83,7 +89,7 @@ protected sealed class SortedTable { /// Remembers rid and key /// [DebuggerDisplay("{rid} {key}")] - struct RowInfo : IComparable { + readonly struct RowInfo : IComparable { public readonly uint rid; public readonly uint key; @@ -97,7 +103,6 @@ public RowInfo(uint rid, uint key) { this.key = key; } - /// public int CompareTo(RowInfo other) { if (key < other.key) return -1; @@ -119,17 +124,17 @@ public SortedTable(MDTable mdTable, int keyColIndex) { void InitializeKeys(MDTable mdTable, int keyColIndex) { var keyColumn = mdTable.TableInfo.Columns[keyColIndex]; + Debug.Assert(keyColumn.Size == 2 || keyColumn.Size == 4); rows = new RowInfo[mdTable.Rows + 1]; if (mdTable.Rows == 0) return; - using (var reader = mdTable.CloneImageStream()) { - reader.Position = keyColumn.Offset; - int increment = mdTable.TableInfo.RowSize - keyColumn.Size; - for (uint i = 1; i <= mdTable.Rows; i++) { - rows[i] = new RowInfo(i, keyColumn.Read(reader)); - if (i < mdTable.Rows) - reader.Position += increment; - } + var reader = mdTable.DataReader; + reader.Position = (uint)keyColumn.Offset; + uint increment = (uint)(mdTable.TableInfo.RowSize - keyColumn.Size); + for (uint i = 1; i <= mdTable.Rows; i++) { + rows[i] = new RowInfo(i, keyColumn.Unsafe_Read24(ref reader)); + if (i < mdTable.Rows) + reader.Position += increment; } } @@ -172,77 +177,27 @@ public RidList FindAllRows(uint key) { if (key != rows[endIndex].key) break; } - var list = new RandomRidList(endIndex - startIndex); + var list = new List(endIndex - startIndex); for (int i = startIndex; i < endIndex; i++) list.Add(rows[i].rid); - return list; + return RidList.Create(list); } } SortedTable eventMapSortedTable; SortedTable propertyMapSortedTable; - /// - public abstract bool IsCompressed { get; } - - /// - public ImageCor20Header ImageCor20Header { - get { return cor20Header; } - } - - /// - public ushort MajorVersion { - get { return mdHeader.MajorVersion; } - } - - /// - public ushort MinorVersion { - get { return mdHeader.MinorVersion; } - } - - /// - public string VersionString { - get { return mdHeader.VersionString; } - } - - /// - public IPEImage PEImage { - get { return peImage; } - } - - /// - public MetaDataHeader MetaDataHeader { - get { return mdHeader; } - } - - /// - public StringsStream StringsStream { - get { return stringsStream; } - } - - /// - public USStream USStream { - get { return usStream; } - } - - /// - public BlobStream BlobStream { - get { return blobStream; } - } - - /// - public GuidStream GuidStream { - get { return guidStream; } - } - - /// - public TablesStream TablesStream { - get { return tablesStream; } - } - - /// - public ThreadSafe.IList AllStreams { - get { return allStreams; } - } + public override ImageCor20Header ImageCor20Header => cor20Header; + public override uint Version => ((uint)mdHeader.MajorVersion << 16) | mdHeader.MinorVersion; + public override string VersionString => mdHeader.VersionString; + public override IPEImage PEImage => peImage; + public override MetadataHeader MetadataHeader => mdHeader; + public override StringsStream StringsStream => stringsStream; + public override USStream USStream => usStream; + public override BlobStream BlobStream => blobStream; + public override GuidStream GuidStream => guidStream; + public override TablesStream TablesStream => tablesStream; + public override PdbStream PdbStream => pdbStream; + public override IList AllStreams => allStreams; /// /// Constructor @@ -250,28 +205,51 @@ public ThreadSafe.IList AllStreams { /// The PE image /// The .NET header /// The MD header - protected MetaData(IPEImage peImage, ImageCor20Header cor20Header, MetaDataHeader mdHeader) { + protected MetadataBase(IPEImage peImage, ImageCor20Header cor20Header, MetadataHeader mdHeader) { try { - this.allStreams = ThreadSafeListCreator.Create(); + allStreams = new List(); this.peImage = peImage; this.cor20Header = cor20Header; this.mdHeader = mdHeader; + isStandalonePortablePdb = false; } catch { - if (peImage != null) + if (peImage is not null) peImage.Dispose(); throw; } } + internal MetadataBase(MetadataHeader mdHeader, bool isStandalonePortablePdb) { + allStreams = new List(); + peImage = null; + cor20Header = null; + this.mdHeader = mdHeader; + this.isStandalonePortablePdb = isStandalonePortablePdb; + } + /// /// Initializes the metadata, tables, streams /// - public void Initialize() { - InitializeInternal(); + public void Initialize(DataReaderFactory mdReaderFactory) { + mdReaderFactoryToDisposeLater = mdReaderFactory; + uint metadataBaseOffset; + if (peImage is not null) { + Debug.Assert(mdReaderFactory is null); + Debug.Assert(cor20Header is not null); + metadataBaseOffset = (uint)peImage.ToFileOffset(cor20Header.Metadata.VirtualAddress); + mdReaderFactory = peImage.DataReaderFactory; + } + else { + Debug.Assert(mdReaderFactory is not null); + metadataBaseOffset = 0; + } + InitializeInternal(mdReaderFactory, metadataBaseOffset); - if (tablesStream == null) + if (tablesStream is null) throw new BadImageFormatException("Missing MD stream"); + if (isStandalonePortablePdb && pdbStream is null) + throw new BadImageFormatException("Missing #Pdb stream"); InitializeNonExistentHeaps(); } @@ -279,57 +257,33 @@ public void Initialize() { /// Creates empty heap objects if they're not present in the metadata /// protected void InitializeNonExistentHeaps() { - if (stringsStream == null) + if (stringsStream is null) stringsStream = new StringsStream(); - if (usStream == null) + if (usStream is null) usStream = new USStream(); - if (blobStream == null) + if (blobStream is null) blobStream = new BlobStream(); - if (guidStream == null) + if (guidStream is null) guidStream = new GuidStream(); } /// - /// Called by + /// Called by /// - protected abstract void InitializeInternal(); - - /// - public virtual RidList GetTypeDefRidList() { - return new ContiguousRidList(1, tablesStream.TypeDefTable.Rows); - } - - /// - public virtual RidList GetExportedTypeRidList() { - return new ContiguousRidList(1, tablesStream.ExportedTypeTable.Rows); - } - - /// - public abstract RidList GetFieldRidList(uint typeDefRid); - - /// - public abstract RidList GetMethodRidList(uint typeDefRid); + protected abstract void InitializeInternal(DataReaderFactory mdReaderFactory, uint metadataBaseOffset); - /// - public abstract RidList GetParamRidList(uint methodRid); - - /// - public abstract RidList GetEventRidList(uint eventMapRid); - - /// - public abstract RidList GetPropertyRidList(uint propertyMapRid); + public override RidList GetTypeDefRidList() => RidList.Create(1, tablesStream.TypeDefTable.Rows); + public override RidList GetExportedTypeRidList() => RidList.Create(1, tablesStream.ExportedTypeTable.Rows); /// /// Binary searches the table for a rid whose key column at index - /// is equal to . The - /// has acquired its lock so only *_NoLock methods - /// may be called. + /// is equal to . /// /// Table to search /// Key column index /// Key /// The rid of the found row, or 0 if none found - protected abstract uint BinarySearch_NoLock(MDTable tableSource, int keyColIndex, uint key); + protected abstract uint BinarySearch(MDTable tableSource, int keyColIndex, uint key); /// /// Finds all rows owned by in table @@ -340,32 +294,24 @@ public virtual RidList GetExportedTypeRidList() { /// Key /// A instance protected RidList FindAllRows(MDTable tableSource, int keyColIndex, uint key) { -#if THREAD_SAFE - tablesStream.theLock.EnterWriteLock(); try { -#endif - uint startRid = BinarySearch_NoLock(tableSource, keyColIndex, key); + uint startRid = BinarySearch(tableSource, keyColIndex, key); if (tableSource.IsInvalidRID(startRid)) return RidList.Empty; uint endRid = startRid + 1; var column = tableSource.TableInfo.Columns[keyColIndex]; for (; startRid > 1; startRid--) { - uint key2; - if (!tablesStream.ReadColumn_NoLock(tableSource, startRid - 1, column, out key2)) + if (!tablesStream.TryReadColumn24(tableSource, startRid - 1, column, out uint key2)) break; // Should never happen since startRid is valid if (key != key2) break; } for (; endRid <= tableSource.Rows; endRid++) { - uint key2; - if (!tablesStream.ReadColumn_NoLock(tableSource, endRid, column, out key2)) + if (!tablesStream.TryReadColumn24(tableSource, endRid, column, out uint key2)) break; // Should never happen since endRid is valid if (key != key2) break; } - return new ContiguousRidList(startRid, endRid - startRid); -#if THREAD_SAFE - } finally { tablesStream.theLock.ExitWriteLock(); } -#endif + return RidList.Create(startRid, endRid - startRid); } /// @@ -377,131 +323,100 @@ protected RidList FindAllRows(MDTable tableSource, int keyColIndex, uint key) { /// Key column index /// Key /// A instance - protected virtual RidList FindAllRowsUnsorted(MDTable tableSource, int keyColIndex, uint key) { - return FindAllRows(tableSource, keyColIndex, key); - } + protected virtual RidList FindAllRowsUnsorted(MDTable tableSource, int keyColIndex, uint key) => FindAllRows(tableSource, keyColIndex, key); - /// - public RidList GetInterfaceImplRidList(uint typeDefRid) { - return FindAllRowsUnsorted(tablesStream.InterfaceImplTable, 0, typeDefRid); - } + public override RidList GetInterfaceImplRidList(uint typeDefRid) => FindAllRowsUnsorted(tablesStream.InterfaceImplTable, 0, typeDefRid); - /// - public RidList GetGenericParamRidList(Table table, uint rid) { - uint codedToken; - if (!CodedToken.TypeOrMethodDef.Encode(new MDToken(table, rid), out codedToken)) + public override RidList GetGenericParamRidList(Table table, uint rid) { + if (!CodedToken.TypeOrMethodDef.Encode(new MDToken(table, rid), out uint codedToken)) return RidList.Empty; return FindAllRowsUnsorted(tablesStream.GenericParamTable, 2, codedToken); } - /// - public RidList GetGenericParamConstraintRidList(uint genericParamRid) { - return FindAllRowsUnsorted(tablesStream.GenericParamConstraintTable, 0, genericParamRid); - } + public override RidList GetGenericParamConstraintRidList(uint genericParamRid) => + FindAllRowsUnsorted(tablesStream.GenericParamConstraintTable, 0, genericParamRid); - /// - public RidList GetCustomAttributeRidList(Table table, uint rid) { - uint codedToken; - if (!CodedToken.HasCustomAttribute.Encode(new MDToken(table, rid), out codedToken)) + public override RidList GetCustomAttributeRidList(Table table, uint rid) { + if (!CodedToken.HasCustomAttribute.Encode(new MDToken(table, rid), out uint codedToken)) return RidList.Empty; return FindAllRowsUnsorted(tablesStream.CustomAttributeTable, 0, codedToken); } - /// - public RidList GetDeclSecurityRidList(Table table, uint rid) { - uint codedToken; - if (!CodedToken.HasDeclSecurity.Encode(new MDToken(table, rid), out codedToken)) + public override RidList GetDeclSecurityRidList(Table table, uint rid) { + if (!CodedToken.HasDeclSecurity.Encode(new MDToken(table, rid), out uint codedToken)) return RidList.Empty; return FindAllRowsUnsorted(tablesStream.DeclSecurityTable, 1, codedToken); } - /// - public RidList GetMethodSemanticsRidList(Table table, uint rid) { - uint codedToken; - if (!CodedToken.HasSemantic.Encode(new MDToken(table, rid), out codedToken)) + public override RidList GetMethodSemanticsRidList(Table table, uint rid) { + if (!CodedToken.HasSemantic.Encode(new MDToken(table, rid), out uint codedToken)) return RidList.Empty; return FindAllRowsUnsorted(tablesStream.MethodSemanticsTable, 2, codedToken); } - /// - public RidList GetMethodImplRidList(uint typeDefRid) { - return FindAllRowsUnsorted(tablesStream.MethodImplTable, 0, typeDefRid); - } + public override RidList GetMethodImplRidList(uint typeDefRid) => FindAllRowsUnsorted(tablesStream.MethodImplTable, 0, typeDefRid); - /// - public uint GetClassLayoutRid(uint typeDefRid) { + public override uint GetClassLayoutRid(uint typeDefRid) { var list = FindAllRowsUnsorted(tablesStream.ClassLayoutTable, 2, typeDefRid); - return list.Length == 0 ? 0 : list[0]; + return list.Count == 0 ? 0 : list[0]; } - /// - public uint GetFieldLayoutRid(uint fieldRid) { + public override uint GetFieldLayoutRid(uint fieldRid) { var list = FindAllRowsUnsorted(tablesStream.FieldLayoutTable, 1, fieldRid); - return list.Length == 0 ? 0 : list[0]; + return list.Count == 0 ? 0 : list[0]; } - /// - public uint GetFieldMarshalRid(Table table, uint rid) { - uint codedToken; - if (!CodedToken.HasFieldMarshal.Encode(new MDToken(table, rid), out codedToken)) + public override uint GetFieldMarshalRid(Table table, uint rid) { + if (!CodedToken.HasFieldMarshal.Encode(new MDToken(table, rid), out uint codedToken)) return 0; var list = FindAllRowsUnsorted(tablesStream.FieldMarshalTable, 0, codedToken); - return list.Length == 0 ? 0 : list[0]; + return list.Count == 0 ? 0 : list[0]; } - /// - public uint GetFieldRVARid(uint fieldRid) { + public override uint GetFieldRVARid(uint fieldRid) { var list = FindAllRowsUnsorted(tablesStream.FieldRVATable, 1, fieldRid); - return list.Length == 0 ? 0 : list[0]; + return list.Count == 0 ? 0 : list[0]; } - /// - public uint GetImplMapRid(Table table, uint rid) { - uint codedToken; - if (!CodedToken.MemberForwarded.Encode(new MDToken(table, rid), out codedToken)) + public override uint GetImplMapRid(Table table, uint rid) { + if (!CodedToken.MemberForwarded.Encode(new MDToken(table, rid), out uint codedToken)) return 0; var list = FindAllRowsUnsorted(tablesStream.ImplMapTable, 1, codedToken); - return list.Length == 0 ? 0 : list[0]; + return list.Count == 0 ? 0 : list[0]; } - /// - public uint GetNestedClassRid(uint typeDefRid) { + public override uint GetNestedClassRid(uint typeDefRid) { var list = FindAllRowsUnsorted(tablesStream.NestedClassTable, 0, typeDefRid); - return list.Length == 0 ? 0 : list[0]; + return list.Count == 0 ? 0 : list[0]; } - /// - public uint GetEventMapRid(uint typeDefRid) { + public override uint GetEventMapRid(uint typeDefRid) { // The EventMap and PropertyMap tables can only be trusted to be sorted if it's // an NGen image and it's the normal #- stream. The IsSorted bit must not be used // to check whether the tables are sorted. See coreclr: md/inc/metamodel.h / IsVerified() - if (eventMapSortedTable == null) + if (eventMapSortedTable is null) Interlocked.CompareExchange(ref eventMapSortedTable, new SortedTable(tablesStream.EventMapTable, 0), null); var list = eventMapSortedTable.FindAllRows(typeDefRid); - return list.Length == 0 ? 0 : list[0]; + return list.Count == 0 ? 0 : list[0]; } - /// - public uint GetPropertyMapRid(uint typeDefRid) { + public override uint GetPropertyMapRid(uint typeDefRid) { // Always unsorted, see comment in GetEventMapRid() above - if (propertyMapSortedTable == null) + if (propertyMapSortedTable is null) Interlocked.CompareExchange(ref propertyMapSortedTable, new SortedTable(tablesStream.PropertyMapTable, 0), null); var list = propertyMapSortedTable.FindAllRows(typeDefRid); - return list.Length == 0 ? 0 : list[0]; + return list.Count == 0 ? 0 : list[0]; } - /// - public uint GetConstantRid(Table table, uint rid) { - uint codedToken; - if (!CodedToken.HasConstant.Encode(new MDToken(table, rid), out codedToken)) + public override uint GetConstantRid(Table table, uint rid) { + if (!CodedToken.HasConstant.Encode(new MDToken(table, rid), out uint codedToken)) return 0; - var list = FindAllRowsUnsorted(tablesStream.ConstantTable, 1, codedToken); - return list.Length == 0 ? 0 : list[0]; + var list = FindAllRowsUnsorted(tablesStream.ConstantTable, 2, codedToken); + return list.Count == 0 ? 0 : list[0]; } - /// - public uint GetOwnerTypeOfField(uint fieldRid) { - if (fieldRidToTypeDefRid == null) + public override uint GetOwnerTypeOfField(uint fieldRid) { + if (fieldRidToTypeDefRid is null) InitializeInverseFieldOwnerRidList(); uint index = fieldRid - 1; if (index >= fieldRidToTypeDefRid.LongLength) @@ -510,14 +425,14 @@ public uint GetOwnerTypeOfField(uint fieldRid) { } void InitializeInverseFieldOwnerRidList() { - if (fieldRidToTypeDefRid != null) + if (fieldRidToTypeDefRid is not null) return; var newFieldRidToTypeDefRid = new uint[tablesStream.FieldTable.Rows]; var ownerList = GetTypeDefRidList(); - for (uint i = 0; i < ownerList.Length; i++) { + for (int i = 0; i < ownerList.Count; i++) { var ownerRid = ownerList[i]; var fieldList = GetFieldRidList(ownerRid); - for (uint j = 0; j < fieldList.Length; j++) { + for (int j = 0; j < fieldList.Count; j++) { uint ridIndex = fieldList[j] - 1; if (newFieldRidToTypeDefRid[ridIndex] != 0) continue; @@ -527,9 +442,8 @@ void InitializeInverseFieldOwnerRidList() { Interlocked.CompareExchange(ref fieldRidToTypeDefRid, newFieldRidToTypeDefRid, null); } - /// - public uint GetOwnerTypeOfMethod(uint methodRid) { - if (methodRidToTypeDefRid == null) + public override uint GetOwnerTypeOfMethod(uint methodRid) { + if (methodRidToTypeDefRid is null) InitializeInverseMethodOwnerRidList(); uint index = methodRid - 1; if (index >= methodRidToTypeDefRid.LongLength) @@ -538,14 +452,14 @@ public uint GetOwnerTypeOfMethod(uint methodRid) { } void InitializeInverseMethodOwnerRidList() { - if (methodRidToTypeDefRid != null) + if (methodRidToTypeDefRid is not null) return; var newMethodRidToTypeDefRid = new uint[tablesStream.MethodTable.Rows]; var ownerList = GetTypeDefRidList(); - for (uint i = 0; i < ownerList.Length; i++) { + for (int i = 0; i < ownerList.Count; i++) { var ownerRid = ownerList[i]; var methodList = GetMethodRidList(ownerRid); - for (uint j = 0; j < methodList.Length; j++) { + for (int j = 0; j < methodList.Count; j++) { uint ridIndex = methodList[j] - 1; if (newMethodRidToTypeDefRid[ridIndex] != 0) continue; @@ -555,9 +469,8 @@ void InitializeInverseMethodOwnerRidList() { Interlocked.CompareExchange(ref methodRidToTypeDefRid, newMethodRidToTypeDefRid, null); } - /// - public uint GetOwnerTypeOfEvent(uint eventRid) { - if (eventRidToTypeDefRid == null) + public override uint GetOwnerTypeOfEvent(uint eventRid) { + if (eventRidToTypeDefRid is null) InitializeInverseEventOwnerRidList(); uint index = eventRid - 1; if (index >= eventRidToTypeDefRid.LongLength) @@ -566,14 +479,14 @@ public uint GetOwnerTypeOfEvent(uint eventRid) { } void InitializeInverseEventOwnerRidList() { - if (eventRidToTypeDefRid != null) + if (eventRidToTypeDefRid is not null) return; var newEventRidToTypeDefRid = new uint[tablesStream.EventTable.Rows]; var ownerList = GetTypeDefRidList(); - for (uint i = 0; i < ownerList.Length; i++) { + for (int i = 0; i < ownerList.Count; i++) { var ownerRid = ownerList[i]; var eventList = GetEventRidList(GetEventMapRid(ownerRid)); - for (uint j = 0; j < eventList.Length; j++) { + for (int j = 0; j < eventList.Count; j++) { uint ridIndex = eventList[j] - 1; if (newEventRidToTypeDefRid[ridIndex] != 0) continue; @@ -583,9 +496,8 @@ void InitializeInverseEventOwnerRidList() { Interlocked.CompareExchange(ref eventRidToTypeDefRid, newEventRidToTypeDefRid, null); } - /// - public uint GetOwnerTypeOfProperty(uint propertyRid) { - if (propertyRidToTypeDefRid == null) + public override uint GetOwnerTypeOfProperty(uint propertyRid) { + if (propertyRidToTypeDefRid is null) InitializeInversePropertyOwnerRidList(); uint index = propertyRid - 1; if (index >= propertyRidToTypeDefRid.LongLength) @@ -594,14 +506,14 @@ public uint GetOwnerTypeOfProperty(uint propertyRid) { } void InitializeInversePropertyOwnerRidList() { - if (propertyRidToTypeDefRid != null) + if (propertyRidToTypeDefRid is not null) return; var newPropertyRidToTypeDefRid = new uint[tablesStream.PropertyTable.Rows]; var ownerList = GetTypeDefRidList(); - for (uint i = 0; i < ownerList.Length; i++) { + for (int i = 0; i < ownerList.Count; i++) { var ownerRid = ownerList[i]; var propertyList = GetPropertyRidList(GetPropertyMapRid(ownerRid)); - for (uint j = 0; j < propertyList.Length; j++) { + for (int j = 0; j < propertyList.Count; j++) { uint ridIndex = propertyList[j] - 1; if (newPropertyRidToTypeDefRid[ridIndex] != 0) continue; @@ -611,15 +523,14 @@ void InitializeInversePropertyOwnerRidList() { Interlocked.CompareExchange(ref propertyRidToTypeDefRid, newPropertyRidToTypeDefRid, null); } - /// - public uint GetOwnerOfGenericParam(uint gpRid) { + public override uint GetOwnerOfGenericParam(uint gpRid) { // Don't use GenericParam.Owner column. If the GP table is sorted, it's // possible to have two blocks of GPs with the same owner. Only one of the // blocks is the "real" generic params for the owner. Of course, rarely // if ever will this occur, but could happen if some obfuscator has // added it. - if (gpRidToOwnerRid == null) + if (gpRidToOwnerRid is null) InitializeInverseGenericParamOwnerRidList(); uint index = gpRid - 1; if (index >= gpRidToOwnerRid.LongLength) @@ -628,7 +539,7 @@ public uint GetOwnerOfGenericParam(uint gpRid) { } void InitializeInverseGenericParamOwnerRidList() { - if (gpRidToOwnerRid != null) + if (gpRidToOwnerRid is not null) return; var gpTable = tablesStream.GenericParamTable; var newGpRidToOwnerRid = new uint[gpTable.Rows]; @@ -636,29 +547,21 @@ void InitializeInverseGenericParamOwnerRidList() { // Find all owners by reading the GenericParam.Owner column var ownerCol = gpTable.TableInfo.Columns[2]; var ownersDict = new Dictionary(); -#if THREAD_SAFE - tablesStream.theLock.EnterWriteLock(); try { -#endif for (uint rid = 1; rid <= gpTable.Rows; rid++) { - uint owner; - if (!tablesStream.ReadColumn_NoLock(gpTable, rid, ownerCol, out owner)) + if (!tablesStream.TryReadColumn24(gpTable, rid, ownerCol, out uint owner)) continue; ownersDict[owner] = true; } -#if THREAD_SAFE - } finally { tablesStream.theLock.ExitWriteLock(); } -#endif // Now that we have the owners, find all the generic params they own. An obfuscated // module could have 2+ owners pointing to the same generic param row. var owners = new List(ownersDict.Keys); owners.Sort(); for (int i = 0; i < owners.Count; i++) { - uint ownerToken; - if (!CodedToken.TypeOrMethodDef.Decode(owners[i], out ownerToken)) + if (!CodedToken.TypeOrMethodDef.Decode(owners[i], out uint ownerToken)) continue; var ridList = GetGenericParamRidList(MDToken.ToTable(ownerToken), MDToken.ToRID(ownerToken)); - for (uint j = 0; j < ridList.Length; j++) { + for (int j = 0; j < ridList.Count; j++) { uint ridIndex = ridList[j] - 1; if (newGpRidToOwnerRid[ridIndex] != 0) continue; @@ -668,12 +571,11 @@ void InitializeInverseGenericParamOwnerRidList() { Interlocked.CompareExchange(ref gpRidToOwnerRid, newGpRidToOwnerRid, null); } - /// - public uint GetOwnerOfGenericParamConstraint(uint gpcRid) { + public override uint GetOwnerOfGenericParamConstraint(uint gpcRid) { // Don't use GenericParamConstraint.Owner column for the same reason // as described in GetOwnerOfGenericParam(). - if (gpcRidToOwnerRid == null) + if (gpcRidToOwnerRid is null) InitializeInverseGenericParamConstraintOwnerRidList(); uint index = gpcRid - 1; if (index >= gpcRidToOwnerRid.LongLength) @@ -682,32 +584,25 @@ public uint GetOwnerOfGenericParamConstraint(uint gpcRid) { } void InitializeInverseGenericParamConstraintOwnerRidList() { - if (gpcRidToOwnerRid != null) + if (gpcRidToOwnerRid is not null) return; var gpcTable = tablesStream.GenericParamConstraintTable; var newGpcRidToOwnerRid = new uint[gpcTable.Rows]; var ownerCol = gpcTable.TableInfo.Columns[0]; var ownersDict = new Dictionary(); -#if THREAD_SAFE - tablesStream.theLock.EnterWriteLock(); try { -#endif for (uint rid = 1; rid <= gpcTable.Rows; rid++) { - uint owner; - if (!tablesStream.ReadColumn_NoLock(gpcTable, rid, ownerCol, out owner)) + if (!tablesStream.TryReadColumn24(gpcTable, rid, ownerCol, out uint owner)) continue; ownersDict[owner] = true; } -#if THREAD_SAFE - } finally { tablesStream.theLock.ExitWriteLock(); } -#endif var owners = new List(ownersDict.Keys); owners.Sort(); for (int i = 0; i < owners.Count; i++) { uint ownerToken = owners[i]; var ridList = GetGenericParamConstraintRidList(ownerToken); - for (uint j = 0; j < ridList.Length; j++) { + for (int j = 0; j < ridList.Count; j++) { uint ridIndex = ridList[j] - 1; if (newGpcRidToOwnerRid[ridIndex] != 0) continue; @@ -717,9 +612,8 @@ void InitializeInverseGenericParamConstraintOwnerRidList() { Interlocked.CompareExchange(ref gpcRidToOwnerRid, newGpcRidToOwnerRid, null); } - /// - public uint GetOwnerOfParam(uint paramRid) { - if (paramRidToOwnerRid == null) + public override uint GetOwnerOfParam(uint paramRid) { + if (paramRidToOwnerRid is null) InitializeInverseParamOwnerRidList(); uint index = paramRid - 1; if (index >= paramRidToOwnerRid.LongLength) @@ -728,14 +622,14 @@ public uint GetOwnerOfParam(uint paramRid) { } void InitializeInverseParamOwnerRidList() { - if (paramRidToOwnerRid != null) + if (paramRidToOwnerRid is not null) return; var newParamRidToOwnerRid = new uint[tablesStream.ParamTable.Rows]; var table = tablesStream.MethodTable; for (uint rid = 1; rid <= table.Rows; rid++) { var ridList = GetParamRidList(rid); - for (uint j = 0; j < ridList.Length; j++) { + for (int j = 0; j < ridList.Count; j++) { uint ridIndex = ridList[j] - 1; if (newParamRidToOwnerRid[ridIndex] != 0) continue; @@ -745,13 +639,11 @@ void InitializeInverseParamOwnerRidList() { Interlocked.CompareExchange(ref paramRidToOwnerRid, newParamRidToOwnerRid, null); } - /// - public RidList GetNestedClassRidList(uint typeDefRid) { - if (typeDefRidToNestedClasses == null) + public override RidList GetNestedClassRidList(uint typeDefRid) { + if (typeDefRidToNestedClasses is null) InitializeNestedClassesDictionary(); - RandomRidList ridList; - if (typeDefRidToNestedClasses.TryGetValue(typeDefRid, out ridList)) - return ridList; + if (typeDefRidToNestedClasses.TryGetValue(typeDefRid, out var ridList)) + return RidList.Create(ridList); return RidList.Empty; } @@ -761,19 +653,18 @@ void InitializeNestedClassesDictionary() { Dictionary validTypeDefRids = null; var typeDefRidList = GetTypeDefRidList(); - if (typeDefRidList.Length != destTable.Rows) { - validTypeDefRids = new Dictionary((int)typeDefRidList.Length); - for (uint i = 0; i < typeDefRidList.Length; i++) + if ((uint)typeDefRidList.Count != destTable.Rows) { + validTypeDefRids = new Dictionary(typeDefRidList.Count); + for (int i = 0; i < typeDefRidList.Count; i++) validTypeDefRids[typeDefRidList[i]] = true; } var nestedRidsDict = new Dictionary((int)table.Rows); var nestedRids = new List((int)table.Rows); // Need it so we add the rids in correct order for (uint rid = 1; rid <= table.Rows; rid++) { - if (validTypeDefRids != null && !validTypeDefRids.ContainsKey(rid)) + if (validTypeDefRids is not null && !validTypeDefRids.ContainsKey(rid)) continue; - var row = tablesStream.ReadNestedClassRow(rid); - if (row == null) + if (!tablesStream.TryReadNestedClassRow(rid, out var row)) continue; // Should never happen since rid is valid if (!destTable.IsValidRID(row.NestedClass) || !destTable.IsValidRID(row.EnclosingClass)) continue; @@ -783,42 +674,54 @@ void InitializeNestedClassesDictionary() { nestedRids.Add(row.NestedClass); } - var newTypeDefRidToNestedClasses = new Dictionary(); - foreach (var nestedRid in nestedRids) { - var row = tablesStream.ReadNestedClassRow(GetNestedClassRid(nestedRid)); - if (row == null) + var newTypeDefRidToNestedClasses = new Dictionary>(); + int count = nestedRids.Count; + for (int i = 0; i < count; i++) { + var nestedRid = nestedRids[i]; + if (!tablesStream.TryReadNestedClassRow(GetNestedClassRid(nestedRid), out var row)) continue; - RandomRidList ridList; - if (!newTypeDefRidToNestedClasses.TryGetValue(row.EnclosingClass, out ridList)) - newTypeDefRidToNestedClasses[row.EnclosingClass] = ridList = new RandomRidList(); + if (!newTypeDefRidToNestedClasses.TryGetValue(row.EnclosingClass, out var ridList)) + newTypeDefRidToNestedClasses[row.EnclosingClass] = ridList = new List(); ridList.Add(nestedRid); } - var newNonNestedTypes = new RandomRidList((int)(destTable.Rows - nestedRidsDict.Count)); + var newNonNestedTypes = new List((int)(destTable.Rows - nestedRidsDict.Count)); for (uint rid = 1; rid <= destTable.Rows; rid++) { - if (validTypeDefRids != null && !validTypeDefRids.ContainsKey(rid)) + if (validTypeDefRids is not null && !validTypeDefRids.ContainsKey(rid)) continue; if (nestedRidsDict.ContainsKey(rid)) continue; newNonNestedTypes.Add(rid); } - Interlocked.CompareExchange(ref nonNestedTypes, newNonNestedTypes, null); + Interlocked.CompareExchange(ref nonNestedTypes, new StrongBox(RidList.Create(newNonNestedTypes)), null); // Initialize this one last since it's tested by the callers of this method Interlocked.CompareExchange(ref typeDefRidToNestedClasses, newTypeDefRidToNestedClasses, null); } - public RidList GetNonNestedClassRidList() { + public override RidList GetNonNestedClassRidList() { // Check typeDefRidToNestedClasses and not nonNestedTypes since // InitializeNestedClassesDictionary() writes to typeDefRidToNestedClasses last. - if (typeDefRidToNestedClasses == null) + if (typeDefRidToNestedClasses is null) InitializeNestedClassesDictionary(); - return nonNestedTypes; + return nonNestedTypes.Value; + } + + public override RidList GetLocalScopeRidList(uint methodRid) => FindAllRows(tablesStream.LocalScopeTable, 0, methodRid); + + public override uint GetStateMachineMethodRid(uint methodRid) { + var list = FindAllRows(tablesStream.StateMachineMethodTable, 0, methodRid); + return list.Count == 0 ? 0 : list[0]; } - /// - public void Dispose() { + public override RidList GetCustomDebugInformationRidList(Table table, uint rid) { + if (!CodedToken.HasCustomDebugInformation.Encode(new MDToken(table, rid), out uint codedToken)) + return RidList.Empty; + return FindAllRows(tablesStream.CustomDebugInformationTable, 0, codedToken); + } + + public override void Dispose() { Dispose(true); GC.SuppressFinalize(this); } @@ -830,17 +733,18 @@ public void Dispose() { protected virtual void Dispose(bool disposing) { if (!disposing) return; - Dispose(peImage); - Dispose(stringsStream); - Dispose(usStream); - Dispose(blobStream); - Dispose(guidStream); - Dispose(tablesStream); + peImage?.Dispose(); + stringsStream?.Dispose(); + usStream?.Dispose(); + blobStream?.Dispose(); + guidStream?.Dispose(); + tablesStream?.Dispose(); var as2 = allStreams; - if (as2 != null) { - foreach (var stream in as2.GetSafeEnumerable()) - Dispose(stream); + if (as2 is not null) { + foreach (var stream in as2) + stream?.Dispose(); } + mdReaderFactoryToDisposeLater?.Dispose(); peImage = null; cor20Header = null; mdHeader = null; @@ -853,11 +757,7 @@ protected virtual void Dispose(bool disposing) { fieldRidToTypeDefRid = null; methodRidToTypeDefRid = null; typeDefRidToNestedClasses = null; - } - - static void Dispose(IDisposable id) { - if (id != null) - id.Dispose(); + mdReaderFactoryToDisposeLater = null; } } } diff --git a/src/DotNet/MD/MetadataFactory.cs b/src/DotNet/MD/MetadataFactory.cs new file mode 100644 index 000000000..1904ed960 --- /dev/null +++ b/src/DotNet/MD/MetadataFactory.cs @@ -0,0 +1,222 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Collections.Generic; +using dnlib.IO; +using dnlib.PE; + +namespace dnlib.DotNet.MD { + /// + /// Low level access to a .NET file's metadata + /// + public static class MetadataFactory { + enum MetadataType { + Unknown, + Compressed, // #~ (normal) + ENC, // #- (edit and continue) + } + + internal static MetadataBase Load(string fileName, CLRRuntimeReaderKind runtime) { + IPEImage peImage = null; + try { + return Load(peImage = new PEImage(fileName), runtime); + } + catch { + if (peImage is not null) + peImage.Dispose(); + throw; + } + } + + internal static MetadataBase Load(byte[] data, CLRRuntimeReaderKind runtime) { + IPEImage peImage = null; + try { + return Load(peImage = new PEImage(data), runtime); + } + catch { + if (peImage is not null) + peImage.Dispose(); + throw; + } + } + + internal static MetadataBase Load(IntPtr addr, CLRRuntimeReaderKind runtime) { + IPEImage peImage = null; + + // We don't know what layout it is. Memory is more common so try that first. + try { + return Load(peImage = new PEImage(addr, ImageLayout.Memory, true), runtime); + } + catch { + if (peImage is not null) + peImage.Dispose(); + peImage = null; + } + + try { + return Load(peImage = new PEImage(addr, ImageLayout.File, true), runtime); + } + catch { + if (peImage is not null) + peImage.Dispose(); + throw; + } + } + + internal static MetadataBase Load(IntPtr addr, ImageLayout imageLayout, CLRRuntimeReaderKind runtime) { + IPEImage peImage = null; + try { + return Load(peImage = new PEImage(addr, imageLayout, true), runtime); + } + catch { + if (peImage is not null) + peImage.Dispose(); + throw; + } + } + + internal static MetadataBase Load(IPEImage peImage, CLRRuntimeReaderKind runtime) => Create(peImage, runtime, true); + + /// + /// Create a instance + /// + /// The PE image + /// A new instance + public static Metadata CreateMetadata(IPEImage peImage) => CreateMetadata(peImage, CLRRuntimeReaderKind.CLR); + + /// + /// Create a instance + /// + /// The PE image + /// Runtime reader kind + /// A new instance + public static Metadata CreateMetadata(IPEImage peImage, CLRRuntimeReaderKind runtime) => Create(peImage, runtime, true); + + /// + /// Create a instance + /// + /// The PE image + /// true if we should verify that it's a .NET PE file + /// A new instance + public static Metadata CreateMetadata(IPEImage peImage, bool verify) => CreateMetadata(peImage, CLRRuntimeReaderKind.CLR, verify); + + /// + /// Create a instance + /// + /// The PE image + /// Runtime reader kind + /// true if we should verify that it's a .NET PE file + /// A new instance + public static Metadata CreateMetadata(IPEImage peImage, CLRRuntimeReaderKind runtime, bool verify) => Create(peImage, runtime, verify); + + /// + /// Create a instance + /// + /// The PE image + /// Runtime reader kind + /// true if we should verify that it's a .NET PE file + /// A new instance + static MetadataBase Create(IPEImage peImage, CLRRuntimeReaderKind runtime, bool verify) { + MetadataBase md = null; + try { + var dotNetDir = peImage.ImageNTHeaders.OptionalHeader.DataDirectories[14]; + // Mono doesn't check that the Size field is >= 0x48 + if (dotNetDir.VirtualAddress == 0) + throw new BadImageFormatException(".NET data directory RVA is 0"); + var cor20HeaderReader = peImage.CreateReader(dotNetDir.VirtualAddress, 0x48); + var cor20Header = new ImageCor20Header(ref cor20HeaderReader, verify && runtime == CLRRuntimeReaderKind.CLR); + if (cor20Header.Metadata.VirtualAddress == 0) + throw new BadImageFormatException(".NET metadata RVA is 0"); + var mdRva = cor20Header.Metadata.VirtualAddress; + // Don't use the size field, Mono ignores it. Create a reader that can read to EOF. + var mdHeaderReader = peImage.CreateReader(mdRva); + var mdHeader = new MetadataHeader(ref mdHeaderReader, runtime, verify); + if (verify) { + foreach (var sh in mdHeader.StreamHeaders) { + if ((ulong)sh.Offset + sh.StreamSize > mdHeaderReader.EndOffset) + throw new BadImageFormatException("Invalid stream header"); + } + } + + md = GetMetadataType(mdHeader.StreamHeaders, runtime) switch { + MetadataType.Compressed => new CompressedMetadata(peImage, cor20Header, mdHeader, runtime), + MetadataType.ENC => new ENCMetadata(peImage, cor20Header, mdHeader, runtime), + _ => throw new BadImageFormatException("No #~ or #- stream found"), + }; + md.Initialize(null); + + return md; + } + catch { + if (md is not null) + md.Dispose(); + throw; + } + } + + /// + /// Create a standalone portable PDB instance + /// + /// Metadata stream + /// true if we should verify that it's a .NET PE file + /// A new instance + internal static MetadataBase CreateStandalonePortablePDB(DataReaderFactory mdReaderFactory, bool verify) { + const CLRRuntimeReaderKind runtime = CLRRuntimeReaderKind.CLR; + MetadataBase md = null; + try { + var reader = mdReaderFactory.CreateReader(); + var mdHeader = new MetadataHeader(ref reader, runtime, verify); + if (verify) { + foreach (var sh in mdHeader.StreamHeaders) { + if (sh.Offset + sh.StreamSize < sh.Offset || sh.Offset + sh.StreamSize > reader.Length) + throw new BadImageFormatException("Invalid stream header"); + } + } + + md = GetMetadataType(mdHeader.StreamHeaders, runtime) switch { + MetadataType.Compressed => new CompressedMetadata(mdHeader, true, runtime), + MetadataType.ENC => new ENCMetadata(mdHeader, true, runtime), + _ => throw new BadImageFormatException("No #~ or #- stream found"), + }; + md.Initialize(mdReaderFactory); + + return md; + } + catch { + md?.Dispose(); + throw; + } + } + + static MetadataType GetMetadataType(IList streamHeaders, CLRRuntimeReaderKind runtime) { + MetadataType? mdType = null; + if (runtime == CLRRuntimeReaderKind.CLR) { + foreach (var sh in streamHeaders) { + if (mdType is null) { + if (sh.Name == "#~") + mdType = MetadataType.Compressed; + else if (sh.Name == "#-") + mdType = MetadataType.ENC; + } + if (sh.Name == "#Schema") + mdType = MetadataType.ENC; + } + } + else if (runtime == CLRRuntimeReaderKind.Mono) { + foreach (var sh in streamHeaders) { + if (sh.Name == "#~") + mdType = MetadataType.Compressed; + else if (sh.Name == "#-") { + mdType = MetadataType.ENC; + break; + } + } + } + else + throw new ArgumentOutOfRangeException(nameof(runtime)); + if (mdType is null) + return MetadataType.Unknown; + return mdType.Value; + } + } +} diff --git a/src/DotNet/MD/MetadataHeader.cs b/src/DotNet/MD/MetadataHeader.cs new file mode 100644 index 000000000..69604afd1 --- /dev/null +++ b/src/DotNet/MD/MetadataHeader.cs @@ -0,0 +1,141 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Collections.Generic; +using System.Text; +using dnlib.IO; + +namespace dnlib.DotNet.MD { + /// + /// Represents the .NET metadata header + /// + /// IMAGE_COR20_HEADER.Metadata points to this header + public sealed class MetadataHeader : FileSection { + readonly uint signature; + readonly ushort majorVersion; + readonly ushort minorVersion; + readonly uint reserved1; + readonly uint stringLength; + readonly string versionString; + readonly FileOffset offset2ndPart; + readonly StorageFlags flags; + readonly byte reserved2; + readonly ushort streams; + readonly IList streamHeaders; + + /// + /// Returns the signature (should be 0x424A5342) + /// + public uint Signature => signature; + + /// + /// Returns the major version + /// + public ushort MajorVersion => majorVersion; + + /// + /// Returns the minor version + /// + public ushort MinorVersion => minorVersion; + + /// + /// Returns the reserved dword (pointer to extra header data) + /// + public uint Reserved1 => reserved1; + + /// + /// Returns the version string length value + /// + public uint StringLength => stringLength; + + /// + /// Returns the version string + /// + public string VersionString => versionString; + + /// + /// Returns the offset of STORAGEHEADER + /// + public FileOffset StorageHeaderOffset => offset2ndPart; + + /// + /// Returns the flags (reserved) + /// + public StorageFlags Flags => flags; + + /// + /// Returns the reserved byte (padding) + /// + public byte Reserved2 => reserved2; + + /// + /// Returns the number of streams + /// + public ushort Streams => streams; + + /// + /// Returns all stream headers + /// + public IList StreamHeaders => streamHeaders; + + /// + /// Constructor + /// + /// PE file reader pointing to the start of this section + /// Verify section + /// Thrown if verification fails + public MetadataHeader(ref DataReader reader, bool verify) + : this(ref reader, CLRRuntimeReaderKind.CLR, verify) { + } + + /// + /// Constructor + /// + /// PE file reader pointing to the start of this section + /// Runtime reader kind + /// Verify section + /// Thrown if verification fails + public MetadataHeader(ref DataReader reader, CLRRuntimeReaderKind runtime, bool verify) { + SetStartOffset(ref reader); + signature = reader.ReadUInt32(); + if (verify && signature != 0x424A5342) + throw new BadImageFormatException("Invalid metadata header signature"); + majorVersion = reader.ReadUInt16(); + minorVersion = reader.ReadUInt16(); + reserved1 = reader.ReadUInt32(); + stringLength = reader.ReadUInt32(); + versionString = ReadString(ref reader, stringLength, runtime); + offset2ndPart = (FileOffset)reader.CurrentOffset; + flags = (StorageFlags)reader.ReadByte(); + reserved2 = reader.ReadByte(); + streams = reader.ReadUInt16(); + streamHeaders = new StreamHeader[streams]; + for (int i = 0; i < streamHeaders.Count; i++) { + // Mono doesn't verify all of these so we can't either + var sh = new StreamHeader(ref reader, throwOnError: false, verify, runtime, out bool failedVerification); + if (failedVerification || (ulong)sh.Offset + sh.StreamSize > reader.EndOffset) + sh = new StreamHeader(0, 0, ""); + streamHeaders[i] = sh; + } + SetEndoffset(ref reader); + } + + static string ReadString(ref DataReader reader, uint maxLength, CLRRuntimeReaderKind runtime) { + ulong endOffset = (ulong)reader.CurrentOffset + maxLength; + if (runtime == CLRRuntimeReaderKind.Mono) + endOffset = (endOffset + 3) / 4 * 4; + if (endOffset > reader.EndOffset) + throw new BadImageFormatException("Invalid MD version string"); + var utf8Bytes = new byte[maxLength]; + uint i; + for (i = 0; i < maxLength; i++) { + byte b = reader.ReadByte(); + if (b == 0) + break; + utf8Bytes[i] = b; + } + reader.CurrentOffset = (uint)endOffset; + return Encoding.UTF8.GetString(utf8Bytes, 0, (int)i); + } + } +} diff --git a/src/DotNet/MD/PdbStream.cs b/src/DotNet/MD/PdbStream.cs new file mode 100644 index 000000000..d5734ae94 --- /dev/null +++ b/src/DotNet/MD/PdbStream.cs @@ -0,0 +1,46 @@ +// dnlib: See LICENSE.txt for more info + +using dnlib.IO; + +namespace dnlib.DotNet.MD { + /// + /// #Pdb stream + /// + public sealed class PdbStream : HeapStream { + /// + /// Gets the PDB id + /// + public byte[] Id { get; private set; } + + /// + /// Gets the entry point token or 0 + /// + public MDToken EntryPoint { get; private set; } + + /// + /// Gets the referenced type system tables in the PE metadata file + /// + public ulong ReferencedTypeSystemTables { get; private set; } + + /// + /// Gets all type system table rows. This array has exactly 64 elements. + /// + public uint[] TypeSystemTableRows { get; private set; } + + /// + public PdbStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader) + : base(mdReaderFactory, metadataBaseOffset, streamHeader) { + var reader = CreateReader(); + Id = reader.ReadBytes(20); + EntryPoint = new MDToken(reader.ReadUInt32()); + var tables = reader.ReadUInt64(); + ReferencedTypeSystemTables = tables; + var rows = new uint[64]; + for (int i = 0; i < rows.Length; i++, tables >>= 1) { + if (((uint)tables & 1) != 0) + rows[i] = reader.ReadUInt32(); + } + TypeSystemTableRows = rows; + } + } +} diff --git a/src/DotNet/MD/RawRowEqualityComparer.cs b/src/DotNet/MD/RawRowEqualityComparer.cs index 0bcaa0101..cf824534c 100644 --- a/src/DotNet/MD/RawRowEqualityComparer.cs +++ b/src/DotNet/MD/RawRowEqualityComparer.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; +using System.Collections.Generic; #pragma warning disable 1591 // XML doc comments @@ -30,549 +30,521 @@ public sealed class RawRowEqualityComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer, IEqualityComparer { + IEqualityComparer, IEqualityComparer, + IEqualityComparer, IEqualityComparer, + IEqualityComparer, IEqualityComparer, + IEqualityComparer, IEqualityComparer, + IEqualityComparer, IEqualityComparer { /// /// Default instance /// public static readonly RawRowEqualityComparer Instance = new RawRowEqualityComparer(); - static int rol(uint val, int shift) { - return (int)((val << shift) | (val >> (32 - shift))); - } - - public bool Equals(RawModuleRow x, RawModuleRow y) { - return x.Generation == y.Generation && - x.Name == y.Name && - x.Mvid == y.Mvid && - x.EncId == y.EncId && - x.EncBaseId == y.EncBaseId; - } - - public int GetHashCode(RawModuleRow obj) { - return obj.Generation + - rol(obj.Name, 3) + - rol(obj.Mvid, 7) + - rol(obj.EncId, 11) + - rol(obj.EncBaseId, 15); - } - - public bool Equals(RawTypeRefRow x, RawTypeRefRow y) { - return x.ResolutionScope == y.ResolutionScope && - x.Name == y.Name && - x.Namespace == y.Namespace; - } - - public int GetHashCode(RawTypeRefRow obj) { - return (int)obj.ResolutionScope + - rol(obj.Name, 3) + - rol(obj.Namespace, 7); - } - - public bool Equals(RawTypeDefRow x, RawTypeDefRow y) { - return x.Flags == y.Flags && - x.Name == y.Name && - x.Namespace == y.Namespace && - x.Extends == y.Extends && - x.FieldList == y.FieldList && - x.MethodList == y.MethodList; - } - - public int GetHashCode(RawTypeDefRow obj) { - return (int)obj.Flags + - rol(obj.Name, 3) + - rol(obj.Namespace, 7) + - rol(obj.Extends, 11) + - rol(obj.FieldList, 15) + - rol(obj.MethodList, 19); - } - - public bool Equals(RawFieldPtrRow x, RawFieldPtrRow y) { - return x.Field == y.Field; - } - - public int GetHashCode(RawFieldPtrRow obj) { - return (int)obj.Field; - } - - public bool Equals(RawFieldRow x, RawFieldRow y) { - return x.Flags == y.Flags && - x.Name == y.Name && - x.Signature == y.Signature; - } - - public int GetHashCode(RawFieldRow obj) { - return (int)obj.Flags + - rol(obj.Name, 3) + - rol(obj.Signature, 7); - } - - public bool Equals(RawMethodPtrRow x, RawMethodPtrRow y) { - return x.Method == y.Method; - } - - public int GetHashCode(RawMethodPtrRow obj) { - return (int)obj.Method; - } - - public bool Equals(RawMethodRow x, RawMethodRow y) { - return x.RVA == y.RVA && - x.ImplFlags == y.ImplFlags && - x.Flags == y.Flags && - x.Name == y.Name && - x.Signature == y.Signature && - x.ParamList == y.ParamList; - } - - public int GetHashCode(RawMethodRow obj) { - return (int)obj.RVA + - rol(obj.ImplFlags, 3) + - rol(obj.Flags, 7) + - rol(obj.Name, 11) + - rol(obj.Signature, 15) + - rol(obj.ParamList, 19); - } - - public bool Equals(RawParamPtrRow x, RawParamPtrRow y) { - return x.Param == y.Param; - } - - public int GetHashCode(RawParamPtrRow obj) { - return (int)obj.Param; - } - - public bool Equals(RawParamRow x, RawParamRow y) { - return x.Flags == y.Flags && - x.Sequence == y.Sequence && - x.Name == y.Name; - } - - public int GetHashCode(RawParamRow obj) { - return (int)obj.Flags + - rol(obj.Sequence, 3) + - rol(obj.Name, 7); - } - - public bool Equals(RawInterfaceImplRow x, RawInterfaceImplRow y) { - return x.Class == y.Class && - x.Interface == y.Interface; - } - - public int GetHashCode(RawInterfaceImplRow obj) { - return (int)obj.Class + - rol(obj.Interface, 3); - } - - public bool Equals(RawMemberRefRow x, RawMemberRefRow y) { - return x.Class == y.Class && - x.Name == y.Name && - x.Signature == y.Signature; - } - - public int GetHashCode(RawMemberRefRow obj) { - return (int)obj.Class + - rol(obj.Name, 3) + - rol(obj.Signature, 7); - } - - public bool Equals(RawConstantRow x, RawConstantRow y) { - return x.Type == y.Type && - x.Padding == y.Padding && - x.Parent == y.Parent && - x.Value == y.Value; - } - - public int GetHashCode(RawConstantRow obj) { - return (int)obj.Type + - rol(obj.Padding, 3) + - rol(obj.Parent, 7) + - rol(obj.Value, 11); - } - - public bool Equals(RawCustomAttributeRow x, RawCustomAttributeRow y) { - return x.Parent == y.Parent && - x.Type == y.Type && - x.Value == y.Value; - } - - public int GetHashCode(RawCustomAttributeRow obj) { - return (int)obj.Parent + - rol(obj.Type, 3) + - rol(obj.Value, 7); - } - - public bool Equals(RawFieldMarshalRow x, RawFieldMarshalRow y) { - return x.Parent == y.Parent && - x.NativeType == y.NativeType; - } - - public int GetHashCode(RawFieldMarshalRow obj) { - return (int)obj.Parent + - rol(obj.NativeType, 3); - } - - public bool Equals(RawDeclSecurityRow x, RawDeclSecurityRow y) { - return x.Action == y.Action && - x.Parent == y.Parent && - x.PermissionSet == y.PermissionSet; - } - - public int GetHashCode(RawDeclSecurityRow obj) { - return (int)obj.Action + - rol(obj.Parent, 3) + - rol(obj.PermissionSet, 7); - } - - public bool Equals(RawClassLayoutRow x, RawClassLayoutRow y) { - return x.PackingSize == y.PackingSize && - x.ClassSize == y.ClassSize && - x.Parent == y.Parent; - } - - public int GetHashCode(RawClassLayoutRow obj) { - return (int)obj.PackingSize + - rol(obj.ClassSize, 3) + - rol(obj.Parent, 7); - } - - public bool Equals(RawFieldLayoutRow x, RawFieldLayoutRow y) { - return x.OffSet == y.OffSet && - x.Field == y.Field; - } - - public int GetHashCode(RawFieldLayoutRow obj) { - return (int)obj.OffSet + - rol(obj.Field, 3); - } - - public bool Equals(RawStandAloneSigRow x, RawStandAloneSigRow y) { - return x.Signature == y.Signature; - } - - public int GetHashCode(RawStandAloneSigRow obj) { - return (int)obj.Signature; - } - - public bool Equals(RawEventMapRow x, RawEventMapRow y) { - return x.Parent == y.Parent && - x.EventList == y.EventList; - } - - public int GetHashCode(RawEventMapRow obj) { - return (int)obj.Parent + - rol(obj.EventList, 3); - } - - public bool Equals(RawEventPtrRow x, RawEventPtrRow y) { - return x.Event == y.Event; - } - - public int GetHashCode(RawEventPtrRow obj) { - return (int)obj.Event; - } - - public bool Equals(RawEventRow x, RawEventRow y) { - return x.EventFlags == y.EventFlags && - x.Name == y.Name && - x.EventType == y.EventType; - } - - public int GetHashCode(RawEventRow obj) { - return (int)obj.EventFlags + - rol(obj.Name, 3) + - rol(obj.EventType, 7); - } - - public bool Equals(RawPropertyMapRow x, RawPropertyMapRow y) { - return x.Parent == y.Parent && - x.PropertyList == y.PropertyList; - } - - public int GetHashCode(RawPropertyMapRow obj) { - return (int)obj.Parent + - rol(obj.PropertyList, 3); - } - - public bool Equals(RawPropertyPtrRow x, RawPropertyPtrRow y) { - return x.Property == y.Property; - } - - public int GetHashCode(RawPropertyPtrRow obj) { - return (int)obj.Property; - } - - public bool Equals(RawPropertyRow x, RawPropertyRow y) { - return x.PropFlags == y.PropFlags && - x.Name == y.Name && - x.Type == y.Type; - } - - public int GetHashCode(RawPropertyRow obj) { - return (int)obj.PropFlags + - rol(obj.Name, 3) + - rol(obj.Type, 7); - } - - public bool Equals(RawMethodSemanticsRow x, RawMethodSemanticsRow y) { - return x.Semantic == y.Semantic && - x.Method == y.Method && - x.Association == y.Association; - } - - public int GetHashCode(RawMethodSemanticsRow obj) { - return (int)obj.Semantic + - rol(obj.Method, 3) + - rol(obj.Association, 7); - } - - public bool Equals(RawMethodImplRow x, RawMethodImplRow y) { - return x.Class == y.Class && - x.MethodBody == y.MethodBody && - x.MethodDeclaration == y.MethodDeclaration; - } - - public int GetHashCode(RawMethodImplRow obj) { - return (int)obj.Class + - rol(obj.MethodBody, 3) + - rol(obj.MethodDeclaration, 7); - } - - public bool Equals(RawModuleRefRow x, RawModuleRefRow y) { - return x.Name == y.Name; - } - - public int GetHashCode(RawModuleRefRow obj) { - return (int)obj.Name; - } - - public bool Equals(RawTypeSpecRow x, RawTypeSpecRow y) { - return x.Signature == y.Signature; - } - - public int GetHashCode(RawTypeSpecRow obj) { - return (int)obj.Signature; - } - - public bool Equals(RawImplMapRow x, RawImplMapRow y) { - return x.MappingFlags == y.MappingFlags && - x.MemberForwarded == y.MemberForwarded && - x.ImportName == y.ImportName && - x.ImportScope == y.ImportScope; - } - - public int GetHashCode(RawImplMapRow obj) { - return (int)obj.MappingFlags + - rol(obj.MemberForwarded, 3) + - rol(obj.ImportName, 7) + - rol(obj.ImportScope, 11); - } - - public bool Equals(RawFieldRVARow x, RawFieldRVARow y) { - return x.RVA == y.RVA && - x.Field == y.Field; - } - - public int GetHashCode(RawFieldRVARow obj) { - return (int)obj.RVA + - rol(obj.Field, 3); - } - - public bool Equals(RawENCLogRow x, RawENCLogRow y) { - return x.Token == y.Token && - x.FuncCode == y.FuncCode; - } - - public int GetHashCode(RawENCLogRow obj) { - return (int)obj.Token + - rol(obj.FuncCode, 3); - } - - public bool Equals(RawENCMapRow x, RawENCMapRow y) { - return x.Token == y.Token; - } - - public int GetHashCode(RawENCMapRow obj) { - return (int)obj.Token; - } - - public bool Equals(RawAssemblyRow x, RawAssemblyRow y) { - return x.HashAlgId == y.HashAlgId && - x.MajorVersion == y.MajorVersion && - x.MinorVersion == y.MinorVersion && - x.BuildNumber == y.BuildNumber && - x.RevisionNumber == y.RevisionNumber && - x.Flags == y.Flags && - x.PublicKey == y.PublicKey && - x.Name == y.Name && - x.Locale == y.Locale; - } - - public int GetHashCode(RawAssemblyRow obj) { - return (int)obj.HashAlgId + - rol(obj.MajorVersion, 3) + - rol(obj.MinorVersion, 7) + - rol(obj.BuildNumber, 11) + - rol(obj.RevisionNumber, 15) + - rol(obj.Flags, 19) + - rol(obj.PublicKey, 23) + - rol(obj.Name, 27) + - rol(obj.Locale, 31); - } - - public bool Equals(RawAssemblyProcessorRow x, RawAssemblyProcessorRow y) { - return x.Processor == y.Processor; - } - - public int GetHashCode(RawAssemblyProcessorRow obj) { - return (int)obj.Processor; - } - - public bool Equals(RawAssemblyOSRow x, RawAssemblyOSRow y) { - return x.OSPlatformId == y.OSPlatformId && - x.OSMajorVersion == y.OSMajorVersion && - x.OSMinorVersion == y.OSMinorVersion; - } - - public int GetHashCode(RawAssemblyOSRow obj) { - return (int)obj.OSPlatformId + - rol(obj.OSMajorVersion, 3) + - rol(obj.OSMinorVersion, 7); - } - - public bool Equals(RawAssemblyRefRow x, RawAssemblyRefRow y) { - return x.MajorVersion == y.MajorVersion && - x.MinorVersion == y.MinorVersion && - x.BuildNumber == y.BuildNumber && - x.RevisionNumber == y.RevisionNumber && - x.Flags == y.Flags && - x.PublicKeyOrToken == y.PublicKeyOrToken && - x.Name == y.Name && - x.Locale == y.Locale && - x.HashValue == y.HashValue; - } - - public int GetHashCode(RawAssemblyRefRow obj) { - return (int)obj.MajorVersion + - rol(obj.MinorVersion, 3) + - rol(obj.BuildNumber, 7) + - rol(obj.RevisionNumber, 11) + - rol(obj.Flags, 15) + - rol(obj.PublicKeyOrToken, 19) + - rol(obj.Name, 23) + - rol(obj.Locale, 27) + - rol(obj.HashValue, 31); - } - - public bool Equals(RawAssemblyRefProcessorRow x, RawAssemblyRefProcessorRow y) { - return x.Processor == y.Processor && - x.AssemblyRef == y.AssemblyRef; - } - - public int GetHashCode(RawAssemblyRefProcessorRow obj) { - return (int)obj.Processor + - rol(obj.AssemblyRef, 3); - } - - public bool Equals(RawAssemblyRefOSRow x, RawAssemblyRefOSRow y) { - return x.OSPlatformId == y.OSPlatformId && - x.OSMajorVersion == y.OSMajorVersion && - x.OSMinorVersion == y.OSMinorVersion && - x.AssemblyRef == y.AssemblyRef; - } - - public int GetHashCode(RawAssemblyRefOSRow obj) { - return (int)obj.OSPlatformId + - rol(obj.OSMajorVersion, 3) + - rol(obj.OSMinorVersion, 7) + - rol(obj.AssemblyRef, 11); - } - - public bool Equals(RawFileRow x, RawFileRow y) { - return x.Flags == y.Flags && - x.Name == y.Name && - x.HashValue == y.HashValue; - } - - public int GetHashCode(RawFileRow obj) { - return (int)obj.Flags + - rol(obj.Name, 3) + - rol(obj.HashValue, 7); - } - - public bool Equals(RawExportedTypeRow x, RawExportedTypeRow y) { - return x.Flags == y.Flags && - x.TypeDefId == y.TypeDefId && - x.TypeName == y.TypeName && - x.TypeNamespace == y.TypeNamespace && - x.Implementation == y.Implementation; - } - - public int GetHashCode(RawExportedTypeRow obj) { - return (int)obj.Flags + - rol(obj.TypeDefId, 3) + - rol(obj.TypeName, 7) + - rol(obj.TypeNamespace, 11) + - rol(obj.Implementation, 15); - } - - public bool Equals(RawManifestResourceRow x, RawManifestResourceRow y) { - return x.Offset == y.Offset && - x.Flags == y.Flags && - x.Name == y.Name && - x.Implementation == y.Implementation; - } - - public int GetHashCode(RawManifestResourceRow obj) { - return (int)obj.Offset + - rol(obj.Flags, 3) + - rol(obj.Name, 7) + - rol(obj.Implementation, 11); - } - - public bool Equals(RawNestedClassRow x, RawNestedClassRow y) { - return x.NestedClass == y.NestedClass && - x.EnclosingClass == y.EnclosingClass; - } - - public int GetHashCode(RawNestedClassRow obj) { - return (int)obj.NestedClass + - rol(obj.EnclosingClass, 3); - } - - public bool Equals(RawGenericParamRow x, RawGenericParamRow y) { - return x.Number == y.Number && - x.Flags == y.Flags && - x.Owner == y.Owner && - x.Name == y.Name && - x.Kind == y.Kind; - } - - public int GetHashCode(RawGenericParamRow obj) { - return (int)obj.Number + - rol(obj.Flags, 3) + - rol(obj.Owner, 7) + - rol(obj.Name, 11) + - rol(obj.Kind, 15); - } - - public bool Equals(RawMethodSpecRow x, RawMethodSpecRow y) { - return x.Method == y.Method && - x.Instantiation == y.Instantiation; - } - - public int GetHashCode(RawMethodSpecRow obj) { - return (int)obj.Method + - rol(obj.Instantiation, 3); - } - - public bool Equals(RawGenericParamConstraintRow x, RawGenericParamConstraintRow y) { - return x.Owner == y.Owner && - x.Constraint == y.Constraint; - } - - public int GetHashCode(RawGenericParamConstraintRow obj) { - return (int)obj.Owner + - rol(obj.Constraint, 3); - } + static int rol(uint val, int shift) => (int)((val << shift) | (val >> (32 - shift))); + + public bool Equals(RawModuleRow x, RawModuleRow y) => + x.Generation == y.Generation && + x.Name == y.Name && + x.Mvid == y.Mvid && + x.EncId == y.EncId && + x.EncBaseId == y.EncBaseId; + + public int GetHashCode(RawModuleRow obj) => + obj.Generation + + rol(obj.Name, 3) + + rol(obj.Mvid, 7) + + rol(obj.EncId, 11) + + rol(obj.EncBaseId, 15); + + public bool Equals(RawTypeRefRow x, RawTypeRefRow y) => + x.ResolutionScope == y.ResolutionScope && + x.Name == y.Name && + x.Namespace == y.Namespace; + + public int GetHashCode(RawTypeRefRow obj) => + (int)obj.ResolutionScope + + rol(obj.Name, 3) + + rol(obj.Namespace, 7); + + public bool Equals(RawTypeDefRow x, RawTypeDefRow y) => + x.Flags == y.Flags && + x.Name == y.Name && + x.Namespace == y.Namespace && + x.Extends == y.Extends && + x.FieldList == y.FieldList && + x.MethodList == y.MethodList; + + public int GetHashCode(RawTypeDefRow obj) => + (int)obj.Flags + + rol(obj.Name, 3) + + rol(obj.Namespace, 7) + + rol(obj.Extends, 11) + + rol(obj.FieldList, 15) + + rol(obj.MethodList, 19); + + public bool Equals(RawFieldPtrRow x, RawFieldPtrRow y) => x.Field == y.Field; + + public int GetHashCode(RawFieldPtrRow obj) => (int)obj.Field; + + public bool Equals(RawFieldRow x, RawFieldRow y) => + x.Flags == y.Flags && + x.Name == y.Name && + x.Signature == y.Signature; + + public int GetHashCode(RawFieldRow obj) => + (int)obj.Flags + + rol(obj.Name, 3) + + rol(obj.Signature, 7); + + public bool Equals(RawMethodPtrRow x, RawMethodPtrRow y) => x.Method == y.Method; + + public int GetHashCode(RawMethodPtrRow obj) => (int)obj.Method; + + public bool Equals(RawMethodRow x, RawMethodRow y) => + x.RVA == y.RVA && + x.ImplFlags == y.ImplFlags && + x.Flags == y.Flags && + x.Name == y.Name && + x.Signature == y.Signature && + x.ParamList == y.ParamList; + + public int GetHashCode(RawMethodRow obj) => + (int)obj.RVA + + rol(obj.ImplFlags, 3) + + rol(obj.Flags, 7) + + rol(obj.Name, 11) + + rol(obj.Signature, 15) + + rol(obj.ParamList, 19); + + public bool Equals(RawParamPtrRow x, RawParamPtrRow y) => x.Param == y.Param; + + public int GetHashCode(RawParamPtrRow obj) => (int)obj.Param; + + public bool Equals(RawParamRow x, RawParamRow y) => + x.Flags == y.Flags && + x.Sequence == y.Sequence && + x.Name == y.Name; + + public int GetHashCode(RawParamRow obj) => + (int)obj.Flags + + rol(obj.Sequence, 3) + + rol(obj.Name, 7); + + public bool Equals(RawInterfaceImplRow x, RawInterfaceImplRow y) => + x.Class == y.Class && + x.Interface == y.Interface; + + public int GetHashCode(RawInterfaceImplRow obj) => + (int)obj.Class + + rol(obj.Interface, 3); + + public bool Equals(RawMemberRefRow x, RawMemberRefRow y) => + x.Class == y.Class && + x.Name == y.Name && + x.Signature == y.Signature; + + public int GetHashCode(RawMemberRefRow obj) => + (int)obj.Class + + rol(obj.Name, 3) + + rol(obj.Signature, 7); + + public bool Equals(RawConstantRow x, RawConstantRow y) => + x.Type == y.Type && + x.Padding == y.Padding && + x.Parent == y.Parent && + x.Value == y.Value; + + public int GetHashCode(RawConstantRow obj) => + (int)obj.Type + + rol(obj.Padding, 3) + + rol(obj.Parent, 7) + + rol(obj.Value, 11); + + public bool Equals(RawCustomAttributeRow x, RawCustomAttributeRow y) => + x.Parent == y.Parent && + x.Type == y.Type && + x.Value == y.Value; + + public int GetHashCode(RawCustomAttributeRow obj) => + (int)obj.Parent + + rol(obj.Type, 3) + + rol(obj.Value, 7); + + public bool Equals(RawFieldMarshalRow x, RawFieldMarshalRow y) => + x.Parent == y.Parent && + x.NativeType == y.NativeType; + + public int GetHashCode(RawFieldMarshalRow obj) => + (int)obj.Parent + + rol(obj.NativeType, 3); + + public bool Equals(RawDeclSecurityRow x, RawDeclSecurityRow y) => + x.Action == y.Action && + x.Parent == y.Parent && + x.PermissionSet == y.PermissionSet; + + public int GetHashCode(RawDeclSecurityRow obj) => + (int)obj.Action + + rol(obj.Parent, 3) + + rol(obj.PermissionSet, 7); + + public bool Equals(RawClassLayoutRow x, RawClassLayoutRow y) => + x.PackingSize == y.PackingSize && + x.ClassSize == y.ClassSize && + x.Parent == y.Parent; + + public int GetHashCode(RawClassLayoutRow obj) => + (int)obj.PackingSize + + rol(obj.ClassSize, 3) + + rol(obj.Parent, 7); + + public bool Equals(RawFieldLayoutRow x, RawFieldLayoutRow y) => + x.OffSet == y.OffSet && + x.Field == y.Field; + + public int GetHashCode(RawFieldLayoutRow obj) => + (int)obj.OffSet + + rol(obj.Field, 3); + + public bool Equals(RawStandAloneSigRow x, RawStandAloneSigRow y) => x.Signature == y.Signature; + + public int GetHashCode(RawStandAloneSigRow obj) => (int)obj.Signature; + + public bool Equals(RawEventMapRow x, RawEventMapRow y) => + x.Parent == y.Parent && + x.EventList == y.EventList; + + public int GetHashCode(RawEventMapRow obj) => + (int)obj.Parent + + rol(obj.EventList, 3); + + public bool Equals(RawEventPtrRow x, RawEventPtrRow y) => x.Event == y.Event; + + public int GetHashCode(RawEventPtrRow obj) => (int)obj.Event; + + public bool Equals(RawEventRow x, RawEventRow y) => + x.EventFlags == y.EventFlags && + x.Name == y.Name && + x.EventType == y.EventType; + + public int GetHashCode(RawEventRow obj) => + (int)obj.EventFlags + + rol(obj.Name, 3) + + rol(obj.EventType, 7); + + public bool Equals(RawPropertyMapRow x, RawPropertyMapRow y) => + x.Parent == y.Parent && + x.PropertyList == y.PropertyList; + + public int GetHashCode(RawPropertyMapRow obj) => + (int)obj.Parent + + rol(obj.PropertyList, 3); + + public bool Equals(RawPropertyPtrRow x, RawPropertyPtrRow y) => x.Property == y.Property; + + public int GetHashCode(RawPropertyPtrRow obj) => (int)obj.Property; + + public bool Equals(RawPropertyRow x, RawPropertyRow y) => + x.PropFlags == y.PropFlags && + x.Name == y.Name && + x.Type == y.Type; + + public int GetHashCode(RawPropertyRow obj) => + (int)obj.PropFlags + + rol(obj.Name, 3) + + rol(obj.Type, 7); + + public bool Equals(RawMethodSemanticsRow x, RawMethodSemanticsRow y) => + x.Semantic == y.Semantic && + x.Method == y.Method && + x.Association == y.Association; + + public int GetHashCode(RawMethodSemanticsRow obj) => + (int)obj.Semantic + + rol(obj.Method, 3) + + rol(obj.Association, 7); + + public bool Equals(RawMethodImplRow x, RawMethodImplRow y) => + x.Class == y.Class && + x.MethodBody == y.MethodBody && + x.MethodDeclaration == y.MethodDeclaration; + + public int GetHashCode(RawMethodImplRow obj) => + (int)obj.Class + + rol(obj.MethodBody, 3) + + rol(obj.MethodDeclaration, 7); + + public bool Equals(RawModuleRefRow x, RawModuleRefRow y) => x.Name == y.Name; + + public int GetHashCode(RawModuleRefRow obj) => (int)obj.Name; + + public bool Equals(RawTypeSpecRow x, RawTypeSpecRow y) => x.Signature == y.Signature; + + public int GetHashCode(RawTypeSpecRow obj) => (int)obj.Signature; + + public bool Equals(RawImplMapRow x, RawImplMapRow y) => + x.MappingFlags == y.MappingFlags && + x.MemberForwarded == y.MemberForwarded && + x.ImportName == y.ImportName && + x.ImportScope == y.ImportScope; + + public int GetHashCode(RawImplMapRow obj) => + (int)obj.MappingFlags + + rol(obj.MemberForwarded, 3) + + rol(obj.ImportName, 7) + + rol(obj.ImportScope, 11); + + public bool Equals(RawFieldRVARow x, RawFieldRVARow y) => + x.RVA == y.RVA && + x.Field == y.Field; + + public int GetHashCode(RawFieldRVARow obj) => + (int)obj.RVA + + rol(obj.Field, 3); + + public bool Equals(RawENCLogRow x, RawENCLogRow y) => + x.Token == y.Token && + x.FuncCode == y.FuncCode; + + public int GetHashCode(RawENCLogRow obj) => + (int)obj.Token + + rol(obj.FuncCode, 3); + + public bool Equals(RawENCMapRow x, RawENCMapRow y) => x.Token == y.Token; + + public int GetHashCode(RawENCMapRow obj) => (int)obj.Token; + + public bool Equals(RawAssemblyRow x, RawAssemblyRow y) => + x.HashAlgId == y.HashAlgId && + x.MajorVersion == y.MajorVersion && + x.MinorVersion == y.MinorVersion && + x.BuildNumber == y.BuildNumber && + x.RevisionNumber == y.RevisionNumber && + x.Flags == y.Flags && + x.PublicKey == y.PublicKey && + x.Name == y.Name && + x.Locale == y.Locale; + + public int GetHashCode(RawAssemblyRow obj) => + (int)obj.HashAlgId + + rol(obj.MajorVersion, 3) + + rol(obj.MinorVersion, 7) + + rol(obj.BuildNumber, 11) + + rol(obj.RevisionNumber, 15) + + rol(obj.Flags, 19) + + rol(obj.PublicKey, 23) + + rol(obj.Name, 27) + + rol(obj.Locale, 31); + + public bool Equals(RawAssemblyProcessorRow x, RawAssemblyProcessorRow y) => x.Processor == y.Processor; + + public int GetHashCode(RawAssemblyProcessorRow obj) => (int)obj.Processor; + + public bool Equals(RawAssemblyOSRow x, RawAssemblyOSRow y) => + x.OSPlatformId == y.OSPlatformId && + x.OSMajorVersion == y.OSMajorVersion && + x.OSMinorVersion == y.OSMinorVersion; + + public int GetHashCode(RawAssemblyOSRow obj) => + (int)obj.OSPlatformId + + rol(obj.OSMajorVersion, 3) + + rol(obj.OSMinorVersion, 7); + + public bool Equals(RawAssemblyRefRow x, RawAssemblyRefRow y) => + x.MajorVersion == y.MajorVersion && + x.MinorVersion == y.MinorVersion && + x.BuildNumber == y.BuildNumber && + x.RevisionNumber == y.RevisionNumber && + x.Flags == y.Flags && + x.PublicKeyOrToken == y.PublicKeyOrToken && + x.Name == y.Name && + x.Locale == y.Locale && + x.HashValue == y.HashValue; + + public int GetHashCode(RawAssemblyRefRow obj) => + (int)obj.MajorVersion + + rol(obj.MinorVersion, 3) + + rol(obj.BuildNumber, 7) + + rol(obj.RevisionNumber, 11) + + rol(obj.Flags, 15) + + rol(obj.PublicKeyOrToken, 19) + + rol(obj.Name, 23) + + rol(obj.Locale, 27) + + rol(obj.HashValue, 31); + + public bool Equals(RawAssemblyRefProcessorRow x, RawAssemblyRefProcessorRow y) => + x.Processor == y.Processor && + x.AssemblyRef == y.AssemblyRef; + + public int GetHashCode(RawAssemblyRefProcessorRow obj) => + (int)obj.Processor + + rol(obj.AssemblyRef, 3); + + public bool Equals(RawAssemblyRefOSRow x, RawAssemblyRefOSRow y) => + x.OSPlatformId == y.OSPlatformId && + x.OSMajorVersion == y.OSMajorVersion && + x.OSMinorVersion == y.OSMinorVersion && + x.AssemblyRef == y.AssemblyRef; + + public int GetHashCode(RawAssemblyRefOSRow obj) => + (int)obj.OSPlatformId + + rol(obj.OSMajorVersion, 3) + + rol(obj.OSMinorVersion, 7) + + rol(obj.AssemblyRef, 11); + + public bool Equals(RawFileRow x, RawFileRow y) => + x.Flags == y.Flags && + x.Name == y.Name && + x.HashValue == y.HashValue; + + public int GetHashCode(RawFileRow obj) => + (int)obj.Flags + + rol(obj.Name, 3) + + rol(obj.HashValue, 7); + + public bool Equals(RawExportedTypeRow x, RawExportedTypeRow y) => + x.Flags == y.Flags && + x.TypeDefId == y.TypeDefId && + x.TypeName == y.TypeName && + x.TypeNamespace == y.TypeNamespace && + x.Implementation == y.Implementation; + + public int GetHashCode(RawExportedTypeRow obj) => + (int)obj.Flags + + rol(obj.TypeDefId, 3) + + rol(obj.TypeName, 7) + + rol(obj.TypeNamespace, 11) + + rol(obj.Implementation, 15); + + public bool Equals(RawManifestResourceRow x, RawManifestResourceRow y) => + x.Offset == y.Offset && + x.Flags == y.Flags && + x.Name == y.Name && + x.Implementation == y.Implementation; + + public int GetHashCode(RawManifestResourceRow obj) => + (int)obj.Offset + + rol(obj.Flags, 3) + + rol(obj.Name, 7) + + rol(obj.Implementation, 11); + + public bool Equals(RawNestedClassRow x, RawNestedClassRow y) => + x.NestedClass == y.NestedClass && + x.EnclosingClass == y.EnclosingClass; + + public int GetHashCode(RawNestedClassRow obj) => + (int)obj.NestedClass + + rol(obj.EnclosingClass, 3); + + public bool Equals(RawGenericParamRow x, RawGenericParamRow y) => + x.Number == y.Number && + x.Flags == y.Flags && + x.Owner == y.Owner && + x.Name == y.Name && + x.Kind == y.Kind; + + public int GetHashCode(RawGenericParamRow obj) => + (int)obj.Number + + rol(obj.Flags, 3) + + rol(obj.Owner, 7) + + rol(obj.Name, 11) + + rol(obj.Kind, 15); + + public bool Equals(RawMethodSpecRow x, RawMethodSpecRow y) => + x.Method == y.Method && + x.Instantiation == y.Instantiation; + + public int GetHashCode(RawMethodSpecRow obj) => + (int)obj.Method + + rol(obj.Instantiation, 3); + + public bool Equals(RawGenericParamConstraintRow x, RawGenericParamConstraintRow y) => + x.Owner == y.Owner && + x.Constraint == y.Constraint; + + public int GetHashCode(RawGenericParamConstraintRow obj) => + (int)obj.Owner + + rol(obj.Constraint, 3); + + public bool Equals(RawDocumentRow x, RawDocumentRow y) => + x.Name == y.Name && + x.HashAlgorithm == y.HashAlgorithm && + x.Hash == y.Hash && + x.Language == y.Language; + + public int GetHashCode(RawDocumentRow obj) => + (int)obj.Name + + rol(obj.HashAlgorithm, 3) + + rol(obj.Hash, 7) + + rol(obj.Language, 11); + + public bool Equals(RawMethodDebugInformationRow x, RawMethodDebugInformationRow y) => + x.Document == y.Document && + x.SequencePoints == y.SequencePoints; + + public int GetHashCode(RawMethodDebugInformationRow obj) => + (int)obj.Document + + rol(obj.SequencePoints, 3); + + public bool Equals(RawLocalScopeRow x, RawLocalScopeRow y) => + x.Method == y.Method && + x.ImportScope == y.ImportScope && + x.VariableList == y.VariableList && + x.ConstantList == y.ConstantList && + x.StartOffset == y.StartOffset && + x.Length == y.Length; + + public int GetHashCode(RawLocalScopeRow obj) => + (int)obj.Method + + rol(obj.ImportScope, 3) + + rol(obj.VariableList, 7) + + rol(obj.ConstantList, 11) + + rol(obj.StartOffset, 15) + + rol(obj.Length, 19); + + public bool Equals(RawLocalVariableRow x, RawLocalVariableRow y) => + x.Attributes == y.Attributes && + x.Index == y.Index && + x.Name == y.Name; + + public int GetHashCode(RawLocalVariableRow obj) => + obj.Attributes + + rol(obj.Index, 3) + + rol(obj.Name, 7); + + public bool Equals(RawLocalConstantRow x, RawLocalConstantRow y) => + x.Name == y.Name && + x.Signature == y.Signature; + + public int GetHashCode(RawLocalConstantRow obj) => + (int)obj.Name + + rol(obj.Signature, 3); + + public bool Equals(RawImportScopeRow x, RawImportScopeRow y) => + x.Parent == y.Parent && + x.Imports == y.Imports; + + public int GetHashCode(RawImportScopeRow obj) => + (int)obj.Parent + + rol(obj.Imports, 3); + + public bool Equals(RawStateMachineMethodRow x, RawStateMachineMethodRow y) => + x.MoveNextMethod == y.MoveNextMethod && + x.KickoffMethod == y.KickoffMethod; + + public int GetHashCode(RawStateMachineMethodRow obj) => + (int)obj.MoveNextMethod + + rol(obj.KickoffMethod, 3); + + public bool Equals(RawCustomDebugInformationRow x, RawCustomDebugInformationRow y) => + x.Parent == y.Parent && + x.Kind == y.Kind && + x.Value == y.Value; + + public int GetHashCode(RawCustomDebugInformationRow obj) => + (int)obj.Parent + + rol(obj.Kind, 3) + + rol(obj.Value, 7); } } diff --git a/src/DotNet/MD/RawTableRows.cs b/src/DotNet/MD/RawTableRows.cs index 255bab4be..609245723 100644 --- a/src/DotNet/MD/RawTableRows.cs +++ b/src/DotNet/MD/RawTableRows.cs @@ -1,45 +1,17 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet.MD { - /// - /// A raw table row - /// - public interface IRawRow { - /// - /// Reads a column - /// - /// Column index - /// Column value - uint Read(int index); - - /// - /// Writes a column - /// - /// Column index - /// New value - void Write(int index, uint value); - } - +namespace dnlib.DotNet.MD { +#pragma warning disable 1591 // Missing XML comment for publicly visible type or member /// /// Raw contents of an uncompressed Module table row /// - public sealed class RawModuleRow : IRawRow { - /// - public ushort Generation; - /// - public uint Name; - /// - public uint Mvid; - /// - public uint EncId; - /// - public uint EncBaseId; - - /// Default constructor - public RawModuleRow() { - } + public readonly struct RawModuleRow { + public readonly ushort Generation; + public readonly uint Name; + public readonly uint Mvid; + public readonly uint EncId; + public readonly uint EncBaseId; - /// Constructor public RawModuleRow(ushort Generation, uint Name, uint Mvid, uint EncId, uint EncBaseId) { this.Generation = Generation; this.Name = Name; @@ -48,96 +20,61 @@ public RawModuleRow(ushort Generation, uint Name, uint Mvid, uint EncId, uint En this.EncBaseId = EncBaseId; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Generation; - case 1: return Name; - case 2: return Mvid; - case 3: return EncId; - case 4: return EncBaseId; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Generation = (ushort)value; break; - case 1: Name = value; break; - case 2: Mvid = value; break; - case 3: EncId = value; break; - case 4: EncBaseId = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Generation, + 1 => Name, + 2 => Mvid, + 3 => EncId, + 4 => EncBaseId, + _ => 0, + }; } /// /// Raw contents of an uncompressed TypeRef table row /// - public sealed class RawTypeRefRow : IRawRow { - /// - public uint ResolutionScope; - /// - public uint Name; - /// - public uint Namespace; - - /// Default constructor - public RawTypeRefRow() { - } + public readonly struct RawTypeRefRow { + public readonly uint ResolutionScope; + public readonly uint Name; + public readonly uint Namespace; - /// Constructor public RawTypeRefRow(uint ResolutionScope, uint Name, uint Namespace) { this.ResolutionScope = ResolutionScope; this.Name = Name; this.Namespace = Namespace; } - /// - public uint Read(int index) { - switch (index) { - case 0: return ResolutionScope; - case 1: return Name; - case 2: return Namespace; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: ResolutionScope = value; break; - case 1: Name = value; break; - case 2: Namespace = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => ResolutionScope, + 1 => Name, + 2 => Namespace, + _ => 0, + }; } /// /// Raw contents of an uncompressed TypeDef table row /// - public sealed class RawTypeDefRow : IRawRow { - /// - public uint Flags; - /// - public uint Name; - /// - public uint Namespace; - /// - public uint Extends; - /// - public uint FieldList; - /// - public uint MethodList; - - /// Default constructor - public RawTypeDefRow() { - } + public readonly struct RawTypeDefRow { + public readonly uint Flags; + public readonly uint Name; + public readonly uint Namespace; + public readonly uint Extends; + public readonly uint FieldList; + public readonly uint MethodList; - /// Constructor public RawTypeDefRow(uint Flags, uint Name, uint Namespace, uint Extends, uint FieldList, uint MethodList) { this.Flags = Flags; this.Name = Name; @@ -147,164 +84,102 @@ public RawTypeDefRow(uint Flags, uint Name, uint Namespace, uint Extends, uint F this.MethodList = MethodList; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Flags; - case 1: return Name; - case 2: return Namespace; - case 3: return Extends; - case 4: return FieldList; - case 5: return MethodList; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Flags = value; break; - case 1: Name = value; break; - case 2: Namespace = value; break; - case 3: Extends = value; break; - case 4: FieldList = value; break; - case 5: MethodList = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Flags, + 1 => Name, + 2 => Namespace, + 3 => Extends, + 4 => FieldList, + 5 => MethodList, + _ => 0, + }; } /// /// Raw contents of an uncompressed FieldPtr table row /// - public sealed class RawFieldPtrRow : IRawRow { - /// - public uint Field; - - /// Default constructor - public RawFieldPtrRow() { - } - - /// Constructor - public RawFieldPtrRow(uint Field) { - this.Field = Field; - } + public readonly struct RawFieldPtrRow { + public readonly uint Field; - /// - public uint Read(int index) { - switch (index) { - case 0: return Field; - default: return 0; - } - } + public RawFieldPtrRow(uint Field) => this.Field = Field; - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Field = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Field, + _ => 0, + }; } /// /// Raw contents of an uncompressed Field table row /// - public sealed class RawFieldRow : IRawRow { - /// - public ushort Flags; - /// - public uint Name; - /// - public uint Signature; - - /// Default constructor - public RawFieldRow() { - } + public readonly struct RawFieldRow { + public readonly ushort Flags; + public readonly uint Name; + public readonly uint Signature; - /// Constructor public RawFieldRow(ushort Flags, uint Name, uint Signature) { this.Flags = Flags; this.Name = Name; this.Signature = Signature; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Flags; - case 1: return Name; - case 2: return Signature; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Flags = (ushort)value; break; - case 1: Name = value; break; - case 2: Signature = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Flags, + 1 => Name, + 2 => Signature, + _ => 0, + }; } /// /// Raw contents of an uncompressed MethodPtr table row /// - public sealed class RawMethodPtrRow : IRawRow { - /// - public uint Method; + public readonly struct RawMethodPtrRow { + public readonly uint Method; - /// Default constructor - public RawMethodPtrRow() { - } - - /// Constructor - public RawMethodPtrRow(uint Method) { - this.Method = Method; - } - - /// - public uint Read(int index) { - switch (index) { - case 0: return Method; - default: return 0; - } - } + public RawMethodPtrRow(uint Method) => this.Method = Method; - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Method = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Method, + _ => 0, + }; } /// /// Raw contents of an uncompressed Method table row /// - public sealed class RawMethodRow : IRawRow { - /// - public uint RVA; - /// - public ushort ImplFlags; - /// - public ushort Flags; - /// - public uint Name; - /// - public uint Signature; - /// - public uint ParamList; - - /// Default constructor - public RawMethodRow() { - } + public readonly struct RawMethodRow { + public readonly uint RVA; + public readonly ushort ImplFlags; + public readonly ushort Flags; + public readonly uint Name; + public readonly uint Signature; + public readonly uint ParamList; - /// Constructor public RawMethodRow(uint RVA, ushort ImplFlags, ushort Flags, uint Name, uint Signature, uint ParamList) { this.RVA = RVA; this.ImplFlags = ImplFlags; @@ -314,208 +189,133 @@ public RawMethodRow(uint RVA, ushort ImplFlags, ushort Flags, uint Name, uint Si this.ParamList = ParamList; } - /// - public uint Read(int index) { - switch (index) { - case 0: return RVA; - case 1: return ImplFlags; - case 2: return Flags; - case 3: return Name; - case 4: return Signature; - case 5: return ParamList; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: RVA = value; break; - case 1: ImplFlags = (ushort)value; break; - case 2: Flags = (ushort)value; break; - case 3: Name = value; break; - case 4: Signature = value; break; - case 5: ParamList = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => RVA, + 1 => ImplFlags, + 2 => Flags, + 3 => Name, + 4 => Signature, + 5 => ParamList, + _ => 0, + }; } /// /// Raw contents of an uncompressed ParamPtr table row /// - public sealed class RawParamPtrRow : IRawRow { - /// - public uint Param; - - /// Default constructor - public RawParamPtrRow() { - } - - /// Constructor - public RawParamPtrRow(uint Param) { - this.Param = Param; - } + public readonly struct RawParamPtrRow { + public readonly uint Param; - /// - public uint Read(int index) { - switch (index) { - case 0: return Param; - default: return 0; - } - } + public RawParamPtrRow(uint Param) => this.Param = Param; - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Param = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Param, + _ => 0, + }; } /// /// Raw contents of an uncompressed Param table row /// - public sealed class RawParamRow : IRawRow { - /// - public ushort Flags; - /// - public ushort Sequence; - /// - public uint Name; - - /// Default constructor - public RawParamRow() { - } + public readonly struct RawParamRow { + public readonly ushort Flags; + public readonly ushort Sequence; + public readonly uint Name; - /// Constructor public RawParamRow(ushort Flags, ushort Sequence, uint Name) { this.Flags = Flags; this.Sequence = Sequence; this.Name = Name; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Flags; - case 1: return Sequence; - case 2: return Name; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Flags = (ushort)value; break; - case 1: Sequence = (ushort)value; break; - case 2: Name = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Flags, + 1 => Sequence, + 2 => Name, + _ => 0, + }; } /// /// Raw contents of an uncompressed InterfaceImpl table row /// - public sealed class RawInterfaceImplRow : IRawRow { - /// - public uint Class; - /// - public uint Interface; - - /// Default constructor - public RawInterfaceImplRow() { - } + public readonly struct RawInterfaceImplRow { + public readonly uint Class; + public readonly uint Interface; - /// Constructor public RawInterfaceImplRow(uint Class, uint Interface) { this.Class = Class; this.Interface = Interface; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Class; - case 1: return Interface; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Class = value; break; - case 1: Interface = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Class, + 1 => Interface, + _ => 0, + }; } /// /// Raw contents of an uncompressed MemberRef table row /// - public sealed class RawMemberRefRow : IRawRow { - /// - public uint Class; - /// - public uint Name; - /// - public uint Signature; - - /// Default constructor - public RawMemberRefRow() { - } + public readonly struct RawMemberRefRow { + public readonly uint Class; + public readonly uint Name; + public readonly uint Signature; - /// Constructor public RawMemberRefRow(uint Class, uint Name, uint Signature) { this.Class = Class; this.Name = Name; this.Signature = Signature; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Class; - case 1: return Name; - case 2: return Signature; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Class = value; break; - case 1: Name = value; break; - case 2: Signature = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Class, + 1 => Name, + 2 => Signature, + _ => 0, + }; } /// /// Raw contents of an uncompressed Constant table row /// - public sealed class RawConstantRow : IRawRow { - /// - public byte Type; - /// - public byte Padding; - /// - public uint Parent; - /// - public uint Value; - - /// Default constructor - public RawConstantRow() { - } + public readonly struct RawConstantRow { + public readonly byte Type; + public readonly byte Padding; + public readonly uint Parent; + public readonly uint Value; - /// Constructor public RawConstantRow(byte Type, byte Padding, uint Parent, uint Value) { this.Type = Type; this.Padding = Padding; @@ -523,663 +323,426 @@ public RawConstantRow(byte Type, byte Padding, uint Parent, uint Value) { this.Value = Value; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Type; - case 1: return Parent; - case 2: return Value; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Type = (byte)value; break; - case 1: Parent = value; break; - case 2: Value = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Type, + 1 => Padding, + 2 => Parent, + 3 => Value, + _ => 0, + }; } /// /// Raw contents of an uncompressed CustomAttribute table row /// - public sealed class RawCustomAttributeRow : IRawRow { - /// - public uint Parent; - /// - public uint Type; - /// - public uint Value; - - /// Default constructor - public RawCustomAttributeRow() { - } + public readonly struct RawCustomAttributeRow { + public readonly uint Parent; + public readonly uint Type; + public readonly uint Value; - /// Constructor public RawCustomAttributeRow(uint Parent, uint Type, uint Value) { this.Parent = Parent; this.Type = Type; this.Value = Value; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Parent; - case 1: return Type; - case 2: return Value; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Parent = value; break; - case 1: Type = value; break; - case 2: Value = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Parent, + 1 => Type, + 2 => Value, + _ => 0, + }; } /// /// Raw contents of an uncompressed FieldMarshal table row /// - public sealed class RawFieldMarshalRow : IRawRow { - /// - public uint Parent; - /// - public uint NativeType; - - /// Default constructor - public RawFieldMarshalRow() { - } + public readonly struct RawFieldMarshalRow { + public readonly uint Parent; + public readonly uint NativeType; - /// Constructor public RawFieldMarshalRow(uint Parent, uint NativeType) { this.Parent = Parent; this.NativeType = NativeType; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Parent; - case 1: return NativeType; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Parent = value; break; - case 1: NativeType = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Parent, + 1 => NativeType, + _ => 0, + }; } /// /// Raw contents of an uncompressed DeclSecurity table row /// - public sealed class RawDeclSecurityRow : IRawRow { - /// - public short Action; - /// - public uint Parent; - /// - public uint PermissionSet; - - /// Default constructor - public RawDeclSecurityRow() { - } + public readonly struct RawDeclSecurityRow { + public readonly short Action; + public readonly uint Parent; + public readonly uint PermissionSet; - /// Constructor public RawDeclSecurityRow(short Action, uint Parent, uint PermissionSet) { this.Action = Action; this.Parent = Parent; this.PermissionSet = PermissionSet; } - /// - public uint Read(int index) { - switch (index) { - case 0: return (uint)(int)Action; - case 1: return Parent; - case 2: return PermissionSet; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Action = (short)value; break; - case 1: Parent = value; break; - case 2: PermissionSet = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => (uint)(int)Action, + 1 => Parent, + 2 => PermissionSet, + _ => 0, + }; } /// /// Raw contents of an uncompressed ClassLayout table row /// - public sealed class RawClassLayoutRow : IRawRow { - /// - public ushort PackingSize; - /// - public uint ClassSize; - /// - public uint Parent; - - /// Default constructor - public RawClassLayoutRow() { - } + public readonly struct RawClassLayoutRow { + public readonly ushort PackingSize; + public readonly uint ClassSize; + public readonly uint Parent; - /// Constructor public RawClassLayoutRow(ushort PackingSize, uint ClassSize, uint Parent) { this.PackingSize = PackingSize; this.ClassSize = ClassSize; this.Parent = Parent; } - /// - public uint Read(int index) { - switch (index) { - case 0: return PackingSize; - case 1: return ClassSize; - case 2: return Parent; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: PackingSize = (ushort)value; break; - case 1: ClassSize = value; break; - case 2: Parent = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => PackingSize, + 1 => ClassSize, + 2 => Parent, + _ => 0, + }; } /// /// Raw contents of an uncompressed FieldLayout table row /// - public sealed class RawFieldLayoutRow : IRawRow { - /// - public uint OffSet; - /// - public uint Field; - - /// Default constructor - public RawFieldLayoutRow() { - } + public readonly struct RawFieldLayoutRow { + public readonly uint OffSet; + public readonly uint Field; - /// Constructor public RawFieldLayoutRow(uint OffSet, uint Field) { this.OffSet = OffSet; this.Field = Field; } - /// - public uint Read(int index) { - switch (index) { - case 0: return OffSet; - case 1: return Field; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: OffSet = value; break; - case 1: Field = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => OffSet, + 1 => Field, + _ => 0, + }; } /// /// Raw contents of an uncompressed StandAloneSig table row /// - public sealed class RawStandAloneSigRow : IRawRow { - /// - public uint Signature; + public readonly struct RawStandAloneSigRow { + public readonly uint Signature; - /// Default constructor - public RawStandAloneSigRow() { - } - - /// Constructor - public RawStandAloneSigRow(uint Signature) { - this.Signature = Signature; - } - - /// - public uint Read(int index) { - switch (index) { - case 0: return Signature; - default: return 0; - } - } + public RawStandAloneSigRow(uint Signature) => this.Signature = Signature; - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Signature = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Signature, + _ => 0, + }; } /// /// Raw contents of an uncompressed EventMap table row /// - public sealed class RawEventMapRow : IRawRow { - /// - public uint Parent; - /// - public uint EventList; - - /// Default constructor - public RawEventMapRow() { - } + public readonly struct RawEventMapRow { + public readonly uint Parent; + public readonly uint EventList; - /// Constructor public RawEventMapRow(uint Parent, uint EventList) { this.Parent = Parent; this.EventList = EventList; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Parent; - case 1: return EventList; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Parent = value; break; - case 1: EventList = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Parent, + 1 => EventList, + _ => 0, + }; } /// /// Raw contents of an uncompressed EventPtr table row /// - public sealed class RawEventPtrRow : IRawRow { - /// - public uint Event; - - /// Default constructor - public RawEventPtrRow() { - } - - /// Constructor - public RawEventPtrRow(uint Event) { - this.Event = Event; - } + public readonly struct RawEventPtrRow { + public readonly uint Event; - /// - public uint Read(int index) { - switch (index) { - case 0: return Event; - default: return 0; - } - } + public RawEventPtrRow(uint Event) => this.Event = Event; - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Event = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Event, + _ => 0, + }; } /// /// Raw contents of an uncompressed Event table row /// - public sealed class RawEventRow : IRawRow { - /// - public ushort EventFlags; - /// - public uint Name; - /// - public uint EventType; - - /// Default constructor - public RawEventRow() { - } + public readonly struct RawEventRow { + public readonly ushort EventFlags; + public readonly uint Name; + public readonly uint EventType; - /// Constructor public RawEventRow(ushort EventFlags, uint Name, uint EventType) { this.EventFlags = EventFlags; this.Name = Name; this.EventType = EventType; } - /// - public uint Read(int index) { - switch (index) { - case 0: return EventFlags; - case 1: return Name; - case 2: return EventType; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: EventFlags = (ushort)value; break; - case 1: Name = value; break; - case 2: EventType = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => EventFlags, + 1 => Name, + 2 => EventType, + _ => 0, + }; } /// /// Raw contents of an uncompressed PropertyMap table row /// - public sealed class RawPropertyMapRow : IRawRow { - /// - public uint Parent; - /// - public uint PropertyList; - - /// Default constructor - public RawPropertyMapRow() { - } + public readonly struct RawPropertyMapRow { + public readonly uint Parent; + public readonly uint PropertyList; - /// Constructor public RawPropertyMapRow(uint Parent, uint PropertyList) { this.Parent = Parent; this.PropertyList = PropertyList; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Parent; - case 1: return PropertyList; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Parent = value; break; - case 1: PropertyList = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Parent, + 1 => PropertyList, + _ => 0, + }; } /// /// Raw contents of an uncompressed PropertyPtr table row /// - public sealed class RawPropertyPtrRow : IRawRow { - /// - public uint Property; - - /// Default constructor - public RawPropertyPtrRow() { - } - - /// Constructor - public RawPropertyPtrRow(uint Property) { - this.Property = Property; - } + public readonly struct RawPropertyPtrRow { + public readonly uint Property; - /// - public uint Read(int index) { - switch (index) { - case 0: return Property; - default: return 0; - } - } + public RawPropertyPtrRow(uint Property) => this.Property = Property; - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Property = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Property, + _ => 0, + }; } /// /// Raw contents of an uncompressed Property table row /// - public sealed class RawPropertyRow : IRawRow { - /// - public ushort PropFlags; - /// - public uint Name; - /// - public uint Type; - - /// Default constructor - public RawPropertyRow() { - } + public readonly struct RawPropertyRow { + public readonly ushort PropFlags; + public readonly uint Name; + public readonly uint Type; - /// Constructor public RawPropertyRow(ushort PropFlags, uint Name, uint Type) { this.PropFlags = PropFlags; this.Name = Name; this.Type = Type; } - /// - public uint Read(int index) { - switch (index) { - case 0: return PropFlags; - case 1: return Name; - case 2: return Type; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: PropFlags = (ushort)value; break; - case 1: Name = value; break; - case 2: Type = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => PropFlags, + 1 => Name, + 2 => Type, + _ => 0, + }; } /// /// Raw contents of an uncompressed MethodSemantics table row /// - public sealed class RawMethodSemanticsRow : IRawRow { - /// - public ushort Semantic; - /// - public uint Method; - /// - public uint Association; - - /// Default constructor - public RawMethodSemanticsRow() { - } + public readonly struct RawMethodSemanticsRow { + public readonly ushort Semantic; + public readonly uint Method; + public readonly uint Association; - /// Constructor public RawMethodSemanticsRow(ushort Semantic, uint Method, uint Association) { this.Semantic = Semantic; this.Method = Method; this.Association = Association; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Semantic; - case 1: return Method; - case 2: return Association; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Semantic = (ushort)value; break; - case 1: Method = value; break; - case 2: Association = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Semantic, + 1 => Method, + 2 => Association, + _ => 0, + }; } /// /// Raw contents of an uncompressed MethodImpl table row /// - public sealed class RawMethodImplRow : IRawRow { - /// - public uint Class; - /// - public uint MethodBody; - /// - public uint MethodDeclaration; - - /// Default constructor - public RawMethodImplRow() { - } + public readonly struct RawMethodImplRow { + public readonly uint Class; + public readonly uint MethodBody; + public readonly uint MethodDeclaration; - /// Constructor public RawMethodImplRow(uint Class, uint MethodBody, uint MethodDeclaration) { this.Class = Class; this.MethodBody = MethodBody; this.MethodDeclaration = MethodDeclaration; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Class; - case 1: return MethodBody; - case 2: return MethodDeclaration; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Class = value; break; - case 1: MethodBody = value; break; - case 2: MethodDeclaration = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Class, + 1 => MethodBody, + 2 => MethodDeclaration, + _ => 0, + }; } /// /// Raw contents of an uncompressed ModuleRef table row /// - public sealed class RawModuleRefRow : IRawRow { - /// - public uint Name; - - /// Default constructor - public RawModuleRefRow() { - } + public readonly struct RawModuleRefRow { + public readonly uint Name; - /// Constructor - public RawModuleRefRow(uint Name) { - this.Name = Name; - } + public RawModuleRefRow(uint Name) => this.Name = Name; - /// - public uint Read(int index) { - switch (index) { - case 0: return Name; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Name = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Name, + _ => 0, + }; } /// /// Raw contents of an uncompressed TypeSpec table row /// - public sealed class RawTypeSpecRow : IRawRow { - /// - public uint Signature; - - /// Default constructor - public RawTypeSpecRow() { - } - - /// Constructor - public RawTypeSpecRow(uint Signature) { - this.Signature = Signature; - } + public readonly struct RawTypeSpecRow { + public readonly uint Signature; - /// - public uint Read(int index) { - switch (index) { - case 0: return Signature; - default: return 0; - } - } + public RawTypeSpecRow(uint Signature) => this.Signature = Signature; - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Signature = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Signature, + _ => 0, + }; } /// /// Raw contents of an uncompressed ImplMap table row /// - public sealed class RawImplMapRow : IRawRow { - /// - public ushort MappingFlags; - /// - public uint MemberForwarded; - /// - public uint ImportName; - /// - public uint ImportScope; - - /// Default constructor - public RawImplMapRow() { - } + public readonly struct RawImplMapRow { + public readonly ushort MappingFlags; + public readonly uint MemberForwarded; + public readonly uint ImportName; + public readonly uint ImportScope; - /// Constructor public RawImplMapRow(ushort MappingFlags, uint MemberForwarded, uint ImportName, uint ImportScope) { this.MappingFlags = MappingFlags; this.MemberForwarded = MemberForwarded; @@ -1187,166 +750,105 @@ public RawImplMapRow(ushort MappingFlags, uint MemberForwarded, uint ImportName, this.ImportScope = ImportScope; } - /// - public uint Read(int index) { - switch (index) { - case 0: return MappingFlags; - case 1: return MemberForwarded; - case 2: return ImportName; - case 3: return ImportScope; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: MappingFlags = (ushort)value; break; - case 1: MemberForwarded = value; break; - case 2: ImportName = value; break; - case 3: ImportScope = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => MappingFlags, + 1 => MemberForwarded, + 2 => ImportName, + 3 => ImportScope, + _ => 0, + }; } /// /// Raw contents of an uncompressed FieldRVA table row /// - public sealed class RawFieldRVARow : IRawRow { - /// - public uint RVA; - /// - public uint Field; - - /// Default constructor - public RawFieldRVARow() { - } + public readonly struct RawFieldRVARow { + public readonly uint RVA; + public readonly uint Field; - /// Constructor public RawFieldRVARow(uint RVA, uint Field) { this.RVA = RVA; this.Field = Field; } - /// - public uint Read(int index) { - switch (index) { - case 0: return RVA; - case 1: return Field; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: RVA = value; break; - case 1: Field = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => RVA, + 1 => Field, + _ => 0, + }; } /// /// Raw contents of an uncompressed ENCLog table row /// - public sealed class RawENCLogRow : IRawRow { - /// - public uint Token; - /// - public uint FuncCode; - - /// Default constructor - public RawENCLogRow() { - } + public readonly struct RawENCLogRow { + public readonly uint Token; + public readonly uint FuncCode; - /// Constructor public RawENCLogRow(uint Token, uint FuncCode) { this.Token = Token; this.FuncCode = FuncCode; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Token; - case 1: return FuncCode; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Token = value; break; - case 1: FuncCode = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Token, + 1 => FuncCode, + _ => 0, + }; } /// /// Raw contents of an uncompressed ENCMap table row /// - public sealed class RawENCMapRow : IRawRow { - /// - public uint Token; - - /// Default constructor - public RawENCMapRow() { - } - - /// Constructor - public RawENCMapRow(uint Token) { - this.Token = Token; - } + public readonly struct RawENCMapRow { + public readonly uint Token; - /// - public uint Read(int index) { - switch (index) { - case 0: return Token; - default: return 0; - } - } + public RawENCMapRow(uint Token) => this.Token = Token; - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Token = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Token, + _ => 0, + }; } /// /// Raw contents of an uncompressed Assembly table row /// - public sealed class RawAssemblyRow : IRawRow { - /// - public uint HashAlgId; - /// - public ushort MajorVersion; - /// - public ushort MinorVersion; - /// - public ushort BuildNumber; - /// - public ushort RevisionNumber; - /// - public uint Flags; - /// - public uint PublicKey; - /// - public uint Name; - /// - public uint Locale; - - /// Default constructor - public RawAssemblyRow() { - } + public readonly struct RawAssemblyRow { + public readonly uint HashAlgId; + public readonly ushort MajorVersion; + public readonly ushort MinorVersion; + public readonly ushort BuildNumber; + public readonly ushort RevisionNumber; + public readonly uint Flags; + public readonly uint PublicKey; + public readonly uint Name; + public readonly uint Locale; - /// Constructor public RawAssemblyRow(uint HashAlgId, ushort MajorVersion, ushort MinorVersion, ushort BuildNumber, ushort RevisionNumber, uint Flags, uint PublicKey, uint Name, uint Locale) { this.HashAlgId = HashAlgId; this.MajorVersion = MajorVersion; @@ -1359,143 +861,88 @@ public RawAssemblyRow(uint HashAlgId, ushort MajorVersion, ushort MinorVersion, this.Locale = Locale; } - /// - public uint Read(int index) { - switch (index) { - case 0: return HashAlgId; - case 1: return MajorVersion; - case 2: return MinorVersion; - case 3: return BuildNumber; - case 4: return RevisionNumber; - case 5: return Flags; - case 6: return PublicKey; - case 7: return Name; - case 8: return Locale; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: HashAlgId = value; break; - case 1: MajorVersion = (ushort)value; break; - case 2: MinorVersion = (ushort)value; break; - case 3: BuildNumber = (ushort)value; break; - case 4: RevisionNumber = (ushort)value; break; - case 5: Flags = value; break; - case 6: PublicKey = value; break; - case 7: Name = value; break; - case 8: Locale = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => HashAlgId, + 1 => MajorVersion, + 2 => MinorVersion, + 3 => BuildNumber, + 4 => RevisionNumber, + 5 => Flags, + 6 => PublicKey, + 7 => Name, + 8 => Locale, + _ => 0, + }; } /// /// Raw contents of an uncompressed AssemblyProcessor table row /// - public sealed class RawAssemblyProcessorRow : IRawRow { - /// - public uint Processor; - - /// Default constructor - public RawAssemblyProcessorRow() { - } - - /// Constructor - public RawAssemblyProcessorRow(uint Processor) { - this.Processor = Processor; - } + public readonly struct RawAssemblyProcessorRow { + public readonly uint Processor; - /// - public uint Read(int index) { - switch (index) { - case 0: return Processor; - default: return 0; - } - } + public RawAssemblyProcessorRow(uint Processor) => this.Processor = Processor; - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Processor = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Processor, + _ => 0, + }; } /// /// Raw contents of an uncompressed AssemblyOS table row /// - public sealed class RawAssemblyOSRow : IRawRow { - /// - public uint OSPlatformId; - /// - public uint OSMajorVersion; - /// - public uint OSMinorVersion; - - /// Default constructor - public RawAssemblyOSRow() { - } + public readonly struct RawAssemblyOSRow { + public readonly uint OSPlatformId; + public readonly uint OSMajorVersion; + public readonly uint OSMinorVersion; - /// Constructor public RawAssemblyOSRow(uint OSPlatformId, uint OSMajorVersion, uint OSMinorVersion) { this.OSPlatformId = OSPlatformId; this.OSMajorVersion = OSMajorVersion; this.OSMinorVersion = OSMinorVersion; } - /// - public uint Read(int index) { - switch (index) { - case 0: return OSPlatformId; - case 1: return OSMajorVersion; - case 2: return OSMinorVersion; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: OSPlatformId = value; break; - case 1: OSMajorVersion = value; break; - case 2: OSMinorVersion = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => OSPlatformId, + 1 => OSMajorVersion, + 2 => OSMinorVersion, + _ => 0, + }; } /// /// Raw contents of an uncompressed AssemblyRef table row /// - public sealed class RawAssemblyRefRow : IRawRow { - /// - public ushort MajorVersion; - /// - public ushort MinorVersion; - /// - public ushort BuildNumber; - /// - public ushort RevisionNumber; - /// - public uint Flags; - /// - public uint PublicKeyOrToken; - /// - public uint Name; - /// - public uint Locale; - /// - public uint HashValue; - - /// Default constructor - public RawAssemblyRefRow() { - } + public readonly struct RawAssemblyRefRow { + public readonly ushort MajorVersion; + public readonly ushort MinorVersion; + public readonly ushort BuildNumber; + public readonly ushort RevisionNumber; + public readonly uint Flags; + public readonly uint PublicKeyOrToken; + public readonly uint Name; + public readonly uint Locale; + public readonly uint HashValue; - /// Constructor public RawAssemblyRefRow(ushort MajorVersion, ushort MinorVersion, ushort BuildNumber, ushort RevisionNumber, uint Flags, uint PublicKeyOrToken, uint Name, uint Locale, uint HashValue) { this.MajorVersion = MajorVersion; this.MinorVersion = MinorVersion; @@ -1508,95 +955,60 @@ public RawAssemblyRefRow(ushort MajorVersion, ushort MinorVersion, ushort BuildN this.HashValue = HashValue; } - /// - public uint Read(int index) { - switch (index) { - case 0: return MajorVersion; - case 1: return MinorVersion; - case 2: return BuildNumber; - case 3: return RevisionNumber; - case 4: return Flags; - case 5: return PublicKeyOrToken; - case 6: return Name; - case 7: return Locale; - case 8: return HashValue; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: MajorVersion = (ushort)value; break; - case 1: MinorVersion = (ushort)value; break; - case 2: BuildNumber = (ushort)value; break; - case 3: RevisionNumber = (ushort)value; break; - case 4: Flags = value; break; - case 5: PublicKeyOrToken = value; break; - case 6: Name = value; break; - case 7: Locale = value; break; - case 8: HashValue = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => MajorVersion, + 1 => MinorVersion, + 2 => BuildNumber, + 3 => RevisionNumber, + 4 => Flags, + 5 => PublicKeyOrToken, + 6 => Name, + 7 => Locale, + 8 => HashValue, + _ => 0, + }; } /// /// Raw contents of an uncompressed AssemblyRefProcessor table row /// - public sealed class RawAssemblyRefProcessorRow : IRawRow { - /// - public uint Processor; - /// - public uint AssemblyRef; - - /// Default constructor - public RawAssemblyRefProcessorRow() { - } + public readonly struct RawAssemblyRefProcessorRow { + public readonly uint Processor; + public readonly uint AssemblyRef; - /// Constructor public RawAssemblyRefProcessorRow(uint Processor, uint AssemblyRef) { this.Processor = Processor; this.AssemblyRef = AssemblyRef; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Processor; - case 1: return AssemblyRef; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Processor = value; break; - case 1: AssemblyRef = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Processor, + 1 => AssemblyRef, + _ => 0, + }; } /// /// Raw contents of an uncompressed AssemblyRefOS table row /// - public sealed class RawAssemblyRefOSRow : IRawRow { - /// - public uint OSPlatformId; - /// - public uint OSMajorVersion; - /// - public uint OSMinorVersion; - /// - public uint AssemblyRef; - - /// Default constructor - public RawAssemblyRefOSRow() { - } + public readonly struct RawAssemblyRefOSRow { + public readonly uint OSPlatformId; + public readonly uint OSMajorVersion; + public readonly uint OSMinorVersion; + public readonly uint AssemblyRef; - /// Constructor public RawAssemblyRefOSRow(uint OSPlatformId, uint OSMajorVersion, uint OSMinorVersion, uint AssemblyRef) { this.OSPlatformId = OSPlatformId; this.OSMajorVersion = OSMajorVersion; @@ -1604,92 +1016,59 @@ public RawAssemblyRefOSRow(uint OSPlatformId, uint OSMajorVersion, uint OSMinorV this.AssemblyRef = AssemblyRef; } - /// - public uint Read(int index) { - switch (index) { - case 0: return OSPlatformId; - case 1: return OSMajorVersion; - case 2: return OSMinorVersion; - case 3: return AssemblyRef; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: OSPlatformId = value; break; - case 1: OSMajorVersion = value; break; - case 2: OSMinorVersion = value; break; - case 3: AssemblyRef = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => OSPlatformId, + 1 => OSMajorVersion, + 2 => OSMinorVersion, + 3 => AssemblyRef, + _ => 0, + }; } /// /// Raw contents of an uncompressed File table row /// - public sealed class RawFileRow : IRawRow { - /// - public uint Flags; - /// - public uint Name; - /// - public uint HashValue; - - /// Default constructor - public RawFileRow() { - } + public readonly struct RawFileRow { + public readonly uint Flags; + public readonly uint Name; + public readonly uint HashValue; - /// Constructor public RawFileRow(uint Flags, uint Name, uint HashValue) { this.Flags = Flags; this.Name = Name; this.HashValue = HashValue; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Flags; - case 1: return Name; - case 2: return HashValue; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Flags = value; break; - case 1: Name = value; break; - case 2: HashValue = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Flags, + 1 => Name, + 2 => HashValue, + _ => 0, + }; } /// /// Raw contents of an uncompressed ExportedType table row /// - public sealed class RawExportedTypeRow : IRawRow { - /// - public uint Flags; - /// - public uint TypeDefId; - /// - public uint TypeName; - /// - public uint TypeNamespace; - /// - public uint Implementation; - - /// Default constructor - public RawExportedTypeRow() { - } + public readonly struct RawExportedTypeRow { + public readonly uint Flags; + public readonly uint TypeDefId; + public readonly uint TypeName; + public readonly uint TypeNamespace; + public readonly uint Implementation; - /// Constructor public RawExportedTypeRow(uint Flags, uint TypeDefId, uint TypeName, uint TypeNamespace, uint Implementation) { this.Flags = Flags; this.TypeDefId = TypeDefId; @@ -1698,49 +1077,31 @@ public RawExportedTypeRow(uint Flags, uint TypeDefId, uint TypeName, uint TypeNa this.Implementation = Implementation; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Flags; - case 1: return TypeDefId; - case 2: return TypeName; - case 3: return TypeNamespace; - case 4: return Implementation; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Flags = value; break; - case 1: TypeDefId = value; break; - case 2: TypeName = value; break; - case 3: TypeNamespace = value; break; - case 4: Implementation = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Flags, + 1 => TypeDefId, + 2 => TypeName, + 3 => TypeNamespace, + 4 => Implementation, + _ => 0, + }; } /// /// Raw contents of an uncompressed ManifestResource table row /// - public sealed class RawManifestResourceRow : IRawRow { - /// - public uint Offset; - /// - public uint Flags; - /// - public uint Name; - /// - public uint Implementation; - - /// Default constructor - public RawManifestResourceRow() { - } + public readonly struct RawManifestResourceRow { + public readonly uint Offset; + public readonly uint Flags; + public readonly uint Name; + public readonly uint Implementation; - /// Constructor public RawManifestResourceRow(uint Offset, uint Flags, uint Name, uint Implementation) { this.Offset = Offset; this.Flags = Flags; @@ -1748,87 +1109,56 @@ public RawManifestResourceRow(uint Offset, uint Flags, uint Name, uint Implement this.Implementation = Implementation; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Offset; - case 1: return Flags; - case 2: return Name; - case 3: return Implementation; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Offset = value; break; - case 1: Flags = value; break; - case 2: Name = value; break; - case 3: Implementation = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Offset, + 1 => Flags, + 2 => Name, + 3 => Implementation, + _ => 0, + }; } /// /// Raw contents of an uncompressed NestedClass table row /// - public sealed class RawNestedClassRow : IRawRow { - /// - public uint NestedClass; - /// - public uint EnclosingClass; - - /// Default constructor - public RawNestedClassRow() { - } + public readonly struct RawNestedClassRow { + public readonly uint NestedClass; + public readonly uint EnclosingClass; - /// Constructor public RawNestedClassRow(uint NestedClass, uint EnclosingClass) { this.NestedClass = NestedClass; this.EnclosingClass = EnclosingClass; } - /// - public uint Read(int index) { - switch (index) { - case 0: return NestedClass; - case 1: return EnclosingClass; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: NestedClass = value; break; - case 1: EnclosingClass = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => NestedClass, + 1 => EnclosingClass, + _ => 0, + }; } /// /// Raw contents of an uncompressed GenericParam table row /// - public sealed class RawGenericParamRow : IRawRow { - /// - public ushort Number; - /// - public ushort Flags; - /// - public uint Owner; - /// - public uint Name; - /// - public uint Kind; - - /// Default constructor - public RawGenericParamRow() { - } + public readonly struct RawGenericParamRow { + public readonly ushort Number; + public readonly ushort Flags; + public readonly uint Owner; + public readonly uint Name; + public readonly uint Kind; - /// Constructor public RawGenericParamRow(ushort Number, ushort Flags, uint Owner, uint Name, uint Kind) { this.Number = Number; this.Flags = Flags; @@ -1837,112 +1167,302 @@ public RawGenericParamRow(ushort Number, ushort Flags, uint Owner, uint Name, ui this.Kind = Kind; } - /// Constructor public RawGenericParamRow(ushort Number, ushort Flags, uint Owner, uint Name) { this.Number = Number; this.Flags = Flags; this.Owner = Owner; this.Name = Name; + Kind = 0; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Number; - case 1: return Flags; - case 2: return Owner; - case 3: return Name; - case 4: return Kind; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Number = (ushort)value; break; - case 1: Flags = (ushort)value; break; - case 2: Owner = value; break; - case 3: Name = value; break; - case 4: Kind = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Number, + 1 => Flags, + 2 => Owner, + 3 => Name, + 4 => Kind, + _ => 0, + }; } /// /// Raw contents of an uncompressed MethodSpec table row /// - public sealed class RawMethodSpecRow : IRawRow { - /// - public uint Method; - /// - public uint Instantiation; - - /// Default constructor - public RawMethodSpecRow() { - } + public readonly struct RawMethodSpecRow { + public readonly uint Method; + public readonly uint Instantiation; - /// Constructor public RawMethodSpecRow(uint Method, uint Instantiation) { this.Method = Method; this.Instantiation = Instantiation; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Method; - case 1: return Instantiation; - default: return 0; - } - } - - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Method = value; break; - case 1: Instantiation = value; break; - default: break; - } - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Method, + 1 => Instantiation, + _ => 0, + }; } /// /// Raw contents of an uncompressed GenericParamConstraint table row /// - public sealed class RawGenericParamConstraintRow : IRawRow { - /// - public uint Owner; - /// - public uint Constraint; - - /// Default constructor - public RawGenericParamConstraintRow() { - } + public readonly struct RawGenericParamConstraintRow { + public readonly uint Owner; + public readonly uint Constraint; - /// Constructor public RawGenericParamConstraintRow(uint Owner, uint Constraint) { this.Owner = Owner; this.Constraint = Constraint; } - /// - public uint Read(int index) { - switch (index) { - case 0: return Owner; - case 1: return Constraint; - default: return 0; - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Owner, + 1 => Constraint, + _ => 0, + }; + } + + /// + /// Raw contents of an uncompressed Document table row + /// + public readonly struct RawDocumentRow { + public readonly uint Name; + public readonly uint HashAlgorithm; + public readonly uint Hash; + public readonly uint Language; + + public RawDocumentRow(uint Name, uint HashAlgorithm, uint Hash, uint Language) { + this.Name = Name; + this.HashAlgorithm = HashAlgorithm; + this.Hash = Hash; + this.Language = Language; + } + + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Name, + 1 => HashAlgorithm, + 2 => Hash, + 3 => Language, + _ => 0, + }; + } + + /// + /// Raw contents of an uncompressed MethodDebugInformation table row + /// + public readonly struct RawMethodDebugInformationRow { + public readonly uint Document; + public readonly uint SequencePoints; + + public RawMethodDebugInformationRow(uint Document, uint SequencePoints) { + this.Document = Document; + this.SequencePoints = SequencePoints; } - /// - public void Write(int index, uint value) { - switch (index) { - case 0: Owner = value; break; - case 1: Constraint = value; break; - default: break; - } + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Document, + 1 => SequencePoints, + _ => 0, + }; + } + + /// + /// Raw contents of an uncompressed LocalScope table row + /// + public readonly struct RawLocalScopeRow { + public readonly uint Method; + public readonly uint ImportScope; + public readonly uint VariableList; + public readonly uint ConstantList; + public readonly uint StartOffset; + public readonly uint Length; + + public RawLocalScopeRow(uint Method, uint ImportScope, uint VariableList, uint ConstantList, uint StartOffset, uint Length) { + this.Method = Method; + this.ImportScope = ImportScope; + this.VariableList = VariableList; + this.ConstantList = ConstantList; + this.StartOffset = StartOffset; + this.Length = Length; } + + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Method, + 1 => ImportScope, + 2 => VariableList, + 3 => ConstantList, + 4 => StartOffset, + 5 => Length, + _ => 0, + }; + } + + /// + /// Raw contents of an uncompressed LocalVariable table row + /// + public readonly struct RawLocalVariableRow { + public readonly ushort Attributes; + public readonly ushort Index; + public readonly uint Name; + + public RawLocalVariableRow(ushort Attributes, ushort Index, uint Name) { + this.Attributes = Attributes; + this.Index = Index; + this.Name = Name; + } + + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Attributes, + 1 => Index, + 2 => Name, + _ => 0, + }; + } + + /// + /// Raw contents of an uncompressed LocalConstant table row + /// + public readonly struct RawLocalConstantRow { + public readonly uint Name; + public readonly uint Signature; + + public RawLocalConstantRow(uint Name, uint Signature) { + this.Name = Name; + this.Signature = Signature; + } + + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Name, + 1 => Signature, + _ => 0, + }; + } + + /// + /// Raw contents of an uncompressed ImportScope table row + /// + public readonly struct RawImportScopeRow { + public readonly uint Parent; + public readonly uint Imports; + + public RawImportScopeRow(uint Parent, uint Imports) { + this.Parent = Parent; + this.Imports = Imports; + } + + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Parent, + 1 => Imports, + _ => 0, + }; + } + + /// + /// Raw contents of an uncompressed StateMachineMethod table row + /// + public readonly struct RawStateMachineMethodRow { + public readonly uint MoveNextMethod; + public readonly uint KickoffMethod; + + public RawStateMachineMethodRow(uint MoveNextMethod, uint KickoffMethod) { + this.MoveNextMethod = MoveNextMethod; + this.KickoffMethod = KickoffMethod; + } + + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => MoveNextMethod, + 1 => KickoffMethod, + _ => 0, + }; + } + + /// + /// Raw contents of an uncompressed CustomDebugInformation table row + /// + public readonly struct RawCustomDebugInformationRow { + public readonly uint Parent; + public readonly uint Kind; + public readonly uint Value; + + public RawCustomDebugInformationRow(uint Parent, uint Kind, uint Value) { + this.Parent = Parent; + this.Kind = Kind; + this.Value = Value; + } + + /// + /// Gets a column + /// + /// Index of column + /// + public uint this[int index] => + index switch { + 0 => Parent, + 1 => Kind, + 2 => Value, + _ => 0, + }; } +#pragma warning restore 1591 // Missing XML comment for publicly visible type or member } diff --git a/src/DotNet/MD/RidList.cs b/src/DotNet/MD/RidList.cs index 7cd9ccc45..62ed3d5df 100644 --- a/src/DotNet/MD/RidList.cs +++ b/src/DotNet/MD/RidList.cs @@ -1,145 +1,141 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; +using System; +using System.Collections; +using System.Collections.Generic; using System.Diagnostics; namespace dnlib.DotNet.MD { /// /// Stores a list of rids /// - [DebuggerDisplay("Length = {Length}")] - public abstract class RidList { - /// - /// The empty - /// - public static readonly RidList Empty = new ContiguousRidList(0, 0); - - /// - /// Gets the number of rids it will iterate over (UInt32) - /// - public abstract uint Length { get; } + [DebuggerDisplay("Count = {Count}")] + public readonly struct RidList : IEnumerable { + readonly uint startRid; + readonly uint length; + readonly IList rids; /// - /// Gets the number of rids it will iterate over (Int32) + /// Gets the empty instance /// - public abstract int Count { get; } + public static readonly RidList Empty = Create(0, 0); /// - /// Gets the 'th rid + /// Creates a new instance /// - /// Index. Must be < - /// A rid or 0 if is invalid - public abstract uint this[uint index] { get; } + /// + /// + /// + public static RidList Create(uint startRid, uint length) => new RidList(startRid, length); /// - /// Gets the 'th rid + /// Creates a new instance /// - /// Index. Must be < - /// A rid or 0 if is invalid - public abstract uint this[int index] { get; } - } + /// List of valid rids + /// + public static RidList Create(IList rids) => new RidList(rids); - /// - /// A where the rids are contiguous - /// - sealed class ContiguousRidList : RidList { - readonly uint startRid; - readonly uint length; - - /// - /// Gets the start rid - /// - public uint StartRID { - get { return startRid; } - } - - /// - public override uint Length { - get { return length; } - } - - /// - public override int Count { - get { return (int)length; } - } - - /// - public override uint this[uint index] { - get { - if (index >= length) - return 0; - return startRid + index; - } - } - - /// - public override uint this[int index] { - get { return this[(uint)index]; } - } - - /// - /// Constructor - /// - /// First rid to return - /// Number of rids to return - public ContiguousRidList(uint startRid, uint length) { + RidList(uint startRid, uint length) { this.startRid = startRid; this.length = length; + rids = null; } - } - /// - /// A where the returned rids aren't necessarily contiguous. - /// This should be used if eg. the pointer tables are present. - /// - sealed class RandomRidList : RidList { - readonly IList indexToRid; - - /// - public override uint Length { - get { return (uint)indexToRid.Count; } + RidList(IList rids) { + this.rids = rids ?? throw new ArgumentNullException(nameof(rids)); + startRid = 0; + length = (uint)rids.Count; } - /// - public override int Count { - get { return indexToRid.Count; } - } - - /// - public override uint this[uint index] { + /// + /// Gets the 'th rid + /// + /// Index. Must be < + /// A rid or 0 if is invalid + public uint this[int index] { get { - if (index >= (uint)indexToRid.Count) - return 0; - return indexToRid[(int)index]; + if (rids is not null) { + if ((uint)index >= (uint)rids.Count) + return 0; + return rids[index]; + } + else { + if ((uint)index >= length) + return 0; + return startRid + (uint)index; + } } } - /// - public override uint this[int index] { - get { return this[(uint)index]; } - } - /// - /// Default constructor + /// Gets the number of rids it will iterate over /// - public RandomRidList() { - this.indexToRid = new List(); - } + public int Count => (int)length; /// - /// Constructor + /// Enumerator /// - /// Approximate number of rids that will be returned - public RandomRidList(int capacity) { - this.indexToRid = new List(capacity); + public struct Enumerator : IEnumerator { + readonly uint startRid; + readonly uint length; + readonly IList rids; + uint index; + uint current; + + internal Enumerator(in RidList list) { + startRid = list.startRid; + length = list.length; + rids = list.rids; + index = 0; + current = 0; + } + + /// + /// Gets the current rid + /// + public uint Current => current; + object IEnumerator.Current => current; + + /// + /// Disposes this instance + /// + public void Dispose() { } + + /// + /// Moves to the next rid + /// + /// + public bool MoveNext() { + if (rids is null && index < length) { + current = startRid + index; + index++; + return true; + } + return MoveNextOther(); + } + + bool MoveNextOther() { + if (index >= length) { + current = 0; + return false; + } + if (rids is not null) + current = rids[(int)index]; + else + current = startRid + index; + index++; + return true; + } + + void IEnumerator.Reset() => throw new NotSupportedException(); } /// - /// Add a new rid that should be returned + /// Gets the enumerator /// - /// The rid - public void Add(uint rid) { - indexToRid.Add(rid); - } + /// + public Enumerator GetEnumerator() => new Enumerator(this); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/src/DotNet/MD/StorageFlags.cs b/src/DotNet/MD/StorageFlags.cs index 7f9ced41a..c5d03873e 100644 --- a/src/DotNet/MD/StorageFlags.cs +++ b/src/DotNet/MD/StorageFlags.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.DotNet.MD { /// diff --git a/src/DotNet/MD/StreamHeader.cs b/src/DotNet/MD/StreamHeader.cs index 5cc581730..dac48bfdf 100644 --- a/src/DotNet/MD/StreamHeader.cs +++ b/src/DotNet/MD/StreamHeader.cs @@ -16,25 +16,19 @@ public sealed class StreamHeader : FileSection { readonly string name; /// - /// The offset of the stream relative to the start of the MetaData header + /// The offset of the stream relative to the start of the metadata header /// - public uint Offset { - get { return offset; } - } + public uint Offset => offset; /// /// The size of the stream /// - public uint StreamSize { - get { return streamSize; } - } + public uint StreamSize => streamSize; /// /// The name of the stream /// - public string Name { - get { return name; } - } + public string Name => name; /// /// Constructor @@ -42,17 +36,36 @@ public string Name { /// PE file reader pointing to the start of this section /// Verify section /// Thrown if verification fails - public StreamHeader(IImageStream reader, bool verify) { - SetStartOffset(reader); - this.offset = reader.ReadUInt32(); - this.streamSize = reader.ReadUInt32(); - this.name = ReadString(reader, 32, verify); - SetEndoffset(reader); + public StreamHeader(ref DataReader reader, bool verify) + : this(ref reader, verify, verify, CLRRuntimeReaderKind.CLR, out _) { + } + + internal StreamHeader(ref DataReader reader, bool throwOnError, bool verify, CLRRuntimeReaderKind runtime, out bool failedVerification) { + failedVerification = false; + SetStartOffset(ref reader); + offset = reader.ReadUInt32(); + streamSize = reader.ReadUInt32(); + name = ReadString(ref reader, 32, verify, ref failedVerification); + SetEndoffset(ref reader); + if (runtime == CLRRuntimeReaderKind.Mono) { + if (offset > reader.Length) + offset = reader.Length; + // Mono ignores the size (eg. it can be 0 or max value) so set it to the max possible value + streamSize = reader.Length - offset; + } if (verify && offset + size < offset) + failedVerification = true; + if (throwOnError && failedVerification) throw new BadImageFormatException("Invalid stream header"); } - static string ReadString(IImageStream reader, int maxLen, bool verify) { + internal StreamHeader(uint offset, uint streamSize, string name) { + this.offset = offset; + this.streamSize = streamSize; + this.name = name ?? throw new ArgumentNullException(nameof(name)); + } + + static string ReadString(ref DataReader reader, int maxLen, bool verify, ref bool failedVerification) { var origPos = reader.Position; var sb = new StringBuilder(maxLen); int i; @@ -63,9 +76,9 @@ static string ReadString(IImageStream reader, int maxLen, bool verify) { sb.Append((char)b); } if (verify && i == maxLen) - throw new BadImageFormatException("Invalid stream name string"); + failedVerification = true; if (i != maxLen) - reader.Position = origPos + ((i + 1 + 3) & ~3); + reader.Position = origPos + (((uint)i + 1 + 3) & ~3U); return sb.ToString(); } } diff --git a/src/DotNet/MD/StringsStream.cs b/src/DotNet/MD/StringsStream.cs index 5c9093b69..e2defdf50 100644 --- a/src/DotNet/MD/StringsStream.cs +++ b/src/DotNet/MD/StringsStream.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using dnlib.IO; +using dnlib.IO; namespace dnlib.DotNet.MD { /// @@ -12,8 +12,8 @@ public StringsStream() { } /// - public StringsStream(IImageStream imageStream, StreamHeader streamHeader) - : base(imageStream, streamHeader) { + public StringsStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader) + : base(mdReaderFactory, metadataBaseOffset, streamHeader) { } /// @@ -22,18 +22,13 @@ public StringsStream(IImageStream imageStream, StreamHeader streamHeader) /// Offset of string /// A instance or null if invalid offset public UTF8String Read(uint offset) { - if (offset >= ImageStreamLength) + if (offset >= StreamLength) return null; byte[] data; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(offset); - data = reader.ReadBytesUntilByte(0); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - if (data == null) + var reader = dataReader; + reader.Position = offset; + data = reader.TryReadBytesUntil(0); + if (data is null) return null; return new UTF8String(data); } @@ -44,8 +39,6 @@ public UTF8String Read(uint offset) { /// /// Offset of string /// A instance - public UTF8String ReadNoNull(uint offset) { - return Read(offset) ?? UTF8String.Empty; - } + public UTF8String ReadNoNull(uint offset) => Read(offset) ?? UTF8String.Empty; } } diff --git a/src/DotNet/MD/Table.cs b/src/DotNet/MD/Table.cs index 2682c226a..6cf111544 100644 --- a/src/DotNet/MD/Table.cs +++ b/src/DotNet/MD/Table.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet.MD { +namespace dnlib.DotNet.MD { /// /// The metadata tables /// @@ -95,5 +95,22 @@ public enum Table : byte { MethodSpec, /// GenericParamConstraint table (2Ch) GenericParamConstraint, + + /// (Portable PDB) Document table (30h) + Document = 0x30, + /// (Portable PDB) MethodDebugInformation table (31h) + MethodDebugInformation, + /// (Portable PDB) LocalScope table (32h) + LocalScope, + /// (Portable PDB) LocalVariable table (33h) + LocalVariable, + /// (Portable PDB) LocalConstant table (34h) + LocalConstant, + /// (Portable PDB) ImportScope table (35h) + ImportScope, + /// (Portable PDB) StateMachineMethod table (36h) + StateMachineMethod, + /// (Portable PDB) CustomDebugInformation table (37h) + CustomDebugInformation, } } diff --git a/src/DotNet/MD/TableInfo.cs b/src/DotNet/MD/TableInfo.cs index 0c4a0c47e..1f15b5d0d 100644 --- a/src/DotNet/MD/TableInfo.cs +++ b/src/DotNet/MD/TableInfo.cs @@ -1,6 +1,5 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; using System.Diagnostics; namespace dnlib.DotNet.MD { @@ -11,37 +10,31 @@ namespace dnlib.DotNet.MD { public sealed class TableInfo { readonly Table table; int rowSize; - readonly IList columns; + readonly ColumnInfo[] columns; readonly string name; /// /// Returns the table type /// - public Table Table { - get { return table; } - } + public Table Table => table; /// /// Returns the total size of a row in bytes /// public int RowSize { - get { return rowSize; } - internal set { rowSize = value; } + get => rowSize; + internal set => rowSize = value; } /// /// Returns all the columns /// - public IList Columns { - get { return columns; } - } + public ColumnInfo[] Columns => columns; /// /// Returns the name of the table /// - public string Name { - get { return name; } - } + public string Name => name; /// /// Constructor @@ -49,7 +42,7 @@ public string Name { /// Table type /// Table name /// All columns - public TableInfo(Table table, string name, IList columns) { + public TableInfo(Table table, string name, ColumnInfo[] columns) { this.table = table; this.name = name; this.columns = columns; @@ -62,7 +55,7 @@ public TableInfo(Table table, string name, IList columns) { /// Table name /// All columns /// Row size - public TableInfo(Table table, string name, IList columns, int rowSize) { + public TableInfo(Table table, string name, ColumnInfo[] columns, int rowSize) { this.table = table; this.name = name; this.columns = columns; diff --git a/src/DotNet/MD/TablesStream.cs b/src/DotNet/MD/TablesStream.cs index 3b09a2a76..3e291ef2b 100644 --- a/src/DotNet/MD/TablesStream.cs +++ b/src/DotNet/MD/TablesStream.cs @@ -2,8 +2,6 @@ using System; using dnlib.IO; -using dnlib.PE; -using dnlib.Threading; namespace dnlib.DotNet.MD { /// @@ -20,10 +18,11 @@ public sealed partial class TablesStream : DotNetStream { ulong sortedMask; uint extraData; MDTable[] mdTables; + uint mdTablesPos; - HotTableStream hotTableStream; IColumnReader columnReader; IRowReader methodRowReader; + readonly CLRRuntimeReaderKind runtime; #pragma warning disable 1591 // XML doc comment public MDTable ModuleTable { get; private set; } @@ -71,169 +70,177 @@ public sealed partial class TablesStream : DotNetStream { public MDTable GenericParamTable { get; private set; } public MDTable MethodSpecTable { get; private set; } public MDTable GenericParamConstraintTable { get; private set; } + public MDTable DocumentTable { get; private set; } + public MDTable MethodDebugInformationTable { get; private set; } + public MDTable LocalScopeTable { get; private set; } + public MDTable LocalVariableTable { get; private set; } + public MDTable LocalConstantTable { get; private set; } + public MDTable ImportScopeTable { get; private set; } + public MDTable StateMachineMethodTable { get; private set; } + public MDTable CustomDebugInformationTable { get; private set; } #pragma warning restore -#if THREAD_SAFE - internal readonly Lock theLock = Lock.Create(); -#endif - - internal HotTableStream HotTableStream { - set { hotTableStream = value; } - } - /// /// Gets/sets the column reader /// public IColumnReader ColumnReader { - get { return columnReader; } - set { columnReader = value; } + get => columnReader; + set => columnReader = value; } /// /// Gets/sets the Method table reader /// public IRowReader MethodRowReader { - get { return methodRowReader; } - set { methodRowReader = value; } + get => methodRowReader; + set => methodRowReader = value; } /// /// Gets the reserved field /// - public uint Reserved1 { - get { return reserved1; } - } + public uint Reserved1 => reserved1; /// /// Gets the version. The major version is in the upper 8 bits, and the minor version /// is in the lower 8 bits. /// - public ushort Version { - get { return (ushort)((majorVersion << 8) | minorVersion); } - } + public ushort Version => (ushort)((majorVersion << 8) | minorVersion); /// /// Gets /// - public MDStreamFlags Flags { - get { return flags; } - } + public MDStreamFlags Flags => flags; /// /// Gets the reserved log2 rid field /// - public byte Log2Rid { - get { return log2Rid; } - } + public byte Log2Rid => log2Rid; /// /// Gets the valid mask /// - public ulong ValidMask { - get { return validMask; } - } + public ulong ValidMask => validMask; /// /// Gets the sorted mask /// - public ulong SortedMask { - get { return sortedMask; } - } + public ulong SortedMask => sortedMask; /// /// Gets the extra data /// - public uint ExtraData { - get { return extraData; } - } + public uint ExtraData => extraData; /// /// Gets the MD tables /// - public MDTable[] MDTables { - get { return mdTables; } - } + public MDTable[] MDTables => mdTables; /// /// Gets the bit /// - public bool HasBigStrings { - get { return (flags & MDStreamFlags.BigStrings) != 0; } - } + public bool HasBigStrings => (flags & MDStreamFlags.BigStrings) != 0; /// /// Gets the bit /// - public bool HasBigGUID { - get { return (flags & MDStreamFlags.BigGUID) != 0; } - } + public bool HasBigGUID => (flags & MDStreamFlags.BigGUID) != 0; /// /// Gets the bit /// - public bool HasBigBlob { - get { return (flags & MDStreamFlags.BigBlob) != 0; } - } + public bool HasBigBlob => (flags & MDStreamFlags.BigBlob) != 0; /// /// Gets the bit /// - public bool HasPadding { - get { return (flags & MDStreamFlags.Padding) != 0; } - } + public bool HasPadding => runtime == CLRRuntimeReaderKind.CLR && (flags & MDStreamFlags.Padding) != 0; /// /// Gets the bit /// - public bool HasDeltaOnly { - get { return (flags & MDStreamFlags.DeltaOnly) != 0; } - } + public bool HasDeltaOnly => runtime == CLRRuntimeReaderKind.CLR && (flags & MDStreamFlags.DeltaOnly) != 0; /// /// Gets the bit /// - public bool HasExtraData { - get { return (flags & MDStreamFlags.ExtraData) != 0; } - } + public bool HasExtraData => runtime == CLRRuntimeReaderKind.CLR && (flags & MDStreamFlags.ExtraData) != 0; /// /// Gets the bit /// - public bool HasDelete { - get { return (flags & MDStreamFlags.HasDelete) != 0; } + public bool HasDelete => runtime == CLRRuntimeReaderKind.CLR && (flags & MDStreamFlags.HasDelete) != 0; + + /// + /// Constructor + /// + /// factory + /// Offset of metadata + /// Stream header + public TablesStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader) + : this(mdReaderFactory, metadataBaseOffset, streamHeader, CLRRuntimeReaderKind.CLR) { } - /// - public TablesStream(IImageStream imageStream, StreamHeader streamHeader) - : base(imageStream, streamHeader) { + /// + /// Constructor + /// + /// factory + /// Offset of metadata + /// Stream header + /// Runtime kind + public TablesStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader, CLRRuntimeReaderKind runtime) + : base(mdReaderFactory, metadataBaseOffset, streamHeader) { + this.runtime = runtime; } /// /// Initializes MD tables /// - /// The PEImage - public void Initialize(IPEImage peImage) { + /// Type system table rows (from #Pdb stream) + public void Initialize(uint[] typeSystemTableRows) => + Initialize(typeSystemTableRows, false); + + /// + /// Initializes MD tables + /// + /// Type system table rows (from #Pdb stream) + /// Force all columns to 4 bytes instead of 2 or 4 bytes + internal void Initialize(uint[] typeSystemTableRows, bool forceAllBig) { if (initialized) throw new Exception("Initialize() has already been called"); initialized = true; - reserved1 = imageStream.ReadUInt32(); - majorVersion = imageStream.ReadByte(); - minorVersion = imageStream.ReadByte(); - flags = (MDStreamFlags)imageStream.ReadByte(); - log2Rid = imageStream.ReadByte(); - validMask = imageStream.ReadUInt64(); - sortedMask = imageStream.ReadUInt64(); + var reader = dataReader; + reserved1 = reader.ReadUInt32(); + majorVersion = reader.ReadByte(); + minorVersion = reader.ReadByte(); + flags = (MDStreamFlags)reader.ReadByte(); + log2Rid = reader.ReadByte(); + validMask = reader.ReadUInt64(); + sortedMask = reader.ReadUInt64(); + // Mono assumes everything is sorted + if (runtime == CLRRuntimeReaderKind.Mono) + sortedMask = ulong.MaxValue; - int maxPresentTables; var dnTableSizes = new DotNetTableSizes(); - var tableInfos = dnTableSizes.CreateTables(majorVersion, minorVersion, out maxPresentTables); + byte tmpMajor = majorVersion, tmpMinor = minorVersion; + // It ignores the version so use 2.0 + if (runtime == CLRRuntimeReaderKind.Mono) { + tmpMajor = 2; + tmpMinor = 0; + } + var tableInfos = dnTableSizes.CreateTables(tmpMajor, tmpMinor, out int maxPresentTables); + if (typeSystemTableRows is not null) + maxPresentTables = DotNetTableSizes.normalMaxTables; mdTables = new MDTable[tableInfos.Length]; ulong valid = validMask; var sizes = new uint[64]; for (int i = 0; i < 64; valid >>= 1, i++) { - uint rows = (valid & 1) == 0 ? 0 : imageStream.ReadUInt32(); + uint rows = (valid & 1) == 0 ? 0 : reader.ReadUInt32(); + // Mono ignores the high byte + rows &= 0x00FFFFFF; if (i >= maxPresentTables) rows = 0; sizes[i] = rows; @@ -242,21 +249,45 @@ public void Initialize(IPEImage peImage) { } if (HasExtraData) - extraData = imageStream.ReadUInt32(); + extraData = reader.ReadUInt32(); + + var debugSizes = sizes; + if (typeSystemTableRows is not null) { + debugSizes = new uint[sizes.Length]; + for (int i = 0; i < 64; i++) { + if (DotNetTableSizes.IsSystemTable((Table)i)) + debugSizes[i] = typeSystemTableRows[i]; + else + debugSizes[i] = sizes[i]; + } + } - dnTableSizes.InitializeSizes(HasBigStrings, HasBigGUID, HasBigBlob, sizes); + dnTableSizes.InitializeSizes(HasBigStrings, HasBigGUID, HasBigBlob, sizes, debugSizes, forceAllBig); - var currentRva = peImage.ToRVA(imageStream.FileOffset) + (uint)imageStream.Position; + mdTablesPos = reader.Position; + InitializeMdTableReaders(); + InitializeTables(); + } + + /// + protected override void OnReaderRecreated() => InitializeMdTableReaders(); + + void InitializeMdTableReaders() { + var reader = dataReader; + reader.Position = mdTablesPos; + var currentPos = reader.Position; foreach (var mdTable in mdTables) { - var dataLen = (long)mdTable.TableInfo.RowSize * (long)mdTable.Rows; - mdTable.ImageStream = peImage.CreateStream(currentRva, dataLen); - var newRva = currentRva + (uint)dataLen; - if (newRva < currentRva) + var dataLen = (uint)mdTable.TableInfo.RowSize * mdTable.Rows; + if (currentPos > reader.Length) + currentPos = reader.Length; + if ((ulong)currentPos + dataLen > reader.Length) + dataLen = reader.Length - currentPos; + mdTable.DataReader = reader.Slice(currentPos, dataLen); + var newPos = currentPos + dataLen; + if (newPos < currentPos) throw new BadImageFormatException("Too big MD table"); - currentRva = newRva; + currentPos = newPos; } - - InitializeTables(); } void InitializeTables() { @@ -305,15 +336,23 @@ void InitializeTables() { GenericParamTable = mdTables[(int)Table.GenericParam]; MethodSpecTable = mdTables[(int)Table.MethodSpec]; GenericParamConstraintTable = mdTables[(int)Table.GenericParamConstraint]; + DocumentTable = mdTables[(int)Table.Document]; + MethodDebugInformationTable = mdTables[(int)Table.MethodDebugInformation]; + LocalScopeTable = mdTables[(int)Table.LocalScope]; + LocalVariableTable = mdTables[(int)Table.LocalVariable]; + LocalConstantTable = mdTables[(int)Table.LocalConstant]; + ImportScopeTable = mdTables[(int)Table.ImportScope]; + StateMachineMethodTable = mdTables[(int)Table.StateMachineMethod]; + CustomDebugInformationTable = mdTables[(int)Table.CustomDebugInformation]; } /// protected override void Dispose(bool disposing) { if (disposing) { var mt = mdTables; - if (mt != null) { + if (mt is not null) { foreach (var mdTable in mt) { - if (mdTable != null) + if (mdTable is not null) mdTable.Dispose(); } mdTables = null; @@ -339,9 +378,7 @@ public MDTable Get(Table table) { /// /// The table type /// true if the table exists - public bool HasTable(Table table) { - return (uint)table < (uint)mdTables.Length; - } + public bool HasTable(Table table) => (uint)table < (uint)mdTables.Length; /// /// Checks whether table is sorted diff --git a/src/DotNet/MD/TablesStream_Read.cs b/src/DotNet/MD/TablesStream_Read.cs index 91315fc2b..6f8dc7a0a 100644 --- a/src/DotNet/MD/TablesStream_Read.cs +++ b/src/DotNet/MD/TablesStream_Read.cs @@ -1,1693 +1,1120 @@ // dnlib: See LICENSE.txt for more info -using System; -using dnlib.IO; -using dnlib.PE; +using System.Diagnostics; -namespace dnlib.DotNet.MD { +namespace dnlib.DotNet.MD { public partial class TablesStream { - IBinaryReader GetReader_NoLock(MDTable table, uint rid) { - IBinaryReader reader; - if (hotTableStream != null) { - reader = hotTableStream.GetTableReader(table, rid); - if (reader != null) - return reader; - } - reader = table.ImageStream; - reader.Position = (rid - 1) * table.TableInfo.RowSize; - return reader; - } - /// - /// Reads a raw Module row + /// Reads a raw Module row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawModuleRow ReadModuleRow(uint rid) { - var table = ModuleTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawModuleRow(reader.ReadUInt16(), - columns[1].Read(reader), - columns[2].Read(reader), - columns[3].Read(reader), - columns[4].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw Module row - /// - /// Row ID - /// - /// - /// - /// + /// Row data /// - internal uint ReadModuleRow(uint rid, out ushort generation, out uint name, out uint mvid, out uint encId) { + public bool TryReadModuleRow(uint rid, out RawModuleRow row) { var table = ModuleTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - generation = reader.ReadUInt16(); - name = columns[1].Read(reader); - mvid = columns[2].Read(reader); - encId = columns[3].Read(reader); - return columns[4].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw TypeRef row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawTypeRefRow ReadTypeRefRow(uint rid) { - var table = TypeRefTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawTypeRefRow(columns[0].Read(reader), - columns[1].Read(reader), - columns[2].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawModuleRow( + reader.Unsafe_ReadUInt16(), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader), + table.Column3.Unsafe_Read24(ref reader), + table.Column4.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw TypeRef row + /// Reads a raw TypeRef row or returns false if the row doesn't exist /// /// Row ID - /// - /// + /// Row data /// - internal uint ReadTypeRefRow(uint rid, out uint resolutionScope, out uint name) { + public bool TryReadTypeRefRow(uint rid, out RawTypeRefRow row) { var table = TypeRefTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - resolutionScope = columns[0].Read(reader); - name = columns[1].Read(reader); - return columns[2].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw TypeDef row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawTypeDefRow ReadTypeDefRow(uint rid) { - var table = TypeDefTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawTypeDefRow(reader.ReadUInt32(), - columns[1].Read(reader), - columns[2].Read(reader), - columns[3].Read(reader), - columns[4].Read(reader), - columns[5].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawTypeRefRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw TypeDef row. Doesn't read field/method rid list. + /// Reads a raw TypeDef row or returns false if the row doesn't exist /// /// Row ID - /// - /// - /// + /// Row data /// - internal uint ReadTypeDefRow(uint rid, out int flags, out uint name, out uint @namespace) { + public bool TryReadTypeDefRow(uint rid, out RawTypeDefRow row) { var table = TypeDefTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - flags = reader.ReadInt32(); - name = columns[1].Read(reader); - @namespace = columns[2].Read(reader); - return columns[3].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawTypeDefRow( + reader.Unsafe_ReadUInt32(), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader), + table.Column3.Unsafe_Read24(ref reader), + table.Column4.Unsafe_Read24(ref reader), + table.Column5.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw FieldPtr row + /// Reads a raw FieldPtr row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawFieldPtrRow ReadFieldPtrRow(uint rid) { + /// Row data + /// + public bool TryReadFieldPtrRow(uint rid, out RawFieldPtrRow row) { var table = FieldPtrTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawFieldPtrRow(columns[0].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw Field row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawFieldRow ReadFieldRow(uint rid) { - var table = FieldTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawFieldRow(reader.ReadUInt16(), - columns[1].Read(reader), - columns[2].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawFieldPtrRow(table.Column0.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw Field row + /// Reads a raw Field row or returns false if the row doesn't exist /// /// Row ID - /// - /// + /// Row data /// - internal uint ReadFieldRow(uint rid, out int flags, out uint name) { + public bool TryReadFieldRow(uint rid, out RawFieldRow row) { var table = FieldTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - flags = reader.ReadUInt16(); - name = columns[1].Read(reader); - return columns[2].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawFieldRow( + reader.Unsafe_ReadUInt16(), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw MethodPtr row + /// Reads a raw MethodPtr row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawMethodPtrRow ReadMethodPtrRow(uint rid) { + /// Row data + /// + public bool TryReadMethodPtrRow(uint rid, out RawMethodPtrRow row) { var table = MethodPtrTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawMethodPtrRow(columns[0].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw Method row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawMethodRow ReadMethodRow(uint rid) { - var table = MethodTable; - if (table.IsInvalidRID(rid)) - return null; - var mrr = methodRowReader; - if (mrr != null) { - var row = mrr.ReadRow(rid); - if (row != null) - return row; + if (table.IsInvalidRID(rid)) { + row = default; + return false; } -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawMethodRow(reader.ReadUInt32(), - reader.ReadUInt16(), - reader.ReadUInt16(), - columns[3].Read(reader), - columns[4].Read(reader), - columns[5].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawMethodPtrRow(table.Column0.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw Method row but not ParamList + /// Reads a raw Method row or returns false if the row doesn't exist /// /// Row ID - /// - /// - /// - /// + /// Row data /// - internal uint ReadMethodRow(uint rid, out RVA rva, out int implFlags, out int flags, out uint name) { + public bool TryReadMethodRow(uint rid, out RawMethodRow row) { var table = MethodTable; - var mrr = methodRowReader; - if (mrr != null) { - var row = mrr.ReadRow(rid); - if (row != null) { - rva = (RVA)row.RVA; - implFlags = row.ImplFlags; - flags = row.Flags; - name = row.Name; - return row.Signature; - } + if (table.IsInvalidRID(rid)) { + row = default; + return false; } -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - rva = (RVA)reader.ReadUInt32(); - implFlags = reader.ReadUInt16(); - flags = reader.ReadUInt16(); - name = columns[3].Read(reader); - return columns[4].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + var mrr = methodRowReader; + if (mrr is not null && mrr.TryReadRow(rid, out row)) + return true; + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawMethodRow( + reader.Unsafe_ReadUInt32(), + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt16(), + table.Column3.Unsafe_Read24(ref reader), + table.Column4.Unsafe_Read24(ref reader), + table.Column5.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw ParamPtr row + /// Reads a raw ParamPtr row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawParamPtrRow ReadParamPtrRow(uint rid) { + /// Row data + /// + public bool TryReadParamPtrRow(uint rid, out RawParamPtrRow row) { var table = ParamPtrTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawParamPtrRow(columns[0].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw Param row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawParamRow ReadParamRow(uint rid) { - var table = ParamTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawParamRow(reader.ReadUInt16(), - reader.ReadUInt16(), - columns[2].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawParamPtrRow(table.Column0.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw Param row + /// Reads a raw Param row or returns false if the row doesn't exist /// /// Row ID - /// - /// + /// Row data /// - internal uint ReadParamRow(uint rid, out int flags, out ushort sequence) { + public bool TryReadParamRow(uint rid, out RawParamRow row) { var table = ParamTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - flags = reader.ReadUInt16(); - sequence = reader.ReadUInt16(); - return columns[2].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw InterfaceImpl row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawInterfaceImplRow ReadInterfaceImplRow(uint rid) { - var table = InterfaceImplTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawInterfaceImplRow(columns[0].Read(reader), - columns[1].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawParamRow( + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt16(), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw InterfaceImpl row + /// Reads a raw InterfaceImpl row or returns false if the row doesn't exist /// /// Row ID - /// The Interface field - internal uint ReadInterfaceImplRow2(uint rid) { + /// Row data + /// + public bool TryReadInterfaceImplRow(uint rid, out RawInterfaceImplRow row) { var table = InterfaceImplTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - reader.Position += columns[0].Size; - return columns[1].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw MemberRef row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawMemberRefRow ReadMemberRefRow(uint rid) { - var table = MemberRefTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawMemberRefRow(columns[0].Read(reader), - columns[1].Read(reader), - columns[2].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawInterfaceImplRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw MemberRef row + /// Reads a raw MemberRef row or returns false if the row doesn't exist /// /// Row ID - /// - /// + /// Row data /// - internal uint ReadMemberRefRow(uint rid, out uint @class, out uint name) { + public bool TryReadMemberRefRow(uint rid, out RawMemberRefRow row) { var table = MemberRefTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - @class = columns[0].Read(reader); - name = columns[1].Read(reader); - return columns[2].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw Constant row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawConstantRow ReadConstantRow(uint rid) { - var table = ConstantTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawConstantRow(reader.ReadByte(), - reader.ReadByte(), - columns[1].Read(reader), - columns[2].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawMemberRefRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw Constant row + /// Reads a raw Constant row or returns false if the row doesn't exist /// /// Row ID - /// + /// Row data /// - internal uint ReadConstantRow(uint rid, out ElementType type) { + public bool TryReadConstantRow(uint rid, out RawConstantRow row) { var table = ConstantTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - type = (ElementType)reader.ReadByte(); - reader.Position += 1 + columns[1].Size; - return columns[2].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawConstantRow( + reader.Unsafe_ReadByte(), + reader.Unsafe_ReadByte(), + table.Column2.Unsafe_Read24(ref reader), + table.Column3.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw CustomAttribute row + /// Reads a raw CustomAttribute row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawCustomAttributeRow ReadCustomAttributeRow(uint rid) { + /// Row data + /// + public bool TryReadCustomAttributeRow(uint rid, out RawCustomAttributeRow row) { var table = CustomAttributeTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawCustomAttributeRow(columns[0].Read(reader), - columns[1].Read(reader), - columns[2].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawCustomAttributeRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw FieldMarshal row + /// Reads a raw FieldMarshal row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawFieldMarshalRow ReadFieldMarshalRow(uint rid) { + /// Row data + /// + public bool TryReadFieldMarshalRow(uint rid, out RawFieldMarshalRow row) { var table = FieldMarshalTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawFieldMarshalRow(columns[0].Read(reader), - columns[1].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw DeclSecurity row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawDeclSecurityRow ReadDeclSecurityRow(uint rid) { - var table = DeclSecurityTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawDeclSecurityRow(reader.ReadInt16(), - columns[1].Read(reader), - columns[2].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawFieldMarshalRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw DeclSecurity row + /// Reads a raw DeclSecurity row or returns false if the row doesn't exist /// /// Row ID - /// + /// Row data /// - internal uint ReadDeclSecurityRow(uint rid, out SecurityAction action) { + public bool TryReadDeclSecurityRow(uint rid, out RawDeclSecurityRow row) { var table = DeclSecurityTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - action = (SecurityAction)reader.ReadInt16(); - reader.Position += columns[1].Size; - return columns[2].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw ClassLayout row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawClassLayoutRow ReadClassLayoutRow(uint rid) { - var table = ClassLayoutTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawClassLayoutRow(reader.ReadUInt16(), - reader.ReadUInt32(), - columns[2].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawDeclSecurityRow( + (short)reader.Unsafe_ReadUInt16(), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw ClassLayout row + /// Reads a raw ClassLayout row or returns false if the row doesn't exist /// /// Row ID - /// + /// Row data /// - internal uint ReadClassLayoutRow(uint rid, out ushort packingSize) { + public bool TryReadClassLayoutRow(uint rid, out RawClassLayoutRow row) { var table = ClassLayoutTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - packingSize = reader.ReadUInt16(); - return reader.ReadUInt32(); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw FieldLayout row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawFieldLayoutRow ReadFieldLayoutRow(uint rid) { - var table = FieldLayoutTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawFieldLayoutRow(reader.ReadUInt32(), - columns[1].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawClassLayoutRow( + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt32(), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw FieldLayout row + /// Reads a raw FieldLayout row or returns false if the row doesn't exist /// /// Row ID + /// Row data /// - internal uint? ReadFieldLayoutRow2(uint rid) { + public bool TryReadFieldLayoutRow(uint rid, out RawFieldLayoutRow row) { var table = FieldLayoutTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return reader.ReadUInt32(); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw StandAloneSig row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawStandAloneSigRow ReadStandAloneSigRow(uint rid) { - var table = StandAloneSigTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawStandAloneSigRow(columns[0].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawFieldLayoutRow( + reader.Unsafe_ReadUInt32(), + table.Column1.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw StandAloneSig row + /// Reads a raw StandAloneSig row or returns false if the row doesn't exist /// /// Row ID + /// Row data /// - internal uint ReadStandAloneSigRow2(uint rid) { + public bool TryReadStandAloneSigRow(uint rid, out RawStandAloneSigRow row) { var table = StandAloneSigTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return columns[0].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawStandAloneSigRow(table.Column0.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw EventMap row + /// Reads a raw EventMap row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawEventMapRow ReadEventMapRow(uint rid) { + /// Row data + /// + public bool TryReadEventMapRow(uint rid, out RawEventMapRow row) { var table = EventMapTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawEventMapRow(columns[0].Read(reader), - columns[1].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawEventMapRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw EventPtr row + /// Reads a raw EventPtr row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawEventPtrRow ReadEventPtrRow(uint rid) { + /// Row data + /// + public bool TryReadEventPtrRow(uint rid, out RawEventPtrRow row) { var table = EventPtrTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawEventPtrRow(columns[0].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw Event row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawEventRow ReadEventRow(uint rid) { - var table = EventTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawEventRow(reader.ReadUInt16(), - columns[1].Read(reader), - columns[2].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawEventPtrRow(table.Column0.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw Event row + /// Reads a raw Event row or returns false if the row doesn't exist /// /// Row ID - /// - /// + /// Row data /// - internal uint ReadEventRow(uint rid, out int eventFlags, out uint name) { + public bool TryReadEventRow(uint rid, out RawEventRow row) { var table = EventTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - eventFlags = reader.ReadUInt16(); - name = columns[1].Read(reader); - return columns[2].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawEventRow( + reader.Unsafe_ReadUInt16(), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw PropertyMap row + /// Reads a raw PropertyMap row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawPropertyMapRow ReadPropertyMapRow(uint rid) { + /// Row data + /// + public bool TryReadPropertyMapRow(uint rid, out RawPropertyMapRow row) { var table = PropertyMapTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawPropertyMapRow(columns[0].Read(reader), - columns[1].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawPropertyMapRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw PropertyPtr row + /// Reads a raw PropertyPtr row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawPropertyPtrRow ReadPropertyPtrRow(uint rid) { + /// Row data + /// + public bool TryReadPropertyPtrRow(uint rid, out RawPropertyPtrRow row) { var table = PropertyPtrTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawPropertyPtrRow(columns[0].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw Property row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawPropertyRow ReadPropertyRow(uint rid) { - var table = PropertyTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawPropertyRow(reader.ReadUInt16(), - columns[1].Read(reader), - columns[2].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawPropertyPtrRow(table.Column0.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw Property row + /// Reads a raw Property row or returns false if the row doesn't exist /// /// Row ID - /// - /// + /// Row data /// - internal uint ReadPropertyRow(uint rid, out int propFlags, out uint name) { + public bool TryReadPropertyRow(uint rid, out RawPropertyRow row) { var table = PropertyTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - propFlags = reader.ReadUInt16(); - name = columns[1].Read(reader); - return columns[2].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw MethodSemantics row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawMethodSemanticsRow ReadMethodSemanticsRow(uint rid) { - var table = MethodSemanticsTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawMethodSemanticsRow(reader.ReadUInt16(), - columns[1].Read(reader), - columns[2].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawPropertyRow( + reader.Unsafe_ReadUInt16(), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw MethodSemantics row + /// Reads a raw MethodSemantics row or returns false if the row doesn't exist /// /// Row ID - /// + /// Row data /// - internal uint ReadMethodSemanticsRow(uint rid, out MethodSemanticsAttributes semantic) { + public bool TryReadMethodSemanticsRow(uint rid, out RawMethodSemanticsRow row) { var table = MethodSemanticsTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - semantic = (MethodSemanticsAttributes)reader.ReadUInt16(); - return columns[1].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw MethodImpl row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawMethodImplRow ReadMethodImplRow(uint rid) { - var table = MethodImplTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawMethodImplRow(columns[0].Read(reader), - columns[1].Read(reader), - columns[2].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawMethodSemanticsRow( + reader.Unsafe_ReadUInt16(), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw MethodImpl row + /// Reads a raw MethodImpl row or returns false if the row doesn't exist /// /// Row ID - /// + /// Row data /// - internal uint ReadMethodImplRow(uint rid, out uint methodBody) { + public bool TryReadMethodImplRow(uint rid, out RawMethodImplRow row) { var table = MethodImplTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - reader.Position += columns[0].Size; - methodBody = columns[1].Read(reader); - return columns[2].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw ModuleRef row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawModuleRefRow ReadModuleRefRow(uint rid) { - var table = ModuleRefTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawModuleRefRow(columns[0].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawMethodImplRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw ModuleRef row + /// Reads a raw ModuleRef row or returns false if the row doesn't exist /// /// Row ID + /// Row data /// - internal uint ReadModuleRefRow2(uint rid) { + public bool TryReadModuleRefRow(uint rid, out RawModuleRefRow row) { var table = ModuleRefTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return columns[0].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw TypeSpec row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawTypeSpecRow ReadTypeSpecRow(uint rid) { - var table = TypeSpecTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawTypeSpecRow(columns[0].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawModuleRefRow(table.Column0.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw TypeSpec row + /// Reads a raw TypeSpec row or returns false if the row doesn't exist /// /// Row ID + /// Row data /// - internal uint ReadTypeSpecRow2(uint rid) { + public bool TryReadTypeSpecRow(uint rid, out RawTypeSpecRow row) { var table = TypeSpecTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return columns[0].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw ImplMap row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawImplMapRow ReadImplMapRow(uint rid) { - var table = ImplMapTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawImplMapRow(reader.ReadUInt16(), - columns[1].Read(reader), - columns[2].Read(reader), - columns[3].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawTypeSpecRow(table.Column0.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw ImplMap row + /// Reads a raw ImplMap row or returns false if the row doesn't exist /// /// Row ID - /// - /// + /// Row data /// - internal uint ReadImplMapRow(uint rid, out int attributes, out uint name) { + public bool TryReadImplMapRow(uint rid, out RawImplMapRow row) { var table = ImplMapTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - attributes = reader.ReadUInt16(); - reader.Position += columns[1].Size; - name = columns[2].Read(reader); - return columns[3].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw FieldRVA row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawFieldRVARow ReadFieldRVARow(uint rid) { - var table = FieldRVATable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawFieldRVARow(reader.ReadUInt32(), - columns[1].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawImplMapRow( + reader.Unsafe_ReadUInt16(), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader), + table.Column3.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw FieldRVA row + /// Reads a raw FieldRVA row or returns false if the row doesn't exist /// /// Row ID - /// + /// Row data /// - internal bool ReadFieldRVARow(uint rid, out RVA rva) { + public bool TryReadFieldRVARow(uint rid, out RawFieldRVARow row) { var table = FieldRVATable; if (table.IsInvalidRID(rid)) { - rva = 0; + row = default; return false; } -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - rva = (RVA)reader.ReadUInt32(); + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawFieldRVARow( + reader.Unsafe_ReadUInt32(), + table.Column1.Unsafe_Read24(ref reader)); return true; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif } /// - /// Reads a raw ENCLog row + /// Reads a raw ENCLog row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawENCLogRow ReadENCLogRow(uint rid) { + /// Row data + /// + public bool TryReadENCLogRow(uint rid, out RawENCLogRow row) { var table = ENCLogTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - return new RawENCLogRow(reader.ReadUInt32(), - reader.ReadUInt32()); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawENCLogRow( + reader.Unsafe_ReadUInt32(), + reader.Unsafe_ReadUInt32()); + return true; } /// - /// Reads a raw ENCMap row + /// Reads a raw ENCMap row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawENCMapRow ReadENCMapRow(uint rid) { + /// Row data + /// + public bool TryReadENCMapRow(uint rid, out RawENCMapRow row) { var table = ENCMapTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - return new RawENCMapRow(reader.ReadUInt32()); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - - /// - /// Reads a raw Assembly row - /// - /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawAssemblyRow ReadAssemblyRow(uint rid) { - var table = AssemblyTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawAssemblyRow(reader.ReadUInt32(), - reader.ReadUInt16(), - reader.ReadUInt16(), - reader.ReadUInt16(), - reader.ReadUInt16(), - reader.ReadUInt32(), - columns[6].Read(reader), - columns[7].Read(reader), - columns[8].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawENCMapRow(reader.Unsafe_ReadUInt32()); + return true; } /// - /// Reads a raw Assembly row + /// Reads a raw Assembly row or returns false if the row doesn't exist /// /// Row ID - /// - /// - /// - /// - /// + /// Row data /// - internal uint ReadAssemblyRow(uint rid, out AssemblyHashAlgorithm hashAlgId, out Version version, out int attributes, out uint publicKey, out uint name) { + public bool TryReadAssemblyRow(uint rid, out RawAssemblyRow row) { var table = AssemblyTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - hashAlgId = (AssemblyHashAlgorithm)reader.ReadUInt32(); - version = new Version(reader.ReadUInt16(), reader.ReadUInt16(), reader.ReadUInt16(), reader.ReadUInt16()); - attributes = reader.ReadInt32(); - publicKey = columns[6].Read(reader); - name = columns[7].Read(reader); - return columns[8].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawAssemblyRow( + reader.Unsafe_ReadUInt32(), + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt32(), + table.Column6.Unsafe_Read24(ref reader), + table.Column7.Unsafe_Read24(ref reader), + table.Column8.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw AssemblyProcessor row + /// Reads a raw AssemblyProcessor row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawAssemblyProcessorRow ReadAssemblyProcessorRow(uint rid) { + /// Row data + /// + public bool TryReadAssemblyProcessorRow(uint rid, out RawAssemblyProcessorRow row) { var table = AssemblyProcessorTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - return new RawAssemblyProcessorRow(reader.ReadUInt32()); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawAssemblyProcessorRow(reader.Unsafe_ReadUInt32()); + return true; } /// - /// Reads a raw AssemblyOS row + /// Reads a raw AssemblyOS row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawAssemblyOSRow ReadAssemblyOSRow(uint rid) { + /// Row data + /// + public bool TryReadAssemblyOSRow(uint rid, out RawAssemblyOSRow row) { var table = AssemblyOSTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - return new RawAssemblyOSRow(reader.ReadUInt32(), - reader.ReadUInt32(), - reader.ReadUInt32()); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawAssemblyOSRow( + reader.Unsafe_ReadUInt32(), + reader.Unsafe_ReadUInt32(), + reader.Unsafe_ReadUInt32()); + return true; } /// - /// Reads a raw AssemblyRef row + /// Reads a raw AssemblyRef row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawAssemblyRefRow ReadAssemblyRefRow(uint rid) { + /// Row data + /// + public bool TryReadAssemblyRefRow(uint rid, out RawAssemblyRefRow row) { var table = AssemblyRefTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawAssemblyRefRow(reader.ReadUInt16(), - reader.ReadUInt16(), - reader.ReadUInt16(), - reader.ReadUInt16(), - reader.ReadUInt32(), - columns[5].Read(reader), - columns[6].Read(reader), - columns[7].Read(reader), - columns[8].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawAssemblyRefRow( + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt32(), + table.Column5.Unsafe_Read24(ref reader), + table.Column6.Unsafe_Read24(ref reader), + table.Column7.Unsafe_Read24(ref reader), + table.Column8.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw AssemblyRef row + /// Reads a raw AssemblyRefProcessor row or returns false if the row doesn't exist /// /// Row ID - /// - /// - /// - /// - /// + /// Row data /// - internal uint ReadAssemblyRefRow(uint rid, out Version version, out int attributes, out uint publicKeyOrToken, out uint name, out uint culture) { - var table = AssemblyRefTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - version = new Version(reader.ReadUInt16(), reader.ReadUInt16(), reader.ReadUInt16(), reader.ReadUInt16()); - attributes = reader.ReadInt32(); - publicKeyOrToken = columns[5].Read(reader); - name = columns[6].Read(reader); - culture = columns[7].Read(reader); - return columns[8].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + public bool TryReadAssemblyRefProcessorRow(uint rid, out RawAssemblyRefProcessorRow row) { + var table = AssemblyRefProcessorTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawAssemblyRefProcessorRow( + reader.Unsafe_ReadUInt32(), + table.Column1.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw AssemblyRefProcessor row + /// Reads a raw AssemblyRefOS row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawAssemblyRefProcessorRow ReadAssemblyRefProcessorRow(uint rid) { - var table = AssemblyRefProcessorTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawAssemblyRefProcessorRow(reader.ReadUInt32(), - columns[1].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + /// Row data + /// + public bool TryReadAssemblyRefOSRow(uint rid, out RawAssemblyRefOSRow row) { + var table = AssemblyRefOSTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawAssemblyRefOSRow( + reader.Unsafe_ReadUInt32(), + reader.Unsafe_ReadUInt32(), + reader.Unsafe_ReadUInt32(), + table.Column3.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw AssemblyRefOS row + /// Reads a raw File row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawAssemblyRefOSRow ReadAssemblyRefOSRow(uint rid) { - var table = AssemblyRefOSTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawAssemblyRefOSRow(reader.ReadUInt32(), - reader.ReadUInt32(), - reader.ReadUInt32(), - columns[3].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + /// Row data + /// + public bool TryReadFileRow(uint rid, out RawFileRow row) { + var table = FileTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawFileRow( + reader.Unsafe_ReadUInt32(), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw File row + /// Reads a raw ExportedType row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawFileRow ReadFileRow(uint rid) { - var table = FileTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawFileRow(reader.ReadUInt32(), - columns[1].Read(reader), - columns[2].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + /// Row data + /// + public bool TryReadExportedTypeRow(uint rid, out RawExportedTypeRow row) { + var table = ExportedTypeTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawExportedTypeRow( + reader.Unsafe_ReadUInt32(), + reader.Unsafe_ReadUInt32(), + table.Column2.Unsafe_Read24(ref reader), + table.Column3.Unsafe_Read24(ref reader), + table.Column4.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw File row + /// Reads a raw ManifestResource row or returns false if the row doesn't exist /// /// Row ID - /// - /// + /// Row data /// - internal uint ReadFileRow(uint rid, out int attributes, out uint name) { - var table = FileTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - attributes = reader.ReadInt32(); - name = columns[1].Read(reader); - return columns[2].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + public bool TryReadManifestResourceRow(uint rid, out RawManifestResourceRow row) { + var table = ManifestResourceTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawManifestResourceRow( + reader.Unsafe_ReadUInt32(), + reader.Unsafe_ReadUInt32(), + table.Column2.Unsafe_Read24(ref reader), + table.Column3.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw ExportedType row + /// Reads a raw NestedClass row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawExportedTypeRow ReadExportedTypeRow(uint rid) { - var table = ExportedTypeTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawExportedTypeRow(reader.ReadUInt32(), - reader.ReadUInt32(), - columns[2].Read(reader), - columns[3].Read(reader), - columns[4].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + /// Row data + /// + public bool TryReadNestedClassRow(uint rid, out RawNestedClassRow row) { + var table = NestedClassTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawNestedClassRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw ExportedType row + /// Reads a raw GenericParam row or returns false if the row doesn't exist /// /// Row ID - /// - /// - /// - /// + /// Row data /// - internal uint ReadExportedTypeRow(uint rid, out int attributes, out uint typeDefId, out uint name, out uint @namespace) { - var table = ExportedTypeTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - attributes = reader.ReadInt32(); - typeDefId = reader.ReadUInt32(); - name = columns[2].Read(reader); - @namespace = columns[3].Read(reader); - return columns[4].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + public bool TryReadGenericParamRow(uint rid, out RawGenericParamRow row) { + var table = GenericParamTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + if (table.Column4 is null) { + row = new RawGenericParamRow( + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt16(), + table.Column2.Unsafe_Read24(ref reader), + table.Column3.Unsafe_Read24(ref reader)); + return true; + } + else { + row = new RawGenericParamRow( + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt16(), + table.Column2.Unsafe_Read24(ref reader), + table.Column3.Unsafe_Read24(ref reader), + table.Column4.Unsafe_Read24(ref reader)); + return true; + } } /// - /// Reads a raw ManifestResource row + /// Reads a raw MethodSpec row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawManifestResourceRow ReadManifestResourceRow(uint rid) { - var table = ManifestResourceTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawManifestResourceRow(reader.ReadUInt32(), - reader.ReadUInt32(), - columns[2].Read(reader), - columns[3].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + /// Row data + /// + public bool TryReadMethodSpecRow(uint rid, out RawMethodSpecRow row) { + var table = MethodSpecTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawMethodSpecRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw ManifestResource row + /// Reads a raw GenericParamConstraint row or returns false if the row doesn't exist /// /// Row ID - /// - /// - /// + /// Row data /// - internal uint ReadManifestResourceRow(uint rid, out uint offset, out int attributes, out uint name) { - var table = ManifestResourceTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - offset = reader.ReadUInt32(); - attributes = reader.ReadInt32(); - name = columns[2].Read(reader); - return columns[3].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + public bool TryReadGenericParamConstraintRow(uint rid, out RawGenericParamConstraintRow row) { + var table = GenericParamConstraintTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawGenericParamConstraintRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw NestedClass row + /// Reads a raw Document row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawNestedClassRow ReadNestedClassRow(uint rid) { - var table = NestedClassTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawNestedClassRow(columns[0].Read(reader), - columns[1].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + /// Row data + /// + public bool TryReadDocumentRow(uint rid, out RawDocumentRow row) { + var table = DocumentTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawDocumentRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader), + table.Column3.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw NestedClass row + /// Reads a raw MethodDebugInformation row or returns false if the row doesn't exist /// /// Row ID + /// Row data /// - internal uint ReadNestedClassRow2(uint rid) { - var table = NestedClassTable; - if (table.IsInvalidRID(rid)) - return 0; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - reader.Position += columns[0].Size; - return columns[1].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + public bool TryReadMethodDebugInformationRow(uint rid, out RawMethodDebugInformationRow row) { + var table = MethodDebugInformationTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawMethodDebugInformationRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw GenericParam row + /// Reads a raw LocalScope row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawGenericParamRow ReadGenericParamRow(uint rid) { - var table = GenericParamTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - if (columns.Count == 4) { - return new RawGenericParamRow(reader.ReadUInt16(), - reader.ReadUInt16(), - columns[2].Read(reader), - columns[3].Read(reader)); + /// Row data + /// + public bool TryReadLocalScopeRow(uint rid, out RawLocalScopeRow row) { + var table = LocalScopeTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; } - return new RawGenericParamRow(reader.ReadUInt16(), - reader.ReadUInt16(), - columns[2].Read(reader), - columns[3].Read(reader), - columns[4].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawLocalScopeRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader), + table.Column3.Unsafe_Read24(ref reader), + reader.Unsafe_ReadUInt32(), + reader.Unsafe_ReadUInt32()); + return true; } /// - /// Reads a raw GenericParam row + /// Reads a raw LocalVariable row or returns false if the row doesn't exist /// /// Row ID - /// - /// - /// + /// Row data /// - internal uint ReadGenericParamRow(uint rid, out ushort number, out int attributes, out uint name) { - var table = GenericParamTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - number = reader.ReadUInt16(); - attributes = reader.ReadUInt16(); - reader.Position += columns[2].Size; - name = columns[3].Read(reader); - if (columns.Count == 4) - return 0; - return columns[4].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + public bool TryReadLocalVariableRow(uint rid, out RawLocalVariableRow row) { + var table = LocalVariableTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawLocalVariableRow( + reader.Unsafe_ReadUInt16(), + reader.Unsafe_ReadUInt16(), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw MethodSpec row + /// Reads a raw LocalConstant row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawMethodSpecRow ReadMethodSpecRow(uint rid) { - var table = MethodSpecTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawMethodSpecRow(columns[0].Read(reader), - columns[1].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + /// Row data + /// + public bool TryReadLocalConstantRow(uint rid, out RawLocalConstantRow row) { + var table = LocalConstantTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawLocalConstantRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw MethodSpec row + /// Reads a raw ImportScope row or returns false if the row doesn't exist /// /// Row ID - /// + /// Row data /// - internal uint ReadMethodSpecRow(uint rid, out uint method) { - var table = MethodSpecTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - method = columns[0].Read(reader); - return columns[1].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + public bool TryReadImportScopeRow(uint rid, out RawImportScopeRow row) { + var table = ImportScopeTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawImportScopeRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw GenericParamConstraint row + /// Reads a raw StateMachineMethod row or returns false if the row doesn't exist /// /// Row ID - /// The row or null if table doesn't exist or if is invalid - public RawGenericParamConstraintRow ReadGenericParamConstraintRow(uint rid) { - var table = GenericParamConstraintTable; - if (table.IsInvalidRID(rid)) - return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - return new RawGenericParamConstraintRow(columns[0].Read(reader), - columns[1].Read(reader)); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + /// Row data + /// + public bool TryReadStateMachineMethodRow(uint rid, out RawStateMachineMethodRow row) { + var table = StateMachineMethodTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawStateMachineMethodRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader)); + return true; } /// - /// Reads a raw GenericParamConstraint row + /// Reads a raw CustomDebugInformation row or returns false if the row doesn't exist /// /// Row ID + /// Row data /// - internal uint ReadGenericParamConstraintRow2(uint rid) { - var table = GenericParamConstraintTable; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - var columns = table.TableInfo.Columns; - reader.Position += columns[0].Size; - return columns[1].Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + public bool TryReadCustomDebugInformationRow(uint rid, out RawCustomDebugInformationRow row) { + var table = CustomDebugInformationTable; + if (table.IsInvalidRID(rid)) { + row = default; + return false; + } + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize; + row = new RawCustomDebugInformationRow( + table.Column0.Unsafe_Read24(ref reader), + table.Column1.Unsafe_Read24(ref reader), + table.Column2.Unsafe_Read24(ref reader)); + return true; } /// @@ -1698,9 +1125,8 @@ internal uint ReadGenericParamConstraintRow2(uint rid) { /// Column index in /// Result is put here or 0 if we return false /// true if we could read the column, false otherwise - public bool ReadColumn(MDTable table, uint rid, int colIndex, out uint value) { - return ReadColumn(table, rid, table.TableInfo.Columns[colIndex], out value); - } + public bool TryReadColumn(MDTable table, uint rid, int colIndex, out uint value) => + TryReadColumn(table, rid, table.TableInfo.Columns[colIndex], out value); /// /// Reads a column @@ -1710,45 +1136,35 @@ public bool ReadColumn(MDTable table, uint rid, int colIndex, out uint value) { /// Column /// Result is put here or 0 if we return false /// true if we could read the column, false otherwise - public bool ReadColumn(MDTable table, uint rid, ColumnInfo column, out uint value) { + public bool TryReadColumn(MDTable table, uint rid, ColumnInfo column, out uint value) { if (table.IsInvalidRID(rid)) { value = 0; return false; } var cr = columnReader; - if (cr != null && cr.ReadColumn(table, rid, column, out value)) + if (cr is not null && cr.ReadColumn(table, rid, column, out value)) return true; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(table, rid); - reader.Position += column.Offset; - value = column.Read(reader); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize + (uint)column.Offset; + value = column.Read(ref reader); return true; } - /// - /// Reads a column - /// - /// The table - /// Row ID - /// Column - /// Result is put here or 0 if we return false - /// true if we could read the column, false otherwise - internal bool ReadColumn_NoLock(MDTable table, uint rid, ColumnInfo column, out uint value) { + internal bool TryReadColumn24(MDTable table, uint rid, int colIndex, out uint value) => + TryReadColumn24(table, rid, table.TableInfo.Columns[colIndex], out value); + + internal bool TryReadColumn24(MDTable table, uint rid, ColumnInfo column, out uint value) { + Debug.Assert(column.Size == 2 || column.Size == 4); if (table.IsInvalidRID(rid)) { value = 0; return false; } var cr = columnReader; - if (cr != null && cr.ReadColumn(table, rid, column, out value)) + if (cr is not null && cr.ReadColumn(table, rid, column, out value)) return true; - var reader = GetReader_NoLock(table, rid); - reader.Position += column.Offset; - value = column.Read(reader); + var reader = table.DataReader; + reader.Position = (rid - 1) * (uint)table.TableInfo.RowSize + (uint)column.Offset; + value = column.Size == 2 ? reader.Unsafe_ReadUInt16() : reader.Unsafe_ReadUInt32(); return true; } } diff --git a/src/DotNet/MD/USStream.cs b/src/DotNet/MD/USStream.cs index 4acd0750e..a6be17e1f 100644 --- a/src/DotNet/MD/USStream.cs +++ b/src/DotNet/MD/USStream.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using dnlib.IO; namespace dnlib.DotNet.MD { @@ -13,8 +13,8 @@ public USStream() { } /// - public USStream(IImageStream imageStream, StreamHeader streamHeader) - : base(imageStream, streamHeader) { + public USStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader) + : base(mdReaderFactory, metadataBaseOffset, streamHeader) { } /// @@ -27,17 +27,14 @@ public string Read(uint offset) { return string.Empty; if (!IsValidOffset(offset)) return null; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - var reader = GetReader_NoLock(offset); - uint length; - if (!reader.ReadCompressedUInt32(out length)) + var reader = dataReader; + reader.Position = offset; + if (!reader.TryReadCompressedUInt32(out uint length)) return null; - if (reader.Position + length < length || reader.Position + length > reader.Length) + if (!reader.CanRead(length)) return null; try { - return reader.ReadString((int)(length / 2)); + return reader.ReadUtf16String((int)(length / 2)); } catch (OutOfMemoryException) { throw; @@ -47,9 +44,6 @@ public string Read(uint offset) { // a string. If so, return an empty string. return string.Empty; } -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif } /// @@ -58,8 +52,6 @@ public string Read(uint offset) { /// /// Offset of unicode string /// The string - public string ReadNoNull(uint offset) { - return Read(offset) ?? string.Empty; - } + public string ReadNoNull(uint offset) => Read(offset) ?? string.Empty; } } diff --git a/src/DotNet/MDToken.cs b/src/DotNet/MDToken.cs index d898adef3..3663039d9 100644 --- a/src/DotNet/MDToken.cs +++ b/src/DotNet/MDToken.cs @@ -1,15 +1,15 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Diagnostics; using dnlib.DotNet.MD; namespace dnlib.DotNet { /// - /// MetaData token + /// Metadata token /// [DebuggerDisplay("{Table} {Rid}")] - public struct MDToken : IEquatable, IComparable { + public readonly struct MDToken : IEquatable, IComparable { /// /// Mask to get the rid from a raw metadata token /// @@ -30,38 +30,28 @@ public struct MDToken : IEquatable, IComparable { /// /// Returns the table type /// - public Table Table { - get { return ToTable(token); } - } + public Table Table => ToTable(token); /// /// Returns the row id /// - public uint Rid { - get { return ToRID(token); } - } + public uint Rid => ToRID(token); /// /// Returns the raw token /// - public uint Raw { - get { return token; } - } + public uint Raw => token; /// /// Returns true if it's a null token /// - public bool IsNull { - get { return Rid == 0; } - } + public bool IsNull => Rid == 0; /// /// Constructor /// /// Raw token - public MDToken(uint token) { - this.token = token; - } + public MDToken(uint token) => this.token = token; /// /// Constructor @@ -94,90 +84,62 @@ public MDToken(Table table, int rid) /// /// A raw metadata token /// A rid - public static uint ToRID(uint token) { - return token & RID_MASK; - } + public static uint ToRID(uint token) => token & RID_MASK; /// /// Returns the rid (row ID) /// /// A raw metadata token /// A rid - public static uint ToRID(int token) { - return ToRID((uint)token); - } + public static uint ToRID(int token) => ToRID((uint)token); /// /// Returns the table /// /// A raw metadata token /// A metadata table index - public static Table ToTable(uint token) { - return (Table)(token >> TABLE_SHIFT); - } + public static Table ToTable(uint token) => (Table)(token >> TABLE_SHIFT); /// /// Returns the table /// /// A raw metadata token /// A metadata table index - public static Table ToTable(int token) { - return ToTable((uint)token); - } + public static Table ToTable(int token) => ToTable((uint)token); /// /// Gets the token as a raw 32-bit signed integer /// - public int ToInt32() { - return (int)token; - } + public int ToInt32() => (int)token; /// /// Gets the token as a raw 32-bit unsigned integer /// - public uint ToUInt32() { - return token; - } + public uint ToUInt32() => token; /// Overloaded operator - public static bool operator ==(MDToken left, MDToken right) { - return left.CompareTo(right) == 0; - } + public static bool operator ==(MDToken left, MDToken right) => left.CompareTo(right) == 0; /// Overloaded operator - public static bool operator !=(MDToken left, MDToken right) { - return left.CompareTo(right) != 0; - } + public static bool operator !=(MDToken left, MDToken right) => left.CompareTo(right) != 0; /// Overloaded operator - public static bool operator <(MDToken left, MDToken right) { - return left.CompareTo(right) < 0; - } + public static bool operator <(MDToken left, MDToken right) => left.CompareTo(right) < 0; /// Overloaded operator - public static bool operator >(MDToken left, MDToken right) { - return left.CompareTo(right) > 0; - } + public static bool operator >(MDToken left, MDToken right) => left.CompareTo(right) > 0; /// Overloaded operator - public static bool operator <=(MDToken left, MDToken right) { - return left.CompareTo(right) <= 0; - } + public static bool operator <=(MDToken left, MDToken right) => left.CompareTo(right) <= 0; /// Overloaded operator - public static bool operator >=(MDToken left, MDToken right) { - return left.CompareTo(right) >= 0; - } + public static bool operator >=(MDToken left, MDToken right) => left.CompareTo(right) >= 0; /// - public int CompareTo(MDToken other) { - return token.CompareTo(other.token); - } + public int CompareTo(MDToken other) => token.CompareTo(other.token); /// - public bool Equals(MDToken other) { - return CompareTo(other) == 0; - } + public bool Equals(MDToken other) => CompareTo(other) == 0; /// public override bool Equals(object obj) { @@ -187,13 +149,9 @@ public override bool Equals(object obj) { } /// - public override int GetHashCode() { - return (int)token; - } + public override int GetHashCode() => (int)token; /// - public override string ToString() { - return string.Format("{0:X8}", token); - } + public override string ToString() => token.ToString("X8"); } } diff --git a/src/DotNet/ManifestResource.cs b/src/DotNet/ManifestResource.cs index 896d9048e..c907349a2 100644 --- a/src/DotNet/ManifestResource.cs +++ b/src/DotNet/ManifestResource.cs @@ -1,43 +1,41 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// /// A high-level representation of a row in the ManifestResource table /// [DebuggerDisplay("{Offset} {Name.String} {Implementation}")] - public abstract class ManifestResource : IHasCustomAttribute { + public abstract class ManifestResource : IHasCustomAttribute, IHasCustomDebugInformation { /// /// The row id in its table /// protected uint rid; /// - public MDToken MDToken { - get { return new MDToken(Table.ManifestResource, rid); } - } + public MDToken MDToken => new MDToken(Table.ManifestResource, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 18; } - } + public int HasCustomAttributeTag => 18; /// /// From column ManifestResource.Offset /// public uint Offset { - get { return offset; } - set { offset = value; } + get => offset; + set => offset = value; } /// protected uint offset; @@ -46,8 +44,8 @@ public uint Offset { /// From column ManifestResource.Flags /// public ManifestResourceAttributes Flags { - get { return (ManifestResourceAttributes)attributes; } - set { attributes = (int)value; } + get => (ManifestResourceAttributes)attributes; + set => attributes = (int)value; } /// Attributes protected int attributes; @@ -56,8 +54,8 @@ public ManifestResourceAttributes Flags { /// From column ManifestResource.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -66,8 +64,8 @@ public UTF8String Name { /// From column ManifestResource.Implementation /// public IImplementation Implementation { - get { return implementation; } - set { implementation = value; } + get => implementation; + set => implementation = value; } /// protected IImplementation implementation; @@ -77,7 +75,7 @@ public IImplementation Implementation { /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -85,14 +83,33 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 18; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// /// Modify property: = @@ -100,39 +117,26 @@ public bool HasCustomAttributes { /// /// Value to AND /// Value to OR - void ModifyAttributes(ManifestResourceAttributes andMask, ManifestResourceAttributes orMask) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - newVal = (origVal & (int)andMask) | (int)orMask; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else + void ModifyAttributes(ManifestResourceAttributes andMask, ManifestResourceAttributes orMask) => attributes = (attributes & (int)andMask) | (int)orMask; -#endif - } /// /// Gets/sets the visibility /// public ManifestResourceAttributes Visibility { - get { return (ManifestResourceAttributes)attributes & ManifestResourceAttributes.VisibilityMask; } - set { ModifyAttributes(~ManifestResourceAttributes.VisibilityMask, value & ManifestResourceAttributes.VisibilityMask); } + get => (ManifestResourceAttributes)attributes & ManifestResourceAttributes.VisibilityMask; + set => ModifyAttributes(~ManifestResourceAttributes.VisibilityMask, value & ManifestResourceAttributes.VisibilityMask); } /// /// true if is set /// - public bool IsPublic { - get { return ((ManifestResourceAttributes)attributes & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Public; } - } + public bool IsPublic => ((ManifestResourceAttributes)attributes & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Public; /// /// true if is set /// - public bool IsPrivate { - get { return ((ManifestResourceAttributes)attributes & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Private; } - } + public bool IsPrivate => ((ManifestResourceAttributes)attributes & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Private; } /// @@ -174,7 +178,7 @@ public ManifestResourceUser(UTF8String name, IImplementation implementation, Man public ManifestResourceUser(UTF8String name, IImplementation implementation, ManifestResourceAttributes flags, uint offset) { this.name = name; this.implementation = implementation; - this.attributes = (int)flags; + attributes = (int)flags; this.offset = offset; } } @@ -189,17 +193,22 @@ sealed class ManifestResourceMD : ManifestResource, IMDTokenProviderMD { readonly uint origRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.ManifestResource, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.ManifestResource, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -209,18 +218,20 @@ protected override void InitializeCustomAttributes() { /// If is invalid public ManifestResourceMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.ManifestResourceTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("ManifestResource rid {0} does not exist", rid)); + throw new BadImageFormatException($"ManifestResource rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint name; - uint implementation = readerModule.TablesStream.ReadManifestResourceRow(origRid, out this.offset, out this.attributes, out name); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.implementation = readerModule.ResolveImplementation(implementation); + bool b = readerModule.TablesStream.TryReadManifestResourceRow(origRid, out var row); + Debug.Assert(b); + offset = row.Offset; + attributes = (int)row.Flags; + name = readerModule.StringsStream.ReadNoNull(row.Name); + implementation = readerModule.ResolveImplementation(row.Implementation); } } } diff --git a/src/DotNet/MarshalBlobReader.cs b/src/DotNet/MarshalBlobReader.cs index e59bb914c..0a8b7b3c8 100644 --- a/src/DotNet/MarshalBlobReader.cs +++ b/src/DotNet/MarshalBlobReader.cs @@ -1,15 +1,14 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info -using System; using dnlib.IO; namespace dnlib.DotNet { /// /// Reads s /// - public struct MarshalBlobReader : IDisposable { + public struct MarshalBlobReader { readonly ModuleDef module; - readonly IBinaryReader reader; + DataReader reader; readonly GenericParamContext gpContext; /// @@ -18,9 +17,7 @@ public struct MarshalBlobReader : IDisposable { /// Module /// Blob offset /// A new instance - public static MarshalType Read(ModuleDefMD module, uint sig) { - return Read(module, module.BlobStream.CreateStream(sig), new GenericParamContext()); - } + public static MarshalType Read(ModuleDefMD module, uint sig) => Read(module, module.BlobStream.CreateReader(sig), new GenericParamContext()); /// /// Reads a from the #Blob heap @@ -29,9 +26,7 @@ public static MarshalType Read(ModuleDefMD module, uint sig) { /// Blob offset /// Generic parameter context /// A new instance - public static MarshalType Read(ModuleDefMD module, uint sig, GenericParamContext gpContext) { - return Read(module, module.BlobStream.CreateStream(sig), gpContext); - } + public static MarshalType Read(ModuleDefMD module, uint sig, GenericParamContext gpContext) => Read(module, module.BlobStream.CreateReader(sig), gpContext); /// /// Reads a from @@ -39,9 +34,7 @@ public static MarshalType Read(ModuleDefMD module, uint sig, GenericParamContext /// Owner module /// Marshal data /// A new instance - public static MarshalType Read(ModuleDef module, byte[] data) { - return Read(module, MemoryImageStream.Create(data), new GenericParamContext()); - } + public static MarshalType Read(ModuleDef module, byte[] data) => Read(module, ByteArrayDataReaderFactory.CreateReader(data), new GenericParamContext()); /// /// Reads a from @@ -50,9 +43,7 @@ public static MarshalType Read(ModuleDef module, byte[] data) { /// Marshal data /// Generic parameter context /// A new instance - public static MarshalType Read(ModuleDef module, byte[] data, GenericParamContext gpContext) { - return Read(module, MemoryImageStream.Create(data), gpContext); - } + public static MarshalType Read(ModuleDef module, byte[] data, GenericParamContext gpContext) => Read(module, ByteArrayDataReaderFactory.CreateReader(data), gpContext); /// /// Reads a from @@ -60,9 +51,7 @@ public static MarshalType Read(ModuleDef module, byte[] data, GenericParamContex /// Owner module /// A reader that will be owned by us /// A new instance - public static MarshalType Read(ModuleDef module, IBinaryReader reader) { - return Read(module, reader, new GenericParamContext()); - } + public static MarshalType Read(ModuleDef module, DataReader reader) => Read(module, reader, new GenericParamContext()); /// /// Reads a from @@ -71,12 +60,12 @@ public static MarshalType Read(ModuleDef module, IBinaryReader reader) { /// A reader that will be owned by us /// Generic parameter context /// A new instance - public static MarshalType Read(ModuleDef module, IBinaryReader reader, GenericParamContext gpContext) { - using (var marshalReader = new MarshalBlobReader(module, reader, gpContext)) - return marshalReader.Read(); + public static MarshalType Read(ModuleDef module, DataReader reader, GenericParamContext gpContext) { + var marshalReader = new MarshalBlobReader(module, ref reader, gpContext); + return marshalReader.Read(); } - MarshalBlobReader(ModuleDef module, IBinaryReader reader, GenericParamContext gpContext) { + MarshalBlobReader(ModuleDef module, ref DataReader reader, GenericParamContext gpContext) { this.module = module; this.reader = reader; this.gpContext = gpContext; @@ -97,7 +86,7 @@ MarshalType Read() { case NativeType.SafeArray: var vt = CanRead() ? (VariantType)reader.ReadCompressedUInt32() : VariantType.NotInitialized; var udtName = CanRead() ? ReadUTF8String() : null; - var udtRef = (object)udtName == null ? null : TypeNameParser.ParseReflection(module, UTF8String.ToSystemStringOrEmpty(udtName), null, gpContext); + var udtRef = udtName is null ? null : TypeNameParser.ParseReflection(module, UTF8String.ToSystemStringOrEmpty(udtName), null, gpContext); returnValue = new SafeArrayMarshalType(vt, udtRef); break; @@ -119,7 +108,7 @@ MarshalType Read() { var guid = ReadUTF8String(); var nativeTypeName = ReadUTF8String(); var custMarshalerName = ReadUTF8String(); - var cmRef = TypeNameParser.ParseReflection(module, UTF8String.ToSystemStringOrEmpty(custMarshalerName), new CAAssemblyRefFinder(module), gpContext); + var cmRef = custMarshalerName.DataLength == 0 ? null : TypeNameParser.ParseReflection(module, UTF8String.ToSystemStringOrEmpty(custMarshalerName), new CAAssemblyRefFinder(module), gpContext); var cookie = ReadUTF8String(); returnValue = new CustomMarshalType(guid, nativeTypeName, cmRef, cookie); break; @@ -136,25 +125,17 @@ MarshalType Read() { } } catch { - returnValue = new RawMarshalType(reader.ReadAllBytes()); + returnValue = new RawMarshalType(reader.ToArray()); } return returnValue; } - bool CanRead() { - return reader.Position < reader.Length; - } + bool CanRead() => reader.Position < reader.Length; UTF8String ReadUTF8String() { uint len = reader.ReadCompressedUInt32(); return len == 0 ? UTF8String.Empty : new UTF8String(reader.ReadBytes((int)len)); } - - /// - public void Dispose() { - if (reader != null) - reader.Dispose(); - } } } diff --git a/src/DotNet/MarshalType.cs b/src/DotNet/MarshalType.cs index 788b6ecca..432e51e47 100644 --- a/src/DotNet/MarshalType.cs +++ b/src/DotNet/MarshalType.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; @@ -15,22 +15,16 @@ public class MarshalType { /// /// Gets the /// - public NativeType NativeType { - get { return nativeType; } - } + public NativeType NativeType => nativeType; /// /// Constructor /// /// Native type - public MarshalType(NativeType nativeType) { - this.nativeType = nativeType; - } + public MarshalType(NativeType nativeType) => this.nativeType = nativeType; /// - public override string ToString() { - return nativeType.ToString(); - } + public override string ToString() => nativeType.ToString(); } /// @@ -43,8 +37,8 @@ public sealed class RawMarshalType : MarshalType { /// Gets/sets the raw data /// public byte[] Data { - get { return data; } - set { data = value; } + get => data; + set => data = value; } /// @@ -52,9 +46,7 @@ public byte[] Data { /// /// Raw data public RawMarshalType(byte[] data) - : base(NativeType.RawBlob) { - this.data = data; - } + : base(NativeType.RawBlob) => this.data = data; } /// @@ -67,16 +59,14 @@ public sealed class FixedSysStringMarshalType : MarshalType { /// Gets/sets the size /// public int Size { - get { return size; } - set { size = value; } + get => size; + set => size = value; } /// /// true if is valid /// - public bool IsSizeValid { - get { return size >= 0; } - } + public bool IsSizeValid => size >= 0; /// /// Default constructor @@ -90,15 +80,13 @@ public FixedSysStringMarshalType() /// /// Size public FixedSysStringMarshalType(int size) - : base(NativeType.FixedSysString) { - this.size = size; - } + : base(NativeType.FixedSysString) => this.size = size; /// public override string ToString() { if (IsSizeValid) - return string.Format("{0} ({1})", nativeType, size); - return string.Format("{0} ()", nativeType); + return $"{nativeType} ({size})"; + return $"{nativeType} ()"; } } @@ -113,31 +101,27 @@ public sealed class SafeArrayMarshalType : MarshalType { /// Gets/sets the variant type /// public VariantType VariantType { - get { return vt; } - set { vt = value; } + get => vt; + set => vt = value; } /// /// Gets/sets the user-defined sub type (it's usually null) /// public ITypeDefOrRef UserDefinedSubType { - get { return userDefinedSubType; } - set { userDefinedSubType = value; } + get => userDefinedSubType; + set => userDefinedSubType = value; } /// /// true if is valid /// - public bool IsVariantTypeValid { - get { return vt != VariantType.NotInitialized; } - } + public bool IsVariantTypeValid => vt != VariantType.NotInitialized; /// /// true if is valid /// - public bool IsUserDefinedSubTypeValid { - get { return userDefinedSubType != null; } - } + public bool IsUserDefinedSubTypeValid => userDefinedSubType is not null; /// /// Default constructor @@ -176,9 +160,9 @@ public SafeArrayMarshalType(VariantType vt, ITypeDefOrRef userDefinedSubType) /// public override string ToString() { var udt = userDefinedSubType; - if (udt != null) - return string.Format("{0} ({1}, {2})", nativeType, vt, udt); - return string.Format("{0} ({1})", nativeType, vt); + if (udt is not null) + return $"{nativeType} ({vt}, {udt})"; + return $"{nativeType} ({vt})"; } } @@ -193,31 +177,27 @@ public sealed class FixedArrayMarshalType : MarshalType { /// Gets/sets the element type /// public NativeType ElementType { - get { return elementType; } - set { elementType = value; } + get => elementType; + set => elementType = value; } /// /// Gets/sets the size /// public int Size { - get { return size; } - set { size = value; } + get => size; + set => size = value; } /// /// true if is valid /// - public bool IsElementTypeValid { - get { return elementType != NativeType.NotInitialized; } - } + public bool IsElementTypeValid => elementType != NativeType.NotInitialized; /// /// true if is valid /// - public bool IsSizeValid { - get { return size >= 0; } - } + public bool IsSizeValid => size >= 0; /// /// Default constructor @@ -246,9 +226,7 @@ public FixedArrayMarshalType(int size, NativeType elementType) } /// - public override string ToString() { - return string.Format("{0} ({1}, {2})", nativeType, size, elementType); - } + public override string ToString() => $"{nativeType} ({size}, {elementType})"; } /// @@ -264,61 +242,53 @@ public sealed class ArrayMarshalType : MarshalType { /// Gets/sets the element type /// public NativeType ElementType { - get { return elementType; } - set { elementType = value; } + get => elementType; + set => elementType = value; } /// /// Gets/sets the parameter number /// public int ParamNumber { - get { return paramNum; } - set { paramNum = value; } + get => paramNum; + set => paramNum = value; } /// /// Gets/sets the size of the array /// public int Size { - get { return numElems; } - set { numElems = value; } + get => numElems; + set => numElems = value; } /// /// Gets/sets the flags /// public int Flags { - get { return flags; } - set { flags = value; } + get => flags; + set => flags = value; } /// /// true if is valid /// - public bool IsElementTypeValid { - get { return elementType != NativeType.NotInitialized; } - } + public bool IsElementTypeValid => elementType != NativeType.NotInitialized; /// /// true if is valid /// - public bool IsParamNumberValid { - get { return paramNum >= 0; } - } + public bool IsParamNumberValid => paramNum >= 0; /// /// true if is valid /// - public bool IsSizeValid { - get { return numElems >= 0; } - } + public bool IsSizeValid => numElems >= 0; /// /// true if is valid /// - public bool IsFlagsValid { - get { return flags >= 0; } - } + public bool IsFlagsValid => flags >= 0; const int ntaSizeParamIndexSpecified = 1; @@ -326,17 +296,13 @@ public bool IsFlagsValid { /// true if ntaSizeParamIndexSpecified bit is set, false if it's not /// set or if is invalid. /// - public bool IsSizeParamIndexSpecified { - get { return IsFlagsValid && (flags & ntaSizeParamIndexSpecified) != 0; } - } + public bool IsSizeParamIndexSpecified => IsFlagsValid && (flags & ntaSizeParamIndexSpecified) != 0; /// /// true if ntaSizeParamIndexSpecified bit is not set, false if it's /// set or if is invalid. /// - public bool IsSizeParamIndexNotSpecified { - get { return IsFlagsValid && (flags & ntaSizeParamIndexSpecified) == 0; } - } + public bool IsSizeParamIndexNotSpecified => IsFlagsValid && (flags & ntaSizeParamIndexSpecified) == 0; /// /// Default constructor @@ -388,9 +354,7 @@ public ArrayMarshalType(NativeType elementType, int paramNum, int numElems, int } /// - public override string ToString() { - return string.Format("{0} ({1}, {2}, {3}, {4})", nativeType, elementType, paramNum, numElems, flags); - } + public override string ToString() => $"{nativeType} ({elementType}, {paramNum}, {numElems}, {flags})"; } /// @@ -406,32 +370,32 @@ public sealed class CustomMarshalType : MarshalType { /// Gets/sets the GUID string /// public UTF8String Guid { - get { return guid; } - set { guid = value; } + get => guid; + set => guid = value; } /// /// Gets/sets the native type name string /// public UTF8String NativeTypeName { - get { return nativeTypeName; } - set { nativeTypeName = value; } + get => nativeTypeName; + set => nativeTypeName = value; } /// /// Gets/sets the custom marshaler /// public ITypeDefOrRef CustomMarshaler { - get { return custMarshaler; } - set { custMarshaler = value; } + get => custMarshaler; + set => custMarshaler = value; } /// /// Gets/sets the cookie string /// public UTF8String Cookie { - get { return cookie; } - set { cookie = value; } + get => cookie; + set => cookie = value; } /// @@ -484,9 +448,7 @@ public CustomMarshalType(UTF8String guid, UTF8String nativeTypeName, ITypeDefOrR } /// - public override string ToString() { - return string.Format("{0} ({1}, {2}, {3}, {4})", nativeType, guid, nativeTypeName, custMarshaler, cookie); - } + public override string ToString() => $"{nativeType} ({guid}, {nativeTypeName}, {custMarshaler}, {cookie})"; } /// @@ -500,16 +462,14 @@ public sealed class InterfaceMarshalType : MarshalType { /// Gets/sets the IID parameter index /// public int IidParamIndex { - get { return iidParamIndex; } - set { iidParamIndex = value; } + get => iidParamIndex; + set => iidParamIndex = value; } /// /// true if is valid /// - public bool IsIidParamIndexValid { - get { return iidParamIndex >= 0; } - } + public bool IsIidParamIndexValid => iidParamIndex >= 0; /// /// Constructor @@ -534,8 +494,6 @@ public InterfaceMarshalType(NativeType nativeType, int iidParamIndex) } /// - public override string ToString() { - return string.Format("{0} ({1})", nativeType, iidParamIndex); - } + public override string ToString() => $"{nativeType} ({iidParamIndex})"; } } diff --git a/src/DotNet/MemberFinder.cs b/src/DotNet/MemberFinder.cs index 32d645c92..926f7f324 100644 --- a/src/DotNet/MemberFinder.cs +++ b/src/DotNet/MemberFinder.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; @@ -114,7 +114,7 @@ public MemberFinder FindAll(ModuleDef module) { } void Push(object mr) { - if (mr == null) + if (mr is null) return; objectStack.Push(mr); } @@ -136,18 +136,17 @@ void ProcessAll() { case ObjectType.TypeSig: Add((TypeSig)o); break; case ObjectType.TypeSpec: Add((TypeSpec)o); break; case ObjectType.ExportedType: Add((ExportedType)o); break; - default: throw new InvalidOperationException(string.Format("Unknown type: {0}", o.GetType())); + default: throw new InvalidOperationException($"Unknown type: {o.GetType()}"); } } } readonly Dictionary toObjectType = new Dictionary(); ObjectType GetObjectType(object o) { - if (o == null) + if (o is null) return ObjectType.Unknown; var type = o.GetType(); - ObjectType mrType; - if (toObjectType.TryGetValue(type, out mrType)) + if (toObjectType.TryGetValue(type, out var mrType)) return mrType; mrType = GetObjectType2(o); toObjectType[type] = mrType; @@ -178,10 +177,11 @@ void Add(ModuleDef mod) { if (mod.IsManifestModule) Add(mod.Assembly); Add(mod.VTableFixups); + Add(mod.Resources); } void Add(VTableFixups fixups) { - if (fixups == null) + if (fixups is null) return; foreach (var fixup in fixups) { foreach (var method in fixup) @@ -189,50 +189,52 @@ void Add(VTableFixups fixups) { } } + void Add(ResourceCollection resources) { + foreach (var resource in resources) { + Add(resource.CustomAttributes); + } + } + void Add(AssemblyDef asm) { - if (asm == null) + if (asm is null) return; Add(asm.DeclSecurities); Add(asm.CustomAttributes); } void Add(CallingConventionSig sig) { - if (sig == null) + if (sig is null) return; - var fs = sig as FieldSig; - if (fs != null) { + if (sig is FieldSig fs) { Add(fs); return; } - var mbs = sig as MethodBaseSig; - if (mbs != null) { + if (sig is MethodBaseSig mbs) { Add(mbs); return; } - var ls = sig as LocalSig; - if (ls != null) { + if (sig is LocalSig ls) { Add(ls); return; } - var gims = sig as GenericInstMethodSig; - if (gims != null) { + if (sig is GenericInstMethodSig gims) { Add(gims); return; } } void Add(FieldSig sig) { - if (sig == null) + if (sig is null) return; Add(sig.Type); } void Add(MethodBaseSig sig) { - if (sig == null) + if (sig is null) return; Add(sig.RetType); Add(sig.Params); @@ -240,26 +242,26 @@ void Add(MethodBaseSig sig) { } void Add(LocalSig sig) { - if (sig == null) + if (sig is null) return; Add(sig.Locals); } void Add(GenericInstMethodSig sig) { - if (sig == null) + if (sig is null) return; Add(sig.GenericArguments); } void Add(IEnumerable cas) { - if (cas == null) + if (cas is null) return; foreach (var ca in cas) Add(ca); } void Add(CustomAttribute ca) { - if (ca == null || CustomAttributes.ContainsKey(ca)) + if (ca is null || CustomAttributes.ContainsKey(ca)) return; CustomAttributes[ca] = true; Push(ca.Constructor); @@ -268,7 +270,7 @@ void Add(CustomAttribute ca) { } void Add(IEnumerable args) { - if (args == null) + if (args is null) return; foreach (var arg in args) Add(arg); @@ -277,81 +279,84 @@ void Add(IEnumerable args) { void Add(CAArgument arg) { // It's a struct so can't be null Add(arg.Type); + if (arg.Value is TypeSig typeSig) + Add(typeSig); + else if (arg.Value is IList args) + Add(args); + else if(arg.Value is CAArgument boxedArgument) + Add(boxedArgument); } void Add(IEnumerable args) { - if (args == null) + if (args is null) return; foreach (var arg in args) Add(arg); } void Add(CANamedArgument arg) { - if (arg == null) + if (arg is null) return; Add(arg.Type); Add(arg.Argument); } void Add(IEnumerable decls) { - if (decls == null) + if (decls is null) return; foreach (var decl in decls) Add(decl); } void Add(DeclSecurity decl) { - if (decl == null) + if (decl is null) return; Add(decl.SecurityAttributes); Add(decl.CustomAttributes); } void Add(IEnumerable secAttrs) { - if (secAttrs == null) + if (secAttrs is null) return; foreach (var secAttr in secAttrs) Add(secAttr); } void Add(SecurityAttribute secAttr) { - if (secAttr == null) + if (secAttr is null) return; Add(secAttr.AttributeType); Add(secAttr.NamedArguments); } void Add(ITypeDefOrRef tdr) { - var td = tdr as TypeDef; - if (td != null) { + if (tdr is TypeDef td) { Add(td); return; } - var tr = tdr as TypeRef; - if (tr != null) { + if (tdr is TypeRef tr) { Add(tr); return; } - var ts = tdr as TypeSpec; - if (ts != null) { + if (tdr is TypeSpec ts) { Add(ts); return; } } void Add(IEnumerable eds) { - if (eds == null) + if (eds is null) return; foreach (var ed in eds) Add(ed); } void Add(EventDef ed) { - if (ed == null || EventDefs.ContainsKey(ed)) + if (ed is null || EventDefs.ContainsKey(ed)) return; - if (ed.DeclaringType != null && ed.DeclaringType.Module != validModule) + if (ed.DeclaringType is not null && ed.DeclaringType.Module != validModule) return; EventDefs[ed] = true; Push(ed.EventType); @@ -364,16 +369,16 @@ void Add(EventDef ed) { } void Add(IEnumerable fds) { - if (fds == null) + if (fds is null) return; foreach (var fd in fds) Add(fd); } void Add(FieldDef fd) { - if (fd == null || FieldDefs.ContainsKey(fd)) + if (fd is null || FieldDefs.ContainsKey(fd)) return; - if (fd.DeclaringType != null && fd.DeclaringType.Module != validModule) + if (fd.DeclaringType is not null && fd.DeclaringType.Module != validModule) return; FieldDefs[fd] = true; Add(fd.CustomAttributes); @@ -383,14 +388,14 @@ void Add(FieldDef fd) { } void Add(IEnumerable gps) { - if (gps == null) + if (gps is null) return; foreach (var gp in gps) Add(gp); } void Add(GenericParam gp) { - if (gp == null || GenericParams.ContainsKey(gp)) + if (gp is null || GenericParams.ContainsKey(gp)) return; GenericParams[gp] = true; Push(gp.Owner); @@ -400,14 +405,14 @@ void Add(GenericParam gp) { } void Add(IEnumerable gpcs) { - if (gpcs == null) + if (gpcs is null) return; foreach (var gpc in gpcs) Add(gpc); } void Add(GenericParamConstraint gpc) { - if (gpc == null) + if (gpc is null) return; Add(gpc.Owner); Push(gpc.Constraint); @@ -415,7 +420,7 @@ void Add(GenericParamConstraint gpc) { } void Add(MemberRef mr) { - if (mr == null || MemberRefs.ContainsKey(mr)) + if (mr is null || MemberRefs.ContainsKey(mr)) return; if (mr.Module != validModule) return; @@ -426,16 +431,16 @@ void Add(MemberRef mr) { } void Add(IEnumerable methods) { - if (methods == null) + if (methods is null) return; foreach (var m in methods) Add(m); } void Add(MethodDef md) { - if (md == null || MethodDefs.ContainsKey(md)) + if (md is null || MethodDefs.ContainsKey(md)) return; - if (md.DeclaringType != null && md.DeclaringType.Module != validModule) + if (md.DeclaringType is not null && md.DeclaringType.Module != validModule) return; MethodDefs[md] = true; Add(md.Signature); @@ -449,13 +454,12 @@ void Add(MethodDef md) { } void Add(MethodBody mb) { - var cb = mb as CilBody; - if (cb != null) + if (mb is CilBody cb) Add(cb); } void Add(CilBody cb) { - if (cb == null) + if (cb is null) return; Add(cb.Instructions); Add(cb.ExceptionHandlers); @@ -463,10 +467,10 @@ void Add(CilBody cb) { } void Add(IEnumerable instrs) { - if (instrs == null) + if (instrs is null) return; foreach (var instr in instrs) { - if (instr == null) + if (instr is null) continue; switch (instr.OpCode.OperandType) { case OperandType.InlineTok: @@ -483,12 +487,12 @@ void Add(IEnumerable instrs) { case OperandType.InlineVar: case OperandType.ShortInlineVar: var local = instr.Operand as Local; - if (local != null) { + if (local is not null) { Add(local); break; } var arg = instr.Operand as Parameter; - if (arg != null) { + if (arg is not null) { Add(arg); break; } @@ -498,48 +502,48 @@ void Add(IEnumerable instrs) { } void Add(IEnumerable ehs) { - if (ehs == null) + if (ehs is null) return; foreach (var eh in ehs) Push(eh.CatchType); } void Add(IEnumerable locals) { - if (locals == null) + if (locals is null) return; foreach (var local in locals) Add(local); } void Add(Local local) { - if (local == null) + if (local is null) return; Add(local.Type); } void Add(IEnumerable ps) { - if (ps == null) + if (ps is null) return; foreach (var p in ps) Add(p); } void Add(Parameter param) { - if (param == null) + if (param is null) return; Add(param.Type); Add(param.Method); } void Add(IEnumerable pds) { - if (pds == null) + if (pds is null) return; foreach (var pd in pds) Add(pd); } void Add(ParamDef pd) { - if (pd == null) + if (pd is null) return; Add(pd.DeclaringMethod); Add(pd.CustomAttributes); @@ -547,7 +551,7 @@ void Add(ParamDef pd) { } void Add(MarshalType mt) { - if (mt == null) + if (mt is null) return; switch (mt.NativeType) { @@ -562,7 +566,7 @@ void Add(MarshalType mt) { } void Add(IEnumerable mos) { - if (mos == null) + if (mos is null) return; foreach (var mo in mos) Add(mo); @@ -575,9 +579,9 @@ void Add(MethodOverride mo) { } void Add(MethodSpec ms) { - if (ms == null || MethodSpecs.ContainsKey(ms)) + if (ms is null || MethodSpecs.ContainsKey(ms)) return; - if (ms.Method != null && ms.Method.DeclaringType != null && ms.Method.DeclaringType.Module != validModule) + if (ms.Method is not null && ms.Method.DeclaringType is not null && ms.Method.DeclaringType.Module != validModule) return; MethodSpecs[ms] = true; Push(ms.Method); @@ -586,16 +590,16 @@ void Add(MethodSpec ms) { } void Add(IEnumerable pds) { - if (pds == null) + if (pds is null) return; foreach (var pd in pds) Add(pd); } void Add(PropertyDef pd) { - if (pd == null || PropertyDefs.ContainsKey(pd)) + if (pd is null || PropertyDefs.ContainsKey(pd)) return; - if (pd.DeclaringType != null && pd.DeclaringType.Module != validModule) + if (pd.DeclaringType is not null && pd.DeclaringType.Module != validModule) return; PropertyDefs[pd] = true; Add(pd.Type); @@ -607,14 +611,14 @@ void Add(PropertyDef pd) { } void Add(IEnumerable tds) { - if (tds == null) + if (tds is null) return; foreach (var td in tds) Add(td); } void Add(TypeDef td) { - if (td == null || TypeDefs.ContainsKey(td)) + if (td is null || TypeDefs.ContainsKey(td)) return; if (td.Module != validModule) return; @@ -633,21 +637,21 @@ void Add(TypeDef td) { } void Add(IEnumerable iis) { - if (iis == null) + if (iis is null) return; foreach (var ii in iis) Add(ii); } void Add(InterfaceImpl ii) { - if (ii == null) + if (ii is null) return; Push(ii.Interface); Add(ii.CustomAttributes); } void Add(TypeRef tr) { - if (tr == null || TypeRefs.ContainsKey(tr)) + if (tr is null || TypeRefs.ContainsKey(tr)) return; if (tr.Module != validModule) return; @@ -657,20 +661,20 @@ void Add(TypeRef tr) { } void Add(IEnumerable tss) { - if (tss == null) + if (tss is null) return; foreach (var ts in tss) Add(ts); } void Add(TypeSig ts) { - if (ts == null || TypeSigs.ContainsKey(ts)) + if (ts is null || TypeSigs.ContainsKey(ts)) return; if (ts.Module != validModule) return; TypeSigs[ts] = true; - for (; ts != null; ts = ts.Next) { + for (; ts is not null; ts = ts.Next) { switch (ts.ElementType) { case ElementType.Void: case ElementType.Boolean: @@ -733,7 +737,7 @@ void Add(TypeSig ts) { } void Add(TypeSpec ts) { - if (ts == null || TypeSpecs.ContainsKey(ts)) + if (ts is null || TypeSpecs.ContainsKey(ts)) return; if (ts.Module != validModule) return; @@ -743,14 +747,14 @@ void Add(TypeSpec ts) { } void Add(IEnumerable ets) { - if (ets == null) + if (ets is null) return; foreach (var et in ets) Add(et); } void Add(ExportedType et) { - if (et == null || ExportedTypes.ContainsKey(et)) + if (et is null || ExportedTypes.ContainsKey(et)) return; if (et.Module != validModule) return; diff --git a/src/DotNet/MemberMDInitializer.cs b/src/DotNet/MemberMDInitializer.cs index e7dc82eda..93f1470b5 100644 --- a/src/DotNet/MemberMDInitializer.cs +++ b/src/DotNet/MemberMDInitializer.cs @@ -1,7 +1,6 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System.Collections.Generic; -using dnlib.Threading; namespace dnlib.DotNet { /// @@ -14,9 +13,9 @@ static class MemberMDInitializer { /// Collection element type /// Collection public static void Initialize(IEnumerable coll) { - if (coll == null) + if (coll is null) return; - foreach (var c in coll.GetSafeEnumerable()) { + foreach (var c in coll) { } } diff --git a/src/DotNet/MemberRef.cs b/src/DotNet/MemberRef.cs index 9b5d371d3..fb0f587fd 100644 --- a/src/DotNet/MemberRef.cs +++ b/src/DotNet/MemberRef.cs @@ -2,14 +2,16 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// /// A high-level representation of a row in the MemberRef table /// - public abstract class MemberRef : IHasCustomAttribute, IMethodDefOrRef, ICustomAttributeType, IField, IContainsGenericParameter { + public abstract class MemberRef : IHasCustomAttribute, IMethodDefOrRef, ICustomAttributeType, IField, IContainsGenericParameter, IHasCustomDebugInformation { /// /// The row id in its table /// @@ -21,37 +23,29 @@ public abstract class MemberRef : IHasCustomAttribute, IMethodDefOrRef, ICustomA protected ModuleDef module; /// - public MDToken MDToken { - get { return new MDToken(Table.MemberRef, rid); } - } + public MDToken MDToken => new MDToken(Table.MemberRef, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 6; } - } + public int HasCustomAttributeTag => 6; /// - public int MethodDefOrRefTag { - get { return 1; } - } + public int MethodDefOrRefTag => 1; /// - public int CustomAttributeTypeTag { - get { return 3; } - } + public int CustomAttributeTypeTag => 3; /// /// From column MemberRef.Class /// public IMemberRefParent Class { - get { return @class; } - set { @class = value; } + get => @class; + set => @class = value; } /// protected IMemberRefParent @class; @@ -60,8 +54,8 @@ public IMemberRefParent Class { /// From column MemberRef.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -70,8 +64,8 @@ public UTF8String Name { /// From column MemberRef.Signature /// public CallingConventionSig Signature { - get { return signature; } - set { signature = value; } + get => signature; + set => signature = value; } /// protected CallingConventionSig signature; @@ -81,7 +75,7 @@ public CallingConventionSig Signature { /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -89,32 +83,48 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 6; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// public ITypeDefOrRef DeclaringType { get { var owner = @class; - var tdr = owner as ITypeDefOrRef; - if (tdr != null) + if (owner is ITypeDefOrRef tdr) return tdr; - var method = owner as MethodDef; - if (method != null) + if (owner is MethodDef method) return method.DeclaringType; - var mr = owner as ModuleRef; - if (mr != null) { + if (owner is ModuleRef mr) { var tr = GetGlobalTypeRef(mr); - if (module != null) + if (module is not null) return module.UpdateRowId(tr); return tr; } @@ -124,116 +134,72 @@ public ITypeDefOrRef DeclaringType { } TypeRefUser GetGlobalTypeRef(ModuleRef mr) { - if (module == null) + if (module is null) return CreateDefaultGlobalTypeRef(mr); var globalType = module.GlobalType; - if (globalType != null && new SigComparer().Equals(module, mr)) + if (globalType is not null && new SigComparer().Equals(module, mr)) return new TypeRefUser(module, globalType.Namespace, globalType.Name, mr); var asm = module.Assembly; - if (asm == null) + if (asm is null) return CreateDefaultGlobalTypeRef(mr); var mod = asm.FindModule(mr.Name); - if (mod == null) + if (mod is null) return CreateDefaultGlobalTypeRef(mr); globalType = mod.GlobalType; - if (globalType == null) + if (globalType is null) return CreateDefaultGlobalTypeRef(mr); return new TypeRefUser(module, globalType.Namespace, globalType.Name, mr); } TypeRefUser CreateDefaultGlobalTypeRef(ModuleRef mr) { var tr = new TypeRefUser(module, string.Empty, "", mr); - if (module != null) + if (module is not null) module.UpdateRowId(tr); return tr; } - bool IIsTypeOrMethod.IsType { - get { return false; } - } - - bool IIsTypeOrMethod.IsMethod { - get { return IsMethodRef; } - } - - bool IMemberRef.IsField { - get { return IsFieldRef; } - } - - bool IMemberRef.IsTypeSpec { - get { return false; } - } - - bool IMemberRef.IsTypeRef { - get { return false; } - } - - bool IMemberRef.IsTypeDef { - get { return false; } - } - - bool IMemberRef.IsMethodSpec { - get { return false; } - } - - bool IMemberRef.IsMethodDef { - get { return false; } - } - - bool IMemberRef.IsMemberRef { - get { return true; } - } - - bool IMemberRef.IsFieldDef { - get { return false; } - } - - bool IMemberRef.IsPropertyDef { - get { return false; } - } - - bool IMemberRef.IsEventDef { - get { return false; } - } - - bool IMemberRef.IsGenericParam { - get { return false; } - } + bool IIsTypeOrMethod.IsType => false; + bool IIsTypeOrMethod.IsMethod => IsMethodRef; + bool IMemberRef.IsField => IsFieldRef; + bool IMemberRef.IsTypeSpec => false; + bool IMemberRef.IsTypeRef => false; + bool IMemberRef.IsTypeDef => false; + bool IMemberRef.IsMethodSpec => false; + bool IMemberRef.IsMethodDef => false; + bool IMemberRef.IsMemberRef => true; + bool IMemberRef.IsFieldDef => false; + bool IMemberRef.IsPropertyDef => false; + bool IMemberRef.IsEventDef => false; + bool IMemberRef.IsGenericParam => false; /// /// true if this is a method reference ( != null) /// - public bool IsMethodRef { - get { return MethodSig != null; } - } + public bool IsMethodRef => MethodSig is not null; /// /// true if this is a field reference ( != null) /// - public bool IsFieldRef { - get { return FieldSig != null; } - } + public bool IsFieldRef => FieldSig is not null; /// /// Gets/sets the method sig /// public MethodSig MethodSig { - get { return signature as MethodSig; } - set { signature = value; } + get => signature as MethodSig; + set => signature = value; } /// /// Gets/sets the field sig /// public FieldSig FieldSig { - get { return signature as FieldSig; } - set { signature = value; } + get => signature as FieldSig; + set => signature = value; } /// - public ModuleDef Module { - get { return module; } - } + public ModuleDef Module => module; /// /// true if the method has a hidden 'this' parameter @@ -241,7 +207,7 @@ public ModuleDef Module { public bool HasThis { get { var ms = MethodSig; - return ms == null ? false : ms.HasThis; + return ms is null ? false : ms.HasThis; } } @@ -251,7 +217,7 @@ public bool HasThis { public bool ExplicitThis { get { var ms = MethodSig; - return ms == null ? false : ms.ExplicitThis; + return ms is null ? false : ms.ExplicitThis; } } @@ -261,7 +227,7 @@ public bool ExplicitThis { public CallingConvention CallingConvention { get { var ms = MethodSig; - return ms == null ? 0 : ms.CallingConvention & CallingConvention.Mask; + return ms is null ? 0 : ms.CallingConvention & CallingConvention.Mask; } } @@ -269,24 +235,16 @@ public CallingConvention CallingConvention { /// Gets/sets the method return type /// public TypeSig ReturnType { - get { - var ms = MethodSig; - return ms == null ? null : ms.RetType; - } + get => MethodSig?.RetType; set { var ms = MethodSig; - if (ms != null) + if (ms is not null) ms.RetType = value; } } /// - int IGenericParameterProvider.NumberOfGenericParameters { - get { - var sig = MethodSig; - return sig == null ? 0 : (int)sig.GenParamCount; - } - } + int IGenericParameterProvider.NumberOfGenericParameters => (int)(MethodSig?.GenParamCount ?? 0); /// /// Gets the full name @@ -296,16 +254,15 @@ public string FullName { var parent = @class; IList typeGenArgs = null; if (parent is TypeSpec) { - var sig = ((TypeSpec)parent).TypeSig as GenericInstSig; - if (sig != null) + if (((TypeSpec)parent).TypeSig is GenericInstSig sig) typeGenArgs = sig.GenericArguments; } var methodSig = MethodSig; - if (methodSig != null) - return FullNameCreator.MethodFullName(GetDeclaringTypeFullName(parent), name, methodSig, typeGenArgs, null); + if (methodSig is not null) + return FullNameFactory.MethodFullName(GetDeclaringTypeFullName(parent), name, methodSig, typeGenArgs, null, null, null); var fieldSig = FieldSig; - if (fieldSig != null) - return FullNameCreator.FieldFullName(GetDeclaringTypeFullName(parent), name, fieldSig, typeGenArgs); + if (fieldSig is not null) + return FullNameFactory.FieldFullName(GetDeclaringTypeFullName(parent), name, fieldSig, typeGenArgs, null); return string.Empty; } } @@ -314,31 +271,49 @@ public string FullName { /// Get the declaring type's full name /// /// Full name or null if there's no declaring type - public string GetDeclaringTypeFullName() { - return GetDeclaringTypeFullName(@class); - } + public string GetDeclaringTypeFullName() => GetDeclaringTypeFullName(@class); string GetDeclaringTypeFullName(IMemberRefParent parent) { - if (parent == null) + if (parent is null) return null; if (parent is ITypeDefOrRef) return ((ITypeDefOrRef)parent).FullName; if (parent is ModuleRef) - return string.Format("[module:{0}]", ((ModuleRef)parent).ToString()); + return $"[module:{((ModuleRef)parent).ToString()}]"; if (parent is MethodDef) { var declaringType = ((MethodDef)parent).DeclaringType; - return declaringType == null ? null : declaringType.FullName; + return declaringType?.FullName; } return null; // Should never be reached - } - + } + + /// + /// Get the declaring type's name + /// + /// Name or null if there's no declaring type + internal string GetDeclaringTypeName() => GetDeclaringTypeName(@class); + + string GetDeclaringTypeName(IMemberRefParent parent) { + if (parent is null) + return null; + if (parent is ITypeDefOrRef) + return ((ITypeDefOrRef)parent).Name; + if (parent is ModuleRef) + return $""; + if (parent is MethodDef) { + var declaringType = ((MethodDef)parent).DeclaringType; + return declaringType?.Name; + } + return null; // Should never be reached + } + /// /// Resolves the method/field /// /// A or a instance or null /// if it couldn't be resolved. public IMemberForwarded Resolve() { - if (module == null) + if (module is null) return null; return module.Context.Resolver.Resolve(this); } @@ -350,18 +325,16 @@ public IMemberForwarded Resolve() { /// If the method/field couldn't be resolved public IMemberForwarded ResolveThrow() { var memberDef = Resolve(); - if (memberDef != null) + if (memberDef is not null) return memberDef; - throw new MemberRefResolveException(string.Format("Could not resolve method/field: {0} ({1})", this, this.GetDefinitionAssembly())); + throw new MemberRefResolveException($"Could not resolve method/field: {this} ({this.GetDefinitionAssembly()})"); } /// /// Resolves the field /// /// A instance or null if it couldn't be resolved. - public FieldDef ResolveField() { - return Resolve() as FieldDef; - } + public FieldDef ResolveField() => Resolve() as FieldDef; /// /// Resolves the field @@ -370,18 +343,16 @@ public FieldDef ResolveField() { /// If the field couldn't be resolved public FieldDef ResolveFieldThrow() { var field = ResolveField(); - if (field != null) + if (field is not null) return field; - throw new MemberRefResolveException(string.Format("Could not resolve field: {0} ({1})", this, this.GetDefinitionAssembly())); + throw new MemberRefResolveException($"Could not resolve field: {this} ({this.GetDefinitionAssembly()})"); } /// /// Resolves the method /// /// A instance or null if it couldn't be resolved. - public MethodDef ResolveMethod() { - return Resolve() as MethodDef; - } + public MethodDef ResolveMethod() => Resolve() as MethodDef; /// /// Resolves the method @@ -390,14 +361,12 @@ public MethodDef ResolveMethod() { /// If the method couldn't be resolved public MethodDef ResolveMethodThrow() { var method = ResolveMethod(); - if (method != null) + if (method is not null) return method; - throw new MemberRefResolveException(string.Format("Could not resolve method: {0} ({1})", this, this.GetDefinitionAssembly())); + throw new MemberRefResolveException($"Could not resolve method: {this} ({this.GetDefinitionAssembly()})"); } - bool IContainsGenericParameter.ContainsGenericParameter { - get { return TypeHelper.ContainsGenericParameter(this); } - } + bool IContainsGenericParameter.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); /// /// Gets a that can be used as signature context @@ -407,22 +376,16 @@ bool IContainsGenericParameter.ContainsGenericParameter { /// protected static GenericParamContext GetSignatureGenericParamContext(GenericParamContext gpContext, IMemberRefParent @class) { TypeDef type = null; - MethodDef method = gpContext.Method; + var method = gpContext.Method; - var ts = @class as TypeSpec; - if (ts != null) { - var gis = ts.TypeSig as GenericInstSig; - if (gis != null) - type = gis.GenericType.ToTypeDefOrRef().ResolveTypeDef(); - } + if (@class is TypeSpec ts && ts.TypeSig is GenericInstSig gis) + type = gis.GenericType.ToTypeDefOrRef().ResolveTypeDef(); return new GenericParamContext(type, method); } /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } /// @@ -433,9 +396,7 @@ public class MemberRefUser : MemberRef { /// Constructor /// /// Owner module - public MemberRefUser(ModuleDef module) { - this.module = module; - } + public MemberRefUser(ModuleDef module) => this.module = module; /// /// Constructor @@ -468,7 +429,7 @@ public MemberRefUser(ModuleDef module, UTF8String name, FieldSig sig, IMemberRef this.module = module; this.name = name; this.@class = @class; - this.signature = sig; + signature = sig; } /// @@ -492,31 +453,39 @@ public MemberRefUser(ModuleDef module, UTF8String name, MethodSig sig, IMemberRe this.module = module; this.name = name; this.@class = @class; - this.signature = sig; + signature = sig; } } /// /// Created from a row in the MemberRef table /// - sealed class MemberRefMD : MemberRef, IMDTokenProviderMD { + sealed class MemberRefMD : MemberRef, IMDTokenProviderMD, IContainsGenericParameter2 { /// The module where this instance is located readonly ModuleDefMD readerModule; readonly uint origRid; + readonly GenericParamContext gpContext; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; + + bool IContainsGenericParameter2.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.MemberRef, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.MemberRef, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), gpContext, list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -527,20 +496,21 @@ protected override void InitializeCustomAttributes() { /// If is invalid public MemberRefMD(ModuleDefMD readerModule, uint rid, GenericParamContext gpContext) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.MemberRefTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("MemberRef rid {0} does not exist", rid)); + throw new BadImageFormatException($"MemberRef rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - this.module = readerModule; - uint @class, name; - uint signature = readerModule.TablesStream.ReadMemberRefRow(origRid, out @class, out name); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.@class = readerModule.ResolveMemberRefParent(@class, gpContext); - this.signature = readerModule.ReadSignature(signature, GetSignatureGenericParamContext(gpContext, this.@class)); + this.gpContext = gpContext; + module = readerModule; + bool b = readerModule.TablesStream.TryReadMemberRefRow(origRid, out var row); + Debug.Assert(b); + name = readerModule.StringsStream.ReadNoNull(row.Name); + @class = readerModule.ResolveMemberRefParent(row.Class, gpContext); + signature = readerModule.ReadSignature(row.Signature, GetSignatureGenericParamContext(gpContext, @class)); } } } diff --git a/src/DotNet/MethodAttributes.cs b/src/DotNet/MethodAttributes.cs index abd7a3c8f..3c66de82e 100644 --- a/src/DotNet/MethodAttributes.cs +++ b/src/DotNet/MethodAttributes.cs @@ -52,8 +52,6 @@ public enum MethodAttributes : ushort { /// Implementation is forwarded through pinvoke. PinvokeImpl = 0x2000, - /// Implementation is forwarded through pinvoke. - PInvokeImpl = PinvokeImpl, /// Managed method exported via thunk to unmanaged code. UnmanagedExport = 0x0008, diff --git a/src/DotNet/MethodDef.cs b/src/DotNet/MethodDef.cs index 52e47f5c2..1411eb1ae 100644 --- a/src/DotNet/MethodDef.cs +++ b/src/DotNet/MethodDef.cs @@ -1,24 +1,21 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Threading; using dnlib.Utils; using dnlib.PE; using dnlib.DotNet.MD; using dnlib.DotNet.Emit; using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif +using dnlib.DotNet.Pdb; +using System.Collections.Generic; +using System.Diagnostics; namespace dnlib.DotNet { /// /// A high-level representation of a row in the Method table /// - public abstract class MethodDef : IHasCustomAttribute, IHasDeclSecurity, IMemberRefParent, IMethodDefOrRef, IMemberForwarded, ICustomAttributeType, ITypeOrMethodDef, IManagedEntryPoint, IListListener, IListListener, IMemberDef { + public abstract class MethodDef : IHasCustomAttribute, IHasDeclSecurity, IMemberRefParent, IMethodDefOrRef, IMemberForwarded, ICustomAttributeType, ITypeOrMethodDef, IManagedEntryPoint, IHasCustomDebugInformation, IListListener, IListListener, IMemberDef { internal static readonly UTF8String StaticConstructorName = ".cctor"; internal static readonly UTF8String InstanceConstructorName = ".ctor"; @@ -37,57 +34,41 @@ public abstract class MethodDef : IHasCustomAttribute, IHasDeclSecurity, IMember protected ParameterList parameterList; /// - public MDToken MDToken { - get { return new MDToken(Table.Method, rid); } - } + public MDToken MDToken => new MDToken(Table.Method, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 0; } - } + public int HasCustomAttributeTag => 0; /// - public int HasDeclSecurityTag { - get { return 1; } - } + public int HasDeclSecurityTag => 1; /// - public int MemberRefParentTag { - get { return 3; } - } + public int MemberRefParentTag => 3; /// - public int MethodDefOrRefTag { - get { return 0; } - } + public int MethodDefOrRefTag => 0; /// - public int MemberForwardedTag { - get { return 1; } - } + public int MemberForwardedTag => 1; /// - public int CustomAttributeTypeTag { - get { return 2; } - } + public int CustomAttributeTypeTag => 2; /// - public int TypeOrMethodDefTag { - get { return 1; } - } + public int TypeOrMethodDefTag => 1; /// /// From column Method.RVA /// public RVA RVA { - get { return rva; } - set { rva = value; } + get => rva; + set => rva = value; } /// protected RVA rva; @@ -96,8 +77,8 @@ public RVA RVA { /// From column Method.ImplFlags /// public MethodImplAttributes ImplAttributes { - get { return (MethodImplAttributes)implAttributes; } - set { implAttributes = (int)value; } + get => (MethodImplAttributes)implAttributes; + set => implAttributes = (int)value; } /// Implementation attributes protected int implAttributes; @@ -106,8 +87,8 @@ public MethodImplAttributes ImplAttributes { /// From column Method.Flags /// public MethodAttributes Attributes { - get { return (MethodAttributes)attributes; } - set { attributes = (int)value; } + get => (MethodAttributes)attributes; + set => attributes = (int)value; } /// Attributes protected int attributes; @@ -116,8 +97,8 @@ public MethodAttributes Attributes { /// From column Method.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -126,8 +107,8 @@ public UTF8String Name { /// From column Method.Signature /// public CallingConventionSig Signature { - get { return signature; } - set { signature = value; } + get => signature; + set => signature = value; } /// protected CallingConventionSig signature; @@ -135,9 +116,9 @@ public CallingConventionSig Signature { /// /// From column Method.ParamList /// - public ThreadSafe.IList ParamDefs { + public IList ParamDefs { get { - if (paramDefs == null) + if (paramDefs is null) InitializeParamDefs(); return paramDefs; } @@ -145,14 +126,13 @@ public ThreadSafe.IList ParamDefs { /// protected LazyList paramDefs; /// Initializes - protected virtual void InitializeParamDefs() { + protected virtual void InitializeParamDefs() => Interlocked.CompareExchange(ref paramDefs, new LazyList(this), null); - } /// - public ThreadSafe.IList GenericParameters { + public IList GenericParameters { get { - if (genericParameters == null) + if (genericParameters is null) InitializeGenericParameters(); return genericParameters; } @@ -160,24 +140,22 @@ public ThreadSafe.IList GenericParameters { /// protected LazyList genericParameters; /// Initializes - protected virtual void InitializeGenericParameters() { + protected virtual void InitializeGenericParameters() => Interlocked.CompareExchange(ref genericParameters, new LazyList(this), null); - } /// - public ThreadSafe.IList DeclSecurities { + public IList DeclSecurities { get { - if (declSecurities == null) + if (declSecurities is null) InitializeDeclSecurities(); return declSecurities; } } /// - protected ThreadSafe.IList declSecurities; + protected IList declSecurities; /// Initializes - protected virtual void InitializeDeclSecurities() { - Interlocked.CompareExchange(ref declSecurities, ThreadSafeListCreator.Create(), null); - } + protected virtual void InitializeDeclSecurities() => + Interlocked.CompareExchange(ref declSecurities, new List(), null); /// public ImplMap ImplMap { @@ -216,14 +194,10 @@ void InitializeImplMap() { } /// Called to initialize - protected virtual ImplMap GetImplMap_NoLock() { - return null; - } + protected virtual ImplMap GetImplMap_NoLock() => null; /// Reset - protected void ResetImplMap() { - implMap_isInitialized = false; - } + protected void ResetImplMap() => implMap_isInitialized = false; /// /// Gets/sets the method body. See also @@ -283,23 +257,19 @@ public void FreeMethodBody() { } /// Called to initialize - protected virtual MethodBody GetMethodBody_NoLock() { - return null; - } + protected virtual MethodBody GetMethodBody_NoLock() => null; /// /// true if can free the method body /// - protected virtual bool CanFreeMethodBody { - get { return true; } - } + protected virtual bool CanFreeMethodBody => true; /// /// Gets all custom attributes /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -307,64 +277,86 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); + + /// + public int HasCustomDebugInformationTag => 0; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// /// Gets the methods this method implements /// - public ThreadSafe.IList Overrides { + public IList Overrides { get { - if (overrides == null) + if (overrides is null) InitializeOverrides(); return overrides; } } /// - protected ThreadSafe.IList overrides; + protected IList overrides; /// Initializes - protected virtual void InitializeOverrides() { - Interlocked.CompareExchange(ref overrides, ThreadSafeListCreator.Create(), null); + protected virtual void InitializeOverrides() => + Interlocked.CompareExchange(ref overrides, new List(), null); + + /// + /// Gets the export info or null if the method isn't exported to unmanaged code. + /// + public MethodExportInfo ExportInfo { + get => exportInfo; + set => exportInfo = value; } + /// + protected MethodExportInfo exportInfo; /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } - } + public bool HasCustomAttributes => CustomAttributes.Count > 0; /// - public bool HasDeclSecurities { - get { return DeclSecurities.Count > 0; } - } + public bool HasDeclSecurities => DeclSecurities.Count > 0; /// /// true if is not empty /// - public bool HasParamDefs { - get { return ParamDefs.Count > 0; } - } + public bool HasParamDefs => ParamDefs.Count > 0; /// /// Gets/sets the declaring type (owner type) /// public TypeDef DeclaringType { - get { return declaringType2; } + get => declaringType2; set { var currentDeclaringType = DeclaringType2; if (currentDeclaringType == value) return; - if (currentDeclaringType != null) + if (currentDeclaringType is not null) currentDeclaringType.Methods.Remove(this); // Will set DeclaringType2 = null - if (value != null) + if (value is not null) value.Methods.Add(this); // Will set DeclaringType2 = value } } /// - ITypeDefOrRef IMemberRef.DeclaringType { - get { return declaringType2; } - } + ITypeDefOrRef IMemberRef.DeclaringType => declaringType2; /// /// Called by and should normally not be called by any user @@ -372,71 +364,28 @@ ITypeDefOrRef IMemberRef.DeclaringType { /// declaring type without inserting it in the declaring type's method list. /// public TypeDef DeclaringType2 { - get { return declaringType2; } - set { declaringType2 = value; } + get => declaringType2; + set => declaringType2 = value; } /// protected TypeDef declaringType2; /// - public ModuleDef Module { - get { - var dt = declaringType2; - return dt == null ? null : dt.Module; - } - } - - bool IIsTypeOrMethod.IsType { - get { return false; } - } - - bool IIsTypeOrMethod.IsMethod { - get { return true; } - } - - bool IMemberRef.IsField { - get { return false; } - } - - bool IMemberRef.IsTypeSpec { - get { return false; } - } - - bool IMemberRef.IsTypeRef { - get { return false; } - } - - bool IMemberRef.IsTypeDef { - get { return false; } - } - - bool IMemberRef.IsMethodSpec { - get { return false; } - } - - bool IMemberRef.IsMethodDef { - get { return true; } - } - - bool IMemberRef.IsMemberRef { - get { return false; } - } - - bool IMemberRef.IsFieldDef { - get { return false; } - } - - bool IMemberRef.IsPropertyDef { - get { return false; } - } - - bool IMemberRef.IsEventDef { - get { return false; } - } - - bool IMemberRef.IsGenericParam { - get { return false; } - } + public ModuleDef Module => declaringType2?.Module; + + bool IIsTypeOrMethod.IsType => false; + bool IIsTypeOrMethod.IsMethod => true; + bool IMemberRef.IsField => false; + bool IMemberRef.IsTypeSpec => false; + bool IMemberRef.IsTypeRef => false; + bool IMemberRef.IsTypeDef => false; + bool IMemberRef.IsMethodSpec => false; + bool IMemberRef.IsMethodDef => true; + bool IMemberRef.IsMemberRef => false; + bool IMemberRef.IsFieldDef => false; + bool IMemberRef.IsPropertyDef => false; + bool IMemberRef.IsEventDef => false; + bool IMemberRef.IsGenericParam => false; /// /// Gets/sets the CIL method body. See also @@ -447,7 +396,7 @@ public CilBody Body { InitializeMethodBody(); return methodBody as CilBody; } - set { MethodBody = value; } + set => MethodBody = value; } /// @@ -459,67 +408,52 @@ public NativeMethodBody NativeBody { InitializeMethodBody(); return methodBody as NativeMethodBody; } - set { MethodBody = value; } + set => MethodBody = value; } /// /// true if there's at least one in /// - public bool HasGenericParameters { - get { return GenericParameters.Count > 0; } - } + public bool HasGenericParameters => GenericParameters.Count > 0; /// /// true if it has a /// - public bool HasBody { - get { return Body != null; } - } + public bool HasBody => Body is not null; /// /// true if there's at least one in /// - public bool HasOverrides { - get { return Overrides.Count > 0; } - } + public bool HasOverrides => Overrides.Count > 0; /// /// true if is not null /// - public bool HasImplMap { - get { return ImplMap != null; } - } + public bool HasImplMap => ImplMap is not null; /// /// Gets the full name /// - public string FullName { - get { - var dt = declaringType2; - return FullNameCreator.MethodFullName(dt == null ? null : dt.FullName, name, MethodSig, null, null, this); - } - } + public string FullName => FullNameFactory.MethodFullName(declaringType2?.FullName, name, MethodSig, null, null, this, null); /// /// Gets/sets the /// public MethodSig MethodSig { - get { return signature as MethodSig; } - set { signature = value; } + get => signature as MethodSig; + set => signature = value; } /// /// Gets the parameters /// - public ParameterList Parameters { - get { return parameterList; } - } + public ParameterList Parameters => parameterList; /// int IGenericParameterProvider.NumberOfGenericParameters { get { var sig = MethodSig; - return sig == null ? 0 : (int)sig.GenParamCount; + return sig is null ? 0 : (int)sig.GenParamCount; } } @@ -529,7 +463,7 @@ int IGenericParameterProvider.NumberOfGenericParameters { public bool HasThis { get { var ms = MethodSig; - return ms == null ? false : ms.HasThis; + return ms is null ? false : ms.HasThis; } } @@ -539,7 +473,7 @@ public bool HasThis { public bool ExplicitThis { get { var ms = MethodSig; - return ms == null ? false : ms.ExplicitThis; + return ms is null ? false : ms.ExplicitThis; } } @@ -549,7 +483,7 @@ public bool ExplicitThis { public CallingConvention CallingConvention { get { var ms = MethodSig; - return ms == null ? 0 : ms.CallingConvention & CallingConvention.Mask; + return ms is null ? 0 : ms.CallingConvention & CallingConvention.Mask; } } @@ -557,23 +491,14 @@ public CallingConvention CallingConvention { /// Gets/sets the method return type /// public TypeSig ReturnType { - get { - var ms = MethodSig; - return ms == null ? null : ms.RetType; - } - set { - var ms = MethodSig; - if (ms != null) - ms.RetType = value; - } + get => MethodSig?.RetType; + set => parameterList.ReturnParameter.Type = value; } /// /// true if the method returns a value (i.e., return type is not ) /// - public bool HasReturnType { - get { return ReturnType.RemovePinnedAndModifiers().GetElementType() != ElementType.Void; } - } + public bool HasReturnType => ReturnType.RemovePinnedAndModifiers().GetElementType() != ElementType.Void; /// /// Gets/sets the method semantics attributes. If you remove/add a method to a property or @@ -586,16 +511,14 @@ public MethodSemanticsAttributes SemanticsAttributes { InitializeSemanticsAttributes(); return (MethodSemanticsAttributes)semAttrs; } - set { semAttrs = (ushort)value | SEMATTRS_INITD; } + set => semAttrs = (ushort)value | SEMATTRS_INITD; } /// Set when has been initialized protected internal static int SEMATTRS_INITD = unchecked((int)0x80000000); /// protected internal int semAttrs; /// Initializes - protected virtual void InitializeSemanticsAttributes() { - semAttrs = 0 | SEMATTRS_INITD; - } + protected virtual void InitializeSemanticsAttributes() => semAttrs = 0 | SEMATTRS_INITD; /// /// Set or clear flags in @@ -606,21 +529,10 @@ protected virtual void InitializeSemanticsAttributes() { void ModifyAttributes(bool set, MethodSemanticsAttributes flags) { if ((semAttrs & SEMATTRS_INITD) == 0) InitializeSemanticsAttributes(); -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = semAttrs; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref semAttrs, newVal, origVal) != origVal); -#else if (set) semAttrs |= (int)flags; else semAttrs &= ~(int)flags; -#endif } /// @@ -629,17 +541,8 @@ void ModifyAttributes(bool set, MethodSemanticsAttributes flags) { /// /// Value to AND /// Value to OR - void ModifyAttributes(MethodAttributes andMask, MethodAttributes orMask) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - newVal = (origVal & (int)andMask) | (int)orMask; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else + void ModifyAttributes(MethodAttributes andMask, MethodAttributes orMask) => attributes = (attributes & (int)andMask) | (int)orMask; -#endif - } /// /// Set or clear flags in @@ -648,21 +551,10 @@ void ModifyAttributes(MethodAttributes andMask, MethodAttributes orMask) { /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, MethodAttributes flags) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else if (set) attributes |= (int)flags; else attributes &= ~(int)flags; -#endif } /// @@ -671,17 +563,8 @@ void ModifyAttributes(bool set, MethodAttributes flags) { /// /// Value to AND /// Value to OR - void ModifyImplAttributes(MethodImplAttributes andMask, MethodImplAttributes orMask) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = implAttributes; - newVal = (origVal & (int)andMask) | (int)orMask; - } while (Interlocked.CompareExchange(ref implAttributes, newVal, origVal) != origVal); -#else + void ModifyImplAttributes(MethodImplAttributes andMask, MethodImplAttributes orMask) => implAttributes = (implAttributes & (int)andMask) | (int)orMask; -#endif - } /// /// Set or clear flags in @@ -690,380 +573,353 @@ void ModifyImplAttributes(MethodImplAttributes andMask, MethodImplAttributes orM /// be cleared /// Flags to set or clear void ModifyImplAttributes(bool set, MethodImplAttributes flags) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = implAttributes; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref implAttributes, newVal, origVal) != origVal); -#else if (set) implAttributes |= (int)flags; else implAttributes &= ~(int)flags; -#endif } /// /// Gets/sets the method access /// public MethodAttributes Access { - get { return (MethodAttributes)attributes & MethodAttributes.MemberAccessMask; } - set { ModifyAttributes(~MethodAttributes.MemberAccessMask, value & MethodAttributes.MemberAccessMask); } + get => (MethodAttributes)attributes & MethodAttributes.MemberAccessMask; + set => ModifyAttributes(~MethodAttributes.MemberAccessMask, value & MethodAttributes.MemberAccessMask); } /// /// true if is set /// - public bool IsCompilerControlled { - get { return IsPrivateScope; } - } + public bool IsCompilerControlled => IsPrivateScope; /// /// true if is set /// - public bool IsPrivateScope { - get { return ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.PrivateScope; } - } + public bool IsPrivateScope => ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.PrivateScope; /// /// true if is set /// - public bool IsPrivate { - get { return ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Private; } - } + public bool IsPrivate => ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Private; /// /// true if is set /// - public bool IsFamilyAndAssembly { - get { return ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamANDAssem; } - } + public bool IsFamilyAndAssembly => ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamANDAssem; /// /// true if is set /// - public bool IsAssembly { - get { return ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Assembly; } - } + public bool IsAssembly => ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Assembly; /// /// true if is set /// - public bool IsFamily { - get { return ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Family; } - } + public bool IsFamily => ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Family; /// /// true if is set /// - public bool IsFamilyOrAssembly { - get { return ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamORAssem; } - } + public bool IsFamilyOrAssembly => ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamORAssem; /// /// true if is set /// - public bool IsPublic { - get { return ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public; } - } + public bool IsPublic => ((MethodAttributes)attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public; /// /// Gets/sets the bit /// public bool IsStatic { - get { return ((MethodAttributes)attributes & MethodAttributes.Static) != 0; } - set { ModifyAttributes(value, MethodAttributes.Static); } + get => ((MethodAttributes)attributes & MethodAttributes.Static) != 0; + set => ModifyAttributes(value, MethodAttributes.Static); } /// /// Gets/sets the bit /// public bool IsFinal { - get { return ((MethodAttributes)attributes & MethodAttributes.Final) != 0; } - set { ModifyAttributes(value, MethodAttributes.Final); } + get => ((MethodAttributes)attributes & MethodAttributes.Final) != 0; + set => ModifyAttributes(value, MethodAttributes.Final); } /// /// Gets/sets the bit /// public bool IsVirtual { - get { return ((MethodAttributes)attributes & MethodAttributes.Virtual) != 0; } - set { ModifyAttributes(value, MethodAttributes.Virtual); } + get => ((MethodAttributes)attributes & MethodAttributes.Virtual) != 0; + set => ModifyAttributes(value, MethodAttributes.Virtual); } /// /// Gets/sets the bit /// public bool IsHideBySig { - get { return ((MethodAttributes)attributes & MethodAttributes.HideBySig) != 0; } - set { ModifyAttributes(value, MethodAttributes.HideBySig); } + get => ((MethodAttributes)attributes & MethodAttributes.HideBySig) != 0; + set => ModifyAttributes(value, MethodAttributes.HideBySig); } /// /// Gets/sets the bit /// public bool IsNewSlot { - get { return ((MethodAttributes)attributes & MethodAttributes.NewSlot) != 0; } - set { ModifyAttributes(value, MethodAttributes.NewSlot); } + get => ((MethodAttributes)attributes & MethodAttributes.NewSlot) != 0; + set => ModifyAttributes(value, MethodAttributes.NewSlot); } /// /// Gets/sets the bit /// public bool IsReuseSlot { - get { return ((MethodAttributes)attributes & MethodAttributes.NewSlot) == 0; } - set { ModifyAttributes(!value, MethodAttributes.NewSlot); } + get => ((MethodAttributes)attributes & MethodAttributes.NewSlot) == 0; + set => ModifyAttributes(!value, MethodAttributes.NewSlot); } /// /// Gets/sets the bit /// public bool IsCheckAccessOnOverride { - get { return ((MethodAttributes)attributes & MethodAttributes.CheckAccessOnOverride) != 0; } - set { ModifyAttributes(value, MethodAttributes.CheckAccessOnOverride); } + get => ((MethodAttributes)attributes & MethodAttributes.CheckAccessOnOverride) != 0; + set => ModifyAttributes(value, MethodAttributes.CheckAccessOnOverride); } /// /// Gets/sets the bit /// public bool IsAbstract { - get { return ((MethodAttributes)attributes & MethodAttributes.Abstract) != 0; } - set { ModifyAttributes(value, MethodAttributes.Abstract); } + get => ((MethodAttributes)attributes & MethodAttributes.Abstract) != 0; + set => ModifyAttributes(value, MethodAttributes.Abstract); } /// /// Gets/sets the bit /// public bool IsSpecialName { - get { return ((MethodAttributes)attributes & MethodAttributes.SpecialName) != 0; } - set { ModifyAttributes(value, MethodAttributes.SpecialName); } + get => ((MethodAttributes)attributes & MethodAttributes.SpecialName) != 0; + set => ModifyAttributes(value, MethodAttributes.SpecialName); } /// /// Gets/sets the bit /// public bool IsPinvokeImpl { - get { return ((MethodAttributes)attributes & MethodAttributes.PinvokeImpl) != 0; } - set { ModifyAttributes(value, MethodAttributes.PinvokeImpl); } + get => ((MethodAttributes)attributes & MethodAttributes.PinvokeImpl) != 0; + set => ModifyAttributes(value, MethodAttributes.PinvokeImpl); } /// /// Gets/sets the bit /// public bool IsUnmanagedExport { - get { return ((MethodAttributes)attributes & MethodAttributes.UnmanagedExport) != 0; } - set { ModifyAttributes(value, MethodAttributes.UnmanagedExport); } + get => ((MethodAttributes)attributes & MethodAttributes.UnmanagedExport) != 0; + set => ModifyAttributes(value, MethodAttributes.UnmanagedExport); } /// /// Gets/sets the bit /// public bool IsRuntimeSpecialName { - get { return ((MethodAttributes)attributes & MethodAttributes.RTSpecialName) != 0; } - set { ModifyAttributes(value, MethodAttributes.RTSpecialName); } + get => ((MethodAttributes)attributes & MethodAttributes.RTSpecialName) != 0; + set => ModifyAttributes(value, MethodAttributes.RTSpecialName); } /// /// Gets/sets the bit /// public bool HasSecurity { - get { return ((MethodAttributes)attributes & MethodAttributes.HasSecurity) != 0; } - set { ModifyAttributes(value, MethodAttributes.HasSecurity); } + get => ((MethodAttributes)attributes & MethodAttributes.HasSecurity) != 0; + set => ModifyAttributes(value, MethodAttributes.HasSecurity); } /// /// Gets/sets the bit /// public bool IsRequireSecObject { - get { return ((MethodAttributes)attributes & MethodAttributes.RequireSecObject) != 0; } - set { ModifyAttributes(value, MethodAttributes.RequireSecObject); } + get => ((MethodAttributes)attributes & MethodAttributes.RequireSecObject) != 0; + set => ModifyAttributes(value, MethodAttributes.RequireSecObject); } /// /// Gets/sets the code type /// public MethodImplAttributes CodeType { - get { return (MethodImplAttributes)implAttributes & MethodImplAttributes.CodeTypeMask; } - set { ModifyImplAttributes(~MethodImplAttributes.CodeTypeMask, value & MethodImplAttributes.CodeTypeMask); } + get => (MethodImplAttributes)implAttributes & MethodImplAttributes.CodeTypeMask; + set => ModifyImplAttributes(~MethodImplAttributes.CodeTypeMask, value & MethodImplAttributes.CodeTypeMask); } /// /// true if is set /// - public bool IsIL { - get { return ((MethodImplAttributes)implAttributes & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.IL; } - } + public bool IsIL => ((MethodImplAttributes)implAttributes & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.IL; /// /// true if is set /// - public bool IsNative { - get { return ((MethodImplAttributes)implAttributes & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.Native; } - } + public bool IsNative => ((MethodImplAttributes)implAttributes & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.Native; /// /// true if is set /// - public bool IsOPTIL { - get { return ((MethodImplAttributes)implAttributes & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.OPTIL; } - } + public bool IsOPTIL => ((MethodImplAttributes)implAttributes & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.OPTIL; /// /// true if is set /// - public bool IsRuntime { - get { return ((MethodImplAttributes)implAttributes & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.Runtime; } - } + public bool IsRuntime => ((MethodImplAttributes)implAttributes & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.Runtime; /// /// Gets/sets the bit /// public bool IsUnmanaged { - get { return ((MethodImplAttributes)implAttributes & MethodImplAttributes.Unmanaged) != 0; } - set { ModifyImplAttributes(value, MethodImplAttributes.Unmanaged); } + get => ((MethodImplAttributes)implAttributes & MethodImplAttributes.Unmanaged) != 0; + set => ModifyImplAttributes(value, MethodImplAttributes.Unmanaged); } /// /// Gets/sets the bit /// public bool IsManaged { - get { return ((MethodImplAttributes)implAttributes & MethodImplAttributes.Unmanaged) == 0; } - set { ModifyImplAttributes(!value, MethodImplAttributes.Unmanaged); } + get => ((MethodImplAttributes)implAttributes & MethodImplAttributes.Unmanaged) == 0; + set => ModifyImplAttributes(!value, MethodImplAttributes.Unmanaged); } /// /// Gets/sets the bit /// public bool IsForwardRef { - get { return ((MethodImplAttributes)implAttributes & MethodImplAttributes.ForwardRef) != 0; } - set { ModifyImplAttributes(value, MethodImplAttributes.ForwardRef); } + get => ((MethodImplAttributes)implAttributes & MethodImplAttributes.ForwardRef) != 0; + set => ModifyImplAttributes(value, MethodImplAttributes.ForwardRef); } /// /// Gets/sets the bit /// public bool IsPreserveSig { - get { return ((MethodImplAttributes)implAttributes & MethodImplAttributes.PreserveSig) != 0; } - set { ModifyImplAttributes(value, MethodImplAttributes.PreserveSig); } + get => ((MethodImplAttributes)implAttributes & MethodImplAttributes.PreserveSig) != 0; + set => ModifyImplAttributes(value, MethodImplAttributes.PreserveSig); } /// /// Gets/sets the bit /// public bool IsInternalCall { - get { return ((MethodImplAttributes)implAttributes & MethodImplAttributes.InternalCall) != 0; } - set { ModifyImplAttributes(value, MethodImplAttributes.InternalCall); } + get => ((MethodImplAttributes)implAttributes & MethodImplAttributes.InternalCall) != 0; + set => ModifyImplAttributes(value, MethodImplAttributes.InternalCall); } /// /// Gets/sets the bit /// public bool IsSynchronized { - get { return ((MethodImplAttributes)implAttributes & MethodImplAttributes.Synchronized) != 0; } - set { ModifyImplAttributes(value, MethodImplAttributes.Synchronized); } + get => ((MethodImplAttributes)implAttributes & MethodImplAttributes.Synchronized) != 0; + set => ModifyImplAttributes(value, MethodImplAttributes.Synchronized); } /// /// Gets/sets the bit /// public bool IsNoInlining { - get { return ((MethodImplAttributes)implAttributes & MethodImplAttributes.NoInlining) != 0; } - set { ModifyImplAttributes(value, MethodImplAttributes.NoInlining); } + get => ((MethodImplAttributes)implAttributes & MethodImplAttributes.NoInlining) != 0; + set => ModifyImplAttributes(value, MethodImplAttributes.NoInlining); } /// /// Gets/sets the bit /// public bool IsAggressiveInlining { - get { return ((MethodImplAttributes)implAttributes & MethodImplAttributes.AggressiveInlining) != 0; } - set { ModifyImplAttributes(value, MethodImplAttributes.AggressiveInlining); } + get => ((MethodImplAttributes)implAttributes & MethodImplAttributes.AggressiveInlining) != 0; + set => ModifyImplAttributes(value, MethodImplAttributes.AggressiveInlining); } /// /// Gets/sets the bit /// public bool IsNoOptimization { - get { return ((MethodImplAttributes)implAttributes & MethodImplAttributes.NoOptimization) != 0; } - set { ModifyImplAttributes(value, MethodImplAttributes.NoOptimization); } + get => ((MethodImplAttributes)implAttributes & MethodImplAttributes.NoOptimization) != 0; + set => ModifyImplAttributes(value, MethodImplAttributes.NoOptimization); + } + + /// + /// Gets/sets the bit + /// + public bool IsAggressiveOptimization { + get => ((MethodImplAttributes)implAttributes & MethodImplAttributes.AggressiveOptimization) != 0; + set => ModifyImplAttributes(value, MethodImplAttributes.AggressiveOptimization); + } + + /// + /// Gets/sets the bit + /// + public bool HasSecurityMitigations { + get => ((MethodImplAttributes)implAttributes & MethodImplAttributes.SecurityMitigations) != 0; + set => ModifyImplAttributes(value, MethodImplAttributes.SecurityMitigations); } /// /// Gets/sets the bit /// public bool IsSetter { - get { return (SemanticsAttributes & MethodSemanticsAttributes.Setter) != 0; } - set { ModifyAttributes(value, MethodSemanticsAttributes.Setter); } + get => (SemanticsAttributes & MethodSemanticsAttributes.Setter) != 0; + set => ModifyAttributes(value, MethodSemanticsAttributes.Setter); } /// /// Gets/sets the bit /// public bool IsGetter { - get { return (SemanticsAttributes & MethodSemanticsAttributes.Getter) != 0; } - set { ModifyAttributes(value, MethodSemanticsAttributes.Getter); } + get => (SemanticsAttributes & MethodSemanticsAttributes.Getter) != 0; + set => ModifyAttributes(value, MethodSemanticsAttributes.Getter); } /// /// Gets/sets the bit /// public bool IsOther { - get { return (SemanticsAttributes & MethodSemanticsAttributes.Other) != 0; } - set { ModifyAttributes(value, MethodSemanticsAttributes.Other); } + get => (SemanticsAttributes & MethodSemanticsAttributes.Other) != 0; + set => ModifyAttributes(value, MethodSemanticsAttributes.Other); } /// /// Gets/sets the bit /// public bool IsAddOn { - get { return (SemanticsAttributes & MethodSemanticsAttributes.AddOn) != 0; } - set { ModifyAttributes(value, MethodSemanticsAttributes.AddOn); } + get => (SemanticsAttributes & MethodSemanticsAttributes.AddOn) != 0; + set => ModifyAttributes(value, MethodSemanticsAttributes.AddOn); } /// /// Gets/sets the bit /// public bool IsRemoveOn { - get { return (SemanticsAttributes & MethodSemanticsAttributes.RemoveOn) != 0; } - set { ModifyAttributes(value, MethodSemanticsAttributes.RemoveOn); } + get => (SemanticsAttributes & MethodSemanticsAttributes.RemoveOn) != 0; + set => ModifyAttributes(value, MethodSemanticsAttributes.RemoveOn); } /// /// Gets/sets the bit /// public bool IsFire { - get { return (SemanticsAttributes & MethodSemanticsAttributes.Fire) != 0; } - set { ModifyAttributes(value, MethodSemanticsAttributes.Fire); } + get => (SemanticsAttributes & MethodSemanticsAttributes.Fire) != 0; + set => ModifyAttributes(value, MethodSemanticsAttributes.Fire); } /// /// true if this is the static type constructor /// - public bool IsStaticConstructor { - get { return IsRuntimeSpecialName && UTF8String.Equals(name, StaticConstructorName); } - } + public bool IsStaticConstructor => IsRuntimeSpecialName && UTF8String.Equals(name, StaticConstructorName); /// /// true if this is an instance constructor /// - public bool IsInstanceConstructor { - get { return IsRuntimeSpecialName && UTF8String.Equals(name, InstanceConstructorName); } - } + public bool IsInstanceConstructor => IsRuntimeSpecialName && UTF8String.Equals(name, InstanceConstructorName); /// /// true if this is a static or an instance constructor /// - public bool IsConstructor { - get { return IsStaticConstructor || IsInstanceConstructor; } - } + public bool IsConstructor => IsStaticConstructor || IsInstanceConstructor; /// - void IListListener.OnLazyAdd(int index, ref GenericParam value) { - OnLazyAdd2(index, ref value); - } + void IListListener.OnLazyAdd(int index, ref GenericParam value) => OnLazyAdd2(index, ref value); internal virtual void OnLazyAdd2(int index, ref GenericParam value) { #if DEBUG @@ -1074,15 +930,13 @@ internal virtual void OnLazyAdd2(int index, ref GenericParam value) { /// void IListListener.OnAdd(int index, GenericParam value) { - if (value.Owner != null) + if (value.Owner is not null) throw new InvalidOperationException("Generic param is already owned by another type/method. Set Owner to null first."); value.Owner = this; } /// - void IListListener.OnRemove(int index, GenericParam value) { - value.Owner = null; - } + void IListListener.OnRemove(int index, GenericParam value) => value.Owner = null; /// void IListListener.OnResize(int index) { @@ -1090,14 +944,12 @@ void IListListener.OnResize(int index) { /// void IListListener.OnClear() { - foreach (var gp in GenericParameters.GetEnumerable_NoLock()) + foreach (var gp in genericParameters.GetEnumerable_NoLock()) gp.Owner = null; } /// - void IListListener.OnLazyAdd(int index, ref ParamDef value) { - OnLazyAdd2(index, ref value); - } + void IListListener.OnLazyAdd(int index, ref ParamDef value) => OnLazyAdd2(index, ref value); internal virtual void OnLazyAdd2(int index, ref ParamDef value) { #if DEBUG @@ -1108,15 +960,13 @@ internal virtual void OnLazyAdd2(int index, ref ParamDef value) { /// void IListListener.OnAdd(int index, ParamDef value) { - if (value.DeclaringMethod != null) + if (value.DeclaringMethod is not null) throw new InvalidOperationException("Param is already owned by another method. Set DeclaringMethod to null first."); value.DeclaringMethod = this; } /// - void IListListener.OnRemove(int index, ParamDef value) { - value.DeclaringMethod = null; - } + void IListListener.OnRemove(int index, ParamDef value) => value.DeclaringMethod = null; /// void IListListener.OnResize(int index) { @@ -1124,14 +974,12 @@ void IListListener.OnResize(int index) { /// void IListListener.OnClear() { - foreach (var pd in ParamDefs.GetEnumerable_NoLock()) + foreach (var pd in paramDefs.GetEnumerable_NoLock()) pd.DeclaringMethod = null; } /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } /// @@ -1142,10 +990,10 @@ public class MethodDefUser : MethodDef { /// Default constructor /// public MethodDefUser() { - this.paramDefs = new LazyList(this); - this.genericParameters = new LazyList(this); - this.parameterList = new ParameterList(this, null); - this.semAttrs = 0 | SEMATTRS_INITD; + paramDefs = new LazyList(this); + genericParameters = new LazyList(this); + parameterList = new ParameterList(this, null); + semAttrs = 0 | SEMATTRS_INITD; } /// @@ -1194,13 +1042,13 @@ public MethodDefUser(UTF8String name, MethodSig methodSig, MethodImplAttributes /// Flags public MethodDefUser(UTF8String name, MethodSig methodSig, MethodImplAttributes implFlags, MethodAttributes flags) { this.name = name; - this.signature = methodSig; - this.paramDefs = new LazyList(this); - this.genericParameters = new LazyList(this); - this.implAttributes = (int)implFlags; - this.attributes = (int)flags; - this.parameterList = new ParameterList(this, null); - this.semAttrs = 0 | SEMATTRS_INITD; + signature = methodSig; + paramDefs = new LazyList(this); + genericParameters = new LazyList(this); + implAttributes = (int)implFlags; + attributes = (int)flags; + parameterList = new ParameterList(this, null); + semAttrs = 0 | SEMATTRS_INITD; } } @@ -1216,59 +1064,61 @@ sealed class MethodDefMD : MethodDef, IMDTokenProviderMD { readonly MethodImplAttributes origImplAttributes; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override void InitializeParamDefs() { - var list = readerModule.MetaData.GetParamRidList(origRid); - var tmp = new LazyList((int)list.Length, this, list, (list2, index) => readerModule.ResolveParam(((RidList)list2)[index])); + var list = readerModule.Metadata.GetParamRidList(origRid); + var tmp = new LazyList(list.Count, this, list, (list2, index) => readerModule.ResolveParam(list2[index])); Interlocked.CompareExchange(ref paramDefs, tmp, null); } /// protected override void InitializeGenericParameters() { - var list = readerModule.MetaData.GetGenericParamRidList(Table.Method, origRid); - var tmp = new LazyList((int)list.Length, this, list, (list2, index) => readerModule.ResolveGenericParam(((RidList)list2)[index])); + var list = readerModule.Metadata.GetGenericParamRidList(Table.Method, origRid); + var tmp = new LazyList(list.Count, this, list, (list2, index) => readerModule.ResolveGenericParam(list2[index])); Interlocked.CompareExchange(ref genericParameters, tmp, null); } /// protected override void InitializeDeclSecurities() { - var list = readerModule.MetaData.GetDeclSecurityRidList(Table.Method, origRid); - var tmp = new LazyList((int)list.Length, list, (list2, index) => readerModule.ResolveDeclSecurity(((RidList)list2)[index])); + var list = readerModule.Metadata.GetDeclSecurityRidList(Table.Method, origRid); + var tmp = new LazyList(list.Count, list, (list2, index) => readerModule.ResolveDeclSecurity(list2[index])); Interlocked.CompareExchange(ref declSecurities, tmp, null); } /// - protected override ImplMap GetImplMap_NoLock() { - return readerModule.ResolveImplMap(readerModule.MetaData.GetImplMapRid(Table.Method, origRid)); - } + protected override ImplMap GetImplMap_NoLock() => readerModule.ResolveImplMap(readerModule.Metadata.GetImplMapRid(Table.Method, origRid)); /// - protected override MethodBody GetMethodBody_NoLock() { - return readerModule.ReadMethodBody(this, origRva, origImplAttributes, new GenericParamContext(declaringType2, this)); - } + protected override MethodBody GetMethodBody_NoLock() => readerModule.ReadMethodBody(this, origRva, origImplAttributes, new GenericParamContext(declaringType2, this)); /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.Method, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Method, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + if (Interlocked.CompareExchange(ref customDebugInfos, list, null) is null) { + var body = Body; + readerModule.InitializeCustomDebugInfos(this, body, list); + } + } + /// protected override void InitializeOverrides() { var dt = declaringType2 as TypeDefMD; - var tmp = dt == null ? ThreadSafeListCreator.Create() : dt.GetMethodOverrides(this, new GenericParamContext(declaringType2, this)); + var tmp = dt is null ? new List() : dt.GetMethodOverrides(this, new GenericParamContext(declaringType2, this)); Interlocked.CompareExchange(ref overrides, tmp, null); } /// protected override void InitializeSemanticsAttributes() { - var dt = DeclaringType as TypeDefMD; - if (dt != null) + if (DeclaringType is TypeDefMD dt) dt.InitializeMethodSemanticsAttributes(); semAttrs |= SEMATTRS_INITD; } @@ -1282,22 +1132,26 @@ protected override void InitializeSemanticsAttributes() { /// If is invalid public MethodDefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.MethodTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("Method rid {0} does not exist", rid)); + throw new BadImageFormatException($"Method rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint name; - uint signature = readerModule.TablesStream.ReadMethodRow(origRid, out this.rva, out this.implAttributes, out this.attributes, out name); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.origRva = rva; - this.origImplAttributes = (MethodImplAttributes)implAttributes; - this.declaringType2 = readerModule.GetOwnerType(this); - this.signature = readerModule.ReadSignature(signature, new GenericParamContext(declaringType2, this)); - this.parameterList = new ParameterList(this, declaringType2); + bool b = readerModule.TablesStream.TryReadMethodRow(origRid, out var row); + Debug.Assert(b); + rva = (RVA)row.RVA; + implAttributes = row.ImplFlags; + attributes = row.Flags; + name = readerModule.StringsStream.ReadNoNull(row.Name); + origRva = rva; + origImplAttributes = (MethodImplAttributes)implAttributes; + declaringType2 = readerModule.GetOwnerType(this); + signature = readerModule.ReadSignature(row.Signature, new GenericParamContext(declaringType2, this)); + parameterList = new ParameterList(this, declaringType2); + exportInfo = readerModule.GetExportInfo(rid); } internal MethodDefMD InitializeAll() { diff --git a/src/DotNet/MethodExportInfo.cs b/src/DotNet/MethodExportInfo.cs new file mode 100644 index 000000000..3a22aee38 --- /dev/null +++ b/src/DotNet/MethodExportInfo.cs @@ -0,0 +1,115 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Diagnostics; + +namespace dnlib.DotNet { + /// + /// Contains the name and ordinal of a method that gets exported to unmanaged code. + /// + [DebuggerDisplay("{Ordinal} {Name} {Options}")] + public sealed class MethodExportInfo { + MethodExportInfoOptions options; + ushort? ordinal; + string name; + + const MethodExportInfoOptions DefaultOptions = MethodExportInfoOptions.FromUnmanaged; + + /// + /// Gets the ordinal or null + /// + public ushort? Ordinal { + get => ordinal; + set => ordinal = value; + } + + /// + /// Gets the name. If it's null, and is also null, the name of the method + /// () is used as the exported name. + /// + public string Name { + get => name; + set => name = value; + } + + /// + /// Gets the options + /// + public MethodExportInfoOptions Options { + get => options; + set => options = value; + } + + /// + /// Constructor + /// + public MethodExportInfo() => options = DefaultOptions; + + /// + /// Constructor + /// + /// Name or null to export by ordinal + public MethodExportInfo(string name) { + options = DefaultOptions; + this.name = name; + } + + /// + /// Constructor + /// + /// Ordinal + public MethodExportInfo(ushort ordinal) { + options = DefaultOptions; + this.ordinal = ordinal; + } + + /// + /// Constructor + /// + /// Name or null to export by ordinal + /// Ordinal or null to export by name + public MethodExportInfo(string name, ushort? ordinal) { + options = DefaultOptions; + this.name = name; + this.ordinal = ordinal; + } + + /// + /// Constructor + /// + /// Name or null to export by ordinal + /// Ordinal or null to export by name + /// Options + public MethodExportInfo(string name, ushort? ordinal, MethodExportInfoOptions options) { + this.options = options; + this.name = name; + this.ordinal = ordinal; + } + } + + /// + /// Exported method options + /// + [Flags] + public enum MethodExportInfoOptions { + /// + /// No bit is set + /// + None = 0, + + /// + /// Transition from unmanaged code + /// + FromUnmanaged = 0x00000001, + + /// + /// Also retain app domain + /// + FromUnmanagedRetainAppDomain = 0x00000002, + + /// + /// Call most derived method + /// + CallMostDerived = 0x00000004, + } +} diff --git a/src/DotNet/MethodExportInfoProvider.cs b/src/DotNet/MethodExportInfoProvider.cs new file mode 100644 index 000000000..592f45fc6 --- /dev/null +++ b/src/DotNet/MethodExportInfoProvider.cs @@ -0,0 +1,153 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Collections.Generic; +using System.IO; +using dnlib.PE; +using dnlib.IO; +using System.Diagnostics; + +namespace dnlib.DotNet { + sealed class MethodExportInfoProvider { + readonly Dictionary toInfo; + + public MethodExportInfoProvider(ModuleDefMD module) { + toInfo = new Dictionary(); + try { + Initialize(module); + } + catch (OutOfMemoryException) { + } + catch (IOException) { + } + } + + void Initialize(ModuleDefMD module) { + var vtblHdr = module.Metadata.ImageCor20Header.VTableFixups; + if (vtblHdr.VirtualAddress == 0 || vtblHdr.Size == 0) + return; + + var peImage = module.Metadata.PEImage; + var exportHdr = peImage.ImageNTHeaders.OptionalHeader.DataDirectories[0]; + if (exportHdr.VirtualAddress == 0 || exportHdr.Size < 0x28) + return; + + if (!CpuArch.TryGetCpuArch(peImage.ImageNTHeaders.FileHeader.Machine, out var cpuArch)) { + Debug.Fail($"Exported methods: Unsupported machine: {peImage.ImageNTHeaders.FileHeader.Machine}"); + return; + } + + var reader = peImage.CreateReader(); + var offsetToInfo = GetOffsetToExportInfoDictionary(ref reader, peImage, exportHdr, cpuArch); + reader.Position = (uint)peImage.ToFileOffset(vtblHdr.VirtualAddress); + ulong endPos = (ulong)reader.Position + vtblHdr.Size; + while ((ulong)reader.Position + 8 <= endPos && reader.CanRead(8U)) { + var tableRva = (RVA)reader.ReadUInt32(); + int numSlots = reader.ReadUInt16(); + var flags = (VTableFlags)reader.ReadUInt16(); + bool is64bit = (flags & VTableFlags.Bit64) != 0; + var exportOptions = ToMethodExportInfoOptions(flags); + + var pos = reader.Position; + reader.Position = (uint)peImage.ToFileOffset(tableRva); + uint slotSize = is64bit ? 8U : 4; + while (numSlots-- > 0 && reader.CanRead(slotSize)) { + var tokenPos = reader.Position; + uint token = reader.ReadUInt32(); + if (offsetToInfo.TryGetValue(tokenPos, out var exportInfo)) + toInfo[token] = new MethodExportInfo(exportInfo.Name, exportInfo.Ordinal, exportOptions); + if (slotSize == 8) + reader.ReadUInt32(); + } + reader.Position = pos; + } + } + + static MethodExportInfoOptions ToMethodExportInfoOptions(VTableFlags flags) { + var res = MethodExportInfoOptions.None; + if ((flags & VTableFlags.FromUnmanaged) != 0) + res |= MethodExportInfoOptions.FromUnmanaged; + if ((flags & VTableFlags.FromUnmanagedRetainAppDomain) != 0) + res |= MethodExportInfoOptions.FromUnmanagedRetainAppDomain; + if ((flags & VTableFlags.CallMostDerived) != 0) + res |= MethodExportInfoOptions.CallMostDerived; + return res; + } + + static Dictionary GetOffsetToExportInfoDictionary(ref DataReader reader, IPEImage peImage, ImageDataDirectory exportHdr, CpuArch cpuArch) { + reader.Position = (uint)peImage.ToFileOffset(exportHdr.VirtualAddress); + // Skip Characteristics(4), TimeDateStamp(4), MajorVersion(2), MinorVersion(2), Name(4) + reader.Position += 16; + uint ordinalBase = reader.ReadUInt32(); + int numFuncs = reader.ReadInt32(); + int numNames = reader.ReadInt32(); + uint offsetOfFuncs = (uint)peImage.ToFileOffset((RVA)reader.ReadUInt32()); + uint offsetOfNames = (uint)peImage.ToFileOffset((RVA)reader.ReadUInt32()); + uint offsetOfNameIndexes = (uint)peImage.ToFileOffset((RVA)reader.ReadUInt32()); + + var names = ReadNames(ref reader, peImage, numNames, offsetOfNames, offsetOfNameIndexes); + reader.Position = offsetOfFuncs; + var allInfos = new MethodExportInfo[numFuncs]; + var dict = new Dictionary(numFuncs); + for (int i = 0; i < allInfos.Length; i++) { + var nextOffset = reader.Position + 4; + uint funcRva = 0; + var rva = (RVA)reader.ReadUInt32(); + reader.Position = (uint)peImage.ToFileOffset(rva); + bool rvaValid = rva != 0 && cpuArch.TryGetExportedRvaFromStub(ref reader, peImage, out funcRva); + uint funcOffset = rvaValid ? (uint)peImage.ToFileOffset((RVA)funcRva) : 0; + var exportInfo = new MethodExportInfo((ushort)(ordinalBase + (uint)i)); + if (funcOffset != 0) + dict[funcOffset] = exportInfo; + allInfos[i] = exportInfo; + reader.Position = nextOffset; + } + + foreach (var info in names) { + int index = info.Index; + if ((uint)index >= (uint)numFuncs) + continue; + allInfos[index].Ordinal = null; + allInfos[index].Name = info.Name; + } + + return dict; + } + + static NameAndIndex[] ReadNames(ref DataReader reader, IPEImage peImage, int numNames, uint offsetOfNames, uint offsetOfNameIndexes) { + var names = new NameAndIndex[numNames]; + + reader.Position = offsetOfNameIndexes; + for (int i = 0; i < names.Length; i++) + names[i].Index = reader.ReadUInt16(); + + var currentOffset = offsetOfNames; + for (int i = 0; i < names.Length; i++, currentOffset += 4) { + reader.Position = currentOffset; + uint offsetOfName = (uint)peImage.ToFileOffset((RVA)reader.ReadUInt32()); + names[i].Name = ReadMethodNameASCIIZ(ref reader, offsetOfName); + } + + return names; + } + + struct NameAndIndex { + public string Name; + public int Index; + } + + // If this method gets updated, also update the writer (ManagedExportsWriter) + static string ReadMethodNameASCIIZ(ref DataReader reader, uint offset) { + reader.Position = offset; + return reader.TryReadZeroTerminatedUtf8String() ?? string.Empty; + } + + public MethodExportInfo GetMethodExportInfo(uint token) { + if (toInfo.Count == 0) + return null; + if (toInfo.TryGetValue(token, out var info)) + return new MethodExportInfo(info.Name, info.Ordinal, info.Options); + return null; + } + } +} diff --git a/src/DotNet/MethodImplAttributes.cs b/src/DotNet/MethodImplAttributes.cs index 06b1b45e4..b1d3b3a48 100644 --- a/src/DotNet/MethodImplAttributes.cs +++ b/src/DotNet/MethodImplAttributes.cs @@ -9,38 +9,42 @@ namespace dnlib.DotNet { [Flags] public enum MethodImplAttributes : ushort { /// Flags about code type. - CodeTypeMask = 0x0003, + CodeTypeMask = 0x0003, /// Method impl is IL. - IL = 0x0000, + IL = 0x0000, /// Method impl is native. - Native = 0x0001, + Native = 0x0001, /// Method impl is OPTIL - OPTIL = 0x0002, + OPTIL = 0x0002, /// Method impl is provided by the runtime. - Runtime = 0x0003, + Runtime = 0x0003, /// Flags specifying whether the code is managed or unmanaged. - ManagedMask = 0x0004, + ManagedMask = 0x0004, /// Method impl is unmanaged, otherwise managed. - Unmanaged = 0x0004, + Unmanaged = 0x0004, /// Method impl is managed. - Managed = 0x0000, + Managed = 0x0000, /// Indicates method is defined; used primarily in merge scenarios. - ForwardRef = 0x0010, + ForwardRef = 0x0010, /// Indicates method sig is not to be mangled to do HRESULT conversion. - PreserveSig = 0x0080, + PreserveSig = 0x0080, /// Reserved for internal use. - InternalCall = 0x1000, + InternalCall = 0x1000, /// Method is single threaded through the body. - Synchronized = 0x0020, + Synchronized = 0x0020, /// Method may not be inlined. - NoInlining = 0x0008, + NoInlining = 0x0008, /// Method should be inlined if possible. - AggressiveInlining = 0x0100, + AggressiveInlining = 0x0100, /// Method may not be optimized. - NoOptimization = 0x0040, + NoOptimization = 0x0040, + /// Method may contain hot code and should be aggressively optimized. + AggressiveOptimization = 0x0200, + /// The JIT compiler should look for security mitigation attributes, such as the user-defined System.Runtime.CompilerServices.SecurityMitigationsAttribute. If found, the JIT compiler applies any related security mitigations. Available starting with .NET Framework 4.8. + SecurityMitigations = 0x0400, } } diff --git a/src/DotNet/MethodOverride.cs b/src/DotNet/MethodOverride.cs index b5d2feeb1..901ed6081 100644 --- a/src/DotNet/MethodOverride.cs +++ b/src/DotNet/MethodOverride.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// Describes which method some method implements /// @@ -21,8 +21,8 @@ public struct MethodOverride { /// Method body /// The method implements public MethodOverride(IMethodDefOrRef methodBody, IMethodDefOrRef methodDeclaration) { - this.MethodBody = methodBody; - this.MethodDeclaration = methodDeclaration; + MethodBody = methodBody; + MethodDeclaration = methodDeclaration; } } } diff --git a/src/DotNet/MethodSemanticsAttributes.cs b/src/DotNet/MethodSemanticsAttributes.cs index 9780bbabd..488e92e9a 100644 --- a/src/DotNet/MethodSemanticsAttributes.cs +++ b/src/DotNet/MethodSemanticsAttributes.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.DotNet { /// diff --git a/src/DotNet/MethodSpec.cs b/src/DotNet/MethodSpec.cs index 5e1903b7a..de3393acf 100644 --- a/src/DotNet/MethodSpec.cs +++ b/src/DotNet/MethodSpec.cs @@ -1,41 +1,40 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// /// A high-level representation of a row in the MethodSpec table /// - public abstract class MethodSpec : IHasCustomAttribute, IMethod, IContainsGenericParameter { + public abstract class MethodSpec : IHasCustomAttribute, IHasCustomDebugInformation, IMethod, IContainsGenericParameter { /// /// The row id in its table /// protected uint rid; /// - public MDToken MDToken { - get { return new MDToken(Table.MethodSpec, rid); } - } + public MDToken MDToken => new MDToken(Table.MethodSpec, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 21; } - } + public int HasCustomAttributeTag => 21; /// /// From column MethodSpec.Method /// public IMethodDefOrRef Method { - get { return method; } - set { method = value; } + get => method; + set => method = value; } /// protected IMethodDefOrRef method; @@ -44,8 +43,8 @@ public IMethodDefOrRef Method { /// From column MethodSpec.Instantiation /// public CallingConventionSig Instantiation { - get { return instantiation; } - set { instantiation = value; } + get => instantiation; + set => instantiation = value; } /// protected CallingConventionSig instantiation; @@ -55,7 +54,7 @@ public CallingConventionSig Instantiation { /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -63,24 +62,40 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } - } + public bool HasCustomAttributes => CustomAttributes.Count > 0; /// - MethodSig IMethod.MethodSig { + public int HasCustomDebugInformationTag => 21; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { get { - var m = method; - return m == null ? null : m.MethodSig; + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; } + } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); + + /// + MethodSig IMethod.MethodSig { + get => method?.MethodSig; set { var m = method; - if (m != null) + if (m is not null) m.MethodSig = value; } } @@ -89,69 +104,48 @@ MethodSig IMethod.MethodSig { public UTF8String Name { get { var m = method; - return m == null ? UTF8String.Empty : m.Name; + return m is null ? UTF8String.Empty : m.Name; } set { var m = method; - if (m != null) + if (m is not null) m.Name = value; } } /// - public ITypeDefOrRef DeclaringType { - get { - var m = method; - return m == null ? null : m.DeclaringType; - } - } + public ITypeDefOrRef DeclaringType => method?.DeclaringType; /// /// Gets/sets the generic instance method sig /// public GenericInstMethodSig GenericInstMethodSig { - get { return instantiation as GenericInstMethodSig; } - set { instantiation = value; } + get => instantiation as GenericInstMethodSig; + set => instantiation = value; } /// - int IGenericParameterProvider.NumberOfGenericParameters { - get { - var sig = GenericInstMethodSig; - return sig == null ? 0 : sig.GenericArguments.Count; - } - } + int IGenericParameterProvider.NumberOfGenericParameters => GenericInstMethodSig?.GenericArguments.Count ?? 0; /// - public ModuleDef Module { - get { - var m = method; - return m == null ? null : m.Module; - } - } + public ModuleDef Module => method?.Module; /// /// Gets the full name /// public string FullName { get { - var gims = GenericInstMethodSig; - var methodGenArgs = gims == null ? null : gims.GenericArguments; + var methodGenArgs = GenericInstMethodSig?.GenericArguments; var m = method; - var methodDef = m as MethodDef; - if (methodDef != null) { - var declaringType = methodDef.DeclaringType; - return FullNameCreator.MethodFullName(declaringType == null ? null : declaringType.FullName, methodDef.Name, methodDef.MethodSig, null, methodGenArgs); - } + if (m is MethodDef methodDef) + return FullNameFactory.MethodFullName(methodDef.DeclaringType?.FullName, methodDef.Name, methodDef.MethodSig, null, methodGenArgs, null, null); - var memberRef = m as MemberRef; - if (memberRef != null) { + if (m is MemberRef memberRef) { var methodSig = memberRef.MethodSig; - if (methodSig != null) { - var tsOwner = memberRef.Class as TypeSpec; - var gis = tsOwner == null ? null : tsOwner.TypeSig as GenericInstSig; - var typeGenArgs = gis == null ? null : gis.GenericArguments; - return FullNameCreator.MethodFullName(memberRef.GetDeclaringTypeFullName(), memberRef.Name, methodSig, typeGenArgs, methodGenArgs); + if (methodSig is not null) { + var gis = (memberRef.Class as TypeSpec)?.TypeSig as GenericInstSig; + var typeGenArgs = gis?.GenericArguments; + return FullNameFactory.MethodFullName(memberRef.GetDeclaringTypeFullName(), memberRef.Name, methodSig, typeGenArgs, methodGenArgs, null, null); } } @@ -159,66 +153,23 @@ public string FullName { } } - bool IIsTypeOrMethod.IsType { - get { return false; } - } - - bool IIsTypeOrMethod.IsMethod { - get { return true; } - } - - bool IMemberRef.IsField { - get { return false; } - } - - bool IMemberRef.IsTypeSpec { - get { return false; } - } - - bool IMemberRef.IsTypeRef { - get { return false; } - } - - bool IMemberRef.IsTypeDef { - get { return false; } - } - - bool IMemberRef.IsMethodSpec { - get { return true; } - } - - bool IMemberRef.IsMethodDef { - get { return false; } - } - - bool IMemberRef.IsMemberRef { - get { return false; } - } - - bool IMemberRef.IsFieldDef { - get { return false; } - } - - bool IMemberRef.IsPropertyDef { - get { return false; } - } - - bool IMemberRef.IsEventDef { - get { return false; } - } - - bool IMemberRef.IsGenericParam { - get { return false; } - } - - bool IContainsGenericParameter.ContainsGenericParameter { - get { return TypeHelper.ContainsGenericParameter(this); } - } + bool IIsTypeOrMethod.IsType => false; + bool IIsTypeOrMethod.IsMethod => true; + bool IMemberRef.IsField => false; + bool IMemberRef.IsTypeSpec => false; + bool IMemberRef.IsTypeRef => false; + bool IMemberRef.IsTypeDef => false; + bool IMemberRef.IsMethodSpec => true; + bool IMemberRef.IsMethodDef => false; + bool IMemberRef.IsMemberRef => false; + bool IMemberRef.IsFieldDef => false; + bool IMemberRef.IsPropertyDef => false; + bool IMemberRef.IsEventDef => false; + bool IMemberRef.IsGenericParam => false; + bool IContainsGenericParameter.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } /// @@ -246,31 +197,39 @@ public MethodSpecUser(IMethodDefOrRef method) /// The instantiated method sig public MethodSpecUser(IMethodDefOrRef method, GenericInstMethodSig sig) { this.method = method; - this.instantiation = sig; + instantiation = sig; } } /// /// Created from a row in the MethodSpec table /// - sealed class MethodSpecMD : MethodSpec, IMDTokenProviderMD { + sealed class MethodSpecMD : MethodSpec, IMDTokenProviderMD, IContainsGenericParameter2 { /// The module where this instance is located readonly ModuleDefMD readerModule; readonly uint origRid; + readonly GenericParamContext gpContext; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; + + bool IContainsGenericParameter2.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.MethodSpec, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.MethodSpec, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), gpContext, list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -281,18 +240,19 @@ protected override void InitializeCustomAttributes() { /// If is invalid public MethodSpecMD(ModuleDefMD readerModule, uint rid, GenericParamContext gpContext) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.MethodSpecTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("MethodSpec rid {0} does not exist", rid)); + throw new BadImageFormatException($"MethodSpec rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint method; - uint instantiation = readerModule.TablesStream.ReadMethodSpecRow(origRid, out method); - this.method = readerModule.ResolveMethodDefOrRef(method, gpContext); - this.instantiation = readerModule.ReadSignature(instantiation, gpContext); + this.gpContext = gpContext; + bool b = readerModule.TablesStream.TryReadMethodSpecRow(origRid, out var row); + Debug.Assert(b); + method = readerModule.ResolveMethodDefOrRef(row.Method, gpContext); + instantiation = readerModule.ReadSignature(row.Instantiation, gpContext); } } } diff --git a/src/DotNet/ModuleContext.cs b/src/DotNet/ModuleContext.cs index 3071923d9..b0ccfe00e 100644 --- a/src/DotNet/ModuleContext.cs +++ b/src/DotNet/ModuleContext.cs @@ -1,25 +1,27 @@ // dnlib: See LICENSE.txt for more info using System.Threading; +using dnlib.DotNet.Emit; -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// context /// public class ModuleContext { IAssemblyResolver assemblyResolver; IResolver resolver; + readonly OpCode[][] experimentalOpCodes = new OpCode[12][]; /// /// Gets/sets the assembly resolver. This is never null. /// public IAssemblyResolver AssemblyResolver { get { - if (assemblyResolver == null) + if (assemblyResolver is null) Interlocked.CompareExchange(ref assemblyResolver, NullResolver.Instance, null); return assemblyResolver; } - set { assemblyResolver = value; } + set => assemblyResolver = value; } /// @@ -27,11 +29,11 @@ public IAssemblyResolver AssemblyResolver { /// public IResolver Resolver { get { - if (resolver == null) + if (resolver is null) Interlocked.CompareExchange(ref resolver, NullResolver.Instance, null); return resolver; } - set { resolver = value; } + set => resolver = value; } /// @@ -64,8 +66,37 @@ public ModuleContext(IResolver resolver) public ModuleContext(IAssemblyResolver assemblyResolver, IResolver resolver) { this.assemblyResolver = assemblyResolver; this.resolver = resolver; - if (resolver == null && assemblyResolver != null) + if (resolver is null && assemblyResolver is not null) this.resolver = new Resolver(assemblyResolver); } + + /// + /// Registers an experimental CIL opcode. It must be a 2-byte opcode + /// where the first byte lies within the range 0xF0..0xFB. + /// + public void RegisterExperimentalOpCode(OpCode opCode) { + byte high = (byte)((ushort)opCode.Value >> 8); + byte low = (byte)opCode.Value; + OpCode[] array = experimentalOpCodes[high - 0xF0] ??= new OpCode[256]; + + array[low] = opCode; + } + + /// + /// Clears an experimental CIL opcode. + /// + public void ClearExperimentalOpCode(byte high, byte low) { + OpCode[] array = experimentalOpCodes[high - 0xF0]; + + if (array != null) + array[low] = null; + } + + /// + /// Attempts to get an experimental CIL opcode. + /// + public OpCode GetExperimentalOpCode(byte high, byte low) { + return experimentalOpCodes[high - 0xF0]?[low]; + } } } diff --git a/src/DotNet/ModuleCreationOptions.cs b/src/DotNet/ModuleCreationOptions.cs index 8172d1a80..dc0020f8d 100644 --- a/src/DotNet/ModuleCreationOptions.cs +++ b/src/DotNet/ModuleCreationOptions.cs @@ -1,6 +1,5 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info -using System.Diagnostics.SymbolStore; using dnlib.IO; using dnlib.DotNet.Pdb; @@ -16,30 +15,26 @@ public sealed class ModuleCreationOptions { /// public ModuleContext Context { get; set; } - /// - /// Set this if you want to decide how to create the PDB symbol reader. You don't need to - /// initialize or . - /// - public CreateSymbolReaderDelegate CreateSymbolReader { get; set; } + internal const PdbReaderOptions DefaultPdbReaderOptions = PdbReaderOptions.None; /// - /// Which PDB reader to use. Default is . + /// PDB reader options /// - public PdbImplType PdbImplementation { get; set; } + public PdbReaderOptions PdbOptions { get; set; } = DefaultPdbReaderOptions; /// /// Set it to A) the path (string) of the PDB file, B) the data (byte[]) of the PDB file or - /// C) to an of the PDB data. The will + /// C) to an of the PDB data. The will /// be owned by the module. You don't need to initialize - /// or /// public object PdbFileOrData { get; set; } /// - /// If true, will load the PDB file from disk if present. You don't need to - /// initialize or . + /// If true, will load the PDB file from disk if present, or an embedded portable PDB file + /// stored in the PE file. The default value is true. + /// You don't need to initialize . /// - public bool TryToLoadPdbFromDisk { get; set; } + public bool TryToLoadPdbFromDisk { get; set; } = true; /// /// corlib assembly reference to use or null if the default one from the opened @@ -47,28 +42,54 @@ public sealed class ModuleCreationOptions { /// public AssemblyRef CorLibAssemblyRef { get; set; } + /// + /// Runtime reader kind, default is . It should be + /// set to if it's an obfuscated Mono/Unity assembly. + /// + public CLRRuntimeReaderKind Runtime { get; set; } = CLRRuntimeReaderKind.CLR; + /// /// Default constructor /// - public ModuleCreationOptions() { - this.PdbImplementation = PdbImplType.Default; - } + public ModuleCreationOptions() { } /// /// Constructor /// /// Module context - public ModuleCreationOptions(ModuleContext context) { - this.Context = context; - this.PdbImplementation = PdbImplType.Default; + public ModuleCreationOptions(ModuleContext context) => Context = context; + + /// + /// Constructor + /// + /// Runtime reader kind, default is . It should be + /// set to if it's an obfuscated Mono/Unity assembly. + public ModuleCreationOptions(CLRRuntimeReaderKind runtime) => Runtime = runtime; + + /// + /// Constructor + /// + /// Module context + /// Runtime reader kind, default is . It should be + /// set to if it's an obfuscated Mono/Unity assembly. + public ModuleCreationOptions(ModuleContext context, CLRRuntimeReaderKind runtime) { + Context = context; + Runtime = runtime; } } /// - /// Creates a + /// Runtime reader kind /// - /// Module - /// A instance for (and now owned by) - /// or null. - public delegate ISymbolReader CreateSymbolReaderDelegate(ModuleDefMD module); + public enum CLRRuntimeReaderKind { + /// + /// Microsoft's CLRs (.NET Framework, .NET Core) + /// + CLR, + + /// + /// Mono's CLR (Mono, Unity) + /// + Mono, + } } diff --git a/src/DotNet/ModuleDef.cs b/src/DotNet/ModuleDef.cs index b8837a04f..4fe5bbc61 100644 --- a/src/DotNet/ModuleDef.cs +++ b/src/DotNet/ModuleDef.cs @@ -12,20 +12,15 @@ using dnlib.PE; using dnlib.Threading; using dnlib.W32Resources; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif +using System.Diagnostics; namespace dnlib.DotNet { /// /// A high-level representation of a row in the Module table /// - public abstract class ModuleDef : IHasCustomAttribute, IResolutionScope, IDisposable, IListListener, IModule, ITypeDefFinder, IDnlibDef, ITokenResolver { + public abstract class ModuleDef : IHasCustomAttribute, IHasCustomDebugInformation, IResolutionScope, IDisposable, IListListener, IModule, ITypeDefFinder, IDnlibDef, ITokenResolver, ISignatureReaderHelper { /// Default characteristics - protected const Characteristics DefaultCharacteristics = Characteristics.ExecutableImage | Characteristics._32BitMachine; + protected const Characteristics DefaultCharacteristics = Characteristics.ExecutableImage | Characteristics.Bit32Machine; /// Default DLL characteristics protected const DllCharacteristics DefaultDllCharacteristics = DllCharacteristics.TerminalServerAware | DllCharacteristics.NoSeh | DllCharacteristics.NxCompat | DllCharacteristics.DynamicBase; @@ -60,51 +55,41 @@ public abstract class ModuleDef : IHasCustomAttribute, IResolutionScope, IDispos protected ModuleContext context; /// - public MDToken MDToken { - get { return new MDToken(Table.Module, rid); } - } + public MDToken MDToken => new MDToken(Table.Module, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 7; } - } + public int HasCustomAttributeTag => 7; /// - public int ResolutionScopeTag { - get { return 0; } - } + public int ResolutionScopeTag => 0; /// /// Gets/sets a user value. This is never used by dnlib. This property isn't thread safe. /// public object Tag { - get { return tag; } - set { tag = value; } + get => tag; + set => tag = value; } object tag; /// - public ScopeType ScopeType { - get { return ScopeType.ModuleDef; } - } + public ScopeType ScopeType => ScopeType.ModuleDef; /// - public string ScopeName { - get { return FullName; } - } + public string ScopeName => FullName; /// /// Gets/sets Module.Generation column /// public ushort Generation { - get { return generation; } - set { generation = value; } + get => generation; + set => generation = value; } /// protected ushort generation; @@ -113,8 +98,8 @@ public ushort Generation { /// Gets/sets Module.Name column /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -123,8 +108,8 @@ public UTF8String Name { /// Gets/sets Module.Mvid column /// public Guid? Mvid { - get { return mvid; } - set { mvid = value; } + get => mvid; + set => mvid = value; } /// protected Guid? mvid; @@ -133,8 +118,8 @@ public Guid? Mvid { /// Gets/sets Module.EncId column /// public Guid? EncId { - get { return encId; } - set { encId = value; } + get => encId; + set => encId = value; } /// protected Guid? encId; @@ -143,8 +128,8 @@ public Guid? EncId { /// Gets/sets Module.EncBaseId column /// public Guid? EncBaseId { - get { return encBaseId; } - set { encBaseId = value; } + get => encBaseId; + set => encBaseId = value; } /// protected Guid? encBaseId; @@ -154,7 +139,7 @@ public Guid? EncBaseId { /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -162,17 +147,38 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); + + /// + public int HasCustomDebugInformationTag => 7; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// /// Gets the module's assembly. To set this value, add this /// to . /// public AssemblyDef Assembly { - get { return assembly; } - internal set { assembly = value; } + get => assembly; + internal set => assembly = value; } /// protected AssemblyDef assembly; @@ -180,9 +186,9 @@ public AssemblyDef Assembly { /// /// Gets a list of all non-nested s. See also /// - public ThreadSafe.IList Types { + public IList Types { get { - if (types == null) + if (types is null) InitializeTypes(); return types; } @@ -190,26 +196,24 @@ public ThreadSafe.IList Types { /// protected LazyList types; /// Initializes - protected virtual void InitializeTypes() { + protected virtual void InitializeTypes() => Interlocked.CompareExchange(ref types, new LazyList(this), null); - } /// /// Gets a list of all s /// - public ThreadSafe.IList ExportedTypes { + public IList ExportedTypes { get { - if (exportedTypes == null) + if (exportedTypes is null) InitializeExportedTypes(); return exportedTypes; } } /// - protected ThreadSafe.IList exportedTypes; + protected IList exportedTypes; /// Initializes - protected virtual void InitializeExportedTypes() { - Interlocked.CompareExchange(ref exportedTypes, ThreadSafeListCreator.Create(), null); - } + protected virtual void InitializeExportedTypes() => + Interlocked.CompareExchange(ref exportedTypes, new List(), null); /// /// Gets/sets the native entry point. Only one of and @@ -227,6 +231,7 @@ public RVA NativeEntryPoint { #endif nativeEntryPoint = value; managedEntryPoint = null; + Cor20HeaderFlags |= ComImageFlags.NativeEntryPoint; nativeAndManagedEntryPoint_initialized = true; #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } @@ -249,6 +254,7 @@ public IManagedEntryPoint ManagedEntryPoint { #endif nativeEntryPoint = 0; managedEntryPoint = value; + Cor20HeaderFlags &= ~ComImageFlags.NativeEntryPoint; nativeAndManagedEntryPoint_initialized = true; #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } @@ -276,54 +282,42 @@ void InitializeNativeAndManagedEntryPoint() { #endif } /// Called to initialize - protected virtual RVA GetNativeEntryPoint_NoLock() { - return 0; - } + protected virtual RVA GetNativeEntryPoint_NoLock() => 0; /// Called to initialize - protected virtual IManagedEntryPoint GetManagedEntryPoint_NoLock() { - return null; - } + protected virtual IManagedEntryPoint GetManagedEntryPoint_NoLock() => null; /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } - } + public bool HasCustomAttributes => CustomAttributes.Count > 0; /// /// Gets/sets the entry point method /// public MethodDef EntryPoint { - get { return ManagedEntryPoint as MethodDef; } - set { ManagedEntryPoint = value; } + get => ManagedEntryPoint as MethodDef; + set => ManagedEntryPoint = value; } /// /// true if is non-zero /// - public bool IsNativeEntryPointValid { - get { return NativeEntryPoint != 0; } - } + public bool IsNativeEntryPointValid => NativeEntryPoint != 0; /// /// true if is non-null /// - public bool IsManagedEntryPointValid { - get { return ManagedEntryPoint != null; } - } + public bool IsManagedEntryPointValid => ManagedEntryPoint is not null; /// /// true if is non-null /// - public bool IsEntryPointValid { - get { return EntryPoint != null; } - } + public bool IsEntryPointValid => EntryPoint is not null; /// /// Gets a list of all s /// public ResourceCollection Resources { get { - if (resources == null) + if (resources is null) InitializeResources(); return resources; } @@ -331,9 +325,8 @@ public ResourceCollection Resources { /// protected ResourceCollection resources; /// Initializes - protected virtual void InitializeResources() { + protected virtual void InitializeResources() => Interlocked.CompareExchange(ref resources, new ResourceCollection(), null); - } /// /// Gets/sets the . This is null if there are no @@ -375,42 +368,32 @@ void InitializeVTableFixups() { } /// Called to initialize - protected virtual VTableFixups GetVTableFixups_NoLock() { - return null; - } + protected virtual VTableFixups GetVTableFixups_NoLock() => null; /// /// true if there's at least one in /// - public bool HasTypes { - get { return Types.Count > 0; } - } + public bool HasTypes => Types.Count > 0; /// /// true if there's at least one in /// - public bool HasExportedTypes { - get { return ExportedTypes.Count > 0; } - } + public bool HasExportedTypes => ExportedTypes.Count > 0; /// /// true if there's at least one in /// - public bool HasResources { - get { return Resources.Count > 0; } - } + public bool HasResources => Resources.Count > 0; /// - public string FullName { - get { return UTF8String.ToSystemStringOrEmpty(name); } - } + public string FullName => UTF8String.ToSystemStringOrEmpty(name); /// /// Gets/sets the path of the module or an empty string if it wasn't loaded from disk /// public string Location { - get { return location; } - set { location = value; } + get => location; + set => location = value; } /// protected string location; @@ -418,16 +401,14 @@ public string Location { /// /// Gets the /// - public ICorLibTypes CorLibTypes { - get { return corLibTypes; } - } + public ICorLibTypes CorLibTypes => corLibTypes; /// /// Gets the instance /// TypeDefFinder TypeDefFinder { get { - if (typeDefFinder == null) + if (typeDefFinder is null) Interlocked.CompareExchange(ref typeDefFinder, new TypeDefFinder(Types), null); return typeDefFinder; } @@ -438,11 +419,11 @@ TypeDefFinder TypeDefFinder { /// public ModuleContext Context { get { - if (context == null) + if (context is null) Interlocked.CompareExchange(ref context, new ModuleContext(), null); return context; } - set { context = value ?? new ModuleContext(); } + set => context = value ?? new ModuleContext(); } /// @@ -458,8 +439,8 @@ public ModuleContext Context { /// /// public bool EnableTypeDefFindCache { - get { return TypeDefFinder.IsCacheEnabled; } - set { TypeDefFinder.IsCacheEnabled = value; } + get => TypeDefFinder.IsCacheEnabled; + set => TypeDefFinder.IsCacheEnabled = value; } /// @@ -468,16 +449,20 @@ public bool EnableTypeDefFindCache { public bool IsManifestModule { get { var asm = assembly; - return asm != null && asm.ManifestModule == this; + return asm is not null && asm.ManifestModule == this; } } /// /// Gets the global (aka. <Module>) type or null if there are no types /// - public TypeDef GlobalType { - get { return Types.Get(0, null); } - } + public TypeDef GlobalType => Types.Count == 0 ? null : Types[0]; + + /// + /// true if it's the core library module, false if it's not the core library module, + /// and null if it's not known. + /// + public bool? IsCoreLibraryModule { get; set; } /// /// Gets/sets the Win32 resources @@ -518,17 +503,13 @@ void InitializeWin32Resources() { } /// Called to initialize - protected virtual Win32Resources GetWin32Resources_NoLock() { - return null; - } + protected virtual Win32Resources GetWin32Resources_NoLock() => null; /// /// Gets the . This is null if no PDB file /// has been loaded or if no PDB file could be found. /// - public PdbState PdbState { - get { return pdbState; } - } + public PdbState PdbState => pdbState; /// /// Module kind @@ -546,12 +527,12 @@ public PdbState PdbState { public DllCharacteristics DllCharacteristics { get; set; } /// - /// Gets/sets the runtime version which is stored in the MetaData header. + /// Gets/sets the runtime version which is stored in the metadata header. /// See . /// /// Not thread safe public string RuntimeVersion { - get { return runtimeVersion; } + get => runtimeVersion; set { if (runtimeVersion != value) { runtimeVersion = value; @@ -570,7 +551,7 @@ public string RuntimeVersion { public WinMDStatus WinMDStatus { get { var cval = cachedWinMDStatus; - if (cval != null) + if (cval is not null) return cval.Value; cachedWinMDStatus = cval = CalculateWinMDStatus(RuntimeVersion); return cval.Value; @@ -581,23 +562,17 @@ public WinMDStatus WinMDStatus { /// /// true if this is a WinMD file /// - public bool IsWinMD { - get { return WinMDStatus != WinMDStatus.None; } - } + public bool IsWinMD => WinMDStatus != WinMDStatus.None; /// /// true if this is a managed WinMD file /// - public bool IsManagedWinMD { - get { return WinMDStatus == WinMDStatus.Managed; } - } + public bool IsManagedWinMD => WinMDStatus == WinMDStatus.Managed; /// /// true if this is a pure (non-managed) WinMD file /// - public bool IsPureWinMD { - get { return WinMDStatus == WinMDStatus.Pure; } - } + public bool IsPureWinMD => WinMDStatus == WinMDStatus.Pure; /// /// Gets the CLR runtime version of the managed WinMD file or null if none. This is @@ -607,7 +582,7 @@ public bool IsPureWinMD { public string RuntimeVersionWinMD { get { var rtver = runtimeVersionWinMD; - if (rtver != null) + if (rtver is not null) return rtver; runtimeVersionWinMD = rtver = CalculateRuntimeVersionWinMD(RuntimeVersion); return rtver; @@ -622,7 +597,7 @@ public string RuntimeVersionWinMD { public string WinMDVersion { get { var ver = winMDVersion; - if (ver != null) + if (ver is not null) return ver; winMDVersion = ver = CalculateWinMDVersion(RuntimeVersion); return ver; @@ -631,7 +606,7 @@ public string WinMDVersion { string winMDVersion; static WinMDStatus CalculateWinMDStatus(string version) { - if (version == null) + if (version is null) return WinMDStatus.None; if (!version.StartsWith("WindowsRuntime ", StringComparison.Ordinal)) return WinMDStatus.None; @@ -643,7 +618,7 @@ static string CalculateRuntimeVersionWinMD(string version) { // Original parser code: // CoreCLR file: src/md/winmd/adapter.cpp // Func: WinMDAdapter::Create(IMDCommon *pRawMDCommon, /*[out]*/ WinMDAdapter **ppAdapter) - if (version == null) + if (version is null) return null; if (!version.StartsWith("WindowsRuntime ", StringComparison.Ordinal)) return null; @@ -659,7 +634,7 @@ static string CalculateRuntimeVersionWinMD(string version) { } static string CalculateWinMDVersion(string version) { - if (version == null) + if (version is null) return null; if (!version.StartsWith("WindowsRuntime ", StringComparison.Ordinal)) return null; @@ -686,88 +661,65 @@ public bool IsClr10 { /// /// true if is the CLR v1.0 string /// - public bool IsClr10Exactly { - get { - return RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10 || - RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10_X86RETAIL || - RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10_RETAIL || - RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10_COMPLUS; - } - } + public bool IsClr10Exactly => + RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10 || + RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10_X86RETAIL || + RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10_RETAIL || + RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10_COMPLUS; /// /// true if is the CLR v1.1 string (only the major /// and minor version numbers are checked) /// - public bool IsClr11 { - get { return (RuntimeVersion ?? string.Empty).StartsWith(MDHeaderRuntimeVersion.MS_CLR_11_PREFIX); } - } + public bool IsClr11 => (RuntimeVersion ?? string.Empty).StartsWith(MDHeaderRuntimeVersion.MS_CLR_11_PREFIX); /// /// true if is the CLR v1.1 string /// - public bool IsClr11Exactly { - get { return RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_11; } - } + public bool IsClr11Exactly => RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_11; /// /// true if is the CLR v1.0 or v1.1 string (only the /// major and minor version numbers are checked) /// - public bool IsClr1x { - get { return IsClr10 || IsClr11; } - } + public bool IsClr1x => IsClr10 || IsClr11; /// /// true if is the CLR v1.0 or v1.1 string /// - public bool IsClr1xExactly { - get { return IsClr10Exactly || IsClr11Exactly; } - } + public bool IsClr1xExactly => IsClr10Exactly || IsClr11Exactly; /// /// true if is the CLR v2.0 string (only the major /// and minor version numbers are checked) /// - public bool IsClr20 { - get { return (RuntimeVersion ?? string.Empty).StartsWith(MDHeaderRuntimeVersion.MS_CLR_20_PREFIX); } - } + public bool IsClr20 => (RuntimeVersion ?? string.Empty).StartsWith(MDHeaderRuntimeVersion.MS_CLR_20_PREFIX); /// /// true if is the CLR v2.0 string /// - public bool IsClr20Exactly { - get { return RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_20; } - } + public bool IsClr20Exactly => RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_20; /// /// true if is the CLR v4.0 string (only the major /// and minor version numbers are checked) /// - public bool IsClr40 { - get { return (RuntimeVersion ?? string.Empty).StartsWith(MDHeaderRuntimeVersion.MS_CLR_40_PREFIX); } - } + public bool IsClr40 => (RuntimeVersion ?? string.Empty).StartsWith(MDHeaderRuntimeVersion.MS_CLR_40_PREFIX); /// /// true if is the CLR v4.0 string /// - public bool IsClr40Exactly { - get { return RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_40; } - } + public bool IsClr40Exactly => RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_40; /// /// true if is the ECMA 2002 string /// - public bool IsEcma2002 { - get { return RuntimeVersion == MDHeaderRuntimeVersion.ECMA_2002; } - } + public bool IsEcma2002 => RuntimeVersion == MDHeaderRuntimeVersion.ECMA_2002; /// /// true if is the ECMA 2005 string /// - public bool IsEcma2005 { - get { return RuntimeVersion == MDHeaderRuntimeVersion.ECMA_2005; } - } + public bool IsEcma2005 => RuntimeVersion == MDHeaderRuntimeVersion.ECMA_2005; /// /// Gets/sets the (from PE header) @@ -775,39 +727,41 @@ public bool IsEcma2005 { public Machine Machine { get; set; } /// - /// true if is + /// true if is , , ... /// - public bool IsI386 { - get { return Machine == Machine.I386; } - } + public bool IsI386 => Machine.IsI386(); /// - /// true if is + /// true if is /// - public bool IsIA64 { - get { return Machine == Machine.IA64; } - } + public bool IsIA64 => Machine == Machine.IA64; /// - /// true if is + /// true if is , , ... /// - public bool IsAMD64 { - get { return Machine == Machine.AMD64; } - } + public bool IsAMD64 => Machine.IsAMD64(); /// - /// true if is + /// true if is , , ... /// - public bool IsARM64 { - get { return Machine == Machine.ARM64; } - } + public bool IsARM => Machine.IsARMNT(); + + /// + /// true if is , , ... + /// + public bool IsARM64 => Machine.IsARM64(); + + /// + /// true if is s390x, , ... + /// + public bool IsS390x => Machine.IsS390x(); /// /// Gets/sets the (from .NET header) /// public ComImageFlags Cor20HeaderFlags { - get { return (ComImageFlags)cor20HeaderFlags; } - set { cor20HeaderFlags = (int)value; } + get => (ComImageFlags)cor20HeaderFlags; + set => cor20HeaderFlags = (int)value; } /// protected int cor20HeaderFlags; @@ -815,7 +769,7 @@ public ComImageFlags Cor20HeaderFlags { /// /// Gets/sets the runtime version number in the COR20 header. The major version is /// in the high 16 bits. The minor version is in the low 16 bits. This is normally 2.5 - /// (0x00020005), but if it's .NET 1.x, it should be 2.0 (0x00020000). If this is + /// (0x00020005), but if it's .NET Framework 1.x, it should be 2.0 (0x00020000). If this is /// null, the default value will be used when saving the module (2.0 if CLR 1.x, /// and 2.5 if not CLR 1.x). /// @@ -823,12 +777,12 @@ public ComImageFlags Cor20HeaderFlags { /// /// Gets the tables header version. The major version is in the upper 8 bits and the - /// minor version is in the lower 8 bits. .NET 1.0/1.1 use version 1.0 (0x0100) and - /// .NET 2.x and later use version 2.0 (0x0200). 1.0 has no support for generics, + /// minor version is in the lower 8 bits. .NET Framework 1.0/1.1 use version 1.0 (0x0100) and + /// .NET Framework 2.x and later use version 2.0 (0x0200). 1.0 has no support for generics, /// 1.1 has support for generics (GenericParam rows have an extra Kind column), /// and 2.0 has support for generics (GenericParam rows have the standard 4 columns). /// No other version is supported. If this is null, the default version is - /// used (1.0 if .NET 1.x, else 2.0). + /// used (1.0 if .NET Framework 1.x, else 2.0). /// public ushort? TablesHeaderVersion { get; set; } @@ -860,40 +814,40 @@ void ModifyComImageFlags(bool set, ComImageFlags flags) { /// Gets/sets the bit /// public bool IsILOnly { - get { return ((ComImageFlags)cor20HeaderFlags & ComImageFlags.ILOnly) != 0; } - set { ModifyComImageFlags(value, ComImageFlags.ILOnly); } + get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.ILOnly) != 0; + set => ModifyComImageFlags(value, ComImageFlags.ILOnly); } /// - /// Gets/sets the bit + /// Gets/sets the bit /// public bool Is32BitRequired { - get { return ((ComImageFlags)cor20HeaderFlags & ComImageFlags._32BitRequired) != 0; } - set { ModifyComImageFlags(value, ComImageFlags._32BitRequired); } + get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.Bit32Required) != 0; + set => ModifyComImageFlags(value, ComImageFlags.Bit32Required); } /// /// Gets/sets the bit /// public bool IsStrongNameSigned { - get { return ((ComImageFlags)cor20HeaderFlags & ComImageFlags.StrongNameSigned) != 0; } - set { ModifyComImageFlags(value, ComImageFlags.StrongNameSigned); } + get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.StrongNameSigned) != 0; + set => ModifyComImageFlags(value, ComImageFlags.StrongNameSigned); } /// /// Gets/sets the bit /// public bool HasNativeEntryPoint { - get { return ((ComImageFlags)cor20HeaderFlags & ComImageFlags.NativeEntryPoint) != 0; } - set { ModifyComImageFlags(value, ComImageFlags.NativeEntryPoint); } + get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.NativeEntryPoint) != 0; + set => ModifyComImageFlags(value, ComImageFlags.NativeEntryPoint); } /// - /// Gets/sets the bit + /// Gets/sets the bit /// public bool Is32BitPreferred { - get { return ((ComImageFlags)cor20HeaderFlags & ComImageFlags._32BitPreferred) != 0; } - set { ModifyComImageFlags(value, ComImageFlags._32BitPreferred); } + get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.Bit32Preferred) != 0; + set => ModifyComImageFlags(value, ComImageFlags.Bit32Preferred); } /// @@ -909,23 +863,19 @@ public void Dispose() { protected virtual void Dispose(bool disposing) { if (!disposing) return; - foreach (var resource in Resources.GetInitializedElements(true)) { - if (resource != null) - resource.Dispose(); - } var tdf = typeDefFinder; - if (tdf != null) { + if (tdf is not null) { tdf.Dispose(); typeDefFinder = null; } + pdbState?.Dispose(); + pdbState = null; } /// /// Gets all the types (including nested types) present in this module /// - public IEnumerable GetTypes() { - return AllTypesHelper.Types(Types); - } + public IEnumerable GetTypes() => AllTypesHelper.Types(Types); /// /// Adds as a non-nested type. If it's already nested, its @@ -933,7 +883,7 @@ public IEnumerable GetTypes() { /// /// The to insert public void AddAsNonNestedType(TypeDef typeDef) { - if (typeDef == null) + if (typeDef is null) return; typeDef.DeclaringType = null; Types.Add(typeDef); @@ -965,9 +915,10 @@ public T ForceUpdateRowId(T tableRow) where T : IMDTokenProvider { } uint GetNextFreeRid(Table table) { + var lastUsedRids = this.lastUsedRids; if ((uint)table >= lastUsedRids.Length) return 0; - return (uint)Interlocked.Increment(ref lastUsedRids[(int)table]); + return (uint)Interlocked.Increment(ref lastUsedRids[(int)table]) & 0x00FFFFFF; } /// @@ -975,18 +926,14 @@ uint GetNextFreeRid(Table table) { /// /// The type /// The imported type or null if is invalid - public ITypeDefOrRef Import(Type type) { - return new Importer(this).Import(type); - } + public ITypeDefOrRef Import(Type type) => new Importer(this).Import(type); /// /// Imports a as a /// /// The type /// The imported type or null if is invalid - public TypeSig ImportAsTypeSig(Type type) { - return new Importer(this).ImportAsTypeSig(type); - } + public TypeSig ImportAsTypeSig(Type type) => new Importer(this).ImportAsTypeSig(type); /// /// Imports a as a @@ -994,9 +941,7 @@ public TypeSig ImportAsTypeSig(Type type) { /// The field /// The imported field or null if is invalid /// or if we failed to import the field - public MemberRef Import(FieldInfo fieldInfo) { - return (MemberRef)new Importer(this).Import(fieldInfo); - } + public MemberRef Import(FieldInfo fieldInfo) => (MemberRef)new Importer(this).Import(fieldInfo); /// /// Imports a as a . This will be either @@ -1005,116 +950,90 @@ public MemberRef Import(FieldInfo fieldInfo) { /// The method /// The imported method or null if is invalid /// or if we failed to import the method - public IMethod Import(MethodBase methodBase) { - return new Importer(this).Import(methodBase); - } + public IMethod Import(MethodBase methodBase) => new Importer(this).Import(methodBase); /// /// Imports a /// /// The type /// The imported type or null - public IType Import(IType type) { - return new Importer(this).Import(type); - } + public IType Import(IType type) => new Importer(this).Import(type); /// /// Imports a as a /// /// The type /// The imported type or null - public TypeRef Import(TypeDef type) { - return (TypeRef)new Importer(this).Import(type); - } + public TypeRef Import(TypeDef type) => (TypeRef)new Importer(this).Import(type); /// /// Imports a /// /// The type /// The imported type or null - public TypeRef Import(TypeRef type) { - return (TypeRef)new Importer(this).Import(type); - } + public TypeRef Import(TypeRef type) => (TypeRef)new Importer(this).Import(type); /// /// Imports a /// /// The type /// The imported type or null - public TypeSpec Import(TypeSpec type) { - return new Importer(this).Import(type); - } + public TypeSpec Import(TypeSpec type) => new Importer(this).Import(type); /// /// Imports a /// /// The type /// The imported type or null - public TypeSig Import(TypeSig type) { - return new Importer(this).Import(type); - } + public TypeSig Import(TypeSig type) => new Importer(this).Import(type); /// /// Imports a /// /// The field /// The imported type or null if is invalid - public MemberRef Import(IField field) { - return (MemberRef)new Importer(this).Import(field); - } + public MemberRef Import(IField field) => (MemberRef)new Importer(this).Import(field); /// /// Imports a as a /// /// The field /// The imported type or null if is invalid - public MemberRef Import(FieldDef field) { - return (MemberRef)new Importer(this).Import(field); - } + public MemberRef Import(FieldDef field) => (MemberRef)new Importer(this).Import(field); /// /// Imports a /// /// The method /// The imported method or null if is invalid - public IMethod Import(IMethod method) { - return new Importer(this).Import(method); - } + public IMethod Import(IMethod method) => new Importer(this).Import(method); /// /// Imports a as a /// /// The method /// The imported method or null if is invalid - public MemberRef Import(MethodDef method) { - return (MemberRef)new Importer(this).Import(method); - } + public MemberRef Import(MethodDef method) => (MemberRef)new Importer(this).Import(method); /// /// Imports a /// /// The method /// The imported method or null if is invalid - public MethodSpec Import(MethodSpec method) { - return new Importer(this).Import(method); - } + public MethodSpec Import(MethodSpec method) => new Importer(this).Import(method); /// /// Imports a /// /// The member ref /// The imported member ref or null if is invalid - public MemberRef Import(MemberRef memberRef) { - return new Importer(this).Import(memberRef); - } + public MemberRef Import(MemberRef memberRef) => new Importer(this).Import(memberRef); /// /// Writes the module to a file on disk. If the file exists, it will be overwritten. /// /// Filename - public void Write(string filename) { - Write(filename, null); - } + public void Write(string filename) => Write(filename, null); /// /// Writes the module to a file on disk. If the file exists, it will be overwritten. @@ -1130,9 +1049,7 @@ public void Write(string filename, ModuleWriterOptions options) { /// Writes the module to a stream. /// /// Destination stream - public void Write(Stream dest) { - Write(dest, null); - } + public void Write(Stream dest) => Write(dest, null); /// /// Writes the module to a stream. @@ -1149,9 +1066,7 @@ public void Write(Stream dest, ModuleWriterOptions options) { /// to true. Use this method if the cache is /// enabled but some of the types have been modified (eg. removed, added, renamed). /// - public void ResetTypeDefFindCache() { - TypeDefFinder.ResetCache(); - } + public void ResetTypeDefFindCache() => TypeDefFinder.ResetCache(); /// /// Finds a @@ -1160,33 +1075,29 @@ public void ResetTypeDefFindCache() { /// Name /// Language ID /// The or null if none found - public ResourceData FindWin32ResourceData(ResourceName type, ResourceName name, ResourceName langId) { - var w32Resources = Win32Resources; - return w32Resources == null ? null : w32Resources.Find(type, name, langId); - } + public ResourceData FindWin32ResourceData(ResourceName type, ResourceName name, ResourceName langId) => Win32Resources?.Find(type, name, langId); /// /// Creates a new /// - public void CreatePdbState() { - SetPdbState(new PdbState()); - } + /// PDB file kind + public void CreatePdbState(PdbFileKind pdbFileKind) => SetPdbState(new PdbState(this, pdbFileKind)); /// /// Sets a /// /// New public void SetPdbState(PdbState pdbState) { - if (pdbState == null) - throw new ArgumentNullException("pdbState"); + if (pdbState is null) + throw new ArgumentNullException(nameof(pdbState)); var orig = Interlocked.CompareExchange(ref this.pdbState, pdbState, null); - if (orig != null) + if (orig is not null) throw new InvalidOperationException("PDB file has already been initialized"); } uint GetCor20RuntimeVersion() { var rtVer = Cor20HeaderRuntimeVersion; - if (rtVer != null) + if (rtVer is not null) return rtVer.Value; return IsClr1x ? 0x00020000U : 0x00020005; } @@ -1196,9 +1107,7 @@ uint GetCor20RuntimeVersion() { /// if it can be 32-bit or 64-bit. /// /// Size of a pointer (4 or 8) - public int GetPointerSize() { - return GetPointerSize(4); - } + public int GetPointerSize() => GetPointerSize(4); /// /// Returns the size of a pointer @@ -1206,11 +1115,19 @@ public int GetPointerSize() { /// Default pointer size if it's not known or if it /// can be 32-bit or 64-bit /// Size of a pointer (4 or 8) - public int GetPointerSize(int defaultPointerSize) { + public int GetPointerSize(int defaultPointerSize) => GetPointerSize(defaultPointerSize, defaultPointerSize); + + /// + /// Returns the size of a pointer + /// + /// Default pointer size + /// Pointer size if it's prefer-32-bit (should usually be 4) + /// + public int GetPointerSize(int defaultPointerSize, int prefer32bitPointerSize) { var machine = Machine; - if (machine == Machine.AMD64 || machine == Machine.IA64 || machine == Machine.ARM64) + if (machine.Is64Bit()) return 8; - if (machine != Machine.I386) + if (!machine.IsI386()) return 4; // Machine is I386 so it's either x86 or platform neutral @@ -1225,23 +1142,23 @@ public int GetPointerSize(int defaultPointerSize) { if ((flags & ComImageFlags.ILOnly) == 0) return 4; - // 32-bit Preferred flag is new in .NET 4.5. See CorHdr.h in Windows SDK for more info - switch (flags & (ComImageFlags._32BitRequired | ComImageFlags._32BitPreferred)) { + // 32-bit Preferred flag is new in .NET Framework 4.5. See CorHdr.h in Windows SDK for more info + switch (flags & (ComImageFlags.Bit32Required | ComImageFlags.Bit32Preferred)) { case 0: // Machine and ILOnly flag should be checked break; - case ComImageFlags._32BitPreferred: + case ComImageFlags.Bit32Preferred: // Illegal break; - case ComImageFlags._32BitRequired: + case ComImageFlags.Bit32Required: // x86 image (32-bit process) return 4; - case ComImageFlags._32BitRequired | ComImageFlags._32BitPreferred: + case ComImageFlags.Bit32Required | ComImageFlags.Bit32Preferred: // Platform neutral but prefers to be 32-bit - return defaultPointerSize; + return prefer32bitPointerSize; } return defaultPointerSize; @@ -1250,25 +1167,23 @@ public int GetPointerSize(int defaultPointerSize) { /// void IListListener.OnLazyAdd(int index, ref TypeDef value) { #if DEBUG - if (value.DeclaringType != null) - throw new InvalidOperationException("Added type's DeclaringType != null"); + if (value.DeclaringType is not null) + throw new InvalidOperationException("Added type's DeclaringType is not null"); #endif value.Module2 = this; } /// void IListListener.OnAdd(int index, TypeDef value) { - if (value.DeclaringType != null) + if (value.DeclaringType is not null) throw new InvalidOperationException("Nested type is already owned by another type. Set DeclaringType to null first."); - if (value.Module != null) + if (value.Module is not null) throw new InvalidOperationException("Type is already owned by another module. Remove it from that module's type list."); value.Module2 = this; } /// - void IListListener.OnRemove(int index, TypeDef value) { - value.Module2 = null; - } + void IListListener.OnRemove(int index, TypeDef value) => value.Module2 = null; /// void IListListener.OnResize(int index) { @@ -1276,7 +1191,7 @@ void IListListener.OnResize(int index) { /// void IListListener.OnClear() { - foreach (var type in Types.GetEnumerable_NoLock()) + foreach (var type in types.GetEnumerable_NoLock()) type.Module2 = null; } @@ -1289,9 +1204,7 @@ void IListListener.OnClear() { /// type names are separated by a + character. If false, nested type names /// are separated by a / character. /// An existing or null if it wasn't found. - public TypeDef Find(string fullName, bool isReflectionName) { - return TypeDefFinder.Find(fullName, isReflectionName); - } + public TypeDef Find(string fullName, bool isReflectionName) => TypeDefFinder.Find(fullName, isReflectionName); /// /// Finds a . Its scope (i.e., module or assembly) is ignored when @@ -1300,9 +1213,7 @@ public TypeDef Find(string fullName, bool isReflectionName) { /// /// The type ref /// An existing or null if it wasn't found. - public TypeDef Find(TypeRef typeRef) { - return TypeDefFinder.Find(typeRef); - } + public TypeDef Find(TypeRef typeRef) => TypeDefFinder.Find(typeRef); /// /// Finds a @@ -1310,27 +1221,25 @@ public TypeDef Find(TypeRef typeRef) { /// The type /// A or null if it wasn't found public TypeDef Find(ITypeDefOrRef typeRef) { - var td = typeRef as TypeDef; - if (td != null) + if (typeRef is TypeDef td) return td.Module == this ? td : null; - var tr = typeRef as TypeRef; - if (tr != null) + if (typeRef is TypeRef tr) return Find(tr); var ts = typeRef as TypeSpec; - if (ts == null) + if (ts is null) return null; var sig = ts.TypeSig as TypeDefOrRefSig; - if (sig == null) + if (sig is null) return null; td = sig.TypeDef; - if (td != null) + if (td is not null) return td.Module == this ? td : null; tr = sig.TypeRef; - if (tr != null) + if (tr is not null) return Find(tr); return null; @@ -1342,55 +1251,31 @@ public TypeDef Find(ITypeDefOrRef typeRef) { /// /// A new instance public static ModuleContext CreateModuleContext() { - return CreateModuleContext(true); - } - - /// - /// Creates a new instance. There should normally only be one - /// instance shared by all s. - /// - /// If true, add other common assembly search - /// paths, not just the module search paths and the GAC. - /// A new instance - public static ModuleContext CreateModuleContext(bool addOtherSearchPaths) { var ctx = new ModuleContext(); - var asmRes = new AssemblyResolver(ctx, addOtherSearchPaths); + var asmRes = new AssemblyResolver(ctx); var res = new Resolver(asmRes); ctx.AssemblyResolver = asmRes; ctx.Resolver = res; + asmRes.DefaultModuleContext = ctx; return ctx; } - /// - /// Load everything in this module. All types, fields, asm refs, etc are loaded, all their - /// properties are read to make sure everything is cached. - /// - public void LoadEverything() { - LoadEverything(null); - } - /// /// Load everything in this module. All types, fields, asm refs, etc are loaded, all their /// properties are read to make sure everything is cached. /// /// Cancellation token or null - public virtual void LoadEverything(ICancellationToken cancellationToken) { - ModuleLoader.LoadAll(this, cancellationToken); - } + public virtual void LoadEverything(ICancellationToken cancellationToken = null) => ModuleLoader.LoadAll(this, cancellationToken); /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; /// /// Resolves a token /// /// The metadata token /// A or null if is invalid - public IMDTokenProvider ResolveToken(MDToken mdToken) { - return ResolveToken(mdToken.Raw, new GenericParamContext()); - } + public IMDTokenProvider ResolveToken(MDToken mdToken) => ResolveToken(mdToken.Raw, new GenericParamContext()); /// /// Resolves a token @@ -1398,18 +1283,14 @@ public IMDTokenProvider ResolveToken(MDToken mdToken) { /// The metadata token /// Generic parameter context /// A or null if is invalid - public IMDTokenProvider ResolveToken(MDToken mdToken, GenericParamContext gpContext) { - return ResolveToken(mdToken.Raw, gpContext); - } + public IMDTokenProvider ResolveToken(MDToken mdToken, GenericParamContext gpContext) => ResolveToken(mdToken.Raw, gpContext); /// /// Resolves a token /// /// The metadata token /// A or null if is invalid - public IMDTokenProvider ResolveToken(uint token) { - return ResolveToken(token, new GenericParamContext()); - } + public IMDTokenProvider ResolveToken(int token) => ResolveToken((uint)token, new GenericParamContext()); /// /// Resolves a token @@ -1417,9 +1298,22 @@ public IMDTokenProvider ResolveToken(uint token) { /// The metadata token /// Generic parameter context /// A or null if is invalid - public virtual IMDTokenProvider ResolveToken(uint token, GenericParamContext gpContext) { - return null; - } + public IMDTokenProvider ResolveToken(int token, GenericParamContext gpContext) => ResolveToken((uint)token, gpContext); + + /// + /// Resolves a token + /// + /// The metadata token + /// A or null if is invalid + public IMDTokenProvider ResolveToken(uint token) => ResolveToken(token, new GenericParamContext()); + + /// + /// Resolves a token + /// + /// The metadata token + /// Generic parameter context + /// A or null if is invalid + public virtual IMDTokenProvider ResolveToken(uint token, GenericParamContext gpContext) => null; /// /// Gets all s @@ -1427,7 +1321,7 @@ public virtual IMDTokenProvider ResolveToken(uint token, GenericParamContext gpC public IEnumerable GetAssemblyRefs() { for (uint rid = 1; ; rid++) { var asmRef = ResolveToken(new MDToken(Table.AssemblyRef, rid).Raw) as AssemblyRef; - if (asmRef == null) + if (asmRef is null) break; yield return asmRef; } @@ -1439,7 +1333,7 @@ public IEnumerable GetAssemblyRefs() { public IEnumerable GetModuleRefs() { for (uint rid = 1; ; rid++) { var modRef = ResolveToken(new MDToken(Table.ModuleRef, rid).Raw) as ModuleRef; - if (modRef == null) + if (modRef is null) break; yield return modRef; } @@ -1449,9 +1343,7 @@ public IEnumerable GetModuleRefs() { /// Gets all s. s with generic parameters /// aren't cached and a new copy is always returned. /// - public IEnumerable GetMemberRefs() { - return GetMemberRefs(new GenericParamContext()); - } + public IEnumerable GetMemberRefs() => GetMemberRefs(new GenericParamContext()); /// /// Gets all s. s with generic parameters @@ -1461,7 +1353,7 @@ public IEnumerable GetMemberRefs() { public IEnumerable GetMemberRefs(GenericParamContext gpContext) { for (uint rid = 1; ; rid++) { var mr = ResolveToken(new MDToken(Table.MemberRef, rid).Raw, gpContext) as MemberRef; - if (mr == null) + if (mr is null) break; yield return mr; } @@ -1473,7 +1365,7 @@ public IEnumerable GetMemberRefs(GenericParamContext gpContext) { public IEnumerable GetTypeRefs() { for (uint rid = 1; ; rid++) { var mr = ResolveToken(new MDToken(Table.TypeRef, rid).Raw) as TypeRef; - if (mr == null) + if (mr is null) break; yield return mr; } @@ -1504,12 +1396,20 @@ public AssemblyRef GetAssemblyRef(UTF8String simpleName) { /// New asm ref /// protected static bool IsGreaterAssemblyRefVersion(AssemblyRef found, AssemblyRef newOne) { - if (found == null) + if (found is null) return true; var foundVer = found.Version; var newVer = newOne.Version; - return foundVer == null || (newVer != null && newVer >= foundVer); + return foundVer is null || (newVer is not null && newVer >= foundVer); } + + ITypeDefOrRef ISignatureReaderHelper.ResolveTypeDefOrRef(uint codedToken, GenericParamContext gpContext) { + if (!CodedToken.TypeDefOrRef.Decode(codedToken, out uint token)) + return null; + return ResolveToken(token) as ITypeDefOrRef; + } + + TypeSig ISignatureReaderHelper.ConvertRTInternalAddress(IntPtr address) => null; } /// @@ -1548,19 +1448,19 @@ public ModuleDefUser(UTF8String name, Guid? mvid) /// Module version ID /// Corlib assembly ref or null public ModuleDefUser(UTF8String name, Guid? mvid, AssemblyRef corLibAssemblyRef) { - this.Kind = ModuleKind.Windows; - this.Characteristics = DefaultCharacteristics; - this.DllCharacteristics = DefaultDllCharacteristics; - this.RuntimeVersion = MDHeaderRuntimeVersion.MS_CLR_20; - this.Machine = Machine.I386; - this.cor20HeaderFlags = (int)ComImageFlags.ILOnly; - this.Cor20HeaderRuntimeVersion = 0x00020005; // .NET 2.0 or later should use 2.5 - this.TablesHeaderVersion = 0x0200; // .NET 2.0 or later should use 2.0 - this.types = new LazyList(this); - this.exportedTypes = new LazyList(); - this.resources = new ResourceCollection(); - this.corLibTypes = new CorLibTypes(this, corLibAssemblyRef); - this.types = new LazyList(this); + Kind = ModuleKind.Windows; + Characteristics = DefaultCharacteristics; + DllCharacteristics = DefaultDllCharacteristics; + RuntimeVersion = MDHeaderRuntimeVersion.MS_CLR_20; + Machine = Machine.I386; + cor20HeaderFlags = (int)ComImageFlags.ILOnly; + Cor20HeaderRuntimeVersion = 0x00020005; // .NET Framework 2.0 or later should use 2.5 + TablesHeaderVersion = 0x0200; // .NET Framework 2.0 or later should use 2.0 + types = new LazyList(this); + exportedTypes = new LazyList(); + resources = new ResourceCollection(); + corLibTypes = new CorLibTypes(this, corLibAssemblyRef); + types = new LazyList(this); this.name = name; this.mvid = mvid; types.Add(CreateModuleType()); @@ -1584,26 +1484,27 @@ public class ModuleDefMD2 : ModuleDef, IMDTokenProviderMD { readonly uint origRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.Module, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Module, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } /// - protected override RVA GetNativeEntryPoint_NoLock() { - return readerModule.GetNativeEntryPoint(); + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); } /// - protected override IManagedEntryPoint GetManagedEntryPoint_NoLock() { - return readerModule.GetManagedEntryPoint(); - } + protected override RVA GetNativeEntryPoint_NoLock() => readerModule.GetNativeEntryPoint(); + + /// + protected override IManagedEntryPoint GetManagedEntryPoint_NoLock() => readerModule.GetManagedEntryPoint(); /// /// Constructor @@ -1613,28 +1514,28 @@ protected override IManagedEntryPoint GetManagedEntryPoint_NoLock() { /// If is null /// If is invalid internal ModuleDefMD2(ModuleDefMD readerModule, uint rid) { - if (rid == 1 && readerModule == null) + if (rid == 1 && readerModule is null) readerModule = (ModuleDefMD)this; #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (rid != 1 && readerModule.TablesStream.ModuleTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("Module rid {0} does not exist", rid)); + throw new BadImageFormatException($"Module rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; if (rid != 1) { - this.Kind = ModuleKind.Windows; - this.Characteristics = DefaultCharacteristics; - this.DllCharacteristics = DefaultDllCharacteristics; - this.RuntimeVersion = MDHeaderRuntimeVersion.MS_CLR_20; - this.Machine = Machine.I386; - this.cor20HeaderFlags = (int)ComImageFlags.ILOnly; - this.Cor20HeaderRuntimeVersion = 0x00020005; // .NET 2.0 or later should use 2.5 - this.TablesHeaderVersion = 0x0200; // .NET 2.0 or later should use 2.0 - this.corLibTypes = new CorLibTypes(this); - this.location = string.Empty; + Kind = ModuleKind.Windows; + Characteristics = DefaultCharacteristics; + DllCharacteristics = DefaultDllCharacteristics; + RuntimeVersion = MDHeaderRuntimeVersion.MS_CLR_20; + Machine = Machine.I386; + cor20HeaderFlags = (int)ComImageFlags.ILOnly; + Cor20HeaderRuntimeVersion = 0x00020005; // .NET Framework 2.0 or later should use 2.5 + TablesHeaderVersion = 0x0200; // .NET Framework 2.0 or later should use 2.0 + corLibTypes = new CorLibTypes(this); + location = string.Empty; InitializeFromRawRow(); } } @@ -1643,12 +1544,13 @@ internal ModuleDefMD2(ModuleDefMD readerModule, uint rid) { /// Initialize fields from the raw Module row /// protected void InitializeFromRawRow() { - uint name, mvid, encId; - uint encBaseId = readerModule.TablesStream.ReadModuleRow(origRid, out generation, out name, out mvid, out encId); - this.mvid = readerModule.GuidStream.Read(mvid); - this.encId = readerModule.GuidStream.Read(encId); - this.encBaseId = readerModule.GuidStream.Read(encBaseId); - this.name = readerModule.StringsStream.ReadNoNull(name); + bool b = readerModule.TablesStream.TryReadModuleRow(origRid, out var row); + Debug.Assert(b); + generation = row.Generation; + mvid = readerModule.GuidStream.Read(row.Mvid); + encId = readerModule.GuidStream.Read(row.EncId); + encBaseId = readerModule.GuidStream.Read(row.EncBaseId); + name = readerModule.StringsStream.ReadNoNull(row.Name); if (origRid == 1) assembly = readerModule.ResolveAssembly(origRid); } diff --git a/src/DotNet/ModuleDefMD.cs b/src/DotNet/ModuleDefMD.cs index 79334ad6a..f37e84f9c 100644 --- a/src/DotNet/ModuleDefMD.cs +++ b/src/DotNet/ModuleDefMD.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics.SymbolStore; using System.IO; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; @@ -17,18 +16,20 @@ using dnlib.W32Resources; using DNW = dnlib.DotNet.Writer; +using dnlib.DotNet.Pdb.Symbols; +using System.Runtime.CompilerServices; namespace dnlib.DotNet { /// /// Created from a row in the Module table /// - public sealed class ModuleDefMD : ModuleDefMD2, IInstructionOperandResolver, ISignatureReaderHelper { + public sealed class ModuleDefMD : ModuleDefMD2, IInstructionOperandResolver { /// The file that contains all .NET metadata - MetaData metaData; + MetadataBase metadata; IMethodDecrypter methodDecrypter; IStringDecrypter stringDecrypter; - RandomRidList moduleRidList; + StrongBox moduleRidList; SimpleLazyList listModuleDefMD; SimpleLazyList listTypeRefMD; @@ -60,112 +61,87 @@ public sealed class ModuleDefMD : ModuleDefMD2, IInstructionOperandResolver, ISi /// Gets/sets the method decrypter /// public IMethodDecrypter MethodDecrypter { - get { return methodDecrypter; } - set { methodDecrypter = value; } + get => methodDecrypter; + set => methodDecrypter = value; } /// /// Gets/sets the string decrypter /// public IStringDecrypter StringDecrypter { - get { return stringDecrypter; } - set { stringDecrypter = value; } + get => stringDecrypter; + set => stringDecrypter = value; } /// /// Returns the .NET metadata interface /// - public IMetaData MetaData { - get { return metaData; } - } + public Metadata Metadata => metadata; /// /// Returns the #~ or #- tables stream /// - public TablesStream TablesStream { - get { return metaData.TablesStream; } - } + public TablesStream TablesStream => metadata.TablesStream; /// /// Returns the #Strings stream /// - public StringsStream StringsStream { - get { return metaData.StringsStream; } - } + public StringsStream StringsStream => metadata.StringsStream; /// /// Returns the #Blob stream /// - public BlobStream BlobStream { - get { return metaData.BlobStream; } - } + public BlobStream BlobStream => metadata.BlobStream; /// /// Returns the #GUID stream /// - public GuidStream GuidStream { - get { return metaData.GuidStream; } - } + public GuidStream GuidStream => metadata.GuidStream; /// /// Returns the #US stream /// - public USStream USStream { - get { return metaData.USStream; } - } + public USStream USStream => metadata.USStream; /// protected override void InitializeTypes() { - var list = MetaData.GetNonNestedClassRidList(); - var tmp = new LazyList((int)list.Length, this, list, (list2, index) => ResolveTypeDef(((RidList)list2)[index])); + var list = Metadata.GetNonNestedClassRidList(); + var tmp = new LazyList(list.Count, this, list, (list2, index) => ResolveTypeDef(list2[index])); Interlocked.CompareExchange(ref types, tmp, null); } /// protected override void InitializeExportedTypes() { - var list = MetaData.GetExportedTypeRidList(); - var tmp = new LazyList((int)list.Length, list, (list2, i) => ResolveExportedType(((RidList)list2)[i])); + var list = Metadata.GetExportedTypeRidList(); + var tmp = new LazyList(list.Count, list, (list2, i) => ResolveExportedType(list2[i])); Interlocked.CompareExchange(ref exportedTypes, tmp, null); } /// protected override void InitializeResources() { var table = TablesStream.ManifestResourceTable; - var tmp = new ResourceCollection((int)table.Rows, null, (ctx, i) => CreateResource(i + 1)); + var tmp = new ResourceCollection((int)table.Rows, null, (ctx, i) => CreateResource((uint)i + 1)); Interlocked.CompareExchange(ref resources, tmp, null); } /// - protected override Win32Resources GetWin32Resources_NoLock() { - return metaData.PEImage.Win32Resources; - } + protected override Win32Resources GetWin32Resources_NoLock() => metadata.PEImage.Win32Resources; /// protected override VTableFixups GetVTableFixups_NoLock() { - var vtableFixupsInfo = metaData.ImageCor20Header.VTableFixups; + var vtableFixupsInfo = metadata.ImageCor20Header.VTableFixups; if (vtableFixupsInfo.VirtualAddress == 0 || vtableFixupsInfo.Size == 0) return null; return new VTableFixups(this); } - /// - /// Creates a instance from a file - /// - /// File name of an existing .NET module/assembly - /// A new instance - public static ModuleDefMD Load(string fileName) { - return Load(fileName, (ModuleCreationOptions)null); - } - /// /// Creates a instance from a file /// /// File name of an existing .NET module/assembly /// Module context or null /// A new instance - public static ModuleDefMD Load(string fileName, ModuleContext context) { - return Load(fileName, new ModuleCreationOptions(context)); - } + public static ModuleDefMD Load(string fileName, ModuleContext context) => Load(fileName, new ModuleCreationOptions(context)); /// /// Creates a instance from a file @@ -173,18 +149,7 @@ public static ModuleDefMD Load(string fileName, ModuleContext context) { /// File name of an existing .NET module/assembly /// Module creation options or null /// A new instance - public static ModuleDefMD Load(string fileName, ModuleCreationOptions options) { - return Load(MetaDataCreator.Load(fileName), options); - } - - /// - /// Creates a instance from a byte[] - /// - /// Contents of a .NET module/assembly - /// A new instance - public static ModuleDefMD Load(byte[] data) { - return Load(data, (ModuleCreationOptions)null); - } + public static ModuleDefMD Load(string fileName, ModuleCreationOptions options = null) => Load(MetadataFactory.Load(fileName, options?.Runtime ?? CLRRuntimeReaderKind.CLR), options); /// /// Creates a instance from a byte[] @@ -192,9 +157,7 @@ public static ModuleDefMD Load(byte[] data) { /// Contents of a .NET module/assembly /// Module context or null /// A new instance - public static ModuleDefMD Load(byte[] data, ModuleContext context) { - return Load(data, new ModuleCreationOptions(context)); - } + public static ModuleDefMD Load(byte[] data, ModuleContext context) => Load(data, new ModuleCreationOptions(context)); /// /// Creates a instance from a byte[] @@ -202,18 +165,14 @@ public static ModuleDefMD Load(byte[] data, ModuleContext context) { /// Contents of a .NET module/assembly /// Module creation options or null /// A new instance - public static ModuleDefMD Load(byte[] data, ModuleCreationOptions options) { - return Load(MetaDataCreator.Load(data), options); - } + public static ModuleDefMD Load(byte[] data, ModuleCreationOptions options = null) => Load(MetadataFactory.Load(data, options?.Runtime ?? CLRRuntimeReaderKind.CLR), options); /// /// Creates a instance from a reflection module /// /// An existing reflection module /// A new instance - public static ModuleDefMD Load(System.Reflection.Module mod) { - return Load(mod, (ModuleCreationOptions)null, GetImageLayout(mod)); - } + public static ModuleDefMD Load(System.Reflection.Module mod) => Load(mod, (ModuleCreationOptions)null, GetImageLayout(mod)); /// /// Creates a instance from a reflection module @@ -221,9 +180,7 @@ public static ModuleDefMD Load(System.Reflection.Module mod) { /// An existing reflection module /// Module context or null /// A new instance - public static ModuleDefMD Load(System.Reflection.Module mod, ModuleContext context) { - return Load(mod, new ModuleCreationOptions(context), GetImageLayout(mod)); - } + public static ModuleDefMD Load(System.Reflection.Module mod, ModuleContext context) => Load(mod, new ModuleCreationOptions(context), GetImageLayout(mod)); /// /// Creates a instance from a reflection module @@ -231,9 +188,7 @@ public static ModuleDefMD Load(System.Reflection.Module mod, ModuleContext conte /// An existing reflection module /// Module creation options or null /// A new instance - public static ModuleDefMD Load(System.Reflection.Module mod, ModuleCreationOptions options) { - return Load(mod, options, GetImageLayout(mod)); - } + public static ModuleDefMD Load(System.Reflection.Module mod, ModuleCreationOptions options) => Load(mod, options, GetImageLayout(mod)); static ImageLayout GetImageLayout(System.Reflection.Module mod) { var fqn = mod.FullyQualifiedName; @@ -249,8 +204,18 @@ static ImageLayout GetImageLayout(System.Reflection.Module mod) { /// Module context or null /// Image layout of the module in memory /// A new instance - public static ModuleDefMD Load(System.Reflection.Module mod, ModuleContext context, ImageLayout imageLayout) { - return Load(mod, new ModuleCreationOptions(context), imageLayout); + public static ModuleDefMD Load(System.Reflection.Module mod, ModuleContext context, ImageLayout imageLayout) => Load(mod, new ModuleCreationOptions(context), imageLayout); + + static IntPtr GetModuleHandle(System.Reflection.Module mod) { +#if NETSTANDARD + var GetHINSTANCE = typeof(Marshal).GetMethod("GetHINSTANCE", new[] { typeof(System.Reflection.Module) }); + if (GetHINSTANCE is null) + return IntPtr.Zero; + + return (IntPtr)GetHINSTANCE.Invoke(null, new[] { mod }); +#else + return Marshal.GetHINSTANCE(mod); +#endif } /// @@ -261,10 +226,13 @@ public static ModuleDefMD Load(System.Reflection.Module mod, ModuleContext conte /// Image layout of the module in memory /// A new instance public static ModuleDefMD Load(System.Reflection.Module mod, ModuleCreationOptions options, ImageLayout imageLayout) { - IntPtr addr = Marshal.GetHINSTANCE(mod); - if (addr == new IntPtr(-1)) - throw new InvalidOperationException(string.Format("Module {0} has no HINSTANCE", mod)); - return Load(addr, options, imageLayout); + var addr = GetModuleHandle(mod); + if (addr != IntPtr.Zero && addr != new IntPtr(-1)) + return Load(addr, options, imageLayout); + var location = mod.FullyQualifiedName; + if (string.IsNullOrEmpty(location) || location[0] == '<') + throw new InvalidOperationException($"Module {mod} has no HINSTANCE"); + return Load(location, options); } /// @@ -272,9 +240,7 @@ public static ModuleDefMD Load(System.Reflection.Module mod, ModuleCreationOptio /// /// Address of a .NET module/assembly /// A new instance - public static ModuleDefMD Load(IntPtr addr) { - return Load(MetaDataCreator.Load(addr), (ModuleCreationOptions)null); - } + public static ModuleDefMD Load(IntPtr addr) => Load(MetadataFactory.Load(addr, CLRRuntimeReaderKind.CLR), (ModuleCreationOptions)null); /// /// Creates a instance from a memory location @@ -282,9 +248,7 @@ public static ModuleDefMD Load(IntPtr addr) { /// Address of a .NET module/assembly /// Module context or null /// A new instance - public static ModuleDefMD Load(IntPtr addr, ModuleContext context) { - return Load(MetaDataCreator.Load(addr), new ModuleCreationOptions(context)); - } + public static ModuleDefMD Load(IntPtr addr, ModuleContext context) => Load(MetadataFactory.Load(addr, CLRRuntimeReaderKind.CLR), new ModuleCreationOptions(context)); /// /// Creates a instance from a memory location @@ -292,18 +256,14 @@ public static ModuleDefMD Load(IntPtr addr, ModuleContext context) { /// Address of a .NET module/assembly /// Module creation options or null /// A new instance - public static ModuleDefMD Load(IntPtr addr, ModuleCreationOptions options) { - return Load(MetaDataCreator.Load(addr), options); - } + public static ModuleDefMD Load(IntPtr addr, ModuleCreationOptions options) => Load(MetadataFactory.Load(addr, options?.Runtime ?? CLRRuntimeReaderKind.CLR), options); /// /// Creates a instance /// /// PE image /// A new instance - public static ModuleDefMD Load(IPEImage peImage) { - return Load(MetaDataCreator.Load(peImage), (ModuleCreationOptions)null); - } + public static ModuleDefMD Load(IPEImage peImage) => Load(MetadataFactory.Load(peImage, CLRRuntimeReaderKind.CLR), (ModuleCreationOptions)null); /// /// Creates a instance @@ -311,9 +271,7 @@ public static ModuleDefMD Load(IPEImage peImage) { /// PE image /// Module context or null /// A new instance - public static ModuleDefMD Load(IPEImage peImage, ModuleContext context) { - return Load(MetaDataCreator.Load(peImage), new ModuleCreationOptions(context)); - } + public static ModuleDefMD Load(IPEImage peImage, ModuleContext context) => Load(MetadataFactory.Load(peImage, CLRRuntimeReaderKind.CLR), new ModuleCreationOptions(context)); /// /// Creates a instance @@ -321,9 +279,7 @@ public static ModuleDefMD Load(IPEImage peImage, ModuleContext context) { /// PE image /// Module creation options or null /// A new instance - public static ModuleDefMD Load(IPEImage peImage, ModuleCreationOptions options) { - return Load(MetaDataCreator.Load(peImage), options); - } + public static ModuleDefMD Load(IPEImage peImage, ModuleCreationOptions options) => Load(MetadataFactory.Load(peImage, options?.Runtime ?? CLRRuntimeReaderKind.CLR), options); /// /// Creates a instance from a memory location @@ -332,9 +288,7 @@ public static ModuleDefMD Load(IPEImage peImage, ModuleCreationOptions options) /// Module context or null /// Image layout of the file in memory /// A new instance - public static ModuleDefMD Load(IntPtr addr, ModuleContext context, ImageLayout imageLayout) { - return Load(MetaDataCreator.Load(addr, imageLayout), new ModuleCreationOptions(context)); - } + public static ModuleDefMD Load(IntPtr addr, ModuleContext context, ImageLayout imageLayout) => Load(MetadataFactory.Load(addr, imageLayout, CLRRuntimeReaderKind.CLR), new ModuleCreationOptions(context)); /// /// Creates a instance from a memory location @@ -343,21 +297,17 @@ public static ModuleDefMD Load(IntPtr addr, ModuleContext context, ImageLayout i /// Module creation options or null /// Image layout of the file in memory /// A new instance - public static ModuleDefMD Load(IntPtr addr, ModuleCreationOptions options, ImageLayout imageLayout) { - return Load(MetaDataCreator.Load(addr, imageLayout), options); - } + public static ModuleDefMD Load(IntPtr addr, ModuleCreationOptions options, ImageLayout imageLayout) => Load(MetadataFactory.Load(addr, imageLayout, options?.Runtime ?? CLRRuntimeReaderKind.CLR), options); /// /// Creates a instance from a stream /// - /// This will read all bytes from the stream and call . + /// This will read all bytes from the stream and call . /// It's better to use one of the other Load() methods. /// The stream (owned by caller) /// A new instance /// If is null - public static ModuleDefMD Load(Stream stream) { - return Load(stream, (ModuleCreationOptions)null); - } + public static ModuleDefMD Load(Stream stream) => Load(stream, (ModuleCreationOptions)null); /// /// Creates a instance from a stream @@ -368,9 +318,7 @@ public static ModuleDefMD Load(Stream stream) { /// Module context or null /// A new instance /// If is null - public static ModuleDefMD Load(Stream stream, ModuleContext context) { - return Load(stream, new ModuleCreationOptions(context)); - } + public static ModuleDefMD Load(Stream stream, ModuleContext context) => Load(stream, new ModuleCreationOptions(context)); /// /// Creates a instance from a stream @@ -382,8 +330,8 @@ public static ModuleDefMD Load(Stream stream, ModuleContext context) { /// A new instance /// If is null public static ModuleDefMD Load(Stream stream, ModuleCreationOptions options) { - if (stream == null) - throw new ArgumentNullException("stream"); + if (stream is null) + throw new ArgumentNullException(nameof(stream)); if (stream.Length > int.MaxValue) throw new ArgumentException("Stream is too big"); var data = new byte[(int)stream.Length]; @@ -394,79 +342,69 @@ public static ModuleDefMD Load(Stream stream, ModuleCreationOptions options) { } /// - /// Creates a instance from a + /// Creates a instance from a /// - /// The metadata + /// The metadata /// Module creation options or null - /// A new instance that now owns - internal static ModuleDefMD Load(MetaData metaData, ModuleCreationOptions options) { - return new ModuleDefMD(metaData, options); - } + /// A new instance that now owns + internal static ModuleDefMD Load(MetadataBase metadata, ModuleCreationOptions options) => new ModuleDefMD(metadata, options); /// /// Constructor /// - /// The metadata + /// The metadata /// Module creation options or null - /// If is null - ModuleDefMD(MetaData metaData, ModuleCreationOptions options) + /// If is null + ModuleDefMD(MetadataBase metadata, ModuleCreationOptions options) : base(null, 1) { #if DEBUG - if (metaData == null) - throw new ArgumentNullException("metaData"); + if (metadata is null) + throw new ArgumentNullException(nameof(metadata)); #endif - if (options == null) + if (options is null) options = ModuleCreationOptions.Default; - this.metaData = metaData; - this.context = options.Context; + this.metadata = metadata; + context = options.Context; Initialize(); InitializeFromRawRow(); - location = metaData.PEImage.FileName ?? string.Empty; - - this.Kind = GetKind(); - this.Characteristics = MetaData.PEImage.ImageNTHeaders.FileHeader.Characteristics; - this.DllCharacteristics = MetaData.PEImage.ImageNTHeaders.OptionalHeader.DllCharacteristics; - this.RuntimeVersion = MetaData.VersionString; - this.Machine = MetaData.PEImage.ImageNTHeaders.FileHeader.Machine; - this.Cor20HeaderFlags = MetaData.ImageCor20Header.Flags; - this.Cor20HeaderRuntimeVersion = (uint)(MetaData.ImageCor20Header.MajorRuntimeVersion << 16) | MetaData.ImageCor20Header.MinorRuntimeVersion; - this.TablesHeaderVersion = MetaData.TablesStream.Version; + location = metadata.PEImage.Filename ?? string.Empty; + + Kind = GetKind(); + Characteristics = Metadata.PEImage.ImageNTHeaders.FileHeader.Characteristics; + DllCharacteristics = Metadata.PEImage.ImageNTHeaders.OptionalHeader.DllCharacteristics; + RuntimeVersion = Metadata.VersionString; + Machine = Metadata.PEImage.ImageNTHeaders.FileHeader.Machine; + Cor20HeaderFlags = Metadata.ImageCor20Header.Flags; + Cor20HeaderRuntimeVersion = (uint)(Metadata.ImageCor20Header.MajorRuntimeVersion << 16) | Metadata.ImageCor20Header.MinorRuntimeVersion; + TablesHeaderVersion = Metadata.TablesStream.Version; corLibTypes = new CorLibTypes(this, options.CorLibAssemblyRef ?? FindCorLibAssemblyRef() ?? CreateDefaultCorLibAssemblyRef()); InitializePdb(options); } void InitializePdb(ModuleCreationOptions options) { - if (options == null) + if (options is null) return; LoadPdb(CreateSymbolReader(options)); } - ISymbolReader CreateSymbolReader(ModuleCreationOptions options) { - if (options.CreateSymbolReader != null) { - var symReader = options.CreateSymbolReader(this); - if (symReader != null) - return symReader; - } - - if (options.PdbFileOrData != null) { + SymbolReader CreateSymbolReader(ModuleCreationOptions options) { + if (options.PdbFileOrData is not null) { var pdbFileName = options.PdbFileOrData as string; if (!string.IsNullOrEmpty(pdbFileName)) { - var symReader = SymbolReaderCreator.Create(options.PdbImplementation, metaData, pdbFileName); - if (symReader != null) + var symReader = SymbolReaderFactory.Create(options.PdbOptions, metadata, pdbFileName); + if (symReader is not null) return symReader; } - var pdbData = options.PdbFileOrData as byte[]; - if (pdbData != null) - return SymbolReaderCreator.Create(options.PdbImplementation, metaData, pdbData); + if (options.PdbFileOrData is byte[] pdbData) + return SymbolReaderFactory.Create(options.PdbOptions, metadata, pdbData); - var pdbStream = options.PdbFileOrData as IImageStream; - if (pdbStream != null) - return SymbolReaderCreator.Create(options.PdbImplementation, metaData, pdbStream); + if (options.PdbFileOrData is DataReaderFactory pdbStream) + return SymbolReaderFactory.Create(options.PdbOptions, metadata, pdbStream); } - if (options.TryToLoadPdbFromDisk && !string.IsNullOrEmpty(location)) - return SymbolReaderCreator.Create(options.PdbImplementation, location); + if (options.TryToLoadPdbFromDisk) + return SymbolReaderFactory.CreateFromAssemblyFile(options.PdbOptions, metadata, location ?? string.Empty); return null; } @@ -475,14 +413,14 @@ ISymbolReader CreateSymbolReader(ModuleCreationOptions options) { /// Loads symbols using /// /// PDB symbol reader - public void LoadPdb(ISymbolReader symbolReader) { - if (symbolReader == null) + public void LoadPdb(SymbolReader symbolReader) { + if (symbolReader is null) return; - if (pdbState != null) + if (pdbState is not null) throw new InvalidOperationException("PDB file has already been initialized"); var orig = Interlocked.CompareExchange(ref pdbState, new PdbState(symbolReader, this), null); - if (orig != null) + if (orig is not null) throw new InvalidOperationException("PDB file has already been initialized"); } @@ -490,91 +428,83 @@ public void LoadPdb(ISymbolReader symbolReader) { /// Loads symbols from a PDB file /// /// PDB file name - public void LoadPdb(string pdbFileName) { - LoadPdb(PdbImplType.Default, pdbFileName); - } + public void LoadPdb(string pdbFileName) => + LoadPdb(ModuleCreationOptions.DefaultPdbReaderOptions, pdbFileName); /// /// Loads symbols from a PDB file /// - /// PDB implementation to use + /// PDB reader options /// PDB file name - public void LoadPdb(PdbImplType pdbImpl, string pdbFileName) { - LoadPdb(SymbolReaderCreator.Create(pdbImpl, metaData, pdbFileName)); - } + public void LoadPdb(PdbReaderOptions options, string pdbFileName) => + LoadPdb(SymbolReaderFactory.Create(options, metadata, pdbFileName)); /// /// Loads symbols from a byte array /// /// PDB data - public void LoadPdb(byte[] pdbData) { - LoadPdb(PdbImplType.Default, pdbData); - } + public void LoadPdb(byte[] pdbData) => + LoadPdb(ModuleCreationOptions.DefaultPdbReaderOptions, pdbData); /// /// Loads symbols from a byte array /// - /// PDB implementation to use + /// PDB reader options /// PDB data - public void LoadPdb(PdbImplType pdbImpl, byte[] pdbData) { - LoadPdb(SymbolReaderCreator.Create(pdbImpl, metaData, pdbData)); - } + public void LoadPdb(PdbReaderOptions options, byte[] pdbData) => + LoadPdb(SymbolReaderFactory.Create(options, metadata, pdbData)); /// /// Loads symbols from a stream /// /// PDB file stream which is now owned by us - public void LoadPdb(IImageStream pdbStream) { - LoadPdb(PdbImplType.Default, pdbStream); - } + public void LoadPdb(DataReaderFactory pdbStream) => + LoadPdb(ModuleCreationOptions.DefaultPdbReaderOptions, pdbStream); /// /// Loads symbols from a stream /// - /// PDB implementation to use + /// PDB reader options /// PDB file stream which is now owned by us - public void LoadPdb(PdbImplType pdbImpl, IImageStream pdbStream) { - LoadPdb(SymbolReaderCreator.Create(pdbImpl, metaData, pdbStream)); - } + public void LoadPdb(PdbReaderOptions options, DataReaderFactory pdbStream) => + LoadPdb(SymbolReaderFactory.Create(options, metadata, pdbStream)); /// /// Loads symbols if a PDB file is available /// - public void LoadPdb() { - LoadPdb(PdbImplType.Default); - } + public void LoadPdb() => + LoadPdb(ModuleCreationOptions.DefaultPdbReaderOptions); /// /// Loads symbols if a PDB file is available /// - /// PDB implementation to use - public void LoadPdb(PdbImplType pdbImpl) { - var loc = location; - if (string.IsNullOrEmpty(loc)) + /// PDB reader options + public void LoadPdb(PdbReaderOptions options) => + LoadPdb(SymbolReaderFactory.CreateFromAssemblyFile(options, metadata, location ?? string.Empty)); + + internal void InitializeCustomDebugInfos(MDToken token, GenericParamContext gpContext, IList result) { + var ps = pdbState; + if (ps is null) return; - LoadPdb(SymbolReaderCreator.Create(pdbImpl, loc)); + ps.InitializeCustomDebugInfos(token, gpContext, result); } ModuleKind GetKind() { if (TablesStream.AssemblyTable.Rows < 1) return ModuleKind.NetModule; - var peImage = MetaData.PEImage; + var peImage = Metadata.PEImage; if ((peImage.ImageNTHeaders.FileHeader.Characteristics & Characteristics.Dll) != 0) return ModuleKind.Dll; - switch (peImage.ImageNTHeaders.OptionalHeader.Subsystem) { - default: - case Subsystem.WindowsGui: - return ModuleKind.Windows; - - case Subsystem.WindowsCui: - return ModuleKind.Console; - } + return peImage.ImageNTHeaders.OptionalHeader.Subsystem switch { + Subsystem.WindowsCui => ModuleKind.Console, + _ => ModuleKind.Windows, + }; } void Initialize() { - var ts = metaData.TablesStream; + var ts = metadata.TablesStream; listModuleDefMD = new SimpleLazyList(ts.ModuleTable.Rows, rid2 => rid2 == 1 ? this : new ModuleDefMD2(this, rid2)); listTypeRefMD = new SimpleLazyList(ts.TypeRefTable.Rows, rid2 => new TypeRefMD(this, rid2)); @@ -604,7 +534,7 @@ void Initialize() { for (int i = 0; i < 64; i++) { var tbl = TablesStream.Get((Table)i); - lastUsedRids[i] = tbl == null ? 0 : (int)tbl.Rows; + lastUsedRids[i] = tbl is null ? 0 : (int)tbl.Rows; } } @@ -626,6 +556,12 @@ void Initialize() { { "mscorlib, Version=3.5.0.0, Culture=neutral, PublicKeyToken=969db8053d3322ac", 60 }, { "mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=969db8053d3322ac", 50 }, }; + static readonly string[] corlibs = new string[] { + "System.Private.CoreLib", + "System.Runtime", + "netstandard", + "mscorlib", + }; /// /// Finds a mscorlib @@ -638,31 +574,34 @@ AssemblyRef FindCorLibAssemblyRef() { int currentPriority = int.MinValue; for (uint i = 1; i <= numAsmRefs; i++) { var asmRef = ResolveAssemblyRef(i); - int priority; - if (!preferredCorLibs.TryGetValue(asmRef.FullName, out priority)) + if (!preferredCorLibs.TryGetValue(asmRef.FullName, out int priority)) continue; if (priority > currentPriority) { currentPriority = priority; corLibAsmRef = asmRef; } } - if (corLibAsmRef != null) + if (corLibAsmRef is not null) return corLibAsmRef; - for (uint i = 1; i <= numAsmRefs; i++) { - var asmRef = ResolveAssemblyRef(i); - if (!UTF8String.ToSystemStringOrEmpty(asmRef.Name).Equals("mscorlib", StringComparison.OrdinalIgnoreCase)) - continue; - if (IsGreaterAssemblyRefVersion(corLibAsmRef, asmRef)) - corLibAsmRef = asmRef; + foreach (var corlib in corlibs) { + for (uint i = 1; i <= numAsmRefs; i++) { + var asmRef = ResolveAssemblyRef(i); + if (!UTF8String.ToSystemStringOrEmpty(asmRef.Name).Equals(corlib, StringComparison.OrdinalIgnoreCase)) + continue; + if (IsGreaterAssemblyRefVersion(corLibAsmRef, asmRef)) + corLibAsmRef = asmRef; + } + if (corLibAsmRef is not null) + return corLibAsmRef; } - if (corLibAsmRef != null) - return corLibAsmRef; // If we've loaded mscorlib itself, it won't have any AssemblyRefs to itself. var asm = Assembly; - if (asm != null && asm.IsCorLib()) + if (asm is not null && (asm.IsCorLib() || Find("System.Object", false) is not null)) { + IsCoreLibraryModule = true; return UpdateRowId(new AssemblyRefUser(asm)); + } return corLibAsmRef; } @@ -672,55 +611,58 @@ AssemblyRef FindCorLibAssemblyRef() { /// /// AssemblyRef CreateDefaultCorLibAssemblyRef() { - AssemblyRef asmRef; - var asm = Assembly; - if (asm != null && Find("System.Int32", false) != null) - asmRef = new AssemblyRefUser(asm); - else if (this.IsClr40) - asmRef = AssemblyRefUser.CreateMscorlibReferenceCLR40(); - else if (this.IsClr20) - asmRef = AssemblyRefUser.CreateMscorlibReferenceCLR20(); - else if (this.IsClr11) - asmRef = AssemblyRefUser.CreateMscorlibReferenceCLR11(); - else if (this.IsClr10) - asmRef = AssemblyRefUser.CreateMscorlibReferenceCLR10(); - else - asmRef = AssemblyRefUser.CreateMscorlibReferenceCLR40(); - return UpdateRowId(asmRef); + var asmRef = GetAlternativeCorLibReference(); + if (asmRef is not null) + return UpdateRowId(asmRef); + + if (IsClr40) + return UpdateRowId(AssemblyRefUser.CreateMscorlibReferenceCLR40()); + if (IsClr20) + return UpdateRowId(AssemblyRefUser.CreateMscorlibReferenceCLR20()); + if (IsClr11) + return UpdateRowId(AssemblyRefUser.CreateMscorlibReferenceCLR11()); + if (IsClr10) + return UpdateRowId(AssemblyRefUser.CreateMscorlibReferenceCLR10()); + return UpdateRowId(AssemblyRefUser.CreateMscorlibReferenceCLR40()); + } + + AssemblyRef GetAlternativeCorLibReference() { + foreach (var asmRef in GetAssemblyRefs()) { + if (IsAssemblyRef(asmRef, systemRuntimeName, contractsPublicKeyToken)) + return asmRef; + } + foreach (var asmRef in GetAssemblyRefs()) { + if (IsAssemblyRef(asmRef, corefxName, contractsPublicKeyToken)) + return asmRef; + } + return null; } + static bool IsAssemblyRef(AssemblyRef asmRef, UTF8String name, PublicKeyToken token) { + if (asmRef.Name != name) + return false; + var pkot = asmRef.PublicKeyOrToken; + if (pkot is null) + return false; + return token.Equals(pkot.Token); + } + static readonly UTF8String systemRuntimeName = new UTF8String("System.Runtime"); + static readonly UTF8String corefxName = new UTF8String("corefx"); + static readonly PublicKeyToken contractsPublicKeyToken = new PublicKeyToken("b03f5f7f11d50a3a"); + /// protected override void Dispose(bool disposing) { // Call base first since it will dispose of all the resources, which will - // eventually use metaData that we will dispose + // eventually use metadata that we will dispose base.Dispose(disposing); if (disposing) { - var md = metaData; - if (md != null) + var md = metadata; + if (md is not null) md.Dispose(); - metaData = null; + metadata = null; } } - /// - /// Resolves a token - /// - /// The metadata token - /// A or null if is invalid - public IMDTokenProvider ResolveToken(int token) { - return ResolveToken((uint)token, new GenericParamContext()); - } - - /// - /// Resolves a token - /// - /// The metadata token - /// Generic parameter context - /// A or null if is invalid - public IMDTokenProvider ResolveToken(int token, GenericParamContext gpContext) { - return ResolveToken((uint)token, gpContext); - } - /// /// Resolves a token /// @@ -729,34 +671,34 @@ public IMDTokenProvider ResolveToken(int token, GenericParamContext gpContext) { /// A or null if is invalid public override IMDTokenProvider ResolveToken(uint token, GenericParamContext gpContext) { uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.Module: return ResolveModule(rid); - case Table.TypeRef: return ResolveTypeRef(rid); - case Table.TypeDef: return ResolveTypeDef(rid); - case Table.Field: return ResolveField(rid); - case Table.Method: return ResolveMethod(rid); - case Table.Param: return ResolveParam(rid); - case Table.InterfaceImpl: return ResolveInterfaceImpl(rid, gpContext); - case Table.MemberRef: return ResolveMemberRef(rid, gpContext); - case Table.Constant: return ResolveConstant(rid); - case Table.DeclSecurity: return ResolveDeclSecurity(rid); - case Table.ClassLayout: return ResolveClassLayout(rid); - case Table.StandAloneSig: return ResolveStandAloneSig(rid, gpContext); - case Table.Event: return ResolveEvent(rid); - case Table.Property: return ResolveProperty(rid); - case Table.ModuleRef: return ResolveModuleRef(rid); - case Table.TypeSpec: return ResolveTypeSpec(rid, gpContext); - case Table.ImplMap: return ResolveImplMap(rid); - case Table.Assembly: return ResolveAssembly(rid); - case Table.AssemblyRef: return ResolveAssemblyRef(rid); - case Table.File: return ResolveFile(rid); - case Table.ExportedType: return ResolveExportedType(rid); - case Table.ManifestResource:return ResolveManifestResource(rid); - case Table.GenericParam: return ResolveGenericParam(rid); - case Table.MethodSpec: return ResolveMethodSpec(rid, gpContext); - case Table.GenericParamConstraint: return ResolveGenericParamConstraint(rid, gpContext); - } - return null; + return MDToken.ToTable(token) switch { + Table.Module => ResolveModule(rid), + Table.TypeRef => ResolveTypeRef(rid), + Table.TypeDef => ResolveTypeDef(rid), + Table.Field => ResolveField(rid), + Table.Method => ResolveMethod(rid), + Table.Param => ResolveParam(rid), + Table.InterfaceImpl => ResolveInterfaceImpl(rid, gpContext), + Table.MemberRef => ResolveMemberRef(rid, gpContext), + Table.Constant => ResolveConstant(rid), + Table.DeclSecurity => ResolveDeclSecurity(rid), + Table.ClassLayout => ResolveClassLayout(rid), + Table.StandAloneSig => ResolveStandAloneSig(rid, gpContext), + Table.Event => ResolveEvent(rid), + Table.Property => ResolveProperty(rid), + Table.ModuleRef => ResolveModuleRef(rid), + Table.TypeSpec => ResolveTypeSpec(rid, gpContext), + Table.ImplMap => ResolveImplMap(rid), + Table.Assembly => ResolveAssembly(rid), + Table.AssemblyRef => ResolveAssemblyRef(rid), + Table.File => ResolveFile(rid), + Table.ExportedType => ResolveExportedType(rid), + Table.ManifestResource => ResolveManifestResource(rid), + Table.GenericParam => ResolveGenericParam(rid), + Table.MethodSpec => ResolveMethodSpec(rid, gpContext), + Table.GenericParamConstraint => ResolveGenericParamConstraint(rid, gpContext), + _ => null, + }; } /// @@ -764,63 +706,49 @@ public override IMDTokenProvider ResolveToken(uint token, GenericParamContext gp /// /// The row ID /// A instance or null if is invalid - public ModuleDef ResolveModule(uint rid) { - return listModuleDefMD[rid - 1]; - } + public ModuleDef ResolveModule(uint rid) => listModuleDefMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public TypeRef ResolveTypeRef(uint rid) { - return listTypeRefMD[rid - 1]; - } + public TypeRef ResolveTypeRef(uint rid) => listTypeRefMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public TypeDef ResolveTypeDef(uint rid) { - return listTypeDefMD[rid - 1]; - } + public TypeDef ResolveTypeDef(uint rid) => listTypeDefMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public FieldDef ResolveField(uint rid) { - return listFieldDefMD[rid - 1]; - } + public FieldDef ResolveField(uint rid) => listFieldDefMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public MethodDef ResolveMethod(uint rid) { - return listMethodDefMD[rid - 1]; - } + public MethodDef ResolveMethod(uint rid) => listMethodDefMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public ParamDef ResolveParam(uint rid) { - return listParamDefMD[rid - 1]; - } + public ParamDef ResolveParam(uint rid) => listParamDefMD[rid - 1]; /// /// Resolves an /// /// The row ID /// A instance or null if is invalid - public InterfaceImpl ResolveInterfaceImpl(uint rid) { - return listInterfaceImplMD[rid - 1, new GenericParamContext()]; - } + public InterfaceImpl ResolveInterfaceImpl(uint rid) => listInterfaceImplMD[rid - 1, new GenericParamContext()]; /// /// Resolves an @@ -828,18 +756,14 @@ public InterfaceImpl ResolveInterfaceImpl(uint rid) { /// The row ID /// Generic parameter context /// A instance or null if is invalid - public InterfaceImpl ResolveInterfaceImpl(uint rid, GenericParamContext gpContext) { - return listInterfaceImplMD[rid - 1, gpContext]; - } + public InterfaceImpl ResolveInterfaceImpl(uint rid, GenericParamContext gpContext) => listInterfaceImplMD[rid - 1, gpContext]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public MemberRef ResolveMemberRef(uint rid) { - return listMemberRefMD[rid - 1, new GenericParamContext()]; - } + public MemberRef ResolveMemberRef(uint rid) => listMemberRefMD[rid - 1, new GenericParamContext()]; /// /// Resolves a @@ -847,45 +771,35 @@ public MemberRef ResolveMemberRef(uint rid) { /// The row ID /// Generic parameter context /// A instance or null if is invalid - public MemberRef ResolveMemberRef(uint rid, GenericParamContext gpContext) { - return listMemberRefMD[rid - 1, gpContext]; - } + public MemberRef ResolveMemberRef(uint rid, GenericParamContext gpContext) => listMemberRefMD[rid - 1, gpContext]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public Constant ResolveConstant(uint rid) { - return listConstantMD[rid - 1]; - } + public Constant ResolveConstant(uint rid) => listConstantMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public DeclSecurity ResolveDeclSecurity(uint rid) { - return listDeclSecurityMD[rid - 1]; - } + public DeclSecurity ResolveDeclSecurity(uint rid) => listDeclSecurityMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public ClassLayout ResolveClassLayout(uint rid) { - return listClassLayoutMD[rid - 1]; - } + public ClassLayout ResolveClassLayout(uint rid) => listClassLayoutMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public StandAloneSig ResolveStandAloneSig(uint rid) { - return listStandAloneSigMD[rid - 1, new GenericParamContext()]; - } + public StandAloneSig ResolveStandAloneSig(uint rid) => listStandAloneSigMD[rid - 1, new GenericParamContext()]; /// /// Resolves a @@ -893,45 +807,35 @@ public StandAloneSig ResolveStandAloneSig(uint rid) { /// The row ID /// Generic parameter context /// A instance or null if is invalid - public StandAloneSig ResolveStandAloneSig(uint rid, GenericParamContext gpContext) { - return listStandAloneSigMD[rid - 1, gpContext]; - } + public StandAloneSig ResolveStandAloneSig(uint rid, GenericParamContext gpContext) => listStandAloneSigMD[rid - 1, gpContext]; /// /// Resolves an /// /// The row ID /// A instance or null if is invalid - public EventDef ResolveEvent(uint rid) { - return listEventDefMD[rid - 1]; - } + public EventDef ResolveEvent(uint rid) => listEventDefMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public PropertyDef ResolveProperty(uint rid) { - return listPropertyDefMD[rid - 1]; - } + public PropertyDef ResolveProperty(uint rid) => listPropertyDefMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public ModuleRef ResolveModuleRef(uint rid) { - return listModuleRefMD[rid - 1]; - } + public ModuleRef ResolveModuleRef(uint rid) => listModuleRefMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public TypeSpec ResolveTypeSpec(uint rid) { - return listTypeSpecMD[rid - 1, new GenericParamContext()]; - } + public TypeSpec ResolveTypeSpec(uint rid) => listTypeSpecMD[rid - 1, new GenericParamContext()]; /// /// Resolves a @@ -939,81 +843,63 @@ public TypeSpec ResolveTypeSpec(uint rid) { /// The row ID /// Generic parameter context /// A instance or null if is invalid - public TypeSpec ResolveTypeSpec(uint rid, GenericParamContext gpContext) { - return listTypeSpecMD[rid - 1, gpContext]; - } + public TypeSpec ResolveTypeSpec(uint rid, GenericParamContext gpContext) => listTypeSpecMD[rid - 1, gpContext]; /// /// Resolves an /// /// The row ID /// A instance or null if is invalid - public ImplMap ResolveImplMap(uint rid) { - return listImplMapMD[rid - 1]; - } + public ImplMap ResolveImplMap(uint rid) => listImplMapMD[rid - 1]; /// /// Resolves an /// /// The row ID /// A instance or null if is invalid - public AssemblyDef ResolveAssembly(uint rid) { - return listAssemblyDefMD[rid - 1]; - } + public AssemblyDef ResolveAssembly(uint rid) => listAssemblyDefMD[rid - 1]; /// /// Resolves an /// /// The row ID /// A instance or null if is invalid - public AssemblyRef ResolveAssemblyRef(uint rid) { - return listAssemblyRefMD[rid - 1]; - } + public AssemblyRef ResolveAssemblyRef(uint rid) => listAssemblyRefMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public FileDef ResolveFile(uint rid) { - return listFileDefMD[rid - 1]; - } + public FileDef ResolveFile(uint rid) => listFileDefMD[rid - 1]; /// /// Resolves an /// /// The row ID /// A instance or null if is invalid - public ExportedType ResolveExportedType(uint rid) { - return listExportedTypeMD[rid - 1]; - } + public ExportedType ResolveExportedType(uint rid) => listExportedTypeMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public ManifestResource ResolveManifestResource(uint rid) { - return listManifestResourceMD[rid - 1]; - } + public ManifestResource ResolveManifestResource(uint rid) => listManifestResourceMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public GenericParam ResolveGenericParam(uint rid) { - return listGenericParamMD[rid - 1]; - } + public GenericParam ResolveGenericParam(uint rid) => listGenericParamMD[rid - 1]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public MethodSpec ResolveMethodSpec(uint rid) { - return listMethodSpecMD[rid - 1, new GenericParamContext()]; - } + public MethodSpec ResolveMethodSpec(uint rid) => listMethodSpecMD[rid - 1, new GenericParamContext()]; /// /// Resolves a @@ -1021,18 +907,14 @@ public MethodSpec ResolveMethodSpec(uint rid) { /// The row ID /// Generic parameter context /// A instance or null if is invalid - public MethodSpec ResolveMethodSpec(uint rid, GenericParamContext gpContext) { - return listMethodSpecMD[rid - 1, gpContext]; - } + public MethodSpec ResolveMethodSpec(uint rid, GenericParamContext gpContext) => listMethodSpecMD[rid - 1, gpContext]; /// /// Resolves a /// /// The row ID /// A instance or null if is invalid - public GenericParamConstraint ResolveGenericParamConstraint(uint rid) { - return listGenericParamConstraintMD[rid - 1, new GenericParamContext()]; - } + public GenericParamConstraint ResolveGenericParamConstraint(uint rid) => listGenericParamConstraintMD[rid - 1, new GenericParamContext()]; /// /// Resolves a @@ -1040,18 +922,14 @@ public GenericParamConstraint ResolveGenericParamConstraint(uint rid) { /// The row ID /// Generic parameter context /// A instance or null if is invalid - public GenericParamConstraint ResolveGenericParamConstraint(uint rid, GenericParamContext gpContext) { - return listGenericParamConstraintMD[rid - 1, gpContext]; - } + public GenericParamConstraint ResolveGenericParamConstraint(uint rid, GenericParamContext gpContext) => listGenericParamConstraintMD[rid - 1, gpContext]; /// /// Resolves a /// /// A TypeDefOrRef coded token /// A or null if is invalid - public ITypeDefOrRef ResolveTypeDefOrRef(uint codedToken) { - return ResolveTypeDefOrRef(codedToken, new GenericParamContext()); - } + public ITypeDefOrRef ResolveTypeDefOrRef(uint codedToken) => ResolveTypeDefOrRef(codedToken, new GenericParamContext()); /// /// Resolves a @@ -1060,16 +938,15 @@ public ITypeDefOrRef ResolveTypeDefOrRef(uint codedToken) { /// Generic parameter context /// A or null if is invalid public ITypeDefOrRef ResolveTypeDefOrRef(uint codedToken, GenericParamContext gpContext) { - uint token; - if (!CodedToken.TypeDefOrRef.Decode(codedToken, out token)) + if (!CodedToken.TypeDefOrRef.Decode(codedToken, out uint token)) return null; uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.TypeDef: return ResolveTypeDef(rid); - case Table.TypeRef: return ResolveTypeRef(rid); - case Table.TypeSpec: return ResolveTypeSpec(rid, gpContext); - } - return null; + return MDToken.ToTable(token) switch { + Table.TypeDef => ResolveTypeDef(rid), + Table.TypeRef => ResolveTypeRef(rid), + Table.TypeSpec => ResolveTypeSpec(rid, gpContext), + _ => null, + }; } /// @@ -1078,16 +955,15 @@ public ITypeDefOrRef ResolveTypeDefOrRef(uint codedToken, GenericParamContext gp /// A HasConstant coded token /// A or null if is invalid public IHasConstant ResolveHasConstant(uint codedToken) { - uint token; - if (!CodedToken.HasConstant.Decode(codedToken, out token)) + if (!CodedToken.HasConstant.Decode(codedToken, out uint token)) return null; uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.Field: return ResolveField(rid); - case Table.Param: return ResolveParam(rid); - case Table.Property:return ResolveProperty(rid); - } - return null; + return MDToken.ToTable(token) switch { + Table.Field => ResolveField(rid), + Table.Param => ResolveParam(rid), + Table.Property => ResolveProperty(rid), + _ => null, + }; } /// @@ -1095,9 +971,7 @@ public IHasConstant ResolveHasConstant(uint codedToken) { /// /// A HasCustomAttribute coded token /// A or null if is invalid - public IHasCustomAttribute ResolveHasCustomAttribute(uint codedToken) { - return ResolveHasCustomAttribute(codedToken, new GenericParamContext()); - } + public IHasCustomAttribute ResolveHasCustomAttribute(uint codedToken) => ResolveHasCustomAttribute(codedToken, new GenericParamContext()); /// /// Resolves a @@ -1106,35 +980,34 @@ public IHasCustomAttribute ResolveHasCustomAttribute(uint codedToken) { /// Generic parameter context /// A or null if is invalid public IHasCustomAttribute ResolveHasCustomAttribute(uint codedToken, GenericParamContext gpContext) { - uint token; - if (!CodedToken.HasCustomAttribute.Decode(codedToken, out token)) + if (!CodedToken.HasCustomAttribute.Decode(codedToken, out uint token)) return null; uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.Method: return ResolveMethod(rid); - case Table.Field: return ResolveField(rid); - case Table.TypeRef: return ResolveTypeRef(rid); - case Table.TypeDef: return ResolveTypeDef(rid); - case Table.Param: return ResolveParam(rid); - case Table.InterfaceImpl: return ResolveInterfaceImpl(rid, gpContext); - case Table.MemberRef: return ResolveMemberRef(rid, gpContext); - case Table.Module: return ResolveModule(rid); - case Table.DeclSecurity:return ResolveDeclSecurity(rid); - case Table.Property: return ResolveProperty(rid); - case Table.Event: return ResolveEvent(rid); - case Table.StandAloneSig: return ResolveStandAloneSig(rid, gpContext); - case Table.ModuleRef: return ResolveModuleRef(rid); - case Table.TypeSpec: return ResolveTypeSpec(rid, gpContext); - case Table.Assembly: return ResolveAssembly(rid); - case Table.AssemblyRef: return ResolveAssemblyRef(rid); - case Table.File: return ResolveFile(rid); - case Table.ExportedType:return ResolveExportedType(rid); - case Table.ManifestResource: return ResolveManifestResource(rid); - case Table.GenericParam:return ResolveGenericParam(rid); - case Table.MethodSpec: return ResolveMethodSpec(rid, gpContext); - case Table.GenericParamConstraint: return ResolveGenericParamConstraint(rid, gpContext); - } - return null; + return MDToken.ToTable(token) switch { + Table.Method => ResolveMethod(rid), + Table.Field => ResolveField(rid), + Table.TypeRef => ResolveTypeRef(rid), + Table.TypeDef => ResolveTypeDef(rid), + Table.Param => ResolveParam(rid), + Table.InterfaceImpl => ResolveInterfaceImpl(rid, gpContext), + Table.MemberRef => ResolveMemberRef(rid, gpContext), + Table.Module => ResolveModule(rid), + Table.DeclSecurity => ResolveDeclSecurity(rid), + Table.Property => ResolveProperty(rid), + Table.Event => ResolveEvent(rid), + Table.StandAloneSig => ResolveStandAloneSig(rid, gpContext), + Table.ModuleRef => ResolveModuleRef(rid), + Table.TypeSpec => ResolveTypeSpec(rid, gpContext), + Table.Assembly => ResolveAssembly(rid), + Table.AssemblyRef => ResolveAssemblyRef(rid), + Table.File => ResolveFile(rid), + Table.ExportedType => ResolveExportedType(rid), + Table.ManifestResource => ResolveManifestResource(rid), + Table.GenericParam => ResolveGenericParam(rid), + Table.MethodSpec => ResolveMethodSpec(rid, gpContext), + Table.GenericParamConstraint => ResolveGenericParamConstraint(rid, gpContext), + _ => null, + }; } /// @@ -1143,15 +1016,14 @@ public IHasCustomAttribute ResolveHasCustomAttribute(uint codedToken, GenericPar /// A HasFieldMarshal coded token /// A or null if is invalid public IHasFieldMarshal ResolveHasFieldMarshal(uint codedToken) { - uint token; - if (!CodedToken.HasFieldMarshal.Decode(codedToken, out token)) + if (!CodedToken.HasFieldMarshal.Decode(codedToken, out uint token)) return null; uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.Field: return ResolveField(rid); - case Table.Param: return ResolveParam(rid); - } - return null; + return MDToken.ToTable(token) switch { + Table.Field => ResolveField(rid), + Table.Param => ResolveParam(rid), + _ => null, + }; } /// @@ -1160,16 +1032,15 @@ public IHasFieldMarshal ResolveHasFieldMarshal(uint codedToken) { /// A HasDeclSecurity coded token /// A or null if is invalid public IHasDeclSecurity ResolveHasDeclSecurity(uint codedToken) { - uint token; - if (!CodedToken.HasDeclSecurity.Decode(codedToken, out token)) + if (!CodedToken.HasDeclSecurity.Decode(codedToken, out uint token)) return null; uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.TypeDef: return ResolveTypeDef(rid); - case Table.Method: return ResolveMethod(rid); - case Table.Assembly: return ResolveAssembly(rid); - } - return null; + return MDToken.ToTable(token) switch { + Table.TypeDef => ResolveTypeDef(rid), + Table.Method => ResolveMethod(rid), + Table.Assembly => ResolveAssembly(rid), + _ => null, + }; } /// @@ -1177,9 +1048,7 @@ public IHasDeclSecurity ResolveHasDeclSecurity(uint codedToken) { /// /// A MemberRefParent coded token /// A or null if is invalid - public IMemberRefParent ResolveMemberRefParent(uint codedToken) { - return ResolveMemberRefParent(codedToken, new GenericParamContext()); - } + public IMemberRefParent ResolveMemberRefParent(uint codedToken) => ResolveMemberRefParent(codedToken, new GenericParamContext()); /// /// Resolves a @@ -1188,18 +1057,17 @@ public IMemberRefParent ResolveMemberRefParent(uint codedToken) { /// Generic parameter context /// A or null if is invalid public IMemberRefParent ResolveMemberRefParent(uint codedToken, GenericParamContext gpContext) { - uint token; - if (!CodedToken.MemberRefParent.Decode(codedToken, out token)) + if (!CodedToken.MemberRefParent.Decode(codedToken, out uint token)) return null; uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.TypeDef: return ResolveTypeDef(rid); - case Table.TypeRef: return ResolveTypeRef(rid); - case Table.ModuleRef: return ResolveModuleRef(rid); - case Table.Method: return ResolveMethod(rid); - case Table.TypeSpec: return ResolveTypeSpec(rid, gpContext); - } - return null; + return MDToken.ToTable(token) switch { + Table.TypeDef => ResolveTypeDef(rid), + Table.TypeRef => ResolveTypeRef(rid), + Table.ModuleRef => ResolveModuleRef(rid), + Table.Method => ResolveMethod(rid), + Table.TypeSpec => ResolveTypeSpec(rid, gpContext), + _ => null, + }; } /// @@ -1208,15 +1076,14 @@ public IMemberRefParent ResolveMemberRefParent(uint codedToken, GenericParamCont /// A HasSemantic coded token /// A or null if is invalid public IHasSemantic ResolveHasSemantic(uint codedToken) { - uint token; - if (!CodedToken.HasSemantic.Decode(codedToken, out token)) + if (!CodedToken.HasSemantic.Decode(codedToken, out uint token)) return null; uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.Event: return ResolveEvent(rid); - case Table.Property: return ResolveProperty(rid); - } - return null; + return MDToken.ToTable(token) switch { + Table.Event => ResolveEvent(rid), + Table.Property => ResolveProperty(rid), + _ => null, + }; } /// @@ -1224,9 +1091,7 @@ public IHasSemantic ResolveHasSemantic(uint codedToken) { /// /// A MethodDefOrRef coded token /// A or null if is invalid - public IMethodDefOrRef ResolveMethodDefOrRef(uint codedToken) { - return ResolveMethodDefOrRef(codedToken, new GenericParamContext()); - } + public IMethodDefOrRef ResolveMethodDefOrRef(uint codedToken) => ResolveMethodDefOrRef(codedToken, new GenericParamContext()); /// /// Resolves a @@ -1235,15 +1100,14 @@ public IMethodDefOrRef ResolveMethodDefOrRef(uint codedToken) { /// Generic parameter context /// A or null if is invalid public IMethodDefOrRef ResolveMethodDefOrRef(uint codedToken, GenericParamContext gpContext) { - uint token; - if (!CodedToken.MethodDefOrRef.Decode(codedToken, out token)) + if (!CodedToken.MethodDefOrRef.Decode(codedToken, out uint token)) return null; uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.Method: return ResolveMethod(rid); - case Table.MemberRef: return ResolveMemberRef(rid, gpContext); - } - return null; + return MDToken.ToTable(token) switch { + Table.Method => ResolveMethod(rid), + Table.MemberRef => ResolveMemberRef(rid, gpContext), + _ => null, + }; } /// @@ -1252,15 +1116,14 @@ public IMethodDefOrRef ResolveMethodDefOrRef(uint codedToken, GenericParamContex /// A MemberForwarded coded token /// A or null if is invalid public IMemberForwarded ResolveMemberForwarded(uint codedToken) { - uint token; - if (!CodedToken.MemberForwarded.Decode(codedToken, out token)) + if (!CodedToken.MemberForwarded.Decode(codedToken, out uint token)) return null; uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.Field: return ResolveField(rid); - case Table.Method: return ResolveMethod(rid); - } - return null; + return MDToken.ToTable(token) switch { + Table.Field => ResolveField(rid), + Table.Method => ResolveMethod(rid), + _ => null, + }; } /// @@ -1269,16 +1132,15 @@ public IMemberForwarded ResolveMemberForwarded(uint codedToken) { /// An Implementation coded token /// A or null if is invalid public IImplementation ResolveImplementation(uint codedToken) { - uint token; - if (!CodedToken.Implementation.Decode(codedToken, out token)) + if (!CodedToken.Implementation.Decode(codedToken, out uint token)) return null; uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.File: return ResolveFile(rid); - case Table.AssemblyRef: return ResolveAssemblyRef(rid); - case Table.ExportedType: return ResolveExportedType(rid); - } - return null; + return MDToken.ToTable(token) switch { + Table.File => ResolveFile(rid), + Table.AssemblyRef => ResolveAssemblyRef(rid), + Table.ExportedType => ResolveExportedType(rid), + _ => null, + }; } /// @@ -1286,9 +1148,7 @@ public IImplementation ResolveImplementation(uint codedToken) { /// /// A CustomAttributeType coded token /// A or null if is invalid - public ICustomAttributeType ResolveCustomAttributeType(uint codedToken) { - return ResolveCustomAttributeType(codedToken, new GenericParamContext()); - } + public ICustomAttributeType ResolveCustomAttributeType(uint codedToken) => ResolveCustomAttributeType(codedToken, new GenericParamContext()); /// /// Resolves a @@ -1297,15 +1157,14 @@ public ICustomAttributeType ResolveCustomAttributeType(uint codedToken) { /// Generic parameter context /// A or null if is invalid public ICustomAttributeType ResolveCustomAttributeType(uint codedToken, GenericParamContext gpContext) { - uint token; - if (!CodedToken.CustomAttributeType.Decode(codedToken, out token)) + if (!CodedToken.CustomAttributeType.Decode(codedToken, out uint token)) return null; uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.Method: return ResolveMethod(rid); - case Table.MemberRef: return ResolveMemberRef(rid, gpContext); - } - return null; + return MDToken.ToTable(token) switch { + Table.Method => ResolveMethod(rid), + Table.MemberRef => ResolveMemberRef(rid, gpContext), + _ => null, + }; } /// @@ -1314,17 +1173,16 @@ public ICustomAttributeType ResolveCustomAttributeType(uint codedToken, GenericP /// A ResolutionScope coded token /// A or null if is invalid public IResolutionScope ResolveResolutionScope(uint codedToken) { - uint token; - if (!CodedToken.ResolutionScope.Decode(codedToken, out token)) + if (!CodedToken.ResolutionScope.Decode(codedToken, out uint token)) return null; uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.Module: return ResolveModule(rid); - case Table.ModuleRef: return ResolveModuleRef(rid); - case Table.AssemblyRef: return ResolveAssemblyRef(rid); - case Table.TypeRef: return ResolveTypeRef(rid); - } - return null; + return MDToken.ToTable(token) switch { + Table.Module => ResolveModule(rid), + Table.ModuleRef => ResolveModuleRef(rid), + Table.AssemblyRef => ResolveAssemblyRef(rid), + Table.TypeRef => ResolveTypeRef(rid), + _ => null, + }; } /// @@ -1333,15 +1191,14 @@ public IResolutionScope ResolveResolutionScope(uint codedToken) { /// A TypeOrMethodDef> coded token /// A or null if is invalid public ITypeOrMethodDef ResolveTypeOrMethodDef(uint codedToken) { - uint token; - if (!CodedToken.TypeOrMethodDef.Decode(codedToken, out token)) + if (!CodedToken.TypeOrMethodDef.Decode(codedToken, out uint token)) return null; uint rid = MDToken.ToRID(token); - switch (MDToken.ToTable(token)) { - case Table.TypeDef: return ResolveTypeDef(rid); - case Table.Method: return ResolveMethod(rid); - } - return null; + return MDToken.ToTable(token) switch { + Table.TypeDef => ResolveTypeDef(rid), + Table.Method => ResolveMethod(rid), + _ => null, + }; } /// @@ -1350,9 +1207,7 @@ public ITypeOrMethodDef ResolveTypeOrMethodDef(uint codedToken) { /// #Blob stream offset of signature /// A new instance or null if /// is invalid. - public CallingConventionSig ReadSignature(uint sig) { - return SignatureReader.ReadSig(this, sig, new GenericParamContext()); - } + public CallingConventionSig ReadSignature(uint sig) => SignatureReader.ReadSig(this, sig, new GenericParamContext()); /// /// Reads a signature from the #Blob stream @@ -1361,9 +1216,7 @@ public CallingConventionSig ReadSignature(uint sig) { /// Generic parameter context /// A new instance or null if /// is invalid. - public CallingConventionSig ReadSignature(uint sig, GenericParamContext gpContext) { - return SignatureReader.ReadSig(this, sig, gpContext); - } + public CallingConventionSig ReadSignature(uint sig, GenericParamContext gpContext) => SignatureReader.ReadSig(this, sig, gpContext); /// /// Reads a type signature from the #Blob stream @@ -1371,9 +1224,7 @@ public CallingConventionSig ReadSignature(uint sig, GenericParamContext gpContex /// #Blob stream offset of signature /// A new instance or null if /// is invalid. - public TypeSig ReadTypeSignature(uint sig) { - return SignatureReader.ReadTypeSig(this, sig, new GenericParamContext()); - } + public TypeSig ReadTypeSignature(uint sig) => SignatureReader.ReadTypeSig(this, sig, new GenericParamContext()); /// /// Reads a type signature from the #Blob stream @@ -1382,9 +1233,7 @@ public TypeSig ReadTypeSignature(uint sig) { /// Generic parameter context /// A new instance or null if /// is invalid. - public TypeSig ReadTypeSignature(uint sig, GenericParamContext gpContext) { - return SignatureReader.ReadTypeSig(this, sig, gpContext); - } + public TypeSig ReadTypeSignature(uint sig, GenericParamContext gpContext) => SignatureReader.ReadTypeSig(this, sig, gpContext); /// /// Reads a type signature from the #Blob stream @@ -1394,9 +1243,7 @@ public TypeSig ReadTypeSignature(uint sig, GenericParamContext gpContext) { /// here, else this will be null /// A new instance or null if /// is invalid. - public TypeSig ReadTypeSignature(uint sig, out byte[] extraData) { - return SignatureReader.ReadTypeSig(this, sig, new GenericParamContext(), out extraData); - } + public TypeSig ReadTypeSignature(uint sig, out byte[] extraData) => SignatureReader.ReadTypeSig(this, sig, new GenericParamContext(), out extraData); /// /// Reads a type signature from the #Blob stream @@ -1407,9 +1254,7 @@ public TypeSig ReadTypeSignature(uint sig, out byte[] extraData) { /// Generic parameter context /// A new instance or null if /// is invalid. - public TypeSig ReadTypeSignature(uint sig, GenericParamContext gpContext, out byte[] extraData) { - return SignatureReader.ReadTypeSig(this, sig, gpContext, out extraData); - } + public TypeSig ReadTypeSignature(uint sig, GenericParamContext gpContext, out byte[] extraData) => SignatureReader.ReadTypeSig(this, sig, gpContext, out extraData); /// /// Reads a from the blob @@ -1420,8 +1265,7 @@ public TypeSig ReadTypeSignature(uint sig, GenericParamContext gpContext, out by /// A new instance or null if there's no field /// marshal for this owner. internal MarshalType ReadMarshalType(Table table, uint rid, GenericParamContext gpContext) { - var row = TablesStream.ReadFieldMarshalRow(MetaData.GetFieldMarshalRid(table, rid)); - if (row == null) + if (!TablesStream.TryReadFieldMarshalRow(Metadata.GetFieldMarshalRid(table, rid), out var row)) return null; return MarshalBlobReader.Read(this, row.NativeType, gpContext); } @@ -1433,9 +1277,7 @@ internal MarshalType ReadMarshalType(Table table, uint rid, GenericParamContext /// RVA /// A new instance. It's empty if RVA is invalid (eg. 0 or /// it doesn't point to a CIL method body) - public CilBody ReadCilBody(IList parameters, RVA rva) { - return ReadCilBody(parameters, rva, new GenericParamContext()); - } + public CilBody ReadCilBody(IList parameters, RVA rva) => ReadCilBody(parameters, rva, new GenericParamContext()); /// /// Reads a CIL method body @@ -1454,10 +1296,13 @@ public CilBody ReadCilBody(IList parameters, RVA rva, GenericParamCon // If we create a partial stream starting from rva, then position will be 0 and always // 4-byte aligned. All fat method bodies should be 4-byte aligned, but the CLR doesn't // seem to verify it. We must parse the method exactly the way the CLR parses it. - using (var reader = metaData.PEImage.CreateFullStream()) { - reader.Position = (long)metaData.PEImage.ToFileOffset(rva); - return MethodBodyReader.CreateCilBody(this, reader, parameters, gpContext); - } + var offset = metadata.PEImage.ToFileOffset(rva); + if (offset == 0) + return new CilBody(); + + var reader = metadata.PEImage.CreateReader(); + reader.Position = (uint)offset; + return MethodBodyReader.CreateCilBody(this, reader, parameters, gpContext, Context); } /// @@ -1465,63 +1310,49 @@ public CilBody ReadCilBody(IList parameters, RVA rva, GenericParamCon /// /// The field /// The owner type or null if none - internal TypeDef GetOwnerType(FieldDefMD field) { - return ResolveTypeDef(MetaData.GetOwnerTypeOfField(field.OrigRid)); - } + internal TypeDef GetOwnerType(FieldDefMD field) => ResolveTypeDef(Metadata.GetOwnerTypeOfField(field.OrigRid)); /// /// Returns the owner type of a method /// /// The method /// The owner type or null if none - internal TypeDef GetOwnerType(MethodDefMD method) { - return ResolveTypeDef(MetaData.GetOwnerTypeOfMethod(method.OrigRid)); - } + internal TypeDef GetOwnerType(MethodDefMD method) => ResolveTypeDef(Metadata.GetOwnerTypeOfMethod(method.OrigRid)); /// /// Returns the owner type of an event /// /// The event /// The owner type or null if none - internal TypeDef GetOwnerType(EventDefMD evt) { - return ResolveTypeDef(MetaData.GetOwnerTypeOfEvent(evt.OrigRid)); - } + internal TypeDef GetOwnerType(EventDefMD evt) => ResolveTypeDef(Metadata.GetOwnerTypeOfEvent(evt.OrigRid)); /// /// Returns the owner type of a property /// /// The property /// The owner type or null if none - internal TypeDef GetOwnerType(PropertyDefMD property) { - return ResolveTypeDef(MetaData.GetOwnerTypeOfProperty(property.OrigRid)); - } + internal TypeDef GetOwnerType(PropertyDefMD property) => ResolveTypeDef(Metadata.GetOwnerTypeOfProperty(property.OrigRid)); /// /// Returns the owner type/method of a generic param /// /// The generic param /// The owner type/method or null if none - internal ITypeOrMethodDef GetOwner(GenericParamMD gp) { - return ResolveTypeOrMethodDef(MetaData.GetOwnerOfGenericParam(gp.OrigRid)); - } + internal ITypeOrMethodDef GetOwner(GenericParamMD gp) => ResolveTypeOrMethodDef(Metadata.GetOwnerOfGenericParam(gp.OrigRid)); /// /// Returns the owner generic param of a generic param constraint /// /// The generic param constraint /// The owner generic param or null if none - internal GenericParam GetOwner(GenericParamConstraintMD gpc) { - return ResolveGenericParam(MetaData.GetOwnerOfGenericParamConstraint(gpc.OrigRid)); - } + internal GenericParam GetOwner(GenericParamConstraintMD gpc) => ResolveGenericParam(Metadata.GetOwnerOfGenericParamConstraint(gpc.OrigRid)); /// /// Returns the owner method of a param /// /// The param /// The owner method or null if none - internal MethodDef GetOwner(ParamDefMD pd) { - return ResolveMethod(MetaData.GetOwnerOfParam(pd.OrigRid)); - } + internal MethodDef GetOwner(ParamDefMD pd) => ResolveMethod(Metadata.GetOwnerOfParam(pd.OrigRid)); /// /// Reads a module @@ -1532,26 +1363,26 @@ internal MethodDef GetOwner(ParamDefMD pd) { /// is invalid or if it's not a .NET module. internal ModuleDefMD ReadModule(uint fileRid, AssemblyDef owner) { var fileDef = ResolveFile(fileRid); - if (fileDef == null) + if (fileDef is null) return null; - if (!fileDef.ContainsMetaData) + if (!fileDef.ContainsMetadata) return null; var fileName = GetValidFilename(GetBaseDirectoryOfImage(), UTF8String.ToSystemString(fileDef.Name)); - if (fileName == null) + if (fileName is null) return null; ModuleDefMD module; try { - module = ModuleDefMD.Load(fileName); + module = Load(fileName); } catch { module = null; } - if (module != null) { + if (module is not null) { // share context module.context = context; var asm = module.Assembly; - if (asm != null && asm != owner) + if (asm is not null && asm != owner) asm.Modules.Remove(module); } return module; @@ -1563,29 +1394,29 @@ internal ModuleDefMD ReadModule(uint fileRid, AssemblyDef owner) { /// /// A new instance internal RidList GetModuleRidList() { - if (moduleRidList == null) + if (moduleRidList is null) InitializeModuleList(); - return moduleRidList; + return moduleRidList.Value; } void InitializeModuleList() { - if (moduleRidList != null) + if (moduleRidList is not null) return; uint rows = TablesStream.FileTable.Rows; - var newModuleRidList = new RandomRidList((int)rows); + var newModuleRidList = new List((int)rows); var baseDir = GetBaseDirectoryOfImage(); for (uint fileRid = 1; fileRid <= rows; fileRid++) { var fileDef = ResolveFile(fileRid); - if (fileDef == null) + if (fileDef is null) continue; // Should never happen - if (!fileDef.ContainsMetaData) + if (!fileDef.ContainsMetadata) continue; var pathName = GetValidFilename(baseDir, UTF8String.ToSystemString(fileDef.Name)); - if (pathName != null) + if (pathName is not null) newModuleRidList.Add(fileRid); } - Interlocked.CompareExchange(ref moduleRidList, newModuleRidList, null); + Interlocked.CompareExchange(ref moduleRidList, new StrongBox(RidList.Create(newModuleRidList)), null); } /// @@ -1595,7 +1426,7 @@ void InitializeModuleList() { /// File name /// Full path to the file or null if one of the inputs is invalid static string GetValidFilename(string baseDir, string name) { - if (baseDir == null) + if (baseDir is null) return null; string pathName; @@ -1639,81 +1470,81 @@ string GetBaseDirectoryOfImage() { /// ManifestResource rid /// A new instance Resource CreateResource(uint rid) { - var row = TablesStream.ReadManifestResourceRow(rid); - if (row == null) - return new EmbeddedResource(UTF8String.Empty, MemoryImageStream.CreateEmpty(), 0) { Rid = rid }; + if (!TablesStream.TryReadManifestResourceRow(rid, out var row)) + return new EmbeddedResource(UTF8String.Empty, Array2.Empty(), 0) { Rid = rid }; - MDToken token; - if (!CodedToken.Implementation.Decode(row.Implementation, out token)) - return new EmbeddedResource(UTF8String.Empty, MemoryImageStream.CreateEmpty(), 0) { Rid = rid }; + if (!CodedToken.Implementation.Decode(row.Implementation, out MDToken token)) + return new EmbeddedResource(UTF8String.Empty, Array2.Empty(), 0) { Rid = rid }; var mr = ResolveManifestResource(rid); - if (mr == null) - return new EmbeddedResource(UTF8String.Empty, MemoryImageStream.CreateEmpty(), 0) { Rid = rid }; + if (mr is null) + return new EmbeddedResource(UTF8String.Empty, Array2.Empty(), 0) { Rid = rid }; - if (token.Rid == 0) - return new EmbeddedResource(mr.Name, CreateResourceStream(mr.Offset), mr.Flags) { Rid = rid, Offset = mr.Offset }; + if (token.Rid == 0) { + if (TryCreateResourceStream(mr.Offset, out var dataReaderFactory, out uint resourceOffset, out uint resourceLength)) + return new EmbeddedResourceMD(this, mr, dataReaderFactory, resourceOffset, resourceLength); + return new EmbeddedResourceMD(this, mr, Array2.Empty()); + } - var file = mr.Implementation as FileDef; - if (file != null) - return new LinkedResource(mr.Name, file, mr.Flags) { Rid = rid, Offset = mr.Offset }; + if (mr.Implementation is FileDef file) + return new LinkedResourceMD(this, mr, file); - var asmRef = mr.Implementation as AssemblyRef; - if (asmRef != null) - return new AssemblyLinkedResource(mr.Name, asmRef, mr.Flags) { Rid = rid, Offset = mr.Offset }; + if (mr.Implementation is AssemblyRef asmRef) + return new AssemblyLinkedResourceMD(this, mr, asmRef); - return new EmbeddedResource(mr.Name, MemoryImageStream.CreateEmpty(), mr.Flags) { Rid = rid, Offset = mr.Offset }; + return new EmbeddedResourceMD(this, mr, Array2.Empty()); } - /// - /// Creates a resource stream that can access part of the resource section of this module - /// - /// Offset of resource relative to the .NET resources section - /// A stream the size of the resource - [HandleProcessCorruptedStateExceptions, SecurityCritical] // Req'd on .NET 4.0 - IImageStream CreateResourceStream(uint offset) { - IImageStream fs = null, imageStream = null; + // Required attributes on .NET Framework 4.0 + // HandleProcessCorruptedStateExceptions is obsolete on .NET Core and newer +#if !NETCOREAPP + [HandleProcessCorruptedStateExceptions] +#endif + [SecurityCritical] + bool TryCreateResourceStream(uint offset, out DataReaderFactory dataReaderFactory, out uint resourceOffset, out uint resourceLength) { + dataReaderFactory = null; + resourceOffset = 0; + resourceLength = 0; + try { - var peImage = metaData.PEImage; - var cor20Header = metaData.ImageCor20Header; + var peImage = metadata.PEImage; + var cor20Header = metadata.ImageCor20Header; var resources = cor20Header.Resources; if (resources.VirtualAddress == 0 || resources.Size == 0) - return MemoryImageStream.CreateEmpty(); - fs = peImage.CreateFullStream(); - - var resourceOffset = (long)peImage.ToFileOffset(resources.VirtualAddress); - if (resourceOffset <= 0 || resourceOffset + offset < resourceOffset) - return MemoryImageStream.CreateEmpty(); - if (offset + 3 <= offset || offset + 3 >= resources.Size) - return MemoryImageStream.CreateEmpty(); - if (resourceOffset + offset + 3 < resourceOffset || resourceOffset + offset + 3 >= fs.Length) - return MemoryImageStream.CreateEmpty(); - fs.Position = resourceOffset + offset; - uint length = fs.ReadUInt32(); // Could throw - if (length == 0 || fs.Position + length - 1 < fs.Position || fs.Position + length - 1 >= fs.Length) - return MemoryImageStream.CreateEmpty(); - if (fs.Position - resourceOffset + length - 1 >= resources.Size) - return MemoryImageStream.CreateEmpty(); - - imageStream = peImage.CreateStream((FileOffset)fs.Position, length); + return false; + var fullReader = peImage.CreateReader(); + + var resourcesBaseOffs = (uint)peImage.ToFileOffset(resources.VirtualAddress); + if (resourcesBaseOffs == 0 || (ulong)resourcesBaseOffs + offset > uint.MaxValue) + return false; + if ((ulong)offset + 4 > resources.Size) + return false; + if ((ulong)resourcesBaseOffs + offset + 4 > fullReader.Length) + return false; + fullReader.Position = resourcesBaseOffs + offset; + resourceLength = fullReader.ReadUInt32(); // Could throw + resourceOffset = fullReader.Position; + if (resourceLength == 0 || (ulong)fullReader.Position + resourceLength > fullReader.Length) + return false; + if ((ulong)fullReader.Position - resourcesBaseOffs + resourceLength - 1 >= resources.Size) + return false; + if (peImage.MayHaveInvalidAddresses) { - for (; imageStream.Position < imageStream.Length; imageStream.Position += 0x1000) - imageStream.ReadByte(); // Could throw - imageStream.Position = imageStream.Length - 1; // length is never 0 if we're here - imageStream.ReadByte(); // Could throw - imageStream.Position = 0; + var rsrcReader = peImage.CreateReader((FileOffset)fullReader.Position, resourceLength); + for (; rsrcReader.Position < rsrcReader.Length; rsrcReader.Position += Math.Min(rsrcReader.BytesLeft, 0x1000)) + rsrcReader.ReadByte(); // Could throw + rsrcReader.Position = rsrcReader.Length - 1; // length is never 0 if we're here + rsrcReader.ReadByte(); // Could throw } + + dataReaderFactory = peImage.DataReaderFactory; + return true; } - catch (AccessViolationException) { - if (imageStream != null) - imageStream.Dispose(); - return MemoryImageStream.CreateEmpty(); + catch (IOException) { } - finally { - if (fs != null) - fs.Dispose(); + catch (AccessViolationException) { } - return imageStream; + return false; } /// @@ -1722,9 +1553,7 @@ IImageStream CreateResourceStream(uint offset) { /// Custom attribute rid /// A new instance or null if /// is invalid - public CustomAttribute ReadCustomAttribute(uint caRid) { - return ReadCustomAttribute(caRid, new GenericParamContext()); - } + public CustomAttribute ReadCustomAttribute(uint caRid) => ReadCustomAttribute(caRid, new GenericParamContext()); /// /// Reads a @@ -1734,8 +1563,7 @@ public CustomAttribute ReadCustomAttribute(uint caRid) { /// A new instance or null if /// is invalid public CustomAttribute ReadCustomAttribute(uint caRid, GenericParamContext gpContext) { - var caRow = TablesStream.ReadCustomAttributeRow(caRid); - if (caRow == null) + if (!TablesStream.TryReadCustomAttributeRow(caRid, out var caRow)) return null; return CustomAttributeReader.Read(this, ResolveCustomAttributeType(caRow.Type, gpContext), caRow.Value, gpContext); } @@ -1750,19 +1578,18 @@ public CustomAttribute ReadCustomAttribute(uint caRid, GenericParamContext gpCon public byte[] ReadDataAt(RVA rva, int size) { if (size < 0) return null; - var peImage = MetaData.PEImage; - using (var reader = peImage.CreateStream(rva, size)) { - if (reader.Length < size) - return null; - return reader.ReadBytes(size); - } + var peImage = Metadata.PEImage; + var reader = peImage.CreateReader(rva, (uint)size); + if (reader.Length < size) + return null; + return reader.ReadBytes(size); } /// /// Gets the native entry point or 0 if none /// public RVA GetNativeEntryPoint() { - var cor20Header = MetaData.ImageCor20Header; + var cor20Header = Metadata.ImageCor20Header; if ((cor20Header.Flags & ComImageFlags.NativeEntryPoint) == 0) return 0; return (RVA)cor20Header.EntryPointToken_or_RVA; @@ -1772,7 +1599,7 @@ public RVA GetNativeEntryPoint() { /// Gets the managed entry point (a Method or a File) or null if none /// public IManagedEntryPoint GetManagedEntryPoint() { - var cor20Header = MetaData.ImageCor20Header; + var cor20Header = Metadata.ImageCor20Header; if ((cor20Header.Flags & ComImageFlags.NativeEntryPoint) != 0) return null; return ResolveToken(cor20Header.EntryPointToken_or_RVA) as IManagedEntryPoint; @@ -1783,63 +1610,49 @@ public IManagedEntryPoint GetManagedEntryPoint() { /// /// Row ID /// A new instance - internal FieldDefMD ReadField(uint rid) { - return new FieldDefMD(this, rid); - } + internal FieldDefMD ReadField(uint rid) => new FieldDefMD(this, rid); /// /// Reads a new instance. This one is not cached. /// /// Row ID /// A new instance - internal MethodDefMD ReadMethod(uint rid) { - return new MethodDefMD(this, rid); - } + internal MethodDefMD ReadMethod(uint rid) => new MethodDefMD(this, rid); /// /// Reads a new instance. This one is not cached. /// /// Row ID /// A new instance - internal EventDefMD ReadEvent(uint rid) { - return new EventDefMD(this, rid); - } + internal EventDefMD ReadEvent(uint rid) => new EventDefMD(this, rid); /// /// Reads a new instance. This one is not cached. /// /// Row ID /// A new instance - internal PropertyDefMD ReadProperty(uint rid) { - return new PropertyDefMD(this, rid); - } + internal PropertyDefMD ReadProperty(uint rid) => new PropertyDefMD(this, rid); /// /// Reads a new instance. This one is not cached. /// /// Row ID /// A new instance - internal ParamDefMD ReadParam(uint rid) { - return new ParamDefMD(this, rid); - } + internal ParamDefMD ReadParam(uint rid) => new ParamDefMD(this, rid); /// /// Reads a new instance. This one is not cached. /// /// Row ID /// A new instance - internal GenericParamMD ReadGenericParam(uint rid) { - return new GenericParamMD(this, rid); - } + internal GenericParamMD ReadGenericParam(uint rid) => new GenericParamMD(this, rid); /// /// Reads a new instance. This one is not cached. /// /// Row ID /// A new instance - internal GenericParamConstraintMD ReadGenericParamConstraint(uint rid) { - return new GenericParamConstraintMD(this, rid, new GenericParamContext()); - } + internal GenericParamConstraintMD ReadGenericParamConstraint(uint rid) => new GenericParamConstraintMD(this, rid, new GenericParamContext()); /// /// Reads a new instance. This one is not cached. @@ -1847,9 +1660,7 @@ internal GenericParamConstraintMD ReadGenericParamConstraint(uint rid) { /// Row ID /// Generic parameter context /// A new instance - internal GenericParamConstraintMD ReadGenericParamConstraint(uint rid, GenericParamContext gpContext) { - return new GenericParamConstraintMD(this, rid, gpContext); - } + internal GenericParamConstraintMD ReadGenericParamConstraint(uint rid, GenericParamContext gpContext) => new GenericParamConstraintMD(this, rid, gpContext); /// /// Reads a method body @@ -1860,12 +1671,10 @@ internal GenericParamConstraintMD ReadGenericParamConstraint(uint rid, GenericPa /// Generic parameter context /// A or null if none internal MethodBody ReadMethodBody(MethodDefMD method, RVA rva, MethodImplAttributes implAttrs, GenericParamContext gpContext) { - MethodBody mb; var mDec = methodDecrypter; - if (mDec != null && mDec.GetMethodBody(method.OrigRid, rva, method.Parameters, gpContext, out mb)) { - var cilBody = mb as CilBody; - if (cilBody != null) - return InitializeBodyFromPdb(cilBody, method.OrigRid); + if (mDec is not null && mDec.GetMethodBody(method.OrigRid, rva, method.Parameters, gpContext, out var mb)) { + if (mb is CilBody cilBody) + return InitializeBodyFromPdb(method, cilBody); return mb; } @@ -1873,7 +1682,7 @@ internal MethodBody ReadMethodBody(MethodDefMD method, RVA rva, MethodImplAttrib return null; var codeType = implAttrs & MethodImplAttributes.CodeTypeMask; if (codeType == MethodImplAttributes.IL) - return InitializeBodyFromPdb(ReadCilBody(method.Parameters, rva, gpContext), method.OrigRid); + return InitializeBodyFromPdb(method, ReadCilBody(method.Parameters, rva, gpContext)); if (codeType == MethodImplAttributes.Native) return new NativeMethodBody(rva); return null; @@ -1882,15 +1691,25 @@ internal MethodBody ReadMethodBody(MethodDefMD method, RVA rva, MethodImplAttrib /// /// Updates with the PDB info (if any) /// + /// Owner method /// Method body - /// Method rid /// Returns originak value - CilBody InitializeBodyFromPdb(CilBody body, uint rid) { - if (pdbState != null) - pdbState.InitializeDontCall(body, rid); + CilBody InitializeBodyFromPdb(MethodDefMD method, CilBody body) { + var ps = pdbState; + if (ps is not null) + ps.InitializeMethodBody(this, method, body); return body; } + internal void InitializeCustomDebugInfos(MethodDefMD method, CilBody body, IList customDebugInfos) { + if (body is null) + return; + + var ps = pdbState; + if (ps is not null) + ps.InitializeCustomDebugInfos(method, body, customDebugInfos); + } + /// /// Reads a string from the #US heap /// @@ -1898,21 +1717,29 @@ CilBody InitializeBodyFromPdb(CilBody body, uint rid) { /// A non-null string public string ReadUserString(uint token) { var sDec = stringDecrypter; - if (sDec != null) { + if (sDec is not null) { var s = sDec.ReadUserString(token); - if (s != null) + if (s is not null) return s; } return USStream.ReadNoNull(token & 0x00FFFFFF); } + internal MethodExportInfo GetExportInfo(uint methodRid) { + if (methodExportInfoProvider is null) + InitializeMethodExportInfoProvider(); + return methodExportInfoProvider.GetMethodExportInfo(0x06000000 + methodRid); + } + + void InitializeMethodExportInfoProvider() => + Interlocked.CompareExchange(ref methodExportInfoProvider, new MethodExportInfoProvider(this), null); + MethodExportInfoProvider methodExportInfoProvider; + /// /// Writes the mixed-mode module to a file on disk. If the file exists, it will be overwritten. /// /// Filename - public void NativeWrite(string filename) { - NativeWrite(filename, null); - } + public void NativeWrite(string filename) => NativeWrite(filename, null); /// /// Writes the mixed-mode module to a file on disk. If the file exists, it will be overwritten. @@ -1920,7 +1747,7 @@ public void NativeWrite(string filename) { /// Filename /// Writer options public void NativeWrite(string filename, DNW.NativeModuleWriterOptions options) { - var writer = new DNW.NativeModuleWriter(this, options ?? new DNW.NativeModuleWriterOptions(this)); + var writer = new DNW.NativeModuleWriter(this, options ?? new DNW.NativeModuleWriterOptions(this, optimizeImageSize: true)); writer.Write(filename); } @@ -1928,9 +1755,7 @@ public void NativeWrite(string filename, DNW.NativeModuleWriterOptions options) /// Writes the mixed-mode module to a stream. /// /// Destination stream - public void NativeWrite(Stream dest) { - NativeWrite(dest, null); - } + public void NativeWrite(Stream dest) => NativeWrite(dest, null); /// /// Writes the mixed-mode module to a stream. @@ -1938,7 +1763,7 @@ public void NativeWrite(Stream dest) { /// Destination stream /// Writer options public void NativeWrite(Stream dest, DNW.NativeModuleWriterOptions options) { - var writer = new DNW.NativeModuleWriter(this, options ?? new DNW.NativeModuleWriterOptions(this)); + var writer = new DNW.NativeModuleWriter(this, options ?? new DNW.NativeModuleWriterOptions(this, optimizeImageSize: true)); writer.Write(dest); } @@ -1965,96 +1790,78 @@ public byte[] ReadBlob(uint token) { uint rid = MDToken.ToRID(token); switch (MDToken.ToTable(token)) { case Table.Field: - var fieldRow = TablesStream.ReadFieldRow(rid); - if (fieldRow == null) + if (!TablesStream.TryReadFieldRow(rid, out var fieldRow)) break; return BlobStream.Read(fieldRow.Signature); case Table.Method: - var methodRow = TablesStream.ReadMethodRow(rid); - if (methodRow == null) + if (!TablesStream.TryReadMethodRow(rid, out var methodRow)) break; return BlobStream.Read(methodRow.Signature); case Table.MemberRef: - var mrRow = TablesStream.ReadMemberRefRow(rid); - if (mrRow == null) + if (!TablesStream.TryReadMemberRefRow(rid, out var mrRow)) break; return BlobStream.Read(mrRow.Signature); case Table.Constant: - var constRow = TablesStream.ReadConstantRow(rid); - if (constRow == null) + if (!TablesStream.TryReadConstantRow(rid, out var constRow)) break; return BlobStream.Read(constRow.Value); case Table.CustomAttribute: - var caRow = TablesStream.ReadCustomAttributeRow(rid); - if (caRow == null) + if (!TablesStream.TryReadCustomAttributeRow(rid, out var caRow)) break; return BlobStream.Read(caRow.Value); case Table.FieldMarshal: - var fmRow = TablesStream.ReadFieldMarshalRow(rid); - if (fmRow == null) + if (!TablesStream.TryReadFieldMarshalRow(rid, out var fmRow)) break; return BlobStream.Read(fmRow.NativeType); case Table.DeclSecurity: - var dsRow = TablesStream.ReadDeclSecurityRow(rid); - if (dsRow == null) + if (!TablesStream.TryReadDeclSecurityRow(rid, out var dsRow)) break; return BlobStream.Read(dsRow.PermissionSet); case Table.StandAloneSig: - var sasRow = TablesStream.ReadStandAloneSigRow(rid); - if (sasRow == null) + if (!TablesStream.TryReadStandAloneSigRow(rid, out var sasRow)) break; return BlobStream.Read(sasRow.Signature); case Table.Property: - var propRow = TablesStream.ReadPropertyRow(rid); - if (propRow == null) + if (!TablesStream.TryReadPropertyRow(rid, out var propRow)) break; return BlobStream.Read(propRow.Type); case Table.TypeSpec: - var tsRow = TablesStream.ReadTypeSpecRow(rid); - if (tsRow == null) + if (!TablesStream.TryReadTypeSpecRow(rid, out var tsRow)) break; return BlobStream.Read(tsRow.Signature); case Table.Assembly: - var asmRow = TablesStream.ReadAssemblyRow(rid); - if (asmRow == null) + if (!TablesStream.TryReadAssemblyRow(rid, out var asmRow)) break; return BlobStream.Read(asmRow.PublicKey); case Table.AssemblyRef: // HashValue is also in the #Blob but the user has to read it some other way - var asmRefRow = TablesStream.ReadAssemblyRefRow(rid); - if (asmRefRow == null) + if (!TablesStream.TryReadAssemblyRefRow(rid, out var asmRefRow)) break; return BlobStream.Read(asmRefRow.PublicKeyOrToken); case Table.File: - var fileRow = TablesStream.ReadFileRow(rid); - if (fileRow == null) + if (!TablesStream.TryReadFileRow(rid, out var fileRow)) break; return BlobStream.Read(fileRow.HashValue); case Table.MethodSpec: - var msRow = TablesStream.ReadMethodSpecRow(rid); - if (msRow == null) + if (!TablesStream.TryReadMethodSpecRow(rid, out var msRow)) break; return BlobStream.Read(msRow.Instantiation); } return null; } - - TypeSig ISignatureReaderHelper.ConvertRTInternalAddress(IntPtr address) { - return null; - } } } diff --git a/src/DotNet/ModuleKind.cs b/src/DotNet/ModuleKind.cs index 332868238..9cd42c93f 100644 --- a/src/DotNet/ModuleKind.cs +++ b/src/DotNet/ModuleKind.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// Module kind /// diff --git a/src/DotNet/ModuleLoader.cs b/src/DotNet/ModuleLoader.cs index f161fdc3e..16ba05561 100644 --- a/src/DotNet/ModuleLoader.cs +++ b/src/DotNet/ModuleLoader.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; @@ -6,13 +6,12 @@ using dnlib.DotNet.Emit; using dnlib.DotNet.MD; using dnlib.DotNet.Pdb; -using dnlib.IO; using dnlib.PE; using dnlib.Threading; using dnlib.W32Resources; namespace dnlib.DotNet { - struct ModuleLoader { + readonly struct ModuleLoader { readonly ModuleDef module; readonly ICancellationToken cancellationToken; readonly Dictionary seen; @@ -22,13 +21,12 @@ struct ModuleLoader { const int CAPACITY = 0x4000; this.module = module; this.cancellationToken = cancellationToken; - this.seen = new Dictionary(CAPACITY); - this.stack = new Stack(CAPACITY); + seen = new Dictionary(CAPACITY); + stack = new Stack(CAPACITY); } - public static void LoadAll(ModuleDef module, ICancellationToken cancellationToken) { + public static void LoadAll(ModuleDef module, ICancellationToken cancellationToken) => new ModuleLoader(module, cancellationToken).Load(); - } void Add(UTF8String a) { } void Add(Guid? a) { } @@ -67,7 +65,7 @@ void Load() { void Process() { while (stack.Count != 0) { - if (cancellationToken != null) + if (cancellationToken is not null) cancellationToken.ThrowIfCancellationRequested(); var o = stack.Pop(); LoadObj(o); @@ -76,12 +74,12 @@ void Process() { void LoadAllTables() { var resolver = module as ITokenResolver; - if (resolver == null) + if (resolver is null) return; for (Table tbl = 0; tbl <= Table.GenericParamConstraint; tbl++) { for (uint rid = 1; ; rid++) { var o = resolver.ResolveToken(new MDToken(tbl, rid).Raw, new GenericParamContext()); - if (o == null) + if (o is null) break; Add(o); Process(); @@ -90,56 +88,47 @@ void LoadAllTables() { } void LoadObj(object o) { - var ts = o as TypeSig; - if (ts != null) { + if (o is TypeSig ts) { Load(ts); return; } - var mdt = o as IMDTokenProvider; - if (mdt != null) { + if (o is IMDTokenProvider mdt) { Load(mdt); return; } - var ca = o as CustomAttribute; - if (ca != null) { + if (o is CustomAttribute ca) { Load(ca); return; } - var sa = o as SecurityAttribute; - if (sa != null) { + if (o is SecurityAttribute sa) { Load(sa); return; } - var na = o as CANamedArgument; - if (na != null) { + if (o is CANamedArgument na) { Load(na); return; } - var p = o as Parameter; - if (p != null) { + if (o is Parameter p) { Load(p); return; } - var scope = o as PdbScope; - if (scope != null) { - Load(scope); + if (o is PdbMethod pdbMethod) { + Load(pdbMethod); return; } - var rd = o as ResourceDirectory; - if (rd != null) { + if (o is ResourceDirectory rd) { Load(rd); return; } - var rdata = o as ResourceData; - if (rdata != null) { + if (o is ResourceData rdata) { Load(rdata); return; } @@ -148,7 +137,7 @@ void LoadObj(object o) { } void Load(TypeSig ts) { - if (ts == null) + if (ts is null) return; Add(ts.Next); @@ -217,7 +206,7 @@ void Load(TypeSig ts) { } void Load(IMDTokenProvider mdt) { - if (mdt == null) + if (mdt is null) return; switch (mdt.MDToken.Table) { case Table.Module: Load((ModuleDef)mdt); break; @@ -247,13 +236,13 @@ void Load(IMDTokenProvider mdt) { case Table.ManifestResource: var rsrc = mdt as Resource; - if (rsrc != null) { + if (rsrc is not null) { Load(rsrc); break; } var mr = mdt as ManifestResource; - if (mr != null) { + if (mr is not null) { Load(mr); break; } @@ -281,6 +270,14 @@ void Load(IMDTokenProvider mdt) { case Table.AssemblyRefProcessor: case Table.AssemblyRefOS: case Table.NestedClass: + case Table.Document: + case Table.MethodDebugInformation: + case Table.LocalScope: + case Table.LocalVariable: + case Table.LocalConstant: + case Table.ImportScope: + case Table.StateMachineMethod: + case Table.CustomDebugInformation: break; default: @@ -290,7 +287,7 @@ void Load(IMDTokenProvider mdt) { } void Load(ModuleDef obj) { - if (obj == null || obj != this.module) + if (obj is null || obj != module) return; Add(obj.Generation); Add(obj.Name); @@ -315,7 +312,7 @@ void Load(ModuleDef obj) { } void Load(TypeRef obj) { - if (obj == null) + if (obj is null) return; Add(obj.ResolutionScope); Add(obj.Name); @@ -324,7 +321,7 @@ void Load(TypeRef obj) { } void Load(TypeDef obj) { - if (obj == null) + if (obj is null) return; Add(obj.Module2); Add(obj.Attributes); @@ -346,7 +343,7 @@ void Load(TypeDef obj) { } void Load(FieldDef obj) { - if (obj == null) + if (obj is null) return; Add(obj.CustomAttributes); Add(obj.Attributes); @@ -362,7 +359,7 @@ void Load(FieldDef obj) { } void Load(MethodDef obj) { - if (obj == null) + if (obj is null) return; Add(obj.RVA); Add(obj.ImplAttributes); @@ -382,7 +379,7 @@ void Load(MethodDef obj) { } void Load(ParamDef obj) { - if (obj == null) + if (obj is null) return; Add(obj.DeclaringMethod); Add(obj.Attributes); @@ -394,14 +391,14 @@ void Load(ParamDef obj) { } void Load(InterfaceImpl obj) { - if (obj == null) + if (obj is null) return; Add(obj.Interface); Add(obj.CustomAttributes); } void Load(MemberRef obj) { - if (obj == null) + if (obj is null) return; Add(obj.Class); Add(obj.Name); @@ -410,14 +407,14 @@ void Load(MemberRef obj) { } void Load(Constant obj) { - if (obj == null) + if (obj is null) return; Add(obj.Type); var o = obj.Value; } void Load(DeclSecurity obj) { - if (obj == null) + if (obj is null) return; Add(obj.Action); Add(obj.SecurityAttributes); @@ -426,21 +423,21 @@ void Load(DeclSecurity obj) { } void Load(ClassLayout obj) { - if (obj == null) + if (obj is null) return; Add(obj.PackingSize); Add(obj.ClassSize); } void Load(StandAloneSig obj) { - if (obj == null) + if (obj is null) return; Add(obj.Signature); Add(obj.CustomAttributes); } void Load(EventDef obj) { - if (obj == null) + if (obj is null) return; Add(obj.Attributes); Add(obj.Name); @@ -454,7 +451,7 @@ void Load(EventDef obj) { } void Load(PropertyDef obj) { - if (obj == null) + if (obj is null) return; Add(obj.Attributes); Add(obj.Name); @@ -468,14 +465,14 @@ void Load(PropertyDef obj) { } void Load(ModuleRef obj) { - if (obj == null) + if (obj is null) return; Add(obj.Name); Add(obj.CustomAttributes); } void Load(TypeSpec obj) { - if (obj == null) + if (obj is null) return; Add(obj.TypeSig); Add(obj.ExtraData); @@ -483,7 +480,7 @@ void Load(TypeSpec obj) { } void Load(ImplMap obj) { - if (obj == null) + if (obj is null) return; Add(obj.Attributes); Add(obj.Name); @@ -491,7 +488,7 @@ void Load(ImplMap obj) { } void Load(AssemblyDef obj) { - if (obj == null) + if (obj is null) return; if (obj.ManifestModule != module) return; @@ -507,7 +504,7 @@ void Load(AssemblyDef obj) { } void Load(AssemblyRef obj) { - if (obj == null) + if (obj is null) return; Add(obj.Version); Add(obj.Attributes); @@ -519,7 +516,7 @@ void Load(AssemblyRef obj) { } void Load(FileDef obj) { - if (obj == null) + if (obj is null) return; Add(obj.Flags); Add(obj.Name); @@ -528,7 +525,7 @@ void Load(FileDef obj) { } void Load(ExportedType obj) { - if (obj == null) + if (obj is null) return; Add(obj.CustomAttributes); Add(obj.Attributes); @@ -539,19 +536,16 @@ void Load(ExportedType obj) { } void Load(Resource obj) { - if (obj == null) + if (obj is null) return; Add(obj.Offset); Add(obj.Name); Add(obj.Attributes); + Add(obj.CustomAttributes); switch (obj.ResourceType) { case ResourceType.Embedded: - var er = (EmbeddedResource)obj; - // Make sure data is cached - if (!(er.Data is MemoryImageStream)) - er.Data = MemoryImageStream.Create(er.GetClonedResourceStream().ReadAllBytes()); break; case ResourceType.AssemblyLinked: @@ -572,7 +566,7 @@ void Load(Resource obj) { } void Load(ManifestResource obj) { - if (obj == null) + if (obj is null) return; Add(obj.Offset); Add(obj.Flags); @@ -582,7 +576,7 @@ void Load(ManifestResource obj) { } void Load(GenericParam obj) { - if (obj == null) + if (obj is null) return; Add(obj.Owner); Add(obj.Number); @@ -594,7 +588,7 @@ void Load(GenericParam obj) { } void Load(MethodSpec obj) { - if (obj == null) + if (obj is null) return; Add(obj.Method); Add(obj.Instantiation); @@ -602,7 +596,7 @@ void Load(MethodSpec obj) { } void Load(GenericParamConstraint obj) { - if (obj == null) + if (obj is null) return; Add(obj.Owner); Add(obj.Constraint); @@ -610,7 +604,7 @@ void Load(GenericParamConstraint obj) { } void Load(CANamedArgument obj) { - if (obj == null) + if (obj is null) return; Add(obj.Type); Add(obj.Name); @@ -618,20 +612,20 @@ void Load(CANamedArgument obj) { } void Load(Parameter obj) { - if (obj == null) + if (obj is null) return; Add(obj.Type); } void Load(SecurityAttribute obj) { - if (obj == null) + if (obj is null) return; Add(obj.AttributeType); Add(obj.NamedArguments); } void Load(CustomAttribute obj) { - if (obj == null) + if (obj is null) return; Add(obj.Constructor); Add(obj.RawData); @@ -650,14 +644,12 @@ void AddCAValue(object obj) { return; } - var list = obj as IList; - if (list != null) { + if (obj is IList list) { Add(list); return; } - var md = obj as IMDTokenProvider; - if (md != null) { + if (obj is IMDTokenProvider md) { Add(md); return; } @@ -668,32 +660,19 @@ void Load(CAArgument obj) { AddCAValue(obj.Value); } - void Load(PdbScope obj) { - if (obj == null) - return; - Add(obj.Start); - Add(obj.End); - Add(obj.Scopes); - Add(obj.Variables); - } + void Load(PdbMethod obj) { } void Load(ResourceDirectory obj) { - if (obj == null) + if (obj is null) return; Add(obj.Directories); Add(obj.Data); } - void Load(ResourceData obj) { - if (obj == null) - return; - var data = obj.Data; - if (data != null && !(data is MemoryImageStream)) - obj.Data = MemoryImageStream.Create(data.ReadAllBytes()); - } + void Load(ResourceData obj) { } void AddToStack(T t) where T : class { - if (t == null) + if (t is null) return; if (seen.ContainsKey(t)) return; @@ -701,142 +680,109 @@ void AddToStack(T t) where T : class { stack.Push(t); } - void Add(CustomAttribute obj) { - AddToStack(obj); - } - - void Add(SecurityAttribute obj) { - AddToStack(obj); - } - - void Add(CANamedArgument obj) { - AddToStack(obj); - } - - void Add(Parameter obj) { - AddToStack(obj); - } - - void Add(IMDTokenProvider o) { - AddToStack(o); - } - - void Add(PdbScope scope) { - AddToStack(scope); - } - - void Add(TypeSig ts) { - AddToStack(ts); - } - - void Add(ResourceDirectory rd) { - AddToStack(rd); - } - - void Add(ResourceData rd) { - AddToStack(rd); - } + void Add(CustomAttribute obj) => AddToStack(obj); + void Add(SecurityAttribute obj) => AddToStack(obj); + void Add(CANamedArgument obj) => AddToStack(obj); + void Add(Parameter obj) => AddToStack(obj); + void Add(IMDTokenProvider o) => AddToStack(o); + void Add(PdbMethod pdbMethod) { } + void Add(TypeSig ts) => AddToStack(ts); + void Add(ResourceDirectory rd) => AddToStack(rd); + void Add(ResourceData rd) => AddToStack(rd); void Add(IList list) where T : IMDTokenProvider { - if (list == null) + if (list is null) return; - foreach (var item in list.GetSafeEnumerable()) + foreach (var item in list) Add(item); } void Add(IList list) { - if (list == null) + if (list is null) return; - foreach (var item in list.GetSafeEnumerable()) + foreach (var item in list) Add(item); } void Add(IList list) { - if (list == null) + if (list is null) return; - foreach (var item in list.GetSafeEnumerable()) + foreach (var item in list) Add(item); } void Add(IList list) { - if (list == null) + if (list is null) return; - foreach (var item in list.GetSafeEnumerable()) + foreach (var item in list) Add(item); } void Add(IList list) { - if (list == null) + if (list is null) return; - foreach (var item in list.GetSafeEnumerable()) + foreach (var item in list) Load(item); } void Add(IList list) { - if (list == null) + if (list is null) return; - foreach (var item in list.GetSafeEnumerable()) + foreach (var item in list) Load(item); } void Add(IList list) { - if (list == null) + if (list is null) return; - foreach (var item in list.GetSafeEnumerable()) + foreach (var item in list) Add(item); } void Add(ParameterList list) { - if (list == null) + if (list is null) return; - foreach (var item in list.GetSafeEnumerable()) + foreach (var item in list) Add(item); } void Add(IList list) { - if (list == null) + if (list is null) return; - foreach (var item in list.GetSafeEnumerable()) + foreach (var item in list) Add(item); } void Add(IList list) { - if (list == null) + if (list is null) return; - foreach (var item in list.GetSafeEnumerable()) + foreach (var item in list) Add(item); } void Add(IList list) { - if (list == null) - return; - foreach (var item in list.GetSafeEnumerable()) - Add(item); - } - - void Add(IList list) { - if (list == null) + if (list is null) return; - foreach (var item in list.GetSafeEnumerable()) + foreach (var item in list) Add(item); } void Add(IList list) { - if (list == null) + if (list is null) return; - foreach (var item in list.GetSafeEnumerable()) + foreach (var item in list) Add(item); } void Add(IList list) { - if (list == null) + if (list is null) return; - foreach (var item in list.GetSafeEnumerable()) + foreach (var item in list) Add(item); } void Add(VTableFixups vtf) { - if (vtf == null) + if (vtf is null) return; foreach (var fixup in vtf) { foreach (var method in fixup) @@ -845,41 +791,37 @@ void Add(VTableFixups vtf) { } void Add(Win32Resources vtf) { - if (vtf == null) + if (vtf is null) return; Add(vtf.Root); } void Add(CallingConventionSig sig) { - var msig = sig as MethodBaseSig; - if (msig != null) { + if (sig is MethodBaseSig msig) { Add(msig); return; } - var fsig = sig as FieldSig; - if (fsig != null) { + if (sig is FieldSig fsig) { Add(fsig); return; } - var lsig = sig as LocalSig; - if (lsig != null) { + if (sig is LocalSig lsig) { Add(lsig); return; } - var gsig = sig as GenericInstMethodSig; - if (gsig != null) { + if (sig is GenericInstMethodSig gsig) { Add(gsig); return; } - Debug.Assert(sig == null); + Debug.Assert(sig is null); } void Add(MethodBaseSig msig) { - if (msig == null) + if (msig is null) return; Add(msig.ExtraData); Add(msig.RetType); @@ -888,106 +830,100 @@ void Add(MethodBaseSig msig) { } void Add(FieldSig fsig) { - if (fsig == null) + if (fsig is null) return; Add(fsig.ExtraData); Add(fsig.Type); } void Add(LocalSig lsig) { - if (lsig == null) + if (lsig is null) return; Add(lsig.ExtraData); Add(lsig.Locals); } void Add(GenericInstMethodSig gsig) { - if (gsig == null) + if (gsig is null) return; Add(gsig.ExtraData); Add(gsig.GenericArguments); } void Add(MarshalType mt) { - if (mt == null) + if (mt is null) return; Add(mt.NativeType); } void Add(MethodBody mb) { - var cilBody = mb as CilBody; - if (cilBody != null) { + if (mb is CilBody cilBody) { Add(cilBody); return; } - var nb = mb as NativeMethodBody; - if (nb != null) { + if (mb is NativeMethodBody nb) { Add(nb); return; } - Debug.Assert(mb == null, "Unknown method body"); + Debug.Assert(mb is null, "Unknown method body"); } void Add(NativeMethodBody body) { - if (body == null) + if (body is null) return; Add(body.RVA); } void Add(CilBody body) { - if (body == null) + if (body is null) return; Add(body.Instructions); Add(body.ExceptionHandlers); Add(body.Variables); - Add(body.Scope); + Add(body.PdbMethod); } void Add(Instruction instr) { - if (instr == null) + if (instr is null) return; - var mdt = instr.Operand as IMDTokenProvider; - if (mdt != null) { + if (instr.Operand is IMDTokenProvider mdt) { Add(mdt); return; } - var p = instr.Operand as Parameter; - if (p != null) { + if (instr.Operand is Parameter p) { Add(p); return; } - var l = instr.Operand as Local; - if (l != null) { + if (instr.Operand is Local l) { Add(l); return; } - var csig = instr.Operand as CallingConventionSig; - if (csig != null) { + if (instr.Operand is CallingConventionSig csig) { Add(csig); return; } } void Add(ExceptionHandler eh) { - if (eh == null) + if (eh is null) return; Add(eh.CatchType); } void Add(Local local) { - if (local == null) + if (local is null) return; Add(local.Type); } void Add(PdbState state) { - if (state == null) + if (state is null) return; Add(state.UserEntryPoint); } diff --git a/src/DotNet/ModuleRef.cs b/src/DotNet/ModuleRef.cs index e0fb2ad0e..22cacc69b 100644 --- a/src/DotNet/ModuleRef.cs +++ b/src/DotNet/ModuleRef.cs @@ -1,14 +1,17 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// /// A high-level representation of a row in the ModuleRef table /// - public abstract class ModuleRef : IHasCustomAttribute, IMemberRefParent, IResolutionScope, IModule, IOwnerModule { + public abstract class ModuleRef : IHasCustomAttribute, IMemberRefParent, IHasCustomDebugInformation, IResolutionScope, IModule, IOwnerModule { /// /// The row id in its table /// @@ -20,47 +23,35 @@ public abstract class ModuleRef : IHasCustomAttribute, IMemberRefParent, IResolu protected ModuleDef module; /// - public MDToken MDToken { - get { return new MDToken(Table.ModuleRef, rid); } - } + public MDToken MDToken => new MDToken(Table.ModuleRef, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 12; } - } + public int HasCustomAttributeTag => 12; /// - public int MemberRefParentTag { - get { return 2; } - } + public int MemberRefParentTag => 2; /// - public int ResolutionScopeTag { - get { return 1; } - } + public int ResolutionScopeTag => 1; /// - public ScopeType ScopeType { - get { return ScopeType.ModuleRef; } - } + public ScopeType ScopeType => ScopeType.ModuleRef; /// - public string ScopeName { - get { return FullName; } - } + public string ScopeName => FullName; /// /// From column ModuleRef.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -70,7 +61,7 @@ public UTF8String Name { /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -78,19 +69,36 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } - } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 12; /// - public ModuleDef Module { - get { return module; } + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); + + /// + public ModuleDef Module => module; /// /// Gets the definition module, i.e., the module which it references, or null @@ -98,13 +106,12 @@ public ModuleDef Module { /// public ModuleDef DefinitionModule { get { - if (module == null) + if (module is null) return null; var n = name; if (UTF8String.CaseInsensitiveEquals(n, module.Name)) return module; - var asm = DefinitionAssembly; - return asm == null ? null : asm.FindModule(n); + return DefinitionAssembly?.FindModule(n); } } @@ -112,19 +119,13 @@ public ModuleDef DefinitionModule { /// Gets the definition assembly, i.e., the assembly of the module it references, or /// null if the assembly can't be found. /// - public AssemblyDef DefinitionAssembly { - get { return module == null ? null : module.Assembly; } - } + public AssemblyDef DefinitionAssembly => module?.Assembly; /// - public string FullName { - get { return UTF8String.ToSystemStringOrEmpty(name); } - } + public string FullName => UTF8String.ToSystemStringOrEmpty(name); /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } /// @@ -141,8 +142,8 @@ public ModuleRefUser(ModuleDef module) /// /// Constructor - /// Owner module /// + /// Owner module /// Module name public ModuleRefUser(ModuleDef module, UTF8String name) { this.module = module; @@ -160,17 +161,22 @@ sealed class ModuleRefMD : ModuleRef, IMDTokenProviderMD { readonly uint origRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.ModuleRef, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.ModuleRef, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -180,17 +186,18 @@ protected override void InitializeCustomAttributes() { /// If is invalid public ModuleRefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.ModuleRefTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("ModuleRef rid {0} does not exist", rid)); + throw new BadImageFormatException($"ModuleRef rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - this.module = readerModule; - uint name = readerModule.TablesStream.ReadModuleRefRow2(origRid); - this.name = readerModule.StringsStream.ReadNoNull(name); + module = readerModule; + bool b = readerModule.TablesStream.TryReadModuleRefRow(origRid, out var row); + Debug.Assert(b); + name = readerModule.StringsStream.ReadNoNull(row.Name); } } } diff --git a/src/DotNet/NativeType.cs b/src/DotNet/NativeType.cs index 548f5d7f5..b1a78e4a4 100644 --- a/src/DotNet/NativeType.cs +++ b/src/DotNet/NativeType.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info namespace dnlib.DotNet { /// @@ -97,6 +97,8 @@ public enum NativeType : uint { IInspectable = 0x2E, /// hstring HString = 0x2F, + /// UTF-8 encoded string + LPUTF8Str = 0x30, /// first invalid element type Max = 0x50, /// Value wasn't present in the blob diff --git a/src/DotNet/NullResolver.cs b/src/DotNet/NullResolver.cs index c06cb553a..805097d54 100644 --- a/src/DotNet/NullResolver.cs +++ b/src/DotNet/NullResolver.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// A resolver that always fails /// @@ -14,32 +14,12 @@ public sealed class NullResolver : IAssemblyResolver, IResolver { } /// - public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) { - return null; - } - - /// - public bool AddToCache(AssemblyDef asm) { - return true; - } - - /// - public bool Remove(AssemblyDef asm) { - return false; - } - - /// - public void Clear() { - } + public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) => null; /// - public TypeDef Resolve(TypeRef typeRef, ModuleDef sourceModule) { - return null; - } + public TypeDef Resolve(TypeRef typeRef, ModuleDef sourceModule) => null; /// - public IMemberForwarded Resolve(MemberRef memberRef) { - return null; - } + public IMemberForwarded Resolve(MemberRef memberRef) => null; } } diff --git a/src/DotNet/ParamAttributes.cs b/src/DotNet/ParamAttributes.cs index 98a7ac87c..39146fc4f 100644 --- a/src/DotNet/ParamAttributes.cs +++ b/src/DotNet/ParamAttributes.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.DotNet { /// @@ -12,6 +12,10 @@ public enum ParamAttributes : ushort { In = 0x0001, /// Param is [out] Out = 0x0002, + /// Param is a locale identifier + Lcid = 0x0004, + /// Param is a return value + Retval = 0x0008, /// Param is optional Optional = 0x0010, diff --git a/src/DotNet/ParamDef.cs b/src/DotNet/ParamDef.cs index 12a77d34f..1b001d028 100644 --- a/src/DotNet/ParamDef.cs +++ b/src/DotNet/ParamDef.cs @@ -1,9 +1,11 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; using dnlib.Threading; namespace dnlib.DotNet { @@ -11,7 +13,7 @@ namespace dnlib.DotNet { /// A high-level representation of a row in the Param table /// [DebuggerDisplay("{Sequence} {Name}")] - public abstract class ParamDef : IHasConstant, IHasCustomAttribute, IHasFieldMarshal { + public abstract class ParamDef : IHasConstant, IHasCustomAttribute, IHasFieldMarshal, IHasCustomDebugInformation { /// /// The row id in its table /// @@ -22,37 +24,29 @@ public abstract class ParamDef : IHasConstant, IHasCustomAttribute, IHasFieldMar #endif /// - public MDToken MDToken { - get { return new MDToken(Table.Param, rid); } - } + public MDToken MDToken => new MDToken(Table.Param, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasConstantTag { - get { return 1; } - } + public int HasConstantTag => 1; /// - public int HasCustomAttributeTag { - get { return 4; } - } + public int HasCustomAttributeTag => 4; /// - public int HasFieldMarshalTag { - get { return 1; } - } + public int HasFieldMarshalTag => 1; /// /// Gets the declaring method /// public MethodDef DeclaringMethod { - get { return declaringMethod; } - internal set { declaringMethod = value; } + get => declaringMethod; + internal set => declaringMethod = value; } /// protected MethodDef declaringMethod; @@ -61,8 +55,8 @@ public MethodDef DeclaringMethod { /// From column Param.Flags /// public ParamAttributes Attributes { - get { return (ParamAttributes)attributes; } - set { attributes = (int)value; } + get => (ParamAttributes)attributes; + set => attributes = (int)value; } /// Attributes protected int attributes; @@ -71,8 +65,8 @@ public ParamAttributes Attributes { /// From column Param.Sequence /// public ushort Sequence { - get { return sequence; } - set { sequence = value; } + get => sequence; + set => sequence = value; } /// protected ushort sequence; @@ -81,8 +75,8 @@ public ushort Sequence { /// From column Param.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -124,14 +118,10 @@ void InitializeMarshalType() { } /// Called to initialize - protected virtual MarshalType GetMarshalType_NoLock() { - return null; - } + protected virtual MarshalType GetMarshalType_NoLock() => null; /// Reset - protected void ResetMarshalType() { - marshalType_isInitialized = false; - } + protected void ResetMarshalType() => marshalType_isInitialized = false; /// public Constant Constant { @@ -170,21 +160,17 @@ void InitializeConstant() { } /// Called to initialize - protected virtual Constant GetConstant_NoLock() { - return null; - } + protected virtual Constant GetConstant_NoLock() => null; /// Reset - protected void ResetConstant() { - constant_isInitialized = false; - } + protected void ResetConstant() => constant_isInitialized = false; /// /// Gets all custom attributes /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -192,21 +178,38 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 4; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// /// true if is not null /// - public bool HasConstant { - get { return Constant != null; } - } + public bool HasConstant => Constant is not null; /// /// Gets the constant element type or if there's no constant @@ -214,23 +217,21 @@ public bool HasConstant { public ElementType ElementType { get { var c = Constant; - return c == null ? ElementType.End : c.Type; + return c is null ? ElementType.End : c.Type; } } /// /// true if is not null /// - public bool HasMarshalType { - get { return MarshalType != null; } - } + public bool HasMarshalType => MarshalType is not null; /// public string FullName { get { var n = name; if (UTF8String.IsNullOrEmpty(n)) - return string.Format("A_{0}", sequence); + return $"A_{sequence}"; return n.String; } } @@ -242,61 +243,66 @@ public string FullName { /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, ParamAttributes flags) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else if (set) attributes |= (int)flags; else attributes &= ~(int)flags; -#endif } /// /// Gets/sets the bit /// public bool IsIn { - get { return ((ParamAttributes)attributes & ParamAttributes.In) != 0; } - set { ModifyAttributes(value, ParamAttributes.In); } + get => ((ParamAttributes)attributes & ParamAttributes.In) != 0; + set => ModifyAttributes(value, ParamAttributes.In); } /// /// Gets/sets the bit /// public bool IsOut { - get { return ((ParamAttributes)attributes & ParamAttributes.Out) != 0; } - set { ModifyAttributes(value, ParamAttributes.Out); } + get => ((ParamAttributes)attributes & ParamAttributes.Out) != 0; + set => ModifyAttributes(value, ParamAttributes.Out); + } + + /// + /// Gets/sets the bit + /// + public bool IsLcid { + get => ((ParamAttributes)attributes & ParamAttributes.Lcid) != 0; + set => ModifyAttributes(value, ParamAttributes.Lcid); + } + + /// + /// Gets/sets the bit + /// + public bool IsRetval { + get => ((ParamAttributes)attributes & ParamAttributes.Retval) != 0; + set => ModifyAttributes(value, ParamAttributes.Retval); } /// /// Gets/sets the bit /// public bool IsOptional { - get { return ((ParamAttributes)attributes & ParamAttributes.Optional) != 0; } - set { ModifyAttributes(value, ParamAttributes.Optional); } + get => ((ParamAttributes)attributes & ParamAttributes.Optional) != 0; + set => ModifyAttributes(value, ParamAttributes.Optional); } /// /// Gets/sets the bit /// public bool HasDefault { - get { return ((ParamAttributes)attributes & ParamAttributes.HasDefault) != 0; } - set { ModifyAttributes(value, ParamAttributes.HasDefault); } + get => ((ParamAttributes)attributes & ParamAttributes.HasDefault) != 0; + set => ModifyAttributes(value, ParamAttributes.HasDefault); } /// /// Gets/sets the bit /// public bool HasFieldMarshal { - get { return ((ParamAttributes)attributes & ParamAttributes.HasFieldMarshal) != 0; } - set { ModifyAttributes(value, ParamAttributes.HasFieldMarshal); } + get => ((ParamAttributes)attributes & ParamAttributes.HasFieldMarshal) != 0; + set => ModifyAttributes(value, ParamAttributes.HasFieldMarshal); } } @@ -336,7 +342,7 @@ public ParamDefUser(UTF8String name, ushort sequence) public ParamDefUser(UTF8String name, ushort sequence, ParamAttributes flags) { this.name = name; this.sequence = sequence; - this.attributes = (int)flags; + attributes = (int)flags; } } @@ -350,27 +356,30 @@ sealed class ParamDefMD : ParamDef, IMDTokenProviderMD { readonly uint origRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// - protected override MarshalType GetMarshalType_NoLock() { - return readerModule.ReadMarshalType(Table.Param, origRid, GenericParamContext.Create(declaringMethod)); - } + protected override MarshalType GetMarshalType_NoLock() => + readerModule.ReadMarshalType(Table.Param, origRid, GenericParamContext.Create(declaringMethod)); /// - protected override Constant GetConstant_NoLock() { - return readerModule.ResolveConstant(readerModule.MetaData.GetConstantRid(Table.Param, origRid)); - } + protected override Constant GetConstant_NoLock() => + readerModule.ResolveConstant(readerModule.Metadata.GetConstantRid(Table.Param, origRid)); /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.Param, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Param, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), GenericParamContext.Create(declaringMethod), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -380,17 +389,20 @@ protected override void InitializeCustomAttributes() { /// If is invalid public ParamDefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.ParamTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("Param rid {0} does not exist", rid)); + throw new BadImageFormatException($"Param rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint name = readerModule.TablesStream.ReadParamRow(origRid, out this.attributes, out this.sequence); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.declaringMethod = readerModule.GetOwner(this); + bool b = readerModule.TablesStream.TryReadParamRow(origRid, out var row); + Debug.Assert(b); + attributes = row.Flags; + sequence = row.Sequence; + name = readerModule.StringsStream.ReadNoNull(row.Name); + declaringMethod = readerModule.GetOwner(this); } internal ParamDefMD InitializeAll() { diff --git a/src/DotNet/ParameterList.cs b/src/DotNet/ParameterList.cs index bacc6978c..080de79e3 100644 --- a/src/DotNet/ParameterList.cs +++ b/src/DotNet/ParameterList.cs @@ -1,22 +1,18 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif +using dnlib.Utils; namespace dnlib.DotNet { /// /// A list of all method parameters /// [DebuggerDisplay("Count = {Count}")] - public sealed class ParameterList : ThreadSafe.IList { + [DebuggerTypeProxy(typeof(ParameterList_CollectionDebugView))] + public sealed class ParameterList : IList { readonly MethodDef method; readonly List parameters; readonly Parameter hiddenThisParameter; @@ -30,9 +26,7 @@ public sealed class ParameterList : ThreadSafe.IList { /// /// Gets the owner method /// - public MethodDef Method { - get { return method; } - } + public MethodDef Method => method; /// /// Gets the number of parameters, including a possible hidden 'this' parameter @@ -41,10 +35,11 @@ public int Count { get { #if THREAD_SAFE theLock.EnterReadLock(); try { - return ((ThreadSafe.IList)this).Count_NoLock; - } finally { theLock.ExitReadLock(); } -#else +#endif return parameters.Count; +#if THREAD_SAFE + } + finally { theLock.ExitReadLock(); } #endif } } @@ -74,13 +69,13 @@ public Parameter this[int index] { get { #if THREAD_SAFE theLock.EnterReadLock(); try { - return ((ThreadSafe.IList)this).Get_NoLock(index); - } finally { theLock.ExitReadLock(); } -#else +#endif return parameters[index]; +#if THREAD_SAFE + } finally { theLock.ExitReadLock(); } #endif } - set { throw new NotSupportedException(); } + set => throw new NotSupportedException(); } /// @@ -105,10 +100,10 @@ public Parameter ReturnParameter { /// 's declaring type public ParameterList(MethodDef method, TypeDef declaringType) { this.method = method; - this.parameters = new List(); - this.methodSigIndexBase = -1; - this.hiddenThisParameter = new Parameter(this, 0, Parameter.HIDDEN_THIS_METHOD_SIG_INDEX); - this.returnParameter = new Parameter(this, -1, Parameter.RETURN_TYPE_METHOD_SIG_INDEX); + parameters = new List(); + methodSigIndexBase = -1; + hiddenThisParameter = new Parameter(this, 0, Parameter.HIDDEN_THIS_METHOD_SIG_INDEX); + returnParameter = new Parameter(this, -1, Parameter.RETURN_TYPE_METHOD_SIG_INDEX); UpdateThisParameterType(declaringType); UpdateParameterTypes(); } @@ -121,12 +116,27 @@ internal void UpdateThisParameterType(TypeDef methodDeclaringType) { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif - if (methodDeclaringType == null) - hiddenThisParameter.SetType(false, null); - else if (methodDeclaringType.IsValueType) - hiddenThisParameter.SetType(false, new ByRefSig(new ValueTypeSig(methodDeclaringType))); - else - hiddenThisParameter.SetType(false, new ClassSig(methodDeclaringType)); + if (methodDeclaringType is null) + hiddenThisParameter.Type = null; + else { + bool isValueType = methodDeclaringType.IsValueType; + ClassOrValueTypeSig instSig; + if (isValueType) + instSig = new ValueTypeSig(methodDeclaringType); + else + instSig = new ClassSig(methodDeclaringType); + TypeSig thisTypeSig; + if (methodDeclaringType.HasGenericParameters) { + int gpCount = methodDeclaringType.GenericParameters.Count; + var genArgs = new List(gpCount); + for (int i = 0; i < gpCount; i++) + genArgs.Add(new GenericVar(i, methodDeclaringType)); + thisTypeSig = new GenericInstSig(instSig, genArgs); + } + else + thisTypeSig = instSig; + hiddenThisParameter.Type = isValueType ? new ByRefSig(thisTypeSig) : thisTypeSig; + } #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif @@ -140,22 +150,19 @@ public void UpdateParameterTypes() { theLock.EnterWriteLock(); try { #endif var sig = method.MethodSig; - if (sig == null) { + if (sig is null) { methodSigIndexBase = -1; parameters.Clear(); return; } if (UpdateThisParameter_NoLock(sig)) parameters.Clear(); - returnParameter.SetType(false, sig.RetType); - sig.Params.ExecuteLocked(null, (tsList, arg) => { - ResizeParameters_NoLock(tsList.Count_NoLock() + methodSigIndexBase); - if (methodSigIndexBase > 0) - parameters[0] = hiddenThisParameter; - for (int i = 0; i < tsList.Count_NoLock(); i++) - parameters[i + methodSigIndexBase].SetType(true, tsList.Get_NoLock(i)); - return null; - }); + returnParameter.Type = sig.RetType; + ResizeParameters_NoLock(sig.Params.Count + methodSigIndexBase); + if (methodSigIndexBase > 0) + parameters[0] = hiddenThisParameter; + for (int i = 0; i < sig.Params.Count; i++) + parameters[i + methodSigIndexBase].Type = sig.Params[i]; #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif @@ -163,7 +170,7 @@ public void UpdateParameterTypes() { bool UpdateThisParameter_NoLock(MethodSig methodSig) { int newIndex; - if (methodSig == null) + if (methodSig is null) newIndex = -1; else newIndex = methodSig.ImplicitThis ? 1 : 0; @@ -205,26 +212,25 @@ ParamDef FindParamDef_NoLock(Parameter param) { else return hiddenThisParamDef; - foreach (var paramDef in method.ParamDefs.GetSafeEnumerable()) { - if (paramDef != null && paramDef.Sequence == seq) + var paramDefs = method.ParamDefs; + int count = paramDefs.Count; + for (int i = 0; i < count; i++) { + var paramDef = paramDefs[i]; + if (paramDef is not null && paramDef.Sequence == seq) return paramDef; } return null; } - internal void TypeUpdated(Parameter param, bool noParamsLock) { + internal void TypeUpdated(Parameter param) { var sig = method.MethodSig; - if (sig == null) + if (sig is null) return; int index = param.MethodSigIndex; if (index == Parameter.RETURN_TYPE_METHOD_SIG_INDEX) sig.RetType = param.Type; - else if (index >= 0) { - if (noParamsLock) - sig.Params.Set_NoLock(index, param.Type); - else - sig.Params.Set(index, param.Type); - } + else if (index >= 0) + sig.Params[index] = param.Type; } internal void CreateParamDef(Parameter param) { @@ -232,7 +238,7 @@ internal void CreateParamDef(Parameter param) { theLock.EnterWriteLock(); try { #endif var paramDef = FindParamDef_NoLock(param); - if (paramDef != null) + if (paramDef is not null) return; if (param.IsHiddenThisParameter) { hiddenThisParamDef = UpdateRowId_NoLock(new ParamDefUser(UTF8String.Empty, ushort.MaxValue, 0)); @@ -248,10 +254,10 @@ internal void CreateParamDef(Parameter param) { ParamDef UpdateRowId_NoLock(ParamDef pd) { var dt = method.DeclaringType; - if (dt == null) + if (dt is null) return pd; var module = dt.Module; - if (module == null) + if (module is null) return pd; return module.UpdateRowId(pd); } @@ -260,130 +266,102 @@ ParamDef UpdateRowId_NoLock(ParamDef pd) { public int IndexOf(Parameter item) { #if THREAD_SAFE theLock.EnterReadLock(); try { - return ((ThreadSafe.IList)this).IndexOf_NoLock(item); - } finally { theLock.ExitReadLock(); } -#else +#endif return parameters.IndexOf(item); +#if THREAD_SAFE + } + finally { theLock.ExitReadLock(); } #endif } - void IList.Insert(int index, Parameter item) { - throw new NotSupportedException(); - } - - void IList.RemoveAt(int index) { - throw new NotSupportedException(); - } - - void ICollection.Add(Parameter item) { - throw new NotSupportedException(); - } - - void ICollection.Clear() { - throw new NotSupportedException(); - } + void IList.Insert(int index, Parameter item) => throw new NotSupportedException(); + void IList.RemoveAt(int index) => throw new NotSupportedException(); + void ICollection.Add(Parameter item) => throw new NotSupportedException(); + void ICollection.Clear() => throw new NotSupportedException(); bool ICollection.Contains(Parameter item) { #if THREAD_SAFE theLock.EnterReadLock(); try { - return ((ThreadSafe.IList)this).Contains_NoLock(item); - } finally { theLock.ExitReadLock(); } -#else +#endif return parameters.Contains(item); +#if THREAD_SAFE + } + finally { theLock.ExitReadLock(); } #endif } void ICollection.CopyTo(Parameter[] array, int arrayIndex) { #if THREAD_SAFE theLock.EnterReadLock(); try { - ((ThreadSafe.IList)this).CopyTo_NoLock(array, arrayIndex); - } finally { theLock.ExitReadLock(); } -#else +#endif parameters.CopyTo(array, arrayIndex); +#if THREAD_SAFE + } + finally { theLock.ExitReadLock(); } #endif } - bool ICollection.IsReadOnly { - get { return true; } - } + bool ICollection.IsReadOnly => true; + bool ICollection.Remove(Parameter item) => throw new NotSupportedException(); - bool ICollection.Remove(Parameter item) { - throw new NotSupportedException(); - } - - IEnumerator IEnumerable.GetEnumerator() { + /// + /// Enumerator + /// + public struct Enumerator : IEnumerator { + readonly ParameterList list; + List.Enumerator listEnumerator; + Parameter current; + + internal Enumerator(ParameterList list) { + this.list = list; + current = default; #if THREAD_SAFE - theLock.EnterReadLock(); try { - return ((ThreadSafe.IList)this).GetEnumerator_NoLock(); - } finally { theLock.ExitReadLock(); } -#else - return parameters.GetEnumerator(); + list.theLock.EnterReadLock(); try { #endif - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return ((IEnumerable)this).GetEnumerator(); - } - + listEnumerator = list.parameters.GetEnumerator(); #if THREAD_SAFE - int ThreadSafe.IList.IndexOf_NoLock(Parameter item) { - return parameters.IndexOf(item); - } - - void ThreadSafe.IList.Insert_NoLock(int index, Parameter item) { - throw new NotSupportedException(); - } - - void ThreadSafe.IList.RemoveAt_NoLock(int index) { - throw new NotSupportedException(); - } - - Parameter ThreadSafe.IList.Get_NoLock(int index) { - return parameters[index]; - } - - void ThreadSafe.IList.Set_NoLock(int index, Parameter value) { - throw new NotSupportedException(); - } - - void ThreadSafe.IList.Add_NoLock(Parameter item) { - throw new NotSupportedException(); - } - - void ThreadSafe.IList.Clear_NoLock() { - throw new NotSupportedException(); - } - - bool ThreadSafe.IList.Contains_NoLock(Parameter item) { - return parameters.Contains(item); - } - - void ThreadSafe.IList.CopyTo_NoLock(Parameter[] array, int arrayIndex) { - parameters.CopyTo(array, arrayIndex); - } - - bool ThreadSafe.IList.Remove_NoLock(Parameter item) { - throw new NotSupportedException(); - } + } finally { list.theLock.ExitReadLock(); } +#endif + } - IEnumerator ThreadSafe.IList.GetEnumerator_NoLock() { - return parameters.GetEnumerator(); - } + /// + /// Gets the current value + /// + public Parameter Current => current; + Parameter IEnumerator.Current => current; + object System.Collections.IEnumerator.Current => current; + + /// + /// Moves to the next element in the collection + /// + /// + public bool MoveNext() { +#if THREAD_SAFE + list.theLock.EnterWriteLock(); try { +#endif + var res = listEnumerator.MoveNext(); + current = listEnumerator.Current; + return res; +#if THREAD_SAFE + } finally { list.theLock.ExitWriteLock(); } +#endif + } - int ThreadSafe.IList.Count_NoLock { - get { return parameters.Count; } - } + /// + /// Disposes the enumerator + /// + public void Dispose() => listEnumerator.Dispose(); - bool ThreadSafe.IList.IsReadOnly_NoLock { - get { return true; } + void System.Collections.IEnumerator.Reset() => throw new NotSupportedException(); } - TRetType ThreadSafe.IList.ExecuteLocked(TArgType arg, ExecuteLockedDelegate handler) { - theLock.EnterWriteLock(); try { - return handler(this, arg); - } finally { theLock.ExitWriteLock(); } - } -#endif + /// + /// Gets the list enumerator + /// + /// + public Enumerator GetEnumerator() => new Enumerator(this); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); } /// @@ -410,85 +388,56 @@ public sealed class Parameter : IVariable { /// has index 0 and the remaining parameters in the method signature start from index 1. /// The method return parameter has index -1. /// - public int Index { - get { return paramIndex; } - } + public int Index => paramIndex; /// /// Gets the index of the parameter in the method signature. See also /// and /// - public int MethodSigIndex { - get { return methodSigIndex; } - } + public int MethodSigIndex => methodSigIndex; /// /// true if it's a normal visible method parameter, i.e., it's not the hidden /// 'this' parameter and it's not the method return type parameter. /// - public bool IsNormalMethodParameter { - get { return methodSigIndex >= 0; } - } + public bool IsNormalMethodParameter => methodSigIndex >= 0; /// /// true if it's the hidden 'this' parameter /// - public bool IsHiddenThisParameter { - get { return methodSigIndex == HIDDEN_THIS_METHOD_SIG_INDEX; } - } + public bool IsHiddenThisParameter => methodSigIndex == HIDDEN_THIS_METHOD_SIG_INDEX; /// /// true if it's the method return type parameter /// - public bool IsReturnTypeParameter { - get { return methodSigIndex == RETURN_TYPE_METHOD_SIG_INDEX; } - } + public bool IsReturnTypeParameter => methodSigIndex == RETURN_TYPE_METHOD_SIG_INDEX; /// /// Gets the parameter type /// public TypeSig Type { - get { return typeSig; } + get => typeSig; set { typeSig = value; - if (parameterList != null) - parameterList.TypeUpdated(this, false); + if (parameterList is not null) + parameterList.TypeUpdated(this); } } - /// - /// This method does exactly what the setter does except that it - /// uses the no-lock method if is true. - /// - /// true if MethodSig.Params lock is being held by - /// us - /// - internal void SetType(bool noParamsLock, TypeSig type) { - typeSig = type; - if (parameterList != null) - parameterList.TypeUpdated(this, noParamsLock); - } - /// /// Gets the owner method /// - public MethodDef Method { - get { return parameterList == null ? null : parameterList.Method; } - } + public MethodDef Method => parameterList?.Method; /// /// Gets the or null if not present /// - public ParamDef ParamDef { - get { return parameterList == null ? null : parameterList.FindParamDef(this); } - } + public ParamDef ParamDef => parameterList?.FindParamDef(this); /// /// true if it has a /// - public bool HasParamDef { - get { return ParamDef != null; } - } + public bool HasParamDef => ParamDef is not null; /// /// Gets the name from . If is null, @@ -497,11 +446,11 @@ public bool HasParamDef { public string Name { get { var paramDef = ParamDef; - return paramDef == null ? string.Empty : UTF8String.ToSystemStringOrEmpty(paramDef.Name); + return paramDef is null ? string.Empty : UTF8String.ToSystemStringOrEmpty(paramDef.Name); } set { var paramDef = ParamDef; - if (paramDef != null) + if (paramDef is not null) paramDef.Name = value; } } @@ -512,7 +461,7 @@ public string Name { /// Parameter index public Parameter(int paramIndex) { this.paramIndex = paramIndex; - this.methodSigIndex = paramIndex; + methodSigIndex = paramIndex; } /// @@ -522,8 +471,8 @@ public Parameter(int paramIndex) { /// Parameter type public Parameter(int paramIndex, TypeSig type) { this.paramIndex = paramIndex; - this.methodSigIndex = paramIndex; - this.typeSig = type; + methodSigIndex = paramIndex; + typeSig = type; } /// @@ -545,7 +494,7 @@ public Parameter(int paramIndex, int methodSigIndex) { public Parameter(int paramIndex, int methodSigIndex, TypeSig type) { this.paramIndex = paramIndex; this.methodSigIndex = methodSigIndex; - this.typeSig = type; + typeSig = type; } internal Parameter(ParameterList parameterList, int paramIndex, int methodSigIndex) { @@ -558,7 +507,7 @@ internal Parameter(ParameterList parameterList, int paramIndex, int methodSigInd /// Creates a if it doesn't already exist /// public void CreateParamDef() { - if (parameterList != null) + if (parameterList is not null) parameterList.CreateParamDef(this); } @@ -568,7 +517,7 @@ public override string ToString() { if (string.IsNullOrEmpty(name)) { if (IsReturnTypeParameter) return "RET_PARAM"; - return string.Format("A_{0}", paramIndex); + return $"A_{paramIndex}"; } return name; } diff --git a/src/DotNet/Pdb/CustomDebugInfoGuids.cs b/src/DotNet/Pdb/CustomDebugInfoGuids.cs new file mode 100644 index 000000000..71342b5c8 --- /dev/null +++ b/src/DotNet/Pdb/CustomDebugInfoGuids.cs @@ -0,0 +1,28 @@ +// dnlib: See LICENSE.txt for more info + +using System; + +namespace dnlib.DotNet.Pdb { + /// + /// Custom debug info guids + /// + public static class CustomDebugInfoGuids { +#pragma warning disable 1591 // Missing XML comment for publicly visible type or member + // Roslyn: PortableCustomDebugInfoKinds.cs + public static readonly Guid AsyncMethodSteppingInformationBlob = new Guid("54FD2AC5-E925-401A-9C2A-F94F171072F8"); + public static readonly Guid DefaultNamespace = new Guid("58B2EAB6-209F-4E4E-A22C-B2D0F910C782"); + public static readonly Guid DynamicLocalVariables = new Guid("83C563C4-B4F3-47D5-B824-BA5441477EA8"); + public static readonly Guid EmbeddedSource = new Guid("0E8A571B-6926-466E-B4AD-8AB04611F5FE"); + public static readonly Guid EncLambdaAndClosureMap = new Guid("A643004C-0240-496F-A783-30D64F4979DE"); + public static readonly Guid EncLocalSlotMap = new Guid("755F52A8-91C5-45BE-B4B8-209571E552BD"); + public static readonly Guid SourceLink = new Guid("CC110556-A091-4D38-9FEC-25AB9A351A6A"); + public static readonly Guid StateMachineHoistedLocalScopes = new Guid("6DA9A61E-F8C7-4874-BE62-68BC5630DF71"); + public static readonly Guid TupleElementNames = new Guid("ED9FDF71-8879-4747-8ED3-FE5EDE3CE710"); + public static readonly Guid CompilationMetadataReferences = new Guid("7E4D4708-096E-4C5C-AEDA-CB10BA6A740D"); + public static readonly Guid CompilationOptions = new Guid("B5FEEC05-8CD0-4A83-96DA-466284BB4BD8"); + public static readonly Guid TypeDefinitionDocuments = new Guid("932E74BC-DBA9-4478-8D46-0F32A7BAB3D3"); + public static readonly Guid EncStateMachineStateMap = new Guid("8B78CD68-2EDE-420B-980B-E15884B8AAA3"); + public static readonly Guid PrimaryConstructorInformationBlob = new Guid("9D40ACE1-C703-4D0E-BF41-7243060A8FB5"); +#pragma warning restore 1591 // Missing XML comment for publicly visible type or member + } +} diff --git a/src/DotNet/Pdb/DataReaderFactoryUtils.cs b/src/DotNet/Pdb/DataReaderFactoryUtils.cs new file mode 100644 index 000000000..8009714e2 --- /dev/null +++ b/src/DotNet/Pdb/DataReaderFactoryUtils.cs @@ -0,0 +1,26 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.IO; +using System.Security; +using dnlib.IO; + +namespace dnlib.DotNet.Pdb { + static class DataReaderFactoryUtils { + public static DataReaderFactory TryCreateDataReaderFactory(string filename) { + try { + if (!File.Exists(filename)) + return null; + // Don't use memory mapped I/O + return ByteArrayDataReaderFactory.Create(File.ReadAllBytes(filename), filename); + } + catch (IOException) { + } + catch (UnauthorizedAccessException) { + } + catch (SecurityException) { + } + return null; + } + } +} diff --git a/src/DotNet/Pdb/Dss/ComInterfaces.cs b/src/DotNet/Pdb/Dss/ComInterfaces.cs index d7c31c539..a57c84672 100644 --- a/src/DotNet/Pdb/Dss/ComInterfaces.cs +++ b/src/DotNet/Pdb/Dss/ComInterfaces.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Runtime.InteropServices; @@ -8,23 +8,20 @@ namespace dnlib.DotNet.Pdb.Dss { [ComVisible(true), ComImport, - Guid("809C652E-7396-11D2-9771-00A0C9B4D50C"), + Guid("969708D2-05E5-4861-A3B0-96E473CDF63F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IMetaDataDispenser { - void DefineScope([In] ref Guid rclsid, [In] uint dwCreateFlags, [In] ref Guid riid, [Out, MarshalAs(UnmanagedType.IUnknown)] out object ppIUnk); - void OpenScope([In, MarshalAs(UnmanagedType.LPWStr)] string szScope, [In] uint dwOpenFlags, [In] ref Guid riid, [Out, MarshalAs(UnmanagedType.IUnknown)] out object ppIUnk); - void OpenScopeOnMemory([In] IntPtr pData, [In] uint cbData, [In] uint dwOpenFlags, [In] ref Guid riid, [Out, MarshalAs(UnmanagedType.IUnknown)] out object ppIUnk); + interface ISymUnmanagedDispose { + [PreserveSig] + int Destroy(); } [ComVisible(true), ComImport, - Guid("AA544D42-28CB-11D3-BD22-0000F80849BD"), + Guid("997DD0CC-A76F-4c82-8D79-EA87559D27AD"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface ISymUnmanagedBinder { - [PreserveSig] - int GetReaderForFile([In, MarshalAs(UnmanagedType.IUnknown)] object importer, [In, MarshalAs(UnmanagedType.LPWStr)] string fileName, [In, MarshalAs(UnmanagedType.LPWStr)] string searchPath, [Out] out ISymUnmanagedReader pRetVal); + interface ISymUnmanagedSourceServerModule { [PreserveSig] - int GetReaderFromStream([In, MarshalAs(UnmanagedType.IUnknown)] object importer, [In] IStream pstream, [Out] out ISymUnmanagedReader pRetVal); + int GetSourceServerData(out int pDataByteCount, out IntPtr ppData); } [ComVisible(true), @@ -36,17 +33,16 @@ interface ISymUnmanagedReader { void GetDocuments([In] uint cDocs, [Out] out uint pcDocs, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ISymUnmanagedDocument[] pDocs); [PreserveSig] int GetUserEntryPoint([Out] out uint pToken); - [PreserveSig] - int GetMethod([In] uint token, [Out] out ISymUnmanagedMethod retVal); + void GetMethod([In] uint token, [Out] out ISymUnmanagedMethod retVal); [PreserveSig] int GetMethodByVersion([In] uint token, [In] int version, [Out] out ISymUnmanagedMethod pRetVal); void GetVariables([In] uint parent, [In] uint cVars, [Out] out uint pcVars, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ISymUnmanagedVariable[] pVars); void GetGlobalVariables([In] uint cVars, [Out] out uint pcVars, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ISymUnmanagedVariable[] pVars); - [PreserveSig] - int GetMethodFromDocumentPosition([In] ISymUnmanagedDocument document, [In] uint line, [In] uint column, [Out] out ISymUnmanagedMethod pRetVal); + void GetMethodFromDocumentPosition([In] ISymUnmanagedDocument document, [In] uint line, [In] uint column, [Out] out ISymUnmanagedMethod pRetVal); void GetSymAttribute([In] uint parent, [In, MarshalAs(UnmanagedType.LPWStr)] string name, [In] uint cBuffer, [Out] out uint pcBuffer, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] buffer); void GetNamespaces([In] uint cNameSpaces, [Out] out uint pcNameSpaces, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ISymUnmanagedNamespace[] namespaces); - void Initialize([In, MarshalAs(UnmanagedType.IUnknown)] object importer, [In, MarshalAs(UnmanagedType.LPWStr)] string filename, [In, MarshalAs(UnmanagedType.LPWStr)] string searchPath, [In] IStream pIStream); + [PreserveSig] + int Initialize([In, MarshalAs(UnmanagedType.IUnknown)] object importer, [In, MarshalAs(UnmanagedType.LPWStr)] string filename, [In, MarshalAs(UnmanagedType.LPWStr)] string searchPath, [In] IStream pIStream); void UpdateSymbolStore([In, MarshalAs(UnmanagedType.LPWStr)] string filename, [In] IStream pIStream); void ReplaceSymbolStore([In, MarshalAs(UnmanagedType.LPWStr)] string filename, [In] IStream pIStream); void GetSymbolStoreFileName([In] uint cchName, [Out] out uint pcchName, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] char[] szName); @@ -55,6 +51,49 @@ interface ISymUnmanagedReader { void GetMethodVersion([In] ISymUnmanagedMethod pMethod, [Out] out int version); } + [ComVisible(true), + ComImport, + Guid("A09E53B2-2A57-4cca-8F63-B84F7C35D4AA"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedReader2 : ISymUnmanagedReader { + void _VtblGap1_17(); + void GetMethodByVersionPreRemap(uint token, uint version, [MarshalAs(UnmanagedType.Interface)] out ISymUnmanagedMethod pRetVal); + void GetSymAttributePreRemap(uint parent, [In, MarshalAs(UnmanagedType.LPWStr)] string name, uint cBuffer, out uint pcBuffer, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] buffer); + void GetMethodsInDocument(ISymUnmanagedDocument document, uint bufferLength, out uint count, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] [In] [Out] ISymUnmanagedMethod[] methods); + } + + [ComVisible(true), + ComImport, + Guid("6151CAD9-E1EE-437A-A808-F64838C0D046"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedReader3 : ISymUnmanagedReader2 { + void _VtblGap1_20(); + void GetSymAttributeByVersion(uint token, uint version, [MarshalAs(UnmanagedType.LPWStr)] string name, uint cBuffer, out uint pcBuffer, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] byte[] buffer); + void GetSymAttributeByVersionPreRemap(int methodToken, int version, [MarshalAs(UnmanagedType.LPWStr)] string name, int cBuffer, out int pcBuffer, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] byte[] buffer); + } + + [ComVisible(true), + ComImport, + Guid("E65C58B7-2948-434D-8A6D-481740A00C16"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedReader4 : ISymUnmanagedReader3 { + void _VtblGap1_22(); + [PreserveSig] + int MatchesModule(Guid guid, uint stamp, uint age, [MarshalAs(UnmanagedType.Bool)] out bool result); + void GetPortableDebugMetadata(out IntPtr pMetadata, out uint pcMetadata); + [PreserveSig] + int GetSourceServerData(out IntPtr data, out int pcData); + } + + [ComVisible(true), + ComImport, + Guid("6576C987-7E8D-4298-A6E1-6F9783165F07"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedReader5 : ISymUnmanagedReader4 { + void _VtblGap1_25(); + void GetPortableDebugMetadataByVersion(uint version, out IntPtr pMetadata, out uint pcMetadata); + } + [ComVisible(true), ComImport, Guid("40DE4037-7C81-3E1E-B022-AE1ABFF2CA08"), @@ -68,8 +107,10 @@ interface ISymUnmanagedDocument { void GetCheckSum([In] uint cData, [Out] out uint pcData, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] byte[] data); void FindClosestLine([In] uint line, [Out] out uint pRetVal); void HasEmbeddedSource([Out] out bool pRetVal); - void GetSourceLength([Out] out uint pRetVal); - void GetSourceRange([In] uint startLine, [In] uint startColumn, [In] uint endLine, [In] uint endColumn, [In] uint cSourceBytes, [Out] out uint pcSourceBytes, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] byte[] source); + [PreserveSig] + int GetSourceLength([Out] out int pRetVal); + [PreserveSig] + int GetSourceRange([In] uint startLine, [In] uint startColumn, [In] uint endLine, [In] uint endColumn, [In] int cSourceBytes, [Out] out int pcSourceBytes, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] byte[] source); } [ComVisible(true), @@ -89,6 +130,28 @@ interface ISymUnmanagedMethod { void GetSequencePoints([In] uint cPoints, [Out] out uint pcPoints, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] offsets, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ISymUnmanagedDocument[] documents, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] lines, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] columns, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] endLines, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] endColumns); } + [ComVisible(true), + ComImport, + Guid("5DA320C8-9C2C-4E5A-B823-027E0677B359"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedMethod2 : ISymUnmanagedMethod { + void _VtblGap1_10(); + void GetLocalSignatureToken(out uint token); + } + + [ComVisible(true), + ComImport, + Guid("B20D55B3-532E-4906-87E7-25BD5734ABD2"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedAsyncMethod { + bool IsAsyncMethod(); + uint GetKickoffMethod(); + bool HasCatchHandlerILOffset(); + uint GetCatchHandlerILOffset(); + uint GetAsyncStepInfoCount(); + void GetAsyncStepInfo([In] uint cStepInfo, [Out] out uint pcStepInfo, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] yieldOffsets, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] breakpointOffset, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] breakpointMethod); + } + [ComVisible(true), ComImport, Guid("9F60EEBE-2D9A-3F7C-BF58-80BC991C60BB"), @@ -130,11 +193,42 @@ interface ISymUnmanagedScope { void GetNamespaces([In] uint cNameSpaces, [Out] out uint pcNameSpaces, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ISymUnmanagedNamespace[] namespaces); } + [ComVisible(true), + ComImport, + Guid("AE932FBA-3FD8-4dba-8232-30A2309B02DB"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedScope2 : ISymUnmanagedScope { +#pragma warning disable 0108 + void GetMethod([Out] out ISymUnmanagedMethod pRetVal); + void GetParent([Out] out ISymUnmanagedScope pRetVal); + void GetChildren([In] uint cChildren, [Out] out uint pcChildren, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ISymUnmanagedScope[] children); + void GetStartOffset([Out] out uint pRetVal); + void GetEndOffset([Out] out uint pRetVal); + void GetLocalCount([Out] out uint pRetVal); + void GetLocals([In] uint cLocals, [Out] out uint pcLocals, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ISymUnmanagedVariable[] locals); + void GetNamespaces([In] uint cNameSpaces, [Out] out uint pcNameSpaces, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ISymUnmanagedNamespace[] namespaces); +#pragma warning restore 0108 + + uint GetConstantCount(); + void GetConstants([In] uint cConstants, [Out] out uint pcConstants, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ISymUnmanagedConstant[] constants); + } + + [ComVisible(true), + ComImport, + Guid("48B25ED8-5BAD-41bc-9CEE-CD62FABC74E9"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedConstant { + void GetName([In] uint cchName, [Out] out uint pcchName, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] char[] szName); + void GetValue(out object pValue); + [PreserveSig] + int GetSignature([In] uint cSig, [Out] out uint pcSig, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] byte[] sig); + } + [ComVisible(true), ComImport, Guid("7DAC8207-D3AE-4C75-9B67-92801A497D44"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IMetaDataImport { + unsafe interface IMetaDataImport { void CloseEnum(IntPtr hEnum); void CountEnum(IntPtr hEnum, ref uint pulCount); void ResetEnum(IntPtr hEnum, uint ulPos); @@ -144,9 +238,9 @@ interface IMetaDataImport { void FindTypeDefByName([In, MarshalAs(UnmanagedType.LPWStr)] string szTypeDef, [In] uint tkEnclosingClass, [Out] out uint ptd); void GetScopeProps([Out] IntPtr szName, [In] uint cchName, [Out] out uint pchName, [Out] out Guid pmvid); void GetModuleFromScope([Out] out uint pmd); - unsafe void GetTypeDefProps([In] uint td, [In] ushort* szTypeDef, [In] uint cchTypeDef, [Out] uint* pchTypeDef, [Out] uint* pdwTypeDefFlags, [Out] uint* ptkExtends); + void GetTypeDefProps([In] uint td, [In] ushort* szTypeDef, [In] uint cchTypeDef, [Out] uint* pchTypeDef, [Out] uint* pdwTypeDefFlags, [Out] uint* ptkExtends); void GetInterfaceImplProps([In] uint iiImpl, [Out] out uint pClass, [Out] out uint ptkIface); - void GetTypeRefProps([In] uint tr, [Out] out uint ptkResolutionScope, [Out] IntPtr szName, [In] uint cchName, [Out] out uint pchName); + void GetTypeRefProps([In] uint tr, [Out] uint* ptkResolutionScope, [Out] ushort* szName, [In] uint cchName, [Out] uint* pchName); void ResolveTypeRef(uint tr, ref Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppIScope, out uint ptd); void EnumMembers([In, Out] ref IntPtr phEnum, [In] uint cl, [Out] uint[] rMembers, [In] uint cMax, [Out] out uint pcTokens); void EnumMembersWithName([In, Out] ref IntPtr phEnum, [In] uint cl, [In] [MarshalAs(UnmanagedType.LPWStr)] string szName, [Out] uint[] rMembers, [In] uint cMax, [Out] out uint pcTokens); @@ -162,7 +256,7 @@ interface IMetaDataImport { void FindMethod([In] uint td, [In] [MarshalAs(UnmanagedType.LPWStr)] string szName, [In] IntPtr pvSigBlob, [In] uint cbSigBlob, [Out] out uint pmb); void FindField([In] uint td, [In] [MarshalAs(UnmanagedType.LPWStr)] string szName, [In] IntPtr pvSigBlob, [In] uint cbSigBlob, [Out] out uint pmb); void FindMemberRef([In] uint td, [In] [MarshalAs(UnmanagedType.LPWStr)] string szName, [In] IntPtr pvSigBlob, [In] uint cbSigBlob, [Out] out uint pmr); - unsafe void GetMethodProps(uint mb, uint* pClass, [In] ushort* szMethod, uint cchMethod, uint* pchMethod, uint* pdwAttr, [Out] IntPtr* ppvSigBlob, [Out] uint* pcbSigBlob, [Out] uint* pulCodeRVA, [Out] uint* pdwImplFlags); + void GetMethodProps(uint mb, uint* pClass, [In] ushort* szMethod, uint cchMethod, uint* pchMethod, uint* pdwAttr, [Out] IntPtr* ppvSigBlob, [Out] uint* pcbSigBlob, [Out] uint* pulCodeRVA, [Out] uint* pdwImplFlags); void GetMemberRefProps([In] uint mr, [Out] out uint ptk, [Out] IntPtr szMember, [In] uint cchMember, [Out] out uint pchMember, [Out] out IntPtr ppvSigBlob, [Out] out uint pbSig); void EnumProperties([In, Out] ref IntPtr phEnum, [In] uint td, [Out] uint[] rProperties, [In] uint cMax, [Out] out uint pcProperties); void EnumEvents([In, Out] ref IntPtr phEnum, [In] uint td, [Out] uint[] rEvents, [In] uint cMax, [Out] out uint pcEvents); @@ -173,7 +267,7 @@ interface IMetaDataImport { void GetFieldMarshal([In] uint tk, [Out] out IntPtr ppvNativeType, [Out] out uint pcbNativeType); void GetRVA(uint tk, out uint pulCodeRVA, out uint pdwImplFlags); void GetPermissionSetProps([In] uint pm, [Out] out uint pdwAction, [Out] out IntPtr ppvPermission, [Out] out uint pcbPermission); - void GetSigFromToken([In] uint mdSig, [Out] out IntPtr ppvSig, [Out] out uint pcbSig); + void GetSigFromToken([In] uint mdSig, [Out] byte** ppvSig, [Out] uint* pcbSig); void GetModuleRefProps([In] uint mur, [Out] IntPtr szName, [In] uint cchName, [Out] out uint pchName); void EnumModuleRefs([In, Out] ref IntPtr phEnum, [Out] uint[] rModuleRefs, [In] uint cmax, [Out] out uint pcModuleRefs); void GetTypeSpecFromToken([In] uint typespec, [Out] out IntPtr ppvSig, [Out] out uint pcbSig); @@ -194,7 +288,7 @@ interface IMetaDataImport { void GetParamProps([In] uint tk, [Out] out uint pmd, [Out] out uint pulSequence, [Out] IntPtr szName, [Out] uint cchName, [Out] out uint pchName, [Out] out uint pdwAttr, [Out] out uint pdwCPlusTypeFlag, [Out] out IntPtr ppValue, [Out] out uint pcchValue); void GetCustomAttributeByName([In] uint tkObj, [In] [MarshalAs(UnmanagedType.LPWStr)] string szName, [Out] out IntPtr ppData, [Out] out uint pcbData); bool IsValidToken([In] uint tk); - unsafe void GetNestedClassProps([In] uint tdNestedClass, [Out] uint* ptdEnclosingClass); + void GetNestedClassProps([In] uint tdNestedClass, [Out] uint* ptdEnclosingClass); void GetNativeCallConvFromSig([In] IntPtr pvSig, [In] uint cbSig, [Out] out uint pCallConv); void IsGlobal([In] uint pd, [Out] out int pbGlobal); } @@ -286,12 +380,11 @@ interface ISymUnmanagedWriter { void Abort(); } -#pragma warning disable 1591 [ComVisible(true), ComImport, Guid("0B97726E-9E6D-4F05-9A26-424022093CAA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface ISymUnmanagedWriter2 { + interface ISymUnmanagedWriter2 { void DefineDocument([In] [MarshalAs(UnmanagedType.LPWStr)] string url, [In] ref Guid language, [In] ref Guid languageVendor, [In] ref Guid documentType, [Out] out ISymUnmanagedDocumentWriter pRetVal); void SetUserEntryPoint([In] uint entryMethod); void OpenMethod([In] uint method); @@ -320,16 +413,91 @@ public interface ISymUnmanagedWriter2 { void DefineGlobalVariable2([In, MarshalAs(UnmanagedType.LPWStr)] string name, [In] uint attributes, [In] uint sigToken, [In] uint addrKind, [In] uint addr1, [In] uint addr2, [In] uint addr3); void DefineConstant2([In, MarshalAs(UnmanagedType.LPWStr)] string name, [In] object value, [In] uint sigToken); } -#pragma warning restore 1591 -#pragma warning disable 1591 + [ComVisible(true), + ComImport, + Guid("12F1E02C-1E05-4B0E-9468-EBC9D1BB040F"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedWriter3 : ISymUnmanagedWriter2 { + void _VtblGap1_27(); + void OpenMethod2(uint method, uint isect, uint offset); + void Commit(); + } + + [ComVisible(true), + ComImport, + Guid("BC7E3F53-F458-4C23-9DBD-A189E6E96594"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedWriter4 : ISymUnmanagedWriter3 { + void _VtblGap1_29(); + void GetDebugInfoWithPadding([In] [Out] ref IMAGE_DEBUG_DIRECTORY pIDD, uint cData, out uint pcData, IntPtr data); + } + + [ComVisible(true), + ComImport, + Guid("DCF7780D-BDE9-45DF-ACFE-21731A32000C"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedWriter5 : ISymUnmanagedWriter4 { + void _VtblGap1_30(); + void OpenMapTokensToSourceSpans(); + void CloseMapTokensToSourceSpans(); + void MapTokenToSourceSpan(uint token, ISymUnmanagedDocumentWriter document, uint line, uint column, uint endLine, uint endColumn); + } + + [ComVisible(true), + ComImport, + Guid("CA6C2ED9-103D-46A9-B03B-05446485848B"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedWriter6 : ISymUnmanagedWriter5 { + void _VtblGap1_33(); + void InitializeDeterministic([MarshalAs(UnmanagedType.IUnknown)] object emitter, [MarshalAs(UnmanagedType.IUnknown)] object stream); + } + + [ComVisible(true), + ComImport, + Guid("22DAEAF2-70F6-4EF1-B0C3-984F0BF27BFD"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedWriter7 : ISymUnmanagedWriter6 { + void _VtblGap1_34(); + void UpdateSignatureByHashingContent(IntPtr buffer, uint cData); + } + + [ComVisible(true), + ComImport, + Guid("5BA52F3B-6BF8-40FC-B476-D39C529B331E"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedWriter8 : ISymUnmanagedWriter7 { + void _VtblGap1_35(); + void UpdateSignature(Guid pdbId, uint stamp, uint age); + void SetSourceServerData(IntPtr data, uint cData); + void SetSourceLinkData(IntPtr data, uint cData); + } + + [ComVisible(true), + ComImport, + Guid("98ECEE1E-752D-11d3-8D56-00C04F680B2B"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface IPdbWriter { + void _VtblGap1_4(); + void GetSignatureAge(out uint sig, out uint age); + } + [ComVisible(true), ComImport, Guid("B01FAFEB-C450-3A4D-BEEC-B4CEEC01E006"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface ISymUnmanagedDocumentWriter { + interface ISymUnmanagedDocumentWriter { void SetSource([In] uint sourceSize, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] byte[] source); void SetCheckSum([In] Guid algorithmId, [In] uint checkSumSize, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] checkSum); } -#pragma warning restore 1591 + + [ComVisible(true), + ComImport, + Guid("FC073774-1739-4232-BD56-A027294BEC15"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ISymUnmanagedAsyncMethodPropertiesWriter { + void DefineKickoffMethod([In] uint kickoffMethod); + void DefineCatchHandlerILOffset([In] uint catchHandlerOffset); + void DefineAsyncStepInfo([In] uint count, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] yieldOffsets, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] breakpointOffset, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] breakpointMethod); + } } diff --git a/src/DotNet/Pdb/Dss/DataReaderIStream.cs b/src/DotNet/Pdb/Dss/DataReaderIStream.cs new file mode 100644 index 000000000..1733c53f2 --- /dev/null +++ b/src/DotNet/Pdb/Dss/DataReaderIStream.cs @@ -0,0 +1,130 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using dnlib.IO; + +namespace dnlib.DotNet.Pdb.Dss { + sealed class DataReaderIStream : IStream, IDisposable { + readonly DataReaderFactory dataReaderFactory; + DataReader reader; + readonly string name; + + const int STG_E_INVALIDFUNCTION = unchecked((int)0x80030001); + const int STG_E_CANTSAVE = unchecked((int)0x80030103); + + public DataReaderIStream(DataReaderFactory dataReaderFactory) + : this(dataReaderFactory, dataReaderFactory.CreateReader(), string.Empty) { + } + + DataReaderIStream(DataReaderFactory dataReaderFactory, DataReader reader, string name) { + this.dataReaderFactory = dataReaderFactory ?? throw new ArgumentNullException(nameof(dataReaderFactory)); + this.reader = reader; + this.name = name ?? string.Empty; + } + + public void Clone(out IStream ppstm) => ppstm = new DataReaderIStream(dataReaderFactory, reader, name); + + public void Commit(int grfCommitFlags) { + } + + public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten) { + if (cb > int.MaxValue) + cb = int.MaxValue; + else if (cb < 0) + cb = 0; + int sizeToRead = (int)cb; + + if ((ulong)reader.Position + (uint)sizeToRead > reader.Length) + sizeToRead = (int)(reader.Length - Math.Min(reader.Position, reader.Length)); + + var buffer = new byte[sizeToRead]; + Read(buffer, sizeToRead, pcbRead); + if (pcbRead != IntPtr.Zero) + Marshal.WriteInt64(pcbRead, Marshal.ReadInt32(pcbRead)); + pstm.Write(buffer, buffer.Length, pcbWritten); + if (pcbWritten != IntPtr.Zero) + Marshal.WriteInt64(pcbWritten, Marshal.ReadInt32(pcbWritten)); + } + + public void LockRegion(long libOffset, long cb, int dwLockType) => Marshal.ThrowExceptionForHR(STG_E_INVALIDFUNCTION); + + public void Read(byte[] pv, int cb, IntPtr pcbRead) { + if (cb < 0) + cb = 0; + + cb = (int)Math.Min(reader.BytesLeft, (uint)cb); + reader.ReadBytes(pv, 0, cb); + + if (pcbRead != IntPtr.Zero) + Marshal.WriteInt32(pcbRead, cb); + } + + public void Revert() { + } + + enum STREAM_SEEK { + SET = 0, + CUR = 1, + END = 2, + } + + public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition) { + switch ((STREAM_SEEK)dwOrigin) { + case STREAM_SEEK.SET: + reader.Position = (uint)dlibMove; + break; + + case STREAM_SEEK.CUR: + reader.Position = (uint)(reader.Position + dlibMove); + break; + + case STREAM_SEEK.END: + reader.Position = (uint)(reader.Length + dlibMove); + break; + } + + if (plibNewPosition != IntPtr.Zero) + Marshal.WriteInt64(plibNewPosition, reader.Position); + } + + public void SetSize(long libNewSize) => Marshal.ThrowExceptionForHR(STG_E_INVALIDFUNCTION); + + enum STATFLAG { + DEFAULT = 0, + NONAME = 1, + NOOPEN = 2, + } + + enum STGTY { + STORAGE = 1, + STREAM = 2, + LOCKBYTES = 3, + PROPERTY = 4, + } + + public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag) { + var s = new System.Runtime.InteropServices.ComTypes.STATSTG(); + + // s.atime = ???; + s.cbSize = reader.Length; + s.clsid = Guid.Empty; + // s.ctime = ???; + s.grfLocksSupported = 0; + s.grfMode = 0; + s.grfStateBits = 0; + // s.mtime = ???; + if ((grfStatFlag & (int)STATFLAG.NONAME) == 0) + s.pwcsName = name; + s.reserved = 0; + s.type = (int)STGTY.STREAM; + + pstatstg = s; + } + + public void UnlockRegion(long libOffset, long cb, int dwLockType) => Marshal.ThrowExceptionForHR(STG_E_INVALIDFUNCTION); + public void Write(byte[] pv, int cb, IntPtr pcbWritten) => Marshal.ThrowExceptionForHR(STG_E_CANTSAVE); + public void Dispose() { } + } +} diff --git a/src/DotNet/Pdb/Dss/ImageStreamIStream.cs b/src/DotNet/Pdb/Dss/ImageStreamIStream.cs deleted file mode 100644 index 1e0178671..000000000 --- a/src/DotNet/Pdb/Dss/ImageStreamIStream.cs +++ /dev/null @@ -1,180 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -using dnlib.IO; - -namespace dnlib.DotNet.Pdb.Dss { - /// - /// Implements and uses an as the underlying - /// stream. - /// - sealed class ImageStreamIStream : IStream, IDisposable { - readonly IImageStream stream; - readonly string name; - - const int STG_E_INVALIDFUNCTION = unchecked((int)0x80030001); - const int STG_E_CANTSAVE = unchecked((int)0x80030103); - - /// - /// User can set this to anything he/she wants. If it implements , - /// its method will get called when this instance - /// is 'd. - /// - public object UserData { get; set; } - - /// - /// Constructor - /// - /// Source stream - public ImageStreamIStream(IImageStream stream) - : this(stream, string.Empty) { - } - - /// - /// Constructor - /// - /// Source stream - /// Name of original file or null if unknown. - public ImageStreamIStream(IImageStream stream, string name) { - if (stream == null) - throw new ArgumentNullException("stream"); - this.stream = stream; - this.name = name ?? string.Empty; - } - - /// - public void Clone(out IStream ppstm) { - var newStream = stream.Clone(); - newStream.Position = stream.Position; - ppstm = new ImageStreamIStream(newStream, name); - } - - /// - public void Commit(int grfCommitFlags) { - } - - /// - public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten) { - if (cb > int.MaxValue) - cb = int.MaxValue; - else if (cb < 0) - cb = 0; - int sizeToRead = (int)cb; - - if (stream.Position + sizeToRead < sizeToRead || stream.Position + sizeToRead > stream.Length) - sizeToRead = (int)(stream.Length - Math.Min(stream.Position, stream.Length)); - - var buffer = new byte[sizeToRead]; - Read(buffer, sizeToRead, pcbRead); - if (pcbRead != null) - Marshal.WriteInt64(pcbRead, Marshal.ReadInt32(pcbRead)); - pstm.Write(buffer, buffer.Length, pcbWritten); - if (pcbWritten != null) - Marshal.WriteInt64(pcbWritten, Marshal.ReadInt32(pcbWritten)); - } - - /// - public void LockRegion(long libOffset, long cb, int dwLockType) { - Marshal.ThrowExceptionForHR(STG_E_INVALIDFUNCTION); - } - - /// - public void Read(byte[] pv, int cb, IntPtr pcbRead) { - if (cb < 0) - cb = 0; - - cb = stream.Read(pv, 0, cb); - - if (pcbRead != IntPtr.Zero) - Marshal.WriteInt32(pcbRead, cb); - } - - /// - public void Revert() { - } - - enum STREAM_SEEK { - SET = 0, - CUR = 1, - END = 2, - } - - /// - public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition) { - switch ((STREAM_SEEK)dwOrigin) { - case STREAM_SEEK.SET: - stream.Position = dlibMove; - break; - - case STREAM_SEEK.CUR: - stream.Position += dlibMove; - break; - - case STREAM_SEEK.END: - stream.Position = stream.Length + dlibMove; - break; - } - - if (plibNewPosition != IntPtr.Zero) - Marshal.WriteInt64(plibNewPosition, stream.Position); - } - - /// - public void SetSize(long libNewSize) { - Marshal.ThrowExceptionForHR(STG_E_INVALIDFUNCTION); - } - - enum STATFLAG { - DEFAULT = 0, - NONAME = 1, - NOOPEN = 2, - } - - enum STGTY { - STORAGE = 1, - STREAM = 2, - LOCKBYTES = 3, - PROPERTY = 4, - } - - /// - public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag) { - var s = new System.Runtime.InteropServices.ComTypes.STATSTG(); - - // s.atime = ???; - s.cbSize = stream.Length; - s.clsid = Guid.Empty; - // s.ctime = ???; - s.grfLocksSupported = 0; - s.grfMode = 0; - s.grfStateBits = 0; - // s.mtime = ???; - if ((grfStatFlag & (int)STATFLAG.NONAME) == 0) - s.pwcsName = name; - s.reserved = 0; - s.type = (int)STGTY.STREAM; - - pstatstg = s; - } - - /// - public void UnlockRegion(long libOffset, long cb, int dwLockType) { - Marshal.ThrowExceptionForHR(STG_E_INVALIDFUNCTION); - } - - /// - public void Write(byte[] pv, int cb, IntPtr pcbWritten) { - Marshal.ThrowExceptionForHR(STG_E_CANTSAVE); - } - - /// - public void Dispose() { - stream.Dispose(); - var id = UserData as IDisposable; - if (id != null) - id.Dispose(); - } - } -} diff --git a/src/DotNet/Pdb/Dss/MDEmitter.cs b/src/DotNet/Pdb/Dss/MDEmitter.cs index 5622ed120..5b38003fb 100644 --- a/src/DotNet/Pdb/Dss/MDEmitter.cs +++ b/src/DotNet/Pdb/Dss/MDEmitter.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; @@ -6,536 +6,138 @@ using dnlib.DotNet.Writer; namespace dnlib.DotNet.Pdb.Dss { - /// - /// Pass this instance to when writing the PDB file - /// - sealed class MDEmitter : IMetaDataImport, IMetaDataEmit { - readonly MetaData metaData; + sealed unsafe class MDEmitter : MetaDataImport, IMetaDataEmit { + readonly Metadata metadata; readonly Dictionary tokenToTypeDef; readonly Dictionary tokenToMethodDef; - /// - /// Constructor - /// - /// Metadata - public MDEmitter(MetaData metaData) { - this.metaData = metaData; + public MDEmitter(Metadata metadata) { + this.metadata = metadata; // We could get these from the metadata tables but it's just easier to get name, // declaring type etc using TypeDef and MethodDef. - tokenToTypeDef = new Dictionary(metaData.TablesHeap.TypeDefTable.Rows); - tokenToMethodDef = new Dictionary(metaData.TablesHeap.MethodTable.Rows); - foreach (var type in metaData.Module.GetTypes()) { - if (type == null) + tokenToTypeDef = new Dictionary(metadata.TablesHeap.TypeDefTable.Rows); + tokenToMethodDef = new Dictionary(metadata.TablesHeap.MethodTable.Rows); + foreach (var type in metadata.Module.GetTypes()) { + if (type is null) continue; - tokenToTypeDef.Add(new MDToken(MD.Table.TypeDef, metaData.GetRid(type)).Raw, type); + tokenToTypeDef.Add(new MDToken(MD.Table.TypeDef, metadata.GetRid(type)).Raw, type); foreach (var method in type.Methods) { - if (method == null) + if (method is null) continue; - tokenToMethodDef.Add(new MDToken(MD.Table.Method, metaData.GetRid(method)).Raw, method); + tokenToMethodDef.Add(new MDToken(MD.Table.Method, metadata.GetRid(method)).Raw, method); } } } - unsafe void IMetaDataImport.GetMethodProps(uint mb, uint* pClass, ushort* szMethod, uint cchMethod, uint* pchMethod, uint* pdwAttr, IntPtr* ppvSigBlob, uint* pcbSigBlob, uint* pulCodeRVA, uint* pdwImplFlags) { + public override void GetMethodProps(uint mb, uint* pClass, ushort* szMethod, uint cchMethod, uint* pchMethod, uint* pdwAttr, IntPtr* ppvSigBlob, uint* pcbSigBlob, uint* pulCodeRVA, uint* pdwImplFlags) { if ((mb >> 24) != 0x06) throw new ArgumentException(); var method = tokenToMethodDef[mb]; - var row = metaData.TablesHeap.MethodTable[mb & 0x00FFFFFF]; + var row = metadata.TablesHeap.MethodTable[mb & 0x00FFFFFF]; - if (pClass != null) - *pClass = new MDToken(MD.Table.TypeDef, metaData.GetRid(method.DeclaringType)).Raw; - if (pdwAttr != null) + if (pClass is not null) + *pClass = new MDToken(MD.Table.TypeDef, metadata.GetRid(method.DeclaringType)).Raw; + if (pdwAttr is not null) *pdwAttr = row.Flags; - if (ppvSigBlob != null) + if (ppvSigBlob is not null) *ppvSigBlob = IntPtr.Zero; - if (pcbSigBlob != null) + if (pcbSigBlob is not null) *pcbSigBlob = 0; - if (pulCodeRVA != null) + if (pulCodeRVA is not null) *pulCodeRVA = row.RVA; - if (pdwImplFlags != null) + if (pdwImplFlags is not null) *pdwImplFlags = row.ImplFlags; string name = method.Name.String ?? string.Empty; int len = (int)Math.Min((uint)name.Length + 1, cchMethod); - if (szMethod != null) { + if (szMethod is not null) { for (int i = 0; i < len - 1; i++, szMethod++) *szMethod = (ushort)name[i]; if (len > 0) *szMethod = 0; } - if (pchMethod != null) + if (pchMethod is not null) *pchMethod = (uint)len; } - unsafe void IMetaDataImport.GetTypeDefProps(uint td, ushort* szTypeDef, uint cchTypeDef, uint* pchTypeDef, uint* pdwTypeDefFlags, uint* ptkExtends) { + public override void GetTypeDefProps(uint td, ushort* szTypeDef, uint cchTypeDef, uint* pchTypeDef, uint* pdwTypeDefFlags, uint* ptkExtends) { if ((td >> 24) != 0x02) throw new ArgumentException(); var type = tokenToTypeDef[td]; - var row = metaData.TablesHeap.TypeDefTable[td & 0x00FFFFFF]; - if (pdwTypeDefFlags != null) + var row = metadata.TablesHeap.TypeDefTable[td & 0x00FFFFFF]; + if (pdwTypeDefFlags is not null) *pdwTypeDefFlags = row.Flags; - if (ptkExtends != null) + if (ptkExtends is not null) *ptkExtends = row.Extends; - - string name = type.Name.String ?? string.Empty; - int len = (int)Math.Min((uint)name.Length + 1, cchTypeDef); - if (szTypeDef != null) { - for (int i = 0; i < len - 1; i++, szTypeDef++) - *szTypeDef = (ushort)name[i]; - if (len > 0) - *szTypeDef = 0; - } - if (pchTypeDef != null) - *pchTypeDef = (uint)len; + CopyTypeName(type.Namespace, type.Name, szTypeDef, cchTypeDef, pchTypeDef); } - unsafe void IMetaDataImport.GetNestedClassProps(uint tdNestedClass, uint* ptdEnclosingClass) { + public override void GetNestedClassProps(uint tdNestedClass, uint* ptdEnclosingClass) { if ((tdNestedClass >> 24) != 0x02) throw new ArgumentException(); var type = tokenToTypeDef[tdNestedClass]; var declType = type.DeclaringType; - if (ptdEnclosingClass != null) { - if (declType == null) + if (ptdEnclosingClass is not null) { + if (declType is null) *ptdEnclosingClass = 0; else - *ptdEnclosingClass = new MDToken(MD.Table.TypeDef, metaData.GetRid(declType)).Raw; + *ptdEnclosingClass = new MDToken(MD.Table.TypeDef, metadata.GetRid(declType)).Raw; } } - // The rest of the methods aren't called - - void IMetaDataImport.CloseEnum(IntPtr hEnum) { - throw new NotImplementedException(); - } - - void IMetaDataImport.CountEnum(IntPtr hEnum, ref uint pulCount) { - throw new NotImplementedException(); - } - - void IMetaDataImport.ResetEnum(IntPtr hEnum, uint ulPos) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumTypeDefs(IntPtr phEnum, uint[] rTypeDefs, uint cMax, out uint pcTypeDefs) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumInterfaceImpls(ref IntPtr phEnum, uint td, uint[] rImpls, uint cMax, ref uint pcImpls) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumTypeRefs(ref IntPtr phEnum, uint[] rTypeRefs, uint cMax, ref uint pcTypeRefs) { - throw new NotImplementedException(); - } - - void IMetaDataImport.FindTypeDefByName(string szTypeDef, uint tkEnclosingClass, out uint ptd) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetScopeProps(IntPtr szName, uint cchName, out uint pchName, out Guid pmvid) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetModuleFromScope(out uint pmd) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetInterfaceImplProps(uint iiImpl, out uint pClass, out uint ptkIface) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetTypeRefProps(uint tr, out uint ptkResolutionScope, IntPtr szName, uint cchName, out uint pchName) { - throw new NotImplementedException(); - } - - void IMetaDataImport.ResolveTypeRef(uint tr, ref Guid riid, out object ppIScope, out uint ptd) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumMembers(ref IntPtr phEnum, uint cl, uint[] rMembers, uint cMax, out uint pcTokens) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumMembersWithName(ref IntPtr phEnum, uint cl, string szName, uint[] rMembers, uint cMax, out uint pcTokens) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumMethods(ref IntPtr phEnum, uint cl, uint[] rMethods, uint cMax, out uint pcTokens) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumMethodsWithName(ref IntPtr phEnum, uint cl, string szName, uint[] rMethods, uint cMax, out uint pcTokens) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumFields(ref IntPtr phEnum, uint cl, uint[] rFields, uint cMax, out uint pcTokens) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumFieldsWithName(ref IntPtr phEnum, uint cl, string szName, uint[] rFields, uint cMax, out uint pcTokens) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumParams(ref IntPtr phEnum, uint mb, uint[] rParams, uint cMax, out uint pcTokens) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumMemberRefs(ref IntPtr phEnum, uint tkParent, uint[] rMemberRefs, uint cMax, out uint pcTokens) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumMethodImpls(ref IntPtr phEnum, uint td, uint[] rMethodBody, uint[] rMethodDecl, uint cMax, out uint pcTokens) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumPermissionSets(ref IntPtr phEnum, uint tk, uint dwActions, uint[] rPermission, uint cMax, out uint pcTokens) { - throw new NotImplementedException(); - } - - void IMetaDataImport.FindMember(uint td, string szName, IntPtr pvSigBlob, uint cbSigBlob, out uint pmb) { - throw new NotImplementedException(); - } - - void IMetaDataImport.FindMethod(uint td, string szName, IntPtr pvSigBlob, uint cbSigBlob, out uint pmb) { - throw new NotImplementedException(); - } - - void IMetaDataImport.FindField(uint td, string szName, IntPtr pvSigBlob, uint cbSigBlob, out uint pmb) { - throw new NotImplementedException(); - } - - void IMetaDataImport.FindMemberRef(uint td, string szName, IntPtr pvSigBlob, uint cbSigBlob, out uint pmr) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetMemberRefProps(uint mr, out uint ptk, IntPtr szMember, uint cchMember, out uint pchMember, out IntPtr ppvSigBlob, out uint pbSig) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumProperties(ref IntPtr phEnum, uint td, uint[] rProperties, uint cMax, out uint pcProperties) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumEvents(ref IntPtr phEnum, uint td, uint[] rEvents, uint cMax, out uint pcEvents) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetEventProps(uint ev, out uint pClass, string szEvent, uint cchEvent, out uint pchEvent, out uint pdwEventFlags, out uint ptkEventType, out uint pmdAddOn, out uint pmdRemoveOn, out uint pmdFire, uint[] rmdOtherMethod, uint cMax, out uint pcOtherMethod) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumMethodSemantics(ref IntPtr phEnum, uint mb, uint[] rEventProp, uint cMax, out uint pcEventProp) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetMethodSemantics(uint mb, uint tkEventProp, out uint pdwSemanticsFlags) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetClassLayout(uint td, out uint pdwPackSize, out IntPtr rFieldOffset, uint cMax, out uint pcFieldOffset, out uint pulClassSize) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetFieldMarshal(uint tk, out IntPtr ppvNativeType, out uint pcbNativeType) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetRVA(uint tk, out uint pulCodeRVA, out uint pdwImplFlags) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetPermissionSetProps(uint pm, out uint pdwAction, out IntPtr ppvPermission, out uint pcbPermission) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetSigFromToken(uint mdSig, out IntPtr ppvSig, out uint pcbSig) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetModuleRefProps(uint mur, IntPtr szName, uint cchName, out uint pchName) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumModuleRefs(ref IntPtr phEnum, uint[] rModuleRefs, uint cmax, out uint pcModuleRefs) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetTypeSpecFromToken(uint typespec, out IntPtr ppvSig, out uint pcbSig) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetNameFromToken(uint tk, out IntPtr pszUtf8NamePtr) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumUnresolvedMethods(ref IntPtr phEnum, uint[] rMethods, uint cMax, out uint pcTokens) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetUserString(uint stk, IntPtr szString, uint cchString, out uint pchString) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetPinvokeMap(uint tk, out uint pdwMappingFlags, IntPtr szImportName, uint cchImportName, out uint pchImportName, out uint pmrImportDLL) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumSignatures(ref IntPtr phEnum, uint[] rSignatures, uint cmax, out uint pcSignatures) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumTypeSpecs(ref IntPtr phEnum, uint[] rTypeSpecs, uint cmax, out uint pcTypeSpecs) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumUserStrings(ref IntPtr phEnum, uint[] rStrings, uint cmax, out uint pcStrings) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetParamForMethodIndex(uint md, uint ulParamSeq, out uint ppd) { - throw new NotImplementedException(); - } - - void IMetaDataImport.EnumCustomAttributes(IntPtr phEnum, uint tk, uint tkType, uint[] rCustomAttributes, uint cMax, out uint pcCustomAttributes) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetCustomAttributeProps(uint cv, out uint ptkObj, out uint ptkType, out IntPtr ppBlob, out uint pcbSize) { - throw new NotImplementedException(); - } - - void IMetaDataImport.FindTypeRef(uint tkResolutionScope, string szName, out uint ptr) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetMemberProps(uint mb, out uint pClass, IntPtr szMember, uint cchMember, out uint pchMember, out uint pdwAttr, out IntPtr ppvSigBlob, out uint pcbSigBlob, out uint pulCodeRVA, out uint pdwImplFlags, out uint pdwCPlusTypeFlag, out IntPtr ppValue, out uint pcchValue) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetFieldProps(uint mb, out uint pClass, IntPtr szField, uint cchField, out uint pchField, out uint pdwAttr, out IntPtr ppvSigBlob, out uint pcbSigBlob, out uint pdwCPlusTypeFlag, out IntPtr ppValue, out uint pcchValue) { - throw new NotImplementedException(); - } + void IMetaDataEmit.GetTokenFromSig(IntPtr pvSig, uint cbSig, out uint pmsig) => pmsig = 0x11000000; - void IMetaDataImport.GetPropertyProps(uint prop, out uint pClass, IntPtr szProperty, uint cchProperty, out uint pchProperty, out uint pdwPropFlags, out IntPtr ppvSig, out uint pbSig, out uint pdwCPlusTypeFlag, out IntPtr ppDefaultValue, out uint pcchDefaultValue, out uint pmdSetter, out uint pmdGetter, uint[] rmdOtherMethod, uint cMax, out uint pcOtherMethod) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetParamProps(uint tk, out uint pmd, out uint pulSequence, IntPtr szName, uint cchName, out uint pchName, out uint pdwAttr, out uint pdwCPlusTypeFlag, out IntPtr ppValue, out uint pcchValue) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetCustomAttributeByName(uint tkObj, string szName, out IntPtr ppData, out uint pcbData) { - throw new NotImplementedException(); - } - - bool IMetaDataImport.IsValidToken(uint tk) { - throw new NotImplementedException(); - } - - void IMetaDataImport.GetNativeCallConvFromSig(IntPtr pvSig, uint cbSig, out uint pCallConv) { - throw new NotImplementedException(); - } - - void IMetaDataImport.IsGlobal(uint pd, out int pbGlobal) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetModuleProps(string szName) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.Save(string szFile, uint dwSaveFlags) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SaveToStream(IStream pIStream, uint dwSaveFlags) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.GetSaveSize(int fSave, out uint pdwSaveSize) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineTypeDef(string szTypeDef, uint dwTypeDefFlags, uint tkExtends, uint[] rtkImplements, out uint ptd) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineNestedType(string szTypeDef, uint dwTypeDefFlags, uint tkExtends, uint[] rtkImplements, uint tdEncloser, out uint ptd) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetHandler(object pUnk) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineMethod(uint td, string szName, uint dwMethodFlags, IntPtr pvSigBlob, uint cbSigBlob, uint ulCodeRVA, uint dwImplFlags, out uint pmd) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineMethodImpl(uint td, uint tkBody, uint tkDecl) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineTypeRefByName(uint tkResolutionScope, string szName, out uint ptr) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineImportType(IntPtr pAssemImport, IntPtr pbHashValue, uint cbHashValue, IMetaDataImport pImport, uint tdImport, IntPtr pAssemEmit, out uint ptr) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineMemberRef(uint tkImport, string szName, IntPtr pvSigBlob, uint cbSigBlob, out uint pmr) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineImportMember(IntPtr pAssemImport, IntPtr pbHashValue, uint cbHashValue, IMetaDataImport pImport, uint mbMember, IntPtr pAssemEmit, uint tkParent, out uint pmr) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineEvent(uint td, string szEvent, uint dwEventFlags, uint tkEventType, uint mdAddOn, uint mdRemoveOn, uint mdFire, uint[] rmdOtherMethods, out uint pmdEvent) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetClassLayout(uint td, uint dwPackSize, IntPtr rFieldOffsets, uint ulClassSize) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DeleteClassLayout(uint td) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetFieldMarshal(uint tk, IntPtr pvNativeType, uint cbNativeType) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DeleteFieldMarshal(uint tk) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefinePermissionSet(uint tk, uint dwAction, IntPtr pvPermission, uint cbPermission, out uint ppm) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetRVA(uint md, uint ulRVA) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.GetTokenFromSig(IntPtr pvSig, uint cbSig, out uint pmsig) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineModuleRef(string szName, out uint pmur) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetParent(uint mr, uint tk) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.GetTokenFromTypeSpec(IntPtr pvSig, uint cbSig, out uint ptypespec) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SaveToMemory(out IntPtr pbData, uint cbData) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineUserString(string szString, uint cchString, out uint pstk) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DeleteToken(uint tkObj) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetMethodProps(uint md, uint dwMethodFlags, uint ulCodeRVA, uint dwImplFlags) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetTypeDefProps(uint td, uint dwTypeDefFlags, uint tkExtends, uint[] rtkImplements) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetEventProps(uint ev, uint dwEventFlags, uint tkEventType, uint mdAddOn, uint mdRemoveOn, uint mdFire, uint[] rmdOtherMethods) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetPermissionSetProps(uint tk, uint dwAction, IntPtr pvPermission, uint cbPermission, out uint ppm) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefinePinvokeMap(uint tk, uint dwMappingFlags, string szImportName, uint mrImportDLL) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetPinvokeMap(uint tk, uint dwMappingFlags, string szImportName, uint mrImportDLL) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DeletePinvokeMap(uint tk) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineCustomAttribute(uint tkOwner, uint tkCtor, IntPtr pCustomAttribute, uint cbCustomAttribute, out uint pcv) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetCustomAttributeValue(uint pcv, IntPtr pCustomAttribute, uint cbCustomAttribute) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineField(uint td, string szName, uint dwFieldFlags, IntPtr pvSigBlob, uint cbSigBlob, uint dwCPlusTypeFlag, IntPtr pValue, uint cchValue, out uint pmd) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineProperty(uint td, string szProperty, uint dwPropFlags, IntPtr pvSig, uint cbSig, uint dwCPlusTypeFlag, IntPtr pValue, uint cchValue, uint mdSetter, uint mdGetter, uint[] rmdOtherMethods, out uint pmdProp) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineParam(uint md, uint ulParamSeq, string szName, uint dwParamFlags, uint dwCPlusTypeFlag, IntPtr pValue, uint cchValue, out uint ppd) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetFieldProps(uint fd, uint dwFieldFlags, uint dwCPlusTypeFlag, IntPtr pValue, uint cchValue) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetPropertyProps(uint pr, uint dwPropFlags, uint dwCPlusTypeFlag, IntPtr pValue, uint cchValue, uint mdSetter, uint mdGetter, uint[] rmdOtherMethods) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetParamProps(uint pd, string szName, uint dwParamFlags, uint dwCPlusTypeFlag, IntPtr pValue, uint cchValue) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.DefineSecurityAttributeSet(uint tkObj, IntPtr rSecAttrs, uint cSecAttrs, out uint pulErrorAttr) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.ApplyEditAndContinue(object pImport) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.TranslateSigWithScope(IntPtr pAssemImport, IntPtr pbHashValue, uint cbHashValue, IMetaDataImport import, IntPtr pbSigBlob, uint cbSigBlob, IntPtr pAssemEmit, IMetaDataEmit emit, IntPtr pvTranslatedSig, uint cbTranslatedSigMax, out uint pcbTranslatedSig) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetMethodImplFlags(uint md, uint dwImplFlags) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.SetFieldRVA(uint fd, uint ulRVA) { - throw new NotImplementedException(); - } - - void IMetaDataEmit.Merge(IMetaDataImport pImport, IntPtr pHostMapToken, object pHandler) { - throw new NotImplementedException(); - } + // The rest of the methods aren't called - void IMetaDataEmit.MergeEnd() { - throw new NotImplementedException(); - } + void IMetaDataEmit.SetModuleProps(string szName) => throw new NotImplementedException(); + void IMetaDataEmit.Save(string szFile, uint dwSaveFlags) => throw new NotImplementedException(); + void IMetaDataEmit.SaveToStream(IStream pIStream, uint dwSaveFlags) => throw new NotImplementedException(); + void IMetaDataEmit.GetSaveSize(int fSave, out uint pdwSaveSize) => throw new NotImplementedException(); + void IMetaDataEmit.DefineTypeDef(string szTypeDef, uint dwTypeDefFlags, uint tkExtends, uint[] rtkImplements, out uint ptd) => throw new NotImplementedException(); + void IMetaDataEmit.DefineNestedType(string szTypeDef, uint dwTypeDefFlags, uint tkExtends, uint[] rtkImplements, uint tdEncloser, out uint ptd) => throw new NotImplementedException(); + void IMetaDataEmit.SetHandler(object pUnk) => throw new NotImplementedException(); + void IMetaDataEmit.DefineMethod(uint td, string szName, uint dwMethodFlags, IntPtr pvSigBlob, uint cbSigBlob, uint ulCodeRVA, uint dwImplFlags, out uint pmd) => throw new NotImplementedException(); + void IMetaDataEmit.DefineMethodImpl(uint td, uint tkBody, uint tkDecl) => throw new NotImplementedException(); + void IMetaDataEmit.DefineTypeRefByName(uint tkResolutionScope, string szName, out uint ptr) => throw new NotImplementedException(); + void IMetaDataEmit.DefineImportType(IntPtr pAssemImport, IntPtr pbHashValue, uint cbHashValue, IMetaDataImport pImport, uint tdImport, IntPtr pAssemEmit, out uint ptr) => throw new NotImplementedException(); + void IMetaDataEmit.DefineMemberRef(uint tkImport, string szName, IntPtr pvSigBlob, uint cbSigBlob, out uint pmr) => throw new NotImplementedException(); + void IMetaDataEmit.DefineImportMember(IntPtr pAssemImport, IntPtr pbHashValue, uint cbHashValue, IMetaDataImport pImport, uint mbMember, IntPtr pAssemEmit, uint tkParent, out uint pmr) => throw new NotImplementedException(); + void IMetaDataEmit.DefineEvent(uint td, string szEvent, uint dwEventFlags, uint tkEventType, uint mdAddOn, uint mdRemoveOn, uint mdFire, uint[] rmdOtherMethods, out uint pmdEvent) => throw new NotImplementedException(); + void IMetaDataEmit.SetClassLayout(uint td, uint dwPackSize, IntPtr rFieldOffsets, uint ulClassSize) => throw new NotImplementedException(); + void IMetaDataEmit.DeleteClassLayout(uint td) => throw new NotImplementedException(); + void IMetaDataEmit.SetFieldMarshal(uint tk, IntPtr pvNativeType, uint cbNativeType) => throw new NotImplementedException(); + void IMetaDataEmit.DeleteFieldMarshal(uint tk) => throw new NotImplementedException(); + void IMetaDataEmit.DefinePermissionSet(uint tk, uint dwAction, IntPtr pvPermission, uint cbPermission, out uint ppm) => throw new NotImplementedException(); + void IMetaDataEmit.SetRVA(uint md, uint ulRVA) => throw new NotImplementedException(); + void IMetaDataEmit.DefineModuleRef(string szName, out uint pmur) => throw new NotImplementedException(); + void IMetaDataEmit.SetParent(uint mr, uint tk) => throw new NotImplementedException(); + void IMetaDataEmit.GetTokenFromTypeSpec(IntPtr pvSig, uint cbSig, out uint ptypespec) => throw new NotImplementedException(); + void IMetaDataEmit.SaveToMemory(out IntPtr pbData, uint cbData) => throw new NotImplementedException(); + void IMetaDataEmit.DefineUserString(string szString, uint cchString, out uint pstk) => throw new NotImplementedException(); + void IMetaDataEmit.DeleteToken(uint tkObj) => throw new NotImplementedException(); + void IMetaDataEmit.SetMethodProps(uint md, uint dwMethodFlags, uint ulCodeRVA, uint dwImplFlags) => throw new NotImplementedException(); + void IMetaDataEmit.SetTypeDefProps(uint td, uint dwTypeDefFlags, uint tkExtends, uint[] rtkImplements) => throw new NotImplementedException(); + void IMetaDataEmit.SetEventProps(uint ev, uint dwEventFlags, uint tkEventType, uint mdAddOn, uint mdRemoveOn, uint mdFire, uint[] rmdOtherMethods) => throw new NotImplementedException(); + void IMetaDataEmit.SetPermissionSetProps(uint tk, uint dwAction, IntPtr pvPermission, uint cbPermission, out uint ppm) => throw new NotImplementedException(); + void IMetaDataEmit.DefinePinvokeMap(uint tk, uint dwMappingFlags, string szImportName, uint mrImportDLL) => throw new NotImplementedException(); + void IMetaDataEmit.SetPinvokeMap(uint tk, uint dwMappingFlags, string szImportName, uint mrImportDLL) => throw new NotImplementedException(); + void IMetaDataEmit.DeletePinvokeMap(uint tk) => throw new NotImplementedException(); + void IMetaDataEmit.DefineCustomAttribute(uint tkOwner, uint tkCtor, IntPtr pCustomAttribute, uint cbCustomAttribute, out uint pcv) => throw new NotImplementedException(); + void IMetaDataEmit.SetCustomAttributeValue(uint pcv, IntPtr pCustomAttribute, uint cbCustomAttribute) => throw new NotImplementedException(); + void IMetaDataEmit.DefineField(uint td, string szName, uint dwFieldFlags, IntPtr pvSigBlob, uint cbSigBlob, uint dwCPlusTypeFlag, IntPtr pValue, uint cchValue, out uint pmd) => throw new NotImplementedException(); + void IMetaDataEmit.DefineProperty(uint td, string szProperty, uint dwPropFlags, IntPtr pvSig, uint cbSig, uint dwCPlusTypeFlag, IntPtr pValue, uint cchValue, uint mdSetter, uint mdGetter, uint[] rmdOtherMethods, out uint pmdProp) => throw new NotImplementedException(); + void IMetaDataEmit.DefineParam(uint md, uint ulParamSeq, string szName, uint dwParamFlags, uint dwCPlusTypeFlag, IntPtr pValue, uint cchValue, out uint ppd) => throw new NotImplementedException(); + void IMetaDataEmit.SetFieldProps(uint fd, uint dwFieldFlags, uint dwCPlusTypeFlag, IntPtr pValue, uint cchValue) => throw new NotImplementedException(); + void IMetaDataEmit.SetPropertyProps(uint pr, uint dwPropFlags, uint dwCPlusTypeFlag, IntPtr pValue, uint cchValue, uint mdSetter, uint mdGetter, uint[] rmdOtherMethods) => throw new NotImplementedException(); + void IMetaDataEmit.SetParamProps(uint pd, string szName, uint dwParamFlags, uint dwCPlusTypeFlag, IntPtr pValue, uint cchValue) => throw new NotImplementedException(); + void IMetaDataEmit.DefineSecurityAttributeSet(uint tkObj, IntPtr rSecAttrs, uint cSecAttrs, out uint pulErrorAttr) => throw new NotImplementedException(); + void IMetaDataEmit.ApplyEditAndContinue(object pImport) => throw new NotImplementedException(); + void IMetaDataEmit.TranslateSigWithScope(IntPtr pAssemImport, IntPtr pbHashValue, uint cbHashValue, IMetaDataImport import, IntPtr pbSigBlob, uint cbSigBlob, IntPtr pAssemEmit, IMetaDataEmit emit, IntPtr pvTranslatedSig, uint cbTranslatedSigMax, out uint pcbTranslatedSig) => throw new NotImplementedException(); + void IMetaDataEmit.SetMethodImplFlags(uint md, uint dwImplFlags) => throw new NotImplementedException(); + void IMetaDataEmit.SetFieldRVA(uint fd, uint ulRVA) => throw new NotImplementedException(); + void IMetaDataEmit.Merge(IMetaDataImport pImport, IntPtr pHostMapToken, object pHandler) => throw new NotImplementedException(); + void IMetaDataEmit.MergeEnd() => throw new NotImplementedException(); } } diff --git a/src/DotNet/Pdb/Dss/MetaDataImport.cs b/src/DotNet/Pdb/Dss/MetaDataImport.cs new file mode 100644 index 000000000..da7f827c2 --- /dev/null +++ b/src/DotNet/Pdb/Dss/MetaDataImport.cs @@ -0,0 +1,106 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace dnlib.DotNet.Pdb.Dss { + unsafe abstract class MetaDataImport : IMetaDataImport { + public virtual void GetTypeDefProps([In] uint td, [In] ushort* szTypeDef, [In] uint cchTypeDef, [Out] uint* pchTypeDef, [Out] uint* pdwTypeDefFlags, [Out] uint* ptkExtends) => throw new NotImplementedException(); + public virtual void GetMethodProps(uint mb, uint* pClass, [In] ushort* szMethod, uint cchMethod, uint* pchMethod, uint* pdwAttr, [Out] IntPtr* ppvSigBlob, [Out] uint* pcbSigBlob, [Out] uint* pulCodeRVA, [Out] uint* pdwImplFlags) => throw new NotImplementedException(); + public virtual void GetNestedClassProps([In] uint tdNestedClass, [Out] uint* ptdEnclosingClass) => throw new NotImplementedException(); + public virtual void GetSigFromToken(uint mdSig, byte** ppvSig, uint* pcbSig) => throw new NotImplementedException(); + public virtual void GetTypeRefProps(uint tr, uint* ptkResolutionScope, ushort* szName, uint cchName, uint* pchName) => throw new NotImplementedException(); + + protected void CopyTypeName(string typeNamespace, string typeName, ushort* destBuffer, uint destBufferLen, uint* requiredLength) { + if (typeName is null) + typeName = string.Empty; + if (typeNamespace is null) + typeNamespace = string.Empty; + + if (destBuffer is not null && destBufferLen > 0) { + uint maxChars = destBufferLen - 1; + uint w = 0; + if (typeNamespace.Length > 0) { + for (int i = 0; i < typeNamespace.Length && w < maxChars; i++, w++) + *destBuffer++ = typeNamespace[i]; + if (w < maxChars) { + *destBuffer++ = '.'; + w++; + } + } + for (int i = 0; i < typeName.Length && w < maxChars; i++, w++) + *destBuffer++ = typeName[i]; + Debug.Assert(w < destBufferLen); + *destBuffer = 0; + } + + if (requiredLength is not null) { + int totalLen = typeNamespace.Length == 0 ? typeName.Length : typeNamespace.Length + 1 + typeName.Length; + int copyLen = Math.Min(totalLen, (int)Math.Min(int.MaxValue, destBufferLen == 0 ? 0 : destBufferLen - 1)); + if (destBuffer is not null) + *requiredLength = (uint)copyLen; + else + *requiredLength = (uint)totalLen; + } + } + + void IMetaDataImport.CloseEnum(IntPtr hEnum) => throw new NotImplementedException(); + void IMetaDataImport.CountEnum(IntPtr hEnum, ref uint pulCount) => throw new NotImplementedException(); + void IMetaDataImport.ResetEnum(IntPtr hEnum, uint ulPos) => throw new NotImplementedException(); + void IMetaDataImport.EnumTypeDefs(IntPtr phEnum, uint[] rTypeDefs, uint cMax, out uint pcTypeDefs) => throw new NotImplementedException(); + void IMetaDataImport.EnumInterfaceImpls(ref IntPtr phEnum, uint td, uint[] rImpls, uint cMax, ref uint pcImpls) => throw new NotImplementedException(); + void IMetaDataImport.EnumTypeRefs(ref IntPtr phEnum, uint[] rTypeRefs, uint cMax, ref uint pcTypeRefs) => throw new NotImplementedException(); + void IMetaDataImport.FindTypeDefByName(string szTypeDef, uint tkEnclosingClass, out uint ptd) => throw new NotImplementedException(); + void IMetaDataImport.GetScopeProps(IntPtr szName, uint cchName, out uint pchName, out Guid pmvid) => throw new NotImplementedException(); + void IMetaDataImport.GetModuleFromScope(out uint pmd) => throw new NotImplementedException(); + void IMetaDataImport.GetInterfaceImplProps(uint iiImpl, out uint pClass, out uint ptkIface) => throw new NotImplementedException(); + void IMetaDataImport.ResolveTypeRef(uint tr, ref Guid riid, out object ppIScope, out uint ptd) => throw new NotImplementedException(); + void IMetaDataImport.EnumMembers(ref IntPtr phEnum, uint cl, uint[] rMembers, uint cMax, out uint pcTokens) => throw new NotImplementedException(); + void IMetaDataImport.EnumMembersWithName(ref IntPtr phEnum, uint cl, string szName, uint[] rMembers, uint cMax, out uint pcTokens) => throw new NotImplementedException(); + void IMetaDataImport.EnumMethods(ref IntPtr phEnum, uint cl, uint[] rMethods, uint cMax, out uint pcTokens) => throw new NotImplementedException(); + void IMetaDataImport.EnumMethodsWithName(ref IntPtr phEnum, uint cl, string szName, uint[] rMethods, uint cMax, out uint pcTokens) => throw new NotImplementedException(); + void IMetaDataImport.EnumFields(ref IntPtr phEnum, uint cl, uint[] rFields, uint cMax, out uint pcTokens) => throw new NotImplementedException(); + void IMetaDataImport.EnumFieldsWithName(ref IntPtr phEnum, uint cl, string szName, uint[] rFields, uint cMax, out uint pcTokens) => throw new NotImplementedException(); + void IMetaDataImport.EnumParams(ref IntPtr phEnum, uint mb, uint[] rParams, uint cMax, out uint pcTokens) => throw new NotImplementedException(); + void IMetaDataImport.EnumMemberRefs(ref IntPtr phEnum, uint tkParent, uint[] rMemberRefs, uint cMax, out uint pcTokens) => throw new NotImplementedException(); + void IMetaDataImport.EnumMethodImpls(ref IntPtr phEnum, uint td, uint[] rMethodBody, uint[] rMethodDecl, uint cMax, out uint pcTokens) => throw new NotImplementedException(); + void IMetaDataImport.EnumPermissionSets(ref IntPtr phEnum, uint tk, uint dwActions, uint[] rPermission, uint cMax, out uint pcTokens) => throw new NotImplementedException(); + void IMetaDataImport.FindMember(uint td, string szName, IntPtr pvSigBlob, uint cbSigBlob, out uint pmb) => throw new NotImplementedException(); + void IMetaDataImport.FindMethod(uint td, string szName, IntPtr pvSigBlob, uint cbSigBlob, out uint pmb) => throw new NotImplementedException(); + void IMetaDataImport.FindField(uint td, string szName, IntPtr pvSigBlob, uint cbSigBlob, out uint pmb) => throw new NotImplementedException(); + void IMetaDataImport.FindMemberRef(uint td, string szName, IntPtr pvSigBlob, uint cbSigBlob, out uint pmr) => throw new NotImplementedException(); + void IMetaDataImport.GetMemberRefProps(uint mr, out uint ptk, IntPtr szMember, uint cchMember, out uint pchMember, out IntPtr ppvSigBlob, out uint pbSig) => throw new NotImplementedException(); + void IMetaDataImport.EnumProperties(ref IntPtr phEnum, uint td, uint[] rProperties, uint cMax, out uint pcProperties) => throw new NotImplementedException(); + void IMetaDataImport.EnumEvents(ref IntPtr phEnum, uint td, uint[] rEvents, uint cMax, out uint pcEvents) => throw new NotImplementedException(); + void IMetaDataImport.GetEventProps(uint ev, out uint pClass, string szEvent, uint cchEvent, out uint pchEvent, out uint pdwEventFlags, out uint ptkEventType, out uint pmdAddOn, out uint pmdRemoveOn, out uint pmdFire, uint[] rmdOtherMethod, uint cMax, out uint pcOtherMethod) => throw new NotImplementedException(); + void IMetaDataImport.EnumMethodSemantics(ref IntPtr phEnum, uint mb, uint[] rEventProp, uint cMax, out uint pcEventProp) => throw new NotImplementedException(); + void IMetaDataImport.GetMethodSemantics(uint mb, uint tkEventProp, out uint pdwSemanticsFlags) => throw new NotImplementedException(); + void IMetaDataImport.GetClassLayout(uint td, out uint pdwPackSize, out IntPtr rFieldOffset, uint cMax, out uint pcFieldOffset, out uint pulClassSize) => throw new NotImplementedException(); + void IMetaDataImport.GetFieldMarshal(uint tk, out IntPtr ppvNativeType, out uint pcbNativeType) => throw new NotImplementedException(); + void IMetaDataImport.GetRVA(uint tk, out uint pulCodeRVA, out uint pdwImplFlags) => throw new NotImplementedException(); + void IMetaDataImport.GetPermissionSetProps(uint pm, out uint pdwAction, out IntPtr ppvPermission, out uint pcbPermission) => throw new NotImplementedException(); + void IMetaDataImport.GetModuleRefProps(uint mur, IntPtr szName, uint cchName, out uint pchName) => throw new NotImplementedException(); + void IMetaDataImport.EnumModuleRefs(ref IntPtr phEnum, uint[] rModuleRefs, uint cmax, out uint pcModuleRefs) => throw new NotImplementedException(); + void IMetaDataImport.GetTypeSpecFromToken(uint typespec, out IntPtr ppvSig, out uint pcbSig) => throw new NotImplementedException(); + void IMetaDataImport.GetNameFromToken(uint tk, out IntPtr pszUtf8NamePtr) => throw new NotImplementedException(); + void IMetaDataImport.EnumUnresolvedMethods(ref IntPtr phEnum, uint[] rMethods, uint cMax, out uint pcTokens) => throw new NotImplementedException(); + void IMetaDataImport.GetUserString(uint stk, IntPtr szString, uint cchString, out uint pchString) => throw new NotImplementedException(); + void IMetaDataImport.GetPinvokeMap(uint tk, out uint pdwMappingFlags, IntPtr szImportName, uint cchImportName, out uint pchImportName, out uint pmrImportDLL) => throw new NotImplementedException(); + void IMetaDataImport.EnumSignatures(ref IntPtr phEnum, uint[] rSignatures, uint cmax, out uint pcSignatures) => throw new NotImplementedException(); + void IMetaDataImport.EnumTypeSpecs(ref IntPtr phEnum, uint[] rTypeSpecs, uint cmax, out uint pcTypeSpecs) => throw new NotImplementedException(); + void IMetaDataImport.EnumUserStrings(ref IntPtr phEnum, uint[] rStrings, uint cmax, out uint pcStrings) => throw new NotImplementedException(); + void IMetaDataImport.GetParamForMethodIndex(uint md, uint ulParamSeq, out uint ppd) => throw new NotImplementedException(); + void IMetaDataImport.EnumCustomAttributes(IntPtr phEnum, uint tk, uint tkType, uint[] rCustomAttributes, uint cMax, out uint pcCustomAttributes) => throw new NotImplementedException(); + void IMetaDataImport.GetCustomAttributeProps(uint cv, out uint ptkObj, out uint ptkType, out IntPtr ppBlob, out uint pcbSize) => throw new NotImplementedException(); + void IMetaDataImport.FindTypeRef(uint tkResolutionScope, string szName, out uint ptr) => throw new NotImplementedException(); + void IMetaDataImport.GetMemberProps(uint mb, out uint pClass, IntPtr szMember, uint cchMember, out uint pchMember, out uint pdwAttr, out IntPtr ppvSigBlob, out uint pcbSigBlob, out uint pulCodeRVA, out uint pdwImplFlags, out uint pdwCPlusTypeFlag, out IntPtr ppValue, out uint pcchValue) => throw new NotImplementedException(); + void IMetaDataImport.GetFieldProps(uint mb, out uint pClass, IntPtr szField, uint cchField, out uint pchField, out uint pdwAttr, out IntPtr ppvSigBlob, out uint pcbSigBlob, out uint pdwCPlusTypeFlag, out IntPtr ppValue, out uint pcchValue) => throw new NotImplementedException(); + void IMetaDataImport.GetPropertyProps(uint prop, out uint pClass, IntPtr szProperty, uint cchProperty, out uint pchProperty, out uint pdwPropFlags, out IntPtr ppvSig, out uint pbSig, out uint pdwCPlusTypeFlag, out IntPtr ppDefaultValue, out uint pcchDefaultValue, out uint pmdSetter, out uint pmdGetter, uint[] rmdOtherMethod, uint cMax, out uint pcOtherMethod) => throw new NotImplementedException(); + void IMetaDataImport.GetParamProps(uint tk, out uint pmd, out uint pulSequence, IntPtr szName, uint cchName, out uint pchName, out uint pdwAttr, out uint pdwCPlusTypeFlag, out IntPtr ppValue, out uint pcchValue) => throw new NotImplementedException(); + void IMetaDataImport.GetCustomAttributeByName(uint tkObj, string szName, out IntPtr ppData, out uint pcbData) => throw new NotImplementedException(); + bool IMetaDataImport.IsValidToken(uint tk) => throw new NotImplementedException(); + void IMetaDataImport.GetNativeCallConvFromSig(IntPtr pvSig, uint cbSig, out uint pCallConv) => throw new NotImplementedException(); + void IMetaDataImport.IsGlobal(uint pd, out int pbGlobal) => throw new NotImplementedException(); + } +} diff --git a/src/DotNet/Pdb/Dss/PinnedMetaData.cs b/src/DotNet/Pdb/Dss/PinnedMetaData.cs deleted file mode 100644 index 38ce738e4..000000000 --- a/src/DotNet/Pdb/Dss/PinnedMetaData.cs +++ /dev/null @@ -1,79 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Runtime.InteropServices; -using dnlib.IO; - -namespace dnlib.DotNet.Pdb.Dss { - /// - /// Pins a metadata stream in memory - /// - sealed class PinnedMetaData : IDisposable { - GCHandle gcHandle; - readonly IImageStream stream; - readonly byte[] streamData; - readonly IntPtr address; - - /// - /// Gets the address - /// - public IntPtr Address { - get { return address; } - } - - /// - /// Gets the size - /// - public int Size { - get { return (int)stream.Length; } - } - - /// - /// Constructor - /// - /// Metadata stream - public PinnedMetaData(IImageStream stream) { - this.stream = stream; - - var umStream = stream as UnmanagedMemoryImageStream; - if (umStream != null) { - this.address = umStream.StartAddress; - GC.SuppressFinalize(this); // no GCHandle so finalizer isn't needed - } - else { - var memStream = stream as MemoryImageStream; - if (memStream != null) { - this.streamData = memStream.DataArray; - this.gcHandle = GCHandle.Alloc(this.streamData, GCHandleType.Pinned); - this.address = new IntPtr(this.gcHandle.AddrOfPinnedObject().ToInt64() + memStream.DataOffset); - } - else { - this.streamData = stream.ReadAllBytes(); - this.gcHandle = GCHandle.Alloc(this.streamData, GCHandleType.Pinned); - this.address = this.gcHandle.AddrOfPinnedObject(); - } - } - } - - ~PinnedMetaData() { - Dispose(false); - } - - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } - - void Dispose(bool disposing) { - if (gcHandle.IsAllocated) { - try { - gcHandle.Free(); - } - catch (InvalidOperationException) { - } - } - if (disposing) - stream.Dispose(); - } - } -} diff --git a/src/DotNet/Pdb/Dss/ReaderMetaDataImport.cs b/src/DotNet/Pdb/Dss/ReaderMetaDataImport.cs new file mode 100644 index 000000000..910904dbb --- /dev/null +++ b/src/DotNet/Pdb/Dss/ReaderMetaDataImport.cs @@ -0,0 +1,85 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Runtime.InteropServices; +using System.Threading; +using dnlib.DotNet.MD; + +namespace dnlib.DotNet.Pdb.Dss { + sealed unsafe class ReaderMetaDataImport : MetaDataImport, IDisposable { + Metadata metadata; + byte* blobPtr; + IntPtr addrToFree; + + public ReaderMetaDataImport(Metadata metadata) { + this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); + var reader = metadata.BlobStream.CreateReader(); + addrToFree = Marshal.AllocHGlobal((int)reader.BytesLeft); + blobPtr = (byte*)addrToFree; + if (blobPtr is null) + throw new OutOfMemoryException(); + reader.ReadBytes(blobPtr, (int)reader.BytesLeft); + } + + ~ReaderMetaDataImport() => Dispose(false); + + public override void GetTypeRefProps(uint tr, uint* ptkResolutionScope, ushort* szName, uint cchName, uint* pchName) { + var token = new MDToken(tr); + if (token.Table != Table.TypeRef) + throw new ArgumentException(); + if (!metadata.TablesStream.TryReadTypeRefRow(token.Rid, out var row)) + throw new ArgumentException(); + if (ptkResolutionScope is not null) + *ptkResolutionScope = row.ResolutionScope; + if (szName is not null || pchName is not null) { + var typeNamespace = metadata.StringsStream.ReadNoNull(row.Namespace); + var typeName = metadata.StringsStream.ReadNoNull(row.Name); + CopyTypeName(typeNamespace, typeName, szName, cchName, pchName); + } + } + + public override void GetTypeDefProps(uint td, ushort* szTypeDef, uint cchTypeDef, uint* pchTypeDef, uint* pdwTypeDefFlags, uint* ptkExtends) { + var token = new MDToken(td); + if (token.Table != Table.TypeDef) + throw new ArgumentException(); + if (!metadata.TablesStream.TryReadTypeDefRow(token.Rid, out var row)) + throw new ArgumentException(); + if (pdwTypeDefFlags is not null) + *pdwTypeDefFlags = row.Flags; + if (ptkExtends is not null) + *ptkExtends = row.Extends; + if (szTypeDef is not null || pchTypeDef is not null) { + var typeNamespace = metadata.StringsStream.ReadNoNull(row.Namespace); + var typeName = metadata.StringsStream.ReadNoNull(row.Name); + CopyTypeName(typeNamespace, typeName, szTypeDef, cchTypeDef, pchTypeDef); + } + } + + public override void GetSigFromToken(uint mdSig, byte** ppvSig, uint* pcbSig) { + var token = new MDToken(mdSig); + if (token.Table != Table.StandAloneSig) + throw new ArgumentException(); + if (!metadata.TablesStream.TryReadStandAloneSigRow(token.Rid, out var row)) + throw new ArgumentException(); + if (!metadata.BlobStream.TryCreateReader(row.Signature, out var reader)) + throw new ArgumentException(); + if (ppvSig is not null) + *ppvSig = blobPtr + (reader.StartOffset - (uint)metadata.BlobStream.StartOffset); + if (pcbSig is not null) + *pcbSig = reader.Length; + } + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + void Dispose(bool disposing) { + metadata = null; + var addrToFreeTmp = Interlocked.Exchange(ref addrToFree, IntPtr.Zero); + blobPtr = null; + if (addrToFreeTmp != IntPtr.Zero) + Marshal.FreeHGlobal(addrToFreeTmp); + } + } +} diff --git a/src/DotNet/Pdb/Dss/StreamIStream.cs b/src/DotNet/Pdb/Dss/StreamIStream.cs index 0034bbb6c..be1b60e0b 100644 --- a/src/DotNet/Pdb/Dss/StreamIStream.cs +++ b/src/DotNet/Pdb/Dss/StreamIStream.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.IO; @@ -30,9 +30,7 @@ public StreamIStream(Stream stream) /// Source stream /// Name of original file or null if unknown. public StreamIStream(Stream stream, string name) { - if (stream == null) - throw new ArgumentNullException("stream"); - this.stream = stream; + this.stream = stream ?? throw new ArgumentNullException(nameof(stream)); this.name = name ?? string.Empty; } @@ -43,9 +41,7 @@ public void Clone(out IStream ppstm) { } /// - public void Commit(int grfCommitFlags) { - stream.Flush(); - } + public void Commit(int grfCommitFlags) => stream.Flush(); /// public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten) { @@ -60,17 +56,16 @@ public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten) { var buffer = new byte[sizeToRead]; Read(buffer, sizeToRead, pcbRead); - if (pcbRead != null) + if (pcbRead != IntPtr.Zero) Marshal.WriteInt64(pcbRead, Marshal.ReadInt32(pcbRead)); pstm.Write(buffer, buffer.Length, pcbWritten); - if (pcbWritten != null) + if (pcbWritten != IntPtr.Zero) Marshal.WriteInt64(pcbWritten, Marshal.ReadInt32(pcbWritten)); } /// - public void LockRegion(long libOffset, long cb, int dwLockType) { + public void LockRegion(long libOffset, long cb, int dwLockType) => Marshal.ThrowExceptionForHR(STG_E_INVALIDFUNCTION); - } /// public void Read(byte[] pv, int cb, IntPtr pcbRead) { @@ -114,9 +109,7 @@ public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition) { } /// - public void SetSize(long libNewSize) { - stream.SetLength(libNewSize); - } + public void SetSize(long libNewSize) => stream.SetLength(libNewSize); enum STATFLAG { DEFAULT = 0, @@ -152,14 +145,13 @@ public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, i } /// - public void UnlockRegion(long libOffset, long cb, int dwLockType) { + public void UnlockRegion(long libOffset, long cb, int dwLockType) => Marshal.ThrowExceptionForHR(STG_E_INVALIDFUNCTION); - } /// public void Write(byte[] pv, int cb, IntPtr pcbWritten) { stream.Write(pv, 0, cb); - if (pcbWritten != null) + if (pcbWritten != IntPtr.Zero) Marshal.WriteInt32(pcbWritten, cb); } } diff --git a/src/DotNet/Pdb/Dss/SymbolDocument.cs b/src/DotNet/Pdb/Dss/SymbolDocument.cs deleted file mode 100644 index 09a257cbc..000000000 --- a/src/DotNet/Pdb/Dss/SymbolDocument.cs +++ /dev/null @@ -1,100 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Diagnostics.SymbolStore; - -namespace dnlib.DotNet.Pdb.Dss { - sealed class SymbolDocument : ISymbolDocument { - readonly ISymUnmanagedDocument document; - - public ISymUnmanagedDocument SymUnmanagedDocument { - get { return document; } - } - - public SymbolDocument(ISymUnmanagedDocument document) { - this.document = document; - } - - public Guid CheckSumAlgorithmId { - get { - Guid guid; - document.GetCheckSumAlgorithmId(out guid); - return guid; - } - } - - public Guid DocumentType { - get { - Guid guid; - document.GetDocumentType(out guid); - return guid; - } - } - - public bool HasEmbeddedSource { - get { - bool result; - document.HasEmbeddedSource(out result); - return result; - } - } - - public Guid Language { - get { - Guid guid; - document.GetLanguage(out guid); - return guid; - } - } - - public Guid LanguageVendor { - get { - Guid guid; - document.GetLanguageVendor(out guid); - return guid; - } - } - - public int SourceLength { - get { - uint result; - document.GetSourceLength(out result); - return (int)result; - } - } - - public string URL { - get { - uint count; - document.GetURL(0, out count, null); - var chars = new char[count]; - document.GetURL((uint)chars.Length, out count, chars); - if (chars.Length == 0) - return string.Empty; - return new string(chars, 0, chars.Length - 1); - } - } - - public int FindClosestLine(int line) { - uint result; - document.FindClosestLine((uint)line, out result); - return (int)result; - } - - public byte[] GetCheckSum() { - uint bufSize; - document.GetCheckSum(0, out bufSize, null); - var buffer = new byte[bufSize]; - document.GetCheckSum((uint)buffer.Length, out bufSize, buffer); - return buffer; - } - - public byte[] GetSourceRange(int startLine, int startColumn, int endLine, int endColumn) { - uint bufSize; - document.GetSourceRange((uint)startLine, (uint)startColumn, (uint)endLine, (uint)endColumn, 0, out bufSize, null); - var buffer = new byte[bufSize]; - document.GetSourceRange((uint)startLine, (uint)startColumn, (uint)endLine, (uint)endColumn, (uint)buffer.Length, out bufSize, buffer); - return buffer; - } - } -} diff --git a/src/DotNet/Pdb/Dss/SymbolDocumentImpl.cs b/src/DotNet/Pdb/Dss/SymbolDocumentImpl.cs new file mode 100644 index 000000000..650797db3 --- /dev/null +++ b/src/DotNet/Pdb/Dss/SymbolDocumentImpl.cs @@ -0,0 +1,95 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using dnlib.DotNet.Pdb.Symbols; + +namespace dnlib.DotNet.Pdb.Dss { + sealed class SymbolDocumentImpl : SymbolDocument { + readonly ISymUnmanagedDocument document; + public ISymUnmanagedDocument SymUnmanagedDocument => document; + public SymbolDocumentImpl(ISymUnmanagedDocument document) => this.document = document; + + public override Guid CheckSumAlgorithmId { + get { + document.GetCheckSumAlgorithmId(out var guid); + return guid; + } + } + + public override Guid DocumentType { + get { + document.GetDocumentType(out var guid); + return guid; + } + } + + public override Guid Language { + get { + document.GetLanguage(out var guid); + return guid; + } + } + + public override Guid LanguageVendor { + get { + document.GetLanguageVendor(out var guid); + return guid; + } + } + + public override string URL { + get { + document.GetURL(0, out uint count, null); + var chars = new char[count]; + document.GetURL((uint)chars.Length, out count, chars); + if (chars.Length == 0) + return string.Empty; + return new string(chars, 0, chars.Length - 1); + } + } + + public override byte[] CheckSum { + get { + document.GetCheckSum(0, out uint bufSize, null); + var buffer = new byte[bufSize]; + document.GetCheckSum((uint)buffer.Length, out bufSize, buffer); + return buffer; + } + } + + byte[] SourceCode { + get { + int hr = document.GetSourceLength(out int size); + if (hr < 0) + return null; + if (size <= 0) + return null; + var sourceCode = new byte[size]; + hr = document.GetSourceRange(0, 0, int.MaxValue, int.MaxValue, size, out var bytesRead, sourceCode); + if (hr < 0) + return null; + if (bytesRead <= 0) + return null; + if (bytesRead != sourceCode.Length) + Array.Resize(ref sourceCode, bytesRead); + return sourceCode; + } + } + + public override PdbCustomDebugInfo[] CustomDebugInfos { + get { + if (customDebugInfos is null) { + var sourceCode = SourceCode; + if (sourceCode is not null) + customDebugInfos = new PdbCustomDebugInfo[1] { new PdbEmbeddedSourceCustomDebugInfo(sourceCode) }; + else + customDebugInfos = Array2.Empty(); + } + return customDebugInfos; + } + } + PdbCustomDebugInfo[] customDebugInfos; + + public override MDToken? MDToken => null; + } +} diff --git a/src/DotNet/Pdb/Dss/SymbolDocumentWriter.cs b/src/DotNet/Pdb/Dss/SymbolDocumentWriter.cs index 9ba6e5d08..564fab8ca 100644 --- a/src/DotNet/Pdb/Dss/SymbolDocumentWriter.cs +++ b/src/DotNet/Pdb/Dss/SymbolDocumentWriter.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Diagnostics.SymbolStore; @@ -6,21 +6,12 @@ namespace dnlib.DotNet.Pdb.Dss { sealed class SymbolDocumentWriter : ISymbolDocumentWriter { readonly ISymUnmanagedDocumentWriter writer; - - public ISymUnmanagedDocumentWriter SymUnmanagedDocumentWriter { - get { return writer; } - } - - public SymbolDocumentWriter(ISymUnmanagedDocumentWriter writer) { - this.writer = writer; - } - + public ISymUnmanagedDocumentWriter SymUnmanagedDocumentWriter => writer; + public SymbolDocumentWriter(ISymUnmanagedDocumentWriter writer) => this.writer = writer; public void SetCheckSum(Guid algorithmId, byte[] checkSum) { - writer.SetCheckSum(algorithmId, (uint)(checkSum == null ? 0 : checkSum.Length), checkSum); - } - - public void SetSource(byte[] source) { - writer.SetSource((uint)source.Length, source); + if (checkSum is not null && checkSum.Length != 0 && algorithmId != Guid.Empty) + writer.SetCheckSum(algorithmId, (uint)checkSum.Length, checkSum); } + public void SetSource(byte[] source) => writer.SetSource((uint)source.Length, source); } } diff --git a/src/DotNet/Pdb/Dss/SymbolMethod.cs b/src/DotNet/Pdb/Dss/SymbolMethod.cs deleted file mode 100644 index a7f0595e2..000000000 --- a/src/DotNet/Pdb/Dss/SymbolMethod.cs +++ /dev/null @@ -1,129 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Diagnostics.SymbolStore; - -namespace dnlib.DotNet.Pdb.Dss { - sealed class SymbolMethod : ISymbolMethod { - readonly ISymUnmanagedMethod method; - - public SymbolMethod(ISymUnmanagedMethod method) { - this.method = method; - } - - public ISymbolScope RootScope { - get { - ISymUnmanagedScope scope; - method.GetRootScope(out scope); - return scope == null ? null : new SymbolScope(scope); - } - } - - public int SequencePointCount { - get { - uint result; - method.GetSequencePointCount(out result); - return (int)result; - } - } - - public SymbolToken Token { - get { - uint result; - method.GetToken(out result); - return new SymbolToken((int)result); - } - } - - public ISymbolNamespace GetNamespace() { - ISymUnmanagedNamespace ns; - method.GetNamespace(out ns); - return ns == null ? null : new SymbolNamespace(ns); - } - - public int GetOffset(ISymbolDocument document, int line, int column) { - var symDoc = document as SymbolDocument; - if (symDoc == null) - throw new ArgumentException("document is not a non-null SymbolDocument instance"); - uint result; - method.GetOffset(symDoc.SymUnmanagedDocument, (uint)line, (uint)column, out result); - return (int)result; - } - - public ISymbolVariable[] GetParameters() { - uint numVars; - method.GetParameters(0, out numVars, null); - var unVars = new ISymUnmanagedVariable[numVars]; - method.GetParameters((uint)unVars.Length, out numVars, unVars); - var vars = new ISymbolVariable[numVars]; - for (uint i = 0; i < numVars; i++) - vars[i] = new SymbolVariable(unVars[i]); - return vars; - } - - public int[] GetRanges(ISymbolDocument document, int line, int column) { - var symDoc = document as SymbolDocument; - if (symDoc == null) - throw new ArgumentException("document is not a non-null SymbolDocument instance"); - uint arySize; - method.GetRanges(symDoc.SymUnmanagedDocument, (uint)line, (uint)column, 0, out arySize, null); - var ary = new int[arySize]; - method.GetRanges(symDoc.SymUnmanagedDocument, (uint)line, (uint)column, (uint)ary.Length, out arySize, ary); - return ary; - } - - public ISymbolScope GetScope(int offset) { - ISymUnmanagedScope scope; - method.GetScopeFromOffset((uint)offset, out scope); - return scope == null ? null : new SymbolScope(scope); - } - - public void GetSequencePoints(int[] offsets, ISymbolDocument[] documents, int[] lines, int[] columns, int[] endLines, int[] endColumns) { - // Any array can be null, and the documentation says we must verify the sizes. - - int arySize = -1; - if (offsets != null) arySize = offsets.Length; - else if (documents != null) arySize = documents.Length; - else if (lines != null) arySize = lines.Length; - else if (columns != null) arySize = columns.Length; - else if (endLines != null) arySize = endLines.Length; - else if (endColumns != null)arySize = endColumns.Length; - - if (offsets != null && offsets.Length != arySize) throw new ArgumentException("Invalid array length: offsets"); - if (documents != null && documents.Length != arySize) throw new ArgumentException("Invalid array length: documents"); - if (lines != null && lines.Length != arySize) throw new ArgumentException("Invalid array length: lines"); - if (columns != null && columns.Length != arySize) throw new ArgumentException("Invalid array length: columns"); - if (endLines != null && endLines.Length != arySize) throw new ArgumentException("Invalid array length: endLines"); - if (endColumns != null && endColumns.Length != arySize) throw new ArgumentException("Invalid array length: endColumns"); - - if (arySize <= 0) - return; - - var unDocs = documents == null ? null : new ISymUnmanagedDocument[documents.Length]; - uint size; - method.GetSequencePoints((uint)arySize, out size, offsets, unDocs, lines, columns, endLines, endColumns); - - if (unDocs != null) { - for (int i = 0; i < unDocs.Length; i++) - documents[i] = unDocs[i] == null ? null : new SymbolDocument(unDocs[i]); - } - } - - public bool GetSourceStartEnd(ISymbolDocument[] docs, int[] lines, int[] columns) { - if (docs != null && docs.Length < 2) throw new ArgumentException("Invalid array: length < 2: docs"); - if (lines != null && lines.Length < 2) throw new ArgumentException("Invalid array: length < 2: lines"); - if (columns != null && columns.Length < 2) throw new ArgumentException("Invalid array: length < 2: columns"); - - var unDocs = docs == null ? null : new ISymUnmanagedDocument[docs.Length]; - bool result; - method.GetSourceStartEnd(unDocs, lines, columns, out result); - - if (unDocs != null) { - for (int i = 0; i < unDocs.Length; i++) - docs[i] = unDocs[i] == null ? null : new SymbolDocument(unDocs[i]); - } - - return result; - } - } -} diff --git a/src/DotNet/Pdb/Dss/SymbolMethodImpl.cs b/src/DotNet/Pdb/Dss/SymbolMethodImpl.cs new file mode 100644 index 000000000..bc96f6e34 --- /dev/null +++ b/src/DotNet/Pdb/Dss/SymbolMethodImpl.cs @@ -0,0 +1,112 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; +using System.Diagnostics.SymbolStore; +using System.Threading; +using dnlib.DotNet.Emit; +using dnlib.DotNet.Pdb.Symbols; + +namespace dnlib.DotNet.Pdb.Dss { + sealed class SymbolMethodImpl : SymbolMethod { + readonly SymbolReaderImpl reader; + readonly ISymUnmanagedMethod method; + readonly ISymUnmanagedAsyncMethod asyncMethod; + + public SymbolMethodImpl(SymbolReaderImpl reader, ISymUnmanagedMethod method) { + this.reader = reader; + this.method = method; + asyncMethod = method as ISymUnmanagedAsyncMethod; + } + + public override int Token { + get { + method.GetToken(out uint result); + return (int)result; + } + } + + public override SymbolScope RootScope { + get { + if (rootScope is null) { + method.GetRootScope(out var scope); + Interlocked.CompareExchange(ref rootScope, scope is null ? null : new SymbolScopeImpl(scope, this, null), null); + } + return rootScope; + } + } + volatile SymbolScope rootScope; + + public override IList SequencePoints { + get { + if (sequencePoints is null) { + method.GetSequencePointCount(out uint seqPointCount); + var seqPoints = new SymbolSequencePoint[seqPointCount]; + + var offsets = new int[seqPoints.Length]; + var documents = new ISymbolDocument[seqPoints.Length]; + var lines = new int[seqPoints.Length]; + var columns = new int[seqPoints.Length]; + var endLines = new int[seqPoints.Length]; + var endColumns = new int[seqPoints.Length]; + var unDocs = new ISymUnmanagedDocument[seqPoints.Length]; + if (seqPoints.Length != 0) + method.GetSequencePoints((uint)seqPoints.Length, out uint size, offsets, unDocs, lines, columns, endLines, endColumns); + for (int i = 0; i < seqPoints.Length; i++) { + seqPoints[i] = new SymbolSequencePoint { + Offset = offsets[i], + Document = new SymbolDocumentImpl(unDocs[i]), + Line = lines[i], + Column = columns[i], + EndLine = endLines[i], + EndColumn = endColumns[i], + }; + } + sequencePoints = seqPoints; + } + return sequencePoints; + } + } + volatile SymbolSequencePoint[] sequencePoints; + + public int AsyncKickoffMethod { + get { + if (asyncMethod is null || !asyncMethod.IsAsyncMethod()) + return 0; + return (int)asyncMethod.GetKickoffMethod(); + } + } + + public uint? AsyncCatchHandlerILOffset { + get { + if (asyncMethod is null || !asyncMethod.IsAsyncMethod()) + return null; + if (!asyncMethod.HasCatchHandlerILOffset()) + return null; + return asyncMethod.GetCatchHandlerILOffset(); + } + } + + public IList AsyncStepInfos { + get { + if (asyncMethod is null || !asyncMethod.IsAsyncMethod()) + return null; + if (asyncStepInfos is null) { + var stepInfoCount = asyncMethod.GetAsyncStepInfoCount(); + var yieldOffsets = new uint[stepInfoCount]; + var breakpointOffsets = new uint[stepInfoCount]; + var breakpointMethods = new uint[stepInfoCount]; + asyncMethod.GetAsyncStepInfo(stepInfoCount, out stepInfoCount, yieldOffsets, breakpointOffsets, breakpointMethods); + var res = new SymbolAsyncStepInfo[stepInfoCount]; + for (int i = 0; i < res.Length; i++) + res[i] = new SymbolAsyncStepInfo(yieldOffsets[i], breakpointOffsets[i], breakpointMethods[i]); + asyncStepInfos = res; + } + return asyncStepInfos; + } + } + volatile SymbolAsyncStepInfo[] asyncStepInfos; + + public override void GetCustomDebugInfos(MethodDef method, CilBody body, IList result) => + reader.GetCustomDebugInfos(this, method, body, result); + } +} diff --git a/src/DotNet/Pdb/Dss/SymbolNamespace.cs b/src/DotNet/Pdb/Dss/SymbolNamespace.cs deleted file mode 100644 index 15c24daed..000000000 --- a/src/DotNet/Pdb/Dss/SymbolNamespace.cs +++ /dev/null @@ -1,47 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System.Diagnostics.SymbolStore; - -namespace dnlib.DotNet.Pdb.Dss { - sealed class SymbolNamespace : ISymbolNamespace { - readonly ISymUnmanagedNamespace ns; - - public SymbolNamespace(ISymUnmanagedNamespace @namespace) { - this.ns = @namespace; - } - - public string Name { - get { - uint count; - ns.GetName(0, out count, null); - var chars = new char[count]; - ns.GetName((uint)chars.Length, out count, chars); - if (chars.Length == 0) - return string.Empty; - return new string(chars, 0, chars.Length - 1); - } - } - - public ISymbolNamespace[] GetNamespaces() { - uint numNss; - ns.GetNamespaces(0, out numNss, null); - var unNss = new ISymUnmanagedNamespace[numNss]; - ns.GetNamespaces((uint)unNss.Length, out numNss, unNss); - var nss = new ISymbolNamespace[numNss]; - for (uint i = 0; i < numNss; i++) - nss[i] = new SymbolNamespace(unNss[i]); - return nss; - } - - public ISymbolVariable[] GetVariables() { - uint numVars; - ns.GetVariables(0, out numVars, null); - var unVars = new ISymUnmanagedVariable[numVars]; - ns.GetVariables((uint)unVars.Length, out numVars, unVars); - var vars = new ISymbolVariable[numVars]; - for (uint i = 0; i < numVars; i++) - vars[i] = new SymbolVariable(unVars[i]); - return vars; - } - } -} diff --git a/src/DotNet/Pdb/Dss/SymbolNamespaceImpl.cs b/src/DotNet/Pdb/Dss/SymbolNamespaceImpl.cs new file mode 100644 index 000000000..2b279780f --- /dev/null +++ b/src/DotNet/Pdb/Dss/SymbolNamespaceImpl.cs @@ -0,0 +1,22 @@ +// dnlib: See LICENSE.txt for more info + +using dnlib.DotNet.Pdb.Symbols; + +namespace dnlib.DotNet.Pdb.Dss { + sealed class SymbolNamespaceImpl : SymbolNamespace { + readonly ISymUnmanagedNamespace ns; + + public SymbolNamespaceImpl(ISymUnmanagedNamespace @namespace) => ns = @namespace; + + public override string Name { + get { + ns.GetName(0, out uint count, null); + var chars = new char[count]; + ns.GetName((uint)chars.Length, out count, chars); + if (chars.Length == 0) + return string.Empty; + return new string(chars, 0, chars.Length - 1); + } + } + } +} diff --git a/src/DotNet/Pdb/Dss/SymbolReader.cs b/src/DotNet/Pdb/Dss/SymbolReader.cs deleted file mode 100644 index 20e21fcdf..000000000 --- a/src/DotNet/Pdb/Dss/SymbolReader.cs +++ /dev/null @@ -1,123 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Diagnostics.SymbolStore; -using System.Runtime.InteropServices; - -namespace dnlib.DotNet.Pdb.Dss { - sealed class SymbolReader : ISymbolReader { - readonly ISymUnmanagedReader reader; - - const int E_FAIL = unchecked((int)0x80004005); - - /// - /// Constructor - /// - /// An unmanaged symbol reader - public SymbolReader(ISymUnmanagedReader reader) { - if (reader == null) - throw new ArgumentNullException("reader"); - this.reader = reader; - } - - public SymbolToken UserEntryPoint { - get { - uint token; - int hr = reader.GetUserEntryPoint(out token); - if (hr == E_FAIL) - token = 0; - else - Marshal.ThrowExceptionForHR(hr); - return new SymbolToken((int)token); - } - } - - public ISymbolDocument GetDocument(string url, Guid language, Guid languageVendor, Guid documentType) { - ISymUnmanagedDocument document; - reader.GetDocument(url, language, languageVendor, documentType, out document); - return document == null ? null : new SymbolDocument(document); - } - - public ISymbolDocument[] GetDocuments() { - uint numDocs; - reader.GetDocuments(0, out numDocs, null); - var unDocs = new ISymUnmanagedDocument[numDocs]; - reader.GetDocuments((uint)unDocs.Length, out numDocs, unDocs); - var docs = new ISymbolDocument[numDocs]; - for (uint i = 0; i < numDocs; i++) - docs[i] = new SymbolDocument(unDocs[i]); - return docs; - } - - public ISymbolVariable[] GetGlobalVariables() { - uint numVars; - reader.GetGlobalVariables(0, out numVars, null); - var unVars = new ISymUnmanagedVariable[numVars]; - reader.GetGlobalVariables((uint)unVars.Length, out numVars, unVars); - var vars = new ISymbolVariable[numVars]; - for (uint i = 0; i < numVars; i++) - vars[i] = new SymbolVariable(unVars[i]); - return vars; - } - - public ISymbolMethod GetMethod(SymbolToken method) { - ISymUnmanagedMethod unMethod; - int hr = reader.GetMethod((uint)method.GetToken(), out unMethod); - if (hr == E_FAIL) - return null; - Marshal.ThrowExceptionForHR(hr); - return unMethod == null ? null : new SymbolMethod(unMethod); - } - - public ISymbolMethod GetMethod(SymbolToken method, int version) { - ISymUnmanagedMethod unMethod; - int hr = reader.GetMethodByVersion((uint)method.GetToken(), version, out unMethod); - if (hr == E_FAIL) - return null; - Marshal.ThrowExceptionForHR(hr); - return unMethod == null ? null : new SymbolMethod(unMethod); - } - - public ISymbolMethod GetMethodFromDocumentPosition(ISymbolDocument document, int line, int column) { - var symDoc = document as SymbolDocument; - if (symDoc == null) - throw new ArgumentException("document is not a non-null SymbolDocument instance"); - ISymUnmanagedMethod unMethod; - int hr = reader.GetMethodFromDocumentPosition(symDoc.SymUnmanagedDocument, (uint)line, (uint)column, out unMethod); - if (hr == E_FAIL) - return null; - Marshal.ThrowExceptionForHR(hr); - return unMethod == null ? null : new SymbolMethod(unMethod); - } - - public ISymbolNamespace[] GetNamespaces() { - uint numNss; - reader.GetNamespaces(0, out numNss, null); - var unNss = new ISymUnmanagedNamespace[numNss]; - reader.GetNamespaces((uint)unNss.Length, out numNss, unNss); - var nss = new ISymbolNamespace[numNss]; - for (uint i = 0; i < numNss; i++) - nss[i] = new SymbolNamespace(unNss[i]); - return nss; - } - - public byte[] GetSymAttribute(SymbolToken parent, string name) { - uint bufSize; - reader.GetSymAttribute((uint)parent.GetToken(), name, 0, out bufSize, null); - var buffer = new byte[bufSize]; - reader.GetSymAttribute((uint)parent.GetToken(), name, (uint)buffer.Length, out bufSize, buffer); - return buffer; - } - - public ISymbolVariable[] GetVariables(SymbolToken parent) { - uint numVars; - reader.GetVariables((uint)parent.GetToken(), 0, out numVars, null); - var unVars = new ISymUnmanagedVariable[numVars]; - reader.GetVariables((uint)parent.GetToken(), (uint)unVars.Length, out numVars, unVars); - var vars = new ISymbolVariable[numVars]; - for (uint i = 0; i < numVars; i++) - vars[i] = new SymbolVariable(unVars[i]); - return vars; - } - } -} diff --git a/src/DotNet/Pdb/Dss/SymbolReaderCreator.cs b/src/DotNet/Pdb/Dss/SymbolReaderCreator.cs deleted file mode 100644 index e71ab9eb5..000000000 --- a/src/DotNet/Pdb/Dss/SymbolReaderCreator.cs +++ /dev/null @@ -1,245 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.IO; -using System.Diagnostics.SymbolStore; -using System.Runtime.InteropServices; -using System.Security; -using dnlib.DotNet.MD; -using dnlib.IO; -using dnlib.PE; - -namespace dnlib.DotNet.Pdb.Dss { - /// - /// Creates a instance - /// - public static class SymbolReaderCreator { - [DllImport("ole32")] - static extern int CoCreateInstance([In] ref Guid rclsid, IntPtr pUnkOuter, [In] uint dwClsContext, [In] ref Guid riid, [Out, MarshalAs(UnmanagedType.IUnknown)] out object ppv); - - static readonly Guid CLSID_CorSymBinder_SxS = new Guid(0x0A29FF9E, 0x7F9C, 0x4437, 0x8B, 0x11, 0xF4, 0x24, 0x49, 0x1E, 0x39, 0x31); - - /// - /// Creates a new instance - /// - /// Path to assembly - /// A new instance or null if there's no PDB - /// file on disk or if any of the COM methods fail. - public static ISymbolReader Create(string assemblyFileName) { - try { - object mdDispObj; - Guid CLSID_CorMetaDataDispenser = new Guid(0xE5CB7A31, 0x7512, 0x11D2, 0x89, 0xCE, 0x0, 0x80, 0xC7, 0x92, 0xE5, 0xD8); - Guid IID_IMetaDataDispenser = new Guid(0x809C652E, 0x7396, 0x11D2, 0x97, 0x71, 0x00, 0xA0, 0xC9, 0xB4, 0xD5, 0x0C); - int hr = CoCreateInstance(ref CLSID_CorMetaDataDispenser, IntPtr.Zero, 1, ref IID_IMetaDataDispenser, out mdDispObj); - if (hr < 0) - return null; - - object mdImportObj; - var mdDisp = (IMetaDataDispenser)mdDispObj; - Guid IID_IMetaDataImport = new Guid(0x7DAC8207, 0xD3AE, 0x4C75, 0x9B, 0x67, 0x92, 0x80, 0x1A, 0x49, 0x7D, 0x44); - mdDisp.OpenScope(assemblyFileName, 0, ref IID_IMetaDataImport, out mdImportObj); - Marshal.FinalReleaseComObject(mdDispObj); - - ISymUnmanagedReader symReader; - var binder = (ISymUnmanagedBinder)Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_CorSymBinder_SxS)); - hr = binder.GetReaderForFile((IMetaDataImport)mdImportObj, assemblyFileName, null, out symReader); - Marshal.FinalReleaseComObject(mdImportObj); - Marshal.FinalReleaseComObject(binder); - if (hr >= 0) - return new SymbolReader(symReader); - } - catch (IOException) { - } - catch (UnauthorizedAccessException) { - } - catch (SecurityException) { - } - catch (InvalidCastException) { - } - catch (COMException) { - } - return null; - } - - static IImageStream OpenImageStream(string fileName) { - try { - if (!File.Exists(fileName)) - return null; - return ImageStreamCreator.CreateImageStream(fileName); - } - catch (IOException) { - } - catch (UnauthorizedAccessException) { - } - catch (SecurityException) { - } - return null; - } - - /// - /// Creates a new instance - /// - /// .NET metadata - /// Path to PDB file - /// A new instance or null if there's no PDB - /// file on disk or if any of the COM methods fail. - public static ISymbolReader Create(IMetaData metaData, string pdbFileName) { - var mdStream = CreateMetaDataStream(metaData); - try { - return Create(mdStream, OpenImageStream(pdbFileName)); - } - catch { - if (mdStream != null) - mdStream.Dispose(); - throw; - } - } - - /// - /// Creates a new instance - /// - /// .NET metadata - /// PDB file data - /// A new instance or null if any of the COM - /// methods fail. - public static ISymbolReader Create(IMetaData metaData, byte[] pdbData) { - if (pdbData == null) - return null; - var mdStream = CreateMetaDataStream(metaData); - try { - return Create(mdStream, MemoryImageStream.Create(pdbData)); - } - catch { - if (mdStream != null) - mdStream.Dispose(); - throw; - } - } - - /// - /// Creates a new instance - /// - /// .NET metadata - /// PDB file stream which is now owned by this method - /// A new instance or null if any of the COM - /// methods fail. - public static ISymbolReader Create(IMetaData metaData, IImageStream pdbStream) { - try { - return Create(CreateMetaDataStream(metaData), pdbStream); - } - catch { - if (pdbStream != null) - pdbStream.Dispose(); - throw; - } - } - - /// - /// Creates a new instance - /// - /// .NET metadata stream which is now owned by this method - /// Path to PDB file - /// A new instance or null if there's no PDB - /// file on disk or if any of the COM methods fail. - public static ISymbolReader Create(IImageStream mdStream, string pdbFileName) { - try { - return Create(mdStream, OpenImageStream(pdbFileName)); - } - catch { - if (mdStream != null) - mdStream.Dispose(); - throw; - } - } - - /// - /// Creates a new instance - /// - /// .NET metadata stream which is now owned by this method - /// PDB file data - /// A new instance or null if any of the COM - /// methods fail. - public static ISymbolReader Create(IImageStream mdStream, byte[] pdbData) { - if (pdbData == null) { - if (mdStream != null) - mdStream.Dispose(); - return null; - } - try { - return Create(mdStream, MemoryImageStream.Create(pdbData)); - } - catch { - if (mdStream != null) - mdStream.Dispose(); - throw; - } - } - - /// - /// Creates a new instance - /// - /// .NET metadata stream which is now owned by this method - /// PDB file stream which is now owned by this method - /// A new instance or null if any of the COM - /// methods fail. - public static ISymbolReader Create(IImageStream mdStream, IImageStream pdbStream) { - ImageStreamIStream stream = null; - PinnedMetaData pinnedMd = null; - bool error = true; - try { - if (pdbStream == null || mdStream == null) - return null; - - object mdDispObj; - Guid CLSID_CorMetaDataDispenser = new Guid(0xE5CB7A31, 0x7512, 0x11D2, 0x89, 0xCE, 0x0, 0x80, 0xC7, 0x92, 0xE5, 0xD8); - Guid IID_IMetaDataDispenser = new Guid(0x809C652E, 0x7396, 0x11D2, 0x97, 0x71, 0x00, 0xA0, 0xC9, 0xB4, 0xD5, 0x0C); - int hr = CoCreateInstance(ref CLSID_CorMetaDataDispenser, IntPtr.Zero, 1, ref IID_IMetaDataDispenser, out mdDispObj); - if (hr < 0) - return null; - - object mdImportObj; - var mdDisp = (IMetaDataDispenser)mdDispObj; - Guid IID_IMetaDataImport = new Guid(0x7DAC8207, 0xD3AE, 0x4C75, 0x9B, 0x67, 0x92, 0x80, 0x1A, 0x49, 0x7D, 0x44); - pinnedMd = new PinnedMetaData(mdStream); - mdDisp.OpenScopeOnMemory(pinnedMd.Address, (uint)pinnedMd.Size, 0x10, ref IID_IMetaDataImport, out mdImportObj); - Marshal.FinalReleaseComObject(mdDispObj); - - ISymUnmanagedReader symReader; - var binder = (ISymUnmanagedBinder)Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_CorSymBinder_SxS)); - stream = new ImageStreamIStream(pdbStream, null) { UserData = pinnedMd }; - hr = binder.GetReaderFromStream((IMetaDataImport)mdImportObj, stream, out symReader); - Marshal.FinalReleaseComObject(mdImportObj); - Marshal.FinalReleaseComObject(binder); - if (hr >= 0) { - error = false; - return new SymbolReader(symReader); - } - } - catch (IOException) { - } - catch (InvalidCastException) { - } - catch (COMException) { - } - finally { - if (error) { - if (stream != null) - stream.Dispose(); - if (pinnedMd != null) - pinnedMd.Dispose(); - if (mdStream != null) - mdStream.Dispose(); - if (pdbStream != null) - pdbStream.Dispose(); - } - } - return null; - } - - static IImageStream CreateMetaDataStream(IMetaData metaData) { - var peImage = metaData.PEImage; - var mdDataDir = metaData.ImageCor20Header.MetaData; - return peImage.CreateStream(mdDataDir.VirtualAddress, mdDataDir.Size); - } - } -} diff --git a/src/DotNet/Pdb/Dss/SymbolReaderImpl.cs b/src/DotNet/Pdb/Dss/SymbolReaderImpl.cs new file mode 100644 index 000000000..2fb78d42d --- /dev/null +++ b/src/DotNet/Pdb/Dss/SymbolReaderImpl.cs @@ -0,0 +1,159 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using dnlib.DotNet.Emit; +using dnlib.DotNet.Pdb.Symbols; +using dnlib.DotNet.Pdb.WindowsPdb; + +namespace dnlib.DotNet.Pdb.Dss { + sealed class SymbolReaderImpl : SymbolReader { + ModuleDef module; + ISymUnmanagedReader reader; + object[] objsToKeepAlive; + + const int E_FAIL = unchecked((int)0x80004005); + + public SymbolReaderImpl(ISymUnmanagedReader reader, object[] objsToKeepAlive) { + this.reader = reader ?? throw new ArgumentNullException(nameof(reader)); + this.objsToKeepAlive = objsToKeepAlive ?? throw new ArgumentNullException(nameof(objsToKeepAlive)); + } + + ~SymbolReaderImpl() => Dispose(false); + + public override PdbFileKind PdbFileKind => PdbFileKind.WindowsPDB; + + public override int UserEntryPoint { + get { + int hr = reader.GetUserEntryPoint(out uint token); + if (hr == E_FAIL) + token = 0; + else + Marshal.ThrowExceptionForHR(hr); + return (int)token; + } + } + + public override IList Documents { + get { + if (documents is null) { + reader.GetDocuments(0, out uint numDocs, null); + var unDocs = new ISymUnmanagedDocument[numDocs]; + reader.GetDocuments((uint)unDocs.Length, out numDocs, unDocs); + var docs = new SymbolDocument[numDocs]; + for (uint i = 0; i < numDocs; i++) + docs[i] = new SymbolDocumentImpl(unDocs[i]); + documents = docs; + } + return documents; + } + } + volatile SymbolDocument[] documents; + + public override void Initialize(ModuleDef module) => this.module = module; + + public override SymbolMethod GetMethod(MethodDef method, int version) { + int hr = reader.GetMethodByVersion(method.MDToken.Raw, version, out var unMethod); + if (hr == E_FAIL) + return null; + Marshal.ThrowExceptionForHR(hr); + return unMethod is null ? null : new SymbolMethodImpl(this, unMethod); + } + + internal void GetCustomDebugInfos(SymbolMethodImpl symMethod, MethodDef method, CilBody body, IList result) { + var asyncMethod = PseudoCustomDebugInfoFactory.TryCreateAsyncMethod(method.Module, method, body, symMethod.AsyncKickoffMethod, symMethod.AsyncStepInfos, symMethod.AsyncCatchHandlerILOffset); + if (asyncMethod is not null) + result.Add(asyncMethod); + + const string CDI_NAME = "MD2"; + reader.GetSymAttribute(method.MDToken.Raw, CDI_NAME, 0, out uint bufSize, null); + if (bufSize == 0) + return; + var cdiData = new byte[bufSize]; + reader.GetSymAttribute(method.MDToken.Raw, CDI_NAME, (uint)cdiData.Length, out bufSize, cdiData); + PdbCustomDebugInfoReader.Read(method, body, result, cdiData); + } + + public override void GetCustomDebugInfos(int token, GenericParamContext gpContext, IList result) { + if (token == 0x00000001) + GetCustomDebugInfos_ModuleDef(result); + } + + void GetCustomDebugInfos_ModuleDef(IList result) { + var sourceLinkData = GetSourceLinkData(); + if (sourceLinkData is not null) + result.Add(new PdbSourceLinkCustomDebugInfo(sourceLinkData)); + var sourceServerData = GetSourceServerData(); + if (sourceServerData is not null) + result.Add(new PdbSourceServerCustomDebugInfo(sourceServerData)); + } + + byte[] GetSourceLinkData() { + if (reader is ISymUnmanagedReader4 reader4) { + // It returns data that it owns. The data is freed once its Destroy() method is called + Debug.Assert(reader is ISymUnmanagedDispose); + // Despite its name, it seems to only return source link data, and not source server data + if (reader4.GetSourceServerData(out var srcLinkData, out int sizeData) == 0) { + if (sizeData == 0) + return Array2.Empty(); + var data = new byte[sizeData]; + Marshal.Copy(srcLinkData, data, 0, data.Length); + return data; + } + } + return null; + } + + byte[] GetSourceServerData() { + if (reader is ISymUnmanagedSourceServerModule srcSrvModule) { + var srcSrvData = IntPtr.Zero; + try { + // This method only returns source server data, not source link data + if (srcSrvModule.GetSourceServerData(out int sizeData, out srcSrvData) == 0) { + if (sizeData == 0) + return Array2.Empty(); + var data = new byte[sizeData]; + Marshal.Copy(srcSrvData, data, 0, data.Length); + return data; + } + } + finally { + if (srcSrvData != IntPtr.Zero) + Marshal.FreeCoTaskMem(srcSrvData); + } + } + return null; + } + + public override void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + void Dispose(bool disposing) { + (reader as ISymUnmanagedDispose)?.Destroy(); + var o = objsToKeepAlive; + if (o is not null) { + foreach (var obj in o) + (obj as IDisposable)?.Dispose(); + } + module = null; + reader = null; + objsToKeepAlive = null; + } + + public bool MatchesModule(Guid pdbId, uint stamp, uint age) { + if (reader is ISymUnmanagedReader4 reader4) { + int hr = reader4.MatchesModule(pdbId, stamp, age, out bool result); + if (hr < 0) + return false; + return result; + } + + // There seems to be no other method that can verify that we opened the correct PDB, so return true + return true; + } + } +} diff --git a/src/DotNet/Pdb/Dss/SymbolReaderWriterFactory.cs b/src/DotNet/Pdb/Dss/SymbolReaderWriterFactory.cs new file mode 100644 index 000000000..c8ddfb91a --- /dev/null +++ b/src/DotNet/Pdb/Dss/SymbolReaderWriterFactory.cs @@ -0,0 +1,214 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.IO; +using System.Runtime.InteropServices; +using dnlib.IO; +using dnlib.DotNet.Pdb.Symbols; +using System.Diagnostics; +using System.Runtime.Versioning; +using dnlib.PE; +using dnlib.DotNet.Writer; +using dnlib.DotNet.Pdb.WindowsPdb; + +namespace dnlib.DotNet.Pdb.Dss { +#if NETCOREAPP + [SupportedOSPlatform("windows")] +#endif + static class SymbolReaderWriterFactory { + [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories | DllImportSearchPath.AssemblyDirectory)] + [DllImport("Microsoft.DiaSymReader.Native.x86.dll", EntryPoint = "CreateSymReader")] + static extern void CreateSymReader_x86(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)] out object symReader); + + [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories | DllImportSearchPath.AssemblyDirectory)] + [DllImport("Microsoft.DiaSymReader.Native.amd64.dll", EntryPoint = "CreateSymReader")] + static extern void CreateSymReader_x64(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)] out object symReader); + + [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories | DllImportSearchPath.AssemblyDirectory)] + [DllImport("Microsoft.DiaSymReader.Native.arm.dll", EntryPoint = "CreateSymReader")] + static extern void CreateSymReader_arm(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)] out object symReader); + + [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories | DllImportSearchPath.AssemblyDirectory)] + [DllImport("Microsoft.DiaSymReader.Native.arm64.dll", EntryPoint = "CreateSymReader")] + static extern void CreateSymReader_arm64(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)] out object symReader); + + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] + [DllImport("Microsoft.DiaSymReader.Native.x86.dll", EntryPoint = "CreateSymWriter")] + static extern void CreateSymWriter_x86(ref Guid guid, [MarshalAs(UnmanagedType.IUnknown)] out object symWriter); + + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] + [DllImport("Microsoft.DiaSymReader.Native.amd64.dll", EntryPoint = "CreateSymWriter")] + static extern void CreateSymWriter_x64(ref Guid guid, [MarshalAs(UnmanagedType.IUnknown)] out object symWriter); + + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] + [DllImport("Microsoft.DiaSymReader.Native.arm.dll", EntryPoint = "CreateSymWriter")] + static extern void CreateSymWriter_arm(ref Guid guid, [MarshalAs(UnmanagedType.IUnknown)] out object symWriter); + + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] + [DllImport("Microsoft.DiaSymReader.Native.arm64.dll", EntryPoint = "CreateSymWriter")] + static extern void CreateSymWriter_arm64(ref Guid guid, [MarshalAs(UnmanagedType.IUnknown)] out object symWriter); + + static readonly Guid CLSID_CorSymReader_SxS = new Guid("0A3976C5-4529-4ef8-B0B0-42EED37082CD"); + static Type CorSymReader_Type; + + static readonly Guid CLSID_CorSymWriter_SxS = new Guid(0x0AE2DEB0, 0xF901, 0x478B, 0xBB, 0x9F, 0x88, 0x1E, 0xE8, 0x06, 0x67, 0x88); + static Type CorSymWriterType; + + static volatile bool canTry_Microsoft_DiaSymReader_Native = true; + + public static SymbolReader Create(PdbReaderContext pdbContext, MD.Metadata metadata, DataReaderFactory pdbStream) { + ISymUnmanagedReader unmanagedReader = null; + SymbolReaderImpl symReader = null; + ReaderMetaDataImport mdImporter = null; + DataReaderIStream comPdbStream = null; + bool error = true; + try { + if (pdbStream is null) + return null; + var debugDir = pdbContext.CodeViewDebugDirectory; + if (debugDir is null) + return null; + if (!pdbContext.TryGetCodeViewData(out var pdbGuid, out uint age)) + return null; + + unmanagedReader = CreateSymUnmanagedReader(pdbContext.Options); + if (unmanagedReader is null) + return null; + + mdImporter = new ReaderMetaDataImport(metadata); + comPdbStream = new DataReaderIStream(pdbStream); + int hr = unmanagedReader.Initialize(mdImporter, null, null, comPdbStream); + if (hr < 0) + return null; + + symReader = new SymbolReaderImpl(unmanagedReader, new object[] { pdbStream, mdImporter, comPdbStream }); + if (!symReader.MatchesModule(pdbGuid, debugDir.TimeDateStamp, age)) + return null; + + error = false; + return symReader; + } + catch (IOException) { + } + catch (InvalidCastException) { + } + catch (COMException) { + } + finally { + if (error) { + pdbStream?.Dispose(); + symReader?.Dispose(); + mdImporter?.Dispose(); + comPdbStream?.Dispose(); + (unmanagedReader as ISymUnmanagedDispose)?.Destroy(); + } + } + return null; + } + + static ISymUnmanagedReader CreateSymUnmanagedReader(PdbReaderOptions options) { + bool useDiaSymReader = (options & PdbReaderOptions.NoDiaSymReader) == 0; + bool useOldDiaSymReader = (options & PdbReaderOptions.NoOldDiaSymReader) == 0; + + if (useDiaSymReader && canTry_Microsoft_DiaSymReader_Native) { + try { + var guid = CLSID_CorSymReader_SxS; + object symReaderObj; + var machine = ProcessorArchUtils.GetProcessCpuArchitecture(); + switch (machine) { + case Machine.AMD64: + CreateSymReader_x64(ref guid, out symReaderObj); + break; + + case Machine.I386: + CreateSymReader_x86(ref guid, out symReaderObj); + break; + + case Machine.ARMNT: + CreateSymReader_arm(ref guid, out symReaderObj); + break; + + case Machine.ARM64: + CreateSymReader_arm64(ref guid, out symReaderObj); + break; + + default: + Debug.Fail($"Microsoft.DiaSymReader.Native doesn't support this CPU arch: {machine}"); + symReaderObj = null; + break; + } + if (symReaderObj is ISymUnmanagedReader symReader) + return symReader; + } + catch (DllNotFoundException) { + Debug.WriteLine("Microsoft.DiaSymReader.Native not found, using diasymreader.dll instead"); + } + catch { + } + canTry_Microsoft_DiaSymReader_Native = false; + } + + if (useOldDiaSymReader) + return (ISymUnmanagedReader)Activator.CreateInstance(CorSymReader_Type ??= Type.GetTypeFromCLSID(CLSID_CorSymReader_SxS)); + + return null; + } + + static ISymUnmanagedWriter2 CreateSymUnmanagedWriter2(PdbWriterOptions options) { + bool useDiaSymReader = (options & PdbWriterOptions.NoDiaSymReader) == 0; + bool useOldDiaSymReader = (options & PdbWriterOptions.NoOldDiaSymReader) == 0; + + if (useDiaSymReader && canTry_Microsoft_DiaSymReader_Native) { + try { + var guid = CLSID_CorSymWriter_SxS; + object symWriterObj; + var machine = ProcessorArchUtils.GetProcessCpuArchitecture(); + switch (machine) { + case Machine.AMD64: + CreateSymWriter_x64(ref guid, out symWriterObj); + break; + + case Machine.I386: + CreateSymWriter_x86(ref guid, out symWriterObj); + break; + + case Machine.ARMNT: + CreateSymWriter_arm(ref guid, out symWriterObj); + break; + + case Machine.ARM64: + CreateSymWriter_arm64(ref guid, out symWriterObj); + break; + + default: + Debug.Fail($"Microsoft.DiaSymReader.Native doesn't support this CPU arch: {machine}"); + symWriterObj = null; + break; + } + if (symWriterObj is ISymUnmanagedWriter2 symWriter) + return symWriter; + } + catch (DllNotFoundException) { + Debug.WriteLine("Microsoft.DiaSymReader.Native not found, using diasymreader.dll instead"); + } + catch { + } + canTry_Microsoft_DiaSymReader_Native = false; + } + + if (useOldDiaSymReader) + return (ISymUnmanagedWriter2)Activator.CreateInstance(CorSymWriterType ??= Type.GetTypeFromCLSID(CLSID_CorSymWriter_SxS)); + + return null; + } + + public static SymbolWriter Create(PdbWriterOptions options, string pdbFileName) { + if (File.Exists(pdbFileName)) + File.Delete(pdbFileName); + return new SymbolWriterImpl(CreateSymUnmanagedWriter2(options), pdbFileName, File.Create(pdbFileName), options, ownsStream: true); + } + + public static SymbolWriter Create(PdbWriterOptions options, Stream pdbStream, string pdbFileName) => + new SymbolWriterImpl(CreateSymUnmanagedWriter2(options), pdbFileName, pdbStream, options, ownsStream: false); + } +} diff --git a/src/DotNet/Pdb/Dss/SymbolScope.cs b/src/DotNet/Pdb/Dss/SymbolScope.cs deleted file mode 100644 index b15848f6a..000000000 --- a/src/DotNet/Pdb/Dss/SymbolScope.cs +++ /dev/null @@ -1,78 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System.Diagnostics.SymbolStore; - -namespace dnlib.DotNet.Pdb.Dss { - sealed class SymbolScope : ISymbolScope { - readonly ISymUnmanagedScope scope; - - public SymbolScope(ISymUnmanagedScope scope) { - this.scope = scope; - } - - public int EndOffset { - get { - uint result; - scope.GetEndOffset(out result); - return (int)result; - } - } - - public ISymbolMethod Method { - get { - ISymUnmanagedMethod method; - scope.GetMethod(out method); - return method == null ? null : new SymbolMethod(method); - } - } - - public ISymbolScope Parent { - get { - ISymUnmanagedScope parentScope; - scope.GetParent(out parentScope); - return parentScope == null ? null : new SymbolScope(parentScope); - } - } - - public int StartOffset { - get { - uint result; - scope.GetStartOffset(out result); - return (int)result; - } - } - - public ISymbolScope[] GetChildren() { - uint numScopes; - scope.GetChildren(0, out numScopes, null); - var unScopes = new ISymUnmanagedScope[numScopes]; - scope.GetChildren((uint)unScopes.Length, out numScopes, unScopes); - var scopes = new ISymbolScope[numScopes]; - for (uint i = 0; i < numScopes; i++) - scopes[i] = new SymbolScope(unScopes[i]); - return scopes; - } - - public ISymbolVariable[] GetLocals() { - uint numVars; - scope.GetLocals(0, out numVars, null); - var unVars = new ISymUnmanagedVariable[numVars]; - scope.GetLocals((uint)unVars.Length, out numVars, unVars); - var vars = new ISymbolVariable[numVars]; - for (uint i = 0; i < numVars; i++) - vars[i] = new SymbolVariable(unVars[i]); - return vars; - } - - public ISymbolNamespace[] GetNamespaces() { - uint numNss; - scope.GetNamespaces(0, out numNss, null); - var unNss = new ISymUnmanagedNamespace[numNss]; - scope.GetNamespaces((uint)unNss.Length, out numNss, unNss); - var nss = new ISymbolNamespace[numNss]; - for (uint i = 0; i < numNss; i++) - nss[i] = new SymbolNamespace(unNss[i]); - return nss; - } - } -} diff --git a/src/DotNet/Pdb/Dss/SymbolScopeImpl.cs b/src/DotNet/Pdb/Dss/SymbolScopeImpl.cs new file mode 100644 index 000000000..ffbdc5816 --- /dev/null +++ b/src/DotNet/Pdb/Dss/SymbolScopeImpl.cs @@ -0,0 +1,136 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using dnlib.DotNet.Pdb.Symbols; + +namespace dnlib.DotNet.Pdb.Dss { + sealed class SymbolScopeImpl : SymbolScope { + readonly ISymUnmanagedScope scope; + readonly SymbolMethod method; + readonly SymbolScope parent; + + public SymbolScopeImpl(ISymUnmanagedScope scope, SymbolMethod method, SymbolScope parent) { + this.scope = scope; + this.method = method; + this.parent = parent; + } + + public override SymbolMethod Method => method; + public override SymbolScope Parent => parent; + + public override int StartOffset { + get { + scope.GetStartOffset(out uint result); + return (int)result; + } + } + + public override int EndOffset { + get { + scope.GetEndOffset(out uint result); + return (int)result; + } + } + + public override IList Children { + get { + if (children is null) { + scope.GetChildren(0, out uint numScopes, null); + var unScopes = new ISymUnmanagedScope[numScopes]; + scope.GetChildren((uint)unScopes.Length, out numScopes, unScopes); + var scopes = new SymbolScope[numScopes]; + for (uint i = 0; i < numScopes; i++) + scopes[i] = new SymbolScopeImpl(unScopes[i], method, this); + children = scopes; + } + return children; + } + } + volatile SymbolScope[] children; + + public override IList Locals { + get { + if (locals is null) { + scope.GetLocals(0, out uint numVars, null); + var unVars = new ISymUnmanagedVariable[numVars]; + scope.GetLocals((uint)unVars.Length, out numVars, unVars); + var vars = new SymbolVariable[numVars]; + for (uint i = 0; i < numVars; i++) + vars[i] = new SymbolVariableImpl(unVars[i]); + locals = vars; + } + return locals; + } + } + volatile SymbolVariable[] locals; + + public override IList Namespaces { + get { + if (namespaces is null) { + scope.GetNamespaces(0, out uint numNss, null); + var unNss = new ISymUnmanagedNamespace[numNss]; + scope.GetNamespaces((uint)unNss.Length, out numNss, unNss); + var nss = new SymbolNamespace[numNss]; + for (uint i = 0; i < numNss; i++) + nss[i] = new SymbolNamespaceImpl(unNss[i]); + namespaces = nss; + } + return namespaces; + } + } + volatile SymbolNamespace[] namespaces; + + public override IList CustomDebugInfos => Array2.Empty(); + public override PdbImportScope ImportScope => null; + + public override IList GetConstants(ModuleDef module, GenericParamContext gpContext) { + var scope2 = scope as ISymUnmanagedScope2; + if (scope2 is null) + return Array2.Empty(); + scope2.GetConstants(0, out uint numCs, null); + if (numCs == 0) + return Array2.Empty(); + var unCs = new ISymUnmanagedConstant[numCs]; + scope2.GetConstants((uint)unCs.Length, out numCs, unCs); + var nss = new PdbConstant[numCs]; + for (uint i = 0; i < numCs; i++) { + var unc = unCs[i]; + var name = GetName(unc); + unc.GetValue(out object value); + var sigBytes = GetSignatureBytes(unc); + TypeSig signature; + if (sigBytes.Length == 0) + signature = null; + else + signature = SignatureReader.ReadTypeSig(module, module.CorLibTypes, sigBytes, gpContext); + nss[i] = new PdbConstant(name, signature, value); + } + return nss; + } + + string GetName(ISymUnmanagedConstant unc) { + unc.GetName(0, out uint count, null); + var chars = new char[count]; + unc.GetName((uint)chars.Length, out count, chars); + if (chars.Length == 0) + return string.Empty; + return new string(chars, 0, chars.Length - 1); + } + + byte[] GetSignatureBytes(ISymUnmanagedConstant unc) { + const int E_FAIL = unchecked((int)0x80004005); + const int E_NOTIMPL = unchecked((int)0x80004001); + int hr = unc.GetSignature(0, out uint bufSize, null); + if (bufSize == 0 || (hr < 0 && hr != E_FAIL && hr != E_NOTIMPL)) + return Array2.Empty(); + var buffer = new byte[bufSize]; + hr = unc.GetSignature((uint)buffer.Length, out bufSize, buffer); + Debug.Assert(hr == 0); + if (hr != 0) + return Array2.Empty(); + return buffer; + } + } +} diff --git a/src/DotNet/Pdb/Dss/SymbolVariable.cs b/src/DotNet/Pdb/Dss/SymbolVariable.cs deleted file mode 100644 index 7baa48894..000000000 --- a/src/DotNet/Pdb/Dss/SymbolVariable.cs +++ /dev/null @@ -1,89 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System.Diagnostics.SymbolStore; - -namespace dnlib.DotNet.Pdb.Dss { - sealed class SymbolVariable : ISymbolVariable { - readonly ISymUnmanagedVariable variable; - - public SymbolVariable(ISymUnmanagedVariable variable) { - this.variable = variable; - } - - public int AddressField1 { - get { - uint result; - variable.GetAddressField1(out result); - return (int)result; - } - } - - public int AddressField2 { - get { - uint result; - variable.GetAddressField2(out result); - return (int)result; - } - } - - public int AddressField3 { - get { - uint result; - variable.GetAddressField3(out result); - return (int)result; - } - } - - public SymAddressKind AddressKind { - get { - uint result; - variable.GetAddressKind(out result); - return (SymAddressKind)result; - } - } - - public object Attributes { - get { - uint result; - variable.GetAttributes(out result); - return (int)result; - } - } - - public int EndOffset { - get { - uint result; - variable.GetEndOffset(out result); - return (int)result; - } - } - - public string Name { - get { - uint count; - variable.GetName(0, out count, null); - var chars = new char[count]; - variable.GetName((uint)chars.Length, out count, chars); - if (chars.Length == 0) - return string.Empty; - return new string(chars, 0, chars.Length - 1); - } - } - - public int StartOffset { - get { - uint result; - variable.GetStartOffset(out result); - return (int)result; - } - } - - public byte[] GetSignature() { - uint bufSize; - variable.GetSignature(0, out bufSize, null); - var buffer = new byte[bufSize]; - variable.GetSignature((uint)buffer.Length, out bufSize, buffer); - return buffer; - } - } -} diff --git a/src/DotNet/Pdb/Dss/SymbolVariableImpl.cs b/src/DotNet/Pdb/Dss/SymbolVariableImpl.cs new file mode 100644 index 000000000..62347c661 --- /dev/null +++ b/src/DotNet/Pdb/Dss/SymbolVariableImpl.cs @@ -0,0 +1,42 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using dnlib.DotNet.Pdb.Symbols; +using dnlib.DotNet.Pdb.WindowsPdb; + +namespace dnlib.DotNet.Pdb.Dss { + sealed class SymbolVariableImpl : SymbolVariable { + readonly ISymUnmanagedVariable variable; + + public SymbolVariableImpl(ISymUnmanagedVariable variable) => this.variable = variable; + + public override int Index { + get { + variable.GetAddressField1(out uint result); + return (int)result; + } + } + + public override PdbLocalAttributes Attributes { + get { + variable.GetAttributes(out uint result); + if ((result & (uint)CorSymVarFlag.VAR_IS_COMP_GEN) != 0) + return PdbLocalAttributes.DebuggerHidden; + return PdbLocalAttributes.None; + } + } + + public override string Name { + get { + variable.GetName(0, out uint count, null); + var chars = new char[count]; + variable.GetName((uint)chars.Length, out count, chars); + if (chars.Length == 0) + return string.Empty; + return new string(chars, 0, chars.Length - 1); + } + } + + public override PdbCustomDebugInfo[] CustomDebugInfos => Array2.Empty(); + } +} diff --git a/src/DotNet/Pdb/Dss/SymbolWriter.cs b/src/DotNet/Pdb/Dss/SymbolWriter.cs deleted file mode 100644 index 493153ac3..000000000 --- a/src/DotNet/Pdb/Dss/SymbolWriter.cs +++ /dev/null @@ -1,182 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Diagnostics.SymbolStore; -using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; -using dnlib.DotNet.Writer; - -namespace dnlib.DotNet.Pdb.Dss { - sealed class SymbolWriter : ISymbolWriter2 { - readonly ISymUnmanagedWriter2 writer; - readonly string pdbFileName; - readonly Stream pdbStream; - bool closeCalled; - - /// - /// Constructor - /// - /// Writer - /// PDB file name - public SymbolWriter(ISymUnmanagedWriter2 writer, string pdbFileName) { - if (writer == null) - throw new ArgumentNullException("writer"); - if (pdbFileName == null) - throw new ArgumentNullException("pdbFileName"); - this.writer = writer; - this.pdbFileName = pdbFileName; - } - - /// - /// Constructor - /// - /// Writer - /// PDB file name - /// PDB output stream - public SymbolWriter(ISymUnmanagedWriter2 writer, string pdbFileName, Stream pdbStream) { - if (writer == null) - throw new ArgumentNullException("writer"); - if (pdbStream == null) - throw new ArgumentNullException("pdbStream"); - this.writer = writer; - this.pdbStream = pdbStream; - this.pdbFileName = pdbFileName; - } - - public void Close() { - if (closeCalled) - return; - closeCalled = true; - writer.Close(); - } - - public void CloseMethod() { - writer.CloseMethod(); - } - - public void CloseNamespace() { - writer.CloseNamespace(); - } - - public void CloseScope(int endOffset) { - writer.CloseScope((uint)endOffset); - } - - public ISymbolDocumentWriter DefineDocument(string url, Guid language, Guid languageVendor, Guid documentType) { - ISymUnmanagedDocumentWriter unDocWriter; - writer.DefineDocument(url, ref language, ref languageVendor, ref documentType, out unDocWriter); - return unDocWriter == null ? null : new SymbolDocumentWriter(unDocWriter); - } - - public void DefineField(SymbolToken parent, string name, System.Reflection.FieldAttributes attributes, byte[] signature, SymAddressKind addrKind, int addr1, int addr2, int addr3) { - writer.DefineField((uint)parent.GetToken(), name, (uint)attributes, (uint)signature.Length, signature, (uint)addrKind, (uint)addr1, (uint)addr2, (uint)addr3); - } - - public void DefineGlobalVariable(string name, System.Reflection.FieldAttributes attributes, byte[] signature, SymAddressKind addrKind, int addr1, int addr2, int addr3) { - writer.DefineGlobalVariable(name, (uint)attributes, (uint)signature.Length, signature, (uint)addrKind, (uint)addr1, (uint)addr2, (uint)addr3); - } - - public void DefineLocalVariable(string name, System.Reflection.FieldAttributes attributes, byte[] signature, SymAddressKind addrKind, int addr1, int addr2, int addr3, int startOffset, int endOffset) { - writer.DefineLocalVariable(name, (uint)attributes, (uint)signature.Length, signature, (uint)addrKind, (uint)addr1, (uint)addr2, (uint)addr3, (uint)startOffset, (uint)endOffset); - } - - public void DefineParameter(string name, ParameterAttributes attributes, int sequence, SymAddressKind addrKind, int addr1, int addr2, int addr3) { - writer.DefineParameter(name, (uint)attributes, (uint)sequence, (uint)addrKind, (uint)addr1, (uint)addr2, (uint)addr3); - } - - public void DefineSequencePoints(ISymbolDocumentWriter document, int[] offsets, int[] lines, int[] columns, int[] endLines, int[] endColumns) { - var doc = document as SymbolDocumentWriter; - if (doc == null) - throw new ArgumentException("document isn't a non-null SymbolDocumentWriter instance"); - if (offsets == null || lines == null || columns == null || - endLines == null || endColumns == null || - offsets.Length != lines.Length || - offsets.Length != columns.Length || - offsets.Length != endLines.Length || - offsets.Length != endColumns.Length) - throw new ArgumentException("Invalid arrays"); - writer.DefineSequencePoints(doc.SymUnmanagedDocumentWriter, (uint)offsets.Length, offsets, lines, columns, endLines, endColumns); - } - - public void DefineSequencePoints(ISymbolDocumentWriter document, uint arraySize, int[] offsets, int[] lines, int[] columns, int[] endLines, int[] endColumns) { - var doc = document as SymbolDocumentWriter; - if (doc == null) - throw new ArgumentException("document isn't a non-null SymbolDocumentWriter instance"); - writer.DefineSequencePoints(doc.SymUnmanagedDocumentWriter, arraySize, offsets, lines, columns, endLines, endColumns); - } - - public void Initialize(IntPtr emitter, string filename, bool fFullBuild) { - writer.Initialize(emitter, filename, null, fFullBuild); - } - - public void OpenMethod(SymbolToken method) { - writer.OpenMethod((uint)method.GetToken()); - } - - public void OpenNamespace(string name) { - writer.OpenNamespace(name); - } - - public int OpenScope(int startOffset) { - uint result; - writer.OpenScope((uint)startOffset, out result); - return (int)result; - } - - public void SetMethodSourceRange(ISymbolDocumentWriter startDoc, int startLine, int startColumn, ISymbolDocumentWriter endDoc, int endLine, int endColumn) { - var sdoc = startDoc as SymbolDocumentWriter; - if (sdoc == null) - throw new ArgumentException("startDoc isn't a non-null SymbolDocumentWriter instance"); - var edoc = endDoc as SymbolDocumentWriter; - if (edoc == null) - throw new ArgumentException("endDoc isn't a non-null SymbolDocumentWriter instance"); - writer.SetMethodSourceRange(sdoc.SymUnmanagedDocumentWriter, (uint)startLine, (uint)startColumn, edoc.SymUnmanagedDocumentWriter, (uint)endLine, (uint)endColumn); - } - - public void SetScopeRange(int scopeID, int startOffset, int endOffset) { - writer.SetScopeRange((uint)scopeID, (uint)startOffset, (uint)endOffset); - } - - public void SetSymAttribute(SymbolToken parent, string name, byte[] data) { - writer.SetSymAttribute((uint)parent.GetToken(), name, (uint)data.Length, data); - } - - public void SetUnderlyingWriter(IntPtr underlyingWriter) { - throw new NotSupportedException(); - } - - public void SetUserEntryPoint(SymbolToken entryMethod) { - writer.SetUserEntryPoint((uint)entryMethod.GetToken()); - } - - public void UsingNamespace(string fullName) { - writer.UsingNamespace(fullName); - } - - public byte[] GetDebugInfo(out IMAGE_DEBUG_DIRECTORY pIDD) { - uint size; - writer.GetDebugInfo(out pIDD, 0, out size, null); - var buffer = new byte[size]; - writer.GetDebugInfo(out pIDD, size, out size, buffer); - return buffer; - } - - public void DefineLocalVariable2(string name, uint attributes, uint sigToken, uint addrKind, uint addr1, uint addr2, uint addr3, uint startOffset, uint endOffset) { - writer.DefineLocalVariable2(name, attributes, sigToken, addrKind, addr1, addr2, addr3, startOffset, endOffset); - } - - public void Initialize(MetaData metaData) { - if (pdbStream != null) - writer.Initialize(new MDEmitter(metaData), pdbFileName, new StreamIStream(pdbStream), true); - else if (!string.IsNullOrEmpty(pdbFileName)) - writer.Initialize(new MDEmitter(metaData), pdbFileName, null, true); - else - throw new InvalidOperationException(); - } - - public void Dispose() { - Marshal.FinalReleaseComObject(writer); - } - } -} diff --git a/src/DotNet/Pdb/Dss/SymbolWriterCreator.cs b/src/DotNet/Pdb/Dss/SymbolWriterCreator.cs deleted file mode 100644 index fcb965a10..000000000 --- a/src/DotNet/Pdb/Dss/SymbolWriterCreator.cs +++ /dev/null @@ -1,42 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.IO; - -namespace dnlib.DotNet.Pdb.Dss { - /// - /// Creates a - /// - public static class SymbolWriterCreator { - static readonly Guid CLSID_CorSymWriter_SxS = new Guid(0x0AE2DEB0, 0xF901, 0x478B, 0xBB, 0x9F, 0x88, 0x1E, 0xE8, 0x06, 0x67, 0x88); - - /// - /// Creates a instance - /// - /// A new instance - public static ISymUnmanagedWriter2 CreateSymUnmanagedWriter2() { - return (ISymUnmanagedWriter2)Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_CorSymWriter_SxS)); - } - - /// - /// Creates a new instance - /// - /// PDB file name - /// A new instance - public static ISymbolWriter2 Create(string pdbFileName) { - if (File.Exists(pdbFileName)) - File.Delete(pdbFileName); - return new SymbolWriter(CreateSymUnmanagedWriter2(), pdbFileName); - } - - /// - /// Creates a new instance - /// - /// PDB output stream - /// PDB file name - /// A new instance - public static ISymbolWriter2 Create(Stream pdbStream, string pdbFileName) { - return new SymbolWriter(CreateSymUnmanagedWriter2(), pdbFileName, pdbStream); - } - } -} diff --git a/src/DotNet/Pdb/Dss/SymbolWriterImpl.cs b/src/DotNet/Pdb/Dss/SymbolWriterImpl.cs new file mode 100644 index 000000000..875ddddec --- /dev/null +++ b/src/DotNet/Pdb/Dss/SymbolWriterImpl.cs @@ -0,0 +1,167 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Diagnostics; +using System.Diagnostics.SymbolStore; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using dnlib.DotNet.Pdb.WindowsPdb; +using dnlib.DotNet.Writer; + +namespace dnlib.DotNet.Pdb.Dss { +#if NETCOREAPP + [SupportedOSPlatform("windows")] +#endif + sealed class SymbolWriterImpl : SymbolWriter { + readonly ISymUnmanagedWriter2 writer; + readonly ISymUnmanagedAsyncMethodPropertiesWriter asyncMethodWriter; + readonly string pdbFileName; + readonly Stream pdbStream; + readonly bool ownsStream; + readonly bool isDeterministic; + bool closeCalled; + + public override bool IsDeterministic => isDeterministic; + public override bool SupportsAsyncMethods => asyncMethodWriter is not null; + + public SymbolWriterImpl(ISymUnmanagedWriter2 writer, string pdbFileName, Stream pdbStream, PdbWriterOptions options, bool ownsStream) { + this.writer = writer ?? throw new ArgumentNullException(nameof(writer)); + asyncMethodWriter = writer as ISymUnmanagedAsyncMethodPropertiesWriter; + this.pdbStream = pdbStream ?? throw new ArgumentNullException(nameof(pdbStream)); + this.pdbFileName = pdbFileName; + this.ownsStream = ownsStream; + isDeterministic = (options & PdbWriterOptions.Deterministic) != 0 && writer is ISymUnmanagedWriter6; + } + + public override void Close() { + if (closeCalled) + return; + closeCalled = true; + writer.Close(); + } + + public override void CloseMethod() => writer.CloseMethod(); + public override void CloseScope(int endOffset) => writer.CloseScope((uint)endOffset); + + public override void DefineAsyncStepInfo(uint[] yieldOffsets, uint[] breakpointOffset, uint[] breakpointMethod) { + if (asyncMethodWriter is null) + throw new InvalidOperationException(); + if (yieldOffsets.Length != breakpointOffset.Length || yieldOffsets.Length != breakpointMethod.Length) + throw new ArgumentException(); + asyncMethodWriter.DefineAsyncStepInfo((uint)yieldOffsets.Length, yieldOffsets, breakpointOffset, breakpointMethod); + } + + public override void DefineCatchHandlerILOffset(uint catchHandlerOffset) { + if (asyncMethodWriter is null) + throw new InvalidOperationException(); + asyncMethodWriter.DefineCatchHandlerILOffset(catchHandlerOffset); + } + + public override void DefineConstant(string name, object value, uint sigToken) => writer.DefineConstant2(name, value, sigToken); + + public override ISymbolDocumentWriter DefineDocument(string url, Guid language, Guid languageVendor, Guid documentType) { + writer.DefineDocument(url, ref language, ref languageVendor, ref documentType, out var unDocWriter); + return unDocWriter is null ? null : new SymbolDocumentWriter(unDocWriter); + } + + public override void DefineKickoffMethod(uint kickoffMethod) { + if (asyncMethodWriter is null) + throw new InvalidOperationException(); + asyncMethodWriter.DefineKickoffMethod(kickoffMethod); + } + + public override void DefineSequencePoints(ISymbolDocumentWriter document, uint arraySize, int[] offsets, int[] lines, int[] columns, int[] endLines, int[] endColumns) { + var doc = document as SymbolDocumentWriter; + if (doc is null) + throw new ArgumentException("document isn't a non-null SymbolDocumentWriter instance"); + writer.DefineSequencePoints(doc.SymUnmanagedDocumentWriter, arraySize, offsets, lines, columns, endLines, endColumns); + } + + public override void OpenMethod(MDToken method) => writer.OpenMethod(method.Raw); + + public override int OpenScope(int startOffset) { + writer.OpenScope((uint)startOffset, out uint result); + return (int)result; + } + + public override void SetSymAttribute(MDToken parent, string name, byte[] data) => writer.SetSymAttribute(parent.Raw, name, (uint)data.Length, data); + public override void SetUserEntryPoint(MDToken entryMethod) => writer.SetUserEntryPoint(entryMethod.Raw); + public override void UsingNamespace(string fullName) => writer.UsingNamespace(fullName); + + public override unsafe bool GetDebugInfo(ChecksumAlgorithm pdbChecksumAlgorithm, ref uint pdbAge, out Guid guid, out uint stamp, out IMAGE_DEBUG_DIRECTORY pIDD, out byte[] codeViewData) { + pIDD = default; + codeViewData = null; + + if (isDeterministic) { + ((ISymUnmanagedWriter3)writer).Commit(); + var oldPos = pdbStream.Position; + pdbStream.Position = 0; + var checksumBytes = Hasher.Hash(pdbChecksumAlgorithm, pdbStream, pdbStream.Length); + pdbStream.Position = oldPos; + if (writer is ISymUnmanagedWriter8 writer8) { + RoslynContentIdProvider.GetContentId(checksumBytes, out guid, out stamp); + writer8.UpdateSignature(guid, stamp, pdbAge); + return true; + } + else if (writer is ISymUnmanagedWriter7 writer7) { + fixed (byte* p = checksumBytes) + writer7.UpdateSignatureByHashingContent(new IntPtr(p), (uint)checksumBytes.Length); + } + } + + writer.GetDebugInfo(out pIDD, 0, out uint size, null); + codeViewData = new byte[size]; + writer.GetDebugInfo(out pIDD, size, out size, codeViewData); + + if (writer is IPdbWriter comPdbWriter) { + var guidBytes = new byte[16]; + Array.Copy(codeViewData, 4, guidBytes, 0, 16); + guid = new Guid(guidBytes); + comPdbWriter.GetSignatureAge(out stamp, out uint age); + Debug.Assert(age == pdbAge); + pdbAge = age; + return true; + } + + Debug.Fail($"COM PDB writer doesn't impl {nameof(IPdbWriter)}"); + guid = default; + stamp = 0; + return false; + } + + public override void DefineLocalVariable(string name, uint attributes, uint sigToken, uint addrKind, uint addr1, uint addr2, uint addr3, uint startOffset, uint endOffset) => + writer.DefineLocalVariable2(name, attributes, sigToken, addrKind, addr1, addr2, addr3, startOffset, endOffset); + + public override void Initialize(Metadata metadata) { + if (isDeterministic) + ((ISymUnmanagedWriter6)writer).InitializeDeterministic(new MDEmitter(metadata), new StreamIStream(pdbStream)); + else + writer.Initialize(new MDEmitter(metadata), pdbFileName, new StreamIStream(pdbStream), true); + } + + public override unsafe void SetSourceServerData(byte[] data) { + if (data is null) + return; + if (writer is ISymUnmanagedWriter8 writer8) { + fixed (void* p = data) + writer8.SetSourceServerData(new IntPtr(p), (uint)data.Length); + } + } + + public override unsafe void SetSourceLinkData(byte[] data) { + if (data is null) + return; + if (writer is ISymUnmanagedWriter8 writer8) { + fixed (void* p = data) + writer8.SetSourceLinkData(new IntPtr(p), (uint)data.Length); + } + } + + public override void Dispose() { + Marshal.FinalReleaseComObject(writer); + if (ownsStream) + pdbStream.Dispose(); + } + } +} diff --git a/src/DotNet/Pdb/IMAGE_DEBUG_DIRECTORY.cs b/src/DotNet/Pdb/IMAGE_DEBUG_DIRECTORY.cs new file mode 100644 index 000000000..c69e14f84 --- /dev/null +++ b/src/DotNet/Pdb/IMAGE_DEBUG_DIRECTORY.cs @@ -0,0 +1,21 @@ +// dnlib: See LICENSE.txt for more info + +using dnlib.PE; + +namespace dnlib.DotNet.Pdb { + /// + /// IMAGE_DEBUG_DIRECTORY + /// + public struct IMAGE_DEBUG_DIRECTORY { +#pragma warning disable 1591 + public uint Characteristics; + public uint TimeDateStamp; + public ushort MajorVersion; + public ushort MinorVersion; + public ImageDebugType Type; + public uint SizeOfData; + public uint AddressOfRawData; + public uint PointerToRawData; +#pragma warning restore 1591 + } +} diff --git a/src/DotNet/Pdb/ISymbolWriter2.cs b/src/DotNet/Pdb/ISymbolWriter2.cs deleted file mode 100644 index 8f33ce380..000000000 --- a/src/DotNet/Pdb/ISymbolWriter2.cs +++ /dev/null @@ -1,70 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Diagnostics.SymbolStore; -using dnlib.DotNet.Writer; - -namespace dnlib.DotNet.Pdb { - /// - /// IMAGE_DEBUG_DIRECTORY - /// - public struct IMAGE_DEBUG_DIRECTORY { -#pragma warning disable 1591 - public uint Characteristics; - public uint TimeDateStamp; - public ushort MajorVersion; - public ushort MinorVersion; - public uint Type; - public uint SizeOfData; - public uint AddressOfRawData; - public uint PointerToRawData; -#pragma warning restore 1591 - } - - /// - /// Implements and adds a few extra methods we need that are part of - /// ISymUnmanagedWriter and ISymUnmanagedWriter2 but not present in - /// . - /// - public interface ISymbolWriter2 : ISymbolWriter, IDisposable { - /// - /// Same as except that this method has an - /// extra that specifies the size of all the arrays. - /// - /// Document - /// Size of the arrays - /// Offsets - /// Start lines - /// Start columns - /// End lines - /// End columns - void DefineSequencePoints(ISymbolDocumentWriter document, uint arraySize, int[] offsets, int[] lines, int[] columns, int[] endLines, int[] endColumns); - - /// - /// Gets debug info. See ISymUnmanagedWriter.GetDebugInfo() - /// - /// Updated by writer - /// Debug data for the symbol store - byte[] GetDebugInfo(out IMAGE_DEBUG_DIRECTORY pIDD); - - /// - /// Define a local. See ISymUnmanagedWriter2.DefineLocalVariable2() - /// - /// Name - /// Attributes - /// Signature token - /// Address kind - /// Address #1 - /// Address #2 - /// Address #3 - /// Start offset - /// End offset - void DefineLocalVariable2(string name, uint attributes, uint sigToken, uint addrKind, uint addr1, uint addr2, uint addr3, uint startOffset, uint endOffset); - - /// - /// Initializes this instance. This must be called before any other method. - /// - /// Metadata - void Initialize(MetaData metaData); - } -} diff --git a/src/DotNet/Pdb/Managed/DbiDocument.cs b/src/DotNet/Pdb/Managed/DbiDocument.cs index 1e0f8fbe0..fde9e87a3 100644 --- a/src/DotNet/Pdb/Managed/DbiDocument.cs +++ b/src/DotNet/Pdb/Managed/DbiDocument.cs @@ -1,79 +1,61 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info -using System; +using System; +using System.Diagnostics; using System.Diagnostics.SymbolStore; +using dnlib.DotNet.Pdb.Symbols; using dnlib.IO; namespace dnlib.DotNet.Pdb.Managed { - sealed class DbiDocument : ISymbolDocument { - public string URL { get; private set; } - public Guid Language { get; private set; } - public Guid LanguageVendor { get; private set; } - public Guid DocumentType { get; private set; } - public Guid CheckSumAlgorithmId { get; private set; } - public byte[] CheckSum { get; private set; } + sealed class DbiDocument : SymbolDocument { + readonly string url; + Guid language; + Guid languageVendor; + Guid documentType; + Guid checkSumAlgorithmId; + byte[] checkSum; + byte[] sourceCode; + + public override string URL => url; + public override Guid Language => language; + public override Guid LanguageVendor => languageVendor; + public override Guid DocumentType => documentType; + public override Guid CheckSumAlgorithmId => checkSumAlgorithmId; + public override byte[] CheckSum => checkSum; + byte[] SourceCode => sourceCode; + + public override PdbCustomDebugInfo[] CustomDebugInfos { + get { + if (customDebugInfos is null) { + var sourceCode = SourceCode; + if (sourceCode is not null) + customDebugInfos = new PdbCustomDebugInfo[1] { new PdbEmbeddedSourceCustomDebugInfo(sourceCode) }; + else + customDebugInfos = Array2.Empty(); + } + return customDebugInfos; + } + } + PdbCustomDebugInfo[] customDebugInfos; + + public override MDToken? MDToken => null; public DbiDocument(string url) { - URL = url; - DocumentType = SymDocumentType.Text; + this.url = url; + documentType = SymDocumentType.Text; + } + + public void Read(ref DataReader reader) { + reader.Position = 0; + language = reader.ReadGuid(); + languageVendor = reader.ReadGuid(); + documentType = reader.ReadGuid(); + checkSumAlgorithmId = reader.ReadGuid(); + int checkSumLen = reader.ReadInt32(); + int sourceLen = reader.ReadInt32(); + checkSum = reader.ReadBytes(checkSumLen); + sourceCode = sourceLen == 0 ? null : reader.ReadBytes(sourceLen); + Debug.Assert(reader.BytesLeft == 0); } - - public void Read(IImageStream stream) { - stream.Position = 0; - Language = new Guid(stream.ReadBytes(0x10)); - LanguageVendor = new Guid(stream.ReadBytes(0x10)); - DocumentType = new Guid(stream.ReadBytes(0x10)); - CheckSumAlgorithmId = new Guid(stream.ReadBytes(0x10)); - - var len = stream.ReadInt32(); - if (stream.ReadUInt32() != 0) - throw new PdbException("Unexpected value"); - - CheckSum = stream.ReadBytes(len); - } - - #region ISymbolDocument - - Guid ISymbolDocument.CheckSumAlgorithmId { - get { return CheckSumAlgorithmId; } - } - - Guid ISymbolDocument.DocumentType { - get { return DocumentType; } - } - - byte[] ISymbolDocument.GetCheckSum() { - return CheckSum; - } - - Guid ISymbolDocument.Language { - get { return Language; } - } - - Guid ISymbolDocument.LanguageVendor { - get { return LanguageVendor; } - } - - string ISymbolDocument.URL { - get { return URL; } - } - - int ISymbolDocument.FindClosestLine(int line) { - throw new NotImplementedException(); - } - - byte[] ISymbolDocument.GetSourceRange(int startLine, int startColumn, int endLine, int endColumn) { - throw new NotImplementedException(); - } - - bool ISymbolDocument.HasEmbeddedSource { - get { throw new NotImplementedException(); } - } - - int ISymbolDocument.SourceLength { - get { throw new NotImplementedException(); } - } - - #endregion } -} \ No newline at end of file +} diff --git a/src/DotNet/Pdb/Managed/DbiFunction.cs b/src/DotNet/Pdb/Managed/DbiFunction.cs index b61a12577..0e543e0e2 100644 --- a/src/DotNet/Pdb/Managed/DbiFunction.cs +++ b/src/DotNet/Pdb/Managed/DbiFunction.cs @@ -1,32 +1,41 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; -using System.Diagnostics.SymbolStore; +using dnlib.DotNet.Emit; +using dnlib.DotNet.Pdb.Symbols; using dnlib.IO; namespace dnlib.DotNet.Pdb.Managed { - sealed class DbiFunction : ISymbolMethod { - public uint Token { get; internal set; } + sealed class DbiFunction : SymbolMethod { + public override int Token => token; + internal int token; + + internal PdbReader reader; + public string Name { get; private set; } public PdbAddress Address { get; private set; } public DbiScope Root { get; private set; } - public IList Lines { get; internal set; } - - public void Read(IImageStream stream, long recEnd) { - stream.Position += 4; - var end = stream.ReadUInt32(); - stream.Position += 4; - var len = stream.ReadUInt32(); - stream.Position += 8; - Token = stream.ReadUInt32(); - Address = PdbAddress.ReadAddress(stream); - stream.Position += 1 + 2; - Name = PdbReader.ReadCString(stream); - - stream.Position = recEnd; - Root = new DbiScope("", Address.Offset, len); - Root.Read(new RecursionCounter(), stream, end); + public List Lines { + get => lines; + set => lines = value; + } + List lines; + + public void Read(ref DataReader reader, uint recEnd) { + reader.Position += 4; + var end = reader.ReadUInt32(); + reader.Position += 4; + var len = reader.ReadUInt32(); + reader.Position += 8; + token = reader.ReadInt32(); + Address = PdbAddress.ReadAddress(ref reader); + reader.Position += 1 + 2; + Name = PdbReader.ReadCString(ref reader); + + reader.Position = recEnd; + Root = new DbiScope(this, null, "", Address.Offset, len); + Root.Read(new RecursionCounter(), ref reader, end); FixOffsets(new RecursionCounter(), Root); } @@ -34,83 +43,76 @@ void FixOffsets(RecursionCounter counter, DbiScope scope) { if (!counter.Increment()) return; - scope.BeginOffset -= Address.Offset; - scope.EndOffset -= Address.Offset; - foreach (var child in scope.Children) - FixOffsets(counter, child); + scope.startOffset -= (int)Address.Offset; + scope.endOffset -= (int)Address.Offset; + var children = scope.Children; + int count = children.Count; + for (int i = 0; i < count; i++) + FixOffsets(counter, (DbiScope)children[i]); counter.Decrement(); } - #region ISymbolMethod - - ISymbolScope ISymbolMethod.RootScope { - get { return Root; } - } - - int ISymbolMethod.SequencePointCount { - get { return Lines == null ? 0 : Lines.Count; } - } + public override SymbolScope RootScope => Root; - void ISymbolMethod.GetSequencePoints(int[] offsets, ISymbolDocument[] documents, int[] lines, int[] columns, - int[] endLines, int[] endColumns) { - int count = Lines == null ? 0 : Lines.Count; - if (offsets != null && offsets.Length != count) - throw new ArgumentException("Invalid array length: offsets"); - if (documents != null && documents.Length != count) - throw new ArgumentException("Invalid array length: documents"); - if (lines != null && lines.Length != count) - throw new ArgumentException("Invalid array length: lines"); - if (columns != null && columns.Length != count) - throw new ArgumentException("Invalid array length: columns"); - if (endLines != null && endLines.Length != count) - throw new ArgumentException("Invalid array length: endLines"); - if (endColumns != null && endColumns.Length != count) - throw new ArgumentException("Invalid array length: endColumns"); - - if (count <= 0) - return; - - int i = 0; - foreach (var line in Lines) { - offsets[i] = (int)line.Offset; - documents[i] = line.Document; - lines[i] = (int)line.LineBegin; - columns[i] = (int)line.ColumnBegin; - endLines[i] = (int)line.LineEnd; - endColumns[i] = (int)line.ColumnEnd; - i++; + public override IList SequencePoints { + get { + var l = lines; + if (l is null) + return Array2.Empty(); + return l; } } - ISymbolNamespace ISymbolMethod.GetNamespace() { - throw new NotImplementedException(); - } - - int ISymbolMethod.GetOffset(ISymbolDocument document, int line, int column) { - throw new NotImplementedException(); - } - - ISymbolVariable[] ISymbolMethod.GetParameters() { - throw new NotImplementedException(); - } - - int[] ISymbolMethod.GetRanges(ISymbolDocument document, int line, int column) { - throw new NotImplementedException(); + const string asyncMethodInfoAttributeName = "asyncMethodInfo"; + public int AsyncKickoffMethod { + get { + var data = Root.GetSymAttribute(asyncMethodInfoAttributeName); + if (data is null || data.Length < 4) + return 0; + return BitConverter.ToInt32(data, 0); + } } - ISymbolScope ISymbolMethod.GetScope(int offset) { - throw new NotImplementedException(); + public uint? AsyncCatchHandlerILOffset { + get { + var data = Root.GetSymAttribute(asyncMethodInfoAttributeName); + if (data is null || data.Length < 8) + return null; + uint token = BitConverter.ToUInt32(data, 4); + return token == uint.MaxValue ? (uint?)null : token; + } } - bool ISymbolMethod.GetSourceStartEnd(ISymbolDocument[] docs, int[] lines, int[] columns) { - throw new NotImplementedException(); + public IList AsyncStepInfos { + get { + if (asyncStepInfos is null) + asyncStepInfos = CreateSymbolAsyncStepInfos(); + return asyncStepInfos; + } } - - SymbolToken ISymbolMethod.Token { - get { throw new NotImplementedException(); } + volatile SymbolAsyncStepInfo[] asyncStepInfos; + + SymbolAsyncStepInfo[] CreateSymbolAsyncStepInfos() { + var data = Root.GetSymAttribute(asyncMethodInfoAttributeName); + if (data is null || data.Length < 12) + return Array2.Empty(); + int pos = 8; + int count = BitConverter.ToInt32(data, pos); + pos += 4; + if (pos + (long)count * 12 > data.Length) + return Array2.Empty(); + if (count == 0) + return Array2.Empty(); + var res = new SymbolAsyncStepInfo[count]; + for (int i = 0; i < res.Length; i++) { + res[i] = new SymbolAsyncStepInfo(BitConverter.ToUInt32(data, pos), BitConverter.ToUInt32(data, pos + 8), BitConverter.ToUInt32(data, pos + 4)); + pos += 12; + } + return res; } - #endregion + public override void GetCustomDebugInfos(MethodDef method, CilBody body, IList result) => + reader.GetCustomDebugInfos(this, method, body, result); } -} \ No newline at end of file +} diff --git a/src/DotNet/Pdb/Managed/DbiModule.cs b/src/DotNet/Pdb/Managed/DbiModule.cs index 8fabb956b..7016cb510 100644 --- a/src/DotNet/Pdb/Managed/DbiModule.cs +++ b/src/DotNet/Pdb/Managed/DbiModule.cs @@ -1,7 +1,8 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; +using dnlib.DotNet.Pdb.Symbols; using dnlib.IO; namespace dnlib.DotNet.Pdb.Managed { @@ -19,16 +20,16 @@ public DbiModule() { public string ModuleName { get; private set; } public string ObjectName { get; private set; } - public IList Functions { get; private set; } - public IList Documents { get; private set; } + public List Functions { get; private set; } + public List Documents { get; private set; } - public void Read(IImageStream stream) { - stream.Position += 34; - StreamId = stream.ReadUInt16(); - cbSyms = stream.ReadUInt32(); - cbOldLines = stream.ReadUInt32(); - cbLines = stream.ReadUInt32(); - stream.Position += 16; + public void Read(ref DataReader reader) { + reader.Position += 34; + StreamId = reader.ReadUInt16(); + cbSyms = reader.ReadUInt32(); + cbOldLines = reader.ReadUInt32(); + cbLines = reader.ReadUInt32(); + reader.Position += 16; if ((int)cbSyms < 0) cbSyms = 0; @@ -37,100 +38,98 @@ public void Read(IImageStream stream) { if ((int)cbLines < 0) cbLines = 0; - ModuleName = PdbReader.ReadCString(stream); - ObjectName = PdbReader.ReadCString(stream); + ModuleName = PdbReader.ReadCString(ref reader); + ObjectName = PdbReader.ReadCString(ref reader); - stream.Position = (stream.Position + 3) & (~3); + reader.Position = (reader.Position + 3) & (~3U); } - public void LoadFunctions(PdbReader reader, IImageStream stream) { - stream.Position = 0; - using (var substream = stream.Create(stream.FileOffset + stream.Position, cbSyms)) - ReadFunctions(substream); + public void LoadFunctions(PdbReader pdbReader, ref DataReader reader) { + reader.Position = 0; + ReadFunctions(reader.Slice(reader.Position, cbSyms)); if (Functions.Count > 0) { - stream.Position += cbSyms + cbOldLines; - using (var substream = stream.Create(stream.FileOffset + stream.Position, cbLines)) - ReadLines(reader, substream); + reader.Position += cbSyms + cbOldLines; + ReadLines(pdbReader, reader.Slice(reader.Position, cbLines)); } } - void ReadFunctions(IImageStream stream) { - if (stream.ReadUInt32() != 4) + void ReadFunctions(DataReader reader) { + if (reader.ReadUInt32() != 4) throw new PdbException("Invalid signature"); - while (stream.Position < stream.Length) { - var size = stream.ReadUInt16(); - var begin = stream.Position; + while (reader.Position < reader.Length) { + var size = reader.ReadUInt16(); + var begin = reader.Position; var end = begin + size; - var type = (SymbolType)stream.ReadUInt16(); + var type = (SymbolType)reader.ReadUInt16(); switch (type) { case SymbolType.S_GMANPROC: case SymbolType.S_LMANPROC: var func = new DbiFunction(); - func.Read(stream, end); + func.Read(ref reader, end); Functions.Add(func); break; default: - stream.Position = end; + reader.Position = end; break; } } } - void ReadLines(PdbReader reader, IImageStream stream) { - var docs = new Dictionary(); + void ReadLines(PdbReader pdbReader, DataReader reader) { + var docs = new Dictionary(); - stream.Position = 0; - while (stream.Position < stream.Length) { - var sig = (ModuleStreamType)stream.ReadUInt32(); - var size = stream.ReadUInt32(); - var begin = stream.Position; - var end = (begin + size + 3) & ~3; + reader.Position = 0; + while (reader.Position < reader.Length) { + var sig = (ModuleStreamType)reader.ReadUInt32(); + var size = reader.ReadUInt32(); + var begin = reader.Position; + var end = (begin + size + 3) & ~3U; if (sig == ModuleStreamType.FileInfo) - ReadFiles(reader, docs, stream, end); + ReadFiles(pdbReader, docs, ref reader, end); - stream.Position = end; + reader.Position = end; } var sortedFuncs = new DbiFunction[Functions.Count]; Functions.CopyTo(sortedFuncs, 0); Array.Sort(sortedFuncs, (a, b) => a.Address.CompareTo(b.Address)); - stream.Position = 0; - while (stream.Position < stream.Length) { - var sig = (ModuleStreamType)stream.ReadUInt32(); - var size = stream.ReadUInt32(); - var begin = stream.Position; + reader.Position = 0; + while (reader.Position < reader.Length) { + var sig = (ModuleStreamType)reader.ReadUInt32(); + var size = reader.ReadUInt32(); + var begin = reader.Position; var end = begin + size; if (sig == ModuleStreamType.Lines) - ReadLines(sortedFuncs, docs, stream, end); + ReadLines(sortedFuncs, docs, ref reader, end); - stream.Position = end; + reader.Position = end; } } - void ReadFiles(PdbReader reader, Dictionary documents, IImageStream stream, long end) { - var begin = stream.Position; - while (stream.Position < end) { - var id = stream.Position - begin; + void ReadFiles(PdbReader pdbReader, Dictionary documents, ref DataReader reader, uint end) { + var begin = reader.Position; + while (reader.Position < end) { + var id = reader.Position - begin; - var nameId = stream.ReadUInt32(); - var len = stream.ReadByte(); - var type = stream.ReadByte(); - var doc = reader.GetDocument(nameId); + var nameId = reader.ReadUInt32(); + var len = reader.ReadByte(); + /*var type = */reader.ReadByte(); + var doc = pdbReader.GetDocument(nameId); documents.Add(id, doc); - stream.Position += len; - stream.Position = (stream.Position + 3) & (~3); + reader.Position += len; + reader.Position = (reader.Position + 3) & (~3U); } } - void ReadLines(DbiFunction[] funcs, Dictionary documents, IImageStream stream, long end) { - var address = PdbAddress.ReadAddress(stream); + void ReadLines(DbiFunction[] funcs, Dictionary documents, ref DataReader reader, uint end) { + var address = PdbAddress.ReadAddress(ref reader); int first = 0; int last = funcs.Length - 1; @@ -152,19 +151,19 @@ void ReadLines(DbiFunction[] funcs, Dictionary documents, IIm if (found == -1) return; - var flags = stream.ReadUInt16(); - stream.Position += 4; + var flags = reader.ReadUInt16(); + reader.Position += 4; - if (funcs[found].Lines == null) { + if (funcs[found].Lines is null) { while (found > 0) { var prevFunc = funcs[found - 1]; - if (prevFunc != null || prevFunc.Address != address) + if (prevFunc is not null && prevFunc.Address != address) break; found--; } } else { - while (found < funcs.Length - 1 && funcs[found] != null) { + while (found < funcs.Length - 1 && funcs[found] is not null) { var nextFunc = funcs[found + 1]; if (nextFunc.Address != address) break; @@ -172,35 +171,35 @@ void ReadLines(DbiFunction[] funcs, Dictionary documents, IIm } } var func = funcs[found]; - if (func.Lines != null) + if (func.Lines is not null) return; - func.Lines = new List(); + func.Lines = new List(); - while (stream.Position < end) { - var document = documents[stream.ReadUInt32()]; - var count = stream.ReadUInt32(); - stream.Position += 4; + while (reader.Position < end) { + var document = documents[reader.ReadUInt32()]; + var count = reader.ReadUInt32(); + reader.Position += 4; const int LINE_ENTRY_SIZE = 8; const int COL_ENTRY_SIZE = 4; - var lineTablePos = stream.Position; - var colTablePos = stream.Position + count * LINE_ENTRY_SIZE; + var lineTablePos = reader.Position; + var colTablePos = reader.Position + count * LINE_ENTRY_SIZE; for (uint i = 0; i < count; i++) { - stream.Position = lineTablePos + i * LINE_ENTRY_SIZE; + reader.Position = lineTablePos + i * LINE_ENTRY_SIZE; - var line = new DbiSourceLine { + var line = new SymbolSequencePoint { Document = document }; - line.Offset = stream.ReadUInt32(); - var lineFlags = stream.ReadUInt32(); + line.Offset = reader.ReadInt32(); + var lineFlags = reader.ReadUInt32(); - line.LineBegin = lineFlags & 0x00ffffff; - line.LineEnd = line.LineBegin + ((lineFlags >> 24) & 0x7F); + line.Line = (int)(lineFlags & 0x00ffffff); + line.EndLine = line.Line + (int)((lineFlags >> 24) & 0x7F); if ((flags & 1) != 0) { - stream.Position = colTablePos + i * COL_ENTRY_SIZE; - line.ColumnBegin = stream.ReadUInt16(); - line.ColumnEnd = stream.ReadUInt16(); + reader.Position = colTablePos + i * COL_ENTRY_SIZE; + line.Column = reader.ReadUInt16(); + line.EndColumn = reader.ReadUInt16(); } func.Lines.Add(line); @@ -208,4 +207,4 @@ void ReadLines(DbiFunction[] funcs, Dictionary documents, IIm } } } -} \ No newline at end of file +} diff --git a/src/DotNet/Pdb/Managed/DbiNamespace.cs b/src/DotNet/Pdb/Managed/DbiNamespace.cs index 8bcd235c5..abe3a888c 100644 --- a/src/DotNet/Pdb/Managed/DbiNamespace.cs +++ b/src/DotNet/Pdb/Managed/DbiNamespace.cs @@ -1,30 +1,12 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info -using System; -using System.Diagnostics.SymbolStore; +using dnlib.DotNet.Pdb.Symbols; namespace dnlib.DotNet.Pdb.Managed { - sealed class DbiNamespace : ISymbolNamespace { - public string Namespace { get; private set; } + sealed class DbiNamespace : SymbolNamespace { + public override string Name => name; + readonly string name; - public DbiNamespace(string ns) { - Namespace = ns; - } - - #region ISymbolNamespace - - public string Name { - get { return Namespace; } - } - - public ISymbolNamespace[] GetNamespaces() { - throw new NotImplementedException(); - } - - public ISymbolVariable[] GetVariables() { - throw new NotImplementedException(); - } - - #endregion + public DbiNamespace(string ns) => name = ns; } -} \ No newline at end of file +} diff --git a/src/DotNet/Pdb/Managed/DbiScope.cs b/src/DotNet/Pdb/Managed/DbiScope.cs index 5a1cdb06f..30c26fb2d 100644 --- a/src/DotNet/Pdb/Managed/DbiScope.cs +++ b/src/DotNet/Pdb/Managed/DbiScope.cs @@ -1,115 +1,202 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.SymbolStore; +using System.Text; +using dnlib.DotNet.Pdb.Symbols; using dnlib.IO; namespace dnlib.DotNet.Pdb.Managed { - sealed class DbiScope : ISymbolScope { - public DbiScope(string name, uint offset, uint length) { + sealed class DbiScope : SymbolScope { + readonly SymbolMethod method; + readonly SymbolScope parent; + internal int startOffset; + internal int endOffset; + readonly List childrenList; + readonly List localsList; + readonly List namespacesList; + + public override SymbolMethod Method => method; + public override SymbolScope Parent => parent; + public override int StartOffset => startOffset; + public override int EndOffset => endOffset; + public override IList Children => childrenList; + public override IList Locals => localsList; + public override IList Namespaces => namespacesList; + public override IList CustomDebugInfos => Array2.Empty(); + public override PdbImportScope ImportScope => null; + + public DbiScope(SymbolMethod method, SymbolScope parent, string name, uint offset, uint length) { + this.method = method; + this.parent = parent; Name = name; - BeginOffset = offset; - EndOffset = offset + length; + startOffset = (int)offset; + endOffset = (int)(offset + length); - Namespaces = new List(); - Variables = new List(); - Children = new List(); + childrenList = new List(); + localsList = new List(); + namespacesList = new List(); } public string Name { get; private set; } - public uint BeginOffset { get; internal set; } - public uint EndOffset { get; internal set; } - public IList Namespaces { get; private set; } - public IList Variables { get; private set; } - public IList Children { get; private set; } + List oemInfos; + List constants; + + readonly struct ConstantInfo { + public readonly string Name; + public readonly uint SignatureToken; + public readonly object Value; + public ConstantInfo(string name, uint signatureToken, object value) { + Name = name; + SignatureToken = signatureToken; + Value = value; + } + } + + internal readonly struct OemInfo { + public readonly string Name; + public readonly byte[] Data; + public OemInfo(string name, byte[] data) { + Name = name; + Data = data; + } + public override string ToString() => $"{Name} = ({Data.Length} bytes)"; + } + + static readonly byte[] dotNetOemGuid = new byte[] { + 0xC9, 0x3F, 0xEA, 0xC6, 0xB3, 0x59, 0xD6, 0x49, 0xBC, 0x25, 0x09, 0x02, 0xBB, 0xAB, 0xB4, 0x60 + }; - public void Read(RecursionCounter counter, IImageStream stream, uint scopeEnd) { + public void Read(RecursionCounter counter, ref DataReader reader, uint scopeEnd) { if (!counter.Increment()) throw new PdbException("Scopes too deep"); - while (stream.Position < scopeEnd) { - var size = stream.ReadUInt16(); - var begin = stream.Position; + while (reader.Position < scopeEnd) { + var size = reader.ReadUInt16(); + var begin = reader.Position; var end = begin + size; - var type = (SymbolType)stream.ReadUInt16(); + var type = (SymbolType)reader.ReadUInt16(); DbiScope child = null; uint? childEnd = null; + string name; switch (type) { case SymbolType.S_BLOCK32: { - stream.Position += 4; - childEnd = stream.ReadUInt32(); - var len = stream.ReadUInt32(); - var addr = PdbAddress.ReadAddress(stream); - var name = PdbReader.ReadCString(stream); - child = new DbiScope(name, addr.Offset, len); + reader.Position += 4; + childEnd = reader.ReadUInt32(); + var len = reader.ReadUInt32(); + var addr = PdbAddress.ReadAddress(ref reader); + name = PdbReader.ReadCString(ref reader); + child = new DbiScope(method, this, name, addr.Offset, len); break; } case SymbolType.S_UNAMESPACE: - Namespaces.Add(new DbiNamespace(PdbReader.ReadCString(stream))); + namespacesList.Add(new DbiNamespace(PdbReader.ReadCString(ref reader))); break; case SymbolType.S_MANSLOT: { var variable = new DbiVariable(); - variable.Read(stream); - Variables.Add(variable); + if (variable.Read(ref reader)) + localsList.Add(variable); break; } + case SymbolType.S_OEM: + if ((ulong)reader.Position + 20 > end) + break; + if (!ReadAndCompareBytes(ref reader, end, dotNetOemGuid)) { + Debug.Fail("Unknown OEM record GUID, not .NET GUID"); + break; + } + reader.Position += 4;// typeIndex or 0 + name = ReadUnicodeString(ref reader, end); + Debug.Assert(name is not null); + if (name is null) + break; + var data = reader.ReadBytes((int)(end - reader.Position)); + if (oemInfos is null) + oemInfos = new List(1); + oemInfos.Add(new OemInfo(name, data)); + break; + case SymbolType.S_MANCONSTANT: + uint signatureToken = reader.ReadUInt32(); + object value; + if (!NumericReader.TryReadNumeric(ref reader, end, out value)) + break; + name = PdbReader.ReadCString(ref reader); + if (constants is null) + constants = new List(); + constants.Add(new ConstantInfo(name, signatureToken, value)); + break; + case SymbolType.S_END: + break; + default: + break; } - stream.Position = end; - if (child != null) { - child.Read(counter, stream, childEnd.Value); - Children.Add(child); + reader.Position = end; + if (child is not null) { + child.Read(counter, ref reader, childEnd.Value); + childrenList.Add(child); child = null; } } counter.Decrement(); - if (stream.Position != scopeEnd) + if (reader.Position != scopeEnd) Debugger.Break(); } - #region ISymbolScope - - int ISymbolScope.StartOffset { - get { return (int)BeginOffset; } - } - - int ISymbolScope.EndOffset { - get { return (int)EndOffset; } - } - - ISymbolScope[] ISymbolScope.GetChildren() { - var scopes = new ISymbolScope[Children.Count]; - for (int i = 0; i < scopes.Length; i++) - scopes[i] = Children[i]; - return scopes; - } - - ISymbolVariable[] ISymbolScope.GetLocals() { - var vars = new ISymbolVariable[Variables.Count]; - for (int i = 0; i < vars.Length; i++) - vars[i] = Variables[i]; - return vars; + static string ReadUnicodeString(ref DataReader reader, uint end) { + var sb = new StringBuilder(); + for (;;) { + if ((ulong)reader.Position + 2 > end) + return null; + var c = reader.ReadChar(); + if (c == 0) + break; + sb.Append(c); + } + return sb.ToString(); } - ISymbolNamespace[] ISymbolScope.GetNamespaces() { - var nss = new ISymbolNamespace[Namespaces.Count]; - for (int i = 0; i < nss.Length; i++) - nss[i] = Namespaces[i]; - return nss; + static bool ReadAndCompareBytes(ref DataReader reader, uint end, byte[] bytes) { + if ((ulong)reader.Position + (uint)bytes.Length > end) + return false; + for (int i = 0; i < bytes.Length; i++) { + if (reader.ReadByte() != bytes[i]) + return false; + } + return true; } - ISymbolMethod ISymbolScope.Method { - get { throw new NotImplementedException(); } + public override IList GetConstants(ModuleDef module, GenericParamContext gpContext) { + if (constants is null) + return Array2.Empty(); + var res = new PdbConstant[constants.Count]; + for (int i = 0; i < res.Length; i++) { + var info = constants[i]; + TypeSig signature; + var saSig = module.ResolveToken(info.SignatureToken, gpContext) as StandAloneSig; + var fieldSig = saSig is null ? null : saSig.Signature as FieldSig; + if (fieldSig is null) { + Debug.Fail("Constant without a signature"); + signature = null; + } + else + signature = fieldSig.Type; + res[i] = new PdbConstant(info.Name, signature, info.Value); + } + return res; } - ISymbolScope ISymbolScope.Parent { - get { throw new NotImplementedException(); } + internal byte[] GetSymAttribute(string name) { + if (oemInfos is null) + return null; + foreach (var info in oemInfos) { + if (info.Name == name) + return info.Data; + } + return null; } - - #endregion } -} \ No newline at end of file +} diff --git a/src/DotNet/Pdb/Managed/DbiSourceLine.cs b/src/DotNet/Pdb/Managed/DbiSourceLine.cs deleted file mode 100644 index 3a5aea924..000000000 --- a/src/DotNet/Pdb/Managed/DbiSourceLine.cs +++ /dev/null @@ -1,12 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -namespace dnlib.DotNet.Pdb.Managed { - struct DbiSourceLine { - public DbiDocument Document; - public uint Offset; - public uint LineBegin; - public uint LineEnd; - public uint ColumnBegin; - public uint ColumnEnd; - } -} \ No newline at end of file diff --git a/src/DotNet/Pdb/Managed/DbiVariable.cs b/src/DotNet/Pdb/Managed/DbiVariable.cs index e3b886bab..46d655663 100644 --- a/src/DotNet/Pdb/Managed/DbiVariable.cs +++ b/src/DotNet/Pdb/Managed/DbiVariable.cs @@ -1,63 +1,39 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info -using System; -using System.Diagnostics.SymbolStore; +using System; +using dnlib.DotNet.Pdb.Symbols; using dnlib.IO; namespace dnlib.DotNet.Pdb.Managed { - sealed class DbiVariable : ISymbolVariable { - public uint Addr1 { get; private set; } - public string Name { get; private set; } - public ushort Flags { get; private set; } - - public void Read(IImageStream stream) { - Addr1 = stream.ReadUInt32(); - stream.Position += 10; - Flags = stream.ReadUInt16(); - Name = PdbReader.ReadCString(stream); - } - - #region ISymbolVariable + sealed class DbiVariable : SymbolVariable { + public override string Name => name; + string name; - public int AddressField1 { - get { return (int)Addr1; } - } + public override PdbLocalAttributes Attributes => attributes; + PdbLocalAttributes attributes; - public SymAddressKind AddressKind { - get { return SymAddressKind.ILOffset; } - } + public override int Index => index; + int index; - public object Attributes { - get { - const int fCompGenx = 4; - const int VAR_IS_COMP_GEN = 1; - if ((Flags & fCompGenx) != 0) - return VAR_IS_COMP_GEN; - else - return 0; - } - } + public override PdbCustomDebugInfo[] CustomDebugInfos => Array2.Empty(); - public int AddressField2 { - get { throw new NotImplementedException(); } - } + public bool Read(ref DataReader reader) { + index = reader.ReadInt32(); + reader.Position += 10; + ushort flags = reader.ReadUInt16(); + attributes = GetAttributes(flags); + name = PdbReader.ReadCString(ref reader); - public int AddressField3 { - get { throw new NotImplementedException(); } + const int fIsParam = 1; + return (flags & fIsParam) == 0; } - public int EndOffset { - get { throw new NotImplementedException(); } + static PdbLocalAttributes GetAttributes(uint flags) { + PdbLocalAttributes res = 0; + const int fCompGenx = 4; + if ((flags & fCompGenx) != 0) + res |= PdbLocalAttributes.DebuggerHidden; + return res; } - - public byte[] GetSignature() { - throw new NotImplementedException(); - } - - public int StartOffset { - get { throw new NotImplementedException(); } - } - - #endregion } -} \ No newline at end of file +} diff --git a/src/DotNet/Pdb/Managed/ModuleStreamType.cs b/src/DotNet/Pdb/Managed/ModuleStreamType.cs index 4354af428..c326068c8 100644 --- a/src/DotNet/Pdb/Managed/ModuleStreamType.cs +++ b/src/DotNet/Pdb/Managed/ModuleStreamType.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info namespace dnlib.DotNet.Pdb.Managed { enum ModuleStreamType : uint { @@ -15,4 +15,4 @@ enum ModuleStreamType : uint { TypeMDTokenMap = 0xFB, MergedAssemblyInput = 0xFC, } -} \ No newline at end of file +} diff --git a/src/DotNet/Pdb/Managed/MsfStream.cs b/src/DotNet/Pdb/Managed/MsfStream.cs index 0ada419a6..95cbdf18d 100644 --- a/src/DotNet/Pdb/Managed/MsfStream.cs +++ b/src/DotNet/Pdb/Managed/MsfStream.cs @@ -1,21 +1,23 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info -using System; +using System; using dnlib.IO; namespace dnlib.DotNet.Pdb.Managed { sealed class MsfStream { - public MsfStream(IImageStream[] pages, uint length) { - byte[] buf = new byte[length]; + public MsfStream(DataReader[] pages, uint length) { + var buf = new byte[length]; int offset = 0; - foreach (var page in pages) { + for (int i = 0; i < pages.Length; i++) { + var page = pages[i]; page.Position = 0; int len = Math.Min((int)page.Length, (int)(length - offset)); - offset += page.Read(buf, offset, len); + page.ReadBytes(buf, offset, len); + offset += len; } - Content = new MemoryImageStream(0, buf, 0, buf.Length); + Content = ByteArrayDataReaderFactory.CreateReader(buf); } - public IImageStream Content { get; set; } + public DataReader Content; } -} \ No newline at end of file +} diff --git a/src/DotNet/Pdb/Managed/NumericLeaf.cs b/src/DotNet/Pdb/Managed/NumericLeaf.cs new file mode 100644 index 000000000..acedd24ec --- /dev/null +++ b/src/DotNet/Pdb/Managed/NumericLeaf.cs @@ -0,0 +1,36 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.DotNet.Pdb.Managed { + enum NumericLeaf : ushort { + LF_NUMERIC = 0x8000, + LF_CHAR = 0x8000, + LF_SHORT = 0x8001, + LF_USHORT = 0x8002, + LF_LONG = 0x8003, + LF_ULONG = 0x8004, + LF_REAL32 = 0x8005, + LF_REAL64 = 0x8006, + LF_REAL80 = 0x8007, + LF_REAL128 = 0x8008, + LF_QUADWORD = 0x8009, + LF_UQUADWORD = 0x800A, + LF_REAL48 = 0x800B, + LF_COMPLEX32 = 0x800C, + LF_COMPLEX64 = 0x800D, + LF_COMPLEX80 = 0x800E, + LF_COMPLEX128 = 0x800F, + LF_VARSTRING = 0x8010, + LF_RESERVED_8011 = 0x8011, + LF_RESERVED_8012 = 0x8012, + LF_RESERVED_8013 = 0x8013, + LF_RESERVED_8014 = 0x8014, + LF_RESERVED_8015 = 0x8015, + LF_RESERVED_8016 = 0x8016, + LF_OCTWORD = 0x8017, + LF_UOCTWORD = 0x8018, + LF_VARIANT = 0x8019, + LF_DATE = 0x801A, + LF_UTF8STRING = 0x801B, + LF_REAL16 = 0x801C, + } +} diff --git a/src/DotNet/Pdb/Managed/NumericReader.cs b/src/DotNet/Pdb/Managed/NumericReader.cs new file mode 100644 index 000000000..566a85427 --- /dev/null +++ b/src/DotNet/Pdb/Managed/NumericReader.cs @@ -0,0 +1,101 @@ +// dnlib: See LICENSE.txt for more info + +using dnlib.IO; + +namespace dnlib.DotNet.Pdb.Managed { + static class NumericReader { + public static bool TryReadNumeric(ref DataReader reader, ulong end, out object value) { + value = null; + ulong position = reader.Position; + if (position + 2 > end) + return false; + var numLeaf = (NumericLeaf)reader.ReadUInt16(); + if (numLeaf < NumericLeaf.LF_NUMERIC) { + value = (short)numLeaf; + return true; + } + + switch (numLeaf) { + case NumericLeaf.LF_CHAR: + if (position > end) + return false; + value = reader.ReadSByte(); + return true; + + case NumericLeaf.LF_SHORT: + if (position + 2 > end) + return false; + value = reader.ReadInt16(); + return true; + + case NumericLeaf.LF_USHORT: + if (position + 2 > end) + return false; + value = reader.ReadUInt16(); + return true; + + case NumericLeaf.LF_LONG: + if (position + 4 > end) + return false; + value = reader.ReadInt32(); + return true; + + case NumericLeaf.LF_ULONG: + if (position + 4 > end) + return false; + value = reader.ReadUInt32(); + return true; + + case NumericLeaf.LF_REAL32: + if (position + 4 > end) + return false; + value = reader.ReadSingle(); + return true; + + case NumericLeaf.LF_REAL64: + if (position + 8 > end) + return false; + value = reader.ReadDouble(); + return true; + + case NumericLeaf.LF_QUADWORD: + if (position + 8 > end) + return false; + value = reader.ReadInt64(); + return true; + + case NumericLeaf.LF_UQUADWORD: + if (position + 8 > end) + return false; + value = reader.ReadUInt64(); + return true; + + case NumericLeaf.LF_VARSTRING: + if (position + 2 > end) + return false; + int varStrLen = reader.ReadUInt16(); + if (position + (uint)varStrLen > end) + return false; + value = reader.ReadUtf8String(varStrLen); + return true; + + case NumericLeaf.LF_VARIANT: + if (position + 0x10 > end) + return false; + int v0 = reader.ReadInt32(); + int v1 = reader.ReadInt32(); + int v2 = reader.ReadInt32(); + int v3 = reader.ReadInt32(); + byte scale = (byte)(v0 >> 16); + if (scale <= 28) + value = new decimal(v2, v3, v1, v0 < 0, scale); + else + value = null; + return true; + + default: + return false; + } + } + } +} diff --git a/src/DotNet/Pdb/Managed/PdbAddress.cs b/src/DotNet/Pdb/Managed/PdbAddress.cs index 1c884c48e..91ad32a02 100644 --- a/src/DotNet/Pdb/Managed/PdbAddress.cs +++ b/src/DotNet/Pdb/Managed/PdbAddress.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Diagnostics; @@ -9,7 +9,7 @@ namespace dnlib.DotNet.Pdb.Managed { /// An address in the image /// [DebuggerDisplay("{Section}:{Offset}")] - struct PdbAddress : IEquatable, IComparable { + readonly struct PdbAddress : IEquatable, IComparable { /// /// Section /// @@ -26,8 +26,8 @@ struct PdbAddress : IEquatable, IComparable { /// Section /// Offset in public PdbAddress(ushort section, int offset) { - this.Section = section; - this.Offset = (uint)offset; + Section = section; + Offset = (uint)offset; } /// @@ -36,8 +36,8 @@ public PdbAddress(ushort section, int offset) { /// Section /// Offset in public PdbAddress(ushort section, uint offset) { - this.Section = section; - this.Offset = offset; + Section = section; + Offset = offset; } /// @@ -46,9 +46,7 @@ public PdbAddress(ushort section, uint offset) { /// First /// Second /// - public static bool operator <=(PdbAddress a, PdbAddress b) { - return a.CompareTo(b) <= 0; - } + public static bool operator <=(PdbAddress a, PdbAddress b) => a.CompareTo(b) <= 0; /// /// Returns true if is less than @@ -56,9 +54,7 @@ public PdbAddress(ushort section, uint offset) { /// First /// Second /// - public static bool operator <(PdbAddress a, PdbAddress b) { - return a.CompareTo(b) < 0; - } + public static bool operator <(PdbAddress a, PdbAddress b) => a.CompareTo(b) < 0; /// /// Returns true if is greater than or equal to @@ -66,9 +62,7 @@ public PdbAddress(ushort section, uint offset) { /// First /// Second /// - public static bool operator >=(PdbAddress a, PdbAddress b) { - return a.CompareTo(b) >= 0; - } + public static bool operator >=(PdbAddress a, PdbAddress b) => a.CompareTo(b) >= 0; /// /// Returns true if is greater than @@ -76,9 +70,7 @@ public PdbAddress(ushort section, uint offset) { /// First /// Second /// - public static bool operator >(PdbAddress a, PdbAddress b) { - return a.CompareTo(b) > 0; - } + public static bool operator >(PdbAddress a, PdbAddress b) => a.CompareTo(b) > 0; /// /// Returns true if is equal to @@ -86,9 +78,7 @@ public PdbAddress(ushort section, uint offset) { /// First /// Second /// - public static bool operator ==(PdbAddress a, PdbAddress b) { - return a.Equals(b); - } + public static bool operator ==(PdbAddress a, PdbAddress b) => a.Equals(b); /// /// Returns true if is not equal to @@ -96,9 +86,7 @@ public PdbAddress(ushort section, uint offset) { /// First /// Second /// - public static bool operator !=(PdbAddress a, PdbAddress b) { - return !a.Equals(b); - } + public static bool operator !=(PdbAddress a, PdbAddress b) => !a.Equals(b); /// /// Compares this instance with and returns less than 0 if it's @@ -118,10 +106,7 @@ public int CompareTo(PdbAddress other) { /// /// The other one /// true if they're equal - public bool Equals(PdbAddress other) { - return Section == other.Section && - Offset == other.Offset; - } + public bool Equals(PdbAddress other) => Section == other.Section && Offset == other.Offset; /// /// Compares this to another instance @@ -138,24 +123,20 @@ public override bool Equals(object obj) { /// Gets the hash code /// /// Hash code - public override int GetHashCode() { - return (Section << 16) ^ (int)Offset; - } + public override int GetHashCode() => (Section << 16) ^ (int)Offset; /// /// ToString() override /// /// - public override string ToString() { - return string.Format("{0:X4}:{1:X8}", Section, Offset); - } + public override string ToString() => $"{Section:X4}:{Offset:X8}"; /// /// Reads a 32-bit offset followed by a 16-bit section and creates a new /// /// Reader /// - public static PdbAddress ReadAddress(IBinaryReader reader) { + public static PdbAddress ReadAddress(ref DataReader reader) { uint offs = reader.ReadUInt32(); return new PdbAddress(reader.ReadUInt16(), offs); } diff --git a/src/DotNet/Pdb/Managed/PdbException.cs b/src/DotNet/Pdb/Managed/PdbException.cs index 74bd308ec..43b8e3e01 100644 --- a/src/DotNet/Pdb/Managed/PdbException.cs +++ b/src/DotNet/Pdb/Managed/PdbException.cs @@ -1,19 +1,26 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info -using System; +using System; +using System.Runtime.Serialization; namespace dnlib.DotNet.Pdb.Managed { /// /// Exception that is thrown when encounters an error. /// [Serializable] - public sealed class PdbException : Exception { + sealed class PdbException : Exception { + /// + /// Constructor + /// + public PdbException() { + } + /// /// Constructor /// /// Exception message public PdbException(string message) - : base("Failed to read PDB: " + message) { + : base($"Failed to read PDB: {message}") { } /// @@ -21,7 +28,16 @@ public PdbException(string message) /// /// Inner exception public PdbException(Exception innerException) - : base("Failed to read PDB: " + innerException.Message, innerException) { + : base($"Failed to read PDB: {innerException.Message}", innerException) { + } + + /// + /// Constructor + /// + /// + /// + public PdbException(SerializationInfo info, StreamingContext context) + : base(info, context) { } } -} \ No newline at end of file +} diff --git a/src/DotNet/Pdb/Managed/PdbReader.cs b/src/DotNet/Pdb/Managed/PdbReader.cs index 99cfd00c3..92a20cc91 100644 --- a/src/DotNet/Pdb/Managed/PdbReader.cs +++ b/src/DotNet/Pdb/Managed/PdbReader.cs @@ -1,21 +1,24 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics.SymbolStore; using System.Text; +using dnlib.DotNet.Emit; +using dnlib.DotNet.Pdb.Symbols; +using dnlib.DotNet.Pdb.WindowsPdb; using dnlib.IO; namespace dnlib.DotNet.Pdb.Managed { /// /// A managed PDB reader implementation for .NET modules. /// - public sealed class PdbReader : ISymbolReader { + sealed class PdbReader : SymbolReader { MsfStream[] streams; Dictionary names; Dictionary strings; List modules; + ModuleDef module; const int STREAM_ROOT = 0; const int STREAM_NAMES = 1; @@ -24,25 +27,34 @@ public sealed class PdbReader : ISymbolReader { const ushort STREAM_INVALID_INDEX = ushort.MaxValue; Dictionary documents; - Dictionary functions; + Dictionary functions; + byte[] sourcelinkData; + byte[] srcsrvData; uint entryPt; - /// - /// The age of PDB file. - /// - public uint Age { get; private set; } - /// - /// The GUID of PDB file. - /// - public Guid Guid { get; private set; } + public override PdbFileKind PdbFileKind => PdbFileKind.WindowsPDB; + + uint Age { get; set; } + Guid Guid { get; set; } + + internal bool MatchesModule => expectedGuid == Guid && expectedAge == Age; + readonly Guid expectedGuid; + readonly uint expectedAge; + + public PdbReader(Guid expectedGuid, uint expectedAge) { + this.expectedGuid = expectedGuid; + this.expectedAge = expectedAge; + } + + public override void Initialize(ModuleDef module) => this.module = module; /// /// Read the PDB in the specified stream. /// - /// The stream that contains the PDB file - public void Read(IImageStream stream) { + /// PDB file data reader + public void Read(DataReader reader) { try { - ReadInternal(stream); + ReadInternal(ref reader); } catch (Exception ex) { if (ex is PdbException) @@ -57,79 +69,82 @@ public void Read(IImageStream stream) { } } - static uint RoundUpDiv(uint value, uint divisor) { - return (value + divisor - 1) / divisor; - } + static uint RoundUpDiv(uint value, uint divisor) => (value + divisor - 1) / divisor; - void ReadInternal(IImageStream stream) { - stream.Position = 0; - string sig = Encoding.ASCII.GetString(stream.ReadBytes(30)); + void ReadInternal(ref DataReader reader) { + string sig = reader.ReadString(30, Encoding.ASCII); if (sig != "Microsoft C/C++ MSF 7.00\r\n\u001ADS\0") throw new PdbException("Invalid signature"); - stream.Position += 2; + reader.Position += 2; - uint pageSize = stream.ReadUInt32(); - uint fpm = stream.ReadUInt32(); - uint pageCount = stream.ReadUInt32(); - uint rootSize = stream.ReadUInt32(); - stream.ReadUInt32(); + uint pageSize = reader.ReadUInt32(); + /*uint fpm = */reader.ReadUInt32(); + uint pageCount = reader.ReadUInt32(); + uint rootSize = reader.ReadUInt32(); + reader.ReadUInt32(); var numOfRootPages = RoundUpDiv(rootSize, pageSize); var numOfPtrPages = RoundUpDiv(numOfRootPages * 4, pageSize); - if (pageCount * pageSize != stream.Length) + if (pageCount * pageSize != reader.Length) throw new PdbException("File size mismatch"); - var pages = new IImageStream[pageCount]; - try { - FileOffset offset = 0; - for (uint i = 0; i < pageCount; i++) { - pages[i] = stream.Create(offset, pageSize); - offset += pageSize; - } - - var rootPages = new IImageStream[numOfRootPages]; - int pageIndex = 0; - for (int i = 0; i < numOfPtrPages && pageIndex < numOfRootPages; i++) { - var ptrPage = pages[stream.ReadUInt32()]; - ptrPage.Position = 0; - for (; ptrPage.Position < ptrPage.Length && pageIndex < numOfRootPages; pageIndex++) - rootPages[pageIndex] = pages[ptrPage.ReadUInt32()]; - } - - ReadRootDirectory(new MsfStream(rootPages, rootSize), pages, pageSize); + var pages = new DataReader[pageCount]; + uint offset = 0; + for (uint i = 0; i < pageCount; i++) { + pages[i] = reader.Slice(offset, pageSize); + offset += pageSize; } - finally { - foreach (var page in pages) { - if (page != null) - page.Dispose(); - } + + var rootPages = new DataReader[numOfRootPages]; + int pageIndex = 0; + for (int i = 0; i < numOfPtrPages && pageIndex < numOfRootPages; i++) { + var ptrPage = pages[reader.ReadUInt32()]; + ptrPage.Position = 0; + for (; ptrPage.Position < ptrPage.Length && pageIndex < numOfRootPages; pageIndex++) + rootPages[pageIndex] = pages[ptrPage.ReadUInt32()]; } + ReadRootDirectory(new MsfStream(rootPages, rootSize), pages, pageSize); + ReadNames(); + if (!MatchesModule) + return; ReadStringTable(); var tokenMapStream = ReadModules(); documents = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var module in modules) + foreach (var module in modules) { if (IsValidStreamIndex(module.StreamId)) - module.LoadFunctions(this, streams[module.StreamId].Content); + module.LoadFunctions(this, ref streams[module.StreamId].Content); + } if (IsValidStreamIndex(tokenMapStream ?? STREAM_INVALID_INDEX)) - ApplyRidMap(streams[tokenMapStream.Value].Content); + ApplyRidMap(ref streams[tokenMapStream.Value].Content); - functions = new Dictionary(); - foreach (var module in modules) + functions = new Dictionary(); + foreach (var module in modules) { foreach (var func in module.Functions) { + func.reader = this; functions.Add(func.Token, func); } + } + + sourcelinkData = TryGetRawFileData("sourcelink"); + srcsrvData = TryGetRawFileData("srcsrv"); } - bool IsValidStreamIndex(ushort index) { - return index != STREAM_INVALID_INDEX && index < streams.Length; + byte[] TryGetRawFileData(string name) { + if (!names.TryGetValue(name, out uint streamId)) + return null; + if (streamId > ushort.MaxValue || !IsValidStreamIndex((ushort)streamId)) + return null; + return streams[streamId].Content.ToArray(); } - void ReadRootDirectory(MsfStream stream, IImageStream[] pages, uint pageSize) { + bool IsValidStreamIndex(ushort index) => index != STREAM_INVALID_INDEX && index < streams.Length; + + void ReadRootDirectory(MsfStream stream, DataReader[] pages, uint pageSize) { uint streamNum = stream.Content.ReadUInt32(); - uint[] streamSizes = new uint[streamNum]; + var streamSizes = new uint[streamNum]; for (int i = 0; i < streamSizes.Length; i++) streamSizes[i] = stream.Content.ReadUInt32(); @@ -140,7 +155,7 @@ void ReadRootDirectory(MsfStream stream, IImageStream[] pages, uint pageSize) { continue; } var pageCount = RoundUpDiv(streamSizes[i], pageSize); - var streamPages = new IImageStream[pageCount]; + var streamPages = new DataReader[pageCount]; for (int j = 0; j < streamPages.Length; j++) streamPages[j] = pages[stream.Content.ReadUInt32()]; streams[i] = new MsfStream(streamPages, streamSizes[i]); @@ -148,92 +163,90 @@ void ReadRootDirectory(MsfStream stream, IImageStream[] pages, uint pageSize) { } void ReadNames() { - var stream = streams[STREAM_NAMES].Content; + ref var stream = ref streams[STREAM_NAMES].Content; stream.Position = 8; Age = stream.ReadUInt32(); - Guid = new Guid(stream.ReadBytes(0x10)); + Guid = stream.ReadGuid(); uint nameSize = stream.ReadUInt32(); - using (var nameData = stream.Create(stream.FileOffset + stream.Position, nameSize)) { - stream.Position += nameSize; - - uint entryCount = stream.ReadUInt32(); - uint entryCapacity = stream.ReadUInt32(); - var entryOk = new BitArray(stream.ReadBytes(stream.ReadInt32() * 4)); - if (stream.ReadUInt32() != 0) - throw new NotSupportedException(); - - names = new Dictionary(StringComparer.OrdinalIgnoreCase); - entryCapacity = Math.Min(entryCapacity, (uint)entryOk.Count); - for (int i = 0; i < entryCapacity; i++) { - if (!entryOk[i]) - continue; - - var pos = stream.ReadUInt32(); - var streamId = stream.ReadUInt32(); - nameData.Position = pos; - var streamName = ReadCString(nameData); - names[streamName] = streamId; - } + var nameData = stream.Slice(stream.Position, nameSize); + stream.Position += nameSize; + + /*uint entryCount = */stream.ReadUInt32(); + uint entryCapacity = stream.ReadUInt32(); + var entryOk = new BitArray(stream.ReadBytes(stream.ReadInt32() * 4)); + if (stream.ReadUInt32() != 0) + throw new NotSupportedException(); + + names = new Dictionary(StringComparer.OrdinalIgnoreCase); + entryCapacity = Math.Min(entryCapacity, (uint)entryOk.Count); + for (int i = 0; i < entryCapacity; i++) { + if (!entryOk[i]) + continue; + + var pos = stream.ReadUInt32(); + var streamId = stream.ReadUInt32(); + nameData.Position = pos; + var streamName = ReadCString(ref nameData); + names[streamName] = streamId; } } void ReadStringTable() { - uint streamId; - if (!names.TryGetValue("/names", out streamId)) + if (!names.TryGetValue("/names", out uint streamId)) throw new PdbException("String table not found"); - var stream = streams[streamId].Content; + ref var stream = ref streams[streamId].Content; stream.Position = 8; uint strSize = stream.ReadUInt32(); - using (var strData = stream.Create(stream.FileOffset + stream.Position, strSize)) { - stream.Position += strSize; - - strings = new Dictionary(); - uint count = stream.ReadUInt32(); - for (uint i = 0; i < count; i++) { - var pos = stream.ReadUInt32(); - if (pos == 0) - continue; - strData.Position = pos; - strings[pos] = ReadCString(strData); - } + var strData = stream.Slice(stream.Position, strSize); + stream.Position += strSize; + + uint count = stream.ReadUInt32(); + strings = new Dictionary((int)count); + for (uint i = 0; i < count; i++) { + var pos = stream.ReadUInt32(); + if (pos == 0) + continue; + strData.Position = pos; + strings[pos] = ReadCString(ref strData); } } - static uint ReadSizeField(IBinaryReader reader) { + static uint ReadSizeField(ref DataReader reader) { int size = reader.ReadInt32(); return size <= 0 ? 0 : (uint)size; } ushort? ReadModules() { - var stream = streams[STREAM_DBI].Content; + ref var stream = ref streams[STREAM_DBI].Content; + modules = new List(); + if (stream.Length == 0) + return null; stream.Position = 20; ushort symrecStream = stream.ReadUInt16(); stream.Position += 2; - uint gpmodiSize = ReadSizeField(stream); // gpmodiSize + uint gpmodiSize = ReadSizeField(ref stream); // gpmodiSize uint otherSize = 0; - otherSize += ReadSizeField(stream); // secconSize - otherSize += ReadSizeField(stream); // secmapSize - otherSize += ReadSizeField(stream); // filinfSize - otherSize += ReadSizeField(stream); // tsmapSize + otherSize += ReadSizeField(ref stream); // secconSize + otherSize += ReadSizeField(ref stream); // secmapSize + otherSize += ReadSizeField(ref stream); // filinfSize + otherSize += ReadSizeField(ref stream); // tsmapSize stream.ReadUInt32(); // mfcIndex - uint dbghdrSize = ReadSizeField(stream); - otherSize += ReadSizeField(stream); // ecinfoSize + uint dbghdrSize = ReadSizeField(ref stream); + otherSize += ReadSizeField(ref stream); // ecinfoSize stream.Position += 8; - modules = new List(); - using (var moduleStream = stream.Create((FileOffset)stream.Position, gpmodiSize)) { - while (moduleStream.Position < moduleStream.Length) { - var module = new DbiModule(); - module.Read(moduleStream); - modules.Add(module); - } + var moduleStream = stream.Slice(stream.Position, gpmodiSize); + while (moduleStream.Position < moduleStream.Length) { + var module = new DbiModule(); + module.Read(ref moduleStream); + modules.Add(module); } if (IsValidStreamIndex(symrecStream)) - ReadGlobalSymbols(streams[symrecStream].Content); + ReadGlobalSymbols(ref streams[symrecStream].Content); if (dbghdrSize != 0) { stream.Position += gpmodiSize; @@ -247,51 +260,52 @@ static uint ReadSizeField(IBinaryReader reader) { internal DbiDocument GetDocument(uint nameId) { var name = strings[nameId]; - DbiDocument doc; - if (!documents.TryGetValue(name, out doc)) { + if (!documents.TryGetValue(name, out var doc)) { doc = new DbiDocument(name); - uint streamId; - if (names.TryGetValue("/src/files/" + name, out streamId)) - doc.Read(streams[streamId].Content); + if (names.TryGetValue($"/src/files/{name}", out uint streamId)) + doc.Read(ref streams[streamId].Content); documents.Add(name, doc); } return doc; } - void ReadGlobalSymbols(IImageStream stream) { - stream.Position = 0; - while (stream.Position < stream.Length) { - var size = stream.ReadUInt16(); - var begin = stream.Position; + void ReadGlobalSymbols(ref DataReader reader) { + reader.Position = 0; + while (reader.Position < reader.Length) { + var size = reader.ReadUInt16(); + var begin = reader.Position; var end = begin + size; - if ((SymbolType)stream.ReadUInt16() == SymbolType.S_PUB32) { - stream.Position += 4; - var offset = stream.ReadUInt32(); - stream.Position += 2; - var name = ReadCString(stream); + if ((SymbolType)reader.ReadUInt16() == SymbolType.S_PUB32) { + reader.Position += 4; + var offset = reader.ReadUInt32(); + reader.Position += 2; + var name = ReadCString(ref reader); - if (name == "COM+_Entry_Point") + if (name == "COM+_Entry_Point") { entryPt = offset; + break; + } } - stream.Position = end; + reader.Position = end; } } - void ApplyRidMap(IImageStream stream) { - stream.Position = 0; - var map = new uint[stream.Length / 4]; + void ApplyRidMap(ref DataReader reader) { + reader.Position = 0; + var map = new uint[reader.Length / 4]; for (int i = 0; i < map.Length; i++) - map[i] = stream.ReadUInt32(); + map[i] = reader.ReadUInt32(); - foreach (var module in modules) + foreach (var module in modules) { foreach (var func in module.Functions) { - var rid = func.Token & 0x00ffffff; + var rid = (uint)func.Token & 0x00ffffff; rid = map[rid]; - func.Token = (func.Token & 0xff000000) | rid; + func.token = (int)((func.Token & 0xff000000) | rid); } + } if (entryPt != 0) { var rid = entryPt & 0x00ffffff; @@ -300,61 +314,54 @@ void ApplyRidMap(IImageStream stream) { } } - internal static string ReadCString(IImageStream stream) { - var value = Encoding.UTF8.GetString(stream.ReadBytesUntilByte(0)); - stream.Position++; - return value; - } - - #region ISymbolReader + internal static string ReadCString(ref DataReader reader) => reader.TryReadZeroTerminatedUtf8String() ?? string.Empty; - ISymbolMethod ISymbolReader.GetMethod(SymbolToken method) { - DbiFunction symMethod; - if (functions.TryGetValue((uint)method.GetToken(), out symMethod)) + public override SymbolMethod GetMethod(MethodDef method, int version) { + if (version != 1) + return null; + if (functions.TryGetValue(method.MDToken.ToInt32(), out var symMethod)) return symMethod; return null; } - ISymbolDocument[] ISymbolReader.GetDocuments() { - var docs = new ISymbolDocument[documents.Count]; - int i = 0; - foreach (var doc in documents.Values) - docs[i++] = doc; - return docs; - } - - SymbolToken ISymbolReader.UserEntryPoint { - get { return new SymbolToken((int)entryPt); } - } - - ISymbolDocument ISymbolReader.GetDocument(string url, Guid language, Guid languageVendor, Guid documentType) { - throw new NotImplementedException(); - } - - ISymbolVariable[] ISymbolReader.GetGlobalVariables() { - throw new NotImplementedException(); + public override IList Documents { + get { + if (documentsResult is null) { + var docs = new SymbolDocument[documents.Count]; + int i = 0; + foreach (var kv in documents) + docs[i++] = kv.Value; + documentsResult = docs; + } + return documentsResult; + } } + volatile SymbolDocument[] documentsResult; - ISymbolMethod ISymbolReader.GetMethod(SymbolToken method, int version) { - throw new NotImplementedException(); - } + public override int UserEntryPoint => (int)entryPt; - ISymbolMethod ISymbolReader.GetMethodFromDocumentPosition(ISymbolDocument document, int line, int column) { - throw new NotImplementedException(); - } + internal void GetCustomDebugInfos(DbiFunction symMethod, MethodDef method, CilBody body, IList result) { + const string CDI_NAME = "MD2"; + var asyncMethod = PseudoCustomDebugInfoFactory.TryCreateAsyncMethod(method.Module, method, body, symMethod.AsyncKickoffMethod, symMethod.AsyncStepInfos, symMethod.AsyncCatchHandlerILOffset); + if (asyncMethod is not null) + result.Add(asyncMethod); - ISymbolNamespace[] ISymbolReader.GetNamespaces() { - throw new NotImplementedException(); + var cdiData = symMethod.Root.GetSymAttribute(CDI_NAME); + if (cdiData is null) + return; + PdbCustomDebugInfoReader.Read(method, body, result, cdiData); } - byte[] ISymbolReader.GetSymAttribute(SymbolToken parent, string name) { - throw new NotImplementedException(); + public override void GetCustomDebugInfos(int token, GenericParamContext gpContext, IList result) { + if (token == 0x00000001) + GetCustomDebugInfos_ModuleDef(result); } - ISymbolVariable[] ISymbolReader.GetVariables(SymbolToken parent) { - throw new NotImplementedException(); + void GetCustomDebugInfos_ModuleDef(IList result) { + if (sourcelinkData is not null) + result.Add(new PdbSourceLinkCustomDebugInfo(sourcelinkData)); + if (srcsrvData is not null) + result.Add(new PdbSourceServerCustomDebugInfo(srcsrvData)); } - - #endregion } -} \ No newline at end of file +} diff --git a/src/DotNet/Pdb/Managed/SymbolReaderCreator.cs b/src/DotNet/Pdb/Managed/SymbolReaderCreator.cs deleted file mode 100644 index 37a237e11..000000000 --- a/src/DotNet/Pdb/Managed/SymbolReaderCreator.cs +++ /dev/null @@ -1,84 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Diagnostics.SymbolStore; -using System.IO; -using System.Security; -using dnlib.IO; - -namespace dnlib.DotNet.Pdb.Managed { - /// - /// Creates a instance - /// - public static class SymbolReaderCreator { - /// - /// Creates a new instance - /// - /// Path to assembly - /// A new instance or null if there's no PDB - /// file. - public static ISymbolReader CreateFromAssemblyFile(string assemblyFileName) { - return Create(Path.ChangeExtension(assemblyFileName, "pdb")); - } - - /// - /// Creates a new instance - /// - /// Path to PDB file - /// A new instance or null if there's no PDB - /// file on disk. - public static ISymbolReader Create(string pdbFileName) { - return Create(OpenImageStream(pdbFileName)); - } - - /// - /// Creates a new instance - /// - /// PDB file data - /// A new instance or null. - public static ISymbolReader Create(byte[] pdbData) { - return Create(MemoryImageStream.Create(pdbData)); - } - - /// - /// Creates a new instance - /// - /// PDB file stream which is now owned by this method - /// A new instance or null. - public static ISymbolReader Create(IImageStream pdbStream) { - if (pdbStream == null) - return null; - try { - var pdbReader = new PdbReader(); - pdbReader.Read(pdbStream); - return pdbReader; - } - catch (IOException) { - } - catch (UnauthorizedAccessException) { - } - catch (SecurityException) { - } - finally { - if (pdbStream != null) - pdbStream.Dispose(); - } - return null; - } - - static IImageStream OpenImageStream(string fileName) { - try { - if (!File.Exists(fileName)) - return null; - return ImageStreamCreator.CreateImageStream(fileName); - } - catch (IOException) { - } - catch (UnauthorizedAccessException) { - } - catch (SecurityException) { - } - return null; - } - } -} diff --git a/src/DotNet/Pdb/Managed/SymbolReaderFactory.cs b/src/DotNet/Pdb/Managed/SymbolReaderFactory.cs new file mode 100644 index 000000000..8962eb631 --- /dev/null +++ b/src/DotNet/Pdb/Managed/SymbolReaderFactory.cs @@ -0,0 +1,44 @@ +// dnlib: See LICENSE.txt for more info + +using System.IO; +using dnlib.DotNet.Pdb.Symbols; +using dnlib.IO; + +namespace dnlib.DotNet.Pdb.Managed { + /// + /// Creates a instance + /// + static class SymbolReaderFactory { + /// + /// Creates a new instance + /// + /// PDB context + /// PDB file stream which is now owned by this method + /// A new instance or null. + public static SymbolReader Create(PdbReaderContext pdbContext, DataReaderFactory pdbStream) { + if (pdbStream is null) + return null; + try { + var debugDir = pdbContext.CodeViewDebugDirectory; + if (debugDir is null) + return null; + if (!pdbContext.TryGetCodeViewData(out var pdbGuid, out uint age)) + return null; + + var pdbReader = new PdbReader(pdbGuid, age); + pdbReader.Read(pdbStream.CreateReader()); + if (pdbReader.MatchesModule) + return pdbReader; + return null; + } + catch (PdbException) { + } + catch (IOException) { + } + finally { + pdbStream?.Dispose(); + } + return null; + } + } +} diff --git a/src/DotNet/Pdb/Managed/SymbolType.cs b/src/DotNet/Pdb/Managed/SymbolType.cs index 831f77f03..66a1becd5 100644 --- a/src/DotNet/Pdb/Managed/SymbolType.cs +++ b/src/DotNet/Pdb/Managed/SymbolType.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info namespace dnlib.DotNet.Pdb.Managed { enum SymbolType : ushort { @@ -198,4 +198,4 @@ enum SymbolType : ushort { S_RECTYPE_MAX, } -} \ No newline at end of file +} diff --git a/src/DotNet/Pdb/PdbConstant.cs b/src/DotNet/Pdb/PdbConstant.cs new file mode 100644 index 000000000..11fb6fabc --- /dev/null +++ b/src/DotNet/Pdb/PdbConstant.cs @@ -0,0 +1,80 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; + +namespace dnlib.DotNet.Pdb { + /// + /// A constant in a method scope, eg. "const int SomeConstant = 123;" + /// + public sealed class PdbConstant : IHasCustomDebugInformation { + string name; + TypeSig type; + object value; + + /// + /// Gets/sets the name + /// + public string Name { + get => name; + set => name = value; + } + + /// + /// Gets/sets the type of the constant + /// + public TypeSig Type { + get => type; + set => type = value; + } + + /// + /// Gets/sets the value of the constant + /// + public object Value { + get => value; + set => this.value = value; + } + + /// + /// Constructor + /// + public PdbConstant() { + } + + /// + /// Constructor + /// + /// Name of constant + /// Type of constant + /// Constant value + public PdbConstant(string name, TypeSig type, object value) { + this.name = name; + this.type = type; + this.value = value; + } + + /// + public int HasCustomDebugInformationTag => 25; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos => customDebugInfos; + readonly IList customDebugInfos = new List(); + + /// + /// ToString() + /// + /// + public override string ToString() { + var type = Type; + var value = Value; + string typeString = type is null ? "" : type.ToString(); + string valueString = value is null ? "null" : $"{value} ({value.GetType().FullName})"; + return$"{typeString} {Name} = {valueString}"; + } + } +} diff --git a/src/DotNet/Pdb/PdbCustomDebugInfo.cs b/src/DotNet/Pdb/PdbCustomDebugInfo.cs new file mode 100644 index 000000000..6b013d0cc --- /dev/null +++ b/src/DotNet/Pdb/PdbCustomDebugInfo.cs @@ -0,0 +1,1309 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Collections.Generic; +using System.Threading; +using dnlib.DotNet.Emit; + +namespace dnlib.DotNet.Pdb { + /// + /// Custom debug info kind + /// + /// See CustomDebugInfoKind in Roslyn source code + public enum PdbCustomDebugInfoKind { + /// + /// + /// + UsingGroups, + + /// + /// + /// + ForwardMethodInfo, + + /// + /// + /// + ForwardModuleInfo, + + /// + /// + /// + StateMachineHoistedLocalScopes, + + /// + /// + /// + StateMachineTypeName, + + /// + /// + /// + DynamicLocals, + + /// + /// + /// + EditAndContinueLocalSlotMap, + + /// + /// + /// + EditAndContinueLambdaMap, + + /// + /// + /// + TupleElementNames, + + // Values 0x00-0xFF are reserved for Windows PDB CDIs. + + /// + /// Unknown + /// + Unknown = int.MinValue, + + /// + /// + /// + TupleElementNames_PortablePdb, + + /// + /// + /// + DefaultNamespace, + + /// + /// + /// + DynamicLocalVariables, + + /// + /// + /// + EmbeddedSource, + + /// + /// + /// + SourceLink, + + /// + /// + /// + SourceServer, + + /// + /// + /// + AsyncMethod, + + /// + /// + /// + IteratorMethod, + + /// + /// + /// + CompilationMetadataReferences, + + /// + /// + /// + CompilationOptions, + + /// + /// + /// + TypeDefinitionDocuments, + + /// + /// + /// + EditAndContinueStateMachineStateMap, + + /// + /// + /// + PrimaryConstructorInformationBlob, + } + + /// + /// Base class of custom debug info added to the PDB file by the compiler + /// + public abstract class PdbCustomDebugInfo { + /// + /// Gets the custom debug info kind + /// + public abstract PdbCustomDebugInfoKind Kind { get; } + + /// + /// Gets the custom debug info guid, see + /// + public abstract Guid Guid { get; } + } + + /// + /// Unknown custom debug info. If you see an instance of this class, you're using an old dnlib version or + /// dnlib hasn't been updated to support this new custom debug info kind. + /// + public sealed class PdbUnknownCustomDebugInfo : PdbCustomDebugInfo { + readonly PdbCustomDebugInfoKind kind; + readonly Guid guid; + readonly byte[] data; + + /// + /// Gets the custom debug info kind + /// + public override PdbCustomDebugInfoKind Kind => kind; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => guid; + + /// + /// Gets the data + /// + public byte[] Data => data; + + /// + /// Constructor + /// + /// Custom debug info kind + /// Raw custom debug info data + public PdbUnknownCustomDebugInfo(PdbCustomDebugInfoKind kind, byte[] data) { + this.kind = kind; + this.data = data ?? throw new ArgumentNullException(nameof(data)); + guid = Guid.Empty; + } + + /// + /// Constructor + /// + /// Custom debug info guid + /// Raw custom debug info data + public PdbUnknownCustomDebugInfo(Guid guid, byte[] data) { + kind = PdbCustomDebugInfoKind.Unknown; + this.data = data ?? throw new ArgumentNullException(nameof(data)); + this.guid = guid; + } + } + + /// + /// Contains sizes of using groups + /// + public sealed class PdbUsingGroupsCustomDebugInfo : PdbCustomDebugInfo { + readonly IList usingCounts; + + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.UsingGroups; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => Guid.Empty; + + /// + /// Gets the using counts + /// + public IList UsingCounts => usingCounts; + + /// + /// Constructor + /// + public PdbUsingGroupsCustomDebugInfo() => usingCounts = new List(); + + /// + /// Constructor + /// + /// Initial capacity of + public PdbUsingGroupsCustomDebugInfo(int capacity) => usingCounts = new List(capacity); + } + + /// + /// Contains a reference to another method that contains the import strings + /// + public sealed class PdbForwardMethodInfoCustomDebugInfo : PdbCustomDebugInfo { + IMethodDefOrRef method; + + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.ForwardMethodInfo; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => Guid.Empty; + + /// + /// Gets/sets the referenced method + /// + public IMethodDefOrRef Method { + get => method; + set => method = value; + } + + /// + /// Constructor + /// + public PdbForwardMethodInfoCustomDebugInfo() { + } + + /// + /// Constructor + /// + /// The referenced method + public PdbForwardMethodInfoCustomDebugInfo(IMethodDefOrRef method) => this.method = method; + } + + /// + /// Contains a reference to another method that contains the per-module debug info (assembly reference aliases) + /// + public sealed class PdbForwardModuleInfoCustomDebugInfo : PdbCustomDebugInfo { + IMethodDefOrRef method; + + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.ForwardModuleInfo; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => Guid.Empty; + + /// + /// Gets/sets the referenced method + /// + public IMethodDefOrRef Method { + get => method; + set => method = value; + } + + /// + /// Constructor + /// + public PdbForwardModuleInfoCustomDebugInfo() { + } + + /// + /// Constructor + /// + /// The referenced method + public PdbForwardModuleInfoCustomDebugInfo(IMethodDefOrRef method) => this.method = method; + } + + /// + /// State machine hosted local scope info + /// + public struct StateMachineHoistedLocalScope { + /// + /// true if it's a syntesized local ( and are both null) + /// + public readonly bool IsSynthesizedLocal => Start is null && End is null; + + /// + /// The instruction of the first operation in the scope. Can be null if it's a synthesized local + /// + public Instruction Start; + + /// + /// The instruction of the first operation outside of the scope or null if it ends at the last instruction in the body. + /// Can also be null if it's a synthesized local (in which case is also null, see ) + /// + public Instruction End; + + /// + /// Constructor + /// + /// Start of the scope + /// First instruction after the end of the scope + public StateMachineHoistedLocalScope(Instruction start, Instruction end) { + Start = start; + End = end; + } + } + + /// + /// Contains local scopes for state machine hoisted local variables. + /// + public sealed class PdbStateMachineHoistedLocalScopesCustomDebugInfo : PdbCustomDebugInfo { + readonly IList scopes; + + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.StateMachineHoistedLocalScopes; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.StateMachineHoistedLocalScopes; + + /// + /// Gets the scopes + /// + public IList Scopes => scopes; + + /// + /// Constructor + /// + public PdbStateMachineHoistedLocalScopesCustomDebugInfo() => scopes = new List(); + + /// + /// Constructor + /// + /// Initial capacity of + public PdbStateMachineHoistedLocalScopesCustomDebugInfo(int capacity) => scopes = new List(capacity); + } + + /// + /// Contains the state machine type + /// + public sealed class PdbStateMachineTypeNameCustomDebugInfo : PdbCustomDebugInfo { + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.StateMachineTypeName; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => Guid.Empty; + + /// + /// Gets/sets the state machine type + /// + public TypeDef Type { get; set; } + + /// + /// Constructor + /// + public PdbStateMachineTypeNameCustomDebugInfo() { + } + + /// + /// Constructor + /// + /// State machine type + public PdbStateMachineTypeNameCustomDebugInfo(TypeDef type) => Type = type; + } + + /// + /// Contains dynamic flags for local variables and constants + /// + public sealed class PdbDynamicLocalsCustomDebugInfo : PdbCustomDebugInfo { + readonly IList locals; + + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.DynamicLocals; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => Guid.Empty; + + /// + /// Gets the dynamic locals + /// + public IList Locals => locals; + + /// + /// Constructor + /// + public PdbDynamicLocalsCustomDebugInfo() => locals = new List(); + + /// + /// Constructor + /// + /// Initial capacity of + public PdbDynamicLocalsCustomDebugInfo(int capacity) => locals = new List(capacity); + } + + /// + /// Dynamic local info + /// + public sealed class PdbDynamicLocal { + readonly IList flags; + string name; + Local local; + + /// + /// Gets the dynamic flags + /// + public IList Flags => flags; + + /// + /// Gets/sets the name of the local. The name must have at most 64 characters and no char can be NUL (0x0000). + /// If null is written, is returned instead. + /// + public string Name { + get { + var n = name; + if (n is not null) + return n; + return local?.Name; + } + set => name = value; + } + + /// + /// true if it's a constant and not a variable ( is null) + /// + public bool IsConstant => Local is null; + + /// + /// true if it's a variable ( is not null) + /// + public bool IsVariable => Local is not null; + + /// + /// Gets/sets the local. Could be null if there's no local (it's a 'const' local). + /// + public Local Local { + get => local; + set => local = value; + } + + /// + /// Constructor + /// + public PdbDynamicLocal() => flags = new List(); + + /// + /// Constructor + /// + /// Initial capacity of + public PdbDynamicLocal(int capacity) => flags = new List(capacity); + } + + /// + /// Contains the EnC local variable slot map + /// + public sealed class PdbEditAndContinueLocalSlotMapCustomDebugInfo : PdbCustomDebugInfo { + readonly byte[] data; + + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.EditAndContinueLocalSlotMap; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.EncLocalSlotMap; + + /// + /// Gets the data. Spec: https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#EditAndContinueLocalSlotMap + /// + public byte[] Data => data; + + /// + /// Constructor + /// + /// Raw custom debug info data + public PdbEditAndContinueLocalSlotMapCustomDebugInfo(byte[] data) => this.data = data ?? throw new ArgumentNullException(nameof(data)); + } + + /// + /// Contains the EnC lambda map + /// + public sealed class PdbEditAndContinueLambdaMapCustomDebugInfo : PdbCustomDebugInfo { + readonly byte[] data; + + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.EditAndContinueLambdaMap; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.EncLambdaAndClosureMap; + + /// + /// Gets the data. Spec: https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#EditAndContinueLambdaAndClosureMap + /// + public byte[] Data => data; + + /// + /// Constructor + /// + /// Raw custom debug info data + public PdbEditAndContinueLambdaMapCustomDebugInfo(byte[] data) => this.data = data ?? throw new ArgumentNullException(nameof(data)); + } + + /// + /// Contains tuple element names for local variables and constants + /// + public sealed class PdbTupleElementNamesCustomDebugInfo : PdbCustomDebugInfo { + readonly IList names; + + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.TupleElementNames; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => Guid.Empty; + + /// + /// Gets the tuple element names + /// + public IList Names => names; + + /// + /// Constructor + /// + public PdbTupleElementNamesCustomDebugInfo() => names = new List(); + + /// + /// Constructor + /// + /// Initial capacity of + public PdbTupleElementNamesCustomDebugInfo(int capacity) => names = new List(capacity); + } + + /// + /// Tuple element name info + /// + public sealed class PdbTupleElementNames { + readonly IList tupleElementNames; + string name; + Local local; + Instruction scopeStart, scopeEnd; + + /// + /// Gets/sets the name of the local. If null is written, is returned instead. + /// + public string Name { + get { + var n = name; + if (n is not null) + return n; + return local?.Name; + } + set => name = value; + } + + /// + /// Gets/sets the local. It's null if it's a constant, and non-null if it's a variable + /// + public Local Local { + get => local; + set => local = value; + } + + /// + /// true if it's a constant. Constants have a scope ( and ) + /// + public bool IsConstant => local is null; + + /// + /// true if it's a variable. Variables don't have a scope ( and ) + /// + public bool IsVariable => local is not null; + + /// + /// Gets/sets the start of the scope or null. Only constants have a scope. + /// + public Instruction ScopeStart { + get => scopeStart; + set => scopeStart = value; + } + + /// + /// Gets/sets the end of the scope or null if it has no scope or if the scope ends at the end of the body. Only constants have a scope. + /// + public Instruction ScopeEnd { + get => scopeEnd; + set => scopeEnd = value; + } + + /// + /// Gets the tuple element names + /// + public IList TupleElementNames => tupleElementNames; + + /// + /// Constructor + /// + public PdbTupleElementNames() => tupleElementNames = new List(); + + /// + /// Constructor + /// + /// Initial capacity of + public PdbTupleElementNames(int capacity) => tupleElementNames = new List(capacity); + } + + /// + /// Contains tuple element names for local variables and constants + /// + public sealed class PortablePdbTupleElementNamesCustomDebugInfo : PdbCustomDebugInfo { + readonly IList names; + + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.TupleElementNames_PortablePdb; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.TupleElementNames; + + /// + /// Gets the tuple element names + /// + public IList Names => names; + + /// + /// Constructor + /// + public PortablePdbTupleElementNamesCustomDebugInfo() => names = new List(); + + /// + /// Constructor + /// + /// Initial capacity of + public PortablePdbTupleElementNamesCustomDebugInfo(int capacity) => names = new List(capacity); + } + + /// + /// Async method stepping info + /// + /// It's internal and translated to a + /// + sealed class PdbAsyncMethodSteppingInformationCustomDebugInfo : PdbCustomDebugInfo { + readonly IList asyncStepInfos; + + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.Unknown; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.AsyncMethodSteppingInformationBlob; + + /// + /// Gets the catch handler instruction or null + /// + public Instruction CatchHandler { get; set; } + + /// + /// Gets all async step infos + /// + public IList AsyncStepInfos => asyncStepInfos; + + /// + /// Constructor + /// + public PdbAsyncMethodSteppingInformationCustomDebugInfo() => asyncStepInfos = new List(); + } + + /// + /// Default namespace + /// + public sealed class PdbDefaultNamespaceCustomDebugInfo : PdbCustomDebugInfo { + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.DefaultNamespace; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.DefaultNamespace; + + /// + /// Gets the default namespace + /// + public string Namespace { get; set; } + + /// + /// Constructor + /// + public PdbDefaultNamespaceCustomDebugInfo() { + } + + /// + /// Constructor + /// + /// Default namespace + public PdbDefaultNamespaceCustomDebugInfo(string defaultNamespace) => Namespace = defaultNamespace; + } + + /// + /// Dynamic flags + /// + public sealed class PdbDynamicLocalVariablesCustomDebugInfo : PdbCustomDebugInfo { + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.DynamicLocalVariables; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.DynamicLocalVariables; + + /// + /// Gets/sets the dynamic flags + /// + public bool[] Flags { get; set; } + + /// + /// Constructor + /// + public PdbDynamicLocalVariablesCustomDebugInfo() { + } + + /// + /// Constructor + /// + /// Dynamic flags + public PdbDynamicLocalVariablesCustomDebugInfo(bool[] flags) => Flags = flags; + } + + /// + /// Contains the source code + /// + public sealed class PdbEmbeddedSourceCustomDebugInfo : PdbCustomDebugInfo { + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.EmbeddedSource; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.EmbeddedSource; + + /// + /// Gets the source code blob. + /// + /// It's not decompressed and converted to a string because the encoding isn't specified. + /// + /// https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#embedded-source-c-and-vb-compilers + /// + public byte[] SourceCodeBlob { get; set; } + + /// + /// Constructor + /// + public PdbEmbeddedSourceCustomDebugInfo() { + } + + /// + /// Constructor + /// + /// Source code blob + public PdbEmbeddedSourceCustomDebugInfo(byte[] sourceCodeBlob) => SourceCodeBlob = sourceCodeBlob; + } + + /// + /// Contains the source link file + /// + public sealed class PdbSourceLinkCustomDebugInfo : PdbCustomDebugInfo { + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.SourceLink; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.SourceLink; + + /// + /// Gets the source link file contents + /// + public byte[] FileBlob { get; set; } + + /// + /// Constructor + /// + public PdbSourceLinkCustomDebugInfo() { + } + + /// + /// Constructor + /// + /// Source link file contents + public PdbSourceLinkCustomDebugInfo(byte[] fileBlob) => FileBlob = fileBlob; + } + + /// + /// Contains the source server file + /// + public sealed class PdbSourceServerCustomDebugInfo : PdbCustomDebugInfo { + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.SourceServer; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => Guid.Empty; + + /// + /// Gets the source server file contents + /// + public byte[] FileBlob { get; set; } + + /// + /// Constructor + /// + public PdbSourceServerCustomDebugInfo() { + } + + /// + /// Constructor + /// + /// Source server file contents + public PdbSourceServerCustomDebugInfo(byte[] fileBlob) => FileBlob = fileBlob; + } + + /// + /// Async method info + /// + public sealed class PdbAsyncMethodCustomDebugInfo : PdbCustomDebugInfo { + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.AsyncMethod; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => Guid.Empty; + + readonly IList asyncStepInfos; + + /// + /// Gets/sets the starting method that initiates the async operation + /// + public MethodDef KickoffMethod { get; set; } + + /// + /// Gets/sets the instruction for the compiler generated catch handler that wraps an async method. + /// This can be null. + /// + public Instruction CatchHandlerInstruction { get; set; } + + /// + /// Gets all step infos used by the debugger + /// + public IList StepInfos => asyncStepInfos; + + /// + /// Constructor + /// + public PdbAsyncMethodCustomDebugInfo() => asyncStepInfos = new List(); + + /// + /// Constructor + /// + /// Default capacity for + public PdbAsyncMethodCustomDebugInfo(int stepInfosCapacity) => asyncStepInfos = new List(stepInfosCapacity); + } + + /// + /// Async step info used by debuggers + /// + public struct PdbAsyncStepInfo { + /// + /// The yield instruction + /// + public Instruction YieldInstruction; + + /// + /// Resume method + /// + public MethodDef BreakpointMethod; + + /// + /// Resume instruction (where the debugger puts a breakpoint) + /// + public Instruction BreakpointInstruction; + + /// + /// Constructor + /// + /// The yield instruction + /// Resume method + /// Resume instruction (where the debugger puts a breakpoint) + public PdbAsyncStepInfo(Instruction yieldInstruction, MethodDef breakpointMethod, Instruction breakpointInstruction) { + YieldInstruction = yieldInstruction; + BreakpointMethod = breakpointMethod; + BreakpointInstruction = breakpointInstruction; + } + } + + /// + /// Iterator method + /// + public sealed class PdbIteratorMethodCustomDebugInfo : PdbCustomDebugInfo { + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.IteratorMethod; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => Guid.Empty; + + /// + /// Gets the kickoff method + /// + public MethodDef KickoffMethod { get; set; } + + /// + /// Constructor + /// + public PdbIteratorMethodCustomDebugInfo() { + } + + /// + /// Constructor + /// + /// Kickoff method + public PdbIteratorMethodCustomDebugInfo(MethodDef kickoffMethod) => KickoffMethod = kickoffMethod; + } + + /// + /// Compilation metadata references + /// + public sealed class PdbCompilationMetadataReferencesCustomDebugInfo : PdbCustomDebugInfo { + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.CompilationMetadataReferences; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.CompilationMetadataReferences; + + /// + /// Gets all references + /// + public List References { get; } + + /// + /// Constructor + /// + public PdbCompilationMetadataReferencesCustomDebugInfo() => References = new List(); + } + + /// + /// Compilation metadata reference flags, see https://github.com/dotnet/roslyn/blob/master/docs/features/pdb-compilation-options.md + /// + [Flags] + public enum PdbCompilationMetadataReferenceFlags : byte { + /// + /// No bit is set + /// + None = 0, + + /// + /// Set if it's an assembly reference, clear if it's a module reference + /// + Assembly = 0x01, + + /// + /// EmbedInteropTypes was enabled + /// + EmbedInteropTypes = 0x02, + } + + /// + /// A compilation metadata reference + /// + public sealed class PdbCompilationMetadataReference { + /// + /// Name of the reference (eg. filename) + /// + public string Name { get; set; } + + /// + /// Aliases (or an empty string), separated with commas + /// + public string Aliases { get; set; } + + /// + /// Gets the flags + /// + public PdbCompilationMetadataReferenceFlags Flags { get; set; } + + /// + /// Gets the timestamp stored in the PE header + /// + public uint Timestamp { get; set; } + + /// + /// Gets SizeOfImage stored in the PE header + /// + public uint SizeOfImage { get; set; } + + /// + /// Gets the MVID stored in the .NET metadata + /// + public Guid Mvid { get; set; } + + /// + /// Constructor + /// + public PdbCompilationMetadataReference() { + Name = string.Empty; + Aliases = string.Empty; + } + + /// + /// Constructor + /// + /// Name of reference + /// Aliases (or an empty string), separated with commas + /// Reference flags + /// Timestamp in PE header + /// SizeOfImage in PE header + /// MVID stored in the .NET metadata + public PdbCompilationMetadataReference(string name, string aliases, PdbCompilationMetadataReferenceFlags flags, uint timestamp, uint sizeOfImage, Guid mvid) { + Name = name; + Aliases = aliases; + Flags = flags; + Timestamp = timestamp; + SizeOfImage = sizeOfImage; + Mvid = mvid; + } + } + + /// + /// Compilation options + /// + public sealed class PdbCompilationOptionsCustomDebugInfo : PdbCustomDebugInfo { + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.CompilationOptions; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.CompilationOptions; + + /// + /// Gets all compilation options, see https://github.com/dotnet/roslyn/blob/master/docs/features/pdb-compilation-options.md . + /// Option names (key): see roslyn/src/Compilers/Core/Portable/PEWriter/CompilationOptionNames.cs + /// + public List> Options { get; } + + /// + /// Constructor + /// + public PdbCompilationOptionsCustomDebugInfo() => Options = new List>(); + } + + /// + /// Links a TypeDef with no method IL with a PDB document. + /// + public class PdbTypeDefinitionDocumentsDebugInfo : PdbCustomDebugInfo { + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.TypeDefinitionDocuments; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.TypeDefinitionDocuments; + + /// + /// List of documents associated with the type + /// + public IList Documents { + get { + if (documents is null) + InitializeDocuments(); + return documents; + } + } + /// + protected IList documents; + /// Initializes + protected virtual void InitializeDocuments() => + Interlocked.CompareExchange(ref documents, new List(), null); + } + + sealed class PdbTypeDefinitionDocumentsDebugInfoMD : PdbTypeDefinitionDocumentsDebugInfo { + readonly ModuleDef readerModule; + readonly IList documentTokens; + + protected override void InitializeDocuments() { + var list = new List(documentTokens.Count); + if (readerModule.PdbState is not null) { + for (var i = 0; i < documentTokens.Count; i++) { + if (readerModule.PdbState.tokenToDocument.TryGetValue(documentTokens[i], out var document)) + list.Add(document); + } + } + Interlocked.CompareExchange(ref documents, list, null); + } + + public PdbTypeDefinitionDocumentsDebugInfoMD(ModuleDef readerModule, IList documentTokens) { + this.readerModule = readerModule; + this.documentTokens = documentTokens; + } + } + + /// + /// Contains the EnC state machine state mapping + /// + public sealed class PdbEditAndContinueStateMachineStateMapDebugInfo : PdbCustomDebugInfo { + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.EditAndContinueStateMachineStateMap; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.EncStateMachineStateMap; + + /// + /// State machine states + /// + public List StateMachineStates { get; } + + /// + /// Constructor + /// + public PdbEditAndContinueStateMachineStateMapDebugInfo() => StateMachineStates = new List(); + } + + /// + /// State machine state information used by debuggers + /// + public struct StateMachineStateInfo { + /// + /// Syntax offset + /// + public readonly int SyntaxOffset; + + /// + /// State machine state + /// + public readonly StateMachineState State; + + /// + /// Constructor + /// + /// Syntax offset + /// State machine state + public StateMachineStateInfo(int syntaxOffset, StateMachineState state) { + SyntaxOffset = syntaxOffset; + State = state; + } + } + + /// + /// State machine state + /// from Roslyn: StateMachineState.cs + /// + public enum StateMachineState { + /// + /// First state of an async iterator state machine that is used to resume the machine after yield return. + /// Initial state is not used to resume state machine that yielded. State numbers decrease as the iterator makes progress. + /// + FirstResumableAsyncIteratorState = InitialAsyncIteratorState - 1, + + /// + /// Initial iterator state of an async iterator. + /// Distinct from so that DisposeAsync can throw in latter case. + /// + InitialAsyncIteratorState = -3, + + /// + /// First state of an iterator state machine. State numbers decrease for subsequent finalize states. + /// + FirstIteratorFinalizeState = -3, + + /// + /// The last state of a state machine. + /// + FinishedState = -2, + + /// + /// State machine not started or is running + /// + NotStartedOrRunningState = -1, + + /// + /// First unused state + /// + FirstUnusedState = 0, + + /// + /// First state in async state machine that is used to resume the machine after await. + /// State numbers increase as the async computation makes progress. + /// + FirstResumableAsyncState = 0, + + /// + /// Initial iterator state of an iterator. + /// + InitialIteratorState = 0, + + /// + /// First state in iterator state machine that is used to resume the machine after yield return. + /// Initial state is not used to resume state machine that yielded. State numbers increase as the iterator makes progress. + /// + FirstResumableIteratorState = InitialIteratorState + 1, + } + + /// + /// Primary constructor information blob + /// + public sealed class PrimaryConstructorInformationBlobDebugInfo : PdbCustomDebugInfo { + /// + /// Returns + /// + public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.PrimaryConstructorInformationBlob; + + /// + /// Gets the custom debug info guid, see + /// + public override Guid Guid => CustomDebugInfoGuids.PrimaryConstructorInformationBlob; + + /// + /// Gets the data blob + /// + public byte[] Blob { get; set; } + + /// + /// Constructor + /// + public PrimaryConstructorInformationBlobDebugInfo() { + } + + /// + /// Constructor + /// + /// Source server file contents + public PrimaryConstructorInformationBlobDebugInfo(byte[] blob) => Blob = blob; + } +} diff --git a/src/DotNet/Pdb/PdbDocument.cs b/src/DotNet/Pdb/PdbDocument.cs index 0cafda3d1..6f25bbe8c 100644 --- a/src/DotNet/Pdb/PdbDocument.cs +++ b/src/DotNet/Pdb/PdbDocument.cs @@ -1,37 +1,38 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.SymbolStore; +using dnlib.DotNet.Pdb.Symbols; namespace dnlib.DotNet.Pdb { /// /// A PDB document /// [DebuggerDisplay("{Url}")] - public sealed class PdbDocument { + public sealed class PdbDocument : IHasCustomDebugInformation { /// /// Gets/sets the document URL /// public string Url { get; set; } /// - /// Gets/sets the language GUID. See + /// Gets/sets the language GUID. See /// public Guid Language { get; set; } /// - /// Gets/sets the language vendor GUID. See + /// Gets/sets the language vendor GUID. See /// public Guid LanguageVendor { get; set; } /// - /// Gets/sets the document type GUID. See + /// Gets/sets the document type GUID. See /// public Guid DocumentType { get; set; } /// - /// Gets/sets the checksum algorithm ID + /// Gets/sets the checksum algorithm ID. See /// public Guid CheckSumAlgorithmId { get; set; } @@ -40,6 +41,23 @@ public sealed class PdbDocument { /// public byte[] CheckSum { get; set; } + /// + public int HasCustomDebugInformationTag => 22; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos => customDebugInfos; + IList customDebugInfos; + + /// + /// Gets the Metadata token of the document if available. + /// + public MDToken? MDToken { get; internal set; } + /// /// Default constructor /// @@ -49,47 +67,60 @@ public PdbDocument() { /// /// Constructor /// - /// A instance - public PdbDocument(ISymbolDocument symDoc) { - if (symDoc == null) - throw new ArgumentNullException("symDoc"); - this.Url = symDoc.URL; - this.Language = symDoc.Language; - this.LanguageVendor = symDoc.LanguageVendor; - this.DocumentType = symDoc.DocumentType; - this.CheckSumAlgorithmId = symDoc.CheckSumAlgorithmId; - this.CheckSum = symDoc.GetCheckSum(); + /// A instance + public PdbDocument(SymbolDocument symDoc) : this(symDoc, partial: false) { + } + + PdbDocument(SymbolDocument symDoc, bool partial) { + if (symDoc is null) + throw new ArgumentNullException(nameof(symDoc)); + Url = symDoc.URL; + if (!partial) + Initialize(symDoc); + } + + internal static PdbDocument CreatePartialForCompare(SymbolDocument symDoc) => + new PdbDocument(symDoc, partial: true); + + internal void Initialize(SymbolDocument symDoc) { + Language = symDoc.Language; + LanguageVendor = symDoc.LanguageVendor; + DocumentType = symDoc.DocumentType; + CheckSumAlgorithmId = symDoc.CheckSumAlgorithmId; + CheckSum = symDoc.CheckSum; + customDebugInfos = new List(); + foreach (var cdi in symDoc.CustomDebugInfos) + customDebugInfos.Add(cdi); + MDToken = symDoc.MDToken; } /// /// Constructor /// /// Document URL - /// Language. See - /// Language vendor. See - /// Document type. See - /// Checksum algorithm ID + /// Language. See + /// Language vendor. See + /// Document type. See + /// Checksum algorithm ID. See /// Checksum public PdbDocument(string url, Guid language, Guid languageVendor, Guid documentType, Guid checkSumAlgorithmId, byte[] checkSum) { - this.Url = url; - this.Language = language; - this.LanguageVendor = languageVendor; - this.DocumentType = documentType; - this.CheckSumAlgorithmId = checkSumAlgorithmId; - this.CheckSum = checkSum; + Url = url; + Language = language; + LanguageVendor = languageVendor; + DocumentType = documentType; + CheckSumAlgorithmId = checkSumAlgorithmId; + CheckSum = checkSum; } /// - public override int GetHashCode() { - return (Url ?? string.Empty).ToUpperInvariant().GetHashCode(); - } + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Url ?? string.Empty); /// public override bool Equals(object obj) { var other = obj as PdbDocument; - if (other == null) + if (other is null) return false; - return (Url ?? string.Empty).Equals(other.Url ?? string.Empty, StringComparison.OrdinalIgnoreCase); + return StringComparer.OrdinalIgnoreCase.Equals(Url ?? string.Empty, other.Url ?? string.Empty); } } } diff --git a/src/DotNet/Pdb/PdbDocumentConstants.cs b/src/DotNet/Pdb/PdbDocumentConstants.cs new file mode 100644 index 000000000..4002545d1 --- /dev/null +++ b/src/DotNet/Pdb/PdbDocumentConstants.cs @@ -0,0 +1,23 @@ +// dnlib: See LICENSE.txt for more info + +using System; + +namespace dnlib.DotNet.Pdb { + /// + /// PDB document constants + /// + public static class PdbDocumentConstants { +#pragma warning disable 1591 // Missing XML comment for publicly visible type or member + public static readonly Guid LanguageCSharp = new Guid("3F5162F8-07C6-11D3-9053-00C04FA302A1"); + public static readonly Guid LanguageVisualBasic = new Guid("3A12D0B8-C26C-11D0-B442-00A0244A1DD2"); + public static readonly Guid LanguageFSharp = new Guid("AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3"); + + public static readonly Guid HashSHA1 = new Guid("FF1816EC-AA5E-4D10-87F7-6F4963833460"); + public static readonly Guid HashSHA256 = new Guid("8829D00F-11B8-4213-878B-770E8597AC16"); + + public static readonly Guid LanguageVendorMicrosoft = new Guid("994B45C4-E6E9-11D2-903F-00C04FA302A1"); + + public static readonly Guid DocumentTypeText = new Guid("5A869D0B-6611-11D3-BD2A-0000F80849BD"); +#pragma warning restore 1591 // Missing XML comment for publicly visible type or member + } +} diff --git a/src/DotNet/Pdb/PdbFileKind.cs b/src/DotNet/Pdb/PdbFileKind.cs new file mode 100644 index 000000000..1ec945f0f --- /dev/null +++ b/src/DotNet/Pdb/PdbFileKind.cs @@ -0,0 +1,23 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.DotNet.Pdb { + /// + /// PDB file kind + /// + public enum PdbFileKind { + /// + /// Windows PDB + /// + WindowsPDB, + + /// + /// Portable PDB + /// + PortablePDB, + + /// + /// Embedded portable PDB + /// + EmbeddedPortablePDB, + } +} diff --git a/src/DotNet/Pdb/PdbImplType.cs b/src/DotNet/Pdb/PdbImplType.cs deleted file mode 100644 index 90e9a94c5..000000000 --- a/src/DotNet/Pdb/PdbImplType.cs +++ /dev/null @@ -1,23 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -namespace dnlib.DotNet.Pdb { - /// - /// PDB implementation type - /// - public enum PdbImplType { - /// - /// Use Microsoft's COM DLL (diasymreader.dll) - /// - MicrosoftCOM, - - /// - /// Use the managed PDB reader - /// - Managed, - - /// - /// Use the default PDB reader - /// - Default = Managed, - } -} diff --git a/src/DotNet/Pdb/PdbImport.cs b/src/DotNet/Pdb/PdbImport.cs new file mode 100644 index 000000000..d300d9130 --- /dev/null +++ b/src/DotNet/Pdb/PdbImport.cs @@ -0,0 +1,424 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; +using System.Diagnostics; + +namespace dnlib.DotNet.Pdb { + /// + /// Import scope + /// + public sealed class PdbImportScope : IHasCustomDebugInformation { + readonly IList imports = new List(); + + /// + /// Constructor + /// + public PdbImportScope() { + } + + /// + /// Gets/sets the parent import scope + /// + public PdbImportScope Parent { get; set; } + + /// + /// Gets all imports + /// + public IList Imports => imports; + + /// + /// true if is not empty + /// + public bool HasImports => imports.Count > 0; + + /// + public int HasCustomDebugInformationTag => 26; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos => customDebugInfos; + readonly IList customDebugInfos = new List(); + } + + /// + /// Import kind + /// + public enum PdbImportDefinitionKind { +#pragma warning disable 1591 // Missing XML comment for publicly visible type or member + ImportNamespace, + ImportAssemblyNamespace, + ImportType, + ImportXmlNamespace, + ImportAssemblyReferenceAlias, + AliasAssemblyReference, + AliasNamespace, + AliasAssemblyNamespace, + AliasType, +#pragma warning restore 1591 // Missing XML comment for publicly visible type or member + } + + /// + /// PDB import base class + /// + public abstract class PdbImport { + /// + /// Gets the import kind + /// + public abstract PdbImportDefinitionKind Kind { get; } + + internal abstract void PreventNewClasses(); + } + + /// + /// Import namespace + /// + [DebuggerDisplay("{GetDebuggerString(),nq}")] + public sealed class PdbImportNamespace : PdbImport { + /// + /// Returns + /// + public sealed override PdbImportDefinitionKind Kind => PdbImportDefinitionKind.ImportNamespace; + + /// + /// Gets the target namespace + /// + public string TargetNamespace { get; set; } + + /// + /// Constructor + /// + public PdbImportNamespace() { + } + + /// + /// Constructor + /// + /// + public PdbImportNamespace(string targetNamespace) => TargetNamespace = targetNamespace; + + internal sealed override void PreventNewClasses() { } + + string GetDebuggerString() => $"{Kind}: {TargetNamespace}"; + } + + /// + /// Import assembly, namespace + /// + [DebuggerDisplay("{GetDebuggerString(),nq}")] + public sealed class PdbImportAssemblyNamespace : PdbImport { + /// + /// Returns + /// + public sealed override PdbImportDefinitionKind Kind => PdbImportDefinitionKind.ImportAssemblyNamespace; + + /// + /// Gets the target assembly + /// + public AssemblyRef TargetAssembly { get; set; } + + /// + /// Gets the target namespace + /// + public string TargetNamespace { get; set; } + + /// + /// Constructor + /// + public PdbImportAssemblyNamespace() { + } + + /// + /// Constructor + /// + /// + /// + public PdbImportAssemblyNamespace(AssemblyRef targetAssembly, string targetNamespace) { + TargetAssembly = targetAssembly; + TargetNamespace = targetNamespace; + } + + internal sealed override void PreventNewClasses() { } + + string GetDebuggerString() => $"{Kind}: {TargetAssembly} {TargetNamespace}"; + } + + /// + /// Import type + /// + [DebuggerDisplay("{GetDebuggerString(),nq}")] + public sealed class PdbImportType : PdbImport { + /// + /// Returns + /// + public sealed override PdbImportDefinitionKind Kind => PdbImportDefinitionKind.ImportType; + + /// + /// Gets the target type + /// + public ITypeDefOrRef TargetType { get; set; } + + /// + /// Constructor + /// + public PdbImportType() { + } + + /// + /// Constructor + /// + /// + public PdbImportType(ITypeDefOrRef targetType) => TargetType = targetType; + + internal sealed override void PreventNewClasses() { } + + string GetDebuggerString() => $"{Kind}: {TargetType}"; + } + + /// + /// Import xml namespace + /// + [DebuggerDisplay("{GetDebuggerString(),nq}")] + public sealed class PdbImportXmlNamespace : PdbImport { + /// + /// Returns + /// + public sealed override PdbImportDefinitionKind Kind => PdbImportDefinitionKind.ImportXmlNamespace; + + /// + /// Gets the alias + /// + public string Alias { get; set; } + + /// + /// Gets the target namespace + /// + public string TargetNamespace { get; set; } + + /// + /// Constructor + /// + public PdbImportXmlNamespace() { + } + + /// + /// Constructor + /// + /// + /// + public PdbImportXmlNamespace(string alias, string targetNamespace) { + Alias = alias; + TargetNamespace = targetNamespace; + } + + internal sealed override void PreventNewClasses() { } + + string GetDebuggerString() => $"{Kind}: {Alias} = {TargetNamespace}"; + } + + /// + /// Import assembly reference alias + /// + [DebuggerDisplay("{GetDebuggerString(),nq}")] + public sealed class PdbImportAssemblyReferenceAlias : PdbImport { + /// + /// Returns + /// + public sealed override PdbImportDefinitionKind Kind => PdbImportDefinitionKind.ImportAssemblyReferenceAlias; + + /// + /// Gets the alias + /// + public string Alias { get; set; } + + /// + /// Constructor + /// + public PdbImportAssemblyReferenceAlias() { + } + + /// + /// Constructor + /// + /// + public PdbImportAssemblyReferenceAlias(string alias) => Alias = alias; + + internal sealed override void PreventNewClasses() { } + + string GetDebuggerString() => $"{Kind}: {Alias}"; + } + + /// + /// Alias assembly reference + /// + [DebuggerDisplay("{GetDebuggerString(),nq}")] + public sealed class PdbAliasAssemblyReference : PdbImport { + /// + /// Returns + /// + public sealed override PdbImportDefinitionKind Kind => PdbImportDefinitionKind.AliasAssemblyReference; + + /// + /// Gets the alias + /// + public string Alias { get; set; } + + /// + /// Gets the target assembly + /// + public AssemblyRef TargetAssembly { get; set; } + + /// + /// Constructor + /// + public PdbAliasAssemblyReference() { + } + + /// + /// Constructor + /// + /// + /// + public PdbAliasAssemblyReference(string alias, AssemblyRef targetAssembly) { + Alias = alias; + TargetAssembly = targetAssembly; + } + + internal sealed override void PreventNewClasses() { } + + string GetDebuggerString() => $"{Kind}: {Alias} = {TargetAssembly}"; + } + + /// + /// Alias namespace + /// + [DebuggerDisplay("{GetDebuggerString(),nq}")] + public sealed class PdbAliasNamespace : PdbImport { + /// + /// Returns + /// + public sealed override PdbImportDefinitionKind Kind => PdbImportDefinitionKind.AliasNamespace; + + /// + /// Gets the alias + /// + public string Alias { get; set; } + + /// + /// Gets the target namespace + /// + public string TargetNamespace { get; set; } + + /// + /// Constructor + /// + public PdbAliasNamespace() { + } + + /// + /// Constructor + /// + /// + /// + public PdbAliasNamespace(string alias, string targetNamespace) { + Alias = alias; + TargetNamespace = targetNamespace; + } + + internal sealed override void PreventNewClasses() { } + + string GetDebuggerString() => $"{Kind}: {Alias} = {TargetNamespace}"; + } + + /// + /// Alias assembly namespace + /// + [DebuggerDisplay("{GetDebuggerString(),nq}")] + public sealed class PdbAliasAssemblyNamespace : PdbImport { + /// + /// Returns + /// + public sealed override PdbImportDefinitionKind Kind => PdbImportDefinitionKind.AliasAssemblyNamespace; + + /// + /// Gets the alias + /// + public string Alias { get; set; } + + /// + /// Gets the target assembly + /// + public AssemblyRef TargetAssembly { get; set; } + + /// + /// Gets the target namespace + /// + public string TargetNamespace { get; set; } + + /// + /// Constructor + /// + public PdbAliasAssemblyNamespace() { + } + + /// + /// Constructor + /// + /// + /// + /// + public PdbAliasAssemblyNamespace(string alias, AssemblyRef targetAssembly, string targetNamespace) { + Alias = alias; + TargetAssembly = targetAssembly; + TargetNamespace = targetNamespace; + } + + internal sealed override void PreventNewClasses() { } + + string GetDebuggerString() => $"{Kind}: {Alias} = {TargetAssembly} {TargetNamespace}"; + } + + /// + /// Alias type + /// + [DebuggerDisplay("{GetDebuggerString(),nq}")] + public sealed class PdbAliasType : PdbImport { + /// + /// Returns + /// + public sealed override PdbImportDefinitionKind Kind => PdbImportDefinitionKind.AliasType; + + /// + /// Gets the alias + /// + public string Alias { get; set; } + + /// + /// Gets the target type + /// + public ITypeDefOrRef TargetType { get; set; } + + /// + /// Constructor + /// + public PdbAliasType() { + } + + /// + /// Constructor + /// + /// + /// + public PdbAliasType(string alias, ITypeDefOrRef targetType) { + Alias = alias; + TargetType = targetType; + } + + internal sealed override void PreventNewClasses() { } + + string GetDebuggerString() => $"{Kind}: {Alias} = {TargetType}"; + } +} diff --git a/src/DotNet/Pdb/PdbLocal.cs b/src/DotNet/Pdb/PdbLocal.cs new file mode 100644 index 000000000..52e406de0 --- /dev/null +++ b/src/DotNet/Pdb/PdbLocal.cs @@ -0,0 +1,74 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; +using dnlib.DotNet.Emit; + +namespace dnlib.DotNet.Pdb { + /// + /// A local variable + /// + public sealed class PdbLocal : IHasCustomDebugInformation { + /// + /// Constructor + /// + public PdbLocal() { + } + + /// + /// Constructor + /// + /// + /// + /// + public PdbLocal(Local local, string name, PdbLocalAttributes attributes) { + Local = local; + Name = name; + Attributes = attributes; + } + + /// + /// Gets/sets the local + /// + public Local Local { get; set; } + + /// + /// Gets/sets the name + /// + public string Name { get; set; } + + /// + /// Gets/sets the attributes + /// + public PdbLocalAttributes Attributes { get; set; } + + /// + /// Gets the index of the local + /// + public int Index => Local.Index; + + /// + /// true if it should be hidden in debugger variables windows. Not all compiler generated locals have this flag set. + /// + public bool IsDebuggerHidden { + get => (Attributes & PdbLocalAttributes.DebuggerHidden) != 0; + set { + if (value) + Attributes |= PdbLocalAttributes.DebuggerHidden; + else + Attributes &= ~PdbLocalAttributes.DebuggerHidden; + } + } + + /// + public int HasCustomDebugInformationTag => 24; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos => customDebugInfos; + readonly IList customDebugInfos = new List(); + } +} diff --git a/src/DotNet/Pdb/PdbLocalAttributes.cs b/src/DotNet/Pdb/PdbLocalAttributes.cs new file mode 100644 index 000000000..e126c28ca --- /dev/null +++ b/src/DotNet/Pdb/PdbLocalAttributes.cs @@ -0,0 +1,21 @@ +// dnlib: See LICENSE.txt for more info + +using System; + +namespace dnlib.DotNet.Pdb { + /// + /// Local attributes + /// + [Flags] + public enum PdbLocalAttributes { + /// + /// No bit is set + /// + None = 0, + + /// + /// Local should be hidden in debugger variables windows. Not all compiler generated locals have this flag set. + /// + DebuggerHidden = 0x00000001, + } +} diff --git a/src/DotNet/Pdb/PdbMethod.cs b/src/DotNet/Pdb/PdbMethod.cs new file mode 100644 index 000000000..c364a5b00 --- /dev/null +++ b/src/DotNet/Pdb/PdbMethod.cs @@ -0,0 +1,19 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.DotNet.Pdb { + /// + /// A PDB method + /// + public sealed class PdbMethod { + /// + /// Gets/sets the root scope. It contains all scopes of the method, using namespaces, variables and constants + /// + public PdbScope Scope { get; set; } + + /// + /// Constructor + /// + public PdbMethod() { + } + } +} diff --git a/src/DotNet/Pdb/PdbReaderContext.cs b/src/DotNet/Pdb/PdbReaderContext.cs new file mode 100644 index 000000000..74f57e823 --- /dev/null +++ b/src/DotNet/Pdb/PdbReaderContext.cs @@ -0,0 +1,69 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using dnlib.IO; +using dnlib.PE; + +namespace dnlib.DotNet.Pdb { + readonly struct PdbReaderContext { + readonly IPEImage peImage; + readonly ImageDebugDirectory codeViewDebugDir; + + public bool HasDebugInfo => codeViewDebugDir is not null; + public ImageDebugDirectory CodeViewDebugDirectory => codeViewDebugDir; + public PdbReaderOptions Options { get; } + + public PdbReaderContext(IPEImage peImage, PdbReaderOptions options) { + this.peImage = peImage; + Options = options; + codeViewDebugDir = TryGetDebugDirectoryEntry(peImage, ImageDebugType.CodeView); + } + + public ImageDebugDirectory TryGetDebugDirectoryEntry(ImageDebugType imageDebugType) => + TryGetDebugDirectoryEntry(peImage, imageDebugType); + + static ImageDebugDirectory TryGetDebugDirectoryEntry(IPEImage peImage, ImageDebugType imageDebugType) { + var list = peImage.ImageDebugDirectories; + int count = list.Count; + for (int i = 0; i < count; i++) { + var entry = list[i]; + if (entry.Type == imageDebugType) + return entry; + } + return null; + } + + public bool TryGetCodeViewData(out Guid guid, out uint age) => TryGetCodeViewData(out guid, out age, out _); + + public bool TryGetCodeViewData(out Guid guid, out uint age, out string pdbFilename) { + guid = Guid.Empty; + age = 0; + pdbFilename = null; + var reader = GetCodeViewDataReader(); + // magic, guid, age, zero-terminated string + if (reader.Length < 4 + 16 + 4 + 1) + return false; + if (reader.ReadUInt32() != 0x53445352) + return false; + guid = reader.ReadGuid(); + age = reader.ReadUInt32(); + pdbFilename = reader.TryReadZeroTerminatedUtf8String(); + return pdbFilename is not null; + } + + DataReader GetCodeViewDataReader() { + if (codeViewDebugDir is null) + return default; + return CreateReader(codeViewDebugDir.AddressOfRawData, codeViewDebugDir.SizeOfData); + } + + public DataReader CreateReader(RVA rva, uint size) { + if (rva == 0 || size == 0) + return default; + var reader = peImage.CreateReader(rva, size); + if (reader.Length != size) + return default; + return reader; + } + } +} diff --git a/src/DotNet/Pdb/PdbReaderOptions.cs b/src/DotNet/Pdb/PdbReaderOptions.cs new file mode 100644 index 000000000..4ee55d868 --- /dev/null +++ b/src/DotNet/Pdb/PdbReaderOptions.cs @@ -0,0 +1,44 @@ +// dnlib: See LICENSE.txt for more info + +using System; + +namespace dnlib.DotNet.Pdb { + /// + /// PDB reader options + /// + [Flags] + public enum PdbReaderOptions { + /// + /// No bit is set + /// + None = 0, + + /// + /// Use the COM Windows PDB reader instead of the managed Windows PDB reader. + /// + /// This is NOT recommended since the COM reader can only be called on the same + /// thread it was created on. It also requires a Windows OS. + /// + /// If this is not set, the managed PDB reader will be used. + /// + /// This option is only used if it's a Windows PDB file, not if it's a Portable PDB file. + /// + MicrosoftComReader = 0x00000001, + + /// + /// Don't use Microsoft.DiaSymReader.Native. This is a NuGet package with an updated Windows PDB reader/writer implementation, + /// and if it's available at runtime, dnlib will try to use it. If this option is set, dnlib won't use it. + /// You have to add a reference to the NuGet package if you want to use it, dnlib has no reference to the NuGet package. + /// + /// Only used if is set and if it's a Windows PDB file + /// + NoDiaSymReader = 0x00000002, + + /// + /// Don't use diasymreader.dll's PDB reader that is shipped with .NET Framework. + /// + /// Only used if is set and if it's a Windows PDB file + /// + NoOldDiaSymReader = 0x00000004, + } +} diff --git a/src/DotNet/Pdb/PdbScope.cs b/src/DotNet/Pdb/PdbScope.cs index 7bc2c2579..1537dc8de 100644 --- a/src/DotNet/Pdb/PdbScope.cs +++ b/src/DotNet/Pdb/PdbScope.cs @@ -1,24 +1,25 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info +using System.Collections.Generic; using System.Diagnostics; using dnlib.DotNet.Emit; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif namespace dnlib.DotNet.Pdb { /// /// A PDB scope /// [DebuggerDisplay("{Start} - {End}")] - public sealed class PdbScope { - readonly ThreadSafe.IList scopes = ThreadSafeListCreator.Create(); - readonly ThreadSafe.IList locals = ThreadSafeListCreator.Create(); - readonly ThreadSafe.IList namespaces = ThreadSafeListCreator.Create(); + public sealed class PdbScope : IHasCustomDebugInformation { + readonly IList scopes = new List(); + readonly IList locals = new List(); + readonly IList namespaces = new List(); + readonly IList constants = new List(); + + /// + /// Constructor + /// + public PdbScope() { + } /// /// Gets/sets the first instruction @@ -33,43 +34,58 @@ public sealed class PdbScope { /// /// Gets all child scopes /// - public ThreadSafe.IList Scopes { - get { return scopes; } - } + public IList Scopes => scopes; /// /// true if is not empty /// - public bool HasScopes { - get { return scopes.Count > 0; } - } + public bool HasScopes => scopes.Count > 0; /// /// Gets all locals in this scope /// - public ThreadSafe.IList Variables { - get { return locals; } - } + public IList Variables => locals; /// /// true if is not empty /// - public bool HasVariables { - get { return locals.Count > 0; } - } + public bool HasVariables => locals.Count > 0; /// - /// Gets all namespaces + /// Gets all namespaces (Windows PDBs). Portable PDBs use /// - public ThreadSafe.IList Namespaces { - get { return namespaces; } - } + public IList Namespaces => namespaces; /// /// true if is not empty /// - public bool HasNamespaces { - get { return namespaces.Count > 0; } - } + public bool HasNamespaces => namespaces.Count > 0; + + /// + /// Gets/sets the import scope (Portable PDBs). Windows PDBs use + /// + public PdbImportScope ImportScope { get; set; } + + /// + /// Gets all constants + /// + public IList Constants => constants; + + /// + /// true if is not empty + /// + public bool HasConstants => constants.Count > 0; + + /// + public int HasCustomDebugInformationTag => 23; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos => customDebugInfos; + readonly IList customDebugInfos = new List(); } } diff --git a/src/DotNet/Pdb/PdbState.cs b/src/DotNet/Pdb/PdbState.cs index 384885db3..39146563f 100644 --- a/src/DotNet/Pdb/PdbState.cs +++ b/src/DotNet/Pdb/PdbState.cs @@ -1,9 +1,11 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; -using System.Diagnostics.SymbolStore; +using System.Diagnostics; using dnlib.DotNet.Emit; +using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb.Symbols; using dnlib.Threading; namespace dnlib.DotNet.Pdb { @@ -11,20 +13,29 @@ namespace dnlib.DotNet.Pdb { /// PDB state for a /// public sealed class PdbState { - readonly ISymbolReader reader; + readonly SymbolReader reader; readonly Dictionary docDict = new Dictionary(); + internal readonly Dictionary tokenToDocument = new Dictionary(); MethodDef userEntryPoint; + readonly Compiler compiler; + readonly PdbFileKind originalPdbFileKind; #if THREAD_SAFE readonly Lock theLock = Lock.Create(); #endif + /// + /// Gets/sets the PDB file kind. You can change it from portable PDB to embedded portable PDB + /// and vice versa. Converting a Windows PDB to a portable PDB isn't supported. + /// + public PdbFileKind PdbFileKind { get; set; } + /// /// Gets/sets the user entry point method. /// public MethodDef UserEntryPoint { - get { return userEntryPoint; } - set { userEntryPoint = value; } + get => userEntryPoint; + set => userEntryPoint = value; } /// @@ -54,32 +65,43 @@ public bool HasDocuments { #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif - + } } /// - /// Default constructor + /// Constructor /// - public PdbState() { + /// Module + /// PDB file kind + public PdbState(ModuleDef module, PdbFileKind pdbFileKind) { + if (module is null) + throw new ArgumentNullException(nameof(module)); + compiler = CalculateCompiler(module); + PdbFileKind = pdbFileKind; + originalPdbFileKind = pdbFileKind; } /// /// Constructor /// - /// A instance + /// A instance /// Owner module - public PdbState(ISymbolReader reader, ModuleDefMD module) { - if (reader == null) - throw new ArgumentNullException("reader"); - if (module == null) - throw new ArgumentNullException("module"); - this.reader = reader; + public PdbState(SymbolReader reader, ModuleDefMD module) { + if (module is null) + throw new ArgumentNullException(nameof(module)); + this.reader = reader ?? throw new ArgumentNullException(nameof(reader)); + reader.Initialize(module); + PdbFileKind = reader.PdbFileKind; + originalPdbFileKind = reader.PdbFileKind; + compiler = CalculateCompiler(module); - this.userEntryPoint = module.ResolveToken(reader.UserEntryPoint.GetToken()) as MethodDef; + userEntryPoint = module.ResolveToken(reader.UserEntryPoint) as MethodDef; - foreach (var doc in reader.GetDocuments()) - Add_NoLock(new PdbDocument(doc)); + var documents = reader.Documents; + int count = documents.Count; + for (int i = 0; i < count; i++) + Add_NoLock(documents[i]); } /// @@ -99,10 +121,23 @@ public PdbDocument Add(PdbDocument doc) { } PdbDocument Add_NoLock(PdbDocument doc) { - PdbDocument orig; - if (docDict.TryGetValue(doc, out orig)) + if (docDict.TryGetValue(doc, out var orig)) return orig; docDict.Add(doc, doc); + if (doc.MDToken.HasValue) + tokenToDocument.Add(doc.MDToken.Value, doc); + return doc; + } + + PdbDocument Add_NoLock(SymbolDocument symDoc) { + var doc = PdbDocument.CreatePartialForCompare(symDoc); + if (docDict.TryGetValue(doc, out var orig)) + return orig; + // Expensive part, can read source code etc + doc.Initialize(symDoc); + docDict.Add(doc, doc); + if (symDoc.MDToken.HasValue) + tokenToDocument.Add(symDoc.MDToken.Value, doc); return doc; } @@ -115,6 +150,8 @@ public bool Remove(PdbDocument doc) { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif + if (doc.MDToken.HasValue) + tokenToDocument.Remove(doc.MDToken.Value); return docDict.Remove(doc); #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } @@ -131,8 +168,7 @@ public PdbDocument GetExisting(PdbDocument doc) { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif - PdbDocument orig; - docDict.TryGetValue(doc, out orig); + docDict.TryGetValue(doc, out var orig); return orig; #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } @@ -143,9 +179,7 @@ public PdbDocument GetExisting(PdbDocument doc) { /// Removes all documents /// /// - public void RemoveAllDocuments() { - RemoveAllDocuments(false); - } + public void RemoveAllDocuments() => RemoveAllDocuments(false); /// /// Removes all documents and optionally returns them @@ -159,6 +193,7 @@ public List RemoveAllDocuments(bool returnDocs) { theLock.EnterWriteLock(); try { #endif var docs = returnDocs ? new List(docDict.Values) : null; + tokenToDocument.Clear(); docDict.Clear(); return docs; #if THREAD_SAFE @@ -166,103 +201,212 @@ public List RemoveAllDocuments(bool returnDocs) { #endif } - /// - /// Initializes a with information found in the PDB file. The - /// instructions in must have valid offsets. This method is - /// automatically called by and you don't need to explicitly call - /// it. - /// - /// Method body - /// Method row ID - public void InitializeDontCall(CilBody body, uint methodRid) { - if (reader == null || body == null) + internal Compiler Compiler => compiler; + + internal void InitializeMethodBody(ModuleDefMD module, MethodDef ownerMethod, CilBody body) { + if (reader is null) return; - var token = new SymbolToken((int)(0x06000000 + methodRid)); - ISymbolMethod method; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - method = reader.GetMethod(token); - if (method != null) { - body.Scope = CreateScope(body, method.RootScope); + + var method = reader.GetMethod(ownerMethod, 1); + if (method is not null) { + var pdbMethod = new PdbMethod(); + pdbMethod.Scope = CreateScope(module, GenericParamContext.Create(ownerMethod), body, method.RootScope); AddSequencePoints(body, method); + body.PdbMethod = pdbMethod; } - //TODO: reader.GetSymAttribute() -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif } - void AddSequencePoints(CilBody body, ISymbolMethod method) { - int numSeqs = method.SequencePointCount; - var offsets = new int[numSeqs]; - var documents = new ISymbolDocument[numSeqs]; - var lines = new int[numSeqs]; - var columns = new int[numSeqs]; - var endLines = new int[numSeqs]; - var endColumns = new int[numSeqs]; - method.GetSequencePoints(offsets, documents, lines, columns, endLines, endColumns); + internal void InitializeCustomDebugInfos(MethodDef ownerMethod, CilBody body, IList customDebugInfos) { + if (reader is null) + return; + + var method = reader.GetMethod(ownerMethod, 1); + if (method is not null) + method.GetCustomDebugInfos(ownerMethod, body, customDebugInfos); + } + + static Compiler CalculateCompiler(ModuleDef module) { + if (module is null) + return Compiler.Other; + + foreach (var asmRef in module.GetAssemblyRefs()) { + if (asmRef.Name == nameAssemblyVisualBasic || asmRef.Name == nameAssemblyVisualBasicCore) + return Compiler.VisualBasic; + } +// Disable this for now, we shouldn't be resolving types this early since we could be called by the ModuleDefMD ctor +#if false + // The VB runtime can also be embedded, and if so, it seems that "Microsoft.VisualBasic.Embedded" + // attribute is added to the assembly's custom attributes. + var asm = module.Assembly; + if (asm is not null && asm.CustomAttributes.IsDefined("Microsoft.VisualBasic.Embedded")) + return Compiler.VisualBasic; +#endif + + return Compiler.Other; + } + static readonly UTF8String nameAssemblyVisualBasic = new UTF8String("Microsoft.VisualBasic"); + // .NET Core 3.0 has this assembly because Microsoft.VisualBasic contains WinForms refs + static readonly UTF8String nameAssemblyVisualBasicCore = new UTF8String("Microsoft.VisualBasic.Core"); + + void AddSequencePoints(CilBody body, SymbolMethod method) { int instrIndex = 0; - for (int i = 0; i < numSeqs; i++) { - var instr = GetInstruction(body.Instructions, offsets[i], ref instrIndex); - if (instr == null) + var sequencePoints = method.SequencePoints; + int count = sequencePoints.Count; + for (int i = 0; i < count; i++) { + var sp = sequencePoints[i]; + var instr = GetInstruction(body.Instructions, sp.Offset, ref instrIndex); + if (instr is null) continue; var seqPoint = new SequencePoint() { - Document = Add_NoLock(new PdbDocument(documents[i])), - StartLine = lines[i], - StartColumn = columns[i], - EndLine = endLines[i], - EndColumn = endColumns[i], + Document = Add_NoLock(sp.Document), + StartLine = sp.Line, + StartColumn = sp.Column, + EndLine = sp.EndLine, + EndColumn = sp.EndColumn, }; instr.SequencePoint = seqPoint; } } struct CreateScopeState { - public ISymbolScope SymScope; + public SymbolScope SymScope; public PdbScope PdbScope; - public ISymbolScope[] Children; + public IList Children; public int ChildrenIndex; } - static PdbScope CreateScope(CilBody body, ISymbolScope symScope) { - if (symScope == null) + PdbScope CreateScope(ModuleDefMD module, GenericParamContext gpContext, CilBody body, SymbolScope symScope) { + if (symScope is null) return null; // Don't use recursive calls var stack = new Stack(); var state = new CreateScopeState() { SymScope = symScope }; + int endIsInclusiveValue = PdbUtils.IsEndInclusive(originalPdbFileKind, Compiler) ? 1 : 0; recursive_call: int instrIndex = 0; state.PdbScope = new PdbScope() { Start = GetInstruction(body.Instructions, state.SymScope.StartOffset, ref instrIndex), - End = GetInstruction(body.Instructions, state.SymScope.EndOffset, ref instrIndex), + End = GetInstruction(body.Instructions, state.SymScope.EndOffset + endIsInclusiveValue, ref instrIndex), }; + var cdis = state.SymScope.CustomDebugInfos; + int count = cdis.Count; + for (int i = 0; i < count; i++) + state.PdbScope.CustomDebugInfos.Add(cdis[i]); - foreach (var symLocal in state.SymScope.GetLocals()) { - if (symLocal.AddressKind != SymAddressKind.ILOffset) - continue; - - int localIndex = symLocal.AddressField1; - if ((uint)localIndex >= (uint)body.Variables.Count) + var locals = state.SymScope.Locals; + count = locals.Count; + for (int i = 0; i < count; i++) { + var symLocal = locals[i]; + int localIndex = symLocal.Index; + if ((uint)localIndex >= (uint)body.Variables.Count) { + // VB sometimes creates a PDB local without a metadata local continue; + } var local = body.Variables[localIndex]; - local.Name = symLocal.Name; + var name = symLocal.Name; + local.SetName(name); var attributes = symLocal.Attributes; - if (attributes is int) - local.PdbAttributes = (int)attributes; - state.PdbScope.Variables.Add(local); + local.SetAttributes(attributes); + var pdbLocal = new PdbLocal(local, name, attributes); + cdis = symLocal.CustomDebugInfos; + int count2 = cdis.Count; + for (int j = 0; j < count2; j++) + pdbLocal.CustomDebugInfos.Add(cdis[j]); + state.PdbScope.Variables.Add(pdbLocal); } - foreach (var ns in state.SymScope.GetNamespaces()) - state.PdbScope.Namespaces.Add(ns.Name); + var namespaces = state.SymScope.Namespaces; + count = namespaces.Count; + for (int i = 0; i < count; i++) + state.PdbScope.Namespaces.Add(namespaces[i].Name); + state.PdbScope.ImportScope = state.SymScope.ImportScope; + + var constants = state.SymScope.GetConstants(module, gpContext); + for (int i = 0; i < constants.Count; i++) { + var constant = constants[i]; + var type = constant.Type.RemovePinnedAndModifiers(); + if (type is not null) { + // Fix a few values since they're stored as some other type in the PDB + switch (type.ElementType) { + case ElementType.Boolean: + if (constant.Value is short) + constant.Value = (short)constant.Value != 0; + break; + case ElementType.Char: + if (constant.Value is ushort) + constant.Value = (char)(ushort)constant.Value; + break; + case ElementType.I1: + if (constant.Value is short) + constant.Value = (sbyte)(short)constant.Value; + break; + case ElementType.U1: + if (constant.Value is short) + constant.Value = (byte)(short)constant.Value; + break; + case ElementType.I2: + case ElementType.U2: + case ElementType.I4: + case ElementType.U4: + case ElementType.I8: + case ElementType.U8: + case ElementType.R4: + case ElementType.R8: + case ElementType.Void: + case ElementType.Ptr: + case ElementType.ByRef: + case ElementType.TypedByRef: + case ElementType.I: + case ElementType.U: + case ElementType.FnPtr: + case ElementType.ValueType: + break; + case ElementType.String: + if (PdbFileKind == PdbFileKind.WindowsPDB) { + // "" is stored as null, and null is stored as (int)0 + if (constant.Value is int && (int)constant.Value == 0) + constant.Value = null; + else if (constant.Value is null) + constant.Value = string.Empty; + } + else + Debug.Assert(PdbFileKind == PdbFileKind.PortablePDB || PdbFileKind == PdbFileKind.EmbeddedPortablePDB); + break; + case ElementType.Object: + case ElementType.Class: + case ElementType.SZArray: + case ElementType.Array: + default: + if (constant.Value is int && (int)constant.Value == 0) + constant.Value = null; + break; + case ElementType.GenericInst: + var gis = (GenericInstSig)type; + if (gis.GenericType is ValueTypeSig) + break; + goto case ElementType.Class; + case ElementType.Var: + case ElementType.MVar: + var gp = ((GenericSig)type).GenericParam; + if (gp is not null) { + if (gp.HasNotNullableValueTypeConstraint) + break; + if (gp.HasReferenceTypeConstraint) + goto case ElementType.Class; + } + break; + } + } + state.PdbScope.Constants.Add(constant); + } // Here's the now somewhat obfuscated for loop state.ChildrenIndex = 0; - state.Children = state.SymScope.GetChildren(); + state.Children = state.SymScope.Children; do_return: - if (state.ChildrenIndex < state.Children.Length) { + if (state.ChildrenIndex < state.Children.Count) { var child = state.Children[state.ChildrenIndex]; stack.Push(state); state = new CreateScopeState() { SymScope = child }; @@ -305,5 +449,17 @@ static Instruction GetInstruction(IList instrs, int offset, ref int } return null; } + + internal void InitializeCustomDebugInfos(MDToken token, GenericParamContext gpContext, IList result) { + Debug.Assert(token.Table != Table.Method, "Methods get initialized when reading the method bodies"); + reader?.GetCustomDebugInfos(token.ToInt32(), gpContext, result); + } + + internal void Dispose() => reader?.Dispose(); + } + + enum Compiler { + Other, + VisualBasic, } } diff --git a/src/DotNet/Pdb/PdbUtils.cs b/src/DotNet/Pdb/PdbUtils.cs new file mode 100644 index 000000000..d5a2181fa --- /dev/null +++ b/src/DotNet/Pdb/PdbUtils.cs @@ -0,0 +1,8 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.DotNet.Pdb { + static class PdbUtils { + public static bool IsEndInclusive(PdbFileKind pdbFileKind, Compiler compiler) => + pdbFileKind == PdbFileKind.WindowsPDB && compiler == Compiler.VisualBasic; + } +} diff --git a/src/DotNet/Pdb/PdbWriter.cs b/src/DotNet/Pdb/PdbWriter.cs deleted file mode 100644 index 0df6d38a6..000000000 --- a/src/DotNet/Pdb/PdbWriter.cs +++ /dev/null @@ -1,281 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Collections.Generic; -using System.Diagnostics.SymbolStore; -using dnlib.DotNet.Emit; -using dnlib.DotNet.Writer; - -namespace dnlib.DotNet.Pdb { - /// - /// PDB writer - /// - /// This class is not thread safe because it's a writer class - public sealed class PdbWriter : IDisposable { - ISymbolWriter2 writer; - readonly PdbState pdbState; - readonly ModuleDef module; - readonly MetaData metaData; - readonly Dictionary pdbDocs = new Dictionary(); - readonly SequencePointHelper seqPointsHelper = new SequencePointHelper(); - - /// - /// Gets/sets the logger - /// - public ILogger Logger { get; set; } - - /// - /// Constructor - /// - /// Symbol writer - /// PDB state - /// Meta data - public PdbWriter(ISymbolWriter2 writer, PdbState pdbState, MetaData metaData) { - if (writer == null) - throw new ArgumentNullException("writer"); - if (pdbState == null) - throw new ArgumentNullException("pdbState"); - if (metaData == null) - throw new ArgumentNullException("metaData"); - this.writer = writer; - this.pdbState = pdbState; - this.metaData = metaData; - this.module = metaData.Module; - writer.Initialize(metaData); - } - - /// - /// Adds if it doesn't exist - /// - /// PDB document - /// A instance - ISymbolDocumentWriter Add(PdbDocument pdbDoc) { - ISymbolDocumentWriter docWriter; - if (pdbDocs.TryGetValue(pdbDoc, out docWriter)) - return docWriter; - docWriter = writer.DefineDocument(pdbDoc.Url, pdbDoc.Language, pdbDoc.LanguageVendor, pdbDoc.DocumentType); - docWriter.SetCheckSum(pdbDoc.CheckSumAlgorithmId, pdbDoc.CheckSum); - pdbDocs.Add(pdbDoc, docWriter); - return docWriter; - } - - /// - /// Writes the PDB file - /// - public void Write() { - writer.SetUserEntryPoint(new SymbolToken(GetUserEntryPointToken())); - - foreach (var type in module.GetTypes()) { - if (type == null) - continue; - foreach (var method in type.Methods) { - if (method == null) - continue; - if (!ShouldAddMethod(method)) - continue; - Write(method); - } - } - } - - bool ShouldAddMethod(MethodDef method) { - var body = method.Body; - if (body == null) - return false; - - if (body.HasScope) - return true; - - foreach (var local in body.Variables) { - // Don't check whether it's the empty string. Only check for null. - if (local.Name != null) - return true; - if (local.PdbAttributes != 0) - return true; - } - - foreach (var instr in body.Instructions) { - if (instr.SequencePoint != null) - return true; - } - - return false; - } - - sealed class SequencePointHelper { - readonly Dictionary checkedPdbDocs = new Dictionary(); - int[] instrOffsets = new int[0]; - int[] startLines; - int[] startColumns; - int[] endLines; - int[] endColumns; - - public void Write(PdbWriter pdbWriter, IList instrs) { - checkedPdbDocs.Clear(); - while (true) { - PdbDocument currPdbDoc = null; - bool otherDocsAvailable = false; - int index = 0, instrOffset = 0; - Instruction instr = null; - for (int i = 0; i < instrs.Count; i++, instrOffset += instr.GetSize()) { - instr = instrs[i]; - var seqp = instr.SequencePoint; - if (seqp == null || seqp.Document == null) - continue; - if (checkedPdbDocs.ContainsKey(seqp.Document)) - continue; - if (currPdbDoc == null) - currPdbDoc = seqp.Document; - else if (currPdbDoc != seqp.Document) { - otherDocsAvailable = true; - continue; - } - - if (index >= instrOffsets.Length) { - int newSize = index * 2; - if (newSize < 64) - newSize = 64; - Array.Resize(ref instrOffsets, newSize); - Array.Resize(ref startLines, newSize); - Array.Resize(ref startColumns, newSize); - Array.Resize(ref endLines, newSize); - Array.Resize(ref endColumns, newSize); - } - - instrOffsets[index] = instrOffset; - startLines[index] = seqp.StartLine; - startColumns[index] = seqp.StartColumn; - endLines[index] = seqp.EndLine; - endColumns[index] = seqp.EndColumn; - index++; - } - if (index != 0) - pdbWriter.writer.DefineSequencePoints(pdbWriter.Add(currPdbDoc), (uint)index, instrOffsets, startLines, startColumns, endLines, endColumns); - - if (!otherDocsAvailable) - break; - if (currPdbDoc != null) - checkedPdbDocs.Add(currPdbDoc, true); - } - } - } - - void Write(MethodDef method) { - uint rid = metaData.GetRid(method); - if (rid == 0) { - Error("Method {0} ({1:X8}) is not defined in this module ({2})", method, method.MDToken.Raw, module); - return; - } - - var body = method.Body; - uint methodSize = GetSizeOfBody(body); - - writer.OpenMethod(new SymbolToken((int)new MDToken(MD.Table.Method, metaData.GetRid(method)).Raw)); - writer.OpenScope(0); - AddLocals(method, body.Variables, 0, methodSize); - seqPointsHelper.Write(this, body.Instructions); - foreach (var scope in GetScopes(body.Scope)) { - foreach (var ns in scope.Namespaces) - writer.UsingNamespace(ns); - } - writer.CloseScope((int)methodSize); - writer.CloseMethod(); - } - - IEnumerable GetScopes(PdbScope root) { - if (root == null) - return new PdbScope[0]; - return GetScopes(new PdbScope[1] { root }); - } - - IEnumerable GetScopes(IEnumerable scopes) { - var visited = new Dictionary(); - var stack = new Stack>(); - if (scopes != null) - stack.Push(scopes.GetEnumerator()); - while (stack.Count > 0) { - var enumerator = stack.Pop(); - while (enumerator.MoveNext()) { - var type = enumerator.Current; - if (visited.ContainsKey(type)) { - Error("PdbScope present more than once"); - continue; - } - visited[type] = true; - yield return type; - if (type.Scopes.Count > 0) { - stack.Push(enumerator); - enumerator = type.Scopes.GetEnumerator(); - } - } - } - } - - void AddLocals(MethodDef method, IList locals, uint startOffset, uint endOffset) { - if (locals.Count == 0) - return; - uint token = metaData.GetLocalVarSigToken(method); - if (token == 0) { - Error("Method {0} ({1:X8}) has no local signature token", method, method.MDToken.Raw); - return; - } - foreach (var local in locals) { - if (local.Name == null && local.PdbAttributes == 0) - continue; - writer.DefineLocalVariable2(local.Name ?? string.Empty, (uint)local.PdbAttributes, - token, 1, (uint)local.Index, 0, 0, startOffset, endOffset); - } - } - - uint GetSizeOfBody(CilBody body) { - uint offset = 0; - foreach (var instr in body.Instructions) - offset += (uint)instr.GetSize(); - return offset; - } - - int GetUserEntryPointToken() { - var ep = pdbState.UserEntryPoint; - if (ep == null) - return 0; - uint rid = metaData.GetRid(ep); - if (rid == 0) { - Error("PDB user entry point method {0} ({1:X8}) is not defined in this module ({2})", ep, ep.MDToken.Raw, module); - return 0; - } - return new MDToken(MD.Table.Method, rid).ToInt32(); - } - - /// - /// Gets the and debug data that should be written to - /// the PE file. - /// - /// Updated with new values - /// Debug data - public byte[] GetDebugInfo(out IMAGE_DEBUG_DIRECTORY idd) { - return writer.GetDebugInfo(out idd); - } - - /// - /// Closes the PDB writer - /// - public void Close() { - writer.Close(); - } - - ILogger GetLogger() { - return Logger ?? DummyLogger.ThrowModuleWriterExceptionOnErrorInstance; - } - - void Error(string message, params object[] args) { - GetLogger().Log(this, LoggerEvent.Error, message, args); - } - - /// - public void Dispose() { - if (writer != null) - Close(); - writer = null; - } - } -} diff --git a/src/DotNet/Pdb/Portable/DocumentNameReader.cs b/src/DotNet/Pdb/Portable/DocumentNameReader.cs new file mode 100644 index 000000000..ee63d76a0 --- /dev/null +++ b/src/DotNet/Pdb/Portable/DocumentNameReader.cs @@ -0,0 +1,98 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; +using System.Text; +using dnlib.DotNet.MD; +using dnlib.IO; + +namespace dnlib.DotNet.Pdb.Portable { + struct DocumentNameReader { + const int MAX_NAME_LENGTH = 64 * 1024; + readonly Dictionary docNamePartDict; + readonly BlobStream blobStream; + readonly StringBuilder sb; + + char[] prevSepChars; + int prevSepCharsLength; + byte[] prevSepCharBytes; + int prevSepCharBytesCount; + + public DocumentNameReader(BlobStream blobStream) { + docNamePartDict = new Dictionary(); + this.blobStream = blobStream; + sb = new StringBuilder(); + + prevSepChars = new char[2]; + prevSepCharsLength = 0; + prevSepCharBytes = new byte[3]; + prevSepCharBytesCount = 0; + } + + public string ReadDocumentName(uint offset) { + sb.Length = 0; + if (!blobStream.TryCreateReader(offset, out var reader)) + return string.Empty; + var sepChars = ReadSeparatorChar(ref reader, out int sepCharsLength); + bool needSep = false; + while (reader.Position < reader.Length) { + if (needSep) + sb.Append(sepChars, 0, sepCharsLength); + needSep = !(sepCharsLength == 1 && sepChars[0] == '\0'); + var part = ReadDocumentNamePart(reader.ReadCompressedUInt32()); + sb.Append(part); + if (sb.Length > MAX_NAME_LENGTH) { + sb.Length = MAX_NAME_LENGTH; + break; + } + } + return sb.ToString(); + } + + string ReadDocumentNamePart(uint offset) { + if (docNamePartDict.TryGetValue(offset, out var name)) + return name; + if (!blobStream.TryCreateReader(offset, out var reader)) + return string.Empty; + name = reader.ReadUtf8String((int)reader.BytesLeft); + docNamePartDict.Add(offset, name); + return name; + } + + char[] ReadSeparatorChar(ref DataReader reader, out int charLength) { + if (prevSepCharBytesCount != 0 && prevSepCharBytesCount <= reader.Length) { + var pos = reader.Position; + bool ok = true; + for (int i = 0; i < prevSepCharBytesCount; i++) { + if (i >= prevSepCharBytes.Length || reader.ReadByte() != prevSepCharBytes[i]) { + ok = false; + break; + } + } + if (ok) { + charLength = prevSepCharsLength; + return prevSepChars; + } + reader.Position = pos; + } + + var decoder = Encoding.UTF8.GetDecoder(); + var bytes = new byte[1]; + prevSepCharBytesCount = 0; + for (int i = 0; ; i++) { + byte b = reader.ReadByte(); + prevSepCharBytesCount++; + if (i == 0 && b == 0) + break; + if (i < prevSepCharBytes.Length) + prevSepCharBytes[i] = b; + bytes[0] = b; + bool isLastByte = reader.Position + 1 == reader.Length; + decoder.Convert(bytes, 0, 1, prevSepChars, 0, prevSepChars.Length, isLastByte, out int bytesUsed, out prevSepCharsLength, out bool completed); + if (prevSepCharsLength > 0) + break; + } + charLength = prevSepCharsLength; + return prevSepChars; + } + } +} diff --git a/src/DotNet/Pdb/Portable/ImportDefinitionKindUtils.cs b/src/DotNet/Pdb/Portable/ImportDefinitionKindUtils.cs new file mode 100644 index 000000000..c49791956 --- /dev/null +++ b/src/DotNet/Pdb/Portable/ImportDefinitionKindUtils.cs @@ -0,0 +1,42 @@ +// dnlib: See LICENSE.txt for more info + +using System.Diagnostics; + +namespace dnlib.DotNet.Pdb.Portable { + static class ImportDefinitionKindUtils { + public const PdbImportDefinitionKind UNKNOWN_IMPORT_KIND = (PdbImportDefinitionKind)(-1); + + public static PdbImportDefinitionKind ToPdbImportDefinitionKind(uint value) { + // See System.Reflection.Metadata.ImportDefinitionKind + switch (value) { + case 1: return PdbImportDefinitionKind.ImportNamespace; + case 2: return PdbImportDefinitionKind.ImportAssemblyNamespace; + case 3: return PdbImportDefinitionKind.ImportType; + case 4: return PdbImportDefinitionKind.ImportXmlNamespace; + case 5: return PdbImportDefinitionKind.ImportAssemblyReferenceAlias; + case 6: return PdbImportDefinitionKind.AliasAssemblyReference; + case 7: return PdbImportDefinitionKind.AliasNamespace; + case 8: return PdbImportDefinitionKind.AliasAssemblyNamespace; + case 9: return PdbImportDefinitionKind.AliasType; + default: + Debug.Fail($"Unknown import definition kind: 0x{value:X}"); + return UNKNOWN_IMPORT_KIND; + } + } + + public static bool ToImportDefinitionKind(PdbImportDefinitionKind kind, out uint rawKind) { + switch (kind) { + case PdbImportDefinitionKind.ImportNamespace: rawKind = 1; return true; + case PdbImportDefinitionKind.ImportAssemblyNamespace: rawKind = 2; return true; + case PdbImportDefinitionKind.ImportType: rawKind = 3; return true; + case PdbImportDefinitionKind.ImportXmlNamespace: rawKind = 4; return true; + case PdbImportDefinitionKind.ImportAssemblyReferenceAlias: rawKind = 5; return true; + case PdbImportDefinitionKind.AliasAssemblyReference: rawKind = 6; return true; + case PdbImportDefinitionKind.AliasNamespace: rawKind = 7; return true; + case PdbImportDefinitionKind.AliasAssemblyNamespace: rawKind = 8; return true; + case PdbImportDefinitionKind.AliasType: rawKind = 9; return true; + default: rawKind = uint.MaxValue; return false; + } + } + } +} diff --git a/src/DotNet/Pdb/Portable/ImportScopeBlobReader.cs b/src/DotNet/Pdb/Portable/ImportScopeBlobReader.cs new file mode 100644 index 000000000..7dad6eb19 --- /dev/null +++ b/src/DotNet/Pdb/Portable/ImportScopeBlobReader.cs @@ -0,0 +1,133 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; +using System.Diagnostics; +using dnlib.DotNet.MD; + +namespace dnlib.DotNet.Pdb.Portable { + // https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#imports-blob + readonly struct ImportScopeBlobReader { + readonly ModuleDef module; + readonly BlobStream blobStream; + + /// + /// Constructor + /// + /// Module that resolves assembly and type references + /// Portable PDB blob stream + public ImportScopeBlobReader(ModuleDef module, BlobStream blobStream) { + this.module = module; + this.blobStream = blobStream; + } + + public void Read(uint imports, IList result) { + if (imports == 0) + return; + if (!blobStream.TryCreateReader(imports, out var reader)) + return; + while (reader.Position < reader.Length) { + var kind = ImportDefinitionKindUtils.ToPdbImportDefinitionKind(reader.ReadCompressedUInt32()); + string targetNamespace, alias; + AssemblyRef targetAssembly; + PdbImport import; + ITypeDefOrRef targetType; + switch (kind) { + case PdbImportDefinitionKind.ImportNamespace: + // ::= ImportNamespace + targetNamespace = ReadUTF8(reader.ReadCompressedUInt32()); + import = new PdbImportNamespace(targetNamespace); + break; + + case PdbImportDefinitionKind.ImportAssemblyNamespace: + // ::= ImportAssemblyNamespace + targetAssembly = TryReadAssemblyRef(reader.ReadCompressedUInt32()); + targetNamespace = ReadUTF8(reader.ReadCompressedUInt32()); + import = new PdbImportAssemblyNamespace(targetAssembly, targetNamespace); + break; + + case PdbImportDefinitionKind.ImportType: + // ::= ImportType + targetType = TryReadType(reader.ReadCompressedUInt32()); + import = new PdbImportType(targetType); + break; + + case PdbImportDefinitionKind.ImportXmlNamespace: + // ::= ImportXmlNamespace + alias = ReadUTF8(reader.ReadCompressedUInt32()); + targetNamespace = ReadUTF8(reader.ReadCompressedUInt32()); + import = new PdbImportXmlNamespace(alias, targetNamespace); + break; + + case PdbImportDefinitionKind.ImportAssemblyReferenceAlias: + // ::= ImportReferenceAlias + alias = ReadUTF8(reader.ReadCompressedUInt32()); + import = new PdbImportAssemblyReferenceAlias(alias); + break; + + case PdbImportDefinitionKind.AliasAssemblyReference: + // ::= AliasAssemblyReference + alias = ReadUTF8(reader.ReadCompressedUInt32()); + targetAssembly = TryReadAssemblyRef(reader.ReadCompressedUInt32()); + import = new PdbAliasAssemblyReference(alias, targetAssembly); + break; + + case PdbImportDefinitionKind.AliasNamespace: + // ::= AliasNamespace + alias = ReadUTF8(reader.ReadCompressedUInt32()); + targetNamespace = ReadUTF8(reader.ReadCompressedUInt32()); + import = new PdbAliasNamespace(alias, targetNamespace); + break; + + case PdbImportDefinitionKind.AliasAssemblyNamespace: + // ::= AliasAssemblyNamespace + alias = ReadUTF8(reader.ReadCompressedUInt32()); + targetAssembly = TryReadAssemblyRef(reader.ReadCompressedUInt32()); + targetNamespace = ReadUTF8(reader.ReadCompressedUInt32()); + import = new PdbAliasAssemblyNamespace(alias, targetAssembly, targetNamespace); + break; + + case PdbImportDefinitionKind.AliasType: + // ::= AliasType + alias = ReadUTF8(reader.ReadCompressedUInt32()); + targetType = TryReadType(reader.ReadCompressedUInt32()); + import = new PdbAliasType(alias, targetType); + break; + + case ImportDefinitionKindUtils.UNKNOWN_IMPORT_KIND: + import = null; + break; + + default: + Debug.Fail($"Unknown import definition kind: {kind}"); + import = null; + break; + } + if (import is not null) + result.Add(import); + } + Debug.Assert(reader.Position == reader.Length); + } + + ITypeDefOrRef TryReadType(uint codedToken) { + bool b = CodedToken.TypeDefOrRef.Decode(codedToken, out uint token); + Debug.Assert(b); + if (!b) + return null; + var type = module.ResolveToken(token) as ITypeDefOrRef; + Debug.Assert(type is not null); + return type; + } + + AssemblyRef TryReadAssemblyRef(uint rid) { + var asmRef = module.ResolveToken(0x23000000 + rid) as AssemblyRef; + Debug.Assert(asmRef is not null); + return asmRef; + } + + string ReadUTF8(uint offset) { + if (!blobStream.TryCreateReader(offset, out var reader)) + return string.Empty; + return reader.ReadUtf8String((int)reader.BytesLeft); + } + } +} diff --git a/src/DotNet/Pdb/Portable/ImportScopeBlobWriter.cs b/src/DotNet/Pdb/Portable/ImportScopeBlobWriter.cs new file mode 100644 index 000000000..eac5468e2 --- /dev/null +++ b/src/DotNet/Pdb/Portable/ImportScopeBlobWriter.cs @@ -0,0 +1,115 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; +using System.Text; +using dnlib.DotNet.Writer; + +namespace dnlib.DotNet.Pdb.Portable { + // https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#imports-blob + readonly struct ImportScopeBlobWriter { + readonly IWriterError helper; + readonly Metadata systemMetadata; + readonly BlobHeap blobHeap; + + ImportScopeBlobWriter(IWriterError helper, Metadata systemMetadata, BlobHeap blobHeap) { + this.helper = helper; + this.systemMetadata = systemMetadata; + this.blobHeap = blobHeap; + } + + public static void Write(IWriterError helper, Metadata systemMetadata, DataWriter writer, BlobHeap blobHeap, IList imports) { + var blobWriter = new ImportScopeBlobWriter(helper, systemMetadata, blobHeap); + blobWriter.Write(writer, imports); + } + + uint WriteUTF8(string s) { + if (s is null) { + helper.Error("String is null"); + s = string.Empty; + } + var bytes = Encoding.UTF8.GetBytes(s); + return blobHeap.Add(bytes); + } + + void Write(DataWriter writer, IList imports) { + int count = imports.Count; + for (int i = 0; i < count; i++) { + var import = imports[i]; + if (!ImportDefinitionKindUtils.ToImportDefinitionKind(import.Kind, out uint rawKind)) { + helper.Error2("Unknown import definition kind: {0}.", import.Kind); + return; + } + writer.WriteCompressedUInt32(rawKind); + switch (import.Kind) { + case PdbImportDefinitionKind.ImportNamespace: + // ::= ImportNamespace + writer.WriteCompressedUInt32(WriteUTF8(((PdbImportNamespace)import).TargetNamespace)); + break; + + case PdbImportDefinitionKind.ImportAssemblyNamespace: + // ::= ImportAssemblyNamespace + writer.WriteCompressedUInt32(systemMetadata.GetToken(((PdbImportAssemblyNamespace)import).TargetAssembly).Rid); + writer.WriteCompressedUInt32(WriteUTF8(((PdbImportAssemblyNamespace)import).TargetNamespace)); + break; + + case PdbImportDefinitionKind.ImportType: + // ::= ImportType + writer.WriteCompressedUInt32(GetTypeDefOrRefEncodedToken(((PdbImportType)import).TargetType)); + break; + + case PdbImportDefinitionKind.ImportXmlNamespace: + // ::= ImportXmlNamespace + writer.WriteCompressedUInt32(WriteUTF8(((PdbImportXmlNamespace)import).Alias)); + writer.WriteCompressedUInt32(WriteUTF8(((PdbImportXmlNamespace)import).TargetNamespace)); + break; + + case PdbImportDefinitionKind.ImportAssemblyReferenceAlias: + // ::= ImportReferenceAlias + writer.WriteCompressedUInt32(WriteUTF8(((PdbImportAssemblyReferenceAlias)import).Alias)); + break; + + case PdbImportDefinitionKind.AliasAssemblyReference: + // ::= AliasAssemblyReference + writer.WriteCompressedUInt32(WriteUTF8(((PdbAliasAssemblyReference)import).Alias)); + writer.WriteCompressedUInt32(systemMetadata.GetToken(((PdbAliasAssemblyReference)import).TargetAssembly).Rid); + break; + + case PdbImportDefinitionKind.AliasNamespace: + // ::= AliasNamespace + writer.WriteCompressedUInt32(WriteUTF8(((PdbAliasNamespace)import).Alias)); + writer.WriteCompressedUInt32(WriteUTF8(((PdbAliasNamespace)import).TargetNamespace)); + break; + + case PdbImportDefinitionKind.AliasAssemblyNamespace: + // ::= AliasAssemblyNamespace + writer.WriteCompressedUInt32(WriteUTF8(((PdbAliasAssemblyNamespace)import).Alias)); + writer.WriteCompressedUInt32(systemMetadata.GetToken(((PdbAliasAssemblyNamespace)import).TargetAssembly).Rid); + writer.WriteCompressedUInt32(WriteUTF8(((PdbAliasAssemblyNamespace)import).TargetNamespace)); + break; + + case PdbImportDefinitionKind.AliasType: + // ::= AliasType + writer.WriteCompressedUInt32(WriteUTF8(((PdbAliasType)import).Alias)); + writer.WriteCompressedUInt32(GetTypeDefOrRefEncodedToken(((PdbAliasType)import).TargetType)); + break; + + default: + helper.Error2("Unknown import definition kind: {0}.", import.Kind); + return; + } + } + } + + uint GetTypeDefOrRefEncodedToken(ITypeDefOrRef tdr) { + if (tdr is null) { + helper.Error("ITypeDefOrRef is null"); + return 0; + } + var token = systemMetadata.GetToken(tdr); + if (MD.CodedToken.TypeDefOrRef.Encode(token, out uint codedToken)) + return codedToken; + helper.Error2("Could not encode token 0x{0:X8}.", token.Raw); + return 0; + } + } +} diff --git a/src/DotNet/Pdb/Portable/ListCache.cs b/src/DotNet/Pdb/Portable/ListCache.cs new file mode 100644 index 000000000..a1e9dcdb9 --- /dev/null +++ b/src/DotNet/Pdb/Portable/ListCache.cs @@ -0,0 +1,20 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; +using System.Threading; + +namespace dnlib.DotNet.Pdb.Portable { + static class ListCache { + static volatile List cachedList; + public static List AllocList() => Interlocked.Exchange(ref cachedList, null) ?? new List(); + public static void Free(ref List list) { + list.Clear(); + cachedList = list; + } + public static T[] FreeAndToArray(ref List list) { + var res = list.ToArray(); + Free(ref list); + return res; + } + } +} diff --git a/src/DotNet/Pdb/Portable/LocalConstantSigBlobReader.cs b/src/DotNet/Pdb/Portable/LocalConstantSigBlobReader.cs new file mode 100644 index 000000000..9f13e2491 --- /dev/null +++ b/src/DotNet/Pdb/Portable/LocalConstantSigBlobReader.cs @@ -0,0 +1,300 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Diagnostics; +using dnlib.IO; + +namespace dnlib.DotNet.Pdb.Portable { + struct LocalConstantSigBlobReader { + readonly ModuleDef module; + DataReader reader; + readonly GenericParamContext gpContext; + RecursionCounter recursionCounter; + + public LocalConstantSigBlobReader(ModuleDef module, ref DataReader reader, GenericParamContext gpContext) { + this.module = module; + this.reader = reader; + this.gpContext = gpContext; + recursionCounter = default; + } + + public bool Read(out TypeSig type, out object value) { + bool b = ReadCatch(out type, out value); + Debug.Assert(!b || reader.Position == reader.Length); + return b; + } + + bool ReadCatch(out TypeSig type, out object value) { + try { + return ReadCore(out type, out value); + } + catch { + } + type = null; + value = null; + return false; + } + + bool ReadCore(out TypeSig type, out object value) { + if (!recursionCounter.Increment()) { + type = null; + value = null; + return false; + } + + bool res; + ITypeDefOrRef tdr; + UTF8String ns, name; + var et = (ElementType)reader.ReadByte(); + switch (et) { + case ElementType.Boolean: + type = module.CorLibTypes.Boolean; + value = reader.ReadBoolean(); + if (reader.Position < reader.Length) + type = ReadTypeDefOrRefSig(); + res = true; + break; + + case ElementType.Char: + type = module.CorLibTypes.Char; + value = reader.ReadChar(); + if (reader.Position < reader.Length) + type = ReadTypeDefOrRefSig(); + res = true; + break; + + case ElementType.I1: + type = module.CorLibTypes.SByte; + value = reader.ReadSByte(); + if (reader.Position < reader.Length) + type = ReadTypeDefOrRefSig(); + res = true; + break; + + case ElementType.U1: + type = module.CorLibTypes.Byte; + value = reader.ReadByte(); + if (reader.Position < reader.Length) + type = ReadTypeDefOrRefSig(); + res = true; + break; + + case ElementType.I2: + type = module.CorLibTypes.Int16; + value = reader.ReadInt16(); + if (reader.Position < reader.Length) + type = ReadTypeDefOrRefSig(); + res = true; + break; + + case ElementType.U2: + type = module.CorLibTypes.UInt16; + value = reader.ReadUInt16(); + if (reader.Position < reader.Length) + type = ReadTypeDefOrRefSig(); + res = true; + break; + + case ElementType.I4: + type = module.CorLibTypes.Int32; + value = reader.ReadInt32(); + if (reader.Position < reader.Length) + type = ReadTypeDefOrRefSig(); + res = true; + break; + + case ElementType.U4: + type = module.CorLibTypes.UInt32; + value = reader.ReadUInt32(); + if (reader.Position < reader.Length) + type = ReadTypeDefOrRefSig(); + res = true; + break; + + case ElementType.I8: + type = module.CorLibTypes.Int64; + value = reader.ReadInt64(); + if (reader.Position < reader.Length) + type = ReadTypeDefOrRefSig(); + res = true; + break; + + case ElementType.U8: + type = module.CorLibTypes.UInt64; + value = reader.ReadUInt64(); + if (reader.Position < reader.Length) + type = ReadTypeDefOrRefSig(); + res = true; + break; + + case ElementType.R4: + type = module.CorLibTypes.Single; + value = reader.ReadSingle(); + res = true; + break; + + case ElementType.R8: + type = module.CorLibTypes.Double; + value = reader.ReadDouble(); + res = true; + break; + + case ElementType.String: + type = module.CorLibTypes.String; + value = ReadString(); + res = true; + break; + + case ElementType.Ptr: + res = ReadCatch(out type, out value); + if (res) + type = new PtrSig(type); + break; + + case ElementType.ByRef: + res = ReadCatch(out type, out value); + if (res) + type = new ByRefSig(type); + break; + + case ElementType.Object: + type = module.CorLibTypes.Object; + value = null; + res = true; + break; + + case ElementType.ValueType: + tdr = ReadTypeDefOrRef(); + type = tdr.ToTypeSig(); + value = null; + if (GetName(tdr, out ns, out name) && ns == stringSystem && tdr.DefinitionAssembly.IsCorLib()) { + if (name == stringDecimal) { + if (reader.Length - reader.Position != 13) + goto default; + try { + byte b = reader.ReadByte(); + value = new Decimal(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(), (b & 0x80) != 0, (byte)(b & 0x7F)); + } + catch { + goto default; + } + } + else if (name == stringDateTime) { + if (reader.Length - reader.Position != 8) + goto default; + try { + value = new DateTime(reader.ReadInt64()); + } + catch { + goto default; + } + } + } + if (value is null && reader.Position != reader.Length) + value = reader.ReadRemainingBytes(); + res = true; + break; + + case ElementType.Class: + type = new ClassSig(ReadTypeDefOrRef()); + value = reader.Position == reader.Length ? null : reader.ReadRemainingBytes(); + res = true; + break; + + case ElementType.CModReqd: + tdr = ReadTypeDefOrRef(); + res = ReadCatch(out type, out value); + if (res) + type = new CModReqdSig(tdr, type); + break; + + case ElementType.CModOpt: + tdr = ReadTypeDefOrRef(); + res = ReadCatch(out type, out value); + if (res) + type = new CModOptSig(tdr, type); + break; + + case ElementType.Var: + case ElementType.Array: + case ElementType.GenericInst: + case ElementType.TypedByRef: + case ElementType.I: + case ElementType.U: + case ElementType.FnPtr: + case ElementType.SZArray: + case ElementType.MVar: + case ElementType.End: + case ElementType.Void: + case ElementType.ValueArray: + case ElementType.R: + case ElementType.Internal: + case ElementType.Module: + case ElementType.Sentinel: + case ElementType.Pinned: + default: + Debug.Fail($"Unsupported element type in LocalConstant sig blob: {et}"); + res = false; + type = null; + value = null; + break; + } + + recursionCounter.Decrement(); + return res; + } + static readonly UTF8String stringSystem = new UTF8String("System"); + static readonly UTF8String stringDecimal = new UTF8String("Decimal"); + static readonly UTF8String stringDateTime = new UTF8String("DateTime"); + + static bool GetName(ITypeDefOrRef tdr, out UTF8String @namespace, out UTF8String name) { + if (tdr is TypeRef tr) { + @namespace = tr.Namespace; + name = tr.Name; + return true; + } + + if (tdr is TypeDef td) { + @namespace = td.Namespace; + name = td.Name; + return true; + } + + @namespace = null; + name = null; + return false; + } + + TypeSig ReadTypeDefOrRefSig() { + uint codedToken; + if (!reader.TryReadCompressedUInt32(out codedToken)) + return null; + ISignatureReaderHelper helper = module; + var tdr = helper.ResolveTypeDefOrRef(codedToken, gpContext); + return tdr.ToTypeSig(); + } + + ITypeDefOrRef ReadTypeDefOrRef() { + uint codedToken; + if (!reader.TryReadCompressedUInt32(out codedToken)) + return null; + ISignatureReaderHelper helper = module; + var tdr = helper.ResolveTypeDefOrRef(codedToken, gpContext); + var corType = module.CorLibTypes.GetCorLibTypeSig(tdr); + if (corType is not null) + return corType.TypeDefOrRef; + return tdr; + } + + string ReadString() { + if (reader.Position == reader.Length) + return string.Empty; + byte b = reader.ReadByte(); + if (b == 0xFF && reader.Position == reader.Length) + return null; + reader.Position--; + Debug.Assert((reader.BytesLeft & 1) == 0); + return reader.ReadUtf16String((int)(reader.BytesLeft / 2)); + } + } +} diff --git a/src/DotNet/Pdb/Portable/LocalConstantSigBlobWriter.cs b/src/DotNet/Pdb/Portable/LocalConstantSigBlobWriter.cs new file mode 100644 index 000000000..e39bd502a --- /dev/null +++ b/src/DotNet/Pdb/Portable/LocalConstantSigBlobWriter.cs @@ -0,0 +1,311 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Text; +using dnlib.DotNet.Writer; + +namespace dnlib.DotNet.Pdb.Portable { + readonly struct LocalConstantSigBlobWriter { + readonly IWriterError helper; + readonly Metadata systemMetadata; + + LocalConstantSigBlobWriter(IWriterError helper, Metadata systemMetadata) { + this.helper = helper; + this.systemMetadata = systemMetadata; + } + + public static void Write(IWriterError helper, Metadata systemMetadata, DataWriter writer, TypeSig type, object value) { + var sigWriter = new LocalConstantSigBlobWriter(helper, systemMetadata); + sigWriter.Write(writer, type, value); + } + + void Write(DataWriter writer, TypeSig type, object value) { + for (; ; type = type.Next) { + if (type is null) + return; + + var et = type.ElementType; + writer.WriteByte((byte)et); + switch (et) { + case ElementType.Boolean: + case ElementType.Char: + case ElementType.I1: + case ElementType.U1: + case ElementType.I2: + case ElementType.U2: + case ElementType.I4: + case ElementType.U4: + case ElementType.I8: + case ElementType.U8: + WritePrimitiveValue(writer, et, value); + return; + + case ElementType.R4: + if (value is float) + writer.WriteSingle((float)value); + else { + helper.Error("Expected a Single constant"); + writer.WriteSingle(0); + } + return; + + case ElementType.R8: + if (value is double) + writer.WriteDouble((double)value); + else { + helper.Error("Expected a Double constant"); + writer.WriteDouble(0); + } + return; + + case ElementType.String: + if (value is null) + writer.WriteByte((byte)0xFF); + else if (value is string) + writer.WriteBytes(Encoding.Unicode.GetBytes((string)value)); + else + helper.Error("Expected a String constant"); + return; + + case ElementType.Ptr: + case ElementType.ByRef: + WriteTypeDefOrRef(writer, new TypeSpecUser(type)); + return; + + case ElementType.Object: + return; + + case ElementType.ValueType: + var tdr = ((ValueTypeSig)type).TypeDefOrRef; + var td = tdr.ResolveTypeDef(); + if (td is null) + helper.Error2("Couldn't resolve type 0x{0:X8}.", tdr?.MDToken.Raw ?? 0); + else if (td.IsEnum) { + var underlyingType = td.GetEnumUnderlyingType().RemovePinnedAndModifiers(); + switch (underlyingType.GetElementType()) { + case ElementType.Boolean: + case ElementType.Char: + case ElementType.I1: + case ElementType.U1: + case ElementType.I2: + case ElementType.U2: + case ElementType.I4: + case ElementType.U4: + case ElementType.I8: + case ElementType.U8: + writer.Position--; + writer.WriteByte((byte)underlyingType.GetElementType()); + WritePrimitiveValue(writer, underlyingType.GetElementType(), value); + WriteTypeDefOrRef(writer, tdr); + return; + default: + helper.Error("Invalid enum underlying type"); + return; + } + } + else { + WriteTypeDefOrRef(writer, tdr); + bool valueWritten = false; + if (GetName(tdr, out var ns, out var name) && ns == stringSystem && tdr.DefinitionAssembly.IsCorLib()) { + if (name == stringDecimal) { + if (value is decimal) { + var bits = decimal.GetBits((decimal)value); + writer.WriteByte((byte)((((uint)bits[3] >> 31) << 7) | (((uint)bits[3] >> 16) & 0x7F))); + writer.WriteInt32(bits[0]); + writer.WriteInt32(bits[1]); + writer.WriteInt32(bits[2]); + } + else { + helper.Error("Expected a Decimal constant"); + writer.WriteBytes(new byte[13]); + } + valueWritten = true; + } + else if (name == stringDateTime) { + if (value is DateTime) + writer.WriteInt64(((DateTime)value).Ticks); + else { + helper.Error("Expected a DateTime constant"); + writer.WriteInt64(0); + } + valueWritten = true; + } + } + if (!valueWritten) { + if (value is byte[]) + writer.WriteBytes((byte[])value); + else if (value is not null) { + helper.Error2("Unsupported constant: {0}.", value.GetType().FullName); + return; + } + } + } + return; + + case ElementType.Class: + WriteTypeDefOrRef(writer, ((ClassSig)type).TypeDefOrRef); + if (value is byte[]) + writer.WriteBytes((byte[])value); + else if (value is not null) + helper.Error("Expected a null constant"); + return; + + case ElementType.CModReqd: + case ElementType.CModOpt: + WriteTypeDefOrRef(writer, ((ModifierSig)type).Modifier); + break; + + case ElementType.Var: + case ElementType.Array: + case ElementType.GenericInst: + case ElementType.TypedByRef: + case ElementType.I: + case ElementType.U: + case ElementType.FnPtr: + case ElementType.SZArray: + case ElementType.MVar: + WriteTypeDefOrRef(writer, new TypeSpecUser(type)); + return; + + case ElementType.End: + case ElementType.Void: + case ElementType.ValueArray: + case ElementType.R: + case ElementType.Internal: + case ElementType.Module: + case ElementType.Sentinel: + case ElementType.Pinned: + default: + helper.Error2("Unsupported element type in LocalConstant sig blob: {0}.", et); + return; + } + } + } + static readonly UTF8String stringSystem = new UTF8String("System"); + static readonly UTF8String stringDecimal = new UTF8String("Decimal"); + static readonly UTF8String stringDateTime = new UTF8String("DateTime"); + + static bool GetName(ITypeDefOrRef tdr, out UTF8String @namespace, out UTF8String name) { + if (tdr is TypeRef tr) { + @namespace = tr.Namespace; + name = tr.Name; + return true; + } + + if (tdr is TypeDef td) { + @namespace = td.Namespace; + name = td.Name; + return true; + } + + @namespace = null; + name = null; + return false; + } + + void WritePrimitiveValue(DataWriter writer, ElementType et, object value) { + switch (et) { + case ElementType.Boolean: + if (value is bool) + writer.WriteBoolean((bool)value); + else { + helper.Error("Expected a Boolean constant"); + writer.WriteBoolean(false); + } + break; + + case ElementType.Char: + if (value is char) + writer.WriteUInt16((char)value); + else { + helper.Error("Expected a Char constant"); + writer.WriteUInt16(0); + } + break; + + case ElementType.I1: + if (value is sbyte) + writer.WriteSByte((sbyte)value); + else { + helper.Error("Expected a SByte constant"); + writer.WriteSByte(0); + } + break; + + case ElementType.U1: + if (value is byte) + writer.WriteByte((byte)value); + else { + helper.Error("Expected a Byte constant"); + writer.WriteByte(0); + } + break; + + case ElementType.I2: + if (value is short) + writer.WriteInt16((short)value); + else { + helper.Error("Expected an Int16 constant"); + writer.WriteInt16(0); + } + break; + + case ElementType.U2: + if (value is ushort) + writer.WriteUInt16((ushort)value); + else { + helper.Error("Expected a UInt16 constant"); + writer.WriteUInt16(0); + } + break; + + case ElementType.I4: + if (value is int) + writer.WriteInt32((int)value); + else { + helper.Error("Expected an Int32 constant"); + writer.WriteInt32(0); + } + break; + + case ElementType.U4: + if (value is uint) + writer.WriteUInt32((uint)value); + else { + helper.Error("Expected a UInt32 constant"); + writer.WriteUInt32(0); + } + break; + + case ElementType.I8: + if (value is long) + writer.WriteInt64((long)value); + else { + helper.Error("Expected an Int64 constant"); + writer.WriteInt64(0); + } + break; + + case ElementType.U8: + if (value is ulong) + writer.WriteUInt64((ulong)value); + else { + helper.Error("Expected a UInt64 constant"); + writer.WriteUInt64(0); + } + break; + + default: + throw new InvalidOperationException(); + } + } + + void WriteTypeDefOrRef(DataWriter writer, ITypeDefOrRef tdr) { + if (!MD.CodedToken.TypeDefOrRef.Encode(systemMetadata.GetToken(tdr), out uint codedToken)) { + helper.Error("Couldn't encode a TypeDefOrRef"); + return; + } + writer.WriteCompressedUInt32(codedToken); + } + } +} diff --git a/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoReader.cs b/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoReader.cs new file mode 100644 index 000000000..d110ab9a1 --- /dev/null +++ b/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoReader.cs @@ -0,0 +1,305 @@ +// dnlib: See LICENSE.txt for more info + +// See Roslyn files: MethodDebugInfo.Portable.cs, MetadataWriter.PortablePdb.cs + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using dnlib.DotNet.Emit; +using dnlib.DotNet.MD; +using dnlib.IO; + +namespace dnlib.DotNet.Pdb.Portable { + struct PortablePdbCustomDebugInfoReader { + public static PdbCustomDebugInfo Read(ModuleDef module, TypeDef typeOpt, CilBody bodyOpt, GenericParamContext gpContext, Guid kind, ref DataReader reader) { + try { + var cdiReader = new PortablePdbCustomDebugInfoReader(module, typeOpt, bodyOpt, gpContext, ref reader); + var cdi = cdiReader.Read(kind); + Debug.Assert(cdiReader.reader.Position == cdiReader.reader.Length); + return cdi; + } + catch (ArgumentException) { + } + catch (OutOfMemoryException) { + } + catch (IOException) { + } + return null; + } + + readonly ModuleDef module; + readonly TypeDef typeOpt; + readonly CilBody bodyOpt; + readonly GenericParamContext gpContext; + DataReader reader; + + PortablePdbCustomDebugInfoReader(ModuleDef module, TypeDef typeOpt, CilBody bodyOpt, GenericParamContext gpContext, ref DataReader reader) { + this.module = module; + this.typeOpt = typeOpt; + this.bodyOpt = bodyOpt; + this.gpContext = gpContext; + this.reader = reader; + } + + PdbCustomDebugInfo Read(Guid kind) { + if (kind == CustomDebugInfoGuids.AsyncMethodSteppingInformationBlob) + return ReadAsyncMethodSteppingInformationBlob(); + if (kind == CustomDebugInfoGuids.DefaultNamespace) + return ReadDefaultNamespace(); + if (kind == CustomDebugInfoGuids.DynamicLocalVariables) + return ReadDynamicLocalVariables(reader.Length); + if (kind == CustomDebugInfoGuids.EmbeddedSource) + return ReadEmbeddedSource(); + if (kind == CustomDebugInfoGuids.EncLambdaAndClosureMap) + return ReadEncLambdaAndClosureMap(reader.Length); + if (kind == CustomDebugInfoGuids.EncLocalSlotMap) + return ReadEncLocalSlotMap(reader.Length); + if (kind == CustomDebugInfoGuids.SourceLink) + return ReadSourceLink(); + if (kind == CustomDebugInfoGuids.StateMachineHoistedLocalScopes) + return ReadStateMachineHoistedLocalScopes(); + if (kind == CustomDebugInfoGuids.TupleElementNames) + return ReadTupleElementNames(); + if (kind == CustomDebugInfoGuids.CompilationMetadataReferences) + return ReadCompilationMetadataReferences(); + if (kind == CustomDebugInfoGuids.CompilationOptions) + return ReadCompilationOptions(); + if (kind == CustomDebugInfoGuids.TypeDefinitionDocuments) + return ReadTypeDefinitionDocuments(); + if (kind == CustomDebugInfoGuids.EncStateMachineStateMap) + return ReadEncStateMachineStateMap(); + if (kind == CustomDebugInfoGuids.PrimaryConstructorInformationBlob) + return ReadPrimaryConstructorInformationBlob(); + Debug.Fail($"Unknown custom debug info guid: {kind}"); + return new PdbUnknownCustomDebugInfo(kind, reader.ReadRemainingBytes()); + } + + PdbCustomDebugInfo ReadAsyncMethodSteppingInformationBlob() { + if (bodyOpt is null) + return null; + uint catchHandlerOffset = reader.ReadUInt32() - 1; + Instruction catchHandler; + if (catchHandlerOffset == uint.MaxValue) + catchHandler = null; + else { + catchHandler = GetInstruction(catchHandlerOffset); + Debug.Assert(catchHandler is not null); + if (catchHandler is null) + return null; + } + var asyncInfo = new PdbAsyncMethodSteppingInformationCustomDebugInfo(); + asyncInfo.CatchHandler = catchHandler; + while (reader.Position < reader.Length) { + var yieldInstr = GetInstruction(reader.ReadUInt32()); + Debug.Assert(yieldInstr is not null); + if (yieldInstr is null) + return null; + uint resumeOffset = reader.ReadUInt32(); + var moveNextRid = reader.ReadCompressedUInt32(); + var moveNextToken = new MDToken(Table.Method, moveNextRid); + MethodDef moveNextMethod; + Instruction resumeInstr; + if (gpContext.Method is not null && moveNextToken == gpContext.Method.MDToken) { + moveNextMethod = gpContext.Method; + resumeInstr = GetInstruction(resumeOffset); + } + else { + moveNextMethod = module.ResolveToken(moveNextToken, gpContext) as MethodDef; + Debug.Assert(moveNextMethod is not null); + if (moveNextMethod is null) + return null; + resumeInstr = GetInstruction(moveNextMethod, resumeOffset); + } + Debug.Assert(resumeInstr is not null); + if (resumeInstr is null) + return null; + asyncInfo.AsyncStepInfos.Add(new PdbAsyncStepInfo(yieldInstr, moveNextMethod, resumeInstr)); + } + return asyncInfo; + } + + PdbCustomDebugInfo ReadDefaultNamespace() { + var defaultNs = reader.ReadUtf8String((int)reader.BytesLeft); + return new PdbDefaultNamespaceCustomDebugInfo(defaultNs); + } + + PdbCustomDebugInfo ReadDynamicLocalVariables(long recPosEnd) { + var flags = new bool[(int)reader.Length * 8]; + int w = 0; + while (reader.Position < reader.Length) { + int b = reader.ReadByte(); + for (int i = 1; i < 0x100; i <<= 1) + flags[w++] = (b & i) != 0; + } + return new PdbDynamicLocalVariablesCustomDebugInfo(flags); + } + + PdbCustomDebugInfo ReadEmbeddedSource() => new PdbEmbeddedSourceCustomDebugInfo(reader.ReadRemainingBytes()); + + PdbCustomDebugInfo ReadEncLambdaAndClosureMap(long recPosEnd) { + var data = reader.ReadBytes((int)(recPosEnd - reader.Position)); + return new PdbEditAndContinueLambdaMapCustomDebugInfo(data); + } + + PdbCustomDebugInfo ReadEncLocalSlotMap(long recPosEnd) { + var data = reader.ReadBytes((int)(recPosEnd - reader.Position)); + return new PdbEditAndContinueLocalSlotMapCustomDebugInfo(data); + } + + PdbCustomDebugInfo ReadSourceLink() => new PdbSourceLinkCustomDebugInfo(reader.ReadRemainingBytes()); + + PdbCustomDebugInfo ReadStateMachineHoistedLocalScopes() { + if (bodyOpt is null) + return null; + int count = (int)(reader.Length / 8); + var smScope = new PdbStateMachineHoistedLocalScopesCustomDebugInfo(count); + for (int i = 0; i < count; i++) { + uint startOffset = reader.ReadUInt32(); + uint length = reader.ReadUInt32(); + if (startOffset == 0 && length == 0) + smScope.Scopes.Add(new StateMachineHoistedLocalScope()); + else { + var start = GetInstruction(startOffset); + var end = GetInstruction(startOffset + length); + Debug.Assert(start is not null); + if (start is null) + return null; + smScope.Scopes.Add(new StateMachineHoistedLocalScope(start, end)); + } + } + return smScope; + } + + PdbCustomDebugInfo ReadTupleElementNames() { + var tupleListRec = new PortablePdbTupleElementNamesCustomDebugInfo(); + while (reader.Position < reader.Length) { + var name = ReadUTF8Z(reader.Length); + tupleListRec.Names.Add(name); + } + return tupleListRec; + } + + string ReadUTF8Z(long recPosEnd) { + if (reader.Position > recPosEnd) + return null; + return reader.TryReadZeroTerminatedUtf8String(); + } + + PdbCustomDebugInfo ReadCompilationMetadataReferences() { + var cdi = new PdbCompilationMetadataReferencesCustomDebugInfo(); + + while (reader.BytesLeft > 0) { + var name = reader.TryReadZeroTerminatedUtf8String(); + Debug.Assert(name is not null); + if (name is null) + break; + var aliases = reader.TryReadZeroTerminatedUtf8String(); + Debug.Assert(aliases is not null); + if (aliases is null) + break; + + const uint RequiredBytes = 1 + 4 + 4 + 16; + Debug.Assert(reader.BytesLeft >= RequiredBytes); + if (reader.BytesLeft < RequiredBytes) + break; + + var flags = (PdbCompilationMetadataReferenceFlags)reader.ReadByte(); + uint timestamp = reader.ReadUInt32(); + uint sizeOfImage = reader.ReadUInt32(); + var mvid = reader.ReadGuid(); + + var mdRef = new PdbCompilationMetadataReference(name, aliases, flags, timestamp, sizeOfImage, mvid); + cdi.References.Add(mdRef); + } + + return cdi; + } + + PdbCustomDebugInfo ReadCompilationOptions() { + var cdi = new PdbCompilationOptionsCustomDebugInfo(); + + while (reader.BytesLeft > 0) { + var key = reader.TryReadZeroTerminatedUtf8String(); + Debug.Assert(key is not null); + if (key is null) + break; + var value = reader.TryReadZeroTerminatedUtf8String(); + Debug.Assert(value is not null); + if (value is null) + break; + cdi.Options.Add(new KeyValuePair(key, value)); + } + + return cdi; + } + + PdbCustomDebugInfo ReadTypeDefinitionDocuments() { + var docList = new List(); + while (reader.BytesLeft > 0) + docList.Add(new MDToken(Table.Document, reader.ReadCompressedUInt32())); + + return new PdbTypeDefinitionDocumentsDebugInfoMD(module, docList); + } + + PdbCustomDebugInfo ReadEncStateMachineStateMap() { + var cdi = new PdbEditAndContinueStateMachineStateMapDebugInfo(); + + var count = reader.ReadCompressedUInt32(); + if (count > 0) { + long syntaxOffsetBaseline = -reader.ReadCompressedUInt32(); + + while (count > 0) { + int stateNumber = reader.ReadCompressedInt32(); + int syntaxOffset = (int)(syntaxOffsetBaseline + reader.ReadCompressedUInt32()); + + cdi.StateMachineStates.Add(new StateMachineStateInfo(syntaxOffset, (StateMachineState)stateNumber)); + + count--; + } + } + + return cdi; + } + + PdbCustomDebugInfo ReadPrimaryConstructorInformationBlob() => + new PrimaryConstructorInformationBlobDebugInfo(reader.ReadRemainingBytes()); + + Instruction GetInstruction(uint offset) { + var instructions = bodyOpt.Instructions; + int lo = 0, hi = instructions.Count - 1; + while (lo <= hi && hi != -1) { + int i = (lo + hi) / 2; + var instr = instructions[i]; + if (instr.Offset == offset) + return instr; + if (offset < instr.Offset) + hi = i - 1; + else + lo = i + 1; + } + return null; + } + + static Instruction GetInstruction(MethodDef method, uint offset) { + if (method is null) + return null; + var body = method.Body; + if (body is null) + return null; + var instructions = body.Instructions; + int lo = 0, hi = instructions.Count - 1; + while (lo <= hi && hi != -1) { + int i = (lo + hi) / 2; + var instr = instructions[i]; + if (instr.Offset == offset) + return instr; + if (offset < instr.Offset) + hi = i - 1; + else + lo = i + 1; + } + return null; + } + } +} diff --git a/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoWriter.cs b/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoWriter.cs new file mode 100644 index 000000000..27e143164 --- /dev/null +++ b/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoWriter.cs @@ -0,0 +1,364 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.IO; +using System.Linq; +using System.Text; +using dnlib.DotNet.Emit; +using dnlib.DotNet.Writer; + +namespace dnlib.DotNet.Pdb.Portable { + interface IPortablePdbCustomDebugInfoWriterHelper : IWriterError { + } + + readonly struct PortablePdbCustomDebugInfoWriter { + readonly IPortablePdbCustomDebugInfoWriterHelper helper; + readonly SerializerMethodContext methodContext; + readonly Metadata systemMetadata; + readonly MemoryStream outStream; + readonly DataWriter writer; + + public static byte[] Write(IPortablePdbCustomDebugInfoWriterHelper helper, SerializerMethodContext methodContext, Metadata systemMetadata, PdbCustomDebugInfo cdi, DataWriterContext context) { + var writer = new PortablePdbCustomDebugInfoWriter(helper, methodContext, systemMetadata, context); + return writer.Write(cdi); + } + + PortablePdbCustomDebugInfoWriter(IPortablePdbCustomDebugInfoWriterHelper helper, SerializerMethodContext methodContext, Metadata systemMetadata, DataWriterContext context) { + this.helper = helper; + this.methodContext = methodContext; + this.systemMetadata = systemMetadata; + outStream = context.OutStream; + writer = context.Writer; + outStream.SetLength(0); + outStream.Position = 0; + } + + byte[] Write(PdbCustomDebugInfo cdi) { + switch (cdi.Kind) { + case PdbCustomDebugInfoKind.UsingGroups: + case PdbCustomDebugInfoKind.ForwardMethodInfo: + case PdbCustomDebugInfoKind.ForwardModuleInfo: + case PdbCustomDebugInfoKind.StateMachineTypeName: + case PdbCustomDebugInfoKind.DynamicLocals: + case PdbCustomDebugInfoKind.TupleElementNames: + case PdbCustomDebugInfoKind.IteratorMethod: + case PdbCustomDebugInfoKind.SourceServer: + default: + helper.Error("Unreachable code, caller should filter these out"); + return null; + + case PdbCustomDebugInfoKind.StateMachineHoistedLocalScopes: + WriteStateMachineHoistedLocalScopes((PdbStateMachineHoistedLocalScopesCustomDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.EditAndContinueLocalSlotMap: + WriteEditAndContinueLocalSlotMap((PdbEditAndContinueLocalSlotMapCustomDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.EditAndContinueLambdaMap: + WriteEditAndContinueLambdaMap((PdbEditAndContinueLambdaMapCustomDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.Unknown: + WriteUnknown((PdbUnknownCustomDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.TupleElementNames_PortablePdb: + WriteTupleElementNames((PortablePdbTupleElementNamesCustomDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.DefaultNamespace: + WriteDefaultNamespace((PdbDefaultNamespaceCustomDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.DynamicLocalVariables: + WriteDynamicLocalVariables((PdbDynamicLocalVariablesCustomDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.EmbeddedSource: + WriteEmbeddedSource((PdbEmbeddedSourceCustomDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.SourceLink: + WriteSourceLink((PdbSourceLinkCustomDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.AsyncMethod: + WriteAsyncMethodSteppingInformation((PdbAsyncMethodCustomDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.CompilationMetadataReferences: + WriteCompilationMetadataReferences((PdbCompilationMetadataReferencesCustomDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.CompilationOptions: + WriteCompilationOptions((PdbCompilationOptionsCustomDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.TypeDefinitionDocuments: + WriteTypeDefinitionDocuments((PdbTypeDefinitionDocumentsDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.EditAndContinueStateMachineStateMap: + WriteEditAndContinueStateMachineStateMap((PdbEditAndContinueStateMachineStateMapDebugInfo)cdi); + break; + + case PdbCustomDebugInfoKind.PrimaryConstructorInformationBlob: + WritePrimaryConstructorInformationBlob((PrimaryConstructorInformationBlobDebugInfo)cdi); + break; + } + return outStream.ToArray(); + } + + void WriteUTF8Z(string s) { + if (s.IndexOf('\0') >= 0) + helper.Error("String must not contain any NUL bytes"); + var bytes = Encoding.UTF8.GetBytes(s); + writer.WriteBytes(bytes); + writer.WriteByte(0); + } + + void WriteStateMachineHoistedLocalScopes(PdbStateMachineHoistedLocalScopesCustomDebugInfo cdi) { + if (!methodContext.HasBody) { + helper.Error2("Method has no body, can't write custom debug info: {0}.", cdi.Kind); + return; + } + var cdiScopes = cdi.Scopes; + int count = cdiScopes.Count; + for (int i = 0; i < count; i++) { + var scope = cdiScopes[i]; + uint startOffset, endOffset; + if (scope.IsSynthesizedLocal) { + startOffset = 0; + endOffset = 0; + } + else { + var startInstr = scope.Start; + if (startInstr is null) { + helper.Error("Instruction is null"); + return; + } + startOffset = methodContext.GetOffset(startInstr); + endOffset = methodContext.GetOffset(scope.End); + } + if (startOffset > endOffset) { + helper.Error("End instruction is before start instruction"); + return; + } + writer.WriteUInt32(startOffset); + writer.WriteUInt32(endOffset - startOffset); + } + } + + void WriteEditAndContinueLocalSlotMap(PdbEditAndContinueLocalSlotMapCustomDebugInfo cdi) { + var d = cdi.Data; + if (d is null) { + helper.Error("Data blob is null"); + return; + } + writer.WriteBytes(d); + } + + void WriteEditAndContinueLambdaMap(PdbEditAndContinueLambdaMapCustomDebugInfo cdi) { + var d = cdi.Data; + if (d is null) { + helper.Error("Data blob is null"); + return; + } + writer.WriteBytes(d); + } + + void WriteUnknown(PdbUnknownCustomDebugInfo cdi) { + var d = cdi.Data; + if (d is null) { + helper.Error("Data blob is null"); + return; + } + writer.WriteBytes(d); + } + + void WriteTupleElementNames(PortablePdbTupleElementNamesCustomDebugInfo cdi) { + var cdiNames = cdi.Names; + int count = cdiNames.Count; + for (int i = 0; i < count; i++) { + var name = cdiNames[i]; + if (name is null) { + helper.Error("Tuple name is null"); + return; + } + WriteUTF8Z(name); + } + } + + void WriteDefaultNamespace(PdbDefaultNamespaceCustomDebugInfo cdi) { + var ns = cdi.Namespace; + if (ns is null) { + helper.Error("Default namespace is null"); + return; + } + var bytes = Encoding.UTF8.GetBytes(ns); + writer.WriteBytes(bytes); + } + + void WriteDynamicLocalVariables(PdbDynamicLocalVariablesCustomDebugInfo cdi) { + var flags = cdi.Flags; + for (int i = 0; i < flags.Length; i += 8) + writer.WriteByte(ToByte(flags, i)); + } + + static byte ToByte(bool[] flags, int index) { + int res = 0; + int bit = 1; + for (int i = index; i < flags.Length; i++, bit <<= 1) { + if (flags[i]) + res |= bit; + } + return (byte)res; + } + + void WriteEmbeddedSource(PdbEmbeddedSourceCustomDebugInfo cdi) { + var d = cdi.SourceCodeBlob; + if (d is null) { + helper.Error("Source code blob is null"); + return; + } + writer.WriteBytes(d); + } + + void WriteSourceLink(PdbSourceLinkCustomDebugInfo cdi) { + var d = cdi.FileBlob; + if (d is null) { + helper.Error("Source link blob is null"); + return; + } + writer.WriteBytes(d); + } + + void WriteAsyncMethodSteppingInformation(PdbAsyncMethodCustomDebugInfo cdi) { + if (!methodContext.HasBody) { + helper.Error2("Method has no body, can't write custom debug info: {0}.", cdi.Kind); + return; + } + + uint catchHandlerOffset; + if (cdi.CatchHandlerInstruction is null) + catchHandlerOffset = 0; + else + catchHandlerOffset = methodContext.GetOffset(cdi.CatchHandlerInstruction) + 1; + writer.WriteUInt32(catchHandlerOffset); + + var cdiStepInfos = cdi.StepInfos; + int count = cdiStepInfos.Count; + for (int i = 0; i < count; i++) { + var info = cdiStepInfos[i]; + if (info.YieldInstruction is null) { + helper.Error("YieldInstruction is null"); + return; + } + if (info.BreakpointMethod is null) { + helper.Error("BreakpointMethod is null"); + return; + } + if (info.BreakpointInstruction is null) { + helper.Error("BreakpointInstruction is null"); + return; + } + uint yieldOffset = methodContext.GetOffset(info.YieldInstruction); + uint resumeOffset; + if (methodContext.IsSameMethod(info.BreakpointMethod)) + resumeOffset = methodContext.GetOffset(info.BreakpointInstruction); + else + resumeOffset = GetOffsetSlow(info.BreakpointMethod, info.BreakpointInstruction); + uint resumeMethodRid = systemMetadata.GetRid(info.BreakpointMethod); + writer.WriteUInt32(yieldOffset); + writer.WriteUInt32(resumeOffset); + writer.WriteCompressedUInt32(resumeMethodRid); + } + } + + uint GetOffsetSlow(MethodDef method, Instruction instr) { + var body = method.Body; + if (body is null) { + helper.Error("Method has no body"); + return uint.MaxValue; + } + var instrs = body.Instructions; + uint offset = 0; + for (int i = 0; i < instrs.Count; i++) { + var instr2 = instrs[i]; + if (instr2 == instr) + return offset; + offset += (uint)instr2.GetSize(); + } + helper.Error("Couldn't find an instruction, maybe it was removed. It's still being referenced by some code or by the PDB"); + return uint.MaxValue; + } + + void WriteCompilationMetadataReferences(PdbCompilationMetadataReferencesCustomDebugInfo cdi) { + foreach (var mdRef in cdi.References) { + var name = mdRef.Name; + if (name is null) { + helper.Error("Metadata reference name is null"); + return; + } + WriteUTF8Z(name); + + var aliases = mdRef.Aliases; + if (aliases is null) { + helper.Error("Metadata reference aliases is null"); + return; + } + WriteUTF8Z(aliases); + + writer.WriteByte((byte)mdRef.Flags); + writer.WriteUInt32(mdRef.Timestamp); + writer.WriteUInt32(mdRef.SizeOfImage); + writer.WriteBytes(mdRef.Mvid.ToByteArray()); + } + } + + void WriteCompilationOptions(PdbCompilationOptionsCustomDebugInfo cdi) { + foreach (var kv in cdi.Options) { + if (kv.Key is null) { + helper.Error("Compiler option `key` is null"); + return; + } + if (kv.Value is null) { + helper.Error("Compiler option `value` is null"); + return; + } + WriteUTF8Z(kv.Key); + WriteUTF8Z(kv.Value); + } + } + + void WriteTypeDefinitionDocuments(PdbTypeDefinitionDocumentsDebugInfo cdi) { + foreach (var document in cdi.Documents) + writer.WriteCompressedUInt32(systemMetadata.GetRid(document)); + } + + void WriteEditAndContinueStateMachineStateMap(PdbEditAndContinueStateMachineStateMapDebugInfo cdi) { + writer.WriteCompressedUInt32((uint)cdi.StateMachineStates.Count); + + if (cdi.StateMachineStates.Count <= 0) + return; + + int syntaxOffsetBaseline = Math.Min(cdi.StateMachineStates.Min(state => state.SyntaxOffset), 0); + writer.WriteCompressedUInt32((uint)-syntaxOffsetBaseline); + + foreach (var state in cdi.StateMachineStates) { + writer.WriteCompressedInt32((int)state.State); + writer.WriteCompressedUInt32((uint)(state.SyntaxOffset - syntaxOffsetBaseline)); + } + } + + void WritePrimaryConstructorInformationBlob(PrimaryConstructorInformationBlobDebugInfo cdi) { + var d = cdi.Blob; + if (d is null) { + helper.Error("Primary constructor information blob is null"); + return; + } + writer.WriteBytes(d); + } + } +} diff --git a/src/DotNet/Pdb/Portable/PortablePdbReader.cs b/src/DotNet/Pdb/Portable/PortablePdbReader.cs new file mode 100644 index 000000000..a02454099 --- /dev/null +++ b/src/DotNet/Pdb/Portable/PortablePdbReader.cs @@ -0,0 +1,396 @@ +// dnlib: See LICENSE.txt for more info + +// https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using dnlib.DotNet.Emit; +using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb.Symbols; +using dnlib.IO; + +namespace dnlib.DotNet.Pdb.Portable { + sealed class PortablePdbReader : SymbolReader { + readonly PdbFileKind pdbFileKind; + ModuleDef module; + readonly Metadata pdbMetadata; + SymbolDocument[] documents; + + public override PdbFileKind PdbFileKind => pdbFileKind; + public override int UserEntryPoint => pdbMetadata.PdbStream.EntryPoint.ToInt32(); + public override IList Documents => documents; + + public PortablePdbReader(DataReaderFactory pdbStream, PdbFileKind pdbFileKind) { + this.pdbFileKind = pdbFileKind; + pdbMetadata = MetadataFactory.CreateStandalonePortablePDB(pdbStream, true); + } + + internal bool MatchesModule(Guid pdbGuid, uint timestamp, uint age) { + if (pdbMetadata.PdbStream is PdbStream pdbStream) { + var pdbGuidArray = pdbStream.Id; + Array.Resize(ref pdbGuidArray, 16); + if (new Guid(pdbGuidArray) != pdbGuid) + return false; + if (BitConverter.ToUInt32(pdbStream.Id, 16) != timestamp) + return false; + if (age != 1) + return false; + + return true; + } + return false; + } + + public override void Initialize(ModuleDef module) { + this.module = module; + documents = ReadDocuments(); + } + + static Guid GetLanguageVendor(Guid language) { + if (language == PdbDocumentConstants.LanguageCSharp || language == PdbDocumentConstants.LanguageVisualBasic || language == PdbDocumentConstants.LanguageFSharp) + return PdbDocumentConstants.LanguageVendorMicrosoft; + return Guid.Empty; + } + + SymbolDocument[] ReadDocuments() { + Debug.Assert(module is not null); + var docTbl = pdbMetadata.TablesStream.DocumentTable; + var docs = new SymbolDocument[docTbl.Rows]; + var nameReader = new DocumentNameReader(pdbMetadata.BlobStream); + var custInfos = ListCache.AllocList(); + var gpContext = new GenericParamContext(); + for (int i = 0; i < docs.Length; i++) { + uint rid = (uint)i + 1; + bool b = pdbMetadata.TablesStream.TryReadDocumentRow(rid, out var row); + Debug.Assert(b); + var url = nameReader.ReadDocumentName(row.Name); + var language = pdbMetadata.GuidStream.Read(row.Language) ?? Guid.Empty; + var languageVendor = GetLanguageVendor(language); + var documentType = PdbDocumentConstants.DocumentTypeText; + var checkSumAlgorithmId = pdbMetadata.GuidStream.Read(row.HashAlgorithm) ?? Guid.Empty; + var checkSum = pdbMetadata.BlobStream.ReadNoNull(row.Hash); + + var mdToken = new MDToken(Table.Document, rid); + var token = mdToken.ToInt32(); + custInfos.Clear(); + GetCustomDebugInfos(token, gpContext, custInfos); + var custInfosArray = custInfos.Count == 0 ? Array2.Empty() : custInfos.ToArray(); + + docs[i] = new SymbolDocumentImpl(url, language, languageVendor, documentType, checkSumAlgorithmId, checkSum, custInfosArray, mdToken); + } + ListCache.Free(ref custInfos); + return docs; + } + + bool TryGetSymbolDocument(uint rid, out SymbolDocument document) { + int index = (int)rid - 1; + if ((uint)index >= (uint)documents.Length) { + Debug.Fail($"Couldn't find document with rid 0x{rid:X6}"); + document = null; + return false; + } + document = documents[index]; + return true; + } + + public override SymbolMethod GetMethod(MethodDef method, int version) { + if (version != 1) + return null; + var mdTable = pdbMetadata.TablesStream.MethodDebugInformationTable; + uint methodRid = method.Rid; + if (!mdTable.IsValidRID(methodRid)) + return null; + + var sequencePoints = ReadSequencePoints(methodRid) ?? Array2.Empty(); + var gpContext = GenericParamContext.Create(method); + var rootScope = ReadScope(methodRid, gpContext); + + var kickoffMethod = GetKickoffMethod(methodRid); + var symbolMethod = new SymbolMethodImpl(this, method.MDToken.ToInt32(), rootScope, sequencePoints, kickoffMethod); + rootScope.method = symbolMethod; + return symbolMethod; + } + + int GetKickoffMethod(uint methodRid) { + uint rid = pdbMetadata.GetStateMachineMethodRid(methodRid); + if (rid == 0) + return 0; + if (!pdbMetadata.TablesStream.TryReadStateMachineMethodRow(rid, out var row)) + return 0; + return 0x06000000 + (int)row.KickoffMethod; + } + + SymbolSequencePoint[] ReadSequencePoints(uint methodRid) { + if (!pdbMetadata.TablesStream.MethodDebugInformationTable.IsValidRID(methodRid)) + return null; + if (!pdbMetadata.TablesStream.TryReadMethodDebugInformationRow(methodRid, out var row)) + return null; + if (row.SequencePoints == 0) + return null; + uint documentRid = row.Document; + + if (!pdbMetadata.BlobStream.TryCreateReader(row.SequencePoints, out var seqPointsReader)) + return null; + var seqPointsBuilder = ListCache.AllocList(); + uint localSig = seqPointsReader.ReadCompressedUInt32(); + if (documentRid == 0) + documentRid = seqPointsReader.ReadCompressedUInt32(); + + TryGetSymbolDocument(documentRid, out var document); + + uint ilOffset = uint.MaxValue; + int line = -1, column = 0; + bool canReadDocumentRecord = false; + while (seqPointsReader.Position < seqPointsReader.Length) { + uint data = seqPointsReader.ReadCompressedUInt32(); + if (data == 0 && canReadDocumentRecord) { + // document-record + + documentRid = seqPointsReader.ReadCompressedUInt32(); + TryGetSymbolDocument(documentRid, out document); + } + else { + // SequencePointRecord + + Debug.Assert(document is not null); + if (document is null) + return null; + + var symSeqPoint = new SymbolSequencePoint { + Document = document, + }; + + if (ilOffset == uint.MaxValue) + ilOffset = data; + else { + Debug.Assert(data != 0); + if (data == 0) + return null; + ilOffset += data; + } + symSeqPoint.Offset = (int)ilOffset; + + uint dlines = seqPointsReader.ReadCompressedUInt32(); + int dcolumns = dlines == 0 ? (int)seqPointsReader.ReadCompressedUInt32() : seqPointsReader.ReadCompressedInt32(); + + if (dlines == 0 && dcolumns == 0) { + // hidden-sequence-point-record + + symSeqPoint.Line = SequencePointConstants.HIDDEN_LINE; + symSeqPoint.EndLine = SequencePointConstants.HIDDEN_LINE; + symSeqPoint.Column = SequencePointConstants.HIDDEN_COLUMN; + symSeqPoint.EndColumn = SequencePointConstants.HIDDEN_COLUMN; + } + else { + // sequence-point-record + + if (line < 0) { + line = (int)seqPointsReader.ReadCompressedUInt32(); + column = (int)seqPointsReader.ReadCompressedUInt32(); + } + else { + line += seqPointsReader.ReadCompressedInt32(); + column += seqPointsReader.ReadCompressedInt32(); + } + + symSeqPoint.Line = line; + symSeqPoint.EndLine = line + (int)dlines; + symSeqPoint.Column = column; + symSeqPoint.EndColumn = column + dcolumns; + } + + seqPointsBuilder.Add(symSeqPoint); + } + + canReadDocumentRecord = true; + } + Debug.Assert(seqPointsReader.Position == seqPointsReader.Length); + + return ListCache.FreeAndToArray(ref seqPointsBuilder); + } + + SymbolScopeImpl ReadScope(uint methodRid, GenericParamContext gpContext) { + var scopesRidList = pdbMetadata.GetLocalScopeRidList(methodRid); + SymbolScopeImpl rootScopeOrNull = null; + if (scopesRidList.Count != 0) { + var custInfos = ListCache.AllocList(); + var stack = ListCache.AllocList(); + var importScopeBlobReader = new ImportScopeBlobReader(module, pdbMetadata.BlobStream); + for (int i = 0; i < scopesRidList.Count; i++) { + var rid = scopesRidList[i]; + int token = new MDToken(Table.LocalScope, rid).ToInt32(); + bool b = pdbMetadata.TablesStream.TryReadLocalScopeRow(rid, out var row); + Debug.Assert(b); + uint startOffset = row.StartOffset; + uint endOffset = startOffset + row.Length; + + SymbolScopeImpl parent = null; + while (stack.Count > 0) { + var nextParent = stack[stack.Count - 1]; + if (startOffset >= nextParent.StartOffset && endOffset <= nextParent.EndOffset) { + parent = nextParent; + break; + } + stack.RemoveAt(stack.Count - 1); + } + + Debug.Assert(parent is not null || rootScopeOrNull is null); + custInfos.Clear(); + GetCustomDebugInfos(token, gpContext, custInfos); + var customDebugInfos = custInfos.Count == 0 ? Array2.Empty() : custInfos.ToArray(); + var scope = new SymbolScopeImpl(this, parent, (int)startOffset, (int)endOffset, customDebugInfos); + if (rootScopeOrNull is null) + rootScopeOrNull = scope; + stack.Add(scope); + if (parent is not null) + parent.childrenList.Add(scope); + + scope.importScope = ReadPdbImportScope(ref importScopeBlobReader, row.ImportScope, gpContext); + ReadVariables(scope, gpContext, pdbMetadata.GetLocalVariableRidList(rid)); + ReadConstants(scope, pdbMetadata.GetLocalConstantRidList(rid)); + } + + ListCache.Free(ref stack); + ListCache.Free(ref custInfos); + } + return rootScopeOrNull ?? new SymbolScopeImpl(this, null, 0, int.MaxValue, Array2.Empty()); + } + + PdbImportScope ReadPdbImportScope(ref ImportScopeBlobReader importScopeBlobReader, uint importScope, GenericParamContext gpContext) { + if (importScope == 0) + return null; + const int MAX = 1000; + PdbImportScope result = null; + PdbImportScope prevScope = null; + for (int i = 0; importScope != 0; i++) { + Debug.Assert(i < MAX); + if (i >= MAX) + return null; + int token = new MDToken(Table.ImportScope, importScope).ToInt32(); + if (!pdbMetadata.TablesStream.TryReadImportScopeRow(importScope, out var row)) + return null; + var scope = new PdbImportScope(); + GetCustomDebugInfos(token, gpContext, scope.CustomDebugInfos); + if (result is null) + result = scope; + if (prevScope is not null) + prevScope.Parent = scope; + importScopeBlobReader.Read(row.Imports, scope.Imports); + prevScope = scope; + importScope = row.Parent; + } + + return result; + } + + void ReadVariables(SymbolScopeImpl scope, GenericParamContext gpContext, RidList rids) { + if (rids.Count == 0) + return; + var table = pdbMetadata.TablesStream.LocalVariableTable; + var custInfos = ListCache.AllocList(); + for (int i = 0; i < rids.Count; i++) { + var rid = rids[i]; + int token = new MDToken(Table.LocalVariable, rid).ToInt32(); + custInfos.Clear(); + GetCustomDebugInfos(token, gpContext, custInfos); + var customDebugInfos = custInfos.Count == 0 ? Array2.Empty() : custInfos.ToArray(); + bool b = pdbMetadata.TablesStream.TryReadLocalVariableRow(rid, out var row); + Debug.Assert(b); + var name = pdbMetadata.StringsStream.Read(row.Name); + scope.localsList.Add(new SymbolVariableImpl(name, ToSymbolVariableAttributes(row.Attributes), row.Index, customDebugInfos)); + } + ListCache.Free(ref custInfos); + } + + static PdbLocalAttributes ToSymbolVariableAttributes(ushort attributes) { + var res = PdbLocalAttributes.None; + const ushort DebuggerHidden = 0x0001; + if ((attributes & DebuggerHidden) != 0) + res |= PdbLocalAttributes.DebuggerHidden; + return res; + } + + void ReadConstants(SymbolScopeImpl scope, RidList rids) { + if (rids.Count == 0) + return; + scope.SetConstants(pdbMetadata, rids); + } + + internal void GetCustomDebugInfos(SymbolMethodImpl symMethod, MethodDef method, CilBody body, IList result) { + Debug.Assert(method.Module == module); + GetCustomDebugInfos(method.MDToken.ToInt32(), GenericParamContext.Create(method), result, method, body, out var asyncStepInfo); + if (asyncStepInfo is not null) { + var asyncMethod = TryCreateAsyncMethod(module, symMethod.KickoffMethod, asyncStepInfo.AsyncStepInfos, asyncStepInfo.CatchHandler); + Debug.Assert(asyncMethod is not null); + if (asyncMethod is not null) + result.Add(asyncMethod); + } + else if (symMethod.KickoffMethod != 0) { + var iteratorMethod = TryCreateIteratorMethod(module, symMethod.KickoffMethod); + Debug.Assert(iteratorMethod is not null); + if (iteratorMethod is not null) + result.Add(iteratorMethod); + } + } + + PdbAsyncMethodCustomDebugInfo TryCreateAsyncMethod(ModuleDef module, int asyncKickoffMethod, IList asyncStepInfos, Instruction asyncCatchHandler) { + var kickoffToken = new MDToken(asyncKickoffMethod); + if (kickoffToken.Table != Table.Method) + return null; + + var asyncMethod = new PdbAsyncMethodCustomDebugInfo(asyncStepInfos.Count); + asyncMethod.KickoffMethod = module.ResolveToken(kickoffToken) as MethodDef; + asyncMethod.CatchHandlerInstruction = asyncCatchHandler; + int count = asyncStepInfos.Count; + for (int i = 0; i < count; i++) + asyncMethod.StepInfos.Add(asyncStepInfos[i]); + return asyncMethod; + } + + PdbIteratorMethodCustomDebugInfo TryCreateIteratorMethod(ModuleDef module, int iteratorKickoffMethod) { + var kickoffToken = new MDToken(iteratorKickoffMethod); + if (kickoffToken.Table != Table.Method) + return null; + var kickoffMethod = module.ResolveToken(kickoffToken) as MethodDef; + return new PdbIteratorMethodCustomDebugInfo(kickoffMethod); + } + + public override void GetCustomDebugInfos(int token, GenericParamContext gpContext, IList result) { + GetCustomDebugInfos(token, gpContext, result, null, null, out var asyncStepInfo); + Debug.Assert(asyncStepInfo is null); + } + + void GetCustomDebugInfos(int token, GenericParamContext gpContext, IList result, MethodDef methodOpt, CilBody bodyOpt, out PdbAsyncMethodSteppingInformationCustomDebugInfo asyncStepInfo) { + asyncStepInfo = null; + var mdToken = new MDToken(token); + var ridList = pdbMetadata.GetCustomDebugInformationRidList(mdToken.Table, mdToken.Rid); + if (ridList.Count == 0) + return; + var typeOpt = methodOpt?.DeclaringType; + for (int i = 0; i < ridList.Count; i++) { + var rid = ridList[i]; + if (!pdbMetadata.TablesStream.TryReadCustomDebugInformationRow(rid, out var row)) + continue; + var guid = pdbMetadata.GuidStream.Read(row.Kind); + if (!pdbMetadata.BlobStream.TryCreateReader(row.Value, out var reader)) + continue; + Debug.Assert(guid is not null); + if (guid is null) + continue; + var cdi = PortablePdbCustomDebugInfoReader.Read(module, typeOpt, bodyOpt, gpContext, guid.Value, ref reader); + Debug.Assert(cdi is not null); + if (cdi is not null) { + if (cdi is PdbAsyncMethodSteppingInformationCustomDebugInfo asyncStepInfoTmp) { + Debug.Assert(asyncStepInfo is null); + asyncStepInfo = asyncStepInfoTmp; + } + else + result.Add(cdi); + } + } + } + + public override void Dispose() => pdbMetadata.Dispose(); + } +} diff --git a/src/DotNet/Pdb/Portable/SequencePointConstants.cs b/src/DotNet/Pdb/Portable/SequencePointConstants.cs new file mode 100644 index 000000000..23aa54370 --- /dev/null +++ b/src/DotNet/Pdb/Portable/SequencePointConstants.cs @@ -0,0 +1,8 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.DotNet.Pdb.Portable { + static class SequencePointConstants { + public const int HIDDEN_LINE = 0xFEEFEE; + public const int HIDDEN_COLUMN = 0; + } +} diff --git a/src/DotNet/Pdb/Portable/SymbolDocumentImpl.cs b/src/DotNet/Pdb/Portable/SymbolDocumentImpl.cs new file mode 100644 index 000000000..dce64fb4a --- /dev/null +++ b/src/DotNet/Pdb/Portable/SymbolDocumentImpl.cs @@ -0,0 +1,62 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Diagnostics; +using System.Text; +using dnlib.DotNet.Pdb.Symbols; + +namespace dnlib.DotNet.Pdb.Portable { + [DebuggerDisplay("{GetDebuggerString(),nq}")] + sealed class SymbolDocumentImpl : SymbolDocument { + readonly string url; + /*readonly*/ Guid language; + /*readonly*/ Guid languageVendor; + /*readonly*/ Guid documentType; + /*readonly*/ Guid checkSumAlgorithmId; + readonly byte[] checkSum; + readonly PdbCustomDebugInfo[] customDebugInfos; + MDToken mdToken; + + string GetDebuggerString() { + var sb = new StringBuilder(); + if (language == PdbDocumentConstants.LanguageCSharp) + sb.Append("C#"); + else if (language == PdbDocumentConstants.LanguageVisualBasic) + sb.Append("VB"); + else if (language == PdbDocumentConstants.LanguageFSharp) + sb.Append("F#"); + else + sb.Append(language.ToString()); + sb.Append(", "); + if (checkSumAlgorithmId == PdbDocumentConstants.HashSHA1) + sb.Append("SHA-1"); + else if (checkSumAlgorithmId == PdbDocumentConstants.HashSHA256) + sb.Append("SHA-256"); + else + sb.Append(checkSumAlgorithmId.ToString()); + sb.Append(": "); + sb.Append(url); + return sb.ToString(); + } + + public override string URL => url; + public override Guid Language => language; + public override Guid LanguageVendor => languageVendor; + public override Guid DocumentType => documentType; + public override Guid CheckSumAlgorithmId => checkSumAlgorithmId; + public override byte[] CheckSum => checkSum; + public override PdbCustomDebugInfo[] CustomDebugInfos => customDebugInfos; + public override MDToken? MDToken => mdToken; + + public SymbolDocumentImpl(string url, Guid language, Guid languageVendor, Guid documentType, Guid checkSumAlgorithmId, byte[] checkSum, PdbCustomDebugInfo[] customDebugInfos, MDToken mdToken) { + this.url = url; + this.language = language; + this.languageVendor = languageVendor; + this.documentType = documentType; + this.checkSumAlgorithmId = checkSumAlgorithmId; + this.checkSum = checkSum; + this.customDebugInfos = customDebugInfos; + this.mdToken = mdToken; + } + } +} diff --git a/src/DotNet/Pdb/Portable/SymbolMethodImpl.cs b/src/DotNet/Pdb/Portable/SymbolMethodImpl.cs new file mode 100644 index 000000000..933d3266f --- /dev/null +++ b/src/DotNet/Pdb/Portable/SymbolMethodImpl.cs @@ -0,0 +1,31 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; +using dnlib.DotNet.Emit; +using dnlib.DotNet.Pdb.Symbols; + +namespace dnlib.DotNet.Pdb.Portable { + sealed class SymbolMethodImpl : SymbolMethod { + readonly PortablePdbReader reader; + readonly int token; + readonly SymbolScope rootScope; + readonly SymbolSequencePoint[] sequencePoints; + readonly int kickoffMethod; + + public override int Token => token; + public override SymbolScope RootScope => rootScope; + public override IList SequencePoints => sequencePoints; + public int KickoffMethod => kickoffMethod; + + public SymbolMethodImpl(PortablePdbReader reader, int token, SymbolScope rootScope, SymbolSequencePoint[] sequencePoints, int kickoffMethod) { + this.reader = reader; + this.token = token; + this.rootScope = rootScope; + this.sequencePoints = sequencePoints; + this.kickoffMethod = kickoffMethod; + } + + public override void GetCustomDebugInfos(MethodDef method, CilBody body, IList result) => + reader.GetCustomDebugInfos(this, method, body, result); + } +} diff --git a/src/DotNet/Pdb/Portable/SymbolReaderFactory.cs b/src/DotNet/Pdb/Portable/SymbolReaderFactory.cs new file mode 100644 index 000000000..af31b7987 --- /dev/null +++ b/src/DotNet/Pdb/Portable/SymbolReaderFactory.cs @@ -0,0 +1,91 @@ +// dnlib: See LICENSE.txt for more info + +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb.Symbols; +using dnlib.IO; +using dnlib.PE; + +namespace dnlib.DotNet.Pdb.Portable { + static class SymbolReaderFactory { + public static SymbolReader TryCreate(PdbReaderContext pdbContext, DataReaderFactory pdbStream, bool isEmbeddedPortablePdb) { + bool disposePdbStream = true; + try { + if (!pdbContext.HasDebugInfo) + return null; + if (pdbStream is null) + return null; + if (pdbStream.Length < 4) + return null; + if (pdbStream.CreateReader().ReadUInt32() != 0x424A5342) + return null; + + var debugDir = pdbContext.CodeViewDebugDirectory; + if (debugDir is null) + return null; + // Don't check that debugDir.MinorVersion == PortablePdbConstants.PortableCodeViewVersionMagic + // and debugDir.MajorVersion == PortablePdbConstants.FormatVersion since it could be a converted + // WindowsPDB file: https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md#codeview-debug-directory-entry-type-2 + if (!pdbContext.TryGetCodeViewData(out var pdbGuid, out uint age)) + return null; + + var reader = new PortablePdbReader(pdbStream, isEmbeddedPortablePdb ? PdbFileKind.EmbeddedPortablePDB : PdbFileKind.PortablePDB); + if (!reader.MatchesModule(pdbGuid, debugDir.TimeDateStamp, age)) + return null; + disposePdbStream = false; + return reader; + } + catch (IOException) { + } + finally { + if (disposePdbStream) + pdbStream?.Dispose(); + } + return null; + } + + public static SymbolReader TryCreateEmbeddedPortablePdbReader(PdbReaderContext pdbContext, Metadata metadata) { + if (metadata is null) + return null; + try { + if (!pdbContext.HasDebugInfo) + return null; + var embeddedDir = pdbContext.TryGetDebugDirectoryEntry(ImageDebugType.EmbeddedPortablePdb); + if (embeddedDir is null) + return null; + var reader = pdbContext.CreateReader(embeddedDir.AddressOfRawData, embeddedDir.SizeOfData); + if (reader.Length < 8) + return null; + // "MPDB" = 0x4244504D + if (reader.ReadUInt32() != 0x4244504D) + return null; + uint uncompressedSize = reader.ReadUInt32(); + // If this fails, see the (hopefully) updated spec: + // https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md#embedded-portable-pdb-debug-directory-entry-type-17 + bool newVersion = (uncompressedSize & 0x80000000) != 0; + Debug.Assert(!newVersion); + if (newVersion) + return null; + var decompressedBytes = new byte[uncompressedSize]; + using (var deflateStream = new DeflateStream(reader.AsStream(), CompressionMode.Decompress)) { + int pos = 0; + while (pos < decompressedBytes.Length) { + int read = deflateStream.Read(decompressedBytes, pos, decompressedBytes.Length - pos); + if (read == 0) + break; + pos += read; + } + if (pos != decompressedBytes.Length) + return null; + var stream = ByteArrayDataReaderFactory.Create(decompressedBytes, filename: null); + return TryCreate(pdbContext, stream, isEmbeddedPortablePdb: true); + } + } + catch (IOException) { + } + return null; + } + } +} diff --git a/src/DotNet/Pdb/Portable/SymbolScopeImpl.cs b/src/DotNet/Pdb/Portable/SymbolScopeImpl.cs new file mode 100644 index 000000000..cc90eebe1 --- /dev/null +++ b/src/DotNet/Pdb/Portable/SymbolScopeImpl.cs @@ -0,0 +1,93 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb.Symbols; + +namespace dnlib.DotNet.Pdb.Portable { + sealed class SymbolScopeImpl : SymbolScope { + readonly PortablePdbReader owner; + internal SymbolMethod method; + readonly SymbolScopeImpl parent; + readonly int startOffset; + readonly int endOffset; + internal readonly List childrenList; + internal readonly List localsList; + internal PdbImportScope importScope; + readonly PdbCustomDebugInfo[] customDebugInfos; + + public override SymbolMethod Method { + get { + if (method is not null) + return method; + var p = parent; + if (p is null) + return method; + for (;;) { + if (p.parent is null) + return method = p.method; + p = p.parent; + } + } + } + + public override SymbolScope Parent => parent; + public override int StartOffset => startOffset; + public override int EndOffset => endOffset; + public override IList Children => childrenList; + public override IList Locals => localsList; + public override IList Namespaces => Array2.Empty(); + public override IList CustomDebugInfos => customDebugInfos; + public override PdbImportScope ImportScope => importScope; + + public SymbolScopeImpl(PortablePdbReader owner, SymbolScopeImpl parent, int startOffset, int endOffset, PdbCustomDebugInfo[] customDebugInfos) { + this.owner = owner; + method = null; + this.parent = parent; + this.startOffset = startOffset; + this.endOffset = endOffset; + childrenList = new List(); + localsList = new List(); + this.customDebugInfos = customDebugInfos; + } + + Metadata constantsMetadata; + RidList constantRidList; + + internal void SetConstants(Metadata metadata, RidList rids) { + constantsMetadata = metadata; + constantRidList = rids; + } + + public override IList GetConstants(ModuleDef module, GenericParamContext gpContext) { + if (constantRidList.Count == 0) + return Array2.Empty(); + Debug.Assert(constantsMetadata is not null); + + var res = new PdbConstant[constantRidList.Count]; + int w = 0; + for (int i = 0; i < res.Length; i++) { + uint rid = constantRidList[i]; + bool b = constantsMetadata.TablesStream.TryReadLocalConstantRow(rid, out var row); + Debug.Assert(b); + var name = constantsMetadata.StringsStream.Read(row.Name); + if (!constantsMetadata.BlobStream.TryCreateReader(row.Signature, out var reader)) + continue; + var localConstantSigBlobReader = new LocalConstantSigBlobReader(module, ref reader, gpContext); + b = localConstantSigBlobReader.Read(out var type, out object value); + Debug.Assert(b); + if (b) { + var pdbConstant = new PdbConstant(name, type, value); + int token = new MDToken(Table.LocalConstant, rid).ToInt32(); + owner.GetCustomDebugInfos(token, gpContext, pdbConstant.CustomDebugInfos); + res[w++] = pdbConstant; + } + } + if (res.Length != w) + Array.Resize(ref res, w); + return res; + } + } +} diff --git a/src/DotNet/Pdb/Portable/SymbolVariableImpl.cs b/src/DotNet/Pdb/Portable/SymbolVariableImpl.cs new file mode 100644 index 000000000..111c35a4d --- /dev/null +++ b/src/DotNet/Pdb/Portable/SymbolVariableImpl.cs @@ -0,0 +1,24 @@ +// dnlib: See LICENSE.txt for more info + +using dnlib.DotNet.Pdb.Symbols; + +namespace dnlib.DotNet.Pdb.Portable { + sealed class SymbolVariableImpl : SymbolVariable { + readonly string name; + readonly PdbLocalAttributes attributes; + readonly int index; + readonly PdbCustomDebugInfo[] customDebugInfos; + + public override string Name => name; + public override PdbLocalAttributes Attributes => attributes; + public override int Index => index; + public override PdbCustomDebugInfo[] CustomDebugInfos => customDebugInfos; + + public SymbolVariableImpl(string name, PdbLocalAttributes attributes, int index, PdbCustomDebugInfo[] customDebugInfos) { + this.name = name; + this.attributes = attributes; + this.index = index; + this.customDebugInfos = customDebugInfos; + } + } +} diff --git a/src/DotNet/Pdb/SequencePoint.cs b/src/DotNet/Pdb/SequencePoint.cs index 0536d6fb7..916c24979 100644 --- a/src/DotNet/Pdb/SequencePoint.cs +++ b/src/DotNet/Pdb/SequencePoint.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System.Diagnostics; diff --git a/src/DotNet/Pdb/SymbolReaderCreator.cs b/src/DotNet/Pdb/SymbolReaderCreator.cs deleted file mode 100644 index c811726de..000000000 --- a/src/DotNet/Pdb/SymbolReaderCreator.cs +++ /dev/null @@ -1,175 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Diagnostics.SymbolStore; -using dnlib.DotNet.MD; -using dnlib.IO; - -namespace dnlib.DotNet.Pdb { - /// - /// Creates a instance - /// - public static class SymbolReaderCreator { - /// - /// Creates a new instance - /// - /// PDB implementation to use - /// Path to assembly - /// A new instance or null if there's no PDB - /// file on disk or if it's not possible to create a . - public static ISymbolReader Create(PdbImplType pdbImpl, string assemblyFileName) { - switch (pdbImpl) { - case PdbImplType.MicrosoftCOM: - return Dss.SymbolReaderCreator.Create(assemblyFileName); - - case PdbImplType.Managed: - return Managed.SymbolReaderCreator.CreateFromAssemblyFile(assemblyFileName); - - default: throw new InvalidOperationException(); - } - } - - /// - /// Creates a new instance - /// - /// PDB implementation to use - /// .NET metadata. Only need to be non-null if MS COM API should be used - /// Path to PDB file - /// A new instance or null if there's no PDB - /// file on disk or if it's not possible to create a . - public static ISymbolReader Create(PdbImplType pdbImpl, IMetaData metaData, string pdbFileName) { - switch (pdbImpl) { - case PdbImplType.MicrosoftCOM: - return Dss.SymbolReaderCreator.Create(metaData, pdbFileName); - - case PdbImplType.Managed: - return Managed.SymbolReaderCreator.Create(pdbFileName); - - default: throw new InvalidOperationException(); - } - } - - /// - /// Creates a new instance - /// - /// PDB implementation to use - /// .NET metadata. Only need to be non-null if MS COM API should be used - /// PDB file data - /// A new instance or null if it's not possible - /// to create a . - public static ISymbolReader Create(PdbImplType pdbImpl, IMetaData metaData, byte[] pdbData) { - switch (pdbImpl) { - case PdbImplType.MicrosoftCOM: - return Dss.SymbolReaderCreator.Create(metaData, pdbData); - - case PdbImplType.Managed: - return Managed.SymbolReaderCreator.Create(pdbData); - - default: throw new InvalidOperationException(); - } - } - - /// - /// Creates a new instance - /// - /// PDB implementation to use - /// .NET metadata. Only need to be non-null if MS COM API should be used - /// PDB file stream which is now owned by us - /// A new instance or null if it's not possible - /// to create a . - public static ISymbolReader Create(PdbImplType pdbImpl, IMetaData metaData, IImageStream pdbStream) { - switch (pdbImpl) { - case PdbImplType.MicrosoftCOM: - return Dss.SymbolReaderCreator.Create(metaData, pdbStream); - - case PdbImplType.Managed: - return Managed.SymbolReaderCreator.Create(pdbStream); - - default: - if (pdbStream != null) - pdbStream.Dispose(); - throw new InvalidOperationException(); - } - } - - /// - /// Creates a new instance - /// - /// PDB implementation to use - /// .NET metadata stream which is now owned by us. Only need to be - /// non-null if MS COM API should be used - /// Path to PDB file - /// A new instance or null if there's no PDB - /// file on disk or if it's not possible to create a . - public static ISymbolReader Create(PdbImplType pdbImpl, IImageStream mdStream, string pdbFileName) { - switch (pdbImpl) { - case PdbImplType.MicrosoftCOM: - return Dss.SymbolReaderCreator.Create(mdStream, pdbFileName); - - case PdbImplType.Managed: - if (mdStream != null) - mdStream.Dispose(); - return Managed.SymbolReaderCreator.Create(pdbFileName); - - default: - if (mdStream != null) - mdStream.Dispose(); - throw new InvalidOperationException(); - } - } - - /// - /// Creates a new instance - /// - /// PDB implementation to use - /// .NET metadata stream which is now owned by us. Only need to be - /// non-null if MS COM API should be used - /// PDB file data - /// A new instance or null if it's not possible - /// to create a . - public static ISymbolReader Create(PdbImplType pdbImpl, IImageStream mdStream, byte[] pdbData) { - switch (pdbImpl) { - case PdbImplType.MicrosoftCOM: - return Dss.SymbolReaderCreator.Create(mdStream, pdbData); - - case PdbImplType.Managed: - if (mdStream != null) - mdStream.Dispose(); - return Managed.SymbolReaderCreator.Create(pdbData); - - default: - if (mdStream != null) - mdStream.Dispose(); - throw new InvalidOperationException(); - } - } - - /// - /// Creates a new instance - /// - /// PDB implementation to use - /// .NET metadata stream which is now owned by us. Only need to be - /// non-null if MS COM API should be used - /// PDB file stream which is now owned by us - /// A new instance or null if it's not possible - /// to create a . - public static ISymbolReader Create(PdbImplType pdbImpl, IImageStream mdStream, IImageStream pdbStream) { - switch (pdbImpl) { - case PdbImplType.MicrosoftCOM: - return Dss.SymbolReaderCreator.Create(mdStream, pdbStream); - - case PdbImplType.Managed: - if (mdStream != null) - mdStream.Dispose(); - return Managed.SymbolReaderCreator.Create(pdbStream); - - default: - if (mdStream != null) - mdStream.Dispose(); - if (pdbStream != null) - pdbStream.Dispose(); - throw new InvalidOperationException(); - } - } - } -} diff --git a/src/DotNet/Pdb/SymbolReaderFactory.cs b/src/DotNet/Pdb/SymbolReaderFactory.cs new file mode 100644 index 000000000..b8aff57ca --- /dev/null +++ b/src/DotNet/Pdb/SymbolReaderFactory.cs @@ -0,0 +1,149 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb.Symbols; +using dnlib.IO; + +namespace dnlib.DotNet.Pdb { + static class SymbolReaderFactory { + public static SymbolReader CreateFromAssemblyFile(PdbReaderOptions options, Metadata metadata, string assemblyFileName) { + var pdbContext = new PdbReaderContext(metadata.PEImage, options); + if (!pdbContext.HasDebugInfo) + return null; + if (!pdbContext.TryGetCodeViewData(out var guid, out uint age, out var pdbWindowsFilename)) + return null; + + string pdbFilename; + int index = pdbWindowsFilename.LastIndexOfAny(windowsPathSepChars); + if (index >= 0) + pdbFilename = pdbWindowsFilename.Substring(index + 1); + else + pdbFilename = pdbWindowsFilename; + + string fileToCheck; + try { + fileToCheck = assemblyFileName == string.Empty ? pdbFilename : Path.Combine(Path.GetDirectoryName(assemblyFileName), pdbFilename); + if (!File.Exists(fileToCheck)) { + var ext = Path.GetExtension(pdbFilename); + if (string.IsNullOrEmpty(ext)) + ext = "pdb"; + fileToCheck = Path.ChangeExtension(assemblyFileName, ext); + } + } + catch (ArgumentException) { + return null;// Invalid filename + } + return Create(options, metadata, fileToCheck); + } + static readonly char[] windowsPathSepChars = new char[] { '\\', '/' }; + + public static SymbolReader Create(PdbReaderOptions options, Metadata metadata, string pdbFileName) { + var pdbContext = new PdbReaderContext(metadata.PEImage, options); + if (!pdbContext.HasDebugInfo) + return null; + return CreateCore(pdbContext, metadata, DataReaderFactoryUtils.TryCreateDataReaderFactory(pdbFileName)); + } + + public static SymbolReader Create(PdbReaderOptions options, Metadata metadata, byte[] pdbData) { + var pdbContext = new PdbReaderContext(metadata.PEImage, options); + if (!pdbContext.HasDebugInfo) + return null; + return CreateCore(pdbContext, metadata, ByteArrayDataReaderFactory.Create(pdbData, filename: null)); + } + + public static SymbolReader Create(PdbReaderOptions options, Metadata metadata, DataReaderFactory pdbStream) { + var pdbContext = new PdbReaderContext(metadata.PEImage, options); + return CreateCore(pdbContext, metadata, pdbStream); + } + + static SymbolReader CreateCore(PdbReaderContext pdbContext, Metadata metadata, DataReaderFactory pdbStream) { + SymbolReader symReader = null; + bool error = true; + try { + if (!pdbContext.HasDebugInfo) + return null; + +#if NETSTANDARD || NETCOREAPP + var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); +#else + var isWindows = true; +#endif + + if ((pdbContext.Options & PdbReaderOptions.MicrosoftComReader) != 0 && isWindows && pdbStream is not null && IsWindowsPdb(pdbStream.CreateReader())) + symReader = Dss.SymbolReaderWriterFactory.Create(pdbContext, metadata, pdbStream); + else + symReader = CreateManaged(pdbContext, metadata, pdbStream); + + if (symReader is not null) { + error = false; + return symReader; + } + } + catch (IOException) { + } + finally { + if (error) { + pdbStream?.Dispose(); + symReader?.Dispose(); + } + } + return null; + } + + static bool IsWindowsPdb(DataReader reader) { + const string SIG = "Microsoft C/C++ MSF 7.00\r\n\u001ADS\0"; + if (!reader.CanRead(SIG.Length)) + return false; + return reader.ReadString(SIG.Length, Encoding.ASCII) == SIG; + } + + public static SymbolReader TryCreateEmbeddedPdbReader(PdbReaderOptions options, Metadata metadata) { + var pdbContext = new PdbReaderContext(metadata.PEImage, options); + if (!pdbContext.HasDebugInfo) + return null; + return TryCreateEmbeddedPortablePdbReader(pdbContext, metadata); + } + + static SymbolReader CreateManaged(PdbReaderContext pdbContext, Metadata metadata, DataReaderFactory pdbStream) { + try { + // Embedded PDBs have priority + var embeddedReader = TryCreateEmbeddedPortablePdbReader(pdbContext, metadata); + if (embeddedReader is not null) { + pdbStream?.Dispose(); + return embeddedReader; + } + + return CreateManagedCore(pdbContext, pdbStream); + } + catch { + pdbStream?.Dispose(); + throw; + } + } + + static SymbolReader CreateManagedCore(PdbReaderContext pdbContext, DataReaderFactory pdbStream) { + if (pdbStream is null) + return null; + try { + var reader = pdbStream.CreateReader(); + if (reader.Length >= 4) { + uint sig = reader.ReadUInt32(); + if (sig == 0x424A5342) + return Portable.SymbolReaderFactory.TryCreate(pdbContext, pdbStream, isEmbeddedPortablePdb: false); + return Managed.SymbolReaderFactory.Create(pdbContext, pdbStream); + } + } + catch (IOException) { + } + pdbStream?.Dispose(); + return null; + } + + static SymbolReader TryCreateEmbeddedPortablePdbReader(PdbReaderContext pdbContext, Metadata metadata) => + Portable.SymbolReaderFactory.TryCreateEmbeddedPortablePdbReader(pdbContext, metadata); + } +} diff --git a/src/DotNet/Pdb/SymbolWriterCreator.cs b/src/DotNet/Pdb/SymbolWriterCreator.cs deleted file mode 100644 index 98748344e..000000000 --- a/src/DotNet/Pdb/SymbolWriterCreator.cs +++ /dev/null @@ -1,29 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System.IO; - -namespace dnlib.DotNet.Pdb { - /// - /// Creates a - /// - public static class SymbolWriterCreator { - /// - /// Creates a new instance - /// - /// PDB file name - /// A new instance - public static ISymbolWriter2 Create(string pdbFileName) { - return Dss.SymbolWriterCreator.Create(pdbFileName); - } - - /// - /// Creates a new instance - /// - /// PDB output stream - /// PDB file name - /// A new instance - public static ISymbolWriter2 Create(Stream pdbStream, string pdbFileName) { - return Dss.SymbolWriterCreator.Create(pdbStream, pdbFileName); - } - } -} diff --git a/src/DotNet/Pdb/Symbols/SymbolAsyncStepInfo.cs b/src/DotNet/Pdb/Symbols/SymbolAsyncStepInfo.cs new file mode 100644 index 000000000..65c9a1c25 --- /dev/null +++ b/src/DotNet/Pdb/Symbols/SymbolAsyncStepInfo.cs @@ -0,0 +1,35 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.DotNet.Pdb.Symbols { + /// + /// Async step info + /// + public struct SymbolAsyncStepInfo { + /// + /// Yield offset + /// + public uint YieldOffset; + + /// + /// Breakpoint offset + /// + public uint BreakpointOffset; + + /// + /// Breakpoint method token + /// + public uint BreakpointMethod; + + /// + /// Constructor + /// + /// Yield offset + /// Breakpoint offset + /// Breakpoint method token + public SymbolAsyncStepInfo(uint yieldOffset, uint breakpointOffset, uint breakpointMethod) { + YieldOffset = yieldOffset; + BreakpointOffset = breakpointOffset; + BreakpointMethod = breakpointMethod; + } + } +} diff --git a/src/DotNet/Pdb/Symbols/SymbolDocument.cs b/src/DotNet/Pdb/Symbols/SymbolDocument.cs new file mode 100644 index 000000000..58509969e --- /dev/null +++ b/src/DotNet/Pdb/Symbols/SymbolDocument.cs @@ -0,0 +1,50 @@ +// dnlib: See LICENSE.txt for more info + +using System; + +namespace dnlib.DotNet.Pdb.Symbols { + /// + /// A document + /// + public abstract class SymbolDocument { + /// + /// Gets the URL + /// + public abstract string URL { get; } + + /// + /// Gets the language + /// + public abstract Guid Language { get; } + + /// + /// Gets the language vendor + /// + public abstract Guid LanguageVendor { get; } + + /// + /// Gets the document type + /// + public abstract Guid DocumentType { get; } + + /// + /// Gets the checksum algorithm id + /// + public abstract Guid CheckSumAlgorithmId { get; } + + /// + /// Gets the checksum + /// + public abstract byte[] CheckSum { get; } + + /// + /// Gets the custom debug infos + /// + public abstract PdbCustomDebugInfo[] CustomDebugInfos { get; } + + /// + /// Gets the Metadata token of the document if available. + /// + public abstract MDToken? MDToken { get; } + } +} diff --git a/src/DotNet/Pdb/Symbols/SymbolMethod.cs b/src/DotNet/Pdb/Symbols/SymbolMethod.cs new file mode 100644 index 000000000..801cfd996 --- /dev/null +++ b/src/DotNet/Pdb/Symbols/SymbolMethod.cs @@ -0,0 +1,34 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; +using dnlib.DotNet.Emit; + +namespace dnlib.DotNet.Pdb.Symbols { + /// + /// A method + /// + public abstract class SymbolMethod { + /// + /// Gets the method token + /// + public abstract int Token { get; } + + /// + /// Gets the root scope + /// + public abstract SymbolScope RootScope { get; } + + /// + /// Gets all sequence points + /// + public abstract IList SequencePoints { get; } + + /// + /// Reads custom debug info + /// + /// Method + /// Method body + /// Updated with custom debug info + public abstract void GetCustomDebugInfos(MethodDef method, CilBody body, IList result); + } +} diff --git a/src/DotNet/Pdb/Symbols/SymbolNamespace.cs b/src/DotNet/Pdb/Symbols/SymbolNamespace.cs new file mode 100644 index 000000000..a556b0fb8 --- /dev/null +++ b/src/DotNet/Pdb/Symbols/SymbolNamespace.cs @@ -0,0 +1,13 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.DotNet.Pdb.Symbols { + /// + /// A namespace + /// + public abstract class SymbolNamespace { + /// + /// Gets the name + /// + public abstract string Name { get; } + } +} diff --git a/src/DotNet/Pdb/Symbols/SymbolReader.cs b/src/DotNet/Pdb/Symbols/SymbolReader.cs new file mode 100644 index 000000000..2cc995ac2 --- /dev/null +++ b/src/DotNet/Pdb/Symbols/SymbolReader.cs @@ -0,0 +1,54 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Collections.Generic; + +namespace dnlib.DotNet.Pdb.Symbols { + /// + /// Reads symbols from a PDB file + /// + public abstract class SymbolReader : IDisposable { + /// + /// Called by the owner module before any other methods and properties are called + /// + /// Owner module + public abstract void Initialize(ModuleDef module); + + /// + /// Gets the PDB file kind + /// + public abstract PdbFileKind PdbFileKind { get; } + + /// + /// Gets the user entry point token or 0 if none + /// + public abstract int UserEntryPoint { get; } + + /// + /// Gets all documents + /// + public abstract IList Documents { get; } + + /// + /// Gets a method or returns null if the method doesn't exist in the PDB file + /// + /// Method + /// Edit and continue version. The first version is 1 + /// + public abstract SymbolMethod GetMethod(MethodDef method, int version); + + /// + /// Reads custom debug info + /// + /// Token of a instance + /// Generic parameter context + /// Updated with custom debug info + public abstract void GetCustomDebugInfos(int token, GenericParamContext gpContext, IList result); + + /// + /// Cleans up resources + /// + public virtual void Dispose() { + } + } +} diff --git a/src/DotNet/Pdb/Symbols/SymbolScope.cs b/src/DotNet/Pdb/Symbols/SymbolScope.cs new file mode 100644 index 000000000..b24b03f4a --- /dev/null +++ b/src/DotNet/Pdb/Symbols/SymbolScope.cs @@ -0,0 +1,63 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; + +namespace dnlib.DotNet.Pdb.Symbols { + /// + /// A scope + /// + public abstract class SymbolScope { + /// + /// Gets the method + /// + public abstract SymbolMethod Method { get; } + + /// + /// Gets the parent scope + /// + public abstract SymbolScope Parent { get; } + + /// + /// Gets the start offset of the scope in the method + /// + public abstract int StartOffset { get; } + + /// + /// Gets the end offset of the scope in the method + /// + public abstract int EndOffset { get; } + + /// + /// Gets all child scopes + /// + public abstract IList Children { get; } + + /// + /// Gets all locals defined in this scope + /// + public abstract IList Locals { get; } + + /// + /// Gets all namespaces in this scope + /// + public abstract IList Namespaces { get; } + + /// + /// Gets all custom debug infos + /// + public abstract IList CustomDebugInfos { get; } + + /// + /// Gets the import scope or null if none + /// + public abstract PdbImportScope ImportScope { get; } + + /// + /// Gets all the constants + /// + /// Owner module if a signature must be read from the #Blob + /// Generic parameter context + /// + public abstract IList GetConstants(ModuleDef module, GenericParamContext gpContext); + } +} diff --git a/src/DotNet/Pdb/Symbols/SymbolSequencePoint.cs b/src/DotNet/Pdb/Symbols/SymbolSequencePoint.cs new file mode 100644 index 000000000..45535e638 --- /dev/null +++ b/src/DotNet/Pdb/Symbols/SymbolSequencePoint.cs @@ -0,0 +1,62 @@ +// dnlib: See LICENSE.txt for more info + +using System.Diagnostics; +using System.Text; + +namespace dnlib.DotNet.Pdb.Symbols { + /// + /// Sequence point + /// + [DebuggerDisplay("{GetDebuggerString(),nq}")] + public struct SymbolSequencePoint { + /// + /// IL offset + /// + public int Offset; + + /// + /// Document + /// + public SymbolDocument Document; + + /// + /// Start line + /// + public int Line; + + /// + /// Start column + /// + public int Column; + + /// + /// End line + /// + public int EndLine; + + /// + /// End column + /// + public int EndColumn; + + readonly string GetDebuggerString() { + var sb = new StringBuilder(); + if (Line == 0xFEEFEE && EndLine == 0xFEEFEE) + sb.Append(""); + else { + sb.Append("("); + sb.Append(Line); + sb.Append(","); + sb.Append(Column); + sb.Append(")-("); + sb.Append(EndLine); + sb.Append(","); + sb.Append(EndColumn); + sb.Append(")"); + } + sb.Append(": "); + sb.Append(Document.URL); + return sb.ToString(); + } + } +} diff --git a/src/DotNet/Pdb/Symbols/SymbolVariable.cs b/src/DotNet/Pdb/Symbols/SymbolVariable.cs new file mode 100644 index 000000000..dc9eb6442 --- /dev/null +++ b/src/DotNet/Pdb/Symbols/SymbolVariable.cs @@ -0,0 +1,28 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.DotNet.Pdb.Symbols { + /// + /// A variable + /// + public abstract class SymbolVariable { + /// + /// Gets the name + /// + public abstract string Name { get; } + + /// + /// Gets the attributes + /// + public abstract PdbLocalAttributes Attributes { get; } + + /// + /// Gets the index of the variable + /// + public abstract int Index { get; } + + /// + /// Gets all custom debug infos + /// + public abstract PdbCustomDebugInfo[] CustomDebugInfos { get; } + } +} diff --git a/src/DotNet/Pdb/WindowsPdb/CorSymVarFlag.cs b/src/DotNet/Pdb/WindowsPdb/CorSymVarFlag.cs new file mode 100644 index 000000000..5869ec5d3 --- /dev/null +++ b/src/DotNet/Pdb/WindowsPdb/CorSymVarFlag.cs @@ -0,0 +1,10 @@ +// dnlib: See LICENSE.txt for more info + +using System; + +namespace dnlib.DotNet.Pdb.WindowsPdb { + [Flags] + enum CorSymVarFlag : uint { + VAR_IS_COMP_GEN = 0x00000001, + } +} diff --git a/src/DotNet/Pdb/WindowsPdb/CustomDebugInfoConstants.cs b/src/DotNet/Pdb/WindowsPdb/CustomDebugInfoConstants.cs new file mode 100644 index 000000000..d4aed7ebf --- /dev/null +++ b/src/DotNet/Pdb/WindowsPdb/CustomDebugInfoConstants.cs @@ -0,0 +1,8 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.DotNet.Pdb.WindowsPdb { + static class CustomDebugInfoConstants { + public const int Version = 4; + public const int RecordVersion = 4; + } +} diff --git a/src/DotNet/Pdb/WindowsPdb/PdbCustomDebugInfoReader.cs b/src/DotNet/Pdb/WindowsPdb/PdbCustomDebugInfoReader.cs new file mode 100644 index 000000000..1a658645d --- /dev/null +++ b/src/DotNet/Pdb/WindowsPdb/PdbCustomDebugInfoReader.cs @@ -0,0 +1,339 @@ +// dnlib: See LICENSE.txt for more info + +// C# & Visual Basic compiler's Custom Debug Info is "documented" in source code only, see Roslyn classes: +// CustomDebugInfoReader, CustomDebugInfoWriter, CustomDebugInfoEncoder + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using dnlib.DotNet.Emit; +using dnlib.IO; + +namespace dnlib.DotNet.Pdb.WindowsPdb { + /// + /// Reads custom debug infos produced by the C# and Visual Basic compilers. They're stored in PDB files + /// as PDB method custom attributes with the name "MD2". + /// + struct PdbCustomDebugInfoReader { + /// + /// Reads custom debug info + /// + /// Method + /// The method's body. Needs to be provided by the caller since we're called from + /// PDB-init code when the Body property hasn't been initialized yet + /// Place all custom debug info in this list + /// Custom debug info from the PDB file + public static void Read(MethodDef method, CilBody body, IList result, byte[] data) { + try { + var reader = ByteArrayDataReaderFactory.CreateReader(data); + var cdiReader = new PdbCustomDebugInfoReader(method, body, ref reader); + cdiReader.Read(result); + } + catch (ArgumentException) { + } + catch (OutOfMemoryException) { + } + catch (IOException) { + } + } + + readonly ModuleDef module; + readonly TypeDef typeOpt; + readonly CilBody bodyOpt; + readonly GenericParamContext gpContext; + DataReader reader; + + PdbCustomDebugInfoReader(MethodDef method, CilBody body, ref DataReader reader) { + module = method.Module; + typeOpt = method.DeclaringType; + bodyOpt = body; + gpContext = GenericParamContext.Create(method); + this.reader = reader; + } + + void Read(IList result) { + if (reader.Length < 4) + return; + int version = reader.ReadByte(); + Debug.Assert(version == CustomDebugInfoConstants.Version); + if (version != CustomDebugInfoConstants.Version) + return; + int count = reader.ReadByte(); + reader.Position += 2; + + while (reader.CanRead(8U)) { + int recVersion = reader.ReadByte(); + Debug.Assert(recVersion == CustomDebugInfoConstants.RecordVersion); + var recKind = (PdbCustomDebugInfoKind)reader.ReadByte(); + reader.Position++; + int alignmentSize = reader.ReadByte(); + int recSize = reader.ReadInt32(); + if (recSize < 8 || (ulong)reader.Position - 8 + (uint)recSize > reader.Length) + return; + if (recKind <= PdbCustomDebugInfoKind.DynamicLocals) + alignmentSize = 0; + if (alignmentSize > 3) + return; + var nextRecPos = reader.Position - 8 + (uint)recSize; + + if (recVersion == CustomDebugInfoConstants.RecordVersion) { + ulong recPosEnd = (ulong)reader.Position - 8 + (uint)recSize - (uint)alignmentSize; + var cdi = ReadRecord(recKind, recPosEnd); + Debug.Assert(cdi is not null); + Debug.Assert(reader.Position <= recPosEnd); + if (reader.Position > recPosEnd) + return; + if (cdi is not null) { + Debug.Assert(cdi.Kind == recKind); + result.Add(cdi); + } + } + + reader.Position = nextRecPos; + } + } + + PdbCustomDebugInfo ReadRecord(PdbCustomDebugInfoKind recKind, ulong recPosEnd) { + IMethodDefOrRef method; + byte[] data; + Local local; + int count; + int localIndex; + switch (recKind) { + case PdbCustomDebugInfoKind.UsingGroups: + count = reader.ReadUInt16(); + if (count < 0) + return null; + var usingCountRec = new PdbUsingGroupsCustomDebugInfo(count); + for (int i = 0; i < count; i++) + usingCountRec.UsingCounts.Add(reader.ReadUInt16()); + return usingCountRec; + + case PdbCustomDebugInfoKind.ForwardMethodInfo: + method = module.ResolveToken(reader.ReadUInt32(), gpContext) as IMethodDefOrRef; + if (method is null) + return null; + return new PdbForwardMethodInfoCustomDebugInfo(method); + + case PdbCustomDebugInfoKind.ForwardModuleInfo: + method = module.ResolveToken(reader.ReadUInt32(), gpContext) as IMethodDefOrRef; + if (method is null) + return null; + return new PdbForwardModuleInfoCustomDebugInfo(method); + + case PdbCustomDebugInfoKind.StateMachineHoistedLocalScopes: + if (bodyOpt is null) + return null; + count = reader.ReadInt32(); + if (count < 0) + return null; + var smScope = new PdbStateMachineHoistedLocalScopesCustomDebugInfo(count); + for (int i = 0; i < count; i++) { + uint startOffset = reader.ReadUInt32(); + uint endOffset = reader.ReadUInt32(); + if (startOffset > endOffset) + return null; + // Try to detect synthesized locals, whose start==end==0. The problem is that endOffset + // read from the PDB is inclusive (add 1 to get 'end'), so a synthesized local and a + // local at [0, 1) will be encoded the same {0, 0}. + if (endOffset == 0) + smScope.Scopes.Add(new StateMachineHoistedLocalScope()); + else { + var start = GetInstruction(startOffset); + var end = GetInstruction(endOffset + 1); + if (start is null) + return null; + smScope.Scopes.Add(new StateMachineHoistedLocalScope(start, end)); + } + } + return smScope; + + case PdbCustomDebugInfoKind.StateMachineTypeName: + var name = ReadUnicodeZ(recPosEnd, needZeroChar: true); + if (name is null) + return null; + var type = GetNestedType(name); + if (type is null) + return null; + return new PdbStateMachineTypeNameCustomDebugInfo(type); + + case PdbCustomDebugInfoKind.DynamicLocals: + if (bodyOpt is null) + return null; + count = reader.ReadInt32(); + const int dynLocalRecSize = 64 + 4 + 4 + 2 * 64; + if (reader.Position + (ulong)(uint)count * dynLocalRecSize > recPosEnd) + return null; + var dynLocListRec = new PdbDynamicLocalsCustomDebugInfo(count); + for (int i = 0; i < count; i++) { + reader.Position += 64; + int flagsCount = reader.ReadInt32(); + if ((uint)flagsCount > 64) + return null; + var dynLocRec = new PdbDynamicLocal(flagsCount); + var afterPos = reader.Position; + + reader.Position -= 64 + 4; + for (int j = 0; j < flagsCount; j++) + dynLocRec.Flags.Add(reader.ReadByte()); + reader.Position = afterPos; + + localIndex = reader.ReadInt32(); + // 'const' locals have index -1 but they're encoded as 0 by Roslyn + if (localIndex != 0 && (uint)localIndex >= (uint)bodyOpt.Variables.Count) + return null; + + var nameEndPos = reader.Position + 2 * 64; + name = ReadUnicodeZ(nameEndPos, needZeroChar: false); + reader.Position = nameEndPos; + + local = localIndex < bodyOpt.Variables.Count ? bodyOpt.Variables[localIndex] : null; + // Roslyn writes 0 to localIndex if it's a 'const' local, try to undo that now + if (localIndex == 0 && local is not null && local.Name != name) + local = null; + if (local is not null && local.Name == name) + name = null; + dynLocRec.Name = name; + dynLocRec.Local = local; + dynLocListRec.Locals.Add(dynLocRec); + } + return dynLocListRec; + + case PdbCustomDebugInfoKind.EditAndContinueLocalSlotMap: + data = reader.ReadBytes((int)(recPosEnd - reader.Position)); + return new PdbEditAndContinueLocalSlotMapCustomDebugInfo(data); + + case PdbCustomDebugInfoKind.EditAndContinueLambdaMap: + data = reader.ReadBytes((int)(recPosEnd - reader.Position)); + return new PdbEditAndContinueLambdaMapCustomDebugInfo(data); + + case PdbCustomDebugInfoKind.TupleElementNames: + if (bodyOpt is null) + return null; + count = reader.ReadInt32(); + if (count < 0) + return null; + var tupleListRec = new PdbTupleElementNamesCustomDebugInfo(count); + for (int i = 0; i < count; i++) { + int nameCount = reader.ReadInt32(); + if ((uint)nameCount >= 10000) + return null; + var tupleInfo = new PdbTupleElementNames(nameCount); + + for (int j = 0; j < nameCount; j++) { + var s = ReadUTF8Z(recPosEnd); + if (s is null) + return null; + tupleInfo.TupleElementNames.Add(s); + } + + localIndex = reader.ReadInt32(); + uint scopeStart = reader.ReadUInt32(); + uint scopeEnd = reader.ReadUInt32(); + name = ReadUTF8Z(recPosEnd); + if (name is null) + return null; + Debug.Assert(localIndex >= -1); + // -1 = 'const' local. Only 'const' locals have a scope + Debug.Assert((localIndex == -1) ^ (scopeStart == 0 && scopeEnd == 0)); + + if (localIndex == -1) { + local = null; + tupleInfo.ScopeStart = GetInstruction(scopeStart); + tupleInfo.ScopeEnd = GetInstruction(scopeEnd); + if (tupleInfo.ScopeStart is null) + return null; + } + else { + if ((uint)localIndex >= (uint)bodyOpt.Variables.Count) + return null; + local = bodyOpt.Variables[localIndex]; + } + + if (local is not null && local.Name == name) + name = null; + tupleInfo.Local = local; + tupleInfo.Name = name; + + tupleListRec.Names.Add(tupleInfo); + } + return tupleListRec; + + default: + Debug.Fail($"Unknown custom debug info kind: 0x{(int)recKind:X}"); + data = reader.ReadBytes((int)(recPosEnd - reader.Position)); + return new PdbUnknownCustomDebugInfo(recKind, data); + } + } + + TypeDef GetNestedType(string name) { + if (typeOpt is null) + return null; + var nestedTypes = typeOpt.NestedTypes; + int count = nestedTypes.Count; + for (int i = 0; i < count; i++) { + var type = nestedTypes[i]; + if (UTF8String.IsNullOrEmpty(type.Namespace)) { + if (type.Name == name) + return type; + var typeName = type.Name.String; + if (typeName.StartsWith(name) && typeName.Length >= name.Length + 2) { + int index = name.Length; + if (typeName[index] == '`') { + Debug.Assert(index + 1 < typeName.Length); + bool ok = true; + index++; + while (index < typeName.Length) { + if (!char.IsDigit(typeName[index])) { + ok = false; + break; + } + index++; + } + if (ok) + return type; + } + } + } + } + return null; + } + + string ReadUnicodeZ(ulong recPosEnd, bool needZeroChar) { + var sb = new StringBuilder(); + + for (;;) { + if (reader.Position >= recPosEnd) + return needZeroChar ? null : sb.ToString(); + var c = reader.ReadChar(); + if (c == 0) + return sb.ToString(); + sb.Append(c); + } + } + + string ReadUTF8Z(ulong recPosEnd) { + if (reader.Position > recPosEnd) + return null; + return reader.TryReadZeroTerminatedUtf8String(); + } + + Instruction GetInstruction(uint offset) { + var instructions = bodyOpt.Instructions; + int lo = 0, hi = instructions.Count - 1; + while (lo <= hi && hi != -1) { + int i = (lo + hi) / 2; + var instr = instructions[i]; + if (instr.Offset == offset) + return instr; + if (offset < instr.Offset) + hi = i - 1; + else + lo = i + 1; + } + return null; + } + } +} diff --git a/src/DotNet/Pdb/WindowsPdb/PdbCustomDebugInfoWriter.cs b/src/DotNet/Pdb/WindowsPdb/PdbCustomDebugInfoWriter.cs new file mode 100644 index 000000000..fbfe6cb92 --- /dev/null +++ b/src/DotNet/Pdb/WindowsPdb/PdbCustomDebugInfoWriter.cs @@ -0,0 +1,399 @@ +// dnlib: See LICENSE.txt for more info + +// C# & Visual Basic compiler's Custom Debug Info is "documented" in source code only, see Roslyn classes: +// CustomDebugInfoReader, CustomDebugInfoWriter, CustomDebugInfoEncoder + +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using dnlib.DotNet.Emit; +using dnlib.DotNet.Writer; + +namespace dnlib.DotNet.Pdb.WindowsPdb { + sealed class PdbCustomDebugInfoWriterContext { + public ILogger Logger; + public readonly MemoryStream MemoryStream; + public readonly DataWriter Writer; + public readonly Dictionary InstructionToOffsetDict; + + public PdbCustomDebugInfoWriterContext() { + MemoryStream = new MemoryStream(); + Writer = new DataWriter(MemoryStream); + InstructionToOffsetDict = new Dictionary(); + } + } + + /// + /// Writes custom debug infos produced by the C# and Visual Basic compilers. They're stored in PDB files + /// as PDB method custom attributes with the name "MD2". + /// + struct PdbCustomDebugInfoWriter { + readonly Metadata metadata; + readonly MethodDef method; + readonly ILogger logger; + readonly MemoryStream memoryStream; + readonly DataWriter writer; + readonly Dictionary instructionToOffsetDict; + uint bodySize; + bool instructionToOffsetDictInitd; + + /// + /// Returns the raw custom debug info or null if there was an error + /// + /// Metadata + /// Writer context + /// Method + /// Custom debug infos to write + /// + public static byte[] Write(Metadata metadata, MethodDef method, PdbCustomDebugInfoWriterContext context, IList customDebugInfos) { + var writer = new PdbCustomDebugInfoWriter(metadata, method, context); + return writer.Write(customDebugInfos); + } + + PdbCustomDebugInfoWriter(Metadata metadata, MethodDef method, PdbCustomDebugInfoWriterContext context) { + this.metadata = metadata; + this.method = method; + logger = context.Logger; + memoryStream = context.MemoryStream; + writer = context.Writer; + instructionToOffsetDict = context.InstructionToOffsetDict; + bodySize = 0; + instructionToOffsetDictInitd = false; + memoryStream.SetLength(0); + memoryStream.Position = 0; + } + + void InitializeInstructionDictionary() { + Debug.Assert(!instructionToOffsetDictInitd); + instructionToOffsetDict.Clear(); + var body = method.Body; + if (body is null) + return; + var instrs = body.Instructions; + uint offset = 0; + for (int i = 0; i < instrs.Count; i++) { + var instr = instrs[i]; + instructionToOffsetDict[instr] = offset; + offset += (uint)instr.GetSize(); + } + bodySize = offset; + instructionToOffsetDictInitd = true; + } + + uint GetInstructionOffset(Instruction instr, bool nullIsEndOfMethod) { + if (!instructionToOffsetDictInitd) + InitializeInstructionDictionary(); + if (instr is null) { + if (nullIsEndOfMethod) + return bodySize; + Error("Instruction is null"); + return uint.MaxValue; + } + if (instructionToOffsetDict.TryGetValue(instr, out uint offset)) + return offset; + Error("Instruction is missing in body but it's still being referenced by PDB data. Method {0} (0x{1:X8}), instruction: {2}", method, method.MDToken.Raw, instr); + return uint.MaxValue; + } + + void Error(string message, params object[] args) => logger.Log(this, LoggerEvent.Error, message, args); + + byte[] Write(IList customDebugInfos) { + if (customDebugInfos.Count == 0) + return null; + if (customDebugInfos.Count > byte.MaxValue) { + Error("Too many custom debug infos. Count must be <= 255"); + return null; + } + + writer.WriteByte(CustomDebugInfoConstants.Version); + writer.WriteByte((byte)customDebugInfos.Count); + writer.WriteUInt16(0); + + for (int i = 0; i < customDebugInfos.Count; i++) { + var info = customDebugInfos[i]; + if (info is null) { + Error("Custom debug info is null"); + return null; + } + if ((uint)info.Kind > byte.MaxValue) { + Error("Invalid custom debug info kind"); + return null; + } + + var recordPos = writer.Position; + writer.WriteByte(CustomDebugInfoConstants.RecordVersion); + writer.WriteByte((byte)info.Kind); + writer.WriteUInt16(0); + writer.WriteUInt32(0); + + int count, j, k; + uint token; + switch (info.Kind) { + case PdbCustomDebugInfoKind.UsingGroups: + var usingRec = info as PdbUsingGroupsCustomDebugInfo; + if (usingRec is null) { + Error("Unsupported custom debug info type {0}", info.GetType()); + return null; + } + count = usingRec.UsingCounts.Count; + if (count > ushort.MaxValue) { + Error("UsingCounts contains more than 0xFFFF elements"); + return null; + } + writer.WriteUInt16((ushort)count); + for (j = 0; j < count; j++) + writer.WriteUInt16(usingRec.UsingCounts[j]); + break; + + case PdbCustomDebugInfoKind.ForwardMethodInfo: + var fwdMethodRec = info as PdbForwardMethodInfoCustomDebugInfo; + if (fwdMethodRec is null) { + Error("Unsupported custom debug info type {0}", info.GetType()); + return null; + } + token = GetMethodToken(fwdMethodRec.Method); + if (token == 0) + return null; + writer.WriteUInt32(token); + break; + + case PdbCustomDebugInfoKind.ForwardModuleInfo: + var fwdModRec = info as PdbForwardModuleInfoCustomDebugInfo; + if (fwdModRec is null) { + Error("Unsupported custom debug info type {0}", info.GetType()); + return null; + } + token = GetMethodToken(fwdModRec.Method); + if (token == 0) + return null; + writer.WriteUInt32(token); + break; + + case PdbCustomDebugInfoKind.StateMachineHoistedLocalScopes: + var smLocalScopesRec = info as PdbStateMachineHoistedLocalScopesCustomDebugInfo; + if (smLocalScopesRec is null) { + Error("Unsupported custom debug info type {0}", info.GetType()); + return null; + } + count = smLocalScopesRec.Scopes.Count; + writer.WriteInt32(count); + for (j = 0; j < count; j++) { + var scope = smLocalScopesRec.Scopes[j]; + if (scope.IsSynthesizedLocal) { + writer.WriteInt32(0); + writer.WriteInt32(0); + } + else { + writer.WriteUInt32(GetInstructionOffset(scope.Start, nullIsEndOfMethod: false)); + writer.WriteUInt32(GetInstructionOffset(scope.End, nullIsEndOfMethod: true) - 1); + } + } + break; + + case PdbCustomDebugInfoKind.StateMachineTypeName: + var smTypeRec = info as PdbStateMachineTypeNameCustomDebugInfo; + if (smTypeRec is null) { + Error("Unsupported custom debug info type {0}", info.GetType()); + return null; + } + var type = smTypeRec.Type; + if (type is null) { + Error("State machine type is null"); + return null; + } + WriteUnicodeZ(MetadataNameToRoslynName(type.Name)); + break; + + case PdbCustomDebugInfoKind.DynamicLocals: + var dynLocListRec = info as PdbDynamicLocalsCustomDebugInfo; + if (dynLocListRec is null) { + Error("Unsupported custom debug info type {0}", info.GetType()); + return null; + } + count = dynLocListRec.Locals.Count; + writer.WriteInt32(count); + for (j = 0; j < count; j++) { + var dynLoc = dynLocListRec.Locals[j]; + if (dynLoc is null) { + Error("Dynamic local is null"); + return null; + } + if (dynLoc.Flags.Count > 64) { + Error("Dynamic local flags is longer than 64 bytes"); + return null; + } + var name = dynLoc.Name; + if (name is null) + name = string.Empty; + if (name.Length > 64) { + Error("Dynamic local name is longer than 64 chars"); + return null; + } + if (name.IndexOf('\0') >= 0) { + Error("Dynamic local name contains a NUL char"); + return null; + } + + for (k = 0; k < dynLoc.Flags.Count; k++) + writer.WriteByte(dynLoc.Flags[k]); + while (k++ < 64) + writer.WriteByte(0); + writer.WriteInt32(dynLoc.Flags.Count); + + if (dynLoc.Local is null) + writer.WriteInt32(0); + else + writer.WriteInt32(dynLoc.Local.Index); + + for (k = 0; k < name.Length; k++) + writer.WriteUInt16(name[k]); + while (k++ < 64) + writer.WriteUInt16(0); + } + break; + + case PdbCustomDebugInfoKind.EditAndContinueLocalSlotMap: + var encLocalMapRec = info as PdbEditAndContinueLocalSlotMapCustomDebugInfo; + if (encLocalMapRec is null) { + Error("Unsupported custom debug info type {0}", info.GetType()); + return null; + } + writer.WriteBytes(encLocalMapRec.Data); + break; + + case PdbCustomDebugInfoKind.EditAndContinueLambdaMap: + var encLambdaRec = info as PdbEditAndContinueLambdaMapCustomDebugInfo; + if (encLambdaRec is null) { + Error("Unsupported custom debug info type {0}", info.GetType()); + return null; + } + writer.WriteBytes(encLambdaRec.Data); + break; + + case PdbCustomDebugInfoKind.TupleElementNames: + var tupleListRec = info as PdbTupleElementNamesCustomDebugInfo; + if (tupleListRec is null) { + Error("Unsupported custom debug info type {0}", info.GetType()); + return null; + } + count = tupleListRec.Names.Count; + writer.WriteInt32(count); + for (j = 0; j < count; j++) { + var tupleInfo = tupleListRec.Names[j]; + if (tupleInfo is null) { + Error("Tuple name info is null"); + return null; + } + writer.WriteInt32(tupleInfo.TupleElementNames.Count); + for (k = 0; k < tupleInfo.TupleElementNames.Count; k++) + WriteUTF8Z(tupleInfo.TupleElementNames[k]); + + if (tupleInfo.Local is null) { + writer.WriteInt32(-1); + writer.WriteUInt32(GetInstructionOffset(tupleInfo.ScopeStart, nullIsEndOfMethod: false)); + writer.WriteUInt32(GetInstructionOffset(tupleInfo.ScopeEnd, nullIsEndOfMethod: true)); + } + else { + writer.WriteInt32(tupleInfo.Local.Index); + writer.WriteInt64(0L); + } + WriteUTF8Z(tupleInfo.Name); + } + break; + + default: + var unkRec = info as PdbUnknownCustomDebugInfo; + if (unkRec is null) { + Error("Unsupported custom debug info class {0}", info.GetType()); + return null; + } + writer.WriteBytes(unkRec.Data); + break; + } + + var pos = writer.Position; + var recLen = (pos - recordPos); + var alignedLen = (recLen + 3) & ~3; + if (alignedLen > uint.MaxValue) { + Error("Custom debug info record is too big"); + return null; + } + writer.Position = recordPos + 3; + if (info.Kind <= PdbCustomDebugInfoKind.DynamicLocals) + writer.WriteByte(0); + else + writer.WriteByte((byte)(alignedLen - recLen)); + writer.WriteUInt32((uint)alignedLen); + + writer.Position = pos; + while (writer.Position < recordPos + alignedLen) + writer.WriteByte(0); + } + + return memoryStream.ToArray(); + } + + string MetadataNameToRoslynName(string name) { + if (name is null) + return name; + int index = name.LastIndexOf('`'); + if (index < 0) + return name; + return name.Substring(0, index); + } + + void WriteUnicodeZ(string s) { + if (s is null) { + Error("String is null"); + return; + } + + if (s.IndexOf('\0') >= 0) { + Error("String contains a NUL char: {0}", s); + return; + } + + for (int i = 0; i < s.Length; i++) + writer.WriteUInt16(s[i]); + writer.WriteUInt16(0); + } + + void WriteUTF8Z(string s) { + if (s is null) { + Error("String is null"); + return; + } + + if (s.IndexOf('\0') >= 0) { + Error("String contains a NUL char: {0}", s); + return; + } + + writer.WriteBytes(Encoding.UTF8.GetBytes(s)); + writer.WriteByte(0); + } + + uint GetMethodToken(IMethodDefOrRef method) { + if (method is null) { + Error("Method is null"); + return 0; + } + + if (method is MethodDef md) { + uint rid = metadata.GetRid(md); + if (rid == 0) { + Error("Method {0} ({1:X8}) is not defined in this module ({2})", method, method.MDToken.Raw, metadata.Module); + return 0; + } + return new MDToken(md.MDToken.Table, rid).Raw; + } + + if (method is MemberRef mr && mr.IsMethodRef) + return metadata.GetToken(mr).Raw; + + Error("Not a method"); + return 0; + } + } +} diff --git a/src/DotNet/Pdb/WindowsPdb/PseudoCustomDebugInfoFactory.cs b/src/DotNet/Pdb/WindowsPdb/PseudoCustomDebugInfoFactory.cs new file mode 100644 index 000000000..6bee81eba --- /dev/null +++ b/src/DotNet/Pdb/WindowsPdb/PseudoCustomDebugInfoFactory.cs @@ -0,0 +1,77 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; +using System.Diagnostics; +using dnlib.DotNet.Emit; +using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb.Symbols; + +namespace dnlib.DotNet.Pdb.WindowsPdb { + static class PseudoCustomDebugInfoFactory { + public static PdbAsyncMethodCustomDebugInfo TryCreateAsyncMethod(ModuleDef module, MethodDef method, CilBody body, int asyncKickoffMethod, IList asyncStepInfos, uint? asyncCatchHandlerILOffset) { + var kickoffToken = new MDToken(asyncKickoffMethod); + if (kickoffToken.Table != Table.Method) + return null; + var kickoffMethod = module.ResolveToken(kickoffToken) as MethodDef; + + var asyncMethod = new PdbAsyncMethodCustomDebugInfo(asyncStepInfos.Count); + asyncMethod.KickoffMethod = kickoffMethod; + + if (asyncCatchHandlerILOffset is not null) { + asyncMethod.CatchHandlerInstruction = GetInstruction(body, asyncCatchHandlerILOffset.Value); + Debug.Assert(asyncMethod.CatchHandlerInstruction is not null); + } + + int count = asyncStepInfos.Count; + for (int i = 0; i < count; i++) { + var rawInfo = asyncStepInfos[i]; + var yieldInstruction = GetInstruction(body, rawInfo.YieldOffset); + Debug.Assert(yieldInstruction is not null); + if (yieldInstruction is null) + continue; + MethodDef breakpointMethod; + Instruction breakpointInstruction; + if (method.MDToken.Raw == rawInfo.BreakpointMethod) { + breakpointMethod = method; + breakpointInstruction = GetInstruction(body, rawInfo.BreakpointOffset); + } + else { + var breakpointMethodToken = new MDToken(rawInfo.BreakpointMethod); + Debug.Assert(breakpointMethodToken.Table == Table.Method); + if (breakpointMethodToken.Table != Table.Method) + continue; + breakpointMethod = module.ResolveToken(breakpointMethodToken) as MethodDef; + Debug.Assert(breakpointMethod is not null); + if (breakpointMethod is null) + continue; + breakpointInstruction = GetInstruction(breakpointMethod.Body, rawInfo.BreakpointOffset); + } + Debug.Assert(breakpointInstruction is not null); + if (breakpointInstruction is null) + continue; + + asyncMethod.StepInfos.Add(new PdbAsyncStepInfo(yieldInstruction, breakpointMethod, breakpointInstruction)); + } + + return asyncMethod; + } + + static Instruction GetInstruction(CilBody body, uint offset) { + if (body is null) + return null; + var instructions = body.Instructions; + int lo = 0, hi = instructions.Count - 1; + while (lo <= hi && hi != -1) { + int i = (lo + hi) / 2; + var instr = instructions[i]; + if (instr.Offset == offset) + return instr; + if (offset < instr.Offset) + hi = i - 1; + else + lo = i + 1; + } + return null; + } + } +} diff --git a/src/DotNet/Pdb/WindowsPdb/SymbolWriter.cs b/src/DotNet/Pdb/WindowsPdb/SymbolWriter.cs new file mode 100644 index 000000000..48fc59db7 --- /dev/null +++ b/src/DotNet/Pdb/WindowsPdb/SymbolWriter.cs @@ -0,0 +1,36 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Diagnostics.SymbolStore; +using dnlib.DotNet.Writer; + +namespace dnlib.DotNet.Pdb.WindowsPdb { + abstract class SymbolWriter : IDisposable { + public abstract bool IsDeterministic { get; } + public abstract bool SupportsAsyncMethods { get; } + + public abstract void Initialize(Metadata metadata); + public abstract void Close(); + public abstract bool GetDebugInfo(ChecksumAlgorithm pdbChecksumAlgorithm, ref uint pdbAge, out Guid guid, out uint stamp, out IMAGE_DEBUG_DIRECTORY pIDD, out byte[] codeViewData); + + public abstract void SetUserEntryPoint(MDToken entryMethod); + public abstract ISymbolDocumentWriter DefineDocument(string url, Guid language, Guid languageVendor, Guid documentType); + public abstract void SetSourceServerData(byte[] data); + public abstract void SetSourceLinkData(byte[] data); + + public abstract void OpenMethod(MDToken method); + public abstract void CloseMethod(); + public abstract int OpenScope(int startOffset); + public abstract void CloseScope(int endOffset); + public abstract void SetSymAttribute(MDToken parent, string name, byte[] data); + public abstract void UsingNamespace(string fullName); + public abstract void DefineSequencePoints(ISymbolDocumentWriter document, uint arraySize, int[] offsets, int[] lines, int[] columns, int[] endLines, int[] endColumns); + public abstract void DefineLocalVariable(string name, uint attributes, uint sigToken, uint addrKind, uint addr1, uint addr2, uint addr3, uint startOffset, uint endOffset); + public abstract void DefineConstant(string name, object value, uint sigToken); + public abstract void DefineKickoffMethod(uint kickoffMethod); + public abstract void DefineCatchHandlerILOffset(uint catchHandlerOffset); + public abstract void DefineAsyncStepInfo(uint[] yieldOffsets, uint[] breakpointOffset, uint[] breakpointMethod); + + public abstract void Dispose(); + } +} diff --git a/src/DotNet/Pdb/WindowsPdb/WindowsPdbWriter.cs b/src/DotNet/Pdb/WindowsPdb/WindowsPdbWriter.cs new file mode 100644 index 000000000..1d7336de0 --- /dev/null +++ b/src/DotNet/Pdb/WindowsPdb/WindowsPdbWriter.cs @@ -0,0 +1,444 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Collections.Generic; +using System.Diagnostics.SymbolStore; +using dnlib.DotNet.Emit; +using dnlib.DotNet.Writer; + +namespace dnlib.DotNet.Pdb.WindowsPdb { + sealed class WindowsPdbWriter : IDisposable { + SymbolWriter writer; + readonly PdbState pdbState; + readonly ModuleDef module; + readonly Metadata metadata; + readonly Dictionary pdbDocs = new Dictionary(); + readonly SequencePointHelper seqPointsHelper = new SequencePointHelper(); + readonly Dictionary instrToOffset; + readonly PdbCustomDebugInfoWriterContext customDebugInfoWriterContext; + readonly int localsEndScopeIncValue; + + public ILogger Logger { get; set; } + + public WindowsPdbWriter(SymbolWriter writer, PdbState pdbState, Metadata metadata) + : this(pdbState, metadata) { + if (pdbState is null) + throw new ArgumentNullException(nameof(pdbState)); + if (metadata is null) + throw new ArgumentNullException(nameof(metadata)); + this.writer = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.Initialize(metadata); + } + + WindowsPdbWriter(PdbState pdbState, Metadata metadata) { + this.pdbState = pdbState; + this.metadata = metadata; + module = metadata.Module; + instrToOffset = new Dictionary(); + customDebugInfoWriterContext = new PdbCustomDebugInfoWriterContext(); + localsEndScopeIncValue = PdbUtils.IsEndInclusive(PdbFileKind.WindowsPDB, pdbState.Compiler) ? 1 : 0; + } + + ISymbolDocumentWriter Add(PdbDocument pdbDoc) { + if (pdbDocs.TryGetValue(pdbDoc, out var docWriter)) + return docWriter; + docWriter = writer.DefineDocument(pdbDoc.Url, pdbDoc.Language, pdbDoc.LanguageVendor, pdbDoc.DocumentType); + docWriter.SetCheckSum(pdbDoc.CheckSumAlgorithmId, pdbDoc.CheckSum); + if (TryGetCustomDebugInfo(pdbDoc, out PdbEmbeddedSourceCustomDebugInfo sourceCdi)) + docWriter.SetSource(sourceCdi.SourceCodeBlob); + pdbDocs.Add(pdbDoc, docWriter); + return docWriter; + } + + static bool TryGetCustomDebugInfo(IHasCustomDebugInformation hci, out TCDI cdi) where TCDI : PdbCustomDebugInfo { + var cdis = hci.CustomDebugInfos; + int count = cdis.Count; + for (int i = 0; i < count; i++) { + if (cdis[i] is TCDI cdi2) { + cdi = cdi2; + return true; + } + } + cdi = null; + return false; + } + + public void Write() { + writer.SetUserEntryPoint(GetUserEntryPointToken()); + + var cdiBuilder = new List(); + foreach (var type in module.GetTypes()) { + if (type is null) + continue; + var typeMethods = type.Methods; + int count = typeMethods.Count; + for (int i = 0; i < count; i++) { + var method = typeMethods[i]; + if (method is null) + continue; + if (!ShouldAddMethod(method)) + continue; + Write(method, cdiBuilder); + } + } + + if (TryGetCustomDebugInfo(module, out PdbSourceLinkCustomDebugInfo sourceLinkCdi)) + writer.SetSourceLinkData(sourceLinkCdi.FileBlob); + if (TryGetCustomDebugInfo(module, out PdbSourceServerCustomDebugInfo sourceServerCdi)) + writer.SetSourceServerData(sourceServerCdi.FileBlob); + } + + bool ShouldAddMethod(MethodDef method) { + var body = method.Body; + if (body is null) + return false; + + if (body.HasPdbMethod) + return true; + + var bodyVariables = body.Variables; + int count = bodyVariables.Count; + for (int i = 0; i < count; i++) { + var local = bodyVariables[i]; + // Don't check whether it's the empty string. Only check for null. + if (local.Name is not null) + return true; + if (local.Attributes != 0) + return true; + } + + var bodyInstructions = body.Instructions; + count = bodyInstructions.Count; + for (int i = 0; i < count; i++) { + if (bodyInstructions[i].SequencePoint is not null) + return true; + } + + return false; + } + + sealed class SequencePointHelper { + readonly Dictionary checkedPdbDocs = new Dictionary(); + int[] instrOffsets = Array2.Empty(); + int[] startLines; + int[] startColumns; + int[] endLines; + int[] endColumns; + + public void Write(WindowsPdbWriter pdbWriter, IList instrs) { + checkedPdbDocs.Clear(); + while (true) { + PdbDocument currPdbDoc = null; + bool otherDocsAvailable = false; + int index = 0, instrOffset = 0; + Instruction instr = null; + for (int i = 0; i < instrs.Count; i++, instrOffset += instr.GetSize()) { + instr = instrs[i]; + var seqp = instr.SequencePoint; + if (seqp is null || seqp.Document is null) + continue; + if (checkedPdbDocs.ContainsKey(seqp.Document)) + continue; + if (currPdbDoc is null) + currPdbDoc = seqp.Document; + else if (currPdbDoc != seqp.Document) { + otherDocsAvailable = true; + continue; + } + + if (index >= instrOffsets.Length) { + int newSize = index * 2; + if (newSize < 64) + newSize = 64; + Array.Resize(ref instrOffsets, newSize); + Array.Resize(ref startLines, newSize); + Array.Resize(ref startColumns, newSize); + Array.Resize(ref endLines, newSize); + Array.Resize(ref endColumns, newSize); + } + + instrOffsets[index] = instrOffset; + startLines[index] = seqp.StartLine; + startColumns[index] = seqp.StartColumn; + endLines[index] = seqp.EndLine; + endColumns[index] = seqp.EndColumn; + index++; + } + if (index != 0) + pdbWriter.writer.DefineSequencePoints(pdbWriter.Add(currPdbDoc), (uint)index, instrOffsets, startLines, startColumns, endLines, endColumns); + + if (!otherDocsAvailable) + break; + if (currPdbDoc is not null) + checkedPdbDocs.Add(currPdbDoc, true); + } + } + } + + struct CurrentMethod { + readonly WindowsPdbWriter pdbWriter; + public readonly MethodDef Method; + readonly Dictionary toOffset; + public readonly uint BodySize; + + public CurrentMethod(WindowsPdbWriter pdbWriter, MethodDef method, Dictionary toOffset) { + this.pdbWriter = pdbWriter; + Method = method; + this.toOffset = toOffset; + toOffset.Clear(); + uint offset = 0; + var instructions = method.Body.Instructions; + int count = instructions.Count; + for (int i = 0; i < count; i++) { + var instr = instructions[i]; + toOffset[instr] = offset; + offset += (uint)instr.GetSize(); + } + BodySize = offset; + } + + public readonly int GetOffset(Instruction instr) { + if (instr is null) + return (int)BodySize; + if (toOffset.TryGetValue(instr, out uint offset)) + return (int)offset; + pdbWriter.Error("Instruction was removed from the body but is referenced from PdbScope: {0}", instr); + return (int)BodySize; + } + } + + void Write(MethodDef method, List cdiBuilder) { + uint rid = metadata.GetRid(method); + if (rid == 0) { + Error("Method {0} ({1:X8}) is not defined in this module ({2})", method, method.MDToken.Raw, module); + return; + } + + var info = new CurrentMethod(this, method, instrToOffset); + var body = method.Body; + var symbolToken = new MDToken(MD.Table.Method, rid); + writer.OpenMethod(symbolToken); + seqPointsHelper.Write(this, info.Method.Body.Instructions); + + var pdbMethod = body.PdbMethod; + if (pdbMethod is null) + body.PdbMethod = pdbMethod = new PdbMethod(); + var scope = pdbMethod.Scope; + if (scope is null) + pdbMethod.Scope = scope = new PdbScope(); + if (scope.Namespaces.Count == 0 && scope.Variables.Count == 0 && scope.Constants.Count == 0) { + if (scope.Scopes.Count == 0) { + // We must open at least one sub scope (the sym writer creates the 'method' scope + // which covers the whole method) or the native PDB reader will fail to read all + // sequence points. + writer.OpenScope(0); + writer.CloseScope((int)info.BodySize); + } + else { + var scopes = scope.Scopes; + int count = scopes.Count; + for (int i = 0; i < count; i++) + WriteScope(ref info, scopes[i], 0); + } + } + else { + // C++/.NET (some methods) + WriteScope(ref info, scope, 0); + } + + GetPseudoCustomDebugInfos(method.CustomDebugInfos, cdiBuilder, out var asyncMethod); + if (cdiBuilder.Count != 0) { + customDebugInfoWriterContext.Logger = GetLogger(); + var cdiData = PdbCustomDebugInfoWriter.Write(metadata, method, customDebugInfoWriterContext, cdiBuilder); + if (cdiData is not null) + writer.SetSymAttribute(symbolToken, "MD2", cdiData); + } + + if (asyncMethod is not null) { + if (!writer.SupportsAsyncMethods) + Error("PDB symbol writer doesn't support writing async methods"); + else + WriteAsyncMethod(ref info, asyncMethod); + } + + writer.CloseMethod(); + } + + void GetPseudoCustomDebugInfos(IList customDebugInfos, List cdiBuilder, out PdbAsyncMethodCustomDebugInfo asyncMethod) { + cdiBuilder.Clear(); + asyncMethod = null; + int count = customDebugInfos.Count; + for (int i = 0; i < count; i++) { + var cdi = customDebugInfos[i]; + switch (cdi.Kind) { + case PdbCustomDebugInfoKind.AsyncMethod: + if (asyncMethod is not null) + Error("Duplicate async method custom debug info"); + else + asyncMethod = (PdbAsyncMethodCustomDebugInfo)cdi; + break; + + default: + if ((uint)cdi.Kind > byte.MaxValue) + Error("Custom debug info {0} isn't supported by Windows PDB files", cdi.Kind); + else + cdiBuilder.Add(cdi); + break; + } + } + } + + uint GetMethodToken(MethodDef method) { + uint rid = metadata.GetRid(method); + if (rid == 0) + Error("Method {0} ({1:X8}) is not defined in this module ({2})", method, method.MDToken.Raw, module); + return new MDToken(MD.Table.Method, rid).Raw; + } + + void WriteAsyncMethod(ref CurrentMethod info, PdbAsyncMethodCustomDebugInfo asyncMethod) { + if (asyncMethod.KickoffMethod is null) { + Error("KickoffMethod is null"); + return; + } + + uint kickoffMethod = GetMethodToken(asyncMethod.KickoffMethod); + writer.DefineKickoffMethod(kickoffMethod); + + if (asyncMethod.CatchHandlerInstruction is not null) { + int catchHandlerILOffset = info.GetOffset(asyncMethod.CatchHandlerInstruction); + writer.DefineCatchHandlerILOffset((uint)catchHandlerILOffset); + } + + var stepInfos = asyncMethod.StepInfos; + var yieldOffsets = new uint[stepInfos.Count]; + var breakpointOffset = new uint[stepInfos.Count]; + var breakpointMethods = new uint[stepInfos.Count]; + for (int i = 0; i < yieldOffsets.Length; i++) { + var stepInfo = stepInfos[i]; + if (stepInfo.YieldInstruction is null) { + Error("YieldInstruction is null"); + return; + } + if (stepInfo.BreakpointMethod is null) { + Error("BreakpointMethod is null"); + return; + } + if (stepInfo.BreakpointInstruction is null) { + Error("BreakpointInstruction is null"); + return; + } + yieldOffsets[i] = (uint)info.GetOffset(stepInfo.YieldInstruction); + breakpointOffset[i] = (uint)GetExternalInstructionOffset(ref info, stepInfo.BreakpointMethod, stepInfo.BreakpointInstruction); + breakpointMethods[i] = GetMethodToken(stepInfo.BreakpointMethod); + } + writer.DefineAsyncStepInfo(yieldOffsets, breakpointOffset, breakpointMethods); + } + + int GetExternalInstructionOffset(ref CurrentMethod info, MethodDef method, Instruction instr) { + if (info.Method == method) + return info.GetOffset(instr); + var body = method.Body; + if (body is null) { + Error("Method body is null"); + return 0; + } + + var instrs = body.Instructions; + int offset = 0; + for (int i = 0; i < instrs.Count; i++) { + var currInstr = instrs[i]; + if (currInstr == instr) + return offset; + offset += currInstr.GetSize(); + } + if (instr is null) + return offset; + Error("Async method instruction has been removed but it's still being referenced by PDB info: BP Instruction: {0}, BP Method: {1} (0x{2:X8}), Current Method: {3} (0x{4:X8})", instr, method, method.MDToken.Raw, info.Method, info.Method.MDToken.Raw); + return 0; + } + + void WriteScope(ref CurrentMethod info, PdbScope scope, int recursionCounter) { + if (recursionCounter >= 1000) { + Error("Too many PdbScopes"); + return; + } + + int startOffset = info.GetOffset(scope.Start); + int endOffset = info.GetOffset(scope.End); + writer.OpenScope(startOffset); + AddLocals(info.Method, scope.Variables, (uint)startOffset, (uint)endOffset); + if (scope.Constants.Count > 0) { + var constants = scope.Constants; + var sig = new FieldSig(); + for (int i = 0; i < constants.Count; i++) { + var constant = constants[i]; + sig.Type = constant.Type; + var token = metadata.GetToken(sig); + writer.DefineConstant(constant.Name, constant.Value ?? boxedZeroInt32, token.Raw); + } + } + var scopeNamespaces = scope.Namespaces; + int count = scopeNamespaces.Count; + for (int i = 0; i < count; i++) + writer.UsingNamespace(scopeNamespaces[i]); + var scopes = scope.Scopes; + count = scopes.Count; + for (int i = 0; i < count; i++) + WriteScope(ref info, scopes[i], recursionCounter + 1); + writer.CloseScope(startOffset == 0 && endOffset == info.BodySize ? endOffset : endOffset - localsEndScopeIncValue); + } + static readonly object boxedZeroInt32 = 0; + + void AddLocals(MethodDef method, IList locals, uint startOffset, uint endOffset) { + if (locals.Count == 0) + return; + uint token = metadata.GetLocalVarSigToken(method); + if (token == 0) { + Error("Method {0} ({1:X8}) has no local signature token", method, method.MDToken.Raw); + return; + } + int count = locals.Count; + for (int i = 0; i < count; i++) { + var local = locals[i]; + uint attrs = GetPdbLocalFlags(local.Attributes); + if (attrs == 0 && local.Name is null) + continue; + writer.DefineLocalVariable(local.Name ?? string.Empty, attrs, + token, 1, (uint)local.Index, 0, 0, startOffset, endOffset); + } + } + + static uint GetPdbLocalFlags(PdbLocalAttributes attributes) { + if ((attributes & PdbLocalAttributes.DebuggerHidden) != 0) + return (uint)CorSymVarFlag.VAR_IS_COMP_GEN; + return 0; + } + + MDToken GetUserEntryPointToken() { + var ep = pdbState.UserEntryPoint; + if (ep is null) + return default; + uint rid = metadata.GetRid(ep); + if (rid == 0) { + Error("PDB user entry point method {0} ({1:X8}) is not defined in this module ({2})", ep, ep.MDToken.Raw, module); + return default; + } + return new MDToken(MD.Table.Method, rid); + } + + public bool GetDebugInfo(ChecksumAlgorithm pdbChecksumAlgorithm, ref uint pdbAge, out Guid guid, out uint stamp, out IMAGE_DEBUG_DIRECTORY idd, out byte[] codeViewData) => + writer.GetDebugInfo(pdbChecksumAlgorithm, ref pdbAge, out guid, out stamp, out idd, out codeViewData); + + public void Close() => writer.Close(); + ILogger GetLogger() => Logger ?? DummyLogger.ThrowModuleWriterExceptionOnErrorInstance; + void Error(string message, params object[] args) => GetLogger().Log(this, LoggerEvent.Error, message, args); + + /// + public void Dispose() { + if (writer is not null) + Close(); + writer?.Dispose(); + writer = null; + } + } +} diff --git a/src/DotNet/PropertyAttributes.cs b/src/DotNet/PropertyAttributes.cs index c2546b3e6..9e08763af 100644 --- a/src/DotNet/PropertyAttributes.cs +++ b/src/DotNet/PropertyAttributes.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.DotNet { /// diff --git a/src/DotNet/PropertyDef.cs b/src/DotNet/PropertyDef.cs index cf0f68940..67bd1f9ce 100644 --- a/src/DotNet/PropertyDef.cs +++ b/src/DotNet/PropertyDef.cs @@ -1,21 +1,18 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; using dnlib.Threading; -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif - namespace dnlib.DotNet { /// /// A high-level representation of a row in the Property table /// - public abstract class PropertyDef : IHasConstant, IHasCustomAttribute, IHasSemantic, IFullName, IMemberDef { + public abstract class PropertyDef : IHasConstant, IHasCustomAttribute, IHasSemantic, IHasCustomDebugInformation, IFullName, IMemberDef { /// /// The row id in its table /// @@ -26,37 +23,29 @@ public abstract class PropertyDef : IHasConstant, IHasCustomAttribute, IHasSeman #endif /// - public MDToken MDToken { - get { return new MDToken(Table.Property, rid); } - } + public MDToken MDToken => new MDToken(Table.Property, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasConstantTag { - get { return 2; } - } + public int HasConstantTag => 2; /// - public int HasCustomAttributeTag { - get { return 9; } - } + public int HasCustomAttributeTag => 9; /// - public int HasSemanticTag { - get { return 1; } - } + public int HasSemanticTag => 1; /// /// From column Property.PropFlags /// public PropertyAttributes Attributes { - get { return (PropertyAttributes)attributes; } - set { attributes = (int)value; } + get => (PropertyAttributes)attributes; + set => attributes = (int)value; } /// Attributes protected int attributes; @@ -65,8 +54,8 @@ public PropertyAttributes Attributes { /// From column Property.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -75,8 +64,8 @@ public UTF8String Name { /// From column Property.Type /// public CallingConventionSig Type { - get { return type; } - set { type = value; } + get => type; + set => type = value; } /// protected CallingConventionSig type; @@ -118,21 +107,17 @@ void InitializeConstant() { } /// Called to initialize - protected virtual Constant GetConstant_NoLock() { - return null; - } + protected virtual Constant GetConstant_NoLock() => null; /// Reset - protected void ResetConstant() { - constant_isInitialized = false; - } + protected void ResetConstant() => constant_isInitialized = false; /// /// Gets all custom attributes /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -140,28 +125,49 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); + + /// + public int HasCustomDebugInformationTag => 9; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// /// Gets/sets the first getter method. Writing null will clear all get methods. /// public MethodDef GetMethod { get { - if (otherMethods == null) + if (otherMethods is null) InitializePropertyMethods(); - return getMethods.Get(0, null); + return getMethods.Count == 0 ? null : getMethods[0]; } set { - if (otherMethods == null) + if (otherMethods is null) InitializePropertyMethods(); - if (value == null) + if (value is null) getMethods.Clear(); else if (getMethods.Count == 0) getMethods.Add(value); else - getMethods.Set(0, value); + getMethods[0] = value; } } @@ -170,28 +176,28 @@ public MethodDef GetMethod { /// public MethodDef SetMethod { get { - if (otherMethods == null) + if (otherMethods is null) InitializePropertyMethods(); - return setMethods.Get(0, null); + return setMethods.Count == 0 ? null : setMethods[0]; } set { - if (otherMethods == null) + if (otherMethods is null) InitializePropertyMethods(); - if (value == null) + if (value is null) setMethods.Clear(); else if (setMethods.Count == 0) setMethods.Add(value); else - setMethods.Set(0, value); + setMethods[0] = value; } } /// /// Gets all getter methods /// - public ThreadSafe.IList GetMethods { + public IList GetMethods { get { - if (otherMethods == null) + if (otherMethods is null) InitializePropertyMethods(); return getMethods; } @@ -200,9 +206,9 @@ public ThreadSafe.IList GetMethods { /// /// Gets all setter methods /// - public ThreadSafe.IList SetMethods { + public IList SetMethods { get { - if (otherMethods == null) + if (otherMethods is null) InitializePropertyMethods(); return setMethods; } @@ -211,9 +217,9 @@ public ThreadSafe.IList SetMethods { /// /// Gets the other methods /// - public ThreadSafe.IList OtherMethods { + public IList OtherMethods { get { - if (otherMethods == null) + if (otherMethods is null) InitializePropertyMethods(); return otherMethods; } @@ -223,7 +229,7 @@ void InitializePropertyMethods() { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif - if (otherMethods == null) + if (otherMethods is null) InitializePropertyMethods_NoLock(); #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } @@ -235,53 +241,42 @@ void InitializePropertyMethods() { /// and . /// protected virtual void InitializePropertyMethods_NoLock() { - getMethods = ThreadSafeListCreator.Create(); - setMethods = ThreadSafeListCreator.Create(); - otherMethods = ThreadSafeListCreator.Create(); + getMethods = new List(); + setMethods = new List(); + otherMethods = new List(); } /// - protected ThreadSafe.IList getMethods; + protected IList getMethods; /// - protected ThreadSafe.IList setMethods; + protected IList setMethods; /// - protected ThreadSafe.IList otherMethods; + protected IList otherMethods; /// Reset , , - protected void ResetMethods() { - otherMethods = null; - } + protected void ResetMethods() => otherMethods = null; /// /// true if there are no methods attached to this property /// - public bool IsEmpty { - get { - // The first property access initializes the other fields we access here - return GetMethods.Count == 0 && - setMethods.Count == 0 && - otherMethods.Count == 0; - } - } + public bool IsEmpty => + // The first property access initializes the other fields we access here + GetMethods.Count == 0 && + setMethods.Count == 0 && + otherMethods.Count == 0; /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } - } + public bool HasCustomAttributes => CustomAttributes.Count > 0; /// /// true if is not empty /// - public bool HasOtherMethods { - get { return OtherMethods.Count > 0; } - } + public bool HasOtherMethods => OtherMethods.Count > 0; /// /// true if is not null /// - public bool HasConstant { - get { return Constant != null; } - } + public bool HasConstant => Constant is not null; /// /// Gets the constant element type or if there's no constant @@ -289,7 +284,7 @@ public bool HasConstant { public ElementType ElementType { get { var c = Constant; - return c == null ? ElementType.End : c.Type; + return c is null ? ElementType.End : c.Type; } } @@ -297,30 +292,28 @@ public ElementType ElementType { /// Gets/sets the property sig /// public PropertySig PropertySig { - get { return type as PropertySig; } - set { type = value; } + get => type as PropertySig; + set => type = value; } /// /// Gets/sets the declaring type (owner type) /// public TypeDef DeclaringType { - get { return declaringType2; } + get => declaringType2; set { var currentDeclaringType = DeclaringType2; if (currentDeclaringType == value) return; - if (currentDeclaringType != null) + if (currentDeclaringType is not null) currentDeclaringType.Properties.Remove(this); // Will set DeclaringType2 = null - if (value != null) + if (value is not null) value.Properties.Add(this); // Will set DeclaringType2 = value } } /// - ITypeDefOrRef IMemberRef.DeclaringType { - get { return declaringType2; } - } + ITypeDefOrRef IMemberRef.DeclaringType => declaringType2; /// /// Called by and should normally not be called by any user @@ -328,81 +321,33 @@ ITypeDefOrRef IMemberRef.DeclaringType { /// declaring type without inserting it in the declaring type's method list. /// public TypeDef DeclaringType2 { - get { return declaringType2; } - set { declaringType2 = value; } + get => declaringType2; + set => declaringType2 = value; } /// protected TypeDef declaringType2; /// - public ModuleDef Module { - get { - var dt = declaringType2; - return dt == null ? null : dt.Module; - } - } + public ModuleDef Module => declaringType2?.Module; /// /// Gets the full name of the property /// - public string FullName { - get { - var dt = declaringType2; - return FullNameCreator.PropertyFullName(dt == null ? null : dt.FullName, name, type); - } - } - - bool IIsTypeOrMethod.IsType { - get { return false; } - } - - bool IIsTypeOrMethod.IsMethod { - get { return false; } - } - - bool IMemberRef.IsField { - get { return false; } - } - - bool IMemberRef.IsTypeSpec { - get { return false; } - } - - bool IMemberRef.IsTypeRef { - get { return false; } - } - - bool IMemberRef.IsTypeDef { - get { return false; } - } - - bool IMemberRef.IsMethodSpec { - get { return false; } - } - - bool IMemberRef.IsMethodDef { - get { return false; } - } - - bool IMemberRef.IsMemberRef { - get { return false; } - } - - bool IMemberRef.IsFieldDef { - get { return false; } - } - - bool IMemberRef.IsPropertyDef { - get { return true; } - } - - bool IMemberRef.IsEventDef { - get { return false; } - } - - bool IMemberRef.IsGenericParam { - get { return false; } - } + public string FullName => FullNameFactory.PropertyFullName(declaringType2?.FullName, name, type, null, null); + + bool IIsTypeOrMethod.IsType => false; + bool IIsTypeOrMethod.IsMethod => false; + bool IMemberRef.IsField => false; + bool IMemberRef.IsTypeSpec => false; + bool IMemberRef.IsTypeRef => false; + bool IMemberRef.IsTypeDef => false; + bool IMemberRef.IsMethodSpec => false; + bool IMemberRef.IsMethodDef => false; + bool IMemberRef.IsMemberRef => false; + bool IMemberRef.IsFieldDef => false; + bool IMemberRef.IsPropertyDef => true; + bool IMemberRef.IsEventDef => false; + bool IMemberRef.IsGenericParam => false; /// /// Set or clear flags in @@ -411,51 +356,38 @@ bool IMemberRef.IsGenericParam { /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, PropertyAttributes flags) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else if (set) attributes |= (int)flags; else attributes &= ~(int)flags; -#endif } /// /// Gets/sets the bit /// public bool IsSpecialName { - get { return ((PropertyAttributes)attributes & PropertyAttributes.SpecialName) != 0; } - set { ModifyAttributes(value, PropertyAttributes.SpecialName); } + get => ((PropertyAttributes)attributes & PropertyAttributes.SpecialName) != 0; + set => ModifyAttributes(value, PropertyAttributes.SpecialName); } /// /// Gets/sets the bit /// public bool IsRuntimeSpecialName { - get { return ((PropertyAttributes)attributes & PropertyAttributes.RTSpecialName) != 0; } - set { ModifyAttributes(value, PropertyAttributes.RTSpecialName); } + get => ((PropertyAttributes)attributes & PropertyAttributes.RTSpecialName) != 0; + set => ModifyAttributes(value, PropertyAttributes.RTSpecialName); } /// /// Gets/sets the bit /// public bool HasDefault { - get { return ((PropertyAttributes)attributes & PropertyAttributes.HasDefault) != 0; } - set { ModifyAttributes(value, PropertyAttributes.HasDefault); } + get => ((PropertyAttributes)attributes & PropertyAttributes.HasDefault) != 0; + set => ModifyAttributes(value, PropertyAttributes.HasDefault); } /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } /// @@ -493,8 +425,8 @@ public PropertyDefUser(UTF8String name, PropertySig sig) /// Flags public PropertyDefUser(UTF8String name, PropertySig sig, PropertyAttributes flags) { this.name = name; - this.type = sig; - this.attributes = (int)flags; + type = sig; + attributes = (int)flags; } } @@ -508,22 +440,25 @@ sealed class PropertyDefMD : PropertyDef, IMDTokenProviderMD { readonly uint origRid; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// - protected override Constant GetConstant_NoLock() { - return readerModule.ResolveConstant(readerModule.MetaData.GetConstantRid(Table.Property, origRid)); - } + protected override Constant GetConstant_NoLock() => readerModule.ResolveConstant(readerModule.Metadata.GetConstantRid(Table.Property, origRid)); /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.Property, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Property, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(declaringType2), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -533,19 +468,20 @@ protected override void InitializeCustomAttributes() { /// If is invalid public PropertyDefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.PropertyTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("Property rid {0} does not exist", rid)); + throw new BadImageFormatException($"Property rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint name; - uint type = readerModule.TablesStream.ReadPropertyRow(origRid, out this.attributes, out name); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.declaringType2 = readerModule.GetOwnerType(this); - this.type = readerModule.ReadSignature(type, new GenericParamContext(declaringType2)); + bool b = readerModule.TablesStream.TryReadPropertyRow(origRid, out var row); + Debug.Assert(b); + attributes = row.PropFlags; + name = readerModule.StringsStream.ReadNoNull(row.Name); + declaringType2 = readerModule.GetOwnerType(this); + type = readerModule.ReadSignature(row.Type, new GenericParamContext(declaringType2)); } internal PropertyDefMD InitializeAll() { @@ -563,15 +499,15 @@ internal PropertyDefMD InitializeAll() { /// protected override void InitializePropertyMethods_NoLock() { - if (otherMethods != null) + if (otherMethods is not null) return; - ThreadSafe.IList newOtherMethods; - ThreadSafe.IList newGetMethods, newSetMethods; + IList newOtherMethods; + IList newGetMethods, newSetMethods; var dt = declaringType2 as TypeDefMD; - if (dt == null) { - newGetMethods = ThreadSafeListCreator.Create(); - newSetMethods = ThreadSafeListCreator.Create(); - newOtherMethods = ThreadSafeListCreator.Create(); + if (dt is null) { + newGetMethods = new List(); + newSetMethods = new List(); + newOtherMethods = new List(); } else dt.InitializeProperty(this, out newGetMethods, out newSetMethods, out newOtherMethods); diff --git a/src/DotNet/PublicKey.cs b/src/DotNet/PublicKey.cs index e941dacfb..14b75d98f 100644 --- a/src/DotNet/PublicKey.cs +++ b/src/DotNet/PublicKey.cs @@ -1,65 +1,33 @@ // dnlib: See LICENSE.txt for more info -using dnlib.Threading; +using System.Threading; -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// Represents a public key /// public sealed class PublicKey : PublicKeyBase { const AssemblyHashAlgorithm DEFAULT_ALGORITHM = AssemblyHashAlgorithm.SHA1; PublicKeyToken publicKeyToken; -#if THREAD_SAFE - readonly Lock theLock = Lock.Create(); -#endif /// /// Gets the /// public override PublicKeyToken Token { get { -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - if (publicKeyToken == null && !IsNullOrEmpty_NoLock) - publicKeyToken = AssemblyHash.CreatePublicKeyToken(data); + if (publicKeyToken is null && !IsNullOrEmpty) + Interlocked.CompareExchange(ref publicKeyToken, AssemblyHash.CreatePublicKeyToken(data), null); return publicKeyToken; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif } } /// - public override byte[] Data { - get { -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif - return data; -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif - } - set { -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - if (data == value) - return; - data = value; - publicKeyToken = null; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - } + public override byte[] Data => data; /// - /// Default constructor + /// Constructor /// - public PublicKey() { - } + public PublicKey() : base((byte[])null) { } /// /// Constructor @@ -83,14 +51,12 @@ public override bool Equals(object obj) { if ((object)this == obj) return true; var other = obj as PublicKey; - if (other == null) + if (other is null) return false; return Utils.Equals(Data, other.Data); } /// - public override int GetHashCode() { - return Utils.GetHashCode(Data); - } + public override int GetHashCode() => Utils.GetHashCode(Data); } } diff --git a/src/DotNet/PublicKeyBase.cs b/src/DotNet/PublicKeyBase.cs index 40490e724..201017433 100644 --- a/src/DotNet/PublicKeyBase.cs +++ b/src/DotNet/PublicKeyBase.cs @@ -1,6 +1,8 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +using System; + +namespace dnlib.DotNet { /// /// Public key / public key token base class /// @@ -8,67 +10,43 @@ public abstract class PublicKeyBase { /// /// The key data /// - protected byte[] data; + protected readonly byte[] data; /// /// Returns true if is null or empty /// - public bool IsNullOrEmpty { - get { return IsNullOrEmpty_NoLock; } - } - - /// - /// The unlocked version of . - /// - protected bool IsNullOrEmpty_NoLock { - get { return data == null || data.Length == 0; } - } + public bool IsNullOrEmpty => data is null || data.Length == 0; /// /// Returns true if is null /// - public bool IsNull { - get { return Data == null; } - } + public bool IsNull => Data is null; /// /// Gets/sets key data /// - public virtual byte[] Data { - get { return data; } - set { data = value; } - } + public virtual byte[] Data => data; /// /// Gets the /// public abstract PublicKeyToken Token { get; } - /// - /// Default constructor - /// - protected PublicKeyBase() { - } - /// /// Constructor /// /// Key data - protected PublicKeyBase(byte[] data) { - this.data = data; - } + protected PublicKeyBase(byte[] data) => this.data = data; /// /// Constructor /// /// Key data as a hex string or the string "null" /// to set key data to null - protected PublicKeyBase(string hexString) { - this.data = Parse(hexString); - } + protected PublicKeyBase(string hexString) => data = Parse(hexString); static byte[] Parse(string hexString) { - if (hexString == null || hexString == "null") + if (hexString is null || hexString == "null") return null; return Utils.ParseBytes(hexString); } @@ -77,20 +55,16 @@ static byte[] Parse(string hexString) { /// Checks whether a public key or token is null or empty /// /// Public key or token instance - public static bool IsNullOrEmpty2(PublicKeyBase a) { - return a == null || a.IsNullOrEmpty; - } + public static bool IsNullOrEmpty2(PublicKeyBase a) => a is null || a.IsNullOrEmpty; /// /// Returns a /// /// A or a instance public static PublicKeyToken ToPublicKeyToken(PublicKeyBase pkb) { - var pkt = pkb as PublicKeyToken; - if (pkt != null) + if (pkb is PublicKeyToken pkt) return pkt; - var pk = pkb as PublicKey; - if (pk != null) + if (pkb is PublicKey pk) return pk.Token; return null; } @@ -113,11 +87,9 @@ public static int TokenCompareTo(PublicKeyBase a, PublicKeyBase b) { /// First /// Second /// true if same, false otherwise - public static bool TokenEquals(PublicKeyBase a, PublicKeyBase b) { - return TokenCompareTo(a, b) == 0; - } + public static bool TokenEquals(PublicKeyBase a, PublicKeyBase b) => TokenCompareTo(a, b) == 0; - static readonly byte[] EmptyByteArray = new byte[0]; + static readonly byte[] EmptyByteArray = Array2.Empty(); /// /// Compares two s /// @@ -127,12 +99,10 @@ public static bool TokenEquals(PublicKeyBase a, PublicKeyBase b) { public static int TokenCompareTo(PublicKeyToken a, PublicKeyToken b) { if (a == b) return 0; - return TokenCompareTo(a == null ? null : a.Data, b == null ? null : b.Data); + return TokenCompareTo(a?.Data, b?.Data); } - static int TokenCompareTo(byte[] a, byte[] b) { - return Utils.CompareTo(a ?? EmptyByteArray, b ?? EmptyByteArray); - } + static int TokenCompareTo(byte[] a, byte[] b) => Utils.CompareTo(a ?? EmptyByteArray, b ?? EmptyByteArray); /// /// Checks whether two public key tokens are equal @@ -140,18 +110,14 @@ static int TokenCompareTo(byte[] a, byte[] b) { /// First /// Second /// true if same, false otherwise - public static bool TokenEquals(PublicKeyToken a, PublicKeyToken b) { - return TokenCompareTo(a, b) == 0; - } + public static bool TokenEquals(PublicKeyToken a, PublicKeyToken b) => TokenCompareTo(a, b) == 0; /// /// Gets the public key token hash code /// /// Public key or token /// The hash code - public static int GetHashCodeToken(PublicKeyBase a) { - return GetHashCode(ToPublicKeyToken(a)); - } + public static int GetHashCodeToken(PublicKeyBase a) => GetHashCode(ToPublicKeyToken(a)); /// /// Gets the public key token hash code @@ -159,7 +125,7 @@ public static int GetHashCodeToken(PublicKeyBase a) { /// Public key token /// The hash code public static int GetHashCode(PublicKeyToken a) { - if (a == null) + if (a is null) return 0; return Utils.GetHashCode(a.Data); } @@ -171,7 +137,7 @@ public static int GetHashCode(PublicKeyToken a) { /// A new instance or null if /// was null public static PublicKey CreatePublicKey(byte[] data) { - if (data == null) + if (data is null) return null; return new PublicKey(data); } @@ -183,7 +149,7 @@ public static PublicKey CreatePublicKey(byte[] data) { /// A new instance or null if /// was null public static PublicKeyToken CreatePublicKeyToken(byte[] data) { - if (data == null) + if (data is null) return null; return new PublicKeyToken(data); } @@ -194,7 +160,7 @@ public static PublicKeyToken CreatePublicKeyToken(byte[] data) { /// The instance or null /// Raw public key / public key token data or null public static byte[] GetRawData(PublicKeyBase pkb) { - if (pkb == null) + if (pkb is null) return null; return pkb.Data; } @@ -202,7 +168,7 @@ public static byte[] GetRawData(PublicKeyBase pkb) { /// public override string ToString() { var d = Data; - if (d == null || d.Length == 0) + if (d is null || d.Length == 0) return "null"; return Utils.ToHex(d, false); } diff --git a/src/DotNet/PublicKeyToken.cs b/src/DotNet/PublicKeyToken.cs index 3f5183576..b6fc50591 100644 --- a/src/DotNet/PublicKeyToken.cs +++ b/src/DotNet/PublicKeyToken.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// Represents a public key token /// @@ -8,14 +8,12 @@ public sealed class PublicKeyToken : PublicKeyBase { /// /// Gets the /// - public override PublicKeyToken Token { - get { return this; } - } + public override PublicKeyToken Token => this; - /// - public PublicKeyToken() - : base() { - } + /// + /// Constructor + /// + public PublicKeyToken() : base((byte[])null) { } /// public PublicKeyToken(byte[] data) @@ -32,14 +30,12 @@ public override bool Equals(object obj) { if ((object)this == obj) return true; var other = obj as PublicKeyToken; - if (other == null) + if (other is null) return false; return Utils.Equals(Data, other.Data); } /// - public override int GetHashCode() { - return Utils.GetHashCode(Data); - } + public override int GetHashCode() => Utils.GetHashCode(Data); } } diff --git a/src/DotNet/RecursionCounter.cs b/src/DotNet/RecursionCounter.cs index 3d428aa0a..a5ddff163 100644 --- a/src/DotNet/RecursionCounter.cs +++ b/src/DotNet/RecursionCounter.cs @@ -1,12 +1,12 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.DotNet { /// /// Recursion counter /// - public struct RecursionCounter { + struct RecursionCounter { /// /// Max recursion count. If this is reached, we won't continue, and will use a default value. /// @@ -16,9 +16,7 @@ public struct RecursionCounter { /// /// Gets the recursion counter /// - public int Counter { - get { return counter; } - } + public int Counter => counter; /// /// Increments if it's not too high. ALL instance methods @@ -47,8 +45,6 @@ public void Decrement() { } /// - public override string ToString() { - return counter.ToString(); - } + public override string ToString() => counter.ToString(); } } diff --git a/src/DotNet/ReflectionExtensions.cs b/src/DotNet/ReflectionExtensions.cs index 54c2ca9e3..5e29bac64 100644 --- a/src/DotNet/ReflectionExtensions.cs +++ b/src/DotNet/ReflectionExtensions.cs @@ -1,23 +1,40 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; +using System.Diagnostics; using System.Reflection; +using System.Text; namespace dnlib.DotNet { /// /// Extension methods for reflection types, methods, fields /// static class ReflectionExtensions { + public static void GetTypeNamespaceAndName_TypeDefOrRef(this Type type, out string @namespace, out string name) { + Debug.Assert(type.IsTypeDef()); + name = Unescape(type.Name) ?? string.Empty; + if (!type.IsNested) + @namespace = type.Namespace ?? string.Empty; + else { + var declTypeFullName = Unescape(type.DeclaringType.FullName); + var typeFullName = Unescape(type.FullName); + if (declTypeFullName.Length + 1 + name.Length == typeFullName.Length) + @namespace = string.Empty; + else + @namespace = typeFullName.Substring(declTypeFullName.Length + 1, typeFullName.Length - declTypeFullName.Length - 1 - name.Length - 1); + } + } + /// /// Checks whether it's a /// /// The type public static bool IsSZArray(this Type self) { - if (self == null || !self.IsArray) + if (self is null || !self.IsArray) return false; var prop = self.GetType().GetProperty("IsSzArray", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - if (prop != null) - return (bool)prop.GetValue(self, new object[0]); + if (prop is not null) + return (bool)prop.GetValue(self, Array2.Empty()); return (self.Name ?? string.Empty).EndsWith("[]"); } @@ -27,7 +44,7 @@ public static bool IsSZArray(this Type self) { /// The type /// The type's element type public static ElementType GetElementType2(this Type a) { - if (a == null) + if (a is null) return ElementType.End; // Any invalid one is good enough if (a.IsArray) return IsSZArray(a) ? ElementType.SZArray : ElementType.Array; @@ -36,7 +53,7 @@ public static ElementType GetElementType2(this Type a) { if (a.IsPointer) return ElementType.Ptr; if (a.IsGenericParameter) - return a.DeclaringMethod == null ? ElementType.Var : ElementType.MVar; + return a.DeclaringMethod is null ? ElementType.Var : ElementType.MVar; if (a.IsGenericType && !a.IsGenericTypeDefinition) return ElementType.GenericInst; @@ -62,14 +79,21 @@ public static ElementType GetElementType2(this Type a) { return a.IsValueType ? ElementType.ValueType : ElementType.Class; } + /// + /// Returns true if is a generic type, but + /// not a generic type definition, i.e., a TypeSpec. + /// + /// The type + public static bool IsGenericButNotGenericTypeDefinition(this Type type) => + type is not null && !type.IsGenericTypeDefinition && type.IsGenericType; + /// /// Returns true if is a generic method, but /// not a generic method definition, i.e., a MethodSpec. /// /// The method - public static bool IsGenericButNotGenericMethodDefinition(this MethodBase mb) { - return mb != null && !mb.IsGenericMethodDefinition && mb.IsGenericMethod; - } + public static bool IsGenericButNotGenericMethodDefinition(this MethodBase mb) => + mb is not null && !mb.IsGenericMethodDefinition && mb.IsGenericMethod; /// /// Checks whether a parameter/prop/event type should be treated as if it is really a @@ -78,23 +102,45 @@ public static bool IsGenericButNotGenericMethodDefinition(this MethodBase mb) { /// a generic type def. This seems to happen only if the parameter type is exactly the same /// type as the declaring type, eg. a method similar to: MyType<!0> MyType::SomeMethod(). /// - /// Declaring type of method/event/property - /// Parameter/property/event type - internal static bool MustTreatTypeAsGenericInstType(this Type declaringType, Type t) { - return declaringType != null && - declaringType.IsGenericTypeDefinition && - t == declaringType; - } + /// Declaring type of field/method/event/property + /// Field/parameter/property/event type + internal static bool MustTreatTypeAsGenericInstType(this Type declaringType, Type t) => + declaringType is not null && declaringType.IsGenericTypeDefinition && t == declaringType; /// /// Checks whether is a type definition and not a type spec /// (eg. pointer or generic type instantiation) /// /// this - public static bool IsTypeDef(this Type type) { - return type != null && - !type.HasElementType && - (!type.IsGenericType || type.IsGenericTypeDefinition); + public static bool IsTypeDef(this Type type) => + type is not null && !type.HasElementType && (!type.IsGenericType || type.IsGenericTypeDefinition); + + internal static string Unescape(string name) { + if (string.IsNullOrEmpty(name) || name.IndexOf('\\') < 0) + return name; + var sb = new StringBuilder(name.Length); + for (int i = 0; i < name.Length; i++) { + if (name[i] == '\\' && i < name.Length - 1 && IsReservedTypeNameChar(name[i + 1])) + sb.Append(name[++i]); + else + sb.Append(name[i]); + } + return sb.ToString(); + } + + static bool IsReservedTypeNameChar(char c) { + switch (c) { + case ',': + case '+': + case '&': + case '*': + case '[': + case ']': + case '\\': + return true; + default: + return false; + } } } } diff --git a/src/DotNet/ResolveException.cs b/src/DotNet/ResolveException.cs index 34d1c6bde..dc6f29433 100644 --- a/src/DotNet/ResolveException.cs +++ b/src/DotNet/ResolveException.cs @@ -1,6 +1,7 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; +using System.Runtime.Serialization; namespace dnlib.DotNet { /// @@ -30,6 +31,15 @@ public ResolveException(string message) public ResolveException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Constructor + /// + /// + /// + protected ResolveException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } } /// @@ -59,6 +69,15 @@ public AssemblyResolveException(string message) public AssemblyResolveException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Constructor + /// + /// + /// + protected AssemblyResolveException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } } /// @@ -88,6 +107,15 @@ public TypeResolveException(string message) public TypeResolveException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Constructor + /// + /// + /// + protected TypeResolveException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } } /// @@ -117,5 +145,14 @@ public MemberRefResolveException(string message) public MemberRefResolveException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Constructor + /// + /// + /// + protected MemberRefResolveException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } } } diff --git a/src/DotNet/Resolver.cs b/src/DotNet/Resolver.cs index 776d1fe45..067ff0672 100644 --- a/src/DotNet/Resolver.cs +++ b/src/DotNet/Resolver.cs @@ -2,9 +2,8 @@ using System; using System.Collections.Generic; -using dnlib.Threading; -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// Resolves types, methods, fields /// @@ -17,8 +16,8 @@ public sealed class Resolver : IResolver { /// by default. /// public bool ProjectWinMDRefs { - get { return projectWinMDRefs; } - set { projectWinMDRefs = value; } + get => projectWinMDRefs; + set => projectWinMDRefs = value; } bool projectWinMDRefs = true; @@ -26,68 +25,67 @@ public bool ProjectWinMDRefs { /// Constructor /// /// The assembly resolver - public Resolver(IAssemblyResolver assemblyResolver) { - if (assemblyResolver == null) - throw new ArgumentNullException("assemblyResolver"); - this.assemblyResolver = assemblyResolver; - } + public Resolver(IAssemblyResolver assemblyResolver) => + this.assemblyResolver = assemblyResolver ?? throw new ArgumentNullException(nameof(assemblyResolver)); /// public TypeDef Resolve(TypeRef typeRef, ModuleDef sourceModule) { - if (typeRef == null) + if (typeRef is null) return null; if (ProjectWinMDRefs) typeRef = WinMDHelpers.ToCLR(typeRef.Module ?? sourceModule, typeRef) ?? typeRef; var nonNestedTypeRef = TypeRef.GetNonNestedTypeRef(typeRef); - if (nonNestedTypeRef == null) + if (nonNestedTypeRef is null) return null; var nonNestedResolutionScope = nonNestedTypeRef.ResolutionScope; var nonNestedModule = nonNestedTypeRef.Module; - var asmRef = nonNestedResolutionScope as AssemblyRef; - if (asmRef != null) { + if (nonNestedResolutionScope is AssemblyRef asmRef) { var asm = assemblyResolver.Resolve(asmRef, sourceModule ?? nonNestedModule); - return asm == null ? null : asm.Find(typeRef) ?? ResolveExportedType(asm.Modules, typeRef, sourceModule); + return asm is null ? null : asm.Find(typeRef) ?? ResolveExportedType(asm.Modules, typeRef, sourceModule); } - var moduleDef = nonNestedResolutionScope as ModuleDef; - if (moduleDef != null) - return moduleDef.Find(typeRef) ?? - ResolveExportedType(new ModuleDef[] { moduleDef }, typeRef, sourceModule); + if (nonNestedResolutionScope is ModuleDef moduleDef) + return moduleDef.Find(typeRef) ?? ResolveExportedType(new ModuleDef[] { moduleDef }, typeRef, sourceModule); - var moduleRef = nonNestedResolutionScope as ModuleRef; - if (moduleRef != null) { - if (nonNestedModule == null) + if (nonNestedResolutionScope is ModuleRef moduleRef) { + if (nonNestedModule is null) return null; if (new SigComparer().Equals(moduleRef, nonNestedModule)) return nonNestedModule.Find(typeRef) ?? ResolveExportedType(new ModuleDef[] { nonNestedModule }, typeRef, sourceModule); var nonNestedAssembly = nonNestedModule.Assembly; - if (nonNestedAssembly == null) + if (nonNestedAssembly is null) return null; var resolvedModule = nonNestedAssembly.FindModule(moduleRef.Name); - return resolvedModule == null ? null : resolvedModule.Find(typeRef) ?? + return resolvedModule is null ? null : resolvedModule.Find(typeRef) ?? ResolveExportedType(new ModuleDef[] { resolvedModule }, typeRef, sourceModule); } + if (nonNestedResolutionScope is null) { + // ECMA II.22.38 states that in this case we should check ExportedTypes only. + // The CLR however checks both TypeDefs and ExportedTypes, with TypeDefs taking precedence. + return nonNestedModule.Find(typeRef) ?? ResolveExportedType(new ModuleDef[] { nonNestedModule }, typeRef, sourceModule); + } + return null; } TypeDef ResolveExportedType(IList modules, TypeRef typeRef, ModuleDef sourceModule) { for (int i = 0; i < 30; i++) { var exportedType = FindExportedType(modules, typeRef); - if (exportedType == null) + if (exportedType is null) return null; var asmResolver = modules[0].Context.AssemblyResolver; var etAsm = asmResolver.Resolve(exportedType.DefinitionAssembly, sourceModule ?? typeRef.Module); - if (etAsm == null) + if (etAsm is null) return null; var td = etAsm.Find(typeRef); - if (td != null) + if (td is not null) return td; modules = etAsm.Modules; @@ -97,10 +95,15 @@ TypeDef ResolveExportedType(IList modules, TypeRef typeRef, ModuleDef } static ExportedType FindExportedType(IList modules, TypeRef typeRef) { - if (typeRef == null) + if (typeRef is null) return null; - foreach (var module in modules.GetSafeEnumerable()) { - foreach (var exportedType in module.ExportedTypes.GetSafeEnumerable()) { + int count = modules.Count; + for (int i = 0; i < count; i++) { + var module = modules[i]; + var exportedTypes = module.ExportedTypes; + int count2 = exportedTypes.Count; + for (int j = 0; j < count2; j++) { + var exportedType = exportedTypes[j]; if (new SigComparer(SigComparerOptions.DontCompareTypeScope).Equals(exportedType, typeRef)) return exportedType; } @@ -110,55 +113,48 @@ static ExportedType FindExportedType(IList modules, TypeRef typeRef) /// public IMemberForwarded Resolve(MemberRef memberRef) { - if (memberRef == null) + if (memberRef is null) return null; if (ProjectWinMDRefs) memberRef = WinMDHelpers.ToCLR(memberRef.Module, memberRef) ?? memberRef; var parent = memberRef.Class; - var method = parent as MethodDef; - if (method != null) + if (parent is MethodDef method) return method; - var declaringType = GetDeclaringType(memberRef, parent); - return declaringType == null ? null : declaringType.Resolve(memberRef); + return GetDeclaringType(memberRef, parent)?.Resolve(memberRef); } TypeDef GetDeclaringType(MemberRef memberRef, IMemberRefParent parent) { - if (memberRef == null || parent == null) + if (memberRef is null || parent is null) return null; - var ts = parent as TypeSpec; - if (ts != null) + if (parent is TypeSpec ts) parent = ts.ScopeType; - var declaringTypeDef = parent as TypeDef; - if (declaringTypeDef != null) + if (parent is TypeDef declaringTypeDef) return declaringTypeDef; - var declaringTypeRef = parent as TypeRef; - if (declaringTypeRef != null) + if (parent is TypeRef declaringTypeRef) return Resolve(declaringTypeRef, memberRef.Module); // A module ref is used to reference the global type of a module in the same // assembly as the current module. - var moduleRef = parent as ModuleRef; - if (moduleRef != null) { + if (parent is ModuleRef moduleRef) { var module = memberRef.Module; - if (module == null) + if (module is null) return null; TypeDef globalType = null; if (new SigComparer().Equals(module, moduleRef)) globalType = module.GlobalType; var modAsm = module.Assembly; - if (globalType == null && modAsm != null) { + if (globalType is null && modAsm is not null) { var moduleDef = modAsm.FindModule(moduleRef.Name); - if (moduleDef != null) + if (moduleDef is not null) globalType = moduleDef.GlobalType; } return globalType; } - var method = parent as MethodDef; - if (method != null) + if (parent is MethodDef method) return method.DeclaringType; return null; diff --git a/src/DotNet/Resource.cs b/src/DotNet/Resource.cs index 7ea735132..58bb2e11a 100644 --- a/src/DotNet/Resource.cs +++ b/src/DotNet/Resource.cs @@ -1,10 +1,11 @@ // dnlib: See LICENSE.txt for more info -using System; -using System.IO; +using System; +using System.Collections.Generic; +using System.Threading; using dnlib.IO; using dnlib.DotNet.MD; -using dnlib.Threading; +using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// @@ -30,45 +31,43 @@ public enum ResourceType { /// /// Resource base class /// - public abstract class Resource : IDisposable, IMDTokenProvider { - uint rid; - uint? offset; + public abstract class Resource : IMDTokenProvider, IHasCustomAttribute, IHasCustomDebugInformation { + private protected uint rid; + private protected uint? offset; UTF8String name; ManifestResourceAttributes flags; /// - public MDToken MDToken { - get { return new MDToken(Table.ManifestResource, rid); } - } + public MDToken MDToken => new MDToken(Table.ManifestResource, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// /// Gets/sets the offset of the resource /// public uint? Offset { - get { return offset; } - set { offset = value; } + get => offset; + set => offset = value; } /// /// Gets/sets the name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// /// Gets/sets the flags /// public ManifestResourceAttributes Attributes { - get { return flags; } - set { flags = value; } + get => flags; + set => flags = value; } /// @@ -80,213 +79,176 @@ public ManifestResourceAttributes Attributes { /// Gets/sets the visibility /// public ManifestResourceAttributes Visibility { - get { return flags & ManifestResourceAttributes.VisibilityMask; } - set { flags = (flags & ~ManifestResourceAttributes.VisibilityMask) | (value & ManifestResourceAttributes.VisibilityMask); } + get => flags & ManifestResourceAttributes.VisibilityMask; + set => flags = (flags & ~ManifestResourceAttributes.VisibilityMask) | (value & ManifestResourceAttributes.VisibilityMask); } /// /// true if is set /// - public bool IsPublic { - get { return (flags & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Public; } - } + public bool IsPublic => (flags & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Public; /// /// true if is set /// - public bool IsPrivate { - get { return (flags & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Private; } - } - - /// - /// Constructor - /// - /// Name - /// flags - protected Resource(UTF8String name, ManifestResourceAttributes flags) { - this.name = name; - this.flags = flags; - } + public bool IsPrivate => (flags & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Private; /// - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } + public int HasCustomAttributeTag => 18; /// - /// Dispose method + /// Gets all custom attributes /// - /// true if called by - protected virtual void Dispose(bool disposing) { + public CustomAttributeCollection CustomAttributes { + get { + if (customAttributes is null) + InitializeCustomAttributes(); + return customAttributes; + } } - } + /// + protected CustomAttributeCollection customAttributes; + /// Initializes + protected virtual void InitializeCustomAttributes() => + Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - /// - /// A resource that is embedded in a .NET module. This is the most common type of resource. - /// - public sealed class EmbeddedResource : Resource { - IImageStream dataStream; -#if THREAD_SAFE - readonly Lock theLock = Lock.Create(); -#endif + /// + public bool HasCustomAttributes => CustomAttributes.Count > 0; /// - public override ResourceType ResourceType { - get { return ResourceType.Embedded; } - } + public int HasCustomDebugInformationTag => 18; /// - /// Gets/sets the resource data. It's never null. + /// Gets all custom debug infos /// - public IImageStream Data { + public IList CustomDebugInfos { get { -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif - return dataStream; -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif - } - set { - if (value == null) - throw new ArgumentNullException("value"); -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - if (value == dataStream) - return; - if (dataStream != null) - dataStream.Dispose(); - dataStream = value; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; /// /// Constructor /// - /// Name of resource - /// Resource data - public EmbeddedResource(UTF8String name, byte[] data) - : this(name, data, ManifestResourceAttributes.Private) { + /// Name + /// flags + protected Resource(UTF8String name, ManifestResourceAttributes flags) { + this.name = name; + this.flags = flags; } + } + + /// + /// A resource that is embedded in a .NET module. This is the most common type of resource. + /// + public class EmbeddedResource : Resource { + readonly DataReaderFactory dataReaderFactory; + readonly uint resourceStartOffset; + readonly uint resourceLength; /// - /// Constructor + /// Gets the length of the data /// - /// Name of resource - /// Resource data - /// Resource flags - public EmbeddedResource(UTF8String name, byte[] data, ManifestResourceAttributes flags) - : this(name, new MemoryImageStream(0, data, 0, data.Length), flags) { - } + public uint Length => resourceLength; + + /// + public override ResourceType ResourceType => ResourceType.Embedded; + /// /// Constructor /// /// Name of resource - /// Resource data - public EmbeddedResource(UTF8String name, IImageStream dataStream) - : this(name, dataStream, ManifestResourceAttributes.Private) { + /// Resource data + /// Resource flags + public EmbeddedResource(UTF8String name, byte[] data, ManifestResourceAttributes flags = ManifestResourceAttributes.Private) + : this(name, ByteArrayDataReaderFactory.Create(data, filename: null), 0, (uint)data.Length, flags) { } /// /// Constructor /// /// Name of resource - /// Resource data + /// Data reader factory + /// Offset of resource data + /// Length of resource data /// Resource flags - public EmbeddedResource(UTF8String name, IImageStream dataStream, ManifestResourceAttributes flags) + public EmbeddedResource(UTF8String name, DataReaderFactory dataReaderFactory, uint offset, uint length, ManifestResourceAttributes flags = ManifestResourceAttributes.Private) : base(name, flags) { - if (dataStream == null) - throw new ArgumentNullException("dataStream"); - this.dataStream = dataStream; + this.dataReaderFactory = dataReaderFactory ?? throw new ArgumentNullException(nameof(dataReaderFactory)); + resourceStartOffset = offset; + resourceLength = length; } /// - /// Creates a new resource stream that can access the same data as the original - /// Stream. Note that the data is shared between these streams. + /// Gets a data reader that can access the resource /// - /// A new instance - public IImageStream GetClonedResourceStream() { -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif - return dataStream.Clone(); -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif - } + /// + public DataReader CreateReader() => dataReaderFactory.CreateReader(resourceStartOffset, resourceLength); - /// - /// Gets the resource data as a - /// - /// A stream - public Stream GetResourceStream() { - return GetClonedResourceStream().CreateStream(true); - } + /// + public override string ToString() => $"{UTF8String.ToSystemStringOrEmpty(Name)} - size: {(resourceLength)}"; + } - /// - /// Gets the resource data as a byte array - /// - /// The resource data - public byte[] GetResourceData() { -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - return dataStream.ReadAllBytes(); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } + sealed class EmbeddedResourceMD : EmbeddedResource, IMDTokenProviderMD { + /// The module where this instance is located + readonly ModuleDefMD readerModule; + + readonly uint origRid; /// - protected override void Dispose(bool disposing) { - if (!disposing) - return; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - if (dataStream != null) - dataStream.Dispose(); - dataStream = null; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - base.Dispose(disposing); + public uint OrigRid => origRid; + + /// + protected override void InitializeCustomAttributes() { + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.ManifestResource, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); + Interlocked.CompareExchange(ref customAttributes, tmp, null); } /// - public override string ToString() { - var ds = dataStream; - return string.Format("{0} - size: {1}", UTF8String.ToSystemStringOrEmpty(Name), ds == null ? 0 : ds.Length); + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + + public EmbeddedResourceMD(ModuleDefMD readerModule, ManifestResource mr, byte[] data) + : this(readerModule, mr, ByteArrayDataReaderFactory.Create(data, filename: null), 0, (uint)data.Length) { + } + + public EmbeddedResourceMD(ModuleDefMD readerModule, ManifestResource mr, DataReaderFactory dataReaderFactory, uint offset, uint length) + : base(mr.Name, dataReaderFactory, offset, length, mr.Flags) { + this.readerModule = readerModule; + origRid = rid = mr.Rid; + this.offset = mr.Offset; } } /// /// A reference to a resource in another assembly /// - public sealed class AssemblyLinkedResource : Resource { + public class AssemblyLinkedResource : Resource { AssemblyRef asmRef; /// - public override ResourceType ResourceType { - get { return ResourceType.AssemblyLinked; } - } + public override ResourceType ResourceType => ResourceType.AssemblyLinked; /// /// Gets/sets the assembly reference /// public AssemblyRef Assembly { - get { return asmRef; } - set { - if (value == null) - throw new ArgumentNullException("value"); - asmRef = value; - } + get => asmRef; + set => asmRef = value ?? throw new ArgumentNullException(nameof(value)); } /// @@ -296,55 +258,71 @@ public AssemblyRef Assembly { /// Assembly reference /// Resource flags public AssemblyLinkedResource(UTF8String name, AssemblyRef asmRef, ManifestResourceAttributes flags) - : base(name, flags) { - if (asmRef == null) - throw new ArgumentNullException("asmRef"); - this.asmRef = asmRef; + : base(name, flags) => this.asmRef = asmRef ?? throw new ArgumentNullException(nameof(asmRef)); + + /// + public override string ToString() => $"{UTF8String.ToSystemStringOrEmpty(Name)} - assembly: {asmRef.FullName}"; + } + + sealed class AssemblyLinkedResourceMD : AssemblyLinkedResource, IMDTokenProviderMD { + /// The module where this instance is located + readonly ModuleDefMD readerModule; + + readonly uint origRid; + + /// + public uint OrigRid => origRid; + + /// + protected override void InitializeCustomAttributes() { + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.ManifestResource, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); + Interlocked.CompareExchange(ref customAttributes, tmp, null); } /// - public override string ToString() { - return string.Format("{0} - assembly: {1}", UTF8String.ToSystemStringOrEmpty(Name), asmRef.FullName); + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + + public AssemblyLinkedResourceMD(ModuleDefMD readerModule, ManifestResource mr, AssemblyRef asmRef) : base(mr.Name, asmRef, mr.Flags) { + this.readerModule = readerModule; + origRid = rid = mr.Rid; + offset = mr.Offset; } } /// /// A resource that is stored in a file on disk /// - public sealed class LinkedResource : Resource { + public class LinkedResource : Resource { FileDef file; /// - public override ResourceType ResourceType { - get { return ResourceType.Linked; } - } + public override ResourceType ResourceType => ResourceType.Linked; /// /// Gets/sets the file /// public FileDef File { - get { return file; } - set { - if (value == null) - throw new ArgumentNullException("value"); - file = value; - } + get => file; + set => file = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Gets/sets the hash /// public byte[] Hash { - get { return file.HashValue; } - set { file.HashValue = value; } + get => file.HashValue; + set => file.HashValue = value; } /// /// Gets/sets the file name /// - public UTF8String FileName { - get { return file == null ? UTF8String.Empty : file.Name; } - } + public UTF8String FileName => file is null ? UTF8String.Empty : file.Name; /// /// Constructor @@ -353,13 +331,39 @@ public UTF8String FileName { /// The file /// Resource flags public LinkedResource(UTF8String name, FileDef file, ManifestResourceAttributes flags) - : base(name, flags) { - this.file = file; + : base(name, flags) => this.file = file; + + /// + public override string ToString() => $"{UTF8String.ToSystemStringOrEmpty(Name)} - file: {UTF8String.ToSystemStringOrEmpty(FileName)}"; + } + + sealed class LinkedResourceMD : LinkedResource, IMDTokenProviderMD { + /// The module where this instance is located + readonly ModuleDefMD readerModule; + + readonly uint origRid; + + /// + public uint OrigRid => origRid; + + /// + protected override void InitializeCustomAttributes() { + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.ManifestResource, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); + Interlocked.CompareExchange(ref customAttributes, tmp, null); } /// - public override string ToString() { - return string.Format("{0} - file: {1}", UTF8String.ToSystemStringOrEmpty(Name), UTF8String.ToSystemStringOrEmpty(FileName)); + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + + public LinkedResourceMD(ModuleDefMD readerModule, ManifestResource mr, FileDef file) : base(mr.Name, file, mr.Flags) { + this.readerModule = readerModule; + origRid = rid = mr.Rid; + offset = mr.Offset; } } } diff --git a/src/DotNet/ResourceCollection.cs b/src/DotNet/ResourceCollection.cs index 676f17115..debc42b03 100644 --- a/src/DotNet/ResourceCollection.cs +++ b/src/DotNet/ResourceCollection.cs @@ -1,13 +1,13 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using dnlib.Utils; -using dnlib.Threading; +using System; namespace dnlib.DotNet { /// /// A collection of s /// - public class ResourceCollection : LazyList { + public class ResourceCollection : LazyList { /// /// Default constructor /// @@ -28,7 +28,7 @@ public ResourceCollection(IListListener listener) /// Initial length of the list /// Context passed to /// Delegate instance that returns original values - public ResourceCollection(int length, object context, MFunc readOriginalValue) + public ResourceCollection(int length, object context, Func readOriginalValue) : base(length, context, readOriginalValue) { } @@ -39,9 +39,9 @@ public ResourceCollection(int length, object context, MFuncThe index of the or -1 if none was found public int IndexOf(UTF8String name) { int i = -1; - foreach (var resource in this.GetSafeEnumerable()) { + foreach (var resource in this) { i++; - if (resource != null && resource.Name == name) + if (resource is not null && resource.Name == name) return i; } return -1; @@ -54,9 +54,9 @@ public int IndexOf(UTF8String name) { /// The index of the or -1 if none was found public int IndexOfEmbeddedResource(UTF8String name) { int i = -1; - foreach (var resource in this.GetSafeEnumerable()) { + foreach (var resource in this) { i++; - if (resource != null && + if (resource is not null && resource.ResourceType == ResourceType.Embedded && resource.Name == name) return i; @@ -71,9 +71,9 @@ public int IndexOfEmbeddedResource(UTF8String name) { /// The index of the or -1 if none was found public int IndexOfAssemblyLinkedResource(UTF8String name) { int i = -1; - foreach (var resource in this.GetSafeEnumerable()) { + foreach (var resource in this) { i++; - if (resource != null && + if (resource is not null && resource.ResourceType == ResourceType.AssemblyLinked && resource.Name == name) return i; @@ -88,9 +88,9 @@ public int IndexOfAssemblyLinkedResource(UTF8String name) { /// The index of the or -1 if none was found public int IndexOfLinkedResource(UTF8String name) { int i = -1; - foreach (var resource in this.GetSafeEnumerable()) { + foreach (var resource in this) { i++; - if (resource != null && + if (resource is not null && resource.ResourceType == ResourceType.Linked && resource.Name == name) return i; @@ -104,8 +104,8 @@ public int IndexOfLinkedResource(UTF8String name) { /// Name of resource /// The or null if none was found public Resource Find(UTF8String name) { - foreach (var resource in this.GetSafeEnumerable()) { - if (resource != null && resource.Name == name) + foreach (var resource in this) { + if (resource is not null && resource.Name == name) return resource; } return null; @@ -117,8 +117,8 @@ public Resource Find(UTF8String name) { /// Name of resource /// The or null if none was found public EmbeddedResource FindEmbeddedResource(UTF8String name) { - foreach (var resource in this.GetSafeEnumerable()) { - if (resource != null && + foreach (var resource in this) { + if (resource is not null && resource.ResourceType == ResourceType.Embedded && resource.Name == name) return (EmbeddedResource)resource; @@ -132,8 +132,8 @@ public EmbeddedResource FindEmbeddedResource(UTF8String name) { /// Name of resource /// The or null if none was found public AssemblyLinkedResource FindAssemblyLinkedResource(UTF8String name) { - foreach (var resource in this.GetSafeEnumerable()) { - if (resource != null && + foreach (var resource in this) { + if (resource is not null && resource.ResourceType == ResourceType.AssemblyLinked && resource.Name == name) return (AssemblyLinkedResource)resource; @@ -147,8 +147,8 @@ public AssemblyLinkedResource FindAssemblyLinkedResource(UTF8String name) { /// Name of resource /// The or null if none was found public LinkedResource FindLinkedResource(UTF8String name) { - foreach (var resource in this.GetSafeEnumerable()) { - if (resource != null && + foreach (var resource in this) { + if (resource is not null && resource.ResourceType == ResourceType.Linked && resource.Name == name) return (LinkedResource)resource; diff --git a/src/DotNet/Resources/BuiltInResourceData.cs b/src/DotNet/Resources/BuiltInResourceData.cs index 45027b2ca..8bb1c82b5 100644 --- a/src/DotNet/Resources/BuiltInResourceData.cs +++ b/src/DotNet/Resources/BuiltInResourceData.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.IO; @@ -16,19 +16,15 @@ public sealed class BuiltInResourceData : IResourceData { /// /// Gets the data /// - public object Data { - get { return data; } - } + public object Data => data; /// - public ResourceTypeCode Code { - get { return code; } - } + public ResourceTypeCode Code => code; - /// + /// public FileOffset StartOffset { get; set; } - /// + /// public FileOffset EndOffset { get; set; } /// @@ -42,7 +38,7 @@ public BuiltInResourceData(ResourceTypeCode code, object data) { } /// - public void WriteData(BinaryWriter writer, IFormatter formatter) { + public void WriteData(ResourceBinaryWriter writer, IFormatter formatter) { switch (code) { case ResourceTypeCode.Null: break; @@ -104,7 +100,11 @@ public void WriteData(BinaryWriter writer, IFormatter formatter) { break; case ResourceTypeCode.DateTime: - writer.Write(((DateTime)data).ToBinary()); + var dateTime = (DateTime)data; + if (writer.FormatVersion == 1) + writer.Write(dateTime.Ticks); + else + writer.Write(dateTime.ToBinary()); break; case ResourceTypeCode.TimeSpan: @@ -113,6 +113,8 @@ public void WriteData(BinaryWriter writer, IFormatter formatter) { case ResourceTypeCode.ByteArray: case ResourceTypeCode.Stream: + if (writer.FormatVersion == 1) + throw new NotSupportedException($"{code} is not supported in format version 1 resources"); var ary = (byte[])data; writer.Write(ary.Length); writer.Write(ary); @@ -145,17 +147,17 @@ public override string ToString() { case ResourceTypeCode.Decimal: case ResourceTypeCode.DateTime: case ResourceTypeCode.TimeSpan: - return string.Format("{0}: '{1}'", code, data); + return $"{code}: '{data}'"; case ResourceTypeCode.ByteArray: case ResourceTypeCode.Stream: var ary = data as byte[]; - if (ary != null) - return string.Format("{0}: Length: {1}", code, ary.Length); - return string.Format("{0}: '{1}'", code, data); + if (ary is not null) + return $"{code}: Length: {ary.Length}"; + return $"{code}: '{data}'"; default: - return string.Format("{0}: '{1}'", code, data); + return $"{code}: '{data}'"; } } } diff --git a/src/DotNet/Resources/IResourceData.cs b/src/DotNet/Resources/IResourceData.cs index c9df95d20..8ec368bb4 100644 --- a/src/DotNet/Resources/IResourceData.cs +++ b/src/DotNet/Resources/IResourceData.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System.IO; using System.Runtime.Serialization; @@ -30,6 +30,6 @@ public interface IResourceData : IFileSection { /// /// Writer /// Formatter if needed by implementer - void WriteData(BinaryWriter writer, IFormatter formatter); + void WriteData(ResourceBinaryWriter writer, IFormatter formatter); } } diff --git a/src/DotNet/Resources/ResourceBinaryWriter.cs b/src/DotNet/Resources/ResourceBinaryWriter.cs new file mode 100644 index 000000000..09d7aef60 --- /dev/null +++ b/src/DotNet/Resources/ResourceBinaryWriter.cs @@ -0,0 +1,26 @@ +using System.IO; + +namespace dnlib.DotNet.Resources { + /// + /// Extension of for writing resource set elements + /// + public sealed class ResourceBinaryWriter : BinaryWriter { + /// + /// Format version of the resource set + /// + public int FormatVersion { get; internal set; } + + /// + /// Specifies the target reader type of the resource set + /// + public ResourceReaderType ReaderType { get; internal set; } + + internal ResourceBinaryWriter(Stream stream) : base(stream) { } + + /// + /// Writes a 7-bit encoded integer. + /// + /// The value to write + public new void Write7BitEncodedInt(int value) => base.Write7BitEncodedInt(value); + } +} diff --git a/src/DotNet/Resources/ResourceDataCreator.cs b/src/DotNet/Resources/ResourceDataFactory.cs similarity index 60% rename from src/DotNet/Resources/ResourceDataCreator.cs rename to src/DotNet/Resources/ResourceDataFactory.cs index 157f634d1..ef437702c 100644 --- a/src/DotNet/Resources/ResourceDataCreator.cs +++ b/src/DotNet/Resources/ResourceDataFactory.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; @@ -10,7 +10,7 @@ namespace dnlib.DotNet.Resources { /// /// Creates resource data /// - public class ResourceDataCreator { + public class ResourceDataFactory { readonly ModuleDef module; readonly ModuleDefMD moduleMD; readonly Dictionary dict = new Dictionary(StringComparer.Ordinal); @@ -19,217 +19,173 @@ public class ResourceDataCreator { /// /// Gets the owner module /// - protected ModuleDef Module { - get { return module; } - } + protected ModuleDef Module => module; /// /// Constructor /// /// Owner module - public ResourceDataCreator(ModuleDef module) { + public ResourceDataFactory(ModuleDef module) { this.module = module; - this.moduleMD = module as ModuleDefMD; + moduleMD = module as ModuleDefMD; } /// /// Gets number of user data types /// - public int Count { - get { return dict.Count; } - } + public int Count => dict.Count; /// /// Create null data /// /// - public BuiltInResourceData CreateNull() { - return new BuiltInResourceData(ResourceTypeCode.Null, null); - } + public BuiltInResourceData CreateNull() => new BuiltInResourceData(ResourceTypeCode.Null, null); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(string value) { - return new BuiltInResourceData(ResourceTypeCode.String, value); - } + public BuiltInResourceData Create(string value) => new BuiltInResourceData(ResourceTypeCode.String, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(bool value) { - return new BuiltInResourceData(ResourceTypeCode.Boolean, value); - } + public BuiltInResourceData Create(bool value) => new BuiltInResourceData(ResourceTypeCode.Boolean, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(char value) { - return new BuiltInResourceData(ResourceTypeCode.Char, value); - } + public BuiltInResourceData Create(char value) => new BuiltInResourceData(ResourceTypeCode.Char, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(byte value) { - return new BuiltInResourceData(ResourceTypeCode.Byte, value); - } + public BuiltInResourceData Create(byte value) => new BuiltInResourceData(ResourceTypeCode.Byte, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(sbyte value) { - return new BuiltInResourceData(ResourceTypeCode.SByte, value); - } + public BuiltInResourceData Create(sbyte value) => new BuiltInResourceData(ResourceTypeCode.SByte, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(short value) { - return new BuiltInResourceData(ResourceTypeCode.Int16, value); - } + public BuiltInResourceData Create(short value) => new BuiltInResourceData(ResourceTypeCode.Int16, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(ushort value) { - return new BuiltInResourceData(ResourceTypeCode.UInt16, value); - } + public BuiltInResourceData Create(ushort value) => new BuiltInResourceData(ResourceTypeCode.UInt16, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(int value) { - return new BuiltInResourceData(ResourceTypeCode.Int32, value); - } + public BuiltInResourceData Create(int value) => new BuiltInResourceData(ResourceTypeCode.Int32, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(uint value) { - return new BuiltInResourceData(ResourceTypeCode.UInt32, value); - } + public BuiltInResourceData Create(uint value) => new BuiltInResourceData(ResourceTypeCode.UInt32, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(long value) { - return new BuiltInResourceData(ResourceTypeCode.Int64, value); - } + public BuiltInResourceData Create(long value) => new BuiltInResourceData(ResourceTypeCode.Int64, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(ulong value) { - return new BuiltInResourceData(ResourceTypeCode.UInt64, value); - } + public BuiltInResourceData Create(ulong value) => new BuiltInResourceData(ResourceTypeCode.UInt64, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(float value) { - return new BuiltInResourceData(ResourceTypeCode.Single, value); - } + public BuiltInResourceData Create(float value) => new BuiltInResourceData(ResourceTypeCode.Single, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(double value) { - return new BuiltInResourceData(ResourceTypeCode.Double, value); - } + public BuiltInResourceData Create(double value) => new BuiltInResourceData(ResourceTypeCode.Double, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(decimal value) { - return new BuiltInResourceData(ResourceTypeCode.Decimal, value); - } + public BuiltInResourceData Create(decimal value) => new BuiltInResourceData(ResourceTypeCode.Decimal, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(DateTime value) { - return new BuiltInResourceData(ResourceTypeCode.DateTime, value); - } + public BuiltInResourceData Create(DateTime value) => new BuiltInResourceData(ResourceTypeCode.DateTime, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData Create(TimeSpan value) { - return new BuiltInResourceData(ResourceTypeCode.TimeSpan, value); - } + public BuiltInResourceData Create(TimeSpan value) => new BuiltInResourceData(ResourceTypeCode.TimeSpan, value); /// /// Creates array data /// /// Value /// - public BuiltInResourceData Create(byte[] value) { - return new BuiltInResourceData(ResourceTypeCode.ByteArray, value); - } + public BuiltInResourceData Create(byte[] value) => new BuiltInResourceData(ResourceTypeCode.ByteArray, value); /// /// Creates data /// /// Value /// - public BuiltInResourceData CreateStream(byte[] value) { - return new BuiltInResourceData(ResourceTypeCode.Stream, value); - } + public BuiltInResourceData CreateStream(byte[] value) => new BuiltInResourceData(ResourceTypeCode.Stream, value); /// /// Creates serialized data /// /// Serialized data + /// Format of the serialized data /// Type of serialized data /// - public BinaryResourceData CreateSerialized(byte[] value, UserResourceType type) { - return new BinaryResourceData(CreateUserResourceType(type.Name, true), value); - } + public BinaryResourceData CreateSerialized(byte[] value, SerializationFormat format, UserResourceType type) => new BinaryResourceData(CreateUserResourceType(type.Name, true), value, format); /// /// Creates serialized data /// /// Serialized data /// - public BinaryResourceData CreateSerialized(byte[] value) { - string assemblyName, typeName; - if (!GetSerializedTypeAndAssemblyName(value, out assemblyName, out typeName)) + public BinaryResourceData CreateBinaryFormatterSerialized(byte[] value) { + if (!GetSerializedTypeAndAssemblyName(value, out var assemblyName, out var typeName)) throw new ApplicationException("Could not get serialized type name"); - string fullName = string.Format("{0}, {1}", typeName, assemblyName); - return new BinaryResourceData(CreateUserResourceType(fullName), value); + string fullName = $"{typeName}, {assemblyName}"; + return new BinaryResourceData(CreateUserResourceType(fullName), value, SerializationFormat.BinaryFormatter); } sealed class MyBinder : SerializationBinder { @@ -238,19 +194,20 @@ public class OkException : Exception { public string TypeName { get; set; } } - public override Type BindToType(string assemblyName, string typeName) { + public override Type BindToType(string assemblyName, string typeName) => throw new OkException { AssemblyName = assemblyName, TypeName = typeName, }; - } } bool GetSerializedTypeAndAssemblyName(byte[] value, out string assemblyName, out string typeName) { try { var formatter = new BinaryFormatter(); formatter.Binder = new MyBinder(); +#pragma warning disable SYSLIB0011 // Type or member is obsolete formatter.Deserialize(new MemoryStream(value)); +#pragma warning restore SYSLIB0011 // Type or member is obsolete } catch (MyBinder.OkException ex) { assemblyName = ex.AssemblyName; @@ -265,14 +222,44 @@ bool GetSerializedTypeAndAssemblyName(byte[] value, out string assemblyName, out return false; } + /// + /// Creates a user type for a built-in resource type code. + /// Useful when writing V1 resources. + /// + /// The built-in resource type code or null if not supported + /// + public UserResourceType CreateBuiltinResourceType(ResourceTypeCode typeCode) { + string typeName = typeCode switch { + ResourceTypeCode.String => "System.String", + ResourceTypeCode.Boolean => "System.Boolean", + ResourceTypeCode.Char => "System.Char", + ResourceTypeCode.Byte => "System.Byte", + ResourceTypeCode.SByte => "System.SByte", + ResourceTypeCode.Int16 => "System.Int16", + ResourceTypeCode.UInt16 => "System.UInt16", + ResourceTypeCode.Int32 => "System.Int32", + ResourceTypeCode.UInt32 => "System.UInt32", + ResourceTypeCode.Int64 => "System.Int64", + ResourceTypeCode.UInt64 => "System.UInt64", + ResourceTypeCode.Single => "System.Single", + ResourceTypeCode.Double => "System.Double", + ResourceTypeCode.Decimal => "System.Decimal", + ResourceTypeCode.DateTime => "System.DateTime", + ResourceTypeCode.TimeSpan => "System.TimeSpan", + _ => null + }; + if (typeName is null) + return null; + + return CreateUserResourceType($"{typeName}, {module.CorLibTypes.AssemblyRef.FullName}", true); + } + /// /// Creates a user type. If the type already exists, the existing value is returned. /// /// Full name of type /// - public UserResourceType CreateUserResourceType(string fullName) { - return CreateUserResourceType(fullName, false); - } + public UserResourceType CreateUserResourceType(string fullName) => CreateUserResourceType(fullName, false); /// /// Creates a user type. If the type already exists, the existing value is returned. @@ -282,8 +269,7 @@ public UserResourceType CreateUserResourceType(string fullName) { /// type in an existing assembly reference /// UserResourceType CreateUserResourceType(string fullName, bool useFullName) { - UserResourceType type; - if (dict.TryGetValue(fullName, out type)) + if (dict.TryGetValue(fullName, out var type)) return type; var newFullName = useFullName ? fullName : GetRealTypeFullName(fullName); @@ -295,25 +281,24 @@ UserResourceType CreateUserResourceType(string fullName, bool useFullName) { string GetRealTypeFullName(string fullName) { var tr = TypeNameParser.ParseReflection(module, fullName, null); - if (tr == null) + if (tr is null) return fullName; var asmRef = tr.DefinitionAssembly; - if (asmRef == null) + if (asmRef is null) return fullName; var newFullName = fullName; string assemblyName = GetRealAssemblyName(asmRef); if (!string.IsNullOrEmpty(assemblyName)) - newFullName = string.Format("{0}, {1}", tr.ReflectionFullName, assemblyName); + newFullName = $"{tr.ReflectionFullName}, {assemblyName}"; return newFullName; } string GetRealAssemblyName(IAssembly asm) { string assemblyName = asm.FullName; - string newAsmName; - if (!asmNameToAsmFullName.TryGetValue(assemblyName, out newAsmName)) + if (!asmNameToAsmFullName.TryGetValue(assemblyName, out var newAsmName)) asmNameToAsmFullName[assemblyName] = newAsmName = TryGetRealAssemblyName(asm); return newAsmName; } @@ -323,9 +308,9 @@ string TryGetRealAssemblyName(IAssembly asm) { if (simpleName == module.CorLibTypes.AssemblyRef.Name) return module.CorLibTypes.AssemblyRef.FullName; - if (moduleMD != null) { + if (moduleMD is not null) { var asmRef = moduleMD.GetAssemblyRef(simpleName); - if (asmRef != null) + if (asmRef is not null) return asmRef.FullName; } @@ -339,9 +324,7 @@ string TryGetRealAssemblyName(IAssembly asm) { /// /// Simple name of assembly /// - protected virtual string GetAssemblyFullName(string simpleName) { - return null; - } + protected virtual string GetAssemblyFullName(string simpleName) => null; /// /// Gets all types sorted by diff --git a/src/DotNet/Resources/ResourceElement.cs b/src/DotNet/Resources/ResourceElement.cs index 53b3307a3..4a4f1dc98 100644 --- a/src/DotNet/Resources/ResourceElement.cs +++ b/src/DotNet/Resources/ResourceElement.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info namespace dnlib.DotNet.Resources { /// @@ -16,8 +16,6 @@ public sealed class ResourceElement { public IResourceData ResourceData { get; set; } /// - public override string ToString() { - return string.Format("N: {0}, V: {1}", Name, ResourceData); - } + public override string ToString() => $"N: {Name}, V: {ResourceData}"; } } diff --git a/src/DotNet/Resources/ResourceElementSet.cs b/src/DotNet/Resources/ResourceElementSet.cs index d90573344..ebb8da4a7 100644 --- a/src/DotNet/Resources/ResourceElementSet.cs +++ b/src/DotNet/Resources/ResourceElementSet.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; @@ -8,28 +8,108 @@ namespace dnlib.DotNet.Resources { /// Resource element set /// public sealed class ResourceElementSet { + internal const string DeserializingResourceReaderTypeNameRegex = @"^System\.Resources\.Extensions\.DeserializingResourceReader,\s*System\.Resources\.Extensions"; + internal const string ResourceReaderTypeNameRegex = @"^System\.Resources\.ResourceReader,\s*mscorlib"; + readonly Dictionary dict = new Dictionary(StringComparer.Ordinal); /// - /// Gets the number of elements in the set + /// The ResourceReader type name used by this set + /// + public string ResourceReaderTypeName { get; } + + /// + /// The ResourceSet type name used by this set /// - public int Count { - get { return dict.Count; } + public string ResourceSetTypeName { get; } + + /// + /// The type of resource reader used to read this set + /// + public ResourceReaderType ReaderType { get; } + + /// + /// Format version of the resource set + /// + public int FormatVersion { get; internal set; } + + /// + /// Creates a new instance + /// + /// The ResourceReader type name to use + /// The ResourceSet type name to use + /// + internal ResourceElementSet(string resourceReaderTypeName, string resourceSetTypeName, ResourceReaderType readerType) { + ResourceReaderTypeName = resourceReaderTypeName; + ResourceSetTypeName = resourceSetTypeName; + ReaderType = readerType; } + /// + /// Gets the number of elements in the set + /// + public int Count => dict.Count; + /// /// Gets all resource elements /// - public IEnumerable ResourceElements { - get { return dict.Values; } - } + public IEnumerable ResourceElements => dict.Values; /// /// Adds a new resource to the set, overwriting any existing resource /// /// - public void Add(ResourceElement elem) { - dict[elem.Name] = elem; + public void Add(ResourceElement elem) => dict[elem.Name] = elem; + + /// + /// Creates a new instance for a DeserializingResourceReader + /// + /// Version of System.Resources.Extensions assembly to use + /// Resource format version, the default is 2 + public static ResourceElementSet CreateForDeserializingResourceReader(Version extensionAssemblyVersion, int formatVersion = 2) { + var extensionAssemblyFullName = $"System.Resources.Extensions, Version={extensionAssemblyVersion.ToString(4)}, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51"; + return new ResourceElementSet($"System.Resources.Extensions.DeserializingResourceReader, {extensionAssemblyFullName}", $"System.Resources.Extensions.RuntimeResourceSet, {extensionAssemblyFullName}", ResourceReaderType.DeserializingResourceReader) { + FormatVersion = formatVersion + }; + } + + /// + /// Creates a new instance for a ResourceReader + /// + /// Module in which the set will reside + /// /// Resource format version, the default is 2 + public static ResourceElementSet CreateForResourceReader(ModuleDef module, int formatVersion = 2) { + string mscorlibFullName; + if (module.CorLibTypes.AssemblyRef.Name == "mscorlib") { + // Use mscorlib reference found in module. + mscorlibFullName = module.CorLibTypes.AssemblyRef.FullName; + } + else { + // Use a reference to 4.0.0.0 mscorlib for compatibility with .NET Core, .NET 5, and later. + mscorlibFullName = "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + } + return new ResourceElementSet($"System.Resources.ResourceReader, {mscorlibFullName}", "System.Resources.RuntimeResourceSet", ResourceReaderType.ResourceReader) { + FormatVersion = formatVersion + }; + } + + /// + /// Creates a new instance for a ResourceReader + /// + /// mscorlib assembly version + /// Resource format version, the default is 2 + public static ResourceElementSet CreateForResourceReader(Version mscorlibVersion, int formatVersion = 2) { + return new ResourceElementSet($"System.Resources.ResourceReader, mscorlib, Version={mscorlibVersion.ToString(4)}, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Resources.RuntimeResourceSet", ResourceReaderType.ResourceReader) { + FormatVersion = formatVersion + }; } + + /// + /// Creates a new instance based on this instance + /// + /// + public ResourceElementSet Clone() => new ResourceElementSet(ResourceReaderTypeName, ResourceSetTypeName, ReaderType) { + FormatVersion = FormatVersion + }; } } diff --git a/src/DotNet/Resources/ResourceReader.cs b/src/DotNet/Resources/ResourceReader.cs index 5dac08a8f..8a35a1473 100644 --- a/src/DotNet/Resources/ResourceReader.cs +++ b/src/DotNet/Resources/ResourceReader.cs @@ -1,7 +1,9 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.Serialization; using System.Text; using System.Text.RegularExpressions; using dnlib.IO; @@ -12,6 +14,12 @@ namespace dnlib.DotNet.Resources { /// [Serializable] public sealed class ResourceReaderException : Exception { + /// + /// Constructor + /// + public ResourceReaderException() { + } + /// /// Constructor /// @@ -19,34 +27,42 @@ public sealed class ResourceReaderException : Exception { public ResourceReaderException(string msg) : base(msg) { } + + /// + /// Constructor + /// + /// + /// + public ResourceReaderException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } } /// /// Gets called to create a from serialized data. Returns null /// if a default instance should be created. /// - /// ResourceDataCreator + /// ResourceDataFactory /// Serialized type /// Serialized data + /// Format of the serialized data /// - public delegate IResourceData CreateResourceDataDelegate(ResourceDataCreator resourceDataCreator, UserResourceType type, byte[] serializedData); + public delegate IResourceData CreateResourceDataDelegate(ResourceDataFactory resourceDataFactory, UserResourceType type, byte[] serializedData, SerializationFormat format); /// /// Reads .NET resources /// public struct ResourceReader { - readonly IBinaryReader reader; - readonly long baseFileOffset; - readonly ResourceDataCreator resourceDataCreator; + DataReader reader; + readonly uint baseFileOffset; + readonly ResourceDataFactory resourceDataFactory; readonly CreateResourceDataDelegate createResourceDataDelegate; - ResourceReader(ModuleDef module, IBinaryReader reader, CreateResourceDataDelegate createResourceDataDelegate) { + ResourceReader(ResourceDataFactory resourceDataFactory, ref DataReader reader, CreateResourceDataDelegate createResourceDataDelegate) { this.reader = reader; - this.resourceDataCreator = new ResourceDataCreator(module); + this.resourceDataFactory = resourceDataFactory; this.createResourceDataDelegate = createResourceDataDelegate; - - var stream = reader as IImageStream; - this.baseFileOffset = stream == null ? 0 : (long)stream.FileOffset; + baseFileOffset = reader.StartOffset; } /// @@ -54,9 +70,8 @@ public struct ResourceReader { /// /// Reader /// - public static bool CouldBeResourcesFile(IBinaryReader reader) { - return reader.CanRead(4) && reader.ReadUInt32() == 0xBEEFCACE; - } + public static bool CouldBeResourcesFile(DataReader reader) => + reader.CanRead(4U) && reader.ReadUInt32() == 0xBEEFCACE; /// /// Reads a .NET resource @@ -64,9 +79,7 @@ public static bool CouldBeResourcesFile(IBinaryReader reader) { /// Owner module /// Data of resource /// - public static ResourceElementSet Read(ModuleDef module, IBinaryReader reader) { - return Read(module, reader, null); - } + public static ResourceElementSet Read(ModuleDef module, DataReader reader) => Read(module, reader, null); /// /// Reads a .NET resource @@ -75,32 +88,40 @@ public static ResourceElementSet Read(ModuleDef module, IBinaryReader reader) { /// Data of resource /// Call back that gets called to create a instance. Can be null. /// - public static ResourceElementSet Read(ModuleDef module, IBinaryReader reader, CreateResourceDataDelegate createResourceDataDelegate) { - return new ResourceReader(module, reader, createResourceDataDelegate).Read(); - } + public static ResourceElementSet Read(ModuleDef module, DataReader reader, CreateResourceDataDelegate createResourceDataDelegate) => + Read(new ResourceDataFactory(module), reader, createResourceDataDelegate); - ResourceElementSet Read() { - ResourceElementSet resources = new ResourceElementSet(); + /// + /// Reads a .NET resource + /// + /// User type resource data factory + /// Data of resource + /// Call back that gets called to create a instance. Can be null. + /// + public static ResourceElementSet Read(ResourceDataFactory resourceDataFactory, DataReader reader, CreateResourceDataDelegate createResourceDataDelegate) => + new ResourceReader(resourceDataFactory, ref reader, createResourceDataDelegate).Read(); + ResourceElementSet Read() { uint sig = reader.ReadUInt32(); if (sig != 0xBEEFCACE) - throw new ResourceReaderException(string.Format("Invalid resource sig: {0:X8}", sig)); - if (!CheckReaders()) + throw new ResourceReaderException($"Invalid resource sig: {sig:X8}"); + var resources = ReadHeader(); + if (resources is null) throw new ResourceReaderException("Invalid resource reader"); - int version = reader.ReadInt32(); - if (version != 2)//TODO: Support version 1 - throw new ResourceReaderException(string.Format("Invalid resource version: {0}", version)); + resources.FormatVersion = reader.ReadInt32(); + if (resources.FormatVersion != 2 && resources.FormatVersion != 1) + throw new ResourceReaderException($"Invalid resource version: {resources.FormatVersion}"); int numResources = reader.ReadInt32(); if (numResources < 0) - throw new ResourceReaderException(string.Format("Invalid number of resources: {0}", numResources)); + throw new ResourceReaderException($"Invalid number of resources: {numResources}"); int numUserTypes = reader.ReadInt32(); if (numUserTypes < 0) - throw new ResourceReaderException(string.Format("Invalid number of user types: {0}", numUserTypes)); + throw new ResourceReaderException($"Invalid number of user types: {numUserTypes}"); var userTypes = new List(); for (int i = 0; i < numUserTypes; i++) - userTypes.Add(new UserResourceType(reader.ReadString(), ResourceTypeCode.UserTypes + i)); - reader.Position = (reader.Position + 7) & ~7; + userTypes.Add(new UserResourceType(reader.ReadSerializedString(), ResourceTypeCode.UserTypes + i)); + reader.Position = (reader.Position + 7) & ~7U; var hashes = new int[numResources]; for (int i = 0; i < numResources; i++) @@ -117,8 +138,8 @@ ResourceElementSet Read() { var infos = new List(numResources); for (int i = 0; i < numResources; i++) { - reader.Position = nameBaseOffset + offsets[i]; - var name = reader.ReadString(Encoding.Unicode); + reader.Position = (uint)(nameBaseOffset + offsets[i]); + var name = reader.ReadSerializedString(Encoding.Unicode); long offset = dataBaseOffset + reader.ReadInt32(); infos.Add(new ResourceInfo(name, offset)); } @@ -128,12 +149,14 @@ ResourceElementSet Read() { var info = infos[i]; var element = new ResourceElement(); element.Name = info.name; - reader.Position = info.offset; + reader.Position = (uint)info.offset; long nextDataOffset = i == infos.Count - 1 ? end : infos[i + 1].offset; int size = (int)(nextDataOffset - info.offset); - element.ResourceData = ReadResourceData(userTypes, size); - element.ResourceData.StartOffset = this.baseFileOffset + (FileOffset)info.offset; - element.ResourceData.EndOffset = this.baseFileOffset + (FileOffset)reader.Position; + element.ResourceData = resources.FormatVersion == 1 + ? ReadResourceDataV1(userTypes, resources.ReaderType, size) + : ReadResourceDataV2(userTypes, resources.ReaderType, size); + element.ResourceData.StartOffset = baseFileOffset + (FileOffset)info.offset; + element.ResourceData.EndOffset = baseFileOffset + (FileOffset)reader.Position; resources.Add(element); } @@ -142,81 +165,119 @@ ResourceElementSet Read() { } sealed class ResourceInfo { - public string name; - public long offset; + public readonly string name; + public readonly long offset; public ResourceInfo(string name, long offset) { this.name = name; this.offset = offset; } - public override string ToString() { - return string.Format("{0:X8} - {1}", offset, name); - } + public override string ToString() => $"{offset:X8} - {name}"; } - IResourceData ReadResourceData(List userTypes, int size) { - uint code = ReadUInt32(reader); + IResourceData ReadResourceDataV2(List userTypes, ResourceReaderType readerType, int size) { + uint endPos = reader.Position + (uint)size; + uint code = reader.Read7BitEncodedUInt32(); switch ((ResourceTypeCode)code) { - case ResourceTypeCode.Null: return resourceDataCreator.CreateNull(); - case ResourceTypeCode.String: return resourceDataCreator.Create(reader.ReadString()); - case ResourceTypeCode.Boolean: return resourceDataCreator.Create(reader.ReadBoolean()); - case ResourceTypeCode.Char: return resourceDataCreator.Create((char)reader.ReadUInt16()); - case ResourceTypeCode.Byte: return resourceDataCreator.Create(reader.ReadByte()); - case ResourceTypeCode.SByte: return resourceDataCreator.Create(reader.ReadSByte()); - case ResourceTypeCode.Int16: return resourceDataCreator.Create(reader.ReadInt16()); - case ResourceTypeCode.UInt16: return resourceDataCreator.Create(reader.ReadUInt16()); - case ResourceTypeCode.Int32: return resourceDataCreator.Create(reader.ReadInt32()); - case ResourceTypeCode.UInt32: return resourceDataCreator.Create(reader.ReadUInt32()); - case ResourceTypeCode.Int64: return resourceDataCreator.Create(reader.ReadInt64()); - case ResourceTypeCode.UInt64: return resourceDataCreator.Create(reader.ReadUInt64()); - case ResourceTypeCode.Single: return resourceDataCreator.Create(reader.ReadSingle()); - case ResourceTypeCode.Double: return resourceDataCreator.Create(reader.ReadDouble()); - case ResourceTypeCode.Decimal: return resourceDataCreator.Create(reader.ReadDecimal()); - case ResourceTypeCode.DateTime: return resourceDataCreator.Create(DateTime.FromBinary(reader.ReadInt64())); - case ResourceTypeCode.TimeSpan: return resourceDataCreator.Create(new TimeSpan(reader.ReadInt64())); - case ResourceTypeCode.ByteArray:return resourceDataCreator.Create(reader.ReadBytes(reader.ReadInt32())); - case ResourceTypeCode.Stream: return resourceDataCreator.CreateStream(reader.ReadBytes(reader.ReadInt32())); + case ResourceTypeCode.Null: return resourceDataFactory.CreateNull(); + case ResourceTypeCode.String: return resourceDataFactory.Create(reader.ReadSerializedString()); + case ResourceTypeCode.Boolean: return resourceDataFactory.Create(reader.ReadBoolean()); + case ResourceTypeCode.Char: return resourceDataFactory.Create(reader.ReadChar()); + case ResourceTypeCode.Byte: return resourceDataFactory.Create(reader.ReadByte()); + case ResourceTypeCode.SByte: return resourceDataFactory.Create(reader.ReadSByte()); + case ResourceTypeCode.Int16: return resourceDataFactory.Create(reader.ReadInt16()); + case ResourceTypeCode.UInt16: return resourceDataFactory.Create(reader.ReadUInt16()); + case ResourceTypeCode.Int32: return resourceDataFactory.Create(reader.ReadInt32()); + case ResourceTypeCode.UInt32: return resourceDataFactory.Create(reader.ReadUInt32()); + case ResourceTypeCode.Int64: return resourceDataFactory.Create(reader.ReadInt64()); + case ResourceTypeCode.UInt64: return resourceDataFactory.Create(reader.ReadUInt64()); + case ResourceTypeCode.Single: return resourceDataFactory.Create(reader.ReadSingle()); + case ResourceTypeCode.Double: return resourceDataFactory.Create(reader.ReadDouble()); + case ResourceTypeCode.Decimal: return resourceDataFactory.Create(reader.ReadDecimal()); + case ResourceTypeCode.DateTime: return resourceDataFactory.Create(DateTime.FromBinary(reader.ReadInt64())); + case ResourceTypeCode.TimeSpan: return resourceDataFactory.Create(new TimeSpan(reader.ReadInt64())); + case ResourceTypeCode.ByteArray:return resourceDataFactory.Create(reader.ReadBytes(reader.ReadInt32())); + case ResourceTypeCode.Stream: return resourceDataFactory.CreateStream(reader.ReadBytes(reader.ReadInt32())); default: int userTypeIndex = (int)(code - (uint)ResourceTypeCode.UserTypes); if (userTypeIndex < 0 || userTypeIndex >= userTypes.Count) - throw new ResourceReaderException(string.Format("Invalid resource data code: {0}", code)); - var userType = userTypes[userTypeIndex]; - var serializedData = reader.ReadBytes(size); - if (createResourceDataDelegate != null) { - var res = createResourceDataDelegate(resourceDataCreator, userType, serializedData); - if (res != null) - return res; - } - return resourceDataCreator.CreateSerialized(serializedData, userType); + throw new ResourceReaderException($"Invalid resource data code: {code}"); + return ReadSerializedObject(endPos, readerType, userTypes[userTypeIndex]); } } - static uint ReadUInt32(IBinaryReader reader) { - try { - return reader.Read7BitEncodedUInt32(); - } - catch { - throw new ResourceReaderException("Invalid encoded int32"); + IResourceData ReadResourceDataV1(List userTypes, ResourceReaderType readerType, int size) { + uint endPos = reader.Position + (uint)size; + int typeIndex = reader.Read7BitEncodedInt32(); + if (typeIndex == -1) + return resourceDataFactory.CreateNull(); + if (typeIndex < 0 || typeIndex >= userTypes.Count) + throw new ResourceReaderException($"Invalid resource type index: {typeIndex}"); + var type = userTypes[typeIndex]; + var commaIndex = type.Name.IndexOf(','); + string actualName = commaIndex == -1 ? type.Name : type.Name.Remove(commaIndex); + switch (actualName) { + case "System.String": return resourceDataFactory.Create(reader.ReadSerializedString()); + case "System.Int32": return resourceDataFactory.Create(reader.ReadInt32()); + case "System.Byte": return resourceDataFactory.Create(reader.ReadByte()); + case "System.SByte": return resourceDataFactory.Create(reader.ReadSByte()); + case "System.Int16": return resourceDataFactory.Create(reader.ReadInt16()); + case "System.Int64": return resourceDataFactory.Create(reader.ReadInt64()); + case "System.UInt16": return resourceDataFactory.Create(reader.ReadUInt16()); + case "System.UInt32": return resourceDataFactory.Create(reader.ReadUInt32()); + case "System.UInt64": return resourceDataFactory.Create(reader.ReadUInt64()); + case "System.Single": return resourceDataFactory.Create(reader.ReadSingle()); + case "System.Double": return resourceDataFactory.Create(reader.ReadDouble()); + case "System.DateTime": return resourceDataFactory.Create(new DateTime(reader.ReadInt64())); + case "System.TimeSpan": return resourceDataFactory.Create(new TimeSpan(reader.ReadInt64())); + case "System.Decimal": return resourceDataFactory.Create(reader.ReadDecimal()); + default: + return ReadSerializedObject(endPos, readerType, type); } } - bool CheckReaders() { - bool validReader = false; - - int numReaders = reader.ReadInt32(); - if (numReaders < 0) - throw new ResourceReaderException(string.Format("Invalid number of readers: {0}", numReaders)); - int readersSize = reader.ReadInt32(); - if (readersSize < 0) - throw new ResourceReaderException(string.Format("Invalid readers size: {0:X8}", readersSize)); - - for (int i = 0; i < numReaders; i++) { - var resourceReaderFullName = reader.ReadString(); - var resourceSetFullName = reader.ReadString(); - if (Regex.IsMatch(resourceReaderFullName, @"^System\.Resources\.ResourceReader,\s*mscorlib,")) - validReader = true; + IResourceData ReadSerializedObject(uint endPos, ResourceReaderType readerType, UserResourceType type) { + byte[] serializedData; + IResourceData res; + switch (readerType) { + case ResourceReaderType.ResourceReader: + serializedData = reader.ReadBytes((int)(endPos - reader.Position)); + res = createResourceDataDelegate?.Invoke(resourceDataFactory, type, serializedData, SerializationFormat.BinaryFormatter); + return res ?? resourceDataFactory.CreateSerialized(serializedData, SerializationFormat.BinaryFormatter, type); + case ResourceReaderType.DeserializingResourceReader: { + var format = (SerializationFormat)reader.Read7BitEncodedInt32(); + if (format < SerializationFormat.BinaryFormatter || format > SerializationFormat.ActivatorStream) + throw new ResourceReaderException($"Invalid serialization format: {format}"); + int length = reader.Read7BitEncodedInt32(); + Debug.Assert(length == (int)(endPos - reader.Position)); + serializedData = reader.ReadBytes(length); + res = createResourceDataDelegate?.Invoke(resourceDataFactory, type, serializedData, format); + return res ?? resourceDataFactory.CreateSerialized(serializedData, format, type); + } + default: + throw new ResourceReaderException($"Invalid reader type: {readerType}"); } + } + + ResourceElementSet ReadHeader() { + int headerVersion = reader.ReadInt32(); + if (headerVersion != 1) + throw new ResourceReaderException($"Invalid or unsupported header version: {headerVersion}"); + int headerSize = reader.ReadInt32(); + if (headerSize < 0) + throw new ResourceReaderException($"Invalid header size: {headerSize:X8}"); + + string resourceReaderTypeName = reader.ReadSerializedString(); + string resourceSetTypeName = reader.ReadSerializedString(); + + ResourceReaderType readerType; + if (Regex.IsMatch(resourceReaderTypeName, ResourceElementSet.ResourceReaderTypeNameRegex)) + readerType = ResourceReaderType.ResourceReader; + else if (Regex.IsMatch(resourceReaderTypeName, ResourceElementSet.DeserializingResourceReaderTypeNameRegex)) + readerType = ResourceReaderType.DeserializingResourceReader; + else + return null; - return validReader; + return new ResourceElementSet(resourceReaderTypeName, resourceSetTypeName, readerType); } } } diff --git a/src/DotNet/Resources/ResourceReaderType.cs b/src/DotNet/Resources/ResourceReaderType.cs new file mode 100644 index 000000000..328eb4c17 --- /dev/null +++ b/src/DotNet/Resources/ResourceReaderType.cs @@ -0,0 +1,15 @@ +namespace dnlib.DotNet.Resources { + /// + /// Resource reader type + /// + public enum ResourceReaderType { + /// + /// System.Resources.ResourceReader + /// + ResourceReader, + /// + /// System.Resources.Extensions.DeserializingResourceReader + /// + DeserializingResourceReader, + } +} diff --git a/src/DotNet/Resources/ResourceTypeCode.cs b/src/DotNet/Resources/ResourceTypeCode.cs index 10839c924..ececc65a2 100644 --- a/src/DotNet/Resources/ResourceTypeCode.cs +++ b/src/DotNet/Resources/ResourceTypeCode.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info namespace dnlib.DotNet.Resources { /// diff --git a/src/DotNet/Resources/ResourceWriter.cs b/src/DotNet/Resources/ResourceWriter.cs index 20d10dfb7..4130390bb 100644 --- a/src/DotNet/Resources/ResourceWriter.cs +++ b/src/DotNet/Resources/ResourceWriter.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; @@ -15,13 +15,13 @@ public sealed class ResourceWriter { ModuleDef module; BinaryWriter writer; ResourceElementSet resources; - ResourceDataCreator typeCreator; - Dictionary dataToNewType = new Dictionary(); + ResourceDataFactory typeCreator; + Dictionary dataToNewType = new Dictionary(); - ResourceWriter(ModuleDef module, Stream stream, ResourceElementSet resources) { + ResourceWriter(ModuleDef module, ResourceDataFactory typeCreator, Stream stream, ResourceElementSet resources) { this.module = module; - this.typeCreator = new ResourceDataCreator(module); - this.writer = new BinaryWriter(stream); + this.typeCreator = typeCreator; + writer = new BinaryWriter(stream); this.resources = resources; } @@ -31,17 +31,29 @@ public sealed class ResourceWriter { /// Owner module /// Output stream /// .NET resources - public static void Write(ModuleDef module, Stream stream, ResourceElementSet resources) { - new ResourceWriter(module, stream, resources).Write(); - } + public static void Write(ModuleDef module, Stream stream, ResourceElementSet resources) => + new ResourceWriter(module, new ResourceDataFactory(module), stream, resources).Write(); + + /// + /// Write .NET resources + /// + /// Owner module + /// User type factory + /// Output stream + /// .NET resources + public static void Write(ModuleDef module, ResourceDataFactory typeCreator, Stream stream, ResourceElementSet resources) => + new ResourceWriter(module, typeCreator, stream, resources).Write(); void Write() { - InitializeUserTypes(); + if (resources.FormatVersion != 1 && resources.FormatVersion != 2) + throw new ArgumentException($"Invalid format version: {resources.FormatVersion}", nameof(resources)); + + InitializeUserTypes(resources.FormatVersion); writer.Write(0xBEEFCACE); writer.Write(1); WriteReaderType(); - writer.Write(2);//TODO: Support version 1 + writer.Write(resources.FormatVersion); writer.Write(resources.Count); writer.Write(typeCreator.Count); foreach (var userType in typeCreator.GetSortedTypes()) @@ -55,7 +67,10 @@ void Write() { var nameOffsetStream = new MemoryStream(); var nameOffsetWriter = new BinaryWriter(nameOffsetStream, Encoding.Unicode); var dataStream = new MemoryStream(); - var dataWriter = new BinaryWriter(dataStream); + var dataWriter = new ResourceBinaryWriter(dataStream) { + FormatVersion = resources.FormatVersion, + ReaderType = resources.ReaderType, + }; var hashes = new int[resources.Count]; var offsets = new int[resources.Count]; var formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File | StreamingContextStates.Persistence)); @@ -79,26 +94,23 @@ void Write() { writer.Write(dataStream.ToArray()); } - void WriteData(BinaryWriter writer, ResourceElement info, IFormatter formatter) { - var code = GetResourceType(info.ResourceData); - WriteUInt32(writer, (uint)code); + void WriteData(ResourceBinaryWriter writer, ResourceElement info, IFormatter formatter) { + var code = GetResourceType(info.ResourceData, writer.FormatVersion); + writer.Write7BitEncodedInt((int)code); info.ResourceData.WriteData(writer, formatter); } - static void WriteUInt32(BinaryWriter writer, uint value) { - while (value >= 0x80) { - writer.Write((byte)(value | 0x80)); - value >>= 7; + ResourceTypeCode GetResourceType(IResourceData data, int formatVersion) { + if (formatVersion == 1) { + if (data.Code == ResourceTypeCode.Null) + return (ResourceTypeCode)(-1); + return (ResourceTypeCode)(dataToNewType[data].Code - ResourceTypeCode.UserTypes); } - writer.Write((byte)value); - } - ResourceTypeCode GetResourceType(IResourceData data) { if (data is BuiltInResourceData) return data.Code; - var userData = (UserResourceData)data; - return dataToNewType[userData].Code; + return dataToNewType[data].Code; } static uint Hash(string key) { @@ -108,22 +120,34 @@ static uint Hash(string key) { return val; } - void InitializeUserTypes() { + void InitializeUserTypes(int formatVersion) { foreach (var resource in resources.ResourceElements) { - var data = resource.ResourceData as UserResourceData; - if (data == null) + UserResourceType newType; + if (formatVersion == 1 && resource.ResourceData is BuiltInResourceData builtinData) { + newType = typeCreator.CreateBuiltinResourceType(builtinData.Code); + if (newType is null) + throw new NotSupportedException($"Unsupported resource type: {builtinData.Code} in format version 1 resource"); + } + else if (resource.ResourceData is UserResourceData userData) + newType = typeCreator.CreateUserResourceType(userData.TypeName); + else continue; - var newType = typeCreator.CreateUserResourceType(data.TypeName); - dataToNewType[data] = newType; + dataToNewType[resource.ResourceData] = newType; } } void WriteReaderType() { var memStream = new MemoryStream(); var headerWriter = new BinaryWriter(memStream); - var mscorlibFullName = GetMscorlibFullname(); - headerWriter.Write("System.Resources.ResourceReader, " + mscorlibFullName); - headerWriter.Write("System.Resources.RuntimeResourceSet"); + if (resources.ResourceReaderTypeName is not null && resources.ResourceSetTypeName is not null) { + headerWriter.Write(resources.ResourceReaderTypeName); + headerWriter.Write(resources.ResourceSetTypeName); + } + else { + var mscorlibFullName = GetMscorlibFullname(); + headerWriter.Write($"System.Resources.ResourceReader, {mscorlibFullName}"); + headerWriter.Write("System.Resources.RuntimeResourceSet"); + } writer.Write((int)memStream.Position); writer.Write(memStream.ToArray()); } diff --git a/src/DotNet/Resources/UserResourceData.cs b/src/DotNet/Resources/UserResourceData.cs index 295b1cd3b..3083f5cb7 100644 --- a/src/DotNet/Resources/UserResourceData.cs +++ b/src/DotNet/Resources/UserResourceData.cs @@ -1,5 +1,6 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info +using System; using System.IO; using System.Runtime.Serialization; using dnlib.IO; @@ -14,33 +15,27 @@ public abstract class UserResourceData : IResourceData { /// /// Full name including assembly of type /// - public string TypeName { - get { return type.Name; } - } + public string TypeName => type.Name; /// /// User type code /// - public ResourceTypeCode Code { - get { return type.Code; } - } + public ResourceTypeCode Code => type.Code; - /// + /// public FileOffset StartOffset { get; set; } - /// + /// public FileOffset EndOffset { get; set; } /// /// Constructor /// /// User resource type - public UserResourceData(UserResourceType type) { - this.type = type; - } + public UserResourceData(UserResourceType type) => this.type = type; /// - public abstract void WriteData(BinaryWriter writer, IFormatter formatter); + public abstract void WriteData(ResourceBinaryWriter writer, IFormatter formatter); } /// @@ -48,32 +43,72 @@ public UserResourceData(UserResourceType type) { /// public sealed class BinaryResourceData : UserResourceData { byte[] data; + SerializationFormat format; /// /// Gets the raw data /// - public byte[] Data { - get { return data; } - } + public byte[] Data => data; + + /// + /// Gets the serialization format of + /// + public SerializationFormat Format => format; /// /// Constructor /// /// User resource type /// Raw serialized data - public BinaryResourceData(UserResourceType type, byte[] data) + /// + public BinaryResourceData(UserResourceType type, byte[] data, SerializationFormat format) : base(type) { this.data = data; + this.format = format; } /// - public override void WriteData(BinaryWriter writer, IFormatter formatter) { + public override void WriteData(ResourceBinaryWriter writer, IFormatter formatter) { + if (writer.ReaderType == ResourceReaderType.ResourceReader && format != SerializationFormat.BinaryFormatter) + throw new NotSupportedException($"Unsupported serialization format: {format} for {writer.ReaderType}"); + + if (writer.ReaderType == ResourceReaderType.DeserializingResourceReader) { + writer.Write7BitEncodedInt((int)format); + writer.Write7BitEncodedInt(data.Length); + } writer.Write(data); } /// - public override string ToString() { - return string.Format("Binary: Length: {0}", data.Length); - } + public override string ToString() => $"Binary: Length: {data.Length} Format: {format}"; + } + + /// + /// Specifies how the data in should be deserialized. + /// + public enum SerializationFormat { + /// + /// The data can be deserialized using . + /// + BinaryFormatter = 1, + + /// + /// The data can be deserialized by passing in the raw data into + /// the method. + /// + TypeConverterByteArray = 2, + + /// + /// The data can be deserialized by passing the UTF-8 string obtained from the raw data into + /// the method. + /// + TypeConverterString = 3, + + /// + /// The data can be deserialized by creating a new instance of the type using a + /// constructor with a single parameter and passing in + /// a of the raw data into it. + /// + ActivatorStream = 4, } } diff --git a/src/DotNet/Resources/UserResourceType.cs b/src/DotNet/Resources/UserResourceType.cs index ed251b947..fde5524f1 100644 --- a/src/DotNet/Resources/UserResourceType.cs +++ b/src/DotNet/Resources/UserResourceType.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info namespace dnlib.DotNet.Resources { /// @@ -11,16 +11,12 @@ public sealed class UserResourceType { /// /// Full name including assembly of type /// - public string Name { - get { return name; } - } + public string Name => name; /// /// User type code /// - public ResourceTypeCode Code { - get { return code; } - } + public ResourceTypeCode Code => code; /// /// Constructor @@ -33,8 +29,6 @@ public UserResourceType(string name, ResourceTypeCode code) { } /// - public override string ToString() { - return string.Format("{0:X2} {1}", (int)code, name); - } + public override string ToString() => $"{(int)code:X2} {name}"; } } diff --git a/src/DotNet/SecurityAction.cs b/src/DotNet/SecurityAction.cs index 85fa0a9aa..e93ba3965 100644 --- a/src/DotNet/SecurityAction.cs +++ b/src/DotNet/SecurityAction.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// Security action. See CorHdr.h/CorDeclSecurity /// diff --git a/src/DotNet/SecurityAttribute.cs b/src/DotNet/SecurityAttribute.cs index 671b968f5..fbbfa0372 100644 --- a/src/DotNet/SecurityAttribute.cs +++ b/src/DotNet/SecurityAttribute.cs @@ -1,13 +1,6 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System.Collections.Generic; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif namespace dnlib.DotNet { /// @@ -15,14 +8,14 @@ namespace dnlib.DotNet { /// public sealed class SecurityAttribute : ICustomAttribute { ITypeDefOrRef attrType; - readonly ThreadSafe.IList namedArguments; + readonly IList namedArguments; /// /// Gets/sets the attribute type /// public ITypeDefOrRef AttributeType { - get { return attrType; } - set { attrType = value; } + get => attrType; + set => attrType = value; } /// @@ -31,30 +24,29 @@ public ITypeDefOrRef AttributeType { public string TypeFullName { get { var at = attrType; - return at == null ? string.Empty : at.FullName; + return at is null ? string.Empty : at.FullName; } } /// /// Gets all named arguments (field and property values) /// - public ThreadSafe.IList NamedArguments { - get { return namedArguments; } - } + public IList NamedArguments => namedArguments; /// /// true if is not empty /// - public bool HasNamedArguments { - get { return namedArguments.Count > 0; } - } + public bool HasNamedArguments => namedArguments.Count > 0; /// /// Gets all s that are field arguments /// public IEnumerable Fields { get { - foreach (var namedArg in namedArguments.GetSafeEnumerable()) { + var namedArguments = this.namedArguments; + int count = namedArguments.Count; + for (int i = 0; i < count; i++) { + var namedArg = namedArguments[i]; if (namedArg.IsField) yield return namedArg; } @@ -66,7 +58,10 @@ public IEnumerable Fields { /// public IEnumerable Properties { get { - foreach (var namedArg in namedArguments.GetSafeEnumerable()) { + var namedArguments = this.namedArguments; + int count = namedArguments.Count; + for (int i = 0; i < count; i++) { + var namedArg = namedArguments[i]; if (namedArg.IsProperty) yield return namedArg; } @@ -83,7 +78,7 @@ public static SecurityAttribute CreateFromXml(ModuleDef module, string xml) { var attrType = module.CorLibTypes.GetTypeRef("System.Security.Permissions", "PermissionSetAttribute"); var utf8Xml = new UTF8String(xml); var namedArg = new CANamedArgument(false, module.CorLibTypes.String, "XML", new CAArgument(module.CorLibTypes.String, utf8Xml)); - var list = ThreadSafeListCreator.Create(namedArg); + var list = new List { namedArg }; return new SecurityAttribute(attrType, list); } @@ -109,12 +104,10 @@ public SecurityAttribute(ITypeDefOrRef attrType) /// Named arguments that will be owned by this instance public SecurityAttribute(ITypeDefOrRef attrType, IList namedArguments) { this.attrType = attrType; - this.namedArguments = ThreadSafeListCreator.MakeThreadSafe(namedArguments ?? new List()); + this.namedArguments = namedArguments ?? new List(); } /// - public override string ToString() { - return TypeFullName; - } + public override string ToString() => TypeFullName; } } diff --git a/src/DotNet/SerializationType.cs b/src/DotNet/SerializationType.cs index 16046d3ce..59a46802a 100644 --- a/src/DotNet/SerializationType.cs +++ b/src/DotNet/SerializationType.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet { +namespace dnlib.DotNet { /// /// See CorSerializationType/CorHdr.h /// diff --git a/src/DotNet/SigComparer.cs b/src/DotNet/SigComparer.cs index 6f3547187..5fbfca772 100644 --- a/src/DotNet/SigComparer.cs +++ b/src/DotNet/SigComparer.cs @@ -21,83 +21,58 @@ public sealed class TypeEqualityComparer : IEqualityComparer, IEqualityCo /// public static readonly TypeEqualityComparer CaseInsensitive = new TypeEqualityComparer(SigComparerOptions.CaseInsensitiveAll); + /// + /// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc. + /// + public static readonly TypeEqualityComparer CompareReferenceInSameModule = new TypeEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule); + /// /// Constructor /// /// Comparison options - public TypeEqualityComparer(SigComparerOptions options) { - this.options = options; - } + public TypeEqualityComparer(SigComparerOptions options) => this.options = options; /// - public bool Equals(IType x, IType y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(IType x, IType y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(IType obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(IType obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(ITypeDefOrRef x, ITypeDefOrRef y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(ITypeDefOrRef x, ITypeDefOrRef y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(ITypeDefOrRef obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(ITypeDefOrRef obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(TypeRef x, TypeRef y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(TypeRef x, TypeRef y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(TypeRef obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(TypeRef obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(TypeDef x, TypeDef y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(TypeDef x, TypeDef y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(TypeDef obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(TypeDef obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(TypeSpec x, TypeSpec y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(TypeSpec x, TypeSpec y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(TypeSpec obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(TypeSpec obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(TypeSig x, TypeSig y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(TypeSig x, TypeSig y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(TypeSig obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(TypeSig obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(ExportedType x, ExportedType y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(ExportedType x, ExportedType y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(ExportedType obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(ExportedType obj) => new SigComparer(options).GetHashCode(obj); } /// @@ -126,43 +101,34 @@ public sealed class FieldEqualityComparer : IEqualityComparer, IEquality /// public static readonly FieldEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new FieldEqualityComparer(SigComparerOptions.CaseInsensitiveAll); + /// + /// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc. + /// + public static readonly FieldEqualityComparer CompareReferenceInSameModule = new FieldEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule); + /// /// Constructor /// /// Comparison options - public FieldEqualityComparer(SigComparerOptions options) { - this.options = options; - } + public FieldEqualityComparer(SigComparerOptions options) => this.options = options; /// - public bool Equals(IField x, IField y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(IField x, IField y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(IField obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(IField obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(FieldDef x, FieldDef y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(FieldDef x, FieldDef y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(FieldDef obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(FieldDef obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(MemberRef x, MemberRef y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(MemberRef x, MemberRef y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(MemberRef obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(MemberRef obj) => new SigComparer(options).GetHashCode(obj); } /// @@ -191,63 +157,46 @@ public sealed class MethodEqualityComparer : IEqualityComparer, IEquali /// public static readonly MethodEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new MethodEqualityComparer(SigComparerOptions.CaseInsensitiveAll); + /// + /// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc. + /// + public static readonly MethodEqualityComparer CompareReferenceInSameModule = new MethodEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule); + /// /// Constructor /// /// Comparison options - public MethodEqualityComparer(SigComparerOptions options) { - this.options = options; - } + public MethodEqualityComparer(SigComparerOptions options) => this.options = options; /// - public bool Equals(IMethod x, IMethod y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(IMethod x, IMethod y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(IMethod obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(IMethod obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(IMethodDefOrRef x, IMethodDefOrRef y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(IMethodDefOrRef x, IMethodDefOrRef y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(IMethodDefOrRef obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(IMethodDefOrRef obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(MethodDef x, MethodDef y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(MethodDef x, MethodDef y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(MethodDef obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(MethodDef obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(MemberRef x, MemberRef y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(MemberRef x, MemberRef y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(MemberRef obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(MemberRef obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(MethodSpec x, MethodSpec y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(MethodSpec x, MethodSpec y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(MethodSpec obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(MethodSpec obj) => new SigComparer(options).GetHashCode(obj); } /// @@ -276,23 +225,22 @@ public sealed class PropertyEqualityComparer : IEqualityComparer { /// public static readonly PropertyEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new PropertyEqualityComparer(SigComparerOptions.CaseInsensitiveAll); + /// + /// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc. + /// + public static readonly PropertyEqualityComparer CompareReferenceInSameModule = new PropertyEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule); + /// /// Constructor /// /// Comparison options - public PropertyEqualityComparer(SigComparerOptions options) { - this.options = options; - } + public PropertyEqualityComparer(SigComparerOptions options) => this.options = options; /// - public bool Equals(PropertyDef x, PropertyDef y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(PropertyDef x, PropertyDef y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(PropertyDef obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(PropertyDef obj) => new SigComparer(options).GetHashCode(obj); } /// @@ -321,23 +269,22 @@ public sealed class EventEqualityComparer : IEqualityComparer { /// public static readonly EventEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new EventEqualityComparer(SigComparerOptions.CaseInsensitiveAll); + /// + /// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc. + /// + public static readonly EventEqualityComparer CompareReferenceInSameModule = new EventEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule); + /// /// Constructor /// /// Comparison options - public EventEqualityComparer(SigComparerOptions options) { - this.options = options; - } + public EventEqualityComparer(SigComparerOptions options) => this.options = options; /// - public bool Equals(EventDef x, EventDef y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(EventDef x, EventDef y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(EventDef obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(EventDef obj) => new SigComparer(options).GetHashCode(obj); } /// @@ -360,79 +307,49 @@ public sealed class SignatureEqualityComparer : IEqualityComparer /// Comparison options - public SignatureEqualityComparer(SigComparerOptions options) { - this.options = options; - } + public SignatureEqualityComparer(SigComparerOptions options) => this.options = options; /// - public bool Equals(CallingConventionSig x, CallingConventionSig y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(CallingConventionSig x, CallingConventionSig y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(CallingConventionSig obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(CallingConventionSig obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(MethodBaseSig x, MethodBaseSig y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(MethodBaseSig x, MethodBaseSig y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(MethodBaseSig obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(MethodBaseSig obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(MethodSig x, MethodSig y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(MethodSig x, MethodSig y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(MethodSig obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(MethodSig obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(PropertySig x, PropertySig y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(PropertySig x, PropertySig y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(PropertySig obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(PropertySig obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(FieldSig x, FieldSig y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(FieldSig x, FieldSig y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(FieldSig obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(FieldSig obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(LocalSig x, LocalSig y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(LocalSig x, LocalSig y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(LocalSig obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(LocalSig obj) => new SigComparer(options).GetHashCode(obj); /// - public bool Equals(GenericInstMethodSig x, GenericInstMethodSig y) { - return new SigComparer(options).Equals(x, y); - } + public bool Equals(GenericInstMethodSig x, GenericInstMethodSig y) => new SigComparer(options).Equals(x, y); /// - public int GetHashCode(GenericInstMethodSig obj) { - return new SigComparer(options).GetHashCode(obj); - } + public int GetHashCode(GenericInstMethodSig obj) => new SigComparer(options).GetHashCode(obj); } /// @@ -498,14 +415,8 @@ public enum SigComparerOptions : uint { /// DontCompareReturnType = 0x200, - /// - /// If set, all generic parameters are replaced with their generic arguments prior - /// to comparing types. You should enable this when comparing a method, field, property - /// or an event to a , , - /// or an if the owner type could - /// be a generic instance type. - /// - SubstituteGenericParameters = 0x400, + // Internal only + //SubstituteGenericParameters = 0x400, /// /// Type namespaces are case insensitive @@ -579,7 +490,7 @@ public enum SigComparerOptions : uint { /// /// By default, all module and assembly compares when they're both the system library /// (eg. mscorlib or System.Runtime.dll) return true, even if they're really different, - /// eg. mscorlib (.NET 2.0) vs mscorlib (Windows CE). If this flag is set, the system + /// eg. mscorlib (.NET Framework 2.0) vs mscorlib (Windows CE). If this flag is set, the system /// library is compared just like any other module/assembly. /// MscorlibIsNotSpecial = 0x100000, @@ -590,16 +501,29 @@ public enum SigComparerOptions : uint { DontProjectWinMDRefs = 0x200000, /// - /// Don't check type equivalence when comparing types. Starting with .NET 4.0, two different + /// Don't check type equivalence when comparing types. Starting with .NET Framework 4.0, two different /// types can be considered equivalent if eg. a TypeIdentifierAttribute is used. /// DontCheckTypeEquivalence = 0x400000, + + /// + /// When comparing types, don't compare a multi-dimensional array's lower bounds and sizes + /// + IgnoreMultiDimensionalArrayLowerBoundsAndSizes = 0x800000, + + /// + /// When comparing MemberDefs in same module, use regular reference comparison instead + /// comparing the signature, name, and other properties. + /// + ReferenceCompareForMemberDefsInSameModule = 0x1000000, } /// /// Compares types, signatures, methods, fields, properties, events /// public struct SigComparer { + const SigComparerOptions SigComparerOptions_DontSubstituteGenericParameters = (SigComparerOptions)0x400; + const int HASHCODE_MAGIC_GLOBAL_TYPE = 1654396648; const int HASHCODE_MAGIC_NESTED_TYPE = -1049070942; const int HASHCODE_MAGIC_ET_MODULE = -299744851; @@ -618,97 +542,31 @@ public struct SigComparer { GenericArguments genericArguments; readonly ModuleDef sourceModule; - bool DontCompareTypeScope { - get { return (options & SigComparerOptions.DontCompareTypeScope) != 0; } - } - - bool CompareMethodFieldDeclaringType { - get { return (options & SigComparerOptions.CompareMethodFieldDeclaringType) != 0; } - } - - bool ComparePropertyDeclaringType { - get { return (options & SigComparerOptions.ComparePropertyDeclaringType) != 0; } - } - - bool CompareEventDeclaringType { - get { return (options & SigComparerOptions.CompareEventDeclaringType) != 0; } - } - - bool CompareSentinelParams { - get { return (options & SigComparerOptions.CompareSentinelParams) != 0; } - } - - bool CompareAssemblyPublicKeyToken { - get { return (options & SigComparerOptions.CompareAssemblyPublicKeyToken) != 0; } - } - - bool CompareAssemblyVersion { - get { return (options & SigComparerOptions.CompareAssemblyVersion) != 0; } - } - - bool CompareAssemblyLocale { - get { return (options & SigComparerOptions.CompareAssemblyLocale) != 0; } - } - - bool TypeRefCanReferenceGlobalType { - get { return (options & SigComparerOptions.TypeRefCanReferenceGlobalType) != 0; } - } - - bool DontCompareReturnType { - get { return (options & SigComparerOptions.DontCompareReturnType) != 0; } - } - - bool SubstituteGenericParameters { - get { return (options & SigComparerOptions.SubstituteGenericParameters) != 0; } - } - - bool CaseInsensitiveTypeNamespaces { - get { return (options & SigComparerOptions.CaseInsensitiveTypeNamespaces) != 0; } - } - - bool CaseInsensitiveTypeNames { - get { return (options & SigComparerOptions.CaseInsensitiveTypeNames) != 0; } - } - - bool CaseInsensitiveMethodFieldNames { - get { return (options & SigComparerOptions.CaseInsensitiveMethodFieldNames) != 0; } - } - - bool CaseInsensitivePropertyNames { - get { return (options & SigComparerOptions.CaseInsensitivePropertyNames) != 0; } - } - - bool CaseInsensitiveEventNames { - get { return (options & SigComparerOptions.CaseInsensitiveEventNames) != 0; } - } - - bool PrivateScopeFieldIsComparable { - get { return (options & SigComparerOptions.PrivateScopeFieldIsComparable) != 0; } - } - - bool PrivateScopeMethodIsComparable { - get { return (options & SigComparerOptions.PrivateScopeMethodIsComparable) != 0; } - } - - bool RawSignatureCompare { - get { return (options & SigComparerOptions.RawSignatureCompare) != 0; } - } - - bool IgnoreModifiers { - get { return (options & SigComparerOptions.IgnoreModifiers) != 0; } - } - - bool MscorlibIsNotSpecial { - get { return (options & SigComparerOptions.MscorlibIsNotSpecial) != 0; } - } - - bool DontProjectWinMDRefs { - get { return (options & SigComparerOptions.DontProjectWinMDRefs) != 0; } - } - - bool DontCheckTypeEquivalence { - get { return (options & SigComparerOptions.DontCheckTypeEquivalence) != 0; } - } + bool DontCompareTypeScope => (options & SigComparerOptions.DontCompareTypeScope) != 0; + bool CompareMethodFieldDeclaringType => (options & SigComparerOptions.CompareMethodFieldDeclaringType) != 0; + bool ComparePropertyDeclaringType => (options & SigComparerOptions.ComparePropertyDeclaringType) != 0; + bool CompareEventDeclaringType => (options & SigComparerOptions.CompareEventDeclaringType) != 0; + bool CompareSentinelParams => (options & SigComparerOptions.CompareSentinelParams) != 0; + bool CompareAssemblyPublicKeyToken => (options & SigComparerOptions.CompareAssemblyPublicKeyToken) != 0; + bool CompareAssemblyVersion => (options & SigComparerOptions.CompareAssemblyVersion) != 0; + bool CompareAssemblyLocale => (options & SigComparerOptions.CompareAssemblyLocale) != 0; + bool TypeRefCanReferenceGlobalType => (options & SigComparerOptions.TypeRefCanReferenceGlobalType) != 0; + bool DontCompareReturnType => (options & SigComparerOptions.DontCompareReturnType) != 0; + bool DontSubstituteGenericParameters => (options & SigComparerOptions_DontSubstituteGenericParameters) != 0; + bool CaseInsensitiveTypeNamespaces => (options & SigComparerOptions.CaseInsensitiveTypeNamespaces) != 0; + bool CaseInsensitiveTypeNames => (options & SigComparerOptions.CaseInsensitiveTypeNames) != 0; + bool CaseInsensitiveMethodFieldNames => (options & SigComparerOptions.CaseInsensitiveMethodFieldNames) != 0; + bool CaseInsensitivePropertyNames => (options & SigComparerOptions.CaseInsensitivePropertyNames) != 0; + bool CaseInsensitiveEventNames => (options & SigComparerOptions.CaseInsensitiveEventNames) != 0; + bool PrivateScopeFieldIsComparable => (options & SigComparerOptions.PrivateScopeFieldIsComparable) != 0; + bool PrivateScopeMethodIsComparable => (options & SigComparerOptions.PrivateScopeMethodIsComparable) != 0; + bool RawSignatureCompare => (options & SigComparerOptions.RawSignatureCompare) != 0; + bool IgnoreModifiers => (options & SigComparerOptions.IgnoreModifiers) != 0; + bool MscorlibIsNotSpecial => (options & SigComparerOptions.MscorlibIsNotSpecial) != 0; + bool DontProjectWinMDRefs => (options & SigComparerOptions.DontProjectWinMDRefs) != 0; + bool DontCheckTypeEquivalence => (options & SigComparerOptions.DontCheckTypeEquivalence) != 0; + bool IgnoreMultiDimensionalArrayLowerBoundsAndSizes => (options & SigComparerOptions.IgnoreMultiDimensionalArrayLowerBoundsAndSizes) != 0; + bool ReferenceCompareForMemberDefsInSameModule => (options & SigComparerOptions.ReferenceCompareForMemberDefsInSameModule) != 0; /// /// Constructor @@ -724,9 +582,9 @@ public SigComparer(SigComparerOptions options) /// Comparison options /// The module which the comparison take place in. public SigComparer(SigComparerOptions options, ModuleDef sourceModule) { - this.recursionCounter = new RecursionCounter(); + recursionCounter = new RecursionCounter(); this.options = options; - this.genericArguments = null; + genericArguments = null; this.sourceModule = sourceModule; } @@ -761,85 +619,26 @@ int GetHashCode_Name(bool caseInsensitive, string a) { return (a ?? string.Empty).GetHashCode(); } - bool Equals_TypeNamespaces(UTF8String a, UTF8String b) { - return Equals_Names(CaseInsensitiveTypeNamespaces, a, b); - } - - bool Equals_TypeNamespaces(UTF8String a, string b) { - return Equals_Names(CaseInsensitiveTypeNamespaces, UTF8String.ToSystemStringOrEmpty(a), b); - } - - int GetHashCode_TypeNamespace(UTF8String a) { - return GetHashCode_Name(CaseInsensitiveTypeNamespaces, UTF8String.ToSystemStringOrEmpty(a)); - } - - int GetHashCode_TypeNamespace(string a) { - return GetHashCode_Name(CaseInsensitiveTypeNamespaces, a); - } - - bool Equals_TypeNames(UTF8String a, UTF8String b) { - return Equals_Names(CaseInsensitiveTypeNames, a, b); - } - - bool Equals_TypeNames(UTF8String a, string b) { - return Equals_Names(CaseInsensitiveTypeNames, UTF8String.ToSystemStringOrEmpty(a), b); - } - - int GetHashCode_TypeName(UTF8String a) { - return GetHashCode_Name(CaseInsensitiveTypeNames, UTF8String.ToSystemStringOrEmpty(a)); - } - - int GetHashCode_TypeName(string a) { - return GetHashCode_Name(CaseInsensitiveTypeNames, a); - } - - bool Equals_MethodFieldNames(UTF8String a, UTF8String b) { - return Equals_Names(CaseInsensitiveMethodFieldNames, a, b); - } - - bool Equals_MethodFieldNames(UTF8String a, string b) { - return Equals_Names(CaseInsensitiveMethodFieldNames, UTF8String.ToSystemStringOrEmpty(a), b); - } - - int GetHashCode_MethodFieldName(UTF8String a) { - return GetHashCode_Name(CaseInsensitiveMethodFieldNames, UTF8String.ToSystemStringOrEmpty(a)); - } - - int GetHashCode_MethodFieldName(string a) { - return GetHashCode_Name(CaseInsensitiveMethodFieldNames, a); - } - - bool Equals_PropertyNames(UTF8String a, UTF8String b) { - return Equals_Names(CaseInsensitivePropertyNames, a, b); - } - - bool Equals_PropertyNames(UTF8String a, string b) { - return Equals_Names(CaseInsensitivePropertyNames, UTF8String.ToSystemStringOrEmpty(a), b); - } - - int GetHashCode_PropertyName(UTF8String a) { - return GetHashCode_Name(CaseInsensitivePropertyNames, UTF8String.ToSystemStringOrEmpty(a)); - } - - int GetHashCode_PropertyName(string a) { - return GetHashCode_Name(CaseInsensitivePropertyNames, a); - } - - bool Equals_EventNames(UTF8String a, UTF8String b) { - return Equals_Names(CaseInsensitiveEventNames, a, b); - } - - bool Equals_EventNames(UTF8String a, string b) { - return Equals_Names(CaseInsensitiveEventNames, UTF8String.ToSystemStringOrEmpty(a), b); - } - - int GetHashCode_EventName(UTF8String a) { - return GetHashCode_Name(CaseInsensitiveEventNames, UTF8String.ToSystemStringOrEmpty(a)); - } - - int GetHashCode_EventName(string a) { - return GetHashCode_Name(CaseInsensitiveEventNames, a); - } + bool Equals_TypeNamespaces(UTF8String a, UTF8String b) => Equals_Names(CaseInsensitiveTypeNamespaces, a, b); + bool Equals_TypeNamespaces(UTF8String a, string b) => Equals_Names(CaseInsensitiveTypeNamespaces, UTF8String.ToSystemStringOrEmpty(a), b); + int GetHashCode_TypeNamespace(UTF8String a) => GetHashCode_Name(CaseInsensitiveTypeNamespaces, UTF8String.ToSystemStringOrEmpty(a)); + int GetHashCode_TypeNamespace(string a) => GetHashCode_Name(CaseInsensitiveTypeNamespaces, a); + bool Equals_TypeNames(UTF8String a, UTF8String b) => Equals_Names(CaseInsensitiveTypeNames, a, b); + bool Equals_TypeNames(UTF8String a, string b) => Equals_Names(CaseInsensitiveTypeNames, UTF8String.ToSystemStringOrEmpty(a), b); + int GetHashCode_TypeName(UTF8String a) => GetHashCode_Name(CaseInsensitiveTypeNames, UTF8String.ToSystemStringOrEmpty(a)); + int GetHashCode_TypeName(string a) => GetHashCode_Name(CaseInsensitiveTypeNames, a); + bool Equals_MethodFieldNames(UTF8String a, UTF8String b) => Equals_Names(CaseInsensitiveMethodFieldNames, a, b); + bool Equals_MethodFieldNames(UTF8String a, string b) => Equals_Names(CaseInsensitiveMethodFieldNames, UTF8String.ToSystemStringOrEmpty(a), b); + int GetHashCode_MethodFieldName(UTF8String a) => GetHashCode_Name(CaseInsensitiveMethodFieldNames, UTF8String.ToSystemStringOrEmpty(a)); + int GetHashCode_MethodFieldName(string a) => GetHashCode_Name(CaseInsensitiveMethodFieldNames, a); + bool Equals_PropertyNames(UTF8String a, UTF8String b) => Equals_Names(CaseInsensitivePropertyNames, a, b); + bool Equals_PropertyNames(UTF8String a, string b) => Equals_Names(CaseInsensitivePropertyNames, UTF8String.ToSystemStringOrEmpty(a), b); + int GetHashCode_PropertyName(UTF8String a) => GetHashCode_Name(CaseInsensitivePropertyNames, UTF8String.ToSystemStringOrEmpty(a)); + int GetHashCode_PropertyName(string a) => GetHashCode_Name(CaseInsensitivePropertyNames, a); + bool Equals_EventNames(UTF8String a, UTF8String b) => Equals_Names(CaseInsensitiveEventNames, a, b); + bool Equals_EventNames(UTF8String a, string b) => Equals_Names(CaseInsensitiveEventNames, UTF8String.ToSystemStringOrEmpty(a), b); + int GetHashCode_EventName(UTF8String a) => GetHashCode_Name(CaseInsensitiveEventNames, UTF8String.ToSystemStringOrEmpty(a)); + int GetHashCode_EventName(string a) => GetHashCode_Name(CaseInsensitiveEventNames, a); SigComparerOptions ClearOptions(SigComparerOptions flags) { var old = options; @@ -853,18 +652,16 @@ SigComparerOptions SetOptions(SigComparerOptions flags) { return old; } - void RestoreOptions(SigComparerOptions oldFlags) { - options = oldFlags; - } + void RestoreOptions(SigComparerOptions oldFlags) => options = oldFlags; void InitializeGenericArguments() { - if (genericArguments == null) + if (genericArguments is null) genericArguments = new GenericArguments(); } static GenericInstSig GetGenericInstanceType(IMemberRefParent parent) { var ts = parent as TypeSpec; - if (ts == null) + if (ts is null) return null; return ts.TypeSig.RemoveModifiers() as GenericInstSig; } @@ -876,7 +673,7 @@ bool Equals(IAssembly aAsm, IAssembly bAsm, TypeRef b) { // Could be an exported type. Resolve it and check again. var td = b.Resolve(sourceModule); - return td != null && Equals(aAsm, td.Module.Assembly); + return td is not null && Equals(aAsm, td.Module.Assembly); } bool Equals(IAssembly aAsm, IAssembly bAsm, ExportedType b) { @@ -884,7 +681,7 @@ bool Equals(IAssembly aAsm, IAssembly bAsm, ExportedType b) { return true; var td = b.Resolve(); - return td != null && Equals(aAsm, td.Module.Assembly); + return td is not null && Equals(aAsm, td.Module.Assembly); } bool Equals(IAssembly aAsm, TypeRef a, IAssembly bAsm, TypeRef b) { @@ -895,7 +692,7 @@ bool Equals(IAssembly aAsm, TypeRef a, IAssembly bAsm, TypeRef b) { var tda = a.Resolve(sourceModule); var tdb = b.Resolve(sourceModule); - return tda != null && tdb != null && Equals(tda.Module.Assembly, tdb.Module.Assembly); + return tda is not null && tdb is not null && Equals(tda.Module.Assembly, tdb.Module.Assembly); } bool Equals(IAssembly aAsm, ExportedType a, IAssembly bAsm, ExportedType b) { @@ -904,7 +701,7 @@ bool Equals(IAssembly aAsm, ExportedType a, IAssembly bAsm, ExportedType b) { var tda = a.Resolve(); var tdb = b.Resolve(); - return tda != null && tdb != null && Equals(tda.Module.Assembly, tdb.Module.Assembly); + return tda is not null && tdb is not null && Equals(tda.Module.Assembly, tdb.Module.Assembly); } bool Equals(IAssembly aAsm, TypeRef a, IAssembly bAsm, ExportedType b) { @@ -915,7 +712,7 @@ bool Equals(IAssembly aAsm, TypeRef a, IAssembly bAsm, ExportedType b) { var tda = a.Resolve(sourceModule); var tdb = b.Resolve(); - return tda != null && tdb != null && Equals(tda.Module.Assembly, tdb.Module.Assembly); + return tda is not null && tdb is not null && Equals(tda.Module.Assembly, tdb.Module.Assembly); } bool Equals(TypeDef a, IModule bMod, TypeRef b) { @@ -925,7 +722,7 @@ bool Equals(TypeDef a, IModule bMod, TypeRef b) { // Could be an exported type. Resolve it and check again. var td = b.Resolve(sourceModule); - if (td == null) + if (td is null) return false; if (!DontCheckTypeEquivalence) { if (TIAHelper.Equivalent(a, td)) @@ -939,11 +736,11 @@ bool Equals(TypeDef a, FileDef bFile, ExportedType b) { return true; var td = b.Resolve(); - return td != null && Equals(a.Module, td.Module) && Equals(a.DefinitionAssembly, td.DefinitionAssembly); + return td is not null && Equals(a.Module, td.Module) && Equals(a.DefinitionAssembly, td.DefinitionAssembly); } bool TypeDefScopeEquals(TypeDef a, TypeDef b) { - if (a == null || b == null) + if (a is null || b is null) return false; if (!DontCheckTypeEquivalence) { if (TIAHelper.Equivalent(a, b)) @@ -960,7 +757,7 @@ bool Equals(TypeRef a, IModule ma, TypeRef b, IModule mb) { var tda = a.Resolve(sourceModule); var tdb = b.Resolve(sourceModule); - return tda != null && tdb != null && + return tda is not null && tdb is not null && Equals(tda.Module, tdb.Module) && Equals(tda.DefinitionAssembly, tdb.DefinitionAssembly); } @@ -972,7 +769,7 @@ bool Equals(TypeRef a, IModule ma, ExportedType b, FileDef fb) { var tda = a.Resolve(sourceModule); var tdb = b.Resolve(); - return tda != null && tdb != null && + return tda is not null && tdb is not null && Equals(tda.Module, tdb.Module) && Equals(tda.DefinitionAssembly, tdb.DefinitionAssembly); } @@ -983,7 +780,7 @@ bool Equals(Assembly aAsm, IAssembly bAsm, TypeRef b) { // Could be an exported type. Resolve it and check again. var td = b.Resolve(sourceModule); - return td != null && Equals(td.Module.Assembly, aAsm); + return td is not null && Equals(td.Module.Assembly, aAsm); } bool Equals(Assembly aAsm, IAssembly bAsm, ExportedType b) { @@ -991,7 +788,7 @@ bool Equals(Assembly aAsm, IAssembly bAsm, ExportedType b) { return true; var td = b.Resolve(); - return td != null && Equals(td.Module.Assembly, aAsm); + return td is not null && Equals(td.Module.Assembly, aAsm); } bool Equals(Type a, IModule bMod, TypeRef b) { @@ -1001,7 +798,7 @@ bool Equals(Type a, IModule bMod, TypeRef b) { // Could be an exported type. Resolve it and check again. var td = b.Resolve(sourceModule); - return td != null && Equals(td.Module, a.Module) && Equals(td.DefinitionAssembly, a.Assembly); + return td is not null && Equals(td.Module, a.Module) && Equals(td.DefinitionAssembly, a.Assembly); } bool Equals(Type a, FileDef bFile, ExportedType b) { @@ -1009,7 +806,7 @@ bool Equals(Type a, FileDef bFile, ExportedType b) { return true; var td = b.Resolve(); - return td != null && Equals(td.Module, a.Module) && Equals(td.DefinitionAssembly, a.Assembly); + return td is not null && Equals(td.Module, a.Module) && Equals(td.DefinitionAssembly, a.Assembly); } /// @@ -1021,7 +818,7 @@ bool Equals(Type a, FileDef bFile, ExportedType b) { public bool Equals(IMemberRef a, IMemberRef b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -1033,15 +830,15 @@ public bool Equals(IMemberRef a, IMemberRef b) { PropertyDef pa, pb; EventDef ea, eb; - if ((ta = a as IType) != null && (tb = b as IType) != null) + if ((ta = a as IType) is not null && (tb = b as IType) is not null) result = Equals(ta, tb); - else if ((fa = a as IField) != null && (fb = b as IField) != null && fa.IsField && fb.IsField) + else if ((fa = a as IField) is not null && (fb = b as IField) is not null && fa.IsField && fb.IsField) result = Equals(fa, fb); - else if ((ma = a as IMethod) != null && (mb = b as IMethod) != null) + else if ((ma = a as IMethod) is not null && (mb = b as IMethod) is not null) result = Equals(ma, mb); - else if ((pa = a as PropertyDef) != null && (pb = b as PropertyDef) != null) + else if ((pa = a as PropertyDef) is not null && (pb = b as PropertyDef) is not null) result = Equals(pa, pb); - else if ((ea = a as EventDef) != null && (eb = b as EventDef) != null) + else if ((ea = a as EventDef) is not null && (eb = b as EventDef) is not null) result = Equals(ea, eb); else result = false; @@ -1056,7 +853,7 @@ public bool Equals(IMemberRef a, IMemberRef b) { /// The member /// The hash code public int GetHashCode(IMemberRef a) { - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -1068,15 +865,15 @@ public int GetHashCode(IMemberRef a) { PropertyDef pa; EventDef ea; - if ((ta = a as IType) != null) + if ((ta = a as IType) is not null) result = GetHashCode(ta); - else if ((fa = a as IField) != null) + else if ((fa = a as IField) is not null) result = GetHashCode(fa); - else if ((ma = a as IMethod) != null) + else if ((ma = a as IMethod) is not null) result = GetHashCode(ma); - else if ((pa = a as PropertyDef) != null) + else if ((pa = a as PropertyDef) is not null) result = GetHashCode(pa); - else if ((ea = a as EventDef) != null) + else if ((ea = a as EventDef) is not null) result = GetHashCode(ea); else result = 0; // Should never be reached @@ -1091,18 +888,14 @@ public int GetHashCode(IMemberRef a) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(ITypeDefOrRef a, ITypeDefOrRef b) { - return Equals((IType)a, (IType)b); - } + public bool Equals(ITypeDefOrRef a, ITypeDefOrRef b) => Equals((IType)a, (IType)b); /// /// Gets the hash code of a type /// /// The type /// The hash code - public int GetHashCode(ITypeDefOrRef a) { - return GetHashCode((IType)a); - } + public int GetHashCode(ITypeDefOrRef a) => GetHashCode((IType)a); /// /// Compares types @@ -1113,7 +906,7 @@ public int GetHashCode(ITypeDefOrRef a) { public bool Equals(IType a, IType b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -1125,55 +918,55 @@ public bool Equals(IType a, IType b) { TypeSig sa, sb; ExportedType eta, etb; - if ((tda = a as TypeDef) != null & (tdb = b as TypeDef) != null) + if ((tda = a as TypeDef) is not null & (tdb = b as TypeDef) is not null) result = Equals(tda, tdb); - else if ((tra = a as TypeRef) != null & (trb = b as TypeRef) != null) + else if ((tra = a as TypeRef) is not null & (trb = b as TypeRef) is not null) result = Equals(tra, trb); - else if ((tsa = a as TypeSpec) != null & (tsb = b as TypeSpec) != null) + else if ((tsa = a as TypeSpec) is not null & (tsb = b as TypeSpec) is not null) result = Equals(tsa, tsb); - else if ((sa = a as TypeSig) != null & (sb = b as TypeSig) != null) + else if ((sa = a as TypeSig) is not null & (sb = b as TypeSig) is not null) result = Equals(sa, sb); - else if ((eta = a as ExportedType) != null & (etb = b as ExportedType) != null) + else if ((eta = a as ExportedType) is not null & (etb = b as ExportedType) is not null) result = Equals(eta, etb); - else if (tda != null && trb != null) + else if (tda is not null && trb is not null) result = Equals(tda, trb); // TypeDef vs TypeRef - else if (tra != null && tdb != null) + else if (tra is not null && tdb is not null) result = Equals(tdb, tra); // TypeDef vs TypeRef - else if (tda != null && tsb != null) + else if (tda is not null && tsb is not null) result = Equals(tda, tsb); // TypeDef vs TypeSpec - else if (tsa != null && tdb != null) + else if (tsa is not null && tdb is not null) result = Equals(tdb, tsa); // TypeDef vs TypeSpec - else if (tda != null && sb != null) + else if (tda is not null && sb is not null) result = Equals(tda, sb); // TypeDef vs TypeSig - else if (sa != null && tdb != null) + else if (sa is not null && tdb is not null) result = Equals(tdb, sa); // TypeDef vs TypeSig - else if (tda != null && etb != null) + else if (tda is not null && etb is not null) result = Equals(tda, etb); // TypeDef vs ExportedType - else if (eta != null && tdb != null) + else if (eta is not null && tdb is not null) result = Equals(tdb, eta); // TypeDef vs ExportedType - else if (tra != null && tsb != null) + else if (tra is not null && tsb is not null) result = Equals(tra, tsb); // TypeRef vs TypeSpec - else if (tsa != null && trb != null) + else if (tsa is not null && trb is not null) result = Equals(trb, tsa); // TypeRef vs TypeSpec - else if (tra != null && sb != null) + else if (tra is not null && sb is not null) result = Equals(tra, sb); // TypeRef vs TypeSig - else if (sa != null && trb != null) + else if (sa is not null && trb is not null) result = Equals(trb, sa); // TypeRef vs TypeSig - else if (tra != null && etb != null) + else if (tra is not null && etb is not null) result = Equals(tra, etb); // TypeRef vs ExportedType - else if (eta != null && trb != null) + else if (eta is not null && trb is not null) result = Equals(trb, eta); // TypeRef vs ExportedType - else if (tsa != null && sb != null) + else if (tsa is not null && sb is not null) result = Equals(tsa, sb); // TypeSpec vs TypeSig - else if (sa != null && tsb != null) + else if (sa is not null && tsb is not null) result = Equals(tsb, sa); // TypeSpec vs TypeSig - else if (tsa != null && etb != null) + else if (tsa is not null && etb is not null) result = Equals(tsa, etb); // TypeSpec vs ExportedType - else if (eta != null && tsb != null) + else if (eta is not null && tsb is not null) result = Equals(tsb, eta); // TypeSpec vs ExportedType - else if (sa != null && etb != null) + else if (sa is not null && etb is not null) result = Equals(sa, etb); // TypeSig vs ExportedType - else if (eta != null && sb != null) + else if (eta is not null && sb is not null) result = Equals(sb, eta); // TypeSig vs ExportedType else result = false; // Should never be reached @@ -1188,7 +981,7 @@ public bool Equals(IType a, IType b) { /// The type /// The hash code public int GetHashCode(IType a) { - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -1200,15 +993,15 @@ public int GetHashCode(IType a) { TypeSig sig; ExportedType et; - if ((td = a as TypeDef) != null) + if ((td = a as TypeDef) is not null) hash = GetHashCode(td); - else if ((tr = a as TypeRef) != null) + else if ((tr = a as TypeRef) is not null) hash = GetHashCode(tr); - else if ((ts = a as TypeSpec) != null) + else if ((ts = a as TypeSpec) is not null) hash = GetHashCode(ts); - else if ((sig = a as TypeSig) != null) + else if ((sig = a as TypeSig) is not null) hash = GetHashCode(sig); - else if ((et = a as ExportedType) != null) + else if ((et = a as ExportedType) is not null) hash = GetHashCode(et); else hash = 0; // Should never be reached @@ -1223,9 +1016,7 @@ public int GetHashCode(IType a) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(TypeRef a, TypeDef b) { - return Equals(b, a); - } + public bool Equals(TypeRef a, TypeDef b) => Equals(b, a); /// /// Compares types @@ -1236,7 +1027,7 @@ public bool Equals(TypeRef a, TypeDef b) { public bool Equals(TypeDef a, TypeRef b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -1249,7 +1040,7 @@ public bool Equals(TypeDef a, TypeRef b) { if (!DontProjectWinMDRefs) { var tra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); b = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b) ?? b; - if (tra != null) { + if (tra is not null) { result = Equals(tra, b); goto exit; } @@ -1259,19 +1050,19 @@ public bool Equals(TypeDef a, TypeRef b) { if (!Equals_TypeNames(a.Name, b.Name) || !Equals_TypeNamespaces(a.Namespace, b.Namespace)) result = false; - else if ((dtb = scope as TypeRef) != null) // nested type + else if ((dtb = scope as TypeRef) is not null) // nested type result = Equals(a.DeclaringType, dtb); // Compare enclosing types - else if (a.DeclaringType != null) { + else if (a.DeclaringType is not null) { // a is nested, b isn't result = false; } else if (DontCompareTypeScope) result = true; - else if ((bMod = scope as IModule) != null) // 'b' is defined in the same assembly as 'a' + else if ((bMod = scope as IModule) is not null) // 'b' is defined in the same assembly as 'a' result = Equals(a, bMod, b); - else if ((bAsm = scope as AssemblyRef) != null) { + else if ((bAsm = scope as AssemblyRef) is not null) { var aMod = a.Module; - result = aMod != null && Equals(aMod.Assembly, bAsm, b); + result = aMod is not null && Equals(aMod.Assembly, bAsm, b); if (!result) { if (!DontCheckTypeEquivalence) { var tdb = b.Resolve(); @@ -1281,7 +1072,7 @@ public bool Equals(TypeDef a, TypeRef b) { } else { result = false; - //TODO: Handle the case where scope == null + //TODO: Handle the case where scope is null } if (result && !TypeRefCanReferenceGlobalType && a.IsGlobalModuleType) @@ -1297,9 +1088,7 @@ public bool Equals(TypeDef a, TypeRef b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(ExportedType a, TypeDef b) { - return Equals(b, a); - } + public bool Equals(ExportedType a, TypeDef b) => Equals(b, a); /// /// Compares types @@ -1310,7 +1099,7 @@ public bool Equals(ExportedType a, TypeDef b) { public bool Equals(TypeDef a, ExportedType b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -1322,7 +1111,7 @@ public bool Equals(TypeDef a, ExportedType b) { if (!DontProjectWinMDRefs) { var tra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); b = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b) ?? b; - if (tra != null) { + if (tra is not null) { result = Equals(tra, b); goto exit; } @@ -1332,20 +1121,20 @@ public bool Equals(TypeDef a, ExportedType b) { if (!Equals_TypeNames(a.Name, b.TypeName) || !Equals_TypeNamespaces(a.Namespace, b.TypeNamespace)) result = false; - else if ((dtb = scope as ExportedType) != null) { // nested type + else if ((dtb = scope as ExportedType) is not null) { // nested type result = Equals(a.DeclaringType, dtb); // Compare enclosing types } - else if (a.DeclaringType != null) { + else if (a.DeclaringType is not null) { result = false; // a is nested, b isn't } else if (DontCompareTypeScope) result = true; else { - if ((bFile = scope as FileDef) != null) + if ((bFile = scope as FileDef) is not null) result = Equals(a, bFile, b); - else if ((bAsm = scope as AssemblyRef) != null) { + else if ((bAsm = scope as AssemblyRef) is not null) { var aMod = a.Module; - result = aMod != null && Equals(aMod.Assembly, bAsm, b); + result = aMod is not null && Equals(aMod.Assembly, bAsm, b); } else result = false; @@ -1368,9 +1157,7 @@ public bool Equals(TypeDef a, ExportedType b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(TypeSpec a, TypeDef b) { - return Equals(b, a); - } + public bool Equals(TypeSpec a, TypeDef b) => Equals(b, a); /// /// Compares types @@ -1381,7 +1168,7 @@ public bool Equals(TypeSpec a, TypeDef b) { public bool Equals(TypeDef a, TypeSpec b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; return Equals(a, b.TypeSig); } @@ -1392,9 +1179,7 @@ public bool Equals(TypeDef a, TypeSpec b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(TypeSig a, TypeDef b) { - return Equals(b, a); - } + public bool Equals(TypeSig a, TypeDef b) => Equals(b, a); /// /// Compares types @@ -1405,7 +1190,7 @@ public bool Equals(TypeSig a, TypeDef b) { public bool Equals(TypeDef a, TypeSig b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -1415,8 +1200,7 @@ public bool Equals(TypeDef a, TypeSig b) { // If this code gets updated, update GetHashCode(TypeSig), // Equals(TypeRef,TypeSig) and Equals(TypeSig,ExportedType) too //************************************************************* - var b2 = b as TypeDefOrRefSig; - if (b2 != null) + if (b is TypeDefOrRefSig b2) result = Equals(a, (IType)b2.TypeDefOrRef); else if (b is ModifierSig || b is PinnedSig) result = Equals(a, b.Next); @@ -1433,9 +1217,7 @@ public bool Equals(TypeDef a, TypeSig b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(TypeSpec a, TypeRef b) { - return Equals(b, a); - } + public bool Equals(TypeSpec a, TypeRef b) => Equals(b, a); /// /// Compares types @@ -1446,7 +1228,7 @@ public bool Equals(TypeSpec a, TypeRef b) { public bool Equals(TypeRef a, TypeSpec b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; return Equals(a, b.TypeSig); } @@ -1457,9 +1239,7 @@ public bool Equals(TypeRef a, TypeSpec b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(ExportedType a, TypeRef b) { - return Equals(b, a); - } + public bool Equals(ExportedType a, TypeRef b) => Equals(b, a); /// /// Compares types @@ -1470,7 +1250,7 @@ public bool Equals(ExportedType a, TypeRef b) { public bool Equals(TypeRef a, ExportedType b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -1493,9 +1273,7 @@ public bool Equals(TypeRef a, ExportedType b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(TypeSig a, TypeRef b) { - return Equals(b, a); - } + public bool Equals(TypeSig a, TypeRef b) => Equals(b, a); /// /// Compares types @@ -1506,7 +1284,7 @@ public bool Equals(TypeSig a, TypeRef b) { public bool Equals(TypeRef a, TypeSig b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -1516,8 +1294,7 @@ public bool Equals(TypeRef a, TypeSig b) { // If this code gets updated, update GetHashCode(TypeSig), // Equals(TypeRef,TypeSig) and Equals(TypeSig,ExportedType) too //************************************************************* - var b2 = b as TypeDefOrRefSig; - if (b2 != null) + if (b is TypeDefOrRefSig b2) result = Equals(a, (IType)b2.TypeDefOrRef); else if (b is ModifierSig || b is PinnedSig) result = Equals(a, b.Next); @@ -1534,9 +1311,7 @@ public bool Equals(TypeRef a, TypeSig b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(TypeSig a, TypeSpec b) { - return Equals(b, a); - } + public bool Equals(TypeSig a, TypeSpec b) => Equals(b, a); /// /// Compares types @@ -1547,7 +1322,7 @@ public bool Equals(TypeSig a, TypeSpec b) { public bool Equals(TypeSpec a, TypeSig b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; return Equals(a.TypeSig, b); } @@ -1558,9 +1333,7 @@ public bool Equals(TypeSpec a, TypeSig b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(ExportedType a, TypeSpec b) { - return Equals(b, a); - } + public bool Equals(ExportedType a, TypeSpec b) => Equals(b, a); /// /// Compares types @@ -1571,7 +1344,7 @@ public bool Equals(ExportedType a, TypeSpec b) { public bool Equals(TypeSpec a, ExportedType b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; return Equals(a.TypeSig, b); } @@ -1582,9 +1355,7 @@ public bool Equals(TypeSpec a, ExportedType b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(ExportedType a, TypeSig b) { - return Equals(b, a); - } + public bool Equals(ExportedType a, TypeSig b) => Equals(b, a); /// /// Compares types @@ -1595,7 +1366,7 @@ public bool Equals(ExportedType a, TypeSig b) { public bool Equals(TypeSig a, ExportedType b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -1605,8 +1376,7 @@ public bool Equals(TypeSig a, ExportedType b) { // If this code gets updated, update GetHashCode(TypeSig), // Equals(TypeRef,TypeSig) and Equals(TypeSig,ExportedType) too //************************************************************* - var a2 = a as TypeDefOrRefSig; - if (a2 != null) + if (a is TypeDefOrRefSig a2) result = Equals(a2.TypeDefOrRef, b); else if (a is ModifierSig || a is PinnedSig) result = Equals(a.Next, b); @@ -1632,7 +1402,7 @@ int GetHashCodeGlobalType() { public bool Equals(TypeRef a, TypeRef b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -1661,7 +1431,7 @@ public int GetHashCode(TypeRef a) { // ************************************************************************************ // See GetHashCode(Type) for the reason why null returns GetHashCodeGlobalType() - if (a == null) + if (a is null) return TypeRefCanReferenceGlobalType ? GetHashCodeGlobalType() : 0; if (!DontProjectWinMDRefs) a = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a) ?? a; @@ -1684,7 +1454,7 @@ public int GetHashCode(TypeRef a) { public bool Equals(ExportedType a, ExportedType b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -1713,7 +1483,7 @@ public int GetHashCode(ExportedType a) { // ************************************************************************************ // See GetHashCode(Type) for the reason why null returns GetHashCodeGlobalType() - if (a == null) + if (a is null) return TypeRefCanReferenceGlobalType ? GetHashCodeGlobalType() : 0; if (!DontProjectWinMDRefs) a = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a) ?? a; @@ -1735,7 +1505,9 @@ public int GetHashCode(ExportedType a) { public bool Equals(TypeDef a, TypeDef b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) + return false; + if (ReferenceCompareForMemberDefsInSameModule && InSameModule(a, b)) return false; if (!recursionCounter.Increment()) return false; @@ -1745,7 +1517,7 @@ public bool Equals(TypeDef a, TypeDef b) { if (!DontProjectWinMDRefs) { var tra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); var trb = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b); - if (tra != null || trb != null) { + if (tra is not null || trb is not null) { result = Equals((IType)tra ?? a, (IType)trb ?? b); goto exit; } @@ -1772,17 +1544,17 @@ public int GetHashCode(TypeDef a) { // ************************************************************************************ // See GetHashCode(Type) for the reason why null returns GetHashCodeGlobalType() - if (a == null || a.IsGlobalModuleType) + if (a is null || a.IsGlobalModuleType) return GetHashCodeGlobalType(); if (!DontProjectWinMDRefs) { var tra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); - if (tra != null) + if (tra is not null) return GetHashCode(tra); } int hash; hash = GetHashCode_TypeName(a.Name); - if (a.DeclaringType != null) + if (a.DeclaringType is not null) hash += HASHCODE_MAGIC_NESTED_TYPE; else hash += GetHashCode_TypeNamespace(a.Namespace); @@ -1798,7 +1570,7 @@ public int GetHashCode(TypeDef a) { public bool Equals(TypeSpec a, TypeSpec b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -1815,7 +1587,7 @@ public bool Equals(TypeSpec a, TypeSpec b) { /// The type /// The hash code public int GetHashCode(TypeSpec a) { - if (a == null) + if (a is null) return 0; return GetHashCode(a.TypeSig); } @@ -1829,13 +1601,13 @@ public int GetHashCode(TypeSpec a) { bool EqualsResolutionScope(TypeRef a, TypeRef b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; var ra = a.ResolutionScope; var rb = b.ResolutionScope; if (ra == rb) return true; - if (ra == null || rb == null) + if (ra is null || rb is null) return false; if (!recursionCounter.Increment()) return false; @@ -1848,29 +1620,29 @@ bool EqualsResolutionScope(TypeRef a, TypeRef b) { bool resolveCheck = true; // if one of them is a TypeRef, the other one must be too - if ((ea = ra as TypeRef) != null | (eb = rb as TypeRef) != null) { + if ((ea = ra as TypeRef) is not null | (eb = rb as TypeRef) is not null) { result = Equals(ea, eb); resolveCheck = false; } else if (DontCompareTypeScope) result = true; // only compare if both are modules - else if ((ma = ra as IModule) != null & (mb = rb as IModule) != null) + else if ((ma = ra as IModule) is not null & (mb = rb as IModule) is not null) result = Equals(a, ma, b, mb); // only compare if both are assemblies - else if ((aa = ra as AssemblyRef) != null & (ab = rb as AssemblyRef) != null) + else if ((aa = ra as AssemblyRef) is not null & (ab = rb as AssemblyRef) is not null) result = Equals(aa, a, ab, b); - else if (aa != null && rb is ModuleRef) { + else if (aa is not null && rb is ModuleRef) { var bMod = b.Module; - result = bMod != null && Equals(bMod.Assembly, b, aa, a); + result = bMod is not null && Equals(bMod.Assembly, b, aa, a); } - else if (ab != null && ra is ModuleRef) { + else if (ab is not null && ra is ModuleRef) { var aMod = a.Module; - result = aMod != null && Equals(aMod.Assembly, a, ab, b); + result = aMod is not null && Equals(aMod.Assembly, a, ab, b); } - else if (aa != null && (modDef = rb as ModuleDef) != null) + else if (aa is not null && (modDef = rb as ModuleDef) is not null) result = Equals(modDef.Assembly, aa, a); - else if (ab != null && (modDef = ra as ModuleDef) != null) + else if (ab is not null && (modDef = ra as ModuleDef) is not null) result = Equals(modDef.Assembly, ab, b); else { result = false; @@ -1880,7 +1652,7 @@ bool EqualsResolutionScope(TypeRef a, TypeRef b) { if (!DontCheckTypeEquivalence) { var td1 = a.Resolve(); var td2 = b.Resolve(); - if (td1 != null && td2 != null) + if (td1 is not null && td2 is not null) result = TypeDefScopeEquals(td1, td2); } } @@ -1898,13 +1670,13 @@ bool EqualsResolutionScope(TypeRef a, TypeRef b) { bool EqualsImplementation(ExportedType a, ExportedType b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; var ia = a.Implementation; var ib = b.Implementation; if (ia == ib) return true; - if (ia == null || ib == null) + if (ia is null || ib is null) return false; if (!recursionCounter.Increment()) return false; @@ -1916,21 +1688,21 @@ bool EqualsImplementation(ExportedType a, ExportedType b) { bool checkResolve = true; // if one of them is an ExportedType, the other one must be too - if ((ea = ia as ExportedType) != null | (eb = ib as ExportedType) != null) { + if ((ea = ia as ExportedType) is not null | (eb = ib as ExportedType) is not null) { result = Equals(ea, eb); checkResolve = false; } else if (DontCompareTypeScope) result = true; // only compare if both are files - else if ((fa = ia as FileDef) != null & (fb = ib as FileDef) != null) + else if ((fa = ia as FileDef) is not null & (fb = ib as FileDef) is not null) result = Equals(fa, fb); // only compare if both are assemblies - else if ((aa = ia as AssemblyRef) != null & (ab = ib as AssemblyRef) != null) + else if ((aa = ia as AssemblyRef) is not null & (ab = ib as AssemblyRef) is not null) result = Equals(aa, a, ab, b); - else if (fa != null && ab != null) + else if (fa is not null && ab is not null) result = Equals(a.DefinitionAssembly, ab, b); - else if (fb != null && aa != null) + else if (fb is not null && aa is not null) result = Equals(b.DefinitionAssembly, aa, a); else { result = false; @@ -1939,7 +1711,7 @@ bool EqualsImplementation(ExportedType a, ExportedType b) { if (!result && checkResolve && !DontCheckTypeEquivalence) { var td1 = a.Resolve(); var td2 = b.Resolve(); - if (td1 != null && td2 != null) + if (td1 is not null && td2 is not null) result = TypeDefScopeEquals(td1, td2); } @@ -1956,13 +1728,13 @@ bool EqualsImplementation(ExportedType a, ExportedType b) { bool EqualsScope(TypeRef a, ExportedType b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; var ra = a.ResolutionScope; var ib = b.Implementation; if (ra == ib) return true; - if (ra == null || ib == null) + if (ra is null || ib is null) return false; if (!recursionCounter.Increment()) return false; @@ -1976,19 +1748,19 @@ bool EqualsScope(TypeRef a, ExportedType b) { bool checkResolve = true; // If one is a nested type, the other one must be too - if ((ea = ra as TypeRef) != null | (eb = ib as ExportedType) != null) { + if ((ea = ra as TypeRef) is not null | (eb = ib as ExportedType) is not null) { result = Equals(ea, eb); checkResolve = false; } else if (DontCompareTypeScope) result = true; - else if ((ma = ra as IModule) != null & (fb = ib as FileDef) != null) + else if ((ma = ra as IModule) is not null & (fb = ib as FileDef) is not null) result = Equals(a, ma, b, fb); - else if ((aa = ra as AssemblyRef) != null & (ab = ib as AssemblyRef) != null) + else if ((aa = ra as AssemblyRef) is not null & (ab = ib as AssemblyRef) is not null) result = Equals(aa, a, ab, b); - else if (ma != null && ab != null) + else if (ma is not null && ab is not null) result = Equals(a.DefinitionAssembly, ab, b); - else if (fb != null && aa != null) + else if (fb is not null && aa is not null) result = Equals(b.DefinitionAssembly, aa, a); else { checkResolve = false; @@ -1997,7 +1769,7 @@ bool EqualsScope(TypeRef a, ExportedType b) { if (!result && checkResolve && !DontCheckTypeEquivalence) { var td1 = a.Resolve(); var td2 = b.Resolve(); - if (td1 != null && td2 != null) + if (td1 is not null && td2 is not null) result = TypeDefScopeEquals(td1, td2); } @@ -2014,7 +1786,7 @@ bool EqualsScope(TypeRef a, ExportedType b) { bool Equals(FileDef a, FileDef b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; return UTF8String.CaseInsensitiveEquals(a.Name, b.Name); @@ -2029,7 +1801,7 @@ bool Equals(FileDef a, FileDef b) { bool Equals(IModule a, FileDef b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; //TODO: You should compare against the module's file name, not the name in the metadata! @@ -2045,7 +1817,7 @@ bool Equals(IModule a, FileDef b) { internal bool Equals(IModule a, IModule b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b)) return true; @@ -2053,26 +1825,16 @@ internal bool Equals(IModule a, IModule b) { return UTF8String.CaseInsensitiveEquals(a.Name, b.Name); } - static bool IsCorLib(ModuleDef a) { - return a != null && a.IsManifestModule && a.Assembly.IsCorLib(); - } + static bool IsCorLib(ModuleDef a) => a is not null && a.IsManifestModule && a.Assembly.IsCorLib(); static bool IsCorLib(IModule a) { var mod = a as ModuleDef; - return mod != null && mod.IsManifestModule && mod.Assembly.IsCorLib(); + return mod is not null && mod.IsManifestModule && mod.Assembly.IsCorLib(); } - static bool IsCorLib(Module a) { - return a != null && a.Assembly.ManifestModule == a && a.Assembly == typeof(void).Assembly; - } - - static bool IsCorLib(IAssembly a) { - return a.IsCorLib(); - } - - static bool IsCorLib(Assembly a) { - return a == typeof(void).Assembly; - } + static bool IsCorLib(Module a) => a is not null && a.Assembly.ManifestModule == a && a.Assembly == typeof(void).Assembly; + static bool IsCorLib(IAssembly a) => a.IsCorLib(); + static bool IsCorLib(Assembly a) => a == typeof(void).Assembly; /// /// Compares modules @@ -2083,7 +1845,7 @@ static bool IsCorLib(Assembly a) { bool Equals(ModuleDef a, ModuleDef b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b)) return true; @@ -2105,7 +1867,7 @@ bool Equals(ModuleDef a, ModuleDef b) { bool Equals(IAssembly a, IAssembly b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b)) return true; @@ -2134,7 +1896,7 @@ public bool Equals(TypeSig a, TypeSig b) { } if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -2185,8 +1947,9 @@ public bool Equals(TypeSig a, TypeSig b) { case ElementType.Array: ArraySig ara = a as ArraySig, arb = b as ArraySig; result = ara.Rank == arb.Rank && - Equals(ara.Sizes, arb.Sizes) && - Equals(ara.LowerBounds, arb.LowerBounds) && + (IgnoreMultiDimensionalArrayLowerBoundsAndSizes || + (Equals(ara.Sizes, arb.Sizes) && + Equals(ara.LowerBounds, arb.LowerBounds))) && Equals(a.Next, b.Next); break; @@ -2209,7 +1972,7 @@ public bool Equals(TypeSig a, TypeSig b) { if (RawSignatureCompare) { var gt1 = gia.GenericType; var gt2 = gib.GenericType; - result = TokenEquals(gt1 == null ? null : gt1.TypeDefOrRef, gt2 == null ? null : gt2.TypeDefOrRef) && + result = TokenEquals(gt1?.TypeDefOrRef, gt2?.TypeDefOrRef) && Equals(gia.GenericArguments, gib.GenericArguments); } else { @@ -2256,7 +2019,7 @@ public bool Equals(TypeSig a, TypeSig b) { static bool TokenEquals(ITypeDefOrRef a, ITypeDefOrRef b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; return a.MDToken == b.MDToken; } @@ -2266,18 +2029,23 @@ static bool TokenEquals(ITypeDefOrRef a, ITypeDefOrRef b) { /// /// The type /// The hash code - public int GetHashCode(TypeSig a) { + public int GetHashCode(TypeSig a) => GetHashCode(a, true); + + int GetHashCode(TypeSig a, bool substituteGenericParameters) { // ******************************************** // IMPORTANT: This must match GetHashCode(Type) // ******************************************** - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; - if (genericArguments != null) + if (substituteGenericParameters && genericArguments is not null) { + var t = a; a = genericArguments.Resolve(a); + substituteGenericParameters = t == a; + } switch (a.ElementType) { case ElementType.Void: @@ -2310,15 +2078,15 @@ public int GetHashCode(TypeSig a) { break; case ElementType.Ptr: - hash = HASHCODE_MAGIC_ET_PTR + GetHashCode(a.Next); + hash = HASHCODE_MAGIC_ET_PTR + GetHashCode(a.Next, substituteGenericParameters); break; case ElementType.ByRef: - hash = HASHCODE_MAGIC_ET_BYREF + GetHashCode(a.Next); + hash = HASHCODE_MAGIC_ET_BYREF + GetHashCode(a.Next, substituteGenericParameters); break; case ElementType.SZArray: - hash = HASHCODE_MAGIC_ET_SZARRAY + GetHashCode(a.Next); + hash = HASHCODE_MAGIC_ET_SZARRAY + GetHashCode(a.Next, substituteGenericParameters); break; case ElementType.CModReqd: @@ -2326,13 +2094,14 @@ public int GetHashCode(TypeSig a) { case ElementType.Pinned: // When comparing an ExportedType/TypeDef/TypeRef to a ModifierSig/PinnedSig, // the ET is ignored, so we must ignore it when calculating the hash. - hash = GetHashCode(a.Next); + hash = GetHashCode(a.Next, substituteGenericParameters); break; case ElementType.Array: // Don't include sizes and lower bounds since GetHashCode(Type) doesn't (and can't). - ArraySig ara = (ArraySig)a; - hash = HASHCODE_MAGIC_ET_ARRAY + (int)ara.Rank + GetHashCode(ara.Next); + // Also, if IgnoreMultiDimensionArrayLowerBoundsAndSizes is set, we shouldn't include them either. + var ara = (ArraySig)a; + hash = HASHCODE_MAGIC_ET_ARRAY + (int)ara.Rank + GetHashCode(ara.Next, substituteGenericParameters); break; case ElementType.Var: @@ -2346,15 +2115,8 @@ public int GetHashCode(TypeSig a) { case ElementType.GenericInst: var gia = (GenericInstSig)a; hash = HASHCODE_MAGIC_ET_GENERICINST; - if (SubstituteGenericParameters) { - InitializeGenericArguments(); - genericArguments.PushTypeArgs(gia.GenericArguments); - hash += GetHashCode(gia.GenericType); - genericArguments.PopTypeArgs(); - } - else - hash += GetHashCode(gia.GenericType); - hash += GetHashCode(gia.GenericArguments); + hash += GetHashCode(gia.GenericType, substituteGenericParameters); + hash += GetHashCode(gia.GenericArguments, substituteGenericParameters); break; case ElementType.FnPtr: @@ -2362,11 +2124,11 @@ public int GetHashCode(TypeSig a) { break; case ElementType.ValueArray: - hash = HASHCODE_MAGIC_ET_VALUEARRAY + (int)(a as ValueArraySig).Size + GetHashCode(a.Next); + hash = HASHCODE_MAGIC_ET_VALUEARRAY + (int)(a as ValueArraySig).Size + GetHashCode(a.Next, substituteGenericParameters); break; case ElementType.Module: - hash = HASHCODE_MAGIC_ET_MODULE + (int)(a as ModuleSig).Index + GetHashCode(a.Next); + hash = HASHCODE_MAGIC_ET_MODULE + (int)(a as ModuleSig).Index + GetHashCode(a.Next, substituteGenericParameters); break; case ElementType.End: @@ -2390,7 +2152,7 @@ public int GetHashCode(TypeSig a) { public bool Equals(IList a, IList b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -2416,17 +2178,19 @@ public bool Equals(IList a, IList b) { /// /// The type list /// The hash code - public int GetHashCode(IList a) { + public int GetHashCode(IList a) => GetHashCode(a, true); + + int GetHashCode(IList a, bool substituteGenericParameters) { //************************************************************************ // IMPORTANT: This code must match any other GetHashCode(IList) //************************************************************************ - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; uint hash = 0; for (int i = 0; i < a.Count; i++) { - hash += (uint)GetHashCode(a[i]); + hash += (uint)GetHashCode(a[i], substituteGenericParameters); hash = (hash << 13) | (hash >> 19); } recursionCounter.Decrement(); @@ -2436,7 +2200,7 @@ public int GetHashCode(IList a) { bool Equals(IList a, IList b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (a.Count != b.Count) return false; @@ -2450,7 +2214,7 @@ bool Equals(IList a, IList b) { bool Equals(IList a, IList b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (a.Count != b.Count) return false; @@ -2470,7 +2234,7 @@ bool Equals(IList a, IList b) { public bool Equals(CallingConventionSig a, CallingConventionSig b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -2488,26 +2252,26 @@ public bool Equals(CallingConventionSig a, CallingConventionSig b) { case CallingConvention.VarArg: case CallingConvention.Property: case CallingConvention.NativeVarArg: + case CallingConvention.Unmanaged: MethodBaseSig ma = a as MethodBaseSig, mb = b as MethodBaseSig; - result = ma != null && mb != null && Equals(ma, mb); + result = ma is not null && mb is not null && Equals(ma, mb); break; case CallingConvention.Field: FieldSig fa = a as FieldSig, fb = b as FieldSig; - result = fa != null && fb != null && Equals(fa, fb); + result = fa is not null && fb is not null && Equals(fa, fb); break; case CallingConvention.LocalSig: LocalSig la = a as LocalSig, lb = b as LocalSig; - result = la != null && lb != null && Equals(la, lb); + result = la is not null && lb is not null && Equals(la, lb); break; case CallingConvention.GenericInst: GenericInstMethodSig ga = a as GenericInstMethodSig, gb = b as GenericInstMethodSig; - result = ga != null && gb != null && Equals(ga, gb); + result = ga is not null && gb is not null && Equals(ga, gb); break; - case CallingConvention.Unmanaged: default: result = false; break; @@ -2524,7 +2288,7 @@ public bool Equals(CallingConventionSig a, CallingConventionSig b) { /// The sig /// The hash code public int GetHashCode(CallingConventionSig a) { - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -2539,26 +2303,26 @@ public int GetHashCode(CallingConventionSig a) { case CallingConvention.VarArg: case CallingConvention.Property: case CallingConvention.NativeVarArg: - MethodBaseSig ma = a as MethodBaseSig; - hash = ma == null ? 0 : GetHashCode(ma); + case CallingConvention.Unmanaged: + var ma = a as MethodBaseSig; + hash = ma is null ? 0 : GetHashCode(ma); break; case CallingConvention.Field: - FieldSig fa = a as FieldSig; - hash = fa == null ? 0 : GetHashCode(fa); + var fa = a as FieldSig; + hash = fa is null ? 0 : GetHashCode(fa); break; case CallingConvention.LocalSig: - LocalSig la = a as LocalSig; - hash = la == null ? 0 : GetHashCode(la); + var la = a as LocalSig; + hash = la is null ? 0 : GetHashCode(la); break; case CallingConvention.GenericInst: - GenericInstMethodSig ga = a as GenericInstMethodSig; - hash = ga == null ? 0 : GetHashCode(ga); + var ga = a as GenericInstMethodSig; + hash = ga is null ? 0 : GetHashCode(ga); break; - case CallingConvention.Unmanaged: default: hash = GetHashCode_CallingConvention(a); break; @@ -2577,7 +2341,7 @@ public int GetHashCode(CallingConventionSig a) { public bool Equals(MethodBaseSig a, MethodBaseSig b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -2598,7 +2362,7 @@ public bool Equals(MethodBaseSig a, MethodBaseSig b) { /// The method/property sig /// The hash code public int GetHashCode(MethodBaseSig a) { - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -2617,9 +2381,7 @@ public int GetHashCode(MethodBaseSig a) { return hash; } - int GetHashCode_CallingConvention(CallingConventionSig a) { - return GetHashCode(a.GetCallingConvention()); - } + int GetHashCode_CallingConvention(CallingConventionSig a) => GetHashCode(a.GetCallingConvention()); int GetHashCode(CallingConvention a) { //******************************************************************* @@ -2655,7 +2417,7 @@ int GetHashCode(CallingConvention a) { public bool Equals(FieldSig a, FieldSig b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -2672,7 +2434,7 @@ public bool Equals(FieldSig a, FieldSig b) { /// The field sig /// The hash code public int GetHashCode(FieldSig a) { - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -2693,7 +2455,7 @@ public int GetHashCode(FieldSig a) { public bool Equals(LocalSig a, LocalSig b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -2710,7 +2472,7 @@ public bool Equals(LocalSig a, LocalSig b) { /// The local sig /// The hash code public int GetHashCode(LocalSig a) { - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -2731,7 +2493,7 @@ public int GetHashCode(LocalSig a) { public bool Equals(GenericInstMethodSig a, GenericInstMethodSig b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -2748,7 +2510,7 @@ public bool Equals(GenericInstMethodSig a, GenericInstMethodSig b) { /// The generic inst method sig /// The hash code public int GetHashCode(GenericInstMethodSig a) { - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -2769,7 +2531,7 @@ public int GetHashCode(GenericInstMethodSig a) { public bool Equals(IMethod a, IMethod b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -2779,15 +2541,15 @@ public bool Equals(IMethod a, IMethod b) { MemberRef mra, mrb; MethodSpec msa, msb; - if ((mda = a as MethodDef) != null & (mdb = b as MethodDef) != null) + if ((mda = a as MethodDef) is not null & (mdb = b as MethodDef) is not null) result = Equals(mda, mdb); - else if ((mra = a as MemberRef) != null & (mrb = b as MemberRef) != null) + else if ((mra = a as MemberRef) is not null & (mrb = b as MemberRef) is not null) result = Equals(mra, mrb); - else if ((msa = a as MethodSpec) != null && (msb = b as MethodSpec) != null) + else if ((msa = a as MethodSpec) is not null && (msb = b as MethodSpec) is not null) result = Equals(msa, msb); - else if (mda != null && mrb != null) + else if (mda is not null && mrb is not null) result = Equals(mda, mrb); - else if (mra != null && mdb != null) + else if (mra is not null && mdb is not null) result = Equals(mdb, mra); else result = false; @@ -2802,7 +2564,7 @@ public bool Equals(IMethod a, IMethod b) { /// The method /// The hash code public int GetHashCode(IMethod a) { - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -2812,11 +2574,11 @@ public int GetHashCode(IMethod a) { MemberRef mra; MethodSpec msa; - if ((mda = a as MethodDef) != null) + if ((mda = a as MethodDef) is not null) hash = GetHashCode(mda); - else if ((mra = a as MemberRef) != null) + else if ((mra = a as MemberRef) is not null) hash = GetHashCode(mra); - else if ((msa = a as MethodSpec) != null) + else if ((msa = a as MethodSpec) is not null) hash = GetHashCode(msa); else hash = 0; @@ -2831,9 +2593,7 @@ public int GetHashCode(IMethod a) { /// Method #1 /// Method #2 /// true if same, false otherwise - public bool Equals(MemberRef a, MethodDef b) { - return Equals(b, a); - } + public bool Equals(MemberRef a, MethodDef b) => Equals(b, a); /// /// Compares methods @@ -2844,7 +2604,7 @@ public bool Equals(MemberRef a, MethodDef b) { public bool Equals(MethodDef a, MemberRef b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -2853,7 +2613,7 @@ public bool Equals(MethodDef a, MemberRef b) { if (!DontProjectWinMDRefs) { var mra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); b = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b) ?? b; - if (mra != null) { + if (mra is not null) { result = Equals(mra, b); goto exit; } @@ -2877,7 +2637,9 @@ public bool Equals(MethodDef a, MemberRef b) { public bool Equals(MethodDef a, MethodDef b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) + return false; + if (ReferenceCompareForMemberDefsInSameModule && InSameModule(a, b)) return false; if (!recursionCounter.Increment()) return false; @@ -2886,7 +2648,7 @@ public bool Equals(MethodDef a, MethodDef b) { if (!DontProjectWinMDRefs) { var mra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); var mrb = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b); - if (mra != null || mrb != null) { + if (mra is not null || mrb is not null) { result = Equals((IMethod)mra ?? a, (IMethod)mrb ?? b); goto exit; } @@ -2909,11 +2671,11 @@ public int GetHashCode(MethodDef a) { // *********************************************************************** // IMPORTANT: This hash code must match the MemberRef/MethodBase hash code // *********************************************************************** - if (a == null) + if (a is null) return 0; if (!DontProjectWinMDRefs) { var mra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); - if (mra != null) + if (mra is not null) return GetHashCode(mra); } @@ -2938,7 +2700,7 @@ public int GetHashCode(MethodDef a) { public bool Equals(MemberRef a, MemberRef b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -2964,7 +2726,7 @@ public int GetHashCode(MemberRef a) { // ******************************************************************************** // IMPORTANT: This hash code must match the MethodDef/FieldDef/MethodBase hash code // ******************************************************************************** - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -2974,7 +2736,7 @@ public int GetHashCode(MemberRef a) { int hash = GetHashCode_MethodFieldName(a.Name); GenericInstSig git; - if (SubstituteGenericParameters && (git = GetGenericInstanceType(a.Class)) != null) { + if (CompareMethodFieldDeclaringType && !DontSubstituteGenericParameters && (git = GetGenericInstanceType(a.Class)) is not null) { InitializeGenericArguments(); genericArguments.PushTypeArgs(git.GenericArguments); hash += GetHashCode(a.Signature); @@ -2998,7 +2760,7 @@ public int GetHashCode(MemberRef a) { public bool Equals(MethodSpec a, MethodSpec b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -3018,22 +2780,19 @@ public int GetHashCode(MethodSpec a) { // ************************************************************* // IMPORTANT: This hash code must match the MethodBase hash code // ************************************************************* - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; - // We must do this or it won't get the same hash code as some MethodInfos - var oldOptions = SetOptions(SigComparerOptions.SubstituteGenericParameters); var gim = a.GenericInstMethodSig; - if (gim != null) { + if (gim is not null) { InitializeGenericArguments(); genericArguments.PushMethodArgs(gim.GenericArguments); } int hash = GetHashCode(a.Method); - if (gim != null) + if (gim is not null) genericArguments.PopMethodArgs(); - RestoreOptions(oldOptions); recursionCounter.Decrement(); return hash; @@ -3048,7 +2807,7 @@ public int GetHashCode(MethodSpec a) { bool Equals(IMemberRefParent a, IMemberRefParent b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -3059,18 +2818,18 @@ bool Equals(IMemberRefParent a, IMemberRefParent b) { MethodDef ma, mb; TypeDef td; - if ((ita = a as ITypeDefOrRef) != null && (itb = b as ITypeDefOrRef) != null) + if ((ita = a as ITypeDefOrRef) is not null && (itb = b as ITypeDefOrRef) is not null) result = Equals((IType)ita, (IType)itb); - else if ((moda = a as ModuleRef) != null & (modb = b as ModuleRef) != null) { + else if ((moda = a as ModuleRef) is not null & (modb = b as ModuleRef) is not null) { ModuleDef omoda = moda.Module, omodb = modb.Module; result = Equals((IModule)moda, (IModule)modb) && - Equals(omoda == null ? null : omoda.Assembly, omodb == null ? null : omodb.Assembly); + Equals(omoda?.Assembly, omodb?.Assembly); } - else if ((ma = a as MethodDef) != null && (mb = b as MethodDef) != null) + else if ((ma = a as MethodDef) is not null && (mb = b as MethodDef) is not null) result = Equals(ma, mb); - else if (modb != null && (td = a as TypeDef) != null) + else if (modb is not null && (td = a as TypeDef) is not null) result = EqualsGlobal(td, modb); - else if (moda != null && (td = b as TypeDef) != null) + else if (moda is not null && (td = b as TypeDef) is not null) result = EqualsGlobal(td, moda); else result = false; @@ -3085,7 +2844,7 @@ bool Equals(IMemberRefParent a, IMemberRefParent b) { /// The MemberRefParent /// The hash code int GetHashCode(IMemberRefParent a) { - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -3094,11 +2853,11 @@ int GetHashCode(IMemberRefParent a) { ITypeDefOrRef ita; MethodDef ma; - if ((ita = a as ITypeDefOrRef) != null) + if ((ita = a as ITypeDefOrRef) is not null) hash = GetHashCode((IType)ita); else if (a is ModuleRef) hash = GetHashCodeGlobalType(); - else if ((ma = a as MethodDef) != null) { + else if ((ma = a as MethodDef) is not null) { // Only use the declaring type so we get the same hash code when hashing a MethodBase. hash = GetHashCode(ma.DeclaringType); } @@ -3118,7 +2877,7 @@ int GetHashCode(IMemberRefParent a) { public bool Equals(IField a, IField b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -3127,13 +2886,13 @@ public bool Equals(IField a, IField b) { FieldDef fa, fb; MemberRef ma, mb; - if ((fa = a as FieldDef) != null & (fb = b as FieldDef) != null) + if ((fa = a as FieldDef) is not null & (fb = b as FieldDef) is not null) result = Equals(fa, fb); - else if ((ma = a as MemberRef) != null & (mb = b as MemberRef) != null) + else if ((ma = a as MemberRef) is not null & (mb = b as MemberRef) is not null) result = Equals(ma, mb); - else if (fa != null && mb != null) + else if (fa is not null && mb is not null) result = Equals(fa, mb); - else if (fb != null && ma != null) + else if (fb is not null && ma is not null) result = Equals(fb, ma); else result = false; @@ -3148,7 +2907,7 @@ public bool Equals(IField a, IField b) { /// The field /// The hash code public int GetHashCode(IField a) { - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -3157,9 +2916,9 @@ public int GetHashCode(IField a) { FieldDef fa; MemberRef ma; - if ((fa = a as FieldDef) != null) + if ((fa = a as FieldDef) is not null) hash = GetHashCode(fa); - else if ((ma = a as MemberRef) != null) + else if ((ma = a as MemberRef) is not null) hash = GetHashCode(ma); else hash = 0; @@ -3174,9 +2933,7 @@ public int GetHashCode(IField a) { /// Field #1 /// Field #2 /// true if same, false otherwise - public bool Equals(MemberRef a, FieldDef b) { - return Equals(b, a); - } + public bool Equals(MemberRef a, FieldDef b) => Equals(b, a); /// /// Compares fields @@ -3187,7 +2944,7 @@ public bool Equals(MemberRef a, FieldDef b) { public bool Equals(FieldDef a, MemberRef b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -3210,7 +2967,9 @@ public bool Equals(FieldDef a, MemberRef b) { public bool Equals(FieldDef a, FieldDef b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) + return false; + if (ReferenceCompareForMemberDefsInSameModule && InSameModule(a, b)) return false; if (!recursionCounter.Increment()) return false; @@ -3232,7 +2991,7 @@ public int GetHashCode(FieldDef a) { // ********************************************************************** // IMPORTANT: This hash code must match the MemberRef/FieldInfo hash code // ********************************************************************** - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -3255,7 +3014,9 @@ public int GetHashCode(FieldDef a) { public bool Equals(PropertyDef a, PropertyDef b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) + return false; + if (ReferenceCompareForMemberDefsInSameModule && InSameModule(a, b)) return false; if (!recursionCounter.Increment()) return false; @@ -3277,14 +3038,14 @@ public int GetHashCode(PropertyDef a) { // *************************************************************** // IMPORTANT: This hash code must match the PropertyInfo hash code // *************************************************************** - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; var sig = a.PropertySig; int hash = GetHashCode_PropertyName(a.Name) + - GetHashCode(sig == null ? null : sig.RetType); + GetHashCode(sig?.RetType); if (ComparePropertyDeclaringType) hash += GetHashCode(a.DeclaringType); @@ -3301,7 +3062,9 @@ public int GetHashCode(PropertyDef a) { public bool Equals(EventDef a, EventDef b) { if (a == b) return true; - if (a == null || b == null) + if (a is null || b is null) + return false; + if (ReferenceCompareForMemberDefsInSameModule && InSameModule(a, b)) return false; if (!recursionCounter.Increment()) return false; @@ -3323,7 +3086,7 @@ public int GetHashCode(EventDef a) { // ************************************************************ // IMPORTANT: This hash code must match the EventInfo hash code // ************************************************************ - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -3341,7 +3104,7 @@ public int GetHashCode(EventDef a) { bool EqualsGlobal(TypeDef a, ModuleRef b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -3354,9 +3117,7 @@ bool EqualsGlobal(TypeDef a, ModuleRef b) { return result; } - static AssemblyDef GetAssembly(ModuleDef module) { - return module == null ? null : module.Assembly; - } + static AssemblyDef GetAssembly(ModuleDef module) => module?.Assembly; /// /// Compares types @@ -3364,9 +3125,7 @@ static AssemblyDef GetAssembly(ModuleDef module) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(Type a, IType b) { - return Equals(b, a); - } + public bool Equals(Type a, IType b) => Equals(b, a); /// /// Compares types @@ -3377,7 +3136,7 @@ public bool Equals(Type a, IType b) { public bool Equals(IType a, Type b) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. - if (a == null) + if (a is null) return false; if (!recursionCounter.Increment()) return false; @@ -3389,15 +3148,15 @@ public bool Equals(IType a, Type b) { TypeSig sig; ExportedType et; - if ((td = a as TypeDef) != null) + if ((td = a as TypeDef) is not null) result = Equals(td, b); - else if ((tr = a as TypeRef) != null) + else if ((tr = a as TypeRef) is not null) result = Equals(tr, b); - else if ((ts = a as TypeSpec) != null) + else if ((ts = a as TypeSpec) is not null) result = Equals(ts, b); - else if ((sig = a as TypeSig) != null) + else if ((sig = a as TypeSig) is not null) result = Equals(sig, b); - else if ((et = a as ExportedType) != null) + else if ((et = a as ExportedType) is not null) result = Equals(et, b); else result = false; @@ -3412,9 +3171,7 @@ public bool Equals(IType a, Type b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(Type a, TypeDef b) { - return Equals(b, a); - } + public bool Equals(Type a, TypeDef b) => Equals(b, a); /// /// Compares types @@ -3425,9 +3182,9 @@ public bool Equals(Type a, TypeDef b) { public bool Equals(TypeDef a, Type b) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. - if (a == null) + if (a is null) return false; - if (b == null) + if (b is null) return a.IsGlobalModuleType; if (!recursionCounter.Increment()) return false; @@ -3436,13 +3193,13 @@ public bool Equals(TypeDef a, Type b) { if (!DontProjectWinMDRefs) { var tra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); - if (tra != null) { + if (tra is not null) { result = Equals(tra, b); goto exit; } } result = !b.HasElementType && - Equals_TypeNames(a.Name, b.Name) && + Equals_TypeNames(a.Name, ReflectionExtensions.Unescape(b.Name)) && Equals_TypeNamespaces(a.Namespace, b) && EnclosingTypeEquals(a.DeclaringType, b.DeclaringType) && (DontCompareTypeScope || Equals(a.Module, b.Module)); @@ -3453,10 +3210,10 @@ public bool Equals(TypeDef a, Type b) { } bool EnclosingTypeEquals(TypeDef a, Type b) { - // b == null doesn't mean that b is the global type + // b is null doesn't mean that b is the global type if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; return Equals(a, b); } @@ -3467,9 +3224,7 @@ bool EnclosingTypeEquals(TypeDef a, Type b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(Type a, TypeRef b) { - return Equals(b, a); - } + public bool Equals(Type a, TypeRef b) => Equals(b, a); /// /// Compares types @@ -3480,9 +3235,9 @@ public bool Equals(Type a, TypeRef b) { public bool Equals(TypeRef a, Type b) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. - if (a == null) + if (a is null) return false; - if (b == null) + if (b is null) return false; // Must use a ModuleRef to reference the global type, so always fail if (!recursionCounter.Increment()) return false; @@ -3498,21 +3253,21 @@ public bool Equals(TypeRef a, Type b) { if (!b.IsTypeDef()) result = false; - else if (!Equals_TypeNames(a.Name, b.Name) || !Equals_TypeNamespaces(a.Namespace, b)) + else if (!Equals_TypeNames(a.Name, ReflectionExtensions.Unescape(b.Name)) || !Equals_TypeNamespaces(a.Namespace, b)) result = false; - else if ((dta = scope as TypeRef) != null) // nested type + else if ((dta = scope as TypeRef) is not null) // nested type result = Equals(dta, b.DeclaringType); // Compare enclosing types else if (b.IsNested) result = false; // b is nested, a isn't else if (DontCompareTypeScope) result = true; - else if ((aMod = scope as IModule) != null) // 'a' is defined in the same assembly as 'b' + else if ((aMod = scope as IModule) is not null) // 'a' is defined in the same assembly as 'b' result = Equals(b, aMod, a); - else if ((aAsm = scope as AssemblyRef) != null) + else if ((aAsm = scope as AssemblyRef) is not null) result = Equals(b.Assembly, aAsm, a); else { result = false; - //TODO: Handle the case where scope == null + //TODO: Handle the case where scope is null } recursionCounter.Decrement(); @@ -3531,9 +3286,7 @@ bool Equals_TypeNamespaces(UTF8String a, Type b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(Type a, TypeSpec b) { - return Equals(b, a); - } + public bool Equals(Type a, TypeSpec b) => Equals(b, a); /// /// Compares types @@ -3544,9 +3297,9 @@ public bool Equals(Type a, TypeSpec b) { public bool Equals(TypeSpec a, Type b) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. - if (a == null) + if (a is null) return false; - if (b == null) + if (b is null) return false; // Must use a ModuleRef to reference the global type, so always fail return Equals(a.TypeSig, b); } @@ -3557,9 +3310,7 @@ public bool Equals(TypeSpec a, Type b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(Type a, TypeSig b) { - return Equals(b, a); - } + public bool Equals(Type a, TypeSig b) => Equals(b, a); /// /// Compares types @@ -3567,14 +3318,11 @@ public bool Equals(Type a, TypeSig b) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(TypeSig a, Type b) { - return Equals(a, b, false); - } + public bool Equals(TypeSig a, Type b) => Equals(a, b, null, false); - bool Equals(ITypeDefOrRef a, Type b, bool treatAsGenericInst) { - var ts = a as TypeSpec; - if (ts != null) - return Equals(ts.TypeSig, b, treatAsGenericInst); + bool Equals(ITypeDefOrRef a, Type b, Type declaringType) { + if (a is TypeSpec ts) + return Equals(ts.TypeSig, b, declaringType); return Equals(a, b); } @@ -3583,10 +3331,10 @@ bool Equals(ITypeDefOrRef a, Type b, bool treatAsGenericInst) { /// /// The type static bool IsFnPtrElementType(Type a) { - if (a == null || !a.HasElementType) + if (a is null || !a.HasElementType) return false; var et = a.GetElementType(); - if (et == null || et.HasElementType) + if (et is null || et.HasElementType) return false; if (et != typeof(IntPtr)) // FnPtr is mapped to System.IntPtr return false; @@ -3601,21 +3349,24 @@ static bool IsFnPtrElementType(Type a) { /// /// Type #1 /// Type #2 + /// Root declaring type to check if we should + /// treat as a generic instance type /// true if we should treat /// as a generic instance type /// true if same, false otherwise - bool Equals(TypeSig a, Type b, bool treatAsGenericInst) { + bool Equals(TypeSig a, Type b, Type declaringType, bool? treatAsGenericInst = null) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. - if (a == null) + if (a is null) return false; - if (b == null) + if (b is null) return false; // Must use a ModuleRef to reference the global type, so always fail if (!recursionCounter.Increment()) return false; bool result; - if (genericArguments != null) + bool treatAsGenericInst2 = treatAsGenericInst ?? declaringType.MustTreatTypeAsGenericInstType(b); + if (genericArguments is not null) a = genericArguments.Resolve(a); switch (a.ElementType) { @@ -3637,7 +3388,7 @@ bool Equals(TypeSig a, Type b, bool treatAsGenericInst) { case ElementType.I: case ElementType.U: case ElementType.Object: - result = Equals(((TypeDefOrRefSig)a).TypeDefOrRef, b, treatAsGenericInst); + result = Equals(((TypeDefOrRefSig)a).TypeDefOrRef, b, declaringType); break; case ElementType.Ptr: @@ -3645,10 +3396,10 @@ bool Equals(TypeSig a, Type b, bool treatAsGenericInst) { result = false; else if (IsFnPtrElementType(b)) { a = a.Next.RemoveModifiers(); - result = a != null && a.ElementType == ElementType.FnPtr; + result = a is not null && a.ElementType == ElementType.FnPtr; } else - result = Equals(a.Next, b.GetElementType()); + result = Equals(a.Next, b.GetElementType(), declaringType); break; case ElementType.ByRef: @@ -3656,10 +3407,10 @@ bool Equals(TypeSig a, Type b, bool treatAsGenericInst) { result = false; else if (IsFnPtrElementType(b)) { a = a.Next.RemoveModifiers(); - result = a != null && a.ElementType == ElementType.FnPtr; + result = a is not null && a.ElementType == ElementType.FnPtr; } else - result = Equals(a.Next, b.GetElementType()); + result = Equals(a.Next, b.GetElementType(), declaringType); break; case ElementType.SZArray: @@ -3667,65 +3418,58 @@ bool Equals(TypeSig a, Type b, bool treatAsGenericInst) { result = false; else if (IsFnPtrElementType(b)) { a = a.Next.RemoveModifiers(); - result = a != null && a.ElementType == ElementType.FnPtr; + result = a is not null && a.ElementType == ElementType.FnPtr; } else - result = Equals(a.Next, b.GetElementType()); + result = Equals(a.Next, b.GetElementType(), declaringType); break; case ElementType.Pinned: - result = Equals(a.Next, b, treatAsGenericInst); + result = Equals(a.Next, b, declaringType); break; case ElementType.Array: if (!b.IsArray || b.IsSZArray()) result = false; else { - ArraySig ara = a as ArraySig; + var ara = a as ArraySig; result = ara.Rank == b.GetArrayRank() && (IsFnPtrElementType(b) ? - (a = a.Next.RemoveModifiers()) != null && a.ElementType == ElementType.FnPtr : - Equals(a.Next, b.GetElementType())); + (a = a.Next.RemoveModifiers()) is not null && a.ElementType == ElementType.FnPtr : + Equals(a.Next, b.GetElementType(), declaringType)); } break; case ElementType.ValueType: case ElementType.Class: - result = Equals((a as ClassOrValueTypeSig).TypeDefOrRef, b, treatAsGenericInst); + result = Equals((a as ClassOrValueTypeSig).TypeDefOrRef, b, declaringType); break; case ElementType.Var: result = b.IsGenericParameter && b.GenericParameterPosition == (a as GenericSig).Number && - b.DeclaringMethod == null; + b.DeclaringMethod is null; break; case ElementType.MVar: result = b.IsGenericParameter && b.GenericParameterPosition == (a as GenericSig).Number && - b.DeclaringMethod != null; + b.DeclaringMethod is not null; break; case ElementType.GenericInst: - if (!(b.IsGenericType && !b.IsGenericTypeDefinition) && !treatAsGenericInst) { + if (!(b.IsGenericType && !b.IsGenericTypeDefinition) && !treatAsGenericInst2) { result = false; break; } var gia = (GenericInstSig)a; - if (SubstituteGenericParameters) { - InitializeGenericArguments(); - genericArguments.PushTypeArgs(gia.GenericArguments); - result = Equals(gia.GenericType, b.GetGenericTypeDefinition()); - genericArguments.PopTypeArgs(); - } - else - result = Equals(gia.GenericType, b.GetGenericTypeDefinition()); - result = result && Equals(gia.GenericArguments, b.GetGenericArguments()); + result = Equals(gia.GenericType, b.GetGenericTypeDefinition(), null, false); + result = result && Equals(gia.GenericArguments, b.GetGenericArguments(), declaringType); break; case ElementType.CModReqd: case ElementType.CModOpt: - result = Equals(a.Next, b, treatAsGenericInst); + result = Equals(a.Next, b, declaringType); break; case ElementType.FnPtr: @@ -3754,9 +3498,7 @@ bool Equals(TypeSig a, Type b, bool treatAsGenericInst) { /// Type #1 /// Type #2 /// true if same, false otherwise - public bool Equals(Type a, ExportedType b) { - return Equals(b, a); - } + public bool Equals(Type a, ExportedType b) => Equals(b, a); /// /// Compares types @@ -3767,9 +3509,9 @@ public bool Equals(Type a, ExportedType b) { public bool Equals(ExportedType a, Type b) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. - if (a == null) + if (a is null) return false; - if (b == null) + if (b is null) return false; // Must use a ModuleRef to reference the global type, so always fail if (!recursionCounter.Increment()) return false; @@ -3785,17 +3527,17 @@ public bool Equals(ExportedType a, Type b) { if (!b.IsTypeDef()) result = false; - else if (!Equals_TypeNames(a.TypeName, b.Name) || !Equals_TypeNamespaces(a.TypeNamespace, b)) + else if (!Equals_TypeNames(a.TypeName, ReflectionExtensions.Unescape(b.Name)) || !Equals_TypeNamespaces(a.TypeNamespace, b)) result = false; - else if ((dta = scope as ExportedType) != null) // nested type + else if ((dta = scope as ExportedType) is not null) // nested type result = Equals(dta, b.DeclaringType); // Compare enclosing types else if (b.IsNested) result = false; // b is nested, a isn't else if (DontCompareTypeScope) result = true; - else if ((aFile = scope as FileDef) != null) + else if ((aFile = scope as FileDef) is not null) result = Equals(b, aFile, a); - else if ((aAsm = scope as AssemblyRef) != null) + else if ((aAsm = scope as AssemblyRef) is not null) result = Equals(b.Assembly, aAsm, a); else result = false; @@ -3809,9 +3551,7 @@ public bool Equals(ExportedType a, Type b) { /// /// The type /// The hash code - public int GetHashCode(Type a) { - return GetHashCode(a, false); - } + public int GetHashCode(Type a) => GetHashCode(a, false); /// /// Gets the hash code of a type @@ -3820,17 +3560,20 @@ public int GetHashCode(Type a) { /// true if we should treat /// as a generic instance type /// The hash code - public int GetHashCode(Type a, bool treatAsGenericInst) { + public int GetHashCode(Type a, bool treatAsGenericInst) => GetHashCode(a, null, treatAsGenericInst); + + int GetHashCode(Type a, Type declaringType, bool? treatAsGenericInst = null) { // ************************************************************************** // IMPORTANT: This hash code must match the TypeSig/TypeDef/TypeRef hash code // ************************************************************************** - if (a == null) // Could be global type + if (a is null) // Could be global type return GetHashCode_TypeDef(a); if (!recursionCounter.Increment()) return 0; int hash; - switch (treatAsGenericInst ? ElementType.GenericInst : a.GetElementType2()) { + bool treatAsGenericInst2 = treatAsGenericInst ?? declaringType.MustTreatTypeAsGenericInstType(a); + switch (treatAsGenericInst2 ? ElementType.GenericInst : a.GetElementType2()) { case ElementType.Void: case ElementType.Boolean: case ElementType.Char: @@ -3864,30 +3607,30 @@ public int GetHashCode(Type a, bool treatAsGenericInst) { case ElementType.Ptr: hash = HASHCODE_MAGIC_ET_PTR + - (IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType())); + (IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType(), declaringType)); break; case ElementType.ByRef: hash = HASHCODE_MAGIC_ET_BYREF + - (IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType())); + (IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType(), declaringType)); break; case ElementType.SZArray: hash = HASHCODE_MAGIC_ET_SZARRAY + - (IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType())); + (IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType(), declaringType)); break; case ElementType.CModReqd: case ElementType.CModOpt: case ElementType.Pinned: - hash = GetHashCode(a.GetElementType()); + hash = GetHashCode(a.GetElementType(), declaringType); break; case ElementType.Array: // The type doesn't store sizes and lower bounds, so can't use them to // create the hash hash = HASHCODE_MAGIC_ET_ARRAY + a.GetArrayRank() + - (IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType())); + (IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType(), declaringType)); break; case ElementType.Var: @@ -3899,7 +3642,8 @@ public int GetHashCode(Type a, bool treatAsGenericInst) { break; case ElementType.GenericInst: - hash = HASHCODE_MAGIC_ET_GENERICINST + GetHashCode(a.GetGenericTypeDefinition()) + GetHashCode(a.GetGenericArguments()); + hash = HASHCODE_MAGIC_ET_GENERICINST + GetHashCode(a.GetGenericTypeDefinition(), false) + + GetHashCode(a.GetGenericArguments(), declaringType); break; case ElementType.ValueArray: @@ -3920,18 +3664,20 @@ public int GetHashCode(Type a, bool treatAsGenericInst) { /// Gets the hash code of a type list /// /// The type list + /// Root declaring type to check if we should + /// treat as a generic instance type /// The hash code - int GetHashCode(IList a) { + int GetHashCode(IList a, Type declaringType) { //************************************************************************ // IMPORTANT: This code must match any other GetHashCode(IList) //************************************************************************ - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; uint hash = 0; for (int i = 0; i < a.Count; i++) { - hash += (uint)GetHashCode(a[i]); + hash += (uint)GetHashCode(a[i], declaringType); hash = (hash << 13) | (hash >> 19); } recursionCounter.Decrement(); @@ -3943,9 +3689,7 @@ int GetHashCode(IList a) { /// /// Number of generic method parameters /// Hash code - static int GetHashCode_ElementType_MVar(int numGenericParams) { - return GetHashCode(numGenericParams, HASHCODE_MAGIC_ET_MVAR); - } + static int GetHashCode_ElementType_MVar(int numGenericParams) => GetHashCode(numGenericParams, HASHCODE_MAGIC_ET_MVAR); static int GetHashCode(int numGenericParams, int etypeHashCode) { //************************************************************************ @@ -3972,10 +3716,10 @@ public int GetHashCode_TypeDef(Type a) { // A global method/field's declaring type is null. This is the reason we must // return GetHashCodeGlobalType() here. - if (a == null) + if (a is null) return GetHashCodeGlobalType(); int hash; - hash = GetHashCode_TypeName(a.Name); + hash = GetHashCode_TypeName(ReflectionExtensions.Unescape(a.Name)); if (a.IsNested) hash += HASHCODE_MAGIC_NESTED_TYPE; else @@ -3988,11 +3732,13 @@ public int GetHashCode_TypeDef(Type a) { /// /// Type list #1 /// Type list #2 + /// Root declaring type to check if we should + /// treat as a generic instance type /// true if same, false otherwise - bool Equals(IList a, IList b) { + bool Equals(IList a, IList b, Type declaringType) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -4003,7 +3749,7 @@ bool Equals(IList a, IList b) { else { int i; for (i = 0; i < a.Count; i++) { - if (!Equals(a[i], b[i])) + if (!Equals(a[i], b[i], declaringType)) break; } result = i == a.Count; @@ -4022,7 +3768,7 @@ bool Equals(IList a, IList b) { bool Equals(ModuleDef a, Module b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b)) return true; @@ -4044,7 +3790,7 @@ bool Equals(ModuleDef a, Module b) { bool Equals(FileDef a, Module b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; // Use b.Name since it's the filename we want to compare, not b.ScopeName @@ -4058,9 +3804,9 @@ bool Equals(FileDef a, Module b) { /// Module #2 /// true if same, false otherwise bool Equals(IModule a, Module b) { - if (a == b) + if ((object)a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b)) return true; @@ -4076,9 +3822,9 @@ bool Equals(IModule a, Module b) { /// Assembly #2 /// true if same, false otherwise bool Equals(IAssembly a, Assembly b) { - if (a == b) + if ((object)a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b)) return true; @@ -4106,9 +3852,9 @@ bool DeclaringTypeEquals(IMethod a, MethodBase b) { if (!CompareMethodFieldDeclaringType) return true; - if (a == b) + if ((object)a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -4118,11 +3864,11 @@ bool DeclaringTypeEquals(IMethod a, MethodBase b) { MemberRef mr; MethodSpec ms; - if ((md = a as MethodDef) != null) + if ((md = a as MethodDef) is not null) result = DeclaringTypeEquals(md, b); - else if ((mr = a as MemberRef) != null) + else if ((mr = a as MemberRef) is not null) result = DeclaringTypeEquals(mr, b); - else if ((ms = a as MethodSpec) != null) + else if ((ms = a as MethodSpec) is not null) result = DeclaringTypeEquals(ms, b); else result = false; @@ -4137,7 +3883,7 @@ bool DeclaringTypeEquals(MethodDef a, MethodBase b) { return true; if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; return Equals(a.DeclaringType, b.DeclaringType); } @@ -4148,7 +3894,7 @@ bool DeclaringTypeEquals(MemberRef a, MethodBase b) { return true; if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; return Equals(a.Class, b.DeclaringType, b.Module); } @@ -4159,7 +3905,7 @@ bool DeclaringTypeEquals(MethodSpec a, MethodBase b) { return true; if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; return DeclaringTypeEquals(a.Method, b); } @@ -4170,9 +3916,7 @@ bool DeclaringTypeEquals(MethodSpec a, MethodBase b) { /// Method #1 /// Method #2 /// true if same, false otherwise - public bool Equals(MethodBase a, IMethod b) { - return Equals(b, a); - } + public bool Equals(MethodBase a, IMethod b) => Equals(b, a); /// /// Compares methods @@ -4181,9 +3925,9 @@ public bool Equals(MethodBase a, IMethod b) { /// Method #2 /// true if same, false otherwise public bool Equals(IMethod a, MethodBase b) { - if (a == b) + if ((object)a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -4193,11 +3937,11 @@ public bool Equals(IMethod a, MethodBase b) { MemberRef mr; MethodSpec ms; - if ((md = a as MethodDef) != null) + if ((md = a as MethodDef) is not null) result = Equals(md, b); - else if ((mr = a as MemberRef) != null) + else if ((mr = a as MemberRef) is not null) result = Equals(mr, b); - else if ((ms = a as MethodSpec) != null) + else if ((ms = a as MethodSpec) is not null) result = Equals(ms, b); else result = false; @@ -4212,9 +3956,7 @@ public bool Equals(IMethod a, MethodBase b) { /// Method #1 /// Method #2 /// true if same, false otherwise - public bool Equals(MethodBase a, MethodDef b) { - return Equals(b, a); - } + public bool Equals(MethodBase a, MethodDef b) => Equals(b, a); /// /// Compares methods @@ -4225,7 +3967,7 @@ public bool Equals(MethodBase a, MethodDef b) { public bool Equals(MethodDef a, MethodBase b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -4233,7 +3975,7 @@ public bool Equals(MethodDef a, MethodBase b) { bool result; if (!DontProjectWinMDRefs) { var mra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); - if (mra != null) { + if (mra is not null) { result = Equals(mra, b); goto exit; } @@ -4241,7 +3983,7 @@ public bool Equals(MethodDef a, MethodBase b) { var amSig = a.MethodSig; result = Equals_MethodFieldNames(a.Name, b.Name) && - amSig != null && + amSig is not null && ((amSig.Generic && b.IsGenericMethodDefinition && b.IsGenericMethod) || (!amSig.Generic && !b.IsGenericMethodDefinition && !b.IsGenericMethod)) && Equals(amSig, b) && @@ -4258,9 +4000,7 @@ public bool Equals(MethodDef a, MethodBase b) { /// Method #1 /// Method #2 /// true if same, false otherwise - public bool Equals(MethodBase a, MethodSig b) { - return Equals(b, a); - } + public bool Equals(MethodBase a, MethodSig b) => Equals(b, a); /// /// Compares method sigs @@ -4271,11 +4011,17 @@ public bool Equals(MethodBase a, MethodSig b) { public bool Equals(MethodSig a, MethodBase b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; + if (!CompareMethodFieldDeclaringType && b.DeclaringType.IsGenericButNotGenericTypeDefinition()) { + var t = b; + b = b.Module.ResolveMethod(b.MetadataToken); + if (b.IsGenericButNotGenericMethodDefinition()) + b = ((MethodInfo)b).MakeGenericMethod(t.GetGenericArguments()); + } bool result = Equals(a.GetCallingConvention(), b) && (DontCompareReturnType || ReturnTypeEquals(a.RetType, b)) && Equals(a.Params, b.GetParameters(), b.DeclaringType) && @@ -4291,9 +4037,7 @@ public bool Equals(MethodSig a, MethodBase b) { /// Method #1 /// Method #2 /// true if same, false otherwise - public bool Equals(MethodBase a, MemberRef b) { - return Equals(b, a); - } + public bool Equals(MethodBase a, MemberRef b) => Equals(b, a); /// /// Compares methods @@ -4304,7 +4048,7 @@ public bool Equals(MethodBase a, MemberRef b) { public bool Equals(MemberRef a, MethodBase b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -4318,6 +4062,7 @@ public bool Equals(MemberRef a, MethodBase b) { result = a.IsMethodRef && a.MethodSig.Generic; var oldOptions = ClearOptions(SigComparerOptions.CompareMethodFieldDeclaringType); + SetOptions(SigComparerOptions_DontSubstituteGenericParameters); result = result && Equals(a, b.Module.ResolveMethod(b.MetadataToken)); RestoreOptions(oldOptions); result = result && DeclaringTypeEquals(a, b); @@ -4327,12 +4072,12 @@ public bool Equals(MemberRef a, MethodBase b) { else { var amSig = a.MethodSig; result = Equals_MethodFieldNames(a.Name, b.Name) && - amSig != null && + amSig is not null && ((amSig.Generic && b.IsGenericMethodDefinition && b.IsGenericMethod) || (!amSig.Generic && !b.IsGenericMethodDefinition && !b.IsGenericMethod)); GenericInstSig git; - if (SubstituteGenericParameters && (git = GetGenericInstanceType(a.Class)) != null) { + if (CompareMethodFieldDeclaringType && !DontSubstituteGenericParameters && (git = GetGenericInstanceType(a.Class)) is not null) { InitializeGenericArguments(); genericArguments.PushTypeArgs(git.GenericArguments); result = result && Equals(amSig, b); @@ -4368,7 +4113,7 @@ static bool GenericMethodArgsEquals(int numMethodArgs, IList methodGenArgs bool Equals(IMemberRefParent a, Type b, Module bModule) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. - if (a == null) + if (a is null) return false; if (!recursionCounter.Increment()) return false; @@ -4379,17 +4124,17 @@ bool Equals(IMemberRefParent a, Type b, Module bModule) { MethodDef ma; TypeDef td; - if ((ita = a as ITypeDefOrRef) != null) + if ((ita = a as ITypeDefOrRef) is not null) result = Equals((IType)ita, b); - else if ((moda = a as ModuleRef) != null) { - ModuleDef omoda = moda.Module; - result = b == null && // b == null => it's the global type + else if ((moda = a as ModuleRef) is not null) { + var omoda = moda.Module; + result = b is null && // b is null => it's the global type Equals(moda, bModule) && - Equals(omoda == null ? null : omoda.Assembly, bModule.Assembly); + Equals(omoda?.Assembly, bModule.Assembly); } - else if ((ma = a as MethodDef) != null) + else if ((ma = a as MethodDef) is not null) result = Equals(ma.DeclaringType, b); - else if (b == null && (td = a as TypeDef) != null) + else if (b is null && (td = a as TypeDef) is not null) result = td.IsGlobalModuleType; else result = false; @@ -4404,9 +4149,7 @@ bool Equals(IMemberRefParent a, Type b, Module bModule) { /// Method #1 /// Method #2 /// true if same, false otherwise - public bool Equals(MethodBase a, MethodSpec b) { - return Equals(b, a); - } + public bool Equals(MethodBase a, MethodSpec b) => Equals(b, a); /// /// Compares methods @@ -4417,7 +4160,7 @@ public bool Equals(MethodBase a, MethodSpec b) { public bool Equals(MethodSpec a, MethodBase b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -4429,12 +4172,13 @@ public bool Equals(MethodSpec a, MethodBase b) { // declaring type (its declaring type is a generic type def). // NOTE: We must not push generic method args when comparing a.Method var oldOptions = ClearOptions(SigComparerOptions.CompareMethodFieldDeclaringType); + SetOptions(SigComparerOptions_DontSubstituteGenericParameters); result = result && Equals(a.Method, b.Module.ResolveMethod(b.MetadataToken)); RestoreOptions(oldOptions); result = result && DeclaringTypeEquals(a.Method, b); var gim = a.GenericInstMethodSig; - result = result && gim != null && Equals(gim.GenericArguments, b.GetGenericArguments()); + result = result && gim is not null && Equals(gim.GenericArguments, b.GetGenericArguments(), b.DeclaringType); recursionCounter.Decrement(); return result; @@ -4446,7 +4190,7 @@ public bool Equals(MethodSpec a, MethodBase b) { /// The MethodBase /// The hash code public int GetHashCode(MethodBase a) { - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -4464,12 +4208,18 @@ public int GetHashCode(MethodBase a) { } int GetHashCode_MethodSig(MethodBase a) { - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; + if (!CompareMethodFieldDeclaringType && a.DeclaringType.IsGenericButNotGenericTypeDefinition()) { + var t = a; + a = a.Module.ResolveMethod(a.MetadataToken); + if (t.IsGenericButNotGenericMethodDefinition()) + a = ((MethodInfo)a).MakeGenericMethod(t.GetGenericArguments()); + } hash = GetHashCode_CallingConvention(a.CallingConvention, a.IsGenericMethod) + GetHashCode(a.GetParameters(), a.DeclaringType); if (!DontCompareReturnType) @@ -4491,7 +4241,7 @@ int GetHashCode(IList a, Type declaringType) { //************************************************************************ // IMPORTANT: This code must match any other GetHashCode(IList) //************************************************************************ - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -4506,18 +4256,12 @@ int GetHashCode(IList a, Type declaringType) { int GetHashCode_ReturnType(MethodBase a) { var mi = a as MethodInfo; - if (mi != null) + if (mi is not null) return GetHashCode(mi.ReturnParameter, a.DeclaringType); return GetHashCode(typeof(void)); } - int GetHashCode(ParameterInfo a, Type declaringType) { - return GetHashCode(a.ParameterType, declaringType.MustTreatTypeAsGenericInstType(a.ParameterType)); - } - - int GetHashCode(Type a, Type declaringType) { - return GetHashCode(a, declaringType.MustTreatTypeAsGenericInstType(a)); - } + int GetHashCode(ParameterInfo a, Type declaringType) => GetHashCode(a.ParameterType, declaringType); /// /// Compares calling conventions @@ -4581,14 +4325,14 @@ static int GetHashCode_CallingConvention(CallingConventions a, bool isGeneric) { bool ReturnTypeEquals(TypeSig a, MethodBase b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; var mi = b as MethodInfo; - if (mi != null) + if (mi is not null) result = Equals(a, mi.ReturnParameter, b.DeclaringType); else if (b is ConstructorInfo) result = IsSystemVoid(a); @@ -4599,9 +4343,7 @@ bool ReturnTypeEquals(TypeSig a, MethodBase b) { return result; } - static bool IsSystemVoid(TypeSig a) { - return a.RemovePinnedAndModifiers().GetElementType() == ElementType.Void; - } + static bool IsSystemVoid(TypeSig a) => a.RemovePinnedAndModifiers().GetElementType() == ElementType.Void; /// /// Compares parameter lists @@ -4613,7 +4355,7 @@ static bool IsSystemVoid(TypeSig a) { bool Equals(IList a, IList b, Type declaringType) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -4644,14 +4386,13 @@ bool Equals(IList a, IList b, Type declaringType) { bool Equals(TypeSig a, ParameterInfo b, Type declaringType) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; - TypeSig a2; - bool result = ModifiersEquals(a, b.GetRequiredCustomModifiers(), b.GetOptionalCustomModifiers(), out a2) && - Equals(a2, b.ParameterType, declaringType.MustTreatTypeAsGenericInstType(b.ParameterType)); + bool result = ModifiersEquals(a, b.GetRequiredCustomModifiers(), b.GetOptionalCustomModifiers(), out var a2) && + Equals(a2, b.ParameterType, declaringType); recursionCounter.Decrement(); return result; @@ -4669,7 +4410,7 @@ bool ModifiersEquals(TypeSig a, IList reqMods2, IList optMods2, out var optMods1 = new List(optMods2.Count); while (true) { var modifierSig = aAfterModifiers as ModifierSig; - if (modifierSig == null) + if (modifierSig is null) break; if (modifierSig is CModOptSig) optMods1.Add(modifierSig.Modifier); @@ -4695,7 +4436,7 @@ bool ModifiersEquals(TypeSig a, IList reqMods2, IList optMods2, out bool ModifiersEquals(IList a, IList b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -4722,9 +4463,7 @@ bool ModifiersEquals(IList a, IList b) { /// Field #1 /// Field #2 /// true if same, false otherwise - public bool Equals(FieldInfo a, IField b) { - return Equals(b, a); - } + public bool Equals(FieldInfo a, IField b) => Equals(b, a); /// /// Compares fields @@ -4733,9 +4472,9 @@ public bool Equals(FieldInfo a, IField b) { /// Field #2 /// true if same, false otherwise public bool Equals(IField a, FieldInfo b) { - if (a == b) + if ((object)a == b) return true; - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -4744,9 +4483,9 @@ public bool Equals(IField a, FieldInfo b) { FieldDef fa; MemberRef ma; - if ((fa = a as FieldDef) != null) + if ((fa = a as FieldDef) is not null) result = Equals(fa, b); - else if ((ma = a as MemberRef) != null) + else if ((ma = a as MemberRef) is not null) result = Equals(ma, b); else result = false; @@ -4761,9 +4500,7 @@ public bool Equals(IField a, FieldInfo b) { /// Field #1 /// Field #2 /// true if same, false otherwise - public bool Equals(FieldInfo a, FieldDef b) { - return Equals(b, a); - } + public bool Equals(FieldInfo a, FieldDef b) => Equals(b, a); /// /// Compares fields @@ -4774,7 +4511,7 @@ public bool Equals(FieldInfo a, FieldDef b) { public bool Equals(FieldDef a, FieldInfo b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -4790,14 +4527,15 @@ public bool Equals(FieldDef a, FieldInfo b) { bool Equals(FieldSig a, FieldInfo b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; - TypeSig a2; - bool result = ModifiersEquals(a.Type, b.GetRequiredCustomModifiers(), b.GetOptionalCustomModifiers(), out a2) && - Equals(a2, b.FieldType, b.DeclaringType.MustTreatTypeAsGenericInstType(b.FieldType)); + if (!CompareMethodFieldDeclaringType && b.DeclaringType.IsGenericButNotGenericTypeDefinition()) + b = b.Module.ResolveField(b.MetadataToken); + bool result = ModifiersEquals(a.Type, b.GetRequiredCustomModifiers(), b.GetOptionalCustomModifiers(), out var a2) && + Equals(a2, b.FieldType, b.DeclaringType); recursionCounter.Decrement(); return result; @@ -4809,9 +4547,7 @@ bool Equals(FieldSig a, FieldInfo b) { /// Field #1 /// Field #2 /// true if same, false otherwise - public bool Equals(FieldInfo a, MemberRef b) { - return Equals(b, a); - } + public bool Equals(FieldInfo a, MemberRef b) => Equals(b, a); /// /// Compares fields @@ -4822,7 +4558,7 @@ public bool Equals(FieldInfo a, MemberRef b) { public bool Equals(MemberRef a, FieldInfo b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -4830,7 +4566,7 @@ public bool Equals(MemberRef a, FieldInfo b) { bool result = Equals_MethodFieldNames(a.Name, b.Name); GenericInstSig git; - if (SubstituteGenericParameters && (git = GetGenericInstanceType(a.Class)) != null) { + if (CompareMethodFieldDeclaringType && !DontSubstituteGenericParameters && (git = GetGenericInstanceType(a.Class)) is not null) { InitializeGenericArguments(); genericArguments.PushTypeArgs(git.GenericArguments); result = result && Equals(a.FieldSig, b); @@ -4854,7 +4590,7 @@ public int GetHashCode(FieldInfo a) { // ************************************************************ // IMPORTANT: This hash code must match the MemberRef hash code // ************************************************************ - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -4869,12 +4605,14 @@ public int GetHashCode(FieldInfo a) { } int GetHashCode_FieldSig(FieldInfo a) { - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; + if (!CompareMethodFieldDeclaringType && a.DeclaringType.IsGenericButNotGenericTypeDefinition()) + a = a.Module.ResolveField(a.MetadataToken); hash = GetHashCode_CallingConvention(0, false) + GetHashCode(a.FieldType, a.DeclaringType); recursionCounter.Decrement(); @@ -4890,7 +4628,7 @@ int GetHashCode_FieldSig(FieldInfo a) { public bool Equals(PropertyDef a, PropertyInfo b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; @@ -4906,14 +4644,13 @@ public bool Equals(PropertyDef a, PropertyInfo b) { bool Equals(PropertySig a, PropertyInfo b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; - TypeSig a2; - bool result = ModifiersEquals(a.RetType, b.GetRequiredCustomModifiers(), b.GetOptionalCustomModifiers(), out a2) && - Equals(a2, b.PropertyType, b.DeclaringType.MustTreatTypeAsGenericInstType(b.PropertyType)); + bool result = ModifiersEquals(a.RetType, b.GetRequiredCustomModifiers(), b.GetOptionalCustomModifiers(), out var a2) && + Equals(a2, b.PropertyType, b.DeclaringType); recursionCounter.Decrement(); return result; @@ -4928,7 +4665,7 @@ public int GetHashCode(PropertyInfo a) { // ************************************************************** // IMPORTANT: This hash code must match the PropertyDef hash code // ************************************************************** - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -4951,13 +4688,13 @@ public int GetHashCode(PropertyInfo a) { public bool Equals(EventDef a, EventInfo b) { if ((object)a == (object)b) return true; // both are null - if (a == null || b == null) + if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = Equals_EventNames(a.Name, b.Name) && - Equals(a.EventType, b.EventHandlerType, b.DeclaringType.MustTreatTypeAsGenericInstType(b.EventHandlerType)) && + Equals(a.EventType, b.EventHandlerType, b.DeclaringType) && (!CompareEventDeclaringType || Equals(a.DeclaringType, b.DeclaringType)); recursionCounter.Decrement(); @@ -4973,7 +4710,7 @@ public int GetHashCode(EventInfo a) { // *********************************************************** // IMPORTANT: This hash code must match the EventDef hash code // *********************************************************** - if (a == null) + if (a is null) return 0; if (!recursionCounter.Increment()) return 0; @@ -4988,8 +4725,8 @@ public int GetHashCode(EventInfo a) { } /// - public override string ToString() { - return string.Format("{0} - {1}", recursionCounter, options); - } + public override string ToString() => $"{recursionCounter} - {options}"; + + static bool InSameModule(IOwnerModule a, IOwnerModule b) => a.Module is { } module && module == b.Module; } } diff --git a/src/DotNet/SignatureReader.cs b/src/DotNet/SignatureReader.cs index 2988fb706..4d9dba10d 100644 --- a/src/DotNet/SignatureReader.cs +++ b/src/DotNet/SignatureReader.cs @@ -1,10 +1,10 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.IO; +using dnlib.DotNet.MD; using dnlib.IO; -using dnlib.Threading; namespace dnlib.DotNet { /// @@ -23,7 +23,7 @@ public interface ISignatureReaderHelper { /// /// Converts the address of a to a /// - /// + /// /// Address of . This is also known as the /// method table and has the same value as /// A or null if not supported @@ -33,10 +33,14 @@ public interface ISignatureReaderHelper { /// /// Reads signatures from the #Blob stream /// - public struct SignatureReader : IDisposable { + public struct SignatureReader { + // .NET Core and .NET Framework limit arrays to 32 dimensions. Use a bigger limit + // so it's possible to read some bad MD, but not big enough to allocate a ton of mem. + const uint MaxArrayRank = 64; + readonly ISignatureReaderHelper helper; readonly ICorLibTypes corLibTypes; - readonly IBinaryReader reader; + DataReader reader; readonly GenericParamContext gpContext; RecursionCounter recursionCounter; @@ -47,9 +51,8 @@ public struct SignatureReader : IDisposable { /// #Blob stream offset of signature /// A new instance or null if /// is invalid. - public static CallingConventionSig ReadSig(ModuleDefMD readerModule, uint sig) { - return ReadSig(readerModule, sig, new GenericParamContext()); - } + public static CallingConventionSig ReadSig(ModuleDefMD readerModule, uint sig) => + ReadSig(readerModule, sig, new GenericParamContext()); /// /// Reads a signature from the #Blob stream @@ -61,14 +64,13 @@ public static CallingConventionSig ReadSig(ModuleDefMD readerModule, uint sig) { /// is invalid. public static CallingConventionSig ReadSig(ModuleDefMD readerModule, uint sig, GenericParamContext gpContext) { try { - using (var reader = new SignatureReader(readerModule, sig, gpContext)) { - if (reader.reader.Length == 0) - return null; - var csig = reader.ReadSig(); - if (csig != null) - csig.ExtraData = reader.GetExtraData(); - return csig; - } + var reader = new SignatureReader(readerModule, sig, gpContext); + if (reader.reader.Length == 0) + return null; + var csig = reader.ReadSig(); + if (csig is not null) + csig.ExtraData = reader.GetExtraData(); + return csig; } catch { return null; @@ -82,9 +84,8 @@ public static CallingConventionSig ReadSig(ModuleDefMD readerModule, uint sig, G /// The signature data /// A new instance or null if /// is invalid. - public static CallingConventionSig ReadSig(ModuleDefMD module, byte[] signature) { - return ReadSig(module, module.CorLibTypes, MemoryImageStream.Create(signature), new GenericParamContext()); - } + public static CallingConventionSig ReadSig(ModuleDefMD module, byte[] signature) => + ReadSig(module, module.CorLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), new GenericParamContext()); /// /// Reads a signature @@ -94,32 +95,29 @@ public static CallingConventionSig ReadSig(ModuleDefMD module, byte[] signature) /// Generic parameter context /// A new instance or null if /// is invalid. - public static CallingConventionSig ReadSig(ModuleDefMD module, byte[] signature, GenericParamContext gpContext) { - return ReadSig(module, module.CorLibTypes, MemoryImageStream.Create(signature), gpContext); - } + public static CallingConventionSig ReadSig(ModuleDefMD module, byte[] signature, GenericParamContext gpContext) => + ReadSig(module, module.CorLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext); /// /// Reads a signature /// /// The module where the signature is located in - /// The signature reader which will be owned by us + /// The signature reader /// A new instance or null if /// is invalid. - public static CallingConventionSig ReadSig(ModuleDefMD module, IBinaryReader signature) { - return ReadSig(module, module.CorLibTypes, signature, new GenericParamContext()); - } + public static CallingConventionSig ReadSig(ModuleDefMD module, DataReader signature) => + ReadSig(module, module.CorLibTypes, signature, new GenericParamContext()); /// /// Reads a signature /// /// The module where the signature is located in - /// The signature reader which will be owned by us + /// The signature reader /// Generic parameter context /// A new instance or null if /// is invalid. - public static CallingConventionSig ReadSig(ModuleDefMD module, IBinaryReader signature, GenericParamContext gpContext) { - return ReadSig(module, module.CorLibTypes, signature, gpContext); - } + public static CallingConventionSig ReadSig(ModuleDefMD module, DataReader signature, GenericParamContext gpContext) => + ReadSig(module, module.CorLibTypes, signature, gpContext); /// /// Reads a signature @@ -129,9 +127,8 @@ public static CallingConventionSig ReadSig(ModuleDefMD module, IBinaryReader sig /// The signature data /// A new instance or null if /// is invalid. - public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature) { - return ReadSig(helper, corLibTypes, MemoryImageStream.Create(signature), new GenericParamContext()); - } + public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature) => + ReadSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), new GenericParamContext()); /// /// Reads a signature @@ -142,38 +139,35 @@ public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLi /// Generic parameter context /// A new instance or null if /// is invalid. - public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature, GenericParamContext gpContext) { - return ReadSig(helper, corLibTypes, MemoryImageStream.Create(signature), gpContext); - } + public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature, GenericParamContext gpContext) => + ReadSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext); /// /// Reads a signature /// /// Token resolver /// A instance - /// The signature reader which will be owned by us + /// The signature reader /// A new instance or null if /// is invalid. - public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, IBinaryReader signature) { - return ReadSig(helper, corLibTypes, signature, new GenericParamContext()); - } + public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature) => + ReadSig(helper, corLibTypes, signature, new GenericParamContext()); /// /// Reads a signature /// /// Token resolver /// A instance - /// The signature reader which will be owned by us + /// The signature reader /// Generic parameter context /// A new instance or null if /// is invalid. - public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, IBinaryReader signature, GenericParamContext gpContext) { + public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature, GenericParamContext gpContext) { try { - using (var reader = new SignatureReader(helper, corLibTypes, signature, gpContext)) { - if (reader.reader.Length == 0) - return null; - return reader.ReadSig(); - } + var reader = new SignatureReader(helper, corLibTypes, ref signature, gpContext); + if (reader.reader.Length == 0) + return null; + return reader.ReadSig(); } catch { return null; @@ -187,9 +181,8 @@ public static CallingConventionSig ReadSig(ISignatureReaderHelper helper, ICorLi /// #Blob stream offset of signature /// A new instance or null if /// is invalid. - public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig) { - return ReadTypeSig(readerModule, sig, new GenericParamContext()); - } + public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig) => + ReadTypeSig(readerModule, sig, new GenericParamContext()); /// /// Reads a type signature from the #Blob stream @@ -201,8 +194,8 @@ public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig) { /// is invalid. public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig, GenericParamContext gpContext) { try { - using (var reader = new SignatureReader(readerModule, sig, gpContext)) - return reader.ReadType(); + var reader = new SignatureReader(readerModule, sig, gpContext); + return reader.ReadType(); } catch { return null; @@ -218,9 +211,8 @@ public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig, GenericPar /// here, else this will be null /// A new instance or null if /// is invalid. - public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig, out byte[] extraData) { - return ReadTypeSig(readerModule, sig, new GenericParamContext(), out extraData); - } + public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig, out byte[] extraData) => + ReadTypeSig(readerModule, sig, new GenericParamContext(), out extraData); /// /// Reads a type signature from the #Blob stream @@ -234,18 +226,17 @@ public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig, out byte[] /// is invalid. public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig, GenericParamContext gpContext, out byte[] extraData) { try { - using (var reader = new SignatureReader(readerModule, sig, gpContext)) { - TypeSig ts; - try { - ts = reader.ReadType(); - } - catch (IOException) { - reader.reader.Position = 0; - ts = null; - } - extraData = reader.GetExtraData(); - return ts; + var reader = new SignatureReader(readerModule, sig, gpContext); + TypeSig ts; + try { + ts = reader.ReadType(); } + catch (IOException) { + reader.reader.Position = 0; + ts = null; + } + extraData = reader.GetExtraData(); + return ts; } catch { extraData = null; @@ -260,9 +251,8 @@ public static TypeSig ReadTypeSig(ModuleDefMD readerModule, uint sig, GenericPar /// The signature data /// A new instance or null if /// is invalid. - public static TypeSig ReadTypeSig(ModuleDefMD module, byte[] signature) { - return ReadTypeSig(module, module.CorLibTypes, MemoryImageStream.Create(signature), new GenericParamContext()); - } + public static TypeSig ReadTypeSig(ModuleDefMD module, byte[] signature) => + ReadTypeSig(module, module.CorLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), new GenericParamContext()); /// /// Reads a signature @@ -272,32 +262,29 @@ public static TypeSig ReadTypeSig(ModuleDefMD module, byte[] signature) { /// Generic parameter context /// A new instance or null if /// is invalid. - public static TypeSig ReadTypeSig(ModuleDefMD module, byte[] signature, GenericParamContext gpContext) { - return ReadTypeSig(module, module.CorLibTypes, MemoryImageStream.Create(signature), gpContext); - } + public static TypeSig ReadTypeSig(ModuleDefMD module, byte[] signature, GenericParamContext gpContext) => + ReadTypeSig(module, module.CorLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext); /// /// Reads a signature /// /// The module where the signature is located in - /// The signature reader which will be owned by us + /// The signature reader /// A new instance or null if /// is invalid. - public static TypeSig ReadTypeSig(ModuleDefMD module, IBinaryReader signature) { - return ReadTypeSig(module, module.CorLibTypes, signature, new GenericParamContext()); - } + public static TypeSig ReadTypeSig(ModuleDefMD module, DataReader signature) => + ReadTypeSig(module, module.CorLibTypes, signature, new GenericParamContext()); /// /// Reads a signature /// /// The module where the signature is located in - /// The signature reader which will be owned by us + /// The signature reader /// Generic parameter context /// A new instance or null if /// is invalid. - public static TypeSig ReadTypeSig(ModuleDefMD module, IBinaryReader signature, GenericParamContext gpContext) { - return ReadTypeSig(module, module.CorLibTypes, signature, gpContext); - } + public static TypeSig ReadTypeSig(ModuleDefMD module, DataReader signature, GenericParamContext gpContext) => + ReadTypeSig(module, module.CorLibTypes, signature, gpContext); /// /// Reads a signature @@ -307,9 +294,8 @@ public static TypeSig ReadTypeSig(ModuleDefMD module, IBinaryReader signature, G /// The signature data /// A new instance or null if /// is invalid. - public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature) { - return ReadTypeSig(helper, corLibTypes, MemoryImageStream.Create(signature), new GenericParamContext()); - } + public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature) => + ReadTypeSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), new GenericParamContext()); /// /// Reads a signature @@ -320,35 +306,31 @@ public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes co /// Generic parameter context /// A new instance or null if /// is invalid. - public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature, GenericParamContext gpContext) { - return ReadTypeSig(helper, corLibTypes, MemoryImageStream.Create(signature), gpContext); - } + public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature, GenericParamContext gpContext) => + ReadTypeSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext); /// /// Reads a signature /// /// Token resolver /// A instance - /// The signature reader which will be owned by us + /// The signature reader /// A new instance or null if /// is invalid. - public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, IBinaryReader signature) { - return ReadTypeSig(helper, corLibTypes, signature, new GenericParamContext()); - } + public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature) => + ReadTypeSig(helper, corLibTypes, signature, new GenericParamContext()); /// /// Reads a signature /// /// Token resolver /// A instance - /// The signature reader which will be owned by us + /// The signature reader /// Generic parameter context /// A new instance or null if /// is invalid. - public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, IBinaryReader signature, GenericParamContext gpContext) { - byte[] extraData; - return ReadTypeSig(helper, corLibTypes, signature, gpContext, out extraData); - } + public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature, GenericParamContext gpContext) => + ReadTypeSig(helper, corLibTypes, signature, gpContext, out var extraData); /// /// Reads a signature @@ -361,35 +343,33 @@ public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes co /// here, else this will be null /// A new instance or null if /// is invalid. - public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature, GenericParamContext gpContext, out byte[] extraData) { - return ReadTypeSig(helper, corLibTypes, MemoryImageStream.Create(signature), gpContext, out extraData); - } + public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, byte[] signature, GenericParamContext gpContext, out byte[] extraData) => + ReadTypeSig(helper, corLibTypes, ByteArrayDataReaderFactory.CreateReader(signature), gpContext, out extraData); /// /// Reads a signature /// /// Token resolver /// A instance - /// The signature reader which will be owned by us + /// The signature reader /// Generic parameter context /// If there's any extra data after the signature, it's saved /// here, else this will be null /// A new instance or null if /// is invalid. - public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, IBinaryReader signature, GenericParamContext gpContext, out byte[] extraData) { + public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, DataReader signature, GenericParamContext gpContext, out byte[] extraData) { try { - using (var reader = new SignatureReader(helper, corLibTypes, signature, gpContext)) { - TypeSig ts; - try { - ts = reader.ReadType(); - } - catch (IOException) { - reader.reader.Position = 0; - ts = null; - } - extraData = reader.GetExtraData(); - return ts; + var reader = new SignatureReader(helper, corLibTypes, ref signature, gpContext); + TypeSig ts; + try { + ts = reader.ReadType(); } + catch (IOException) { + reader.reader.Position = 0; + ts = null; + } + extraData = reader.GetExtraData(); + return ts; } catch { extraData = null; @@ -403,8 +383,12 @@ public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes co /// Reader module /// #Blob stream offset of signature /// Generic parameter context - SignatureReader(ModuleDefMD readerModule, uint sig, GenericParamContext gpContext) - : this(readerModule, readerModule.CorLibTypes, readerModule.BlobStream.CreateStream(sig), gpContext) { + SignatureReader(ModuleDefMD readerModule, uint sig, GenericParamContext gpContext) { + helper = readerModule; + corLibTypes = readerModule.CorLibTypes; + reader = readerModule.BlobStream.CreateReader(sig); + this.gpContext = gpContext; + recursionCounter = new RecursionCounter(); } /// @@ -414,16 +398,16 @@ public static TypeSig ReadTypeSig(ISignatureReaderHelper helper, ICorLibTypes co /// A instance /// The signature data /// Generic parameter context - SignatureReader(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, IBinaryReader reader, GenericParamContext gpContext) { + SignatureReader(ISignatureReaderHelper helper, ICorLibTypes corLibTypes, ref DataReader reader, GenericParamContext gpContext) { this.helper = helper; this.corLibTypes = corLibTypes; this.reader = reader; this.gpContext = gpContext; - this.recursionCounter = new RecursionCounter(); + recursionCounter = new RecursionCounter(); } byte[] GetExtraData() { - if (reader.Position >= reader.Length) + if (reader.Position == reader.Length) return null; return reader.ReadRemainingBytes(); } @@ -445,6 +429,7 @@ CallingConventionSig ReadSig() { case CallingConvention.ThisCall: case CallingConvention.FastCall: case CallingConvention.VarArg: + case CallingConvention.Unmanaged: case CallingConvention.NativeVarArg: result = ReadMethod(callingConvention); break; @@ -465,7 +450,6 @@ CallingConventionSig ReadSig() { result = ReadGenericInstMethod(callingConvention); break; - case CallingConvention.Unmanaged: default: result = null; break; @@ -480,38 +464,30 @@ CallingConventionSig ReadSig() { /// /// First byte of signature /// A new instance - FieldSig ReadField(CallingConvention callingConvention) { - return new FieldSig(callingConvention, ReadType()); - } + FieldSig ReadField(CallingConvention callingConvention) => new FieldSig(callingConvention, ReadType()); /// /// Reads a /// /// First byte of signature /// A new instance - MethodSig ReadMethod(CallingConvention callingConvention) { - return ReadSig(new MethodSig(callingConvention)); - } + MethodSig ReadMethod(CallingConvention callingConvention) => ReadSig(new MethodSig(callingConvention)); /// /// Reads a /// /// First byte of signature /// A new instance - PropertySig ReadProperty(CallingConvention callingConvention) { - return ReadSig(new PropertySig(callingConvention)); - } + PropertySig ReadProperty(CallingConvention callingConvention) => ReadSig(new PropertySig(callingConvention)); T ReadSig(T methodSig) where T : MethodBaseSig { if (methodSig.Generic) { - uint count; - if (!reader.ReadCompressedUInt32(out count)) + if (!reader.TryReadCompressedUInt32(out uint count) || count > 0x10000) return null; methodSig.GenParamCount = count; } - uint numParams; - if (!reader.ReadCompressedUInt32(out numParams)) + if (!reader.TryReadCompressedUInt32(out uint numParams) || numParams > 0x10000 || numParams > reader.BytesLeft) return null; methodSig.RetType = ReadType(); @@ -520,8 +496,8 @@ T ReadSig(T methodSig) where T : MethodBaseSig { for (uint i = 0; i < numParams; i++) { var type = ReadType(); if (type is SentinelSig) { - if (methodSig.ParamsAfterSentinel == null) - methodSig.ParamsAfterSentinel = parameters = ThreadSafeListCreator.Create((int)(numParams - i)); + if (methodSig.ParamsAfterSentinel is null) + methodSig.ParamsAfterSentinel = parameters = new List((int)(numParams - i)); i--; } else @@ -537,8 +513,7 @@ T ReadSig(T methodSig) where T : MethodBaseSig { /// First byte of signature /// A new instance LocalSig ReadLocalSig(CallingConvention callingConvention) { - uint count; - if (!reader.ReadCompressedUInt32(out count)) + if (!reader.TryReadCompressedUInt32(out uint count) || count > 0x10000 || count > reader.BytesLeft) return null; var sig = new LocalSig(callingConvention, count); var locals = sig.Locals; @@ -553,8 +528,7 @@ LocalSig ReadLocalSig(CallingConvention callingConvention) { /// First byte of signature /// A new instance GenericInstMethodSig ReadGenericInstMethod(CallingConvention callingConvention) { - uint count; - if (!reader.ReadCompressedUInt32(out count)) + if (!reader.TryReadCompressedUInt32(out uint count) || count > 0x10000 || count > reader.BytesLeft) return null; var sig = new GenericInstMethodSig(callingConvention, count); var args = sig.GenericArguments; @@ -566,12 +540,13 @@ GenericInstMethodSig ReadGenericInstMethod(CallingConvention callingConvention) /// /// Reads the next type /// + /// true if a TypeSpec is allowed if the next type is a class/value-type /// A new instance or null if invalid element type - TypeSig ReadType() { + TypeSig ReadType(bool allowTypeSpec = false) { if (!recursionCounter.Increment()) return null; - uint num; + uint num, i; TypeSig nextType, result = null; switch ((ElementType)reader.ReadByte()) { case ElementType.Void: result = corLibTypes.Void; break; @@ -595,47 +570,47 @@ TypeSig ReadType() { case ElementType.Ptr: result = new PtrSig(ReadType()); break; case ElementType.ByRef: result = new ByRefSig(ReadType()); break; - case ElementType.ValueType: result = new ValueTypeSig(ReadTypeDefOrRef()); break; - case ElementType.Class: result = new ClassSig(ReadTypeDefOrRef()); break; + case ElementType.ValueType: result = new ValueTypeSig(ReadTypeDefOrRef(allowTypeSpec)); break; + case ElementType.Class: result = new ClassSig(ReadTypeDefOrRef(allowTypeSpec)); break; case ElementType.FnPtr: result = new FnPtrSig(ReadSig()); break; case ElementType.SZArray: result = new SZArraySig(ReadType()); break; - case ElementType.CModReqd: result = new CModReqdSig(ReadTypeDefOrRef(), ReadType()); break; - case ElementType.CModOpt: result = new CModOptSig(ReadTypeDefOrRef(), ReadType()); break; + case ElementType.CModReqd: result = new CModReqdSig(ReadTypeDefOrRef(true), ReadType()); break; + case ElementType.CModOpt: result = new CModOptSig(ReadTypeDefOrRef(true), ReadType()); break; case ElementType.Sentinel: result = new SentinelSig(); break; case ElementType.Pinned: result = new PinnedSig(ReadType()); break; case ElementType.Var: - if (!reader.ReadCompressedUInt32(out num)) + if (!reader.TryReadCompressedUInt32(out num)) break; result = new GenericVar(num, gpContext.Type); break; case ElementType.MVar: - if (!reader.ReadCompressedUInt32(out num)) + if (!reader.TryReadCompressedUInt32(out num)) break; result = new GenericMVar(num, gpContext.Method); break; case ElementType.ValueArray: nextType = ReadType(); - if (!reader.ReadCompressedUInt32(out num)) + if (!reader.TryReadCompressedUInt32(out num)) break; result = new ValueArraySig(nextType, num); break; case ElementType.Module: - if (!reader.ReadCompressedUInt32(out num)) + if (!reader.TryReadCompressedUInt32(out num)) break; result = new ModuleSig(num, ReadType()); break; case ElementType.GenericInst: nextType = ReadType(); - if (!reader.ReadCompressedUInt32(out num)) + if (!reader.TryReadCompressedUInt32(out num) || num > 0x10000 || num > reader.BytesLeft) break; var genericInstSig = new GenericInstSig(nextType as ClassOrValueTypeSig, num); var args = genericInstSig.GenericArguments; - for (uint i = 0; i < num; i++) + for (i = 0; i < num; i++) args.Add(ReadType()); result = genericInstSig; break; @@ -643,27 +618,31 @@ TypeSig ReadType() { case ElementType.Array: nextType = ReadType(); uint rank; - if (!reader.ReadCompressedUInt32(out rank)) + if (!reader.TryReadCompressedUInt32(out rank)) + break; + if (rank > MaxArrayRank) break; if (rank == 0) { result = new ArraySig(nextType, rank); break; } - if (!reader.ReadCompressedUInt32(out num)) + if (!reader.TryReadCompressedUInt32(out num)) + break; + if (num > rank) break; var sizes = new List((int)num); - for (uint i = 0; i < num; i++) { - uint size; - if (!reader.ReadCompressedUInt32(out size)) + for (i = 0; i < num; i++) { + if (!reader.TryReadCompressedUInt32(out uint size)) goto exit; sizes.Add(size); } - if (!reader.ReadCompressedUInt32(out num)) + if (!reader.TryReadCompressedUInt32(out num)) + break; + if (num > rank) break; var lowerBounds = new List((int)num); - for (uint i = 0; i < num; i++) { - int size; - if (!reader.ReadCompressedInt32(out size)) + for (i = 0; i < num; i++) { + if (!reader.TryReadCompressedInt32(out int size)) goto exit; lowerBounds.Add(size); } @@ -690,21 +669,12 @@ TypeSig ReadType() { return result; } - /// - /// Reads a TypeDefOrRef - /// - /// A instance - ITypeDefOrRef ReadTypeDefOrRef() { - uint codedToken; - if (!reader.ReadCompressedUInt32(out codedToken)) + ITypeDefOrRef ReadTypeDefOrRef(bool allowTypeSpec) { + if (!reader.TryReadCompressedUInt32(out uint codedToken)) return null; - return helper.ResolveTypeDefOrRef(codedToken, gpContext); - } - - /// - public void Dispose() { - if (reader != null) - reader.Dispose(); + if (!allowTypeSpec && CodedToken.TypeDefOrRef.Decode2(codedToken).Table == Table.TypeSpec) + return null; + return helper.ResolveTypeDefOrRef(codedToken, default); } } } diff --git a/src/DotNet/StandAloneSig.cs b/src/DotNet/StandAloneSig.cs index 336524d92..84c362577 100644 --- a/src/DotNet/StandAloneSig.cs +++ b/src/DotNet/StandAloneSig.cs @@ -1,41 +1,40 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// /// A high-level representation of a row in the StandAloneSig table /// - public abstract class StandAloneSig : IHasCustomAttribute, IContainsGenericParameter { + public abstract class StandAloneSig : IHasCustomAttribute, IHasCustomDebugInformation, IContainsGenericParameter { /// /// The row id in its table /// protected uint rid; /// - public MDToken MDToken { - get { return new MDToken(Table.StandAloneSig, rid); } - } + public MDToken MDToken => new MDToken(Table.StandAloneSig, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int HasCustomAttributeTag { - get { return 11; } - } + public int HasCustomAttributeTag => 11; /// /// From column StandAloneSig.Signature /// public CallingConventionSig Signature { - get { return signature; } - set { signature = value; } + get => signature; + set => signature = value; } /// protected CallingConventionSig signature; @@ -45,7 +44,7 @@ public CallingConventionSig Signature { /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -53,35 +52,52 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 11; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// /// Gets/sets the method sig /// public MethodSig MethodSig { - get { return signature as MethodSig; } - set { signature = value; } + get => signature as MethodSig; + set => signature = value; } /// /// Gets/sets the locals sig /// public LocalSig LocalSig { - get { return signature as LocalSig; } - set { signature = value; } + get => signature as LocalSig; + set => signature = value; } /// - public bool ContainsGenericParameter { - get { return TypeHelper.ContainsGenericParameter(this); } - } + public bool ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); } /// @@ -98,40 +114,42 @@ public StandAloneSigUser() { /// Constructor /// /// A locals sig - public StandAloneSigUser(LocalSig localSig) { - this.signature = localSig; - } + public StandAloneSigUser(LocalSig localSig) => signature = localSig; /// /// Constructor /// /// A method sig - public StandAloneSigUser(MethodSig methodSig) { - this.signature = methodSig; - } + public StandAloneSigUser(MethodSig methodSig) => signature = methodSig; } /// /// Created from a row in the StandAloneSig table /// - sealed class StandAloneSigMD : StandAloneSig, IMDTokenProviderMD { + sealed class StandAloneSigMD : StandAloneSig, IMDTokenProviderMD, IContainsGenericParameter2 { /// The module where this instance is located readonly ModuleDefMD readerModule; readonly uint origRid; + readonly GenericParamContext gpContext; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.StandAloneSig, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.StandAloneSig, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), gpContext, list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -142,16 +160,18 @@ protected override void InitializeCustomAttributes() { /// If is invalid public StandAloneSigMD(ModuleDefMD readerModule, uint rid, GenericParamContext gpContext) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.StandAloneSigTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("StandAloneSig rid {0} does not exist", rid)); + throw new BadImageFormatException($"StandAloneSig rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint signature = readerModule.TablesStream.ReadStandAloneSigRow2(origRid); - this.signature = readerModule.ReadSignature(signature, gpContext); + this.gpContext = gpContext; + bool b = readerModule.TablesStream.TryReadStandAloneSigRow(origRid, out var row); + Debug.Assert(b); + signature = readerModule.ReadSignature(row.Signature, gpContext); } } } diff --git a/src/DotNet/StrongNameKey.cs b/src/DotNet/StrongNameKey.cs index 5942f888b..25016a757 100644 --- a/src/DotNet/StrongNameKey.cs +++ b/src/DotNet/StrongNameKey.cs @@ -1,9 +1,10 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.IO; +using System.Runtime.Serialization; using System.Security.Cryptography; -using dnlib.Threading; +using System.Threading; namespace dnlib.DotNet { /// @@ -33,6 +34,15 @@ public InvalidKeyException(string message) public InvalidKeyException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Constructor + /// + /// + /// + protected InvalidKeyException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } } /// @@ -66,46 +76,30 @@ public static void WriteReverse(this BinaryWriter writer, byte[] data) { /// public sealed class StrongNamePublicKey { const uint RSA1_SIG = 0x31415352; - SignatureAlgorithm signatureAlgorithm; - AssemblyHashAlgorithm hashAlgorithm; - byte[] modulus; - byte[] publicExponent; + readonly SignatureAlgorithm signatureAlgorithm; + readonly AssemblyHashAlgorithm hashAlgorithm; + readonly byte[] modulus; + readonly byte[] publicExponent; /// /// Gets/sets the signature algorithm /// - public SignatureAlgorithm SignatureAlgorithm { - get { return signatureAlgorithm; } - set { signatureAlgorithm = value; } - } + public SignatureAlgorithm SignatureAlgorithm => signatureAlgorithm; /// /// Gets/sets the hash algorithm /// - public AssemblyHashAlgorithm HashAlgorithm { - get { return hashAlgorithm; } - set { hashAlgorithm = value; } - } + public AssemblyHashAlgorithm HashAlgorithm => hashAlgorithm; /// /// Gets/sets the modulus /// - public byte[] Modulus { - get { return modulus; } - set { modulus = value; } - } + public byte[] Modulus => modulus; /// /// Gets/sets the public exponent /// - public byte[] PublicExponent { - get { return publicExponent; } - set { - if (value == null || value.Length != 4) - throw new ArgumentException("PublicExponent must be exactly 4 bytes"); - publicExponent = value; - } - } + public byte[] PublicExponent => publicExponent; /// /// Default constructor @@ -159,28 +153,21 @@ public StrongNamePublicKey(PublicKey pk) /// /// Public key data /// Strong name key is invalid - public StrongNamePublicKey(byte[] pk) { - Initialize(new BinaryReader(new MemoryStream(pk))); - } + public StrongNamePublicKey(byte[] pk) : this(new BinaryReader(new MemoryStream(pk))) { } /// /// Constructor /// /// Public key file /// Strong name key is invalid - public StrongNamePublicKey(string filename) { - using (var fileStream = File.OpenRead(filename)) - Initialize(new BinaryReader(fileStream)); - } + public StrongNamePublicKey(string filename) : this(File.ReadAllBytes(filename)) { } /// /// Constructor /// /// Public key stream /// Strong name key is invalid - public StrongNamePublicKey(Stream stream) { - Initialize(new BinaryReader(stream)); - } + public StrongNamePublicKey(Stream stream) : this(new BinaryReader(stream)) { } /// /// Constructor @@ -188,15 +175,11 @@ public StrongNamePublicKey(Stream stream) { /// Public key reader /// Strong name key is invalid public StrongNamePublicKey(BinaryReader reader) { - Initialize(reader); - } - - void Initialize(BinaryReader reader) { try { // Read PublicKeyBlob signatureAlgorithm = (SignatureAlgorithm)reader.ReadUInt32(); hashAlgorithm = (AssemblyHashAlgorithm)reader.ReadUInt32(); - int pkLen = reader.ReadInt32(); + /*int pkLen = */reader.ReadInt32(); // Read PUBLICKEYSTRUC if (reader.ReadByte() != 6) @@ -223,9 +206,7 @@ void Initialize(BinaryReader reader) { /// /// Creates a public key blob /// - public byte[] CreatePublicKey() { - return CreatePublicKey(signatureAlgorithm, hashAlgorithm, modulus, publicExponent); - } + public byte[] CreatePublicKey() => CreatePublicKey(signatureAlgorithm, hashAlgorithm, modulus, publicExponent); internal static byte[] CreatePublicKey(SignatureAlgorithm sigAlg, AssemblyHashAlgorithm hashAlg, byte[] modulus, byte[] publicExponent) { if (sigAlg != SignatureAlgorithm.CALG_RSA_SIGN) @@ -247,9 +228,7 @@ internal static byte[] CreatePublicKey(SignatureAlgorithm sigAlg, AssemblyHashAl } /// - public override string ToString() { - return Utils.ToHex(CreatePublicKey(), false); - } + public override string ToString() => Utils.ToHex(CreatePublicKey(), false); } /// @@ -258,319 +237,97 @@ public override string ToString() { public sealed class StrongNameKey { const uint RSA2_SIG = 0x32415352; byte[] publicKey; - AssemblyHashAlgorithm hashAlg; - byte[] publicExponent; - byte[] modulus; - byte[] prime1; - byte[] prime2; - byte[] exponent1; - byte[] exponent2; - byte[] coefficient; - byte[] privateExponent; -#if THREAD_SAFE - readonly Lock theLock = Lock.Create(); -#endif + readonly AssemblyHashAlgorithm hashAlg; + readonly byte[] publicExponent; + readonly byte[] modulus; + readonly byte[] prime1; + readonly byte[] prime2; + readonly byte[] exponent1; + readonly byte[] exponent2; + readonly byte[] coefficient; + readonly byte[] privateExponent; /// /// Gets the public key /// public byte[] PublicKey { get { -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - if (publicKey == null) - publicKey = CreatePublicKey_NoLock(); + if (publicKey is null) + Interlocked.CompareExchange(ref publicKey, CreatePublicKey(), null); return publicKey; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif } } /// /// Gets the strong name signature size in bytes /// - public int SignatureSize { - get { -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif - return modulus.Length; -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif - } - } - - /// - /// Gets/sets the public key hash algorithm. It's usually - /// - public AssemblyHashAlgorithm HashAlgorithm { - get { -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif - return hashAlg; -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif - } - set { -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - if (hashAlg == value) - return; - publicKey = null; - hashAlg = value; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - } + public int SignatureSize => modulus.Length; /// - /// Gets/sets the public exponent + /// Gets the public key hash algorithm. It's usually /// - public byte[] PublicExponent { - get { -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif - return publicExponent; -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif - } - set { - if (value == null || value.Length != 4) - throw new ArgumentException("PublicExponent must be exactly 4 bytes"); -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - publicExponent = value; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - } + public AssemblyHashAlgorithm HashAlgorithm => hashAlg; /// - /// Gets/sets the modulus + /// Gets the public exponent /// - public byte[] Modulus { - get { -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif - return modulus; -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif - } - set { - if (value == null) - throw new ArgumentNullException("value"); -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - modulus = value; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - } + public byte[] PublicExponent => publicExponent; /// - /// Gets/sets prime1 + /// Gets the modulus /// - public byte[] Prime1 { - get { -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif - return prime1; -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif - } - set { - if (value == null) - throw new ArgumentNullException("value"); -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - prime1 = value; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - } + public byte[] Modulus => modulus; /// - /// Gets/sets prime2 + /// Gets prime1 /// - public byte[] Prime2 { - get { -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif - return prime2; -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif - } - set { - if (value == null) - throw new ArgumentNullException("value"); -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - prime2 = value; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - } + public byte[] Prime1 => prime1; /// - /// Gets/sets exponent1 + /// Gets prime2 /// - public byte[] Exponent1 { - get { -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif - return exponent1; -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif - } - set { - if (value == null) - throw new ArgumentNullException("value"); -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - exponent1 = value; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - } + public byte[] Prime2 => prime2; /// - /// Gets/sets exponent2 + /// Gets exponent1 /// - public byte[] Exponent2 { - get { -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif - return exponent2; -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif - } - set { - if (value == null) - throw new ArgumentNullException("value"); -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - exponent2 = value; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - } + public byte[] Exponent1 => exponent1; /// - /// Gets/sets the coefficient + /// Gets exponent2 /// - public byte[] Coefficient { - get { -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif - return coefficient; -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif - } - set { - if (value == null) - throw new ArgumentNullException("value"); -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - coefficient = value; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - } + public byte[] Exponent2 => exponent2; /// - /// Gets/sets the private exponent + /// Gets the coefficient /// - public byte[] PrivateExponent { - get { -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif - return privateExponent; -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif - } - set { - if (value == null) - throw new ArgumentNullException("value"); -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - privateExponent = value; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - } - } + public byte[] Coefficient => coefficient; /// - /// Default constructor + /// Gets the private exponent /// - public StrongNameKey() { - } + public byte[] PrivateExponent => privateExponent; /// /// Constructor /// /// Strong name key data /// Strong name key is invalid - public StrongNameKey(byte[] keyData) { - Initialize(new BinaryReader(new MemoryStream(keyData))); - } + public StrongNameKey(byte[] keyData) : this(new BinaryReader(new MemoryStream(keyData))) { } /// /// Constructor /// /// Strong name key file /// Strong name key is invalid - public StrongNameKey(string filename) { - using (var fileStream = File.OpenRead(filename)) - Initialize(new BinaryReader(fileStream)); - } + public StrongNameKey(string filename) : this(File.ReadAllBytes(filename)) { } /// /// Constructor /// /// Strong name key stream /// Strong name key is invalid - public StrongNameKey(Stream stream) { - Initialize(new BinaryReader(stream)); - } + public StrongNameKey(Stream stream) : this(new BinaryReader(stream)) { } /// /// Constructor @@ -578,19 +335,10 @@ public StrongNameKey(Stream stream) { /// Strong name key reader /// Strong name key is invalid public StrongNameKey(BinaryReader reader) { - Initialize(reader); - } - - /// - /// Initializes the public/private key pair data - /// - /// Public/private key pair reader - /// Strong name key is invalid - public void Initialize(BinaryReader reader) { /* * Links: - * http://msdn.microsoft.com/en-us/library/cc250013%28v=prot.20%29.aspx - * http://msdn.microsoft.com/en-us/library/windows/desktop/aa387689%28v=vs.85%29.aspx + * https://msdn.microsoft.com/en-us/library/cc250013%28v=prot.20%29.aspx + * https://docs.microsoft.com/en-us/windows/desktop/SecCrypto/rsa-schannel-key-blobs * * struct PublicKeyBlob { * unsigned int SigAlgID; // sig algorithm used to create the sig (00002400 = CALG_RSA_SIGN) @@ -665,7 +413,30 @@ public void Initialize(BinaryReader reader) { } } - byte[] CreatePublicKey_NoLock() { + StrongNameKey(AssemblyHashAlgorithm hashAlg, byte[] publicExponent, byte[] modulus, byte[] prime1, byte[] prime2, byte[] exponent1, byte[] exponent2, byte[] coefficient, byte[] privateExponent) { + this.hashAlg = hashAlg; + this.publicExponent = publicExponent; + this.modulus = modulus; + this.prime1 = prime1; + this.prime2 = prime2; + this.exponent1 = exponent1; + this.exponent2 = exponent2; + this.coefficient = coefficient; + this.privateExponent = privateExponent; + } + + /// + /// Creates a strong name key with a new hash algorithm + /// + /// Algorithm + /// + public StrongNameKey WithHashAlgorithm(AssemblyHashAlgorithm hashAlgorithm) { + if (hashAlg == hashAlgorithm) + return this; + return new StrongNameKey(hashAlgorithm, publicExponent, modulus, prime1, prime2, exponent1, exponent2, coefficient, privateExponent); + } + + byte[] CreatePublicKey() { var halg = hashAlg == 0 ? AssemblyHashAlgorithm.SHA1 : hashAlg; return StrongNamePublicKey.CreatePublicKey(SignatureAlgorithm.CALG_RSA_SIGN, halg, modulus, publicExponent); } @@ -675,9 +446,6 @@ byte[] CreatePublicKey_NoLock() { /// public RSA CreateRSA() { RSAParameters rsaParams; -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif rsaParams = new RSAParameters { Exponent = publicExponent, Modulus = modulus, @@ -688,9 +456,6 @@ public RSA CreateRSA() { InverseQ = coefficient, D = privateExponent, }; -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif var rsa = RSA.Create(); try { rsa.ImportParameters(rsaParams); @@ -713,9 +478,6 @@ public byte[] CreateStrongName() { writer.Write((ushort)0); // reserved writer.Write((uint)SignatureAlgorithm.CALG_RSA_SIGN); // aiKeyAlg writer.Write(RSA2_SIG); // magic (RSA2) -#if THREAD_SAFE - theLock.EnterReadLock(); try { -#endif writer.Write(modulus.Length * 8); // bitlen writer.WriteReverse(publicExponent); writer.WriteReverse(modulus); @@ -725,9 +487,6 @@ public byte[] CreateStrongName() { writer.WriteReverse(exponent2); writer.WriteReverse(coefficient); writer.WriteReverse(privateExponent); -#if THREAD_SAFE - } finally { theLock.ExitReadLock(); } -#endif return outStream.ToArray(); } diff --git a/src/DotNet/StrongNameSigner.cs b/src/DotNet/StrongNameSigner.cs index 0e8d42edd..75e93792e 100644 --- a/src/DotNet/StrongNameSigner.cs +++ b/src/DotNet/StrongNameSigner.cs @@ -1,15 +1,15 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.IO; using System.Security.Cryptography; namespace dnlib.DotNet { /// /// Strong name signs an assembly. It supports normal strong name signing and the new - /// (.NET 4.5) enhanced strong name signing. + /// (.NET Framework 4.5) enhanced strong name signing. /// - public struct StrongNameSigner { + public readonly struct StrongNameSigner { readonly Stream stream; readonly long baseOffset; @@ -78,7 +78,7 @@ byte[] StrongNameHashData(AssemblyHashAlgorithm hashAlg, long snSigOffset, uint long snSigOffsetEnd = snSigOffset + snSigSize; using (var hasher = new AssemblyHash(hashAlg)) { - byte[] buffer = new byte[0x8000]; + var buffer = new byte[0x8000]; // Hash the DOS header. It's defined to be all data from the start of // the file up to the NT headers. diff --git a/src/DotNet/TIAHelper.cs b/src/DotNet/TIAHelper.cs index 441e52627..3fc034946 100644 --- a/src/DotNet/TIAHelper.cs +++ b/src/DotNet/TIAHelper.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info // See coreclr/src/vm/siginfo.cpp @@ -10,26 +10,23 @@ namespace dnlib.DotNet { /// System.Runtime.InteropServices.TypeIdentifierAttribute helper code used by /// static class TIAHelper { - struct Info : IEquatable { + readonly struct Info : IEquatable { public readonly UTF8String Scope; public readonly UTF8String Identifier; public Info(UTF8String scope, UTF8String identifier) { - this.Scope = scope; - this.Identifier = identifier; + Scope = scope; + Identifier = identifier; } - public bool Equals(Info other) { - return stricmp(Scope, other.Scope) && - UTF8String.Equals(Identifier, other.Identifier); - } + public bool Equals(Info other) => stricmp(Scope, other.Scope) && UTF8String.Equals(Identifier, other.Identifier); static bool stricmp(UTF8String a, UTF8String b) { - var da = (object)a == null ? null : a.Data; - var db = (object)b == null ? null : b.Data; + var da = a?.Data; + var db = b?.Data; if (da == db) return true; - if (da == null || db == null) + if (da is null || db is null) return false; if (da.Length != db.Length) return false; @@ -47,14 +44,14 @@ static bool stricmp(UTF8String a, UTF8String b) { } static Info? GetInfo(TypeDef td) { - if (td == null) + if (td is null) return null; if (td.IsWindowsRuntime) return null; UTF8String scope = null, identifier = null; var tia = td.CustomAttributes.Find("System.Runtime.InteropServices.TypeIdentifierAttribute"); - if (tia != null) { + if (tia is not null) { if (tia.ConstructorArguments.Count >= 2) { if (tia.ConstructorArguments[0].Type.GetElementType() != ElementType.String) return null; @@ -65,9 +62,8 @@ static bool stricmp(UTF8String a, UTF8String b) { } } else { - var mod = td.Module; - var asm = mod == null ? null : mod.Assembly; - if (asm == null) + var asm = td.Module?.Assembly; + if (asm is null) return null; bool isTypeLib = asm.CustomAttributes.IsDefined("System.Runtime.InteropServices.ImportedFromTypeLibAttribute") || asm.CustomAttributes.IsDefined("System.Runtime.InteropServices.PrimaryInteropAssemblyAttribute"); @@ -80,13 +76,12 @@ static bool stricmp(UTF8String a, UTF8String b) { if (td.IsInterface && td.IsImport) gca = td.CustomAttributes.Find("System.Runtime.InteropServices.GuidAttribute"); else { - var mod = td.Module; - var asm = mod == null ? null : mod.Assembly; - if (asm == null) + var asm = td.Module?.Assembly; + if (asm is null) return null; gca = asm.CustomAttributes.Find("System.Runtime.InteropServices.GuidAttribute"); } - if (gca == null) + if (gca is null) return null; if (gca.ConstructorArguments.Count < 1) return null; @@ -98,13 +93,12 @@ static bool stricmp(UTF8String a, UTF8String b) { if (UTF8String.IsNullOrEmpty(ns)) identifier = name; else if (UTF8String.IsNullOrEmpty(name)) - identifier = new UTF8String(Concat(ns.Data, (byte)'.', empty)); + identifier = new UTF8String(Concat(ns.Data, (byte)'.', Array2.Empty())); else identifier = new UTF8String(Concat(ns.Data, (byte)'.', name.Data)); } return new Info(scope, identifier); } - static readonly byte[] empty = new byte[0]; static byte[] Concat(byte[] a, byte b, byte[] c) { var data = new byte[a.Length + 1 + c.Length]; @@ -116,13 +110,15 @@ static byte[] Concat(byte[] a, byte b, byte[] c) { return data; } + internal static bool IsTypeDefEquivalent(TypeDef td) => GetInfo(td) is not null && CheckEquivalent(td); + static bool CheckEquivalent(TypeDef td) { - Debug.Assert(td != null); + Debug.Assert(td is not null); - for (int i = 0; td != null && i < 1000; i++) { + for (int i = 0; td is not null && i < 1000; i++) { if (i != 0) { var info = GetInfo(td); - if (info == null) + if (info is null) return false; } @@ -137,7 +133,7 @@ static bool CheckEquivalent(TypeDef td) { return false; var declType = td.DeclaringType; - if (declType == null) + if (declType is null) return td.IsPublic; if (!td.IsNestedPublic) @@ -150,10 +146,10 @@ static bool CheckEquivalent(TypeDef td) { public static bool Equivalent(TypeDef td1, TypeDef td2) { var info1 = GetInfo(td1); - if (info1 == null) + if (info1 is null) return false; var info2 = GetInfo(td2); - if (info2 == null) + if (info2 is null) return false; if (!CheckEquivalent(td1) || !CheckEquivalent(td2)) return false; @@ -170,7 +166,7 @@ public static bool Equivalent(TypeDef td1, TypeDef td2) { else { var bt1 = td1.BaseType; var bt2 = td2.BaseType; - if (bt1 == null || bt2 == null) + if (bt1 is null || bt2 is null) return false; if (td1.IsDelegate) { if (!td2.IsDelegate) @@ -192,9 +188,9 @@ public static bool Equivalent(TypeDef td1, TypeDef td2) { td1 = td1.DeclaringType; td2 = td2.DeclaringType; - if (td1 == null && td2 == null) + if (td1 is null && td2 is null) break; - if (td1 == null || td2 == null) + if (td1 is null || td2 is null) return false; } @@ -204,7 +200,7 @@ public static bool Equivalent(TypeDef td1, TypeDef td2) { static bool DelegateEquals(TypeDef td1, TypeDef td2) { var invoke1 = td1.FindMethod(InvokeString); var invoke2 = td2.FindMethod(InvokeString); - if (invoke1 == null || invoke2 == null) + if (invoke1 is null || invoke2 is null) return false; //TODO: Compare method signatures. Prevent infinite recursion... diff --git a/src/DotNet/TypeAttributes.cs b/src/DotNet/TypeAttributes.cs index 2ae0aa21b..bae7b4930 100644 --- a/src/DotNet/TypeAttributes.cs +++ b/src/DotNet/TypeAttributes.cs @@ -35,6 +35,8 @@ public enum TypeAttributes : uint { SequentialLayout = 0x00000008, /// Layout is supplied explicitly ExplicitLayout = 0x00000010, + /// Layout is supplied via the System.Runtime.InteropServices.ExtendedLayoutAttribute + ExtendedLayout = 0x00000018, /// Use this mask to retrieve class semantics information. ClassSemanticsMask = 0x00000020, diff --git a/src/DotNet/TypeDef.cs b/src/DotNet/TypeDef.cs index df1146c60..9c2c94327 100644 --- a/src/DotNet/TypeDef.cs +++ b/src/DotNet/TypeDef.cs @@ -1,24 +1,20 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.Threading; using dnlib.Utils; using dnlib.DotNet.MD; using dnlib.DotNet.Emit; using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif +using dnlib.DotNet.Pdb; +using System.Diagnostics; namespace dnlib.DotNet { /// /// A high-level representation of a row in the TypeDef table /// - public abstract class TypeDef : ITypeDefOrRef, IHasCustomAttribute, IHasDeclSecurity, IMemberRefParent, ITypeOrMethodDef, IListListener, IListListener, IListListener, IListListener, IListListener, IListListener, IMemberRefResolver, IMemberDef { + public abstract class TypeDef : ITypeDefOrRef, IHasCustomAttribute, IHasDeclSecurity, IMemberRefParent, ITypeOrMethodDef, IHasCustomDebugInformation, IListListener, IListListener, IListListener, IListListener, IListListener, IListListener, IMemberRefResolver, IMemberDef { /// /// The row id in its table /// @@ -29,108 +25,70 @@ public abstract class TypeDef : ITypeDefOrRef, IHasCustomAttribute, IHasDeclSecu #endif /// - public MDToken MDToken { - get { return new MDToken(Table.TypeDef, rid); } - } + public MDToken MDToken => new MDToken(Table.TypeDef, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int TypeDefOrRefTag { - get { return 0; } - } + public int TypeDefOrRefTag => 0; /// - public int HasCustomAttributeTag { - get { return 3; } - } + public int HasCustomAttributeTag => 3; /// - public int HasDeclSecurityTag { - get { return 0; } - } + public int HasDeclSecurityTag => 0; /// - public int MemberRefParentTag { - get { return 0; } - } + public int MemberRefParentTag => 0; /// - public int TypeOrMethodDefTag { - get { return 0; } - } + public int TypeOrMethodDefTag => 0; /// - int IGenericParameterProvider.NumberOfGenericParameters { - get { return GenericParameters.Count; } - } + int IGenericParameterProvider.NumberOfGenericParameters => GenericParameters.Count; /// - string IType.TypeName { - get { return FullNameCreator.Name(this, false); } - } + string IType.TypeName => Name; /// - public string ReflectionName { - get { return FullNameCreator.Name(this, true); } - } + public string ReflectionName => FullNameFactory.Name(this, true, null); /// - string IType.Namespace { - get { return FullNameCreator.Namespace(this, false); } - } + string IType.Namespace => Namespace; /// - public string ReflectionNamespace { - get { return FullNameCreator.Namespace(this, true); } - } + public string ReflectionNamespace => FullNameFactory.Namespace(this, true, null); /// - public string FullName { - get { return FullNameCreator.FullName(this, false); } - } + public string FullName => FullNameFactory.FullName(this, false, null, null); /// - public string ReflectionFullName { - get { return FullNameCreator.FullName(this, true); } - } + public string ReflectionFullName => FullNameFactory.FullName(this, true, null, null); /// - public string AssemblyQualifiedName { - get { return FullNameCreator.AssemblyQualifiedName(this); } - } + public string AssemblyQualifiedName => FullNameFactory.AssemblyQualifiedName(this, null, null); /// - public IAssembly DefinitionAssembly { - get { return FullNameCreator.DefinitionAssembly(this); } - } + public IAssembly DefinitionAssembly => FullNameFactory.DefinitionAssembly(this); /// - public IScope Scope { - get { return Module; } - } + public IScope Scope => Module; /// - public ITypeDefOrRef ScopeType { - get { return this; } - } + public ITypeDefOrRef ScopeType => this; /// /// Always returns false since a does not contain any /// or . /// - public bool ContainsGenericParameter { - get { return false; } - } + public bool ContainsGenericParameter => false; /// - public ModuleDef Module { - get { return FullNameCreator.OwnerModule(this); } - } + public ModuleDef Module => FullNameFactory.OwnerModule(this); /// /// Gets/sets the owner module @@ -171,68 +129,28 @@ void InitializeModule2() { } /// Called to initialize - protected virtual ModuleDef GetModule2_NoLock() { - return null; - } - - bool IIsTypeOrMethod.IsType { - get { return true; } - } - - bool IIsTypeOrMethod.IsMethod { - get { return false; } - } - - bool IMemberRef.IsField { - get { return false; } - } - - bool IMemberRef.IsTypeSpec { - get { return false; } - } - - bool IMemberRef.IsTypeRef { - get { return false; } - } - - bool IMemberRef.IsTypeDef { - get { return true; } - } - - bool IMemberRef.IsMethodSpec { - get { return false; } - } - - bool IMemberRef.IsMethodDef { - get { return false; } - } - - bool IMemberRef.IsMemberRef { - get { return false; } - } - - bool IMemberRef.IsFieldDef { - get { return false; } - } - - bool IMemberRef.IsPropertyDef { - get { return false; } - } - - bool IMemberRef.IsEventDef { - get { return false; } - } - - bool IMemberRef.IsGenericParam { - get { return false; } - } + protected virtual ModuleDef GetModule2_NoLock() => null; + + bool IIsTypeOrMethod.IsType => true; + bool IIsTypeOrMethod.IsMethod => false; + bool IMemberRef.IsField => false; + bool IMemberRef.IsTypeSpec => false; + bool IMemberRef.IsTypeRef => false; + bool IMemberRef.IsTypeDef => true; + bool IMemberRef.IsMethodSpec => false; + bool IMemberRef.IsMethodDef => false; + bool IMemberRef.IsMemberRef => false; + bool IMemberRef.IsFieldDef => false; + bool IMemberRef.IsPropertyDef => false; + bool IMemberRef.IsEventDef => false; + bool IMemberRef.IsGenericParam => false; /// /// From column TypeDef.Flags /// public TypeAttributes Attributes { - get { return (TypeAttributes)attributes; } - set { attributes = (int)value; } + get => (TypeAttributes)attributes; + set => attributes = (int)value; } /// Attributes protected int attributes; @@ -241,8 +159,8 @@ public TypeAttributes Attributes { /// From column TypeDef.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -251,8 +169,8 @@ public UTF8String Name { /// From column TypeDef.Namespace /// public UTF8String Namespace { - get { return @namespace; } - set { @namespace = value; } + get => @namespace; + set => @namespace = value; } /// Name protected UTF8String @namespace; @@ -296,21 +214,17 @@ void InitializeBaseType() { } /// Called to initialize - protected virtual ITypeDefOrRef GetBaseType_NoLock() { - return null; - } + protected virtual ITypeDefOrRef GetBaseType_NoLock() => null; /// Reset - protected void ResetBaseType() { - baseType_isInitialized = false; - } + protected void ResetBaseType() => baseType_isInitialized = false; /// /// From column TypeDef.FieldList /// - public ThreadSafe.IList Fields { + public IList Fields { get { - if (fields == null) + if (fields is null) InitializeFields(); return fields; } @@ -318,16 +232,15 @@ public ThreadSafe.IList Fields { /// protected LazyList fields; /// Initializes - protected virtual void InitializeFields() { + protected virtual void InitializeFields() => Interlocked.CompareExchange(ref fields, new LazyList(this), null); - } /// /// From column TypeDef.MethodList /// - public ThreadSafe.IList Methods { + public IList Methods { get { - if (methods == null) + if (methods is null) InitializeMethods(); return methods; } @@ -335,14 +248,13 @@ public ThreadSafe.IList Methods { /// protected LazyList methods; /// Initializes - protected virtual void InitializeMethods() { + protected virtual void InitializeMethods() => Interlocked.CompareExchange(ref methods, new LazyList(this), null); - } /// - public ThreadSafe.IList GenericParameters { + public IList GenericParameters { get { - if (genericParameters == null) + if (genericParameters is null) InitializeGenericParameters(); return genericParameters; } @@ -350,41 +262,38 @@ public ThreadSafe.IList GenericParameters { /// protected LazyList genericParameters; /// Initializes - protected virtual void InitializeGenericParameters() { + protected virtual void InitializeGenericParameters() => Interlocked.CompareExchange(ref genericParameters, new LazyList(this), null); - } /// /// Gets the interfaces /// - public ThreadSafe.IList Interfaces { + public IList Interfaces { get { - if (interfaces == null) + if (interfaces is null) InitializeInterfaces(); return interfaces; } } /// - protected ThreadSafe.IList interfaces; + protected IList interfaces; /// Initializes - protected virtual void InitializeInterfaces() { - Interlocked.CompareExchange(ref interfaces, ThreadSafeListCreator.Create(), null); - } + protected virtual void InitializeInterfaces() => + Interlocked.CompareExchange(ref interfaces, new List(), null); /// - public ThreadSafe.IList DeclSecurities { + public IList DeclSecurities { get { - if (declSecurities == null) + if (declSecurities is null) InitializeDeclSecurities(); return declSecurities; } } /// - protected ThreadSafe.IList declSecurities; + protected IList declSecurities; /// Initializes - protected virtual void InitializeDeclSecurities() { - Interlocked.CompareExchange(ref declSecurities, ThreadSafeListCreator.Create(), null); - } + protected virtual void InitializeDeclSecurities() => + Interlocked.CompareExchange(ref declSecurities, new List(), null); /// /// Gets/sets the class layout @@ -425,21 +334,17 @@ void InitializeClassLayout() { } ClassLayout GetOrCreateClassLayout() { var cl = ClassLayout; - if (cl != null) + if (cl is not null) return cl; Interlocked.CompareExchange(ref classLayout, new ClassLayoutUser(0, 0), null); return classLayout; } /// Called to initialize - protected virtual ClassLayout GetClassLayout_NoLock() { - return null; - } + protected virtual ClassLayout GetClassLayout_NoLock() => null; /// - public bool HasDeclSecurities { - get { return DeclSecurities.Count > 0; } - } + public bool HasDeclSecurities => DeclSecurities.Count > 0; /// /// Gets/sets the enclosing type. It's null if this isn't a nested class. @@ -454,9 +359,9 @@ public TypeDef DeclaringType { var currentDeclaringType = DeclaringType2; if (currentDeclaringType == value) return; - if (currentDeclaringType != null) + if (currentDeclaringType is not null) currentDeclaringType.NestedTypes.Remove(this); // Will set DeclaringType2 = null - if (value != null) + if (value is not null) value.NestedTypes.Add(this); // Will set DeclaringType2 = value // Make sure this is clear. Will be set whenever it's inserted into ModulDef.Types @@ -465,9 +370,7 @@ public TypeDef DeclaringType { } /// - ITypeDefOrRef IMemberRef.DeclaringType { - get { return DeclaringType; } - } + ITypeDefOrRef IMemberRef.DeclaringType => DeclaringType; /// /// Called by and should normally not be called by any user @@ -510,16 +413,14 @@ void InitializeDeclaringType2() { } /// Called to initialize - protected virtual TypeDef GetDeclaringType2_NoLock() { - return null; - } + protected virtual TypeDef GetDeclaringType2_NoLock() => null; /// /// Gets all the nested types /// - public ThreadSafe.IList NestedTypes { + public IList NestedTypes { get { - if (nestedTypes == null) + if (nestedTypes is null) InitializeNestedTypes(); return nestedTypes; } @@ -527,16 +428,15 @@ public ThreadSafe.IList NestedTypes { /// protected LazyList nestedTypes; /// Initializes - protected virtual void InitializeNestedTypes() { + protected virtual void InitializeNestedTypes() => Interlocked.CompareExchange(ref nestedTypes, new LazyList(this), null); - } /// /// Gets all events /// - public ThreadSafe.IList Events { + public IList Events { get { - if (events == null) + if (events is null) InitializeEvents(); return events; } @@ -544,16 +444,15 @@ public ThreadSafe.IList Events { /// protected LazyList events; /// Initializes - protected virtual void InitializeEvents() { + protected virtual void InitializeEvents() => Interlocked.CompareExchange(ref events, new LazyList(this), null); - } /// /// Gets all properties /// - public ThreadSafe.IList Properties { + public IList Properties { get { - if (properties == null) + if (properties is null) InitializeProperties(); return properties; } @@ -561,16 +460,15 @@ public ThreadSafe.IList Properties { /// protected LazyList properties; /// Initializes - protected virtual void InitializeProperties() { + protected virtual void InitializeProperties() => Interlocked.CompareExchange(ref properties, new LazyList(this), null); - } /// /// Gets all custom attributes /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -578,70 +476,73 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 3; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// /// true if there's at least one in /// - public bool HasFields { - get { return Fields.Count > 0; } - } + public bool HasFields => Fields.Count > 0; /// /// true if there's at least one in /// - public bool HasMethods { - get { return Methods.Count > 0; } - } + public bool HasMethods => Methods.Count > 0; /// /// true if there's at least one in /// - public bool HasGenericParameters { - get { return GenericParameters.Count > 0; } - } + public bool HasGenericParameters => GenericParameters.Count > 0; /// /// true if there's at least one in /// - public bool HasEvents { - get { return Events.Count > 0; } - } + public bool HasEvents => Events.Count > 0; /// /// true if there's at least one in /// - public bool HasProperties { - get { return Properties.Count > 0; } - } + public bool HasProperties => Properties.Count > 0; /// /// true if there's at least one in /// - public bool HasNestedTypes { - get { return NestedTypes.Count > 0; } - } + public bool HasNestedTypes => NestedTypes.Count > 0; /// /// true if there's at least one in /// - public bool HasInterfaces { - get { return Interfaces.Count > 0; } - } + public bool HasInterfaces => Interfaces.Count > 0; /// /// true if is not null /// - public bool HasClassLayout { - get { return ClassLayout != null; } - } + public bool HasClassLayout => ClassLayout is not null; /// /// Gets/sets the packing size. If you write to this property but @@ -651,7 +552,7 @@ public bool HasClassLayout { public ushort PackingSize { get { var cl = ClassLayout; - return cl == null ? ushort.MaxValue : cl.PackingSize; + return cl is null ? ushort.MaxValue : cl.PackingSize; } set { var cl = GetOrCreateClassLayout(); @@ -667,7 +568,7 @@ public ushort PackingSize { public uint ClassSize { get { var cl = ClassLayout; - return cl == null ? uint.MaxValue : cl.ClassSize; + return cl is null ? uint.MaxValue : cl.ClassSize; } set { var cl = GetOrCreateClassLayout(); @@ -678,32 +579,66 @@ public uint ClassSize { /// public bool IsValueType { get { + // Don't include abstract since value types can be abstract without throwing at runtime + // Also don't check for sealed, since the CLR doesn't throw at runtime + if ((Attributes & TypeAttributes.ClassSemanticsMask) != TypeAttributes.Class) + return false; var baseType = BaseType; - if (baseType == null) + if (baseType is null) return false; - if (baseType.Namespace != "System") + if (!baseType.DefinitionAssembly.IsCorLib()) return false; - if (baseType.TypeName != "ValueType" && baseType.TypeName != "Enum") + + // PERF: Don't allocate a System.String by calling FullName etc. + UTF8String baseName, baseNamespace; + if (baseType is TypeRef baseTr) { + baseName = baseTr.Name; + baseNamespace = baseTr.Namespace; + } + else { + var baseTd = baseType as TypeDef; + if (baseTd is null) + return false; + baseName = baseTd.Name; + baseNamespace = baseTd.Namespace; + } + + if (baseNamespace != systemString) return false; - if (!baseType.DefinitionAssembly.IsCorLib()) + if (baseName != valueTypeString && baseName != enumString) return false; - return !(FullName == "System.Enum" && DefinitionAssembly.IsCorLib()); + + if (!DefinitionAssembly.IsCorLib()) + return true; + return !(Name == enumString && Namespace == systemString); } } + static readonly UTF8String systemString = new UTF8String("System"); + static readonly UTF8String enumString = new UTF8String("Enum"); + static readonly UTF8String valueTypeString = new UTF8String("ValueType"); + static readonly UTF8String multicastDelegateString = new UTF8String("MulticastDelegate"); /// /// true if it's an enum /// public bool IsEnum { get { - var baseType = BaseType; - if (baseType == null) + // Don't include abstract since value types can be abstract without throwing at runtime + // Also don't check for sealed, since the CLR doesn't throw at runtime + if ((Attributes & TypeAttributes.ClassSemanticsMask) != TypeAttributes.Class) return false; - if (baseType.Namespace != "System") + var baseType = BaseType; + if (baseType is null) return false; - if (baseType.TypeName != "Enum") + if (!baseType.DefinitionAssembly.IsCorLib()) return false; - return baseType.DefinitionAssembly.IsCorLib(); + + // PERF: Don't allocate a System.String by calling FullName etc. + if (baseType is TypeRef baseTr) + return baseTr.Namespace == systemString && baseTr.Name == enumString; + if (baseType is TypeDef baseTd) + return baseTd.Namespace == systemString && baseTd.Name == enumString; + return false; } } @@ -712,28 +647,35 @@ public bool IsEnum { /// public bool IsDelegate { get { - var baseType = BaseType; - if (baseType == null) + if ((Attributes & (TypeAttributes.Abstract | TypeAttributes.ClassSemanticsMask)) != TypeAttributes.Class) return false; - if (baseType.Namespace != "System") + var baseType = BaseType; + if (baseType is null) return false; - if (baseType.TypeName != "MulticastDelegate") + if (!baseType.DefinitionAssembly.IsCorLib()) return false; - return baseType.DefinitionAssembly.IsCorLib(); + + // PERF: Don't allocate a System.String by calling FullName etc. + if (baseType is TypeRef baseTr) + return baseTr.Namespace == systemString && baseTr.Name == multicastDelegateString; + if (baseType is TypeDef baseTd) + return baseTd.Namespace == systemString && baseTd.Name == multicastDelegateString; + return false; } } /// /// true if this is a nested type (it has a declaring type) /// - public bool IsNested { - get { return DeclaringType != null; } - } + public bool IsNested => DeclaringType is not null; /// - public bool IsPrimitive { - get { return this.IsPrimitive(); } - } + public bool IsPrimitive => this.IsPrimitive(); + + /// + /// Checks whether this type has opted into equivalence + /// + public bool IsEquivalent => TIAHelper.IsTypeDefEquivalent(this); /// /// Modify property: = @@ -741,17 +683,8 @@ public bool IsPrimitive { /// /// Value to AND /// Value to OR - void ModifyAttributes(TypeAttributes andMask, TypeAttributes orMask) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - newVal = (origVal & (int)andMask) | (int)orMask; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else + void ModifyAttributes(TypeAttributes andMask, TypeAttributes orMask) => attributes = (attributes & (int)andMask) | (int)orMask; -#endif - } /// /// Set or clear flags in @@ -760,246 +693,210 @@ void ModifyAttributes(TypeAttributes andMask, TypeAttributes orMask) { /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, TypeAttributes flags) { -#if THREAD_SAFE - int origVal, newVal; - do { - origVal = attributes; - if (set) - newVal = origVal | (int)flags; - else - newVal = origVal & ~(int)flags; - } while (Interlocked.CompareExchange(ref attributes, newVal, origVal) != origVal); -#else if (set) attributes |= (int)flags; else attributes &= ~(int)flags; -#endif } /// /// Gets/sets the visibility /// public TypeAttributes Visibility { - get { return (TypeAttributes)attributes & TypeAttributes.VisibilityMask; } - set { ModifyAttributes(~TypeAttributes.VisibilityMask, value & TypeAttributes.VisibilityMask); } + get => (TypeAttributes)attributes & TypeAttributes.VisibilityMask; + set => ModifyAttributes(~TypeAttributes.VisibilityMask, value & TypeAttributes.VisibilityMask); } /// /// true if is set /// - public bool IsNotPublic { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NotPublic; } - } + public bool IsNotPublic => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NotPublic; /// /// true if is set /// - public bool IsPublic { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public; } - } + public bool IsPublic => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public; /// /// true if is set /// - public bool IsNestedPublic { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic; } - } + public bool IsNestedPublic => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic; /// /// true if is set /// - public bool IsNestedPrivate { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate; } - } + public bool IsNestedPrivate => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate; /// /// true if is set /// - public bool IsNestedFamily { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily; } - } + public bool IsNestedFamily => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily; /// /// true if is set /// - public bool IsNestedAssembly { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedAssembly; } - } + public bool IsNestedAssembly => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedAssembly; /// /// true if is set /// - public bool IsNestedFamilyAndAssembly { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamANDAssem; } - } + public bool IsNestedFamilyAndAssembly => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamANDAssem; /// /// true if is set /// - public bool IsNestedFamilyOrAssembly { - get { return ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem; } - } + public bool IsNestedFamilyOrAssembly => ((TypeAttributes)attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem; /// /// Gets/sets the layout /// public TypeAttributes Layout { - get { return (TypeAttributes)attributes & TypeAttributes.LayoutMask; } - set { ModifyAttributes(~TypeAttributes.LayoutMask, value & TypeAttributes.LayoutMask); } + get => (TypeAttributes)attributes & TypeAttributes.LayoutMask; + set => ModifyAttributes(~TypeAttributes.LayoutMask, value & TypeAttributes.LayoutMask); } /// /// true if is set /// - public bool IsAutoLayout { - get { return ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.AutoLayout; } - } + public bool IsAutoLayout => ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.AutoLayout; /// /// true if is set /// - public bool IsSequentialLayout { - get { return ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.SequentialLayout; } - } + public bool IsSequentialLayout => ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.SequentialLayout; /// /// true if is set /// - public bool IsExplicitLayout { - get { return ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.ExplicitLayout; } - } + public bool IsExplicitLayout => ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.ExplicitLayout; + + /// + /// true if is set + /// + public bool IsExtendedLayout => ((TypeAttributes)attributes & TypeAttributes.LayoutMask) == TypeAttributes.ExtendedLayout; /// /// Gets/sets the bit /// public bool IsInterface { - get { return ((TypeAttributes)attributes & TypeAttributes.Interface) != 0; } - set { ModifyAttributes(value, TypeAttributes.Interface); } + get => ((TypeAttributes)attributes & TypeAttributes.Interface) != 0; + set => ModifyAttributes(value, TypeAttributes.Interface); } /// /// Gets/sets the bit /// public bool IsClass { - get { return ((TypeAttributes)attributes & TypeAttributes.Interface) == 0; } - set { ModifyAttributes(!value, TypeAttributes.Interface); } + get => ((TypeAttributes)attributes & TypeAttributes.Interface) == 0; + set => ModifyAttributes(!value, TypeAttributes.Interface); } /// /// Gets/sets the bit /// public bool IsAbstract { - get { return ((TypeAttributes)attributes & TypeAttributes.Abstract) != 0; } - set { ModifyAttributes(value, TypeAttributes.Abstract); } + get => ((TypeAttributes)attributes & TypeAttributes.Abstract) != 0; + set => ModifyAttributes(value, TypeAttributes.Abstract); } /// /// Gets/sets the bit /// public bool IsSealed { - get { return ((TypeAttributes)attributes & TypeAttributes.Sealed) != 0; } - set { ModifyAttributes(value, TypeAttributes.Sealed); } + get => ((TypeAttributes)attributes & TypeAttributes.Sealed) != 0; + set => ModifyAttributes(value, TypeAttributes.Sealed); } /// /// Gets/sets the bit /// public bool IsSpecialName { - get { return ((TypeAttributes)attributes & TypeAttributes.SpecialName) != 0; } - set { ModifyAttributes(value, TypeAttributes.SpecialName); } + get => ((TypeAttributes)attributes & TypeAttributes.SpecialName) != 0; + set => ModifyAttributes(value, TypeAttributes.SpecialName); } /// /// Gets/sets the bit /// public bool IsImport { - get { return ((TypeAttributes)attributes & TypeAttributes.Import) != 0; } - set { ModifyAttributes(value, TypeAttributes.Import); } + get => ((TypeAttributes)attributes & TypeAttributes.Import) != 0; + set => ModifyAttributes(value, TypeAttributes.Import); } /// /// Gets/sets the bit /// public bool IsSerializable { - get { return ((TypeAttributes)attributes & TypeAttributes.Serializable) != 0; } - set { ModifyAttributes(value, TypeAttributes.Serializable); } + get => ((TypeAttributes)attributes & TypeAttributes.Serializable) != 0; + set => ModifyAttributes(value, TypeAttributes.Serializable); } /// /// Gets/sets the bit /// public bool IsWindowsRuntime { - get { return ((TypeAttributes)attributes & TypeAttributes.WindowsRuntime) != 0; } - set { ModifyAttributes(value, TypeAttributes.WindowsRuntime); } + get => ((TypeAttributes)attributes & TypeAttributes.WindowsRuntime) != 0; + set => ModifyAttributes(value, TypeAttributes.WindowsRuntime); } /// /// Gets/sets the string format /// public TypeAttributes StringFormat { - get { return (TypeAttributes)attributes & TypeAttributes.StringFormatMask; } - set { ModifyAttributes(~TypeAttributes.StringFormatMask, value & TypeAttributes.StringFormatMask); } + get => (TypeAttributes)attributes & TypeAttributes.StringFormatMask; + set => ModifyAttributes(~TypeAttributes.StringFormatMask, value & TypeAttributes.StringFormatMask); } /// /// true if is set /// - public bool IsAnsiClass { - get { return ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.AnsiClass; } - } + public bool IsAnsiClass => ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.AnsiClass; /// /// true if is set /// - public bool IsUnicodeClass { - get { return ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.UnicodeClass; } - } + public bool IsUnicodeClass => ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.UnicodeClass; /// /// true if is set /// - public bool IsAutoClass { - get { return ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.AutoClass; } - } + public bool IsAutoClass => ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.AutoClass; /// /// true if is set /// - public bool IsCustomFormatClass { - get { return ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.CustomFormatClass; } - } + public bool IsCustomFormatClass => ((TypeAttributes)attributes & TypeAttributes.StringFormatMask) == TypeAttributes.CustomFormatClass; /// /// Gets/sets the bit /// public bool IsBeforeFieldInit { - get { return ((TypeAttributes)attributes & TypeAttributes.BeforeFieldInit) != 0; } - set { ModifyAttributes(value, TypeAttributes.BeforeFieldInit); } + get => ((TypeAttributes)attributes & TypeAttributes.BeforeFieldInit) != 0; + set => ModifyAttributes(value, TypeAttributes.BeforeFieldInit); } /// /// Gets/sets the bit /// public bool IsForwarder { - get { return ((TypeAttributes)attributes & TypeAttributes.Forwarder) != 0; } - set { ModifyAttributes(value, TypeAttributes.Forwarder); } + get => ((TypeAttributes)attributes & TypeAttributes.Forwarder) != 0; + set => ModifyAttributes(value, TypeAttributes.Forwarder); } /// /// Gets/sets the bit /// public bool IsRuntimeSpecialName { - get { return ((TypeAttributes)attributes & TypeAttributes.RTSpecialName) != 0; } - set { ModifyAttributes(value, TypeAttributes.RTSpecialName); } + get => ((TypeAttributes)attributes & TypeAttributes.RTSpecialName) != 0; + set => ModifyAttributes(value, TypeAttributes.RTSpecialName); } /// /// Gets/sets the bit /// public bool HasSecurity { - get { return ((TypeAttributes)attributes & TypeAttributes.HasSecurity) != 0; } - set { ModifyAttributes(value, TypeAttributes.HasSecurity); } + get => ((TypeAttributes)attributes & TypeAttributes.HasSecurity) != 0; + set => ModifyAttributes(value, TypeAttributes.HasSecurity); } /// @@ -1008,26 +905,27 @@ public bool HasSecurity { public bool IsGlobalModuleType { get { var mod = Module; - return mod != null && mod.GlobalType == this; + return mod is not null && mod.GlobalType == this; } } /// /// Gets a list of all nested types and all their nested types /// - public IEnumerable GetTypes() { - return AllTypesHelper.Types(NestedTypes); - } + public IEnumerable GetTypes() => AllTypesHelper.Types(NestedTypes); /// /// Gets an enum's underlying type or null if none. Should only be called /// if this is an enum. /// public TypeSig GetEnumUnderlyingType() { - foreach (var field in Fields.GetSafeEnumerable()) { + var fields = Fields; + int count = fields.Count; + for (int i = 0; i < count; i++) { + var field = fields[i]; if (!field.IsLiteral && !field.IsStatic) { var fieldSig = field.FieldSig; - if (fieldSig != null) + if (fieldSig is not null) return fieldSig.Type; } } @@ -1041,9 +939,7 @@ public TypeSig GetEnumUnderlyingType() { /// A method/field reference /// A or a instance or null /// if it couldn't be resolved. - public IMemberForwarded Resolve(MemberRef memberRef) { - return Resolve(memberRef, 0); - } + public IMemberForwarded Resolve(MemberRef memberRef) => Resolve(memberRef, 0); /// /// Resolves a method or a field. (owner type) is ignored when @@ -1054,15 +950,15 @@ public IMemberForwarded Resolve(MemberRef memberRef) { /// A or a instance or null /// if it couldn't be resolved. public IMemberForwarded Resolve(MemberRef memberRef, SigComparerOptions options) { - if (memberRef == null) + if (memberRef is null) return null; var methodSig = memberRef.MethodSig; - if (methodSig != null) + if (methodSig is not null) return FindMethodCheckBaseType(memberRef.Name, methodSig, options, memberRef.Module); var fieldSig = memberRef.FieldSig; - if (fieldSig != null) + if (fieldSig is not null) return FindFieldCheckBaseType(memberRef.Name, fieldSig, options, memberRef.Module); return null; @@ -1074,9 +970,7 @@ public IMemberForwarded Resolve(MemberRef memberRef, SigComparerOptions options) /// Method name /// Method signature /// The first method that matches or null if none found - public MethodDef FindMethod(UTF8String name, MethodSig sig) { - return FindMethod(name, sig, 0, null); - } + public MethodDef FindMethod(UTF8String name, MethodSig sig) => FindMethod(name, sig, 0, null); /// /// Finds a method @@ -1085,9 +979,7 @@ public MethodDef FindMethod(UTF8String name, MethodSig sig) { /// Method signature /// Method signature comparison options /// The first method that matches or null if none found - public MethodDef FindMethod(UTF8String name, MethodSig sig, SigComparerOptions options) { - return FindMethod(name, sig, options, null); - } + public MethodDef FindMethod(UTF8String name, MethodSig sig, SigComparerOptions options) => FindMethod(name, sig, options, null); /// /// Finds a method @@ -1098,12 +990,15 @@ public MethodDef FindMethod(UTF8String name, MethodSig sig, SigComparerOptions o /// The module that needs to find the method or null /// The first method that matches or null if none found public MethodDef FindMethod(UTF8String name, MethodSig sig, SigComparerOptions options, ModuleDef sourceModule) { - if (UTF8String.IsNull(name) || sig == null) + if (UTF8String.IsNull(name) || sig is null) return null; var comparer = new SigComparer(options, sourceModule); bool allowPrivateScope = (options & SigComparerOptions.PrivateScopeMethodIsComparable) != 0; - foreach (var method in Methods.GetSafeEnumerable()) { - if (!allowPrivateScope && method.IsPrivateScope) + var methods = Methods; + int count = methods.Count; + for (int i = 0; i < count; i++) { + var method = methods[i]; + if (!allowPrivateScope && method.IsPrivateScope && sourceModule != Module) continue; if (!UTF8String.Equals(method.Name, name)) continue; @@ -1119,7 +1014,10 @@ public MethodDef FindMethod(UTF8String name, MethodSig sig, SigComparerOptions o /// Name of method /// The or null if not found public MethodDef FindMethod(UTF8String name) { - foreach (var method in Methods.GetSafeEnumerable()) { + var methods = Methods; + int count = methods.Count; + for (int i = 0; i < count; i++) { + var method = methods[i]; if (UTF8String.Equals(method.Name, name)) return method; } @@ -1132,7 +1030,10 @@ public MethodDef FindMethod(UTF8String name) { /// Name of method /// All methods with that name public IEnumerable FindMethods(UTF8String name) { - foreach (var method in Methods.GetSafeEnumerable()) { + var methods = Methods; + int count = methods.Count; + for (int i = 0; i < count; i++) { + var method = methods[i]; if (UTF8String.Equals(method.Name, name)) yield return method; } @@ -1143,11 +1044,10 @@ public IEnumerable FindMethods(UTF8String name) { /// /// The class constructor or null if none found public MethodDef FindStaticConstructor() { - return Methods.ExecuteLocked(null, (tsList, arg) => FindStaticConstructor_NoMethodsLock()); - } - - MethodDef FindStaticConstructor_NoMethodsLock() { - foreach (var method in Methods.GetEnumerable_NoLock()) { + var methods = Methods; + int count = methods.Count; + for (int i = 0; i < count; i++) { + var method = methods[i]; if (method.IsStaticConstructor) return method; } @@ -1162,7 +1062,7 @@ MethodDef FindStaticConstructor_NoMethodsLock() { /// The class constructor public MethodDef FindOrCreateStaticConstructor() { var cctor = FindStaticConstructor(); - if (cctor != null) + if (cctor is not null) return cctor; var implFlags = MethodImplAttributes.IL | MethodImplAttributes.Managed; @@ -1177,13 +1077,8 @@ public MethodDef FindOrCreateStaticConstructor() { body.MaxStack = 8; body.Instructions.Add(OpCodes.Ret.ToInstruction()); cctor.Body = body; - return Methods.ExecuteLocked(cctor, (tsList, cctor2) => { - var cctor3 = FindStaticConstructor_NoMethodsLock(); - if (cctor3 != null) - return cctor3; - tsList.Add_NoLock(cctor2); - return cctor2; - }); + Methods.Add(cctor); + return cctor; } /// @@ -1191,7 +1086,10 @@ public MethodDef FindOrCreateStaticConstructor() { /// /// All instance constructors public IEnumerable FindInstanceConstructors() { - foreach (var method in Methods.GetSafeEnumerable()) { + var methods = Methods; + int count = methods.Count; + for (int i = 0; i < count; i++) { + var method = methods[i]; if (method.IsInstanceConstructor) yield return method; } @@ -1202,7 +1100,10 @@ public IEnumerable FindInstanceConstructors() { /// /// All static and instance constructors public IEnumerable FindConstructors() { - foreach (var method in Methods.GetSafeEnumerable()) { + var methods = Methods; + int count = methods.Count; + for (int i = 0; i < count; i++) { + var method = methods[i]; if (method.IsConstructor) yield return method; } @@ -1213,11 +1114,14 @@ public IEnumerable FindConstructors() { /// /// The default instance constructor or null if none public MethodDef FindDefaultConstructor() { - foreach (var method in Methods.GetSafeEnumerable()) { + var methods = Methods; + int count = methods.Count; + for (int i = 0; i < count; i++) { + var method = methods[i]; if (!method.IsInstanceConstructor) continue; var sig = method.MethodSig; - if (sig != null && sig.Params.Count == 0) + if (sig is not null && sig.Params.Count == 0) return method; } return null; @@ -1229,9 +1133,7 @@ public MethodDef FindDefaultConstructor() { /// Field name /// Field signature /// The first field that matches or null if none found - public FieldDef FindField(UTF8String name, FieldSig sig) { - return FindField(name, sig, 0, null); - } + public FieldDef FindField(UTF8String name, FieldSig sig) => FindField(name, sig, 0, null); /// /// Finds a field @@ -1240,9 +1142,7 @@ public FieldDef FindField(UTF8String name, FieldSig sig) { /// Field signature /// Field signature comparison options /// The first field that matches or null if none found - public FieldDef FindField(UTF8String name, FieldSig sig, SigComparerOptions options) { - return FindField(name, sig, options, null); - } + public FieldDef FindField(UTF8String name, FieldSig sig, SigComparerOptions options) => FindField(name, sig, options, null); /// /// Finds a field @@ -1253,12 +1153,15 @@ public FieldDef FindField(UTF8String name, FieldSig sig, SigComparerOptions opti /// The module that needs to find the field or null /// The first field that matches or null if none found public FieldDef FindField(UTF8String name, FieldSig sig, SigComparerOptions options, ModuleDef sourceModule) { - if (UTF8String.IsNull(name) || sig == null) + if (UTF8String.IsNull(name) || sig is null) return null; var comparer = new SigComparer(options, sourceModule); bool allowPrivateScope = (options & SigComparerOptions.PrivateScopeFieldIsComparable) != 0; - foreach (var field in Fields.GetSafeEnumerable()) { - if (!allowPrivateScope && field.IsPrivateScope) + var fields = Fields; + int count = fields.Count; + for (int i = 0; i < count; i++) { + var field = fields[i]; + if (!allowPrivateScope && field.IsPrivateScope && sourceModule != Module) continue; if (!UTF8String.Equals(field.Name, name)) continue; @@ -1274,7 +1177,10 @@ public FieldDef FindField(UTF8String name, FieldSig sig, SigComparerOptions opti /// Name of field /// The or null if not found public FieldDef FindField(UTF8String name) { - foreach (var field in Fields.GetSafeEnumerable()) { + var fields = Fields; + int count = fields.Count; + for (int i = 0; i < count; i++) { + var field = fields[i]; if (UTF8String.Equals(field.Name, name)) return field; } @@ -1287,7 +1193,10 @@ public FieldDef FindField(UTF8String name) { /// Name of field /// All fields with that name public IEnumerable FindFields(UTF8String name) { - foreach (var field in Fields.GetSafeEnumerable()) { + var fields = Fields; + int count = fields.Count; + for (int i = 0; i < count; i++) { + var field = fields[i]; if (UTF8String.Equals(field.Name, name)) yield return field; } @@ -1299,9 +1208,7 @@ public IEnumerable FindFields(UTF8String name) { /// Name of event /// Type of event /// A or null if not found - public EventDef FindEvent(UTF8String name, IType type) { - return FindEvent(name, type, 0, null); - } + public EventDef FindEvent(UTF8String name, IType type) => FindEvent(name, type, 0, null); /// /// Finds an event @@ -1310,9 +1217,7 @@ public EventDef FindEvent(UTF8String name, IType type) { /// Type of event /// Event type comparison options /// A or null if not found - public EventDef FindEvent(UTF8String name, IType type, SigComparerOptions options) { - return FindEvent(name, type, options, null); - } + public EventDef FindEvent(UTF8String name, IType type, SigComparerOptions options) => FindEvent(name, type, options, null); /// /// Finds an event @@ -1323,10 +1228,13 @@ public EventDef FindEvent(UTF8String name, IType type, SigComparerOptions option /// The module that needs to find the event or null /// A or null if not found public EventDef FindEvent(UTF8String name, IType type, SigComparerOptions options, ModuleDef sourceModule) { - if (UTF8String.IsNull(name) || type == null) + if (UTF8String.IsNull(name) || type is null) return null; var comparer = new SigComparer(options, sourceModule); - foreach (var @event in Events.GetSafeEnumerable()) { + var events = Events; + int count = events.Count; + for (int i = 0; i < count; i++) { + var @event = events[i]; if (!UTF8String.Equals(@event.Name, name)) continue; if (comparer.Equals(@event.EventType, type)) @@ -1341,7 +1249,10 @@ public EventDef FindEvent(UTF8String name, IType type, SigComparerOptions option /// Name of event /// The or null if not found public EventDef FindEvent(UTF8String name) { - foreach (var @event in Events.GetSafeEnumerable()) { + var events = Events; + int count = events.Count; + for (int i = 0; i < count; i++) { + var @event = events[i]; if (UTF8String.Equals(@event.Name, name)) return @event; } @@ -1354,7 +1265,10 @@ public EventDef FindEvent(UTF8String name) { /// Name of event /// All events with that name public IEnumerable FindEvents(UTF8String name) { - foreach (var @event in Events.GetSafeEnumerable()) { + var events = Events; + int count = events.Count; + for (int i = 0; i < count; i++) { + var @event = events[i]; if (UTF8String.Equals(@event.Name, name)) yield return @event; } @@ -1366,9 +1280,7 @@ public IEnumerable FindEvents(UTF8String name) { /// Name of property /// Property signature /// A or null if not found - public PropertyDef FindProperty(UTF8String name, CallingConventionSig propSig) { - return FindProperty(name, propSig, 0, null); - } + public PropertyDef FindProperty(UTF8String name, CallingConventionSig propSig) => FindProperty(name, propSig, 0, null); /// /// Finds a property @@ -1377,9 +1289,7 @@ public PropertyDef FindProperty(UTF8String name, CallingConventionSig propSig) { /// Property signature /// Property signature comparison options /// A or null if not found - public PropertyDef FindProperty(UTF8String name, CallingConventionSig propSig, SigComparerOptions options) { - return FindProperty(name, propSig, options, null); - } + public PropertyDef FindProperty(UTF8String name, CallingConventionSig propSig, SigComparerOptions options) => FindProperty(name, propSig, options, null); /// /// Finds a property @@ -1390,10 +1300,13 @@ public PropertyDef FindProperty(UTF8String name, CallingConventionSig propSig, S /// The module that needs to find the property or null /// A or null if not found public PropertyDef FindProperty(UTF8String name, CallingConventionSig propSig, SigComparerOptions options, ModuleDef sourceModule) { - if (UTF8String.IsNull(name) || propSig == null) + if (UTF8String.IsNull(name) || propSig is null) return null; var comparer = new SigComparer(options, sourceModule); - foreach (var prop in Properties.GetSafeEnumerable()) { + var properties = Properties; + int count = properties.Count; + for (int i = 0; i < count; i++) { + var prop = properties[i]; if (!UTF8String.Equals(prop.Name, name)) continue; if (comparer.Equals(prop.Type, propSig)) @@ -1408,7 +1321,10 @@ public PropertyDef FindProperty(UTF8String name, CallingConventionSig propSig, S /// Name of prop /// The or null if not found public PropertyDef FindProperty(UTF8String name) { - foreach (var prop in Properties.GetSafeEnumerable()) { + var properties = Properties; + int count = properties.Count; + for (int i = 0; i < count; i++) { + var prop = properties[i]; if (UTF8String.Equals(prop.Name, name)) return prop; } @@ -1421,7 +1337,10 @@ public PropertyDef FindProperty(UTF8String name) { /// Name of prop /// All props with that name public IEnumerable FindProperties(UTF8String name) { - foreach (var prop in Properties.GetSafeEnumerable()) { + var properties = Properties; + int count = properties.Count; + for (int i = 0; i < count; i++) { + var prop = properties[i]; if (UTF8String.Equals(prop.Name, name)) yield return prop; } @@ -1433,9 +1352,7 @@ public IEnumerable FindProperties(UTF8String name) { /// Method name /// Method signature /// The method or null if it wasn't found - public MethodDef FindMethodCheckBaseType(UTF8String name, MethodSig sig) { - return FindMethodCheckBaseType(name, sig, 0, null); - } + public MethodDef FindMethodCheckBaseType(UTF8String name, MethodSig sig) => FindMethodCheckBaseType(name, sig, 0, null); /// /// Finds a method by checking this type or any of its base types @@ -1444,9 +1361,7 @@ public MethodDef FindMethodCheckBaseType(UTF8String name, MethodSig sig) { /// Method signature /// Method signature comparison options /// The method or null if it wasn't found - public MethodDef FindMethodCheckBaseType(UTF8String name, MethodSig sig, SigComparerOptions options) { - return FindMethodCheckBaseType(name, sig, options, null); - } + public MethodDef FindMethodCheckBaseType(UTF8String name, MethodSig sig, SigComparerOptions options) => FindMethodCheckBaseType(name, sig, options, null); /// /// Finds a method by checking this type or any of its base types @@ -1458,9 +1373,9 @@ public MethodDef FindMethodCheckBaseType(UTF8String name, MethodSig sig, SigComp /// The method or null if it wasn't found public MethodDef FindMethodCheckBaseType(UTF8String name, MethodSig sig, SigComparerOptions options, ModuleDef sourceModule) { var td = this; - while (td != null) { + while (td is not null) { var md = td.FindMethod(name, sig, options, sourceModule); - if (md != null) + if (md is not null) return md; td = td.BaseType.ResolveTypeDef(); } @@ -1474,9 +1389,9 @@ public MethodDef FindMethodCheckBaseType(UTF8String name, MethodSig sig, SigComp /// The method or null if it wasn't found public MethodDef FindMethodCheckBaseType(UTF8String name) { var td = this; - while (td != null) { + while (td is not null) { var md = td.FindMethod(name); - if (md != null) + if (md is not null) return md; td = td.BaseType.ResolveTypeDef(); } @@ -1489,9 +1404,7 @@ public MethodDef FindMethodCheckBaseType(UTF8String name) { /// Field name /// Field signature /// The field or null if it wasn't found - public FieldDef FindFieldCheckBaseType(UTF8String name, FieldSig sig) { - return FindFieldCheckBaseType(name, sig, 0, null); - } + public FieldDef FindFieldCheckBaseType(UTF8String name, FieldSig sig) => FindFieldCheckBaseType(name, sig, 0, null); /// /// Finds a field by checking this type or any of its base types @@ -1500,9 +1413,7 @@ public FieldDef FindFieldCheckBaseType(UTF8String name, FieldSig sig) { /// Field signature /// Field signature comparison options /// The field or null if it wasn't found - public FieldDef FindFieldCheckBaseType(UTF8String name, FieldSig sig, SigComparerOptions options) { - return FindFieldCheckBaseType(name, sig, options, null); - } + public FieldDef FindFieldCheckBaseType(UTF8String name, FieldSig sig, SigComparerOptions options) => FindFieldCheckBaseType(name, sig, options, null); /// /// Finds a field by checking this type or any of its base types @@ -1514,9 +1425,9 @@ public FieldDef FindFieldCheckBaseType(UTF8String name, FieldSig sig, SigCompare /// The field or null if it wasn't found public FieldDef FindFieldCheckBaseType(UTF8String name, FieldSig sig, SigComparerOptions options, ModuleDef sourceModule) { var td = this; - while (td != null) { + while (td is not null) { var fd = td.FindField(name, sig, options, sourceModule); - if (fd != null) + if (fd is not null) return fd; td = td.BaseType.ResolveTypeDef(); } @@ -1530,9 +1441,9 @@ public FieldDef FindFieldCheckBaseType(UTF8String name, FieldSig sig, SigCompare /// The field or null if it wasn't found public FieldDef FindFieldCheckBaseType(UTF8String name) { var td = this; - while (td != null) { + while (td is not null) { var fd = td.FindField(name); - if (fd != null) + if (fd is not null) return fd; td = td.BaseType.ResolveTypeDef(); } @@ -1547,9 +1458,9 @@ public FieldDef FindFieldCheckBaseType(UTF8String name) { /// The event or null if it wasn't found public EventDef FindEventCheckBaseType(UTF8String name, ITypeDefOrRef eventType) { var td = this; - while (td != null) { + while (td is not null) { var ed = td.FindEvent(name, eventType); - if (ed != null) + if (ed is not null) return ed; td = td.BaseType.ResolveTypeDef(); } @@ -1563,9 +1474,9 @@ public EventDef FindEventCheckBaseType(UTF8String name, ITypeDefOrRef eventType) /// The event or null if it wasn't found public EventDef FindEventCheckBaseType(UTF8String name) { var td = this; - while (td != null) { + while (td is not null) { var ed = td.FindEvent(name); - if (ed != null) + if (ed is not null) return ed; td = td.BaseType.ResolveTypeDef(); } @@ -1578,11 +1489,30 @@ public EventDef FindEventCheckBaseType(UTF8String name) { /// Property name /// Property signature /// The property or null if it wasn't found - public PropertyDef FindPropertyCheckBaseType(UTF8String name, FieldSig sig) { + public PropertyDef FindPropertyCheckBaseType(UTF8String name, PropertySig sig) => FindPropertyCheckBaseType(name, sig, 0, null); + + /// + /// Finds a property by checking this type or any of its base types + /// + /// Property name + /// Property signature + /// Property signature comparison options + /// The property or null if it wasn't found + public PropertyDef FindPropertyCheckBaseType(UTF8String name, PropertySig sig, SigComparerOptions options) => FindPropertyCheckBaseType(name, sig, options, null); + + /// + /// Finds a property by checking this type or any of its base types + /// + /// Property name + /// Property signature + /// Property signature comparison options + /// The module that needs to find the property or null + /// The property or null if it wasn't found + public PropertyDef FindPropertyCheckBaseType(UTF8String name, PropertySig sig, SigComparerOptions options, ModuleDef sourceModule) { var td = this; - while (td != null) { - var pd = td.FindProperty(name, sig); - if (pd != null) + while (td is not null) { + var pd = td.FindProperty(name, sig, options, sourceModule); + if (pd is not null) return pd; td = td.BaseType.ResolveTypeDef(); } @@ -1596,9 +1526,9 @@ public PropertyDef FindPropertyCheckBaseType(UTF8String name, FieldSig sig) { /// The property or null if it wasn't found public PropertyDef FindPropertyCheckBaseType(UTF8String name) { var td = this; - while (td != null) { + while (td is not null) { var pd = td.FindProperty(name); - if (pd != null) + if (pd is not null) return pd; td = td.BaseType.ResolveTypeDef(); } @@ -1609,9 +1539,7 @@ public PropertyDef FindPropertyCheckBaseType(UTF8String name) { /// Removes a method from this type. It also removes it from any properties and events. /// /// The method to remove - public void Remove(MethodDef method) { - Remove(method, false); - } + public void Remove(MethodDef method) => Remove(method, false); /// /// Removes a method from this type. It also removes it from any properties and events. @@ -1620,16 +1548,22 @@ public void Remove(MethodDef method) { /// true if we should remove all /// empty properties and events. public void Remove(MethodDef method, bool removeEmptyPropertiesEvents) { - if (method == null) + if (method is null) return; - foreach (var prop in Properties.GetSafeEnumerable()) { + var properties = Properties; + int count = properties.Count; + for (int i = 0; i < count; i++) { + var prop = properties[i]; prop.GetMethods.Remove(method); prop.SetMethods.Remove(method); prop.OtherMethods.Remove(method); } - foreach (var evt in Events.GetSafeEnumerable()) { + var events = Events; + count = events.Count; + for (int i = 0; i < count; i++) { + var evt = events[i]; if (evt.AddMethod == method) evt.AddMethod = null; if (evt.RemoveMethod == method) @@ -1648,23 +1582,23 @@ public void Remove(MethodDef method, bool removeEmptyPropertiesEvents) { } void RemoveEmptyProperties() { - Properties.IterateAllReverse((tsList, index, value) => { - if (value.IsEmpty) - tsList.RemoveAt_NoLock(index); - }); + var properties = Properties; + for (int i = properties.Count - 1; i >= 0; i--) { + if (properties[i].IsEmpty) + properties.RemoveAt(i); + } } void RemoveEmptyEvents() { - Events.IterateAllReverse((tsList, index, value) => { - if (value.IsEmpty) - tsList.RemoveAt_NoLock(index); - }); + var events = Events; + for (int i = events.Count - 1; i >= 0; i--) { + if (events[i].IsEmpty) + events.RemoveAt(i); + } } /// - void IListListener.OnLazyAdd(int index, ref FieldDef value) { - OnLazyAdd2(index, ref value); - } + void IListListener.OnLazyAdd(int index, ref FieldDef value) => OnLazyAdd2(index, ref value); internal virtual void OnLazyAdd2(int index, ref FieldDef value) { #if DEBUG @@ -1675,15 +1609,13 @@ internal virtual void OnLazyAdd2(int index, ref FieldDef value) { /// void IListListener.OnAdd(int index, FieldDef value) { - if (value.DeclaringType != null) + if (value.DeclaringType is not null) throw new InvalidOperationException("Field is already owned by another type. Set DeclaringType to null first."); value.DeclaringType2 = this; } /// - void IListListener.OnRemove(int index, FieldDef value) { - value.DeclaringType2 = null; - } + void IListListener.OnRemove(int index, FieldDef value) => value.DeclaringType2 = null; /// void IListListener.OnResize(int index) { @@ -1691,14 +1623,12 @@ void IListListener.OnResize(int index) { /// void IListListener.OnClear() { - foreach (var field in Fields.GetEnumerable_NoLock()) + foreach (var field in fields.GetEnumerable_NoLock()) field.DeclaringType2 = null; } /// - void IListListener.OnLazyAdd(int index, ref MethodDef value) { - OnLazyAdd2(index, ref value); - } + void IListListener.OnLazyAdd(int index, ref MethodDef value) => OnLazyAdd2(index, ref value); internal virtual void OnLazyAdd2(int index, ref MethodDef value) { #if DEBUG @@ -1709,7 +1639,7 @@ internal virtual void OnLazyAdd2(int index, ref MethodDef value) { /// void IListListener.OnAdd(int index, MethodDef value) { - if (value.DeclaringType != null) + if (value.DeclaringType is not null) throw new InvalidOperationException("Method is already owned by another type. Set DeclaringType to null first."); value.DeclaringType2 = this; value.Parameters.UpdateThisParameterType(this); @@ -1727,7 +1657,7 @@ void IListListener.OnResize(int index) { /// void IListListener.OnClear() { - foreach (var method in Methods.GetEnumerable_NoLock()) { + foreach (var method in methods.GetEnumerable_NoLock()) { method.DeclaringType2 = null; method.Parameters.UpdateThisParameterType(null); } @@ -1736,8 +1666,8 @@ void IListListener.OnClear() { /// void IListListener.OnLazyAdd(int index, ref TypeDef value) { #if DEBUG - if (value.Module2 != null) - throw new InvalidOperationException("Added nested type's Module != null"); + if (value.Module2 is not null) + throw new InvalidOperationException("Added nested type's Module is not null"); if (value.DeclaringType != this) throw new InvalidOperationException("Added nested type's DeclaringType != this"); #endif @@ -1745,9 +1675,9 @@ void IListListener.OnLazyAdd(int index, ref TypeDef value) { /// void IListListener.OnAdd(int index, TypeDef value) { - if (value.DeclaringType != null) + if (value.DeclaringType is not null) throw new InvalidOperationException("Nested type is already owned by another type. Set DeclaringType to null first."); - if (value.Module != null) + if (value.Module is not null) throw new InvalidOperationException("Type is already owned by another module. Remove it from that module's type list."); value.DeclaringType2 = this; } @@ -1755,6 +1685,7 @@ void IListListener.OnAdd(int index, TypeDef value) { /// void IListListener.OnRemove(int index, TypeDef value) { value.DeclaringType2 = null; + value.Module2 = null; } /// @@ -1763,14 +1694,12 @@ void IListListener.OnResize(int index) { /// void IListListener.OnClear() { - foreach (var type in NestedTypes.GetEnumerable_NoLock()) + foreach (var type in nestedTypes.GetEnumerable_NoLock()) type.DeclaringType2 = null; } /// - void IListListener.OnLazyAdd(int index, ref EventDef value) { - OnLazyAdd2(index, ref value); - } + void IListListener.OnLazyAdd(int index, ref EventDef value) => OnLazyAdd2(index, ref value); internal virtual void OnLazyAdd2(int index, ref EventDef value) { #if DEBUG @@ -1781,15 +1710,13 @@ internal virtual void OnLazyAdd2(int index, ref EventDef value) { /// void IListListener.OnAdd(int index, EventDef value) { - if (value.DeclaringType != null) + if (value.DeclaringType is not null) throw new InvalidOperationException("Event is already owned by another type. Set DeclaringType to null first."); value.DeclaringType2 = this; } /// - void IListListener.OnRemove(int index, EventDef value) { - value.DeclaringType2 = null; - } + void IListListener.OnRemove(int index, EventDef value) => value.DeclaringType2 = null; /// void IListListener.OnResize(int index) { @@ -1797,14 +1724,12 @@ void IListListener.OnResize(int index) { /// void IListListener.OnClear() { - foreach (var @event in Events.GetEnumerable_NoLock()) + foreach (var @event in events.GetEnumerable_NoLock()) @event.DeclaringType2 = null; } /// - void IListListener.OnLazyAdd(int index, ref PropertyDef value) { - OnLazyAdd2(index, ref value); - } + void IListListener.OnLazyAdd(int index, ref PropertyDef value) => OnLazyAdd2(index, ref value); internal virtual void OnLazyAdd2(int index, ref PropertyDef value) { #if DEBUG @@ -1815,15 +1740,13 @@ internal virtual void OnLazyAdd2(int index, ref PropertyDef value) { /// void IListListener.OnAdd(int index, PropertyDef value) { - if (value.DeclaringType != null) + if (value.DeclaringType is not null) throw new InvalidOperationException("Property is already owned by another type. Set DeclaringType to null first."); value.DeclaringType2 = this; } /// - void IListListener.OnRemove(int index, PropertyDef value) { - value.DeclaringType2 = null; - } + void IListListener.OnRemove(int index, PropertyDef value) => value.DeclaringType2 = null; /// void IListListener.OnResize(int index) { @@ -1831,14 +1754,12 @@ void IListListener.OnResize(int index) { /// void IListListener.OnClear() { - foreach (var prop in Properties.GetEnumerable_NoLock()) + foreach (var prop in properties.GetEnumerable_NoLock()) prop.DeclaringType2 = null; } /// - void IListListener.OnLazyAdd(int index, ref GenericParam value) { - OnLazyAdd2(index, ref value); - } + void IListListener.OnLazyAdd(int index, ref GenericParam value) => OnLazyAdd2(index, ref value); internal virtual void OnLazyAdd2(int index, ref GenericParam value) { #if DEBUG @@ -1849,15 +1770,13 @@ internal virtual void OnLazyAdd2(int index, ref GenericParam value) { /// void IListListener.OnAdd(int index, GenericParam value) { - if (value.Owner != null) + if (value.Owner is not null) throw new InvalidOperationException("Generic param is already owned by another type/method. Set Owner to null first."); value.Owner = this; } /// - void IListListener.OnRemove(int index, GenericParam value) { - value.Owner = null; - } + void IListListener.OnRemove(int index, GenericParam value) => value.Owner = null; /// void IListListener.OnResize(int index) { @@ -1865,7 +1784,7 @@ void IListListener.OnResize(int index) { /// void IListListener.OnClear() { - foreach (var gp in GenericParameters.GetEnumerable_NoLock()) + foreach (var gp in genericParameters.GetEnumerable_NoLock()) gp.Owner = null; } @@ -1875,12 +1794,15 @@ void IListListener.OnClear() { /// Field name /// A list of 0 or more fields with name public IList GetFields(UTF8String name) { - var fields = new List(); - foreach (var field in Fields.GetSafeEnumerable()) { + var result = new List(); + var fields = Fields; + int count = fields.Count; + for (int i = 0; i < count; i++) { + var field = fields[i]; if (field.Name == name) - fields.Add(field); + result.Add(field); } - return fields; + return result; } /// @@ -1889,7 +1811,10 @@ public IList GetFields(UTF8String name) { /// Field name /// The field or null if none found public FieldDef GetField(UTF8String name) { - foreach (var field in Fields.GetSafeEnumerable()) { + var fields = Fields; + int count = fields.Count; + for (int i = 0; i < count; i++) { + var field = fields[i]; if (field.Name == name) return field; } @@ -1898,21 +1823,21 @@ public FieldDef GetField(UTF8String name) { internal static bool GetClassSize(TypeDef td, out uint size) { size = 0; - if (td == null) + if (td is null) return false; if (!td.IsValueType) return false; // Not supported by us if (!td.IsSequentialLayout && !td.IsExplicitLayout) { if (td.Fields.Count != 1) return false; - var fd = td.Fields.Get(0, null); - if (fd == null) + var fd = td.Fields[0]; + if (fd is null) return false; return fd.GetFieldSize(out size); } var classLayout = td.ClassLayout; - if (classLayout == null) + if (classLayout is null) return false; uint classSize = classLayout.ClassSize; if (classSize != 0) { @@ -1931,51 +1856,47 @@ internal static bool GetClassSize(TypeDef td, out uint size) { /// protected MethodDef FindMethodImplMethod(IMethodDefOrRef mdr) { // Check common case first - var md = mdr as MethodDef; - if (md != null) + if (mdr is MethodDef md) return md; // Must be a member ref var mr = mdr as MemberRef; - if (mr == null) + if (mr is null) return null; // If Class is MethodDef, then it should be a vararg method var parent = mr.Class; md = parent as MethodDef; - if (md != null) + if (md is not null) return md; // If it's a TypeSpec, it must be a generic instance type for (int i = 0; i < 10; i++) { var ts = parent as TypeSpec; - if (ts == null) + if (ts is null) break; var gis = ts.TypeSig as GenericInstSig; - if (gis == null || gis.GenericType == null) + if (gis is null || gis.GenericType is null) return null; parent = gis.GenericType.TypeDefOrRef; } var td = parent as TypeDef; - if (td == null) { + if (td is null) { // If it's a TypeRef, resolve it as if it is a reference to a type in the // current module, even if its ResolutionScope happens to be some other // assembly/module (that's what the CLR does) - var tr = parent as TypeRef; - if (tr != null && Module != null) + if (parent is TypeRef tr && Module is not null) td = Module.Find(tr); } - if (td == null) + if (td is null) return null; return td.FindMethod(mr.Name, mr.MethodSig); } /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } /// @@ -2015,16 +1936,16 @@ public TypeDefUser(UTF8String name, ITypeDefOrRef baseType) /// Name /// Base class or null if it's an interface public TypeDefUser(UTF8String @namespace, UTF8String name, ITypeDefOrRef baseType) { - this.fields = new LazyList(this); - this.methods = new LazyList(this); - this.genericParameters = new LazyList(this); - this.nestedTypes = new LazyList(this); - this.events = new LazyList(this); - this.properties = new LazyList(this); + fields = new LazyList(this); + methods = new LazyList(this); + genericParameters = new LazyList(this); + nestedTypes = new LazyList(this); + events = new LazyList(this); + properties = new LazyList(this); this.@namespace = @namespace; this.name = name; this.baseType = baseType; - this.baseType_isInitialized = true; + baseType_isInitialized = true; } } @@ -2035,64 +1956,61 @@ sealed class TypeDefMD : TypeDef, IMDTokenProviderMD { /// The module where this instance is located readonly ModuleDefMD readerModule; + internal ModuleDefMD ReaderModule => readerModule; + readonly uint origRid; readonly uint extendsCodedToken; - Dictionary> methodRidToOverrides; + Dictionary> methodRidToOverrides; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// - protected override ITypeDefOrRef GetBaseType_NoLock() { - return readerModule.ResolveTypeDefOrRef(extendsCodedToken, new GenericParamContext(this)); - } + protected override ITypeDefOrRef GetBaseType_NoLock() => readerModule.ResolveTypeDefOrRef(extendsCodedToken, new GenericParamContext(this)); /// protected override void InitializeFields() { - var list = readerModule.MetaData.GetFieldRidList(origRid); - var tmp = new LazyList((int)list.Length, this, list, (list2, index) => readerModule.ResolveField(((RidList)list2)[index])); + var list = readerModule.Metadata.GetFieldRidList(origRid); + var tmp = new LazyList(list.Count, this, list, (list2, index) => readerModule.ResolveField(list2[index])); Interlocked.CompareExchange(ref fields, tmp, null); } /// protected override void InitializeMethods() { - var list = readerModule.MetaData.GetMethodRidList(origRid); - var tmp = new LazyList((int)list.Length, this, list, (list2, index) => readerModule.ResolveMethod(((RidList)list2)[index])); + var list = readerModule.Metadata.GetMethodRidList(origRid); + var tmp = new LazyList(list.Count, this, list, (list2, index) => readerModule.ResolveMethod(list2[index])); Interlocked.CompareExchange(ref methods, tmp, null); } /// protected override void InitializeGenericParameters() { - var list = readerModule.MetaData.GetGenericParamRidList(Table.TypeDef, origRid); - var tmp = new LazyList((int)list.Length, this, list, (list2, index) => readerModule.ResolveGenericParam(((RidList)list2)[index])); + var list = readerModule.Metadata.GetGenericParamRidList(Table.TypeDef, origRid); + var tmp = new LazyList(list.Count, this, list, (list2, index) => readerModule.ResolveGenericParam(list2[index])); Interlocked.CompareExchange(ref genericParameters, tmp, null); } /// protected override void InitializeInterfaces() { - var list = readerModule.MetaData.GetInterfaceImplRidList(origRid); - var tmp = new LazyList((int)list.Length, list, (list2, index) => readerModule.ResolveInterfaceImpl(((RidList)list2)[index], new GenericParamContext(this))); + var list = readerModule.Metadata.GetInterfaceImplRidList(origRid); + var tmp = new LazyList(list.Count, list, (list2, index) => readerModule.ResolveInterfaceImpl(list2[index], new GenericParamContext(this))); Interlocked.CompareExchange(ref interfaces, tmp, null); } /// protected override void InitializeDeclSecurities() { - var list = readerModule.MetaData.GetDeclSecurityRidList(Table.TypeDef, origRid); - var tmp = new LazyList((int)list.Length, list, (list2, index) => readerModule.ResolveDeclSecurity(((RidList)list2)[index])); + var list = readerModule.Metadata.GetDeclSecurityRidList(Table.TypeDef, origRid); + var tmp = new LazyList(list.Count, list, (list2, index) => readerModule.ResolveDeclSecurity(list2[index])); Interlocked.CompareExchange(ref declSecurities, tmp, null); } /// - protected override ClassLayout GetClassLayout_NoLock() { - return readerModule.ResolveClassLayout(readerModule.MetaData.GetClassLayoutRid(origRid)); - } + protected override ClassLayout GetClassLayout_NoLock() => readerModule.ResolveClassLayout(readerModule.Metadata.GetClassLayoutRid(origRid)); /// protected override TypeDef GetDeclaringType2_NoLock() { - uint enclosingClass = readerModule.TablesStream.ReadNestedClassRow2(readerModule.MetaData.GetNestedClassRid(origRid)); - return enclosingClass == 0 ? null : readerModule.ResolveTypeDef(enclosingClass); + if (!readerModule.TablesStream.TryReadNestedClassRow(readerModule.Metadata.GetNestedClassRid(origRid), out var row)) + return null; + return readerModule.ResolveTypeDef(row.EnclosingClass); } TypeDef DeclaringType2_NoLock { @@ -2107,39 +2025,44 @@ TypeDef DeclaringType2_NoLock { /// protected override void InitializeEvents() { - var mapRid = readerModule.MetaData.GetEventMapRid(origRid); - var list = readerModule.MetaData.GetEventRidList(mapRid); - var tmp = new LazyList((int)list.Length, this, list, (list2, index) => readerModule.ResolveEvent(((RidList)list2)[index])); + var mapRid = readerModule.Metadata.GetEventMapRid(origRid); + var list = readerModule.Metadata.GetEventRidList(mapRid); + var tmp = new LazyList(list.Count, this, list, (list2, index) => readerModule.ResolveEvent(list2[index])); Interlocked.CompareExchange(ref events, tmp, null); } /// protected override void InitializeProperties() { - var mapRid = readerModule.MetaData.GetPropertyMapRid(origRid); - var list = readerModule.MetaData.GetPropertyRidList(mapRid); - var tmp = new LazyList((int)list.Length, this, list, (list2, index) => readerModule.ResolveProperty(((RidList)list2)[index])); + var mapRid = readerModule.Metadata.GetPropertyMapRid(origRid); + var list = readerModule.Metadata.GetPropertyRidList(mapRid); + var tmp = new LazyList(list.Count, this, list, (list2, index) => readerModule.ResolveProperty(list2[index])); Interlocked.CompareExchange(ref properties, tmp, null); } /// protected override void InitializeNestedTypes() { - var list = readerModule.MetaData.GetNestedClassRidList(origRid); - var tmp = new LazyList((int)list.Length, this, list, (list2, index) => readerModule.ResolveTypeDef(((RidList)list2)[index])); + var list = readerModule.Metadata.GetNestedClassRidList(origRid); + var tmp = new LazyList(list.Count, this, list, (list2, index) => readerModule.ResolveTypeDef(list2[index])); Interlocked.CompareExchange(ref nestedTypes, tmp, null); } /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.TypeDef, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.TypeDef, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } /// - protected override ModuleDef GetModule2_NoLock() { - return DeclaringType2_NoLock != null ? null : readerModule; + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(this), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); } + /// + protected override ModuleDef GetModule2_NoLock() => DeclaringType2_NoLock is not null ? null : readerModule; + /// /// Constructor /// @@ -2149,18 +2072,20 @@ protected override ModuleDef GetModule2_NoLock() { /// If is invalid public TypeDefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.TypeDefTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("TypeDef rid {0} does not exist", rid)); + throw new BadImageFormatException($"TypeDef rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - uint name, @namespace; - extendsCodedToken = readerModule.TablesStream.ReadTypeDefRow(origRid, out this.attributes, out name, out @namespace); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.@namespace = readerModule.StringsStream.ReadNoNull(@namespace); + bool b = readerModule.TablesStream.TryReadTypeDefRow(origRid, out var row); + Debug.Assert(b); + extendsCodedToken = row.Extends; + attributes = (int)row.Flags; + name = readerModule.StringsStream.ReadNoNull(row.Name); + @namespace = readerModule.StringsStream.ReadNoNull(row.Namespace); } /// @@ -2169,16 +2094,15 @@ public TypeDefMD(ModuleDefMD readerModule, uint rid) { /// The method /// Generic parameter context /// A list (possibly empty) of all methods overrides - internal ThreadSafe.IList GetMethodOverrides(MethodDefMD method, GenericParamContext gpContext) { - if (method == null) - return ThreadSafeListCreator.Create(); + internal IList GetMethodOverrides(MethodDefMD method, GenericParamContext gpContext) { + if (method is null) + return new List(); - if (methodRidToOverrides == null) + if (methodRidToOverrides is null) InitializeMethodOverrides(); - ThreadSafe.IList overrides; - if (methodRidToOverrides.TryGetValue(method.OrigRid, out overrides)) { - var newList = ThreadSafeListCreator.Create(overrides.Count); + if (methodRidToOverrides.TryGetValue(method.OrigRid, out var overrides)) { + var newList = new List(overrides.Count); for (int i = 0; i < overrides.Count; i++) { var ovr = overrides[i]; @@ -2188,43 +2112,42 @@ internal ThreadSafe.IList GetMethodOverrides(MethodDefMD method, } return newList; } - return ThreadSafeListCreator.Create(); + return new List(); } - struct MethodOverrideTokens { + readonly struct MethodOverrideTokens { public readonly uint MethodBodyToken; public readonly uint MethodDeclarationToken; public MethodOverrideTokens(uint methodBodyToken, uint methodDeclarationToken) { - this.MethodBodyToken = methodBodyToken; - this.MethodDeclarationToken = methodDeclarationToken; + MethodBodyToken = methodBodyToken; + MethodDeclarationToken = methodDeclarationToken; } } void InitializeMethodOverrides() { - var newMethodRidToOverrides = new Dictionary>(); + var newMethodRidToOverrides = new Dictionary>(); - var ridList = readerModule.MetaData.GetMethodImplRidList(origRid); - for (uint i = 0; i < ridList.Length; i++) { - uint methodBodyToken; - uint methodDeclToken = readerModule.TablesStream.ReadMethodImplRow(ridList[i], out methodBodyToken); + var ridList = readerModule.Metadata.GetMethodImplRidList(origRid); + for (int i = 0; i < ridList.Count; i++) { + if (!readerModule.TablesStream.TryReadMethodImplRow(ridList[i], out var row)) + continue; - var methodBody = readerModule.ResolveMethodDefOrRef(methodBodyToken); - var methodDecl = readerModule.ResolveMethodDefOrRef(methodDeclToken); - if (methodBody == null || methodDecl == null) + var methodBody = readerModule.ResolveMethodDefOrRef(row.MethodBody); + var methodDecl = readerModule.ResolveMethodDefOrRef(row.MethodDeclaration); + if (methodBody is null || methodDecl is null) continue; // Should only happen if some obfuscator added invalid metadata // Find the real method. This is usually methodBody since it's usually a // MethodDef. The CLR only allows method bodies in the current type, and // so shall we. var method = FindMethodImplMethod(methodBody); - if (method == null || method.DeclaringType != this) + if (method is null || method.DeclaringType != this) continue; - ThreadSafe.IList overrides; uint rid = method.Rid; - if (!newMethodRidToOverrides.TryGetValue(rid, out overrides)) - newMethodRidToOverrides[rid] = overrides = ThreadSafeListCreator.Create(); + if (!newMethodRidToOverrides.TryGetValue(rid, out var overrides)) + newMethodRidToOverrides[rid] = overrides = new List(); overrides.Add(new MethodOverrideTokens(methodBody.MDToken.Raw, methodDecl.MDToken.Raw)); } Interlocked.CompareExchange(ref methodRidToOverrides, newMethodRidToOverrides, null); @@ -2235,33 +2158,33 @@ void InitializeMethodOverrides() { /// that are property or event handlers get updated. /// internal void InitializeMethodSemanticsAttributes() { - var mapRid = readerModule.MetaData.GetPropertyMapRid(origRid); - var list = readerModule.MetaData.GetPropertyRidList(mapRid); - for (uint i = 0; i < list.Length; i++) { - var ridList = readerModule.MetaData.GetMethodSemanticsRidList(Table.Property, list[i]); - for (uint j = 0; j < ridList.Length; j++) { - MethodSemanticsAttributes semantics; - uint methodToken = readerModule.TablesStream.ReadMethodSemanticsRow(ridList[j], out semantics); - var method = readerModule.ResolveMethod(methodToken); - if (method == null) + var mapRid = readerModule.Metadata.GetPropertyMapRid(origRid); + var list = readerModule.Metadata.GetPropertyRidList(mapRid); + for (int i = 0; i < list.Count; i++) { + var ridList = readerModule.Metadata.GetMethodSemanticsRidList(Table.Property, list[i]); + for (int j = 0; j < ridList.Count; j++) { + if (!readerModule.TablesStream.TryReadMethodSemanticsRow(ridList[j], out var row)) + continue; + var method = readerModule.ResolveMethod(row.Method); + if (method is null) continue; - Interlocked.CompareExchange(ref method.semAttrs, (int)semantics | MethodDef.SEMATTRS_INITD, 0); + Interlocked.CompareExchange(ref method.semAttrs, row.Semantic | MethodDef.SEMATTRS_INITD, 0); } } - mapRid = readerModule.MetaData.GetEventMapRid(origRid); - list = readerModule.MetaData.GetEventRidList(mapRid); - for (uint i = 0; i < list.Length; i++) { - var ridList = readerModule.MetaData.GetMethodSemanticsRidList(Table.Event, list[i]); - for (uint j = 0; j < ridList.Length; j++) { - MethodSemanticsAttributes semantics; - uint methodToken = readerModule.TablesStream.ReadMethodSemanticsRow(ridList[j], out semantics); - var method = readerModule.ResolveMethod(methodToken); - if (method == null) + mapRid = readerModule.Metadata.GetEventMapRid(origRid); + list = readerModule.Metadata.GetEventRidList(mapRid); + for (int i = 0; i < list.Count; i++) { + var ridList = readerModule.Metadata.GetMethodSemanticsRidList(Table.Event, list[i]); + for (int j = 0; j < ridList.Count; j++) { + if (!readerModule.TablesStream.TryReadMethodSemanticsRow(ridList[j], out var row)) + continue; + var method = readerModule.ResolveMethod(row.Method); + if (method is null) continue; - Interlocked.CompareExchange(ref method.semAttrs, (int)semantics | MethodDef.SEMATTRS_INITD, 0); + Interlocked.CompareExchange(ref method.semAttrs, row.Semantic | MethodDef.SEMATTRS_INITD, 0); } } } @@ -2273,23 +2196,23 @@ internal void InitializeMethodSemanticsAttributes() { /// Updated with a list of all get methods /// Updated with a list of all set methods /// Updated with a list of all other methods - internal void InitializeProperty(PropertyDefMD prop, out ThreadSafe.IList getMethods, out ThreadSafe.IList setMethods, out ThreadSafe.IList otherMethods) { - getMethods = ThreadSafeListCreator.Create(); - setMethods = ThreadSafeListCreator.Create(); - otherMethods = ThreadSafeListCreator.Create(); - if (prop == null) + internal void InitializeProperty(PropertyDefMD prop, out IList getMethods, out IList setMethods, out IList otherMethods) { + getMethods = new List(); + setMethods = new List(); + otherMethods = new List(); + if (prop is null) return; - var ridList = readerModule.MetaData.GetMethodSemanticsRidList(Table.Property, prop.OrigRid); - for (uint i = 0; i < ridList.Length; i++) { - MethodSemanticsAttributes semantics; - uint methodToken = readerModule.TablesStream.ReadMethodSemanticsRow(ridList[i], out semantics); - var method = readerModule.ResolveMethod(methodToken); - if (method == null || method.DeclaringType != prop.DeclaringType) + var ridList = readerModule.Metadata.GetMethodSemanticsRidList(Table.Property, prop.OrigRid); + for (int i = 0; i < ridList.Count; i++) { + if (!readerModule.TablesStream.TryReadMethodSemanticsRow(ridList[i], out var row)) + continue; + var method = readerModule.ResolveMethod(row.Method); + if (method is null || method.DeclaringType != prop.DeclaringType) continue; // It's documented to be flags, but ignore those with more than one bit set - switch (semantics) { + switch ((MethodSemanticsAttributes)row.Semantic) { case MethodSemanticsAttributes.Setter: if (!setMethods.Contains(method)) setMethods.Add(method); @@ -2320,36 +2243,36 @@ internal void InitializeProperty(PropertyDefMD prop, out ThreadSafe.IListUpdated with the fire method or null if none /// Updated with the removeOn method or null if none /// Updated with a list of all other methods - internal void InitializeEvent(EventDefMD evt, out MethodDef addMethod, out MethodDef invokeMethod, out MethodDef removeMethod, out ThreadSafe.IList otherMethods) { + internal void InitializeEvent(EventDefMD evt, out MethodDef addMethod, out MethodDef invokeMethod, out MethodDef removeMethod, out IList otherMethods) { addMethod = null; invokeMethod = null; removeMethod = null; - otherMethods = ThreadSafeListCreator.Create(); - if (evt == null) + otherMethods = new List(); + if (evt is null) return; - var ridList = readerModule.MetaData.GetMethodSemanticsRidList(Table.Event, evt.OrigRid); - for (uint i = 0; i < ridList.Length; i++) { - MethodSemanticsAttributes semantics; - uint methodToken = readerModule.TablesStream.ReadMethodSemanticsRow(ridList[i], out semantics); - var method = readerModule.ResolveMethod(methodToken); - if (method == null || method.DeclaringType != evt.DeclaringType) + var ridList = readerModule.Metadata.GetMethodSemanticsRidList(Table.Event, evt.OrigRid); + for (int i = 0; i < ridList.Count; i++) { + if (!readerModule.TablesStream.TryReadMethodSemanticsRow(ridList[i], out var row)) + continue; + var method = readerModule.ResolveMethod(row.Method); + if (method is null || method.DeclaringType != evt.DeclaringType) continue; // It's documented to be flags, but ignore those with more than one bit set - switch (semantics) { + switch ((MethodSemanticsAttributes)row.Semantic) { case MethodSemanticsAttributes.AddOn: - if (addMethod == null) + if (addMethod is null) addMethod = method; break; case MethodSemanticsAttributes.RemoveOn: - if (removeMethod == null) + if (removeMethod is null) removeMethod = method; break; case MethodSemanticsAttributes.Fire: - if (invokeMethod == null) + if (invokeMethod is null) invokeMethod = method; break; diff --git a/src/DotNet/TypeDefFinder.cs b/src/DotNet/TypeDefFinder.cs index 619af563a..bfd560093 100644 --- a/src/DotNet/TypeDefFinder.cs +++ b/src/DotNet/TypeDefFinder.cs @@ -1,7 +1,8 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; +using System.Text; using dnlib.Threading; namespace dnlib.DotNet { @@ -15,6 +16,7 @@ sealed class TypeDefFinder : ITypeDefFinder, IDisposable { Dictionary typeRefCache = new Dictionary(new TypeEqualityComparer(TypeComparerOptions)); Dictionary normalNameCache = new Dictionary(StringComparer.Ordinal); Dictionary reflectionNameCache = new Dictionary(StringComparer.Ordinal); + readonly StringBuilder sb = new StringBuilder(); IEnumerator typeEnumerator; readonly IEnumerable rootTypes; #if THREAD_SAFE @@ -47,12 +49,12 @@ public bool IsCacheEnabled { } bool IsCacheEnabled_NoLock { - get { return isCacheEnabled; } + get => isCacheEnabled; set { if (isCacheEnabled == value) return; - if (typeEnumerator != null) { + if (typeEnumerator is not null) { typeEnumerator.Dispose(); typeEnumerator = null; } @@ -85,14 +87,12 @@ public TypeDefFinder(IEnumerable rootTypes) /// from should also be included. /// If is null public TypeDefFinder(IEnumerable rootTypes, bool includeNestedTypes) { - if (rootTypes == null) - throw new ArgumentNullException("rootTypes"); - this.rootTypes = rootTypes; + this.rootTypes = rootTypes ?? throw new ArgumentNullException(nameof(rootTypes)); this.includeNestedTypes = includeNestedTypes; } void InitializeTypeEnumerator() { - if (typeEnumerator != null) { + if (typeEnumerator is not null) { typeEnumerator.Dispose(); typeEnumerator = null; } @@ -117,7 +117,7 @@ public void ResetCache() { /// public TypeDef Find(string fullName, bool isReflectionName) { - if (fullName == null) + if (fullName is null) return null; #if THREAD_SAFE theLock.EnterWriteLock(); try { @@ -132,7 +132,7 @@ public TypeDef Find(string fullName, bool isReflectionName) { /// public TypeDef Find(TypeRef typeRef) { - if (typeRef == null) + if (typeRef is null) return null; #if THREAD_SAFE theLock.EnterWriteLock(); try { @@ -144,41 +144,44 @@ public TypeDef Find(TypeRef typeRef) { } TypeDef FindCache(TypeRef typeRef) { - TypeDef cachedType; - if (typeRefCache.TryGetValue(typeRef, out cachedType)) + if (typeRefCache.TryGetValue(typeRef, out var cachedType)) return cachedType; // Build the cache lazily var comparer = new SigComparer(TypeComparerOptions); while (true) { cachedType = GetNextTypeDefCache(); - if (cachedType == null || comparer.Equals(cachedType, typeRef)) + if (cachedType is null || comparer.Equals(cachedType, typeRef)) return cachedType; } } TypeDef FindCacheReflection(string fullName) { - TypeDef cachedType; - if (reflectionNameCache.TryGetValue(fullName, out cachedType)) + if (reflectionNameCache.TryGetValue(fullName, out var cachedType)) return cachedType; // Build the cache lazily while (true) { cachedType = GetNextTypeDefCache(); - if (cachedType == null || cachedType.ReflectionFullName == fullName) + if (cachedType is null) + return cachedType; + sb.Length = 0; + if (FullNameFactory.FullName(cachedType, true, null, sb) == fullName) return cachedType; } } TypeDef FindCacheNormal(string fullName) { - TypeDef cachedType; - if (normalNameCache.TryGetValue(fullName, out cachedType)) + if (normalNameCache.TryGetValue(fullName, out var cachedType)) return cachedType; // Build the cache lazily while (true) { cachedType = GetNextTypeDefCache(); - if (cachedType == null || cachedType.FullName == fullName) + if (cachedType is null) + return cachedType; + sb.Length = 0; + if (FullNameFactory.FullName(cachedType, false, null, sb) == fullName) return cachedType; } } @@ -188,7 +191,7 @@ TypeDef FindSlow(TypeRef typeRef) { var comparer = new SigComparer(TypeComparerOptions); while (true) { var type = GetNextTypeDef(); - if (type == null || comparer.Equals(type, typeRef)) + if (type is null || comparer.Equals(type, typeRef)) return type; } } @@ -197,7 +200,10 @@ TypeDef FindSlowReflection(string fullName) { InitializeTypeEnumerator(); while (true) { var type = GetNextTypeDef(); - if (type == null || type.ReflectionFullName == fullName) + if (type is null) + return type; + sb.Length = 0; + if (FullNameFactory.FullName(type, true, null, sb) == fullName) return type; } } @@ -206,7 +212,10 @@ TypeDef FindSlowNormal(string fullName) { InitializeTypeEnumerator(); while (true) { var type = GetNextTypeDef(); - if (type == null || type.FullName == fullName) + if (type is null) + return type; + sb.Length = 0; + if (FullNameFactory.FullName(type, false, null, sb) == fullName) return type; } } @@ -218,7 +227,7 @@ TypeDef FindSlowNormal(string fullName) { TypeDef GetNextTypeDef() { while (typeEnumerator.MoveNext()) { var type = typeEnumerator.Current; - if (type != null) + if (type is not null) return type; } return null; @@ -232,7 +241,7 @@ TypeDef GetNextTypeDef() { /// The next or null if none TypeDef GetNextTypeDefCache() { var type = GetNextTypeDef(); - if (type == null) + if (type is null) return null; // Only insert it if another type with the exact same sig/name isn't already @@ -241,9 +250,11 @@ TypeDef GetNextTypeDefCache() { if (!typeRefCache.ContainsKey(type)) typeRefCache[type] = type; string fn; - if (!normalNameCache.ContainsKey(fn = type.FullName)) + sb.Length = 0; + if (!normalNameCache.ContainsKey(fn = FullNameFactory.FullName(type, false, null, sb))) normalNameCache[fn] = type; - if (!reflectionNameCache.ContainsKey(fn = type.ReflectionFullName)) + sb.Length = 0; + if (!reflectionNameCache.ContainsKey(fn = FullNameFactory.FullName(type, true, null, sb))) reflectionNameCache[fn] = type; return type; @@ -254,7 +265,7 @@ public void Dispose() { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif - if (typeEnumerator != null) + if (typeEnumerator is not null) typeEnumerator.Dispose(); typeEnumerator = null; typeRefCache = null; diff --git a/src/DotNet/TypeHelper.cs b/src/DotNet/TypeHelper.cs index a0e665eb4..ddfe4d47c 100644 --- a/src/DotNet/TypeHelper.cs +++ b/src/DotNet/TypeHelper.cs @@ -1,12 +1,6 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif +using System.Collections.Generic; namespace dnlib.DotNet { /// @@ -15,29 +9,14 @@ namespace dnlib.DotNet { struct TypeHelper { RecursionCounter recursionCounter; - internal static bool ContainsGenericParameter(StandAloneSig ss) { - return ss != null && TypeHelper.ContainsGenericParameter(ss.Signature); - } - - internal static bool ContainsGenericParameter(InterfaceImpl ii) { - return ii != null && TypeHelper.ContainsGenericParameter(ii.Interface); - } + internal static bool ContainsGenericParameter(StandAloneSig ss) => ss is not null && TypeHelper.ContainsGenericParameter(ss.Signature); + internal static bool ContainsGenericParameter(InterfaceImpl ii) => ii is not null && TypeHelper.ContainsGenericParameter(ii.Interface); + internal static bool ContainsGenericParameter(GenericParamConstraint gpc) => gpc is not null && ContainsGenericParameter(gpc.Constraint); - internal static bool ContainsGenericParameter(GenericParamConstraint gpc) { - return gpc != null && ContainsGenericParameter(gpc.Constraint); - } - - internal static bool ContainsGenericParameter(MethodSpec ms) { - if (ms == null) - return false; - - // A normal MethodSpec should always contain generic arguments and thus - // its MethodDef is always a generic method with generic parameters. - return true; - } + internal static bool ContainsGenericParameter(MethodSpec ms) => ms is not null && ContainsGenericParameter(ms.GenericInstMethodSig); internal static bool ContainsGenericParameter(MemberRef mr) { - if (mr == null) + if (mr is null) return false; if (ContainsGenericParameter(mr.Signature)) @@ -45,12 +24,10 @@ internal static bool ContainsGenericParameter(MemberRef mr) { var cl = mr.Class; - var tdr = cl as ITypeDefOrRef; - if (tdr != null) + if (cl is ITypeDefOrRef tdr) return tdr.ContainsGenericParameter; - var md = cl as MethodDef; - if (md != null) + if (cl is MethodDef md) return TypeHelper.ContainsGenericParameter(md.Signature); return false; @@ -64,20 +41,16 @@ internal static bool ContainsGenericParameter(MemberRef mr) { /// true if contains a /// or a . public static bool ContainsGenericParameter(CallingConventionSig callConv) { - var fs = callConv as FieldSig; - if (fs != null) + if (callConv is FieldSig fs) return ContainsGenericParameter(fs); - var mbs = callConv as MethodBaseSig; - if (mbs != null) + if (callConv is MethodBaseSig mbs) return ContainsGenericParameter(mbs); - var ls = callConv as LocalSig; - if (ls != null) + if (callConv is LocalSig ls) return ContainsGenericParameter(ls); - var gim = callConv as GenericInstMethodSig; - if (gim != null) + if (callConv is GenericInstMethodSig gim) return ContainsGenericParameter(gim); return false; @@ -90,9 +63,7 @@ public static bool ContainsGenericParameter(CallingConventionSig callConv) { /// Field signature /// true if contains a /// or a . - public static bool ContainsGenericParameter(FieldSig fieldSig) { - return new TypeHelper().ContainsGenericParameterInternal(fieldSig); - } + public static bool ContainsGenericParameter(FieldSig fieldSig) => new TypeHelper().ContainsGenericParameterInternal(fieldSig); /// /// Checks whether contains a or a @@ -101,9 +72,7 @@ public static bool ContainsGenericParameter(FieldSig fieldSig) { /// Method or property signature /// true if contains a /// or a . - public static bool ContainsGenericParameter(MethodBaseSig methodSig) { - return new TypeHelper().ContainsGenericParameterInternal(methodSig); - } + public static bool ContainsGenericParameter(MethodBaseSig methodSig) => new TypeHelper().ContainsGenericParameterInternal(methodSig); /// /// Checks whether contains a or a @@ -112,9 +81,7 @@ public static bool ContainsGenericParameter(MethodBaseSig methodSig) { /// Local signature /// true if contains a /// or a . - public static bool ContainsGenericParameter(LocalSig localSig) { - return new TypeHelper().ContainsGenericParameterInternal(localSig); - } + public static bool ContainsGenericParameter(LocalSig localSig) => new TypeHelper().ContainsGenericParameterInternal(localSig); /// /// Checks whether contains a or a @@ -123,9 +90,7 @@ public static bool ContainsGenericParameter(LocalSig localSig) { /// Generic method signature /// true if contains a /// or a . - public static bool ContainsGenericParameter(GenericInstMethodSig gim) { - return new TypeHelper().ContainsGenericParameterInternal(gim); - } + public static bool ContainsGenericParameter(GenericInstMethodSig gim) => new TypeHelper().ContainsGenericParameterInternal(gim); /// /// Checks whether contains a or a @@ -135,24 +100,19 @@ public static bool ContainsGenericParameter(GenericInstMethodSig gim) { /// true if contains a or a /// . public static bool ContainsGenericParameter(IType type) { - var td = type as TypeDef; - if (td != null) + if (type is TypeDef td) return ContainsGenericParameter(td); - var tr = type as TypeRef; - if (tr != null) + if (type is TypeRef tr) return ContainsGenericParameter(tr); - var ts = type as TypeSpec; - if (ts != null) + if (type is TypeSpec ts) return ContainsGenericParameter(ts); - var sig = type as TypeSig; - if (sig != null) + if (type is TypeSig sig) return ContainsGenericParameter(sig); - var et = type as ExportedType; - if (et != null) + if (type is ExportedType et) return ContainsGenericParameter(et); return false; @@ -165,9 +125,7 @@ public static bool ContainsGenericParameter(IType type) { /// Type /// true if contains a or a /// . - public static bool ContainsGenericParameter(TypeDef type) { - return new TypeHelper().ContainsGenericParameterInternal(type); - } + public static bool ContainsGenericParameter(TypeDef type) => new TypeHelper().ContainsGenericParameterInternal(type); /// /// Checks whether contains a or a @@ -176,9 +134,7 @@ public static bool ContainsGenericParameter(TypeDef type) { /// Type /// true if contains a or a /// . - public static bool ContainsGenericParameter(TypeRef type) { - return new TypeHelper().ContainsGenericParameterInternal(type); - } + public static bool ContainsGenericParameter(TypeRef type) => new TypeHelper().ContainsGenericParameterInternal(type); /// /// Checks whether contains a or a @@ -187,9 +143,7 @@ public static bool ContainsGenericParameter(TypeRef type) { /// Type /// true if contains a or a /// . - public static bool ContainsGenericParameter(TypeSpec type) { - return new TypeHelper().ContainsGenericParameterInternal(type); - } + public static bool ContainsGenericParameter(TypeSpec type) => new TypeHelper().ContainsGenericParameterInternal(type); /// /// Checks whether contains a or a @@ -198,9 +152,7 @@ public static bool ContainsGenericParameter(TypeSpec type) { /// Type /// true if contains a or a /// . - public static bool ContainsGenericParameter(TypeSig type) { - return new TypeHelper().ContainsGenericParameterInternal(type); - } + public static bool ContainsGenericParameter(TypeSig type) => new TypeHelper().ContainsGenericParameterInternal(type); /// /// Checks whether contains a or a @@ -209,20 +161,14 @@ public static bool ContainsGenericParameter(TypeSig type) { /// Type /// true if contains a or a /// . - public static bool ContainsGenericParameter(ExportedType type) { - return new TypeHelper().ContainsGenericParameterInternal(type); - } + public static bool ContainsGenericParameter(ExportedType type) => new TypeHelper().ContainsGenericParameterInternal(type); - bool ContainsGenericParameterInternal(TypeDef type) { - return false; - } + bool ContainsGenericParameterInternal(TypeDef type) => false; - bool ContainsGenericParameterInternal(TypeRef type) { - return false; - } + bool ContainsGenericParameterInternal(TypeRef type) => false; bool ContainsGenericParameterInternal(TypeSpec type) { - if (type == null) + if (type is null) return false; if (!recursionCounter.Increment()) return false; @@ -234,14 +180,14 @@ bool ContainsGenericParameterInternal(TypeSpec type) { } bool ContainsGenericParameterInternal(ITypeDefOrRef tdr) { - if (tdr == null) + if (tdr is null) return false; // TypeDef and TypeRef contain no generic parameters return ContainsGenericParameterInternal(tdr as TypeSpec); } bool ContainsGenericParameterInternal(TypeSig type) { - if (type == null) + if (type is null) return false; if (!recursionCounter.Increment()) return false; @@ -302,7 +248,6 @@ bool ContainsGenericParameterInternal(TypeSig type) { ContainsGenericParameterInternal((type as NonLeafSig).Next); break; - case ElementType.End: case ElementType.R: case ElementType.Internal: @@ -316,32 +261,26 @@ bool ContainsGenericParameterInternal(TypeSig type) { return res; } - bool ContainsGenericParameterInternal(ExportedType type) { - return false; - } + bool ContainsGenericParameterInternal(ExportedType type) => false; bool ContainsGenericParameterInternal(CallingConventionSig callConv) { - var fs = callConv as FieldSig; - if (fs != null) + if (callConv is FieldSig fs) return ContainsGenericParameterInternal(fs); - var mbs = callConv as MethodBaseSig; - if (mbs != null) + if (callConv is MethodBaseSig mbs) return ContainsGenericParameterInternal(mbs); - var ls = callConv as LocalSig; - if (ls != null) + if (callConv is LocalSig ls) return ContainsGenericParameterInternal(ls); - var gim = callConv as GenericInstMethodSig; - if (gim != null) + if (callConv is GenericInstMethodSig gim) return ContainsGenericParameterInternal(gim); return false; } bool ContainsGenericParameterInternal(FieldSig fs) { - if (fs == null) + if (fs is null) return false; if (!recursionCounter.Increment()) return false; @@ -353,7 +292,7 @@ bool ContainsGenericParameterInternal(FieldSig fs) { } bool ContainsGenericParameterInternal(MethodBaseSig mbs) { - if (mbs == null) + if (mbs is null) return false; if (!recursionCounter.Increment()) return false; @@ -367,7 +306,7 @@ bool ContainsGenericParameterInternal(MethodBaseSig mbs) { } bool ContainsGenericParameterInternal(LocalSig ls) { - if (ls == null) + if (ls is null) return false; if (!recursionCounter.Increment()) return false; @@ -379,7 +318,7 @@ bool ContainsGenericParameterInternal(LocalSig ls) { } bool ContainsGenericParameterInternal(GenericInstMethodSig gim) { - if (gim == null) + if (gim is null) return false; if (!recursionCounter.Increment()) return false; @@ -390,14 +329,16 @@ bool ContainsGenericParameterInternal(GenericInstMethodSig gim) { return res; } - bool ContainsGenericParameter(ThreadSafe.IList types) { - if (types == null) + bool ContainsGenericParameter(IList types) { + if (types is null) return false; if (!recursionCounter.Increment()) return false; bool res = false; - foreach (var type in types.GetSafeEnumerable()) { + int count = types.Count; + for (int i = 0; i < count; i++) { + var type = types[i]; if (ContainsGenericParameter(type)) { res = true; break; diff --git a/src/DotNet/TypeNameParser.cs b/src/DotNet/TypeNameParser.cs index 628351d74..30c2fa3ac 100644 --- a/src/DotNet/TypeNameParser.cs +++ b/src/DotNet/TypeNameParser.cs @@ -1,8 +1,9 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.IO; +using System.Runtime.Serialization; using System.Text; namespace dnlib.DotNet { @@ -33,6 +34,15 @@ public TypeNameParserException(string message) public TypeNameParserException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Constructor + /// + /// + /// + protected TypeNameParserException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } } /// @@ -67,9 +77,8 @@ public abstract class TypeNameParser : IDisposable { /// Helper class /// A new instance /// If parsing failed - public static ITypeDefOrRef ParseReflectionThrow(ModuleDef ownerModule, string typeFullName, IAssemblyRefFinder typeNameParserHelper) { - return ParseReflectionThrow(ownerModule, typeFullName, typeNameParserHelper, new GenericParamContext()); - } + public static ITypeDefOrRef ParseReflectionThrow(ModuleDef ownerModule, string typeFullName, IAssemblyRefFinder typeNameParserHelper) => + ParseReflectionThrow(ownerModule, typeFullName, typeNameParserHelper, new GenericParamContext()); /// /// Parses a Reflection type name and creates a @@ -92,9 +101,8 @@ public static ITypeDefOrRef ParseReflectionThrow(ModuleDef ownerModule, string t /// Full name of type /// Helper class /// A new instance or null if parsing failed - public static ITypeDefOrRef ParseReflection(ModuleDef ownerModule, string typeFullName, IAssemblyRefFinder typeNameParserHelper) { - return ParseReflection(ownerModule, typeFullName, typeNameParserHelper, new GenericParamContext()); - } + public static ITypeDefOrRef ParseReflection(ModuleDef ownerModule, string typeFullName, IAssemblyRefFinder typeNameParserHelper) => + ParseReflection(ownerModule, typeFullName, typeNameParserHelper, new GenericParamContext()); /// /// Parses a Reflection type name and creates a @@ -121,9 +129,8 @@ public static ITypeDefOrRef ParseReflection(ModuleDef ownerModule, string typeFu /// Helper class /// A new instance /// If parsing failed - public static TypeSig ParseAsTypeSigReflectionThrow(ModuleDef ownerModule, string typeFullName, IAssemblyRefFinder typeNameParserHelper) { - return ParseAsTypeSigReflectionThrow(ownerModule, typeFullName, typeNameParserHelper, new GenericParamContext()); - } + public static TypeSig ParseAsTypeSigReflectionThrow(ModuleDef ownerModule, string typeFullName, IAssemblyRefFinder typeNameParserHelper) => + ParseAsTypeSigReflectionThrow(ownerModule, typeFullName, typeNameParserHelper, new GenericParamContext()); /// /// Parses a Reflection type name and creates a @@ -146,9 +153,8 @@ public static TypeSig ParseAsTypeSigReflectionThrow(ModuleDef ownerModule, strin /// Full name of type /// Helper class /// A new instance or null if parsing failed - public static TypeSig ParseAsTypeSigReflection(ModuleDef ownerModule, string typeFullName, IAssemblyRefFinder typeNameParserHelper) { - return ParseAsTypeSigReflection(ownerModule, typeFullName, typeNameParserHelper, new GenericParamContext()); - } + public static TypeSig ParseAsTypeSigReflection(ModuleDef ownerModule, string typeFullName, IAssemblyRefFinder typeNameParserHelper) => + ParseAsTypeSigReflection(ownerModule, typeFullName, typeNameParserHelper, new GenericParamContext()); /// /// Parses a Reflection type name and creates a @@ -186,7 +192,7 @@ protected TypeNameParser(ModuleDef ownerModule, string typeFullName, IAssemblyRe /// Generic parameter context protected TypeNameParser(ModuleDef ownerModule, string typeFullName, IAssemblyRefFinder typeNameParserHelper, GenericParamContext gpContext) { this.ownerModule = ownerModule; - this.reader = new StringReader(typeFullName ?? string.Empty); + reader = new StringReader(typeFullName ?? string.Empty); this.typeNameParserHelper = typeNameParserHelper; this.gpContext = gpContext; } @@ -196,9 +202,7 @@ protected TypeNameParser(ModuleDef ownerModule, string typeFullName, IAssemblyRe /// /// A new instance /// If parsing failed - internal ITypeDefOrRef Parse() { - return ownerModule.UpdateRowId(ParseAsTypeSig().ToTypeDefOrRef()); - } + internal ITypeDefOrRef Parse() => ownerModule.UpdateRowId(ParseAsTypeSig().ToTypeDefOrRef()); /// /// Parses a type name and creates a @@ -219,9 +223,7 @@ protected void RecursionIncrement() { /// /// Decrement recursion counter /// - protected void RecursionDecrement() { - recursionCounter.Decrement(); - } + protected void RecursionDecrement() => recursionCounter.Decrement(); /// public void Dispose() { @@ -236,7 +238,7 @@ public void Dispose() { protected virtual void Dispose(bool disposing) { if (!disposing) return; - if (reader != null) + if (reader is not null) reader.Dispose(); reader = null; } @@ -244,9 +246,7 @@ protected virtual void Dispose(bool disposing) { internal abstract class TSpec { public readonly ElementType etype; - protected TSpec(ElementType etype) { - this.etype = etype; - } + protected TSpec(ElementType etype) => this.etype = etype; } internal sealed class SZArraySpec : TSpec { @@ -298,7 +298,9 @@ internal GenericSig ReadGenericSig() { } internal TypeSig CreateTypeSig(IList tspecs, TypeSig currentSig) { - foreach (var tspec in tspecs) { + int count = tspecs.Count; + for (int i = 0; i < count; i++) { + var tspec = tspecs[i]; switch (tspec.etype) { case ElementType.SZArray: currentSig = new SZArraySig(currentSig); @@ -354,10 +356,9 @@ protected TypeRef ReadTypeRefAndNestedNoAssembly(char nestedChar) { /// /// A new instance protected TypeRef ReadTypeRefNoAssembly() { - string ns, name; // White space is important here. Any white space before the comma/EOF must be // parsed as part of the name. - GetNamespaceAndName(ReadId(false), out ns, out name); + GetNamespaceAndName(ReadId(false, false), out var ns, out var name); return ownerModule.UpdateRowId(new TypeRefUser(ownerModule, ns, name)); } @@ -374,38 +375,31 @@ static void GetNamespaceAndName(string fullName, out string ns, out string name) } internal TypeSig ToTypeSig(ITypeDefOrRef type) { - var td = type as TypeDef; - if (td != null) + if (type is TypeDef td) return ToTypeSig(td, td.IsValueType); - var tr = type as TypeRef; - if (tr != null) + if (type is TypeRef tr) return ToTypeSig(tr, IsValueType(tr)); - var ts = type as TypeSpec; - if (ts != null) + if (type is TypeSpec ts) return ts.TypeSig; Verify(false, "Unknown type"); return null; } - static TypeSig ToTypeSig(ITypeDefOrRef type, bool isValueType) { - return isValueType ? (TypeSig)new ValueTypeSig(type) : new ClassSig(type); - } + static TypeSig ToTypeSig(ITypeDefOrRef type, bool isValueType) => isValueType ? (TypeSig)new ValueTypeSig(type) : new ClassSig(type); internal AssemblyRef FindAssemblyRef(TypeRef nonNestedTypeRef) { AssemblyRef asmRef = null; - if (nonNestedTypeRef != null && typeNameParserHelper != null) + if (nonNestedTypeRef is not null && typeNameParserHelper is not null) asmRef = typeNameParserHelper.FindAssemblyRef(nonNestedTypeRef); - if (asmRef != null) + if (asmRef is not null) return asmRef; var ownerAsm = ownerModule.Assembly; - if (ownerAsm != null) + if (ownerAsm is not null) return ownerModule.UpdateRowId(ownerAsm.ToAssemblyRef()); return AssemblyRef.CurrentAssembly; } - internal bool IsValueType(TypeRef typeRef) { - return typeRef != null && typeRef.IsValueType; - } + internal bool IsValueType(TypeRef typeRef) => typeRef is not null && typeRef.IsValueType; internal static void Verify(bool b, string msg) { if (!b) @@ -461,15 +455,13 @@ internal int ReadInt32() { } } - internal string ReadId() { - return ReadId(true); - } + internal string ReadId() => ReadId(true, true); - internal string ReadId(bool ignoreWhiteSpace) { + internal string ReadId(bool ignoreWhiteSpace, bool ignoreEqualSign) { SkipWhite(); var sb = new StringBuilder(); int c; - while ((c = GetIdChar(ignoreWhiteSpace)) != -1) + while ((c = GetIdChar(ignoreWhiteSpace, ignoreEqualSign)) != -1) sb.Append((char)c); Verify(sb.Length > 0, "Expected an id"); return sb.ToString(); @@ -478,22 +470,19 @@ internal string ReadId(bool ignoreWhiteSpace) { /// /// Peeks the next char. -1 if no more chars. /// - protected int PeekChar() { - return reader.Peek(); - } + protected int PeekChar() => reader.Peek(); /// /// Gets the next char or -1 if no more chars /// - protected int ReadChar() { - return reader.Read(); - } + protected int ReadChar() => reader.Read(); /// /// Gets the next ID char or -1 if no more ID chars /// /// true if white space should be ignored - internal abstract int GetIdChar(bool ignoreWhiteSpace); + /// true if equal sign '=' should be ignored + internal abstract int GetIdChar(bool ignoreWhiteSpace, bool ignoreEqualSign); } /// @@ -526,9 +515,7 @@ public ReflectionTypeNameParser(ModuleDef ownerModule, string typeFullName, IAss /// /// Full assembly name /// A new instance or null if parsing failed - public static AssemblyRef ParseAssemblyRef(string asmFullName) { - return ParseAssemblyRef(asmFullName, new GenericParamContext()); - } + public static AssemblyRef ParseAssemblyRef(string asmFullName) => ParseAssemblyRef(asmFullName, new GenericParamContext()); /// /// Parses an assembly name @@ -574,7 +561,7 @@ TypeSig ReadType(bool readAssemblyReference) { result = CreateTypeSig(tspecs, currentSig); } else { - TypeRef typeRef = ReadTypeRefAndNestedNoAssembly('+'); + var typeRef = ReadTypeRefAndNestedNoAssembly('+'); var tspecs = ReadTSpecs(); var nonNestedTypeRef = TypeRef.GetNonNestedTypeRef(typeRef); AssemblyRef asmRef; @@ -588,12 +575,12 @@ TypeSig ReadType(bool readAssemblyReference) { result = null; if (typeRef == nonNestedTypeRef) { var corLibSig = ownerModule.CorLibTypes.GetCorLibTypeSig(typeRef.Namespace, typeRef.Name, typeRef.DefinitionAssembly); - if (corLibSig != null) + if (corLibSig is not null) result = corLibSig; } - if (result == null) { + if (result is null) { var typeDef = Resolve(asmRef, typeRef); - result = ToTypeSig(typeDef != null ? (ITypeDefOrRef)typeDef : typeRef); + result = ToTypeSig(typeDef is not null ? (ITypeDefOrRef)typeDef : typeRef); } if (tspecs.Count != 0) @@ -606,12 +593,12 @@ TypeSig ReadType(bool readAssemblyReference) { TypeDef Resolve(AssemblyRef asmRef, TypeRef typeRef) { var asm = ownerModule.Assembly; - if (asm == null) + if (asm is null) return null; - if (asmRef.FullName != asm.GetFullNameWithPublicKey() && asmRef.FullName != asm.GetFullNameWithPublicKeyToken()) + if (!(AssemblyNameComparer.CompareAll.Equals(asmRef, asm) && asmRef.IsRetargetable == asm.IsRetargetable)) return null; - var td = typeRef.Resolve(); - return td != null && td.Module == ownerModule ? td : null; + var td = asm.Find(typeRef); + return td is not null && td.Module == ownerModule ? td : null; } AssemblyRef ReadOptionalAssemblyRef() { @@ -735,7 +722,7 @@ IList ReadTSpecs() { AssemblyRef ReadAssemblyRef() { var asmRef = new AssemblyRefUser(); - if (ownerModule != null) + if (ownerModule is not null) ownerModule.UpdateRowId(asmRef); asmRef.Name = ReadAssemblyNameId(); @@ -786,6 +773,7 @@ AssemblyRef ReadAssemblyRef() { asmRef.PublicKeyOrToken = new PublicKey(); else asmRef.PublicKeyOrToken = PublicKeyBase.CreatePublicKey(Utils.ParseBytes(value)); + asmRef.Attributes |= AssemblyAttributes.PublicKey; break; case "PUBLICKEYTOKEN": @@ -794,6 +782,7 @@ AssemblyRef ReadAssemblyRef() { asmRef.PublicKeyOrToken = new PublicKeyToken(); else asmRef.PublicKeyOrToken = PublicKeyBase.CreatePublicKeyToken(Utils.ParseBytes(value)); + asmRef.Attributes &= ~AssemblyAttributes.PublicKey; break; case "CULTURE": @@ -838,7 +827,7 @@ int GetAsmNameChar() { } } - internal override int GetIdChar(bool ignoreWhiteSpace) { + internal override int GetIdChar(bool ignoreWhiteSpace, bool ignoreEqualSign) { int c = PeekChar(); if (c == -1) return -1; @@ -855,7 +844,7 @@ internal override int GetIdChar(bool ignoreWhiteSpace) { case '*': case '[': case ']': - case '=': + case '=' when ignoreEqualSign: return -1; default: diff --git a/src/DotNet/TypeRef.cs b/src/DotNet/TypeRef.cs index 9d9bffeb9..bfaa511f5 100644 --- a/src/DotNet/TypeRef.cs +++ b/src/DotNet/TypeRef.cs @@ -1,15 +1,18 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; using dnlib.Threading; namespace dnlib.DotNet { /// /// A high-level representation of a row in the TypeRef table /// - public abstract class TypeRef : ITypeDefOrRef, IHasCustomAttribute, IMemberRefParent, IResolutionScope { + public abstract class TypeRef : ITypeDefOrRef, IHasCustomAttribute, IMemberRefParent, IHasCustomDebugInformation, IResolutionScope { /// /// The row id in its table /// @@ -25,103 +28,67 @@ public abstract class TypeRef : ITypeDefOrRef, IHasCustomAttribute, IMemberRefPa #endif /// - public MDToken MDToken { - get { return new MDToken(Table.TypeRef, rid); } - } + public MDToken MDToken => new MDToken(Table.TypeRef, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int TypeDefOrRefTag { - get { return 1; } - } + public int TypeDefOrRefTag => 1; /// - public int HasCustomAttributeTag { - get { return 2; } - } + public int HasCustomAttributeTag => 2; /// - public int MemberRefParentTag { - get { return 1; } - } + public int MemberRefParentTag => 1; /// - public int ResolutionScopeTag { - get { return 3; } - } + public int ResolutionScopeTag => 3; /// - int IGenericParameterProvider.NumberOfGenericParameters { - get { return 0; } - } + int IGenericParameterProvider.NumberOfGenericParameters => 0; /// - string IType.TypeName { - get { return FullNameCreator.Name(this, false); } - } + string IType.TypeName => Name; /// - public string ReflectionName { - get { return FullNameCreator.Name(this, true); } - } + public string ReflectionName => FullNameFactory.Name(this, true, null); /// - string IType.Namespace { - get { return FullNameCreator.Namespace(this, false); } - } + string IType.Namespace => Namespace; /// - public string ReflectionNamespace { - get { return FullNameCreator.Namespace(this, true); } - } + public string ReflectionNamespace => FullNameFactory.Namespace(this, true, null); /// - public string FullName { - get { return FullNameCreator.FullName(this, false); } - } + public string FullName => FullNameFactory.FullName(this, false, null, null); /// - public string ReflectionFullName { - get { return FullNameCreator.FullName(this, true); } - } + public string ReflectionFullName => FullNameFactory.FullName(this, true, null, null); /// - public string AssemblyQualifiedName { - get { return FullNameCreator.AssemblyQualifiedName(this); } - } + public string AssemblyQualifiedName => FullNameFactory.AssemblyQualifiedName(this, null, null); /// - public IAssembly DefinitionAssembly { - get { return FullNameCreator.DefinitionAssembly(this); } - } + public IAssembly DefinitionAssembly => FullNameFactory.DefinitionAssembly(this); /// - public IScope Scope { - get { return FullNameCreator.Scope(this); } - } + public IScope Scope => FullNameFactory.Scope(this); /// - public ITypeDefOrRef ScopeType { - get { return this; } - } + public ITypeDefOrRef ScopeType => this; /// /// Always returns false since a does not contain any /// or . /// - public bool ContainsGenericParameter { - get { return false; } - } + public bool ContainsGenericParameter => false; /// - public ModuleDef Module { - get { return module; } - } + public ModuleDef Module => module; /// /// From column TypeRef.ResolutionScope @@ -162,16 +129,14 @@ void InitializeResolutionScope() { } /// Called to initialize - protected virtual IResolutionScope GetResolutionScope_NoLock() { - return null; - } + protected virtual IResolutionScope GetResolutionScope_NoLock() => null; /// /// From column TypeRef.Name /// public UTF8String Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// Name protected UTF8String name; @@ -180,8 +145,8 @@ public UTF8String Name { /// From column TypeRef.Namespace /// public UTF8String Namespace { - get { return @namespace; } - set { @namespace = value; } + get => @namespace; + set => @namespace = value; } /// Name protected UTF8String @namespace; @@ -191,7 +156,7 @@ public UTF8String Namespace { /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -199,106 +164,77 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + /// + public int HasCustomDebugInformationTag => 2; + + /// + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// /// true if it's nested within another /// - public bool IsNested { - get { return DeclaringType != null; } - } + public bool IsNested => DeclaringType is not null; /// public bool IsValueType { get { var td = Resolve(); - return td != null && td.IsValueType; + return td is not null && td.IsValueType; } } /// - public bool IsPrimitive { - get { return this.IsPrimitive(); } - } + public bool IsPrimitive => this.IsPrimitive(); /// /// Gets the declaring type, if any /// - public TypeRef DeclaringType { - get { return ResolutionScope as TypeRef; } - } + public TypeRef DeclaringType => ResolutionScope as TypeRef; /// - ITypeDefOrRef IMemberRef.DeclaringType { - get { return DeclaringType; } - } - - bool IIsTypeOrMethod.IsType { - get { return true; } - } - - bool IIsTypeOrMethod.IsMethod { - get { return false; } - } - - bool IMemberRef.IsField { - get { return false; } - } - - bool IMemberRef.IsTypeSpec { - get { return false; } - } - - bool IMemberRef.IsTypeRef { - get { return true; } - } - - bool IMemberRef.IsTypeDef { - get { return false; } - } - - bool IMemberRef.IsMethodSpec { - get { return false; } - } - - bool IMemberRef.IsMethodDef { - get { return false; } - } - - bool IMemberRef.IsMemberRef { - get { return false; } - } - - bool IMemberRef.IsFieldDef { - get { return false; } - } - - bool IMemberRef.IsPropertyDef { - get { return false; } - } - - bool IMemberRef.IsEventDef { - get { return false; } - } - - bool IMemberRef.IsGenericParam { - get { return false; } - } + ITypeDefOrRef IMemberRef.DeclaringType => DeclaringType; + + bool IIsTypeOrMethod.IsType => true; + bool IIsTypeOrMethod.IsMethod => false; + bool IMemberRef.IsField => false; + bool IMemberRef.IsTypeSpec => false; + bool IMemberRef.IsTypeRef => true; + bool IMemberRef.IsTypeDef => false; + bool IMemberRef.IsMethodSpec => false; + bool IMemberRef.IsMethodDef => false; + bool IMemberRef.IsMemberRef => false; + bool IMemberRef.IsFieldDef => false; + bool IMemberRef.IsPropertyDef => false; + bool IMemberRef.IsEventDef => false; + bool IMemberRef.IsGenericParam => false; /// /// Resolves the type /// /// A instance or null if it couldn't be resolved - public TypeDef Resolve() { - return Resolve(null); - } + public TypeDef Resolve() => Resolve(null); /// /// Resolves the type @@ -306,9 +242,9 @@ public TypeDef Resolve() { /// The module that needs to resolve the type or null /// A instance or null if it couldn't be resolved public TypeDef Resolve(ModuleDef sourceModule) { - if (module == null) + if (module is null) return null; - return module.Context.Resolver.Resolve(this, sourceModule); + return module.Context.Resolver.Resolve(this, sourceModule ?? module); } /// @@ -316,9 +252,7 @@ public TypeDef Resolve(ModuleDef sourceModule) { /// /// A instance /// If the type couldn't be resolved - public TypeDef ResolveThrow() { - return ResolveThrow(null); - } + public TypeDef ResolveThrow() => ResolveThrow(null); /// /// Resolves the type @@ -328,9 +262,9 @@ public TypeDef ResolveThrow() { /// If the type couldn't be resolved public TypeDef ResolveThrow(ModuleDef sourceModule) { var type = Resolve(sourceModule); - if (type != null) + if (type is not null) return type; - throw new TypeResolveException(string.Format("Could not resolve type: {0} ({1})", this, DefinitionAssembly)); + throw new TypeResolveException($"Could not resolve type: {this} ({DefinitionAssembly})"); } /// @@ -339,11 +273,11 @@ public TypeDef ResolveThrow(ModuleDef sourceModule) { /// Input /// The non-nested or null internal static TypeRef GetNonNestedTypeRef(TypeRef typeRef) { - if (typeRef == null) + if (typeRef is null) return null; for (int i = 0; i < 1000; i++) { var next = typeRef.ResolutionScope as TypeRef; - if (next == null) + if (next is null) return typeRef; typeRef = next; } @@ -351,9 +285,7 @@ internal static TypeRef GetNonNestedTypeRef(TypeRef typeRef) { } /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } /// @@ -390,7 +322,7 @@ public TypeRefUser(ModuleDef module, UTF8String @namespace, UTF8String name) public TypeRefUser(ModuleDef module, UTF8String @namespace, UTF8String name, IResolutionScope resolutionScope) { this.module = module; this.resolutionScope = resolutionScope; - this.resolutionScope_isInitialized = true; + resolutionScope_isInitialized = true; this.name = name; this.@namespace = @namespace; } @@ -407,22 +339,25 @@ sealed class TypeRefMD : TypeRef, IMDTokenProviderMD { readonly uint resolutionScopeCodedToken; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// - protected override IResolutionScope GetResolutionScope_NoLock() { - return readerModule.ResolveResolutionScope(resolutionScopeCodedToken); - } + protected override IResolutionScope GetResolutionScope_NoLock() => readerModule.ResolveResolutionScope(resolutionScopeCodedToken); /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.TypeRef, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.TypeRef, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + /// /// Constructor /// @@ -432,20 +367,20 @@ protected override void InitializeCustomAttributes() { /// If is invalid public TypeRefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.TypeRefTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("TypeRef rid {0} does not exist", rid)); + throw new BadImageFormatException($"TypeRef rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; - this.module = readerModule; - uint resolutionScope, name; - uint @namespace = readerModule.TablesStream.ReadTypeRefRow(origRid, out resolutionScope, out name); - this.name = readerModule.StringsStream.ReadNoNull(name); - this.@namespace = readerModule.StringsStream.ReadNoNull(@namespace); - this.resolutionScopeCodedToken = resolutionScope; + module = readerModule; + bool b = readerModule.TablesStream.TryReadTypeRefRow(origRid, out var row); + Debug.Assert(b); + name = readerModule.StringsStream.ReadNoNull(row.Name); + @namespace = readerModule.StringsStream.ReadNoNull(row.Namespace); + resolutionScopeCodedToken = row.ResolutionScope; } } } diff --git a/src/DotNet/TypeSig.cs b/src/DotNet/TypeSig.cs index 55f4912d0..2132725f5 100644 --- a/src/DotNet/TypeSig.cs +++ b/src/DotNet/TypeSig.cs @@ -1,15 +1,8 @@ // dnlib: See LICENSE.txt for more info using System; -using System.Collections.Generic; +using System.Collections.Generic; using dnlib.DotNet.MD; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif /* All TypeSig classes: @@ -60,31 +53,25 @@ public abstract class TypeSig : IType { public abstract ElementType ElementType { get; } /// - public MDToken MDToken { - get { return new MDToken(Table.TypeSpec, rid); } - } + public MDToken MDToken => new MDToken(Table.TypeSpec, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - bool IIsTypeOrMethod.IsMethod { - get { return false; } - } + bool IIsTypeOrMethod.IsMethod => false; /// - bool IIsTypeOrMethod.IsType { - get { return true; } - } + bool IIsTypeOrMethod.IsType => true; /// int IGenericParameterProvider.NumberOfGenericParameters { get { var type = this.RemovePinnedAndModifiers() as GenericInstSig; - return type == null ? 0 : type.GenericArguments.Count; + return type is null ? 0 : type.GenericArguments.Count; } } @@ -92,12 +79,12 @@ int IGenericParameterProvider.NumberOfGenericParameters { public bool IsValueType { get { var t = this.RemovePinnedAndModifiers(); - if (t == null) + if (t is null) return false; if (t.ElementType == ElementType.GenericInst) { var gis = (GenericInstSig)t; t = gis.GenericType; - if (t == null) + if (t is null) return false; } return t.ElementType.IsValueType(); @@ -105,230 +92,160 @@ public bool IsValueType { } /// - public bool IsPrimitive { - get { return ElementType.IsPrimitive(); } - } + public bool IsPrimitive => ElementType.IsPrimitive(); /// - public string TypeName { - get { return FullNameCreator.Name(this, false); } - } + public string TypeName => FullNameFactory.Name(this, false, null); /// UTF8String IFullName.Name { - get { return new UTF8String(FullNameCreator.Name(this, false)); } - set { throw new NotSupportedException(); } + get => new UTF8String(FullNameFactory.Name(this, false, null)); + set => throw new NotSupportedException(); } /// - public string ReflectionName { - get { return FullNameCreator.Name(this, true); } - } + public string ReflectionName => FullNameFactory.Name(this, true, null); /// - public string Namespace { - get { return FullNameCreator.Namespace(this, false); } - } + public string Namespace => FullNameFactory.Namespace(this, false, null); /// - public string ReflectionNamespace { - get { return FullNameCreator.Namespace(this, true); } - } + public string ReflectionNamespace => FullNameFactory.Namespace(this, true, null); /// - public string FullName { - get { return FullNameCreator.FullName(this, false); } - } + public string FullName => FullNameFactory.FullName(this, false, null, null, null, null); /// - public string ReflectionFullName { - get { return FullNameCreator.FullName(this, true); } - } + public string ReflectionFullName => FullNameFactory.FullName(this, true, null, null, null, null); /// - public string AssemblyQualifiedName { - get { return FullNameCreator.AssemblyQualifiedName(this); } - } + public string AssemblyQualifiedName => FullNameFactory.AssemblyQualifiedName(this, null, null); /// - public IAssembly DefinitionAssembly { - get { return FullNameCreator.DefinitionAssembly(this); } - } + public IAssembly DefinitionAssembly => FullNameFactory.DefinitionAssembly(this); /// - public IScope Scope { - get { return FullNameCreator.Scope(this); } - } + public IScope Scope => FullNameFactory.Scope(this); /// - public ITypeDefOrRef ScopeType { - get { return FullNameCreator.ScopeType(this); } - } + public ITypeDefOrRef ScopeType => FullNameFactory.ScopeType(this); /// - public ModuleDef Module { - get { return FullNameCreator.OwnerModule(this); } - } + public ModuleDef Module => FullNameFactory.OwnerModule(this); /// /// true if it's a /// - public bool IsTypeDefOrRef { - get { return this is TypeDefOrRefSig; } - } + public bool IsTypeDefOrRef => this is TypeDefOrRefSig; /// /// true if it's a /// - public bool IsCorLibType { - get { return this is CorLibTypeSig; } - } + public bool IsCorLibType => this is CorLibTypeSig; /// /// true if it's a /// - public bool IsClassSig { - get { return this is ClassSig; } - } + public bool IsClassSig => this is ClassSig; /// /// true if it's a /// - public bool IsValueTypeSig { - get { return this is ValueTypeSig; } - } + public bool IsValueTypeSig => this is ValueTypeSig; /// /// true if it's a /// - public bool IsGenericParameter { - get { return this is GenericSig; } - } + public bool IsGenericParameter => this is GenericSig; /// /// true if it's a /// - public bool IsGenericTypeParameter { - get { return this is GenericVar; } - } + public bool IsGenericTypeParameter => this is GenericVar; /// /// true if it's a /// - public bool IsGenericMethodParameter { - get { return this is GenericMVar; } - } + public bool IsGenericMethodParameter => this is GenericMVar; /// /// true if it's a /// - public bool IsSentinel { - get { return this is SentinelSig; } - } + public bool IsSentinel => this is SentinelSig; /// /// true if it's a /// - public bool IsFunctionPointer { - get { return this is FnPtrSig; } - } + public bool IsFunctionPointer => this is FnPtrSig; /// /// true if it's a /// - public bool IsGenericInstanceType { - get { return this is GenericInstSig; } - } + public bool IsGenericInstanceType => this is GenericInstSig; /// /// true if it's a /// - public bool IsPointer { - get { return this is PtrSig; } - } + public bool IsPointer => this is PtrSig; /// /// true if it's a /// - public bool IsByRef { - get { return this is ByRefSig; } - } + public bool IsByRef => this is ByRefSig; /// /// true if it's a or a /// - public bool IsSingleOrMultiDimensionalArray { - get { return this is ArraySigBase; } - } + public bool IsSingleOrMultiDimensionalArray => this is ArraySigBase; /// /// true if it's a /// - public bool IsArray { - get { return this is ArraySig; } - } + public bool IsArray => this is ArraySig; /// /// true if it's a /// - public bool IsSZArray { - get { return this is SZArraySig; } - } + public bool IsSZArray => this is SZArraySig; /// /// true if it's a /// - public bool IsModifier { - get { return this is ModifierSig; } - } + public bool IsModifier => this is ModifierSig; /// /// true if it's a /// - public bool IsRequiredModifier { - get { return this is CModReqdSig; } - } + public bool IsRequiredModifier => this is CModReqdSig; /// /// true if it's a /// - public bool IsOptionalModifier { - get { return this is CModOptSig; } - } + public bool IsOptionalModifier => this is CModOptSig; /// /// true if it's a /// - public bool IsPinned { - get { return this is PinnedSig; } - } + public bool IsPinned => this is PinnedSig; /// /// true if it's a /// - public bool IsValueArray { - get { return this is ValueArraySig; } - } + public bool IsValueArray => this is ValueArraySig; /// /// true if it's a /// - public bool IsModuleSig { - get { return this is ModuleSig; } - } + public bool IsModuleSig => this is ModuleSig; /// /// true if this contains a or a /// . /// - public bool ContainsGenericParameter { - get { return TypeHelper.ContainsGenericParameter(this); } - } + public bool ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); /// - public override string ToString() { - return FullName; - } + public override string ToString() => FullName; } public static partial class Extensions { @@ -338,11 +255,11 @@ public static partial class Extensions { /// A instance /// Input after all modifiers public static TypeSig RemoveModifiers(this TypeSig a) { - if (a == null) + if (a is null) return null; while (true) { var modifier = a as ModifierSig; - if (modifier == null) + if (modifier is null) return a; a = a.Next; } @@ -355,7 +272,7 @@ public static TypeSig RemoveModifiers(this TypeSig a) { /// Input after pinned signature public static TypeSig RemovePinned(this TypeSig a) { var pinned = a as PinnedSig; - if (pinned == null) + if (pinned is null) return a; return pinned.Next; } @@ -378,9 +295,7 @@ public static TypeSig RemovePinnedAndModifiers(this TypeSig a) { /// The type /// A or null if it's not a /// - public static TypeDefOrRefSig ToTypeDefOrRefSig(this TypeSig type) { - return type.RemovePinnedAndModifiers() as TypeDefOrRefSig; - } + public static TypeDefOrRefSig ToTypeDefOrRefSig(this TypeSig type) => type.RemovePinnedAndModifiers() as TypeDefOrRefSig; /// /// Returns a @@ -388,9 +303,7 @@ public static TypeDefOrRefSig ToTypeDefOrRefSig(this TypeSig type) { /// The type /// A or null if it's not a /// - public static ClassOrValueTypeSig ToClassOrValueTypeSig(this TypeSig type) { - return type.RemovePinnedAndModifiers() as ClassOrValueTypeSig; - } + public static ClassOrValueTypeSig ToClassOrValueTypeSig(this TypeSig type) => type.RemovePinnedAndModifiers() as ClassOrValueTypeSig; /// /// Returns a @@ -398,9 +311,7 @@ public static ClassOrValueTypeSig ToClassOrValueTypeSig(this TypeSig type) { /// The type /// A or null if it's not a /// - public static ValueTypeSig ToValueTypeSig(this TypeSig type) { - return type.RemovePinnedAndModifiers() as ValueTypeSig; - } + public static ValueTypeSig ToValueTypeSig(this TypeSig type) => type.RemovePinnedAndModifiers() as ValueTypeSig; /// /// Returns a @@ -408,9 +319,7 @@ public static ValueTypeSig ToValueTypeSig(this TypeSig type) { /// The type /// A or null if it's not a /// - public static ClassSig ToClassSig(this TypeSig type) { - return type.RemovePinnedAndModifiers() as ClassSig; - } + public static ClassSig ToClassSig(this TypeSig type) => type.RemovePinnedAndModifiers() as ClassSig; /// /// Returns a @@ -418,9 +327,7 @@ public static ClassSig ToClassSig(this TypeSig type) { /// The type /// A or null if it's not a /// - public static GenericSig ToGenericSig(this TypeSig type) { - return type.RemovePinnedAndModifiers() as GenericSig; - } + public static GenericSig ToGenericSig(this TypeSig type) => type.RemovePinnedAndModifiers() as GenericSig; /// /// Returns a @@ -428,9 +335,7 @@ public static GenericSig ToGenericSig(this TypeSig type) { /// The type /// A or null if it's not a /// - public static GenericVar ToGenericVar(this TypeSig type) { - return type.RemovePinnedAndModifiers() as GenericVar; - } + public static GenericVar ToGenericVar(this TypeSig type) => type.RemovePinnedAndModifiers() as GenericVar; /// /// Returns a @@ -438,9 +343,7 @@ public static GenericVar ToGenericVar(this TypeSig type) { /// The type /// A or null if it's not a /// - public static GenericMVar ToGenericMVar(this TypeSig type) { - return type.RemovePinnedAndModifiers() as GenericMVar; - } + public static GenericMVar ToGenericMVar(this TypeSig type) => type.RemovePinnedAndModifiers() as GenericMVar; /// /// Returns a @@ -448,9 +351,7 @@ public static GenericMVar ToGenericMVar(this TypeSig type) { /// The type /// A or null if it's not a /// - public static GenericInstSig ToGenericInstSig(this TypeSig type) { - return type.RemovePinnedAndModifiers() as GenericInstSig; - } + public static GenericInstSig ToGenericInstSig(this TypeSig type) => type.RemovePinnedAndModifiers() as GenericInstSig; /// /// Returns a @@ -458,9 +359,7 @@ public static GenericInstSig ToGenericInstSig(this TypeSig type) { /// The type /// A or null if it's not a /// - public static PtrSig ToPtrSig(this TypeSig type) { - return type.RemovePinnedAndModifiers() as PtrSig; - } + public static PtrSig ToPtrSig(this TypeSig type) => type.RemovePinnedAndModifiers() as PtrSig; /// /// Returns a @@ -468,9 +367,7 @@ public static PtrSig ToPtrSig(this TypeSig type) { /// The type /// A or null if it's not a /// - public static ByRefSig ToByRefSig(this TypeSig type) { - return type.RemovePinnedAndModifiers() as ByRefSig; - } + public static ByRefSig ToByRefSig(this TypeSig type) => type.RemovePinnedAndModifiers() as ByRefSig; /// /// Returns a @@ -478,9 +375,7 @@ public static ByRefSig ToByRefSig(this TypeSig type) { /// The type /// A or null if it's not a /// - public static ArraySig ToArraySig(this TypeSig type) { - return type.RemovePinnedAndModifiers() as ArraySig; - } + public static ArraySig ToArraySig(this TypeSig type) => type.RemovePinnedAndModifiers() as ArraySig; /// /// Returns a @@ -488,18 +383,14 @@ public static ArraySig ToArraySig(this TypeSig type) { /// The type /// A or null if it's not a /// - public static SZArraySig ToSZArraySig(this TypeSig type) { - return type.RemovePinnedAndModifiers() as SZArraySig; - } + public static SZArraySig ToSZArraySig(this TypeSig type) => type.RemovePinnedAndModifiers() as SZArraySig; /// /// Gets the next field or null /// /// this /// - public static TypeSig GetNext(this TypeSig self) { - return self == null ? null : self.Next; - } + public static TypeSig GetNext(this TypeSig self) => self?.Next; /// /// Gets the value or false if @@ -507,9 +398,7 @@ public static TypeSig GetNext(this TypeSig self) { /// /// this /// - public static bool GetIsValueType(this TypeSig self) { - return self == null ? false : self.IsValueType; - } + public static bool GetIsValueType(this TypeSig self) => self is null ? false : self.IsValueType; /// /// Gets the value or false if @@ -517,55 +406,49 @@ public static bool GetIsValueType(this TypeSig self) { /// /// this /// - public static bool GetIsPrimitive(this TypeSig self) { - return self == null ? false : self.IsPrimitive; - } + public static bool GetIsPrimitive(this TypeSig self) => self is null ? false : self.IsPrimitive; /// /// Gets the element type /// /// this /// The element type - public static ElementType GetElementType(this TypeSig a) { - return a == null ? ElementType.End : a.ElementType; - } + public static ElementType GetElementType(this TypeSig a) => a is null ? ElementType.End : a.ElementType; /// /// Gets the full name of the type /// /// this /// Full name of the type - public static string GetFullName(this TypeSig a) { - return a == null ? string.Empty : a.FullName; - } + public static string GetFullName(this TypeSig a) => a is null ? string.Empty : a.FullName; /// /// Gets the name of the type /// /// this /// Name of the type - public static string GetName(this TypeSig a) { - return a == null ? string.Empty : a.TypeName; - } + public static string GetName(this TypeSig a) => a is null ? string.Empty : a.TypeName; /// /// Gets the namespace of the type /// /// this /// Namespace of the type - public static string GetNamespace(this TypeSig a) { - return a == null ? string.Empty : a.Namespace; - } + public static string GetNamespace(this TypeSig a) => a is null ? string.Empty : a.Namespace; + + /// + /// Returns the if it is a . + /// + /// this + /// A or null if none found + public static ITypeDefOrRef TryGetTypeDefOrRef(this TypeSig a) => (a.RemovePinnedAndModifiers() as TypeDefOrRefSig)?.TypeDefOrRef; /// /// Returns the if it is a . /// /// this /// A or null if none found - public static TypeRef TryGetTypeRef(this TypeSig a) { - var tdr = a.RemovePinnedAndModifiers() as TypeDefOrRefSig; - return tdr == null ? null : tdr.TypeRef; - } + public static TypeRef TryGetTypeRef(this TypeSig a) => (a.RemovePinnedAndModifiers() as TypeDefOrRefSig)?.TypeRef; /// /// Returns the if it is a . @@ -573,20 +456,14 @@ public static TypeRef TryGetTypeRef(this TypeSig a) { /// /// this /// A or null if none found - public static TypeDef TryGetTypeDef(this TypeSig a) { - var tdr = a.RemovePinnedAndModifiers() as TypeDefOrRefSig; - return tdr == null ? null : tdr.TypeDef; - } + public static TypeDef TryGetTypeDef(this TypeSig a) => (a.RemovePinnedAndModifiers() as TypeDefOrRefSig)?.TypeDef; /// /// Returns the if it is a . /// /// this /// A or null if none found - public static TypeSpec TryGetTypeSpec(this TypeSig a) { - var tdr = a.RemovePinnedAndModifiers() as TypeDefOrRefSig; - return tdr == null ? null : tdr.TypeSpec; - } + public static TypeSpec TryGetTypeSpec(this TypeSig a) => (a.RemovePinnedAndModifiers() as TypeDefOrRefSig)?.TypeSpec; } /// @@ -596,9 +473,7 @@ public static TypeSpec TryGetTypeSpec(this TypeSig a) { /// public abstract class LeafSig : TypeSig { /// - public sealed override TypeSig Next { - get { return null; } - } + public sealed override TypeSig Next => null; } /// @@ -610,60 +485,44 @@ public abstract class TypeDefOrRefSig : LeafSig { /// /// Gets the the TypeDefOrRef /// - public ITypeDefOrRef TypeDefOrRef { - get { return typeDefOrRef; } - } + public ITypeDefOrRef TypeDefOrRef => typeDefOrRef; /// /// Returns true if != null /// - public bool IsTypeRef { - get { return TypeRef != null; } - } + public bool IsTypeRef => TypeRef is not null; /// /// Returns true if != null /// - public bool IsTypeDef { - get { return TypeDef != null; } - } + public bool IsTypeDef => TypeDef is not null; /// /// Returns true if != null /// - public bool IsTypeSpec { - get { return TypeSpec != null; } - } + public bool IsTypeSpec => TypeSpec is not null; /// /// Gets the or null if it's not a /// - public TypeRef TypeRef { - get { return typeDefOrRef as TypeRef; } - } + public TypeRef TypeRef => typeDefOrRef as TypeRef; /// /// Gets the or null if it's not a /// - public TypeDef TypeDef { - get { return typeDefOrRef as TypeDef; } - } + public TypeDef TypeDef => typeDefOrRef as TypeDef; /// /// Gets the or null if it's not a /// - public TypeSpec TypeSpec { - get { return typeDefOrRef as TypeSpec; } - } + public TypeSpec TypeSpec => typeDefOrRef as TypeSpec; /// /// Constructor /// /// A , or /// a - protected TypeDefOrRefSig(ITypeDefOrRef typeDefOrRef) { - this.typeDefOrRef = typeDefOrRef; - } + protected TypeDefOrRefSig(ITypeDefOrRef typeDefOrRef) => this.typeDefOrRef = typeDefOrRef; } /// @@ -675,9 +534,7 @@ public sealed class CorLibTypeSig : TypeDefOrRefSig { /// /// Gets the element type /// - public override ElementType ElementType { - get { return elementType; } - } + public override ElementType ElementType => elementType; /// /// Constructor @@ -711,9 +568,7 @@ protected ClassOrValueTypeSig(ITypeDefOrRef typeDefOrRef) /// public sealed class ValueTypeSig : ClassOrValueTypeSig { /// - public override ElementType ElementType { - get { return ElementType.ValueType; } - } + public override ElementType ElementType => ElementType.ValueType; /// /// Constructor @@ -729,9 +584,7 @@ public ValueTypeSig(ITypeDefOrRef typeDefOrRef) /// public sealed class ClassSig : ClassOrValueTypeSig { /// - public override ElementType ElementType { - get { return ElementType.Class; } - } + public override ElementType ElementType => ElementType.Class; /// /// Constructor @@ -753,48 +606,36 @@ public abstract class GenericSig : LeafSig { /// /// true if it has an owner or /// - public bool HasOwner { - get { return genericParamProvider != null; } - } + public bool HasOwner => genericParamProvider is not null; /// /// true if it has an owner ( is /// not null) /// - public bool HasOwnerType { - get { return OwnerType != null; } - } + public bool HasOwnerType => OwnerType is not null; /// /// true if it has an owner ( is /// not null) /// - public bool HasOwnerMethod { - get { return OwnerMethod != null; } - } + public bool HasOwnerMethod => OwnerMethod is not null; /// /// Gets the owner type or null if the owner is a or if it /// has no owner. /// - public TypeDef OwnerType { - get { return genericParamProvider as TypeDef; } - } + public TypeDef OwnerType => genericParamProvider as TypeDef; /// /// Gets the owner method or null if the owner is a or if it /// has no owner. /// - public MethodDef OwnerMethod { - get { return genericParamProvider as MethodDef; } - } + public MethodDef OwnerMethod => genericParamProvider as MethodDef; /// /// Gets the generic param number /// - public uint Number { - get { return number; } - } + public uint Number => number; /// /// Gets the corresponding or null if none exists. @@ -802,9 +643,12 @@ public uint Number { public GenericParam GenericParam { get { var gpp = genericParamProvider; - if (gpp == null) + if (gpp is null) return null; - foreach (var gp in gpp.GenericParameters.GetSafeEnumerable()) { + var gps = gpp.GenericParameters; + int count = gps.Count; + for (int i = 0; i < count; i++) { + var gp = gps[i]; if (gp.Number == number) return gp; } @@ -836,16 +680,12 @@ protected GenericSig(bool isTypeVar, uint number, ITypeOrMethodDef genericParamP /// /// Returns true if it's a MVar element type /// - public bool IsMethodVar { - get { return !isTypeVar; } - } + public bool IsMethodVar => !isTypeVar; /// /// Returns true if it's a Var element type /// - public bool IsTypeVar { - get { return isTypeVar; } - } + public bool IsTypeVar => isTypeVar; } /// @@ -853,9 +693,7 @@ public bool IsTypeVar { /// public sealed class GenericVar : GenericSig { /// - public override ElementType ElementType { - get { return ElementType.Var; } - } + public override ElementType ElementType => ElementType.Var; /// public GenericVar(uint number) @@ -891,9 +729,7 @@ public GenericVar(int number, TypeDef genericParamProvider) /// public sealed class GenericMVar : GenericSig { /// - public override ElementType ElementType { - get { return ElementType.MVar; } - } + public override ElementType ElementType => ElementType.MVar; /// public GenericMVar(uint number) @@ -929,9 +765,7 @@ public GenericMVar(int number, MethodDef genericParamProvider) /// public sealed class SentinelSig : LeafSig { /// - public override ElementType ElementType { - get { return ElementType.Sentinel; } - } + public override ElementType ElementType => ElementType.Sentinel; } /// @@ -941,31 +775,23 @@ public sealed class FnPtrSig : LeafSig { readonly CallingConventionSig signature; /// - public override ElementType ElementType { - get { return ElementType.FnPtr; } - } + public override ElementType ElementType => ElementType.FnPtr; /// /// Gets the signature /// - public CallingConventionSig Signature { - get { return signature; } - } + public CallingConventionSig Signature => signature; /// /// Gets the /// - public MethodSig MethodSig { - get { return signature as MethodSig; } - } + public MethodSig MethodSig => signature as MethodSig; /// /// Constructor /// /// The method signature - public FnPtrSig(CallingConventionSig signature) { - this.signature = signature; - } + public FnPtrSig(CallingConventionSig signature) => this.signature = signature; } /// @@ -973,34 +799,28 @@ public FnPtrSig(CallingConventionSig signature) { /// public sealed class GenericInstSig : LeafSig { ClassOrValueTypeSig genericType; - readonly ThreadSafe.IList genericArgs; + readonly IList genericArgs; /// - public override ElementType ElementType { - get { return ElementType.GenericInst; } - } + public override ElementType ElementType => ElementType.GenericInst; /// /// Gets the generic type /// public ClassOrValueTypeSig GenericType { - get { return genericType; } - set { genericType = value; } + get => genericType; + set => genericType = value; } /// /// Gets the generic arguments (it's never null) /// - public ThreadSafe.IList GenericArguments { - get { return genericArgs; } - } + public IList GenericArguments => genericArgs; /// /// Default constructor /// - public GenericInstSig() { - this.genericArgs = ThreadSafeListCreator.Create(); - } + public GenericInstSig() => genericArgs = new List(); /// /// Constructor @@ -1008,7 +828,7 @@ public GenericInstSig() { /// The generic type public GenericInstSig(ClassOrValueTypeSig genericType) { this.genericType = genericType; - this.genericArgs = ThreadSafeListCreator.Create(); + genericArgs = new List(); } /// @@ -1018,7 +838,7 @@ public GenericInstSig(ClassOrValueTypeSig genericType) { /// Number of generic arguments public GenericInstSig(ClassOrValueTypeSig genericType, uint genArgCount) { this.genericType = genericType; - this.genericArgs = ThreadSafeListCreator.Create((int)genArgCount); + genericArgs = new List((int)genArgCount); } /// @@ -1037,7 +857,7 @@ public GenericInstSig(ClassOrValueTypeSig genericType, int genArgCount) /// Generic argument #1 public GenericInstSig(ClassOrValueTypeSig genericType, TypeSig genArg1) { this.genericType = genericType; - this.genericArgs = ThreadSafeListCreator.Create(genArg1); + genericArgs = new List { genArg1 }; } /// @@ -1048,7 +868,7 @@ public GenericInstSig(ClassOrValueTypeSig genericType, TypeSig genArg1) { /// Generic argument #2 public GenericInstSig(ClassOrValueTypeSig genericType, TypeSig genArg1, TypeSig genArg2) { this.genericType = genericType; - this.genericArgs = ThreadSafeListCreator.Create(genArg1, genArg2); + genericArgs = new List { genArg1, genArg2 }; } /// @@ -1060,7 +880,7 @@ public GenericInstSig(ClassOrValueTypeSig genericType, TypeSig genArg1, TypeSig /// Generic argument #3 public GenericInstSig(ClassOrValueTypeSig genericType, TypeSig genArg1, TypeSig genArg2, TypeSig genArg3) { this.genericType = genericType; - this.genericArgs = ThreadSafeListCreator.Create(genArg1, genArg2, genArg3); + genericArgs = new List { genArg1, genArg2, genArg3 }; } /// @@ -1070,7 +890,7 @@ public GenericInstSig(ClassOrValueTypeSig genericType, TypeSig genArg1, TypeSig /// Generic arguments public GenericInstSig(ClassOrValueTypeSig genericType, params TypeSig[] genArgs) { this.genericType = genericType; - this.genericArgs = ThreadSafeListCreator.Create(genArgs); + genericArgs = new List(genArgs); } /// @@ -1080,7 +900,7 @@ public GenericInstSig(ClassOrValueTypeSig genericType, params TypeSig[] genArgs) /// Generic arguments public GenericInstSig(ClassOrValueTypeSig genericType, IList genArgs) { this.genericType = genericType; - this.genericArgs = ThreadSafeListCreator.Create(genArgs); + genericArgs = new List(genArgs); } } @@ -1091,17 +911,13 @@ public abstract class NonLeafSig : TypeSig { readonly TypeSig nextSig; /// - public sealed override TypeSig Next { - get { return nextSig; } - } + public sealed override TypeSig Next => nextSig; /// /// Constructor /// /// Next sig - protected NonLeafSig(TypeSig nextSig) { - this.nextSig = nextSig; - } + protected NonLeafSig(TypeSig nextSig) => this.nextSig = nextSig; } /// @@ -1109,9 +925,7 @@ protected NonLeafSig(TypeSig nextSig) { /// public sealed class PtrSig : NonLeafSig { /// - public override ElementType ElementType { - get { return ElementType.Ptr; } - } + public override ElementType ElementType => ElementType.Ptr; /// /// Constructor @@ -1127,9 +941,7 @@ public PtrSig(TypeSig nextSig) /// public sealed class ByRefSig : NonLeafSig { /// - public override ElementType ElementType { - get { return ElementType.ByRef; } - } + public override ElementType ElementType => ElementType.ByRef; /// /// Constructor @@ -1157,18 +969,14 @@ protected ArraySigBase(TypeSig arrayType) /// and false if it's a single-dimensional array (i.e., ) /// /// - public bool IsMultiDimensional { - get { return ElementType == ElementType.Array; } - } + public bool IsMultiDimensional => ElementType == ElementType.Array; /// /// true if it's a single-dimensional array (i.e., ), /// and false if it's a multi-dimensional array (i.e., ) /// /// - public bool IsSingleDimensional { - get { return ElementType == ElementType.SZArray; } - } + public bool IsSingleDimensional => ElementType == ElementType.SZArray; /// /// Gets/sets the rank (number of dimensions). This can only be set if @@ -1181,14 +989,14 @@ public bool IsSingleDimensional { /// list that is re-created every time this method is called. /// /// A list of sizes - public abstract ThreadSafe.IList GetSizes(); + public abstract IList GetSizes(); /// /// Gets all lower bounds. If it's a , then it will be an empty /// temporary list that is re-created every time this method is called. /// /// A list of lower bounds - public abstract ThreadSafe.IList GetLowerBounds(); + public abstract IList GetLowerBounds(); } /// @@ -1197,35 +1005,29 @@ public bool IsSingleDimensional { /// public sealed class ArraySig : ArraySigBase { uint rank; - readonly ThreadSafe.IList sizes; - readonly ThreadSafe.IList lowerBounds; + readonly IList sizes; + readonly IList lowerBounds; /// - public override ElementType ElementType { - get { return ElementType.Array; } - } + public override ElementType ElementType => ElementType.Array; /// /// Gets/sets the rank (max value is 0x1FFFFFFF) /// public override uint Rank { - get { return rank; } - set { rank = value; } + get => rank; + set => rank = value; } /// /// Gets all sizes (max elements is 0x1FFFFFFF) /// - public ThreadSafe.IList Sizes { - get { return sizes; } - } + public IList Sizes => sizes; /// /// Gets all lower bounds (max elements is 0x1FFFFFFF) /// - public ThreadSafe.IList LowerBounds { - get { return lowerBounds; } - } + public IList LowerBounds => lowerBounds; /// /// Constructor @@ -1233,8 +1035,8 @@ public ThreadSafe.IList LowerBounds { /// Array type public ArraySig(TypeSig arrayType) : base(arrayType) { - this.sizes = ThreadSafeListCreator.Create(); - this.lowerBounds = ThreadSafeListCreator.Create(); + sizes = new List(); + lowerBounds = new List(); } /// @@ -1245,8 +1047,8 @@ public ArraySig(TypeSig arrayType) public ArraySig(TypeSig arrayType, uint rank) : base(arrayType) { this.rank = rank; - this.sizes = ThreadSafeListCreator.Create(); - this.lowerBounds = ThreadSafeListCreator.Create(); + sizes = new List(); + lowerBounds = new List(); } /// @@ -1268,8 +1070,8 @@ public ArraySig(TypeSig arrayType, int rank) public ArraySig(TypeSig arrayType, uint rank, IEnumerable sizes, IEnumerable lowerBounds) : base(arrayType) { this.rank = rank; - this.sizes = ThreadSafeListCreator.Create(sizes); - this.lowerBounds = ThreadSafeListCreator.Create(lowerBounds); + this.sizes = new List(sizes); + this.lowerBounds = new List(lowerBounds); } /// @@ -1293,19 +1095,15 @@ public ArraySig(TypeSig arrayType, int rank, IEnumerable sizes, IEnumerabl internal ArraySig(TypeSig arrayType, uint rank, IList sizes, IList lowerBounds) : base(arrayType) { this.rank = rank; - this.sizes = ThreadSafeListCreator.MakeThreadSafe(sizes); - this.lowerBounds = ThreadSafeListCreator.MakeThreadSafe(lowerBounds); + this.sizes = sizes; + this.lowerBounds = lowerBounds; } /// - public override ThreadSafe.IList GetSizes() { - return sizes; - } + public override IList GetSizes() => sizes; /// - public override ThreadSafe.IList GetLowerBounds() { - return lowerBounds; - } + public override IList GetLowerBounds() => lowerBounds; } /// @@ -1314,14 +1112,12 @@ public override ThreadSafe.IList GetLowerBounds() { /// public sealed class SZArraySig : ArraySigBase { /// - public override ElementType ElementType { - get { return ElementType.SZArray; } - } + public override ElementType ElementType => ElementType.SZArray; /// public override uint Rank { - get { return 1; } - set { throw new NotSupportedException(); } + get => 1; + set => throw new NotSupportedException(); } /// @@ -1333,14 +1129,10 @@ public SZArraySig(TypeSig nextSig) } /// - public override ThreadSafe.IList GetSizes() { - return ThreadSafeListCreator.Create(); - } + public override IList GetSizes() => Array2.Empty(); /// - public override ThreadSafe.IList GetLowerBounds() { - return ThreadSafeListCreator.Create(); - } + public override IList GetLowerBounds() => Array2.Empty(); } /// @@ -1352,9 +1144,7 @@ public abstract class ModifierSig : NonLeafSig { /// /// Returns the modifier type /// - public ITypeDefOrRef Modifier { - get { return modifier; } - } + public ITypeDefOrRef Modifier => modifier; /// /// Constructor @@ -1362,9 +1152,7 @@ public ITypeDefOrRef Modifier { /// Modifier type /// The next element type protected ModifierSig(ITypeDefOrRef modifier, TypeSig nextSig) - : base(nextSig) { - this.modifier = modifier; - } + : base(nextSig) => this.modifier = modifier; } /// @@ -1372,9 +1160,7 @@ protected ModifierSig(ITypeDefOrRef modifier, TypeSig nextSig) /// public sealed class CModReqdSig : ModifierSig { /// - public override ElementType ElementType { - get { return ElementType.CModReqd; } - } + public override ElementType ElementType => ElementType.CModReqd; /// public CModReqdSig(ITypeDefOrRef modifier, TypeSig nextSig) @@ -1387,9 +1173,7 @@ public CModReqdSig(ITypeDefOrRef modifier, TypeSig nextSig) /// public sealed class CModOptSig : ModifierSig { /// - public override ElementType ElementType { - get { return ElementType.CModOpt; } - } + public override ElementType ElementType => ElementType.CModOpt; /// public CModOptSig(ITypeDefOrRef modifier, TypeSig nextSig) @@ -1402,9 +1186,7 @@ public CModOptSig(ITypeDefOrRef modifier, TypeSig nextSig) /// public sealed class PinnedSig : NonLeafSig { /// - public override ElementType ElementType { - get { return ElementType.Pinned; } - } + public override ElementType ElementType => ElementType.Pinned; /// /// Constructor @@ -1422,16 +1204,14 @@ public sealed class ValueArraySig : NonLeafSig { uint size; /// - public override ElementType ElementType { - get { return ElementType.ValueArray; } - } + public override ElementType ElementType => ElementType.ValueArray; /// /// Gets/sets the size /// public uint Size { - get { return size; } - set { size = value; } + get => size; + set => size = value; } /// @@ -1440,9 +1220,7 @@ public uint Size { /// The next element type /// Size of the array public ValueArraySig(TypeSig nextSig, uint size) - : base(nextSig) { - this.size = size; - } + : base(nextSig) => this.size = size; } /// @@ -1452,16 +1230,14 @@ public sealed class ModuleSig : NonLeafSig { uint index; /// - public override ElementType ElementType { - get { return ElementType.Module; } - } + public override ElementType ElementType => ElementType.Module; /// /// Gets/sets the index /// public uint Index { - get { return index; } - set { index = value; } + get => index; + set => index = value; } /// @@ -1470,8 +1246,6 @@ public uint Index { /// Index /// The next element type public ModuleSig(uint index, TypeSig nextSig) - : base(nextSig) { - this.index = index; - } + : base(nextSig) => this.index = index; } } diff --git a/src/DotNet/TypeSpec.cs b/src/DotNet/TypeSpec.cs index 7e6c9635f..e7e82063f 100644 --- a/src/DotNet/TypeSpec.cs +++ b/src/DotNet/TypeSpec.cs @@ -1,15 +1,18 @@ // dnlib: See LICENSE.txt for more info using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; using dnlib.Threading; namespace dnlib.DotNet { /// /// A high-level representation of a row in the TypeSpec table /// - public abstract class TypeSpec : ITypeDefOrRef, IHasCustomAttribute, IMemberRefParent { + public abstract class TypeSpec : ITypeDefOrRef, IHasCustomAttribute, IMemberRefParent, IHasCustomDebugInformation { /// /// The row id in its table /// @@ -20,36 +23,28 @@ public abstract class TypeSpec : ITypeDefOrRef, IHasCustomAttribute, IMemberRefP #endif /// - public MDToken MDToken { - get { return new MDToken(Table.TypeSpec, rid); } - } + public MDToken MDToken => new MDToken(Table.TypeSpec, rid); /// public uint Rid { - get { return rid; } - set { rid = value; } + get => rid; + set => rid = value; } /// - public int TypeDefOrRefTag { - get { return 2; } - } + public int TypeDefOrRefTag => 2; /// - public int HasCustomAttributeTag { - get { return 13; } - } + public int HasCustomAttributeTag => 13; /// - public int MemberRefParentTag { - get { return 4; } - } + public int MemberRefParentTag => 4; /// int IGenericParameterProvider.NumberOfGenericParameters { get { var ts = TypeSig; - return ts == null ? 0 : ((IGenericParameterProvider)ts).NumberOfGenericParameters; + return ts is null ? 0 : ((IGenericParameterProvider)ts).NumberOfGenericParameters; } } @@ -57,11 +52,11 @@ int IGenericParameterProvider.NumberOfGenericParameters { UTF8String IFullName.Name { get { var mr = ScopeType; - return mr == null ? UTF8String.Empty : mr.Name; + return mr is null ? UTF8String.Empty : mr.Name; } set { var mr = ScopeType; - if (mr != null) + if (mr is not null) mr.Name = value; } } @@ -71,12 +66,10 @@ ITypeDefOrRef IMemberRef.DeclaringType { get { var sig = TypeSig.RemovePinnedAndModifiers(); - var gis = sig as GenericInstSig; - if (gis != null) + if (sig is GenericInstSig gis) sig = gis.GenericType; - var tdr = sig as TypeDefOrRefSig; - if (tdr != null) { + if (sig is TypeDefOrRefSig tdr) { if (tdr.IsTypeDef || tdr.IsTypeRef) return tdr.TypeDefOrRef.DeclaringType; return null; // If it's another TypeSpec, just stop. Don't want possible inf recursion. @@ -86,63 +79,25 @@ ITypeDefOrRef IMemberRef.DeclaringType { } } - bool IIsTypeOrMethod.IsType { - get { return true; } - } - - bool IIsTypeOrMethod.IsMethod { - get { return false; } - } - - bool IMemberRef.IsField { - get { return false; } - } - - bool IMemberRef.IsTypeSpec { - get { return true; } - } - - bool IMemberRef.IsTypeRef { - get { return false; } - } - - bool IMemberRef.IsTypeDef { - get { return false; } - } - - bool IMemberRef.IsMethodSpec { - get { return false; } - } - - bool IMemberRef.IsMethodDef { - get { return false; } - } - - bool IMemberRef.IsMemberRef { - get { return false; } - } - - bool IMemberRef.IsFieldDef { - get { return false; } - } - - bool IMemberRef.IsPropertyDef { - get { return false; } - } - - bool IMemberRef.IsEventDef { - get { return false; } - } - - bool IMemberRef.IsGenericParam { - get { return false; } - } + bool IIsTypeOrMethod.IsType => true; + bool IIsTypeOrMethod.IsMethod => false; + bool IMemberRef.IsField => false; + bool IMemberRef.IsTypeSpec => true; + bool IMemberRef.IsTypeRef => false; + bool IMemberRef.IsTypeDef => false; + bool IMemberRef.IsMethodSpec => false; + bool IMemberRef.IsMethodDef => false; + bool IMemberRef.IsMemberRef => false; + bool IMemberRef.IsFieldDef => false; + bool IMemberRef.IsPropertyDef => false; + bool IMemberRef.IsEventDef => false; + bool IMemberRef.IsGenericParam => false; /// public bool IsValueType { get { var sig = TypeSig; - return sig != null && sig.IsValueType; + return sig is not null && sig.IsValueType; } } @@ -150,69 +105,45 @@ public bool IsValueType { public bool IsPrimitive { get { var sig = TypeSig; - return sig != null && sig.IsPrimitive; + return sig is not null && sig.IsPrimitive; } } /// - public string TypeName { - get { return FullNameCreator.Name(this, false); } - } + public string TypeName => FullNameFactory.Name(this, false, null); /// - public string ReflectionName { - get { return FullNameCreator.Name(this, true); } - } + public string ReflectionName => FullNameFactory.Name(this, true, null); /// - string IType.Namespace { - get { return FullNameCreator.Namespace(this, false); } - } + string IType.Namespace => FullNameFactory.Namespace(this, false, null); /// - public string ReflectionNamespace { - get { return FullNameCreator.Namespace(this, true); } - } + public string ReflectionNamespace => FullNameFactory.Namespace(this, true, null); /// - public string FullName { - get { return FullNameCreator.FullName(this, false); } - } + public string FullName => FullNameFactory.FullName(this, false, null, null); /// - public string ReflectionFullName { - get { return FullNameCreator.FullName(this, true); } - } + public string ReflectionFullName => FullNameFactory.FullName(this, true, null, null); /// - public string AssemblyQualifiedName { - get { return FullNameCreator.AssemblyQualifiedName(this); } - } + public string AssemblyQualifiedName => FullNameFactory.AssemblyQualifiedName(this, null, null); /// - public IAssembly DefinitionAssembly { - get { return FullNameCreator.DefinitionAssembly(this); } - } + public IAssembly DefinitionAssembly => FullNameFactory.DefinitionAssembly(this); /// - public IScope Scope { - get { return FullNameCreator.Scope(this); } - } + public IScope Scope => FullNameFactory.Scope(this); /// - public ITypeDefOrRef ScopeType { - get { return FullNameCreator.ScopeType(this); } - } + public ITypeDefOrRef ScopeType => FullNameFactory.ScopeType(this); /// - public bool ContainsGenericParameter { - get { return TypeHelper.ContainsGenericParameter(this); } - } + public bool ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); /// - public ModuleDef Module { - get { return FullNameCreator.OwnerModule(this); } - } + public ModuleDef Module => FullNameFactory.OwnerModule(this); /// /// From column TypeSpec.Signature @@ -282,7 +213,7 @@ protected virtual TypeSig GetTypeSigAndExtraData_NoLock(out byte[] extraData) { /// public CustomAttributeCollection CustomAttributes { get { - if (customAttributes == null) + if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } @@ -290,19 +221,36 @@ public CustomAttributeCollection CustomAttributes { /// protected CustomAttributeCollection customAttributes; /// Initializes - protected virtual void InitializeCustomAttributes() { + protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); - } /// - public bool HasCustomAttributes { - get { return CustomAttributes.Count > 0; } - } + public bool HasCustomAttributes => CustomAttributes.Count > 0; + + + /// + public int HasCustomDebugInformationTag => 13; /// - public override string ToString() { - return FullName; + public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; + + /// + /// Gets all custom debug infos + /// + public IList CustomDebugInfos { + get { + if (customDebugInfos is null) + InitializeCustomDebugInfos(); + return customDebugInfos; + } } + /// + protected IList customDebugInfos; + /// Initializes + protected virtual void InitializeCustomDebugInfos() => + Interlocked.CompareExchange(ref customDebugInfos, new List(), null); + /// + public override string ToString() => FullName; } /// @@ -321,42 +269,49 @@ public TypeSpecUser() { /// A type sig public TypeSpecUser(TypeSig typeSig) { this.typeSig = typeSig; - this.extraData = null; - this.typeSigAndExtraData_isInitialized = true; + extraData = null; + typeSigAndExtraData_isInitialized = true; } } /// /// Created from a row in the TypeSpec table /// - sealed class TypeSpecMD : TypeSpec, IMDTokenProviderMD { + sealed class TypeSpecMD : TypeSpec, IMDTokenProviderMD, IContainsGenericParameter2 { /// The module where this instance is located readonly ModuleDefMD readerModule; - readonly GenericParamContext gpContext; readonly uint origRid; + readonly GenericParamContext gpContext; readonly uint signatureOffset; /// - public uint OrigRid { - get { return origRid; } - } + public uint OrigRid => origRid; /// protected override TypeSig GetTypeSigAndExtraData_NoLock(out byte[] extraData) { var sig = readerModule.ReadTypeSignature(signatureOffset, gpContext, out extraData); - if (sig != null) + if (sig is not null) sig.Rid = origRid; return sig; } /// protected override void InitializeCustomAttributes() { - var list = readerModule.MetaData.GetCustomAttributeRidList(Table.TypeSpec, origRid); - var tmp = new CustomAttributeCollection((int)list.Length, list, (list2, index) => readerModule.ReadCustomAttribute(((RidList)list2)[index])); + var list = readerModule.Metadata.GetCustomAttributeRidList(Table.TypeSpec, origRid); + var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } + /// + protected override void InitializeCustomDebugInfos() { + var list = new List(); + readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), gpContext, list); + Interlocked.CompareExchange(ref customDebugInfos, list, null); + } + + bool IContainsGenericParameter2.ContainsGenericParameter => ContainsGenericParameter; + /// /// Constructor /// @@ -367,16 +322,18 @@ protected override void InitializeCustomAttributes() { /// If is invalid public TypeSpecMD(ModuleDefMD readerModule, uint rid, GenericParamContext gpContext) { #if DEBUG - if (readerModule == null) + if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.TypeSpecTable.IsInvalidRID(rid)) - throw new BadImageFormatException(string.Format("TypeSpec rid {0} does not exist", rid)); + throw new BadImageFormatException($"TypeSpec rid {rid} does not exist"); #endif - this.origRid = rid; + origRid = rid; this.rid = rid; this.readerModule = readerModule; this.gpContext = gpContext; - this.signatureOffset = readerModule.TablesStream.ReadTypeSpecRow2(origRid); + bool b = readerModule.TablesStream.TryReadTypeSpecRow(origRid, out var row); + Debug.Assert(b); + signatureOffset = row.Signature; } } } diff --git a/src/DotNet/UTF8String.cs b/src/DotNet/UTF8String.cs index 0eebccffc..2397ee226 100644 --- a/src/DotNet/UTF8String.cs +++ b/src/DotNet/UTF8String.cs @@ -1,11 +1,10 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Text; -using System.Threading; namespace dnlib.DotNet { /// @@ -18,14 +17,10 @@ public sealed class UTF8StringEqualityComparer : IEqualityComparer { public static readonly UTF8StringEqualityComparer Instance = new UTF8StringEqualityComparer(); /// - public bool Equals(UTF8String x, UTF8String y) { - return UTF8String.Equals(x, y); - } + public bool Equals(UTF8String x, UTF8String y) => UTF8String.Equals(x, y); /// - public int GetHashCode(UTF8String obj) { - return UTF8String.GetHashCode(obj); - } + public int GetHashCode(UTF8String obj) => UTF8String.GetHashCode(obj); } /// @@ -50,8 +45,8 @@ public sealed class UTF8String : IEquatable, IComparable /// public string String { get { - if (asString == null) - Interlocked.CompareExchange(ref asString, ConvertFromUTF8(data), null); + if (asString is null) + asString = ConvertFromUTF8(data); return asString; } } @@ -59,35 +54,27 @@ public string String { /// /// Gets the original encoded data. Don't modify this data. /// - public byte[] Data { - get { return data; } - } + public byte[] Data => data; /// /// Gets the length of the this as a . I.e., it's the same as /// String.Length. /// /// - public int Length { - get { return String.Length; } - } + public int Length => String.Length; /// /// Gets the length of the raw data. It's the same as Data.Length /// /// - public int DataLength { - get { return data == null ? 0 : data.Length; } - } + public int DataLength => data is null ? 0 : data.Length; /// /// Checks whether is null or if its data is null. /// /// The instance to check /// true if null or empty, false otherwise - public static bool IsNull(UTF8String utf8) { - return (object)utf8 == null || utf8.data == null; - } + public static bool IsNull(UTF8String utf8) => utf8 is null || utf8.data is null; /// /// Checks whether is null or if its data is null or the @@ -95,19 +82,13 @@ public static bool IsNull(UTF8String utf8) { /// /// The instance to check /// true if null or empty, false otherwise - public static bool IsNullOrEmpty(UTF8String utf8) { - return (object)utf8 == null || utf8.data == null || utf8.data.Length == 0; - } + public static bool IsNullOrEmpty(UTF8String utf8) => utf8 is null || utf8.data is null || utf8.data.Length == 0; /// Implicit conversion from to - public static implicit operator string(UTF8String s) { - return UTF8String.ToSystemString(s); - } + public static implicit operator string(UTF8String s) => UTF8String.ToSystemString(s); /// Implicit conversion from to - public static implicit operator UTF8String(string s) { - return s == null ? null : new UTF8String(s); - } + public static implicit operator UTF8String(string s) => s is null ? null : new UTF8String(s); /// /// Converts it to a @@ -115,7 +96,7 @@ public static implicit operator UTF8String(string s) { /// The UTF-8 string instace or null /// A or null if is null public static string ToSystemString(UTF8String utf8) { - if ((object)utf8 == null || utf8.data == null) + if (utf8 is null || utf8.data is null) return null; if (utf8.data.Length == 0) return string.Empty; @@ -127,9 +108,7 @@ public static string ToSystemString(UTF8String utf8) { /// /// The UTF-8 string instace or null /// A (never null) - public static string ToSystemStringOrEmpty(UTF8String utf8) { - return ToSystemString(utf8) ?? string.Empty; - } + public static string ToSystemStringOrEmpty(UTF8String utf8) => ToSystemString(utf8) ?? string.Empty; /// /// Gets the hash code of a @@ -142,9 +121,7 @@ public static int GetHashCode(UTF8String utf8) { } /// - public int CompareTo(UTF8String other) { - return CompareTo(this, other); - } + public int CompareTo(UTF8String other) => CompareTo(this, other); /// /// Compares two instances (case sensitive) @@ -152,9 +129,7 @@ public int CompareTo(UTF8String other) { /// Instance #1 or null /// Instance #2 or null /// < 0 if a < b, 0 if a == b, > 0 if a > b - public static int CompareTo(UTF8String a, UTF8String b) { - return Utils.CompareTo((object)a == null ? null : a.data, (object)b == null ? null : b.data); - } + public static int CompareTo(UTF8String a, UTF8String b) => Utils.CompareTo(a?.data, b?.data); /// /// Compares two instances (case insensitive) @@ -169,11 +144,11 @@ public static int CaseInsensitiveCompareTo(UTF8String a, UTF8String b) { var sb = ToSystemString(b); if ((object)sa == (object)sb) return 0; - if (sa == null) + if (sa is null) return -1; - if (sb == null) + if (sb is null) return 1; - return sa.ToUpperInvariant().CompareTo(sb.ToUpperInvariant()); + return StringComparer.OrdinalIgnoreCase.Compare(sa, sb); } /// @@ -182,78 +157,54 @@ public static int CaseInsensitiveCompareTo(UTF8String a, UTF8String b) { /// Instance #1 or null /// Instance #2 or null /// true if equals, false otherwise - public static bool CaseInsensitiveEquals(UTF8String a, UTF8String b) { - return CaseInsensitiveCompareTo(a, b) == 0; - } + public static bool CaseInsensitiveEquals(UTF8String a, UTF8String b) => CaseInsensitiveCompareTo(a, b) == 0; /// Overloaded operator - public static bool operator ==(UTF8String left, UTF8String right) { - return CompareTo(left, right) == 0; - } + public static bool operator ==(UTF8String left, UTF8String right) => CompareTo(left, right) == 0; /// Overloaded operator - public static bool operator ==(UTF8String left, string right) { - return ToSystemString(left) == right; - } + public static bool operator ==(UTF8String left, string right) => ToSystemString(left) == right; /// Overloaded operator - public static bool operator ==(string left, UTF8String right) { - return left == ToSystemString(right); - } + public static bool operator ==(string left, UTF8String right) => left == ToSystemString(right); /// Overloaded operator - public static bool operator !=(UTF8String left, UTF8String right) { - return CompareTo(left, right) != 0; - } + public static bool operator !=(UTF8String left, UTF8String right) => CompareTo(left, right) != 0; /// Overloaded operator - public static bool operator !=(UTF8String left, string right) { - return ToSystemString(left) != right; - } + public static bool operator !=(UTF8String left, string right) => ToSystemString(left) != right; /// Overloaded operator - public static bool operator !=(string left, UTF8String right) { - return left != ToSystemString(right); - } + public static bool operator !=(string left, UTF8String right) => left != ToSystemString(right); /// Overloaded operator - public static bool operator >(UTF8String left, UTF8String right) { - return CompareTo(left, right) > 0; - } + public static bool operator >(UTF8String left, UTF8String right) => CompareTo(left, right) > 0; /// Overloaded operator - public static bool operator <(UTF8String left, UTF8String right) { - return CompareTo(left, right) < 0; - } + public static bool operator <(UTF8String left, UTF8String right) => CompareTo(left, right) < 0; /// Overloaded operator - public static bool operator >=(UTF8String left, UTF8String right) { - return CompareTo(left, right) >= 0; - } + public static bool operator >=(UTF8String left, UTF8String right) => CompareTo(left, right) >= 0; /// Overloaded operator - public static bool operator <=(UTF8String left, UTF8String right) { - return CompareTo(left, right) <= 0; - } + public static bool operator <=(UTF8String left, UTF8String right) => CompareTo(left, right) <= 0; /// /// Constructor /// /// UTF-8 data that this instance now owns - public UTF8String(byte[] data) { - this.data = data; - } + public UTF8String(byte[] data) => this.data = data; /// /// Constructor /// /// The string public UTF8String(string s) - : this(s == null ? null : Encoding.UTF8.GetBytes(s)) { + : this(s is null ? null : Encoding.UTF8.GetBytes(s)) { } static string ConvertFromUTF8(byte[] data) { - if (data == null) + if (data is null) return null; try { return Encoding.UTF8.GetString(data); @@ -269,19 +220,15 @@ static string ConvertFromUTF8(byte[] data) { /// First /// Second /// true if equals, false otherwise - public static bool Equals(UTF8String a, UTF8String b) { - return CompareTo(a, b) == 0; - } + public static bool Equals(UTF8String a, UTF8String b) => CompareTo(a, b) == 0; /// - public bool Equals(UTF8String other) { - return CompareTo(this, other) == 0; - } + public bool Equals(UTF8String other) => CompareTo(this, other) == 0; /// public override bool Equals(object obj) { var other = obj as UTF8String; - if ((object)other == null) + if (other is null) return false; return CompareTo(this, other) == 0; } @@ -292,18 +239,14 @@ public override bool Equals(object obj) { /// Value to find /// true if exists in string or is the /// empty string, else false - public bool Contains(string value) { - return String.Contains(value); - } + public bool Contains(string value) => String.Contains(value); /// /// Checks whether matches the end of this string /// /// Value /// - public bool EndsWith(string value) { - return String.EndsWith(value); - } + public bool EndsWith(string value) => String.EndsWith(value); /// /// Checks whether matches the end of this string @@ -312,9 +255,7 @@ public bool EndsWith(string value) { /// true to ignore case /// Culture info /// - public bool EndsWith(string value, bool ignoreCase, CultureInfo culture) { - return String.EndsWith(value, ignoreCase, culture); - } + public bool EndsWith(string value, bool ignoreCase, CultureInfo culture) => String.EndsWith(value, ignoreCase, culture); /// /// Checks whether matches the end of this string @@ -322,18 +263,14 @@ public bool EndsWith(string value, bool ignoreCase, CultureInfo culture) { /// Value /// Comparison type /// - public bool EndsWith(string value, StringComparison comparisonType) { - return String.EndsWith(value, comparisonType); - } + public bool EndsWith(string value, StringComparison comparisonType) => String.EndsWith(value, comparisonType); /// /// Checks whether matches the beginning of this string /// /// Value /// - public bool StartsWith(string value) { - return String.StartsWith(value); - } + public bool StartsWith(string value) => String.StartsWith(value); /// /// Checks whether matches the beginning of this string @@ -342,9 +279,7 @@ public bool StartsWith(string value) { /// true to ignore case /// Culture info /// - public bool StartsWith(string value, bool ignoreCase, CultureInfo culture) { - return String.StartsWith(value, ignoreCase, culture); - } + public bool StartsWith(string value, bool ignoreCase, CultureInfo culture) => String.StartsWith(value, ignoreCase, culture); /// /// Checks whether matches the beginning of this string @@ -352,27 +287,21 @@ public bool StartsWith(string value, bool ignoreCase, CultureInfo culture) { /// Value /// Comparison type /// - public bool StartsWith(string value, StringComparison comparisonType) { - return String.StartsWith(value, comparisonType); - } + public bool StartsWith(string value, StringComparison comparisonType) => String.StartsWith(value, comparisonType); /// /// Compares this instance with /// /// Other string /// < 0 if a < b, 0 if a == b, > 0 if a > b - public int CompareTo(string strB) { - return String.CompareTo(strB); - } + public int CompareTo(string strB) => String.CompareTo(strB); /// /// Returns the index of the first character in this string /// /// Character /// The index of or -1 if not found - public int IndexOf(char value) { - return String.IndexOf(value); - } + public int IndexOf(char value) => String.IndexOf(value); /// /// Returns the index of the first character in this string @@ -381,9 +310,7 @@ public int IndexOf(char value) { /// Character /// Start index /// The index of or -1 if not found - public int IndexOf(char value, int startIndex) { - return String.IndexOf(value, startIndex); - } + public int IndexOf(char value, int startIndex) => String.IndexOf(value, startIndex); /// /// Returns the index of the first character in this string @@ -394,18 +321,14 @@ public int IndexOf(char value, int startIndex) { /// Start index /// Max number of chars to scan /// The index of or -1 if not found - public int IndexOf(char value, int startIndex, int count) { - return String.IndexOf(value, startIndex, count); - } + public int IndexOf(char value, int startIndex, int count) => String.IndexOf(value, startIndex, count); /// /// Returns the index of the first sub string in this string /// /// String /// The index of or -1 if not found - public int IndexOf(string value) { - return String.IndexOf(value); - } + public int IndexOf(string value) => String.IndexOf(value); /// /// Returns the index of the first sub string in this string @@ -414,9 +337,7 @@ public int IndexOf(string value) { /// String /// Start index /// The index of or -1 if not found - public int IndexOf(string value, int startIndex) { - return String.IndexOf(value, startIndex); - } + public int IndexOf(string value, int startIndex) => String.IndexOf(value, startIndex); /// /// Returns the index of the first sub string in this string @@ -427,9 +348,7 @@ public int IndexOf(string value, int startIndex) { /// Start index /// Max number of chars to scan /// The index of or -1 if not found - public int IndexOf(string value, int startIndex, int count) { - return String.IndexOf(value, startIndex, count); - } + public int IndexOf(string value, int startIndex, int count) => String.IndexOf(value, startIndex, count); /// /// Returns the index of the first sub string in this string @@ -441,9 +360,7 @@ public int IndexOf(string value, int startIndex, int count) { /// Max number of chars to scan /// Comparison type /// The index of or -1 if not found - public int IndexOf(string value, int startIndex, int count, StringComparison comparisonType) { - return String.IndexOf(value, startIndex, count, comparisonType); - } + public int IndexOf(string value, int startIndex, int count, StringComparison comparisonType) => String.IndexOf(value, startIndex, count, comparisonType); /// /// Returns the index of the first sub string in this string @@ -453,9 +370,7 @@ public int IndexOf(string value, int startIndex, int count, StringComparison com /// Start index /// Comparison type /// The index of or -1 if not found - public int IndexOf(string value, int startIndex, StringComparison comparisonType) { - return String.IndexOf(value, startIndex, comparisonType); - } + public int IndexOf(string value, int startIndex, StringComparison comparisonType) => String.IndexOf(value, startIndex, comparisonType); /// /// Returns the index of the first sub string in this string @@ -463,18 +378,14 @@ public int IndexOf(string value, int startIndex, StringComparison comparisonType /// String /// Comparison type /// The index of or -1 if not found - public int IndexOf(string value, StringComparison comparisonType) { - return String.IndexOf(value, comparisonType); - } + public int IndexOf(string value, StringComparison comparisonType) => String.IndexOf(value, comparisonType); /// /// Returns the index of the last character in this string /// /// Character /// The index of or -1 if not found - public int LastIndexOf(char value) { - return String.LastIndexOf(value); - } + public int LastIndexOf(char value) => String.LastIndexOf(value); /// /// Returns the index of the last character in this string @@ -483,9 +394,7 @@ public int LastIndexOf(char value) { /// Character /// Start index /// The index of or -1 if not found - public int LastIndexOf(char value, int startIndex) { - return String.LastIndexOf(value, startIndex); - } + public int LastIndexOf(char value, int startIndex) => String.LastIndexOf(value, startIndex); /// /// Returns the index of the last character in this string @@ -496,18 +405,14 @@ public int LastIndexOf(char value, int startIndex) { /// Start index /// Max number of chars to scan /// The index of or -1 if not found - public int LastIndexOf(char value, int startIndex, int count) { - return String.LastIndexOf(value, startIndex, count); - } + public int LastIndexOf(char value, int startIndex, int count) => String.LastIndexOf(value, startIndex, count); /// /// Returns the index of the last sub string in this string /// /// String /// The index of or -1 if not found - public int LastIndexOf(string value) { - return String.LastIndexOf(value); - } + public int LastIndexOf(string value) => String.LastIndexOf(value); /// /// Returns the index of the last sub string in this string @@ -516,9 +421,7 @@ public int LastIndexOf(string value) { /// String /// Start index /// The index of or -1 if not found - public int LastIndexOf(string value, int startIndex) { - return String.LastIndexOf(value, startIndex); - } + public int LastIndexOf(string value, int startIndex) => String.LastIndexOf(value, startIndex); /// /// Returns the index of the last sub string in this string @@ -529,9 +432,7 @@ public int LastIndexOf(string value, int startIndex) { /// Start index /// Max number of chars to scan /// The index of or -1 if not found - public int LastIndexOf(string value, int startIndex, int count) { - return String.LastIndexOf(value, startIndex, count); - } + public int LastIndexOf(string value, int startIndex, int count) => String.LastIndexOf(value, startIndex, count); /// /// Returns the index of the last sub string in this string @@ -543,9 +444,7 @@ public int LastIndexOf(string value, int startIndex, int count) { /// Max number of chars to scan /// Comparison type /// The index of or -1 if not found - public int LastIndexOf(string value, int startIndex, int count, StringComparison comparisonType) { - return String.LastIndexOf(value, startIndex, count, comparisonType); - } + public int LastIndexOf(string value, int startIndex, int count, StringComparison comparisonType) => String.LastIndexOf(value, startIndex, count, comparisonType); /// /// Returns the index of the last sub string in this string @@ -555,9 +454,7 @@ public int LastIndexOf(string value, int startIndex, int count, StringComparison /// Start index /// Comparison type /// The index of or -1 if not found - public int LastIndexOf(string value, int startIndex, StringComparison comparisonType) { - return String.LastIndexOf(value, startIndex, comparisonType); - } + public int LastIndexOf(string value, int startIndex, StringComparison comparisonType) => String.LastIndexOf(value, startIndex, comparisonType); /// /// Returns the index of the last sub string in this string @@ -565,9 +462,7 @@ public int LastIndexOf(string value, int startIndex, StringComparison comparison /// String /// Comparison type /// The index of or -1 if not found - public int LastIndexOf(string value, StringComparison comparisonType) { - return String.LastIndexOf(value, comparisonType); - } + public int LastIndexOf(string value, StringComparison comparisonType) => String.LastIndexOf(value, comparisonType); /// /// Inserts string at a index @@ -576,18 +471,14 @@ public int LastIndexOf(string value, StringComparison comparisonType) { /// Value to insert /// A new instance with the inserted at position /// - public UTF8String Insert(int startIndex, string value) { - return new UTF8String(String.Insert(startIndex, value)); - } + public UTF8String Insert(int startIndex, string value) => new UTF8String(String.Insert(startIndex, value)); /// /// Removes all characters starting from position /// /// Start index /// A new instance - public UTF8String Remove(int startIndex) { - return new UTF8String(String.Remove(startIndex)); - } + public UTF8String Remove(int startIndex) => new UTF8String(String.Remove(startIndex)); /// /// Removes characters starting from position @@ -596,9 +487,7 @@ public UTF8String Remove(int startIndex) { /// Start index /// Number of characters to remove /// A new instance - public UTF8String Remove(int startIndex, int count) { - return new UTF8String(String.Remove(startIndex, count)); - } + public UTF8String Remove(int startIndex, int count) => new UTF8String(String.Remove(startIndex, count)); /// /// Replaces all characters with @@ -606,9 +495,7 @@ public UTF8String Remove(int startIndex, int count) { /// Character to find /// Character to replace all /// A new instance - public UTF8String Replace(char oldChar, char newChar) { - return new UTF8String(String.Replace(oldChar, newChar)); - } + public UTF8String Replace(char oldChar, char newChar) => new UTF8String(String.Replace(oldChar, newChar)); /// /// Replaces all sub strings with @@ -616,18 +503,14 @@ public UTF8String Replace(char oldChar, char newChar) { /// Sub string to find /// Sub string to replace all /// A new instance - public UTF8String Replace(string oldValue, string newValue) { - return new UTF8String(String.Replace(oldValue, newValue)); - } + public UTF8String Replace(string oldValue, string newValue) => new UTF8String(String.Replace(oldValue, newValue)); /// /// Returns a sub string of this string starting at offset /// /// Start index /// A new instance - public UTF8String Substring(int startIndex) { - return new UTF8String(String.Substring(startIndex)); - } + public UTF8String Substring(int startIndex) => new UTF8String(String.Substring(startIndex)); /// /// Returns a sub string of this string starting at offset . @@ -636,76 +519,56 @@ public UTF8String Substring(int startIndex) { /// Start index /// Length of sub string /// A new instance - public UTF8String Substring(int startIndex, int length) { - return new UTF8String(String.Substring(startIndex, length)); - } + public UTF8String Substring(int startIndex, int length) => new UTF8String(String.Substring(startIndex, length)); /// /// Returns the lower case version of this string /// /// A new instance - public UTF8String ToLower() { - return new UTF8String(String.ToLower()); - } + public UTF8String ToLower() => new UTF8String(String.ToLower()); /// /// Returns the lower case version of this string /// /// Culture info /// A new instance - public UTF8String ToLower(CultureInfo culture) { - return new UTF8String(String.ToLower(culture)); - } + public UTF8String ToLower(CultureInfo culture) => new UTF8String(String.ToLower(culture)); /// /// Returns the lower case version of this string using the invariant culture /// /// A new instance - public UTF8String ToLowerInvariant() { - return new UTF8String(String.ToLowerInvariant()); - } + public UTF8String ToLowerInvariant() => new UTF8String(String.ToLowerInvariant()); /// /// Returns the upper case version of this string /// /// A new instance - public UTF8String ToUpper() { - return new UTF8String(String.ToUpper()); - } + public UTF8String ToUpper() => new UTF8String(String.ToUpper()); /// /// Returns the upper case version of this string /// /// Culture info /// A new instance - public UTF8String ToUpper(CultureInfo culture) { - return new UTF8String(String.ToUpper(culture)); - } + public UTF8String ToUpper(CultureInfo culture) => new UTF8String(String.ToUpper(culture)); /// /// Returns the upper case version of this string using the invariant culture /// /// A new instance - public UTF8String ToUpperInvariant() { - return new UTF8String(String.ToUpperInvariant()); - } + public UTF8String ToUpperInvariant() => new UTF8String(String.ToUpperInvariant()); /// /// Removes all leading and trailing whitespace characters /// /// A new instance - public UTF8String Trim() { - return new UTF8String(String.Trim()); - } + public UTF8String Trim() => new UTF8String(String.Trim()); /// - public override int GetHashCode() { - return UTF8String.GetHashCode(this); - } + public override int GetHashCode() => UTF8String.GetHashCode(this); /// - public override string ToString() { - return String; - } + public override string ToString() => String; } } diff --git a/src/DotNet/Utils.cs b/src/DotNet/Utils.cs index dd0de3db9..90d3797ce 100644 --- a/src/DotNet/Utils.cs +++ b/src/DotNet/Utils.cs @@ -2,72 +2,26 @@ using System; using System.Collections.Generic; -using System.IO; using System.Text; namespace dnlib.DotNet { /// /// Compares byte arrays /// - public sealed class ByteArrayEqualityComparer : IEqualityComparer { + sealed class ByteArrayEqualityComparer : IEqualityComparer { /// /// Default instance /// public static readonly ByteArrayEqualityComparer Instance = new ByteArrayEqualityComparer(); /// - public bool Equals(byte[] x, byte[] y) { - return Utils.Equals(x, y); - } + public bool Equals(byte[] x, byte[] y) => Utils.Equals(x, y); /// - public int GetHashCode(byte[] obj) { - return Utils.GetHashCode(obj); - } + public int GetHashCode(byte[] obj) => Utils.GetHashCode(obj); } static class Utils { - /// - /// Returns an assembly name string - /// - /// Simple assembly name - /// Version or null - /// Culture or null - /// Public key / public key token or null - /// Assembly attributes - /// An assembly name string - internal static string GetAssemblyNameString(UTF8String name, Version version, UTF8String culture, PublicKeyBase publicKey, AssemblyAttributes attributes) { - var sb = new StringBuilder(); - - foreach (var c in UTF8String.ToSystemStringOrEmpty(name)) { - if (c == ',' || c == '=') - sb.Append('\\'); - sb.Append(c); - } - - if (version != null) { - sb.Append(", Version="); - sb.Append(CreateVersionWithNoUndefinedValues(version).ToString()); - } - - if ((object)culture != null) { - sb.Append(", Culture="); - sb.Append(UTF8String.IsNullOrEmpty(culture) ? "neutral" : culture.String); - } - - sb.Append(", "); - sb.Append(publicKey == null || publicKey is PublicKeyToken ? "PublicKeyToken=" : "PublicKey="); - sb.Append(publicKey == null ? "null" : publicKey.ToString()); - - if ((attributes & AssemblyAttributes.Retargetable) != 0) - sb.Append(", Retargetable=Yes"); - - if ((attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime) - sb.Append(", ContentType=WindowsRuntime"); - - return sb.ToString(); - } - /// /// Convert a byte[] to a /// @@ -75,7 +29,7 @@ internal static string GetAssemblyNameString(UTF8String name, Version version, U /// true if output should be in upper case hex /// as a hex string internal static string ToHex(byte[] bytes, bool upper) { - if (bytes == null) + if (bytes is null) return ""; var chars = new char[bytes.Length * 2]; for (int i = 0, j = 0; i < bytes.Length; i++) { @@ -142,9 +96,9 @@ static int TryParseHexChar(char c) { internal static int CompareTo(byte[] a, byte[] b) { if (a == b) return 0; - if (a == null) + if (a is null) return -1; - if (b == null) + if (b is null) return 1; int count = Math.Min(a.Length, b.Length); for (int i = 0; i < count; i++) { @@ -165,7 +119,17 @@ internal static int CompareTo(byte[] a, byte[] b) { /// Second /// true if same, false otherwise internal static bool Equals(byte[] a, byte[] b) { - return CompareTo(a, b) == 0; + if (a == b) + return true; + if (a is null || b is null) + return false; + if (a.Length != b.Length) + return false; + for (int i = 0; i < a.Length; i++) { + if (a[i] != b[i]) + return false; + } + return true; } /// @@ -174,7 +138,7 @@ internal static bool Equals(byte[] a, byte[] b) { /// Byte array /// The hash code internal static int GetHashCode(byte[] a) { - if (a == null || a.Length == 0) + if (a is null || a.Length == 0) return 0; int count = Math.Min(a.Length / 2, 20); if (count == 0) @@ -197,9 +161,9 @@ internal static int GetHashCode(byte[] a) { /// Version #2 or null to be treated as v0.0.0.0 /// < 0 if a < b, 0 if a == b, > 0 if a > b internal static int CompareTo(Version a, Version b) { - if (a == null) + if (a is null) a = new Version(); - if (b == null) + if (b is null) b = new Version(); if (a.Major != b.Major) return a.Major.CompareTo(b.Major); @@ -219,9 +183,7 @@ internal static int CompareTo(Version a, Version b) { /// Version #1 or null to be treated as v0.0.0.0 /// Version #2 or null to be treated as v0.0.0.0 /// true if same, false otherwise - internal static bool Equals(Version a, Version b) { - return CompareTo(a, b) == 0; - } + internal static bool Equals(Version a, Version b) => CompareTo(a, b) == 0; /// /// Creates a new instance with no undefined version values (eg. @@ -230,14 +192,12 @@ internal static bool Equals(Version a, Version b) { /// A instance /// A new instance internal static Version CreateVersionWithNoUndefinedValues(Version a) { - if (a == null) + if (a is null) return new Version(0, 0, 0, 0); return new Version(a.Major, a.Minor, GetDefaultVersionValue(a.Build), GetDefaultVersionValue(a.Revision)); } - static int GetDefaultVersionValue(int val) { - return val == -1 ? 0 : val; - } + static int GetDefaultVersionValue(int val) => val == -1 ? 0 : val; /// /// Parses a version string @@ -260,9 +220,7 @@ internal static Version ParseVersion(string versionString) { /// First /// Second /// < 0 if a < b, 0 if a == b, > 0 if a > b - internal static int LocaleCompareTo(UTF8String a, UTF8String b) { - return GetCanonicalLocale(a).CompareTo(GetCanonicalLocale(b)); - } + internal static int LocaleCompareTo(UTF8String a, UTF8String b) => GetCanonicalLocale(a).CompareTo(GetCanonicalLocale(b)); /// /// Compares two locales (cultures) @@ -270,9 +228,7 @@ internal static int LocaleCompareTo(UTF8String a, UTF8String b) { /// First /// Second /// true if same, false otherwise - internal static bool LocaleEquals(UTF8String a, UTF8String b) { - return LocaleCompareTo(a, b) == 0; - } + internal static bool LocaleEquals(UTF8String a, UTF8String b) => LocaleCompareTo(a, b) == 0; /// /// Compares two locales (cultures) @@ -280,9 +236,7 @@ internal static bool LocaleEquals(UTF8String a, UTF8String b) { /// First /// Second /// < 0 if a < b, 0 if a == b, > 0 if a > b - internal static int LocaleCompareTo(UTF8String a, string b) { - return GetCanonicalLocale(a).CompareTo(GetCanonicalLocale(b)); - } + internal static int LocaleCompareTo(UTF8String a, string b) => GetCanonicalLocale(a).CompareTo(GetCanonicalLocale(b)); /// /// Compares two locales (cultures) @@ -290,22 +244,16 @@ internal static int LocaleCompareTo(UTF8String a, string b) { /// First /// Second /// true if same, false otherwise - internal static bool LocaleEquals(UTF8String a, string b) { - return LocaleCompareTo(a, b) == 0; - } + internal static bool LocaleEquals(UTF8String a, string b) => LocaleCompareTo(a, b) == 0; /// /// Gets the hash code of a locale /// /// Value /// The hash code - internal static int GetHashCodeLocale(UTF8String a) { - return GetCanonicalLocale(a).GetHashCode(); - } + internal static int GetHashCodeLocale(UTF8String a) => GetCanonicalLocale(a).GetHashCode(); - static string GetCanonicalLocale(UTF8String locale) { - return GetCanonicalLocale(UTF8String.ToSystemStringOrEmpty(locale)); - } + static string GetCanonicalLocale(UTF8String locale) => GetCanonicalLocale(UTF8String.ToSystemStringOrEmpty(locale)); static string GetCanonicalLocale(string locale) { var s = locale.ToUpperInvariant(); @@ -319,87 +267,27 @@ static string GetCanonicalLocale(string locale) { /// /// Value /// Alignment - public static uint AlignUp(uint v, uint alignment) { - return (v + alignment - 1) & ~(alignment - 1); - } + public static uint AlignUp(uint v, uint alignment) => (v + alignment - 1) & ~(alignment - 1); /// /// Align up /// /// Value /// Alignment - public static int AlignUp(int v, uint alignment) { - return (int)AlignUp((uint)v, alignment); - } + public static int AlignUp(int v, uint alignment) => (int)AlignUp((uint)v, alignment); /// - /// Gets length of compressed integer + /// Rounds up the provided number to the next power of 2 /// - /// Integer - /// Size of compressed integer in bytes (1, 2 or 4 bytes) - /// can't be compressed (too big) - public static int GetCompressedUInt32Length(uint value) { - if (value <= 0x7F) - return 1; - if (value <= 0x3FFF) - return 2; - if (value <= 0x1FFFFFFF) - return 4; - throw new ArgumentOutOfRangeException("UInt32 value can't be compressed"); - } - - /// - /// Writes a compressed - /// - /// Writer - /// Value - /// can't be compressed (too big) - public static void WriteCompressedUInt32(this BinaryWriter writer, uint value) { - if (value <= 0x7F) - writer.Write((byte)value); - else if (value <= 0x3FFF) { - writer.Write((byte)((value >> 8) | 0x80)); - writer.Write((byte)value); - } - else if (value <= 0x1FFFFFFF) { - writer.Write((byte)((value >> 24) | 0xC0)); - writer.Write((byte)(value >> 16)); - writer.Write((byte)(value >> 8)); - writer.Write((byte)value); - } - else - throw new ArgumentOutOfRangeException("UInt32 value can't be compressed"); - } - - /// - /// Writes a compressed - /// - /// Writer - /// Value - /// can't be compressed (too big/small) - public static void WriteCompressedInt32(this BinaryWriter writer, int value) { - // This is almost identical to compressing a UInt32, except that we first - // recode value so the sign bit is in bit 0. Then we compress it the same - // way a UInt32 is compressed. - uint sign = (uint)value >> 31; - if (-0x40 <= value && value <= 0x3F) { - uint v = (uint)((value & 0x3F) << 1) | sign; - writer.Write((byte)v); - } - else if (-0x2000 <= value && value <= 0x1FFF) { - uint v = ((uint)(value & 0x1FFF) << 1) | sign; - writer.Write((byte)((v >> 8) | 0x80)); - writer.Write((byte)v); - } - else if (-0x10000000 <= value && value <= 0x0FFFFFFF) { - uint v = ((uint)(value & 0x0FFFFFFF) << 1) | sign; - writer.Write((byte)((v >> 24) | 0xC0)); - writer.Write((byte)(v >> 16)); - writer.Write((byte)(v >> 8)); - writer.Write((byte)v); - } - else - throw new ArgumentOutOfRangeException("Int32 value can't be compressed"); + /// The number to round + public static uint RoundToNextPowerOfTwo(uint num) { + num--; + num |= num >> 1; + num |= num >> 2; + num |= num >> 4; + num |= num >> 8; + num |= num >> 16; + return num + 1; } } } diff --git a/src/DotNet/VTableFixups.cs b/src/DotNet/VTableFixups.cs index 024012bdb..2dfb1c851 100644 --- a/src/DotNet/VTableFixups.cs +++ b/src/DotNet/VTableFixups.cs @@ -1,17 +1,9 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using dnlib.PE; -using dnlib.IO; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif namespace dnlib.DotNet { /// @@ -20,80 +12,69 @@ namespace dnlib.DotNet { [DebuggerDisplay("RVA = {RVA}, Count = {VTables.Count}")] public sealed class VTableFixups : IEnumerable { RVA rva; - ThreadSafe.IList vtables; + IList vtables; /// /// Gets/sets the RVA of the vtable fixups /// public RVA RVA { - get { return rva; } - set { rva = value; } + get => rva; + set => rva = value; } /// /// Gets all s /// - public ThreadSafe.IList VTables { - get { return vtables; } - } + public IList VTables => vtables; /// /// Default constructor /// - public VTableFixups() { - this.vtables = ThreadSafeListCreator.Create(); - } + public VTableFixups() => vtables = new List(); /// /// Constructor /// /// Module - public VTableFixups(ModuleDefMD module) { - Initialize(module); - } + public VTableFixups(ModuleDefMD module) => Initialize(module); void Initialize(ModuleDefMD module) { - var info = module.MetaData.ImageCor20Header.VTableFixups; + var info = module.Metadata.ImageCor20Header.VTableFixups; if (info.VirtualAddress == 0 || info.Size == 0) { - this.vtables = ThreadSafeListCreator.Create(); + vtables = new List(); return; } - this.rva = info.VirtualAddress; - this.vtables = ThreadSafeListCreator.Create((int)info.Size / 8); - - var peImage = module.MetaData.PEImage; - using (var reader = peImage.CreateFullStream()) { - reader.Position = (long)peImage.ToFileOffset(info.VirtualAddress); - long endPos = reader.Position + info.Size; - while (reader.Position + 8 <= endPos && reader.CanRead(8)) { - RVA tableRva = (RVA)reader.ReadUInt32(); - int numSlots = reader.ReadUInt16(); - var flags = (VTableFlags)reader.ReadUInt16(); - var vtable = new VTable(tableRva, flags, numSlots); - vtables.Add(vtable); - - var pos = reader.Position; - reader.Position = (long)peImage.ToFileOffset(tableRva); - int slotSize = vtable.Is64Bit ? 8 : 4; - while (numSlots-- > 0 && reader.CanRead(slotSize)) { - vtable.Methods.Add(module.ResolveToken(reader.ReadUInt32()) as IMethod); - if (slotSize == 8) - reader.ReadUInt32(); - } - reader.Position = pos; + rva = info.VirtualAddress; + vtables = new List((int)info.Size / 8); + + var peImage = module.Metadata.PEImage; + var reader = peImage.CreateReader(); + reader.Position = (uint)peImage.ToFileOffset(info.VirtualAddress); + ulong endPos = (ulong)reader.Position + info.Size; + while ((ulong)reader.Position + 8 <= endPos && reader.CanRead(8U)) { + var tableRva = (RVA)reader.ReadUInt32(); + int numSlots = reader.ReadUInt16(); + var flags = (VTableFlags)reader.ReadUInt16(); + var vtable = new VTable(tableRva, flags, numSlots); + vtables.Add(vtable); + + var pos = reader.Position; + reader.Position = (uint)peImage.ToFileOffset(tableRva); + uint slotSize = vtable.Is64Bit ? 8U : 4; + while (numSlots-- > 0 && reader.CanRead(slotSize)) { + vtable.Methods.Add(module.ResolveToken(reader.ReadUInt32()) as IMethod); + if (slotSize == 8) + reader.ReadUInt32(); } + reader.Position = pos; } } /// - public IEnumerator GetEnumerator() { - return vtables.GetEnumerator(); - } + public IEnumerator GetEnumerator() => vtables.GetEnumerator(); /// - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return GetEnumerator(); - } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); } /// @@ -104,12 +85,12 @@ public enum VTableFlags : ushort { /// /// 32-bit vtable slots /// - _32Bit = 0x01, + Bit32 = 0x01, /// /// 64-bit vtable slots /// - _64Bit = 0x02, + Bit64 = 0x02, /// /// Transition from unmanaged code @@ -133,51 +114,43 @@ public enum VTableFlags : ushort { public sealed class VTable : IEnumerable { RVA rva; VTableFlags flags; - readonly ThreadSafe.IList methods; + readonly IList methods; /// /// Gets/sets the of this vtable /// public RVA RVA { - get { return rva; } - set { rva = value; } + get => rva; + set => rva = value; } /// /// Gets/sets the flags /// public VTableFlags Flags { - get { return flags; } - set { flags = value; } + get => flags; + set => flags = value; } /// /// true if each vtable slot is 32 bits in size /// - public bool Is32Bit { - get { return (flags & VTableFlags._32Bit) != 0; } - } + public bool Is32Bit => (flags & VTableFlags.Bit32) != 0; /// /// true if each vtable slot is 64 bits in size /// - public bool Is64Bit { - get { return (flags & VTableFlags._64Bit) != 0; } - } + public bool Is64Bit => (flags & VTableFlags.Bit64) != 0; /// /// Gets the vtable methods /// - public ThreadSafe.IList Methods { - get { return methods; } - } + public IList Methods => methods; /// /// Default constructor /// - public VTable() { - this.methods = ThreadSafeListCreator.Create(); - } + public VTable() => methods = new List(); /// /// Constructor @@ -185,7 +158,7 @@ public VTable() { /// Flags public VTable(VTableFlags flags) { this.flags = flags; - this.methods = ThreadSafeListCreator.Create(); + methods = new List(); } /// @@ -197,7 +170,7 @@ public VTable(VTableFlags flags) { public VTable(RVA rva, VTableFlags flags, int numSlots) { this.rva = rva; this.flags = flags; - this.methods = ThreadSafeListCreator.Create(numSlots); + methods = new List(numSlots); } /// @@ -209,24 +182,20 @@ public VTable(RVA rva, VTableFlags flags, int numSlots) { public VTable(RVA rva, VTableFlags flags, IEnumerable methods) { this.rva = rva; this.flags = flags; - this.methods = ThreadSafeListCreator.Create(methods); + this.methods = new List(methods); } /// - public IEnumerator GetEnumerator() { - return methods.GetEnumerator(); - } + public IEnumerator GetEnumerator() => methods.GetEnumerator(); /// - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return GetEnumerator(); - } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); /// public override string ToString() { if (methods.Count == 0) - return string.Format("{0} {1:X8}", methods.Count, (uint)rva); - return string.Format("{0} {1:X8} {2}", methods.Count, (uint)rva, methods.Get(0, null)); + return $"{methods.Count} {(uint)rva:X8}"; + return $"{methods.Count} {(uint)rva:X8} {methods[0]}"; } } } diff --git a/src/DotNet/VariantType.cs b/src/DotNet/VariantType.cs index 9b0f6b9de..5c80ee97e 100644 --- a/src/DotNet/VariantType.cs +++ b/src/DotNet/VariantType.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info namespace dnlib.DotNet { /// diff --git a/src/DotNet/WinMDHelpers.cs b/src/DotNet/WinMDHelpers.cs index b5483be8e..2211269aa 100644 --- a/src/DotNet/WinMDHelpers.cs +++ b/src/DotNet/WinMDHelpers.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; @@ -14,38 +14,34 @@ enum ClrAssembly { SystemRuntimeWindowsRuntimeUIXaml, } - static class WinMDHelpers { - struct ClassName : IEquatable { + /// + /// Helper class to project WinMD types to CLR types + /// + public static class WinMDHelpers { + readonly struct ClassName : IEquatable { public readonly UTF8String Namespace; public readonly UTF8String Name; // Not used when comparing for equality etc public readonly bool IsValueType; public ClassName(UTF8String ns, UTF8String name, bool isValueType = false) { - this.Namespace = ns; - this.Name = name; - this.IsValueType = isValueType; + Namespace = ns; + Name = name; + IsValueType = isValueType; } public ClassName(string ns, string name, bool isValueType = false) { - this.Namespace = ns; - this.Name = name; - this.IsValueType = isValueType; + Namespace = ns; + Name = name; + IsValueType = isValueType; } - public static bool operator ==(ClassName a, ClassName b) { - return a.Equals(b); - } - - public static bool operator !=(ClassName a, ClassName b) { - return !a.Equals(b); - } + public static bool operator ==(ClassName a, ClassName b) => a.Equals(b); + public static bool operator !=(ClassName a, ClassName b) => !a.Equals(b); - public bool Equals(ClassName other) { + public bool Equals(ClassName other) => // Don't check IsValueType - return UTF8String.Equals(Namespace, other.Namespace) && - UTF8String.Equals(Name, other.Name); - } + UTF8String.Equals(Namespace, other.Namespace) && UTF8String.Equals(Name, other.Name); public override bool Equals(object obj) { if (!(obj is ClassName)) @@ -53,14 +49,11 @@ public override bool Equals(object obj) { return Equals((ClassName)obj); } - public override int GetHashCode() { + public override int GetHashCode() => // Don't use IsValueType - return UTF8String.GetHashCode(Namespace) ^ UTF8String.GetHashCode(Name); - } + UTF8String.GetHashCode(Namespace) ^ UTF8String.GetHashCode(Name); - public override string ToString() { - return string.Format("{0}.{1}", Namespace, Name); - } + public override string ToString() => $"{Namespace}.{Name}"; } sealed class ProjectedClass { @@ -70,15 +63,13 @@ sealed class ProjectedClass { public readonly ClrAssembly ContractAssembly; public ProjectedClass(string mdns, string mdname, string clrns, string clrname, ClrAssembly clrAsm, ClrAssembly contractAsm, bool winMDValueType, bool clrValueType) { - this.WinMDClass = new ClassName(mdns, mdname, winMDValueType); - this.ClrClass = new ClassName(clrns, clrname, clrValueType); - this.ClrAssembly = clrAsm; - this.ContractAssembly = contractAsm; + WinMDClass = new ClassName(mdns, mdname, winMDValueType); + ClrClass = new ClassName(clrns, clrname, clrValueType); + ClrAssembly = clrAsm; + ContractAssembly = contractAsm; } - public override string ToString() { - return string.Format("{0} <-> {1}, {2}", WinMDClass, ClrClass, CreateAssembly(null, ContractAssembly)); - } + public override string ToString() => $"{WinMDClass} <-> {ClrClass}, {CreateAssembly(null, ContractAssembly)}"; } // See https://github.com/dotnet/coreclr/blob/master/src/inc/winrtprojectedtypes.h @@ -172,8 +163,7 @@ static WinMDHelpers() { } static AssemblyRef ToCLR(ModuleDef module, ref UTF8String ns, ref UTF8String name) { - ProjectedClass pc; - if (!winMDToCLR.TryGetValue(new ClassName(ns, name), out pc)) + if (!winMDToCLR.TryGetValue(new ClassName(ns, name), out var pc)) return null; ns = pc.ClrClass.Namespace; @@ -182,14 +172,12 @@ static AssemblyRef ToCLR(ModuleDef module, ref UTF8String ns, ref UTF8String nam } static AssemblyRef CreateAssembly(ModuleDef module, ClrAssembly clrAsm) { - var mscorlib = module == null ? null : module.CorLibTypes.AssemblyRef; - + var mscorlib = module?.CorLibTypes.AssemblyRef; var asm = new AssemblyRefUser(GetName(clrAsm), contractAsmVersion, new PublicKeyToken(GetPublicKeyToken(clrAsm)), UTF8String.Empty); - if (mscorlib != null && mscorlib.Name == mscorlibName && mscorlib.Version != invalidWinMDVersion) + if (mscorlib is not null && mscorlib.Name == mscorlibName && IsValidMscorlibVersion(mscorlib.Version)) asm.Version = mscorlib.Version; - var mod = module as ModuleDefMD; - if (mod != null) { + if (module is ModuleDefMD mod) { Version ver = null; foreach (var asmRef in mod.GetAssemblyRefs()) { if (asmRef.IsContentTypeWindowsRuntime) @@ -200,34 +188,35 @@ static AssemblyRef CreateAssembly(ModuleDef module, ClrAssembly clrAsm) { continue; if (!PublicKeyBase.TokenEquals(asmRef.PublicKeyOrToken, asm.PublicKeyOrToken)) continue; - if (asmRef.Version == invalidWinMDVersion) + if (!IsValidMscorlibVersion(asmRef.Version)) continue; - if (ver == null || asmRef.Version > ver) + if (ver is null || asmRef.Version > ver) ver = asmRef.Version; } - if (ver != null) + if (ver is not null) asm.Version = ver; } return asm; } static readonly Version contractAsmVersion = new Version(4, 0, 0, 0); - static readonly Version invalidWinMDVersion = new Version(255, 255, 255, 255); static readonly UTF8String mscorlibName = new UTF8String("mscorlib"); - static UTF8String GetName(ClrAssembly clrAsm) { - switch (clrAsm) { - case ClrAssembly.Mscorlib: return clrAsmName_Mscorlib; - case ClrAssembly.SystemNumericsVectors: return clrAsmName_SystemNumericsVectors; - case ClrAssembly.SystemObjectModel: return clrAsmName_SystemObjectModel; - case ClrAssembly.SystemRuntime: return clrAsmName_SystemRuntime; - case ClrAssembly.SystemRuntimeInteropServicesWindowsRuntime: return clrAsmName_SystemRuntimeInteropServicesWindowsRuntime; - case ClrAssembly.SystemRuntimeWindowsRuntime: return clrAsmName_SystemRuntimeWindowsRuntime; - case ClrAssembly.SystemRuntimeWindowsRuntimeUIXaml: return clrAsmName_SystemRuntimeWindowsRuntimeUIXaml; - default: throw new InvalidOperationException(); - } - } + // Silverlight uses 5.0.5.0 + static bool IsValidMscorlibVersion(Version version) => version is not null && (uint)version.Major <= 5; + + static UTF8String GetName(ClrAssembly clrAsm) => + clrAsm switch { + ClrAssembly.Mscorlib => clrAsmName_Mscorlib, + ClrAssembly.SystemNumericsVectors => clrAsmName_SystemNumericsVectors, + ClrAssembly.SystemObjectModel => clrAsmName_SystemObjectModel, + ClrAssembly.SystemRuntime => clrAsmName_SystemRuntime, + ClrAssembly.SystemRuntimeInteropServicesWindowsRuntime => clrAsmName_SystemRuntimeInteropServicesWindowsRuntime, + ClrAssembly.SystemRuntimeWindowsRuntime => clrAsmName_SystemRuntimeWindowsRuntime, + ClrAssembly.SystemRuntimeWindowsRuntimeUIXaml => clrAsmName_SystemRuntimeWindowsRuntimeUIXaml, + _ => throw new InvalidOperationException(), + }; static readonly UTF8String clrAsmName_Mscorlib = new UTF8String("mscorlib"); static readonly UTF8String clrAsmName_SystemNumericsVectors = new UTF8String("System.Numerics.Vectors"); static readonly UTF8String clrAsmName_SystemObjectModel = new UTF8String("System.ObjectModel"); @@ -236,18 +225,17 @@ static UTF8String GetName(ClrAssembly clrAsm) { static readonly UTF8String clrAsmName_SystemRuntimeWindowsRuntime = new UTF8String("System.Runtime.WindowsRuntime"); static readonly UTF8String clrAsmName_SystemRuntimeWindowsRuntimeUIXaml = new UTF8String("System.Runtime.WindowsRuntime.UI.Xaml"); - static byte[] GetPublicKeyToken(ClrAssembly clrAsm) { - switch (clrAsm) { - case ClrAssembly.Mscorlib: return neutralPublicKey; - case ClrAssembly.SystemNumericsVectors: return contractPublicKeyToken; - case ClrAssembly.SystemObjectModel: return contractPublicKeyToken; - case ClrAssembly.SystemRuntime: return contractPublicKeyToken; - case ClrAssembly.SystemRuntimeInteropServicesWindowsRuntime: return contractPublicKeyToken; - case ClrAssembly.SystemRuntimeWindowsRuntime: return neutralPublicKey; - case ClrAssembly.SystemRuntimeWindowsRuntimeUIXaml: return neutralPublicKey; - default: throw new InvalidOperationException(); - } - } + static byte[] GetPublicKeyToken(ClrAssembly clrAsm) => + clrAsm switch { + ClrAssembly.Mscorlib => neutralPublicKey, + ClrAssembly.SystemNumericsVectors => contractPublicKeyToken, + ClrAssembly.SystemObjectModel => contractPublicKeyToken, + ClrAssembly.SystemRuntime => contractPublicKeyToken, + ClrAssembly.SystemRuntimeInteropServicesWindowsRuntime => contractPublicKeyToken, + ClrAssembly.SystemRuntimeWindowsRuntime => neutralPublicKey, + ClrAssembly.SystemRuntimeWindowsRuntimeUIXaml => neutralPublicKey, + _ => throw new InvalidOperationException(), + }; static readonly byte[] contractPublicKeyToken = new byte[] { 0xB0, 0x3F, 0x5F, 0x7F, 0x11, 0xD5, 0x0A, 0x3A }; static readonly byte[] neutralPublicKey = new byte[] { 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 }; @@ -258,10 +246,7 @@ static byte[] GetPublicKeyToken(ClrAssembly clrAsm) { /// Owner module or null /// Type /// - public static TypeRef ToCLR(ModuleDef module, TypeDef td) { - bool isClrValueType; - return ToCLR(module, td, out isClrValueType); - } + public static TypeRef ToCLR(ModuleDef module, TypeDef td) => ToCLR(module, td, out bool isClrValueType); /// /// Converts WinMD type to a CLR type. Returns null @@ -273,14 +258,13 @@ public static TypeRef ToCLR(ModuleDef module, TypeDef td) { /// public static TypeRef ToCLR(ModuleDef module, TypeDef td, out bool isClrValueType) { isClrValueType = false; - if (td == null || !td.IsWindowsRuntime) + if (td is null || !td.IsWindowsRuntime) return null; var asm = td.DefinitionAssembly; - if (asm == null || !asm.IsContentTypeWindowsRuntime) + if (asm is null || !asm.IsContentTypeWindowsRuntime) return null; - ProjectedClass pc; - if (!winMDToCLR.TryGetValue(new ClassName(td.Namespace, td.Name), out pc)) + if (!winMDToCLR.TryGetValue(new ClassName(td.Namespace, td.Name), out var pc)) return null; isClrValueType = pc.ClrClass.IsValueType; @@ -294,10 +278,7 @@ public static TypeRef ToCLR(ModuleDef module, TypeDef td, out bool isClrValueTyp /// Owner module or null /// Type /// - public static TypeRef ToCLR(ModuleDef module, TypeRef tr) { - bool isClrValueType; - return ToCLR(module, tr, out isClrValueType); - } + public static TypeRef ToCLR(ModuleDef module, TypeRef tr) => ToCLR(module, tr, out bool isClrValueType); /// /// Converts WinMD type to a CLR type. Returns null @@ -309,16 +290,15 @@ public static TypeRef ToCLR(ModuleDef module, TypeRef tr) { /// public static TypeRef ToCLR(ModuleDef module, TypeRef tr, out bool isClrValueType) { isClrValueType = false; - if (tr == null) + if (tr is null) return null; var defAsm = tr.DefinitionAssembly; - if (defAsm == null || !defAsm.IsContentTypeWindowsRuntime) + if (defAsm is null || !defAsm.IsContentTypeWindowsRuntime) return null; - if (tr.DeclaringType != null) + if (tr.DeclaringType is not null) return null; - ProjectedClass pc; - if (!winMDToCLR.TryGetValue(new ClassName(tr.Namespace, tr.Name), out pc)) + if (!winMDToCLR.TryGetValue(new ClassName(tr.Namespace, tr.Name), out var pc)) return null; isClrValueType = pc.ClrClass.IsValueType; @@ -333,16 +313,15 @@ public static TypeRef ToCLR(ModuleDef module, TypeRef tr, out bool isClrValueTyp /// Type /// public static ExportedType ToCLR(ModuleDef module, ExportedType et) { - if (et == null) + if (et is null) return null; var defAsm = et.DefinitionAssembly; - if (defAsm == null || !defAsm.IsContentTypeWindowsRuntime) + if (defAsm is null || !defAsm.IsContentTypeWindowsRuntime) return null; - if (et.DeclaringType != null) + if (et.DeclaringType is not null) return null; - ProjectedClass pc; - if (!winMDToCLR.TryGetValue(new ClassName(et.TypeNamespace, et.TypeName), out pc)) + if (!winMDToCLR.TryGetValue(new ClassName(et.TypeNamespace, et.TypeName), out var pc)) return null; return new ExportedTypeUser(module, 0, pc.ClrClass.Namespace, pc.ClrClass.Name, et.Attributes, CreateAssembly(module, pc.ContractAssembly)); @@ -356,7 +335,7 @@ public static ExportedType ToCLR(ModuleDef module, ExportedType et) { /// Type /// public static TypeSig ToCLR(ModuleDef module, TypeSig ts) { - if (ts == null) + if (ts is null) return null; var et = ts.ElementType; if (et != ElementType.Class && et != ElementType.ValueType) @@ -364,17 +343,16 @@ public static TypeSig ToCLR(ModuleDef module, TypeSig ts) { var tdr = ((ClassOrValueTypeSig)ts).TypeDefOrRef; - TypeDef td; TypeRef tr, newTr; bool isClrValueType; - if ((td = tdr as TypeDef) != null) { + if (tdr is TypeDef td) { newTr = ToCLR(module, td, out isClrValueType); - if (newTr == null) + if (newTr is null) return null; } - else if ((tr = tdr as TypeRef) != null) { + else if ((tr = tdr as TypeRef) is not null) { newTr = ToCLR(module, tr, out isClrValueType); - if (newTr == null) + if (newTr is null) return null; } else @@ -395,37 +373,35 @@ public static TypeSig ToCLR(ModuleDef module, TypeSig ts) { public static MemberRef ToCLR(ModuleDef module, MemberRef mr) { // See WinMDAdapter::CheckIfMethodImplImplementsARedirectedInterface // in coreclr: md/winmd/adapter.cpp - if (mr == null) + if (mr is null) return null; if (mr.Name != CloseName) return null; var msig = mr.MethodSig; - if (msig == null) + if (msig is null) return null; var cl = mr.Class; IMemberRefParent newCl; - TypeRef tr; TypeSpec ts; - if ((tr = cl as TypeRef) != null) { + if (cl is TypeRef tr) { var newTr = ToCLR(module, tr); - if (newTr == null || !IsIDisposable(newTr)) + if (newTr is null || !IsIDisposable(newTr)) return null; newCl = newTr; } - else if ((ts = cl as TypeSpec) != null) { + else if ((ts = cl as TypeSpec) is not null) { var gis = ts.TypeSig as GenericInstSig; - if (gis == null || !(gis.GenericType is ClassSig)) + if (gis is null || !(gis.GenericType is ClassSig)) return null; tr = gis.GenericType.TypeRef; - if (tr == null) + if (tr is null) return null; - bool isClrValueType; - var newTr = ToCLR(module, tr, out isClrValueType); - if (newTr == null || !IsIDisposable(newTr)) + var newTr = ToCLR(module, tr, out bool isClrValueType); + if (newTr is null || !IsIDisposable(newTr)) return null; newCl = new TypeSpecUser(new GenericInstSig(isClrValueType ? @@ -440,9 +416,7 @@ public static MemberRef ToCLR(ModuleDef module, MemberRef mr) { static readonly UTF8String CloseName = new UTF8String("Close"); static readonly UTF8String DisposeName = new UTF8String("Dispose"); - static bool IsIDisposable(TypeRef tr) { - return tr.Name == IDisposableName && tr.Namespace == IDisposableNamespace; - } + static bool IsIDisposable(TypeRef tr) => tr.Name == IDisposableName && tr.Namespace == IDisposableNamespace; static readonly UTF8String IDisposableNamespace = new UTF8String("System"); static readonly UTF8String IDisposableName = new UTF8String("IDisposable"); @@ -454,16 +428,16 @@ static bool IsIDisposable(TypeRef tr) { /// Method /// public static MemberRef ToCLR(ModuleDef module, MethodDef md) { - if (md == null) + if (md is null) return null; if (md.Name != CloseName) return null; var declType = md.DeclaringType; - if (declType == null) + if (declType is null) return null; var tr = ToCLR(module, declType); - if (tr == null || !IsIDisposable(tr)) + if (tr is null || !IsIDisposable(tr)) return null; return new MemberRefUser(md.Module, DisposeName, md.MethodSig, tr); diff --git a/src/DotNet/WinMDStatus.cs b/src/DotNet/WinMDStatus.cs index e285ce368..d60cb7ee5 100644 --- a/src/DotNet/WinMDStatus.cs +++ b/src/DotNet/WinMDStatus.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info namespace dnlib.DotNet { /// diff --git a/src/DotNet/Writer/ArrayWriter.cs b/src/DotNet/Writer/ArrayWriter.cs new file mode 100644 index 000000000..d997ee5d8 --- /dev/null +++ b/src/DotNet/Writer/ArrayWriter.cs @@ -0,0 +1,150 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Diagnostics; + +namespace dnlib.DotNet.Writer { + /// + /// Writes data + /// + public unsafe struct ArrayWriter { + /// + /// Gets the current position + /// + public int Position { + get => position; + set => position = value; + } + + readonly byte[] data; + int position; + + /// + /// Constructor + /// + /// Destination array + public ArrayWriter(byte[] data) { + this.data = data; + position = 0; + } + + /// + /// Writes a + /// + /// Value + public void WriteSByte(sbyte value) => data[position++] = (byte)value; + + /// + /// Writes a + /// + /// Value + public void WriteByte(byte value) => data[position++] = value; + + /// + /// Writes a + /// + /// Value + public void WriteInt16(short value) { + data[position++] = (byte)value; + data[position++] = (byte)(value >> 8); + } + + /// + /// Writes a + /// + /// Value + public void WriteUInt16(ushort value) { + data[position++] = (byte)value; + data[position++] = (byte)(value >> 8); + } + + /// + /// Writes a + /// + /// Value + public void WriteInt32(int value) { + Debug.Assert(this.position + 4 <= data.Length); + var position = this.position; + fixed (byte* p = data) + *(int*)(p + position) = value; + this.position = position + 4; + } + + /// + /// Writes a + /// + /// Value + public void WriteUInt32(uint value) { + Debug.Assert(this.position + 4 <= data.Length); + var position = this.position; + fixed (byte* p = data) + *(uint*)(p + position) = value; + this.position = position + 4; + } + + /// + /// Writes a + /// + /// Value + public void WriteInt64(long value) { + Debug.Assert(this.position + 8 <= data.Length); + var position = this.position; + fixed (byte* p = data) + *(long*)(p + position) = value; + this.position = position + 8; + } + + /// + /// Writes a + /// + /// Value + public void WriteUInt64(ulong value) { + Debug.Assert(this.position + 8 <= data.Length); + var position = this.position; + fixed (byte* p = data) + *(ulong*)(p + position) = value; + this.position = position + 8; + } + + /// + /// Writes a + /// + /// Value + public void WriteSingle(float value) { + Debug.Assert(this.position + 4 <= data.Length); + var position = this.position; + fixed (byte* p = data) + *(float*)(p + position) = value; + this.position = position + 4; + } + + /// + /// Writes a + /// + /// Value + public void WriteDouble(double value) { + Debug.Assert(this.position + 8 <= data.Length); + var position = this.position; + fixed (byte* p = data) + *(double*)(p + position) = value; + this.position = position + 8; + } + + /// + /// Writes bytes + /// + /// Bytes + public void WriteBytes(byte[] source) => WriteBytes(source, 0, source.Length); + + /// + /// Writes bytes + /// + /// Bytes + /// Source index + /// Number of bytes to write + public void WriteBytes(byte[] source, int index, int length) { + Array.Copy(source, index, data, position, length); + position += length; + } + } +} diff --git a/src/DotNet/Writer/BinaryReaderChunk.cs b/src/DotNet/Writer/BinaryReaderChunk.cs deleted file mode 100644 index 5d06428d5..000000000 --- a/src/DotNet/Writer/BinaryReaderChunk.cs +++ /dev/null @@ -1,74 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System.IO; -using dnlib.IO; -using dnlib.PE; - -namespace dnlib.DotNet.Writer { - /// - /// A chunk - /// - public class BinaryReaderChunk : IChunk { - FileOffset offset; - RVA rva; - readonly IBinaryReader data; - readonly uint virtualSize; - - /// - /// Gets the data - /// - public IBinaryReader Data { - get { return data; } - } - - /// - public FileOffset FileOffset { - get { return offset; } - } - - /// - public RVA RVA { - get { return rva; } - } - - /// - /// Constructor - /// - /// The data - public BinaryReaderChunk(IBinaryReader data) - : this(data, (uint)data.Length) { - } - - /// - /// Constructor - /// - /// The data - /// Virtual size of - public BinaryReaderChunk(IBinaryReader data, uint virtualSize) { - this.data = data; - this.virtualSize = virtualSize; - } - - /// - public void SetOffset(FileOffset offset, RVA rva) { - this.offset = offset; - this.rva = rva; - } - - /// - public uint GetFileLength() { - return (uint)data.Length; - } - - /// - public uint GetVirtualSize() { - return virtualSize; - } - - /// - public void WriteTo(BinaryWriter writer) { - data.Position = 0; - data.WriteTo(writer); - } - } -} diff --git a/src/DotNet/Writer/BlobHeap.cs b/src/DotNet/Writer/BlobHeap.cs index d86ceac2e..b34b1729d 100644 --- a/src/DotNet/Writer/BlobHeap.cs +++ b/src/DotNet/Writer/BlobHeap.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.IO; using dnlib.IO; @@ -18,9 +18,7 @@ public sealed class BlobHeap : HeapBase, IOffsetHeap { Dictionary userRawData; /// - public override string Name { - get { return "#Blob"; } - } + public override string Name => "#Blob"; /// /// Populates blobs from an existing (eg. to preserve @@ -30,31 +28,29 @@ public override string Name { public void Populate(BlobStream blobStream) { if (isReadOnly) throw new ModuleWriterException("Trying to modify #Blob when it's read-only"); - if (originalData != null) + if (originalData is not null) throw new InvalidOperationException("Can't call method twice"); if (nextOffset != 1) throw new InvalidOperationException("Add() has already been called"); - if (blobStream == null || blobStream.ImageStreamLength == 0) + if (blobStream is null || blobStream.StreamLength == 0) return; - using (var reader = blobStream.GetClonedImageStream()) { - originalData = reader.ReadAllBytes(); - nextOffset = (uint)originalData.Length; - Populate(reader); - } + var reader = blobStream.CreateReader(); + originalData = reader.ToArray(); + nextOffset = (uint)originalData.Length; + Populate(ref reader); } - void Populate(IImageStream reader) { + void Populate(ref DataReader reader) { reader.Position = 1; while (reader.Position < reader.Length) { - uint offset = (uint)reader.Position; - uint len; - if (!reader.ReadCompressedUInt32(out len)) { + uint offset = reader.Position; + if (!reader.TryReadCompressedUInt32(out uint len)) { if (offset == reader.Position) reader.Position++; continue; } - if (len == 0 || reader.Position + len > reader.Length) + if (len == 0 || (ulong)reader.Position + len > reader.Length) continue; var data = reader.ReadBytes((int)len); @@ -71,11 +67,10 @@ void Populate(IImageStream reader) { public uint Add(byte[] data) { if (isReadOnly) throw new ModuleWriterException("Trying to modify #Blob when it's read-only"); - if (data == null || data.Length == 0) + if (data is null || data.Length == 0) return 0; - uint offset; - if (cachedDict.TryGetValue(data, out offset)) + if (cachedDict.TryGetValue(data, out uint offset)) return offset; return AddToCache(data); } @@ -88,7 +83,7 @@ public uint Add(byte[] data) { public uint Create(byte[] data) { if (isReadOnly) throw new ModuleWriterException("Trying to modify #Blob when it's read-only"); - return AddToCache(data ?? new byte[0]); + return AddToCache(data ?? Array2.Empty()); } uint AddToCache(byte[] data) { @@ -100,58 +95,51 @@ uint AddToCache(byte[] data) { } /// - public override uint GetRawLength() { - return nextOffset; - } + public override uint GetRawLength() => nextOffset; /// - protected override void WriteToImpl(BinaryWriter writer) { - if (originalData != null) - writer.Write(originalData); + protected override void WriteToImpl(DataWriter writer) { + if (originalData is not null) + writer.WriteBytes(originalData); else - writer.Write((byte)0); + writer.WriteByte(0); - uint offset = originalData != null ? (uint)originalData.Length : 1; + uint offset = originalData is not null ? (uint)originalData.Length : 1; foreach (var data in cached) { int rawLen = GetRawDataSize(data); - byte[] rawData; - if (userRawData != null && userRawData.TryGetValue(offset, out rawData)) { + if (userRawData is not null && userRawData.TryGetValue(offset, out var rawData)) { if (rawData.Length != rawLen) throw new InvalidOperationException("Invalid length of raw data"); - writer.Write(rawData); + writer.WriteBytes(rawData); } else { writer.WriteCompressedUInt32((uint)data.Length); - writer.Write(data); + writer.WriteBytes(data); } offset += (uint)rawLen; } } /// - public int GetRawDataSize(byte[] data) { - return Utils.GetCompressedUInt32Length((uint)data.Length) + data.Length; - } + public int GetRawDataSize(byte[] data) => DataWriter.GetCompressedUInt32Length((uint)data.Length) + data.Length; /// public void SetRawData(uint offset, byte[] rawData) { - if (rawData == null) - throw new ArgumentNullException("rawData"); - if (userRawData == null) + if (userRawData is null) userRawData = new Dictionary(); - userRawData[offset] = rawData; + userRawData[offset] = rawData ?? throw new ArgumentNullException(nameof(rawData)); } /// public IEnumerable> GetAllRawData() { var memStream = new MemoryStream(); - var writer = new BinaryWriter(memStream); - uint offset = originalData != null ? (uint)originalData.Length : 1; + var writer = new DataWriter(memStream); + uint offset = originalData is not null ? (uint)originalData.Length : 1; foreach (var data in cached) { memStream.Position = 0; memStream.SetLength(0); writer.WriteCompressedUInt32((uint)data.Length); - writer.Write(data); + writer.WriteBytes(data); yield return new KeyValuePair(offset, memStream.ToArray()); offset += (uint)memStream.Length; } diff --git a/src/DotNet/Writer/ByteArrayChunk.cs b/src/DotNet/Writer/ByteArrayChunk.cs index 75f1f9a89..4a6b4c3a4 100644 --- a/src/DotNet/Writer/ByteArrayChunk.cs +++ b/src/DotNet/Writer/ByteArrayChunk.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System.IO; +using System; using dnlib.IO; using dnlib.PE; @@ -8,27 +8,22 @@ namespace dnlib.DotNet.Writer { /// /// Stores a byte array /// - public sealed class ByteArrayChunk : IChunk { + public sealed class ByteArrayChunk : IReuseChunk { readonly byte[] array; + readonly uint alignment; FileOffset offset; RVA rva; /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } - } + public RVA RVA => rva; /// /// Gets the data /// - public byte[] Data { - get { return array; } - } + public byte[] Data => array; /// /// Constructor @@ -38,10 +33,14 @@ public byte[] Data { /// return value will be different if you modify the array). If /// it's never inserted as a key in a dictionary, then the contents can be modified, /// but shouldn't be resized after has been called. - public ByteArrayChunk(byte[] array) { - this.array = array ?? new byte[0]; + /// The alignment of the data + public ByteArrayChunk(byte[] array, uint alignment = 0) { + this.array = array ?? Array2.Empty(); + this.alignment = alignment; } + bool IReuseChunk.CanReuse(RVA origRva, uint origSize) => (uint)array.Length <= origSize; + /// public void SetOffset(FileOffset offset, RVA rva) { this.offset = offset; @@ -49,29 +48,24 @@ public void SetOffset(FileOffset offset, RVA rva) { } /// - public uint GetFileLength() { - return (uint)array.Length; - } + public uint GetFileLength() => (uint)array.Length; /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetVirtualSize() => GetFileLength(); /// - public void WriteTo(BinaryWriter writer) { - writer.Write(array); - } + public uint CalculateAlignment() => alignment; /// - public override int GetHashCode() { - return Utils.GetHashCode(array); - } + public void WriteTo(DataWriter writer) => writer.WriteBytes(array); + + /// + public override int GetHashCode() => Utils.GetHashCode(array); /// public override bool Equals(object obj) { var other = obj as ByteArrayChunk; - return other != null && Utils.Equals(array, other.array); + return other is not null && Utils.Equals(array, other.array); } } } diff --git a/src/DotNet/Writer/ChecksumAlgorithm.cs b/src/DotNet/Writer/ChecksumAlgorithm.cs new file mode 100644 index 000000000..c0094189e --- /dev/null +++ b/src/DotNet/Writer/ChecksumAlgorithm.cs @@ -0,0 +1,28 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.DotNet.Writer { + /// + /// Checksum algorithm + /// + public enum ChecksumAlgorithm { + /// + /// SHA-1 + /// + SHA1, + + /// + /// SHA-256 + /// + SHA256, + + /// + /// SHA-384 + /// + SHA384, + + /// + /// SHA-512 + /// + SHA512, + } +} diff --git a/src/DotNet/Writer/ChunkList.cs b/src/DotNet/Writer/ChunkList.cs index 9b44b7f66..2f2fd0848 100644 --- a/src/DotNet/Writer/ChunkList.cs +++ b/src/DotNet/Writer/ChunkList.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; namespace dnlib.DotNet.Writer { @@ -11,9 +11,7 @@ public class ChunkList : ChunkListBase where T : class, IChunk { /// /// Default constructor /// - public ChunkList() { - this.chunks = new List(); - } + public ChunkList() => chunks = new List(); /// /// Add a @@ -23,7 +21,7 @@ public ChunkList() { public void Add(T chunk, uint alignment) { if (setOffsetCalled) throw new InvalidOperationException("SetOffset() has already been called"); - if (chunk != null) + if (chunk is not null) chunks.Add(new Elem(chunk, alignment)); } @@ -35,7 +33,8 @@ public void Add(T chunk, uint alignment) { public uint? Remove(T chunk) { if (setOffsetCalled) throw new InvalidOperationException("SetOffset() has already been called"); - if (chunk != null) { + if (chunk is not null) { + var chunks = this.chunks; for (int i = 0; i < chunks.Count; i++) { if (chunks[i].chunk == chunk) { uint alignment = chunks[i].alignment; diff --git a/src/DotNet/Writer/ChunkListBase.cs b/src/DotNet/Writer/ChunkListBase.cs index 0e7d89867..b7bd2b40d 100644 --- a/src/DotNet/Writer/ChunkListBase.cs +++ b/src/DotNet/Writer/ChunkListBase.cs @@ -1,7 +1,7 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; -using System.IO; +using System; +using System.Collections.Generic; using dnlib.IO; using dnlib.PE; @@ -20,10 +20,12 @@ public abstract class ChunkListBase : IChunk where T : IChunk { FileOffset offset; RVA rva; + internal bool IsEmpty => chunks.Count == 0; + /// /// Helper struct /// - protected struct Elem { + protected readonly struct Elem { /// Data public readonly T chunk; /// Alignment @@ -50,31 +52,22 @@ protected sealed class ElemEqualityComparer : IEqualityComparer { /// Constructor /// /// Compares the chunk type - public ElemEqualityComparer(IEqualityComparer chunkComparer) { - this.chunkComparer = chunkComparer; - } + public ElemEqualityComparer(IEqualityComparer chunkComparer) => this.chunkComparer = chunkComparer; /// - public bool Equals(Elem x, Elem y) { - return x.alignment == y.alignment && - chunkComparer.Equals(x.chunk, y.chunk); - } + public bool Equals(Elem x, Elem y) => + x.alignment == y.alignment && + chunkComparer.Equals(x.chunk, y.chunk); /// - public int GetHashCode(Elem obj) { - return (int)obj.alignment + chunkComparer.GetHashCode(obj.chunk); - } + public int GetHashCode(Elem obj) => (int)obj.alignment + chunkComparer.GetHashCode(obj.chunk); } /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } - } + public RVA RVA => rva; /// public virtual void SetOffset(FileOffset offset, RVA rva) { @@ -89,34 +82,52 @@ public virtual void SetOffset(FileOffset offset, RVA rva) { offset += paddingF; rva += paddingV; elem.chunk.SetOffset(offset, rva); - uint chunkLenF = elem.chunk.GetFileLength(); - uint chunkLenV = elem.chunk.GetVirtualSize(); - offset += chunkLenF; - rva += chunkLenV; - length += paddingF + chunkLenF; - virtualSize += paddingV + chunkLenV; + if (elem.chunk.GetVirtualSize() == 0) { + offset -= paddingF; + rva -= paddingV; + } + else { + uint chunkLenF = elem.chunk.GetFileLength(); + uint chunkLenV = elem.chunk.GetVirtualSize(); + offset += chunkLenF; + rva += chunkLenV; + length += paddingF + chunkLenF; + virtualSize += paddingV + chunkLenV; + } } } /// - public uint GetFileLength() { - return length; - } + public uint GetFileLength() => length; /// - public uint GetVirtualSize() { - return virtualSize; - } + public uint GetVirtualSize() => virtualSize; /// - public void WriteTo(BinaryWriter writer) { - FileOffset offset2 = offset; + public void WriteTo(DataWriter writer) { + var offset2 = offset; foreach (var elem in chunks) { + if (elem.chunk.GetVirtualSize() == 0) + continue; int paddingF = (int)offset2.AlignUp(elem.alignment) - (int)offset2; - writer.WriteZeros(paddingF); + writer.WriteZeroes(paddingF); elem.chunk.VerifyWriteTo(writer); offset2 += (uint)paddingF + elem.chunk.GetFileLength(); } } + + /// + public virtual uint CalculateAlignment() { + uint alignment = 0; + for (int i = 0; i < chunks.Count; i++) { + var elem = chunks[i]; + uint newAlignment = Math.Max(elem.alignment, elem.chunk.CalculateAlignment()); + chunks[i] = new Elem(elem.chunk, newAlignment); + + alignment = Math.Max(alignment, newAlignment); + } + + return alignment; + } } } diff --git a/src/DotNet/Writer/CustomAttributeWriter.cs b/src/DotNet/Writer/CustomAttributeWriter.cs index 113452736..2aed607af 100644 --- a/src/DotNet/Writer/CustomAttributeWriter.cs +++ b/src/DotNet/Writer/CustomAttributeWriter.cs @@ -1,14 +1,15 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.IO; +using System.Text; namespace dnlib.DotNet.Writer { /// /// Helps write custom attributes /// - public interface ICustomAttributeWriterHelper : IWriterError, IFullNameCreatorHelper { + public interface ICustomAttributeWriterHelper : IWriterError, IFullNameFactoryHelper { } /// @@ -17,8 +18,10 @@ public interface ICustomAttributeWriterHelper : IWriterError, IFullNameCreatorHe public struct CustomAttributeWriter : IDisposable { readonly ICustomAttributeWriterHelper helper; RecursionCounter recursionCounter; + readonly StringBuilder sb; readonly MemoryStream outStream; - readonly BinaryWriter writer; + readonly DataWriter writer; + readonly bool disposeStream; GenericArguments genericArguments; /// @@ -34,6 +37,13 @@ public static byte[] Write(ICustomAttributeWriterHelper helper, CustomAttribute } } + internal static byte[] Write(ICustomAttributeWriterHelper helper, CustomAttribute ca, DataWriterContext context) { + using (var writer = new CustomAttributeWriter(helper, context)) { + writer.Write(ca); + return writer.GetResult(); + } + } + /// /// Writes custom attribute named arguments /// @@ -47,20 +57,39 @@ internal static byte[] Write(ICustomAttributeWriterHelper helper, IList namedArgs, DataWriterContext context) { + using (var writer = new CustomAttributeWriter(helper, context)) { + writer.Write(namedArgs); + return writer.GetResult(); + } + } + CustomAttributeWriter(ICustomAttributeWriterHelper helper) { this.helper = helper; - this.recursionCounter = new RecursionCounter(); - this.outStream = new MemoryStream(); - this.writer = new BinaryWriter(outStream); - this.genericArguments = null; + recursionCounter = new RecursionCounter(); + sb = new StringBuilder(); + outStream = new MemoryStream(); + writer = new DataWriter(outStream); + genericArguments = null; + disposeStream = true; } - byte[] GetResult() { - return outStream.ToArray(); + CustomAttributeWriter(ICustomAttributeWriterHelper helper, DataWriterContext context) { + this.helper = helper; + recursionCounter = new RecursionCounter(); + sb = new StringBuilder(); + outStream = context.OutStream; + writer = context.Writer; + genericArguments = null; + disposeStream = false; + outStream.SetLength(0); + outStream.Position = 0; } + byte[] GetResult() => outStream.ToArray(); + void Write(CustomAttribute ca) { - if (ca == null) { + if (ca is null) { helper.Error("The custom attribute is null"); return; } @@ -68,78 +97,67 @@ void Write(CustomAttribute ca) { // Check whether it's raw first. If it is, we don't care whether the ctor is // invalid. Just use the raw data. if (ca.IsRawBlob) { - if ((ca.ConstructorArguments != null && ca.ConstructorArguments.Count > 0) || (ca.NamedArguments != null && ca.NamedArguments.Count > 0)) + if ((ca.ConstructorArguments is not null && ca.ConstructorArguments.Count > 0) || (ca.NamedArguments is not null && ca.NamedArguments.Count > 0)) helper.Error("Raw custom attribute contains arguments and/or named arguments"); - writer.Write(ca.RawData); + writer.WriteBytes(ca.RawData); return; } - if (ca.Constructor == null) { + if (ca.Constructor is null) { helper.Error("Custom attribute ctor is null"); return; } var methodSig = GetMethodSig(ca.Constructor); - if (methodSig == null) { + if (methodSig is null) { helper.Error("Custom attribute ctor's method signature is invalid"); return; } if (ca.ConstructorArguments.Count != methodSig.Params.Count) helper.Error("Custom attribute arguments count != method sig arguments count"); - if (methodSig.ParamsAfterSentinel != null && methodSig.ParamsAfterSentinel.Count > 0) + if (methodSig.ParamsAfterSentinel is not null && methodSig.ParamsAfterSentinel.Count > 0) helper.Error("Custom attribute ctor has parameters after the sentinel"); if (ca.NamedArguments.Count > ushort.MaxValue) helper.Error("Custom attribute has too many named arguments"); - // A generic custom attribute isn't allowed by most .NET languages (eg. C#) but - // the CLR probably supports it. - var mrCtor = ca.Constructor as MemberRef; - if (mrCtor != null) { - var owner = mrCtor.Class as TypeSpec; - if (owner != null) { - var gis = owner.TypeSig as GenericInstSig; - if (gis != null) { - genericArguments = new GenericArguments(); - genericArguments.PushTypeArgs(gis.GenericArguments); - } - } + if (ca.Constructor is MemberRef mrCtor && mrCtor.Class is TypeSpec owner && owner.TypeSig is GenericInstSig gis) { + genericArguments = new GenericArguments(); + genericArguments.PushTypeArgs(gis.GenericArguments); } - writer.Write((ushort)1); + writer.WriteUInt16((ushort)1); int numArgs = Math.Min(methodSig.Params.Count, ca.ConstructorArguments.Count); for (int i = 0; i < numArgs; i++) WriteValue(FixTypeSig(methodSig.Params[i]), ca.ConstructorArguments[i]); int numNamedArgs = Math.Min((int)ushort.MaxValue, ca.NamedArguments.Count); - writer.Write((ushort)numNamedArgs); + writer.WriteUInt16((ushort)numNamedArgs); for (int i = 0; i < numNamedArgs; i++) Write(ca.NamedArguments[i]); } void Write(IList namedArgs) { - if (namedArgs == null || namedArgs.Count > 0x1FFFFFFF) { + if (namedArgs is null || namedArgs.Count > 0x1FFFFFFF) { helper.Error("Too many custom attribute named arguments"); - namedArgs = new CANamedArgument[0]; + namedArgs = Array2.Empty(); } writer.WriteCompressedUInt32((uint)namedArgs.Count); for (int i = 0; i < namedArgs.Count; i++) Write(namedArgs[i]); } - TypeSig FixTypeSig(TypeSig type) { - return SubstituteGenericParameter(type.RemoveModifiers()).RemoveModifiers(); - } + TypeSig FixTypeSig(TypeSig type) => SubstituteGenericParameter(type.RemoveModifiers()).RemoveModifiers(); TypeSig SubstituteGenericParameter(TypeSig type) { - if (genericArguments == null) + if (genericArguments is null) return type; return genericArguments.Resolve(type); } void WriteValue(TypeSig argType, CAArgument value) { - if (argType == null || value.Type == null) { + if (argType is null || value.Type is null) { helper.Error("Custom attribute argument type is null"); return; } @@ -148,10 +166,9 @@ void WriteValue(TypeSig argType, CAArgument value) { return; } - var arrayType = argType as SZArraySig; - if (arrayType != null) { + if (argType is SZArraySig arrayType) { var argsArray = value.Value as IList; - if (argsArray == null && value.Value != null) + if (argsArray is null && value.Value is not null) helper.Error("CAArgument.Value is not null or an array"); WriteArrayValue(arrayType, argsArray); } @@ -162,15 +179,15 @@ void WriteValue(TypeSig argType, CAArgument value) { } void WriteArrayValue(SZArraySig arrayType, IList args) { - if (arrayType == null) { + if (arrayType is null) { helper.Error("Custom attribute: Array type is null"); return; } - if (args == null) - writer.Write(uint.MaxValue); + if (args is null) + writer.WriteUInt32(uint.MaxValue); else { - writer.Write((uint)args.Count); + writer.WriteUInt32((uint)args.Count); var arrayElementType = FixTypeSig(arrayType.Next); for (int i = 0; i < args.Count; i++) WriteValue(arrayElementType, args[i]); @@ -194,44 +211,43 @@ bool VerifyTypeAndValue(CAArgument value, ElementType etype, Type valueType) { helper.Error("Custom attribute arg type != value.Type"); return false; } - return value.Value == null || value.Value.GetType() == valueType; + return value.Value is null || value.Value.GetType() == valueType; } static bool VerifyType(TypeSig type, ElementType etype) { type = type.RemoveModifiers(); // Assume it's an enum if it's a ValueType - return type != null && (etype == type.ElementType || type.ElementType == ElementType.ValueType); + return type is not null && (etype == type.ElementType || type.ElementType == ElementType.ValueType); } static bool VerifyValue(object o, ElementType etype) { - if (o == null) + if (o is null) return false; - switch (Type.GetTypeCode(o.GetType())) { - case TypeCode.Boolean: return etype == ElementType.Boolean; - case TypeCode.Char: return etype == ElementType.Char; - case TypeCode.SByte: return etype == ElementType.I1; - case TypeCode.Byte: return etype == ElementType.U1; - case TypeCode.Int16: return etype == ElementType.I2; - case TypeCode.UInt16: return etype == ElementType.U2; - case TypeCode.Int32: return etype == ElementType.I4; - case TypeCode.UInt32: return etype == ElementType.U4; - case TypeCode.Int64: return etype == ElementType.I8; - case TypeCode.UInt64: return etype == ElementType.U8; - case TypeCode.Single: return etype == ElementType.R4; - case TypeCode.Double: return etype == ElementType.R8; - default: return false; - } + return Type.GetTypeCode(o.GetType()) switch { + TypeCode.Boolean => etype == ElementType.Boolean, + TypeCode.Char => etype == ElementType.Char, + TypeCode.SByte => etype == ElementType.I1, + TypeCode.Byte => etype == ElementType.U1, + TypeCode.Int16 => etype == ElementType.I2, + TypeCode.UInt16 => etype == ElementType.U2, + TypeCode.Int32 => etype == ElementType.I4, + TypeCode.UInt32 => etype == ElementType.U4, + TypeCode.Int64 => etype == ElementType.I8, + TypeCode.UInt64 => etype == ElementType.U8, + TypeCode.Single => etype == ElementType.R4, + TypeCode.Double => etype == ElementType.R8, + _ => false, + }; } static ulong ToUInt64(object o) { - ulong result; - ToUInt64(o, out result); + ToUInt64(o, out ulong result); return result; } static bool ToUInt64(object o, out ulong result) { - if (o == null) { + if (o is null) { result = 0; return false; } @@ -291,13 +307,12 @@ static bool ToUInt64(object o, out ulong result) { } static double ToDouble(object o) { - double result; - ToDouble(o, out result); + ToDouble(o, out double result); return result; } static bool ToDouble(object o, out double result) { - if (o == null) { + if (o is null) { result = double.NaN; return false; } @@ -362,10 +377,10 @@ static bool ToDouble(object o, out double result) { /// The ctor arg type, field type, or property type /// The value to write void WriteElem(TypeSig argType, CAArgument value) { - if (argType == null) { + if (argType is null) { helper.Error("Custom attribute: Arg type is null"); argType = value.Type; - if (argType == null) + if (argType is null) return; } if (!recursionCounter.Increment()) { @@ -378,86 +393,86 @@ void WriteElem(TypeSig argType, CAArgument value) { switch (argType.ElementType) { case ElementType.Boolean: if (!VerifyTypeAndValue(value, ElementType.Boolean)) - writer.Write(ToUInt64(value.Value) != 0); + writer.WriteBoolean(ToUInt64(value.Value) != 0); else - writer.Write((bool)value.Value); + writer.WriteBoolean((bool)value.Value); break; case ElementType.Char: if (!VerifyTypeAndValue(value, ElementType.Char)) - writer.Write((ushort)ToUInt64(value.Value)); + writer.WriteUInt16((ushort)ToUInt64(value.Value)); else - writer.Write((ushort)(char)value.Value); + writer.WriteUInt16((ushort)(char)value.Value); break; case ElementType.I1: if (!VerifyTypeAndValue(value, ElementType.I1)) - writer.Write((sbyte)ToUInt64(value.Value)); + writer.WriteSByte((sbyte)ToUInt64(value.Value)); else - writer.Write((sbyte)value.Value); + writer.WriteSByte((sbyte)value.Value); break; case ElementType.U1: if (!VerifyTypeAndValue(value, ElementType.U1)) - writer.Write((byte)ToUInt64(value.Value)); + writer.WriteByte((byte)ToUInt64(value.Value)); else - writer.Write((byte)value.Value); + writer.WriteByte((byte)value.Value); break; case ElementType.I2: if (!VerifyTypeAndValue(value, ElementType.I2)) - writer.Write((short)ToUInt64(value.Value)); + writer.WriteInt16((short)ToUInt64(value.Value)); else - writer.Write((short)value.Value); + writer.WriteInt16((short)value.Value); break; case ElementType.U2: if (!VerifyTypeAndValue(value, ElementType.U2)) - writer.Write((ushort)ToUInt64(value.Value)); + writer.WriteUInt16((ushort)ToUInt64(value.Value)); else - writer.Write((ushort)value.Value); + writer.WriteUInt16((ushort)value.Value); break; case ElementType.I4: if (!VerifyTypeAndValue(value, ElementType.I4)) - writer.Write((int)ToUInt64(value.Value)); + writer.WriteInt32((int)ToUInt64(value.Value)); else - writer.Write((int)value.Value); + writer.WriteInt32((int)value.Value); break; case ElementType.U4: if (!VerifyTypeAndValue(value, ElementType.U4)) - writer.Write((uint)ToUInt64(value.Value)); + writer.WriteUInt32((uint)ToUInt64(value.Value)); else - writer.Write((uint)value.Value); + writer.WriteUInt32((uint)value.Value); break; case ElementType.I8: if (!VerifyTypeAndValue(value, ElementType.I8)) - writer.Write((long)ToUInt64(value.Value)); + writer.WriteInt64((long)ToUInt64(value.Value)); else - writer.Write((long)value.Value); + writer.WriteInt64((long)value.Value); break; case ElementType.U8: if (!VerifyTypeAndValue(value, ElementType.U8)) - writer.Write(ToUInt64(value.Value)); + writer.WriteUInt64(ToUInt64(value.Value)); else - writer.Write((ulong)value.Value); + writer.WriteUInt64((ulong)value.Value); break; case ElementType.R4: if (!VerifyTypeAndValue(value, ElementType.R4)) - writer.Write((float)ToDouble(value.Value)); + writer.WriteSingle((float)ToDouble(value.Value)); else - writer.Write((float)value.Value); + writer.WriteSingle((float)value.Value); break; case ElementType.R8: if (!VerifyTypeAndValue(value, ElementType.R8)) - writer.Write(ToDouble(value.Value)); + writer.WriteDouble(ToDouble(value.Value)); else - writer.Write((double)value.Value); + writer.WriteDouble((double)value.Value); break; case ElementType.String: @@ -472,7 +487,7 @@ void WriteElem(TypeSig argType, CAArgument value) { case ElementType.ValueType: tdr = ((TypeDefOrRefSig)argType).TypeDefOrRef; underlyingType = GetEnumUnderlyingType(argType); - if (underlyingType != null) + if (underlyingType is not null) WriteElem(underlyingType, value); else if (tdr is TypeRef && TryWriteEnumUnderlyingTypeValue(value.Value)) { // No error. Assume it's an enum that couldn't be resolved. @@ -485,10 +500,9 @@ void WriteElem(TypeSig argType, CAArgument value) { tdr = ((TypeDefOrRefSig)argType).TypeDefOrRef; if (CheckCorLibType(argType, "Type")) { if (CheckCorLibType(value.Type, "Type")) { - var ts = value.Value as TypeSig; - if (ts != null) + if (value.Value is TypeSig ts) WriteType(ts); - else if (value.Value == null) + else if (value.Value is null) WriteUTF8String(null); else { helper.Error("Custom attribute value is not a type"); @@ -545,19 +559,19 @@ void WriteElem(TypeSig argType, CAArgument value) { } bool TryWriteEnumUnderlyingTypeValue(object o) { - if (o == null) + if (o is null) return false; switch (Type.GetTypeCode(o.GetType())) { - case TypeCode.Boolean: writer.Write((bool)o); break; - case TypeCode.Char: writer.Write((ushort)(char)o); break; - case TypeCode.SByte: writer.Write((sbyte)o); break; - case TypeCode.Byte: writer.Write((byte)o); break; - case TypeCode.Int16: writer.Write((short)o); break; - case TypeCode.UInt16: writer.Write((ushort)o); break; - case TypeCode.Int32: writer.Write((int)o); break; - case TypeCode.UInt32: writer.Write((uint)o); break; - case TypeCode.Int64: writer.Write((long)o); break; - case TypeCode.UInt64: writer.Write((ulong)o); break; + case TypeCode.Boolean: writer.WriteBoolean((bool)o); break; + case TypeCode.Char: writer.WriteUInt16((ushort)(char)o); break; + case TypeCode.SByte: writer.WriteSByte((sbyte)o); break; + case TypeCode.Byte: writer.WriteByte((byte)o); break; + case TypeCode.Int16: writer.WriteInt16((short)o); break; + case TypeCode.UInt16: writer.WriteUInt16((ushort)o); break; + case TypeCode.Int32: writer.WriteInt32((int)o); break; + case TypeCode.UInt32: writer.WriteUInt32((uint)o); break; + case TypeCode.Int64: writer.WriteInt64((long)o); break; + case TypeCode.UInt64: writer.WriteUInt64((ulong)o); break; default: return false; } return true; @@ -570,16 +584,16 @@ bool TryWriteEnumUnderlyingTypeValue(object o) { /// The underlying type or null if we couldn't resolve the type ref static TypeSig GetEnumUnderlyingType(TypeSig type) { var td = GetEnumTypeDef(type); - if (td == null) + if (td is null) return null; return td.GetEnumUnderlyingType().RemoveModifiers(); } static TypeDef GetEnumTypeDef(TypeSig type) { - if (type == null) + if (type is null) return null; var td = GetTypeDef(type); - if (td == null) + if (td is null) return null; if (!td.IsEnum) return null; @@ -594,14 +608,13 @@ static TypeDef GetEnumTypeDef(TypeSig type) { /// A or null if we couldn't resolve the /// or if is a type spec static TypeDef GetTypeDef(TypeSig type) { - var tdr = type as TypeDefOrRefSig; - if (tdr != null) { + if (type is TypeDefOrRefSig tdr) { var td = tdr.TypeDef; - if (td != null) + if (td is not null) return td; var tr = tdr.TypeRef; - if (tr != null) + if (tr is not null) return tr.Resolve(); } @@ -609,7 +622,7 @@ static TypeDef GetTypeDef(TypeSig type) { } void Write(CANamedArgument namedArg) { - if (namedArg == null) { + if (namedArg is null) { helper.Error("Custom attribute named arg is null"); return; } @@ -619,9 +632,9 @@ void Write(CANamedArgument namedArg) { } if (namedArg.IsProperty) - writer.Write((byte)SerializationType.Property); + writer.WriteByte((byte)SerializationType.Property); else - writer.Write((byte)SerializationType.Field); + writer.WriteByte((byte)SerializationType.Field); WriteFieldOrPropType(namedArg.Type); WriteUTF8String(namedArg.Name); @@ -632,7 +645,7 @@ void Write(CANamedArgument namedArg) { void WriteFieldOrPropType(TypeSig type) { type = type.RemoveModifiers(); - if (type == null) { + if (type is null) { helper.Error("Custom attribute: Field/property type is null"); return; } @@ -643,34 +656,34 @@ void WriteFieldOrPropType(TypeSig type) { ITypeDefOrRef tdr; switch (type.ElementType) { - case ElementType.Boolean: writer.Write((byte)SerializationType.Boolean); break; - case ElementType.Char: writer.Write((byte)SerializationType.Char); break; - case ElementType.I1: writer.Write((byte)SerializationType.I1); break; - case ElementType.U1: writer.Write((byte)SerializationType.U1); break; - case ElementType.I2: writer.Write((byte)SerializationType.I2); break; - case ElementType.U2: writer.Write((byte)SerializationType.U2); break; - case ElementType.I4: writer.Write((byte)SerializationType.I4); break; - case ElementType.U4: writer.Write((byte)SerializationType.U4); break; - case ElementType.I8: writer.Write((byte)SerializationType.I8); break; - case ElementType.U8: writer.Write((byte)SerializationType.U8); break; - case ElementType.R4: writer.Write((byte)SerializationType.R4); break; - case ElementType.R8: writer.Write((byte)SerializationType.R8); break; - case ElementType.String: writer.Write((byte)SerializationType.String); break; - case ElementType.Object: writer.Write((byte)SerializationType.TaggedObject); break; + case ElementType.Boolean: writer.WriteByte((byte)SerializationType.Boolean); break; + case ElementType.Char: writer.WriteByte((byte)SerializationType.Char); break; + case ElementType.I1: writer.WriteByte((byte)SerializationType.I1); break; + case ElementType.U1: writer.WriteByte((byte)SerializationType.U1); break; + case ElementType.I2: writer.WriteByte((byte)SerializationType.I2); break; + case ElementType.U2: writer.WriteByte((byte)SerializationType.U2); break; + case ElementType.I4: writer.WriteByte((byte)SerializationType.I4); break; + case ElementType.U4: writer.WriteByte((byte)SerializationType.U4); break; + case ElementType.I8: writer.WriteByte((byte)SerializationType.I8); break; + case ElementType.U8: writer.WriteByte((byte)SerializationType.U8); break; + case ElementType.R4: writer.WriteByte((byte)SerializationType.R4); break; + case ElementType.R8: writer.WriteByte((byte)SerializationType.R8); break; + case ElementType.String: writer.WriteByte((byte)SerializationType.String); break; + case ElementType.Object: writer.WriteByte((byte)SerializationType.TaggedObject); break; case ElementType.SZArray: - writer.Write((byte)SerializationType.SZArray); + writer.WriteByte((byte)SerializationType.SZArray); WriteFieldOrPropType(type.Next); break; case ElementType.Class: tdr = ((TypeDefOrRefSig)type).TypeDefOrRef; if (CheckCorLibType(type, "Type")) - writer.Write((byte)SerializationType.Type); + writer.WriteByte((byte)SerializationType.Type); else if (tdr is TypeRef) { // Could be an enum TypeRef that couldn't be resolved, so the code // assumed it's a class and created a ClassSig. - writer.Write((byte)SerializationType.Enum); + writer.WriteByte((byte)SerializationType.Enum); WriteType(tdr); } else @@ -681,20 +694,20 @@ void WriteFieldOrPropType(TypeSig type) { tdr = ((TypeDefOrRefSig)type).TypeDefOrRef; var enumType = GetEnumTypeDef(type); // If TypeRef => assume it's an enum that couldn't be resolved - if (enumType != null || tdr is TypeRef) { - writer.Write((byte)SerializationType.Enum); + if (enumType is not null || tdr is TypeRef) { + writer.WriteByte((byte)SerializationType.Enum); WriteType(tdr); } else { helper.Error("Custom attribute type doesn't seem to be an enum."); - writer.Write((byte)SerializationType.Enum); + writer.WriteByte((byte)SerializationType.Enum); WriteType(tdr); } break; default: helper.Error("Custom attribute: Invalid type"); - writer.Write((byte)0xFF); + writer.WriteByte((byte)0xFF); break; } @@ -702,23 +715,25 @@ void WriteFieldOrPropType(TypeSig type) { } void WriteType(IType type) { - if (type == null) { + if (type is null) { helper.Error("Custom attribute: Type is null"); WriteUTF8String(UTF8String.Empty); } - else - WriteUTF8String(FullNameCreator.AssemblyQualifiedName(type, helper)); + else { + sb.Length = 0; + WriteUTF8String(FullNameFactory.AssemblyQualifiedName(type, helper, sb)); + } } static bool CheckCorLibType(TypeSig ts, string name) { var tdrs = ts as TypeDefOrRefSig; - if (tdrs == null) + if (tdrs is null) return false; return CheckCorLibType(tdrs.TypeDefOrRef, name); } static bool CheckCorLibType(ITypeDefOrRef tdr, string name) { - if (tdr == null) + if (tdr is null) return false; if (!tdr.DefinitionAssembly.IsCorLib()) return false; @@ -727,25 +742,23 @@ static bool CheckCorLibType(ITypeDefOrRef tdr, string name) { return tdr.TypeName == name && tdr.Namespace == "System"; } - static MethodSig GetMethodSig(ICustomAttributeType ctor) { - return ctor == null ? null : ctor.MethodSig; - } + static MethodSig GetMethodSig(ICustomAttributeType ctor) => ctor?.MethodSig; void WriteUTF8String(UTF8String s) { - if ((object)s == null || s.Data == null) - writer.Write((byte)0xFF); + if (s is null || s.Data is null) + writer.WriteByte((byte)0xFF); else { writer.WriteCompressedUInt32((uint)s.Data.Length); - writer.Write(s.Data); + writer.WriteBytes(s.Data); } } /// public void Dispose() { - if (outStream != null) + if (!disposeStream) + return; + if (outStream is not null) outStream.Dispose(); - if (writer != null) - ((IDisposable)writer).Dispose(); } } } diff --git a/src/DotNet/Writer/DataReaderChunk.cs b/src/DotNet/Writer/DataReaderChunk.cs new file mode 100644 index 000000000..cf9a20c0d --- /dev/null +++ b/src/DotNet/Writer/DataReaderChunk.cs @@ -0,0 +1,98 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using dnlib.IO; +using dnlib.PE; + +namespace dnlib.DotNet.Writer { + /// + /// A chunk + /// + public class DataReaderChunk : IChunk { + FileOffset offset; + RVA rva; + DataReader data; + readonly uint virtualSize; + bool setOffsetCalled; + + /// + public FileOffset FileOffset => offset; + + /// + public RVA RVA => rva; + + /// + /// Constructor + /// + /// The data + public DataReaderChunk(DataReader data) + : this(ref data) { + } + + /// + /// Constructor + /// + /// The data + /// Virtual size of + public DataReaderChunk(DataReader data, uint virtualSize) + : this(ref data, virtualSize) { + } + + /// + /// Constructor + /// + /// The data + internal DataReaderChunk(ref DataReader data) + : this(ref data, (uint)data.Length) { + } + + /// + /// Constructor + /// + /// The data + /// Virtual size of + internal DataReaderChunk(ref DataReader data, uint virtualSize) { + this.data = data; + this.virtualSize = virtualSize; + } + + /// + /// Gets the data reader + /// + public DataReader CreateReader() => data; + + /// + /// Replaces the old data with new data. The new data must be the same size as the old data if + /// has been called. That method gets called after + /// event + /// + /// + public void SetData(DataReader newData) { + if (setOffsetCalled && newData.Length != data.Length) + throw new InvalidOperationException("New data must be the same size as the old data after SetOffset() has been called"); + data = newData; + } + + /// + public void SetOffset(FileOffset offset, RVA rva) { + this.offset = offset; + this.rva = rva; + setOffsetCalled = true; + } + + /// + public uint GetFileLength() => (uint)data.Length; + + /// + public uint GetVirtualSize() => virtualSize; + + /// + public uint CalculateAlignment() => 0; + + /// + public void WriteTo(DataWriter writer) { + data.Position = 0; + data.CopyTo(writer); + } + } +} diff --git a/src/DotNet/Writer/DataReaderHeap.cs b/src/DotNet/Writer/DataReaderHeap.cs new file mode 100644 index 000000000..8a4f2b40c --- /dev/null +++ b/src/DotNet/Writer/DataReaderHeap.cs @@ -0,0 +1,48 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using dnlib.DotNet.MD; +using dnlib.IO; + +namespace dnlib.DotNet.Writer { + /// + /// Copies existing data to a new metadata heap + /// + public sealed class DataReaderHeap : HeapBase { + /// + /// Gets the name of the heap + /// + public override string Name { get; } + + internal DotNetStream OptionalOriginalStream { get; } + + readonly DataReader heapReader; + + /// + /// Constructor + /// + /// The stream whose data will be copied to the new metadata file + public DataReaderHeap(DotNetStream stream) { + OptionalOriginalStream = stream ?? throw new ArgumentNullException(nameof(stream)); + heapReader = stream.CreateReader(); + Name = stream.Name; + } + + /// + /// Constructor + /// + /// Heap name + /// Heap content + public DataReaderHeap(string name, DataReader heapReader) { + this.heapReader = heapReader; + this.heapReader.Position = 0; + Name = name ?? throw new ArgumentNullException(nameof(name)); + } + + /// + public override uint GetRawLength() => heapReader.Length; + + /// + protected override void WriteToImpl(DataWriter writer) => heapReader.CopyTo(writer); + } +} diff --git a/src/DotNet/Writer/DataWriter.cs b/src/DotNet/Writer/DataWriter.cs new file mode 100644 index 000000000..50a5eb242 --- /dev/null +++ b/src/DotNet/Writer/DataWriter.cs @@ -0,0 +1,257 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.IO; + +namespace dnlib.DotNet.Writer { + /// + /// Writes data + /// + public sealed class DataWriter { + readonly Stream stream; + readonly byte[] buffer; + const int BUFFER_LEN = 8; + + internal Stream InternalStream => stream; + + /// + /// Gets/sets the position + /// + public long Position { + get => stream.Position; + set => stream.Position = value; + } + + /// + /// Constructor + /// + /// Destination stream + public DataWriter(Stream stream) { + if (stream is null) + ThrowArgumentNullException(nameof(stream)); + this.stream = stream; + buffer = new byte[BUFFER_LEN]; + } + + static void ThrowArgumentNullException(string paramName) => throw new ArgumentNullException(paramName); + static void ThrowArgumentOutOfRangeException(string message) => throw new ArgumentOutOfRangeException(message); + + /// + /// Writes a + /// + /// Value + public void WriteBoolean(bool value) => stream.WriteByte(value ? (byte)1 : (byte)0); + + /// + /// Writes a + /// + /// Value + public void WriteSByte(sbyte value) => stream.WriteByte((byte)value); + + /// + /// Writes a + /// + /// Value + public void WriteByte(byte value) => stream.WriteByte(value); + + /// + /// Writes a + /// + /// Value + public void WriteInt16(short value) { + var buffer = this.buffer; + buffer[0] = (byte)value; + buffer[1] = (byte)(value >> 8); + stream.Write(buffer, 0, 2); + } + + /// + /// Writes a + /// + /// Value + public void WriteUInt16(ushort value) { + var buffer = this.buffer; + buffer[0] = (byte)value; + buffer[1] = (byte)(value >> 8); + stream.Write(buffer, 0, 2); + } + + /// + /// Writes a + /// + /// Value + public void WriteInt32(int value) { + var buffer = this.buffer; + buffer[0] = (byte)value; + buffer[1] = (byte)(value >> 8); + buffer[2] = (byte)(value >> 16); + buffer[3] = (byte)(value >> 24); + stream.Write(buffer, 0, 4); + } + + /// + /// Writes a + /// + /// Value + public void WriteUInt32(uint value) { + var buffer = this.buffer; + buffer[0] = (byte)value; + buffer[1] = (byte)(value >> 8); + buffer[2] = (byte)(value >> 16); + buffer[3] = (byte)(value >> 24); + stream.Write(buffer, 0, 4); + } + + /// + /// Writes a + /// + /// Value + public void WriteInt64(long value) { + var buffer = this.buffer; + buffer[0] = (byte)value; + buffer[1] = (byte)(value >> 8); + buffer[2] = (byte)(value >> 16); + buffer[3] = (byte)(value >> 24); + buffer[4] = (byte)(value >> 32); + buffer[5] = (byte)(value >> 40); + buffer[6] = (byte)(value >> 48); + buffer[7] = (byte)(value >> 56); + stream.Write(buffer, 0, 8); + } + + /// + /// Writes a + /// + /// Value + public void WriteUInt64(ulong value) { + var buffer = this.buffer; + buffer[0] = (byte)value; + buffer[1] = (byte)(value >> 8); + buffer[2] = (byte)(value >> 16); + buffer[3] = (byte)(value >> 24); + buffer[4] = (byte)(value >> 32); + buffer[5] = (byte)(value >> 40); + buffer[6] = (byte)(value >> 48); + buffer[7] = (byte)(value >> 56); + stream.Write(buffer, 0, 8); + } + + /// + /// Writes a + /// + /// Value + public unsafe void WriteSingle(float value) { + uint tmp = *(uint*)&value; + var buffer = this.buffer; + buffer[0] = (byte)tmp; + buffer[1] = (byte)(tmp >> 8); + buffer[2] = (byte)(tmp >> 16); + buffer[3] = (byte)(tmp >> 24); + stream.Write(buffer, 0, 4); + } + + /// + /// Writes a + /// + /// Value + public unsafe void WriteDouble(double value) { + ulong tmp = *(ulong*)&value; + var buffer = this.buffer; + buffer[0] = (byte)tmp; + buffer[1] = (byte)(tmp >> 8); + buffer[2] = (byte)(tmp >> 16); + buffer[3] = (byte)(tmp >> 24); + buffer[4] = (byte)(tmp >> 32); + buffer[5] = (byte)(tmp >> 40); + buffer[6] = (byte)(tmp >> 48); + buffer[7] = (byte)(tmp >> 56); + stream.Write(buffer, 0, 8); + } + + /// + /// Writes bytes + /// + /// Bytes to write + public void WriteBytes(byte[] source) => stream.Write(source, 0, source.Length); + + /// + /// Writes bytes + /// + /// Bytes to write + /// Index to start copying from + /// Number of bytes to copy + public void WriteBytes(byte[] source, int index, int length) => stream.Write(source, index, length); + + /// + /// Writes a compressed + /// + /// Value + public void WriteCompressedUInt32(uint value) { + var stream = this.stream; + if (value <= 0x7F) + stream.WriteByte((byte)value); + else if (value <= 0x3FFF) { + stream.WriteByte((byte)((value >> 8) | 0x80)); + stream.WriteByte((byte)value); + } + else if (value <= 0x1FFFFFFF) { + var buffer = this.buffer; + buffer[0] = (byte)((value >> 24) | 0xC0); + buffer[1] = (byte)(value >> 16); + buffer[2] = (byte)(value >> 8); + buffer[3] = (byte)value; + stream.Write(buffer, 0, 4); + } + else + ThrowArgumentOutOfRangeException("UInt32 value can't be compressed"); + } + + /// + /// Writes a compressed + /// + /// + public void WriteCompressedInt32(int value) { + var stream = this.stream; + // This is almost identical to compressing a UInt32, except that we first + // recode value so the sign bit is in bit 0. Then we compress it the same + // way a UInt32 is compressed. + uint sign = (uint)value >> 31; + if (-0x40 <= value && value <= 0x3F) { + uint v = (uint)((value & 0x3F) << 1) | sign; + stream.WriteByte((byte)v); + } + else if (-0x2000 <= value && value <= 0x1FFF) { + uint v = ((uint)(value & 0x1FFF) << 1) | sign; + stream.WriteByte((byte)((v >> 8) | 0x80)); + stream.WriteByte((byte)v); + } + else if (-0x10000000 <= value && value <= 0x0FFFFFFF) { + uint v = ((uint)(value & 0x0FFFFFFF) << 1) | sign; + var buffer = this.buffer; + buffer[0] = (byte)((v >> 24) | 0xC0); + buffer[1] = (byte)(v >> 16); + buffer[2] = (byte)(v >> 8); + buffer[3] = (byte)v; + stream.Write(buffer, 0, 4); + } + else + ThrowArgumentOutOfRangeException("Int32 value can't be compressed"); + } + + /// + /// Gets the size of a compressed , see + /// + /// Value + /// + public static int GetCompressedUInt32Length(uint value) { + if (value <= 0x7F) + return 1; + if (value <= 0x3FFF) + return 2; + if (value <= 0x1FFFFFFF) + return 4; + ThrowArgumentOutOfRangeException("UInt32 value can't be compressed"); + return 0; + } + } +} diff --git a/src/DotNet/Writer/DebugDirectory.cs b/src/DotNet/Writer/DebugDirectory.cs index 750f4327d..f8d09faaa 100644 --- a/src/DotNet/Writer/DebugDirectory.cs +++ b/src/DotNet/Writer/DebugDirectory.cs @@ -1,105 +1,174 @@ // dnlib: See LICENSE.txt for more info using System; -using System.IO; +using System.Collections.Generic; using dnlib.DotNet.Pdb; using dnlib.IO; using dnlib.PE; namespace dnlib.DotNet.Writer { + /// + /// Debug directory entry + /// + public sealed class DebugDirectoryEntry { + /// + /// Gets the header + /// + public IMAGE_DEBUG_DIRECTORY DebugDirectory; + + /// + /// Gets the data + /// + public readonly IChunk Chunk; + + /// + /// Constructor + /// + /// Data + public DebugDirectoryEntry(IChunk chunk) => Chunk = chunk; + } + /// /// Debug directory chunk /// - public sealed class DebugDirectory : IChunk { + public sealed class DebugDirectory : IReuseChunk { + /// Default debug directory alignment + public const uint DEFAULT_DEBUGDIRECTORY_ALIGNMENT = 4; + internal const int HEADER_SIZE = 28; + + internal int Count => entries.Count; + FileOffset offset; RVA rva; - bool dontWriteAnything; uint length; - internal IMAGE_DEBUG_DIRECTORY debugDirData; - uint timeDateStamp; - byte[] data; + readonly List entries; + bool isReadonly; + + /// + public FileOffset FileOffset => offset; + + /// + public RVA RVA => rva; /// - /// Size of + /// Constructor /// - public const int HEADER_SIZE = 28; + public DebugDirectory() => entries = new List(); /// - /// Gets/sets the time date stamp that should be written. This should be the same time date - /// stamp that is written to the PE header. + /// Adds data /// - public uint TimeDateStamp { - get { return timeDateStamp; } - set { timeDateStamp = value; } - } + /// Data + /// + public DebugDirectoryEntry Add(byte[] data) => Add(new ByteArrayChunk(data)); /// - /// Gets/sets the raw debug data + /// Adds data /// - public byte[] Data { - get { return data; } - set { data = value; } + /// Data + /// + public DebugDirectoryEntry Add(IChunk chunk) { + if (isReadonly) + throw new InvalidOperationException("Can't add a new DebugDirectory entry when the DebugDirectory is read-only!"); + var entry = new DebugDirectoryEntry(chunk); + entries.Add(entry); + return entry; } /// - /// Set it to true if eg. the PDB file couldn't be created. If true, the size - /// of this chunk will be 0. + /// Adds data /// - public bool DontWriteAnything { - get { return dontWriteAnything; } - set { - if (length != 0) - throw new InvalidOperationException("SetOffset() has already been called"); - dontWriteAnything = value; - } - } + /// Data + /// Debug type + /// Major version + /// Minor version + /// Timestamp + /// + public DebugDirectoryEntry Add(byte[] data, ImageDebugType type, ushort majorVersion, ushort minorVersion, uint timeDateStamp) => + Add(new ByteArrayChunk(data), type, majorVersion, minorVersion, timeDateStamp); - /// - public FileOffset FileOffset { - get { return offset; } + /// + /// Adds data + /// + /// Data + /// Debug type + /// Major version + /// Minor version + /// Timestamp + /// + public DebugDirectoryEntry Add(IChunk chunk, ImageDebugType type, ushort majorVersion, ushort minorVersion, uint timeDateStamp) { + var entry = Add(chunk); + entry.DebugDirectory.Type = type; + entry.DebugDirectory.MajorVersion = majorVersion; + entry.DebugDirectory.MinorVersion = minorVersion; + entry.DebugDirectory.TimeDateStamp = timeDateStamp; + return entry; } - /// - public RVA RVA { - get { return rva; } + bool IReuseChunk.CanReuse(RVA origRva, uint origSize) { + uint newLength = GetLength(entries, (FileOffset)origRva, origRva); + if (newLength > origSize) + return false; + + isReadonly = true; + return true; } /// public void SetOffset(FileOffset offset, RVA rva) { + isReadonly = true; this.offset = offset; this.rva = rva; - length = HEADER_SIZE; - if (data != null) // Could be null if dontWriteAnything is true - length += (uint)data.Length; + length = GetLength(entries, offset, rva); } - /// - public uint GetFileLength() { - if (dontWriteAnything) - return 0; + static uint GetLength(List entries, FileOffset offset, RVA rva) { + uint length = HEADER_SIZE * (uint)entries.Count; + foreach (var entry in entries) { + length = Utils.AlignUp(length, DEFAULT_DEBUGDIRECTORY_ALIGNMENT); + entry.Chunk.SetOffset(offset + length, rva + length); + length += entry.Chunk.GetFileLength(); + } return length; } /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetFileLength() => length; /// - public void WriteTo(BinaryWriter writer) { - if (dontWriteAnything) - return; - - writer.Write(debugDirData.Characteristics); - writer.Write(timeDateStamp); - writer.Write(debugDirData.MajorVersion); - writer.Write(debugDirData.MinorVersion); - writer.Write(debugDirData.Type); - writer.Write(debugDirData.SizeOfData); - writer.Write((uint)rva + HEADER_SIZE); - writer.Write((uint)offset + HEADER_SIZE); - writer.Write(data); + public uint GetVirtualSize() => GetFileLength(); + + /// + public uint CalculateAlignment() => 0; + + /// + public void WriteTo(DataWriter writer) { + uint offset = 0; + foreach (var entry in entries) { + writer.WriteUInt32(entry.DebugDirectory.Characteristics); + writer.WriteUInt32(entry.DebugDirectory.TimeDateStamp); + writer.WriteUInt16(entry.DebugDirectory.MajorVersion); + writer.WriteUInt16(entry.DebugDirectory.MinorVersion); + writer.WriteUInt32((uint)entry.DebugDirectory.Type); + uint length = entry.Chunk.GetFileLength(); + writer.WriteUInt32(length); + writer.WriteUInt32(length == 0 ? 0 : (uint)entry.Chunk.RVA); + writer.WriteUInt32(length == 0 ? 0 : (uint)entry.Chunk.FileOffset); + offset += HEADER_SIZE; + } + + foreach (var entry in entries) { + WriteAlign(writer, ref offset); + entry.Chunk.VerifyWriteTo(writer); + offset += entry.Chunk.GetFileLength(); + } + } + + static void WriteAlign(DataWriter writer, ref uint offs) { + uint align = Utils.AlignUp(offs, DEFAULT_DEBUGDIRECTORY_ALIGNMENT) - offs; + offs += align; + writer.WriteZeroes((int)align); } } } diff --git a/src/DotNet/Writer/DeclSecurityWriter.cs b/src/DotNet/Writer/DeclSecurityWriter.cs index 71d40c528..8624d9e66 100644 --- a/src/DotNet/Writer/DeclSecurityWriter.cs +++ b/src/DotNet/Writer/DeclSecurityWriter.cs @@ -1,5 +1,6 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info +using System; using System.Collections.Generic; using System.IO; using System.Text; @@ -8,9 +9,11 @@ namespace dnlib.DotNet.Writer { /// /// Writes DeclSecurity blobs /// - public struct DeclSecurityWriter : ICustomAttributeWriterHelper { + public readonly struct DeclSecurityWriter : ICustomAttributeWriterHelper { readonly ModuleDef module; readonly IWriterError helper; + readonly DataWriterContext context; + readonly bool optimizeCustomAttributeSerializedTypeNames; /// /// Creates a DeclSecurity blob from @@ -19,80 +22,90 @@ public struct DeclSecurityWriter : ICustomAttributeWriterHelper { /// List of s /// Helps this class /// A DeclSecurity blob - public static byte[] Write(ModuleDef module, IList secAttrs, IWriterError helper) { - return new DeclSecurityWriter(module, helper).Write(secAttrs); - } + public static byte[] Write(ModuleDef module, IList secAttrs, IWriterError helper) => + Write(module, secAttrs, helper, false); - DeclSecurityWriter(ModuleDef module, IWriterError helper) { + /// + /// Creates a DeclSecurity blob from + /// + /// Owner module + /// List of s + /// Helps this class + /// Optimize serialized type strings in custom attributes. + /// For more info, see + /// A DeclSecurity blob + public static byte[] Write(ModuleDef module, IList secAttrs, IWriterError helper, bool optimizeCustomAttributeSerializedTypeNames) => + new DeclSecurityWriter(module, helper, optimizeCustomAttributeSerializedTypeNames, null).Write(secAttrs); + + internal static byte[] Write(ModuleDef module, IList secAttrs, IWriterError helper, bool optimizeCustomAttributeSerializedTypeNames, DataWriterContext context) => + new DeclSecurityWriter(module, helper, optimizeCustomAttributeSerializedTypeNames, context).Write(secAttrs); + + DeclSecurityWriter(ModuleDef module, IWriterError helper, bool optimizeCustomAttributeSerializedTypeNames, DataWriterContext context) { this.module = module; this.helper = helper; + this.context = context; + this.optimizeCustomAttributeSerializedTypeNames = optimizeCustomAttributeSerializedTypeNames; } byte[] Write(IList secAttrs) { - if (secAttrs == null) - secAttrs = new SecurityAttribute[0]; + if (secAttrs is null) + secAttrs = Array2.Empty(); var xml = DeclSecurity.GetNet1xXmlStringInternal(secAttrs); - if (xml != null) + if (xml is not null) return WriteFormat1(xml); return WriteFormat2(secAttrs); } - byte[] WriteFormat1(string xml) { - return Encoding.Unicode.GetBytes(xml); - } + byte[] WriteFormat1(string xml) => Encoding.Unicode.GetBytes(xml); byte[] WriteFormat2(IList secAttrs) { - using (var stream = new MemoryStream()) - using (var writer = new BinaryWriter(stream)) { - writer.Write((byte)'.'); - WriteCompressedUInt32(writer, (uint)secAttrs.Count); + var sb = new StringBuilder(); + var stream = new MemoryStream(); + var writer = new DataWriter(stream); + writer.WriteByte((byte)'.'); + WriteCompressedUInt32(writer, (uint)secAttrs.Count); - foreach (var sa in secAttrs) { - if (sa == null) { - helper.Error("SecurityAttribute is null"); - Write(writer, UTF8String.Empty); - WriteCompressedUInt32(writer, 1); - WriteCompressedUInt32(writer, 0); - continue; - } - var attrType = sa.AttributeType; - string fqn; - if (attrType == null) { - helper.Error("SecurityAttribute attribute type is null"); - fqn = string.Empty; - } - else - fqn = attrType.AssemblyQualifiedName; - Write(writer, fqn); - - var namedArgsBlob = CustomAttributeWriter.Write(this, sa.NamedArguments); - if (namedArgsBlob.Length > 0x1FFFFFFF) { - helper.Error("Named arguments blob size doesn't fit in 29 bits"); - namedArgsBlob = new byte[0]; - } - WriteCompressedUInt32(writer, (uint)namedArgsBlob.Length); - writer.Write(namedArgsBlob); + int count = secAttrs.Count; + for (int i = 0; i < count; i++) { + var sa = secAttrs[i]; + if (sa is null) { + helper.Error("SecurityAttribute is null"); + Write(writer, UTF8String.Empty); + WriteCompressedUInt32(writer, 1); + WriteCompressedUInt32(writer, 0); + continue; + } + var attrType = sa.AttributeType; + string fqn; + if (attrType is null) { + helper.Error("SecurityAttribute attribute type is null"); + fqn = string.Empty; + } + else { + sb.Length = 0; + fqn = FullNameFactory.AssemblyQualifiedName(attrType, null, sb); } + Write(writer, fqn); - return stream.ToArray(); + var namedArgsBlob = context is null ? + CustomAttributeWriter.Write(this, sa.NamedArguments) : + CustomAttributeWriter.Write(this, sa.NamedArguments, context); + if (namedArgsBlob.Length > 0x1FFFFFFF) { + helper.Error("Named arguments blob size doesn't fit in 29 bits"); + namedArgsBlob = Array2.Empty(); + } + WriteCompressedUInt32(writer, (uint)namedArgsBlob.Length); + writer.WriteBytes(namedArgsBlob); } - } - uint WriteCompressedUInt32(BinaryWriter writer, uint value) { - return writer.WriteCompressedUInt32(helper, value); + return stream.ToArray(); } - void Write(BinaryWriter writer, UTF8String s) { - writer.Write(helper, s); - } - - void IWriterError.Error(string message) { - helper.Error(message); - } - - bool IFullNameCreatorHelper.MustUseAssemblyName(IType type) { - return FullNameCreator.MustUseAssemblyName(module, type); - } + uint WriteCompressedUInt32(DataWriter writer, uint value) => writer.WriteCompressedUInt32(helper, value); + void Write(DataWriter writer, UTF8String s) => writer.Write(helper, s); + void IWriterError.Error(string message) => helper.Error(message); + bool IFullNameFactoryHelper.MustUseAssemblyName(IType type) => + FullNameFactory.MustUseAssemblyName(module, type, optimizeCustomAttributeSerializedTypeNames); } } diff --git a/src/DotNet/Writer/Extensions.cs b/src/DotNet/Writer/Extensions.cs index 71a92c83d..d28bc84c6 100644 --- a/src/DotNet/Writer/Extensions.cs +++ b/src/DotNet/Writer/Extensions.cs @@ -1,24 +1,22 @@ // dnlib: See LICENSE.txt for more info -using System.IO; - namespace dnlib.DotNet.Writer { /// /// Extension methods /// - public static partial class Extensions { + static partial class Extensions { /// /// Write zeros /// /// this /// Number of zeros - public static void WriteZeros(this BinaryWriter writer, int count) { - if (count <= 0x20) { - for (int i = 0; i < count; i++) - writer.Write((byte)0); + public static void WriteZeroes(this DataWriter writer, int count) { + while (count >= 8) { + writer.WriteUInt64(0); + count -= 8; } - else - writer.Write(new byte[count]); + for (int i = 0; i < count; i++) + writer.WriteByte(0); } } } diff --git a/src/DotNet/Writer/GuidHeap.cs b/src/DotNet/Writer/GuidHeap.cs index 42c4e2336..0d674d155 100644 --- a/src/DotNet/Writer/GuidHeap.cs +++ b/src/DotNet/Writer/GuidHeap.cs @@ -1,21 +1,18 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; -using System.IO; namespace dnlib.DotNet.Writer { /// /// #GUID heap /// public sealed class GuidHeap : HeapBase, IOffsetHeap { - readonly List guids = new List(); + readonly Dictionary guids = new Dictionary(); Dictionary userRawData; /// - public override string Name { - get { return "#GUID"; } - } + public override string Name => "#GUID"; /// /// Adds a guid to the #GUID heap @@ -25,47 +22,39 @@ public override string Name { public uint Add(Guid? guid) { if (isReadOnly) throw new ModuleWriterException("Trying to modify #GUID when it's read-only"); - if (guid == null) + if (guid is null) return 0; - // The number of GUIDs will almost always be 1 so there's no need for a dictionary. - // The only table that contains GUIDs is the Module table, and it has three GUID - // columns. Only one of them (Mvid) is normally set and the others are null. - int index = guids.IndexOf(guid.Value); - if (index >= 0) - return (uint)index + 1; + if (guids.TryGetValue(guid.Value, out uint index)) + return index; - guids.Add(guid.Value); - return (uint)guids.Count; + index = (uint)guids.Count + 1; + guids.Add(guid.Value, index); + return index; } /// - public override uint GetRawLength() { - return (uint)guids.Count * 16; - } + public override uint GetRawLength() => (uint)guids.Count * 16; /// - protected override void WriteToImpl(BinaryWriter writer) { + protected override void WriteToImpl(DataWriter writer) { uint offset = 0; - foreach (var guid in guids) { - byte[] rawData; - if (userRawData == null || !userRawData.TryGetValue(offset, out rawData)) - rawData = guid.ToByteArray(); - writer.Write(rawData); + foreach (var kv in guids) { + if (userRawData is null || !userRawData.TryGetValue(offset, out var rawData)) + rawData = kv.Key.ToByteArray(); + writer.WriteBytes(rawData); offset += 16; } } /// - public int GetRawDataSize(Guid data) { - return 16; - } + public int GetRawDataSize(Guid data) => 16; /// public void SetRawData(uint offset, byte[] rawData) { - if (rawData == null || rawData.Length != 16) + if (rawData is null || rawData.Length != 16) throw new ArgumentException("Invalid size of GUID raw data"); - if (userRawData == null) + if (userRawData is null) userRawData = new Dictionary(); userRawData[offset] = rawData; } @@ -73,8 +62,8 @@ public void SetRawData(uint offset, byte[] rawData) { /// public IEnumerable> GetAllRawData() { uint offset = 0; - foreach (var guid in guids) { - yield return new KeyValuePair(offset, guid.ToByteArray()); + foreach (var kv in guids) { + yield return new KeyValuePair(offset, kv.Key.ToByteArray()); offset += 16; } } diff --git a/src/DotNet/Writer/Hasher.cs b/src/DotNet/Writer/Hasher.cs new file mode 100644 index 000000000..ddef9290b --- /dev/null +++ b/src/DotNet/Writer/Hasher.cs @@ -0,0 +1,73 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.IO; +using System.Security.Cryptography; + +namespace dnlib.DotNet.Writer { + static class Hasher { + static HashAlgorithm CreateHasher(ChecksumAlgorithm checksumAlgorithm) => + checksumAlgorithm switch { + ChecksumAlgorithm.SHA1 => SHA1.Create(), + ChecksumAlgorithm.SHA256 => SHA256.Create(), + ChecksumAlgorithm.SHA384 => SHA384.Create(), + ChecksumAlgorithm.SHA512 => SHA512.Create(), + _ => throw new ArgumentOutOfRangeException(nameof(checksumAlgorithm)), + }; + + public static string GetChecksumName(ChecksumAlgorithm checksumAlgorithm) => + // https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md#pdb-checksum-debug-directory-entry-type-19 + checksumAlgorithm switch { + ChecksumAlgorithm.SHA1 => "SHA1", + ChecksumAlgorithm.SHA256 => "SHA256", + ChecksumAlgorithm.SHA384 => "SHA384", + ChecksumAlgorithm.SHA512 => "SHA512", + _ => throw new ArgumentOutOfRangeException(nameof(checksumAlgorithm)), + }; + + public static bool TryGetChecksumAlgorithm(string checksumName, out ChecksumAlgorithm pdbChecksumAlgorithm, out int checksumSize) { + switch (checksumName) { + case "SHA1": + pdbChecksumAlgorithm = ChecksumAlgorithm.SHA1; + checksumSize = 20; + return true; + + case "SHA256": + pdbChecksumAlgorithm = ChecksumAlgorithm.SHA256; + checksumSize = 32; + return true; + + case "SHA384": + pdbChecksumAlgorithm = ChecksumAlgorithm.SHA384; + checksumSize = 48; + return true; + + case "SHA512": + pdbChecksumAlgorithm = ChecksumAlgorithm.SHA512; + checksumSize = 64; + return true; + + default: + pdbChecksumAlgorithm = 0; + checksumSize = -1; + return false; + } + } + + public static byte[] Hash(ChecksumAlgorithm checksumAlgorithm, Stream stream, long length) { + var buffer = new byte[(int)Math.Min(0x2000, length)]; + using (var hasher = CreateHasher(checksumAlgorithm)) { + while (length > 0) { + int len = (int)Math.Min(length, buffer.Length); + int read = stream.Read(buffer, 0, len); + if (read == 0) + throw new InvalidOperationException("Couldn't read all bytes"); + hasher.TransformBlock(buffer, 0, read, buffer, 0); + length -= read; + } + hasher.TransformFinalBlock(Array2.Empty(), 0, 0); + return hasher.Hash; + } + } + } +} diff --git a/src/DotNet/Writer/HeapBase.cs b/src/DotNet/Writer/HeapBase.cs index 88c1e7934..567850e4c 100644 --- a/src/DotNet/Writer/HeapBase.cs +++ b/src/DotNet/Writer/HeapBase.cs @@ -1,6 +1,5 @@ // dnlib: See LICENSE.txt for more info -using System.IO; using dnlib.IO; using dnlib.PE; @@ -19,50 +18,41 @@ public abstract class HeapBase : IHeap { protected bool isReadOnly; /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } - } + public RVA RVA => rva; /// public abstract string Name { get; } /// - public bool IsEmpty { - get { return GetRawLength() <= 1; } - } + public bool IsEmpty => GetRawLength() <= 1; /// /// true if offsets require 4 bytes instead of 2 bytes. /// - public bool IsBig { - get { return GetFileLength() > 0xFFFF; } - } + public bool IsBig => GetFileLength() > 0xFFFF; /// - public void SetReadOnly() { - isReadOnly = true; - } + public void SetReadOnly() => isReadOnly = true; /// public virtual void SetOffset(FileOffset offset, RVA rva) { this.offset = offset; this.rva = rva; + + // NOTE: This method can be called twice by NativeModuleWriter, see Metadata.SetOffset() for more info } /// - public uint GetFileLength() { - return Utils.AlignUp(GetRawLength(), ALIGNMENT); - } + public uint GetFileLength() => Utils.AlignUp(GetRawLength(), ALIGNMENT); /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetVirtualSize() => GetFileLength(); + + /// + public uint CalculateAlignment() => 0; /// /// Gets the raw length of the heap @@ -71,20 +61,18 @@ public uint GetVirtualSize() { public abstract uint GetRawLength(); /// - public void WriteTo(BinaryWriter writer) { + public void WriteTo(DataWriter writer) { WriteToImpl(writer); - writer.WriteZeros((int)(Utils.AlignUp(GetRawLength(), ALIGNMENT) - GetRawLength())); + writer.WriteZeroes((int)(Utils.AlignUp(GetRawLength(), ALIGNMENT) - GetRawLength())); } /// /// Writes all data to at its current location. /// /// Destination - protected abstract void WriteToImpl(BinaryWriter writer); + protected abstract void WriteToImpl(DataWriter writer); /// - public override string ToString() { - return Name; - } + public override string ToString() => Name; } } diff --git a/src/DotNet/Writer/HotHeap.cs b/src/DotNet/Writer/HotHeap.cs deleted file mode 100644 index be4a0f8d9..000000000 --- a/src/DotNet/Writer/HotHeap.cs +++ /dev/null @@ -1,243 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Collections.Generic; -using System.IO; -using dnlib.DotNet.MD; -using dnlib.IO; -using dnlib.PE; - -namespace dnlib.DotNet.Writer { - /// - /// The hot metadata heap: #!. MS' CLR 2.0+ can read this heap. It's used by native - /// images (NGEN'd) only, but if you patch the CLR, you can use it with normal non-NGEN'd - /// images as well. - /// It's only used by the CLR when the compressed heap (#~) is present, not when - /// the ENC heap (#-) is present. - /// - public abstract class HotHeap : HeapBase { - const uint HOT_HEAP_MAGIC = 0x484F4E44; // "HOND" - const int MAX_TABLES = (int)Table.GenericParamConstraint + 1; - const uint HOT_HEAP_DIR_SIZE = 4 + MAX_TABLES * 4; - const uint HH_ALIGNMENT = 4; - - uint totalLength; - readonly HotTable[] headers = new HotTable[MAX_TABLES]; - readonly List hotPools = new List(); - - /// - /// Gets the hot heap version - /// - public abstract HotHeapVersion HotHeapVersion { get; } - - /// - public override string Name { - get { return "#!"; } - } - - /// - /// Creates a instance - /// - /// The MD table - public abstract HotTable CreateHotTable(IMDTable mdTable); - - /// - /// Creates a instance - /// - /// Pool type - public abstract HotPool CreateHotPool(HeapType heapType); - - /// - /// Creates a hot heap instance - /// - /// Target module - public static HotHeap Create(ModuleDef module) { - return Create(GetHotHeapVersion(module)); - } - - /// - /// Creates a hot heap instance - /// - /// Hot heap version - public static HotHeap Create(HotHeapVersion version) { - switch (version) { - case HotHeapVersion.CLR20: return new HotHeap20(); - case HotHeapVersion.CLR40: return new HotHeap40(); - default: throw new ArgumentException("Invalid version"); - } - } - - /// - /// Returns the correct hot heap version that should be used - /// - /// Target module - public static HotHeapVersion GetHotHeapVersion(ModuleDef module) { - if (module.IsClr20) - return HotHeapVersion.CLR20; - return HotHeapVersion.CLR40; - } - - /// - /// Adds a hot table - /// - /// The hot table - public void Add(HotTable hotTable) { - var table = hotTable.Table; - if ((uint)table >= (uint)headers.Length) - throw new ArgumentException("Invalid table type"); - headers[(int)table] = hotTable; - } - - /// - /// Adds a hot pool - /// - /// The hot pool - public void Add(HotPool hotPool) { - hotPools.Add(hotPool); - } - - /// - public override void SetOffset(FileOffset offset, RVA rva) { - base.SetOffset(offset, rva); - - uint startOffs = (uint)offset; - - offset += HOT_HEAP_DIR_SIZE; - rva += HOT_HEAP_DIR_SIZE; - foreach (var header in headers) { - if (header == null) - continue; - header.SetOffset(offset, rva); - uint len = header.GetFileLength(); - offset += len; - rva += len; - Align(ref offset, ref rva); - } - - foreach (var hotPool in hotPools) { - Align(ref offset, ref rva); - hotPool.SetOffset(offset, rva); - uint len = hotPool.GetFileLength(); - offset += len; - rva += len; - } - - Align(ref offset, ref rva); - offset += hotPools.Count * 8; - rva += (uint)hotPools.Count * 8; - - offset += 8; - rva += 8; - - totalLength = (uint)offset - startOffs; - } - - static void Align(ref FileOffset offset, ref RVA rva) { - offset = offset.AlignUp(HH_ALIGNMENT); - rva = rva.AlignUp(HH_ALIGNMENT); - } - - /// - public override uint GetRawLength() { - return totalLength; - } - - /// - protected override void WriteToImpl(BinaryWriter writer) { - // The CLR doesn't verify this magic value - writer.Write(HOT_HEAP_MAGIC); - uint offs = HOT_HEAP_DIR_SIZE; - foreach (var header in headers) { - if (header == null) - writer.Write(0); - else { - writer.Write(offs); // any alignment, all bits are used - offs += header.GetFileLength(); - offs = Utils.AlignUp(offs, HH_ALIGNMENT); - } - } - - offs = HOT_HEAP_DIR_SIZE; - - foreach (var header in headers) { - if (header == null) - continue; - header.VerifyWriteTo(writer); - offs += header.GetFileLength(); - WriteAlign(writer, ref offs); - } - - var hotPoolOffset = new List(); - foreach (var hotPool in hotPools) { - WriteAlign(writer, ref offs); - hotPoolOffset.Add(writer.BaseStream.Position); - hotPool.VerifyWriteTo(writer); - offs += hotPool.GetFileLength(); - } - - WriteAlign(writer, ref offs); - long poolDBOffs = writer.BaseStream.Position; - for (int i = 0; i < hotPools.Count; i++) { - var hotPool = hotPools[i]; - writer.Write((uint)hotPool.HeapType); - // CLR 2.0: low 2 bits are ignored - // CLR 4.0: any alignment, all bits are used - writer.Write((uint)(poolDBOffs - hotPoolOffset[i] - hotPool.HeaderOffset)); - } - offs += (uint)hotPools.Count * 8; - - long hotMDDirOffs = writer.BaseStream.Position; - // any alignment - writer.Write((uint)(offs - HOT_HEAP_DIR_SIZE)); - // CLR 2.0: low 2 bits are ignored - // CLR 4.0: any alignment, all bits are used - writer.Write((uint)(hotMDDirOffs - poolDBOffs)); - } - - static void WriteAlign(BinaryWriter writer, ref uint offs) { - uint align = Utils.AlignUp(offs, HH_ALIGNMENT) - offs; - offs += align; - writer.WriteZeros((int)align); - } - } - - /// - /// CLR 2.0 (.NET 2.0 - 3.5) hot heap (#!) - /// - public sealed class HotHeap20 : HotHeap { - /// - public override HotHeapVersion HotHeapVersion { - get { return HotHeapVersion.CLR20; } - } - - /// - public override HotTable CreateHotTable(IMDTable mdTable) { - return new HotTable20(mdTable); - } - - /// - public override HotPool CreateHotPool(HeapType heapType) { - return new HotPool20(heapType); - } - } - - /// - /// CLR 4.0 (.NET 4.0 - 4.5) hot heap (#!) - /// - public sealed class HotHeap40 : HotHeap { - /// - public override HotHeapVersion HotHeapVersion { - get { return HotHeapVersion.CLR40; } - } - - /// - public override HotTable CreateHotTable(IMDTable mdTable) { - return new HotTable40(mdTable); - } - - /// - public override HotPool CreateHotPool(HeapType heapType) { - return new HotPool40(heapType); - } - } -} diff --git a/src/DotNet/Writer/HotPool.cs b/src/DotNet/Writer/HotPool.cs deleted file mode 100644 index d629c51a9..000000000 --- a/src/DotNet/Writer/HotPool.cs +++ /dev/null @@ -1,391 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Collections.Generic; -using System.IO; -using dnlib.DotNet.MD; -using dnlib.IO; -using dnlib.PE; - -namespace dnlib.DotNet.Writer { - /// - /// Hot pool - /// - public abstract class HotPool : IChunk { - internal const uint HP_ALIGNMENT = 4; - FileOffset offset; - RVA rva; - readonly HeapType heapType; - Dictionary allData; - internal DataInfo[] dataList; - int[] sortedIndexes; - internal uint dataOffset; - internal uint indexesOffset; - internal uint ridsOffset; - internal uint headerOffset; - uint totalLength; - bool indexesIsSorted; - - internal class DataInfo { - public readonly uint HeapOffset; - public uint PoolOffset; - public readonly byte[] Data; - public DataInfo(uint heapOffset, byte[] data) { - this.HeapOffset = heapOffset; - this.Data = data; - } - } - - /// - public FileOffset FileOffset { - get { return offset; } - } - - /// - public RVA RVA { - get { return rva; } - } - - /// - /// Gets the offset of the data relative to the start of this chunk. This is valid only - /// after has been called. - /// - public uint DataOffset { - get { return dataOffset; } - } - - /// - /// Gets the offset of the indexes relative to the start of this chunk. This is valid only - /// after has been called. - /// - public uint IndexesOffset { - get { return indexesOffset; } - } - - /// - /// Gets the offset of the rids relative to the start of this chunk. This is valid only - /// after has been called. - /// - public uint RidsOffset { - get { return ridsOffset; } - } - - /// - /// Gets the offset of the header relative to the start of this chunk. This is valid only - /// after has been called. - /// - public uint HeaderOffset { - get { return headerOffset; } - } - - /// - /// Gets the pool type - /// - public HeapType HeapType { - get { return heapType; } - } - - /// - /// Constructor - /// - /// Pool type - internal HotPool(HeapType heapType) { - this.heapType = heapType; - this.allData = new Dictionary(); - } - - /// - /// Adds raw data - /// - /// Offset of data in the original heap. If it's the #GUID, this - /// should be (index - 1) * 16. - /// - public void Add(uint offset, byte[] rawData) { - if (dataList != null) - throw new InvalidOperationException("Can't Add() after CreateData() has been called"); - allData[offset] = rawData; - } - - /// - /// Creates if it hasn't been created yet - /// - void CreateData() { - if (dataList != null) - return; - - dataList = new DataInfo[allData.Count]; - int i = 0; - foreach (var kv in allData) - dataList[i++] = new DataInfo(kv.Key, kv.Value); - // Make sure it's sorted by heap offset - Array.Sort(dataList, (a, b) => a.HeapOffset.CompareTo(b.HeapOffset)); - - sortedIndexes = new int[allData.Count]; - for (i = 0; i < sortedIndexes.Length; i++) - sortedIndexes[i] = i; - - allData = null; - indexesIsSorted = false; - } - - /// - /// Creates the data and sorts it according to the original data's heap offsets - /// - public void SortData() { - CreateData(); - Array.Sort(sortedIndexes, (a, b) => a.CompareTo(b)); - indexesIsSorted = true; - } - - /// - /// Creates the data and shuffles it - /// - public void ShuffleData() { - ShuffleData(new Random()); - } - - /// - /// Creates the data and shuffles it - /// - /// Random number generator instance that should be used - public void ShuffleData(Random rand) { - CreateData(); - - int start = 0, len = sortedIndexes.Length; - GetValidShuffleRange(ref start, ref len); - Shuffle(rand, sortedIndexes, start, len); - - indexesIsSorted = true; - } - - /// - /// Returns the range that can be shuffled - /// - /// Start index - /// Length - internal virtual void GetValidShuffleRange(ref int start, ref int length) { - } - - static void Shuffle(Random rand, IList list, int start, int count) { - if (list == null || count <= 1) - return; - - // Fisher-Yates algo, see http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle - for (int i = count - 1; i > 0; i--) { - int j = rand.Next(i + 1); - T tmp = list[start + i]; - list[start + i] = list[start + j]; - list[start + j] = tmp; - } - } - - /// - public void SetOffset(FileOffset offset, RVA rva) { - this.offset = offset; - this.rva = rva; - - CreateData(); - totalLength = SetOffsetImpl(); - } - - /// - /// Initializes all offsets - /// - /// Total size of data - internal abstract uint SetOffsetImpl(); - - internal uint GetDataSize() { - uint size = 0; - foreach (var data in dataList) - size += (uint)data.Data.Length; - return size; - } - - /// - public uint GetFileLength() { - return totalLength; - } - - /// - public uint GetVirtualSize() { - return GetFileLength(); - } - - /// - public void WriteTo(BinaryWriter writer) { - // Sort it unless it's already been sorted. Don't want random order - // unless the user wants it. - if (!indexesIsSorted) - SortData(); - - WriteToImpl(writer); - dataList = null; - sortedIndexes = null; - } - - /// - /// Writes all data - /// - internal abstract void WriteToImpl(BinaryWriter writer); - - internal DataInfo[] GetPoolDataOrder() { - var dataList2 = (DataInfo[])dataList.Clone(); - Array.Sort(sortedIndexes, dataList2); - uint offset = 0; - foreach (var info in dataList2) { - info.PoolOffset = offset; - offset += (uint)info.Data.Length; - } - return dataList2; - } - } - - /// - /// CLR 2.0 (.NET 2.0 - 3.5) hot pool writer - /// - sealed class HotPool20 : HotPool { - /// - /// Constructor - /// - /// Pool type - public HotPool20(HeapType heapType) - : base(heapType) { - } - - /// - internal override void GetValidShuffleRange(ref int start, ref int length) { - // First one must always be first - start++; - length--; - } - - /// - internal override uint SetOffsetImpl() { - uint offs = 0; - - // data can be anywhere except where rids can be - offs = Utils.AlignUp(offs, HP_ALIGNMENT); - dataOffset = offs; - offs += GetDataSize(); - - // indexes can be anywhere except where rids can be - offs = Utils.AlignUp(offs, HP_ALIGNMENT); - indexesOffset = offs; - offs += ((uint)dataList.Length - 1) * 4; - - // rids must be right before the header - offs = Utils.AlignUp(offs, HP_ALIGNMENT); - ridsOffset = offs; - offs += (uint)dataList.Length * 4; - - offs = Utils.AlignUp(offs, HP_ALIGNMENT); - headerOffset = offs; - offs += 3 * 4; - - return offs; - } - - /// - internal override void WriteToImpl(BinaryWriter writer) { - uint offs = 0; - long startPos = writer.BaseStream.Position; - var dataList2 = GetPoolDataOrder(); - - // Write data - writer.WriteZeros((int)(dataOffset - offs)); - foreach (var kv in dataList2) - writer.Write(kv.Data); - offs = (uint)(writer.BaseStream.Position - startPos); - - // Write indexes (hot pool offsets) - writer.WriteZeros((int)(indexesOffset - offs)); - if (dataList.Length > 1) { - for (int i = 1; i < dataList.Length; i++) - writer.Write(dataList[i].PoolOffset); - } - offs = (uint)(writer.BaseStream.Position - startPos); - - // Write rids (heap offsets) - writer.WriteZeros((int)(ridsOffset - offs)); - foreach (var kv in dataList) - writer.Write(kv.HeapOffset); - offs = (uint)(writer.BaseStream.Position - startPos); - - // Write header - writer.WriteZeros((int)(headerOffset - offs)); - writer.Write(headerOffset - dataOffset); // any alignment - writer.Write(headerOffset - indexesOffset); // low 2 bits are ignored - writer.Write(headerOffset - ridsOffset); // low 2 bits are ignored - } - } - - /// - /// CLR 4.0 (.NET 4.0 - 4.5) hot pool writer - /// - sealed class HotPool40 : HotPool { - /// - /// Constructor - /// - /// Pool type - public HotPool40(HeapType heapType) - : base(heapType) { - } - - /// - internal override uint SetOffsetImpl() { - uint offs = 0; - - // data can be anywhere except where rids can be - offs = Utils.AlignUp(offs, HP_ALIGNMENT); - dataOffset = offs; - offs += GetDataSize(); - - // indexes can be anywhere except where rids can be - offs = Utils.AlignUp(offs, HP_ALIGNMENT); - indexesOffset = offs; - offs += (uint)dataList.Length * 4; - - // rids must be right before the header - offs = Utils.AlignUp(offs, HP_ALIGNMENT); - ridsOffset = offs; - offs += (uint)dataList.Length * 4; - - offs = Utils.AlignUp(offs, HP_ALIGNMENT); - headerOffset = offs; - offs += 3 * 4; - - return offs; - } - - /// - internal override void WriteToImpl(BinaryWriter writer) { - uint offs = 0; - long startPos = writer.BaseStream.Position; - var dataList2 = GetPoolDataOrder(); - - // Write data - writer.WriteZeros((int)(dataOffset - offs)); - foreach (var info in dataList2) - writer.Write(info.Data); - offs = (uint)(writer.BaseStream.Position - startPos); - - // Write indexes (hot pool offsets) - writer.WriteZeros((int)(indexesOffset - offs)); - foreach (var info in dataList) - writer.Write(info.PoolOffset); - offs = (uint)(writer.BaseStream.Position - startPos); - - // Write rids (heap offsets) - writer.WriteZeros((int)(ridsOffset - offs)); - foreach (var kv in dataList) - writer.Write(kv.HeapOffset); - offs = (uint)(writer.BaseStream.Position - startPos); - - // Write header - writer.WriteZeros((int)(headerOffset - offs)); - writer.Write(headerOffset - ridsOffset); // must be 4-byte aligned - writer.Write(headerOffset - indexesOffset); // must be 4-byte aligned - writer.Write(headerOffset - dataOffset); // any alignment - } - } -} diff --git a/src/DotNet/Writer/HotTable.cs b/src/DotNet/Writer/HotTable.cs deleted file mode 100644 index 4a5fbe77a..000000000 --- a/src/DotNet/Writer/HotTable.cs +++ /dev/null @@ -1,482 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Collections.Generic; -using System.IO; -using dnlib.DotNet.MD; -using dnlib.IO; -using dnlib.PE; - -namespace dnlib.DotNet.Writer { - /// - /// Hot metadata table base class - /// - public abstract class HotTable : IChunk { - /// - /// At most 64K rows can be used when only a partial table is stored in the - /// hot table. The first level table indexes into the second level table, - /// and the index is 16 bits. - /// - public const int MAX_ROWS = 0x10000; - internal const uint HT_ALIGNMENT = 4; - - FileOffset offset; - RVA rva; - - internal readonly IMDTable mdTable; - readonly HotHeapVersion version; - readonly int hotTableHeaderSize; - internal readonly int alignedHotTableHeaderSize; - uint totalLength; - - // full table fields - byte[] data; - - // partial table fields - internal List rids; - internal Dictionary partialData; - internal int shift; - uint mask; - internal ushort[] firstLevelTable; - internal byte[] secondLevelTable; - internal uint dataOffset; - internal uint firstLevelOffset; - internal uint secondLevelOffset; - - /// - /// true if we can write a partial table, false if we must write - /// the full table. - /// - public bool CanWritePartialTable { - get { - return data == null && rids != null && rids.Count <= MAX_ROWS; - } - } - - /// - /// Gets the full size of the table - /// - uint FullTableSize { - get { return (uint)(mdTable.Rows * mdTable.TableInfo.RowSize); } - } - - /// - /// Gets the table type - /// - public Table Table { - get { return mdTable.Table; } - } - - /// - /// Constructor - /// - /// Hot heap version - /// The MD table - internal HotTable(HotHeapVersion version, IMDTable mdTable) { - this.mdTable = mdTable; - this.version = version; - - switch (version) { - case HotHeapVersion.CLR20: - hotTableHeaderSize = 0x12; - break; - case HotHeapVersion.CLR40: - hotTableHeaderSize = 0x16; - break; - default: - throw new ArgumentException("Invalid hot heap version"); - } - - this.alignedHotTableHeaderSize = Utils.AlignUp(this.hotTableHeaderSize, HT_ALIGNMENT); - } - - /// - public FileOffset FileOffset { - get { return offset; } - } - - /// - public RVA RVA { - get { return rva; } - } - - /// - public void SetOffset(FileOffset offset, RVA rva) { - this.offset = offset; - this.rva = rva; - - mdTable.SetReadOnly(); - if (CanWritePartialTable) { - InitializePartialData(); - totalLength = CalculatePartialTableLength(); - } - else - totalLength = CalculateFullTableLength(); - } - - /// - /// Calculates the total size required to write a partial table. It is only called if - /// the partial table will be written. - /// - internal abstract uint CalculatePartialTableLength(); - - /// - /// Calculates the total size required to write a full table. It is only called if - /// the full table will be written. - /// - uint CalculateFullTableLength() { - return (uint)alignedHotTableHeaderSize + FullTableSize; - } - - /// - public uint GetFileLength() { - return totalLength; - } - - /// - public uint GetVirtualSize() { - return GetFileLength(); - } - - /// - /// Adds a row. This method must be called to write a partial table. If too many rows - /// are added (see ), then the full table will be written. If this - /// method is never called, a full table will be written. - /// - /// The row ID. This must be the new rid, so this method can only be - /// called after the table row has been assigned a new rid. - public void Add(uint rid) { - if (totalLength != 0) - throw new InvalidOperationException("Can't call Add() after SetOffset() has been called"); - if (partialData != null || data != null) - throw new InvalidOperationException("Can't add data when full/partial data has been created"); - if (rid == 0 || rid > (uint)mdTable.Rows) - return; - if (rids == null) - rids = new List(); - rids.Add(rid); - } - - /// - /// Calls the to write all its rows to a buffer. This is the data - /// that will be written to this hot table. If this is not explicitly called, it will - /// be implicitly called later when all data must be written. The table will be set to - /// read-only. If this method is called, all data will be written to the heap even if - /// has been called. - /// - public void CreateFullData() { - mdTable.SetReadOnly(); - rids = null; - if (data != null) - return; - - data = new byte[FullTableSize]; - var writer = new BinaryWriter(new MemoryStream(data)); - writer.Write(mdTable); - if (writer.BaseStream.Position != data.Length) - throw new InvalidOperationException("Didn't write all MD table data"); - } - - /// - /// Creates the partial data of all rows that have been 'd so far. - /// If a partial table can't be created, is automatically - /// called instead. If this method isn't explicitly called, it will be implicitly called - /// later when the partial data must be written. The table will be set to read-only. - /// - public void CreatePartialData() { - mdTable.SetReadOnly(); - if (!CanWritePartialTable) { - CreateFullData(); - return; - } - InitializePartialData(); - var memStream = new MemoryStream(mdTable.TableInfo.RowSize); - var writer = new BinaryWriter(memStream); - foreach (var rid in rids) { - memStream.Position = 0; - var row = mdTable.Get(rid); - writer.Write(mdTable, row); - partialData[rid] = memStream.ToArray(); - } - } - - void InitializePartialData() { - if (partialData != null) - return; - - partialData = new Dictionary(rids.Count); - foreach (var rid in rids) - partialData[rid] = null; - InitializePartialRids(); - } - - /// - public void WriteTo(BinaryWriter writer) { - if (CanWritePartialTable) - PartialWriteTo(writer); - else - FullWriteTo(writer); - - firstLevelTable = null; - secondLevelTable = null; - partialData = null; - rids = null; - data = null; - } - - /// - /// Writes the full table to the hot table - /// - /// Writer - void FullWriteTo(BinaryWriter writer) { - CreateFullData(); - - var startPos = writer.BaseStream.Position; - writer.Write(mdTable.Rows); // hot records - writer.Write(0); // offset of 1st level table - writer.Write(0); // offset of 2nd level table - if (version == HotHeapVersion.CLR40) - writer.Write(0); // offset of indexes table - writer.Write(alignedHotTableHeaderSize); // offset of hot data (4-byte aligned) - writer.Write((ushort)0); // shift count - writer.WriteZeros(alignedHotTableHeaderSize - (int)(writer.BaseStream.Position - startPos)); - writer.Write(data); - } - - /// - /// Writes the partial table to the hot table - /// - /// Writer - internal abstract void PartialWriteTo(BinaryWriter writer); - - static int CountMaxBits(uint val) { - int bits = 0; - while (val != 0) { - val >>= 1; - bits++; - } - return bits; - } - - void InitializePartialRids() { - shift = CalculateShift(); - mask = (1U << shift) - 1; - SortRids(); - CreateFirstAndSecondLevelTables(); - } - - int CalculateShift() { - mdTable.SetReadOnly(); - int maxBits = CountMaxBits((uint)mdTable.Rows); - if (maxBits >= 16) - return maxBits - 8; - else - return maxBits / 2; - } - - void SortRids() { - // Make sure dupes are removed - rids.Clear(); - rids.AddRange(partialData.Keys); - - rids.Sort((a, b) => { - uint la = a & mask; - uint lb = b & mask; - if (la != lb) - return la.CompareTo(lb); - return (a >> shift).CompareTo(b >> shift); - }); - } - - void CreateFirstAndSecondLevelTables() { - // rids has already been sorted, first on lower bits, then on upper bits - firstLevelTable = new ushort[(1 << shift) + 1]; - int prevRid = 0, i2 = 0; - for (; i2 < partialData.Count; i2++) { - int rid = (int)(rids[i2] & mask); - while (prevRid <= rid) - firstLevelTable[prevRid++] = (ushort)i2; - } - while (prevRid < firstLevelTable.Length) - firstLevelTable[prevRid++] = (ushort)i2; - - secondLevelTable = new byte[partialData.Count]; - for (int i = 0; i < secondLevelTable.Length; i++) - secondLevelTable[i] = (byte)(rids[i] >> shift); - } - - /// - /// Writes the data - /// - /// Writer - internal void WritePartialData(BinaryWriter writer) { - foreach (var rid in rids) - writer.Write(partialData[rid]); - } - - /// - /// Writes the first level table - /// - /// Writer - internal void WriteFirstLevelTable(BinaryWriter writer) { - foreach (var s in firstLevelTable) - writer.Write(s); - } - - /// - /// Writes the second level table - /// - /// Writer - internal void WriteSecondLevelTable(BinaryWriter writer) { - writer.Write(secondLevelTable); - } - } - - /// - /// CLR 2.0 (.NET 2.0 - 3.5) hot table - /// - public sealed class HotTable20 : HotTable { - /// - /// Constructor - /// - /// The MD table - public HotTable20(IMDTable mdTable) - : base(HotHeapVersion.CLR20, mdTable) { - } - - /// - internal override uint CalculatePartialTableLength() { - uint len = 0; - - len += (uint)alignedHotTableHeaderSize; - - // Data - len = Utils.AlignUp(len, HT_ALIGNMENT); - dataOffset = len; - len += (uint)(partialData.Count * mdTable.TableInfo.RowSize); - - // First level table - len = Utils.AlignUp(len, HT_ALIGNMENT); - firstLevelOffset = len; - len += (uint)(firstLevelTable.Length * 2); - - // Second level table - len = Utils.AlignUp(len, HT_ALIGNMENT); - secondLevelOffset = len; - len += (uint)secondLevelTable.Length; - - return len; - } - - /// - internal override void PartialWriteTo(BinaryWriter writer) { - var startPos = writer.BaseStream.Position; - writer.Write(partialData.Count);// hot records - writer.Write(firstLevelOffset); // any alignment, all bits are used - writer.Write(secondLevelOffset);// any alignment, all bits are used - writer.Write(dataOffset); // any alignment, all bits are used - writer.Write((ushort)shift);// shift count - writer.WriteZeros(alignedHotTableHeaderSize - (int)(writer.BaseStream.Position - startPos)); - - uint offs; - - // Data - offs = (uint)(writer.BaseStream.Position - startPos); - writer.WriteZeros((int)(dataOffset - offs)); - WritePartialData(writer); - - // First level table - offs = (uint)(writer.BaseStream.Position - startPos); - writer.WriteZeros((int)(firstLevelOffset - offs)); - WriteFirstLevelTable(writer); - - // Second level table - offs = (uint)(writer.BaseStream.Position - startPos); - writer.WriteZeros((int)(secondLevelOffset - offs)); - WriteSecondLevelTable(writer); - } - } - - /// - /// CLR 4.0 (.NET 4.0 - 4.5) partial hot table - /// - public sealed class HotTable40 : HotTable { - uint indexesOffset; - - /// - /// Constructor - /// - /// The MD table - public HotTable40(IMDTable mdTable) - : base(HotHeapVersion.CLR40, mdTable) { - } - - /// - internal override uint CalculatePartialTableLength() { - uint len = 0; - - len += (uint)alignedHotTableHeaderSize; - - // Data - len = Utils.AlignUp(len, HT_ALIGNMENT); - dataOffset = len; - len += (uint)(partialData.Count * mdTable.TableInfo.RowSize); - - // First level table - len = Utils.AlignUp(len, HT_ALIGNMENT); - firstLevelOffset = len; - len += (uint)(firstLevelTable.Length * 2); - - // Second level table - len = Utils.AlignUp(len, HT_ALIGNMENT); - secondLevelOffset = len; - len += (uint)secondLevelTable.Length; - - // Indexes table - len = Utils.AlignUp(len, HT_ALIGNMENT); - indexesOffset = len; - len += (uint)(partialData.Count * 2); - - return len; - } - - /// - internal override void PartialWriteTo(BinaryWriter writer) { - var startPos = writer.BaseStream.Position; - writer.Write(partialData.Count);// hot records - writer.Write(firstLevelOffset); // any alignment, all bits are used - writer.Write(secondLevelOffset);// any alignment, all bits are used - writer.Write(indexesOffset);// any alignment, all bits are used - writer.Write(dataOffset); // any alignment, all bits are used - writer.Write((ushort)shift);// shift count - writer.WriteZeros(alignedHotTableHeaderSize - (int)(writer.BaseStream.Position - startPos)); - - uint offs; - - // Data - offs = (uint)(writer.BaseStream.Position - startPos); - writer.WriteZeros((int)(dataOffset - offs)); - WritePartialData(writer); - - // First level table - offs = (uint)(writer.BaseStream.Position - startPos); - writer.WriteZeros((int)(firstLevelOffset - offs)); - WriteFirstLevelTable(writer); - - // Second level table - offs = (uint)(writer.BaseStream.Position - startPos); - writer.WriteZeros((int)(secondLevelOffset - offs)); - WriteSecondLevelTable(writer); - - // Indexes table - offs = (uint)(writer.BaseStream.Position - startPos); - writer.WriteZeros((int)(indexesOffset - offs)); - WriteIndexesTable(writer); - } - - void WriteIndexesTable(BinaryWriter writer) { - for (int i = 0; i < partialData.Count; i++) - writer.Write((ushort)i); - } - } -} diff --git a/src/DotNet/Writer/IChunk.cs b/src/DotNet/Writer/IChunk.cs index ed1161361..061321a48 100644 --- a/src/DotNet/Writer/IChunk.cs +++ b/src/DotNet/Writer/IChunk.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System.IO; +using System.IO; using dnlib.IO; using dnlib.PE; @@ -40,6 +40,13 @@ public interface IChunk { /// Virtual size of this chunk uint GetVirtualSize(); + /// + /// Calculates the requires alignment of this chunk. + /// Returns 0 for default/no alignment. + /// + /// Required alignment + uint CalculateAlignment(); + /// /// Writes all data to at its current location. It's only /// called after and have been called. @@ -47,7 +54,20 @@ public interface IChunk { /// chunk's file position. /// /// Destination - void WriteTo(BinaryWriter writer); + void WriteTo(DataWriter writer); + } + + /// + /// Implemented by s that can reuse the old data location in the original PE file + /// + interface IReuseChunk : IChunk { + /// + /// Returns true if this chunk fits in the old location + /// + /// Original RVA of data + /// Size of the original location + /// + bool CanReuse(RVA origRva, uint origSize); } public static partial class Extensions { @@ -57,39 +77,38 @@ public static partial class Extensions { /// this /// Destination /// Not all bytes were written - public static void VerifyWriteTo(this IChunk chunk, BinaryWriter writer) { - long pos = writer.BaseStream.Position; + public static void VerifyWriteTo(this IChunk chunk, DataWriter writer) { + long pos = writer.Position; + // Uncomment this to add some debug info, useful when comparing old vs new version + //System.Diagnostics.Debug.WriteLine($" RVA 0x{(uint)chunk.RVA:X8} OFFS 0x{(uint)chunk.FileOffset:X8} VSIZE 0x{chunk.GetVirtualSize():X8} {chunk.GetType().FullName}"); chunk.WriteTo(writer); - if (writer.BaseStream.Position - pos != chunk.GetFileLength()) - throw new IOException("Did not write all bytes"); + if (writer.Position - pos != chunk.GetFileLength()) + VerifyWriteToThrow(chunk); } + static void VerifyWriteToThrow(IChunk chunk) => + throw new IOException($"Did not write all bytes: {chunk.GetType().FullName}"); + /// /// Writes a data directory /// /// Writer /// The data - internal static void WriteDataDirectory(this BinaryWriter writer, IChunk chunk) { - if (chunk == null || chunk.GetVirtualSize() == 0) - writer.Write(0UL); + internal static void WriteDataDirectory(this DataWriter writer, IChunk chunk) { + if (chunk is null || chunk.GetVirtualSize() == 0) + writer.WriteUInt64(0); else { - writer.Write((uint)chunk.RVA); - writer.Write(chunk.GetVirtualSize()); + writer.WriteUInt32((uint)chunk.RVA); + writer.WriteUInt32(chunk.GetVirtualSize()); } } - /// - /// Writes a data directory - /// - /// Writer - /// The data - /// Fixed size of - internal static void WriteDataDirectory(this BinaryWriter writer, IChunk chunk, uint size) { - if (chunk == null || chunk.GetVirtualSize() == 0 || size == 0) - writer.Write(0UL); + internal static void WriteDebugDirectory(this DataWriter writer, DebugDirectory chunk) { + if (chunk is null || chunk.GetVirtualSize() == 0) + writer.WriteUInt64(0); else { - writer.Write((uint)chunk.RVA); - writer.Write(size); + writer.WriteUInt32((uint)chunk.RVA); + writer.WriteUInt32((uint)(chunk.Count * DebugDirectory.HEADER_SIZE)); } } } diff --git a/src/DotNet/Writer/IHeap.cs b/src/DotNet/Writer/IHeap.cs index 80a871b88..b75e0754e 100644 --- a/src/DotNet/Writer/IHeap.cs +++ b/src/DotNet/Writer/IHeap.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.DotNet.Writer { +namespace dnlib.DotNet.Writer { /// /// .NET Heap interface /// diff --git a/src/DotNet/Writer/IMetaDataListener.cs b/src/DotNet/Writer/IMetaDataListener.cs deleted file mode 100644 index 6d704754e..000000000 --- a/src/DotNet/Writer/IMetaDataListener.cs +++ /dev/null @@ -1,235 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -namespace dnlib.DotNet.Writer { - /// - /// Gets notified of various events when writing the metadata - /// - public interface IMetaDataListener { - /// - /// Called by - /// - /// The metadata - /// Type of metadata event - void OnMetaDataEvent(MetaData metaData, MetaDataEvent evt); - } - - /// - /// A which does nothing - /// - public sealed class DummyMetaDataListener : IMetaDataListener { - /// - /// An instance of this dummy listener - /// - public static readonly DummyMetaDataListener Instance = new DummyMetaDataListener(); - - /// - public void OnMetaDataEvent(MetaData metaData, MetaDataEvent evt) { - } - } - - /// - /// All events - /// - public enum MetaDataEvent { - /// - /// Creating the tables has just begun - /// - BeginCreateTables, - - /// - /// Before allocating all TypeDef RIDs - /// - AllocateTypeDefRids, - - /// - /// Before allocating all MemberDef RIDs - /// - AllocateMemberDefRids, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - AllocateMemberDefRids0, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - AllocateMemberDefRids1, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - AllocateMemberDefRids2, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - AllocateMemberDefRids3, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - AllocateMemberDefRids4, - - /// - /// The rids of types, fields, methods, events, properties and parameters are - /// now known. - /// - MemberDefRidsAllocated, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - InitializeTypeDefsAndMemberDefs0, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - InitializeTypeDefsAndMemberDefs1, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - InitializeTypeDefsAndMemberDefs2, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - InitializeTypeDefsAndMemberDefs3, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - InitializeTypeDefsAndMemberDefs4, - - /// - /// The tables and rows of all types, fields, methods, events, properties and parameters - /// have been initialized. Method body RVAs are still not known, and no method has been - /// written yet. - /// - MemberDefsInitialized, - - /// - /// Before sorting most tables - /// - BeforeSortTables, - - /// - /// Most of the tables that should be sorted have been sorted. The CustomAttribute - /// table is still unsorted since it's not been created yet. - /// - MostTablesSorted, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteTypeDefAndMemberDefCustomAttributes0, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteTypeDefAndMemberDefCustomAttributes1, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteTypeDefAndMemberDefCustomAttributes2, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteTypeDefAndMemberDefCustomAttributes3, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteTypeDefAndMemberDefCustomAttributes4, - - /// - /// Custom attributes of all types, fields, methods, events, properties and parameters - /// have now been written. - /// - MemberDefCustomAttributesWritten, - - /// - /// All resources are about to be added to the .NET resources table - /// - BeginAddResources, - - /// - /// All resources have been added to the .NET resources table - /// - EndAddResources, - - /// - /// All method bodies are about to be written - /// - BeginWriteMethodBodies, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteMethodBodies0, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteMethodBodies1, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteMethodBodies2, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteMethodBodies3, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteMethodBodies4, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteMethodBodies5, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteMethodBodies6, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteMethodBodies7, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteMethodBodies8, - - /// - /// Sent by the metadata writer so a UI can update its progress bar - /// - WriteMethodBodies9, - - /// - /// All method bodies have been written. Their RVAs are still not known. - /// - EndWriteMethodBodies, - - /// - /// All tables are now sorted, including the CustomAttribute table. - /// - OnAllTablesSorted, - - /// - /// All tables have been created and all rows populated. The only columns that haven't - /// been initialized yet are the ones that are RVAs. - /// - EndCreateTables, - } -} diff --git a/src/DotNet/Writer/IModuleWriterListener.cs b/src/DotNet/Writer/IModuleWriterListener.cs deleted file mode 100644 index 63718110b..000000000 --- a/src/DotNet/Writer/IModuleWriterListener.cs +++ /dev/null @@ -1,356 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -namespace dnlib.DotNet.Writer { - /// - /// Gets notified of various events when writing a module - /// - public interface IModuleWriterListener { - /// - /// Called by and its sub classes. - /// - /// The module writer - /// Type of writer event - void OnWriterEvent(ModuleWriterBase writer, ModuleWriterEvent evt); - } - - /// - /// A which does nothing - /// - public sealed class DummyModuleWriterListener : IModuleWriterListener { - /// - /// An instance of this dummy listener - /// - public static readonly DummyModuleWriterListener Instance = new DummyModuleWriterListener(); - - /// - public void OnWriterEvent(ModuleWriterBase writer, ModuleWriterEvent evt) { - } - } - - /// - /// All / events - /// - public enum ModuleWriterEvent { - /// - /// Writing has just begun - /// - Begin, - - /// - /// All PE sections have been created - /// - PESectionsCreated, - - /// - /// All chunks have been created - /// - ChunksCreated, - - /// - /// All chunks have been added to their sections - /// - ChunksAddedToSections, - - /// - /// Original event: . - /// Creating the metadata tables has just begun - /// - MDBeginCreateTables, - - /// - /// Original event: . - /// Before allocating all TypeDef RIDs - /// - MDAllocateTypeDefRids, - - /// - /// Original event: . - /// Before allocating all MemberDef RIDs - /// - MDAllocateMemberDefRids, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDAllocateMemberDefRids0, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDAllocateMemberDefRids1, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDAllocateMemberDefRids2, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDAllocateMemberDefRids3, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDAllocateMemberDefRids4, - - /// - /// Original event: . - /// The rids of types, fields, methods, events, properties and parameters are - /// now known. - /// - MDMemberDefRidsAllocated, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDInitializeTypeDefsAndMemberDefs0, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDInitializeTypeDefsAndMemberDefs1, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDInitializeTypeDefsAndMemberDefs2, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDInitializeTypeDefsAndMemberDefs3, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDInitializeTypeDefsAndMemberDefs4, - - /// - /// Original event: . - /// The tables and rows of all types, fields, methods, events, properties and parameters - /// have been initialized. Method body RVAs are still not known, and no method has been - /// written yet. - /// - MDMemberDefsInitialized, - - /// - /// Original event: . - /// Before sorting most tables - /// - MDBeforeSortTables, - - /// - /// Original event: . - /// Most of the tables that should be sorted have been sorted. The CustomAttribute - /// table is still unsorted since it's not been created yet. - /// - MDMostTablesSorted, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteTypeDefAndMemberDefCustomAttributes0, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteTypeDefAndMemberDefCustomAttributes1, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteTypeDefAndMemberDefCustomAttributes2, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteTypeDefAndMemberDefCustomAttributes3, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteTypeDefAndMemberDefCustomAttributes4, - - /// - /// Original event: . - /// Custom attributes of all types, fields, methods, events, properties and parameters - /// have now been written. - /// - MDMemberDefCustomAttributesWritten, - - /// - /// Original event: . - /// All resources are about to be added to the .NET resources table - /// - MDBeginAddResources, - - /// - /// Original event: . - /// All resources have been added to the .NET resources table - /// - MDEndAddResources, - - /// - /// Original event: . - /// All method bodies are about to be written - /// - MDBeginWriteMethodBodies, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteMethodBodies0, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteMethodBodies1, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteMethodBodies2, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteMethodBodies3, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteMethodBodies4, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteMethodBodies5, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteMethodBodies6, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteMethodBodies7, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteMethodBodies8, - - /// - /// Original event: . - /// Sent by the metadata writer so a UI can update its progress bar - /// - MDWriteMethodBodies9, - - /// - /// Original event: . - /// All method bodies have been written. Their RVAs are still not known. - /// - MDEndWriteMethodBodies, - - /// - /// Original event: . - /// All tables are now sorted, including the CustomAttribute table. - /// - MDOnAllTablesSorted, - - /// - /// Original event: . - /// All tables have been created and all rows populated. The only columns that haven't - /// been initialized yet are the ones that are RVAs. - /// - MDEndCreateTables, - - /// - /// This event occurs before the PDB file is written. This event occurs even if no PDB file - /// will be written. - /// - BeginWritePdb, - - /// - /// The PDB file has been written. This event occurs even if no PDB file has been written. - /// - EndWritePdb, - - /// - /// This event occurs just before all RVAs and file offsets of the chunks are calculated. - /// - BeginCalculateRvasAndFileOffsets, - - /// - /// File offsets and RVAs of all chunks are now known. This includes method body and - /// field RVAs. Nothing has been written to the destination stream yet. - /// - EndCalculateRvasAndFileOffsets, - - /// - /// This event occurs before all chunks are written to the destination stream, and after - /// all RVAs and file offsets are known. - /// - BeginWriteChunks, - - /// - /// All chunks have been written to the destination stream. - /// - EndWriteChunks, - - /// - /// This event occurs before the strong name signature is calculated. This event - /// occurs even if the assembly isn't strong name signed. - /// - BeginStrongNameSign, - - /// - /// This event occurs after the strong name signature has been calculated. This event - /// occurs even if the assembly isn't strong name signed. - /// - EndStrongNameSign, - - /// - /// This event occurs before the checksum in the PE header is updated. This event - /// occurs even if the checksum isn't updated. - /// - BeginWritePEChecksum, - - /// - /// This event occurs after the checksum in the PE header has been updated. This event - /// occurs even if the checksum isn't updated. - /// - EndWritePEChecksum, - - /// - /// Writing has ended - /// - End, - } -} diff --git a/src/DotNet/Writer/IOffsetHeap.cs b/src/DotNet/Writer/IOffsetHeap.cs index 1e47faeeb..3c104dcff 100644 --- a/src/DotNet/Writer/IOffsetHeap.cs +++ b/src/DotNet/Writer/IOffsetHeap.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System.Collections.Generic; diff --git a/src/DotNet/Writer/IWriterError.cs b/src/DotNet/Writer/IWriterError.cs index 2b88a65f3..c6e3005ec 100644 --- a/src/DotNet/Writer/IWriterError.cs +++ b/src/DotNet/Writer/IWriterError.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info namespace dnlib.DotNet.Writer { /// @@ -13,4 +13,34 @@ public interface IWriterError { /// Error message void Error(string message); } + + /// + /// Gets notified of errors. The default handler should normally throw since the written data + /// will probably be invalid. Any error can be ignored. + /// + public interface IWriterError2 : IWriterError { + /// + /// Called when an error is detected (eg. a null pointer or other invalid value). The error + /// can be ignored but the written data won't be valid. + /// + /// Error message + /// Optional message arguments + void Error(string message, params object[] args); + } + + static partial class Extensions { + /// + /// Called when an error is detected (eg. a null pointer or other invalid value). The error + /// can be ignored but the written data won't be valid. + /// + /// The instance of + /// Error message + /// Optional message arguments + internal static void Error2(this IWriterError helper, string message, params object[] args) { + if (helper is IWriterError2 helper2) + helper2.Error(message, args); + else + helper.Error(string.Format(message, args)); + } + } } diff --git a/src/DotNet/Writer/ImageCor20Header.cs b/src/DotNet/Writer/ImageCor20Header.cs index 0610a9b92..154c3fa6d 100644 --- a/src/DotNet/Writer/ImageCor20Header.cs +++ b/src/DotNet/Writer/ImageCor20Header.cs @@ -1,6 +1,5 @@ // dnlib: See LICENSE.txt for more info -using System.IO; using dnlib.IO; using dnlib.PE; using dnlib.DotNet.MD; @@ -50,9 +49,7 @@ public Cor20HeaderOptions() { /// Constructor /// /// Flags - public Cor20HeaderOptions(ComImageFlags flags) { - this.Flags = flags; - } + public Cor20HeaderOptions(ComImageFlags flags) => Flags = flags; /// /// Constructor @@ -61,9 +58,9 @@ public Cor20HeaderOptions(ComImageFlags flags) { /// Minor runtime version (default is ) /// Flags public Cor20HeaderOptions(ushort major, ushort minor, ComImageFlags flags) { - this.MajorRuntimeVersion = major; - this.MinorRuntimeVersion = minor; - this.Flags = flags; + MajorRuntimeVersion = major; + MinorRuntimeVersion = minor; + Flags = flags; } } @@ -76,9 +73,9 @@ public sealed class ImageCor20Header : IChunk { Cor20HeaderOptions options; /// - /// Gets/sets the + /// Gets/sets the /// - public MetaData MetaData { get; set; } + public Metadata Metadata { get; set; } /// /// Gets/sets the .NET resources @@ -90,23 +87,19 @@ public sealed class ImageCor20Header : IChunk { /// public StrongNameSignature StrongNameSignature { get; set; } + internal IChunk VtableFixups { get; set; } + /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } - } + public RVA RVA => rva; /// /// Constructor /// /// Options - public ImageCor20Header(Cor20HeaderOptions options) { - this.options = options; - } + public ImageCor20Header(Cor20HeaderOptions options) => this.options = options; /// public void SetOffset(FileOffset offset, RVA rva) { @@ -115,27 +108,26 @@ public void SetOffset(FileOffset offset, RVA rva) { } /// - public uint GetFileLength() { - return 0x48; - } + public uint GetFileLength() => 0x48; /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetVirtualSize() => GetFileLength(); + + /// + public uint CalculateAlignment() => 0; /// - public void WriteTo(BinaryWriter writer) { - writer.Write(0x48); // cb - writer.Write(options.MajorRuntimeVersion ?? Cor20HeaderOptions.DEFAULT_MAJOR_RT_VER); - writer.Write(options.MinorRuntimeVersion ?? Cor20HeaderOptions.DEFAULT_MINOR_RT_VER); - writer.WriteDataDirectory(MetaData); - writer.Write((uint)(options.Flags ?? ComImageFlags.ILOnly)); - writer.Write(options.EntryPoint ?? 0); + public void WriteTo(DataWriter writer) { + writer.WriteInt32(0x48); // cb + writer.WriteUInt16(options.MajorRuntimeVersion ?? Cor20HeaderOptions.DEFAULT_MAJOR_RT_VER); + writer.WriteUInt16(options.MinorRuntimeVersion ?? Cor20HeaderOptions.DEFAULT_MINOR_RT_VER); + writer.WriteDataDirectory(Metadata); + writer.WriteUInt32((uint)(options.Flags ?? ComImageFlags.ILOnly)); + writer.WriteUInt32(options.EntryPoint ?? 0); writer.WriteDataDirectory(NetResources); writer.WriteDataDirectory(StrongNameSignature); writer.WriteDataDirectory(null); // Code manager table - writer.WriteDataDirectory(null); // Vtable fixups + writer.WriteDataDirectory(VtableFixups); writer.WriteDataDirectory(null); // Export address table jumps writer.WriteDataDirectory(null); // Managed native header } diff --git a/src/DotNet/Writer/ImportAddressTable.cs b/src/DotNet/Writer/ImportAddressTable.cs index ab183bf58..d789e7890 100644 --- a/src/DotNet/Writer/ImportAddressTable.cs +++ b/src/DotNet/Writer/ImportAddressTable.cs @@ -1,6 +1,5 @@ // dnlib: See LICENSE.txt for more info -using System.IO; using dnlib.IO; using dnlib.PE; @@ -9,6 +8,7 @@ namespace dnlib.DotNet.Writer { /// Import address table chunk /// public sealed class ImportAddressTable : IChunk { + readonly bool is64bit; FileOffset offset; RVA rva; @@ -18,14 +18,18 @@ public sealed class ImportAddressTable : IChunk { public ImportDirectory ImportDirectory { get; set; } /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } - } + public RVA RVA => rva; + + internal bool Enable { get; set; } + + /// + /// Constructor + /// + /// true if it's a 64-bit PE file, false if it's a 32-bit PE file + public ImportAddressTable(bool is64bit) => this.is64bit = is64bit; /// public void SetOffset(FileOffset offset, RVA rva) { @@ -35,18 +39,29 @@ public void SetOffset(FileOffset offset, RVA rva) { /// public uint GetFileLength() { - return 8; + if (!Enable) + return 0; + return is64bit ? 16U : 8; } /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetVirtualSize() => GetFileLength(); + + /// + public uint CalculateAlignment() => 0; /// - public void WriteTo(BinaryWriter writer) { - writer.Write((uint)ImportDirectory.CorXxxMainRVA); - writer.Write(0); + public void WriteTo(DataWriter writer) { + if (!Enable) + return; + if (is64bit) { + writer.WriteUInt64((ulong)(uint)ImportDirectory.CorXxxMainRVA); + writer.WriteUInt64(0); + } + else { + writer.WriteUInt32((uint)ImportDirectory.CorXxxMainRVA); + writer.WriteInt32(0); + } } } } diff --git a/src/DotNet/Writer/ImportDirectory.cs b/src/DotNet/Writer/ImportDirectory.cs index ffc16dea3..23fe2b931 100644 --- a/src/DotNet/Writer/ImportDirectory.cs +++ b/src/DotNet/Writer/ImportDirectory.cs @@ -1,6 +1,5 @@ // dnlib: See LICENSE.txt for more info -using System.IO; using System.Text; using dnlib.IO; using dnlib.PE; @@ -10,14 +9,17 @@ namespace dnlib.DotNet.Writer { /// Import directory chunk /// public sealed class ImportDirectory : IChunk { + readonly bool is64bit; FileOffset offset; RVA rva; bool isExeFile; uint length; RVA importLookupTableRVA; RVA corXxxMainRVA; - RVA mscoreeDllRVA; + RVA dllToImportRVA; int stringsPadding; + string dllToImport; + string entryPointName; /// /// Gets/sets the @@ -27,37 +29,53 @@ public sealed class ImportDirectory : IChunk { /// /// Gets the RVA of _CorDllMain/_CorExeMain in the import lookup table /// - public RVA CorXxxMainRVA { - get { return corXxxMainRVA; } - } + public RVA CorXxxMainRVA => corXxxMainRVA; /// /// Gets RVA of _CorExeMain/_CorDllMain in the IAT /// - public RVA IatCorXxxMainRVA { - get { return ImportAddressTable.RVA; } - } + public RVA IatCorXxxMainRVA => ImportAddressTable.RVA; /// /// Gets/sets a value indicating whether this is a EXE or a DLL file /// public bool IsExeFile { - get { return isExeFile; } - set { isExeFile = value; } + get => isExeFile; + set => isExeFile = value; } /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } + public RVA RVA => rva; + + internal bool Enable { get; set; } + + /// + /// Gets/sets the name of the dll which should be imported. + /// + public string DllToImport { + get => dllToImport ?? "mscoree.dll"; + set => dllToImport = value; + } + + /// + /// Gets/sets the name of the entry point of the imported dll. + /// + public string EntryPointName { + get => entryPointName ?? (IsExeFile ? "_CorExeMain" : "_CorDllMain"); + set => entryPointName = value; } const uint STRINGS_ALIGNMENT = 16; + /// + /// Constructor + /// + /// true if it's a 64-bit PE file, false if it's a 32-bit PE file + public ImportDirectory(bool is64bit) => this.is64bit = is64bit; + /// public void SetOffset(FileOffset offset, RVA rva) { this.offset = offset; @@ -65,48 +83,59 @@ public void SetOffset(FileOffset offset, RVA rva) { length = 0x28; importLookupTableRVA = rva + length; - length += 8; + length += is64bit ? 16U : 8; stringsPadding = (int)(rva.AlignUp(STRINGS_ALIGNMENT) - rva); length += (uint)stringsPadding; corXxxMainRVA = rva + length; - length += 0xE; - mscoreeDllRVA = rva + length; - length += 0xC; + length += 2 + (uint)EntryPointName.Length + 1; + dllToImportRVA = rva + length; + length += (uint)DllToImport.Length + 1; length++; } /// public uint GetFileLength() { + if (!Enable) + return 0; return length; } /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetVirtualSize() => GetFileLength(); /// - public void WriteTo(BinaryWriter writer) { - writer.Write((uint)importLookupTableRVA); - writer.Write(0); // DateTimeStamp - writer.Write(0); // ForwarderChain - writer.Write((uint)mscoreeDllRVA); // Name - writer.Write((uint)ImportAddressTable.RVA); - writer.Write(0UL); - writer.Write(0UL); - writer.Write(0); + public uint CalculateAlignment() => 0; - // ImportLookupTable - writer.Write((uint)corXxxMainRVA); - writer.Write(0); - - writer.WriteZeros(stringsPadding); - writer.Write((ushort)0); - writer.Write(Encoding.UTF8.GetBytes(IsExeFile ? "_CorExeMain\0" : "_CorDllMain\0")); - writer.Write(Encoding.UTF8.GetBytes("mscoree.dll\0")); + /// + public void WriteTo(DataWriter writer) { + if (!Enable) + return; + writer.WriteUInt32((uint)importLookupTableRVA); + writer.WriteInt32(0); // DateTimeStamp + writer.WriteInt32(0); // ForwarderChain + writer.WriteUInt32((uint)dllToImportRVA); // Name + writer.WriteUInt32((uint)ImportAddressTable.RVA); + writer.WriteUInt64(0); + writer.WriteUInt64(0); + writer.WriteInt32(0); - writer.Write((byte)0); + // ImportLookupTable + if (is64bit) { + writer.WriteUInt64((ulong)(uint)corXxxMainRVA); + writer.WriteUInt64(0); + } + else { + writer.WriteUInt32((uint)corXxxMainRVA); + writer.WriteInt32(0); + } + + writer.WriteZeroes(stringsPadding); + writer.WriteUInt16(0); + writer.WriteBytes(Encoding.UTF8.GetBytes(EntryPointName + "\0")); + writer.WriteBytes(Encoding.UTF8.GetBytes(DllToImport + "\0")); + + writer.WriteByte(0); } } } diff --git a/src/DotNet/Writer/MDTable.cs b/src/DotNet/Writer/MDTable.cs index b3874f9a9..0f9f356d1 100644 --- a/src/DotNet/Writer/MDTable.cs +++ b/src/DotNet/Writer/MDTable.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; +using System.Collections.Generic; using dnlib.DotNet.MD; namespace dnlib.DotNet.Writer { @@ -42,75 +42,51 @@ public interface IMDTable { /// Called when the table can't be modified any more /// void SetReadOnly(); - - /// - /// Gets a raw row - /// - /// Row ID - /// The raw row - IRawRow Get(uint rid); - - /// - /// Gets all raw rows - /// - IEnumerable GetRawRows(); } /// /// Creates rows in a table. Rows can optionally be shared to create a compact table. /// - /// The raw row type - public sealed class MDTable : IMDTable, IEnumerable where T : IRawRow { + /// The raw row type + public sealed class MDTable : IMDTable where TRow : struct { readonly Table table; - readonly Dictionary cachedDict; - readonly List cached; + readonly Dictionary cachedDict; + readonly List cached; TableInfo tableInfo; bool isSorted; bool isReadOnly; /// - public Table Table { - get { return table; } - } + public Table Table => table; /// - public bool IsEmpty { - get { return cached.Count == 0; } - } + public bool IsEmpty => cached.Count == 0; /// - public int Rows { - get { return cached.Count; } - } + public int Rows => cached.Count; /// public bool IsSorted { - get { return isSorted; } - set { isSorted = value; } + get => isSorted; + set => isSorted = value; } /// - public bool IsReadOnly { - get { return isReadOnly; } - } + public bool IsReadOnly => isReadOnly; /// public TableInfo TableInfo { - get { return tableInfo; } - set { tableInfo = value; } + get => tableInfo; + set => tableInfo = value; } /// /// Gets the value with rid /// /// The row ID - public T this[uint rid] { - get { return cached[(int)rid - 1]; } - } - - /// - public IRawRow Get(uint rid) { - return this[rid]; + public TRow this[uint rid] { + get => cached[(int)rid - 1]; + set => cached[(int)rid - 1] = value; } /// @@ -118,16 +94,14 @@ public IRawRow Get(uint rid) { /// /// The table type /// Equality comparer - public MDTable(Table table, IEqualityComparer equalityComparer) { + public MDTable(Table table, IEqualityComparer equalityComparer) { this.table = table; - this.cachedDict = new Dictionary(equalityComparer); - this.cached = new List(); + cachedDict = new Dictionary(equalityComparer); + cached = new List(); } /// - public void SetReadOnly() { - isReadOnly = true; - } + public void SetReadOnly() => isReadOnly = true; /// /// Adds a row. If the row already exists, returns a rid to the existing one, else @@ -135,11 +109,10 @@ public void SetReadOnly() { /// /// The row. It's now owned by us and must NOT be modified by the caller. /// The RID (row ID) of the row - public uint Add(T row) { + public uint Add(TRow row) { if (isReadOnly) - throw new ModuleWriterException(string.Format("Trying to modify table {0} after it's been set to read-only", table)); - uint rid; - if (cachedDict.TryGetValue(row, out rid)) + throw new ModuleWriterException($"Trying to modify table {table} after it's been set to read-only"); + if (cachedDict.TryGetValue(row, out uint rid)) return rid; return Create(row); } @@ -149,9 +122,9 @@ public uint Add(T row) { /// /// The row. It's now owned by us and must NOT be modified by the caller. /// The RID (row ID) of the row - public uint Create(T row) { + public uint Create(TRow row) { if (isReadOnly) - throw new ModuleWriterException(string.Format("Trying to modify table {0} after it's been set to read-only", table)); + throw new ModuleWriterException($"Trying to modify table {table} after it's been set to read-only"); uint rid = (uint)cached.Count + 1; if (!cachedDict.ContainsKey(row)) cachedDict[row] = rid; @@ -165,7 +138,7 @@ public uint Create(T row) { /// public void ReAddRows() { if (isReadOnly) - throw new ModuleWriterException(string.Format("Trying to modify table {0} after it's been set to read-only", table)); + throw new ModuleWriterException($"Trying to modify table {table} after it's been set to read-only"); cachedDict.Clear(); for (int i = 0; i < cached.Count; i++) { uint rid = (uint)i + 1; @@ -180,25 +153,9 @@ public void ReAddRows() { /// public void Reset() { if (isReadOnly) - throw new ModuleWriterException(string.Format("Trying to modify table {0} after it's been set to read-only", table)); + throw new ModuleWriterException($"Trying to modify table {table} after it's been set to read-only"); cachedDict.Clear(); cached.Clear(); } - - /// - public IEnumerator GetEnumerator() { - return cached.GetEnumerator(); - } - - /// - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return GetEnumerator(); - } - - /// - public IEnumerable GetRawRows() { - foreach (var rawRow in cached) - yield return rawRow; - } } } diff --git a/src/DotNet/Writer/MDTableWriter.cs b/src/DotNet/Writer/MDTableWriter.cs index 755e7434c..be57119b6 100644 --- a/src/DotNet/Writer/MDTableWriter.cs +++ b/src/DotNet/Writer/MDTableWriter.cs @@ -1,6 +1,5 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info -using System.IO; using dnlib.DotNet.MD; namespace dnlib.DotNet.Writer { @@ -8,104 +7,26 @@ namespace dnlib.DotNet.Writer { /// Writes s /// public static class MDTableWriter { - /// - /// Writes a raw row - /// - /// Writer - /// Table - /// Row - public static void Write(this BinaryWriter writer, IMDTable table, IRawRow row) { - if (table.Table == Table.Constant) { - var cols = table.TableInfo.Columns; - var row2 = (RawConstantRow)row; - writer.Write(row2.Type); - writer.Write(row2.Padding); - cols[1].Write(writer, row2.Parent); - cols[2].Write(writer, row2.Value); - } - else { - var cols = table.TableInfo.Columns; - foreach (var col in cols) - col.Write(writer, row.Read(col.Index)); - } - } - - /// - /// Writes a metadata table - /// - /// Writer - /// Table - public static void Write(this BinaryWriter writer, IMDTable table) { - switch (table.Table) { - case Table.Module: writer.Write((MDTable)table); break; - case Table.TypeRef: writer.Write((MDTable)table); break; - case Table.TypeDef: writer.Write((MDTable)table); break; - case Table.FieldPtr: writer.Write((MDTable)table); break; - case Table.Field: writer.Write((MDTable)table); break; - case Table.MethodPtr: writer.Write((MDTable)table); break; - case Table.Method: writer.Write((MDTable)table); break; - case Table.ParamPtr: writer.Write((MDTable)table); break; - case Table.Param: writer.Write((MDTable)table); break; - case Table.InterfaceImpl: writer.Write((MDTable)table); break; - case Table.MemberRef: writer.Write((MDTable)table); break; - case Table.Constant: writer.Write((MDTable)table); break; - case Table.CustomAttribute: writer.Write((MDTable)table); break; - case Table.FieldMarshal: writer.Write((MDTable)table); break; - case Table.DeclSecurity: writer.Write((MDTable)table); break; - case Table.ClassLayout: writer.Write((MDTable)table); break; - case Table.FieldLayout: writer.Write((MDTable)table); break; - case Table.StandAloneSig: writer.Write((MDTable)table); break; - case Table.EventMap: writer.Write((MDTable)table); break; - case Table.EventPtr: writer.Write((MDTable)table); break; - case Table.Event: writer.Write((MDTable)table); break; - case Table.PropertyMap: writer.Write((MDTable)table); break; - case Table.PropertyPtr: writer.Write((MDTable)table); break; - case Table.Property: writer.Write((MDTable)table); break; - case Table.MethodSemantics: writer.Write((MDTable)table); break; - case Table.MethodImpl: writer.Write((MDTable)table); break; - case Table.ModuleRef: writer.Write((MDTable)table); break; - case Table.TypeSpec: writer.Write((MDTable)table); break; - case Table.ImplMap: writer.Write((MDTable)table); break; - case Table.FieldRVA: writer.Write((MDTable)table); break; - case Table.ENCLog: writer.Write((MDTable)table); break; - case Table.ENCMap: writer.Write((MDTable)table); break; - case Table.Assembly: writer.Write((MDTable)table); break; - case Table.AssemblyProcessor: writer.Write((MDTable)table); break; - case Table.AssemblyOS: writer.Write((MDTable)table); break; - case Table.AssemblyRef: writer.Write((MDTable)table); break; - case Table.AssemblyRefProcessor: writer.Write((MDTable)table); break; - case Table.AssemblyRefOS: writer.Write((MDTable)table); break; - case Table.File: writer.Write((MDTable)table); break; - case Table.ExportedType: writer.Write((MDTable)table); break; - case Table.ManifestResource:writer.Write((MDTable)table); break; - case Table.NestedClass: writer.Write((MDTable)table); break; - case Table.GenericParam: writer.Write((MDTable)table); break; - case Table.MethodSpec: writer.Write((MDTable)table); break; - case Table.GenericParamConstraint: writer.Write((MDTable)table); break; - - default: - var cols = table.TableInfo.Columns; - foreach (var row in table.GetRawRows()) { - foreach (var col in cols) - col.Write(writer, row.Read(col.Index)); - } - break; - } - } - /// /// Writes a Module table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.Generation); - cols[1].Write(writer, row.Name); - cols[2].Write(writer, row.Mvid); - cols[3].Write(writer, row.EncId); - cols[4].Write(writer, row.EncBaseId); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns1 = columns[1]; + var columns2 = columns[2]; + var columns3 = columns[3]; + var columns4 = columns[4]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt16(row.Generation); + columns1.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns2.Write24(writer, row.Mvid); + columns3.Write24(writer, row.EncId); + columns4.Write24(writer, row.EncBaseId); } } @@ -113,13 +34,19 @@ public static void Write(this BinaryWriter writer, MDTable table) /// Writes a TypeRef table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - cols[0].Write(writer, row.ResolutionScope); - cols[1].Write(writer, row.Name); - cols[2].Write(writer, row.Namespace); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + var columns2 = columns[2]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.ResolutionScope); + columns1.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns2.Write24(writer, stringsHeap.GetOffset(row.Namespace)); } } @@ -127,16 +54,24 @@ public static void Write(this BinaryWriter writer, MDTable table) /// Writes a TypeDef table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.Flags); - cols[1].Write(writer, row.Name); - cols[2].Write(writer, row.Namespace); - cols[3].Write(writer, row.Extends); - cols[4].Write(writer, row.FieldList); - cols[5].Write(writer, row.MethodList); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns1 = columns[1]; + var columns2 = columns[2]; + var columns3 = columns[3]; + var columns4 = columns[4]; + var columns5 = columns[5]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.Flags); + columns1.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns2.Write24(writer, stringsHeap.GetOffset(row.Namespace)); + columns3.Write24(writer, row.Extends); + columns4.Write24(writer, row.FieldList); + columns5.Write24(writer, row.MethodList); } } @@ -144,24 +79,33 @@ public static void Write(this BinaryWriter writer, MDTable table) /// Writes a FieldPtr table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) - cols[0].Write(writer, row.Field); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Field); + } } /// /// Writes a Field table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.Flags); - cols[1].Write(writer, row.Name); - cols[2].Write(writer, row.Signature); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns1 = columns[1]; + var columns2 = columns[2]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt16(row.Flags); + columns1.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns2.Write24(writer, row.Signature); } } @@ -169,27 +113,37 @@ public static void Write(this BinaryWriter writer, MDTable table) { /// Writes a MethodPtr table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) - cols[0].Write(writer, row.Method); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Method); + } } /// /// Writes a Method table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.RVA); - writer.Write(row.ImplFlags); - writer.Write(row.Flags); - cols[3].Write(writer, row.Name); - cols[4].Write(writer, row.Signature); - cols[5].Write(writer, row.ParamList); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns3 = columns[3]; + var columns4 = columns[4]; + var columns5 = columns[5]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.RVA); + writer.WriteUInt16(row.ImplFlags); + writer.WriteUInt16(row.Flags); + columns3.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns4.Write24(writer, row.Signature); + columns5.Write24(writer, row.ParamList); } } @@ -197,24 +151,32 @@ public static void Write(this BinaryWriter writer, MDTable table) /// Writes a ParamPtr table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) - cols[0].Write(writer, row.Param); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Param); + } } /// /// Writes a Param table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.Flags); - writer.Write(row.Sequence); - cols[2].Write(writer, row.Name); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns2 = columns[2]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt16(row.Flags); + writer.WriteUInt16(row.Sequence); + columns2.Write24(writer, stringsHeap.GetOffset(row.Name)); } } @@ -222,12 +184,16 @@ public static void Write(this BinaryWriter writer, MDTable table) { /// Writes a InterfaceImpl table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - cols[0].Write(writer, row.Class); - cols[1].Write(writer, row.Interface); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Class); + columns1.Write24(writer, row.Interface); } } @@ -235,13 +201,19 @@ public static void Write(this BinaryWriter writer, MDTable /// Writes a MemberRef table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - cols[0].Write(writer, row.Class); - cols[1].Write(writer, row.Name); - cols[2].Write(writer, row.Signature); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + var columns2 = columns[2]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Class); + columns1.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns2.Write24(writer, row.Signature); } } @@ -249,14 +221,18 @@ public static void Write(this BinaryWriter writer, MDTable tabl /// Writes a Constant table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.Type); - writer.Write(row.Padding); - cols[1].Write(writer, row.Parent); - cols[2].Write(writer, row.Value); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns2 = columns[2]; + var columns3 = columns[3]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteByte(row.Type); + writer.WriteByte(row.Padding); + columns2.Write24(writer, row.Parent); + columns3.Write24(writer, row.Value); } } @@ -264,13 +240,18 @@ public static void Write(this BinaryWriter writer, MDTable table /// Writes a CustomAttribute table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - cols[0].Write(writer, row.Parent); - cols[1].Write(writer, row.Type); - cols[2].Write(writer, row.Value); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + var columns2 = columns[2]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Parent); + columns1.Write24(writer, row.Type); + columns2.Write24(writer, row.Value); } } @@ -278,12 +259,16 @@ public static void Write(this BinaryWriter writer, MDTableFieldMarshal table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - cols[0].Write(writer, row.Parent); - cols[1].Write(writer, row.NativeType); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Parent); + columns1.Write24(writer, row.NativeType); } } @@ -291,13 +276,17 @@ public static void Write(this BinaryWriter writer, MDTable t /// Writes a DeclSecurity table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.Action); - cols[1].Write(writer, row.Parent); - cols[2].Write(writer, row.PermissionSet); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns1 = columns[1]; + var columns2 = columns[2]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteInt16(row.Action); + columns1.Write24(writer, row.Parent); + columns2.Write24(writer, row.PermissionSet); } } @@ -305,13 +294,16 @@ public static void Write(this BinaryWriter writer, MDTable t /// Writes a ClassLayout table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.PackingSize); - writer.Write(row.ClassSize); - cols[2].Write(writer, row.Parent); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns2 = columns[2]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt16(row.PackingSize); + writer.WriteUInt32(row.ClassSize); + columns2.Write24(writer, row.Parent); } } @@ -319,12 +311,15 @@ public static void Write(this BinaryWriter writer, MDTable ta /// Writes a FieldLayout table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.OffSet); - cols[1].Write(writer, row.Field); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns1 = columns[1]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.OffSet); + columns1.Write24(writer, row.Field); } } @@ -332,23 +327,31 @@ public static void Write(this BinaryWriter writer, MDTable ta /// Writes a StandAloneSig table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) - cols[0].Write(writer, row.Signature); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Signature); + } } /// /// Writes a EventMap table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - cols[0].Write(writer, row.Parent); - cols[1].Write(writer, row.EventList); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Parent); + columns1.Write24(writer, row.EventList); } } @@ -356,24 +359,33 @@ public static void Write(this BinaryWriter writer, MDTable table /// Writes a EventPtr table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) - cols[0].Write(writer, row.Event); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Event); + } } /// /// Writes a Event table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.EventFlags); - cols[1].Write(writer, row.Name); - cols[2].Write(writer, row.EventType); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns1 = columns[1]; + var columns2 = columns[2]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt16(row.EventFlags); + columns1.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns2.Write24(writer, row.EventType); } } @@ -381,12 +393,16 @@ public static void Write(this BinaryWriter writer, MDTable table) { /// Writes a PropertyMap table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - cols[0].Write(writer, row.Parent); - cols[1].Write(writer, row.PropertyList); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Parent); + columns1.Write24(writer, row.PropertyList); } } @@ -394,24 +410,33 @@ public static void Write(this BinaryWriter writer, MDTable ta /// Writes a PropertyPtr table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) - cols[0].Write(writer, row.Property); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Property); + } } /// /// Writes a Property table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.PropFlags); - cols[1].Write(writer, row.Name); - cols[2].Write(writer, row.Type); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns1 = columns[1]; + var columns2 = columns[2]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt16(row.PropFlags); + columns1.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns2.Write24(writer, row.Type); } } @@ -419,13 +444,17 @@ public static void Write(this BinaryWriter writer, MDTable table /// Writes a MethodSemantics table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.Semantic); - cols[1].Write(writer, row.Method); - cols[2].Write(writer, row.Association); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns1 = columns[1]; + var columns2 = columns[2]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt16(row.Semantic); + columns1.Write24(writer, row.Method); + columns2.Write24(writer, row.Association); } } @@ -433,13 +462,18 @@ public static void Write(this BinaryWriter writer, MDTableMethodImpl table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - cols[0].Write(writer, row.Class); - cols[1].Write(writer, row.MethodBody); - cols[2].Write(writer, row.MethodDeclaration); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + var columns2 = columns[2]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Class); + columns1.Write24(writer, row.MethodBody); + columns2.Write24(writer, row.MethodDeclaration); } } @@ -447,36 +481,51 @@ public static void Write(this BinaryWriter writer, MDTable tab /// Writes a ModuleRef table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) - cols[0].Write(writer, row.Name); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, stringsHeap.GetOffset(row.Name)); + } } /// /// Writes a TypeSpec table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) - cols[0].Write(writer, row.Signature); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Signature); + } } /// /// Writes a ImplMap table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.MappingFlags); - cols[1].Write(writer, row.MemberForwarded); - cols[2].Write(writer, row.ImportName); - cols[3].Write(writer, row.ImportScope); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns1 = columns[1]; + var columns2 = columns[2]; + var columns3 = columns[3]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt16(row.MappingFlags); + columns1.Write24(writer, row.MemberForwarded); + columns2.Write24(writer, stringsHeap.GetOffset(row.ImportName)); + columns3.Write24(writer, row.ImportScope); } } @@ -484,12 +533,15 @@ public static void Write(this BinaryWriter writer, MDTable table) /// Writes a FieldRVA table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.RVA); - cols[1].Write(writer, row.Field); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns1 = columns[1]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.RVA); + columns1.Write24(writer, row.Field); } } @@ -497,11 +549,13 @@ public static void Write(this BinaryWriter writer, MDTable table /// Writes a ENCLog table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - foreach (var row in table) { - writer.Write(row.Token); - writer.Write(row.FuncCode); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.Token); + writer.WriteUInt32(row.FuncCode); } } @@ -509,29 +563,38 @@ public static void Write(this BinaryWriter writer, MDTable table) /// Writes a ENCMap table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - foreach (var row in table) - writer.Write(row.Token); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.Token); + } } /// /// Writes a Assembly table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.HashAlgId); - writer.Write(row.MajorVersion); - writer.Write(row.MinorVersion); - writer.Write(row.BuildNumber); - writer.Write(row.RevisionNumber); - writer.Write(row.Flags); - cols[6].Write(writer, row.PublicKey); - cols[7].Write(writer, row.Name); - cols[8].Write(writer, row.Locale); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns6 = columns[6]; + var columns7 = columns[7]; + var columns8 = columns[8]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.HashAlgId); + writer.WriteUInt16(row.MajorVersion); + writer.WriteUInt16(row.MinorVersion); + writer.WriteUInt16(row.BuildNumber); + writer.WriteUInt16(row.RevisionNumber); + writer.WriteUInt32(row.Flags); + columns6.Write24(writer, row.PublicKey); + columns7.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns8.Write24(writer, stringsHeap.GetOffset(row.Locale)); } } @@ -539,22 +602,27 @@ public static void Write(this BinaryWriter writer, MDTable table /// Writes a AssemblyProcessor table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - foreach (var row in table) - writer.Write(row.Processor); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.Processor); + } } /// /// Writes a AssemblyOS table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - foreach (var row in table) { - writer.Write(row.OSPlatformId); - writer.Write(row.OSMajorVersion); - writer.Write(row.OSMinorVersion); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.OSPlatformId); + writer.WriteUInt32(row.OSMajorVersion); + writer.WriteUInt32(row.OSMinorVersion); } } @@ -562,19 +630,26 @@ public static void Write(this BinaryWriter writer, MDTable tab /// Writes a AssemblyRef table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.MajorVersion); - writer.Write(row.MinorVersion); - writer.Write(row.BuildNumber); - writer.Write(row.RevisionNumber); - writer.Write(row.Flags); - cols[5].Write(writer, row.PublicKeyOrToken); - cols[6].Write(writer, row.Name); - cols[7].Write(writer, row.Locale); - cols[8].Write(writer, row.HashValue); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns5 = columns[5]; + var columns6 = columns[6]; + var columns7 = columns[7]; + var columns8 = columns[8]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt16(row.MajorVersion); + writer.WriteUInt16(row.MinorVersion); + writer.WriteUInt16(row.BuildNumber); + writer.WriteUInt16(row.RevisionNumber); + writer.WriteUInt32(row.Flags); + columns5.Write24(writer, row.PublicKeyOrToken); + columns6.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns7.Write24(writer, stringsHeap.GetOffset(row.Locale)); + columns8.Write24(writer, row.HashValue); } } @@ -582,12 +657,15 @@ public static void Write(this BinaryWriter writer, MDTable ta /// Writes a AssemblyRefProcessor table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.Processor); - cols[1].Write(writer, row.AssemblyRef); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns1 = columns[1]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.Processor); + columns1.Write24(writer, row.AssemblyRef); } } @@ -595,14 +673,17 @@ public static void Write(this BinaryWriter writer, MDTableAssemblyRefOS table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.OSPlatformId); - writer.Write(row.OSMajorVersion); - writer.Write(row.OSMinorVersion); - cols[3].Write(writer, row.AssemblyRef); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns3 = columns[3]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.OSPlatformId); + writer.WriteUInt32(row.OSMajorVersion); + writer.WriteUInt32(row.OSMinorVersion); + columns3.Write24(writer, row.AssemblyRef); } } @@ -610,13 +691,18 @@ public static void Write(this BinaryWriter writer, MDTable /// Writes a File table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.Flags); - cols[1].Write(writer, row.Name); - cols[2].Write(writer, row.HashValue); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns1 = columns[1]; + var columns2 = columns[2]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.Flags); + columns1.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns2.Write24(writer, row.HashValue); } } @@ -624,15 +710,21 @@ public static void Write(this BinaryWriter writer, MDTable table) { /// Writes a ExportedType table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.Flags); - writer.Write(row.TypeDefId); - cols[2].Write(writer, row.TypeName); - cols[3].Write(writer, row.TypeNamespace); - cols[4].Write(writer, row.Implementation); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns2 = columns[2]; + var columns3 = columns[3]; + var columns4 = columns[4]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.Flags); + writer.WriteUInt32(row.TypeDefId); + columns2.Write24(writer, stringsHeap.GetOffset(row.TypeName)); + columns3.Write24(writer, stringsHeap.GetOffset(row.TypeNamespace)); + columns4.Write24(writer, row.Implementation); } } @@ -640,14 +732,19 @@ public static void Write(this BinaryWriter writer, MDTable t /// Writes a ManifestResource table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - writer.Write(row.Offset); - writer.Write(row.Flags); - cols[2].Write(writer, row.Name); - cols[3].Write(writer, row.Implementation); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns2 = columns[2]; + var columns3 = columns[3]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt32(row.Offset); + writer.WriteUInt32(row.Flags); + columns2.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns3.Write24(writer, row.Implementation); } } @@ -655,12 +752,16 @@ public static void Write(this BinaryWriter writer, MDTableNestedClass table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - cols[0].Write(writer, row.NestedClass); - cols[1].Write(writer, row.EnclosingClass); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.NestedClass); + columns1.Write24(writer, row.EnclosingClass); } } @@ -668,17 +769,32 @@ public static void Write(this BinaryWriter writer, MDTable ta /// Writes a GenericParam table /// /// Writer - /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - bool useKindColumn = cols.Count >= 5; - foreach (var row in table) { - writer.Write(row.Number); - writer.Write(row.Flags); - cols[2].Write(writer, row.Owner); - cols[3].Write(writer, row.Name); - if (useKindColumn) - cols[4].Write(writer, row.Kind); + /// Metadata + /// Table + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns2 = columns[2]; + var columns3 = columns[3]; + var stringsHeap = metadata.StringsHeap; + if (columns.Length >= 5) { + var columns4 = columns[4]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt16(row.Number); + writer.WriteUInt16(row.Flags); + columns2.Write24(writer, row.Owner); + columns3.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns4.Write24(writer, row.Kind); + } + } + else { + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt16(row.Number); + writer.WriteUInt16(row.Flags); + columns2.Write24(writer, row.Owner); + columns3.Write24(writer, stringsHeap.GetOffset(row.Name)); + } } } @@ -686,12 +802,16 @@ public static void Write(this BinaryWriter writer, MDTable t /// Writes a MethodSpec table /// /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - cols[0].Write(writer, row.Method); - cols[1].Write(writer, row.Instantiation); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Method); + columns1.Write24(writer, row.Instantiation); } } @@ -699,12 +819,166 @@ public static void Write(this BinaryWriter writer, MDTable tab /// Writes a GenericParamConstraint table /// /// Writer + /// Metadata + /// Table + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Owner); + columns1.Write24(writer, row.Constraint); + } + } + + /// + /// Writes a Document table + /// + /// Writer + /// Metadata + /// Table + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + var columns2 = columns[2]; + var columns3 = columns[3]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Name); + columns1.Write24(writer, row.HashAlgorithm); + columns2.Write24(writer, row.Hash); + columns3.Write24(writer, row.Language); + } + } + + /// + /// Writes a MethodDebugInformation table + /// + /// Writer + /// Metadata + /// Table + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Document); + columns1.Write24(writer, row.SequencePoints); + } + } + + /// + /// Writes a LocalScope table + /// + /// Writer + /// Metadata + /// Table + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + var columns2 = columns[2]; + var columns3 = columns[3]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Method); + columns1.Write24(writer, row.ImportScope); + columns2.Write24(writer, row.VariableList); + columns3.Write24(writer, row.ConstantList); + writer.WriteUInt32(row.StartOffset); + writer.WriteUInt32(row.Length); + } + } + + /// + /// Writes a LocalVariable table + /// + /// Writer + /// Metadata + /// Table + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns2 = columns[2]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + writer.WriteUInt16(row.Attributes); + writer.WriteUInt16(row.Index); + columns2.Write24(writer, stringsHeap.GetOffset(row.Name)); + } + } + + /// + /// Writes a LocalConstant table + /// + /// Writer + /// Metadata + /// Table + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + var stringsHeap = metadata.StringsHeap; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, stringsHeap.GetOffset(row.Name)); + columns1.Write24(writer, row.Signature); + } + } + + /// + /// Writes a ImportScope table + /// + /// Writer + /// Metadata + /// Table + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Parent); + columns1.Write24(writer, row.Imports); + } + } + + /// + /// Writes a StateMachineMethod table + /// + /// Writer + /// Metadata + /// Table + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.MoveNextMethod); + columns1.Write24(writer, row.KickoffMethod); + } + } + + /// + /// Writes a CustomDebugInformation table + /// + /// Writer + /// Metadata /// Table - public static void Write(this BinaryWriter writer, MDTable table) { - var cols = table.TableInfo.Columns; - foreach (var row in table) { - cols[0].Write(writer, row.Owner); - cols[1].Write(writer, row.Constraint); + public static void Write(this DataWriter writer, Metadata metadata, MDTable table) { + var columns = table.TableInfo.Columns; + var columns0 = columns[0]; + var columns1 = columns[1]; + var columns2 = columns[2]; + for (int i = 0; i < table.Rows; i++) { + var row = table[(uint)i + 1]; + columns0.Write24(writer, row.Parent); + columns1.Write24(writer, row.Kind); + columns2.Write24(writer, row.Value); } } } diff --git a/src/DotNet/Writer/ManagedExportsWriter.cs b/src/DotNet/Writer/ManagedExportsWriter.cs new file mode 100644 index 000000000..6455b2ccb --- /dev/null +++ b/src/DotNet/Writer/ManagedExportsWriter.cs @@ -0,0 +1,531 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using dnlib.IO; +using dnlib.PE; + +namespace dnlib.DotNet.Writer { + sealed class ManagedExportsWriter { + const uint DEFAULT_VTBL_FIXUPS_ALIGNMENT = 4; + const uint DEFAULT_SDATA_ALIGNMENT = 8; + const StubType stubType = StubType.Export; + readonly string moduleName; + readonly Machine machine; + readonly RelocDirectory relocDirectory; + readonly Metadata metadata; + readonly PEHeaders peHeaders; + readonly Action logError; + readonly VtableFixupsChunk vtableFixups; + readonly StubsChunk stubsChunk; + readonly SdataChunk sdataChunk; + readonly ExportDir exportDir; + readonly List vtables; + readonly List allMethodInfos; + readonly List sortedOrdinalMethodInfos; + readonly List sortedNameMethodInfos; + readonly CpuArch cpuArch; + uint exportDirOffset; + + bool Is64Bit => machine.Is64Bit(); + FileOffset ExportDirOffset => sdataChunk.FileOffset + exportDirOffset; + RVA ExportDirRVA => sdataChunk.RVA + exportDirOffset; + uint ExportDirSize => 0x28; + internal bool HasExports => vtables.Count != 0; + + sealed class ExportDir : IChunk { + readonly ManagedExportsWriter owner; + public FileOffset FileOffset => owner.ExportDirOffset; + public RVA RVA => owner.ExportDirRVA; + public ExportDir(ManagedExportsWriter owner) => this.owner = owner; + void IChunk.SetOffset(FileOffset offset, RVA rva) => throw new NotSupportedException(); + public uint GetFileLength() => owner.ExportDirSize; + public uint GetVirtualSize() => GetFileLength(); + public uint CalculateAlignment() => 0; + void IChunk.WriteTo(DataWriter writer) => throw new NotSupportedException(); + } + + sealed class VtableFixupsChunk : IChunk { + readonly ManagedExportsWriter owner; + FileOffset offset; + RVA rva; + internal uint length; + public FileOffset FileOffset => offset; + public RVA RVA => rva; + public VtableFixupsChunk(ManagedExportsWriter owner) => this.owner = owner; + public void SetOffset(FileOffset offset, RVA rva) { + this.offset = offset; + this.rva = rva; + } + public uint GetFileLength() => length; + public uint GetVirtualSize() => GetFileLength(); + public uint CalculateAlignment() => 0; + public void WriteTo(DataWriter writer) => owner.WriteVtableFixups(writer); + } + + sealed class StubsChunk : IChunk { + readonly ManagedExportsWriter owner; + FileOffset offset; + RVA rva; + internal uint length; + public FileOffset FileOffset => offset; + public RVA RVA => rva; + public StubsChunk(ManagedExportsWriter owner) => this.owner = owner; + public void SetOffset(FileOffset offset, RVA rva) { + this.offset = offset; + this.rva = rva; + } + public uint GetFileLength() => length; + public uint GetVirtualSize() => GetFileLength(); + public uint CalculateAlignment() => 0; + public void WriteTo(DataWriter writer) => owner.WriteStubs(writer); + } + + sealed class SdataChunk : IChunk { + readonly ManagedExportsWriter owner; + FileOffset offset; + RVA rva; + internal uint length; + public FileOffset FileOffset => offset; + public RVA RVA => rva; + public SdataChunk(ManagedExportsWriter owner) => this.owner = owner; + public void SetOffset(FileOffset offset, RVA rva) { + this.offset = offset; + this.rva = rva; + } + public uint GetFileLength() => length; + public uint GetVirtualSize() => GetFileLength(); + public uint CalculateAlignment() => 0; + public void WriteTo(DataWriter writer) => owner.WriteSdata(writer); + } + + public ManagedExportsWriter(string moduleName, Machine machine, RelocDirectory relocDirectory, Metadata metadata, PEHeaders peHeaders, Action logError) { + this.moduleName = moduleName; + this.machine = machine; + this.relocDirectory = relocDirectory; + this.metadata = metadata; + this.peHeaders = peHeaders; + this.logError = logError; + vtableFixups = new VtableFixupsChunk(this); + stubsChunk = new StubsChunk(this); + sdataChunk = new SdataChunk(this); + exportDir = new ExportDir(this); + vtables = new List(); + allMethodInfos = new List(); + sortedOrdinalMethodInfos = new List(); + sortedNameMethodInfos = new List(); + // The error is reported later when we know that there's at least one exported method + CpuArch.TryGetCpuArch(machine, out cpuArch); + } + + internal void AddTextChunks(PESection textSection) { + textSection.Add(vtableFixups, DEFAULT_VTBL_FIXUPS_ALIGNMENT); + if (cpuArch is not null) + textSection.Add(stubsChunk, cpuArch.GetStubAlignment(stubType)); + } + + internal void AddSdataChunks(PESection sdataSection) => sdataSection.Add(sdataChunk, DEFAULT_SDATA_ALIGNMENT); + + internal void InitializeChunkProperties() { + if (allMethodInfos.Count == 0) + return; + peHeaders.ExportDirectory = exportDir; + peHeaders.ImageCor20Header.VtableFixups = vtableFixups; + } + + internal void AddExportedMethods(List methods, uint timestamp) { + if (methods.Count == 0) + return; + + // Only check for an unsupported machine when we know there's at least one exported method + if (cpuArch is null) { + logError("The module has exported methods but the CPU architecture isn't supported: {0} (0x{1:X4})", new object[] { machine, (ushort)machine }); + return; + } + if (methods.Count > 0x10000) { + logError("Too many methods have been exported. No more than 2^16 methods can be exported. Number of exported methods: {0}", new object[] { methods.Count }); + return; + } + + Initialize(methods, timestamp); + } + + sealed class MethodInfo { + public readonly MethodDef Method; + public readonly uint StubChunkOffset; + public int FunctionIndex; + public uint ManagedVtblOffset; + public uint NameOffset; + public int NameIndex; + public byte[] NameBytes; + public MethodInfo(MethodDef method, uint stubChunkOffset) { + Method = method; + StubChunkOffset = stubChunkOffset; + } + } + + sealed class VTableInfo { + public uint SdataChunkOffset { get; set; } + public readonly VTableFlags Flags; + public readonly List Methods; + public VTableInfo(VTableFlags flags) { + Flags = flags; + Methods = new List(); + } + } + + void Initialize(List methods, uint timestamp) { + var dict = new Dictionary>(); + var baseFlags = Is64Bit ? VTableFlags.Bit64 : VTableFlags.Bit32; + uint stubOffset = 0; + uint stubAlignment = cpuArch.GetStubAlignment(stubType); + uint stubCodeOffset = cpuArch.GetStubCodeOffset(stubType); + uint stubSize = cpuArch.GetStubSize(stubType); + foreach (var method in methods) { + var exportInfo = method.ExportInfo; + Debug.Assert(exportInfo is not null); + if (exportInfo is null) + continue; + + var flags = baseFlags; + if ((exportInfo.Options & MethodExportInfoOptions.FromUnmanaged) != 0) + flags |= VTableFlags.FromUnmanaged; + if ((exportInfo.Options & MethodExportInfoOptions.FromUnmanagedRetainAppDomain) != 0) + flags |= VTableFlags.FromUnmanagedRetainAppDomain; + if ((exportInfo.Options & MethodExportInfoOptions.CallMostDerived) != 0) + flags |= VTableFlags.CallMostDerived; + + if (!dict.TryGetValue((int)flags, out var list)) + dict.Add((int)flags, list = new List()); + if (list.Count == 0 || list[list.Count - 1].Methods.Count >= ushort.MaxValue) + list.Add(new VTableInfo(flags)); + var info = new MethodInfo(method, stubOffset + stubCodeOffset); + allMethodInfos.Add(info); + list[list.Count - 1].Methods.Add(info); + stubOffset = (stubOffset + stubSize + stubAlignment - 1) & ~(stubAlignment - 1); + } + + foreach (var kv in dict) + vtables.AddRange(kv.Value); + + WriteSdataBlob(timestamp); + + vtableFixups.length = (uint)vtables.Count * 8; + stubsChunk.length = stubOffset; + sdataChunk.length = (uint)sdataBytesInfo.Data.Length; + + uint expectedOffset = 0; + foreach (var info in allMethodInfos) { + uint currentOffset = info.StubChunkOffset - stubCodeOffset; + if (expectedOffset != currentOffset) + throw new InvalidOperationException(); + cpuArch.WriteStubRelocs(stubType, relocDirectory, stubsChunk, currentOffset); + expectedOffset = (currentOffset + stubSize + stubAlignment - 1) & ~(stubAlignment - 1); + } + if (expectedOffset != stubOffset) + throw new InvalidOperationException(); + } + + struct NamesBlob { + readonly Dictionary nameOffsets; + readonly List names; + readonly List methodNameOffsets; + uint currentOffset; + int methodNamesCount; + bool methodNamesIsFrozen; + + public int MethodNamesCount => methodNamesCount; + + readonly struct NameInfo { + public readonly uint Offset; + public readonly byte[] Bytes; + public NameInfo(uint offset, byte[] bytes) { + Offset = offset; + Bytes = bytes; + } + } + + public NamesBlob(bool dummy) { + nameOffsets = new Dictionary(StringComparer.Ordinal); + names = new List(); + methodNameOffsets = new List(); + currentOffset = 0; + methodNamesCount = 0; + methodNamesIsFrozen = false; + } + + public uint GetMethodNameOffset(string name, out byte[] bytes) { + if (methodNamesIsFrozen) + throw new InvalidOperationException(); + methodNamesCount++; + uint offset = GetOffset(name, out bytes); + methodNameOffsets.Add(offset); + return offset; + } + + public uint GetOtherNameOffset(string name) { + methodNamesIsFrozen = true; + return GetOffset(name, out var bytes); + } + + uint GetOffset(string name, out byte[] bytes) { + if (nameOffsets.TryGetValue(name, out var nameInfo)) { + bytes = nameInfo.Bytes; + return nameInfo.Offset; + } + bytes = GetNameASCIIZ(name); + names.Add(bytes); + uint offset = currentOffset; + nameOffsets.Add(name, new NameInfo(offset, bytes)); + currentOffset += (uint)bytes.Length; + return offset; + } + + // If this method gets updated, also update the reader (MethodExportInfoProvider) + static byte[] GetNameASCIIZ(string name) { + Debug.Assert(name is not null); + int size = Encoding.UTF8.GetByteCount(name); + var bytes = new byte[size + 1]; + Encoding.UTF8.GetBytes(name, 0, name.Length, bytes, 0); + if (bytes[bytes.Length - 1] != 0) + throw new ModuleWriterException(); + return bytes; + } + + public void Write(DataWriter writer) { + foreach (var name in names) + writer.WriteBytes(name); + } + + public uint[] GetMethodNameOffsets() => methodNameOffsets.ToArray(); + } + + struct SdataBytesInfo { + public byte[] Data; + public uint namesBlobStreamOffset; + public uint moduleNameOffset; + public uint exportDirModuleNameStreamOffset; + public uint exportDirAddressOfFunctionsStreamOffset; + public uint addressOfFunctionsStreamOffset; + public uint addressOfNamesStreamOffset; + public uint addressOfNameOrdinalsStreamOffset; + public uint[] MethodNameOffsets; + } + SdataBytesInfo sdataBytesInfo; + + /// + /// Writes the .sdata blob. We could write the data in any order, but we write the data in the same order as ILASM + /// + /// PE timestamp + void WriteSdataBlob(uint timestamp) { + var stream = new MemoryStream(); + var writer = new DataWriter(stream); + + // Write all vtables (referenced from the .text section) + Debug.Assert((writer.Position & 7) == 0); + foreach (var vtbl in vtables) { + vtbl.SdataChunkOffset = (uint)writer.Position; + foreach (var info in vtbl.Methods) { + info.ManagedVtblOffset = (uint)writer.Position; + writer.WriteUInt32(0x06000000 + metadata.GetRid(info.Method)); + if ((vtbl.Flags & VTableFlags.Bit64) != 0) + writer.WriteUInt32(0); + } + } + + var namesBlob = new NamesBlob(1 == 2); + int nameIndex = 0; + bool error = false; + foreach (var info in allMethodInfos) { + var exportInfo = info.Method.ExportInfo; + var name = exportInfo.Name; + if (name is null) { + if (exportInfo.Ordinal is not null) { + sortedOrdinalMethodInfos.Add(info); + continue; + } + name = info.Method.Name; + } + if (string.IsNullOrEmpty(name)) { + error = true; + logError("Exported method name is null or empty, method: {0} (0x{1:X8})", new object[] { info.Method, info.Method.MDToken.Raw }); + continue; + } + info.NameOffset = namesBlob.GetMethodNameOffset(name, out info.NameBytes); + info.NameIndex = nameIndex++; + sortedNameMethodInfos.Add(info); + } + Debug.Assert(error || sortedOrdinalMethodInfos.Count + sortedNameMethodInfos.Count == allMethodInfos.Count); + sdataBytesInfo.MethodNameOffsets = namesBlob.GetMethodNameOffsets(); + Debug.Assert(sortedNameMethodInfos.Count == sdataBytesInfo.MethodNameOffsets.Length); + sdataBytesInfo.moduleNameOffset = namesBlob.GetOtherNameOffset(moduleName); + + sortedOrdinalMethodInfos.Sort((a, b) => a.Method.ExportInfo.Ordinal.Value.CompareTo(b.Method.ExportInfo.Ordinal.Value)); + sortedNameMethodInfos.Sort((a, b) => CompareTo(a.NameBytes, b.NameBytes)); + + int ordinalBase, nextFreeOrdinal; + if (sortedOrdinalMethodInfos.Count == 0) { + ordinalBase = 0; + nextFreeOrdinal = 0; + } + else { + ordinalBase = sortedOrdinalMethodInfos[0].Method.ExportInfo.Ordinal.Value; + nextFreeOrdinal = sortedOrdinalMethodInfos[sortedOrdinalMethodInfos.Count - 1].Method.ExportInfo.Ordinal.Value + 1; + } + int nameFuncBaseIndex = nextFreeOrdinal - ordinalBase; + int lastFuncIndex = 0; + for (int i = 0; i < sortedOrdinalMethodInfos.Count; i++) { + int index = sortedOrdinalMethodInfos[i].Method.ExportInfo.Ordinal.Value - ordinalBase; + sortedOrdinalMethodInfos[i].FunctionIndex = index; + lastFuncIndex = index; + } + for (int i = 0; i < sortedNameMethodInfos.Count; i++) { + lastFuncIndex = nameFuncBaseIndex + i; + sortedNameMethodInfos[i].FunctionIndex = lastFuncIndex; + } + int funcSize = lastFuncIndex + 1; + if (funcSize > 0x10000) { + logError("Exported function array is too big", Array2.Empty()); + return; + } + + // Write IMAGE_EXPORT_DIRECTORY + Debug.Assert((writer.Position & 3) == 0); + exportDirOffset = (uint)writer.Position; + writer.WriteUInt32(0); // Characteristics + writer.WriteUInt32(timestamp); + writer.WriteUInt32(0); // MajorVersion, MinorVersion + sdataBytesInfo.exportDirModuleNameStreamOffset = (uint)writer.Position; + writer.WriteUInt32(0); // Name + writer.WriteInt32(ordinalBase); // Base + writer.WriteUInt32((uint)funcSize); // NumberOfFunctions + writer.WriteInt32(sdataBytesInfo.MethodNameOffsets.Length); // NumberOfNames + sdataBytesInfo.exportDirAddressOfFunctionsStreamOffset = (uint)writer.Position; + writer.WriteUInt32(0); // AddressOfFunctions + writer.WriteUInt32(0); // AddressOfNames + writer.WriteUInt32(0); // AddressOfNameOrdinals + + sdataBytesInfo.addressOfFunctionsStreamOffset = (uint)writer.Position; + writer.WriteZeroes(funcSize * 4); + sdataBytesInfo.addressOfNamesStreamOffset = (uint)writer.Position; + writer.WriteZeroes(sdataBytesInfo.MethodNameOffsets.Length * 4); + sdataBytesInfo.addressOfNameOrdinalsStreamOffset = (uint)writer.Position; + writer.WriteZeroes(sdataBytesInfo.MethodNameOffsets.Length * 2); + sdataBytesInfo.namesBlobStreamOffset = (uint)writer.Position; + namesBlob.Write(writer); + + sdataBytesInfo.Data = stream.ToArray(); + } + + void WriteSdata(DataWriter writer) { + if (sdataBytesInfo.Data is null) + return; + PatchSdataBytesBlob(); + writer.WriteBytes(sdataBytesInfo.Data); + } + + void PatchSdataBytesBlob() { + uint rva = (uint)sdataChunk.RVA; + uint namesBaseOffset = rva + sdataBytesInfo.namesBlobStreamOffset; + + var writer = new DataWriter(new MemoryStream(sdataBytesInfo.Data)); + + writer.Position = sdataBytesInfo.exportDirModuleNameStreamOffset; + writer.WriteUInt32(namesBaseOffset + sdataBytesInfo.moduleNameOffset); + + writer.Position = sdataBytesInfo.exportDirAddressOfFunctionsStreamOffset; + writer.WriteUInt32(rva + sdataBytesInfo.addressOfFunctionsStreamOffset); // AddressOfFunctions + if (sdataBytesInfo.MethodNameOffsets.Length != 0) { + writer.WriteUInt32(rva + sdataBytesInfo.addressOfNamesStreamOffset); // AddressOfNames + writer.WriteUInt32(rva + sdataBytesInfo.addressOfNameOrdinalsStreamOffset); // AddressOfNameOrdinals + } + + uint funcBaseRva = (uint)stubsChunk.RVA; + writer.Position = sdataBytesInfo.addressOfFunctionsStreamOffset; + int currentFuncIndex = 0; + foreach (var info in sortedOrdinalMethodInfos) { + int zeroes = info.FunctionIndex - currentFuncIndex; + if (zeroes < 0) + throw new InvalidOperationException(); + while (zeroes-- > 0) + writer.WriteInt32(0); + writer.WriteUInt32(funcBaseRva + info.StubChunkOffset); + currentFuncIndex = info.FunctionIndex + 1; + } + foreach (var info in sortedNameMethodInfos) { + if (info.FunctionIndex != currentFuncIndex++) + throw new InvalidOperationException(); + writer.WriteUInt32(funcBaseRva + info.StubChunkOffset); + } + + var nameOffsets = sdataBytesInfo.MethodNameOffsets; + if (nameOffsets.Length != 0) { + writer.Position = sdataBytesInfo.addressOfNamesStreamOffset; + foreach (var info in sortedNameMethodInfos) + writer.WriteUInt32(namesBaseOffset + nameOffsets[info.NameIndex]); + + writer.Position = sdataBytesInfo.addressOfNameOrdinalsStreamOffset; + foreach (var info in sortedNameMethodInfos) + writer.WriteUInt16((ushort)info.FunctionIndex); + } + } + + void WriteVtableFixups(DataWriter writer) { + if (vtables.Count == 0) + return; + + foreach (var vtbl in vtables) { + Debug.Assert(vtbl.Methods.Count <= ushort.MaxValue); + writer.WriteUInt32((uint)sdataChunk.RVA + vtbl.SdataChunkOffset); + writer.WriteUInt16((ushort)vtbl.Methods.Count); + writer.WriteUInt16((ushort)vtbl.Flags); + } + } + + void WriteStubs(DataWriter writer) { + if (vtables.Count == 0) + return; + if (cpuArch is null) + return; + + ulong imageBase = peHeaders.ImageBase; + uint stubsBaseRva = (uint)stubsChunk.RVA; + uint vtblBaseRva = (uint)sdataChunk.RVA; + uint expectedOffset = 0; + uint stubCodeOffset = cpuArch.GetStubCodeOffset(stubType); + uint stubSize = cpuArch.GetStubSize(stubType); + uint stubAlignment = cpuArch.GetStubAlignment(stubType); + int zeroes = (int)((stubSize + stubAlignment - 1 & ~(stubAlignment - 1)) - stubSize); + foreach (var info in allMethodInfos) { + uint currentOffset = info.StubChunkOffset - stubCodeOffset; + if (expectedOffset != currentOffset) + throw new InvalidOperationException(); + var pos = writer.Position; + cpuArch.WriteStub(stubType, writer, imageBase, stubsBaseRva + currentOffset, vtblBaseRva + info.ManagedVtblOffset); + Debug.Assert(pos + stubSize == writer.Position, "The full stub wasn't written"); + if (pos + stubSize != writer.Position) + throw new InvalidOperationException(); + if (zeroes != 0) + writer.WriteZeroes(zeroes); + expectedOffset = (currentOffset + stubSize + stubAlignment - 1) & ~(stubAlignment - 1); + } + if (expectedOffset != stubsChunk.length) + throw new InvalidOperationException(); + } + + static int CompareTo(byte[] a, byte[] b) { + if (a == b) + return 0; + int max = Math.Min(a.Length, b.Length); + for (int i = 0; i < max; i++) { + int c = a[i] - b[i]; + if (c != 0) + return c; + } + return a.Length - b.Length; + } + } +} diff --git a/src/DotNet/Writer/MarshalBlobWriter.cs b/src/DotNet/Writer/MarshalBlobWriter.cs index ae650354a..df5ead840 100644 --- a/src/DotNet/Writer/MarshalBlobWriter.cs +++ b/src/DotNet/Writer/MarshalBlobWriter.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; using System.IO; @@ -7,11 +7,12 @@ namespace dnlib.DotNet.Writer { /// /// Writes field marshal blobs /// - public struct MarshalBlobWriter : IDisposable, IFullNameCreatorHelper { + public readonly struct MarshalBlobWriter : IDisposable, IFullNameFactoryHelper { readonly ModuleDef module; readonly MemoryStream outStream; - readonly BinaryWriter writer; + readonly DataWriter writer; readonly IWriterError helper; + readonly bool optimizeCustomAttributeSerializedTypeNames; /// /// Creates a field marshal blob from @@ -21,27 +22,41 @@ public struct MarshalBlobWriter : IDisposable, IFullNameCreatorHelper { /// Helps this class /// A field marshal blob or null if is /// null - public static byte[] Write(ModuleDef module, MarshalType marshalType, IWriterError helper) { - using (var writer = new MarshalBlobWriter(module, helper)) + public static byte[] Write(ModuleDef module, MarshalType marshalType, IWriterError helper) => + Write(module, marshalType, helper, false); + + /// + /// Creates a field marshal blob from + /// + /// Owner module + /// Marshal type + /// Helps this class + /// Optimize serialized type strings in custom attributes. + /// For more info, see + /// A field marshal blob or null if is + /// null + public static byte[] Write(ModuleDef module, MarshalType marshalType, IWriterError helper, bool optimizeCustomAttributeSerializedTypeNames) { + using (var writer = new MarshalBlobWriter(module, helper, optimizeCustomAttributeSerializedTypeNames)) return writer.Write(marshalType); } - MarshalBlobWriter(ModuleDef module, IWriterError helper) { + MarshalBlobWriter(ModuleDef module, IWriterError helper, bool optimizeCustomAttributeSerializedTypeNames) { this.module = module; - this.outStream = new MemoryStream(); - this.writer = new BinaryWriter(outStream); + outStream = new MemoryStream(); + writer = new DataWriter(outStream); this.helper = helper; + this.optimizeCustomAttributeSerializedTypeNames = optimizeCustomAttributeSerializedTypeNames; } byte[] Write(MarshalType marshalType) { - if (marshalType == null) + if (marshalType is null) return null; var type = marshalType.NativeType; if (type != NativeType.RawBlob) { if ((uint)type > byte.MaxValue) helper.Error("Invalid MarshalType.NativeType"); - writer.Write((byte)type); + writer.WriteByte((byte)type); } bool canWrite = true; switch (type) { @@ -84,7 +99,7 @@ byte[] Write(MarshalType marshalType) { Write(custMarshaler.Guid); Write(custMarshaler.NativeTypeName); var cm = custMarshaler.CustomMarshaler; - var cmName = cm == null ? string.Empty : FullNameCreator.AssemblyQualifiedName(cm, this); + var cmName = cm is null ? string.Empty : FullNameFactory.AssemblyQualifiedName(cm, this); Write(cmName); Write(custMarshaler.Cookie); break; @@ -99,22 +114,21 @@ byte[] Write(MarshalType marshalType) { case NativeType.RawBlob: var data = ((RawMarshalType)marshalType).Data; - if (data != null) - writer.Write(data); + if (data is not null) + writer.WriteBytes(data); break; default: break; } - writer.Flush(); return outStream.ToArray(); } bool UpdateCanWrite(bool isValid, string field, ref bool canWriteMore) { if (!canWriteMore) { if (isValid) - helper.Error(string.Format("MarshalType field {0} is valid even though a previous field was invalid", field)); + helper.Error2("MarshalType field {0} is valid even though a previous field was invalid.", field); return canWriteMore; } @@ -124,22 +138,14 @@ bool UpdateCanWrite(bool isValid, string field, ref bool canWriteMore) { return canWriteMore; } - uint WriteCompressedUInt32(uint value) { - return writer.WriteCompressedUInt32(helper, value); - } + uint WriteCompressedUInt32(uint value) => writer.WriteCompressedUInt32(helper, value); - void Write(UTF8String s) { - writer.Write(helper, s); - } + void Write(UTF8String s) => writer.Write(helper, s); /// - public void Dispose() { - if (outStream != null) - outStream.Dispose(); - } + public void Dispose() => outStream?.Dispose(); - bool IFullNameCreatorHelper.MustUseAssemblyName(IType type) { - return FullNameCreator.MustUseAssemblyName(module, type); - } + bool IFullNameFactoryHelper.MustUseAssemblyName(IType type) => + FullNameFactory.MustUseAssemblyName(module, type, optimizeCustomAttributeSerializedTypeNames); } } diff --git a/src/DotNet/Writer/MaxStackCalculator.cs b/src/DotNet/Writer/MaxStackCalculator.cs index 7654194a8..ea745e2d6 100644 --- a/src/DotNet/Writer/MaxStackCalculator.cs +++ b/src/DotNet/Writer/MaxStackCalculator.cs @@ -1,6 +1,5 @@ // dnlib: See LICENSE.txt for more info -using System; using System.Collections.Generic; using dnlib.DotNet.Emit; @@ -10,10 +9,11 @@ namespace dnlib.DotNet.Writer { /// can be placed in the fat method header's MaxStack field. /// public struct MaxStackCalculator { - readonly IList instructions; - readonly IList exceptionHandlers; + IList instructions; + IList exceptionHandlers; readonly Dictionary stackHeights; - int errors; + bool hasError; + int currentMaxStack; /// /// Gets max stack value @@ -22,8 +22,7 @@ public struct MaxStackCalculator { /// All exception handlers /// Max stack value public static uint GetMaxStack(IList instructions, IList exceptionHandlers) { - uint maxStack; - new MaxStackCalculator(instructions, exceptionHandlers).Calculate(out maxStack); + new MaxStackCalculator(instructions, exceptionHandlers).Calculate(out uint maxStack); return maxStack; } @@ -34,35 +33,66 @@ public static uint GetMaxStack(IList instructions, IListAll exception handlers /// Updated with max stack value /// true if no errors were detected, false otherwise - public static bool GetMaxStack(IList instructions, IList exceptionHandlers, out uint maxStack) { - return new MaxStackCalculator(instructions, exceptionHandlers).Calculate(out maxStack); + public static bool GetMaxStack(IList instructions, IList exceptionHandlers, out uint maxStack) => + new MaxStackCalculator(instructions, exceptionHandlers).Calculate(out maxStack); + + internal static MaxStackCalculator Create() => new MaxStackCalculator(true); + + MaxStackCalculator(bool dummy) { + instructions = null; + exceptionHandlers = null; + stackHeights = new Dictionary(); + hasError = false; + currentMaxStack = 0; } MaxStackCalculator(IList instructions, IList exceptionHandlers) { this.instructions = instructions; this.exceptionHandlers = exceptionHandlers; - this.stackHeights = new Dictionary(); - this.errors = 0; + stackHeights = new Dictionary(); + hasError = false; + currentMaxStack = 0; + } + + internal void Reset(IList instructions, IList exceptionHandlers) { + this.instructions = instructions; + this.exceptionHandlers = exceptionHandlers; + stackHeights.Clear(); + hasError = false; + currentMaxStack = 0; } - bool Calculate(out uint maxStack) { - foreach (var eh in exceptionHandlers) { - if (eh == null) + internal bool Calculate(out uint maxStack) { + var exceptionHandlers = this.exceptionHandlers; + var stackHeights = this.stackHeights; + for (int i = 0; i < exceptionHandlers.Count; i++) { + var eh = exceptionHandlers[i]; + if (eh is null) continue; - if (eh.TryStart != null) - stackHeights[eh.TryStart] = 0; - if (eh.FilterStart != null) - stackHeights[eh.FilterStart] = 1; - if (eh.HandlerStart != null) { - bool pushed = eh.HandlerType == ExceptionHandlerType.Catch || eh.HandlerType == ExceptionHandlerType.Filter; - stackHeights[eh.HandlerStart] = pushed ? 1 : 0; + Instruction instr; + if ((instr = eh.TryStart) is not null) + stackHeights[instr] = 0; + if ((instr = eh.FilterStart) is not null) { + stackHeights[instr] = 1; + currentMaxStack = 1; + } + if ((instr = eh.HandlerStart) is not null) { + bool pushed = eh.IsCatch || eh.IsFilter; + if (pushed) { + stackHeights[instr] = 1; + currentMaxStack = 1; + } + else + stackHeights[instr] = 0; } } int stack = 0; bool resetStack = false; - foreach (var instr in instructions) { - if (instr == null) + var instructions = this.instructions; + for (int i = 0; i < instructions.Count; i++) { + var instr = instructions[i]; + if (instr is null) continue; if (resetStack) { @@ -70,47 +100,46 @@ bool Calculate(out uint maxStack) { resetStack = false; } stack = WriteStack(instr, stack); - - if (instr.OpCode.Code == Code.Jmp) { + var opCode = instr.OpCode; + var code = opCode.Code; + if (code == Code.Jmp) { if (stack != 0) - errors++; + hasError = true; } else { - int pushes, pops; - instr.CalculateStackUsage(out pushes, out pops); + instr.CalculateStackUsage(out int pushes, out int pops); if (pops == -1) stack = 0; else { stack -= pops; if (stack < 0) { - errors++; + hasError = true; stack = 0; } stack += pushes; } } if (stack < 0) { - errors++; + hasError = true; stack = 0; } - switch (instr.OpCode.FlowControl) { + switch (opCode.FlowControl) { case FlowControl.Branch: WriteStack(instr.Operand as Instruction, stack); resetStack = true; break; case FlowControl.Call: - if (instr.OpCode.Code == Code.Jmp) + if (code == Code.Jmp) resetStack = true; break; case FlowControl.Cond_Branch: - if (instr.OpCode.Code == Code.Switch) { - var targets = instr.Operand as IList; - if (targets != null) { - foreach (var target in targets) - WriteStack(target, stack); + if (code == Code.Switch) { + if (instr.Operand is IList targets) { + for (int j = 0; j < targets.Count; j++) + WriteStack(targets[j], stack); } } else @@ -124,25 +153,24 @@ bool Calculate(out uint maxStack) { } } - stack = 0; - foreach (var v in stackHeights.Values) - stack = Math.Max(stack, v); - maxStack = (uint)stack; - return errors == 0; + maxStack = (uint)currentMaxStack; + return !hasError; } int WriteStack(Instruction instr, int stack) { - if (instr == null) { - errors++; + if (instr is null) { + hasError = true; return stack; } - int stack2; - if (stackHeights.TryGetValue(instr, out stack2)) { + var stackHeights = this.stackHeights; + if (stackHeights.TryGetValue(instr, out int stack2)) { if (stack != stack2) - errors++; + hasError = true; return stack2; } stackHeights[instr] = stack; + if (stack > currentMaxStack) + currentMaxStack = stack; return stack; } } diff --git a/src/DotNet/Writer/MetaData.cs b/src/DotNet/Writer/MetaData.cs deleted file mode 100644 index ebe43b5b4..000000000 --- a/src/DotNet/Writer/MetaData.cs +++ /dev/null @@ -1,2766 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using dnlib.IO; -using dnlib.PE; -using dnlib.DotNet.MD; -using dnlib.DotNet.Emit; - -namespace dnlib.DotNet.Writer { - /// - /// flags - /// - [Flags] - public enum MetaDataFlags : uint { - /// - /// Preserves all rids in the TypeRef table - /// - PreserveTypeRefRids = 1, - - /// - /// Preserves all rids in the TypeDef table - /// - PreserveTypeDefRids = 2, - - /// - /// Preserves all rids in the Field table - /// - PreserveFieldRids = 4, - - /// - /// Preserves all rids in the Method table - /// - PreserveMethodRids = 8, - - /// - /// Preserves all rids in the Param table - /// - PreserveParamRids = 0x10, - - /// - /// Preserves all rids in the MemberRef table - /// - PreserveMemberRefRids = 0x20, - - /// - /// Preserves all rids in the StandAloneSig table - /// - PreserveStandAloneSigRids = 0x40, - - /// - /// Preserves all rids in the Event table - /// - PreserveEventRids = 0x80, - - /// - /// Preserves all rids in the Property table - /// - PreservePropertyRids = 0x100, - - /// - /// Preserves all rids in the TypeSpec table - /// - PreserveTypeSpecRids = 0x200, - - /// - /// Preserves all rids in the MethodSpec table - /// - PreserveMethodSpecRids = 0x400, - - /// - /// Preserves all method rids, i.e., Method, MemberRef and - /// MethodSpec rids. - /// - PreserveAllMethodRids = PreserveMethodRids | PreserveMemberRefRids | PreserveMethodSpecRids, - - /// - /// Preserves all rids in the following tables: TypeRef, TypeDef, - /// Field, Method, Param, MemberRef, StandAloneSig, - /// Event, Property, TypeSpec, MethodSpec - /// - PreserveRids = PreserveTypeRefRids | - PreserveTypeDefRids | - PreserveFieldRids | - PreserveMethodRids | - PreserveParamRids | - PreserveMemberRefRids | - PreserveStandAloneSigRids | - PreserveEventRids | - PreservePropertyRids | - PreserveTypeSpecRids | - PreserveMethodSpecRids, - - /// - /// Preserves all offsets in the #Strings heap (the original #Strings heap will be saved - /// in the new file). Type names, field names, and other non-user strings are stored - /// in the #Strings heap. - /// - PreserveStringsOffsets = 0x800, - - /// - /// Preserves all offsets in the #US heap (the original #US heap will be saved - /// in the new file). User strings (referenced by the ldstr instruction) are stored in - /// the #US heap. - /// - PreserveUSOffsets = 0x1000, - - /// - /// Preserves all offsets in the #Blob heap (the original #Blob heap will be saved - /// in the new file). Custom attributes, signatures and other blobs are stored in the - /// #Blob heap. - /// - PreserveBlobOffsets = 0x2000, - - /// - /// Preserves the extra data that is present after the original signature in the #Blob - /// heap. This extra data shouldn't be present but might be present if an obfuscator - /// has added this extra data and is eg. using it to decrypt stuff. - /// - PreserveExtraSignatureData = 0x4000, - - /// - /// Preserves as much as possible - /// - PreserveAll = PreserveRids | PreserveStringsOffsets | PreserveUSOffsets | - PreserveBlobOffsets | PreserveExtraSignatureData, - - /// - /// The original method body's max stack field should be used and a new one should not - /// be calculated. - /// - KeepOldMaxStack = 0x8000, - - /// - /// Always create the #GUID heap even if it's empty - /// - AlwaysCreateGuidHeap = 0x10000, - - /// - /// Always create the #Strings heap even if it's empty - /// - AlwaysCreateStringsHeap = 0x20000, - - /// - /// Always create the #US heap even if it's empty - /// - AlwaysCreateUSHeap = 0x40000, - - /// - /// Always create the #Blob heap even if it's empty - /// - AlwaysCreateBlobHeap = 0x80000, - } - - /// - /// options - /// - public sealed class MetaDataOptions { - MetaDataHeaderOptions metaDataHeaderOptions; - TablesHeapOptions tablesHeapOptions; - List otherHeaps; - List otherHeapsEnd; - - /// - /// Gets/sets the options. This is never null. - /// - public MetaDataHeaderOptions MetaDataHeaderOptions { - get { return metaDataHeaderOptions ?? (metaDataHeaderOptions = new MetaDataHeaderOptions()); } - set { metaDataHeaderOptions = value; } - } - - /// - /// Gets/sets the options. This is never null. - /// - public TablesHeapOptions TablesHeapOptions { - get { return tablesHeapOptions ?? (tablesHeapOptions = new TablesHeapOptions()); } - set { tablesHeapOptions = value; } - } - - /// - /// Various options - /// - public MetaDataFlags Flags; - - /// - /// Any additional heaps that should be added to the beginning of the heaps list - /// - public List OtherHeaps { - get { return otherHeaps ?? (otherHeaps = new List()); } - } - - /// - /// Any additional heaps that should be added to end of the heaps list - /// - public List OtherHeapsEnd { - get { return otherHeapsEnd ?? (otherHeapsEnd = new List()); } - } - - /// - /// Default constructor - /// - public MetaDataOptions() { - } - - /// - /// Constructor - /// - /// Flags - public MetaDataOptions(MetaDataFlags flags) { - this.Flags = flags; - } - - /// - /// Constructor - /// - /// Meta data header options - public MetaDataOptions(MetaDataHeaderOptions mdhOptions) { - this.metaDataHeaderOptions = mdhOptions; - } - - /// - /// Constructor - /// - /// Meta data header options - /// Flags - public MetaDataOptions(MetaDataHeaderOptions mdhOptions, MetaDataFlags flags) { - this.Flags = flags; - this.metaDataHeaderOptions = mdhOptions; - } - } - - /// - /// .NET meta data - /// - public abstract class MetaData : IChunk, ISignatureWriterHelper, ITokenCreator, ICustomAttributeWriterHelper { - uint length; - FileOffset offset; - RVA rva; - readonly MetaDataOptions options; - IMetaDataListener listener; - ILogger logger; - internal readonly ModuleDef module; - internal readonly UniqueChunkList constants; - internal readonly MethodBodyChunks methodBodies; - internal readonly NetResources netResources; - internal readonly MetaDataHeader metaDataHeader; - internal HotHeap hotHeap; - internal readonly TablesHeap tablesHeap; - internal readonly StringsHeap stringsHeap; - internal readonly USHeap usHeap; - internal readonly GuidHeap guidHeap; - internal readonly BlobHeap blobHeap; - internal List allTypeDefs; - internal readonly Rows moduleDefInfos = new Rows(); - internal readonly SortedRows interfaceImplInfos = new SortedRows(); - internal readonly SortedRows hasConstantInfos = new SortedRows(); - internal readonly SortedRows customAttributeInfos = new SortedRows(); - internal readonly SortedRows fieldMarshalInfos = new SortedRows(); - internal readonly SortedRows declSecurityInfos = new SortedRows(); - internal readonly SortedRows classLayoutInfos = new SortedRows(); - internal readonly SortedRows fieldLayoutInfos = new SortedRows(); - internal readonly Rows eventMapInfos = new Rows(); - internal readonly Rows propertyMapInfos = new Rows(); - internal readonly SortedRows methodSemanticsInfos = new SortedRows(); - internal readonly SortedRows methodImplInfos = new SortedRows(); - internal readonly Rows moduleRefInfos = new Rows(); - internal readonly SortedRows implMapInfos = new SortedRows(); - internal readonly SortedRows fieldRVAInfos = new SortedRows(); - internal readonly Rows assemblyInfos = new Rows(); - internal readonly Rows assemblyRefInfos = new Rows(); - internal readonly Rows fileDefInfos = new Rows(); - internal readonly Rows exportedTypeInfos = new Rows(); - internal readonly Rows manifestResourceInfos = new Rows(); - internal readonly SortedRows nestedClassInfos = new SortedRows(); - internal readonly SortedRows genericParamInfos = new SortedRows(); - internal readonly SortedRows genericParamConstraintInfos = new SortedRows(); - internal readonly Dictionary methodToBody = new Dictionary(); - internal readonly Dictionary methodToNativeBody = new Dictionary(); - internal readonly Dictionary embeddedResourceToByteArray = new Dictionary(); - readonly Dictionary fieldToInitialValue = new Dictionary(); - - /// - /// Gets/sets the listener - /// - public IMetaDataListener Listener { - get { return listener ?? (listener = DummyMetaDataListener.Instance); } - set { listener = value; } - } - - /// - /// Gets/sets the logger - /// - public ILogger Logger { - get { return logger; } - set { logger = value; } - } - - /// - /// Gets the module - /// - public ModuleDef Module { - get { return module; } - } - - /// - /// Gets the constants - /// - public UniqueChunkList Constants { - get { return constants; } - } - - /// - /// Gets the method body chunks - /// - public MethodBodyChunks MethodBodyChunks { - get { return methodBodies; } - } - - /// - /// Gets the .NET resources - /// - public NetResources NetResources { - get { return netResources; } - } - - /// - /// Gets the MD header - /// - public MetaDataHeader MetaDataHeader { - get { return metaDataHeader; } - } - - /// - /// Gets/sets the hot heap (#!) - /// - public HotHeap HotHeap { - get { return hotHeap; } - set { hotHeap = value; } - } - - /// - /// Gets the tables heap. Access to this heap is not recommended, but is useful if you - /// want to add random table entries. - /// - public TablesHeap TablesHeap { - get { return tablesHeap; } - } - - /// - /// Gets the #Strings heap. Access to this heap is not recommended, but is useful if you - /// want to add random strings. - /// - public StringsHeap StringsHeap { - get { return stringsHeap; } - } - - /// - /// Gets the #US heap. Access to this heap is not recommended, but is useful if - /// you want to add random user strings. - /// - public USHeap USHeap { - get { return usHeap; } - } - - /// - /// Gets the #GUID heap. Access to this heap is not recommended, but is useful if you - /// want to add random GUIDs. - /// - public GuidHeap GuidHeap { - get { return guidHeap; } - } - - /// - /// Gets the #Blob heap. Access to this heap is not recommended, but is useful if you - /// want to add random blobs. - /// - public BlobHeap BlobHeap { - get { return blobHeap; } - } - - /// - /// The public key that should be used instead of the one in . - /// - internal byte[] AssemblyPublicKey { get; set; } - - internal sealed class SortedRows where T : class where TRow : class { - public List infos = new List(); - Dictionary toRid = new Dictionary(); - bool isSorted; - - public struct Info { - public T data; - public TRow row; - public Info(T data, TRow row) { - this.data = data; - this.row = row; - } - } - - public void Add(T data, TRow row) { - if (isSorted) - throw new ModuleWriterException(string.Format("Adding a row after it's been sorted. Table: {0}", row.GetType())); - infos.Add(new Info(data, row)); - toRid[data] = (uint)toRid.Count + 1; - } - - public void Sort(Comparison.Info> comparison) { - infos.Sort(comparison); - toRid.Clear(); - for (int i = 0; i < infos.Count; i++) - toRid[infos[i].data] = (uint)i + 1; - isSorted = true; - } - - public uint Rid(T data) { - return toRid[data]; - } - - public bool TryGetRid(T data, out uint rid) { - if (data == null) { - rid = 0; - return false; - } - return toRid.TryGetValue(data, out rid); - } - } - - internal sealed class Rows where T : class { - Dictionary dict = new Dictionary(); - - public int Count { - get { return dict.Count; } - } - - public bool TryGetRid(T value, out uint rid) { - if (value == null) { - rid = 0; - return false; - } - return dict.TryGetValue(value, out rid); - } - - public bool Exists(T value) { - return dict.ContainsKey(value); - } - - public void Add(T value, uint rid) { - dict.Add(value, rid); - } - - public uint Rid(T value) { - return dict[value]; - } - - public void SetRid(T value, uint rid) { - dict[value] = rid; - } - } - - /// - /// Creates a instance - /// - /// Module - /// Constants list - /// Method bodies list - /// .NET resources list - /// A new instance - public static MetaData Create(ModuleDef module, UniqueChunkList constants, MethodBodyChunks methodBodies, NetResources netResources) { - return Create(module, constants, methodBodies, netResources, null); - } - - /// - /// Creates a instance - /// - /// Module - /// Constants list - /// Method bodies list - /// .NET resources list - /// Options - /// A new instance - public static MetaData Create(ModuleDef module, UniqueChunkList constants, MethodBodyChunks methodBodies, NetResources netResources, MetaDataOptions options) { - if (options == null) - options = new MetaDataOptions(); - if ((options.Flags & MetaDataFlags.PreserveRids) != 0 && module is ModuleDefMD) - return new PreserveTokensMetaData(module, constants, methodBodies, netResources, options); - return new NormalMetaData(module, constants, methodBodies, netResources, options); - } - - /// - public FileOffset FileOffset { - get { return offset; } - } - - /// - public RVA RVA { - get { return rva; } - } - - /// - /// Gets the bit - /// - public bool PreserveTypeRefRids { - get { return (options.Flags & MetaDataFlags.PreserveTypeRefRids) != 0; } - } - - /// - /// Gets the bit - /// - public bool PreserveTypeDefRids { - get { return (options.Flags & MetaDataFlags.PreserveTypeDefRids) != 0; } - } - - /// - /// Gets the bit - /// - public bool PreserveFieldRids { - get { return (options.Flags & MetaDataFlags.PreserveFieldRids) != 0; } - } - - /// - /// Gets the bit - /// - public bool PreserveMethodRids { - get { return (options.Flags & MetaDataFlags.PreserveMethodRids) != 0; } - } - - /// - /// Gets the bit - /// - public bool PreserveParamRids { - get { return (options.Flags & MetaDataFlags.PreserveParamRids) != 0; } - } - - /// - /// Gets the bit - /// - public bool PreserveMemberRefRids { - get { return (options.Flags & MetaDataFlags.PreserveMemberRefRids) != 0; } - } - - /// - /// Gets the bit - /// - public bool PreserveStandAloneSigRids { - get { return (options.Flags & MetaDataFlags.PreserveStandAloneSigRids) != 0; } - } - - /// - /// Gets the bit - /// - public bool PreserveEventRids { - get { return (options.Flags & MetaDataFlags.PreserveEventRids) != 0; } - } - - /// - /// Gets the bit - /// - public bool PreservePropertyRids { - get { return (options.Flags & MetaDataFlags.PreservePropertyRids) != 0; } - } - - /// - /// Gets the bit - /// - public bool PreserveTypeSpecRids { - get { return (options.Flags & MetaDataFlags.PreserveTypeSpecRids) != 0; } - } - - /// - /// Gets the bit - /// - public bool PreserveMethodSpecRids { - get { return (options.Flags & MetaDataFlags.PreserveMethodSpecRids) != 0; } - } - - /// - /// Gets/sets the bit - /// - public bool PreserveStringsOffsets { - get { return (options.Flags & MetaDataFlags.PreserveStringsOffsets) != 0; } - set { - if (value) - options.Flags |= MetaDataFlags.PreserveStringsOffsets; - else - options.Flags &= ~MetaDataFlags.PreserveStringsOffsets; - } - } - - /// - /// Gets/sets the bit - /// - public bool PreserveUSOffsets { - get { return (options.Flags & MetaDataFlags.PreserveUSOffsets) != 0; } - set { - if (value) - options.Flags |= MetaDataFlags.PreserveUSOffsets; - else - options.Flags &= ~MetaDataFlags.PreserveUSOffsets; - } - } - - /// - /// Gets/sets the bit - /// - public bool PreserveBlobOffsets { - get { return (options.Flags & MetaDataFlags.PreserveBlobOffsets) != 0; } - set { - if (value) - options.Flags |= MetaDataFlags.PreserveBlobOffsets; - else - options.Flags &= ~MetaDataFlags.PreserveBlobOffsets; - } - } - - /// - /// Gets/sets the bit - /// - public bool PreserveExtraSignatureData { - get { return (options.Flags & MetaDataFlags.PreserveExtraSignatureData) != 0; } - set { - if (value) - options.Flags |= MetaDataFlags.PreserveExtraSignatureData; - else - options.Flags &= ~MetaDataFlags.PreserveExtraSignatureData; - } - } - - /// - /// Gets/sets the bit - /// - public bool KeepOldMaxStack { - get { return (options.Flags & MetaDataFlags.KeepOldMaxStack) != 0; } - set { - if (value) - options.Flags |= MetaDataFlags.KeepOldMaxStack; - else - options.Flags &= ~MetaDataFlags.KeepOldMaxStack; - } - } - - /// - /// Gets/sets the bit - /// - public bool AlwaysCreateGuidHeap { - get { return (options.Flags & MetaDataFlags.AlwaysCreateGuidHeap) != 0; } - set { - if (value) - options.Flags |= MetaDataFlags.AlwaysCreateGuidHeap; - else - options.Flags &= ~MetaDataFlags.AlwaysCreateGuidHeap; - } - } - - /// - /// Gets/sets the bit - /// - public bool AlwaysCreateStringsHeap { - get { return (options.Flags & MetaDataFlags.AlwaysCreateStringsHeap) != 0; } - set { - if (value) - options.Flags |= MetaDataFlags.AlwaysCreateStringsHeap; - else - options.Flags &= ~MetaDataFlags.AlwaysCreateStringsHeap; - } - } - - /// - /// Gets/sets the bit - /// - public bool AlwaysCreateUSHeap { - get { return (options.Flags & MetaDataFlags.AlwaysCreateUSHeap) != 0; } - set { - if (value) - options.Flags |= MetaDataFlags.AlwaysCreateUSHeap; - else - options.Flags &= ~MetaDataFlags.AlwaysCreateUSHeap; - } - } - - /// - /// Gets/sets the bit - /// - public bool AlwaysCreateBlobHeap { - get { return (options.Flags & MetaDataFlags.AlwaysCreateBlobHeap) != 0; } - set { - if (value) - options.Flags |= MetaDataFlags.AlwaysCreateBlobHeap; - else - options.Flags &= ~MetaDataFlags.AlwaysCreateBlobHeap; - } - } - - /// - /// If true, use the original Field RVAs. If it has no RVA, assume it's a new - /// field value and create a new Field RVA. - /// - internal bool KeepFieldRVA { get; set; } - - /// - /// Gets the number of methods that will be written. - /// - protected abstract int NumberOfMethods { get; } - - /// - /// Constructor - /// - /// Module - /// Constants list - /// Method bodies list - /// .NET resources list - /// Options - internal MetaData(ModuleDef module, UniqueChunkList constants, MethodBodyChunks methodBodies, NetResources netResources, MetaDataOptions options) { - this.module = module; - this.constants = constants; - this.methodBodies = methodBodies; - this.netResources = netResources; - this.options = options ?? new MetaDataOptions(); - this.metaDataHeader = new MetaDataHeader(this.options.MetaDataHeaderOptions); - this.tablesHeap = new TablesHeap(this.options.TablesHeapOptions); - this.stringsHeap = new StringsHeap(); - this.usHeap = new USHeap(); - this.guidHeap = new GuidHeap(); - this.blobHeap = new BlobHeap(); - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetRid(ModuleDef module) { - uint rid; - moduleDefInfos.TryGetRid(module, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public abstract uint GetRid(TypeRef tr); - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public abstract uint GetRid(TypeDef td); - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public abstract uint GetRid(FieldDef fd); - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public abstract uint GetRid(MethodDef md); - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public abstract uint GetRid(ParamDef pd); - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetRid(InterfaceImpl ii) { - uint rid; - interfaceImplInfos.TryGetRid(ii, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public abstract uint GetRid(MemberRef mr); - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetConstantRid(IHasConstant hc) { - uint rid; - hasConstantInfos.TryGetRid(hc, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetCustomAttributeRid(CustomAttribute ca) { - uint rid; - customAttributeInfos.TryGetRid(ca, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetFieldMarshalRid(IHasFieldMarshal hfm) { - uint rid; - fieldMarshalInfos.TryGetRid(hfm, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetRid(DeclSecurity ds) { - uint rid; - declSecurityInfos.TryGetRid(ds, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetClassLayoutRid(TypeDef td) { - uint rid; - classLayoutInfos.TryGetRid(td, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetFieldLayoutRid(FieldDef fd) { - uint rid; - fieldLayoutInfos.TryGetRid(fd, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public abstract uint GetRid(StandAloneSig sas); - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetEventMapRid(TypeDef td) { - uint rid; - eventMapInfos.TryGetRid(td, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public abstract uint GetRid(EventDef ed); - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetPropertyMapRid(TypeDef td) { - uint rid; - propertyMapInfos.TryGetRid(td, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public abstract uint GetRid(PropertyDef pd); - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetMethodSemanticsRid(MethodDef md) { - uint rid; - methodSemanticsInfos.TryGetRid(md, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetRid(ModuleRef mr) { - uint rid; - moduleRefInfos.TryGetRid(mr, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public abstract uint GetRid(TypeSpec ts); - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetImplMapRid(IMemberForwarded mf) { - uint rid; - implMapInfos.TryGetRid(mf, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetFieldRVARid(FieldDef fd) { - uint rid; - fieldRVAInfos.TryGetRid(fd, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetRid(AssemblyDef asm) { - uint rid; - assemblyInfos.TryGetRid(asm, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetRid(AssemblyRef asmRef) { - uint rid; - assemblyRefInfos.TryGetRid(asmRef, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetRid(FileDef fd) { - uint rid; - fileDefInfos.TryGetRid(fd, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetRid(ExportedType et) { - uint rid; - exportedTypeInfos.TryGetRid(et, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetManifestResourceRid(Resource resource) { - uint rid; - manifestResourceInfos.TryGetRid(resource, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetNestedClassRid(TypeDef td) { - uint rid; - nestedClassInfos.TryGetRid(td, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetRid(GenericParam gp) { - uint rid; - genericParamInfos.TryGetRid(gp, out rid); - return rid; - } - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public abstract uint GetRid(MethodSpec ms); - - /// - /// Gets the new rid - /// - /// Value - /// Its new rid or 0 - public uint GetRid(GenericParamConstraint gpc) { - uint rid; - genericParamConstraintInfos.TryGetRid(gpc, out rid); - return rid; - } - - /// - /// Gets the - /// - /// Method - /// The or null if is - /// null or not a method defined in this module. - public MethodBody GetMethodBody(MethodDef md) { - if (md == null) - return null; - MethodBody mb; - methodToBody.TryGetValue(md, out mb); - return mb; - } - - /// - /// Gets a method's local variable signature token - /// - /// Method - /// Locals sig token or 0 - public uint GetLocalVarSigToken(MethodDef md) { - var mb = GetMethodBody(md); - return mb == null ? 0 : mb.LocalVarSigTok; - } - - /// - /// Gets the where the resource data will be stored - /// - /// Embedded resource - /// A instance or null if - /// is invalid - public ByteArrayChunk GetChunk(EmbeddedResource er) { - if (er == null) - return null; - ByteArrayChunk chunk; - embeddedResourceToByteArray.TryGetValue(er, out chunk); - return chunk; - } - - /// - /// Gets the where the initial value is stored - /// - /// Field - /// A instance or null if - /// is invalid - public ByteArrayChunk GetInitialValueChunk(FieldDef fd) { - if (fd == null) - return null; - ByteArrayChunk chunk; - fieldToInitialValue.TryGetValue(fd, out chunk); - return chunk; - } - - ILogger GetLogger() { - return logger ?? DummyLogger.ThrowModuleWriterExceptionOnErrorInstance; - } - - /// - /// Called when an error is detected - /// - /// Error message - /// Optional message arguments - protected void Error(string message, params object[] args) { - GetLogger().Log(this, LoggerEvent.Error, message, args); - } - - /// - /// Called to warn of something - /// - /// Warning message - /// Optional message arguments - protected void Warning(string message, params object[] args) { - GetLogger().Log(this, LoggerEvent.Warning, message, args); - } - - /// - /// Creates the .NET metadata tables - /// - public void CreateTables() { - Listener.OnMetaDataEvent(this, MetaDataEvent.BeginCreateTables); - - if (module.Types.Count == 0 || module.Types[0] == null) - throw new ModuleWriterException("Missing global type"); - - var moduleDefMD = module as ModuleDefMD; - if (moduleDefMD != null) { - if (PreserveStringsOffsets) - stringsHeap.Populate(moduleDefMD.StringsStream); - if (PreserveUSOffsets) - usHeap.Populate(moduleDefMD.USStream); - if (PreserveBlobOffsets) - blobHeap.Populate(moduleDefMD.BlobStream); - } - - Create(); - } - - /// - /// Updates each Method row's RVA column if it has any code - /// - void UpdateMethodRvas() { - foreach (var kv in methodToBody) { - var method = kv.Key; - var body = kv.Value; - var row = tablesHeap.MethodTable[GetRid(method)]; - row.RVA = (uint)body.RVA; - } - foreach (var kv in methodToNativeBody) { - var method = kv.Key; - var body = kv.Value; - var row = tablesHeap.MethodTable[GetRid(method)]; - row.RVA = (uint)body.RVA; - } - } - - /// - /// Updates the FieldRVA rows - /// - void UpdateFieldRvas() { - foreach (var kv in fieldToInitialValue) { - var field = kv.Key; - var iv = kv.Value; - var row = tablesHeap.FieldRVATable[fieldRVAInfos.Rid(field)]; - row.RVA = (uint)iv.RVA; - } - } - - void Create() { - Initialize(); - allTypeDefs = GetAllTypeDefs(); - Listener.OnMetaDataEvent(this, MetaDataEvent.AllocateTypeDefRids); - AllocateTypeDefRids(); - Listener.OnMetaDataEvent(this, MetaDataEvent.AllocateMemberDefRids); - AllocateMemberDefRids(); - Listener.OnMetaDataEvent(this, MetaDataEvent.MemberDefRidsAllocated); - - AddModule(module); - InitializeTypeDefsAndMemberDefs(); - Listener.OnMetaDataEvent(this, MetaDataEvent.MemberDefsInitialized); - - InitializeVTableFixups(); - - AddExportedTypes(); - InitializeEntryPoint(); - if (module.Assembly != null) - AddAssembly(module.Assembly, AssemblyPublicKey); - - Listener.OnMetaDataEvent(this, MetaDataEvent.BeforeSortTables); - SortTables(); - InitializeGenericParamConstraintTable(); - Listener.OnMetaDataEvent(this, MetaDataEvent.MostTablesSorted); - - WriteTypeDefAndMemberDefCustomAttributes(); - Listener.OnMetaDataEvent(this, MetaDataEvent.MemberDefCustomAttributesWritten); - - Listener.OnMetaDataEvent(this, MetaDataEvent.BeginAddResources); - AddResources(module.Resources); - Listener.OnMetaDataEvent(this, MetaDataEvent.EndAddResources); - - Listener.OnMetaDataEvent(this, MetaDataEvent.BeginWriteMethodBodies); - WriteMethodBodies(); - Listener.OnMetaDataEvent(this, MetaDataEvent.EndWriteMethodBodies); - - BeforeSortingCustomAttributes(); - InitializeCustomAttributeTable(); - Listener.OnMetaDataEvent(this, MetaDataEvent.OnAllTablesSorted); - - EverythingInitialized(); - Listener.OnMetaDataEvent(this, MetaDataEvent.EndCreateTables); - } - - /// - /// Initializes all TypeDef, Field, Method, Event, - /// Property and Param rows. Other tables that are related to these six - /// tables are also updated. No custom attributes are written yet, though. Method bodies - /// aren't written either. - /// - void InitializeTypeDefsAndMemberDefs() { - int numTypes = allTypeDefs.Count; - int typeNum = 0; - int notifyNum = 0; - const int numNotifyEvents = 5; // InitializeTypeDefsAndMemberDefs0 - InitializeTypeDefsAndMemberDefs4 - int notifyAfter = numTypes / numNotifyEvents; - - foreach (var type in allTypeDefs) { - if (typeNum++ == notifyAfter && notifyNum < numNotifyEvents) { - Listener.OnMetaDataEvent(this, MetaDataEvent.InitializeTypeDefsAndMemberDefs0 + notifyNum++); - notifyAfter += numTypes / numNotifyEvents; - } - - if (type == null) { - Error("TypeDef is null"); - continue; - } - uint typeRid = GetRid(type); - var typeRow = tablesHeap.TypeDefTable[typeRid]; - typeRow.Flags = (uint)type.Attributes; - typeRow.Name = stringsHeap.Add(type.Name); - typeRow.Namespace = stringsHeap.Add(type.Namespace); - typeRow.Extends = type.BaseType == null ? 0 : AddTypeDefOrRef(type.BaseType); - AddGenericParams(new MDToken(Table.TypeDef, typeRid), type.GenericParameters); - AddDeclSecurities(new MDToken(Table.TypeDef, typeRid), type.DeclSecurities); - AddInterfaceImpls(typeRid, type.Interfaces); - AddClassLayout(type); - AddNestedType(type, type.DeclaringType); - - foreach (var field in type.Fields) { - if (field == null) { - Error("Field is null. TypeDef {0} ({1:X8})", type, type.MDToken.Raw); - continue; - } - uint rid = GetRid(field); - var row = tablesHeap.FieldTable[rid]; - row.Flags = (ushort)field.Attributes; - row.Name = stringsHeap.Add(field.Name); - row.Signature = GetSignature(field.Signature); - AddFieldLayout(field); - AddFieldMarshal(new MDToken(Table.Field, rid), field); - AddFieldRVA(field); - AddImplMap(new MDToken(Table.Field, rid), field); - AddConstant(new MDToken(Table.Field, rid), field); - } - - foreach (var method in type.Methods) { - if (method == null) { - Error("Method is null. TypeDef {0} ({1:X8})", type, type.MDToken.Raw); - continue; - } - uint rid = GetRid(method); - var row = tablesHeap.MethodTable[rid]; - row.ImplFlags = (ushort)method.ImplAttributes; - row.Flags = (ushort)method.Attributes; - row.Name = stringsHeap.Add(method.Name); - row.Signature = GetSignature(method.Signature); - AddGenericParams(new MDToken(Table.Method, rid), method.GenericParameters); - AddDeclSecurities(new MDToken(Table.Method, rid), method.DeclSecurities); - AddImplMap(new MDToken(Table.Method, rid), method); - AddMethodImpls(method, method.Overrides); - foreach (var pd in method.ParamDefs) { - if (pd == null) { - Error("Param is null. Method {0} ({1:X8})", method, method.MDToken.Raw); - continue; - } - uint pdRid = GetRid(pd); - var pdRow = tablesHeap.ParamTable[pdRid]; - pdRow.Flags = (ushort)pd.Attributes; - pdRow.Sequence = pd.Sequence; - pdRow.Name = stringsHeap.Add(pd.Name); - AddConstant(new MDToken(Table.Param, pdRid), pd); - AddFieldMarshal(new MDToken(Table.Param, pdRid), pd); - } - } - - if (!IsEmpty(type.Events)) { - foreach (var evt in type.Events) { - if (evt == null) { - Error("Event is null. TypeDef {0} ({1:X8})", type, type.MDToken.Raw); - continue; - } - uint rid = GetRid(evt); - var row = tablesHeap.EventTable[rid]; - row.EventFlags = (ushort)evt.Attributes; - row.Name = stringsHeap.Add(evt.Name); - row.EventType = AddTypeDefOrRef(evt.EventType); - AddMethodSemantics(evt); - } - } - - if (!IsEmpty(type.Properties)) { - foreach (var prop in type.Properties) { - if (prop == null) { - Error("Property is null. TypeDef {0} ({1:X8})", type, type.MDToken.Raw); - continue; - } - uint rid = GetRid(prop); - var row = tablesHeap.PropertyTable[rid]; - row.PropFlags = (ushort)prop.Attributes; - row.Name = stringsHeap.Add(prop.Name); - row.Type = GetSignature(prop.Type); - AddConstant(new MDToken(Table.Property, rid), prop); - AddMethodSemantics(prop); - } - } - } - while (notifyNum < numNotifyEvents) - Listener.OnMetaDataEvent(this, MetaDataEvent.InitializeTypeDefsAndMemberDefs0 + notifyNum++); - } - - /// - /// Writes TypeDef, Field, Method, Event, - /// Property and Param custom attributes. - /// - void WriteTypeDefAndMemberDefCustomAttributes() { - int numTypes = allTypeDefs.Count; - int typeNum = 0; - int notifyNum = 0; - const int numNotifyEvents = 5; // WriteTypeDefAndMemberDefCustomAttributes0 - WriteTypeDefAndMemberDefCustomAttributes4 - int notifyAfter = numTypes / numNotifyEvents; - - foreach (var type in allTypeDefs) { - if (typeNum++ == notifyAfter && notifyNum < numNotifyEvents) { - Listener.OnMetaDataEvent(this, MetaDataEvent.WriteTypeDefAndMemberDefCustomAttributes0 + notifyNum++); - notifyAfter += numTypes / numNotifyEvents; - } - - if (type == null) - continue; - AddCustomAttributes(Table.TypeDef, GetRid(type), type); - - foreach (var field in type.Fields) { - if (field == null) - continue; - AddCustomAttributes(Table.Field, GetRid(field), field); - } - - foreach (var method in type.Methods) { - if (method == null) - continue; - AddCustomAttributes(Table.Method, GetRid(method), method); - foreach (var pd in method.ParamDefs) { - if (pd == null) - continue; - AddCustomAttributes(Table.Param, GetRid(pd), pd); - } - } - foreach (var evt in type.Events) { - if (evt == null) - continue; - AddCustomAttributes(Table.Event, GetRid(evt), evt); - } - foreach (var prop in type.Properties) { - if (prop == null) - continue; - AddCustomAttributes(Table.Property, GetRid(prop), prop); - } - } - while (notifyNum < numNotifyEvents) - Listener.OnMetaDataEvent(this, MetaDataEvent.WriteTypeDefAndMemberDefCustomAttributes0 + notifyNum++); - } - - /// - /// Adds the tokens of all methods in all vtables, if any - /// - void InitializeVTableFixups() { - var fixups = module.VTableFixups; - if (fixups == null || fixups.VTables.Count == 0) - return; - - foreach (var vtable in fixups) { - if (vtable == null) { - Error("VTable is null"); - continue; - } - foreach (var method in vtable) { - if (method == null) { - Error("VTable method is null"); - continue; - } - AddMDTokenProvider(method); - } - } - } - - void AddExportedTypes() { - foreach (var et in module.ExportedTypes) - AddExportedType(et); - } - - /// - /// Adds the entry point. It's only needed if it's a since if it's - /// a , it will have already been added. - /// - void InitializeEntryPoint() { - var epFile = module.ManagedEntryPoint as FileDef; - if (epFile != null) - AddFile(epFile); - } - - /// - /// Sorts all unsorted tables except GenericParamConstraint and CustomAttribute - /// - void SortTables() { - classLayoutInfos.Sort((a, b) => a.row.Parent.CompareTo(b.row.Parent)); - hasConstantInfos.Sort((a, b) => a.row.Parent.CompareTo(b.row.Parent)); - declSecurityInfos.Sort((a, b) => a.row.Parent.CompareTo(b.row.Parent)); - fieldLayoutInfos.Sort((a, b) => a.row.Field.CompareTo(b.row.Field)); - fieldMarshalInfos.Sort((a, b) => a.row.Parent.CompareTo(b.row.Parent)); - fieldRVAInfos.Sort((a, b) => a.row.Field.CompareTo(b.row.Field)); - implMapInfos.Sort((a, b) => a.row.MemberForwarded.CompareTo(b.row.MemberForwarded)); - methodImplInfos.Sort((a, b) => a.row.Class.CompareTo(b.row.Class)); - methodSemanticsInfos.Sort((a, b)=> a.row.Association.CompareTo(b.row.Association)); - nestedClassInfos.Sort((a, b) => a.row.NestedClass.CompareTo(b.row.NestedClass)); - genericParamInfos.Sort((a, b) => { - if (a.row.Owner != b.row.Owner) - return a.row.Owner.CompareTo(b.row.Owner); - return a.row.Number.CompareTo(b.row.Number); - }); - interfaceImplInfos.Sort((a, b) => { - if (a.row.Class != b.row.Class) - return a.row.Class.CompareTo(b.row.Class); - return a.row.Interface.CompareTo(b.row.Interface); - }); - - tablesHeap.ClassLayoutTable.IsSorted = true; - tablesHeap.ConstantTable.IsSorted = true; - tablesHeap.DeclSecurityTable.IsSorted = true; - tablesHeap.FieldLayoutTable.IsSorted = true; - tablesHeap.FieldMarshalTable.IsSorted = true; - tablesHeap.FieldRVATable.IsSorted = true; - tablesHeap.GenericParamTable.IsSorted = true; - tablesHeap.ImplMapTable.IsSorted = true; - tablesHeap.InterfaceImplTable.IsSorted = true; - tablesHeap.MethodImplTable.IsSorted = true; - tablesHeap.MethodSemanticsTable.IsSorted = true; - tablesHeap.NestedClassTable.IsSorted = true; - - // These two are also sorted - tablesHeap.EventMapTable.IsSorted = true; - tablesHeap.PropertyMapTable.IsSorted = true; - - foreach (var info in classLayoutInfos.infos) tablesHeap.ClassLayoutTable.Create(info.row); - foreach (var info in hasConstantInfos.infos) tablesHeap.ConstantTable.Create(info.row); - foreach (var info in declSecurityInfos.infos) tablesHeap.DeclSecurityTable.Create(info.row); - foreach (var info in fieldLayoutInfos.infos) tablesHeap.FieldLayoutTable.Create(info.row); - foreach (var info in fieldMarshalInfos.infos) tablesHeap.FieldMarshalTable.Create(info.row); - foreach (var info in fieldRVAInfos.infos) tablesHeap.FieldRVATable.Create(info.row); - foreach (var info in genericParamInfos.infos) tablesHeap.GenericParamTable.Create(info.row); - foreach (var info in implMapInfos.infos) tablesHeap.ImplMapTable.Create(info.row); - foreach (var info in interfaceImplInfos.infos) tablesHeap.InterfaceImplTable.Create(info.row); - foreach (var info in methodImplInfos.infos) tablesHeap.MethodImplTable.Create(info.row); - foreach (var info in methodSemanticsInfos.infos) tablesHeap.MethodSemanticsTable.Create(info.row); - foreach (var info in nestedClassInfos.infos) tablesHeap.NestedClassTable.Create(info.row); - - foreach (var info in interfaceImplInfos.infos) - AddCustomAttributes(Table.InterfaceImpl, interfaceImplInfos.Rid(info.data), info.data); - foreach (var info in declSecurityInfos.infos) - AddCustomAttributes(Table.DeclSecurity, declSecurityInfos.Rid(info.data), info.data); - foreach (var info in genericParamInfos.infos) - AddCustomAttributes(Table.GenericParam, genericParamInfos.Rid(info.data), info.data); - } - - /// - /// Initializes the GenericParamConstraint table - /// - void InitializeGenericParamConstraintTable() { - foreach (var type in allTypeDefs) { - if (type == null) - continue; - AddGenericParamConstraints(type.GenericParameters); - foreach (var method in type.Methods) { - if (method == null) - continue; - AddGenericParamConstraints(method.GenericParameters); - } - } - genericParamConstraintInfos.Sort((a, b) => a.row.Owner.CompareTo(b.row.Owner)); - tablesHeap.GenericParamConstraintTable.IsSorted = true; - foreach (var info in genericParamConstraintInfos.infos) - tablesHeap.GenericParamConstraintTable.Create(info.row); - foreach (var info in genericParamConstraintInfos.infos) - AddCustomAttributes(Table.GenericParamConstraint, genericParamConstraintInfos.Rid(info.data), info.data); - } - - /// - /// Inserts all custom attribute rows in the table and sorts it - /// - void InitializeCustomAttributeTable() { - customAttributeInfos.Sort((a, b) => a.row.Parent.CompareTo(b.row.Parent)); - tablesHeap.CustomAttributeTable.IsSorted = true; - foreach (var info in customAttributeInfos.infos) - tablesHeap.CustomAttributeTable.Create(info.row); - } - - /// - /// Writes all method bodies - /// - void WriteMethodBodies() { - int numMethods = NumberOfMethods; - int methodNum = 0; - int notifyNum = 0; - const int numNotifyEvents = 10; // WriteMethodBodies0 - WriteMethodBodies9 - int notifyAfter = numMethods / numNotifyEvents; - - bool keepMaxStack = KeepOldMaxStack; - foreach (var type in allTypeDefs) { - if (type == null) - continue; - - foreach (var method in type.Methods) { - if (method == null) - continue; - - if (methodNum++ == notifyAfter && notifyNum < numNotifyEvents) { - Listener.OnMetaDataEvent(this, MetaDataEvent.WriteMethodBodies0 + notifyNum++); - notifyAfter += numMethods / numNotifyEvents; - } - - if (method.MethodBody == null) - continue; - - var cilBody = method.Body; - if (cilBody != null) { - if (cilBody.Instructions.Count == 0 && cilBody.Variables.Count == 0) - continue; - var writer = new MethodBodyWriter(this, cilBody, keepMaxStack || cilBody.KeepOldMaxStack); - writer.Write(); - var mb = methodBodies.Add(new MethodBody(writer.Code, writer.ExtraSections, writer.LocalVarSigTok)); - methodToBody[method] = mb; - continue; - } - - var nativeBody = method.NativeBody; - if (nativeBody != null) { - methodToNativeBody[method] = nativeBody; - continue; - } - - Error("Unsupported method body"); - } - } - while (notifyNum < numNotifyEvents) - Listener.OnMetaDataEvent(this, MetaDataEvent.WriteMethodBodies0 + notifyNum++); - } - - /// - /// Checks whether a list is empty or whether it contains only nulls - /// - /// Any type - /// The list - /// true if the list is empty or if it contains only nulls, false otherwise - protected static bool IsEmpty(IList list) where T : class { - if (list == null) - return true; - foreach (var e in list) { - if (e != null) - return false; - } - return true; - } - - /// - public MDToken GetToken(object o) { - var tp = o as IMDTokenProvider; - if (tp != null) - return new MDToken(tp.MDToken.Table, AddMDTokenProvider(tp)); - - var s = o as string; - if (s != null) - return new MDToken((Table)0x70, usHeap.Add(s)); - - var methodSig = o as MethodSig; - if (methodSig != null) - return new MDToken(Table.StandAloneSig, AddStandAloneSig(methodSig, methodSig.OriginalToken)); - - if (o == null) - Error("Instruction operand is null"); - else - Error("Invalid instruction operand"); - return new MDToken((Table)0xFF, 0x00FFFFFF); - } - - /// - public virtual MDToken GetToken(IList locals, uint origToken) { - if (locals == null || locals.Count == 0) - return new MDToken((Table)0, 0); - - var row = new RawStandAloneSigRow(GetSignature(new LocalSig(locals, false))); - uint rid = tablesHeap.StandAloneSigTable.Add(row); - //TODO: Add custom attributes - return new MDToken(Table.StandAloneSig, rid); - } - - /// - /// Adds a - /// - /// Method signature - /// Original StandAloneSig token or 0 if none - /// Its new rid - protected virtual uint AddStandAloneSig(MethodSig methodSig, uint origToken) { - if (methodSig == null) { - Error("StandAloneSig: MethodSig is null"); - return 0; - } - - var row = new RawStandAloneSigRow(GetSignature(methodSig)); - uint rid = tablesHeap.StandAloneSigTable.Add(row); - //TODO: Add custom attributes - return rid; - } - - uint AddMDTokenProvider(IMDTokenProvider tp) { - if (tp != null) { - switch (tp.MDToken.Table) { - case Table.Module: - return AddModule((ModuleDef)tp); - - case Table.TypeRef: - return AddTypeRef((TypeRef)tp); - - case Table.TypeDef: - return GetRid((TypeDef)tp); - - case Table.Field: - return GetRid((FieldDef)tp); - - case Table.Method: - return GetRid((MethodDef)tp); - - case Table.Param: - return GetRid((ParamDef)tp); - - case Table.MemberRef: - return AddMemberRef((MemberRef)tp); - - case Table.StandAloneSig: - return AddStandAloneSig((StandAloneSig)tp); - - case Table.Event: - return GetRid((EventDef)tp); - - case Table.Property: - return GetRid((PropertyDef)tp); - - case Table.ModuleRef: - return AddModuleRef((ModuleRef)tp); - - case Table.TypeSpec: - return AddTypeSpec((TypeSpec)tp); - - case Table.Assembly: - return AddAssembly((AssemblyDef)tp, null); - - case Table.AssemblyRef: - return AddAssemblyRef((AssemblyRef)tp); - - case Table.File: - return AddFile((FileDef)tp); - - case Table.ExportedType: - return AddExportedType((ExportedType)tp); - - case Table.MethodSpec: - return AddMethodSpec((MethodSpec)tp); - - case Table.FieldPtr: - case Table.MethodPtr: - case Table.ParamPtr: - case Table.InterfaceImpl: - case Table.Constant: - case Table.CustomAttribute: - case Table.FieldMarshal: - case Table.DeclSecurity: - case Table.ClassLayout: - case Table.FieldLayout: - case Table.EventMap: - case Table.EventPtr: - case Table.PropertyMap: - case Table.PropertyPtr: - case Table.MethodSemantics: - case Table.MethodImpl: - case Table.ImplMap: - case Table.FieldRVA: - case Table.ENCLog: - case Table.ENCMap: - case Table.AssemblyProcessor: - case Table.AssemblyOS: - case Table.AssemblyRefProcessor: - case Table.AssemblyRefOS: - case Table.ManifestResource: - case Table.NestedClass: - case Table.GenericParam: - case Table.GenericParamConstraint: - default: - break; - } - } - - if (tp == null) - Error("IMDTokenProvider is null"); - else - Error("Invalid IMDTokenProvider"); - return 0; - } - - /// - /// Adds a - /// - /// Value - /// Its encoded token - protected uint AddTypeDefOrRef(ITypeDefOrRef tdr) { - if (tdr == null) { - Error("TypeDefOrRef is null"); - return 0; - } - - var token = new MDToken(tdr.MDToken.Table, AddMDTokenProvider(tdr)); - uint encodedToken; - if (!CodedToken.TypeDefOrRef.Encode(token, out encodedToken)) { - Error("Can't encode TypeDefOrRef token {0:X8}", token.Raw); - encodedToken = 0; - } - return encodedToken; - } - - /// - /// Adds a - /// - /// Value - /// Its encoded token - protected uint AddResolutionScope(IResolutionScope rs) { - if (rs == null) { - Error("ResolutionScope is null"); - return 0; - } - - var token = new MDToken(rs.MDToken.Table, AddMDTokenProvider(rs)); - uint encodedToken; - if (!CodedToken.ResolutionScope.Encode(token, out encodedToken)) { - Error("Can't encode ResolutionScope token {0:X8}", token.Raw); - encodedToken = 0; - } - return encodedToken; - } - - /// - /// Adds a - /// - /// Value - /// Its encoded token - protected uint AddMethodDefOrRef(IMethodDefOrRef mdr) { - if (mdr == null) { - Error("MethodDefOrRef is null"); - return 0; - } - - var token = new MDToken(mdr.MDToken.Table, AddMDTokenProvider(mdr)); - uint encodedToken; - if (!CodedToken.MethodDefOrRef.Encode(token, out encodedToken)) { - Error("Can't encode MethodDefOrRef token {0:X8}", token.Raw); - encodedToken = 0; - } - return encodedToken; - } - - /// - /// Adds a - /// - /// Value - /// Its encoded token - protected uint AddMemberRefParent(IMemberRefParent parent) { - if (parent == null) { - Error("MemberRefParent is null"); - return 0; - } - - var token = new MDToken(parent.MDToken.Table, AddMDTokenProvider(parent)); - uint encodedToken; - if (!CodedToken.MemberRefParent.Encode(token, out encodedToken)) { - Error("Can't encode MemberRefParent token {0:X8}", token.Raw); - encodedToken = 0; - } - return encodedToken; - } - - /// - /// Adds a - /// - /// Value - /// Its encoded token - protected uint AddImplementation(IImplementation impl) { - if (impl == null) { - Error("Implementation is null"); - return 0; - } - - var token = new MDToken(impl.MDToken.Table, AddMDTokenProvider(impl)); - uint encodedToken; - if (!CodedToken.Implementation.Encode(token, out encodedToken)) { - Error("Can't encode Implementation token {0:X8}", token.Raw); - encodedToken = 0; - } - return encodedToken; - } - - /// - /// Adds a - /// - /// Value - /// Its encoded token - protected uint AddCustomAttributeType(ICustomAttributeType cat) { - if (cat == null) { - Error("CustomAttributeType is null"); - return 0; - } - - var token = new MDToken(cat.MDToken.Table, AddMDTokenProvider(cat)); - uint encodedToken; - if (!CodedToken.CustomAttributeType.Encode(token, out encodedToken)) { - Error("Can't encode CustomAttributeType token {0:X8}", token.Raw); - encodedToken = 0; - } - return encodedToken; - } - - /// - /// Adds a NestedType row - /// - /// Nested type - /// Declaring type - protected void AddNestedType(TypeDef nestedType, TypeDef declaringType) { - if (nestedType == null || declaringType == null) - return; - uint nestedRid = GetRid(nestedType); - uint dtRid = GetRid(declaringType); - if (nestedRid == 0 || dtRid == 0) - return; - var row = new RawNestedClassRow(nestedRid, dtRid); - nestedClassInfos.Add(declaringType, row); - } - - /// - /// Adds a Module row - /// - /// Module - /// Its new rid - protected uint AddModule(ModuleDef module) { - if (module == null) { - Error("Module is null"); - return 0; - } - if (this.module != module) - Error("Module {0} must be referenced with a ModuleRef, not a ModuleDef", module); - uint rid; - if (moduleDefInfos.TryGetRid(module, out rid)) - return rid; - var row = new RawModuleRow(module.Generation, - stringsHeap.Add(module.Name), - guidHeap.Add(module.Mvid), - guidHeap.Add(module.EncId), - guidHeap.Add(module.EncBaseId)); - rid = tablesHeap.ModuleTable.Add(row); - moduleDefInfos.Add(module, rid); - AddCustomAttributes(Table.Module, rid, module); - return rid; - } - - /// - /// Adds a ModuleRef row - /// - /// Module ref - /// Its new rid - protected uint AddModuleRef(ModuleRef modRef) { - if (modRef == null) { - Error("ModuleRef is null"); - return 0; - } - uint rid; - if (moduleRefInfos.TryGetRid(modRef, out rid)) - return rid; - var row = new RawModuleRefRow(stringsHeap.Add(modRef.Name)); - rid = tablesHeap.ModuleRefTable.Add(row); - moduleRefInfos.Add(modRef, rid); - AddCustomAttributes(Table.ModuleRef, rid, modRef); - return rid; - } - - /// - /// Adds an AssemblyRef row - /// - /// Assembly ref - /// Its new rid - protected uint AddAssemblyRef(AssemblyRef asmRef) { - if (asmRef == null) { - Error("AssemblyRef is null"); - return 0; - } - uint rid; - if (assemblyRefInfos.TryGetRid(asmRef, out rid)) - return rid; - var version = Utils.CreateVersionWithNoUndefinedValues(asmRef.Version); - var row = new RawAssemblyRefRow((ushort)version.Major, - (ushort)version.Minor, - (ushort)version.Build, - (ushort)version.Revision, - (uint)asmRef.Attributes, - blobHeap.Add(PublicKeyBase.GetRawData(asmRef.PublicKeyOrToken)), - stringsHeap.Add(asmRef.Name), - stringsHeap.Add(asmRef.Culture), - blobHeap.Add(asmRef.Hash)); - rid = tablesHeap.AssemblyRefTable.Add(row); - assemblyRefInfos.Add(asmRef, rid); - AddCustomAttributes(Table.AssemblyRef, rid, asmRef); - return rid; - } - - /// - /// Adds an Assembly row - /// - /// Assembly - /// The public key that should be used - /// Its new rid - protected uint AddAssembly(AssemblyDef asm, byte[] publicKey) { - if (asm == null) { - Error("Assembly is null"); - return 0; - } - uint rid; - if (assemblyInfos.TryGetRid(asm, out rid)) - return rid; - - var asmAttrs = asm.Attributes; - if (publicKey != null) - asmAttrs |= AssemblyAttributes.PublicKey; - else - publicKey = PublicKeyBase.GetRawData(asm.PublicKeyOrToken); - - var version = Utils.CreateVersionWithNoUndefinedValues(asm.Version); - var row = new RawAssemblyRow((uint)asm.HashAlgorithm, - (ushort)version.Major, - (ushort)version.Minor, - (ushort)version.Build, - (ushort)version.Revision, - (uint)asmAttrs, - blobHeap.Add(publicKey), - stringsHeap.Add(asm.Name), - stringsHeap.Add(asm.Culture)); - rid = tablesHeap.AssemblyTable.Add(row); - assemblyInfos.Add(asm, rid); - AddDeclSecurities(new MDToken(Table.Assembly, rid), asm.DeclSecurities); - AddCustomAttributes(Table.Assembly, rid, asm); - return rid; - } - - /// - /// Adds generic parameters - /// - /// New token of owner - /// All generic params - protected void AddGenericParams(MDToken token, IList gps) { - if (gps == null) - return; - foreach (var gp in gps) - AddGenericParam(token, gp); - } - - /// - /// Adds a generic param - /// - /// New token of owner - /// Generic paramater - protected void AddGenericParam(MDToken owner, GenericParam gp) { - if (gp == null) { - Error("GenericParam is null"); - return; - } - uint encodedOwner; - if (!CodedToken.TypeOrMethodDef.Encode(owner, out encodedOwner)) { - Error("Can't encode TypeOrMethodDef token {0:X8}", owner.Raw); - encodedOwner = 0; - } - var row = new RawGenericParamRow(gp.Number, - (ushort)gp.Flags, - encodedOwner, - stringsHeap.Add(gp.Name), - gp.Kind == null ? 0 : AddTypeDefOrRef(gp.Kind)); - genericParamInfos.Add(gp, row); - } - - void AddGenericParamConstraints(IList gps) { - if (gps == null) - return; - foreach (var gp in gps) { - if (gp == null) - continue; - uint rid = genericParamInfos.Rid(gp); - AddGenericParamConstraints(rid, gp.GenericParamConstraints); - } - } - - /// - /// Adds generic parameter constraints - /// - /// New rid of owner generic param - /// Its constraints - protected void AddGenericParamConstraints(uint gpRid, IList constraints) { - if (constraints == null) - return; - foreach (var gpc in constraints) - AddGenericParamConstraint(gpRid, gpc); - } - - /// - /// Adds a generic parameter constraint - /// - /// New rid of owner generic param - /// Generic paramter constraint - protected void AddGenericParamConstraint(uint gpRid, GenericParamConstraint gpc) { - if (gpc == null) { - Error("GenericParamConstraint is null"); - return; - } - var row = new RawGenericParamConstraintRow(gpRid, AddTypeDefOrRef(gpc.Constraint)); - genericParamConstraintInfos.Add(gpc, row); - } - - /// - /// Adds a InterfaceImpl row - /// - /// New rid of owner - /// All interfaces - protected void AddInterfaceImpls(uint typeDefRid, IList ifaces) { - foreach (var iface in ifaces) { - if (iface == null) - continue; - var row = new RawInterfaceImplRow(typeDefRid, - AddTypeDefOrRef(iface.Interface)); - interfaceImplInfos.Add(iface, row); - } - } - - /// - /// Adds a FieldLayout row - /// - /// Owner field - protected void AddFieldLayout(FieldDef field) { - if (field == null || field.FieldOffset == null) - return; - var rid = GetRid(field); - var row = new RawFieldLayoutRow(field.FieldOffset.Value, rid); - fieldLayoutInfos.Add(field, row); - } - - /// - /// Adds a FieldMarshal row - /// - /// New owner token - /// Owner - protected void AddFieldMarshal(MDToken parent, IHasFieldMarshal hfm) { - if (hfm == null || hfm.MarshalType == null) - return; - var fieldMarshal = hfm.MarshalType; - uint encodedParent; - if (!CodedToken.HasFieldMarshal.Encode(parent, out encodedParent)) { - Error("Can't encode HasFieldMarshal token {0:X8}", parent.Raw); - encodedParent = 0; - } - var row = new RawFieldMarshalRow(encodedParent, - blobHeap.Add(MarshalBlobWriter.Write(module, fieldMarshal, this))); - fieldMarshalInfos.Add(hfm, row); - } - - /// - /// Adds a FieldRVA row - /// - /// The field - protected void AddFieldRVA(FieldDef field) { - if (field.RVA != 0 && KeepFieldRVA) { - uint rid = GetRid(field); - var row = new RawFieldRVARow((uint)field.RVA, rid); - fieldRVAInfos.Add(field, row); - } - else { - if (field == null || field.InitialValue == null) - return; - var ivBytes = field.InitialValue; - if (!VerifyFieldSize(field, ivBytes.Length)) - Error("Field {0} ({1:X8}) initial value size != size of field type", field, field.MDToken.Raw); - uint rid = GetRid(field); - var iv = constants.Add(new ByteArrayChunk(ivBytes), ModuleWriterBase.DEFAULT_CONSTANTS_ALIGNMENT); - fieldToInitialValue[field] = iv; - var row = new RawFieldRVARow(0, rid); - fieldRVAInfos.Add(field, row); - } - } - - static bool VerifyFieldSize(FieldDef field, int size) { - if (field == null) - return false; - var sig = field.FieldSig; - if (sig == null) - return false; - return field.GetFieldSize() == size; - } - - /// - /// Adds a ImplMap row - /// - /// New owner token - /// Owner - protected void AddImplMap(MDToken parent, IMemberForwarded mf) { - if (mf == null || mf.ImplMap == null) - return; - var implMap = mf.ImplMap; - uint encodedParent; - if (!CodedToken.MemberForwarded.Encode(parent, out encodedParent)) { - Error("Can't encode MemberForwarded token {0:X8}", parent.Raw); - encodedParent = 0; - } - var row = new RawImplMapRow((ushort)implMap.Attributes, - encodedParent, - stringsHeap.Add(implMap.Name), - AddModuleRef(implMap.Module)); - implMapInfos.Add(mf, row); - } - - /// - /// Adds a Constant row - /// - /// New owner token - /// Owner - protected void AddConstant(MDToken parent, IHasConstant hc) { - if (hc == null || hc.Constant == null) - return; - var constant = hc.Constant; - uint encodedParent; - if (!CodedToken.HasConstant.Encode(parent, out encodedParent)) { - Error("Can't encode HasConstant token {0:X8}", parent.Raw); - encodedParent = 0; - } - var row = new RawConstantRow((byte)constant.Type, 0, - encodedParent, - blobHeap.Add(GetConstantValueAsByteArray(constant.Type, constant.Value))); - hasConstantInfos.Add(hc, row); - } - - static readonly byte[] constantClassByteArray = new byte[4]; - static readonly byte[] constantDefaultByteArray = new byte[8]; - byte[] GetConstantValueAsByteArray(ElementType etype, object o) { - if (o == null) { - if (etype == ElementType.Class) - return constantClassByteArray; - Error("Constant is null"); - return constantDefaultByteArray; - } - - var typeCode = Type.GetTypeCode(o.GetType()); - switch (typeCode) { - case TypeCode.Boolean: - VerifyConstantType(etype, ElementType.Boolean); - return BitConverter.GetBytes((bool)o); - - case TypeCode.Char: - VerifyConstantType(etype, ElementType.Char); - return BitConverter.GetBytes((char)o); - - case TypeCode.SByte: - VerifyConstantType(etype, ElementType.I1); - return new byte[1] { (byte)(sbyte)o }; - - case TypeCode.Byte: - VerifyConstantType(etype, ElementType.U1); - return new byte[1] { (byte)o }; - - case TypeCode.Int16: - VerifyConstantType(etype, ElementType.I2); - return BitConverter.GetBytes((short)o); - - case TypeCode.UInt16: - VerifyConstantType(etype, ElementType.U2); - return BitConverter.GetBytes((ushort)o); - - case TypeCode.Int32: - VerifyConstantType(etype, ElementType.I4); - return BitConverter.GetBytes((int)o); - - case TypeCode.UInt32: - VerifyConstantType(etype, ElementType.U4); - return BitConverter.GetBytes((uint)o); - - case TypeCode.Int64: - VerifyConstantType(etype, ElementType.I8); - return BitConverter.GetBytes((long)o); - - case TypeCode.UInt64: - VerifyConstantType(etype, ElementType.U8); - return BitConverter.GetBytes((ulong)o); - - case TypeCode.Single: - VerifyConstantType(etype, ElementType.R4); - return BitConverter.GetBytes((float)o); - - case TypeCode.Double: - VerifyConstantType(etype, ElementType.R8); - return BitConverter.GetBytes((double)o); - - case TypeCode.String: - VerifyConstantType(etype, ElementType.String); - return Encoding.Unicode.GetBytes((string)o); - - default: - Error("Invalid constant type: {0}", typeCode); - return constantDefaultByteArray; - } - } - - void VerifyConstantType(ElementType realType, ElementType expectedType) { - if (realType != expectedType) - Error("Constant value's type is the wrong type: {0} != {1}", realType, expectedType); - } - - /// - /// Adds a DeclSecurity row - /// - /// New owner token - /// All DeclSecurity rows - protected void AddDeclSecurities(MDToken parent, IList declSecurities) { - if (declSecurities == null) - return; - uint encodedParent; - if (!CodedToken.HasDeclSecurity.Encode(parent, out encodedParent)) { - Error("Can't encode HasDeclSecurity token {0:X8}", parent.Raw); - encodedParent = 0; - } - foreach (var decl in declSecurities) { - if (decl == null) - continue; - var row = new RawDeclSecurityRow((short)decl.Action, - encodedParent, - blobHeap.Add(DeclSecurityWriter.Write(module, decl.SecurityAttributes, this))); - declSecurityInfos.Add(decl, row); - } - } - - /// - /// Adds MethodSemantics rows - /// - /// Event - protected void AddMethodSemantics(EventDef evt) { - if (evt == null) { - Error("Event is null"); - return; - } - uint rid = GetRid(evt); - if (rid == 0) - return; - var token = new MDToken(Table.Event, rid); - AddMethodSemantics(token, evt.AddMethod, MethodSemanticsAttributes.AddOn); - AddMethodSemantics(token, evt.RemoveMethod, MethodSemanticsAttributes.RemoveOn); - AddMethodSemantics(token, evt.InvokeMethod, MethodSemanticsAttributes.Fire); - AddMethodSemantics(token, evt.OtherMethods, MethodSemanticsAttributes.Other); - } - - /// - /// Adds MethodSemantics rows - /// - /// Property - protected void AddMethodSemantics(PropertyDef prop) { - if (prop == null) { - Error("Property is null"); - return; - } - uint rid = GetRid(prop); - if (rid == 0) - return; - var token = new MDToken(Table.Property, rid); - AddMethodSemantics(token, prop.GetMethods, MethodSemanticsAttributes.Getter); - AddMethodSemantics(token, prop.SetMethods, MethodSemanticsAttributes.Setter); - AddMethodSemantics(token, prop.OtherMethods, MethodSemanticsAttributes.Other); - } - - void AddMethodSemantics(MDToken owner, IList methods, MethodSemanticsAttributes attrs) { - if (methods == null) - return; - foreach (var method in methods) - AddMethodSemantics(owner, method, attrs); - } - - void AddMethodSemantics(MDToken owner, MethodDef method, MethodSemanticsAttributes flags) { - if (method == null) - return; - uint methodRid = GetRid(method); - if (methodRid == 0) - return; - uint encodedOwner; - if (!CodedToken.HasSemantic.Encode(owner, out encodedOwner)) { - Error("Can't encode HasSemantic token {0:X8}", owner.Raw); - encodedOwner = 0; - } - var row = new RawMethodSemanticsRow((ushort)flags, methodRid, encodedOwner); - methodSemanticsInfos.Add(method, row); - } - - void AddMethodImpls(MethodDef method, IList overrides) { - if (overrides == null) - return; - if (method.DeclaringType == null) { - Error("Method declaring type == null. Method {0} ({1:X8})", method, method.MDToken.Raw); - return; - } - uint rid = GetRid(method.DeclaringType); - foreach (var ovr in overrides) { - var row = new RawMethodImplRow(rid, - AddMethodDefOrRef(ovr.MethodBody), - AddMethodDefOrRef(ovr.MethodDeclaration)); - methodImplInfos.Add(method, row); - } - } - - /// - /// Adds a ClassLayout row - /// - /// Type - protected void AddClassLayout(TypeDef type) { - if (type == null || type.ClassLayout == null) - return; - var rid = GetRid(type); - var classLayout = type.ClassLayout; - var row = new RawClassLayoutRow(classLayout.PackingSize, classLayout.ClassSize, rid); - classLayoutInfos.Add(type, row); - } - - void AddResources(IList resources) { - if (resources == null) - return; - foreach (var resource in resources) - AddResource(resource); - } - - void AddResource(Resource resource) { - var er = resource as EmbeddedResource; - if (er != null) { - AddEmbeddedResource(er); - return; - } - - var alr = resource as AssemblyLinkedResource; - if (alr != null) { - AddAssemblyLinkedResource(alr); - return; - } - - var lr = resource as LinkedResource; - if (lr != null) { - AddLinkedResource(lr); - return; - } - - if (resource == null) - Error("Resource is null"); - else - Error("Invalid resource type: {0}", resource.GetType()); - } - - uint AddEmbeddedResource(EmbeddedResource er) { - if (er == null) { - Error("EmbeddedResource is null"); - return 0; - } - uint rid; - if (manifestResourceInfos.TryGetRid(er, out rid)) - return rid; - var row = new RawManifestResourceRow(netResources.NextOffset, - (uint)er.Attributes, - stringsHeap.Add(er.Name), - 0); - rid = tablesHeap.ManifestResourceTable.Add(row); - manifestResourceInfos.Add(er, rid); - embeddedResourceToByteArray[er] = netResources.Add(er.Data); - //TODO: Add custom attributes - return rid; - } - - uint AddAssemblyLinkedResource(AssemblyLinkedResource alr) { - if (alr == null) { - Error("AssemblyLinkedResource is null"); - return 0; - } - uint rid; - if (manifestResourceInfos.TryGetRid(alr, out rid)) - return rid; - var row = new RawManifestResourceRow(0, - (uint)alr.Attributes, - stringsHeap.Add(alr.Name), - AddImplementation(alr.Assembly)); - rid = tablesHeap.ManifestResourceTable.Add(row); - manifestResourceInfos.Add(alr, rid); - //TODO: Add custom attributes - return rid; - } - - uint AddLinkedResource(LinkedResource lr) { - if (lr == null) { - Error("LinkedResource is null"); - return 0; - } - uint rid; - if (manifestResourceInfos.TryGetRid(lr, out rid)) - return rid; - var row = new RawManifestResourceRow(0, - (uint)lr.Attributes, - stringsHeap.Add(lr.Name), - AddImplementation(lr.File)); - rid = tablesHeap.ManifestResourceTable.Add(row); - manifestResourceInfos.Add(lr, rid); - //TODO: Add custom attributes - return rid; - } - - /// - /// Adds a File row - /// - /// File - /// Its new rid - protected uint AddFile(FileDef file) { - if (file == null) { - Error("FileDef is null"); - return 0; - } - uint rid; - if (fileDefInfos.TryGetRid(file, out rid)) - return rid; - var row = new RawFileRow((uint)file.Flags, - stringsHeap.Add(file.Name), - blobHeap.Add(file.HashValue)); //TODO: Re-calculate the hash value if possible - rid = tablesHeap.FileTable.Add(row); - fileDefInfos.Add(file, rid); - AddCustomAttributes(Table.File, rid, file); - return rid; - } - - /// - /// Adds a ExportedType row - /// - /// Exported type - /// Its new rid - protected uint AddExportedType(ExportedType et) { - if (et == null) { - Error("ExportedType is null"); - return 0; - } - uint rid; - if (exportedTypeInfos.TryGetRid(et, out rid)) - return rid; - exportedTypeInfos.Add(et, 0); // Prevent inf recursion - var row = new RawExportedTypeRow((uint)et.Attributes, - et.TypeDefId, //TODO: Should be updated with the new rid - stringsHeap.Add(et.TypeName), - stringsHeap.Add(et.TypeNamespace), - AddImplementation(et.Implementation)); - rid = tablesHeap.ExportedTypeTable.Add(row); - exportedTypeInfos.SetRid(et, rid); - AddCustomAttributes(Table.ExportedType, rid, et); - return rid; - } - - /// - /// Gets a #Blob offset of a type signature - /// - /// Type sig - /// Extra data to append the signature if - /// is true. - /// #Blob offset - protected uint GetSignature(TypeSig ts, byte[] extraData) { - byte[] blob; - if (ts == null) { - Error("TypeSig is null"); - blob = null; - } - else - blob = SignatureWriter.Write(this, ts); - AppendExtraData(ref blob, extraData); - return blobHeap.Add(blob); - } - - /// - /// Gets a #Blob offset of a calling convention signature - /// - /// Signature - /// #Blob offset - protected uint GetSignature(CallingConventionSig sig) { - if (sig == null) { - Error("CallingConventionSig is null"); - return 0; - } - - var blob = SignatureWriter.Write(this, sig); - AppendExtraData(ref blob, sig.ExtraData); - return blobHeap.Add(blob); - } - - void AppendExtraData(ref byte[] blob, byte[] extraData) { - if (PreserveExtraSignatureData && extraData != null && extraData.Length > 0) { - int blen = blob == null ? 0 : blob.Length; - Array.Resize(ref blob, blen + extraData.Length); - Array.Copy(extraData, 0, blob, blen, extraData.Length); - } - } - - /// - /// Adds a CustomAttribute row - /// - /// Owner table - /// New owner rid - /// Onwer - protected void AddCustomAttributes(Table table, uint rid, IHasCustomAttribute hca) { - AddCustomAttributes(table, rid, hca.CustomAttributes); - } - - void AddCustomAttributes(Table table, uint rid, CustomAttributeCollection caList) { - var token = new MDToken(table, rid); - foreach (var ca in caList) - AddCustomAttribute(token, ca); - } - - void AddCustomAttribute(MDToken token, CustomAttribute ca) { - if (ca == null) { - Error("Custom attribute is null"); - return; - } - uint encodedToken; - if (!CodedToken.HasCustomAttribute.Encode(token, out encodedToken)) { - Error("Can't encode HasCustomAttribute token {0:X8}", token.Raw); - encodedToken = 0; - } - var caBlob = CustomAttributeWriter.Write(this, ca); - var row = new RawCustomAttributeRow(encodedToken, - AddCustomAttributeType(ca.Constructor), - blobHeap.Add(caBlob)); - customAttributeInfos.Add(ca, row); - } - - /// - uint ISignatureWriterHelper.ToEncodedToken(ITypeDefOrRef typeDefOrRef) { - return AddTypeDefOrRef(typeDefOrRef); - } - - /// - void IWriterError.Error(string message) { - Error(message); - } - - /// - bool IFullNameCreatorHelper.MustUseAssemblyName(IType type) { - return FullNameCreator.MustUseAssemblyName(module, type); - } - - /// - /// Called before any other methods - /// - protected virtual void Initialize() { - } - - /// - /// Gets all s that should be saved in the meta data - /// - protected abstract List GetAllTypeDefs(); - - /// - /// Initializes TypeDef rids and creates raw rows, but does not initialize - /// any columns. - /// - protected abstract void AllocateTypeDefRids(); - - /// - /// Allocates Field, Method, Property, Event, Param: - /// rid and raw row, but doesn't initialize the raw row. - /// Initializes TypeDef columns: FieldList, MethodList. - /// Initializes Method column: ParamList. - /// Initializes and . - /// - protected abstract void AllocateMemberDefRids(); - - /// - /// Adds a . Its custom attributes are also added. - /// - /// Type reference - /// Its new rid - protected abstract uint AddTypeRef(TypeRef tr); - - /// - /// Adds a . Its custom attributes are also added. - /// - /// Type spec - /// Its new rid - protected abstract uint AddTypeSpec(TypeSpec ts); - - /// - /// Adds a . Its custom attributes are also added. - /// - /// Member ref - /// Its new rid - protected abstract uint AddMemberRef(MemberRef mr); - - /// - /// Adds a . Its custom attributes are also added. - /// - /// Stand alone sig - /// Its new rid - protected abstract uint AddStandAloneSig(StandAloneSig sas); - - /// - /// Adds a . Its custom attributes are also added. - /// - /// Method spec - /// Its new rid - protected abstract uint AddMethodSpec(MethodSpec ms); - - /// - /// Called before sorting the CustomAttribute table. This is the last time anything - /// can be inserted into this table. - /// - protected virtual void BeforeSortingCustomAttributes() { - } - - /// - /// Called after everything has been initialized. The sub class can initialize more - /// rows if necessary or do nothing. After this method has been called, nothing else - /// can be added. - /// - protected virtual void EverythingInitialized() { - } - - const uint HEAP_ALIGNMENT = 4; - - /// - public void SetOffset(FileOffset offset, RVA rva) { - this.offset = offset; - this.rva = rva; - - stringsHeap.SetReadOnly(); - blobHeap.SetReadOnly(); - guidHeap.SetReadOnly(); - tablesHeap.SetReadOnly(); - tablesHeap.BigStrings = stringsHeap.IsBig; - tablesHeap.BigBlob = blobHeap.IsBig; - tablesHeap.BigGuid = guidHeap.IsBig; - - metaDataHeader.Heaps = GetHeaps(); - - metaDataHeader.SetOffset(offset, rva); - uint len = metaDataHeader.GetFileLength(); - offset += len; - rva += len; - - foreach (var heap in metaDataHeader.Heaps) { - offset = offset.AlignUp(HEAP_ALIGNMENT); - rva = rva.AlignUp(HEAP_ALIGNMENT); - heap.SetOffset(offset, rva); - len = heap.GetFileLength(); - offset += len; - rva += len; - } - length = rva - this.rva; - - UpdateMethodRvas(); - UpdateFieldRvas(); - } - - IList GetHeaps() { - var heaps = new List(); - - if (options.OtherHeaps != null) - heaps.AddRange(options.OtherHeaps); - - // The #! heap must be added before the other heaps or the CLR can - // sometimes flag an error. Eg., it can check whether a pointer is valid. - // It does this by comparing the pointer to the last valid address for - // the particular heap. If this pointer really is in the #! heap and the - // #! heap is at an address > than the other heap, then the CLR will think - // it's an invalid pointer. - if (hotHeap != null) // Don't check whether it's empty - heaps.Add(hotHeap); - - heaps.Add(tablesHeap); - if (!stringsHeap.IsEmpty || AlwaysCreateStringsHeap) - heaps.Add(stringsHeap); - if (!usHeap.IsEmpty || AlwaysCreateUSHeap) - heaps.Add(usHeap); - if (!guidHeap.IsEmpty || AlwaysCreateGuidHeap) - heaps.Add(guidHeap); - if (!blobHeap.IsEmpty || AlwaysCreateBlobHeap) - heaps.Add(blobHeap); - - if (options.OtherHeapsEnd != null) - heaps.AddRange(options.OtherHeapsEnd); - - return heaps; - } - - /// - public uint GetFileLength() { - return length; - } - - /// - public uint GetVirtualSize() { - return GetFileLength(); - } - - /// - public void WriteTo(BinaryWriter writer) { - var rva2 = rva; - metaDataHeader.VerifyWriteTo(writer); - rva2 += metaDataHeader.GetFileLength(); - - foreach (var heap in metaDataHeader.Heaps) { - writer.WriteZeros((int)(rva2.AlignUp(HEAP_ALIGNMENT) - rva2)); - rva2 = rva2.AlignUp(HEAP_ALIGNMENT); - heap.VerifyWriteTo(writer); - rva2 += heap.GetFileLength(); - } - } - - /// - /// Sorts the s - /// - /// All s - /// A sorted list - protected static List Sort(IEnumerable pds) { - var sorted = new List(pds); - sorted.Sort((a, b) => { - if (a == null) - return -1; - if (b == null) - return 1; - return a.Sequence.CompareTo(b.Sequence); - }); - return sorted; - } - } -} diff --git a/src/DotNet/Writer/MetaDataHeader.cs b/src/DotNet/Writer/MetaDataHeader.cs deleted file mode 100644 index 22a51c6a4..000000000 --- a/src/DotNet/Writer/MetaDataHeader.cs +++ /dev/null @@ -1,162 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System.Collections.Generic; -using System.IO; -using System.Text; -using dnlib.IO; -using dnlib.PE; -using dnlib.DotNet.MD; - -namespace dnlib.DotNet.Writer { - /// - /// options - /// - public sealed class MetaDataHeaderOptions { - /// - /// Default version string - /// - public const string DEFAULT_VERSION_STRING = MDHeaderRuntimeVersion.MS_CLR_20; - - /// - /// Default header signature - /// - public const uint DEFAULT_SIGNATURE = 0x424A5342; - - /// - /// MD header signature. Default value is - /// - public uint? Signature; - - /// - /// Major version. Default is 1. MS' CLR supports v0.x (x >= 19) and v1.1, nothing else. - /// - public ushort? MajorVersion; - - /// - /// Minor version. Default is 1. - /// - public ushort? MinorVersion; - - /// - /// Reserved and should be 0. - /// - public uint? Reserved1; - - /// - /// Version string. Default is . It's stored as a - /// zero-terminated UTF-8 string. Length should be <= 255 bytes. - /// - public string VersionString; - - /// - /// Storage flags should be 0 - /// - public StorageFlags? StorageFlags; - - /// - /// Reserved and should be 0 - /// - public byte? Reserved2; - } - - /// - /// Meta data header. IMAGE_COR20_HEADER.MetaData points to this header. - /// - public sealed class MetaDataHeader : IChunk { - IList heaps; - readonly MetaDataHeaderOptions options; - uint length; - FileOffset offset; - RVA rva; - - /// - public FileOffset FileOffset { - get { return offset; } - } - - /// - public RVA RVA { - get { return rva; } - } - - /// - /// Gets/sets the heaps - /// - public IList Heaps { - get { return heaps; } - set { heaps = value; } - } - - /// - /// Default constructor - /// - public MetaDataHeader() - : this(null) { - } - - /// - /// Constructor - /// - /// Options - public MetaDataHeader(MetaDataHeaderOptions options) { - this.options = options ?? new MetaDataHeaderOptions(); - } - - /// - public void SetOffset(FileOffset offset, RVA rva) { - this.offset = offset; - this.rva = rva; - - length = 16; - length += (uint)GetVersionString().Length; - length = Utils.AlignUp(length, 4); - length += 4; - foreach (var heap in heaps) { - length += 8; - length += (uint)GetAsciizName(heap.Name).Length; - length = Utils.AlignUp(length, 4); - } - } - - /// - public uint GetFileLength() { - return length; - } - - /// - public uint GetVirtualSize() { - return GetFileLength(); - } - - /// - public void WriteTo(BinaryWriter writer) { - writer.Write(options.Signature ?? MetaDataHeaderOptions.DEFAULT_SIGNATURE); - writer.Write(options.MajorVersion ?? 1); - writer.Write(options.MinorVersion ?? 1); - writer.Write(options.Reserved1 ?? 0); - var s = GetVersionString(); - writer.Write(Utils.AlignUp(s.Length, 4)); - writer.Write(s); - writer.WriteZeros(Utils.AlignUp(s.Length, 4) - s.Length); - writer.Write((byte)(options.StorageFlags ?? 0)); - writer.Write(options.Reserved2 ?? 0); - writer.Write((ushort)heaps.Count); - foreach (var heap in heaps) { - writer.Write((uint)(heap.FileOffset - offset)); - writer.Write(heap.GetFileLength()); - writer.Write(s = GetAsciizName(heap.Name)); - if (s.Length > 32) - throw new ModuleWriterException(string.Format("Heap name '{0}' is > 32 bytes", heap.Name)); - writer.WriteZeros(Utils.AlignUp(s.Length, 4) - s.Length); - } - } - - byte[] GetVersionString() { - return Encoding.UTF8.GetBytes((options.VersionString ?? MetaDataHeaderOptions.DEFAULT_VERSION_STRING) + "\0"); - } - - byte[] GetAsciizName(string s) { - return Encoding.ASCII.GetBytes(s + "\0"); - } - } -} diff --git a/src/DotNet/Writer/Metadata.cs b/src/DotNet/Writer/Metadata.cs new file mode 100644 index 000000000..9b474e6f3 --- /dev/null +++ b/src/DotNet/Writer/Metadata.cs @@ -0,0 +1,3872 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using dnlib.DotNet.Emit; +using dnlib.DotNet.MD; +using dnlib.DotNet.Pdb; +using dnlib.DotNet.Pdb.Portable; +using dnlib.IO; +using dnlib.PE; + +namespace dnlib.DotNet.Writer { + /// + /// flags + /// + [Flags] + public enum MetadataFlags : uint { + /// + /// Preserves all rids in the TypeRef table + /// + PreserveTypeRefRids = 1, + + /// + /// Preserves all rids in the TypeDef table + /// + PreserveTypeDefRids = 2, + + /// + /// Preserves all rids in the Field table + /// + PreserveFieldRids = 4, + + /// + /// Preserves all rids in the Method table + /// + PreserveMethodRids = 8, + + /// + /// Preserves all rids in the Param table + /// + PreserveParamRids = 0x10, + + /// + /// Preserves all rids in the MemberRef table + /// + PreserveMemberRefRids = 0x20, + + /// + /// Preserves all rids in the StandAloneSig table + /// + PreserveStandAloneSigRids = 0x40, + + /// + /// Preserves all rids in the Event table + /// + PreserveEventRids = 0x80, + + /// + /// Preserves all rids in the Property table + /// + PreservePropertyRids = 0x100, + + /// + /// Preserves all rids in the TypeSpec table + /// + PreserveTypeSpecRids = 0x200, + + /// + /// Preserves all rids in the MethodSpec table + /// + PreserveMethodSpecRids = 0x400, + + /// + /// Preserves all method rids, i.e., Method, MemberRef and + /// MethodSpec rids. + /// + PreserveAllMethodRids = PreserveMethodRids | PreserveMemberRefRids | PreserveMethodSpecRids, + + /// + /// Preserves all rids in the following tables: TypeRef, TypeDef, + /// Field, Method, Param, MemberRef, StandAloneSig, + /// Event, Property, TypeSpec, MethodSpec + /// + PreserveRids = PreserveTypeRefRids | + PreserveTypeDefRids | + PreserveFieldRids | + PreserveMethodRids | + PreserveParamRids | + PreserveMemberRefRids | + PreserveStandAloneSigRids | + PreserveEventRids | + PreservePropertyRids | + PreserveTypeSpecRids | + PreserveMethodSpecRids, + + /// + /// Preserves all offsets in the #Strings heap (the original #Strings heap will be saved + /// in the new file). Type names, field names, and other non-user strings are stored + /// in the #Strings heap. + /// + PreserveStringsOffsets = 0x800, + + /// + /// Preserves all offsets in the #US heap (the original #US heap will be saved + /// in the new file). User strings (referenced by the ldstr instruction) are stored in + /// the #US heap. + /// + PreserveUSOffsets = 0x1000, + + /// + /// Preserves all offsets in the #Blob heap (the original #Blob heap will be saved + /// in the new file). Custom attributes, signatures and other blobs are stored in the + /// #Blob heap. + /// + PreserveBlobOffsets = 0x2000, + + /// + /// Preserves the extra data that is present after the original signature in the #Blob + /// heap. This extra data shouldn't be present but might be present if an obfuscator + /// has added this extra data and is eg. using it to decrypt stuff. + /// + PreserveExtraSignatureData = 0x4000, + + /// + /// Preserves as much as possible + /// + PreserveAll = PreserveRids | PreserveStringsOffsets | PreserveUSOffsets | + PreserveBlobOffsets | PreserveExtraSignatureData, + + /// + /// The original method body's max stack field should be used and a new one should not + /// be calculated. + /// + KeepOldMaxStack = 0x8000, + + /// + /// Always create the #GUID heap even if it's empty + /// + AlwaysCreateGuidHeap = 0x10000, + + /// + /// Always create the #Strings heap even if it's empty + /// + AlwaysCreateStringsHeap = 0x20000, + + /// + /// Always create the #US heap even if it's empty + /// + AlwaysCreateUSHeap = 0x40000, + + /// + /// Always create the #Blob heap even if it's empty + /// + AlwaysCreateBlobHeap = 0x80000, + + /// + /// DEPRECATED: + /// Sort the InterfaceImpl table the same way Roslyn sorts it. Roslyn doesn't sort it + /// according to the ECMA spec, see https://github.com/dotnet/roslyn/issues/3905 + /// + RoslynSortInterfaceImpl = 0x100000, + + /// + /// Don't write method bodies + /// + NoMethodBodies = 0x200000, + + /// + /// Don't write .NET resources + /// + NoDotNetResources = 0x400000, + + /// + /// Don't write field data + /// + NoFieldData = 0x800000, + + /// + /// Serialized type names stored in custom attributes are optimized if the types + /// exist in the core library (eg. mscorlib/System.Private.CoreLib). + /// Instead of storing type-name + assembly-name, only type-name is stored. This results in + /// slightly smaller assemblies. + ///
+ ///
+ /// If it's a type in the current module, the type name is optimized and no assembly name is stored in the custom attribute. + ///
+ ///
+ /// This is disabled by default. It's safe to enable if the reference core assembly + /// is the same as the runtime core assembly (eg. it's mscorlib.dll and .NET Framework, + /// but not .NET Core / .NET Standard). + ///
+ OptimizeCustomAttributeSerializedTypeNames = 0x1000000, + } + + /// + /// Metadata heaps event args + /// + public readonly struct MetadataHeapsAddedEventArgs { + /// + /// Gets the metadata writer + /// + public Metadata Metadata { get; } + + /// + /// Gets all heaps + /// + public List Heaps { get; } + + /// + /// Constructor + /// + /// Metadata writer + /// All heaps + public MetadataHeapsAddedEventArgs(Metadata metadata, List heaps) { + Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); + Heaps = heaps ?? throw new ArgumentNullException(nameof(heaps)); + } + } + + /// + /// options + /// + public sealed class MetadataOptions { + MetadataHeaderOptions metadataHeaderOptions; + MetadataHeaderOptions debugMetadataHeaderOptions; + TablesHeapOptions tablesHeapOptions; + List customHeaps; + + /// + /// Gets/sets the options. This is never null. + /// + public MetadataHeaderOptions MetadataHeaderOptions { + get => metadataHeaderOptions ??= new MetadataHeaderOptions(); + set => metadataHeaderOptions = value; + } + + /// + /// Gets/sets the debug (portable PDB) options. This is never null. + /// + public MetadataHeaderOptions DebugMetadataHeaderOptions { + get => debugMetadataHeaderOptions ??= MetadataHeaderOptions.CreatePortablePdbV1_0(); + set => debugMetadataHeaderOptions = value; + } + + /// + /// Gets/sets the options. This is never null. + /// + public TablesHeapOptions TablesHeapOptions { + get => tablesHeapOptions ??= new TablesHeapOptions(); + set => tablesHeapOptions = value; + } + + /// + /// Gets/sets the debug (portable PDB) options. This is never null. + /// + public TablesHeapOptions DebugTablesHeapOptions { + get => tablesHeapOptions ??= TablesHeapOptions.CreatePortablePdbV1_0(); + set => tablesHeapOptions = value; + } + + /// + /// Various options + /// + public MetadataFlags Flags; + + /// + /// Extra heaps to add to the metadata. Also see and + /// + public List CustomHeaps => customHeaps ??= new List(); + + /// + /// Raised after all heaps have been added. The caller can sort the list if needed + /// + public event EventHandler2 MetadataHeapsAdded; + internal void RaiseMetadataHeapsAdded(MetadataHeapsAddedEventArgs e) => MetadataHeapsAdded?.Invoke(e.Metadata, e); + + /// + /// Preserves the original order of heaps, and optionally adds all custom heaps to . + /// + /// Original module with the heaps + /// If true, all custom streams are added to + public void PreserveHeapOrder(ModuleDef module, bool addCustomHeaps) { + if (module is null) + throw new ArgumentNullException(nameof(module)); + if (module is ModuleDefMD mod) { + if (addCustomHeaps) { + var otherStreams = mod.Metadata.AllStreams.Where(a => a.GetType() == typeof(CustomDotNetStream)).Select(a => new DataReaderHeap(a)); + CustomHeaps.AddRange(otherStreams.OfType()); + } + var streamToOrder = new Dictionary(mod.Metadata.AllStreams.Count); + for (int i = 0, order = 0; i < mod.Metadata.AllStreams.Count; i++) { + var stream = mod.Metadata.AllStreams[i]; + if (stream.StartOffset == 0) + continue; + streamToOrder.Add(stream, order++); + } + var nameToOrder = new Dictionary(mod.Metadata.AllStreams.Count, StringComparer.Ordinal); + for (int i = 0, order = 0; i < mod.Metadata.AllStreams.Count; i++) { + var stream = mod.Metadata.AllStreams[i]; + if (stream.StartOffset == 0) + continue; + bool isKnownStream = stream is BlobStream || stream is GuidStream || + stream is PdbStream || stream is StringsStream || stream is TablesStream || stream is USStream; + if (!nameToOrder.ContainsKey(stream.Name) || isKnownStream) + nameToOrder[stream.Name] = order; + order++; + } + MetadataHeapsAdded += (s, e) => { + e.Heaps.Sort((a, b) => { + int oa = GetOrder(streamToOrder, nameToOrder, a); + int ob = GetOrder(streamToOrder, nameToOrder, b); + int c = oa - ob; + if (c != 0) + return c; + return StringComparer.Ordinal.Compare(a.Name, b.Name); + }); + }; + } + } + + static int GetOrder(Dictionary streamToOrder, Dictionary nameToOrder, IHeap heap) { + if (heap is DataReaderHeap drHeap && drHeap.OptionalOriginalStream is DotNetStream dnHeap && streamToOrder.TryGetValue(dnHeap, out int order)) + return order; + if (nameToOrder.TryGetValue(heap.Name, out order)) + return order; + + return int.MaxValue; + } + + /// + /// Default constructor + /// + public MetadataOptions() { + } + + /// + /// Constructor + /// + /// Flags + public MetadataOptions(MetadataFlags flags) => Flags = flags; + + /// + /// Constructor + /// + /// Meta data header options + public MetadataOptions(MetadataHeaderOptions mdhOptions) => metadataHeaderOptions = mdhOptions; + + /// + /// Constructor + /// + /// Meta data header options + /// Flags + public MetadataOptions(MetadataHeaderOptions mdhOptions, MetadataFlags flags) { + Flags = flags; + metadataHeaderOptions = mdhOptions; + } + } + + sealed class DataWriterContext { + public readonly MemoryStream OutStream; + public readonly DataWriter Writer; + public DataWriterContext() { + OutStream = new MemoryStream(); + Writer = new DataWriter(OutStream); + } + } + + /// + /// Portable PDB metadata kind + /// + public enum DebugMetadataKind { + /// + /// No debugging metadata + /// + None, + + /// + /// Standalone / embedded portable PDB metadata + /// + Standalone, + } + + /// + /// Metadata writer event args + /// + public readonly struct MetadataWriterEventArgs { + /// + /// Gets the metadata writer + /// + public Metadata Metadata { get; } + + /// + /// Gets the event + /// + public MetadataEvent Event { get; } + + /// + /// Constructor + /// + /// Writer + /// Event + public MetadataWriterEventArgs(Metadata metadata, MetadataEvent @event) { + Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); + Event = @event; + } + } + + /// + /// Metadata writer progress event args + /// + public readonly struct MetadataProgressEventArgs { + /// + /// Gets the metadata writer + /// + public Metadata Metadata { get; } + + /// + /// Gets the progress, 0.0 - 1.0 + /// + public double Progress { get; } + + /// + /// Constructor + /// + /// Writer + /// Progress, 0.0 - 1.0 + public MetadataProgressEventArgs(Metadata metadata, double progress) { + if (progress < 0 || progress > 1) + throw new ArgumentOutOfRangeException(nameof(progress)); + Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); + Progress = progress; + } + } + + /// + /// .NET meta data + /// + public abstract class Metadata : IReuseChunk, ISignatureWriterHelper, ITokenProvider, ICustomAttributeWriterHelper, IPortablePdbCustomDebugInfoWriterHelper, IWriterError2 { + uint length; + FileOffset offset; + RVA rva; + readonly MetadataOptions options; + ILogger logger; + readonly MetadataErrorContext errorContext; + readonly NormalMetadata debugMetadata; + readonly bool isStandaloneDebugMetadata; + internal readonly ModuleDef module; + internal readonly UniqueChunkList constants; + internal readonly MethodBodyChunks methodBodies; + internal readonly NetResources netResources; + internal readonly MetadataHeader metadataHeader; + internal readonly PdbHeap pdbHeap; + internal readonly TablesHeap tablesHeap; + internal readonly StringsHeap stringsHeap; + internal readonly USHeap usHeap; + internal readonly GuidHeap guidHeap; + internal readonly BlobHeap blobHeap; + internal TypeDef[] allTypeDefs; + internal readonly Rows moduleDefInfos = new Rows(); + internal readonly SortedRows interfaceImplInfos = new SortedRows(); + internal readonly SortedRows hasConstantInfos = new SortedRows(); + internal readonly SortedRows customAttributeInfos = new SortedRows(); + internal readonly SortedRows fieldMarshalInfos = new SortedRows(); + internal readonly SortedRows declSecurityInfos = new SortedRows(); + internal readonly SortedRows classLayoutInfos = new SortedRows(); + internal readonly SortedRows fieldLayoutInfos = new SortedRows(); + internal readonly Rows eventMapInfos = new Rows(); + internal readonly Rows propertyMapInfos = new Rows(); + internal readonly SortedRows methodSemanticsInfos = new SortedRows(); + internal readonly SortedRows methodImplInfos = new SortedRows(); + internal readonly Rows moduleRefInfos = new Rows(); + internal readonly SortedRows implMapInfos = new SortedRows(); + internal readonly SortedRows fieldRVAInfos = new SortedRows(); + internal readonly Rows assemblyInfos = new Rows(); + internal readonly Rows assemblyRefInfos = new Rows(); + internal readonly Rows fileDefInfos = new Rows(); + internal readonly Rows exportedTypeInfos = new Rows(); + internal readonly Rows manifestResourceInfos = new Rows(); + internal readonly SortedRows nestedClassInfos = new SortedRows(); + internal readonly SortedRows genericParamInfos = new SortedRows(); + internal readonly SortedRows genericParamConstraintInfos = new SortedRows(); + internal readonly Dictionary methodToBody = new Dictionary(); + internal readonly Dictionary methodToNativeBody = new Dictionary(); + internal readonly Dictionary embeddedResourceToByteArray = new Dictionary(); + readonly Dictionary fieldToInitialValue = new Dictionary(); + readonly Rows pdbDocumentInfos = new Rows(); + bool methodDebugInformationInfosUsed; + readonly SortedRows localScopeInfos = new SortedRows(); + readonly Rows localVariableInfos = new Rows(); + readonly Rows localConstantInfos = new Rows(); + readonly Rows importScopeInfos = new Rows(); + readonly SortedRows stateMachineMethodInfos = new SortedRows(); + readonly SortedRows customDebugInfos = new SortedRows(); + readonly List binaryWriterContexts = new List(); + readonly List serializerMethodContexts = new List(); + readonly List exportedMethods = new List(); + + /// + /// Raised at various times when writing the metadata + /// + public event EventHandler2 MetadataEvent; + + /// + /// Raised when the progress is updated + /// + public event EventHandler2 ProgressUpdated; + + /// + /// Gets/sets the logger + /// + public ILogger Logger { + get => logger; + set => logger = value; + } + + /// + /// Gets the module + /// + public ModuleDef Module => module; + + /// + /// Gets the constants + /// + public UniqueChunkList Constants => constants; + + /// + /// Gets the method body chunks + /// + public MethodBodyChunks MethodBodyChunks => methodBodies; + + /// + /// Gets the .NET resources + /// + public NetResources NetResources => netResources; + + /// + /// Gets the MD header + /// + public MetadataHeader MetadataHeader => metadataHeader; + + /// + /// Gets the tables heap. Access to this heap is not recommended, but is useful if you + /// want to add random table entries. + /// + public TablesHeap TablesHeap => tablesHeap; + + /// + /// Gets the #Strings heap. Access to this heap is not recommended, but is useful if you + /// want to add random strings. + /// + public StringsHeap StringsHeap => stringsHeap; + + /// + /// Gets the #US heap. Access to this heap is not recommended, but is useful if + /// you want to add random user strings. + /// + public USHeap USHeap => usHeap; + + /// + /// Gets the #GUID heap. Access to this heap is not recommended, but is useful if you + /// want to add random GUIDs. + /// + public GuidHeap GuidHeap => guidHeap; + + /// + /// Gets the #Blob heap. Access to this heap is not recommended, but is useful if you + /// want to add random blobs. + /// + public BlobHeap BlobHeap => blobHeap; + + /// + /// Gets the #Pdb heap. It's only used if it's portable PDB metadata + /// + public PdbHeap PdbHeap => pdbHeap; + + /// + /// Gets all exported methods + /// + public List ExportedMethods => exportedMethods; + + /// + /// The public key that should be used instead of the one in . + /// + internal byte[] AssemblyPublicKey { get; set; } + + internal sealed class SortedRows where T : class where TRow : struct { + public List infos = new List(); + Dictionary toRid = new Dictionary(); + bool isSorted; + + public struct Info { + public readonly T data; + public /*readonly*/ TRow row; + public Info(T data, ref TRow row) { + this.data = data; + this.row = row; + } + } + + public void Add(T data, TRow row) { + if (isSorted) + throw new ModuleWriterException($"Adding a row after it's been sorted. Table: {row.GetType()}"); + infos.Add(new Info(data, ref row)); + toRid[data] = (uint)toRid.Count + 1; + } + + public void Sort(Comparison comparison) { + infos.Sort(CreateComparison(comparison)); + toRid.Clear(); + for (int i = 0; i < infos.Count; i++) + toRid[infos[i].data] = (uint)i + 1; + isSorted = true; + } + + Comparison CreateComparison(Comparison comparison) => + (a, b) => { + int c = comparison(a, b); + if (c != 0) + return c; + // Make sure it's a stable sort + return toRid[a.data].CompareTo(toRid[b.data]); + }; + + public uint Rid(T data) => toRid[data]; + + public bool TryGetRid(T data, out uint rid) { + if (data is null) { + rid = 0; + return false; + } + return toRid.TryGetValue(data, out rid); + } + } + + internal sealed class Rows where T : class { + Dictionary dict = new Dictionary(); + + public int Count => dict.Count; + + public bool TryGetRid(T value, out uint rid) { + if (value is null) { + rid = 0; + return false; + } + return dict.TryGetValue(value, out rid); + } + + public bool Exists(T value) => dict.ContainsKey(value); + public void Add(T value, uint rid) => dict.Add(value, rid); + public uint Rid(T value) => dict[value]; + public void SetRid(T value, uint rid) => dict[value] = rid; + } + + /// + /// Creates a instance + /// + /// Module + /// Constants list + /// Method bodies list + /// .NET resources list + /// Options + /// Debug metadata kind + /// A new instance + public static Metadata Create(ModuleDef module, UniqueChunkList constants, MethodBodyChunks methodBodies, NetResources netResources, MetadataOptions options = null, DebugMetadataKind debugKind = DebugMetadataKind.None) { + if (options is null) + options = new MetadataOptions(); + if ((options.Flags & MetadataFlags.PreserveRids) != 0 && module is ModuleDefMD) + return new PreserveTokensMetadata(module, constants, methodBodies, netResources, options, debugKind, false); + return new NormalMetadata(module, constants, methodBodies, netResources, options, debugKind, false); + } + + /// + public FileOffset FileOffset => offset; + + /// + public RVA RVA => rva; + + /// + /// Gets the bit + /// + public bool PreserveTypeRefRids => (options.Flags & MetadataFlags.PreserveTypeRefRids) != 0; + + /// + /// Gets the bit + /// + public bool PreserveTypeDefRids => (options.Flags & MetadataFlags.PreserveTypeDefRids) != 0; + + /// + /// Gets the bit + /// + public bool PreserveFieldRids => (options.Flags & MetadataFlags.PreserveFieldRids) != 0; + + /// + /// Gets the bit + /// + public bool PreserveMethodRids => (options.Flags & MetadataFlags.PreserveMethodRids) != 0; + + /// + /// Gets the bit + /// + public bool PreserveParamRids => (options.Flags & MetadataFlags.PreserveParamRids) != 0; + + /// + /// Gets the bit + /// + public bool PreserveMemberRefRids => (options.Flags & MetadataFlags.PreserveMemberRefRids) != 0; + + /// + /// Gets the bit + /// + public bool PreserveStandAloneSigRids => (options.Flags & MetadataFlags.PreserveStandAloneSigRids) != 0; + + /// + /// Gets the bit + /// + public bool PreserveEventRids => (options.Flags & MetadataFlags.PreserveEventRids) != 0; + + /// + /// Gets the bit + /// + public bool PreservePropertyRids => (options.Flags & MetadataFlags.PreservePropertyRids) != 0; + + /// + /// Gets the bit + /// + public bool PreserveTypeSpecRids => (options.Flags & MetadataFlags.PreserveTypeSpecRids) != 0; + + /// + /// Gets the bit + /// + public bool PreserveMethodSpecRids => (options.Flags & MetadataFlags.PreserveMethodSpecRids) != 0; + + /// + /// Gets/sets the bit + /// + public bool PreserveStringsOffsets { + get => (options.Flags & MetadataFlags.PreserveStringsOffsets) != 0; + set { + if (value) + options.Flags |= MetadataFlags.PreserveStringsOffsets; + else + options.Flags &= ~MetadataFlags.PreserveStringsOffsets; + } + } + + /// + /// Gets/sets the bit + /// + public bool PreserveUSOffsets { + get => (options.Flags & MetadataFlags.PreserveUSOffsets) != 0; + set { + if (value) + options.Flags |= MetadataFlags.PreserveUSOffsets; + else + options.Flags &= ~MetadataFlags.PreserveUSOffsets; + } + } + + /// + /// Gets/sets the bit + /// + public bool PreserveBlobOffsets { + get => (options.Flags & MetadataFlags.PreserveBlobOffsets) != 0; + set { + if (value) + options.Flags |= MetadataFlags.PreserveBlobOffsets; + else + options.Flags &= ~MetadataFlags.PreserveBlobOffsets; + } + } + + /// + /// Gets/sets the bit + /// + public bool PreserveExtraSignatureData { + get => (options.Flags & MetadataFlags.PreserveExtraSignatureData) != 0; + set { + if (value) + options.Flags |= MetadataFlags.PreserveExtraSignatureData; + else + options.Flags &= ~MetadataFlags.PreserveExtraSignatureData; + } + } + + /// + /// Gets/sets the bit + /// + public bool KeepOldMaxStack { + get => (options.Flags & MetadataFlags.KeepOldMaxStack) != 0; + set { + if (value) + options.Flags |= MetadataFlags.KeepOldMaxStack; + else + options.Flags &= ~MetadataFlags.KeepOldMaxStack; + } + } + + /// + /// Gets/sets the bit + /// + public bool AlwaysCreateGuidHeap { + get => (options.Flags & MetadataFlags.AlwaysCreateGuidHeap) != 0; + set { + if (value) + options.Flags |= MetadataFlags.AlwaysCreateGuidHeap; + else + options.Flags &= ~MetadataFlags.AlwaysCreateGuidHeap; + } + } + + /// + /// Gets/sets the bit + /// + public bool AlwaysCreateStringsHeap { + get => (options.Flags & MetadataFlags.AlwaysCreateStringsHeap) != 0; + set { + if (value) + options.Flags |= MetadataFlags.AlwaysCreateStringsHeap; + else + options.Flags &= ~MetadataFlags.AlwaysCreateStringsHeap; + } + } + + /// + /// Gets/sets the bit + /// + public bool AlwaysCreateUSHeap { + get => (options.Flags & MetadataFlags.AlwaysCreateUSHeap) != 0; + set { + if (value) + options.Flags |= MetadataFlags.AlwaysCreateUSHeap; + else + options.Flags &= ~MetadataFlags.AlwaysCreateUSHeap; + } + } + + /// + /// Gets/sets the bit + /// + public bool AlwaysCreateBlobHeap { + get => (options.Flags & MetadataFlags.AlwaysCreateBlobHeap) != 0; + set { + if (value) + options.Flags |= MetadataFlags.AlwaysCreateBlobHeap; + else + options.Flags &= ~MetadataFlags.AlwaysCreateBlobHeap; + } + } + + /// + /// Gets/sets the bit + /// + public bool RoslynSortInterfaceImpl { + get => (options.Flags & MetadataFlags.RoslynSortInterfaceImpl) != 0; + set { + if (value) + options.Flags |= MetadataFlags.RoslynSortInterfaceImpl; + else + options.Flags &= ~MetadataFlags.RoslynSortInterfaceImpl; + } + } + + /// + /// Gets/sets the bit + /// + public bool NoMethodBodies { + get => (options.Flags & MetadataFlags.NoMethodBodies) != 0; + set { + if (value) + options.Flags |= MetadataFlags.NoMethodBodies; + else + options.Flags &= ~MetadataFlags.NoMethodBodies; + } + } + + /// + /// Gets/sets the bit + /// + public bool NoDotNetResources { + get => (options.Flags & MetadataFlags.NoDotNetResources) != 0; + set { + if (value) + options.Flags |= MetadataFlags.NoDotNetResources; + else + options.Flags &= ~MetadataFlags.NoDotNetResources; + } + } + + /// + /// Gets/sets the bit + /// + public bool NoFieldData { + get => (options.Flags & MetadataFlags.NoFieldData) != 0; + set { + if (value) + options.Flags |= MetadataFlags.NoFieldData; + else + options.Flags &= ~MetadataFlags.NoFieldData; + } + } + + /// + /// Gets/sets the bit + /// + public bool OptimizeCustomAttributeSerializedTypeNames { + get => (options.Flags & MetadataFlags.OptimizeCustomAttributeSerializedTypeNames) != 0; + set { + if (value) + options.Flags |= MetadataFlags.OptimizeCustomAttributeSerializedTypeNames; + else + options.Flags &= ~MetadataFlags.OptimizeCustomAttributeSerializedTypeNames; + } + } + + /// + /// If true, use the original Field RVAs. If it has no RVA, assume it's a new + /// field value and create a new Field RVA. + /// + internal bool KeepFieldRVA { get; set; } + + /// + /// Gets the number of methods that will be written. + /// + protected abstract int NumberOfMethods { get; } + + internal Metadata(ModuleDef module, UniqueChunkList constants, MethodBodyChunks methodBodies, NetResources netResources, MetadataOptions options, DebugMetadataKind debugKind, bool isStandaloneDebugMetadata) { + this.module = module; + this.constants = constants; + this.methodBodies = methodBodies; + this.netResources = netResources; + this.options = options ?? new MetadataOptions(); + metadataHeader = new MetadataHeader(isStandaloneDebugMetadata ? this.options.DebugMetadataHeaderOptions : this.options.MetadataHeaderOptions); + tablesHeap = new TablesHeap(this, isStandaloneDebugMetadata ? this.options.DebugTablesHeapOptions : this.options.TablesHeapOptions); + stringsHeap = new StringsHeap(); + usHeap = new USHeap(); + guidHeap = new GuidHeap(); + blobHeap = new BlobHeap(); + pdbHeap = new PdbHeap(); + + errorContext = new MetadataErrorContext(); + + this.isStandaloneDebugMetadata = isStandaloneDebugMetadata; + switch (debugKind) { + case DebugMetadataKind.None: + break; + + case DebugMetadataKind.Standalone: + Debug.Assert(!isStandaloneDebugMetadata); + //TODO: Refactor this into a smaller class + debugMetadata = new NormalMetadata(module, constants, methodBodies, netResources, options, DebugMetadataKind.None, true); + break; + + default: + throw new ArgumentOutOfRangeException(nameof(debugKind)); + } + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(ModuleDef module) { + moduleDefInfos.TryGetRid(module, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public abstract uint GetRid(TypeRef tr); + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public abstract uint GetRid(TypeDef td); + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public abstract uint GetRid(FieldDef fd); + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public abstract uint GetRid(MethodDef md); + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public abstract uint GetRid(ParamDef pd); + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(InterfaceImpl ii) { + interfaceImplInfos.TryGetRid(ii, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public abstract uint GetRid(MemberRef mr); + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetConstantRid(IHasConstant hc) { + hasConstantInfos.TryGetRid(hc, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetCustomAttributeRid(CustomAttribute ca) { + customAttributeInfos.TryGetRid(ca, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetFieldMarshalRid(IHasFieldMarshal hfm) { + fieldMarshalInfos.TryGetRid(hfm, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(DeclSecurity ds) { + declSecurityInfos.TryGetRid(ds, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetClassLayoutRid(TypeDef td) { + classLayoutInfos.TryGetRid(td, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetFieldLayoutRid(FieldDef fd) { + fieldLayoutInfos.TryGetRid(fd, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public abstract uint GetRid(StandAloneSig sas); + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetEventMapRid(TypeDef td) { + eventMapInfos.TryGetRid(td, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public abstract uint GetRid(EventDef ed); + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetPropertyMapRid(TypeDef td) { + propertyMapInfos.TryGetRid(td, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public abstract uint GetRid(PropertyDef pd); + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetMethodSemanticsRid(MethodDef md) { + methodSemanticsInfos.TryGetRid(md, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(ModuleRef mr) { + moduleRefInfos.TryGetRid(mr, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public abstract uint GetRid(TypeSpec ts); + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetImplMapRid(IMemberForwarded mf) { + implMapInfos.TryGetRid(mf, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetFieldRVARid(FieldDef fd) { + fieldRVAInfos.TryGetRid(fd, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(AssemblyDef asm) { + assemblyInfos.TryGetRid(asm, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(AssemblyRef asmRef) { + assemblyRefInfos.TryGetRid(asmRef, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(FileDef fd) { + fileDefInfos.TryGetRid(fd, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(ExportedType et) { + exportedTypeInfos.TryGetRid(et, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetManifestResourceRid(Resource resource) { + manifestResourceInfos.TryGetRid(resource, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetNestedClassRid(TypeDef td) { + nestedClassInfos.TryGetRid(td, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(GenericParam gp) { + genericParamInfos.TryGetRid(gp, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public abstract uint GetRid(MethodSpec ms); + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(GenericParamConstraint gpc) { + genericParamConstraintInfos.TryGetRid(gpc, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(PdbDocument doc) { + if (debugMetadata is null) + return 0; + debugMetadata.pdbDocumentInfos.TryGetRid(doc, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(PdbScope scope) { + if (debugMetadata is null) + return 0; + debugMetadata.localScopeInfos.TryGetRid(scope, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(PdbLocal local) { + if (debugMetadata is null) + return 0; + debugMetadata.localVariableInfos.TryGetRid(local, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(PdbConstant constant) { + if (debugMetadata is null) + return 0; + + debugMetadata.localConstantInfos.TryGetRid(constant, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetRid(PdbImportScope importScope) { + if (debugMetadata is null) + return 0; + debugMetadata.importScopeInfos.TryGetRid(importScope, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetStateMachineMethodRid(PdbAsyncMethodCustomDebugInfo asyncMethod) { + if (debugMetadata is null) + return 0; + debugMetadata.stateMachineMethodInfos.TryGetRid(asyncMethod, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetStateMachineMethodRid(PdbIteratorMethodCustomDebugInfo iteratorMethod) { + if (debugMetadata is null) + return 0; + debugMetadata.stateMachineMethodInfos.TryGetRid(iteratorMethod, out uint rid); + return rid; + } + + /// + /// Gets the new rid + /// + /// Value + /// Its new rid or 0 + public uint GetCustomDebugInfoRid(PdbCustomDebugInfo cdi) { + if (debugMetadata is null) + return 0; + debugMetadata.customDebugInfos.TryGetRid(cdi, out uint rid); + return rid; + } + + /// + /// Gets the + /// + /// Method + /// The or null if is + /// null or not a method defined in this module. + public MethodBody GetMethodBody(MethodDef md) { + if (md is null) + return null; + methodToBody.TryGetValue(md, out var mb); + return mb; + } + + /// + /// Gets a method's local variable signature token + /// + /// Method + /// Locals sig token or 0 + public uint GetLocalVarSigToken(MethodDef md) => GetMethodBody(md)?.LocalVarSigTok ?? 0; + + /// + /// Gets the where the resource data will be stored + /// + /// Embedded resource + /// A instance or null if + /// is invalid + public DataReaderChunk GetChunk(EmbeddedResource er) { + if (er is null) + return null; + embeddedResourceToByteArray.TryGetValue(er, out var chunk); + return chunk; + } + + /// + /// Gets the where the initial value is stored + /// + /// Field + /// A instance or null if + /// is invalid + public ByteArrayChunk GetInitialValueChunk(FieldDef fd) { + if (fd is null) + return null; + fieldToInitialValue.TryGetValue(fd, out var chunk); + return chunk; + } + + ILogger GetLogger() => logger ?? DummyLogger.ThrowModuleWriterExceptionOnErrorInstance; + + /// + /// Called when an error is detected + /// + /// Error message + /// Optional message arguments + protected void Error(string message, params object[] args) { + errorContext.Append("Error", ref message, ref args); + GetLogger().Log(this, LoggerEvent.Error, message, args); + } + + /// + /// Called to warn of something + /// + /// Warning message + /// Optional message arguments + protected void Warning(string message, params object[] args) { + errorContext.Append("Warning", ref message, ref args); + GetLogger().Log(this, LoggerEvent.Warning, message, args); + } + + /// + /// Raises + /// + /// Event + protected void OnMetadataEvent(MetadataEvent evt) { + errorContext.Event = evt; + RaiseProgress(evt, 0); + MetadataEvent?.Invoke(this, new MetadataWriterEventArgs(this, evt)); + } + + static readonly double[] eventToProgress = new double[(int)Writer.MetadataEvent.EndCreateTables - (int)Writer.MetadataEvent.BeginCreateTables + 1 + 1] { + 0, // BeginCreateTables + 0.00134240009466231,// AllocateTypeDefRids + 0.00257484711254305,// AllocateMemberDefRids + 0.0762721800615359, // MemberDefRidsAllocated + 0.196633787905108, // MemberDefsInitialized + 0.207788892253819, // BeforeSortTables + 0.270543867900699, // MostTablesSorted + 0.451478814851716, // MemberDefCustomAttributesWritten + 0.451478949929206, // BeginAddResources + 0.454664752528583, // EndAddResources + 0.454664887606073, // BeginWriteMethodBodies + 0.992591810143725, // EndWriteMethodBodies + 0.999984331011171, // OnAllTablesSorted + 1, // EndCreateTables + 1,// An extra one so we can get the next base progress without checking the index + }; + + /// + /// Raises the progress event + /// + /// Base event + /// Sub progress + protected void RaiseProgress(MetadataEvent evt, double subProgress) { + subProgress = Math.Min(1, Math.Max(0, subProgress)); + var baseProgress = eventToProgress[(int)evt]; + var nextProgress = eventToProgress[(int)evt + 1]; + var progress = baseProgress + (nextProgress - baseProgress) * subProgress; + progress = Math.Min(1, Math.Max(0, progress)); + ProgressUpdated?.Invoke(this, new MetadataProgressEventArgs(this, progress)); + } + + /// + /// Creates the .NET metadata tables + /// + public void CreateTables() { + OnMetadataEvent(Writer.MetadataEvent.BeginCreateTables); + + if (module.Types.Count == 0 || module.Types[0] is null) + throw new ModuleWriterException("Missing global type"); + + if (module is ModuleDefMD moduleDefMD) { + if (PreserveStringsOffsets) + stringsHeap.Populate(moduleDefMD.StringsStream); + if (PreserveUSOffsets) + usHeap.Populate(moduleDefMD.USStream); + if (PreserveBlobOffsets) + blobHeap.Populate(moduleDefMD.BlobStream); + } + + Create(); + } + + /// + /// Updates each Method row's RVA column if it has any code + /// + void UpdateMethodRvas() { + foreach (var kv in methodToBody) { + var method = kv.Key; + var body = kv.Value; + uint rid = GetRid(method); + var row = tablesHeap.MethodTable[rid]; + row = new RawMethodRow((uint)body.RVA, row.ImplFlags, row.Flags, row.Name, row.Signature, row.ParamList); + tablesHeap.MethodTable[rid] = row; + } + foreach (var kv in methodToNativeBody) { + var method = kv.Key; + var body = kv.Value; + uint rid = GetRid(method); + var row = tablesHeap.MethodTable[rid]; + row = new RawMethodRow((uint)body.RVA, row.ImplFlags, row.Flags, row.Name, row.Signature, row.ParamList); + tablesHeap.MethodTable[rid] = row; + } + } + + /// + /// Updates the FieldRVA rows + /// + void UpdateFieldRvas() { + foreach (var kv in fieldToInitialValue) { + var field = kv.Key; + var iv = kv.Value; + uint rid = fieldRVAInfos.Rid(field); + var row = tablesHeap.FieldRVATable[rid]; + row = new RawFieldRVARow((uint)iv.RVA, row.Field); + tablesHeap.FieldRVATable[rid] = row; + } + } + + void Create() { + Debug.Assert(!isStandaloneDebugMetadata); + Initialize(); + allTypeDefs = GetAllTypeDefs(); + OnMetadataEvent(Writer.MetadataEvent.AllocateTypeDefRids); + AllocateTypeDefRids(); + OnMetadataEvent(Writer.MetadataEvent.AllocateMemberDefRids); + AllocateMemberDefRids(); + OnMetadataEvent(Writer.MetadataEvent.MemberDefRidsAllocated); + + AddModule(module); + AddPdbDocuments(); + InitializeMethodDebugInformation(); + InitializeTypeDefsAndMemberDefs(); + OnMetadataEvent(Writer.MetadataEvent.MemberDefsInitialized); + + InitializeVTableFixups(); + + AddExportedTypes(); + InitializeEntryPoint(); + if (module.Assembly is not null) + AddAssembly(module.Assembly, AssemblyPublicKey); + + OnMetadataEvent(Writer.MetadataEvent.BeforeSortTables); + SortTables(); + InitializeGenericParamConstraintTable(); + OnMetadataEvent(Writer.MetadataEvent.MostTablesSorted); + + WriteTypeDefAndMemberDefCustomAttributesAndCustomDebugInfos(); + OnMetadataEvent(Writer.MetadataEvent.MemberDefCustomAttributesWritten); + + OnMetadataEvent(Writer.MetadataEvent.BeginAddResources); + AddResources(module.Resources); + OnMetadataEvent(Writer.MetadataEvent.EndAddResources); + + OnMetadataEvent(Writer.MetadataEvent.BeginWriteMethodBodies); + WriteMethodBodies(); + OnMetadataEvent(Writer.MetadataEvent.EndWriteMethodBodies); + + BeforeSortingCustomAttributes(); + InitializeCustomAttributeAndCustomDebugInfoTables(); + OnMetadataEvent(Writer.MetadataEvent.OnAllTablesSorted); + + EverythingInitialized(); + OnMetadataEvent(Writer.MetadataEvent.EndCreateTables); + } + + /// + /// Initializes all TypeDef, Field, Method, Event, + /// Property and Param rows. Other tables that are related to these six + /// tables are also updated. No custom attributes are written yet, though. Method bodies + /// aren't written either. + /// + void InitializeTypeDefsAndMemberDefs() { + int count; + int numTypes = allTypeDefs.Length; + int typeNum = 0; + int notifyNum = 0; + const int numNotifyEvents = 5; + int notifyAfter = numTypes / numNotifyEvents; + + foreach (var type in allTypeDefs) { + using var _ = errorContext.SetSource(type); + + if (typeNum++ == notifyAfter && notifyNum < numNotifyEvents) { + RaiseProgress(Writer.MetadataEvent.MemberDefRidsAllocated, (double)typeNum / numTypes); + notifyNum++; + notifyAfter = (int)((double)numTypes / numNotifyEvents * (notifyNum + 1)); + } + + if (type is null) { + Error("TypeDef is null"); + continue; + } + uint typeRid = GetRid(type); + var typeRow = tablesHeap.TypeDefTable[typeRid]; + typeRow = new RawTypeDefRow((uint)type.Attributes, stringsHeap.Add(type.Name), stringsHeap.Add(type.Namespace), type.BaseType is null ? 0 : AddTypeDefOrRef(type.BaseType), typeRow.FieldList, typeRow.MethodList); + tablesHeap.TypeDefTable[typeRid] = typeRow; + AddGenericParams(new MDToken(Table.TypeDef, typeRid), type.GenericParameters); + AddDeclSecurities(new MDToken(Table.TypeDef, typeRid), type.DeclSecurities); + AddInterfaceImpls(typeRid, type.Interfaces); + AddClassLayout(type); + AddNestedType(type, type.DeclaringType); + + var fields = type.Fields; + count = fields.Count; + for (int i = 0; i < count; i++) { + var field = fields[i]; + if (field is null) { + Error("Field is null"); + continue; + } + using var __ = errorContext.SetSource(field); + uint rid = GetRid(field); + var row = new RawFieldRow((ushort)field.Attributes, stringsHeap.Add(field.Name), GetSignature(field.Signature)); + tablesHeap.FieldTable[rid] = row; + AddFieldLayout(field); + AddFieldMarshal(new MDToken(Table.Field, rid), field); + AddFieldRVA(field); + AddImplMap(new MDToken(Table.Field, rid), field); + AddConstant(new MDToken(Table.Field, rid), field); + } + + var methods = type.Methods; + count = methods.Count; + for (int i = 0; i < count; i++) { + var method = methods[i]; + if (method is null) { + Error("Method is null"); + continue; + } + using var __ = errorContext.SetSource(method); + if (method.ExportInfo is not null) + ExportedMethods.Add(method); + uint rid = GetRid(method); + var row = tablesHeap.MethodTable[rid]; + row = new RawMethodRow(row.RVA, (ushort)method.ImplAttributes, (ushort)method.Attributes, stringsHeap.Add(method.Name), GetSignature(method.Signature), row.ParamList); + tablesHeap.MethodTable[rid] = row; + AddGenericParams(new MDToken(Table.Method, rid), method.GenericParameters); + AddDeclSecurities(new MDToken(Table.Method, rid), method.DeclSecurities); + AddImplMap(new MDToken(Table.Method, rid), method); + AddMethodImpls(method, method.Overrides); + var paramDefs = method.ParamDefs; + int count2 = paramDefs.Count; + for (int j = 0; j < count2; j++) { + var pd = paramDefs[j]; + if (pd is null) { + Error("Param is null"); + continue; + } + uint pdRid = GetRid(pd); + var pdRow = new RawParamRow((ushort)pd.Attributes, pd.Sequence, stringsHeap.Add(pd.Name)); + tablesHeap.ParamTable[pdRid] = pdRow; + AddConstant(new MDToken(Table.Param, pdRid), pd); + AddFieldMarshal(new MDToken(Table.Param, pdRid), pd); + } + } + + var events = type.Events; + count = events.Count; + for (int i = 0; i < count; i++) { + var evt = events[i]; + if (evt is null) { + Error("Event is null"); + continue; + } + using var __ = errorContext.SetSource(evt); + uint rid = GetRid(evt); + var row = new RawEventRow((ushort)evt.Attributes, stringsHeap.Add(evt.Name), AddTypeDefOrRef(evt.EventType)); + tablesHeap.EventTable[rid] = row; + AddMethodSemantics(evt); + } + + var properties = type.Properties; + count = properties.Count; + for (int i = 0; i < count; i++) { + var prop = properties[i]; + if (prop is null) { + Error("Property is null"); + continue; + } + using var __ = errorContext.SetSource(prop); + uint rid = GetRid(prop); + var row = new RawPropertyRow((ushort)prop.Attributes, stringsHeap.Add(prop.Name), GetSignature(prop.Type)); + tablesHeap.PropertyTable[rid] = row; + AddConstant(new MDToken(Table.Property, rid), prop); + AddMethodSemantics(prop); + } + } + } + + /// + /// Writes TypeDef, Field, Method, Event, + /// Property and Param custom attributes and custom debug infos. + /// + void WriteTypeDefAndMemberDefCustomAttributesAndCustomDebugInfos() { + int count; + int numTypes = allTypeDefs.Length; + int typeNum = 0; + int notifyNum = 0; + const int numNotifyEvents = 5; + int notifyAfter = numTypes / numNotifyEvents; + + uint rid; + foreach (var type in allTypeDefs) { + using var _ = errorContext.SetSource(type); + + if (typeNum++ == notifyAfter && notifyNum < numNotifyEvents) { + RaiseProgress(Writer.MetadataEvent.MostTablesSorted, (double)typeNum / numTypes); + notifyNum++; + notifyAfter = (int)((double)numTypes / numNotifyEvents * (notifyNum + 1)); + } + + if (type is null) + continue; + if (type.HasCustomAttributes || type.HasCustomDebugInfos) { + rid = GetRid(type); + AddCustomAttributes(Table.TypeDef, rid, type); + AddCustomDebugInformationList(Table.TypeDef, rid, type); + } + + var fields = type.Fields; + count = fields.Count; + for (int i = 0; i < count; i++) { + var field = fields[i]; + if (field is null) + continue; + if (field.HasCustomAttributes || field.HasCustomDebugInfos) { + rid = GetRid(field); + AddCustomAttributes(Table.Field, rid, field); + AddCustomDebugInformationList(Table.Field, rid, field); + } + } + + var methods = type.Methods; + count = methods.Count; + for (int i = 0; i < count; i++) { + var method = methods[i]; + if (method is null) + continue; + using var __ = errorContext.SetSource(method); + if (method.HasCustomAttributes) { + rid = GetRid(method); + AddCustomAttributes(Table.Method, rid, method); + // Method custom debug info is added later when writing method bodies + } + var paramDefs = method.ParamDefs; + int count2 = paramDefs.Count; + for (int j = 0; j < count2; j++) { + var pd = paramDefs[j]; + if (pd is null) + continue; + if (pd.HasCustomAttributes || pd.HasCustomDebugInfos) { + rid = GetRid(pd); + AddCustomAttributes(Table.Param, rid, pd); + AddCustomDebugInformationList(Table.Param, rid, pd); + } + } + } + var events = type.Events; + count = events.Count; + for (int i = 0; i < count; i++) { + var evt = events[i]; + if (evt is null) + continue; + if (evt.HasCustomAttributes || evt.HasCustomDebugInfos) { + rid = GetRid(evt); + AddCustomAttributes(Table.Event, rid, evt); + AddCustomDebugInformationList(Table.Event, rid, evt); + } + } + var properties = type.Properties; + count = properties.Count; + for (int i = 0; i < count; i++) { + var prop = properties[i]; + if (prop is null) + continue; + if (prop.HasCustomAttributes || prop.HasCustomDebugInfos) { + rid = GetRid(prop); + AddCustomAttributes(Table.Property, rid, prop); + AddCustomDebugInformationList(Table.Property, rid, prop); + } + } + } + } + + /// + /// Adds the tokens of all methods in all vtables, if any + /// + void InitializeVTableFixups() { + var fixups = module.VTableFixups; + if (fixups is null || fixups.VTables.Count == 0) + return; + + using var _ = errorContext.SetSource("vtable fixups"); + foreach (var vtable in fixups) { + if (vtable is null) { + Error("VTable is null"); + continue; + } + foreach (var method in vtable) { + if (method is null) + continue; + AddMDTokenProvider(method); + } + } + } + + void AddExportedTypes() { + using var _ = errorContext.SetSource("exported types"); + var exportedTypes = module.ExportedTypes; + + var nestedTypeDict = new Dictionary>(); + for (var i = 0; i < exportedTypes.Count; i++) { + var exportedType = exportedTypes[i]; + if (exportedType.Implementation is not ExportedType decl) + continue; + if (!nestedTypeDict.TryGetValue(decl, out var nestedTypes)) + nestedTypes = nestedTypeDict[decl] = new List(); + nestedTypes.Add(exportedType); + } + + var sortedTypes = new List(exportedTypes.Count); + var visited = new Dictionary(); + var stack = new Stack>(); + stack.Push(exportedTypes.GetEnumerator()); + while (stack.Count > 0) { + var enumerator = stack.Pop(); + while (enumerator.MoveNext()) { + var type = enumerator.Current; + if (visited.ContainsKey(type)) + continue; + visited[type] = true; + sortedTypes.Add(type); + if (nestedTypeDict.TryGetValue(type, out var nested) && nested.Count > 0) { + stack.Push(enumerator); + enumerator = nested.GetEnumerator(); + } + } + } + + int count = sortedTypes.Count; + for (int i = 0; i < count; i++) + AddExportedType(sortedTypes[i]); + } + + /// + /// Adds the entry point. It's only needed if it's a since if it's + /// a , it will have already been added. + /// + void InitializeEntryPoint() { + using var _ = errorContext.SetSource("entry point"); + if (module.ManagedEntryPoint is FileDef epFile) + AddFile(epFile); + } + + /// + /// Sorts all unsorted tables except GenericParamConstraint and CustomAttribute + /// + void SortTables() { + classLayoutInfos.Sort((a, b) => a.row.Parent.CompareTo(b.row.Parent)); + hasConstantInfos.Sort((a, b) => a.row.Parent.CompareTo(b.row.Parent)); + declSecurityInfos.Sort((a, b) => a.row.Parent.CompareTo(b.row.Parent)); + fieldLayoutInfos.Sort((a, b) => a.row.Field.CompareTo(b.row.Field)); + fieldMarshalInfos.Sort((a, b) => a.row.Parent.CompareTo(b.row.Parent)); + fieldRVAInfos.Sort((a, b) => a.row.Field.CompareTo(b.row.Field)); + implMapInfos.Sort((a, b) => a.row.MemberForwarded.CompareTo(b.row.MemberForwarded)); + methodImplInfos.Sort((a, b) => a.row.Class.CompareTo(b.row.Class)); + methodSemanticsInfos.Sort((a, b)=> a.row.Association.CompareTo(b.row.Association)); + nestedClassInfos.Sort((a, b) => a.row.NestedClass.CompareTo(b.row.NestedClass)); + genericParamInfos.Sort((a, b) => { + if (a.row.Owner != b.row.Owner) + return a.row.Owner.CompareTo(b.row.Owner); + return a.row.Number.CompareTo(b.row.Number); + }); + interfaceImplInfos.Sort((a, b) => a.row.Class.CompareTo(b.row.Class)); + + tablesHeap.ClassLayoutTable.IsSorted = true; + tablesHeap.ConstantTable.IsSorted = true; + tablesHeap.DeclSecurityTable.IsSorted = true; + tablesHeap.FieldLayoutTable.IsSorted = true; + tablesHeap.FieldMarshalTable.IsSorted = true; + tablesHeap.FieldRVATable.IsSorted = true; + tablesHeap.GenericParamTable.IsSorted = true; + tablesHeap.ImplMapTable.IsSorted = true; + tablesHeap.InterfaceImplTable.IsSorted = true; + tablesHeap.MethodImplTable.IsSorted = true; + tablesHeap.MethodSemanticsTable.IsSorted = true; + tablesHeap.NestedClassTable.IsSorted = true; + + // These two are also sorted + tablesHeap.EventMapTable.IsSorted = true; + tablesHeap.PropertyMapTable.IsSorted = true; + + foreach (var info in classLayoutInfos.infos) tablesHeap.ClassLayoutTable.Create(info.row); + foreach (var info in hasConstantInfos.infos) tablesHeap.ConstantTable.Create(info.row); + foreach (var info in declSecurityInfos.infos) tablesHeap.DeclSecurityTable.Create(info.row); + foreach (var info in fieldLayoutInfos.infos) tablesHeap.FieldLayoutTable.Create(info.row); + foreach (var info in fieldMarshalInfos.infos) tablesHeap.FieldMarshalTable.Create(info.row); + foreach (var info in fieldRVAInfos.infos) tablesHeap.FieldRVATable.Create(info.row); + foreach (var info in genericParamInfos.infos) tablesHeap.GenericParamTable.Create(info.row); + foreach (var info in implMapInfos.infos) tablesHeap.ImplMapTable.Create(info.row); + foreach (var info in interfaceImplInfos.infos) tablesHeap.InterfaceImplTable.Create(info.row); + foreach (var info in methodImplInfos.infos) tablesHeap.MethodImplTable.Create(info.row); + foreach (var info in methodSemanticsInfos.infos) tablesHeap.MethodSemanticsTable.Create(info.row); + foreach (var info in nestedClassInfos.infos) tablesHeap.NestedClassTable.Create(info.row); + + foreach (var info in interfaceImplInfos.infos) { + if (info.data.HasCustomAttributes || info.data.HasCustomDebugInfos) { + uint rid = interfaceImplInfos.Rid(info.data); + AddCustomAttributes(Table.InterfaceImpl, rid, info.data); + AddCustomDebugInformationList(Table.InterfaceImpl, rid, info.data); + } + } + foreach (var info in declSecurityInfos.infos) { + if (info.data.HasCustomAttributes || info.data.HasCustomDebugInfos) { + uint rid = declSecurityInfos.Rid(info.data); + AddCustomAttributes(Table.DeclSecurity, rid, info.data); + AddCustomDebugInformationList(Table.DeclSecurity, rid, info.data); + } + } + foreach (var info in genericParamInfos.infos) { + if (info.data.HasCustomAttributes || info.data.HasCustomDebugInfos) { + uint rid = genericParamInfos.Rid(info.data); + AddCustomAttributes(Table.GenericParam, rid, info.data); + AddCustomDebugInformationList(Table.GenericParam, rid, info.data); + } + } + } + + /// + /// Initializes the GenericParamConstraint table + /// + void InitializeGenericParamConstraintTable() { + foreach (var type in allTypeDefs) { + if (type is null) + continue; + using var _ = errorContext.SetSource(type); + AddGenericParamConstraints(type.GenericParameters); + var methods = type.Methods; + int count = methods.Count; + for (int i = 0; i < count; i++) { + var method = methods[i]; + if (method is null) + continue; + using var __ = errorContext.SetSource(method); + AddGenericParamConstraints(method.GenericParameters); + } + } + genericParamConstraintInfos.Sort((a, b) => a.row.Owner.CompareTo(b.row.Owner)); + tablesHeap.GenericParamConstraintTable.IsSorted = true; + foreach (var info in genericParamConstraintInfos.infos) + tablesHeap.GenericParamConstraintTable.Create(info.row); + foreach (var info in genericParamConstraintInfos.infos) { + if (info.data.HasCustomAttributes || info.data.HasCustomDebugInfos) { + uint rid = genericParamConstraintInfos.Rid(info.data); + AddCustomAttributes(Table.GenericParamConstraint, rid, info.data); + AddCustomDebugInformationList(Table.GenericParamConstraint, rid, info.data); + } + } + } + + /// + /// Inserts all custom attribute / custom debug info rows in the tables and sort them + /// + void InitializeCustomAttributeAndCustomDebugInfoTables() { + customAttributeInfos.Sort((a, b) => a.row.Parent.CompareTo(b.row.Parent)); + tablesHeap.CustomAttributeTable.IsSorted = true; + foreach (var info in customAttributeInfos.infos) + tablesHeap.CustomAttributeTable.Create(info.row); + + if (debugMetadata is not null) { + debugMetadata.stateMachineMethodInfos.Sort((a, b) => a.row.MoveNextMethod.CompareTo(b.row.MoveNextMethod)); + debugMetadata.tablesHeap.StateMachineMethodTable.IsSorted = true; + foreach (var info in debugMetadata.stateMachineMethodInfos.infos) + debugMetadata.tablesHeap.StateMachineMethodTable.Create(info.row); + + debugMetadata.customDebugInfos.Sort((a, b) => a.row.Parent.CompareTo(b.row.Parent)); + debugMetadata.tablesHeap.CustomDebugInformationTable.IsSorted = true; + foreach (var info in debugMetadata.customDebugInfos.infos) + debugMetadata.tablesHeap.CustomDebugInformationTable.Create(info.row); + } + } + + struct MethodScopeDebugInfo { + public uint MethodRid; + public PdbScope Scope; + public uint ScopeStart; + public uint ScopeLength; + } + + /// + /// Writes all method bodies + /// + void WriteMethodBodies() { + Debug.Assert(!isStandaloneDebugMetadata); + if (NoMethodBodies) + return; + int numMethods = NumberOfMethods; + int methodNum = 0; + int notifyNum = 0; + // Writing method bodies is the most expensive part and takes the longest + const int numNotifyEvents = 40; + int notifyAfter = numMethods / numNotifyEvents; + + var debugMetadata = this.debugMetadata; + var methodBodies = this.methodBodies; + var methodToBody = this.methodToBody; + + List methodScopeDebugInfos; + List scopeStack; + SerializerMethodContext serializerMethodContext; + if (debugMetadata is null) { + methodScopeDebugInfos = null; + scopeStack = null; + serializerMethodContext = null; + } + else { + methodScopeDebugInfos = new List(); + scopeStack = new List(); + serializerMethodContext = AllocSerializerMethodContext(); + } + + bool keepMaxStack = KeepOldMaxStack; + var writer = new MethodBodyWriter(this); + foreach (var type in allTypeDefs) { + if (type is null) + continue; + + using var _ = errorContext.SetSource(type); + + var methods = type.Methods; + for (int i = 0; i < methods.Count; i++) { + var method = methods[i]; + if (method is null) + continue; + + using var __ = errorContext.SetSource(method); + + if (methodNum++ == notifyAfter && notifyNum < numNotifyEvents) { + RaiseProgress(Writer.MetadataEvent.BeginWriteMethodBodies, (double)methodNum / numMethods); + notifyNum++; + notifyAfter = (int)((double)numMethods / numNotifyEvents * (notifyNum + 1)); + } + + uint localVarSigTok = 0; + + var cilBody = method.Body; + if (cilBody is not null) { + if (!(cilBody.Instructions.Count == 0 && cilBody.Variables.Count == 0)) { + writer.Reset(cilBody, keepMaxStack || cilBody.KeepOldMaxStack); + writer.Write(); + var origRva = method.RVA; + uint origSize = cilBody.MetadataBodySize; + var mb = methodBodies.Add(new MethodBody(writer.Code, writer.ExtraSections, writer.LocalVarSigTok), origRva, origSize); + methodToBody[method] = mb; + localVarSigTok = writer.LocalVarSigTok; + } + } + else { + var nativeBody = method.NativeBody; + if (nativeBody is not null) + methodToNativeBody[method] = nativeBody; + else if (method.MethodBody is not null) + Error("Unsupported method body"); + } + + if (debugMetadata is not null) { + uint rid = GetRid(method); + + if (cilBody is not null) { + var pdbMethod = cilBody.PdbMethod; + if (pdbMethod is not null) { + // We don't need to write empty scopes + if (!IsEmptyRootScope(cilBody, pdbMethod.Scope)) { + serializerMethodContext.SetBody(method); + scopeStack.Add(pdbMethod.Scope); + while (scopeStack.Count > 0) { + var scope = scopeStack[scopeStack.Count - 1]; + scopeStack.RemoveAt(scopeStack.Count - 1); + scopeStack.AddRange(scope.Scopes); + uint scopeStart = serializerMethodContext.GetOffset(scope.Start); + uint scopeEnd = serializerMethodContext.GetOffset(scope.End); + methodScopeDebugInfos.Add(new MethodScopeDebugInfo() { + MethodRid = rid, + Scope = scope, + ScopeStart = scopeStart, + ScopeLength = scopeEnd - scopeStart, + }); + } + } + } + } + + // Always add CDIs even if it has no managed method body + AddCustomDebugInformationList(method, rid, localVarSigTok); + } + } + } + if (debugMetadata is not null) { + methodScopeDebugInfos.Sort((a, b) => { + int c = a.MethodRid.CompareTo(b.MethodRid); + if (c != 0) + return c; + c = a.ScopeStart.CompareTo(b.ScopeStart); + if (c != 0) + return c; + return b.ScopeLength.CompareTo(a.ScopeLength); + }); + foreach (var info in methodScopeDebugInfos) { + uint localScopeRid = (uint)debugMetadata.localScopeInfos.infos.Count + 1; + var row = new RawLocalScopeRow(info.MethodRid, AddImportScope(info.Scope.ImportScope), + (uint)debugMetadata.tablesHeap.LocalVariableTable.Rows + 1, + (uint)debugMetadata.tablesHeap.LocalConstantTable.Rows + 1, + info.ScopeStart, info.ScopeLength); + debugMetadata.localScopeInfos.Add(info.Scope, row); + var variables = info.Scope.Variables; + int count = variables.Count; + for (int i = 0; i < count; i++) { + var local = variables[i]; + AddLocalVariable(local); + } + var constants = info.Scope.Constants; + count = constants.Count; + for (int i = 0; i < count; i++) { + var constant = constants[i]; + AddLocalConstant(constant); + } + AddCustomDebugInformationList(Table.LocalScope, localScopeRid, info.Scope.CustomDebugInfos); + } + + debugMetadata.tablesHeap.LocalScopeTable.IsSorted = true; + foreach (var info in debugMetadata.localScopeInfos.infos) + debugMetadata.tablesHeap.LocalScopeTable.Create(info.row); + } + if (serializerMethodContext is not null) + Free(ref serializerMethodContext); + } + + static bool IsEmptyRootScope(CilBody cilBody, PdbScope scope) { + if (scope.Variables.Count != 0) + return false; + if (scope.Constants.Count != 0) + return false; + if (scope.Namespaces.Count != 0) + return false; + if (scope.ImportScope is not null) + return false; + if (scope.Scopes.Count != 0) + return false; + if (scope.CustomDebugInfos.Count != 0) + return false; + if (scope.End is not null) + return false; + if (cilBody.Instructions.Count != 0 && cilBody.Instructions[0] != scope.Start) + return false; + + return true; + } + + /// + /// Checks whether a list is empty or whether it contains only nulls + /// + /// Any type + /// The list + /// true if the list is empty or if it contains only nulls, false otherwise + protected static bool IsEmpty(IList list) where T : class { + if (list is null) + return true; + int count = list.Count; + for (int i = 0; i < count; i++) { + if (list[i] is not null) + return false; + } + return true; + } + + /// + public MDToken GetToken(object o) { + if (o is IMDTokenProvider tp) + return new MDToken(tp.MDToken.Table, AddMDTokenProvider(tp)); + + if (o is string s) + return new MDToken((Table)0x70, usHeap.Add(s)); + + if (o is MethodSig methodSig) + return new MDToken(Table.StandAloneSig, AddStandAloneSig(methodSig, methodSig.OriginalToken)); + + if (o is FieldSig fieldSig) + return new MDToken(Table.StandAloneSig, AddStandAloneSig(fieldSig, 0)); + + if (o is null) + Error("Instruction operand is null"); + else + Error("Invalid instruction operand"); + return new MDToken((Table)0xFF, 0x00FFFFFF); + } + + /// + public virtual MDToken GetToken(IList locals, uint origToken) { + if (locals is null || locals.Count == 0) + return new MDToken((Table)0, 0); + + var row = new RawStandAloneSigRow(GetSignature(new LocalSig(locals, false))); + uint rid = tablesHeap.StandAloneSigTable.Add(row); + //TODO: Add custom attributes + //TODO: Add custom debug infos + return new MDToken(Table.StandAloneSig, rid); + } + + /// + /// Adds a + /// + /// Method signature + /// Original StandAloneSig token or 0 if none + /// Its new rid + protected virtual uint AddStandAloneSig(MethodSig methodSig, uint origToken) { + if (methodSig is null) { + Error("StandAloneSig: MethodSig is null"); + return 0; + } + + var row = new RawStandAloneSigRow(GetSignature(methodSig)); + uint rid = tablesHeap.StandAloneSigTable.Add(row); + //TODO: Add custom attributes + //TODO: Add custom debug infos + return rid; + } + + /// + /// Adds a + /// + /// FIeld signature + /// Original StandAloneSig token or 0 if none + /// Its new rid + protected virtual uint AddStandAloneSig(FieldSig fieldSig, uint origToken) { + if (fieldSig is null) { + Error("StandAloneSig: FieldSig is null"); + return 0; + } + + var row = new RawStandAloneSigRow(GetSignature(fieldSig)); + uint rid = tablesHeap.StandAloneSigTable.Add(row); + //TODO: Add custom attributes + //TODO: Add custom debug infos + return rid; + } + + uint AddMDTokenProvider(IMDTokenProvider tp) { + if (tp is not null) { + switch (tp.MDToken.Table) { + case Table.Module: + return AddModule((ModuleDef)tp); + + case Table.TypeRef: + return AddTypeRef((TypeRef)tp); + + case Table.TypeDef: + return GetRid((TypeDef)tp); + + case Table.Field: + return GetRid((FieldDef)tp); + + case Table.Method: + return GetRid((MethodDef)tp); + + case Table.Param: + return GetRid((ParamDef)tp); + + case Table.MemberRef: + return AddMemberRef((MemberRef)tp); + + case Table.StandAloneSig: + return AddStandAloneSig((StandAloneSig)tp); + + case Table.Event: + return GetRid((EventDef)tp); + + case Table.Property: + return GetRid((PropertyDef)tp); + + case Table.ModuleRef: + return AddModuleRef((ModuleRef)tp); + + case Table.TypeSpec: + return AddTypeSpec((TypeSpec)tp); + + case Table.Assembly: + return AddAssembly((AssemblyDef)tp, null); + + case Table.AssemblyRef: + return AddAssemblyRef((AssemblyRef)tp); + + case Table.File: + return AddFile((FileDef)tp); + + case Table.ExportedType: + return AddExportedType((ExportedType)tp); + + case Table.MethodSpec: + return AddMethodSpec((MethodSpec)tp); + + case Table.FieldPtr: + case Table.MethodPtr: + case Table.ParamPtr: + case Table.InterfaceImpl: + case Table.Constant: + case Table.CustomAttribute: + case Table.FieldMarshal: + case Table.DeclSecurity: + case Table.ClassLayout: + case Table.FieldLayout: + case Table.EventMap: + case Table.EventPtr: + case Table.PropertyMap: + case Table.PropertyPtr: + case Table.MethodSemantics: + case Table.MethodImpl: + case Table.ImplMap: + case Table.FieldRVA: + case Table.ENCLog: + case Table.ENCMap: + case Table.AssemblyProcessor: + case Table.AssemblyOS: + case Table.AssemblyRefProcessor: + case Table.AssemblyRefOS: + case Table.ManifestResource: + case Table.NestedClass: + case Table.GenericParam: + case Table.GenericParamConstraint: + case Table.Document: + case Table.MethodDebugInformation: + case Table.LocalScope: + case Table.LocalVariable: + case Table.LocalConstant: + case Table.ImportScope: + case Table.StateMachineMethod: + case Table.CustomDebugInformation: + default: + break; + } + } + + if (tp is null) + Error("IMDTokenProvider is null"); + else + Error("Invalid IMDTokenProvider"); + return 0; + } + + /// + /// Adds a + /// + /// Value + /// Its encoded token + protected uint AddTypeDefOrRef(ITypeDefOrRef tdr) { + if (tdr is null) { + Error("TypeDefOrRef is null"); + return 0; + } + + var token = new MDToken(tdr.MDToken.Table, AddMDTokenProvider(tdr)); + if (!CodedToken.TypeDefOrRef.Encode(token, out uint encodedToken)) { + Error("Can't encode TypeDefOrRef token 0x{0:X8}.", token.Raw); + encodedToken = 0; + } + return encodedToken; + } + + /// + /// Adds a + /// + /// Value + /// Its encoded token + protected uint AddResolutionScope(IResolutionScope rs) { + if (rs is null) { + return 0; + } + + var token = new MDToken(rs.MDToken.Table, AddMDTokenProvider(rs)); + if (!CodedToken.ResolutionScope.Encode(token, out uint encodedToken)) { + Error("Can't encode ResolutionScope token 0x{0:X8}.", token.Raw); + encodedToken = 0; + } + return encodedToken; + } + + /// + /// Adds a + /// + /// Value + /// Its encoded token + protected uint AddMethodDefOrRef(IMethodDefOrRef mdr) { + if (mdr is null) { + Error("MethodDefOrRef is null"); + return 0; + } + + var token = new MDToken(mdr.MDToken.Table, AddMDTokenProvider(mdr)); + if (!CodedToken.MethodDefOrRef.Encode(token, out uint encodedToken)) { + Error("Can't encode MethodDefOrRef token 0x{0:X8}.", token.Raw); + encodedToken = 0; + } + return encodedToken; + } + + /// + /// Adds a + /// + /// Value + /// Its encoded token + protected uint AddMemberRefParent(IMemberRefParent parent) { + if (parent is null) { + Error("MemberRefParent is null"); + return 0; + } + + var token = new MDToken(parent.MDToken.Table, AddMDTokenProvider(parent)); + if (!CodedToken.MemberRefParent.Encode(token, out uint encodedToken)) { + Error("Can't encode MemberRefParent token 0x{0:X8}.", token.Raw); + encodedToken = 0; + } + return encodedToken; + } + + /// + /// Adds a + /// + /// Value + /// Its encoded token + protected uint AddImplementation(IImplementation impl) { + if (impl is null) { + Error("Implementation is null"); + return 0; + } + + var token = new MDToken(impl.MDToken.Table, AddMDTokenProvider(impl)); + if (!CodedToken.Implementation.Encode(token, out uint encodedToken)) { + Error("Can't encode Implementation token 0x{0:X8}.", token.Raw); + encodedToken = 0; + } + return encodedToken; + } + + /// + /// Adds a + /// + /// Value + /// Its encoded token + protected uint AddCustomAttributeType(ICustomAttributeType cat) { + if (cat is null) { + Error("CustomAttributeType is null"); + return 0; + } + + var token = new MDToken(cat.MDToken.Table, AddMDTokenProvider(cat)); + if (!CodedToken.CustomAttributeType.Encode(token, out uint encodedToken)) { + Error("Can't encode CustomAttributeType token 0x{0:X8}.", token.Raw); + encodedToken = 0; + } + return encodedToken; + } + + /// + /// Adds a NestedType row + /// + /// Nested type + /// Declaring type + protected void AddNestedType(TypeDef nestedType, TypeDef declaringType) { + if (nestedType is null || declaringType is null) + return; + uint nestedRid = GetRid(nestedType); + uint dtRid = GetRid(declaringType); + if (nestedRid == 0 || dtRid == 0) + return; + var row = new RawNestedClassRow(nestedRid, dtRid); + nestedClassInfos.Add(declaringType, row); + } + + /// + /// Adds a Module row + /// + /// Module + /// Its new rid + protected uint AddModule(ModuleDef module) { + if (module is null) { + Error("Module is null"); + return 0; + } + if (this.module != module) + Error("Module '{0}' must be referenced with a ModuleRef, not a ModuleDef.", module); + if (moduleDefInfos.TryGetRid(module, out uint rid)) + return rid; + var row = new RawModuleRow(module.Generation, + stringsHeap.Add(module.Name), + guidHeap.Add(module.Mvid), + guidHeap.Add(module.EncId), + guidHeap.Add(module.EncBaseId)); + rid = tablesHeap.ModuleTable.Add(row); + moduleDefInfos.Add(module, rid); + AddCustomAttributes(Table.Module, rid, module); + AddCustomDebugInformationList(Table.Module, rid, module); + return rid; + } + + /// + /// Adds a ModuleRef row + /// + /// Module ref + /// Its new rid + protected uint AddModuleRef(ModuleRef modRef) { + if (modRef is null) { + Error("ModuleRef is null"); + return 0; + } + if (moduleRefInfos.TryGetRid(modRef, out uint rid)) + return rid; + var row = new RawModuleRefRow(stringsHeap.Add(modRef.Name)); + rid = tablesHeap.ModuleRefTable.Add(row); + moduleRefInfos.Add(modRef, rid); + AddCustomAttributes(Table.ModuleRef, rid, modRef); + AddCustomDebugInformationList(Table.ModuleRef, rid, modRef); + return rid; + } + + /// + /// Adds an AssemblyRef row + /// + /// Assembly ref + /// Its new rid + protected uint AddAssemblyRef(AssemblyRef asmRef) { + if (asmRef is null) { + Error("AssemblyRef is null"); + return 0; + } + if (assemblyRefInfos.TryGetRid(asmRef, out uint rid)) + return rid; + var version = Utils.CreateVersionWithNoUndefinedValues(asmRef.Version); + var row = new RawAssemblyRefRow((ushort)version.Major, + (ushort)version.Minor, + (ushort)version.Build, + (ushort)version.Revision, + (uint)asmRef.Attributes, + blobHeap.Add(PublicKeyBase.GetRawData(asmRef.PublicKeyOrToken)), + stringsHeap.Add(asmRef.Name), + stringsHeap.Add(asmRef.Culture), + blobHeap.Add(asmRef.Hash)); + rid = tablesHeap.AssemblyRefTable.Add(row); + assemblyRefInfos.Add(asmRef, rid); + AddCustomAttributes(Table.AssemblyRef, rid, asmRef); + AddCustomDebugInformationList(Table.AssemblyRef, rid, asmRef); + return rid; + } + + /// + /// Adds an Assembly row + /// + /// Assembly + /// The public key that should be used + /// Its new rid + protected uint AddAssembly(AssemblyDef asm, byte[] publicKey) { + if (asm is null) { + Error("Assembly is null"); + return 0; + } + if (assemblyInfos.TryGetRid(asm, out uint rid)) + return rid; + + var asmAttrs = asm.Attributes; + if (publicKey is not null) + asmAttrs |= AssemblyAttributes.PublicKey; + else + publicKey = PublicKeyBase.GetRawData(asm.PublicKeyOrToken); + + var version = Utils.CreateVersionWithNoUndefinedValues(asm.Version); + var row = new RawAssemblyRow((uint)asm.HashAlgorithm, + (ushort)version.Major, + (ushort)version.Minor, + (ushort)version.Build, + (ushort)version.Revision, + (uint)asmAttrs, + blobHeap.Add(publicKey), + stringsHeap.Add(asm.Name), + stringsHeap.Add(asm.Culture)); + rid = tablesHeap.AssemblyTable.Add(row); + assemblyInfos.Add(asm, rid); + AddDeclSecurities(new MDToken(Table.Assembly, rid), asm.DeclSecurities); + AddCustomAttributes(Table.Assembly, rid, asm); + AddCustomDebugInformationList(Table.Assembly, rid, asm); + return rid; + } + + /// + /// Adds generic parameters + /// + /// New token of owner + /// All generic params + protected void AddGenericParams(MDToken token, IList gps) { + if (gps is null) + return; + int count = gps.Count; + for (int i = 0; i < count; i++) + AddGenericParam(token, gps[i]); + } + + /// + /// Adds a generic param + /// + /// New token of owner + /// Generic paramater + protected void AddGenericParam(MDToken owner, GenericParam gp) { + if (gp is null) { + Error("GenericParam is null"); + return; + } + if (!CodedToken.TypeOrMethodDef.Encode(owner, out uint encodedOwner)) { + Error("Can't encode TypeOrMethodDef token 0x{0:X8}.", owner.Raw); + encodedOwner = 0; + } + var row = new RawGenericParamRow(gp.Number, + (ushort)gp.Flags, + encodedOwner, + stringsHeap.Add(gp.Name), + gp.Kind is null ? 0 : AddTypeDefOrRef(gp.Kind)); + genericParamInfos.Add(gp, row); + } + + void AddGenericParamConstraints(IList gps) { + if (gps is null) + return; + int count = gps.Count; + for (int i = 0; i < count; i++) { + var gp = gps[i]; + if (gp is null) + continue; + uint rid = genericParamInfos.Rid(gp); + AddGenericParamConstraints(rid, gp.GenericParamConstraints); + } + } + + /// + /// Adds generic parameter constraints + /// + /// New rid of owner generic param + /// Its constraints + protected void AddGenericParamConstraints(uint gpRid, IList constraints) { + if (constraints is null) + return; + int count = constraints.Count; + for (int i = 0; i < count; i++) + AddGenericParamConstraint(gpRid, constraints[i]); + } + + /// + /// Adds a generic parameter constraint + /// + /// New rid of owner generic param + /// Generic parameter constraint + protected void AddGenericParamConstraint(uint gpRid, GenericParamConstraint gpc) { + if (gpc is null) { + Error("GenericParamConstraint is null"); + return; + } + var row = new RawGenericParamConstraintRow(gpRid, AddTypeDefOrRef(gpc.Constraint)); + genericParamConstraintInfos.Add(gpc, row); + } + + /// + /// Adds a InterfaceImpl row + /// + /// New rid of owner + /// All interfaces + protected void AddInterfaceImpls(uint typeDefRid, IList ifaces) { + int count = ifaces.Count; + for (int i = 0; i < count; i++) { + var iface = ifaces[i]; + if (iface is null) + continue; + var row = new RawInterfaceImplRow(typeDefRid, + AddTypeDefOrRef(iface.Interface)); + interfaceImplInfos.Add(iface, row); + } + } + + /// + /// Adds a FieldLayout row + /// + /// Owner field + protected void AddFieldLayout(FieldDef field) { + if (field is null || field.FieldOffset is null) + return; + var rid = GetRid(field); + var row = new RawFieldLayoutRow(field.FieldOffset.Value, rid); + fieldLayoutInfos.Add(field, row); + } + + /// + /// Adds a FieldMarshal row + /// + /// New owner token + /// Owner + protected void AddFieldMarshal(MDToken parent, IHasFieldMarshal hfm) { + if (hfm is null || hfm.MarshalType is null) + return; + var fieldMarshal = hfm.MarshalType; + if (!CodedToken.HasFieldMarshal.Encode(parent, out uint encodedParent)) { + Error("Can't encode HasFieldMarshal token 0x{0:X8}.", parent.Raw); + encodedParent = 0; + } + var row = new RawFieldMarshalRow(encodedParent, + blobHeap.Add(MarshalBlobWriter.Write(module, fieldMarshal, this, OptimizeCustomAttributeSerializedTypeNames))); + fieldMarshalInfos.Add(hfm, row); + } + + /// + /// Adds a FieldRVA row + /// + /// The field + protected void AddFieldRVA(FieldDef field) { + Debug.Assert(!isStandaloneDebugMetadata); + if (NoFieldData) + return; + if (field.RVA != 0 && KeepFieldRVA) { + uint rid = GetRid(field); + var row = new RawFieldRVARow((uint)field.RVA, rid); + fieldRVAInfos.Add(field, row); + } + else { + if (field is null || field.InitialValue is null) + return; + var ivBytes = field.InitialValue; + if (!VerifyFieldSize(field, ivBytes.Length)) + Error("Field '{0}' (0x{1:X8}) initial value size != size of field type.", field, field.MDToken.Raw); + uint rid = GetRid(field); + + uint alignment = ModuleWriterBase.DEFAULT_CONSTANTS_ALIGNMENT; + if (field.FieldType is TypeDefOrRefSig tdrSig && tdrSig.TypeDef?.ClassLayout is {} classLayout) + alignment = Math.Max(alignment, Utils.RoundToNextPowerOfTwo(classLayout.PackingSize)); + + var iv = constants.Add(new ByteArrayChunk(ivBytes, alignment), alignment); + fieldToInitialValue[field] = iv; + var row = new RawFieldRVARow(0, rid); + fieldRVAInfos.Add(field, row); + } + } + + static bool VerifyFieldSize(FieldDef field, int size) { + if (field is null) + return false; + var sig = field.FieldSig; + if (sig is null) + return false; + return field.GetFieldSize() == size; + } + + /// + /// Adds a ImplMap row + /// + /// New owner token + /// Owner + protected void AddImplMap(MDToken parent, IMemberForwarded mf) { + if (mf is null || mf.ImplMap is null) + return; + var implMap = mf.ImplMap; + if (!CodedToken.MemberForwarded.Encode(parent, out uint encodedParent)) { + Error("Can't encode MemberForwarded token 0x{0:X8}.", parent.Raw); + encodedParent = 0; + } + var row = new RawImplMapRow((ushort)implMap.Attributes, + encodedParent, + stringsHeap.Add(implMap.Name), + AddModuleRef(implMap.Module)); + implMapInfos.Add(mf, row); + } + + /// + /// Adds a Constant row + /// + /// New owner token + /// Owner + protected void AddConstant(MDToken parent, IHasConstant hc) { + if (hc is null || hc.Constant is null) + return; + var constant = hc.Constant; + if (!CodedToken.HasConstant.Encode(parent, out uint encodedParent)) { + Error("Can't encode HasConstant token 0x{0:X8}.", parent.Raw); + encodedParent = 0; + } + var row = new RawConstantRow((byte)constant.Type, 0, + encodedParent, + blobHeap.Add(GetConstantValueAsByteArray(constant.Type, constant.Value))); + hasConstantInfos.Add(hc, row); + } + + static readonly byte[] constantClassByteArray = new byte[4]; + static readonly byte[] constantDefaultByteArray = new byte[8]; + byte[] GetConstantValueAsByteArray(ElementType etype, object o) { + if (o is null) { + if (etype == ElementType.Class) + return constantClassByteArray; + Error("Constant is null"); + return constantDefaultByteArray; + } + + var typeCode = Type.GetTypeCode(o.GetType()); + switch (typeCode) { + case TypeCode.Boolean: + VerifyConstantType(etype, ElementType.Boolean); + return BitConverter.GetBytes((bool)o); + + case TypeCode.Char: + VerifyConstantType(etype, ElementType.Char); + return BitConverter.GetBytes((char)o); + + case TypeCode.SByte: + VerifyConstantType(etype, ElementType.I1); + return new byte[1] { (byte)(sbyte)o }; + + case TypeCode.Byte: + VerifyConstantType(etype, ElementType.U1); + return new byte[1] { (byte)o }; + + case TypeCode.Int16: + VerifyConstantType(etype, ElementType.I2); + return BitConverter.GetBytes((short)o); + + case TypeCode.UInt16: + VerifyConstantType(etype, ElementType.U2); + return BitConverter.GetBytes((ushort)o); + + case TypeCode.Int32: + VerifyConstantType(etype, ElementType.I4); + return BitConverter.GetBytes((int)o); + + case TypeCode.UInt32: + VerifyConstantType(etype, ElementType.U4); + return BitConverter.GetBytes((uint)o); + + case TypeCode.Int64: + VerifyConstantType(etype, ElementType.I8); + return BitConverter.GetBytes((long)o); + + case TypeCode.UInt64: + VerifyConstantType(etype, ElementType.U8); + return BitConverter.GetBytes((ulong)o); + + case TypeCode.Single: + VerifyConstantType(etype, ElementType.R4); + return BitConverter.GetBytes((float)o); + + case TypeCode.Double: + VerifyConstantType(etype, ElementType.R8); + return BitConverter.GetBytes((double)o); + + case TypeCode.String: + VerifyConstantType(etype, ElementType.String); + return Encoding.Unicode.GetBytes((string)o); + + default: + Error("Invalid constant type: {0}", typeCode); + return constantDefaultByteArray; + } + } + + void VerifyConstantType(ElementType realType, ElementType expectedType) { + if (realType != expectedType) + Error("Constant value's type is the wrong type: {0} != {1}", realType, expectedType); + } + + /// + /// Adds a DeclSecurity row + /// + /// New owner token + /// All DeclSecurity rows + protected void AddDeclSecurities(MDToken parent, IList declSecurities) { + if (declSecurities is null) + return; + if (!CodedToken.HasDeclSecurity.Encode(parent, out uint encodedParent)) { + Error("Can't encode HasDeclSecurity token 0x{0:X8}.", parent.Raw); + encodedParent = 0; + } + var bwctx = AllocBinaryWriterContext(); + int count = declSecurities.Count; + for (int i = 0; i < count; i++) { + var decl = declSecurities[i]; + if (decl is null) + continue; + var row = new RawDeclSecurityRow((short)decl.Action, + encodedParent, + blobHeap.Add(DeclSecurityWriter.Write(module, decl.SecurityAttributes, this, OptimizeCustomAttributeSerializedTypeNames, bwctx))); + declSecurityInfos.Add(decl, row); + } + Free(ref bwctx); + } + + /// + /// Adds MethodSemantics rows + /// + /// Event + protected void AddMethodSemantics(EventDef evt) { + if (evt is null) { + Error("Event is null"); + return; + } + uint rid = GetRid(evt); + if (rid == 0) + return; + var token = new MDToken(Table.Event, rid); + AddMethodSemantics(token, evt.AddMethod, MethodSemanticsAttributes.AddOn); + AddMethodSemantics(token, evt.RemoveMethod, MethodSemanticsAttributes.RemoveOn); + AddMethodSemantics(token, evt.InvokeMethod, MethodSemanticsAttributes.Fire); + AddMethodSemantics(token, evt.OtherMethods, MethodSemanticsAttributes.Other); + } + + /// + /// Adds MethodSemantics rows + /// + /// Property + protected void AddMethodSemantics(PropertyDef prop) { + if (prop is null) { + Error("Property is null"); + return; + } + uint rid = GetRid(prop); + if (rid == 0) + return; + var token = new MDToken(Table.Property, rid); + AddMethodSemantics(token, prop.GetMethods, MethodSemanticsAttributes.Getter); + AddMethodSemantics(token, prop.SetMethods, MethodSemanticsAttributes.Setter); + AddMethodSemantics(token, prop.OtherMethods, MethodSemanticsAttributes.Other); + } + + void AddMethodSemantics(MDToken owner, IList methods, MethodSemanticsAttributes attrs) { + if (methods is null) + return; + int count = methods.Count; + for (int i = 0; i < count; i++) + AddMethodSemantics(owner, methods[i], attrs); + } + + void AddMethodSemantics(MDToken owner, MethodDef method, MethodSemanticsAttributes flags) { + if (method is null) + return; + uint methodRid = GetRid(method); + if (methodRid == 0) + return; + if (!CodedToken.HasSemantic.Encode(owner, out uint encodedOwner)) { + Error("Can't encode HasSemantic token 0x{0:X8}.", owner.Raw); + encodedOwner = 0; + } + var row = new RawMethodSemanticsRow((ushort)flags, methodRid, encodedOwner); + methodSemanticsInfos.Add(method, row); + } + + void AddMethodImpls(MethodDef method, IList overrides) { + if (overrides is null) + return; + if (method.DeclaringType is null) { + Error("Method declaring type is null"); + return; + } + if (overrides.Count != 0) { + uint rid = GetRid(method.DeclaringType); + int count = overrides.Count; + for (int i = 0; i < count; i++) { + var ovr = overrides[i]; + var row = new RawMethodImplRow(rid, + AddMethodDefOrRef(ovr.MethodBody), + AddMethodDefOrRef(ovr.MethodDeclaration)); + methodImplInfos.Add(method, row); + } + } + } + + /// + /// Adds a ClassLayout row + /// + /// Type + protected void AddClassLayout(TypeDef type) { + if (type is null || type.ClassLayout is null) + return; + var rid = GetRid(type); + var classLayout = type.ClassLayout; + var row = new RawClassLayoutRow(classLayout.PackingSize, classLayout.ClassSize, rid); + classLayoutInfos.Add(type, row); + } + + void AddResources(IList resources) { + if (NoDotNetResources) + return; + if (resources is null) + return; + int count = resources.Count; + for (int i = 0; i < count; i++) + AddResource(resources[i]); + } + + void AddResource(Resource resource) { + Debug.Assert(!NoDotNetResources); + if (resource is EmbeddedResource er) { + AddEmbeddedResource(er); + return; + } + + if (resource is AssemblyLinkedResource alr) { + AddAssemblyLinkedResource(alr); + return; + } + + if (resource is LinkedResource lr) { + AddLinkedResource(lr); + return; + } + + if (resource is null) + Error("Resource is null"); + else + Error("Invalid resource type: '{0}'.", resource.GetType()); + } + + uint AddEmbeddedResource(EmbeddedResource er) { + Debug.Assert(!isStandaloneDebugMetadata); + Debug.Assert(!NoDotNetResources); + if (er is null) { + Error("EmbeddedResource is null"); + return 0; + } + if (manifestResourceInfos.TryGetRid(er, out uint rid)) + return rid; + var row = new RawManifestResourceRow(netResources.NextOffset, + (uint)er.Attributes, + stringsHeap.Add(er.Name), + 0); + rid = tablesHeap.ManifestResourceTable.Add(row); + manifestResourceInfos.Add(er, rid); + embeddedResourceToByteArray[er] = netResources.Add(er.CreateReader()); + AddCustomAttributes(Table.ManifestResource, rid, er); + AddCustomDebugInformationList(Table.ManifestResource, rid, er); + return rid; + } + + uint AddAssemblyLinkedResource(AssemblyLinkedResource alr) { + Debug.Assert(!NoDotNetResources); + if (alr is null) { + Error("AssemblyLinkedResource is null"); + return 0; + } + if (manifestResourceInfos.TryGetRid(alr, out uint rid)) + return rid; + var row = new RawManifestResourceRow(0, + (uint)alr.Attributes, + stringsHeap.Add(alr.Name), + AddImplementation(alr.Assembly)); + rid = tablesHeap.ManifestResourceTable.Add(row); + manifestResourceInfos.Add(alr, rid); + AddCustomAttributes(Table.ManifestResource, rid, alr); + AddCustomDebugInformationList(Table.ManifestResource, rid, alr); + return rid; + } + + uint AddLinkedResource(LinkedResource lr) { + Debug.Assert(!NoDotNetResources); + if (lr is null) { + Error("LinkedResource is null"); + return 0; + } + if (manifestResourceInfos.TryGetRid(lr, out uint rid)) + return rid; + var row = new RawManifestResourceRow(0, + (uint)lr.Attributes, + stringsHeap.Add(lr.Name), + AddImplementation(lr.File)); + rid = tablesHeap.ManifestResourceTable.Add(row); + manifestResourceInfos.Add(lr, rid); + AddCustomAttributes(Table.ManifestResource, rid, lr); + AddCustomDebugInformationList(Table.ManifestResource, rid, lr); + return rid; + } + + /// + /// Adds a File row + /// + /// File + /// Its new rid + protected uint AddFile(FileDef file) { + if (file is null) { + Error("FileDef is null"); + return 0; + } + if (fileDefInfos.TryGetRid(file, out uint rid)) + return rid; + var row = new RawFileRow((uint)file.Flags, + stringsHeap.Add(file.Name), + blobHeap.Add(file.HashValue)); //TODO: Re-calculate the hash value if possible + rid = tablesHeap.FileTable.Add(row); + fileDefInfos.Add(file, rid); + AddCustomAttributes(Table.File, rid, file); + AddCustomDebugInformationList(Table.File, rid, file); + return rid; + } + + /// + /// Adds a ExportedType row + /// + /// Exported type + /// Its new rid + protected uint AddExportedType(ExportedType et) { + if (et is null) { + Error("ExportedType is null"); + return 0; + } + if (exportedTypeInfos.TryGetRid(et, out uint rid)) + return rid; + exportedTypeInfos.Add(et, 0); // Prevent inf recursion + var row = new RawExportedTypeRow((uint)et.Attributes, + et.TypeDefId, //TODO: Should be updated with the new rid + stringsHeap.Add(et.TypeName), + stringsHeap.Add(et.TypeNamespace), + AddImplementation(et.Implementation)); + rid = tablesHeap.ExportedTypeTable.Add(row); + exportedTypeInfos.SetRid(et, rid); + AddCustomAttributes(Table.ExportedType, rid, et); + AddCustomDebugInformationList(Table.ExportedType, rid, et); + return rid; + } + + /// + /// Gets a #Blob offset of a type signature + /// + /// Type sig + /// Extra data to append the signature if + /// is true. + /// #Blob offset + protected uint GetSignature(TypeSig ts, byte[] extraData) { + byte[] blob; + if (ts is null) { + Error("TypeSig is null"); + blob = null; + } + else { + var bwctx = AllocBinaryWriterContext(); + blob = SignatureWriter.Write(this, ts, bwctx); + Free(ref bwctx); + } + AppendExtraData(ref blob, extraData); + return blobHeap.Add(blob); + } + + /// + /// Gets a #Blob offset of a calling convention signature + /// + /// Signature + /// #Blob offset + protected uint GetSignature(CallingConventionSig sig) { + if (sig is null) { + Error("CallingConventionSig is null"); + return 0; + } + + var bwctx = AllocBinaryWriterContext(); + var blob = SignatureWriter.Write(this, sig, bwctx); + Free(ref bwctx); + AppendExtraData(ref blob, sig.ExtraData); + return blobHeap.Add(blob); + } + + void AppendExtraData(ref byte[] blob, byte[] extraData) { + if (PreserveExtraSignatureData && extraData is not null && extraData.Length > 0) { + int blen = blob is null ? 0 : blob.Length; + Array.Resize(ref blob, blen + extraData.Length); + Array.Copy(extraData, 0, blob, blen, extraData.Length); + } + } + + /// + /// Adds a CustomAttribute row + /// + /// Owner table + /// New owner rid + /// Onwer + protected void AddCustomAttributes(Table table, uint rid, IHasCustomAttribute hca) => AddCustomAttributes(table, rid, hca.CustomAttributes); + + void AddCustomAttributes(Table table, uint rid, CustomAttributeCollection caList) { + var token = new MDToken(table, rid); + int count = caList.Count; + for (int i = 0; i < count; i++) + AddCustomAttribute(token, caList[i]); + } + + void AddCustomAttribute(MDToken token, CustomAttribute ca) { + if (ca is null) { + Error("Custom attribute is null"); + return; + } + if (!CodedToken.HasCustomAttribute.Encode(token, out uint encodedToken)) { + Error("Can't encode HasCustomAttribute token 0x{0:X8}.", token.Raw); + encodedToken = 0; + } + var bwctx = AllocBinaryWriterContext(); + var caBlob = CustomAttributeWriter.Write(this, ca, bwctx); + Free(ref bwctx); + var row = new RawCustomAttributeRow(encodedToken, + AddCustomAttributeType(ca.Constructor), + blobHeap.Add(caBlob)); + customAttributeInfos.Add(ca, row); + } + + void AddCustomDebugInformationList(MethodDef method, uint rid, uint localVarSigToken) { + Debug.Assert(debugMetadata is not null); + if (debugMetadata is null) + return; + var serializerMethodContext = AllocSerializerMethodContext(); + serializerMethodContext.SetBody(method); + if (method.CustomDebugInfos.Count != 0) + AddCustomDebugInformationCore(serializerMethodContext, Table.Method, rid, method.CustomDebugInfos); + AddMethodDebugInformation(method, rid, localVarSigToken); + Free(ref serializerMethodContext); + } + + void AddMethodDebugInformation(MethodDef method, uint rid, uint localVarSigToken) { + Debug.Assert(debugMetadata is not null); + var body = method.Body; + if (body is null) + return; + + GetSingleDocument(body, out var singleDoc, out var firstDoc, out bool hasNoSeqPoints); + if (hasNoSeqPoints) + return; + + var bwctx = AllocBinaryWriterContext(); + var outStream = bwctx.OutStream; + var writer = bwctx.Writer; + outStream.SetLength(0); + outStream.Position = 0; + + writer.WriteCompressedUInt32(localVarSigToken); + if (singleDoc is null) + writer.WriteCompressedUInt32(VerifyGetRid(firstDoc)); + + var instrs = body.Instructions; + var currentDoc = firstDoc; + uint ilOffset = uint.MaxValue; + int line = -1, column = 0; + uint instrOffset = 0; + Instruction instr = null; + for (int i = 0; i < instrs.Count; i++, instrOffset += (uint)instr.GetSize()) { + instr = instrs[i]; + var seqPoint = instr.SequencePoint; + if (seqPoint is null) + continue; + if (seqPoint.Document is null) { + Error("PDB document is null"); + return; + } + if (currentDoc != seqPoint.Document) { + // document-record + + currentDoc = seqPoint.Document; + writer.WriteCompressedUInt32(0); + writer.WriteCompressedUInt32(VerifyGetRid(currentDoc)); + } + + // SequencePointRecord + + if (ilOffset == uint.MaxValue) + writer.WriteCompressedUInt32(instrOffset); + else + writer.WriteCompressedUInt32(instrOffset - ilOffset); + ilOffset = instrOffset; + + if (seqPoint.StartLine == SequencePointConstants.HIDDEN_LINE && seqPoint.EndLine == SequencePointConstants.HIDDEN_LINE) { + // hidden-sequence-point-record + + writer.WriteCompressedUInt32(0); + writer.WriteCompressedUInt32(0); + } + else { + // sequence-point-record + + uint dlines = (uint)(seqPoint.EndLine - seqPoint.StartLine); + int dcolumns = seqPoint.EndColumn - seqPoint.StartColumn; + writer.WriteCompressedUInt32(dlines); + if (dlines == 0) + writer.WriteCompressedUInt32((uint)dcolumns); + else + writer.WriteCompressedInt32(dcolumns); + + if (line < 0) { + writer.WriteCompressedUInt32((uint)seqPoint.StartLine); + writer.WriteCompressedUInt32((uint)seqPoint.StartColumn); + } + else { + writer.WriteCompressedInt32(seqPoint.StartLine - line); + writer.WriteCompressedInt32(seqPoint.StartColumn - column); + } + line = seqPoint.StartLine; + column = seqPoint.StartColumn; + } + } + + var seqPointsBlob = outStream.ToArray(); + var row = new RawMethodDebugInformationRow(singleDoc is null ? 0 : AddPdbDocument(singleDoc), debugMetadata.blobHeap.Add(seqPointsBlob)); + debugMetadata.tablesHeap.MethodDebugInformationTable[rid] = row; + debugMetadata.methodDebugInformationInfosUsed = true; + Free(ref bwctx); + } + + uint VerifyGetRid(PdbDocument doc) { + Debug.Assert(debugMetadata is not null); + if (!debugMetadata.pdbDocumentInfos.TryGetRid(doc, out uint rid)) { + Error("PDB document has been removed"); + return 0; + } + return rid; + } + + static void GetSingleDocument(CilBody body, out PdbDocument singleDoc, out PdbDocument firstDoc, out bool hasNoSeqPoints) { + var instrs = body.Instructions; + int docCount = 0; + singleDoc = null; + firstDoc = null; + for (int i = 0; i < instrs.Count; i++) { + var seqPt = instrs[i].SequencePoint; + if (seqPt is null) + continue; + var doc = seqPt.Document; + if (doc is null) + continue; + if (firstDoc is null) + firstDoc = doc; + if (singleDoc != doc) { + singleDoc = doc; + docCount++; + if (docCount > 1) + break; + } + } + hasNoSeqPoints = docCount == 0; + if (docCount != 1) + singleDoc = null; + } + + /// + /// Adds a CustomDebugInformation row + /// + /// Owner table + /// New owner rid + /// Onwer + protected void AddCustomDebugInformationList(Table table, uint rid, IHasCustomDebugInformation hcdi) { + Debug.Assert(table != Table.Method); + if (debugMetadata is null) + return; + if (hcdi.CustomDebugInfos.Count == 0) + return; + var serializerMethodContext = AllocSerializerMethodContext(); + serializerMethodContext.SetBody(null); + AddCustomDebugInformationCore(serializerMethodContext, table, rid, hcdi.CustomDebugInfos); + Free(ref serializerMethodContext); + } + + void AddCustomDebugInformationList(Table table, uint rid, IList cdis) { + Debug.Assert(table != Table.Method); + if (debugMetadata is null) + return; + if (cdis.Count == 0) + return; + var serializerMethodContext = AllocSerializerMethodContext(); + serializerMethodContext.SetBody(null); + AddCustomDebugInformationCore(serializerMethodContext, table, rid, cdis); + Free(ref serializerMethodContext); + } + + void AddCustomDebugInformationCore(SerializerMethodContext serializerMethodContext, Table table, uint rid, IList cdis) { + Debug.Assert(debugMetadata is not null); + Debug.Assert(cdis.Count != 0); + + var token = new MDToken(table, rid); + if (!CodedToken.HasCustomDebugInformation.Encode(token, out uint encodedToken)) { + Error("Couldn't encode HasCustomDebugInformation token 0x{0:X8}.", token.Raw); + return; + } + + for (int i = 0; i < cdis.Count; i++) { + var cdi = cdis[i]; + if (cdi is null) { + Error("Custom debug info is null"); + continue; + } + + AddCustomDebugInformation(serializerMethodContext, token.Raw, encodedToken, cdi); + } + } + + void AddCustomDebugInformation(SerializerMethodContext serializerMethodContext, uint token, uint encodedToken, PdbCustomDebugInfo cdi) { + Debug.Assert(debugMetadata is not null); + + switch (cdi.Kind) { + case PdbCustomDebugInfoKind.UsingGroups: + case PdbCustomDebugInfoKind.ForwardMethodInfo: + case PdbCustomDebugInfoKind.ForwardModuleInfo: + case PdbCustomDebugInfoKind.StateMachineTypeName: + case PdbCustomDebugInfoKind.DynamicLocals: + case PdbCustomDebugInfoKind.TupleElementNames: + case PdbCustomDebugInfoKind.SourceServer: + // These are Windows PDB CDIs + Error("Unsupported custom debug info {0}", cdi.Kind); + break; + + case PdbCustomDebugInfoKind.StateMachineHoistedLocalScopes: + case PdbCustomDebugInfoKind.EditAndContinueLocalSlotMap: + case PdbCustomDebugInfoKind.EditAndContinueLambdaMap: + case PdbCustomDebugInfoKind.Unknown: + case PdbCustomDebugInfoKind.TupleElementNames_PortablePdb: + case PdbCustomDebugInfoKind.DefaultNamespace: + case PdbCustomDebugInfoKind.DynamicLocalVariables: + case PdbCustomDebugInfoKind.EmbeddedSource: + case PdbCustomDebugInfoKind.SourceLink: + case PdbCustomDebugInfoKind.CompilationMetadataReferences: + case PdbCustomDebugInfoKind.CompilationOptions: + case PdbCustomDebugInfoKind.TypeDefinitionDocuments: + case PdbCustomDebugInfoKind.EditAndContinueStateMachineStateMap: + case PdbCustomDebugInfoKind.PrimaryConstructorInformationBlob: + AddCustomDebugInformationCore(serializerMethodContext, encodedToken, cdi, cdi.Guid); + break; + + case PdbCustomDebugInfoKind.AsyncMethod: + // This is a portable PDB pseudo CDI + AddCustomDebugInformationCore(serializerMethodContext, encodedToken, cdi, CustomDebugInfoGuids.AsyncMethodSteppingInformationBlob); + AddStateMachineMethod(cdi, token, ((PdbAsyncMethodCustomDebugInfo)cdi).KickoffMethod); + break; + + case PdbCustomDebugInfoKind.IteratorMethod: + // This is a portable PDB pseudo CDI + AddStateMachineMethod(cdi, token, ((PdbIteratorMethodCustomDebugInfo)cdi).KickoffMethod); + break; + + default: + Error("Unknown custom debug info {0}.", cdi.Kind); + break; + } + } + + void AddStateMachineMethod(PdbCustomDebugInfo cdi, uint moveNextMethodToken, MethodDef kickoffMethod) { + Debug.Assert(new MDToken(moveNextMethodToken).Table == Table.Method); + Debug.Assert(debugMetadata is not null); + if (kickoffMethod is null) { + Error("KickoffMethod is null"); + return; + } + var row = new RawStateMachineMethodRow(new MDToken(moveNextMethodToken).Rid, GetRid(kickoffMethod)); + debugMetadata.stateMachineMethodInfos.Add(cdi, row); + } + + void AddCustomDebugInformationCore(SerializerMethodContext serializerMethodContext, uint encodedToken, PdbCustomDebugInfo cdi, Guid cdiGuid) { + Debug.Assert(debugMetadata is not null); + + var bwctx = AllocBinaryWriterContext(); + var cdiBlob = PortablePdbCustomDebugInfoWriter.Write(this, serializerMethodContext, this, cdi, bwctx); + Debug.Assert(cdiGuid != Guid.Empty); + Free(ref bwctx); + var row = new RawCustomDebugInformationRow(encodedToken, + debugMetadata.guidHeap.Add(cdiGuid), + debugMetadata.blobHeap.Add(cdiBlob)); + debugMetadata.customDebugInfos.Add(cdi, row); + } + + void InitializeMethodDebugInformation() { + if (debugMetadata is null) + return; + int numMethods = NumberOfMethods; + for (int i = 0; i < numMethods; i++) + debugMetadata.tablesHeap.MethodDebugInformationTable.Create(new RawMethodDebugInformationRow()); + } + + void AddPdbDocuments() { + if (debugMetadata is null) + return; + foreach (var doc in module.PdbState.Documents) + AddPdbDocument(doc); + } + + uint AddPdbDocument(PdbDocument doc) { + Debug.Assert(debugMetadata is not null); + if (doc is null) { + Error("PdbDocument is null"); + return 0; + } + if (debugMetadata.pdbDocumentInfos.TryGetRid(doc, out uint rid)) + return rid; + var row = new RawDocumentRow(GetDocumentNameBlobOffset(doc.Url), + debugMetadata.guidHeap.Add(doc.CheckSumAlgorithmId), + debugMetadata.blobHeap.Add(doc.CheckSum), + debugMetadata.guidHeap.Add(doc.Language)); + rid = debugMetadata.tablesHeap.DocumentTable.Add(row); + debugMetadata.pdbDocumentInfos.Add(doc, rid); + AddCustomDebugInformationList(Table.Document, rid, doc.CustomDebugInfos); + return rid; + } + + uint GetDocumentNameBlobOffset(string name) { + Debug.Assert(debugMetadata is not null); + if (name is null) { + Error("Document name is null"); + name = string.Empty; + } + + var bwctx = AllocBinaryWriterContext(); + var outStream = bwctx.OutStream; + var writer = bwctx.Writer; + outStream.SetLength(0); + outStream.Position = 0; + var parts = name.Split(directorySeparatorCharArray); + if (parts.Length == 1) + writer.WriteByte(0); + else + writer.WriteBytes(directorySeparatorCharUtf8); + for (int i = 0; i < parts.Length; i++) { + var part = parts[i]; + uint partOffset = debugMetadata.blobHeap.Add(Encoding.UTF8.GetBytes(part)); + writer.WriteCompressedUInt32(partOffset); + } + + var res = debugMetadata.blobHeap.Add(outStream.ToArray()); + Free(ref bwctx); + return res; + } + static readonly byte[] directorySeparatorCharUtf8 = Encoding.UTF8.GetBytes(Path.DirectorySeparatorChar.ToString()); + static readonly char[] directorySeparatorCharArray = new char[] { Path.DirectorySeparatorChar }; + + uint AddImportScope(PdbImportScope scope) { + Debug.Assert(debugMetadata is not null); + if (scope is null) + return 0; + if (debugMetadata.importScopeInfos.TryGetRid(scope, out uint rid)) { + if (rid == 0) + Error("PdbImportScope has an infinite Parent loop"); + return rid; + } + debugMetadata.importScopeInfos.Add(scope, 0); // Prevent inf recursion + + var bwctx = AllocBinaryWriterContext(); + var outStream = bwctx.OutStream; + var writer = bwctx.Writer; + outStream.SetLength(0); + outStream.Position = 0; + ImportScopeBlobWriter.Write(this, this, writer, debugMetadata.blobHeap, scope.Imports); + var importsData = outStream.ToArray(); + Free(ref bwctx); + + var row = new RawImportScopeRow(AddImportScope(scope.Parent), debugMetadata.blobHeap.Add(importsData)); + rid = debugMetadata.tablesHeap.ImportScopeTable.Add(row); + debugMetadata.importScopeInfos.SetRid(scope, rid); + + AddCustomDebugInformationList(Table.ImportScope, rid, scope.CustomDebugInfos); + return rid; + } + + void AddLocalVariable(PdbLocal local) { + Debug.Assert(debugMetadata is not null); + if (local is null) { + Error("PDB local is null"); + return; + } + var row = new RawLocalVariableRow((ushort)local.Attributes, (ushort)local.Index, debugMetadata.stringsHeap.Add(local.Name)); + uint rid = debugMetadata.tablesHeap.LocalVariableTable.Create(row); + debugMetadata.localVariableInfos.Add(local, rid); + AddCustomDebugInformationList(Table.LocalVariable, rid, local.CustomDebugInfos); + } + + void AddLocalConstant(PdbConstant constant) { + Debug.Assert(debugMetadata is not null); + if (constant is null) { + Error("PDB constant is null"); + return; + } + + var bwctx = AllocBinaryWriterContext(); + var outStream = bwctx.OutStream; + var writer = bwctx.Writer; + outStream.SetLength(0); + outStream.Position = 0; + LocalConstantSigBlobWriter.Write(this, this, writer, constant.Type, constant.Value); + var signature = outStream.ToArray(); + Free(ref bwctx); + + var row = new RawLocalConstantRow(debugMetadata.stringsHeap.Add(constant.Name), debugMetadata.blobHeap.Add(signature)); + uint rid = debugMetadata.tablesHeap.LocalConstantTable.Create(row); + debugMetadata.localConstantInfos.Add(constant, rid); + AddCustomDebugInformationList(Table.LocalConstant, rid, constant.CustomDebugInfos); + } + + /// + /// Writes the portable PDB to . + /// + /// Output stream + /// Entry point token + /// Updated with the offset of the 20-byte PDB ID. The caller is responsible for initializing it with the PDB ID + internal void WritePortablePdb(Stream output, uint entryPointToken, out long pdbIdOffset) { + if (debugMetadata is null) + throw new InvalidOperationException(); + var pdbHeap = debugMetadata.PdbHeap; + pdbHeap.EntryPoint = entryPointToken; + + tablesHeap.GetSystemTableRows(out ulong systemTablesMask, pdbHeap.TypeSystemTableRows); + debugMetadata.tablesHeap.SetSystemTableRows(pdbHeap.TypeSystemTableRows); + if (!debugMetadata.methodDebugInformationInfosUsed) + debugMetadata.tablesHeap.MethodDebugInformationTable.Reset(); + pdbHeap.ReferencedTypeSystemTables = systemTablesMask; + var writer = new DataWriter(output); + debugMetadata.OnBeforeSetOffset(); + debugMetadata.SetOffset(0, 0); + debugMetadata.GetFileLength(); + debugMetadata.VerifyWriteTo(writer); + pdbIdOffset = (long)pdbHeap.PdbIdOffset; + } + + /// + uint ISignatureWriterHelper.ToEncodedToken(ITypeDefOrRef typeDefOrRef) => AddTypeDefOrRef(typeDefOrRef); + + /// + void IWriterError.Error(string message) => Error(message); + + /// + void IWriterError2.Error(string message, params object[] args) => Error(message, args); + + /// + bool IFullNameFactoryHelper.MustUseAssemblyName(IType type) => + FullNameFactory.MustUseAssemblyName(module, type, OptimizeCustomAttributeSerializedTypeNames); + + /// + /// Called before any other methods + /// + protected virtual void Initialize() { + } + + /// + /// Gets all s that should be saved in the meta data + /// + protected abstract TypeDef[] GetAllTypeDefs(); + + /// + /// Initializes TypeDef rids and creates raw rows, but does not initialize + /// any columns. + /// + protected abstract void AllocateTypeDefRids(); + + /// + /// Allocates Field, Method, Property, Event, Param: + /// rid and raw row, but doesn't initialize the raw row. + /// Initializes TypeDef columns: FieldList, MethodList. + /// Initializes Method column: ParamList. + /// Initializes and . + /// + protected abstract void AllocateMemberDefRids(); + + /// + /// Adds a . Its custom attributes are also added. + /// + /// Type reference + /// Its new rid + protected abstract uint AddTypeRef(TypeRef tr); + + /// + /// Adds a . Its custom attributes are also added. + /// + /// Type spec + /// Its new rid + protected abstract uint AddTypeSpec(TypeSpec ts); + + /// + /// Adds a . Its custom attributes are also added. + /// + /// Member ref + /// Its new rid + protected abstract uint AddMemberRef(MemberRef mr); + + /// + /// Adds a . Its custom attributes are also added. + /// + /// Stand alone sig + /// Its new rid + protected abstract uint AddStandAloneSig(StandAloneSig sas); + + /// + /// Adds a . Its custom attributes are also added. + /// + /// Method spec + /// Its new rid + protected abstract uint AddMethodSpec(MethodSpec ms); + + /// + /// Called before sorting the CustomAttribute table. This is the last time anything + /// can be inserted into this table. + /// + protected virtual void BeforeSortingCustomAttributes() { + } + + /// + /// Called after everything has been initialized. The sub class can initialize more + /// rows if necessary or do nothing. After this method has been called, nothing else + /// can be added. + /// + protected virtual void EverythingInitialized() { + } + + const uint HEAP_ALIGNMENT = 4; + + bool IReuseChunk.CanReuse(RVA origRva, uint origSize) { + // The caller should've called SetOffset() so we know our final size + Debug.Assert(length != 0); + if (length == 0) + throw new InvalidOperationException(); + return length <= origSize; + } + + /// + /// Should be called before all chunks get an RVA + /// + internal void OnBeforeSetOffset() => + stringsHeap.AddOptimizedStringsAndSetReadOnly(); + + /// + public void SetOffset(FileOffset offset, RVA rva) { + // This method can be called twice by NativeModuleWriter. It needs to know the size + // of the final metadata. If it fits in the old location, the new MD will be written + // there (smaller file size). If the new MD doesn't fit in the old location, this + // method gets called a second time with the updated offset + rva. + bool initAll = this.offset == 0; + this.offset = offset; + this.rva = rva; + + if (initAll) { + // #Strings heap is initialized in OnBeforeSetOffset() + blobHeap.SetReadOnly(); + guidHeap.SetReadOnly(); + tablesHeap.SetReadOnly(); + pdbHeap.SetReadOnly(); + tablesHeap.BigStrings = stringsHeap.IsBig; + tablesHeap.BigBlob = blobHeap.IsBig; + tablesHeap.BigGuid = guidHeap.IsBig; + metadataHeader.Heaps = GetHeaps(); + } + + metadataHeader.SetOffset(offset, rva); + uint len = metadataHeader.GetFileLength(); + offset += len; + rva += len; + + foreach (var heap in metadataHeader.Heaps) { + offset = offset.AlignUp(HEAP_ALIGNMENT); + rva = rva.AlignUp(HEAP_ALIGNMENT); + heap.SetOffset(offset, rva); + len = heap.GetFileLength(); + offset += len; + rva += len; + } + Debug.Assert(initAll || length == rva - this.rva); + if (!(initAll || length == rva - this.rva)) + throw new InvalidOperationException(); + length = rva - this.rva; + + if (!isStandaloneDebugMetadata && initAll) + UpdateMethodAndFieldRvas(); + } + + internal void UpdateMethodAndFieldRvas() { + UpdateMethodRvas(); + UpdateFieldRvas(); + } + + IList GetHeaps() { + var heaps = new List(); + + if (isStandaloneDebugMetadata) { + heaps.Add(pdbHeap); + heaps.Add(tablesHeap); + if (!stringsHeap.IsEmpty) + heaps.Add(stringsHeap); + if (!usHeap.IsEmpty) + heaps.Add(usHeap); + if (!guidHeap.IsEmpty) + heaps.Add(guidHeap); + if (!blobHeap.IsEmpty) + heaps.Add(blobHeap); + } + else { + heaps.Add(tablesHeap); + if (!stringsHeap.IsEmpty || AlwaysCreateStringsHeap) + heaps.Add(stringsHeap); + if (!usHeap.IsEmpty || AlwaysCreateUSHeap) + heaps.Add(usHeap); + if (!guidHeap.IsEmpty || AlwaysCreateGuidHeap) + heaps.Add(guidHeap); + if (!blobHeap.IsEmpty || AlwaysCreateBlobHeap) + heaps.Add(blobHeap); + + heaps.AddRange(options.CustomHeaps); + options.RaiseMetadataHeapsAdded(new MetadataHeapsAddedEventArgs(this, heaps)); + } + + return heaps; + } + + /// + public uint GetFileLength() => length; + + /// + public uint GetVirtualSize() => GetFileLength(); + + /// + public uint CalculateAlignment() => 0; + + /// + public void WriteTo(DataWriter writer) { + var rva2 = rva; + metadataHeader.VerifyWriteTo(writer); + rva2 += metadataHeader.GetFileLength(); + + foreach (var heap in metadataHeader.Heaps) { + writer.WriteZeroes((int)(rva2.AlignUp(HEAP_ALIGNMENT) - rva2)); + rva2 = rva2.AlignUp(HEAP_ALIGNMENT); + heap.VerifyWriteTo(writer); + rva2 += heap.GetFileLength(); + } + } + + /// + /// Sorts the s + /// + /// All s + /// A sorted list + protected static List Sort(IEnumerable pds) { + var sorted = new List(pds); + sorted.Sort((a, b) => { + if (a is null) + return -1; + if (b is null) + return 1; + return a.Sequence.CompareTo(b.Sequence); + }); + return sorted; + } + + DataWriterContext AllocBinaryWriterContext() { + if (binaryWriterContexts.Count == 0) + return new DataWriterContext(); + var res = binaryWriterContexts[binaryWriterContexts.Count - 1]; + binaryWriterContexts.RemoveAt(binaryWriterContexts.Count - 1); + return res; + } + + void Free(ref DataWriterContext ctx) { + binaryWriterContexts.Add(ctx); + ctx = null; + } + + SerializerMethodContext AllocSerializerMethodContext() { + if (serializerMethodContexts.Count == 0) + return new SerializerMethodContext(this); + var res = serializerMethodContexts[serializerMethodContexts.Count - 1]; + serializerMethodContexts.RemoveAt(serializerMethodContexts.Count - 1); + return res; + } + + void Free(ref SerializerMethodContext ctx) { + serializerMethodContexts.Add(ctx); + ctx = null; + } + } +} diff --git a/src/DotNet/Writer/MetadataErrorContext.cs b/src/DotNet/Writer/MetadataErrorContext.cs new file mode 100644 index 000000000..dee31d720 --- /dev/null +++ b/src/DotNet/Writer/MetadataErrorContext.cs @@ -0,0 +1,72 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Text; + +namespace dnlib.DotNet.Writer { + sealed class MetadataErrorContext { + sealed class ErrorSource : IDisposable { + MetadataErrorContext context; + readonly ErrorSource originalValue; + + public object Value { get; } + + public ErrorSource(MetadataErrorContext context, object value) { + this.context = context; + Value = value; + originalValue = context.source; + } + + public void Dispose() { + if (context is null) + return; + context.source = originalValue; + context = null; + } + } + + ErrorSource source; + + public MetadataEvent Event { get; set; } + + public IDisposable SetSource(object source) => this.source = new ErrorSource(this, source); + + public void Append(string errorLevel, ref string message, ref object[] args) { + int count = 1; + var stringSource = source?.Value as string; + var tokenSource = source?.Value as IMDTokenProvider; + if (tokenSource is not null) + count += 2; + int ctxArgIndex = args.Length; + + var newMessage = new StringBuilder(message); + var newArgs = new object[args.Length + count]; + Array.Copy(args, 0, newArgs, 0, args.Length); + + if (newMessage.Length != 0 && newMessage[newMessage.Length - 1] != '.') + newMessage.Append('.'); + newMessage.AppendFormat(" {0} occurred after metadata event {{{1}}}", errorLevel, ctxArgIndex); + newArgs[ctxArgIndex] = Event; + + if (tokenSource is not null) { + string sourceType = tokenSource switch { + TypeDef => "type", + FieldDef => "field", + MethodDef => "method", + EventDef => "event", + PropertyDef => "property", + _ => "???" + }; + newMessage.AppendFormat(" during writing {0} '{{{1}}}' (0x{{{2}:X8}})", sourceType, ctxArgIndex + 1, ctxArgIndex + 2); + newArgs[ctxArgIndex + 1] = tokenSource; + newArgs[ctxArgIndex + 2] = tokenSource.MDToken.Raw; + } + else if (stringSource is not null) { + newMessage.AppendFormat(" during writing {0}", stringSource); + } + + message = newMessage.Append('.').ToString(); + args = newArgs; + } + } +} diff --git a/src/DotNet/Writer/MetadataEvent.cs b/src/DotNet/Writer/MetadataEvent.cs new file mode 100644 index 000000000..0e9068778 --- /dev/null +++ b/src/DotNet/Writer/MetadataEvent.cs @@ -0,0 +1,84 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.DotNet.Writer { + /// + /// All events + /// + public enum MetadataEvent { + /// + /// Creating the tables has just begun + /// + BeginCreateTables, + + /// + /// Before allocating all TypeDef RIDs + /// + AllocateTypeDefRids, + + /// + /// Before allocating all MemberDef RIDs + /// + AllocateMemberDefRids, + + /// + /// The rids of types, fields, methods, events, properties and parameters are + /// now known. + /// + MemberDefRidsAllocated, + + /// + /// The tables and rows of all types, fields, methods, events, properties and parameters + /// have been initialized. Method body RVAs are still not known, and no method has been + /// written yet. + /// + MemberDefsInitialized, + + /// + /// Before sorting most tables + /// + BeforeSortTables, + + /// + /// Most of the tables that should be sorted have been sorted. The CustomAttribute + /// table is still unsorted since it hasn't been created yet. + /// + MostTablesSorted, + + /// + /// Custom attributes of all types, fields, methods, events, properties and parameters + /// have now been written. + /// + MemberDefCustomAttributesWritten, + + /// + /// All resources are about to be added to the .NET resources table + /// + BeginAddResources, + + /// + /// All resources have been added to the .NET resources table + /// + EndAddResources, + + /// + /// All method bodies are about to be written + /// + BeginWriteMethodBodies, + + /// + /// All method bodies have been written. Their RVAs are still not known. + /// + EndWriteMethodBodies, + + /// + /// All tables are now sorted, including the CustomAttribute table. + /// + OnAllTablesSorted, + + /// + /// All tables have been created and all rows populated. The only columns that haven't + /// been initialized yet are the ones that are RVAs. + /// + EndCreateTables, + } +} diff --git a/src/DotNet/Writer/MetadataHeader.cs b/src/DotNet/Writer/MetadataHeader.cs new file mode 100644 index 000000000..4fe2841fe --- /dev/null +++ b/src/DotNet/Writer/MetadataHeader.cs @@ -0,0 +1,170 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; +using System.Text; +using dnlib.IO; +using dnlib.PE; +using dnlib.DotNet.MD; + +namespace dnlib.DotNet.Writer { + /// + /// options + /// + public sealed class MetadataHeaderOptions { + /// + /// Default version string + /// + public const string DEFAULT_VERSION_STRING = MDHeaderRuntimeVersion.MS_CLR_20; + + /// + /// Default header signature + /// + public const uint DEFAULT_SIGNATURE = 0x424A5342; + + /// + /// MD header signature. Default value is + /// + public uint? Signature; + + /// + /// Major version. Default is 1. MS' CLR supports v0.x (x >= 19) and v1.1, nothing else. + /// + public ushort? MajorVersion; + + /// + /// Minor version. Default is 1. + /// + public ushort? MinorVersion; + + /// + /// Reserved and should be 0. + /// + public uint? Reserved1; + + /// + /// Version string. Default is . It's stored as a + /// zero-terminated UTF-8 string. Length should be <= 255 bytes. + /// + public string VersionString; + + /// + /// Storage flags should be 0 + /// + public StorageFlags? StorageFlags; + + /// + /// Reserved and should be 0 + /// + public byte? Reserved2; + + /// + /// Creates portable PDB v1.0 options + /// + /// + public static MetadataHeaderOptions CreatePortablePdbV1_0() => + new MetadataHeaderOptions() { + Signature = DEFAULT_SIGNATURE, + MajorVersion = 1, + MinorVersion = 1, + Reserved1 = 0, + VersionString = MDHeaderRuntimeVersion.PORTABLE_PDB_V1_0, + StorageFlags = 0, + Reserved2 = 0, + }; + } + + /// + /// Meta data header. IMAGE_COR20_HEADER.Metadata points to this header. + /// + public sealed class MetadataHeader : IChunk { + IList heaps; + readonly MetadataHeaderOptions options; + uint length; + FileOffset offset; + RVA rva; + + /// + public FileOffset FileOffset => offset; + + /// + public RVA RVA => rva; + + /// + /// Gets/sets the heaps + /// + public IList Heaps { + get => heaps; + set => heaps = value; + } + + /// + /// Default constructor + /// + public MetadataHeader() + : this(null) { + } + + /// + /// Constructor + /// + /// Options + public MetadataHeader(MetadataHeaderOptions options) => this.options = options ?? new MetadataHeaderOptions(); + + /// + public void SetOffset(FileOffset offset, RVA rva) { + this.offset = offset; + this.rva = rva; + + length = 16; + length += (uint)GetVersionString().Length; + length = Utils.AlignUp(length, 4); + length += 4; + var heaps = this.heaps; + int count = heaps.Count; + for (int i = 0; i < count; i++) { + var heap = heaps[i]; + length += 8; + length += (uint)GetAsciizName(heap.Name).Length; + length = Utils.AlignUp(length, 4); + } + } + + /// + public uint GetFileLength() => length; + + /// + public uint GetVirtualSize() => GetFileLength(); + + /// + public uint CalculateAlignment() => 0; + + /// + public void WriteTo(DataWriter writer) { + writer.WriteUInt32(options.Signature ?? MetadataHeaderOptions.DEFAULT_SIGNATURE); + writer.WriteUInt16(options.MajorVersion ?? 1); + writer.WriteUInt16(options.MinorVersion ?? 1); + writer.WriteUInt32(options.Reserved1 ?? 0); + var s = GetVersionString(); + writer.WriteInt32(Utils.AlignUp(s.Length, 4)); + writer.WriteBytes(s); + writer.WriteZeroes(Utils.AlignUp(s.Length, 4) - s.Length); + writer.WriteByte((byte)(options.StorageFlags ?? 0)); + writer.WriteByte(options.Reserved2 ?? 0); + var heaps = this.heaps; + writer.WriteUInt16((ushort)heaps.Count); + int count = heaps.Count; + for (int i = 0; i < count; i++) { + var heap = heaps[i]; + writer.WriteUInt32((uint)(heap.FileOffset - offset)); + writer.WriteUInt32(heap.GetFileLength()); + writer.WriteBytes(s = GetAsciizName(heap.Name)); + if (s.Length > 32) + throw new ModuleWriterException($"Heap name '{heap.Name}' is > 32 bytes"); + writer.WriteZeroes(Utils.AlignUp(s.Length, 4) - s.Length); + } + } + + byte[] GetVersionString() => Encoding.UTF8.GetBytes((options.VersionString ?? MetadataHeaderOptions.DEFAULT_VERSION_STRING) + "\0"); + byte[] GetAsciizName(string s) => Encoding.ASCII.GetBytes(s + "\0"); + } +} diff --git a/src/DotNet/Writer/MethodBody.cs b/src/DotNet/Writer/MethodBody.cs index 8c8da069e..3c6464dd7 100644 --- a/src/DotNet/Writer/MethodBody.cs +++ b/src/DotNet/Writer/MethodBody.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System.IO; +using System.Diagnostics; using dnlib.IO; using dnlib.PE; @@ -17,59 +17,43 @@ public sealed class MethodBody : IChunk { uint length; FileOffset offset; RVA rva; - uint localVarSigTok; + readonly uint localVarSigTok; /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } - } + public RVA RVA => rva; /// /// Gets the code /// - public byte[] Code { - get { return code; } - } + public byte[] Code => code; /// /// Gets the extra sections (exception handlers) or null /// - public byte[] ExtraSections { - get { return extraSections; } - } + public byte[] ExtraSections => extraSections; /// /// Gets the token of the locals /// - public uint LocalVarSigTok { - get { return localVarSigTok; } - } + public uint LocalVarSigTok => localVarSigTok; /// /// true if it's a fat body /// - public bool IsFat { - get { return !isTiny; } - } + public bool IsFat => !isTiny; /// /// true if it's a tiny body /// - public bool IsTiny { - get { return isTiny; } - } + public bool IsTiny => isTiny; /// /// true if there's an extra section /// - public bool HasExtraSections { - get { return extraSections != null && extraSections.Length > 0; } - } + public bool HasExtraSections => extraSections is not null && extraSections.Length > 0; /// /// Constructor @@ -95,7 +79,7 @@ public MethodBody(byte[] code, byte[] extraSections) /// Extra sections or null /// Token of locals public MethodBody(byte[] code, byte[] extraSections, uint localVarSigTok) { - this.isTiny = (code[0] & 3) == 2; + isTiny = (code[0] & 3) == 2; this.code = code; this.extraSections = extraSections; this.localVarSigTok = localVarSigTok; @@ -104,9 +88,9 @@ public MethodBody(byte[] code, byte[] extraSections, uint localVarSigTok) { /// /// Gets the approximate size of the method body (code + exception handlers) /// - public int GetSizeOfMethodBody() { + public int GetApproximateSizeOfMethodBody() { int len = code.Length; - if (extraSections != null) { + if (extraSections is not null) { len = Utils.AlignUp(len, EXTRA_SECTIONS_ALIGNMENT); len += extraSections.Length; len = Utils.AlignUp(len, EXTRA_SECTIONS_ALIGNMENT); @@ -114,12 +98,26 @@ public int GetSizeOfMethodBody() { return len; } + internal bool CanReuse(RVA origRva, uint origSize) { + uint length; + if (HasExtraSections) { + var rva2 = origRva + (uint)code.Length; + rva2 = rva2.AlignUp(EXTRA_SECTIONS_ALIGNMENT); + rva2 += (uint)extraSections.Length; + length = (uint)rva2 - (uint)origRva; + } + else + length = (uint)code.Length; + return length <= origSize; + } + /// public void SetOffset(FileOffset offset, RVA rva) { + Debug.Assert(this.rva == 0); this.offset = offset; this.rva = rva; if (HasExtraSections) { - RVA rva2 = rva + (uint)code.Length; + var rva2 = rva + (uint)code.Length; rva2 = rva2.AlignUp(EXTRA_SECTIONS_ALIGNMENT); rva2 += (uint)extraSections.Length; length = (uint)rva2 - (uint)rva; @@ -129,34 +127,31 @@ public void SetOffset(FileOffset offset, RVA rva) { } /// - public uint GetFileLength() { - return length; - } + public uint GetFileLength() => length; /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetVirtualSize() => GetFileLength(); /// - public void WriteTo(BinaryWriter writer) { - writer.Write(code); + public uint CalculateAlignment() => 0; + + /// + public void WriteTo(DataWriter writer) { + writer.WriteBytes(code); if (HasExtraSections) { - RVA rva2 = rva + (uint)code.Length; - writer.WriteZeros((int)rva2.AlignUp(EXTRA_SECTIONS_ALIGNMENT) - (int)rva2); - writer.Write(extraSections); + var rva2 = rva + (uint)code.Length; + writer.WriteZeroes((int)rva2.AlignUp(EXTRA_SECTIONS_ALIGNMENT) - (int)rva2); + writer.WriteBytes(extraSections); } } /// - public override int GetHashCode() { - return Utils.GetHashCode(code) + Utils.GetHashCode(extraSections); - } + public override int GetHashCode() => Utils.GetHashCode(code) + Utils.GetHashCode(extraSections); /// public override bool Equals(object obj) { var other = obj as MethodBody; - if (other == null) + if (other is null) return false; return Utils.Equals(code, other.code) && Utils.Equals(extraSections, other.extraSections); diff --git a/src/DotNet/Writer/MethodBodyChunks.cs b/src/DotNet/Writer/MethodBodyChunks.cs index 41ab6149d..63ab29ae2 100644 --- a/src/DotNet/Writer/MethodBodyChunks.cs +++ b/src/DotNet/Writer/MethodBodyChunks.cs @@ -1,8 +1,8 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; -using System.IO; +using System.Diagnostics; using dnlib.IO; using dnlib.PE; @@ -16,6 +16,8 @@ public sealed class MethodBodyChunks : IChunk { Dictionary fatMethodsDict; readonly List tinyMethods; readonly List fatMethods; + readonly List reusedMethods; + readonly Dictionary rvaToReusedMethod; readonly bool shareBodies; FileOffset offset; RVA rva; @@ -24,22 +26,29 @@ public sealed class MethodBodyChunks : IChunk { readonly bool alignFatBodies; uint savedBytes; - /// - public FileOffset FileOffset { - get { return offset; } + readonly struct ReusedMethodInfo { + public readonly MethodBody MethodBody; + public readonly RVA RVA; + public ReusedMethodInfo(MethodBody methodBody, RVA rva) { + MethodBody = methodBody; + RVA = rva; + } } /// - public RVA RVA { - get { return rva; } - } + public FileOffset FileOffset => offset; + + /// + public RVA RVA => rva; /// /// Gets the number of bytes saved by re-using method bodies /// - public uint SavedBytes { - get { return savedBytes; } - } + public uint SavedBytes => savedBytes; + + internal bool CanReuseOldBodyLocation { get; set; } + internal bool ReusedAllMethodBodyLocations => tinyMethods.Count == 0 && fatMethods.Count == 0; + internal bool HasReusedMethods => reusedMethods.Count > 0; /// /// Constructor @@ -47,13 +56,15 @@ public uint SavedBytes { /// true if bodies can be shared public MethodBodyChunks(bool shareBodies) { this.shareBodies = shareBodies; - this.alignFatBodies = true; + alignFatBodies = true; if (shareBodies) { tinyMethodsDict = new Dictionary(); fatMethodsDict = new Dictionary(); } tinyMethods = new List(); fatMethods = new List(); + reusedMethods = new List(); + rvaToReusedMethod = new Dictionary(); } /// @@ -61,14 +72,26 @@ public MethodBodyChunks(bool shareBodies) { /// /// The method body /// The cached method body - public MethodBody Add(MethodBody methodBody) { + public MethodBody Add(MethodBody methodBody) => Add(methodBody, 0, 0); + + internal MethodBody Add(MethodBody methodBody, RVA origRva, uint origSize) { if (setOffsetCalled) throw new InvalidOperationException("SetOffset() has already been called"); + if (CanReuseOldBodyLocation && origRva != 0 && origSize != 0 && methodBody.CanReuse(origRva, origSize)) { + if (rvaToReusedMethod.TryGetValue((uint)origRva, out var reusedMethod)) { + if (methodBody.Equals(reusedMethod)) + return reusedMethod; + } + else { + rvaToReusedMethod.Add((uint)origRva, methodBody); + reusedMethods.Add(new ReusedMethodInfo(methodBody, origRva)); + return methodBody; + } + } if (shareBodies) { var dict = methodBody.IsFat ? fatMethodsDict : tinyMethodsDict; - MethodBody cached; - if (dict.TryGetValue(methodBody, out cached)) { - savedBytes += (uint)methodBody.GetSizeOfMethodBody(); + if (dict.TryGetValue(methodBody, out var cached)) { + savedBytes += (uint)methodBody.GetApproximateSizeOfMethodBody(); return cached; } dict[methodBody] = methodBody; @@ -78,6 +101,38 @@ public MethodBody Add(MethodBody methodBody) { return methodBody; } + /// Removes the specified method body from this chunk + /// The method body + /// if the method body is removed + public bool Remove(MethodBody methodBody) { + if (methodBody is null) + throw new ArgumentNullException(nameof(methodBody)); + if (setOffsetCalled) + throw new InvalidOperationException("SetOffset() has already been called"); + if (CanReuseOldBodyLocation) + throw new InvalidOperationException("Reusing old body locations is enabled. Can't remove bodies."); + + var list = methodBody.IsFat ? fatMethods : tinyMethods; + return list.Remove(methodBody); + } + + internal void InitializeReusedMethodBodies(Func getNewFileOffset) { + foreach (var info in reusedMethods) { + var offset = getNewFileOffset(info.RVA); + info.MethodBody.SetOffset(offset, info.RVA); + } + } + + internal void WriteReusedMethodBodies(DataWriter writer, long destStreamBaseOffset) { + foreach (var info in reusedMethods) { + Debug.Assert(info.MethodBody.RVA == info.RVA); + if (info.MethodBody.RVA != info.RVA) + throw new InvalidOperationException(); + writer.Position = destStreamBaseOffset + (uint)info.MethodBody.FileOffset; + info.MethodBody.VerifyWriteTo(writer); + } + } + /// public void SetOffset(FileOffset offset, RVA rva) { setOffsetCalled = true; @@ -111,17 +166,16 @@ public void SetOffset(FileOffset offset, RVA rva) { } /// - public uint GetFileLength() { - return length; - } + public uint GetFileLength() => length; /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetVirtualSize() => GetFileLength(); + + /// + public uint CalculateAlignment() => 0; /// - public void WriteTo(BinaryWriter writer) { + public void WriteTo(DataWriter writer) { var rva2 = rva; foreach (var mb in tinyMethods) { mb.VerifyWriteTo(writer); @@ -131,7 +185,7 @@ public void WriteTo(BinaryWriter writer) { foreach (var mb in fatMethods) { if (alignFatBodies) { int padding = (int)rva2.AlignUp(FAT_BODY_ALIGNMENT) - (int)rva2; - writer.WriteZeros(padding); + writer.WriteZeroes(padding); rva2 += (uint)padding; } mb.VerifyWriteTo(writer); diff --git a/src/DotNet/Writer/MethodBodyWriter.cs b/src/DotNet/Writer/MethodBodyWriter.cs index 25aac1fc1..0fad93f32 100644 --- a/src/DotNet/Writer/MethodBodyWriter.cs +++ b/src/DotNet/Writer/MethodBodyWriter.cs @@ -1,15 +1,14 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; -using System.IO; using dnlib.DotNet.Emit; namespace dnlib.DotNet.Writer { /// /// Returns tokens of token types, strings and signatures /// - public interface ITokenCreator : IWriterError { + public interface ITokenProvider : IWriterError { /// /// Gets the token of /// @@ -31,9 +30,9 @@ public interface ITokenCreator : IWriterError { /// Writes CIL method bodies /// public sealed class MethodBodyWriter : MethodBodyWriterBase { - readonly ITokenCreator helper; - readonly CilBody cilBody; - readonly bool keepMaxStack; + readonly ITokenProvider helper; + CilBody cilBody; + bool keepMaxStack; uint codeSize; uint maxStack; byte[] code; @@ -45,23 +44,40 @@ public sealed class MethodBodyWriter : MethodBodyWriterBase { /// The size of this array is not necessarily a multiple of 4, even if there are exception /// handlers present. See also /// - public byte[] Code { - get { return code; } - } + public byte[] Code => code; /// /// Gets the extra sections (exception handlers) as a byte array or null if there /// are no exception handlers. This is valid only after calling /// - public byte[] ExtraSections { - get { return extraSections; } - } + public byte[] ExtraSections => extraSections; /// /// Gets the token of the locals /// - public uint LocalVarSigTok { - get { return localVarSigTok; } + public uint LocalVarSigTok => localVarSigTok; + + /// + /// Constructor + /// + /// Helps this instance + /// The method + public MethodBodyWriter(ITokenProvider helper, MethodDef method) + : this(helper, method, false) { + } + + /// + /// Constructor + /// + /// Helps this instance + /// The method + /// Keep the original max stack value that has been initialized + /// in + public MethodBodyWriter(ITokenProvider helper, MethodDef method, bool keepMaxStack) + : base(method.Body.Instructions, method.Body.ExceptionHandlers) { + this.helper = helper; + cilBody = method.Body; + this.keepMaxStack = keepMaxStack; } /// @@ -69,7 +85,7 @@ public uint LocalVarSigTok { /// /// Helps this instance /// The CIL method body - public MethodBodyWriter(ITokenCreator helper, CilBody cilBody) + public MethodBodyWriter(ITokenProvider helper, CilBody cilBody) : this(helper, cilBody, false) { } @@ -80,13 +96,28 @@ public MethodBodyWriter(ITokenCreator helper, CilBody cilBody) /// The CIL method body /// Keep the original max stack value that has been initialized /// in - public MethodBodyWriter(ITokenCreator helper, CilBody cilBody, bool keepMaxStack) + public MethodBodyWriter(ITokenProvider helper, CilBody cilBody, bool keepMaxStack) : base(cilBody.Instructions, cilBody.ExceptionHandlers) { this.helper = helper; this.cilBody = cilBody; this.keepMaxStack = keepMaxStack; } + internal MethodBodyWriter(ITokenProvider helper) { + this.helper = helper; + } + + internal void Reset(CilBody cilBody, bool keepMaxStack) { + Reset(cilBody.Instructions, cilBody.ExceptionHandlers); + this.cilBody = cilBody; + this.keepMaxStack = keepMaxStack; + codeSize = 0; + maxStack = 0; + code = null; + extraSections = null; + localVarSigTok = 0; + } + /// /// Writes the method body /// @@ -108,15 +139,15 @@ public void Write() { /// The code and any exception handlers public byte[] GetFullMethodBody() { int padding = Utils.AlignUp(code.Length, 4) - code.Length; - var bytes = new byte[code.Length + (extraSections == null ? 0 : padding + extraSections.Length)]; + var bytes = new byte[code.Length + (extraSections is null ? 0 : padding + extraSections.Length)]; Array.Copy(code, 0, bytes, 0, code.Length); - if (extraSections != null) + if (extraSections is not null) Array.Copy(extraSections, 0, bytes, code.Length + padding, extraSections.Length); return bytes; } bool NeedFatHeader() { - //TODO: If locals has cust attrs, we also need a fat header + //TODO: If locals has cust attrs or custom debug infos, we also need a fat header return codeSize > 0x3F || exceptionHandlers.Count > 0 || cilBody.HasVariables || @@ -136,12 +167,12 @@ void WriteFatHeader() { flags |= 0x10; code = new byte[12 + codeSize]; - var writer = new BinaryWriter(new MemoryStream(code)); - writer.Write(flags); - writer.Write((ushort)maxStack); - writer.Write(codeSize); - writer.Write(localVarSigTok = helper.GetToken(GetLocals(), cilBody.LocalVarSigTok).Raw); - if (WriteInstructions(writer) != codeSize) + var writer = new ArrayWriter(code); + writer.WriteUInt16(flags); + writer.WriteUInt16((ushort)maxStack); + writer.WriteUInt32(codeSize); + writer.WriteUInt32(localVarSigTok = helper.GetToken(GetLocals(), cilBody.LocalVarSigTok).Raw); + if (WriteInstructions(ref writer) != codeSize) Error("Didn't write all code bytes"); } @@ -155,29 +186,28 @@ IList GetLocals() { void WriteTinyHeader() { localVarSigTok = 0; code = new byte[1 + codeSize]; - var writer = new BinaryWriter(new MemoryStream(code)); - writer.Write((byte)((codeSize << 2) | 2)); - if (WriteInstructions(writer) != codeSize) + var writer = new ArrayWriter(code); + writer.WriteByte((byte)((codeSize << 2) | 2)); + if (WriteInstructions(ref writer) != codeSize) Error("Didn't write all code bytes"); } void WriteExceptionHandlers() { - var outStream = new MemoryStream(); - var writer = new BinaryWriter(outStream); if (NeedFatExceptionClauses()) - WriteFatExceptionClauses(writer); + extraSections = WriteFatExceptionClauses(); else - WriteSmallExceptionClauses(writer); - extraSections = outStream.ToArray(); + extraSections = WriteSmallExceptionClauses(); } bool NeedFatExceptionClauses() { // Size must fit in a byte, and since one small exception record is 12 bytes // and header is 4 bytes: x*12+4 <= 255 ==> x <= 20 + var exceptionHandlers = this.exceptionHandlers; if (exceptionHandlers.Count > 20) return true; - foreach (var eh in exceptionHandlers) { + for (int i = 0; i < exceptionHandlers.Count; i++) { + var eh = exceptionHandlers[i]; if (!FitsInSmallExceptionClause(eh.TryStart, eh.TryEnd)) return true; if (!FitsInSmallExceptionClause(eh.HandlerStart, eh.HandlerEnd)) @@ -196,120 +226,120 @@ bool FitsInSmallExceptionClause(Instruction start, Instruction end) { } uint GetOffset2(Instruction instr) { - if (instr == null) + if (instr is null) return codeSize; return GetOffset(instr); } - void WriteFatExceptionClauses(BinaryWriter writer) { + byte[] WriteFatExceptionClauses() { const int maxExceptionHandlers = (0x00FFFFFF - 4) / 24; + var exceptionHandlers = this.exceptionHandlers; int numExceptionHandlers = exceptionHandlers.Count; if (numExceptionHandlers > maxExceptionHandlers) { Error("Too many exception handlers"); numExceptionHandlers = maxExceptionHandlers; } - writer.Write((((uint)numExceptionHandlers * 24 + 4) << 8) | 0x41); + var data = new byte[numExceptionHandlers * 24 + 4]; + var writer = new ArrayWriter(data); + writer.WriteUInt32((((uint)numExceptionHandlers * 24 + 4) << 8) | 0x41); for (int i = 0; i < numExceptionHandlers; i++) { var eh = exceptionHandlers[i]; uint offs1, offs2; - writer.Write((uint)eh.HandlerType); + writer.WriteUInt32((uint)eh.HandlerType); offs1 = GetOffset2(eh.TryStart); offs2 = GetOffset2(eh.TryEnd); if (offs2 <= offs1) Error("Exception handler: TryEnd <= TryStart"); - writer.Write(offs1); - writer.Write(offs2 - offs1); + writer.WriteUInt32(offs1); + writer.WriteUInt32(offs2 - offs1); offs1 = GetOffset2(eh.HandlerStart); offs2 = GetOffset2(eh.HandlerEnd); if (offs2 <= offs1) Error("Exception handler: HandlerEnd <= HandlerStart"); - writer.Write(offs1); - writer.Write(offs2 - offs1); + writer.WriteUInt32(offs1); + writer.WriteUInt32(offs2 - offs1); - if (eh.HandlerType == ExceptionHandlerType.Catch) - writer.Write(helper.GetToken(eh.CatchType).Raw); - else if (eh.HandlerType == ExceptionHandlerType.Filter) - writer.Write(GetOffset2(eh.FilterStart)); + if (eh.IsCatch) + writer.WriteUInt32(helper.GetToken(eh.CatchType).Raw); + else if (eh.IsFilter) + writer.WriteUInt32(GetOffset2(eh.FilterStart)); else - writer.Write(0); + writer.WriteInt32(0); } + + if (writer.Position != data.Length) + throw new InvalidOperationException(); + return data; } - void WriteSmallExceptionClauses(BinaryWriter writer) { + byte[] WriteSmallExceptionClauses() { const int maxExceptionHandlers = (0xFF - 4) / 12; + var exceptionHandlers = this.exceptionHandlers; int numExceptionHandlers = exceptionHandlers.Count; if (numExceptionHandlers > maxExceptionHandlers) { Error("Too many exception handlers"); numExceptionHandlers = maxExceptionHandlers; } - writer.Write((((uint)numExceptionHandlers * 12 + 4) << 8) | 1); + var data = new byte[numExceptionHandlers * 12 + 4]; + var writer = new ArrayWriter(data); + writer.WriteUInt32((((uint)numExceptionHandlers * 12 + 4) << 8) | 1); for (int i = 0; i < numExceptionHandlers; i++) { var eh = exceptionHandlers[i]; uint offs1, offs2; - writer.Write((ushort)eh.HandlerType); + writer.WriteUInt16((ushort)eh.HandlerType); offs1 = GetOffset2(eh.TryStart); offs2 = GetOffset2(eh.TryEnd); if (offs2 <= offs1) Error("Exception handler: TryEnd <= TryStart"); - writer.Write((ushort)offs1); - writer.Write((byte)(offs2 - offs1)); + writer.WriteUInt16((ushort)offs1); + writer.WriteByte((byte)(offs2 - offs1)); offs1 = GetOffset2(eh.HandlerStart); offs2 = GetOffset2(eh.HandlerEnd); if (offs2 <= offs1) Error("Exception handler: HandlerEnd <= HandlerStart"); - writer.Write((ushort)offs1); - writer.Write((byte)(offs2 - offs1)); + writer.WriteUInt16((ushort)offs1); + writer.WriteByte((byte)(offs2 - offs1)); - if (eh.HandlerType == ExceptionHandlerType.Catch) - writer.Write(helper.GetToken(eh.CatchType).Raw); - else if (eh.HandlerType == ExceptionHandlerType.Filter) - writer.Write(GetOffset2(eh.FilterStart)); + if (eh.IsCatch) + writer.WriteUInt32(helper.GetToken(eh.CatchType).Raw); + else if (eh.IsFilter) + writer.WriteUInt32(GetOffset2(eh.FilterStart)); else - writer.Write(0); + writer.WriteInt32(0); } + + if (writer.Position != data.Length) + throw new InvalidOperationException(); + return data; } /// - protected override void ErrorImpl(string message) { - helper.Error(message); - } + protected override void ErrorImpl(string message) => helper.Error(message); /// - protected override void WriteInlineField(BinaryWriter writer, Instruction instr) { - writer.Write(helper.GetToken(instr.Operand).Raw); - } + protected override void WriteInlineField(ref ArrayWriter writer, Instruction instr) => writer.WriteUInt32(helper.GetToken(instr.Operand).Raw); /// - protected override void WriteInlineMethod(BinaryWriter writer, Instruction instr) { - writer.Write(helper.GetToken(instr.Operand).Raw); - } + protected override void WriteInlineMethod(ref ArrayWriter writer, Instruction instr) => writer.WriteUInt32(helper.GetToken(instr.Operand).Raw); /// - protected override void WriteInlineSig(BinaryWriter writer, Instruction instr) { - writer.Write(helper.GetToken(instr.Operand).Raw); - } + protected override void WriteInlineSig(ref ArrayWriter writer, Instruction instr) => writer.WriteUInt32(helper.GetToken(instr.Operand).Raw); /// - protected override void WriteInlineString(BinaryWriter writer, Instruction instr) { - writer.Write(helper.GetToken(instr.Operand).Raw); - } + protected override void WriteInlineString(ref ArrayWriter writer, Instruction instr) => writer.WriteUInt32(helper.GetToken(instr.Operand).Raw); /// - protected override void WriteInlineTok(BinaryWriter writer, Instruction instr) { - writer.Write(helper.GetToken(instr.Operand).Raw); - } + protected override void WriteInlineTok(ref ArrayWriter writer, Instruction instr) => writer.WriteUInt32(helper.GetToken(instr.Operand).Raw); /// - protected override void WriteInlineType(BinaryWriter writer, Instruction instr) { - writer.Write(helper.GetToken(instr.Operand).Raw); - } + protected override void WriteInlineType(ref ArrayWriter writer, Instruction instr) => writer.WriteUInt32(helper.GetToken(instr.Operand).Raw); } } diff --git a/src/DotNet/Writer/MethodBodyWriterBase.cs b/src/DotNet/Writer/MethodBodyWriterBase.cs index af659c6f4..dad3c6fee 100644 --- a/src/DotNet/Writer/MethodBodyWriterBase.cs +++ b/src/DotNet/Writer/MethodBodyWriterBase.cs @@ -1,7 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; -using System.IO; +using System.Collections.Generic; using dnlib.DotNet.Emit; namespace dnlib.DotNet.Writer { @@ -10,18 +9,20 @@ namespace dnlib.DotNet.Writer { ///
public abstract class MethodBodyWriterBase { /// - protected readonly IList instructions; + protected IList instructions; /// - protected readonly IList exceptionHandlers; + protected IList exceptionHandlers; readonly Dictionary offsets = new Dictionary(); uint firstInstructionOffset; int errors; + MaxStackCalculator maxStackCalculator = MaxStackCalculator.Create(); /// /// true if there was at least one error /// - public bool ErrorDetected { - get { return errors > 0; } + public bool ErrorDetected => errors > 0; + + internal MethodBodyWriterBase() { } /// @@ -34,6 +35,14 @@ protected MethodBodyWriterBase(IList instructions, IList instructions, IList exceptionHandlers) { + this.instructions = instructions; + this.exceptionHandlers = exceptionHandlers; + offsets.Clear(); + firstInstructionOffset = 0; + errors = 0; + } + /// /// Called when an error is detected (eg. a null pointer). The error can be /// ignored but the method won't be valid. @@ -58,9 +67,9 @@ protected virtual void ErrorImpl(string message) { protected uint GetMaxStack() { if (instructions.Count == 0) return 0; - uint maxStack; - if (!MaxStackCalculator.GetMaxStack(instructions, exceptionHandlers, out maxStack)) { - Error("Error calculating max stack value. If the method's obfuscated, set CilBody.KeepOldMaxStack or MetaDataOptions.Flags (KeepOldMaxStack, global option) to ignore this error. Otherwise fix your generated CIL code so it conforms to the ECMA standard."); + maxStackCalculator.Reset(instructions, exceptionHandlers); + if (!maxStackCalculator.Calculate(out uint maxStack)) { + Error("Error calculating max stack value. If the method's obfuscated, set CilBody.KeepOldMaxStack or MetadataOptions.Flags (KeepOldMaxStack, global option) to ignore this error. Otherwise fix your generated CIL code so it conforms to the ECMA standard."); maxStack += 8; } return maxStack; @@ -73,12 +82,11 @@ protected uint GetMaxStack() { /// The offset or 0 if is null or not /// present in the list of all instructions. protected uint GetOffset(Instruction instr) { - if (instr == null) { + if (instr is null) { Error("Instruction is null"); return 0; } - uint offset; - if (offsets.TryGetValue(instr, out offset)) + if (offsets.TryGetValue(instr, out uint offset)) return offset; Error("Found some other method's instruction or a removed instruction. You probably removed an instruction that is the target of a branch instruction or an instruction that's the first/last instruction in an exception handler."); return 0; @@ -90,8 +98,10 @@ protected uint GetOffset(Instruction instr) { /// Size of code protected uint InitializeInstructionOffsets() { uint offset = 0; - foreach (var instr in instructions) { - if (instr == null) + var instructions = this.instructions; + for (int i = 0; i < instructions.Count; i++) { + var instr = instructions[i]; + if (instr is null) continue; offsets[instr] = offset; offset += GetSizeOfInstruction(instr); @@ -104,23 +114,23 @@ protected uint InitializeInstructionOffsets() { /// /// The instruction /// Size of the instruction in bytes - protected virtual uint GetSizeOfInstruction(Instruction instr) { - return (uint)instr.GetSize(); - } + protected virtual uint GetSizeOfInstruction(Instruction instr) => (uint)instr.GetSize(); /// /// Writes all instructions to at its current offset /// /// The instruction writer /// Number of bytes written - protected uint WriteInstructions(BinaryWriter writer) { - firstInstructionOffset = (uint)writer.BaseStream.Position; - foreach (var instr in instructions) { - if (instr == null) + protected uint WriteInstructions(ref ArrayWriter writer) { + firstInstructionOffset = (uint)writer.Position; + var instructions = this.instructions; + for (int i = 0; i < instructions.Count; i++) { + var instr = instructions[i]; + if (instr is null) continue; - WriteInstruction(writer, instr); + WriteInstruction(ref writer, instr); } - return ToInstructionOffset(writer); + return ToInstructionOffset(ref writer); } /// @@ -129,18 +139,16 @@ protected uint WriteInstructions(BinaryWriter writer) { /// /// The instruction writer /// Current offset, relative to the first written instruction - protected uint ToInstructionOffset(BinaryWriter writer) { - return (uint)writer.BaseStream.Position - firstInstructionOffset; - } + protected uint ToInstructionOffset(ref ArrayWriter writer) => (uint)writer.Position - firstInstructionOffset; /// /// Writes an instruction /// /// The instruction writer /// The instruction - protected virtual void WriteInstruction(BinaryWriter writer, Instruction instr) { - WriteOpCode(writer, instr); - WriteOperand(writer, instr); + protected virtual void WriteInstruction(ref ArrayWriter writer, Instruction instr) { + WriteOpCode(ref writer, instr); + WriteOperand(ref writer, instr); } /// @@ -148,21 +156,22 @@ protected virtual void WriteInstruction(BinaryWriter writer, Instruction instr) /// /// The instruction writer /// The instruction - protected virtual void WriteOpCode(BinaryWriter writer, Instruction instr) { + protected void WriteOpCode(ref ArrayWriter writer, Instruction instr) { var code = instr.OpCode.Code; + var hi = (ushort)code >> 8; if ((ushort)code <= 0xFF) - writer.Write((byte)code); - else if (((ushort)code >> 8) == 0xFE) { - writer.Write((byte)((ushort)code >> 8)); - writer.Write((byte)code); + writer.WriteByte((byte)code); + else if (hi == 0xFE || (hi >= 0xF0 && hi <= 0xFB)) { + writer.WriteByte((byte)((ushort)code >> 8)); + writer.WriteByte((byte)code); } else if (code == Code.UNKNOWN1) - writer.Write((byte)Code.Nop); + writer.WriteByte((byte)Code.Nop); else if (code == Code.UNKNOWN2) - writer.Write((ushort)(((ushort)Code.Nop << 8) | Code.Nop)); + writer.WriteUInt16((ushort)(((ushort)Code.Nop << 8) | Code.Nop)); else { Error("Unknown instruction"); - writer.Write((byte)Code.Nop); + writer.WriteByte((byte)Code.Nop); } } @@ -171,26 +180,26 @@ protected virtual void WriteOpCode(BinaryWriter writer, Instruction instr) { /// /// The instruction writer /// The instruction - protected virtual void WriteOperand(BinaryWriter writer, Instruction instr) { + protected void WriteOperand(ref ArrayWriter writer, Instruction instr) { switch (instr.OpCode.OperandType) { - case OperandType.InlineBrTarget: WriteInlineBrTarget(writer, instr); break; - case OperandType.InlineField: WriteInlineField(writer, instr); break; - case OperandType.InlineI: WriteInlineI(writer, instr); break; - case OperandType.InlineI8: WriteInlineI8(writer, instr); break; - case OperandType.InlineMethod: WriteInlineMethod(writer, instr); break; - case OperandType.InlineNone: WriteInlineNone(writer, instr); break; - case OperandType.InlinePhi: WriteInlinePhi(writer, instr); break; - case OperandType.InlineR: WriteInlineR(writer, instr); break; - case OperandType.InlineSig: WriteInlineSig(writer, instr); break; - case OperandType.InlineString: WriteInlineString(writer, instr); break; - case OperandType.InlineSwitch: WriteInlineSwitch(writer, instr); break; - case OperandType.InlineTok: WriteInlineTok(writer, instr); break; - case OperandType.InlineType: WriteInlineType(writer, instr); break; - case OperandType.InlineVar: WriteInlineVar(writer, instr); break; - case OperandType.ShortInlineBrTarget: WriteShortInlineBrTarget(writer, instr); break; - case OperandType.ShortInlineI: WriteShortInlineI(writer, instr); break; - case OperandType.ShortInlineR: WriteShortInlineR(writer, instr); break; - case OperandType.ShortInlineVar: WriteShortInlineVar(writer, instr); break; + case OperandType.InlineBrTarget: WriteInlineBrTarget(ref writer, instr); break; + case OperandType.InlineField: WriteInlineField(ref writer, instr); break; + case OperandType.InlineI: WriteInlineI(ref writer, instr); break; + case OperandType.InlineI8: WriteInlineI8(ref writer, instr); break; + case OperandType.InlineMethod: WriteInlineMethod(ref writer, instr); break; + case OperandType.InlineNone: WriteInlineNone(ref writer, instr); break; + case OperandType.InlinePhi: WriteInlinePhi(ref writer, instr); break; + case OperandType.InlineR: WriteInlineR(ref writer, instr); break; + case OperandType.InlineSig: WriteInlineSig(ref writer, instr); break; + case OperandType.InlineString: WriteInlineString(ref writer, instr); break; + case OperandType.InlineSwitch: WriteInlineSwitch(ref writer, instr); break; + case OperandType.InlineTok: WriteInlineTok(ref writer, instr); break; + case OperandType.InlineType: WriteInlineType(ref writer, instr); break; + case OperandType.InlineVar: WriteInlineVar(ref writer, instr); break; + case OperandType.ShortInlineBrTarget: WriteShortInlineBrTarget(ref writer, instr); break; + case OperandType.ShortInlineI: WriteShortInlineI(ref writer, instr); break; + case OperandType.ShortInlineR: WriteShortInlineR(ref writer, instr); break; + case OperandType.ShortInlineVar: WriteShortInlineVar(ref writer, instr); break; default: Error("Unknown operand type"); @@ -203,9 +212,9 @@ protected virtual void WriteOperand(BinaryWriter writer, Instruction instr) { /// /// Instruction writer /// Instruction - protected virtual void WriteInlineBrTarget(BinaryWriter writer, Instruction instr) { - uint displ = GetOffset(instr.Operand as Instruction) - (ToInstructionOffset(writer) + 4); - writer.Write(displ); + protected virtual void WriteInlineBrTarget(ref ArrayWriter writer, Instruction instr) { + uint displ = GetOffset(instr.Operand as Instruction) - (ToInstructionOffset(ref writer) + 4); + writer.WriteUInt32(displ); } /// @@ -213,19 +222,19 @@ protected virtual void WriteInlineBrTarget(BinaryWriter writer, Instruction inst /// /// Instruction writer /// Instruction - protected abstract void WriteInlineField(BinaryWriter writer, Instruction instr); + protected abstract void WriteInlineField(ref ArrayWriter writer, Instruction instr); /// /// Writes an operand /// /// Instruction writer /// Instruction - protected virtual void WriteInlineI(BinaryWriter writer, Instruction instr) { + protected virtual void WriteInlineI(ref ArrayWriter writer, Instruction instr) { if (instr.Operand is int) - writer.Write((int)instr.Operand); + writer.WriteInt32((int)instr.Operand); else { Error("Operand is not an Int32"); - writer.Write(0); + writer.WriteInt32(0); } } @@ -234,12 +243,12 @@ protected virtual void WriteInlineI(BinaryWriter writer, Instruction instr) { /// /// Instruction writer /// Instruction - protected virtual void WriteInlineI8(BinaryWriter writer, Instruction instr) { + protected virtual void WriteInlineI8(ref ArrayWriter writer, Instruction instr) { if (instr.Operand is long) - writer.Write((long)instr.Operand); + writer.WriteInt64((long)instr.Operand); else { Error("Operand is not an Int64"); - writer.Write(0L); + writer.WriteInt64(0); } } @@ -248,14 +257,14 @@ protected virtual void WriteInlineI8(BinaryWriter writer, Instruction instr) { /// /// Instruction writer /// Instruction - protected abstract void WriteInlineMethod(BinaryWriter writer, Instruction instr); + protected abstract void WriteInlineMethod(ref ArrayWriter writer, Instruction instr); /// /// Writes an operand /// /// Instruction writer /// Instruction - protected virtual void WriteInlineNone(BinaryWriter writer, Instruction instr) { + protected virtual void WriteInlineNone(ref ArrayWriter writer, Instruction instr) { } /// @@ -263,7 +272,7 @@ protected virtual void WriteInlineNone(BinaryWriter writer, Instruction instr) { /// /// Instruction writer /// Instruction - protected virtual void WriteInlinePhi(BinaryWriter writer, Instruction instr) { + protected virtual void WriteInlinePhi(ref ArrayWriter writer, Instruction instr) { } /// @@ -271,12 +280,12 @@ protected virtual void WriteInlinePhi(BinaryWriter writer, Instruction instr) { /// /// Instruction writer /// Instruction - protected virtual void WriteInlineR(BinaryWriter writer, Instruction instr) { + protected virtual void WriteInlineR(ref ArrayWriter writer, Instruction instr) { if (instr.Operand is double) - writer.Write((double)instr.Operand); + writer.WriteDouble((double)instr.Operand); else { Error("Operand is not a Double"); - writer.Write(0D); + writer.WriteDouble(0); } } @@ -285,31 +294,33 @@ protected virtual void WriteInlineR(BinaryWriter writer, Instruction instr) { /// /// Instruction writer /// Instruction - protected abstract void WriteInlineSig(BinaryWriter writer, Instruction instr); + protected abstract void WriteInlineSig(ref ArrayWriter writer, Instruction instr); /// /// Writes an operand /// /// Instruction writer /// Instruction - protected abstract void WriteInlineString(BinaryWriter writer, Instruction instr); + protected abstract void WriteInlineString(ref ArrayWriter writer, Instruction instr); /// /// Writes an operand /// /// Instruction writer /// Instruction - protected virtual void WriteInlineSwitch(BinaryWriter writer, Instruction instr) { + protected virtual void WriteInlineSwitch(ref ArrayWriter writer, Instruction instr) { var targets = instr.Operand as IList; - if (targets == null) { + if (targets is null) { Error("switch operand is not a list of instructions"); - writer.Write(0); + writer.WriteInt32(0); } else { - uint offsetAfter = (uint)(ToInstructionOffset(writer) + 4 + targets.Count * 4); - writer.Write(targets.Count); - foreach (var target in targets) - writer.Write(GetOffset(target) - offsetAfter); + uint offsetAfter = (uint)(ToInstructionOffset(ref writer) + 4 + targets.Count * 4); + writer.WriteInt32(targets.Count); + for (int i = 0; i < targets.Count; i++) { + var target = targets[i]; + writer.WriteUInt32(GetOffset(target) - offsetAfter); + } } } @@ -318,31 +329,34 @@ protected virtual void WriteInlineSwitch(BinaryWriter writer, Instruction instr) /// /// Instruction writer /// Instruction - protected abstract void WriteInlineTok(BinaryWriter writer, Instruction instr); + protected abstract void WriteInlineTok(ref ArrayWriter writer, Instruction instr); /// /// Writes an operand /// /// Instruction writer /// Instruction - protected abstract void WriteInlineType(BinaryWriter writer, Instruction instr); + protected abstract void WriteInlineType(ref ArrayWriter writer, Instruction instr); /// /// Writes an operand /// /// Instruction writer /// Instruction - protected virtual void WriteInlineVar(BinaryWriter writer, Instruction instr) { + protected virtual void WriteInlineVar(ref ArrayWriter writer, Instruction instr) { var variable = instr.Operand as IVariable; - if (variable == null) { + if (variable is null) { Error("Operand is not a local/arg"); - writer.Write((ushort)0); + writer.WriteUInt16(0); } - else if (ushort.MinValue <= variable.Index && variable.Index <= ushort.MaxValue) - writer.Write((ushort)variable.Index); else { - Error("Local/arg index doesn't fit in a UInt16"); - writer.Write((ushort)0); + int index = variable.Index; + if (ushort.MinValue <= index && index <= ushort.MaxValue) + writer.WriteUInt16((ushort)index); + else { + Error("Local/arg index doesn't fit in a UInt16"); + writer.WriteUInt16(0); + } } } @@ -351,13 +365,13 @@ protected virtual void WriteInlineVar(BinaryWriter writer, Instruction instr) { /// /// Instruction writer /// Instruction - protected virtual void WriteShortInlineBrTarget(BinaryWriter writer, Instruction instr) { - int displ = (int)(GetOffset(instr.Operand as Instruction) - (ToInstructionOffset(writer) + 1)); + protected virtual void WriteShortInlineBrTarget(ref ArrayWriter writer, Instruction instr) { + int displ = (int)(GetOffset(instr.Operand as Instruction) - (ToInstructionOffset(ref writer) + 1)); if (sbyte.MinValue <= displ && displ <= sbyte.MaxValue) - writer.Write((sbyte)displ); + writer.WriteSByte((sbyte)displ); else { Error("Target instruction is too far away for a short branch. Use the long branch or call CilBody.SimplifyBranches() and CilBody.OptimizeBranches()"); - writer.Write((byte)0); + writer.WriteByte(0); } } @@ -366,14 +380,14 @@ protected virtual void WriteShortInlineBrTarget(BinaryWriter writer, Instruction /// /// Instruction writer /// Instruction - protected virtual void WriteShortInlineI(BinaryWriter writer, Instruction instr) { + protected virtual void WriteShortInlineI(ref ArrayWriter writer, Instruction instr) { if (instr.Operand is sbyte) - writer.Write((sbyte)instr.Operand); + writer.WriteSByte((sbyte)instr.Operand); else if (instr.Operand is byte) - writer.Write((byte)instr.Operand); + writer.WriteByte((byte)instr.Operand); else { Error("Operand is not a Byte or a SByte"); - writer.Write((byte)0); + writer.WriteByte(0); } } @@ -382,12 +396,12 @@ protected virtual void WriteShortInlineI(BinaryWriter writer, Instruction instr) /// /// Instruction writer /// Instruction - protected virtual void WriteShortInlineR(BinaryWriter writer, Instruction instr) { + protected virtual void WriteShortInlineR(ref ArrayWriter writer, Instruction instr) { if (instr.Operand is float) - writer.Write((float)instr.Operand); + writer.WriteSingle((float)instr.Operand); else { Error("Operand is not a Single"); - writer.Write(0F); + writer.WriteSingle(0); } } @@ -396,17 +410,20 @@ protected virtual void WriteShortInlineR(BinaryWriter writer, Instruction instr) /// /// Instruction writer /// Instruction - protected virtual void WriteShortInlineVar(BinaryWriter writer, Instruction instr) { + protected virtual void WriteShortInlineVar(ref ArrayWriter writer, Instruction instr) { var variable = instr.Operand as IVariable; - if (variable == null) { + if (variable is null) { Error("Operand is not a local/arg"); - writer.Write((byte)0); + writer.WriteByte(0); } - else if (byte.MinValue <= variable.Index && variable.Index <= byte.MaxValue) - writer.Write((byte)variable.Index); else { - Error("Local/arg index doesn't fit in a Byte. Use the longer ldloc/ldarg/stloc/starg instruction."); - writer.Write((byte)0); + int index = variable.Index; + if (byte.MinValue <= index && index <= byte.MaxValue) + writer.WriteByte((byte)index); + else { + Error("Local/arg index doesn't fit in a Byte. Use the longer ldloc/ldarg/stloc/starg instruction."); + writer.WriteByte(0); + } } } } diff --git a/src/DotNet/Writer/ModuleWriter.cs b/src/DotNet/Writer/ModuleWriter.cs index 02fe004a6..e73005362 100644 --- a/src/DotNet/Writer/ModuleWriter.cs +++ b/src/DotNet/Writer/ModuleWriter.cs @@ -1,8 +1,9 @@ // dnlib: See LICENSE.txt for more info +using System; using System.Collections.Generic; -using System.IO; using dnlib.DotNet.MD; +using dnlib.PE; using dnlib.W32Resources; namespace dnlib.DotNet.Writer { @@ -10,44 +11,27 @@ namespace dnlib.DotNet.Writer { /// options /// public sealed class ModuleWriterOptions : ModuleWriterOptionsBase { - /// - /// Default constructor - /// - public ModuleWriterOptions() { - } - - /// - /// Constructor - /// - /// The module - public ModuleWriterOptions(ModuleDef module) - : this(module, null) { - } - /// /// Constructor /// /// The module - /// Module writer listener - public ModuleWriterOptions(ModuleDef module, IModuleWriterListener listener) - : base(module, listener) { - } + public ModuleWriterOptions(ModuleDef module) : base(module) { } } /// /// Writes a .NET PE file. See also /// public sealed class ModuleWriter : ModuleWriterBase { - const uint DEFAULT_IAT_ALIGNMENT = 4; - const uint DEFAULT_IMPORTDIRECTORY_ALIGNMENT = 4; - const uint DEFAULT_STARTUPSTUB_ALIGNMENT = 1; const uint DEFAULT_RELOC_ALIGNMENT = 4; + const uint MVID_ALIGNMENT = 1; readonly ModuleDef module; ModuleWriterOptions options; List sections; + PESection mvidSection; PESection textSection; + PESection sdataSection; PESection rsrcSection; PESection relocSection; @@ -57,94 +41,88 @@ public sealed class ModuleWriter : ModuleWriterBase { ImportDirectory importDirectory; StartupStub startupStub; RelocDirectory relocDirectory; + ManagedExportsWriter managedExportsWriter; + bool needStartupStub; /// - public override ModuleDef Module { - get { return module; } - } + public override ModuleDef Module => module; /// - public override ModuleWriterOptionsBase TheOptions { - get { return Options; } - } + public override ModuleWriterOptionsBase TheOptions => Options; /// /// Gets/sets the writer options. This is never null /// public ModuleWriterOptions Options { - get { return options ?? (options = new ModuleWriterOptions(module)); } - set { options = value; } + get => options ??= new ModuleWriterOptions(module); + set => options = value; } /// - /// Gets all s + /// Gets all s. The reloc section must be the last section, so use if you need to append a section /// - public override List Sections { - get { return sections; } + public override List Sections => sections; + + /// + /// Adds to the sections list, but before the reloc section which must be last + /// + /// New section to add to the list + public override void AddSection(PESection section) { + if (sections.Count > 0 && sections[sections.Count - 1] == relocSection) + sections.Insert(sections.Count - 1, section); + else + sections.Add(section); } /// /// Gets the .text section /// - public override PESection TextSection { - get { return textSection; } - } + public override PESection TextSection => textSection; /// - /// Gets the .rsrc section or null if there's none + /// Gets the .sdata section /// - public override PESection RsrcSection { - get { return rsrcSection; } - } + internal PESection SdataSection => sdataSection; /// - /// Gets the .reloc section or null if there's none + /// Gets the .rsrc section or null if none /// - public PESection RelocSection { - get { return relocSection; } - } + public override PESection RsrcSection => rsrcSection; + + /// + /// Gets the .reloc section + /// + public PESection RelocSection => relocSection; /// /// Gets the PE headers /// - public PEHeaders PEHeaders { - get { return peHeaders; } - } + public PEHeaders PEHeaders => peHeaders; /// /// Gets the IAT or null if there's none /// - public ImportAddressTable ImportAddressTable { - get { return importAddressTable; } - } + public ImportAddressTable ImportAddressTable => importAddressTable; /// /// Gets the .NET header /// - public ImageCor20Header ImageCor20Header { - get { return imageCor20Header; } - } + public ImageCor20Header ImageCor20Header => imageCor20Header; /// /// Gets the import directory or null if there's none /// - public ImportDirectory ImportDirectory { - get { return importDirectory; } - } + public ImportDirectory ImportDirectory => importDirectory; /// /// Gets the startup stub or null if there's none /// - public StartupStub StartupStub { - get { return startupStub; } - } + public StartupStub StartupStub => startupStub; /// /// Gets the reloc directory or null if there's none /// - public RelocDirectory RelocDirectory { - get { return relocDirectory; } - } + public RelocDirectory RelocDirectory => relocDirectory; /// /// Constructor @@ -167,106 +145,137 @@ public ModuleWriter(ModuleDef module, ModuleWriterOptions options) { /// protected override long WriteImpl() { Initialize(); - metaData.CreateTables(); + metadata.CreateTables(); return WriteFile(); } void Initialize() { CreateSections(); - Listener.OnWriterEvent(this, ModuleWriterEvent.PESectionsCreated); + OnWriterEvent(ModuleWriterEvent.PESectionsCreated); CreateChunks(); - Listener.OnWriterEvent(this, ModuleWriterEvent.ChunksCreated); + OnWriterEvent(ModuleWriterEvent.ChunksCreated); AddChunksToSections(); - Listener.OnWriterEvent(this, ModuleWriterEvent.ChunksAddedToSections); + OnWriterEvent(ModuleWriterEvent.ChunksAddedToSections); } /// protected override Win32Resources GetWin32Resources() { + if (Options.NoWin32Resources) + return null; return Options.Win32Resources ?? module.Win32Resources; } void CreateSections() { sections = new List(); + if (TheOptions.AddMvidSection) + sections.Add(mvidSection = new PESection(".mvid", 0x42000040)); sections.Add(textSection = new PESection(".text", 0x60000020)); - if (GetWin32Resources() != null) + sections.Add(sdataSection = new PESection(".sdata", 0xC0000040)); + if (GetWin32Resources() is not null) sections.Add(rsrcSection = new PESection(".rsrc", 0x40000040)); - if (!Options.Is64Bit) - sections.Add(relocSection = new PESection(".reloc", 0x42000040)); + // Should be last so any data in a previous section can add relocations + sections.Add(relocSection = new PESection(".reloc", 0x42000040)); } void CreateChunks() { peHeaders = new PEHeaders(Options.PEHeadersOptions); - if (!Options.Is64Bit) { - importAddressTable = new ImportAddressTable(); - importDirectory = new ImportDirectory(); - startupStub = new StartupStub(); - relocDirectory = new RelocDirectory(); - } + var machine = Options.PEHeadersOptions.Machine ?? Machine.I386; + bool is64bit = machine.Is64Bit(); + relocDirectory = new RelocDirectory(machine); + if (machine.IsI386()) + needStartupStub = true; + + importAddressTable = new ImportAddressTable(is64bit); + importDirectory = new ImportDirectory(is64bit); + startupStub = new StartupStub(relocDirectory, machine, (format, args) => Error(format, args)); CreateStrongNameSignature(); imageCor20Header = new ImageCor20Header(Options.Cor20HeaderOptions); - CreateMetaDataChunks(module); + CreateMetadataChunks(module); + managedExportsWriter = new ManagedExportsWriter(UTF8String.ToSystemStringOrEmpty(module.Name), machine, relocDirectory, metadata, peHeaders, (format, args) => Error(format, args)); CreateDebugDirectory(); - if (importDirectory != null) - importDirectory.IsExeFile = Options.IsExeFile; - + importDirectory.IsExeFile = Options.IsExeFile; peHeaders.IsExeFile = Options.IsExeFile; } void AddChunksToSections() { - textSection.Add(importAddressTable, DEFAULT_IAT_ALIGNMENT); + var machine = Options.PEHeadersOptions.Machine ?? Machine.I386; + bool is64bit = machine.Is64Bit(); + uint pointerAlignment = is64bit ? 8U : 4; + + if (mvidSection is not null) + mvidSection.Add(new ByteArrayChunk((module.Mvid ?? Guid.Empty).ToByteArray()), MVID_ALIGNMENT); + textSection.Add(importAddressTable, pointerAlignment); textSection.Add(imageCor20Header, DEFAULT_COR20HEADER_ALIGNMENT); textSection.Add(strongNameSignature, DEFAULT_STRONGNAMESIG_ALIGNMENT); + managedExportsWriter.AddTextChunks(textSection); textSection.Add(constants, DEFAULT_CONSTANTS_ALIGNMENT); textSection.Add(methodBodies, DEFAULT_METHODBODIES_ALIGNMENT); textSection.Add(netResources, DEFAULT_NETRESOURCES_ALIGNMENT); - textSection.Add(metaData, DEFAULT_METADATA_ALIGNMENT); - textSection.Add(debugDirectory, DEFAULT_DEBUGDIRECTORY_ALIGNMENT); - textSection.Add(importDirectory, DEFAULT_IMPORTDIRECTORY_ALIGNMENT); - textSection.Add(startupStub, DEFAULT_STARTUPSTUB_ALIGNMENT); - if (rsrcSection != null) + textSection.Add(metadata, DEFAULT_METADATA_ALIGNMENT); + textSection.Add(debugDirectory, DebugDirectory.DEFAULT_DEBUGDIRECTORY_ALIGNMENT); + textSection.Add(importDirectory, pointerAlignment); + textSection.Add(startupStub, startupStub.Alignment); + managedExportsWriter.AddSdataChunks(sdataSection); + if (GetWin32Resources() is not null) rsrcSection.Add(win32Resources, DEFAULT_WIN32_RESOURCES_ALIGNMENT); - if (relocSection != null) - relocSection.Add(relocDirectory, DEFAULT_RELOC_ALIGNMENT); + relocSection.Add(relocDirectory, DEFAULT_RELOC_ALIGNMENT); } long WriteFile() { - Listener.OnWriterEvent(this, ModuleWriterEvent.BeginWritePdb); + managedExportsWriter.AddExportedMethods(metadata.ExportedMethods, GetTimeDateStamp()); + if (managedExportsWriter.HasExports) + needStartupStub = true; + + OnWriterEvent(ModuleWriterEvent.BeginWritePdb); WritePdbFile(); - Listener.OnWriterEvent(this, ModuleWriterEvent.EndWritePdb); + OnWriterEvent(ModuleWriterEvent.EndWritePdb); - Listener.OnWriterEvent(this, ModuleWriterEvent.BeginCalculateRvasAndFileOffsets); + metadata.OnBeforeSetOffset(); + OnWriterEvent(ModuleWriterEvent.BeginCalculateRvasAndFileOffsets); var chunks = new List(); chunks.Add(peHeaders); + if (!managedExportsWriter.HasExports) + sections.Remove(sdataSection); + if (!(relocDirectory.NeedsRelocSection || managedExportsWriter.HasExports || needStartupStub)) + sections.Remove(relocSection); + + importAddressTable.Enable = needStartupStub; + importDirectory.Enable = needStartupStub; + startupStub.Enable = needStartupStub; + foreach (var section in sections) chunks.Add(section); peHeaders.PESections = sections; + int relocIndex = sections.IndexOf(relocSection); + if (relocIndex >= 0 && relocIndex != sections.Count - 1) + throw new InvalidOperationException("Reloc section must be the last section, use AddSection() to add a section"); CalculateRvasAndFileOffsets(chunks, 0, 0, peHeaders.FileAlignment, peHeaders.SectionAlignment); - Listener.OnWriterEvent(this, ModuleWriterEvent.EndCalculateRvasAndFileOffsets); + OnWriterEvent(ModuleWriterEvent.EndCalculateRvasAndFileOffsets); InitializeChunkProperties(); - Listener.OnWriterEvent(this, ModuleWriterEvent.BeginWriteChunks); - var writer = new BinaryWriter(destStream); + OnWriterEvent(ModuleWriterEvent.BeginWriteChunks); + var writer = new DataWriter(destStream); WriteChunks(writer, chunks, 0, peHeaders.FileAlignment); - long imageLength = writer.BaseStream.Position - destStreamBaseOffset; - Listener.OnWriterEvent(this, ModuleWriterEvent.EndWriteChunks); + long imageLength = writer.Position - destStreamBaseOffset; + OnWriterEvent(ModuleWriterEvent.EndWriteChunks); - Listener.OnWriterEvent(this, ModuleWriterEvent.BeginStrongNameSign); - if (Options.StrongNameKey != null) + OnWriterEvent(ModuleWriterEvent.BeginStrongNameSign); + if (Options.StrongNameKey is not null) StrongNameSign((long)strongNameSignature.FileOffset); - Listener.OnWriterEvent(this, ModuleWriterEvent.EndStrongNameSign); + OnWriterEvent(ModuleWriterEvent.EndStrongNameSign); - Listener.OnWriterEvent(this, ModuleWriterEvent.BeginWritePEChecksum); + OnWriterEvent(ModuleWriterEvent.BeginWritePEChecksum); if (Options.AddCheckSum) peHeaders.WriteCheckSum(writer, imageLength); - Listener.OnWriterEvent(this, ModuleWriterEvent.EndWritePEChecksum); + OnWriterEvent(ModuleWriterEvent.EndWritePEChecksum); return imageLength; } @@ -274,13 +283,10 @@ long WriteFile() { void InitializeChunkProperties() { Options.Cor20HeaderOptions.EntryPoint = GetEntryPoint(); - if (importAddressTable != null) { - importAddressTable.ImportDirectory = importDirectory; - importDirectory.ImportAddressTable = importAddressTable; - startupStub.ImportDirectory = importDirectory; - startupStub.PEHeaders = peHeaders; - relocDirectory.StartupStub = startupStub; - } + importAddressTable.ImportDirectory = importDirectory; + importDirectory.ImportAddressTable = importAddressTable; + startupStub.ImportDirectory = importDirectory; + startupStub.PEHeaders = peHeaders; peHeaders.StartupStub = startupStub; peHeaders.ImageCor20Header = imageCor20Header; peHeaders.ImportAddressTable = importAddressTable; @@ -288,19 +294,22 @@ void InitializeChunkProperties() { peHeaders.Win32Resources = win32Resources; peHeaders.RelocDirectory = relocDirectory; peHeaders.DebugDirectory = debugDirectory; - imageCor20Header.MetaData = metaData; + imageCor20Header.Metadata = metadata; imageCor20Header.NetResources = netResources; imageCor20Header.StrongNameSignature = strongNameSignature; + managedExportsWriter.InitializeChunkProperties(); } uint GetEntryPoint() { - var methodEntryPoint = module.ManagedEntryPoint as MethodDef; - if (methodEntryPoint != null) - return new MDToken(Table.Method, metaData.GetRid(methodEntryPoint)).Raw; + var ep = Options.Cor20HeaderOptions.EntryPoint; + if (ep is not null) + return ep.Value; + + if (module.ManagedEntryPoint is MethodDef methodEntryPoint) + return new MDToken(Table.Method, metadata.GetRid(methodEntryPoint)).Raw; - var fileEntryPoint = module.ManagedEntryPoint as FileDef; - if (fileEntryPoint != null) - return new MDToken(Table.File, metaData.GetRid(fileEntryPoint)).Raw; + if (module.ManagedEntryPoint is FileDef fileEntryPoint) + return new MDToken(Table.File, metadata.GetRid(fileEntryPoint)).Raw; uint nativeEntryPoint = (uint)module.NativeEntryPoint; if (nativeEntryPoint != 0) diff --git a/src/DotNet/Writer/ModuleWriterBase.cs b/src/DotNet/Writer/ModuleWriterBase.cs index 2163a78c3..e1b56d6fb 100644 --- a/src/DotNet/Writer/ModuleWriterBase.cs +++ b/src/DotNet/Writer/ModuleWriterBase.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.IO; using dnlib.IO; @@ -8,29 +8,166 @@ using dnlib.PE; using dnlib.W32Resources; using dnlib.DotNet.MD; +using System.Diagnostics; +using dnlib.DotNet.Pdb.WindowsPdb; +using System.Text; +using System.IO.Compression; +using System.Runtime.InteropServices; namespace dnlib.DotNet.Writer { + /// + /// Module writer event args + /// + public readonly struct ModuleWriterEventArgs { + /// + /// Gets the writer ( or ) + /// + public ModuleWriterBase Writer { get; } + + /// + /// Gets the event + /// + public ModuleWriterEvent Event { get; } + + /// + /// Constructor + /// + /// Writer + /// Event + public ModuleWriterEventArgs(ModuleWriterBase writer, ModuleWriterEvent @event) { + Writer = writer ?? throw new ArgumentNullException(nameof(writer)); + Event = @event; + } + } + + /// + /// Module writer progress event args + /// + public readonly struct ModuleWriterProgressEventArgs { + /// + /// Gets the writer ( or ) + /// + public ModuleWriterBase Writer { get; } + + /// + /// Gets the progress, 0.0 - 1.0 + /// + public double Progress { get; } + + /// + /// Constructor + /// + /// Writer + /// Progress, 0.0 - 1.0 + public ModuleWriterProgressEventArgs(ModuleWriterBase writer, double progress) { + if (progress < 0 || progress > 1) + throw new ArgumentOutOfRangeException(nameof(progress)); + Writer = writer ?? throw new ArgumentNullException(nameof(writer)); + Progress = progress; + } + } + + /// + /// Content ID + /// + public readonly struct ContentId { + /// + /// Gets the GUID + /// + public readonly Guid Guid; + + /// + /// Gets the timestamp + /// + public readonly uint Timestamp; + + /// + /// Constructor + /// + /// Guid + /// Timestamp + public ContentId(Guid guid, uint timestamp) { + Guid = guid; + Timestamp = timestamp; + } + } + + /// + /// Event handler + /// + /// Event args type + /// Sender + /// Event args + public delegate void EventHandler2(object sender, TEventArgs e); + + /// + /// PDB writer options + /// + [Flags] + public enum PdbWriterOptions { + /// + /// No bit is set + /// + None = 0, + + /// + /// Don't use Microsoft.DiaSymReader.Native. This is a NuGet package with an updated Windows PDB reader/writer implementation, + /// and if it's available at runtime, dnlib will try to use it. If this option is set, dnlib won't use it. + /// You have to add a reference to the NuGet package if you want to use it, dnlib has no reference to the NuGet package. + /// + /// This is only used if it's a Windows PDB file. + /// + NoDiaSymReader = 0x00000001, + + /// + /// Don't use diasymreader.dll's PDB writer that is shipped with .NET Framework. + /// + /// This is only used if it's a Windows PDB file. + /// + NoOldDiaSymReader = 0x00000002, + + /// + /// Create a deterministic PDB file and add a debug directory entry to the PE file. + /// + /// It's ignored if the PDB writer doesn't support it. + /// + Deterministic = 0x00000004, + + /// + /// Hash the PDB file and add a PDB checksum debug directory entry to the PE file. + /// + /// It's ignored if the PDB writer doesn't support it. + /// + PdbChecksum = 0x00000008, + } + /// /// Common module writer options base class /// public class ModuleWriterOptionsBase { - IModuleWriterListener listener; PEHeadersOptions peHeadersOptions; Cor20HeaderOptions cor20HeaderOptions; - MetaDataOptions metaDataOptions; + MetadataOptions metadataOptions; ILogger logger; - ILogger metaDataLogger; + ILogger metadataLogger; + bool noWin32Resources; Win32Resources win32Resources; StrongNameKey strongNameKey; StrongNamePublicKey strongNamePublicKey; + bool delaySign; /// - /// Gets/sets the listener + /// Raised at various times when writing the file. The listener has a chance to modify + /// the file, eg. add extra metadata, encrypt methods, etc. /// - public IModuleWriterListener Listener { - get { return listener; } - set { listener = value; } - } + public event EventHandler2 WriterEvent; + internal void RaiseEvent(object sender, ModuleWriterEventArgs e) => WriterEvent?.Invoke(sender, e); + + /// + /// Raised when the progress is updated + /// + public event EventHandler2 ProgressUpdated; + internal void RaiseEvent(object sender, ModuleWriterProgressEventArgs e) => ProgressUpdated?.Invoke(sender, e); /// /// Gets/sets the logger. If this is null, any errors result in a @@ -38,41 +175,49 @@ public IModuleWriterListener Listener { /// create your own logger or use . /// public ILogger Logger { - get { return logger; } - set { logger = value; } + get => logger; + set => logger = value; } /// - /// Gets/sets the writer logger. If this is null, use + /// Gets/sets the writer logger. If this is null, use /// . /// - public ILogger MetaDataLogger { - get { return metaDataLogger; } - set { metaDataLogger = value; } + public ILogger MetadataLogger { + get => metadataLogger; + set => metadataLogger = value; } /// /// Gets/sets the options. This is never null. /// public PEHeadersOptions PEHeadersOptions { - get { return peHeadersOptions ?? (peHeadersOptions = new PEHeadersOptions()); } - set { peHeadersOptions = value; } + get => peHeadersOptions ??= new PEHeadersOptions(); + set => peHeadersOptions = value; } /// /// Gets/sets the options. This is never null. /// public Cor20HeaderOptions Cor20HeaderOptions { - get { return cor20HeaderOptions ?? (cor20HeaderOptions = new Cor20HeaderOptions()); } - set { cor20HeaderOptions = value; } + get => cor20HeaderOptions ??= new Cor20HeaderOptions(); + set => cor20HeaderOptions = value; } /// - /// Gets/sets the options. This is never null. + /// Gets/sets the options. This is never null. /// - public MetaDataOptions MetaDataOptions { - get { return metaDataOptions ?? (metaDataOptions = new MetaDataOptions()); } - set { metaDataOptions = value; } + public MetadataOptions MetadataOptions { + get => metadataOptions ??= new MetadataOptions(); + set => metadataOptions = value; + } + + /// + /// If true, Win32 resources aren't written to the output + /// + public bool NoWin32Resources { + get => noWin32Resources; + set => noWin32Resources = value; } /// @@ -80,8 +225,18 @@ public MetaDataOptions MetaDataOptions { /// Win32 resources if any. /// public Win32Resources Win32Resources { - get { return win32Resources; } - set { win32Resources = value; } + get => win32Resources; + set => win32Resources = value; + } + + /// + /// true to delay sign the assembly. Initialize to the + /// public key to use, and don't initialize . To generate the + /// public key from your strong name key file, execute sn -p mykey.snk mypublickey.snk + /// + public bool DelaySign { + get => delaySign; + set => delaySign = value; } /// @@ -94,8 +249,8 @@ public Win32Resources Win32Resources { /// to initialize this property if you use enhanced strong name signing. /// public StrongNameKey StrongNameKey { - get { return strongNameKey; } - set { strongNameKey = value; } + get => strongNameKey; + set => strongNameKey = value; } /// @@ -107,14 +262,15 @@ public StrongNameKey StrongNameKey { /// to initialize this property if you use enhanced strong name signing. /// public StrongNamePublicKey StrongNamePublicKey { - get { return strongNamePublicKey; } - set { strongNamePublicKey = value; } + get => strongNamePublicKey; + set => strongNamePublicKey = value; } /// /// true if method bodies can be shared (two or more method bodies can share the /// same RVA), false if method bodies can't be shared. Don't enable it if there /// must be a 1:1 relationship with method bodies and their RVAs. + /// This is enabled by default and results in smaller files. /// public bool ShareMethodBodies { get; set; } @@ -131,9 +287,7 @@ public bool Is64Bit { get { if (!PEHeadersOptions.Machine.HasValue) return false; - return PEHeadersOptions.Machine == Machine.IA64 || - PEHeadersOptions.Machine == Machine.AMD64 || - PEHeadersOptions.Machine == Machine.ARM64; + return PEHeadersOptions.Machine.Value.Is64Bit(); } } @@ -146,12 +300,7 @@ public bool Is64Bit { /// true if it should be written as an EXE file, false if it should be /// written as a DLL file. /// - public bool IsExeFile { - get { - return ModuleKind != ModuleKind.Dll && - ModuleKind != ModuleKind.NetModule; - } - } + public bool IsExeFile => ModuleKind != ModuleKind.Dll && ModuleKind != ModuleKind.NetModule; /// /// Set it to true to enable writing a PDB file. Default is false (a PDB file @@ -159,6 +308,11 @@ public bool IsExeFile { /// public bool WritePdb { get; set; } + /// + /// PDB writer options. This property is ignored if is false. + /// + public PdbWriterOptions PdbOptions { get; set; } + /// /// PDB file name. If it's null a PDB file with the same name as the output assembly /// will be created but with a PDB extension. must be true or @@ -166,6 +320,11 @@ public bool IsExeFile { /// public string PdbFileName { get; set; } + /// + /// PDB file name stored in the debug directory, or null to use + /// + public string PdbFileNameInDebugDirectory { get; set; } + /// /// PDB stream. If this is initialized, then you should also set /// to the name of the PDB file since the file name must be written to the PE debug directory. @@ -174,37 +333,31 @@ public bool IsExeFile { public Stream PdbStream { get; set; } /// - /// If or aren't enough, this can be used - /// to create a new instance. must be - /// true or this property is ignored. + /// Gets the PDB content id (portable PDBs). The argument is the PDB stream with the PDB ID zeroed out, + /// and the 2nd argument is the default timestamp. + /// This property is ignored if a deterministic PDB file is created or if the PDB checksum is calculated. /// - public CreatePdbSymbolWriterDelegate CreatePdbSymbolWriter { get; set; } + public Func GetPdbContentId { get; set; } + + const ChecksumAlgorithm DefaultPdbChecksumAlgorithm = ChecksumAlgorithm.SHA256; /// - /// Default constructor + /// PDB checksum algorithm /// - protected ModuleWriterOptionsBase() { - ShareMethodBodies = true; - ModuleKind = ModuleKind.Windows; - } + public ChecksumAlgorithm PdbChecksumAlgorithm { get; set; } = DefaultPdbChecksumAlgorithm; /// - /// Constructor + /// true if an .mvid section should be added to the assembly. Not used by native module writer. /// - /// The module - protected ModuleWriterOptionsBase(ModuleDef module) - : this(module, null) { - } + public bool AddMvidSection { get; set; } /// /// Constructor /// /// The module - /// Module writer listener - protected ModuleWriterOptionsBase(ModuleDef module, IModuleWriterListener listener) { - this.listener = listener; + protected ModuleWriterOptionsBase(ModuleDef module) { ShareMethodBodies = true; - MetaDataOptions.MetaDataHeaderOptions.VersionString = module.RuntimeVersion; + MetadataOptions.MetadataHeaderOptions.VersionString = module.RuntimeVersion; ModuleKind = module.Kind; PEHeadersOptions.Machine = module.Machine; PEHeadersOptions.Characteristics = module.Characteristics; @@ -216,10 +369,10 @@ protected ModuleWriterOptionsBase(ModuleDef module, IModuleWriterListener listen PEHeadersOptions.NumberOfRvaAndSizes = 0x10; Cor20HeaderOptions.Flags = module.Cor20HeaderFlags; - if (module.Assembly != null && !PublicKeyBase.IsNullOrEmpty2(module.Assembly.PublicKey)) + if (module.Assembly is not null && !PublicKeyBase.IsNullOrEmpty2(module.Assembly.PublicKey)) Cor20HeaderOptions.Flags |= ComImageFlags.StrongNameSigned; - if (module.Cor20HeaderRuntimeVersion != null) { + if (module.Cor20HeaderRuntimeVersion is not null) { Cor20HeaderOptions.MajorRuntimeVersion = (ushort)(module.Cor20HeaderRuntimeVersion.Value >> 16); Cor20HeaderOptions.MinorRuntimeVersion = (ushort)module.Cor20HeaderRuntimeVersion.Value; } @@ -232,40 +385,108 @@ protected ModuleWriterOptionsBase(ModuleDef module, IModuleWriterListener listen Cor20HeaderOptions.MinorRuntimeVersion = 5; } - if (module.TablesHeaderVersion != null) { - MetaDataOptions.TablesHeapOptions.MajorVersion = (byte)(module.TablesHeaderVersion.Value >> 8); - MetaDataOptions.TablesHeapOptions.MinorVersion = (byte)module.TablesHeaderVersion.Value; + if (module.TablesHeaderVersion is not null) { + MetadataOptions.TablesHeapOptions.MajorVersion = (byte)(module.TablesHeaderVersion.Value >> 8); + MetadataOptions.TablesHeapOptions.MinorVersion = (byte)module.TablesHeaderVersion.Value; } else if (module.IsClr1x) { // Generics aren't supported - MetaDataOptions.TablesHeapOptions.MajorVersion = 1; - MetaDataOptions.TablesHeapOptions.MinorVersion = 0; + MetadataOptions.TablesHeapOptions.MajorVersion = 1; + MetadataOptions.TablesHeapOptions.MinorVersion = 0; } else { // Generics are supported - MetaDataOptions.TablesHeapOptions.MajorVersion = 2; - MetaDataOptions.TablesHeapOptions.MinorVersion = 0; + MetadataOptions.TablesHeapOptions.MajorVersion = 2; + MetadataOptions.TablesHeapOptions.MinorVersion = 0; } // Some tools crash if #GUID is missing so always create it by default - MetaDataOptions.Flags |= MetaDataFlags.AlwaysCreateGuidHeap; + MetadataOptions.Flags |= MetadataFlags.AlwaysCreateGuidHeap; var modDefMD = module as ModuleDefMD; - if (modDefMD != null) { - var ntHeaders = modDefMD.MetaData.PEImage.ImageNTHeaders; + if (modDefMD is not null) { + var ntHeaders = modDefMD.Metadata.PEImage.ImageNTHeaders; PEHeadersOptions.TimeDateStamp = ntHeaders.FileHeader.TimeDateStamp; PEHeadersOptions.MajorLinkerVersion = ntHeaders.OptionalHeader.MajorLinkerVersion; PEHeadersOptions.MinorLinkerVersion = ntHeaders.OptionalHeader.MinorLinkerVersion; PEHeadersOptions.ImageBase = ntHeaders.OptionalHeader.ImageBase; + PEHeadersOptions.MajorOperatingSystemVersion = ntHeaders.OptionalHeader.MajorOperatingSystemVersion; + PEHeadersOptions.MinorOperatingSystemVersion = ntHeaders.OptionalHeader.MinorOperatingSystemVersion; + PEHeadersOptions.MajorImageVersion = ntHeaders.OptionalHeader.MajorImageVersion; + PEHeadersOptions.MinorImageVersion = ntHeaders.OptionalHeader.MinorImageVersion; + PEHeadersOptions.MajorSubsystemVersion = ntHeaders.OptionalHeader.MajorSubsystemVersion; + PEHeadersOptions.MinorSubsystemVersion = ntHeaders.OptionalHeader.MinorSubsystemVersion; + PEHeadersOptions.Win32VersionValue = ntHeaders.OptionalHeader.Win32VersionValue; AddCheckSum = ntHeaders.OptionalHeader.CheckSum != 0; + AddMvidSection = HasMvidSection(modDefMD.Metadata.PEImage.ImageSectionHeaders); + if (HasDebugDirectoryEntry(modDefMD.Metadata.PEImage.ImageDebugDirectories, ImageDebugType.Reproducible)) + PdbOptions |= PdbWriterOptions.Deterministic; + if (HasDebugDirectoryEntry(modDefMD.Metadata.PEImage.ImageDebugDirectories, ImageDebugType.PdbChecksum)) + PdbOptions |= PdbWriterOptions.PdbChecksum; + if (TryGetPdbChecksumAlgorithm(modDefMD.Metadata.PEImage, modDefMD.Metadata.PEImage.ImageDebugDirectories, out var pdbChecksumAlgorithm)) + PdbChecksumAlgorithm = pdbChecksumAlgorithm; + MetadataOptions.TablesHeapOptions.Log2Rid = modDefMD.TablesStream.Log2Rid; } if (Is64Bit) { - PEHeadersOptions.Characteristics &= ~Characteristics._32BitMachine; + PEHeadersOptions.Characteristics &= ~Characteristics.Bit32Machine; PEHeadersOptions.Characteristics |= Characteristics.LargeAddressAware; } - else - PEHeadersOptions.Characteristics |= Characteristics._32BitMachine; + else if (modDefMD is null) + PEHeadersOptions.Characteristics |= Characteristics.Bit32Machine; + } + + static bool HasMvidSection(IList sections) { + int count = sections.Count; + for (int i = 0; i < count; i++) { + var section = sections[i]; + if (section.VirtualSize != 16) + continue; + var name = section.Name; + // Roslyn ignores the last 2 bytes + if (name[0] == '.' && name[1] == 'm' && name[2] == 'v' && name[3] == 'i' && name[4] == 'd' && name[5] == 0) + return true; + } + return false; + } + + static bool HasDebugDirectoryEntry(IList debugDirs, ImageDebugType type) { + int count = debugDirs.Count; + for (int i = 0; i < count; i++) { + if (debugDirs[i].Type == type) + return true; + } + return false; + } + + static bool TryGetPdbChecksumAlgorithm(IPEImage peImage, IList debugDirs, out ChecksumAlgorithm pdbChecksumAlgorithm) { + int count = debugDirs.Count; + for (int i = 0; i < count; i++) { + var dir = debugDirs[i]; + if (dir.Type == ImageDebugType.PdbChecksum) { + var reader = peImage.CreateReader(dir.AddressOfRawData, dir.SizeOfData); + if (TryGetPdbChecksumAlgorithm(ref reader, out pdbChecksumAlgorithm)) + return true; + } + } + + pdbChecksumAlgorithm = DefaultPdbChecksumAlgorithm; + return false; + } + + static bool TryGetPdbChecksumAlgorithm(ref DataReader reader, out ChecksumAlgorithm pdbChecksumAlgorithm) { + try { + var checksumName = reader.TryReadZeroTerminatedUtf8String(); + if (Hasher.TryGetChecksumAlgorithm(checksumName, out pdbChecksumAlgorithm, out int checksumSize) && (uint)checksumSize == reader.BytesLeft) + return true; + } + catch (IOException) { + } + catch (ArgumentException) { + } + + pdbChecksumAlgorithm = DefaultPdbChecksumAlgorithm; + return false; } /// @@ -277,7 +498,7 @@ protected ModuleWriterOptionsBase(ModuleDef module, IModuleWriterListener listen public void InitializeStrongNameSigning(ModuleDef module, StrongNameKey signatureKey) { StrongNameKey = signatureKey; StrongNamePublicKey = null; - if (module.Assembly != null) + if (module.Assembly is not null) module.Assembly.CustomAttributes.RemoveAll("System.Reflection.AssemblySignatureKeyAttribute"); } @@ -291,7 +512,7 @@ public void InitializeStrongNameSigning(ModuleDef module, StrongNameKey signatur /// Signature public key public void InitializeEnhancedStrongNameSigning(ModuleDef module, StrongNameKey signatureKey, StrongNamePublicKey signaturePubKey) { InitializeStrongNameSigning(module, signatureKey); - StrongNameKey.HashAlgorithm = signaturePubKey.HashAlgorithm; + StrongNameKey = StrongNameKey.WithHashAlgorithm(signaturePubKey.HashAlgorithm); } /// @@ -305,41 +526,31 @@ public void InitializeEnhancedStrongNameSigning(ModuleDef module, StrongNameKey /// Identity strong name key pair /// Identity public key public void InitializeEnhancedStrongNameSigning(ModuleDef module, StrongNameKey signatureKey, StrongNamePublicKey signaturePubKey, StrongNameKey identityKey, StrongNamePublicKey identityPubKey) { - StrongNameKey = signatureKey; - StrongNameKey.HashAlgorithm = signaturePubKey.HashAlgorithm; + StrongNameKey = signatureKey.WithHashAlgorithm(signaturePubKey.HashAlgorithm); StrongNamePublicKey = identityPubKey; - if (module.Assembly != null) + if (module.Assembly is not null) module.Assembly.UpdateOrCreateAssemblySignatureKeyAttribute(identityPubKey, identityKey, signaturePubKey); } } - /// - /// Creates a new instance - /// - /// Module writer - /// A new instance - public delegate ISymbolWriter2 CreatePdbSymbolWriterDelegate(ModuleWriterBase writer); - /// /// Module writer base class /// - public abstract class ModuleWriterBase : IMetaDataListener, ILogger { + public abstract class ModuleWriterBase : ILogger { /// Default alignment of all constants protected internal const uint DEFAULT_CONSTANTS_ALIGNMENT = 8; /// Default alignment of all method bodies protected const uint DEFAULT_METHODBODIES_ALIGNMENT = 4; /// Default alignment of all .NET resources - protected const uint DEFAULT_NETRESOURCES_ALIGNMENT = 8; + protected const uint DEFAULT_NETRESOURCES_ALIGNMENT = 4; /// Default alignment of the .NET metadata protected const uint DEFAULT_METADATA_ALIGNMENT = 4; /// Default Win32 resources alignment protected internal const uint DEFAULT_WIN32_RESOURCES_ALIGNMENT = 8; /// Default strong name signature alignment - protected const uint DEFAULT_STRONGNAMESIG_ALIGNMENT = 16; + protected const uint DEFAULT_STRONGNAMESIG_ALIGNMENT = 4; /// Default COR20 header alignment protected const uint DEFAULT_COR20HEADER_ALIGNMENT = 4; - /// Default debug directory alignment - protected const uint DEFAULT_DEBUGDIRECTORY_ALIGNMENT = 4; /// See protected Stream destStream; @@ -349,13 +560,12 @@ public abstract class ModuleWriterBase : IMetaDataListener, ILogger { protected MethodBodyChunks methodBodies; /// See protected NetResources netResources; - /// See - protected MetaData metaData; + /// See + protected Metadata metadata; /// See protected Win32ResourcesChunk win32Resources; /// Offset where the module is written. Usually 0. protected long destStreamBaseOffset; - IModuleWriterListener listener; /// Debug directory protected DebugDirectory debugDirectory; @@ -371,68 +581,52 @@ public abstract class ModuleWriterBase : IMetaDataListener, ILogger { /// public abstract ModuleWriterOptionsBase TheOptions { get; } - /// - /// Gets/sets the module writer listener - /// - protected IModuleWriterListener Listener { - get { return listener ?? DummyModuleWriterListener.Instance; } - set { listener = value; } - } - /// /// Gets the destination stream /// - public Stream DestinationStream { - get { return destStream; } - } + public Stream DestinationStream => destStream; /// /// Gets the constants /// - public UniqueChunkList Constants { - get { return constants; } - } + public UniqueChunkList Constants => constants; /// /// Gets the method bodies /// - public MethodBodyChunks MethodBodies { - get { return methodBodies; } - } + public MethodBodyChunks MethodBodies => methodBodies; /// /// Gets the .NET resources /// - public NetResources NetResources { - get { return netResources; } - } + public NetResources NetResources => netResources; /// /// Gets the .NET metadata /// - public MetaData MetaData { - get { return metaData; } - } + public Metadata Metadata => metadata; /// /// Gets the Win32 resources or null if there's none /// - public Win32ResourcesChunk Win32Resources { - get { return win32Resources; } - } + public Win32ResourcesChunk Win32Resources => win32Resources; /// /// Gets the strong name signature or null if there's none /// - public StrongNameSignature StrongNameSignature { - get { return strongNameSignature; } - } + public StrongNameSignature StrongNameSignature => strongNameSignature; /// - /// Gets all s + /// Gets all s. The reloc section must be the last section, so use if you need to append a section /// public abstract List Sections { get; } + /// + /// Adds to the sections list, but before the reloc section which must be last + /// + /// New section to add to the list + public virtual void AddSection(PESection section) => Sections.Add(section); + /// /// Gets the .text section /// @@ -446,17 +640,18 @@ public StrongNameSignature StrongNameSignature { /// /// Gets the debug directory or null if there's none /// - public DebugDirectory DebugDirectory { - get { return debugDirectory; } - } + public DebugDirectory DebugDirectory => debugDirectory; /// /// true if this is a , false if /// this is a . /// - public bool IsNativeWriter { - get { return this is NativeModuleWriter; } - } + public bool IsNativeWriter => this is NativeModuleWriter; + + /// + /// null if we're not writing a PDB + /// + PdbState pdbState; /// /// Writes the module to a file @@ -492,16 +687,21 @@ static void DeleteFileNoThrow(string fileName) { /// /// Destination stream public void Write(Stream dest) { - if (TheOptions.StrongNameKey != null || TheOptions.StrongNamePublicKey != null) + pdbState = TheOptions.WritePdb && Module.PdbState is not null ? Module.PdbState : null; + if (TheOptions.DelaySign) { + Debug.Assert(TheOptions.StrongNamePublicKey is not null, "Options.StrongNamePublicKey must be initialized when delay signing the assembly"); + Debug.Assert(TheOptions.StrongNameKey is null, "Options.StrongNameKey must be null when delay signing the assembly"); + TheOptions.Cor20HeaderOptions.Flags &= ~ComImageFlags.StrongNameSigned; + } + else if (TheOptions.StrongNameKey is not null || TheOptions.StrongNamePublicKey is not null) TheOptions.Cor20HeaderOptions.Flags |= ComImageFlags.StrongNameSigned; - Listener = TheOptions.Listener ?? DummyModuleWriterListener.Instance; destStream = dest; destStreamBaseOffset = destStream.Position; - Listener.OnWriterEvent(this, ModuleWriterEvent.Begin); + OnWriterEvent(ModuleWriterEvent.Begin); var imageLength = WriteImpl(); destStream.Position = destStreamBaseOffset + imageLength; - Listener.OnWriterEvent(this, ModuleWriterEvent.End); + OnWriterEvent(ModuleWriterEvent.End); } /// @@ -510,7 +710,7 @@ public void Write(Stream dest) { public abstract ModuleDef Module { get; } /// - /// Writes the module to . and + /// Writes the module to . Event listeners and /// have been initialized when this method is called. /// /// Number of bytes written @@ -521,9 +721,13 @@ public void Write(Stream dest) { /// set or wants to sign the assembly. /// protected void CreateStrongNameSignature() { - if (TheOptions.StrongNameKey != null) + if (TheOptions.DelaySign && TheOptions.StrongNamePublicKey is not null) { + int len = TheOptions.StrongNamePublicKey.CreatePublicKey().Length - 0x20; + strongNameSignature = new StrongNameSignature(len > 0 ? len : 0x80); + } + else if (TheOptions.StrongNameKey is not null) strongNameSignature = new StrongNameSignature(TheOptions.StrongNameKey.SignatureSize); - else if (Module.Assembly != null && !PublicKeyBase.IsNullOrEmpty2(Module.Assembly.PublicKey)) { + else if (Module.Assembly is not null && !PublicKeyBase.IsNullOrEmpty2(Module.Assembly.PublicKey)) { int len = Module.Assembly.PublicKey.Data.Length - 0x20; strongNameSignature = new StrongNameSignature(len > 0 ? len : 0x80); } @@ -536,25 +740,31 @@ protected void CreateStrongNameSignature() { /// the metadata, and Win32 resources) /// /// - protected void CreateMetaDataChunks(ModuleDef module) { + protected void CreateMetadataChunks(ModuleDef module) { constants = new UniqueChunkList(); methodBodies = new MethodBodyChunks(TheOptions.ShareMethodBodies); netResources = new NetResources(DEFAULT_NETRESOURCES_ALIGNMENT); - metaData = MetaData.Create(module, constants, methodBodies, netResources, TheOptions.MetaDataOptions); - metaData.Logger = TheOptions.MetaDataLogger ?? this; - metaData.Listener = this; + DebugMetadataKind debugKind; + if (pdbState is not null && (pdbState.PdbFileKind == PdbFileKind.PortablePDB || pdbState.PdbFileKind == PdbFileKind.EmbeddedPortablePDB)) + debugKind = DebugMetadataKind.Standalone; + else + debugKind = DebugMetadataKind.None; + metadata = Metadata.Create(module, constants, methodBodies, netResources, TheOptions.MetadataOptions, debugKind); + metadata.Logger = TheOptions.MetadataLogger ?? this; + metadata.MetadataEvent += Metadata_MetadataEvent; + metadata.ProgressUpdated += Metadata_ProgressUpdated; // StrongNamePublicKey is used if the user wants to override the assembly's // public key or when enhanced strong naming the assembly. var pk = TheOptions.StrongNamePublicKey; - if (pk != null) - metaData.AssemblyPublicKey = pk.CreatePublicKey(); - else if (TheOptions.StrongNameKey != null) - metaData.AssemblyPublicKey = TheOptions.StrongNameKey.PublicKey; + if (pk is not null) + metadata.AssemblyPublicKey = pk.CreatePublicKey(); + else if (TheOptions.StrongNameKey is not null) + metadata.AssemblyPublicKey = TheOptions.StrongNameKey.PublicKey; var w32Resources = GetWin32Resources(); - if (w32Resources != null) + if (w32Resources is not null) win32Resources = new Win32ResourcesChunk(w32Resources); } @@ -572,12 +782,24 @@ protected void CreateMetaDataChunks(ModuleDef module) { /// File alignment /// Section alignment protected void CalculateRvasAndFileOffsets(List chunks, FileOffset offset, RVA rva, uint fileAlignment, uint sectionAlignment) { - foreach (var chunk in chunks) { + int count = chunks.Count; + var maxAlignment = Math.Min(fileAlignment, sectionAlignment); + for (int i = 0; i < count; i++) { + var chunk = chunks[i]; + // We don't need to align to result of CalculateAlignment as all the chunks in `chunks` either + // don't need a specific alignment or align themselves. + var alignment = chunk.CalculateAlignment(); + if (alignment > maxAlignment) + Error("Chunk alignment is too big. Chunk: {0}, alignment: {1:X4}", chunk, alignment); + chunk.SetOffset(offset, rva); - offset += chunk.GetFileLength(); - rva += chunk.GetVirtualSize(); - offset = offset.AlignUp(fileAlignment); - rva = rva.AlignUp(sectionAlignment); + // If it has zero size, it's not present in the file (eg. a section that wasn't needed) + if (chunk.GetVirtualSize() != 0) { + offset += chunk.GetFileLength(); + rva += chunk.GetVirtualSize(); + offset = offset.AlignUp(fileAlignment); + rva = rva.AlignUp(sectionAlignment); + } } } @@ -588,13 +810,18 @@ protected void CalculateRvasAndFileOffsets(List chunks, FileOffset offse /// All chunks /// File offset of first chunk /// File alignment - protected void WriteChunks(BinaryWriter writer, List chunks, FileOffset offset, uint fileAlignment) { - foreach (var chunk in chunks) { + protected void WriteChunks(DataWriter writer, List chunks, FileOffset offset, uint fileAlignment) { + int count = chunks.Count; + for (int i = 0; i < count; i++) { + var chunk = chunks[i]; chunk.VerifyWriteTo(writer); - offset += chunk.GetFileLength(); - var newOffset = offset.AlignUp(fileAlignment); - writer.WriteZeros((int)(newOffset - offset)); - offset = newOffset; + // If it has zero size, it's not present in the file (eg. a section that wasn't needed) + if (chunk.GetVirtualSize() != 0) { + offset += chunk.GetFileLength(); + var newOffset = offset.AlignUp(fileAlignment); + writer.WriteZeroes((int)(newOffset - offset)); + offset = newOffset; + } } } @@ -607,9 +834,7 @@ protected void StrongNameSign(long snSigOffset) { snSigner.WriteSignature(TheOptions.StrongNameKey, snSigOffset); } - bool CanWritePdb() { - return TheOptions.WritePdb && Module.PdbState != null; - } + bool CanWritePdb() => pdbState is not null; /// /// Creates the debug directory if a PDB file should be written @@ -626,40 +851,98 @@ protected void CreateDebugDirectory() { protected void WritePdbFile() { if (!CanWritePdb()) return; - if (debugDirectory == null) + if (debugDirectory is null) throw new InvalidOperationException("debugDirectory is null but WritePdb is true"); - var pdbState = Module.PdbState; - if (pdbState == null) { + if (pdbState is null) { Error("TheOptions.WritePdb is true but module has no PdbState"); - debugDirectory.DontWriteAnything = true; return; } - var symWriter = GetSymbolWriter2(); - if (symWriter == null) { + try { + switch (pdbState.PdbFileKind) { + case PdbFileKind.WindowsPDB: + WriteWindowsPdb(pdbState); + break; + + case PdbFileKind.PortablePDB: + WritePortablePdb(pdbState, false); + break; + + case PdbFileKind.EmbeddedPortablePDB: + WritePortablePdb(pdbState, true); + break; + + default: + Error("Invalid PDB file kind {0}", pdbState.PdbFileKind); + break; + } + } + catch { + DeleteFileNoThrow(createdPdbFileName); + throw; + } + } + + void AddReproduciblePdbDebugDirectoryEntry() => + debugDirectory.Add(Array2.Empty(), type: ImageDebugType.Reproducible, majorVersion: 0, minorVersion: 0, timeDateStamp: 0); + + void AddPdbChecksumDebugDirectoryEntry(byte[] checksumBytes, ChecksumAlgorithm checksumAlgorithm) { + var stream = new MemoryStream(); + var writer = new DataWriter(stream); + var checksumName = Hasher.GetChecksumName(checksumAlgorithm); + writer.WriteBytes(Encoding.UTF8.GetBytes(checksumName)); + writer.WriteByte(0); + writer.WriteBytes(checksumBytes); + var blob = stream.ToArray(); + debugDirectory.Add(blob, ImageDebugType.PdbChecksum, majorVersion: 1, minorVersion: 0, timeDateStamp: 0); + } + + const uint PdbAge = 1; + void WriteWindowsPdb(PdbState pdbState) { + bool addPdbChecksumDebugDirectoryEntry = (TheOptions.PdbOptions & PdbWriterOptions.PdbChecksum) != 0; + addPdbChecksumDebugDirectoryEntry = false;//TODO: If this is true, get the checksum from the PDB writer + var symWriter = GetWindowsPdbSymbolWriter(TheOptions.PdbOptions, out var pdbFilename); + if (symWriter is null) { Error("Could not create a PDB symbol writer. A Windows OS might be required."); - debugDirectory.DontWriteAnything = true; return; } - var pdbWriter = new PdbWriter(symWriter, pdbState, metaData); - try { + using (var pdbWriter = new WindowsPdbWriter(symWriter, pdbState, metadata)) { pdbWriter.Logger = TheOptions.Logger; pdbWriter.Write(); - debugDirectory.Data = pdbWriter.GetDebugInfo(out debugDirectory.debugDirData); - debugDirectory.TimeDateStamp = GetTimeDateStamp(); - pdbWriter.Dispose(); - } - catch { - pdbWriter.Dispose(); - DeleteFileNoThrow(createdPdbFileName); - throw; + var pdbAge = PdbAge; + bool hasContentId = pdbWriter.GetDebugInfo(TheOptions.PdbChecksumAlgorithm, ref pdbAge, out var pdbGuid, out uint stamp, out var idd, out var codeViewData); + if (hasContentId) { + debugDirectory.Add(GetCodeViewData(pdbGuid, pdbAge, TheOptions.PdbFileNameInDebugDirectory ?? pdbFilename), + type: ImageDebugType.CodeView, + majorVersion: 0, + minorVersion: 0, + timeDateStamp: stamp); + } + else { + Debug.Fail("Failed to get the PDB content ID"); + if (codeViewData is null) + throw new InvalidOperationException(); + var entry = debugDirectory.Add(codeViewData); + entry.DebugDirectory = idd; + entry.DebugDirectory.TimeDateStamp = GetTimeDateStamp(); + } + + //TODO: Only do this if symWriter supports PDB checksums + if (addPdbChecksumDebugDirectoryEntry) + {}//TODO: AddPdbChecksumDebugDirectoryEntry(checksumBytes, TheOptions.PdbChecksumAlgorithm);, and verify that the order of the debug dir entries is the same as Roslyn created binaries + if (symWriter.IsDeterministic) + AddReproduciblePdbDebugDirectoryEntry(); } } - uint GetTimeDateStamp() { + /// + /// Gets the timestamp stored in the PE header + /// + /// + protected uint GetTimeDateStamp() { var td = TheOptions.PEHeadersOptions.TimeDateStamp; if (td.HasValue) return (uint)td; @@ -667,38 +950,44 @@ uint GetTimeDateStamp() { return (uint)TheOptions.PEHeadersOptions.TimeDateStamp; } - ISymbolWriter2 GetSymbolWriter2() { - if (TheOptions.CreatePdbSymbolWriter != null) { - var writer = TheOptions.CreatePdbSymbolWriter(this); - if (writer != null) - return writer; + SymbolWriter GetWindowsPdbSymbolWriter(PdbWriterOptions options, out string pdbFilename) { +#if NETSTANDARD || NETCOREAPP + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + pdbFilename = null; + return null; } - - if (TheOptions.PdbStream != null) { - return SymbolWriterCreator.Create(TheOptions.PdbStream, - TheOptions.PdbFileName ?? +#endif + if (TheOptions.PdbStream is not null) { + return Pdb.Dss.SymbolReaderWriterFactory.Create(options, TheOptions.PdbStream, + pdbFilename = TheOptions.PdbFileName ?? GetStreamName(TheOptions.PdbStream) ?? GetDefaultPdbFileName()); } if (!string.IsNullOrEmpty(TheOptions.PdbFileName)) { - createdPdbFileName = TheOptions.PdbFileName; - return SymbolWriterCreator.Create(createdPdbFileName); + createdPdbFileName = pdbFilename = TheOptions.PdbFileName; + return Pdb.Dss.SymbolReaderWriterFactory.Create(options, createdPdbFileName); } - createdPdbFileName = GetDefaultPdbFileName(); - if (createdPdbFileName == null) + createdPdbFileName = pdbFilename = GetDefaultPdbFileName(); + if (createdPdbFileName is null) return null; - return SymbolWriterCreator.Create(createdPdbFileName); + return Pdb.Dss.SymbolReaderWriterFactory.Create(options, createdPdbFileName); } - static string GetStreamName(Stream stream) { - var fs = stream as FileStream; - return fs == null ? null : fs.Name; + static string GetStreamName(Stream stream) => (stream as FileStream)?.Name; + + static string GetModuleName(ModuleDef module) { + var name = module.Name ?? string.Empty; + if (string.IsNullOrEmpty(name)) + return null; + if (name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) || name.EndsWith(".netmodule", StringComparison.OrdinalIgnoreCase)) + return name; + return name + ".pdb"; } string GetDefaultPdbFileName() { - var destFileName = GetStreamName(destStream); + var destFileName = GetStreamName(destStream) ?? GetModuleName(Module); if (string.IsNullOrEmpty(destFileName)) { Error("TheOptions.WritePdb is true but it's not possible to guess the default PDB file name. Set PdbFileName to the name of the PDB file."); return null; @@ -707,200 +996,285 @@ string GetDefaultPdbFileName() { return Path.ChangeExtension(destFileName, "pdb"); } - /// - void IMetaDataListener.OnMetaDataEvent(MetaData metaData, MetaDataEvent evt) { - switch (evt) { - case MetaDataEvent.BeginCreateTables: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDBeginCreateTables); - break; - - case MetaDataEvent.AllocateTypeDefRids: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDAllocateTypeDefRids); - break; - - case MetaDataEvent.AllocateMemberDefRids: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDAllocateMemberDefRids); - break; - - case MetaDataEvent.AllocateMemberDefRids0: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDAllocateMemberDefRids0); - break; - - case MetaDataEvent.AllocateMemberDefRids1: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDAllocateMemberDefRids1); - break; - - case MetaDataEvent.AllocateMemberDefRids2: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDAllocateMemberDefRids2); - break; - - case MetaDataEvent.AllocateMemberDefRids3: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDAllocateMemberDefRids3); - break; - - case MetaDataEvent.AllocateMemberDefRids4: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDAllocateMemberDefRids4); - break; - - case MetaDataEvent.MemberDefRidsAllocated: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDMemberDefRidsAllocated); - break; - - case MetaDataEvent.InitializeTypeDefsAndMemberDefs0: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDInitializeTypeDefsAndMemberDefs0); - break; - - case MetaDataEvent.InitializeTypeDefsAndMemberDefs1: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDInitializeTypeDefsAndMemberDefs1); - break; - - case MetaDataEvent.InitializeTypeDefsAndMemberDefs2: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDInitializeTypeDefsAndMemberDefs2); - break; - - case MetaDataEvent.InitializeTypeDefsAndMemberDefs3: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDInitializeTypeDefsAndMemberDefs3); - break; - - case MetaDataEvent.InitializeTypeDefsAndMemberDefs4: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDInitializeTypeDefsAndMemberDefs4); - break; - - case MetaDataEvent.MemberDefsInitialized: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDMemberDefsInitialized); - break; - - case MetaDataEvent.BeforeSortTables: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDBeforeSortTables); - break; - - case MetaDataEvent.MostTablesSorted: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDMostTablesSorted); - break; - - case MetaDataEvent.WriteTypeDefAndMemberDefCustomAttributes0: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteTypeDefAndMemberDefCustomAttributes0); - break; - - case MetaDataEvent.WriteTypeDefAndMemberDefCustomAttributes1: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteTypeDefAndMemberDefCustomAttributes1); - break; - - case MetaDataEvent.WriteTypeDefAndMemberDefCustomAttributes2: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteTypeDefAndMemberDefCustomAttributes2); - break; + void WritePortablePdb(PdbState pdbState, bool isEmbeddedPortablePdb) { + bool ownsStream = false; + Stream pdbStream = null; + try { + MemoryStream embeddedMemoryStream = null; + if (isEmbeddedPortablePdb) { + pdbStream = embeddedMemoryStream = new MemoryStream(); + ownsStream = true; + } + else + pdbStream = GetStandalonePortablePdbStream(out ownsStream); + if (pdbStream is null) + throw new ModuleWriterException("Couldn't create a PDB stream"); + + var pdbFilename = TheOptions.PdbFileName ?? GetStreamName(pdbStream) ?? GetDefaultPdbFileName(); + if (isEmbeddedPortablePdb) + pdbFilename = Path.GetFileName(pdbFilename); + + uint entryPointToken; + if (pdbState.UserEntryPoint is null) + entryPointToken = 0; + else + entryPointToken = new MDToken(Table.Method, metadata.GetRid(pdbState.UserEntryPoint)).Raw; + + metadata.WritePortablePdb(pdbStream, entryPointToken, out var pdbIdOffset); + + Guid pdbGuid; + var pdbId = new byte[20]; + var pdbIdWriter = new ArrayWriter(pdbId); + uint codeViewTimestamp; + byte[] checksumBytes; + if ((TheOptions.PdbOptions & PdbWriterOptions.Deterministic) != 0 || + (TheOptions.PdbOptions & PdbWriterOptions.PdbChecksum) != 0 || + TheOptions.GetPdbContentId is null) { + pdbStream.Position = 0; + checksumBytes = Hasher.Hash(TheOptions.PdbChecksumAlgorithm, pdbStream, pdbStream.Length); + if (checksumBytes.Length < 20) + throw new ModuleWriterException("Checksum bytes length < 20"); + RoslynContentIdProvider.GetContentId(checksumBytes, out pdbGuid, out codeViewTimestamp); + } + else { + var contentId = TheOptions.GetPdbContentId(pdbStream, GetTimeDateStamp()); + codeViewTimestamp = contentId.Timestamp; + pdbGuid = contentId.Guid; + checksumBytes = null; + } + pdbIdWriter.WriteBytes(pdbGuid.ToByteArray()); + pdbIdWriter.WriteUInt32(codeViewTimestamp); + Debug.Assert(pdbIdWriter.Position == pdbId.Length); + pdbStream.Position = pdbIdOffset; + pdbStream.Write(pdbId, 0, pdbId.Length); + + // NOTE: We add these directory entries in the same order as Roslyn seems to do: + // - CodeView + // - PdbChecksum + // - Reproducible + // - EmbeddedPortablePdb + + debugDirectory.Add(GetCodeViewData(pdbGuid, PdbAge, TheOptions.PdbFileNameInDebugDirectory ?? pdbFilename), + type: ImageDebugType.CodeView, + majorVersion: PortablePdbConstants.FormatVersion, + minorVersion: PortablePdbConstants.PortableCodeViewVersionMagic, + timeDateStamp: codeViewTimestamp); + + if (checksumBytes is not null) + AddPdbChecksumDebugDirectoryEntry(checksumBytes, TheOptions.PdbChecksumAlgorithm); + + if ((TheOptions.PdbOptions & PdbWriterOptions.Deterministic) != 0) + AddReproduciblePdbDebugDirectoryEntry(); + + if (isEmbeddedPortablePdb) { + Debug.Assert(embeddedMemoryStream is not null); + debugDirectory.Add(CreateEmbeddedPortablePdbBlob(embeddedMemoryStream), + type: ImageDebugType.EmbeddedPortablePdb, + majorVersion: PortablePdbConstants.FormatVersion, + minorVersion: PortablePdbConstants.EmbeddedVersion, + timeDateStamp: 0); + } + } + finally { + if (ownsStream && pdbStream is not null) + pdbStream.Dispose(); + } + } - case MetaDataEvent.WriteTypeDefAndMemberDefCustomAttributes3: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteTypeDefAndMemberDefCustomAttributes3); - break; + static byte[] CreateEmbeddedPortablePdbBlob(MemoryStream portablePdbStream) { + var compressedData = Compress(portablePdbStream); + var data = new byte[4 + 4 + compressedData.Length]; + var stream = new MemoryStream(data); + var writer = new DataWriter(stream); + writer.WriteInt32(0x4244504D);//"MPDB" + writer.WriteUInt32((uint)portablePdbStream.Length); + writer.WriteBytes(compressedData); + Debug.Assert(stream.Position == data.Length); + return data; + } - case MetaDataEvent.WriteTypeDefAndMemberDefCustomAttributes4: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteTypeDefAndMemberDefCustomAttributes4); - break; + static byte[] Compress(MemoryStream sourceStream) { + sourceStream.Position = 0; + var destStream = new MemoryStream(); + using (var deflate = new DeflateStream(destStream, CompressionMode.Compress)) { + var source = sourceStream.ToArray(); + deflate.Write(source, 0, source.Length); + } + return destStream.ToArray(); + } - case MetaDataEvent.MemberDefCustomAttributesWritten: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDMemberDefCustomAttributesWritten); - break; + static byte[] GetCodeViewData(Guid guid, uint age, string filename) { + var stream = new MemoryStream(); + var writer = new DataWriter(stream); + writer.WriteInt32(0x53445352); + writer.WriteBytes(guid.ToByteArray()); + writer.WriteUInt32(age); + writer.WriteBytes(Encoding.UTF8.GetBytes(filename)); + writer.WriteByte(0); + return stream.ToArray(); + } - case MetaDataEvent.BeginAddResources: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDBeginAddResources); - break; + Stream GetStandalonePortablePdbStream(out bool ownsStream) { + if (TheOptions.PdbStream is not null) { + ownsStream = false; + return TheOptions.PdbStream; + } - case MetaDataEvent.EndAddResources: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDEndAddResources); - break; + if (!string.IsNullOrEmpty(TheOptions.PdbFileName)) + createdPdbFileName = TheOptions.PdbFileName; + else + createdPdbFileName = GetDefaultPdbFileName(); + if (createdPdbFileName is null) { + ownsStream = false; + return null; + } + ownsStream = true; + return File.Create(createdPdbFileName); + } - case MetaDataEvent.BeginWriteMethodBodies: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDBeginWriteMethodBodies); + void Metadata_MetadataEvent(object sender, MetadataWriterEventArgs e) { + switch (e.Event) { + case MetadataEvent.BeginCreateTables: + OnWriterEvent(ModuleWriterEvent.MDBeginCreateTables); break; - case MetaDataEvent.WriteMethodBodies0: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteMethodBodies0); + case MetadataEvent.AllocateTypeDefRids: + OnWriterEvent(ModuleWriterEvent.MDAllocateTypeDefRids); break; - case MetaDataEvent.WriteMethodBodies1: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteMethodBodies1); + case MetadataEvent.AllocateMemberDefRids: + OnWriterEvent(ModuleWriterEvent.MDAllocateMemberDefRids); break; - case MetaDataEvent.WriteMethodBodies2: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteMethodBodies2); + case MetadataEvent.MemberDefRidsAllocated: + OnWriterEvent(ModuleWriterEvent.MDMemberDefRidsAllocated); break; - case MetaDataEvent.WriteMethodBodies3: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteMethodBodies3); + case MetadataEvent.MemberDefsInitialized: + OnWriterEvent(ModuleWriterEvent.MDMemberDefsInitialized); break; - case MetaDataEvent.WriteMethodBodies4: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteMethodBodies4); + case MetadataEvent.BeforeSortTables: + OnWriterEvent(ModuleWriterEvent.MDBeforeSortTables); break; - case MetaDataEvent.WriteMethodBodies5: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteMethodBodies5); + case MetadataEvent.MostTablesSorted: + OnWriterEvent(ModuleWriterEvent.MDMostTablesSorted); break; - case MetaDataEvent.WriteMethodBodies6: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteMethodBodies6); + case MetadataEvent.MemberDefCustomAttributesWritten: + OnWriterEvent(ModuleWriterEvent.MDMemberDefCustomAttributesWritten); break; - case MetaDataEvent.WriteMethodBodies7: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteMethodBodies7); + case MetadataEvent.BeginAddResources: + OnWriterEvent(ModuleWriterEvent.MDBeginAddResources); break; - case MetaDataEvent.WriteMethodBodies8: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteMethodBodies8); + case MetadataEvent.EndAddResources: + OnWriterEvent(ModuleWriterEvent.MDEndAddResources); break; - case MetaDataEvent.WriteMethodBodies9: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDWriteMethodBodies9); + case MetadataEvent.BeginWriteMethodBodies: + OnWriterEvent(ModuleWriterEvent.MDBeginWriteMethodBodies); break; - case MetaDataEvent.EndWriteMethodBodies: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDEndWriteMethodBodies); + case MetadataEvent.EndWriteMethodBodies: + OnWriterEvent(ModuleWriterEvent.MDEndWriteMethodBodies); break; - case MetaDataEvent.OnAllTablesSorted: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDOnAllTablesSorted); + case MetadataEvent.OnAllTablesSorted: + OnWriterEvent(ModuleWriterEvent.MDOnAllTablesSorted); break; - case MetaDataEvent.EndCreateTables: - Listener.OnWriterEvent(this, ModuleWriterEvent.MDEndCreateTables); + case MetadataEvent.EndCreateTables: + OnWriterEvent(ModuleWriterEvent.MDEndCreateTables); break; default: + Debug.Fail($"Unknown MD event: {e.Event}"); break; } } - ILogger GetLogger() { - return TheOptions.Logger ?? DummyLogger.ThrowModuleWriterExceptionOnErrorInstance; + void Metadata_ProgressUpdated(object sender, MetadataProgressEventArgs e) => + RaiseProgress(ModuleWriterEvent.MDBeginCreateTables, ModuleWriterEvent.MDEndCreateTables + 1, e.Progress); + + /// + /// Raises a writer event + /// + /// Event + protected void OnWriterEvent(ModuleWriterEvent evt) { + RaiseProgress(evt, 0); + TheOptions.RaiseEvent(this, new ModuleWriterEventArgs(this, evt)); + } + + static readonly double[] eventToProgress = new double[(int)ModuleWriterEvent.End - (int)ModuleWriterEvent.Begin + 1 + 1] { + 0, // Begin + 0.00128048488389907,// PESectionsCreated + 0.0524625293056615, // ChunksCreated + 0.0531036610555682, // ChunksAddedToSections + 0.0535679983835939, // MDBeginCreateTables + 0.0547784058004697, // MDAllocateTypeDefRids + 0.0558606342971218, // MDAllocateMemberDefRids + 0.120553993799033, // MDMemberDefRidsAllocated + 0.226210300699921, // MDMemberDefsInitialized + 0.236002648477671, // MDBeforeSortTables + 0.291089703426468, // MDMostTablesSorted + 0.449919748849947, // MDMemberDefCustomAttributesWritten + 0.449919985998736, // MDBeginAddResources + 0.452716444513587, // MDEndAddResources + 0.452716681662375, // MDBeginWriteMethodBodies + 0.924922132195272, // MDEndWriteMethodBodies + 0.931410404476231, // MDOnAllTablesSorted + 0.931425463424305, // MDEndCreateTables + 0.932072998191503, // BeginWritePdb + 0.932175327893773, // EndWritePdb + 0.932175446468167, // BeginCalculateRvasAndFileOffsets + 0.954646479929387, // EndCalculateRvasAndFileOffsets + 0.95492263969368, // BeginWriteChunks + 0.980563166714175, // EndWriteChunks + 0.980563403862964, // BeginStrongNameSign + 0.980563403862964, // EndStrongNameSign + 0.980563522437358, // BeginWritePEChecksum + 0.999975573674777, // EndWritePEChecksum + 1, // End + 1,// An extra one so we can get the next base progress without checking the index + }; + + void RaiseProgress(ModuleWriterEvent evt, double subProgress) => RaiseProgress(evt, evt + 1, subProgress); + + void RaiseProgress(ModuleWriterEvent evt, ModuleWriterEvent nextEvt, double subProgress) { + subProgress = Math.Min(1, Math.Max(0, subProgress)); + var baseProgress = eventToProgress[(int)evt]; + var nextProgress = eventToProgress[(int)nextEvt]; + var progress = baseProgress + (nextProgress - baseProgress) * subProgress; + progress = Math.Min(1, Math.Max(0, progress)); + TheOptions.RaiseEvent(this, new ModuleWriterProgressEventArgs(this, progress)); } + ILogger GetLogger() => TheOptions.Logger ?? DummyLogger.ThrowModuleWriterExceptionOnErrorInstance; + /// - void ILogger.Log(object sender, LoggerEvent loggerEvent, string format, params object[] args) { + void ILogger.Log(object sender, LoggerEvent loggerEvent, string format, params object[] args) => GetLogger().Log(this, loggerEvent, format, args); - } /// - bool ILogger.IgnoresEvent(LoggerEvent loggerEvent) { - return GetLogger().IgnoresEvent(loggerEvent); - } + bool ILogger.IgnoresEvent(LoggerEvent loggerEvent) => GetLogger().IgnoresEvent(loggerEvent); /// /// Logs an error message /// /// Format /// Format args - protected void Error(string format, params object[] args) { + protected void Error(string format, params object[] args) => GetLogger().Log(this, LoggerEvent.Error, format, args); - } /// /// Logs a warning message /// /// Format /// Format args - protected void Warning(string format, params object[] args) { + protected void Warning(string format, params object[] args) => GetLogger().Log(this, LoggerEvent.Warning, format, args); - } } } diff --git a/src/DotNet/Writer/ModuleWriterEvent.cs b/src/DotNet/Writer/ModuleWriterEvent.cs new file mode 100644 index 000000000..ee4dca09f --- /dev/null +++ b/src/DotNet/Writer/ModuleWriterEvent.cs @@ -0,0 +1,180 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.DotNet.Writer { + /// + /// All / events + /// + public enum ModuleWriterEvent { + /// + /// Writing has just begun + /// + Begin, + + /// + /// All PE sections have been created + /// + PESectionsCreated, + + /// + /// All chunks have been created + /// + ChunksCreated, + + /// + /// All chunks have been added to their sections + /// + ChunksAddedToSections, + + /// + /// Original event: . + /// Creating the metadata tables has just begun + /// + MDBeginCreateTables, + + /// + /// Original event: . + /// Before allocating all TypeDef RIDs + /// + MDAllocateTypeDefRids, + + /// + /// Original event: . + /// Before allocating all MemberDef RIDs + /// + MDAllocateMemberDefRids, + + /// + /// Original event: . + /// The rids of types, fields, methods, events, properties and parameters are + /// now known. + /// + MDMemberDefRidsAllocated, + + /// + /// Original event: . + /// The tables and rows of all types, fields, methods, events, properties and parameters + /// have been initialized. Method body RVAs are still not known, and no method has been + /// written yet. + /// + MDMemberDefsInitialized, + + /// + /// Original event: . + /// Before sorting most tables + /// + MDBeforeSortTables, + + /// + /// Original event: . + /// Most of the tables that should be sorted have been sorted. The CustomAttribute + /// table is still unsorted since it hasn't been created yet. + /// + MDMostTablesSorted, + + /// + /// Original event: . + /// Custom attributes of all types, fields, methods, events, properties and parameters + /// have now been written. + /// + MDMemberDefCustomAttributesWritten, + + /// + /// Original event: . + /// All resources are about to be added to the .NET resources table + /// + MDBeginAddResources, + + /// + /// Original event: . + /// All resources have been added to the .NET resources table + /// + MDEndAddResources, + + /// + /// Original event: . + /// All method bodies are about to be written + /// + MDBeginWriteMethodBodies, + + /// + /// Original event: . + /// All method bodies have been written. Their RVAs are still not known. + /// + MDEndWriteMethodBodies, + + /// + /// Original event: . + /// All tables are now sorted, including the CustomAttribute table. + /// + MDOnAllTablesSorted, + + /// + /// Original event: . + /// All tables have been created and all rows populated. The only columns that haven't + /// been initialized yet are the ones that are RVAs. + /// + MDEndCreateTables, + + /// + /// This event occurs before the PDB file is written. This event occurs even if no PDB file + /// will be written. + /// + BeginWritePdb, + + /// + /// The PDB file has been written. This event occurs even if no PDB file has been written. + /// + EndWritePdb, + + /// + /// This event occurs just before all RVAs and file offsets of the chunks are calculated. + /// + BeginCalculateRvasAndFileOffsets, + + /// + /// File offsets and RVAs of all chunks are now known. This includes method body and + /// field RVAs. Nothing has been written to the destination stream yet. + /// + EndCalculateRvasAndFileOffsets, + + /// + /// This event occurs before all chunks are written to the destination stream, and after + /// all RVAs and file offsets are known. + /// + BeginWriteChunks, + + /// + /// All chunks have been written to the destination stream. + /// + EndWriteChunks, + + /// + /// This event occurs before the strong name signature is calculated. This event + /// occurs even if the assembly isn't strong name signed. + /// + BeginStrongNameSign, + + /// + /// This event occurs after the strong name signature has been calculated. This event + /// occurs even if the assembly isn't strong name signed. + /// + EndStrongNameSign, + + /// + /// This event occurs before the checksum in the PE header is updated. This event + /// occurs even if the checksum isn't updated. + /// + BeginWritePEChecksum, + + /// + /// This event occurs after the checksum in the PE header has been updated. This event + /// occurs even if the checksum isn't updated. + /// + EndWritePEChecksum, + + /// + /// Writing has ended + /// + End, + } +} diff --git a/src/DotNet/Writer/ModuleWriterException.cs b/src/DotNet/Writer/ModuleWriterException.cs index 50975f8fc..201471396 100644 --- a/src/DotNet/Writer/ModuleWriterException.cs +++ b/src/DotNet/Writer/ModuleWriterException.cs @@ -1,6 +1,7 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; +using System.Runtime.Serialization; namespace dnlib.DotNet.Writer { /// @@ -30,5 +31,14 @@ public ModuleWriterException(string message) public ModuleWriterException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Constructor + /// + /// + /// + protected ModuleWriterException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } } } diff --git a/src/DotNet/Writer/NativeModuleWriter.cs b/src/DotNet/Writer/NativeModuleWriter.cs index 2c09141dd..7fba11f0e 100644 --- a/src/DotNet/Writer/NativeModuleWriter.cs +++ b/src/DotNet/Writer/NativeModuleWriter.cs @@ -1,12 +1,12 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; -using System.IO; using dnlib.IO; using dnlib.PE; using dnlib.W32Resources; using dnlib.DotNet.MD; +using System.Diagnostics; namespace dnlib.DotNet.Writer { /// @@ -25,25 +25,30 @@ public sealed class NativeModuleWriterOptions : ModuleWriterOptionsBase { /// public bool KeepWin32Resources { get; set; } - /// - /// Constructor - /// - /// Module - public NativeModuleWriterOptions(ModuleDefMD module) - : this(module, null) { - } + internal bool OptimizeImageSize { get; } /// /// Constructor /// /// Module - /// Module writer listener - public NativeModuleWriterOptions(ModuleDefMD module, IModuleWriterListener listener) - : base(module, listener) { - + /// true to optimize the image size so it's as small as possible. + /// Since the file can contain native methods and other native data, we re-use the + /// original file when writing the new file. If is true, + /// we'll try to re-use the old method body locations in the original file and + /// also try to fit the new metadata in the old metadata location. + public NativeModuleWriterOptions(ModuleDefMD module, bool optimizeImageSize) : base(module) { // C++ .NET mixed mode assemblies sometimes/often call Module.ResolveMethod(), // so method metadata tokens must be preserved. - MetaDataOptions.Flags |= MetaDataFlags.PreserveAllMethodRids; + MetadataOptions.Flags |= MetadataFlags.PreserveAllMethodRids; + + if (optimizeImageSize) { + OptimizeImageSize = true; + + // Prevent the #Blob heap from getting bigger. Encoded TypeDefOrRef tokens are stored there (in + // typesigs and callingconvsigs) so we must preserve TypeDefOrRef tokens (or the #Blob heap could + // grow in size and new MD won't fit in old location) + MetadataOptions.Flags |= MetadataFlags.PreserveTypeRefRids | MetadataFlags.PreserveTypeDefRids | MetadataFlags.PreserveTypeSpecRids; + } } } @@ -63,14 +68,22 @@ public sealed class NativeModuleWriter : ModuleWriterBase { /// no extra data or if is /// false. /// - BinaryReaderChunk extraData; - - /// The original PE headers - BinaryReaderChunk headerSection; + DataReaderChunk extraData; /// The original PE sections and their data List origSections; + readonly struct ReusedChunkInfo { + public IReuseChunk Chunk { get; } + public RVA RVA { get; } + public ReusedChunkInfo(IReuseChunk chunk, RVA rva) { + Chunk = chunk; + RVA = rva; + } + } + + List reusedChunks; + /// Original PE image readonly IPEImage peImage; @@ -102,83 +115,66 @@ public sealed class OrigSection : IDisposable { /// PE section public ImageSectionHeader PESection; /// PE section data - public BinaryReaderChunk Chunk; + public DataReaderChunk Chunk; /// /// Constructor /// /// PE section - public OrigSection(ImageSectionHeader peSection) { - this.PESection = peSection; - } + public OrigSection(ImageSectionHeader peSection) => + PESection = peSection; /// public void Dispose() { - if (Chunk != null) - Chunk.Data.Dispose(); Chunk = null; PESection = null; } /// public override string ToString() { - uint offs = Chunk.Data is IImageStream ? (uint)((IImageStream)Chunk.Data).FileOffset : 0; - return string.Format("{0} FO:{1:X8} L:{2:X8}", PESection.DisplayName, offs, (uint)Chunk.Data.Length); + uint offs = Chunk.CreateReader().StartOffset; + return $"{PESection.DisplayName} FO:{offs:X8} L:{Chunk.CreateReader().Length:X8}"; } } /// /// Gets the module /// - public ModuleDefMD ModuleDefMD { - get { return module; } - } + public ModuleDefMD ModuleDefMD => module; /// - public override ModuleDef Module { - get { return module; } - } + public override ModuleDef Module => module; /// - public override ModuleWriterOptionsBase TheOptions { - get { return Options; } - } + public override ModuleWriterOptionsBase TheOptions => Options; /// /// Gets/sets the writer options. This is never null /// public NativeModuleWriterOptions Options { - get { return options ?? (options = new NativeModuleWriterOptions(module)); } - set { options = value; } + get => options ??= new NativeModuleWriterOptions(module, optimizeImageSize: true); + set => options = value; } /// /// Gets all s /// - public override List Sections { - get { return sections; } - } + public override List Sections => sections; /// /// Gets the original PE sections and their data /// - public List OrigSections { - get { return origSections; } - } + public List OrigSections => origSections; /// /// Gets the .text section /// - public override PESection TextSection { - get { return textSection; } - } + public override PESection TextSection => textSection; /// /// Gets the .rsrc section or null if there's none /// - public override PESection RsrcSection { - get { return rsrcSection; } - } + public override PESection RsrcSection => rsrcSection; /// /// Constructor @@ -188,7 +184,8 @@ public override PESection RsrcSection { public NativeModuleWriter(ModuleDefMD module, NativeModuleWriterOptions options) { this.module = module; this.options = options; - this.peImage = module.MetaData.PEImage; + peImage = module.Metadata.PEImage; + reusedChunks = new List(); } /// @@ -197,14 +194,10 @@ protected override long WriteImpl() { return Write(); } finally { - if (origSections != null) { + if (origSections is not null) { foreach (var section in origSections) section.Dispose(); } - if (headerSection != null) - headerSection.Data.Dispose(); - if (extraData != null) - extraData.Data.Dispose(); } } @@ -213,32 +206,32 @@ long Write() { // It's not safe to create new Field RVAs so re-use them all. The user can override // this by setting field.RVA = 0 when creating a new field.InitialValue. - metaData.KeepFieldRVA = true; + metadata.KeepFieldRVA = true; - metaData.CreateTables(); + metadata.CreateTables(); return WriteFile(); } void Initialize() { CreateSections(); - Listener.OnWriterEvent(this, ModuleWriterEvent.PESectionsCreated); + OnWriterEvent(ModuleWriterEvent.PESectionsCreated); CreateChunks(); - Listener.OnWriterEvent(this, ModuleWriterEvent.ChunksCreated); + OnWriterEvent(ModuleWriterEvent.ChunksCreated); AddChunksToSections(); - Listener.OnWriterEvent(this, ModuleWriterEvent.ChunksAddedToSections); + OnWriterEvent(ModuleWriterEvent.ChunksAddedToSections); } void CreateSections() { CreatePESections(); CreateRawSections(); - CreateHeaderSection(); CreateExtraData(); } void CreateChunks() { - CreateMetaDataChunks(module); + CreateMetadataChunks(module); + methodBodies.CanReuseOldBodyLocation = Options.OptimizeImageSize; CreateDebugDirectory(); @@ -252,9 +245,9 @@ void AddChunksToSections() { textSection.Add(constants, DEFAULT_CONSTANTS_ALIGNMENT); textSection.Add(methodBodies, DEFAULT_METHODBODIES_ALIGNMENT); textSection.Add(netResources, DEFAULT_NETRESOURCES_ALIGNMENT); - textSection.Add(metaData, DEFAULT_METADATA_ALIGNMENT); - textSection.Add(debugDirectory, DEFAULT_DEBUGDIRECTORY_ALIGNMENT); - if (rsrcSection != null) + textSection.Add(metadata, DEFAULT_METADATA_ALIGNMENT); + textSection.Add(debugDirectory, DebugDirectory.DEFAULT_DEBUGDIRECTORY_ALIGNMENT); + if (rsrcSection is not null) rsrcSection.Add(win32Resources, DEFAULT_WIN32_RESOURCES_ALIGNMENT); } @@ -262,13 +255,15 @@ void AddChunksToSections() { protected override Win32Resources GetWin32Resources() { if (Options.KeepWin32Resources) return null; + if (Options.NoWin32Resources) + return null; return Options.Win32Resources ?? module.Win32Resources; } void CreatePESections() { sections = new List(); sections.Add(textSection = new PESection(".text", 0x60000020)); - if (GetWin32Resources() != null) + if (GetWin32Resources() is not null) sections.Add(rsrcSection = new PESection(".rsrc", 0x40000040)); } @@ -284,14 +279,14 @@ void CreateRawSections() { var newSection = new OrigSection(peSection); origSections.Add(newSection); uint sectionSize = Utils.AlignUp(peSection.SizeOfRawData, fileAlignment); - newSection.Chunk = new BinaryReaderChunk(peImage.CreateStream(peSection.VirtualAddress, sectionSize), peSection.VirtualSize); + newSection.Chunk = new DataReaderChunk(peImage.CreateReader(peSection.VirtualAddress, sectionSize), peSection.VirtualSize); } } /// /// Creates the PE header "section" /// - void CreateHeaderSection() { + DataReaderChunk CreateHeaderSection(out IChunk extraHeaderData) { uint afterLastSectHeader = GetOffsetAfterLastSectionHeader() + (uint)sections.Count * 0x28; uint firstRawOffset = Math.Min(GetFirstRawDataFileOffset(), peImage.ImageNTHeaders.OptionalHeader.SectionAlignment); uint headerLen = afterLastSectHeader; @@ -299,8 +294,19 @@ void CreateHeaderSection() { headerLen = firstRawOffset; headerLen = Utils.AlignUp(headerLen, peImage.ImageNTHeaders.OptionalHeader.FileAlignment); if (headerLen <= peImage.ImageNTHeaders.OptionalHeader.SectionAlignment) { - headerSection = new BinaryReaderChunk(peImage.CreateStream(0, headerLen)); - return; + uint origSizeOfHeaders = peImage.ImageNTHeaders.OptionalHeader.SizeOfHeaders; + uint extraLen; + if (headerLen <= origSizeOfHeaders) + extraLen = 0; + else { + extraLen = headerLen - origSizeOfHeaders; + headerLen = origSizeOfHeaders; + } + if (extraLen > 0) + extraHeaderData = new ByteArrayChunk(new byte[(int)extraLen]); + else + extraHeaderData = null; + return new DataReaderChunk(peImage.CreateReader((FileOffset)0, headerLen)); } //TODO: Support this too @@ -326,11 +332,9 @@ void CreateExtraData() { if (!Options.KeepExtraPEData) return; var lastOffs = GetLastFileSectionOffset(); - extraData = new BinaryReaderChunk(peImage.CreateStream((FileOffset)lastOffs)); - if (extraData.Data.Length == 0) { - extraData.Data.Dispose(); + extraData = new DataReaderChunk(peImage.CreateReader((FileOffset)lastOffs)); + if (extraData.CreateReader().Length == 0) extraData = null; - } } uint GetLastFileSectionOffset() { @@ -340,66 +344,197 @@ uint GetLastFileSectionOffset() { return (uint)peImage.ToFileOffset((RVA)(rva - 1)) + 1; } + void ReuseIfPossible(PESection section, IReuseChunk chunk, RVA origRva, uint origSize, uint requiredAlignment) { + if (origRva == 0 || origSize == 0) + return; + if (chunk is null) + return; + if (!chunk.CanReuse(origRva, origSize)) + return; + if (((uint)origRva & (requiredAlignment - 1)) != 0) + return; + + var origEnd = origRva + origSize; + foreach (var reusedChunk in reusedChunks) { + if (origRva < reusedChunk.RVA + reusedChunk.Chunk.GetVirtualSize() && origEnd > reusedChunk.RVA) + return; + } + + if (section.Remove(chunk) is null) + throw new InvalidOperationException(); + reusedChunks.Add(new ReusedChunkInfo(chunk, origRva)); + } + + FileOffset GetNewFileOffset(RVA rva) { + foreach (var sect in origSections) { + var section = sect.PESection; + if (section.VirtualAddress <= rva && rva < section.VirtualAddress + Math.Max(section.VirtualSize, section.SizeOfRawData)) + return sect.Chunk.FileOffset + (rva - section.VirtualAddress); + } + return (FileOffset)rva; + } + long WriteFile() { - Listener.OnWriterEvent(this, ModuleWriterEvent.BeginWritePdb); + bool entryPointIsManagedOrNoEntryPoint = GetEntryPoint(out uint entryPointToken); + + OnWriterEvent(ModuleWriterEvent.BeginWritePdb); WritePdbFile(); - Listener.OnWriterEvent(this, ModuleWriterEvent.EndWritePdb); + OnWriterEvent(ModuleWriterEvent.EndWritePdb); + + metadata.OnBeforeSetOffset(); + OnWriterEvent(ModuleWriterEvent.BeginCalculateRvasAndFileOffsets); + + if (Options.OptimizeImageSize) { + // Check if we can reuse the old MD location for the new MD. + // If we can't reuse it, it could be due to several reasons: + // - TypeDefOrRef tokens weren't preserved resulting in a new #Blob heap that's bigger than the old #Blob heap + // - New MD was added or existing MD was modified (eg. types were renamed) by the user so it's + // now bigger and doesn't fit in the old location + // - The original location wasn't aligned properly + // - The new MD is bigger because the other MD writer was slightly better at optimizing the MD. + // This should be considered a bug. + var mdDataDir = module.Metadata.ImageCor20Header.Metadata; + metadata.SetOffset(peImage.ToFileOffset(mdDataDir.VirtualAddress), mdDataDir.VirtualAddress); + ReuseIfPossible(textSection, metadata, mdDataDir.VirtualAddress, mdDataDir.Size, DEFAULT_METADATA_ALIGNMENT); + + var resourceDataDir = peImage.ImageNTHeaders.OptionalHeader.DataDirectories[2]; + if (win32Resources is not null && resourceDataDir.VirtualAddress != 0 && resourceDataDir.Size != 0) { + var win32ResourcesOffset = peImage.ToFileOffset(resourceDataDir.VirtualAddress); + if (win32Resources.CheckValidOffset(win32ResourcesOffset)) { + win32Resources.SetOffset(win32ResourcesOffset, resourceDataDir.VirtualAddress); + ReuseIfPossible(rsrcSection, win32Resources, resourceDataDir.VirtualAddress, resourceDataDir.Size, DEFAULT_WIN32_RESOURCES_ALIGNMENT); + } + } + + ReuseIfPossible(textSection, imageCor20Header, module.Metadata.PEImage.ImageNTHeaders.OptionalHeader.DataDirectories[14].VirtualAddress, module.Metadata.PEImage.ImageNTHeaders.OptionalHeader.DataDirectories[14].Size, DEFAULT_COR20HEADER_ALIGNMENT); + if ((module.Metadata.ImageCor20Header.Flags & ComImageFlags.StrongNameSigned) != 0) + ReuseIfPossible(textSection, strongNameSignature, module.Metadata.ImageCor20Header.StrongNameSignature.VirtualAddress, module.Metadata.ImageCor20Header.StrongNameSignature.Size, DEFAULT_STRONGNAMESIG_ALIGNMENT); + ReuseIfPossible(textSection, netResources, module.Metadata.ImageCor20Header.Resources.VirtualAddress, module.Metadata.ImageCor20Header.Resources.Size, DEFAULT_NETRESOURCES_ALIGNMENT); + if (methodBodies.ReusedAllMethodBodyLocations) + textSection.Remove(methodBodies); + + var debugDataDir = peImage.ImageNTHeaders.OptionalHeader.DataDirectories[6]; + if (debugDataDir.VirtualAddress != 0 && debugDataDir.Size != 0 && TryGetRealDebugDirectorySize(peImage, out uint realDebugDirSize)) + ReuseIfPossible(textSection, debugDirectory, debugDataDir.VirtualAddress, realDebugDirSize, DebugDirectory.DEFAULT_DEBUGDIRECTORY_ALIGNMENT); + } - Listener.OnWriterEvent(this, ModuleWriterEvent.BeginCalculateRvasAndFileOffsets); + if (constants.IsEmpty) + textSection.Remove(constants); + if (netResources.IsEmpty) + textSection.Remove(netResources); + if (textSection.IsEmpty) + sections.Remove(textSection); + if (rsrcSection is not null && rsrcSection.IsEmpty) { + sections.Remove(rsrcSection); + rsrcSection = null; + } + var headerSection = CreateHeaderSection(out var extraHeaderData); var chunks = new List(); - chunks.Add(headerSection); + uint headerLen; + if (extraHeaderData is not null) { + var list = new ChunkList(); + list.Add(headerSection, 1); + list.Add(extraHeaderData, 1); + chunks.Add(list); + headerLen = headerSection.GetVirtualSize() + extraHeaderData.GetVirtualSize(); + } + else { + chunks.Add(headerSection); + headerLen = headerSection.GetVirtualSize(); + } foreach (var origSection in origSections) chunks.Add(origSection.Chunk); foreach (var section in sections) chunks.Add(section); - if (extraData != null) + if (extraData is not null) chunks.Add(extraData); CalculateRvasAndFileOffsets(chunks, 0, 0, peImage.ImageNTHeaders.OptionalHeader.FileAlignment, peImage.ImageNTHeaders.OptionalHeader.SectionAlignment); + if (reusedChunks.Count > 0 || methodBodies.HasReusedMethods) { + methodBodies.InitializeReusedMethodBodies(GetNewFileOffset); + foreach (var info in reusedChunks) { + var offset = GetNewFileOffset(info.RVA); + info.Chunk.SetOffset(offset, info.RVA); + } + } + metadata.UpdateMethodAndFieldRvas(); foreach (var section in origSections) { if (section.Chunk.RVA != section.PESection.VirtualAddress) throw new ModuleWriterException("Invalid section RVA"); } - Listener.OnWriterEvent(this, ModuleWriterEvent.EndCalculateRvasAndFileOffsets); + OnWriterEvent(ModuleWriterEvent.EndCalculateRvasAndFileOffsets); - Listener.OnWriterEvent(this, ModuleWriterEvent.BeginWriteChunks); - var writer = new BinaryWriter(destStream); + OnWriterEvent(ModuleWriterEvent.BeginWriteChunks); + var writer = new DataWriter(destStream); WriteChunks(writer, chunks, 0, peImage.ImageNTHeaders.OptionalHeader.FileAlignment); - long imageLength = writer.BaseStream.Position - destStreamBaseOffset; - UpdateHeaderFields(writer); - Listener.OnWriterEvent(this, ModuleWriterEvent.EndWriteChunks); + long imageLength = writer.Position - destStreamBaseOffset; + if (reusedChunks.Count > 0 || methodBodies.HasReusedMethods) { + var pos = writer.Position; + foreach (var info in reusedChunks) { + Debug.Assert(info.Chunk.RVA == info.RVA); + if (info.Chunk.RVA != info.RVA) + throw new InvalidOperationException(); + writer.Position = destStreamBaseOffset + (uint)info.Chunk.FileOffset; + info.Chunk.VerifyWriteTo(writer); + } + methodBodies.WriteReusedMethodBodies(writer, destStreamBaseOffset); + writer.Position = pos; + } + var sectionSizes = new SectionSizes(peImage.ImageNTHeaders.OptionalHeader.FileAlignment, peImage.ImageNTHeaders.OptionalHeader.SectionAlignment, headerLen, GetSectionSizeInfos); + UpdateHeaderFields(writer, entryPointIsManagedOrNoEntryPoint, entryPointToken, ref sectionSizes); + OnWriterEvent(ModuleWriterEvent.EndWriteChunks); - Listener.OnWriterEvent(this, ModuleWriterEvent.BeginStrongNameSign); - if (Options.StrongNameKey != null) + OnWriterEvent(ModuleWriterEvent.BeginStrongNameSign); + if (Options.StrongNameKey is not null) StrongNameSign((long)strongNameSignature.FileOffset); - Listener.OnWriterEvent(this, ModuleWriterEvent.EndStrongNameSign); + OnWriterEvent(ModuleWriterEvent.EndStrongNameSign); - Listener.OnWriterEvent(this, ModuleWriterEvent.BeginWritePEChecksum); + OnWriterEvent(ModuleWriterEvent.BeginWritePEChecksum); if (Options.AddCheckSum) { destStream.Position = destStreamBaseOffset; - uint newCheckSum = new BinaryReader(destStream).CalculatePECheckSum(imageLength, checkSumOffset); - writer.BaseStream.Position = checkSumOffset; - writer.Write(newCheckSum); + uint newCheckSum = destStream.CalculatePECheckSum(imageLength, checkSumOffset); + writer.Position = checkSumOffset; + writer.WriteUInt32(newCheckSum); } - Listener.OnWriterEvent(this, ModuleWriterEvent.EndWritePEChecksum); + OnWriterEvent(ModuleWriterEvent.EndWritePEChecksum); return imageLength; } + static bool TryGetRealDebugDirectorySize(IPEImage peImage, out uint realSize) { + realSize = 0; + if (peImage.ImageDebugDirectories.Count == 0) + return false; + var dirs = new List(peImage.ImageDebugDirectories); + dirs.Sort((a, b) => a.AddressOfRawData.CompareTo(b.AddressOfRawData)); + var debugDataDir = peImage.ImageNTHeaders.OptionalHeader.DataDirectories[6]; + var prevEnd = (uint)debugDataDir.VirtualAddress + debugDataDir.Size; + for (int i = 0; i < dirs.Count; i++) { + uint prevEndAligned = (prevEnd + 3) & ~3U; + var dir = dirs[i]; + if (dir.AddressOfRawData == 0 || dir.SizeOfData == 0) + continue; + if (!(prevEnd <= (uint)dir.AddressOfRawData && (uint)dir.AddressOfRawData <= prevEndAligned)) + return false; + prevEnd = (uint)dir.AddressOfRawData + dir.SizeOfData; + } + + realSize = prevEnd - (uint)debugDataDir.VirtualAddress; + return true; + } + /// /// true if image is 64-bit /// - bool Is64Bit() { - return peImage.ImageNTHeaders.OptionalHeader is ImageOptionalHeader64; - } + bool Is64Bit() => peImage.ImageNTHeaders.OptionalHeader is ImageOptionalHeader64; Characteristics GetCharacteristics() { var ch = module.Characteristics; if (Is64Bit()) - ch &= ~Characteristics._32BitMachine; + ch &= ~Characteristics.Bit32Machine; else - ch |= Characteristics._32BitMachine; + ch |= Characteristics.Bit32Machine; if (Options.IsExeFile) ch &= ~Characteristics.Dll; else @@ -411,43 +546,39 @@ Characteristics GetCharacteristics() { /// Updates the PE header and COR20 header fields that need updating. All sections are /// also updated, and the new ones are added. /// - void UpdateHeaderFields(BinaryWriter writer) { + void UpdateHeaderFields(DataWriter writer, bool entryPointIsManagedOrNoEntryPoint, uint entryPointToken, ref SectionSizes sectionSizes) { long fileHeaderOffset = destStreamBaseOffset + (long)peImage.ImageNTHeaders.FileHeader.StartOffset; long optionalHeaderOffset = destStreamBaseOffset + (long)peImage.ImageNTHeaders.OptionalHeader.StartOffset; long sectionsOffset = destStreamBaseOffset + (long)peImage.ImageSectionHeaders[0].StartOffset; long dataDirOffset = destStreamBaseOffset + (long)peImage.ImageNTHeaders.OptionalHeader.EndOffset - 16 * 8; long cor20Offset = destStreamBaseOffset + (long)imageCor20Header.FileOffset; - uint fileAlignment = peImage.ImageNTHeaders.OptionalHeader.FileAlignment; - uint sectionAlignment = peImage.ImageNTHeaders.OptionalHeader.SectionAlignment; - // Update PE file header var peOptions = Options.PEHeadersOptions; - writer.BaseStream.Position = fileHeaderOffset; - writer.Write((ushort)(peOptions.Machine ?? module.Machine)); - writer.Write((ushort)(origSections.Count + sections.Count)); + writer.Position = fileHeaderOffset; + writer.WriteUInt16((ushort)(peOptions.Machine ?? module.Machine)); + writer.WriteUInt16((ushort)(origSections.Count + sections.Count)); WriteUInt32(writer, peOptions.TimeDateStamp); WriteUInt32(writer, peOptions.PointerToSymbolTable); WriteUInt32(writer, peOptions.NumberOfSymbols); - writer.BaseStream.Position += 2; // sizeof(SizeOfOptionalHeader) - writer.Write((ushort)(peOptions.Characteristics ?? GetCharacteristics())); + writer.Position += 2; // sizeof(SizeOfOptionalHeader) + writer.WriteUInt16((ushort)(peOptions.Characteristics ?? GetCharacteristics())); // Update optional header - var sectionSizes = new SectionSizes(fileAlignment, sectionAlignment, headerSection.GetVirtualSize(), GetSectionSizeInfos); - writer.BaseStream.Position = optionalHeaderOffset; + writer.Position = optionalHeaderOffset; bool is32BitOptionalHeader = peImage.ImageNTHeaders.OptionalHeader is ImageOptionalHeader32; if (is32BitOptionalHeader) { - writer.BaseStream.Position += 2; + writer.Position += 2; WriteByte(writer, peOptions.MajorLinkerVersion); WriteByte(writer, peOptions.MinorLinkerVersion); - writer.Write(sectionSizes.SizeOfCode); - writer.Write(sectionSizes.SizeOfInitdData); - writer.Write(sectionSizes.SizeOfUninitdData); - writer.BaseStream.Position += 4; // EntryPoint - writer.Write(sectionSizes.BaseOfCode); - writer.Write(sectionSizes.BaseOfData); + writer.WriteUInt32(sectionSizes.SizeOfCode); + writer.WriteUInt32(sectionSizes.SizeOfInitdData); + writer.WriteUInt32(sectionSizes.SizeOfUninitdData); + writer.Position += 4; // EntryPoint + writer.WriteUInt32(sectionSizes.BaseOfCode); + writer.WriteUInt32(sectionSizes.BaseOfData); WriteUInt32(writer, peOptions.ImageBase); - writer.BaseStream.Position += 8; // SectionAlignment, FileAlignment + writer.Position += 8; // SectionAlignment, FileAlignment WriteUInt16(writer, peOptions.MajorOperatingSystemVersion); WriteUInt16(writer, peOptions.MinorOperatingSystemVersion); WriteUInt16(writer, peOptions.MajorImageVersion); @@ -455,10 +586,10 @@ void UpdateHeaderFields(BinaryWriter writer) { WriteUInt16(writer, peOptions.MajorSubsystemVersion); WriteUInt16(writer, peOptions.MinorSubsystemVersion); WriteUInt32(writer, peOptions.Win32VersionValue); - writer.Write(sectionSizes.SizeOfImage); - writer.Write(sectionSizes.SizeOfHeaders); - checkSumOffset = writer.BaseStream.Position; - writer.Write(0); // CheckSum + writer.WriteUInt32(sectionSizes.SizeOfImage); + writer.WriteUInt32(sectionSizes.SizeOfHeaders); + checkSumOffset = writer.Position; + writer.WriteInt32(0); // CheckSum WriteUInt16(writer, peOptions.Subsystem); WriteUInt16(writer, peOptions.DllCharacteristics); WriteUInt32(writer, peOptions.SizeOfStackReserve); @@ -469,16 +600,16 @@ void UpdateHeaderFields(BinaryWriter writer) { WriteUInt32(writer, peOptions.NumberOfRvaAndSizes); } else { - writer.BaseStream.Position += 2; + writer.Position += 2; WriteByte(writer, peOptions.MajorLinkerVersion); WriteByte(writer, peOptions.MinorLinkerVersion); - writer.Write(sectionSizes.SizeOfCode); - writer.Write(sectionSizes.SizeOfInitdData); - writer.Write(sectionSizes.SizeOfUninitdData); - writer.BaseStream.Position += 4; // EntryPoint - writer.Write(sectionSizes.BaseOfCode); + writer.WriteUInt32(sectionSizes.SizeOfCode); + writer.WriteUInt32(sectionSizes.SizeOfInitdData); + writer.WriteUInt32(sectionSizes.SizeOfUninitdData); + writer.Position += 4; // EntryPoint + writer.WriteUInt32(sectionSizes.BaseOfCode); WriteUInt64(writer, peOptions.ImageBase); - writer.BaseStream.Position += 8; // SectionAlignment, FileAlignment + writer.Position += 8; // SectionAlignment, FileAlignment WriteUInt16(writer, peOptions.MajorOperatingSystemVersion); WriteUInt16(writer, peOptions.MinorOperatingSystemVersion); WriteUInt16(writer, peOptions.MajorImageVersion); @@ -486,10 +617,10 @@ void UpdateHeaderFields(BinaryWriter writer) { WriteUInt16(writer, peOptions.MajorSubsystemVersion); WriteUInt16(writer, peOptions.MinorSubsystemVersion); WriteUInt32(writer, peOptions.Win32VersionValue); - writer.Write(sectionSizes.SizeOfImage); - writer.Write(sectionSizes.SizeOfHeaders); - checkSumOffset = writer.BaseStream.Position; - writer.Write(0); // CheckSum + writer.WriteUInt32(sectionSizes.SizeOfImage); + writer.WriteUInt32(sectionSizes.SizeOfHeaders); + checkSumOffset = writer.Position; + writer.WriteInt32(0); // CheckSum WriteUInt16(writer, peOptions.Subsystem ?? GetSubsystem()); WriteUInt16(writer, peOptions.DllCharacteristics ?? module.DllCharacteristics); WriteUInt64(writer, peOptions.SizeOfStackReserve); @@ -501,109 +632,108 @@ void UpdateHeaderFields(BinaryWriter writer) { } // Update Win32 resources data directory, if we wrote a new one - if (win32Resources != null) { - writer.BaseStream.Position = dataDirOffset + 2 * 8; + if (win32Resources is not null) { + writer.Position = dataDirOffset + 2 * 8; writer.WriteDataDirectory(win32Resources); } // Clear the security descriptor directory - writer.BaseStream.Position = dataDirOffset + 4 * 8; + writer.Position = dataDirOffset + 4 * 8; writer.WriteDataDirectory(null); // Write a new debug directory - writer.BaseStream.Position = dataDirOffset + 6 * 8; - writer.WriteDataDirectory(debugDirectory, DebugDirectory.HEADER_SIZE); + writer.Position = dataDirOffset + 6 * 8; + writer.WriteDebugDirectory(debugDirectory); // Write a new Metadata data directory - writer.BaseStream.Position = dataDirOffset + 14 * 8; + writer.Position = dataDirOffset + 14 * 8; writer.WriteDataDirectory(imageCor20Header); // Update old sections, and add new sections - writer.BaseStream.Position = sectionsOffset; + writer.Position = sectionsOffset; foreach (var section in origSections) { - writer.BaseStream.Position += 0x14; - writer.Write((uint)section.Chunk.FileOffset); // PointerToRawData - writer.BaseStream.Position += 0x10; + writer.Position += 0x14; + writer.WriteUInt32((uint)section.Chunk.FileOffset); // PointerToRawData + writer.Position += 0x10; } foreach (var section in sections) - section.WriteHeaderTo(writer, fileAlignment, sectionAlignment, (uint)section.RVA); + section.WriteHeaderTo(writer, peImage.ImageNTHeaders.OptionalHeader.FileAlignment, peImage.ImageNTHeaders.OptionalHeader.SectionAlignment, (uint)section.RVA); // Write the .NET header - writer.BaseStream.Position = cor20Offset; - writer.Write(0x48); // cb + writer.Position = cor20Offset; + writer.WriteInt32(0x48); // cb WriteUInt16(writer, Options.Cor20HeaderOptions.MajorRuntimeVersion); WriteUInt16(writer, Options.Cor20HeaderOptions.MinorRuntimeVersion); - writer.WriteDataDirectory(metaData); - uint entryPoint; - writer.Write((uint)GetComImageFlags(GetEntryPoint(out entryPoint))); - writer.Write(Options.Cor20HeaderOptions.EntryPoint ?? entryPoint); + writer.WriteDataDirectory(metadata); + writer.WriteUInt32((uint)GetComImageFlags(entryPointIsManagedOrNoEntryPoint)); + writer.WriteUInt32(entryPointToken); writer.WriteDataDirectory(netResources); writer.WriteDataDirectory(strongNameSignature); - WriteDataDirectory(writer, module.MetaData.ImageCor20Header.CodeManagerTable); - WriteDataDirectory(writer, module.MetaData.ImageCor20Header.VTableFixups); - WriteDataDirectory(writer, module.MetaData.ImageCor20Header.ExportAddressTableJumps); - WriteDataDirectory(writer, module.MetaData.ImageCor20Header.ManagedNativeHeader); + WriteDataDirectory(writer, module.Metadata.ImageCor20Header.CodeManagerTable); + WriteDataDirectory(writer, module.Metadata.ImageCor20Header.VTableFixups); + WriteDataDirectory(writer, module.Metadata.ImageCor20Header.ExportAddressTableJumps); + WriteDataDirectory(writer, module.Metadata.ImageCor20Header.ManagedNativeHeader); UpdateVTableFixups(writer); } - static void WriteDataDirectory(BinaryWriter writer, ImageDataDirectory dataDir) { - writer.Write((uint)dataDir.VirtualAddress); - writer.Write(dataDir.Size); + static void WriteDataDirectory(DataWriter writer, ImageDataDirectory dataDir) { + writer.WriteUInt32((uint)dataDir.VirtualAddress); + writer.WriteUInt32(dataDir.Size); } - static void WriteByte(BinaryWriter writer, byte? value) { - if (value == null) - writer.BaseStream.Position++; + static void WriteByte(DataWriter writer, byte? value) { + if (value is null) + writer.Position++; else - writer.Write(value.Value); + writer.WriteByte(value.Value); } - static void WriteUInt16(BinaryWriter writer, ushort? value) { - if (value == null) - writer.BaseStream.Position += 2; + static void WriteUInt16(DataWriter writer, ushort? value) { + if (value is null) + writer.Position += 2; else - writer.Write(value.Value); + writer.WriteUInt16(value.Value); } - static void WriteUInt16(BinaryWriter writer, Subsystem? value) { - if (value == null) - writer.BaseStream.Position += 2; + static void WriteUInt16(DataWriter writer, Subsystem? value) { + if (value is null) + writer.Position += 2; else - writer.Write((ushort)value.Value); + writer.WriteUInt16((ushort)value.Value); } - static void WriteUInt16(BinaryWriter writer, DllCharacteristics? value) { - if (value == null) - writer.BaseStream.Position += 2; + static void WriteUInt16(DataWriter writer, DllCharacteristics? value) { + if (value is null) + writer.Position += 2; else - writer.Write((ushort)value.Value); + writer.WriteUInt16((ushort)value.Value); } - static void WriteUInt32(BinaryWriter writer, uint? value) { - if (value == null) - writer.BaseStream.Position += 4; + static void WriteUInt32(DataWriter writer, uint? value) { + if (value is null) + writer.Position += 4; else - writer.Write(value.Value); + writer.WriteUInt32(value.Value); } - static void WriteUInt32(BinaryWriter writer, ulong? value) { - if (value == null) - writer.BaseStream.Position += 4; + static void WriteUInt32(DataWriter writer, ulong? value) { + if (value is null) + writer.Position += 4; else - writer.Write((uint)value.Value); + writer.WriteUInt32((uint)value.Value); } - static void WriteUInt64(BinaryWriter writer, ulong? value) { - if (value == null) - writer.BaseStream.Position += 8; + static void WriteUInt64(DataWriter writer, ulong? value) { + if (value is null) + writer.Position += 8; else - writer.Write(value.Value); + writer.WriteUInt64(value.Value); } ComImageFlags GetComImageFlags(bool isManagedEntryPoint) { var flags = Options.Cor20HeaderOptions.Flags ?? module.Cor20HeaderFlags; - if (Options.Cor20HeaderOptions.EntryPoint != null) + if (Options.Cor20HeaderOptions.EntryPoint is not null) return flags; if (isManagedEntryPoint) return flags & ~ComImageFlags.NativeEntryPoint; @@ -638,55 +768,57 @@ IEnumerable GetSectionSizeInfos() { yield return new SectionSizeInfo(section.GetVirtualSize(), section.Characteristics); } - void UpdateVTableFixups(BinaryWriter writer) { + void UpdateVTableFixups(DataWriter writer) { var vtableFixups = module.VTableFixups; - if (vtableFixups == null || vtableFixups.VTables.Count == 0) + if (vtableFixups is null || vtableFixups.VTables.Count == 0) return; - writer.BaseStream.Position = ToWriterOffset(vtableFixups.RVA); - if (writer.BaseStream.Position == 0) { + writer.Position = ToWriterOffset(vtableFixups.RVA); + if (writer.Position == 0) { Error("Could not convert RVA to file offset"); return; } foreach (var vtable in vtableFixups) { if (vtable.Methods.Count > ushort.MaxValue) throw new ModuleWriterException("Too many methods in vtable"); - writer.Write((uint)vtable.RVA); - writer.Write((ushort)vtable.Methods.Count); - writer.Write((ushort)vtable.Flags); - - long pos = writer.BaseStream.Position; - writer.BaseStream.Position = ToWriterOffset(vtable.RVA); - if (writer.BaseStream.Position == 0) - Error("Could not convert RVA to file offset"); + writer.WriteUInt32((uint)vtable.RVA); + writer.WriteUInt16((ushort)vtable.Methods.Count); + writer.WriteUInt16((ushort)vtable.Flags); + + long pos = writer.Position; + writer.Position = ToWriterOffset(vtable.RVA); + if (writer.Position == 0) { + if (vtable.RVA != 0 || vtable.Methods.Count > 0) + Error("Could not convert RVA to file offset"); + } else { - foreach (var method in vtable.Methods) { - writer.Write(GetMethodToken(method)); + var methods = vtable.Methods; + int count = methods.Count; + for (int i = 0; i < count; i++) { + var method = methods[i]; + writer.WriteUInt32(GetMethodToken(method)); if (vtable.Is64Bit) - writer.Write(0); + writer.WriteInt32(0); } } - writer.BaseStream.Position = pos; + writer.Position = pos; } } uint GetMethodToken(IMethod method) { - var md = method as MethodDef; - if (md != null) - return new MDToken(Table.Method, metaData.GetRid(md)).Raw; + if (method is MethodDef md) + return new MDToken(Table.Method, metadata.GetRid(md)).Raw; - var mr = method as MemberRef; - if (mr != null) - return new MDToken(Table.MemberRef, metaData.GetRid(mr)).Raw; + if (method is MemberRef mr) + return new MDToken(Table.MemberRef, metadata.GetRid(mr)).Raw; - var ms = method as MethodSpec; - if (ms != null) - return new MDToken(Table.MethodSpec, metaData.GetRid(ms)).Raw; + if (method is MethodSpec ms) + return new MDToken(Table.MethodSpec, metadata.GetRid(ms)).Raw; - if (method == null) - Error("VTable method is null"); - else - Error("Invalid VTable method type: {0}", method.GetType()); + if (method is null) + return 0; + + Error("Invalid VTable method type: {0}", method.GetType()); return 0; } @@ -697,14 +829,18 @@ uint GetMethodToken(IMethod method) { /// true if it's a managed entry point or there's no entry point, /// false if it's a native entry point bool GetEntryPoint(out uint ep) { - var epMethod = module.ManagedEntryPoint as MethodDef; - if (epMethod != null) { - ep = new MDToken(Table.Method, metaData.GetRid(epMethod)).Raw; + var tok = Options.Cor20HeaderOptions.EntryPoint; + if (tok is not null) { + ep = tok.Value; + return ep == 0 || ((Options.Cor20HeaderOptions.Flags ?? 0) & ComImageFlags.NativeEntryPoint) == 0; + } + + if (module.ManagedEntryPoint is MethodDef epMethod) { + ep = new MDToken(Table.Method, metadata.GetRid(epMethod)).Raw; return true; } - var file = module.ManagedEntryPoint as FileDef; - if (file != null) { - ep = new MDToken(Table.File, metaData.GetRid(file)).Raw; + if (module.ManagedEntryPoint is FileDef file) { + ep = new MDToken(Table.File, metadata.GetRid(file)).Raw; return true; } ep = (uint)module.NativeEntryPoint; diff --git a/src/DotNet/Writer/NetResources.cs b/src/DotNet/Writer/NetResources.cs index 3f8a85427..f2986f2ea 100644 --- a/src/DotNet/Writer/NetResources.cs +++ b/src/DotNet/Writer/NetResources.cs @@ -1,8 +1,7 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; -using System.IO; using dnlib.IO; using dnlib.PE; @@ -10,88 +9,84 @@ namespace dnlib.DotNet.Writer { /// /// .NET resources /// - public sealed class NetResources : IChunk { - readonly List resources = new List(); + public sealed class NetResources : IReuseChunk { + readonly List resources = new List(); readonly uint alignment; uint length; bool setOffsetCalled; FileOffset offset; RVA rva; + internal bool IsEmpty => resources.Count == 0; + /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } - } + public RVA RVA => rva; /// /// Gets offset of next resource. This offset is relative to the start of /// the .NET resources and is always aligned. /// - public uint NextOffset { - get { return length; } - } + public uint NextOffset => Utils.AlignUp(length, alignment); /// /// Constructor /// /// Alignment of all resources - public NetResources(uint alignment) { - this.alignment = alignment; - } + public NetResources(uint alignment) => this.alignment = alignment; /// /// Adds a resource /// - /// The resource data + /// The resource data /// The resource data - public ByteArrayChunk Add(IImageStream stream) { + public DataReaderChunk Add(DataReader reader) { if (setOffsetCalled) throw new InvalidOperationException("SetOffset() has already been called"); - var rawData = stream.ReadAllBytes(); - length = Utils.AlignUp(length + 4 + (uint)rawData.Length, alignment); - var data = new ByteArrayChunk(rawData); + length = NextOffset + 4 + reader.Length; + var data = new DataReaderChunk(ref reader); resources.Add(data); return data; } + bool IReuseChunk.CanReuse(RVA origRva, uint origSize) => length <= origSize; + /// public void SetOffset(FileOffset offset, RVA rva) { setOffsetCalled = true; this.offset = offset; this.rva = rva; foreach (var resource in resources) { + offset = offset.AlignUp(alignment); + rva = rva.AlignUp(alignment); resource.SetOffset(offset + 4, rva + 4); uint len = 4 + resource.GetFileLength(); - offset = (offset + len).AlignUp(alignment); - rva = (rva + len).AlignUp(alignment); + offset += len; + rva += len; } } /// - public uint GetFileLength() { - return length; - } + public uint GetFileLength() => length; /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetVirtualSize() => GetFileLength(); + + /// + public uint CalculateAlignment() => 0; /// - public void WriteTo(BinaryWriter writer) { - RVA rva2 = rva; + public void WriteTo(DataWriter writer) { + var rva2 = rva; foreach (var resourceData in resources) { - writer.Write(resourceData.GetFileLength()); - resourceData.VerifyWriteTo(writer); - rva2 += 4 + resourceData.GetFileLength(); int padding = (int)rva2.AlignUp(alignment) - (int)rva2; - writer.WriteZeros(padding); + writer.WriteZeroes(padding); rva2 += (uint)padding; + writer.WriteUInt32(resourceData.GetFileLength()); + resourceData.VerifyWriteTo(writer); + rva2 += 4 + resourceData.GetFileLength(); } } } diff --git a/src/DotNet/Writer/NormalMetaData.cs b/src/DotNet/Writer/NormalMetadata.cs similarity index 63% rename from src/DotNet/Writer/NormalMetaData.cs rename to src/DotNet/Writer/NormalMetadata.cs index 59023a2cc..1e8443cbb 100644 --- a/src/DotNet/Writer/NormalMetaData.cs +++ b/src/DotNet/Writer/NormalMetadata.cs @@ -1,13 +1,13 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; +using System.Linq; using dnlib.DotNet.MD; namespace dnlib.DotNet.Writer { /// /// Does not preserve metadata tokens /// - sealed class NormalMetaData : MetaData { + sealed class NormalMetadata : Metadata { readonly Rows typeRefInfos = new Rows(); readonly Rows typeDefInfos = new Rows(); readonly Rows fieldDefInfos = new Rows(); @@ -20,33 +20,23 @@ sealed class NormalMetaData : MetaData { readonly Rows typeSpecInfos = new Rows(); readonly Rows methodSpecInfos = new Rows(); - protected override int NumberOfMethods { - get { return methodDefInfos.Count; } - } + protected override int NumberOfMethods => methodDefInfos.Count; - /// - /// Constructor - /// - /// Module - /// Constants list - /// Method bodies list - /// .NET resources list - /// Options - public NormalMetaData(ModuleDef module, UniqueChunkList constants, MethodBodyChunks methodBodies, NetResources netResources, MetaDataOptions options) - : base(module, constants, methodBodies, netResources, options) { + public NormalMetadata(ModuleDef module, UniqueChunkList constants, MethodBodyChunks methodBodies, NetResources netResources, MetadataOptions options, DebugMetadataKind debugKind, bool isStandaloneDebugMetadata) + : base(module, constants, methodBodies, netResources, options, debugKind, isStandaloneDebugMetadata) { } /// - protected override List GetAllTypeDefs() { + protected override TypeDef[] GetAllTypeDefs() { // All nested types must be after their enclosing type. This is exactly // what module.GetTypes() does. - return new List(module.GetTypes()); + return module.GetTypes().ToArray(); } /// protected override void AllocateTypeDefRids() { foreach (var type in allTypeDefs) { - if (type == null) + if (type is null) continue; uint rid = tablesHeap.TypeDefTable.Create(new RawTypeDefRow()); typeDefInfos.Add(type, rid); @@ -55,30 +45,35 @@ protected override void AllocateTypeDefRids() { /// protected override void AllocateMemberDefRids() { - int numTypes = allTypeDefs.Count; + int numTypes = allTypeDefs.Length; int typeNum = 0; int notifyNum = 0; - const int numNotifyEvents = 5; // AllocateMemberDefRids0 - AllocateMemberDefRids4 + const int numNotifyEvents = 5; int notifyAfter = numTypes / numNotifyEvents; uint fieldListRid = 1, methodListRid = 1; uint eventListRid = 1, propertyListRid = 1; uint paramListRid = 1; + int count; foreach (var type in allTypeDefs) { if (typeNum++ == notifyAfter && notifyNum < numNotifyEvents) { - Listener.OnMetaDataEvent(this, MetaDataEvent.AllocateMemberDefRids0 + notifyNum++); - notifyAfter += numTypes / numNotifyEvents; + RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, (double)typeNum / numTypes); + notifyNum++; + notifyAfter = (int)((double)numTypes / numNotifyEvents * (notifyNum + 1)); } - if (type == null) + if (type is null) continue; uint typeRid = GetRid(type); var typeRow = tablesHeap.TypeDefTable[typeRid]; - typeRow.FieldList = fieldListRid; - typeRow.MethodList = methodListRid; + typeRow = new RawTypeDefRow(typeRow.Flags, typeRow.Name, typeRow.Namespace, typeRow.Extends, fieldListRid, methodListRid); + tablesHeap.TypeDefTable[typeRid] = typeRow; - foreach (var field in type.Fields) { - if (field == null) + var fields = type.Fields; + count = fields.Count; + for (int i = 0; i < count; i++) { + var field = fields[i]; + if (field is null) continue; uint rid = fieldListRid++; if (rid != tablesHeap.FieldTable.Create(new RawFieldRow())) @@ -86,8 +81,11 @@ protected override void AllocateMemberDefRids() { fieldDefInfos.Add(field, rid); } - foreach (var method in type.Methods) { - if (method == null) + var methods = type.Methods; + count = methods.Count; + for (int i = 0; i < count; i++) { + var method = methods[i]; + if (method is null) continue; uint rid = methodListRid++; var row = new RawMethodRow(0, 0, 0, 0, 0, paramListRid); @@ -95,7 +93,7 @@ protected override void AllocateMemberDefRids() { throw new ModuleWriterException("Invalid method rid"); methodDefInfos.Add(method, rid); foreach (var pd in Sort(method.ParamDefs)) { - if (pd == null) + if (pd is null) continue; uint pdRid = paramListRid++; if (pdRid != tablesHeap.ParamTable.Create(new RawParamRow())) @@ -107,8 +105,11 @@ protected override void AllocateMemberDefRids() { if (!IsEmpty(type.Events)) { uint eventMapRid = tablesHeap.EventMapTable.Create(new RawEventMapRow(typeRid, eventListRid)); eventMapInfos.Add(type, eventMapRid); - foreach (var evt in type.Events) { - if (evt == null) + var events = type.Events; + count = events.Count; + for (int i = 0; i < count; i++) { + var evt = events[i]; + if (evt is null) continue; uint rid = eventListRid++; if (rid != tablesHeap.EventTable.Create(new RawEventRow())) @@ -120,8 +121,11 @@ protected override void AllocateMemberDefRids() { if (!IsEmpty(type.Properties)) { uint propertyMapRid = tablesHeap.PropertyMapTable.Create(new RawPropertyMapRow(typeRid, propertyListRid)); propertyMapInfos.Add(type, propertyMapRid); - foreach (var prop in type.Properties) { - if (prop == null) + var properties = type.Properties; + count = properties.Count; + for (int i = 0; i < count; i++) { + var prop = properties[i]; + if (prop is null) continue; uint rid = propertyListRid++; if (rid != tablesHeap.PropertyTable.Create(new RawPropertyRow())) @@ -130,127 +134,113 @@ protected override void AllocateMemberDefRids() { } } } - while (notifyNum < numNotifyEvents) - Listener.OnMetaDataEvent(this, MetaDataEvent.AllocateMemberDefRids0 + notifyNum++); } /// public override uint GetRid(TypeRef tr) { - uint rid; - typeRefInfos.TryGetRid(tr, out rid); + typeRefInfos.TryGetRid(tr, out uint rid); return rid; } /// public override uint GetRid(TypeDef td) { - uint rid; - if (typeDefInfos.TryGetRid(td, out rid)) + if (typeDefInfos.TryGetRid(td, out uint rid)) return rid; - if (td == null) + if (td is null) Error("TypeDef is null"); else - Error("TypeDef {0} ({1:X8}) is not defined in this module ({2}). A type was removed that is still referenced by this module.", td, td.MDToken.Raw, module); + Error("TypeDef '{0}' (0x{1:X8}) is not defined in this module '{2}'. A type was removed that is still referenced by this module.", td, td.MDToken.Raw, module); return 0; } /// public override uint GetRid(FieldDef fd) { - uint rid; - if (fieldDefInfos.TryGetRid(fd, out rid)) + if (fieldDefInfos.TryGetRid(fd, out uint rid)) return rid; - if (fd == null) + if (fd is null) Error("Field is null"); else - Error("Field {0} ({1:X8}) is not defined in this module ({2}). A field was removed that is still referenced by this module.", fd, fd.MDToken.Raw, module); + Error("Field '{0}' (0x{1:X8}) is not defined in this module '{2}'. A field was removed that is still referenced by this module.", fd, fd.MDToken.Raw, module); return 0; } /// public override uint GetRid(MethodDef md) { - uint rid; - if (methodDefInfos.TryGetRid(md, out rid)) + if (methodDefInfos.TryGetRid(md, out uint rid)) return rid; - if (md == null) + if (md is null) Error("Method is null"); else - Error("Method {0} ({1:X8}) is not defined in this module ({2}). A method was removed that is still referenced by this module.", md, md.MDToken.Raw, module); + Error("Method '{0}' (0x{1:X8}) is not defined in this module '{2}'. A method was removed that is still referenced by this module.", md, md.MDToken.Raw, module); return 0; } /// public override uint GetRid(ParamDef pd) { - uint rid; - if (paramDefInfos.TryGetRid(pd, out rid)) + if (paramDefInfos.TryGetRid(pd, out uint rid)) return rid; - if (pd == null) + if (pd is null) Error("Param is null"); else - Error("Param {0} ({1:X8}) is not defined in this module ({2}). A parameter was removed that is still referenced by this module.", pd, pd.MDToken.Raw, module); + Error("Param '{0}' (0x{1:X8}) is not defined in this module '{2}'. A parameter was removed that is still referenced by this module.", pd, pd.MDToken.Raw, module); return 0; } /// public override uint GetRid(MemberRef mr) { - uint rid; - memberRefInfos.TryGetRid(mr, out rid); + memberRefInfos.TryGetRid(mr, out uint rid); return rid; } /// public override uint GetRid(StandAloneSig sas) { - uint rid; - standAloneSigInfos.TryGetRid(sas, out rid); + standAloneSigInfos.TryGetRid(sas, out uint rid); return rid; } /// public override uint GetRid(EventDef ed) { - uint rid; - if (eventDefInfos.TryGetRid(ed, out rid)) + if (eventDefInfos.TryGetRid(ed, out uint rid)) return rid; - if (ed == null) + if (ed is null) Error("Event is null"); else - Error("Event {0} ({1:X8}) is not defined in this module ({2}). An event was removed that is still referenced by this module.", ed, ed.MDToken.Raw, module); + Error("Event '{0}' (0x{1:X8}) is not defined in this module '{2}'. An event was removed that is still referenced by this module.", ed, ed.MDToken.Raw, module); return 0; } /// public override uint GetRid(PropertyDef pd) { - uint rid; - if (propertyDefInfos.TryGetRid(pd, out rid)) + if (propertyDefInfos.TryGetRid(pd, out uint rid)) return rid; - if (pd == null) + if (pd is null) Error("Property is null"); else - Error("Property {0} ({1:X8}) is not defined in this module ({2}). A property was removed that is still referenced by this module.", pd, pd.MDToken.Raw, module); + Error("Property '{0}' (0x{1:X8}) is not defined in this module '{2}'. A property was removed that is still referenced by this module.", pd, pd.MDToken.Raw, module); return 0; } /// public override uint GetRid(TypeSpec ts) { - uint rid; - typeSpecInfos.TryGetRid(ts, out rid); + typeSpecInfos.TryGetRid(ts, out uint rid); return rid; } /// public override uint GetRid(MethodSpec ms) { - uint rid; - methodSpecInfos.TryGetRid(ms, out rid); + methodSpecInfos.TryGetRid(ms, out uint rid); return rid; } /// protected override uint AddTypeRef(TypeRef tr) { - if (tr == null) { + if (tr is null) { Error("TypeRef is null"); return 0; } - uint rid; - if (typeRefInfos.TryGetRid(tr, out rid)) { + if (typeRefInfos.TryGetRid(tr, out uint rid)) { if (rid == 0) - Error("TypeRef {0:X8} has an infinite ResolutionScope loop", tr.MDToken.Raw); + Error("TypeRef 0x{0:X8} has an infinite ResolutionScope loop.", tr.MDToken.Raw); return rid; } typeRefInfos.Add(tr, 0); // Prevent inf recursion @@ -260,19 +250,19 @@ protected override uint AddTypeRef(TypeRef tr) { rid = tablesHeap.TypeRefTable.Add(row); typeRefInfos.SetRid(tr, rid); AddCustomAttributes(Table.TypeRef, rid, tr); + AddCustomDebugInformationList(Table.TypeRef, rid, tr); return rid; } /// protected override uint AddTypeSpec(TypeSpec ts) { - if (ts == null) { + if (ts is null) { Error("TypeSpec is null"); return 0; } - uint rid; - if (typeSpecInfos.TryGetRid(ts, out rid)) { + if (typeSpecInfos.TryGetRid(ts, out uint rid)) { if (rid == 0) - Error("TypeSpec {0:X8} has an infinite TypeSig loop", ts.MDToken.Raw); + Error("TypeSpec 0x{0:X8} has an infinite TypeSig loop.", ts.MDToken.Raw); return rid; } typeSpecInfos.Add(ts, 0); // Prevent inf recursion @@ -280,17 +270,18 @@ protected override uint AddTypeSpec(TypeSpec ts) { rid = tablesHeap.TypeSpecTable.Add(row); typeSpecInfos.SetRid(ts, rid); AddCustomAttributes(Table.TypeSpec, rid, ts); + AddCustomDebugInformationList(Table.TypeSpec, rid, ts); return rid; } /// protected override uint AddMemberRef(MemberRef mr) { - if (mr == null) { + if (mr is null) { Error("MemberRef is null"); return 0; } - uint rid; - if (memberRefInfos.TryGetRid(mr, out rid)) + + if (memberRefInfos.TryGetRid(mr, out uint rid)) return rid; var row = new RawMemberRefRow(AddMemberRefParent(mr.Class), stringsHeap.Add(mr.Name), @@ -298,39 +289,40 @@ protected override uint AddMemberRef(MemberRef mr) { rid = tablesHeap.MemberRefTable.Add(row); memberRefInfos.Add(mr, rid); AddCustomAttributes(Table.MemberRef, rid, mr); + AddCustomDebugInformationList(Table.MemberRef, rid, mr); return rid; } /// protected override uint AddStandAloneSig(StandAloneSig sas) { - if (sas == null) { + if (sas is null) { Error("StandAloneSig is null"); return 0; } - uint rid; - if (standAloneSigInfos.TryGetRid(sas, out rid)) + if (standAloneSigInfos.TryGetRid(sas, out uint rid)) return rid; var row = new RawStandAloneSigRow(GetSignature(sas.Signature)); rid = tablesHeap.StandAloneSigTable.Add(row); standAloneSigInfos.Add(sas, rid); AddCustomAttributes(Table.StandAloneSig, rid, sas); + AddCustomDebugInformationList(Table.StandAloneSig, rid, sas); return rid; } /// protected override uint AddMethodSpec(MethodSpec ms) { - if (ms == null) { + if (ms is null) { Error("MethodSpec is null"); return 0; } - uint rid; - if (methodSpecInfos.TryGetRid(ms, out rid)) + if (methodSpecInfos.TryGetRid(ms, out uint rid)) return rid; var row = new RawMethodSpecRow(AddMethodDefOrRef(ms.Method), GetSignature(ms.Instantiation)); rid = tablesHeap.MethodSpecTable.Add(row); methodSpecInfos.Add(ms, rid); AddCustomAttributes(Table.MethodSpec, rid, ms); + AddCustomDebugInformationList(Table.MethodSpec, rid, ms); return rid; } } diff --git a/src/DotNet/Writer/PEHeaders.cs b/src/DotNet/Writer/PEHeaders.cs index 5a58f16e3..34502b779 100644 --- a/src/DotNet/Writer/PEHeaders.cs +++ b/src/DotNet/Writer/PEHeaders.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -using System.IO; +using System.Diagnostics; using dnlib.IO; using dnlib.PE; @@ -22,7 +22,7 @@ public sealed class PEHeadersOptions { public const Subsystem DEFAULT_SUBSYSTEM = dnlib.PE.Subsystem.WindowsGui; /// - /// Default major linker version + /// Default major linker version. Roslyn C# defaults to 0x30, and Roslyn VB defaults to 0x50. /// public const byte DEFAULT_MAJOR_LINKER_VERSION = 11; @@ -161,9 +161,7 @@ public sealed class PEHeadersOptions { /// Creates a new time date stamp using current time /// /// A new time date stamp - public static uint CreateNewTimeDateStamp() { - return (uint)(DateTime.UtcNow - Epoch).TotalSeconds; - } + public static uint CreateNewTimeDateStamp() => (uint)(DateTime.UtcNow - Epoch).TotalSeconds; static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); } @@ -238,51 +236,43 @@ public sealed class PEHeaders : IChunk { /// public DebugDirectory DebugDirectory { get; set; } + internal IChunk ExportDirectory { get; set; } + /// /// Gets the image base /// - public ulong ImageBase { - get { return imageBase; } - } + public ulong ImageBase => imageBase; /// /// Gets/sets a value indicating whether this is a EXE or a DLL file /// public bool IsExeFile { - get { return isExeFile; } - set { isExeFile = value; } + get => isExeFile; + set => isExeFile = value; } /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } - } + public RVA RVA => rva; /// /// Gets the section alignment /// - public uint SectionAlignment { - get { return sectionAlignment; } - } + public uint SectionAlignment => sectionAlignment; /// /// Gets the file alignment /// - public uint FileAlignment { - get { return fileAlignment; } - } + public uint FileAlignment => fileAlignment; /// /// Gets/sets the s /// public IList PESections { - get { return sections; } - set { sections = value; } + get => sections; + set => sections = value; } /// @@ -298,8 +288,8 @@ public PEHeaders() /// Options public PEHeaders(PEHeadersOptions options) { this.options = options ?? new PEHeadersOptions(); - this.sectionAlignment = this.options.SectionAlignment ?? 0x2000; - this.fileAlignment = this.options.FileAlignment ?? 0x200; + sectionAlignment = this.options.SectionAlignment ?? 0x2000; + fileAlignment = this.options.FileAlignment ?? 0x200; } /// @@ -313,122 +303,136 @@ public void SetOffset(FileOffset offset, RVA rva) { length += (uint)sections.Count * 0x28; if (Use32BitOptionalHeader()) - imageBase = options.ImageBase ?? 0x00400000; + imageBase = options.ImageBase ?? (IsExeFile ? 0x00400000UL : 0x10000000); else - imageBase = options.ImageBase ?? 0x0000000140000000; + imageBase = options.ImageBase ?? (IsExeFile ? 0x0000000140000000UL : 0x0000000180000000); } - /// - public uint GetFileLength() { - return length; + int SectionsCount { + get { + int count = 0; + foreach (var section in sections) { + if (section.GetVirtualSize() != 0) + count++; + } + return count; + } } /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetFileLength() => length; + + /// + public uint GetVirtualSize() => GetFileLength(); + + /// + public uint CalculateAlignment() => 0; IEnumerable GetSectionSizeInfos() { - foreach (var section in PESections) - yield return new SectionSizeInfo(section.GetVirtualSize(), section.Characteristics); + foreach (var section in sections) { + uint virtSize = section.GetVirtualSize(); + if (virtSize != 0) + yield return new SectionSizeInfo(virtSize, section.Characteristics); + } } /// - public void WriteTo(BinaryWriter writer) { - startOffset = writer.BaseStream.Position; + public void WriteTo(DataWriter writer) { + startOffset = writer.Position; // DOS header - writer.Write(dosHeader); + writer.WriteBytes(dosHeader); // PE magic - writer.Write(0x00004550); + writer.WriteInt32(0x00004550); // Image file header - writer.Write((ushort)GetMachine()); - writer.Write((ushort)sections.Count); - writer.Write(options.TimeDateStamp ?? PEHeadersOptions.CreateNewTimeDateStamp()); - writer.Write(options.PointerToSymbolTable ?? 0); - writer.Write(options.NumberOfSymbols ?? 0); - writer.Write((ushort)(Use32BitOptionalHeader() ? 0xE0U : 0xF0)); - writer.Write((ushort)GetCharacteristics()); + writer.WriteUInt16((ushort)GetMachine()); + writer.WriteUInt16((ushort)SectionsCount); + Debug.Assert(SectionsCount == sections.Count, "One or more sections are empty! The PE file could be bigger than it should be. Empty sections should be removed."); + writer.WriteUInt32(options.TimeDateStamp ?? PEHeadersOptions.CreateNewTimeDateStamp()); + writer.WriteUInt32(options.PointerToSymbolTable ?? 0); + writer.WriteUInt32(options.NumberOfSymbols ?? 0); + writer.WriteUInt16((ushort)(Use32BitOptionalHeader() ? 0xE0U : 0xF0)); + writer.WriteUInt16((ushort)GetCharacteristics()); var sectionSizes = new SectionSizes(fileAlignment, sectionAlignment, length, () => GetSectionSizeInfos()); // Image optional header - uint ep = StartupStub == null ? 0 : (uint)StartupStub.EntryPointRVA; + uint ep = StartupStub is null || !StartupStub.Enable ? 0 : (uint)StartupStub.EntryPointRVA; if (Use32BitOptionalHeader()) { - writer.Write((ushort)0x010B); - writer.Write(options.MajorLinkerVersion ?? PEHeadersOptions.DEFAULT_MAJOR_LINKER_VERSION); - writer.Write(options.MinorLinkerVersion ?? PEHeadersOptions.DEFAULT_MINOR_LINKER_VERSION); - writer.Write(sectionSizes.SizeOfCode); - writer.Write(sectionSizes.SizeOfInitdData); - writer.Write(sectionSizes.SizeOfUninitdData); - writer.Write(ep); - writer.Write(sectionSizes.BaseOfCode); - writer.Write(sectionSizes.BaseOfData); - writer.Write((uint)imageBase); - writer.Write(sectionAlignment); - writer.Write(fileAlignment); - writer.Write(options.MajorOperatingSystemVersion ?? 4); - writer.Write(options.MinorOperatingSystemVersion ?? 0); - writer.Write(options.MajorImageVersion ?? 0); - writer.Write(options.MinorImageVersion ?? 0); - writer.Write(options.MajorSubsystemVersion ?? 4); - writer.Write(options.MinorSubsystemVersion ?? 0); - writer.Write(options.Win32VersionValue ?? 0); - writer.Write(sectionSizes.SizeOfImage); - writer.Write(sectionSizes.SizeOfHeaders); - checkSumOffset = writer.BaseStream.Position; - writer.Write(0); // CheckSum - writer.Write((ushort)(options.Subsystem ?? PEHeadersOptions.DEFAULT_SUBSYSTEM)); - writer.Write((ushort)(options.DllCharacteristics ?? PEHeadersOptions.DefaultDllCharacteristics)); - writer.Write((uint)(options.SizeOfStackReserve ?? 0x00100000)); - writer.Write((uint)(options.SizeOfStackCommit ?? 0x00001000)); - writer.Write((uint)(options.SizeOfHeapReserve ?? 0x00100000)); - writer.Write((uint)(options.SizeOfHeapCommit ?? 0x00001000)); - writer.Write(options.LoaderFlags ?? 0x00000000); - writer.Write(options.NumberOfRvaAndSizes ?? 0x00000010); + writer.WriteUInt16((ushort)0x010B); + writer.WriteByte(options.MajorLinkerVersion ?? PEHeadersOptions.DEFAULT_MAJOR_LINKER_VERSION); + writer.WriteByte(options.MinorLinkerVersion ?? PEHeadersOptions.DEFAULT_MINOR_LINKER_VERSION); + writer.WriteUInt32(sectionSizes.SizeOfCode); + writer.WriteUInt32(sectionSizes.SizeOfInitdData); + writer.WriteUInt32(sectionSizes.SizeOfUninitdData); + writer.WriteUInt32(ep); + writer.WriteUInt32(sectionSizes.BaseOfCode); + writer.WriteUInt32(sectionSizes.BaseOfData); + writer.WriteUInt32((uint)imageBase); + writer.WriteUInt32(sectionAlignment); + writer.WriteUInt32(fileAlignment); + writer.WriteUInt16(options.MajorOperatingSystemVersion ?? 4); + writer.WriteUInt16(options.MinorOperatingSystemVersion ?? 0); + writer.WriteUInt16(options.MajorImageVersion ?? 0); + writer.WriteUInt16(options.MinorImageVersion ?? 0); + writer.WriteUInt16(options.MajorSubsystemVersion ?? 4); + writer.WriteUInt16(options.MinorSubsystemVersion ?? 0); + writer.WriteUInt32(options.Win32VersionValue ?? 0); + writer.WriteUInt32(sectionSizes.SizeOfImage); + writer.WriteUInt32(sectionSizes.SizeOfHeaders); + checkSumOffset = writer.Position; + writer.WriteInt32(0); // CheckSum + writer.WriteUInt16((ushort)(options.Subsystem ?? PEHeadersOptions.DEFAULT_SUBSYSTEM)); + writer.WriteUInt16((ushort)(options.DllCharacteristics ?? PEHeadersOptions.DefaultDllCharacteristics)); + writer.WriteUInt32((uint)(options.SizeOfStackReserve ?? 0x00100000)); + writer.WriteUInt32((uint)(options.SizeOfStackCommit ?? 0x00001000)); + writer.WriteUInt32((uint)(options.SizeOfHeapReserve ?? 0x00100000)); + writer.WriteUInt32((uint)(options.SizeOfHeapCommit ?? 0x00001000)); + writer.WriteUInt32(options.LoaderFlags ?? 0x00000000); + writer.WriteUInt32(options.NumberOfRvaAndSizes ?? 0x00000010); } else { - writer.Write((ushort)0x020B); - writer.Write(options.MajorLinkerVersion ?? PEHeadersOptions.DEFAULT_MAJOR_LINKER_VERSION); - writer.Write(options.MinorLinkerVersion ?? PEHeadersOptions.DEFAULT_MINOR_LINKER_VERSION); - writer.Write(sectionSizes.SizeOfCode); - writer.Write(sectionSizes.SizeOfInitdData); - writer.Write(sectionSizes.SizeOfUninitdData); - writer.Write(ep); - writer.Write(sectionSizes.BaseOfCode); - writer.Write(imageBase); - writer.Write(sectionAlignment); - writer.Write(fileAlignment); - writer.Write(options.MajorOperatingSystemVersion ?? 4); - writer.Write(options.MinorOperatingSystemVersion ?? 0); - writer.Write(options.MajorImageVersion ?? 0); - writer.Write(options.MinorImageVersion ?? 0); - writer.Write(options.MajorSubsystemVersion ?? 4); - writer.Write(options.MinorSubsystemVersion ?? 0); - writer.Write(options.Win32VersionValue ?? 0); - writer.Write(sectionSizes.SizeOfImage); - writer.Write(sectionSizes.SizeOfHeaders); - checkSumOffset = writer.BaseStream.Position; - writer.Write(0); // CheckSum - writer.Write((ushort)(options.Subsystem ?? PEHeadersOptions.DEFAULT_SUBSYSTEM)); - writer.Write((ushort)(options.DllCharacteristics ?? PEHeadersOptions.DefaultDllCharacteristics)); - writer.Write(options.SizeOfStackReserve ?? 0x0000000000400000); - writer.Write(options.SizeOfStackCommit ?? 0x0000000000004000); - writer.Write(options.SizeOfHeapReserve ?? 0x0000000000100000); - writer.Write(options.SizeOfHeapCommit ?? 0x0000000000002000); - writer.Write(options.LoaderFlags ?? 0x00000000); - writer.Write(options.NumberOfRvaAndSizes ?? 0x00000010); + writer.WriteUInt16((ushort)0x020B); + writer.WriteByte(options.MajorLinkerVersion ?? PEHeadersOptions.DEFAULT_MAJOR_LINKER_VERSION); + writer.WriteByte(options.MinorLinkerVersion ?? PEHeadersOptions.DEFAULT_MINOR_LINKER_VERSION); + writer.WriteUInt32(sectionSizes.SizeOfCode); + writer.WriteUInt32(sectionSizes.SizeOfInitdData); + writer.WriteUInt32(sectionSizes.SizeOfUninitdData); + writer.WriteUInt32(ep); + writer.WriteUInt32(sectionSizes.BaseOfCode); + writer.WriteUInt64(imageBase); + writer.WriteUInt32(sectionAlignment); + writer.WriteUInt32(fileAlignment); + writer.WriteUInt16(options.MajorOperatingSystemVersion ?? 4); + writer.WriteUInt16(options.MinorOperatingSystemVersion ?? 0); + writer.WriteUInt16(options.MajorImageVersion ?? 0); + writer.WriteUInt16(options.MinorImageVersion ?? 0); + writer.WriteUInt16(options.MajorSubsystemVersion ?? 4); + writer.WriteUInt16(options.MinorSubsystemVersion ?? 0); + writer.WriteUInt32(options.Win32VersionValue ?? 0); + writer.WriteUInt32(sectionSizes.SizeOfImage); + writer.WriteUInt32(sectionSizes.SizeOfHeaders); + checkSumOffset = writer.Position; + writer.WriteInt32(0); // CheckSum + writer.WriteUInt16((ushort)(options.Subsystem ?? PEHeadersOptions.DEFAULT_SUBSYSTEM)); + writer.WriteUInt16((ushort)(options.DllCharacteristics ?? PEHeadersOptions.DefaultDllCharacteristics)); + writer.WriteUInt64(options.SizeOfStackReserve ?? 0x0000000000400000); + writer.WriteUInt64(options.SizeOfStackCommit ?? 0x0000000000004000); + writer.WriteUInt64(options.SizeOfHeapReserve ?? 0x0000000000100000); + writer.WriteUInt64(options.SizeOfHeapCommit ?? 0x0000000000002000); + writer.WriteUInt32(options.LoaderFlags ?? 0x00000000); + writer.WriteUInt32(options.NumberOfRvaAndSizes ?? 0x00000010); } - writer.WriteDataDirectory(null); // Export table + writer.WriteDataDirectory(ExportDirectory); writer.WriteDataDirectory(ImportDirectory); writer.WriteDataDirectory(Win32Resources); writer.WriteDataDirectory(null); // Exception table writer.WriteDataDirectory(null); // Certificate table writer.WriteDataDirectory(RelocDirectory); - writer.WriteDataDirectory(DebugDirectory, DebugDirectory.HEADER_SIZE); + writer.WriteDebugDirectory(DebugDirectory); writer.WriteDataDirectory(null); // Architecture-specific data writer.WriteDataDirectory(null); // Global pointer register RVA writer.WriteDataDirectory(null); // Thread local storage @@ -441,8 +445,15 @@ public void WriteTo(BinaryWriter writer) { // Sections uint rva = Utils.AlignUp(sectionSizes.SizeOfHeaders, sectionAlignment); - foreach (var section in sections) - rva += section.WriteHeaderTo(writer, fileAlignment, sectionAlignment, rva); + int emptySections = 0; + foreach (var section in sections) { + if (section.GetVirtualSize() != 0) + rva += section.WriteHeaderTo(writer, fileAlignment, sectionAlignment, rva); + else + emptySections++; + } + if (emptySections != 0) + writer.Position += emptySections * 0x28; } /// @@ -450,21 +461,16 @@ public void WriteTo(BinaryWriter writer) { /// /// Writer /// Length of PE file - public void WriteCheckSum(BinaryWriter writer, long length) { - writer.BaseStream.Position = startOffset; - uint checkSum = new BinaryReader(writer.BaseStream).CalculatePECheckSum(length, checkSumOffset); - writer.BaseStream.Position = checkSumOffset; - writer.Write(checkSum); + public void WriteCheckSum(DataWriter writer, long length) { + writer.Position = startOffset; + uint checkSum = writer.InternalStream.CalculatePECheckSum(length, checkSumOffset); + writer.Position = checkSumOffset; + writer.WriteUInt32(checkSum); } - Machine GetMachine() { - return options.Machine ?? Machine.I386; - } + Machine GetMachine() => options.Machine ?? Machine.I386; - bool Use32BitOptionalHeader() { - var mach = GetMachine(); - return mach != Machine.IA64 && mach != Machine.AMD64 && mach != Machine.ARM64; - } + bool Use32BitOptionalHeader() => !GetMachine().Is64Bit(); Characteristics GetCharacteristics() { var chr = options.Characteristics ?? GetDefaultCharacteristics(); @@ -477,7 +483,7 @@ Characteristics GetCharacteristics() { Characteristics GetDefaultCharacteristics() { if (Use32BitOptionalHeader()) - return Characteristics._32BitMachine | Characteristics.ExecutableImage; + return Characteristics.Bit32Machine | Characteristics.ExecutableImage; return Characteristics.ExecutableImage | Characteristics.LargeAddressAware; } } diff --git a/src/DotNet/Writer/PESection.cs b/src/DotNet/Writer/PESection.cs index 1ca3fbcd9..df7a44c98 100644 --- a/src/DotNet/Writer/PESection.cs +++ b/src/DotNet/Writer/PESection.cs @@ -1,6 +1,5 @@ // dnlib: See LICENSE.txt for more info -using System.IO; using System.Text; using dnlib.PE; @@ -16,38 +15,32 @@ public sealed class PESection : ChunkList { /// Gets the name /// public string Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// /// Gets the Characteristics /// public uint Characteristics { - get { return characteristics; } - set { characteristics = value; } + get => characteristics; + set => characteristics = value; } /// /// true if this is a code section /// - public bool IsCode { - get { return (characteristics & 0x20) != 0; } - } + public bool IsCode => (characteristics & 0x20) != 0; /// /// true if this is an initialized data section /// - public bool IsInitializedData { - get { return (characteristics & 0x40) != 0; } - } + public bool IsInitializedData => (characteristics & 0x40) != 0; /// /// true if this is an uninitialized data section /// - public bool IsUninitializedData { - get { return (characteristics & 0x80) != 0; } - } + public bool IsUninitializedData => (characteristics & 0x80) != 0; /// /// Constructor @@ -67,23 +60,23 @@ public PESection(string name, uint characteristics) { /// File alignment /// Section alignment /// Current - public uint WriteHeaderTo(BinaryWriter writer, uint fileAlignment, uint sectionAlignment, uint rva) { + public uint WriteHeaderTo(DataWriter writer, uint fileAlignment, uint sectionAlignment, uint rva) { uint vs = GetVirtualSize(); uint fileLen = GetFileLength(); uint alignedVs = Utils.AlignUp(vs, sectionAlignment); uint rawSize = Utils.AlignUp(fileLen, fileAlignment); uint dataOffset = (uint)FileOffset; - writer.Write(Encoding.UTF8.GetBytes(Name + "\0\0\0\0\0\0\0\0"), 0, 8); - writer.Write(vs); // VirtualSize - writer.Write((uint)rva); // VirtualAddress - writer.Write(rawSize); // SizeOfRawData - writer.Write(dataOffset); // PointerToRawData - writer.Write(0); // PointerToRelocations - writer.Write(0); // PointerToLinenumbers - writer.Write((ushort)0); // NumberOfRelocations - writer.Write((ushort)0); // NumberOfLinenumbers - writer.Write(Characteristics); + writer.WriteBytes(Encoding.UTF8.GetBytes(Name + "\0\0\0\0\0\0\0\0"), 0, 8); + writer.WriteUInt32(vs); // VirtualSize + writer.WriteUInt32(rva); // VirtualAddress + writer.WriteUInt32(rawSize); // SizeOfRawData + writer.WriteUInt32(dataOffset); // PointerToRawData + writer.WriteInt32(0); // PointerToRelocations + writer.WriteInt32(0); // PointerToLinenumbers + writer.WriteUInt16(0); // NumberOfRelocations + writer.WriteUInt16(0); // NumberOfLinenumbers + writer.WriteUInt32(Characteristics); return alignedVs; } diff --git a/src/DotNet/Writer/PdbHeap.cs b/src/DotNet/Writer/PdbHeap.cs new file mode 100644 index 000000000..c13652d03 --- /dev/null +++ b/src/DotNet/Writer/PdbHeap.cs @@ -0,0 +1,97 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using dnlib.IO; + +namespace dnlib.DotNet.Writer { + /// + /// #Pdb heap + /// + public sealed class PdbHeap : HeapBase { + /// + public override string Name => "#Pdb"; + + /// + /// Gets the PDB ID. This is always 20 bytes in size. + /// + public byte[] PdbId => pdbId; + readonly byte[] pdbId; + + /// + /// Gets/sets the entry point token + /// + public uint EntryPoint { + get => entryPoint; + set => entryPoint = value; + } + uint entryPoint; + + /// + /// Gets the offset of the 20-byte PDB ID + /// + public FileOffset PdbIdOffset => FileOffset; + + /// + /// Gets/sets the referenced type system tables + /// + public ulong ReferencedTypeSystemTables { + get { + if (!referencedTypeSystemTablesInitd) + throw new InvalidOperationException("ReferencedTypeSystemTables hasn't been initialized yet"); + return referencedTypeSystemTables; + } + set { + if (isReadOnly) + throw new InvalidOperationException("Size has already been calculated, can't write a new value"); + referencedTypeSystemTables = value; + referencedTypeSystemTablesInitd = true; + + typeSystemTablesCount = 0; + ulong l = value; + while (l != 0) { + if (((int)l & 1) != 0) + typeSystemTablesCount++; + l >>= 1; + } + } + } + ulong referencedTypeSystemTables; + bool referencedTypeSystemTablesInitd; + int typeSystemTablesCount; + + /// + /// Gets the type system table rows. This table has 64 elements. + /// + public uint[] TypeSystemTableRows => typeSystemTableRows; + readonly uint[] typeSystemTableRows; + + /// + /// Constructor + /// + public PdbHeap() { + pdbId = new byte[20]; + typeSystemTableRows = new uint[64]; + } + + /// + public override uint GetRawLength() { + if (!referencedTypeSystemTablesInitd) + throw new InvalidOperationException("ReferencedTypeSystemTables hasn't been initialized yet"); + return (uint)(pdbId.Length + 4 + 8 + 4 * typeSystemTablesCount); + } + + /// + protected override void WriteToImpl(DataWriter writer) { + if (!referencedTypeSystemTablesInitd) + throw new InvalidOperationException("ReferencedTypeSystemTables hasn't been initialized yet"); + writer.WriteBytes(pdbId); + writer.WriteUInt32(entryPoint); + writer.WriteUInt64(referencedTypeSystemTables); + ulong t = referencedTypeSystemTables; + for (int i = 0; i < typeSystemTableRows.Length; i++, t >>= 1) { + if (((int)t & 1) != 0) + writer.WriteUInt32(typeSystemTableRows[i]); + } + } + } +} diff --git a/src/DotNet/Writer/PortablePdbConstants.cs b/src/DotNet/Writer/PortablePdbConstants.cs new file mode 100644 index 000000000..3bd5f143d --- /dev/null +++ b/src/DotNet/Writer/PortablePdbConstants.cs @@ -0,0 +1,21 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.DotNet.Writer { + static class PortablePdbConstants { + // See System.Reflection.Metadata.PortablePdbVersions + + // Portable PDB version (v1.0) + // Format version is stored in DebugDirectory.MajorVersion + // SRM: DefaultFormatVersion, MinFormatVersion + public const ushort FormatVersion = 0x0100; + + // Embedded Portable PDB Blob verison (v1.0) + // Embedded version is stored in DebugDirectory.MinorVersion + // SRM: MinEmbeddedVersion, DefaultEmbeddedVersion, MinUnsupportedEmbeddedVersion + public const ushort EmbeddedVersion = 0x0100; + + // Stored in DebugDirectory.MinorVersion and indicates that it's a portable PDB file + // and not a Windows PDB file + public const ushort PortableCodeViewVersionMagic = 0x504D; + } +} diff --git a/src/DotNet/Writer/PreserveTokensMetaData.cs b/src/DotNet/Writer/PreserveTokensMetadata.cs similarity index 76% rename from src/DotNet/Writer/PreserveTokensMetaData.cs rename to src/DotNet/Writer/PreserveTokensMetadata.cs index f9e0904a0..376adaf89 100644 --- a/src/DotNet/Writer/PreserveTokensMetaData.cs +++ b/src/DotNet/Writer/PreserveTokensMetadata.cs @@ -3,13 +3,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using dnlib.DotNet.MD; namespace dnlib.DotNet.Writer { /// /// Preserves metadata tokens /// - sealed class PreserveTokensMetaData : MetaData { + sealed class PreserveTokensMetadata : Metadata { readonly ModuleDefMD mod; readonly Rows typeRefInfos = new Rows(); readonly Dictionary typeToRid = new Dictionary(); @@ -40,9 +41,9 @@ sealed class MemberDefInfo where T : IMDTokenProvider { public uint NewRid; public MemberDefInfo(T def, uint rid) { - this.Def = def; - this.Rid = rid; - this.NewRid = rid; + Def = def; + Rid = rid; + NewRid = rid; } } @@ -67,23 +68,17 @@ sealed class MemberDefDict where T : IMDTokenProvider { /// Gets total number of defs in the list. It does not necessarily return /// the table size. Use for that. /// - public int Count { - get { return defs.Count; } - } + public int Count => defs.Count; /// /// Gets the number of rows that need to be created in the table /// - public int TableSize { - get { return tableSize; } - } + public int TableSize => tableSize; /// /// Returns true if the ptr table (eg. MethodPtr) is needed /// - public bool NeedPtrTable { - get { return preserveRids && !wasSorted; } - } + public bool NeedPtrTable => preserveRids && !wasSorted; public MemberDefDict(Type defMDType, bool preserveRids) : this(defMDType, preserveRids, false) { @@ -95,13 +90,10 @@ public MemberDefDict(Type defMDType, bool preserveRids, bool enableRidToInfo) { this.enableRidToInfo = enableRidToInfo; } - public uint Rid(T def) { - return defToInfo[def].Rid; - } + public uint Rid(T def) => defToInfo[def].Rid; public bool TryGetRid(T def, out uint rid) { - MemberDefInfo info; - if (def == null || !defToInfo.TryGetValue(def, out info)) { + if (def == null || !defToInfo.TryGetValue(def, out var info)) { rid = 0; return false; } @@ -132,17 +124,11 @@ public void Sort(Comparison> comparer) { } } - public MemberDefInfo Get(int i) { - return defs[i]; - } - - public MemberDefInfo GetSorted(int i) { - return sortedDefs[i]; - } + public MemberDefInfo Get(int i) => defs[i]; + public MemberDefInfo GetSorted(int i) => sortedDefs[i]; public MemberDefInfo GetByRid(uint rid) { - MemberDefInfo info; - ridToInfo.TryGetValue(rid, out info); + ridToInfo.TryGetValue(rid, out var info); return info; } @@ -198,135 +184,112 @@ public void SortDefs() { throw new ModuleWriterException("Table is too big"); } - public int GetCollectionPosition(T def) { - return collectionPositions[def]; - } + public int GetCollectionPosition(T def) => collectionPositions[def]; } - protected override int NumberOfMethods { - get { return methodDefInfos.Count; } - } + protected override int NumberOfMethods => methodDefInfos.Count; - /// - /// Constructor - /// - /// Module - /// Constants list - /// Method bodies list - /// .NET resources list - /// Options - public PreserveTokensMetaData(ModuleDef module, UniqueChunkList constants, MethodBodyChunks methodBodies, NetResources netResources, MetaDataOptions options) - : base(module, constants, methodBodies, netResources, options) { + public PreserveTokensMetadata(ModuleDef module, UniqueChunkList constants, MethodBodyChunks methodBodies, NetResources netResources, MetadataOptions options, DebugMetadataKind debugKind, bool isStandaloneDebugMetadata) + : base(module, constants, methodBodies, netResources, options, debugKind, isStandaloneDebugMetadata) { mod = module as ModuleDefMD; - if (mod == null) + if (mod is null) throw new ModuleWriterException("Not a ModuleDefMD"); } /// public override uint GetRid(TypeRef tr) { - uint rid; - typeRefInfos.TryGetRid(tr, out rid); + typeRefInfos.TryGetRid(tr, out uint rid); return rid; } /// public override uint GetRid(TypeDef td) { - if (td == null) { + if (td is null) { Error("TypeDef is null"); return 0; } - uint rid; - if (typeToRid.TryGetValue(td, out rid)) + if (typeToRid.TryGetValue(td, out uint rid)) return rid; - Error("TypeDef {0} ({1:X8}) is not defined in this module ({2}). A type was removed that is still referenced by this module.", td, td.MDToken.Raw, module); + Error("TypeDef '{0}' (0x{1:X8}) is not defined in this module '{2}'. A type was removed that is still referenced by this module.", td, td.MDToken.Raw, module); return 0; } /// public override uint GetRid(FieldDef fd) { - uint rid; - if (fieldDefInfos.TryGetRid(fd, out rid)) + if (fieldDefInfos.TryGetRid(fd, out uint rid)) return rid; - if (fd == null) + if (fd is null) Error("Field is null"); else - Error("Field {0} ({1:X8}) is not defined in this module ({2}). A field was removed that is still referenced by this module.", fd, fd.MDToken.Raw, module); + Error("Field '{0}' (0x{1:X8}) is not defined in this module '{2}'. A field was removed that is still referenced by this module.", fd, fd.MDToken.Raw, module); return 0; } /// public override uint GetRid(MethodDef md) { - uint rid; - if (methodDefInfos.TryGetRid(md, out rid)) + if (methodDefInfos.TryGetRid(md, out uint rid)) return rid; - if (md == null) + if (md is null) Error("Method is null"); else - Error("Method {0} ({1:X8}) is not defined in this module ({2}). A method was removed that is still referenced by this module.", md, md.MDToken.Raw, module); + Error("Method '{0}' (0x{1:X8}) is not defined in this module '{2}'. A method was removed that is still referenced by this module.", md, md.MDToken.Raw, module); return 0; } /// public override uint GetRid(ParamDef pd) { - uint rid; - if (paramDefInfos.TryGetRid(pd, out rid)) + if (paramDefInfos.TryGetRid(pd, out uint rid)) return rid; - if (pd == null) + if (pd is null) Error("Param is null"); else - Error("Param {0} ({1:X8}) is not defined in this module ({2}). A parameter was removed that is still referenced by this module.", pd, pd.MDToken.Raw, module); + Error("Param '{0}' (0x{1:X8}) is not defined in this module '{2}'. A parameter was removed that is still referenced by this module.", pd, pd.MDToken.Raw, module); return 0; } /// public override uint GetRid(MemberRef mr) { - uint rid; - memberRefInfos.TryGetRid(mr, out rid); + memberRefInfos.TryGetRid(mr, out uint rid); return rid; } /// public override uint GetRid(StandAloneSig sas) { - uint rid; - standAloneSigInfos.TryGetRid(sas, out rid); + standAloneSigInfos.TryGetRid(sas, out uint rid); return rid; } /// public override uint GetRid(EventDef ed) { - uint rid; - if (eventDefInfos.TryGetRid(ed, out rid)) + if (eventDefInfos.TryGetRid(ed, out uint rid)) return rid; - if (ed == null) + if (ed is null) Error("Event is null"); else - Error("Event {0} ({1:X8}) is not defined in this module ({2}). An event was removed that is still referenced by this module.", ed, ed.MDToken.Raw, module); + Error("Event '{0}' (0x{1:X8}) is not defined in this module '{2}'. An event was removed that is still referenced by this module.", ed, ed.MDToken.Raw, module); return 0; } /// public override uint GetRid(PropertyDef pd) { - uint rid; - if (propertyDefInfos.TryGetRid(pd, out rid)) + if (propertyDefInfos.TryGetRid(pd, out uint rid)) return rid; - if (pd == null) + if (pd is null) Error("Property is null"); else - Error("Property {0} ({1:X8}) is not defined in this module ({2}). A property was removed that is still referenced by this module.", pd, pd.MDToken.Raw, module); + Error("Property '{0}' (0x{1:X8}) is not defined in this module '{2}'. A property was removed that is still referenced by this module.", pd, pd.MDToken.Raw, module); return 0; } /// public override uint GetRid(TypeSpec ts) { - uint rid; - typeSpecInfos.TryGetRid(ts, out rid); + typeSpecInfos.TryGetRid(ts, out uint rid); return rid; } /// public override uint GetRid(MethodSpec ms) { - uint rid; - methodSpecInfos.TryGetRid(ms, out rid); + methodSpecInfos.TryGetRid(ms, out uint rid); return rid; } @@ -342,9 +305,9 @@ protected override void Initialize() { } /// - protected override List GetAllTypeDefs() { + protected override TypeDef[] GetAllTypeDefs() { if (!PreserveTypeDefRids) { - var types2 = new List(module.GetTypes()); + var types2 = module.GetTypes().ToArray(); InitializeTypeToRid(types2); return types2; } @@ -355,7 +318,7 @@ protected override List GetAllTypeDefs() { const uint IS_TYPEDEFMD = 0x80000000; const uint INDEX_BITS = 0x00FFFFFF; foreach (var type in module.GetTypes()) { - if (type == null) + if (type is null) continue; types.Add(type); uint val = (uint)index++; @@ -419,14 +382,15 @@ protected override List GetAllTypeDefs() { prevRid = currRid; } - InitializeTypeToRid(newTypes); - return newTypes; + var newTypesArray = newTypes.ToArray(); + InitializeTypeToRid(newTypesArray); + return newTypesArray; } - void InitializeTypeToRid(IEnumerable types) { + void InitializeTypeToRid(TypeDef[] types) { uint rid = 1; foreach (var type in types) { - if (type == null) + if (type is null) continue; if (typeToRid.ContainsKey(type)) continue; @@ -516,8 +480,11 @@ void InitializeMemberRefTableRows() { initdMemberRef = true; uint rows = mod.TablesStream.MemberRefTable.Rows; - for (uint rid = 1; rid <= rows; rid++) + for (uint rid = 1; rid <= rows; rid++) { + if (tablesHeap.MemberRefTable[rid].Class != 0) + continue; AddMemberRef(mod.ResolveMemberRef(rid), true); + } tablesHeap.MemberRefTable.ReAddRows(); } @@ -528,8 +495,11 @@ void InitializeStandAloneSigTableRows() { initdStandAloneSig = true; uint rows = mod.TablesStream.StandAloneSigTable.Rows; - for (uint rid = 1; rid <= rows; rid++) + for (uint rid = 1; rid <= rows; rid++) { + if (tablesHeap.StandAloneSigTable[rid].Signature != 0) + continue; AddStandAloneSig(mod.ResolveStandAloneSig(rid), true); + } tablesHeap.StandAloneSigTable.ReAddRows(); } @@ -540,8 +510,11 @@ void InitializeTypeSpecTableRows() { initdTypeSpec = true; uint rows = mod.TablesStream.TypeSpecTable.Rows; - for (uint rid = 1; rid <= rows; rid++) + for (uint rid = 1; rid <= rows; rid++) { + if (tablesHeap.TypeSpecTable[rid].Signature != 0) + continue; AddTypeSpec(mod.ResolveTypeSpec(rid), true); + } tablesHeap.TypeSpecTable.ReAddRows(); } @@ -552,8 +525,11 @@ void InitializeMethodSpecTableRows() { initdMethodSpec = true; uint rows = mod.TablesStream.MethodSpecTable.Rows; - for (uint rid = 1; rid <= rows; rid++) + for (uint rid = 1; rid <= rows; rid++) { + if (tablesHeap.MethodSpecTable[rid].Method != 0) + continue; AddMethodSpec(mod.ResolveMethodSpec(rid), true); + } tablesHeap.MethodSpecTable.ReAddRows(); } @@ -561,7 +537,8 @@ void InitializeMethodSpecTableRows() { protected override void AllocateMemberDefRids() { FindMemberDefs(); - Listener.OnMetaDataEvent(this, MetaDataEvent.AllocateMemberDefRids0); + const int numEvents = 5; + RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 0.0 / numEvents); for (int i = 1; i <= fieldDefInfos.TableSize; i++) { if ((uint)i != tablesHeap.FieldTable.Create(new RawFieldRow())) @@ -594,7 +571,7 @@ protected override void AllocateMemberDefRids() { SortEvents(); SortProperties(); - Listener.OnMetaDataEvent(this, MetaDataEvent.AllocateMemberDefRids1); + RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 1.0 / numEvents); if (fieldDefInfos.NeedPtrTable) { for (int i = 0; i < fieldDefInfos.Count; i++) { @@ -640,14 +617,14 @@ protected override void AllocateMemberDefRids() { } } - Listener.OnMetaDataEvent(this, MetaDataEvent.AllocateMemberDefRids2); + RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 2.0 / numEvents); InitializeMethodAndFieldList(); InitializeParamList(); InitializeEventMap(); InitializePropertyMap(); - Listener.OnMetaDataEvent(this, MetaDataEvent.AllocateMemberDefRids3); + RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 3.0 / numEvents); // We must re-use deleted event/property rows after we've initialized // the event/prop map tables. @@ -656,7 +633,7 @@ protected override void AllocateMemberDefRids() { if (propertyDefInfos.NeedPtrTable) ReUseDeletedPropertyRows(); - Listener.OnMetaDataEvent(this, MetaDataEvent.AllocateMemberDefRids4); + RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 4.0 / numEvents); InitializeTypeRefTableRows(); InitializeTypeSpecTableRows(); @@ -687,10 +664,8 @@ void ReUseDeletedFieldRows() { continue; uint frid = (uint)i + 1; - var frow = tablesHeap.FieldTable[frid]; - frow.Flags = (ushort)(FieldAttributes.Public | FieldAttributes.Static); - frow.Name = stringsHeap.Add(string.Format("f{0:X6}", frid)); - frow.Signature = fieldSig; + var frow = new RawFieldRow((ushort)(FieldAttributes.Public | FieldAttributes.Static), stringsHeap.Add($"f{frid:X6}"), fieldSig); + tablesHeap.FieldTable[frid] = frow; tablesHeap.FieldPtrTable.Create(new RawFieldPtrRow(frid)); } @@ -721,13 +696,10 @@ void ReUseDeletedMethodRows() { continue; uint mrid = (uint)i + 1; - var mrow = tablesHeap.MethodTable[mrid]; - mrow.RVA = 0; - mrow.ImplFlags = (ushort)(MethodImplAttributes.IL | MethodImplAttributes.Managed); - mrow.Flags = (ushort)(MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Abstract); - mrow.Name = stringsHeap.Add(string.Format("m{0:X6}", mrid)); - mrow.Signature = methodSig; - mrow.ParamList = (uint)paramDefInfos.Count; + var mrow = new RawMethodRow(0, (ushort)(MethodImplAttributes.IL | MethodImplAttributes.Managed), + (ushort)(MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Abstract), + stringsHeap.Add($"m{mrid:X6}"), methodSig, (uint)paramDefInfos.Count); + tablesHeap.MethodTable[mrid] = mrow; tablesHeap.MethodPtrTable.Create(new RawMethodPtrRow(mrid)); } @@ -762,16 +734,14 @@ void ReUseDeletedParamRows() { continue; uint prid = (uint)i + 1; - var prow = tablesHeap.ParamTable[prid]; - prow.Flags = 0; - prow.Sequence = 0; // Return type parameter - prow.Name = stringsHeap.Add(string.Format("p{0:X6}", prid)); + var prow = new RawParamRow(0, 0, stringsHeap.Add($"p{prid:X6}")); + tablesHeap.ParamTable[prid] = prow; uint ptrRid = tablesHeap.ParamPtrTable.Create(new RawParamPtrRow(prid)); var mrow = new RawMethodRow(0, (ushort)(MethodImplAttributes.IL | MethodImplAttributes.Managed), (ushort)(MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Abstract), - stringsHeap.Add(string.Format("mp{0:X6}", prid)), + stringsHeap.Add($"mp{prid:X6}"), methodSig, ptrRid); uint mrid = tablesHeap.MethodTable.Create(mrow); @@ -807,10 +777,8 @@ void ReUseDeletedEventRows() { continue; uint erid = (uint)i + 1; - var frow = tablesHeap.EventTable[erid]; - frow.EventFlags = 0; - frow.Name = stringsHeap.Add(string.Format("E{0:X6}", erid)); - frow.EventType = eventType; + var frow = new RawEventRow(0, stringsHeap.Add($"E{erid:X6}"), eventType); + tablesHeap.EventTable[erid] = frow; tablesHeap.EventPtrTable.Create(new RawEventPtrRow(erid)); } @@ -842,10 +810,8 @@ void ReUseDeletedPropertyRows() { continue; uint prid = (uint)i + 1; - var frow = tablesHeap.PropertyTable[prid]; - frow.PropFlags = 0; - frow.Name = stringsHeap.Add(string.Format("P{0:X6}", prid)); - frow.Type = propertySig; + var frow = new RawPropertyRow(0, stringsHeap.Add($"P{prid:X6}"), propertySig); + tablesHeap.PropertyTable[prid] = frow; tablesHeap.PropertyPtrTable.Create(new RawPropertyPtrRow(prid)); } @@ -883,36 +849,52 @@ uint CreateDummyPtrTableType() { uint dummyPtrTableTypeRid; void FindMemberDefs() { + int count; + var added = new Dictionary(); int pos; foreach (var type in allTypeDefs) { - if (type == null) + if (type is null) continue; pos = 0; - foreach (var field in type.Fields) { - if (field == null) + var fields = type.Fields; + count = fields.Count; + for (int i = 0; i < count; i++) { + var field = fields[i]; + if (field is null) continue; fieldDefInfos.Add(field, pos++); } pos = 0; - foreach (var method in type.Methods) { - if (method == null) + var methods = type.Methods; + count = methods.Count; + for (int i = 0; i < count; i++) { + var method = methods[i]; + if (method is null) continue; methodDefInfos.Add(method, pos++); } pos = 0; - foreach (var evt in type.Events) { - if (evt == null) + var events = type.Events; + count = events.Count; + for (int i = 0; i < count; i++) { + var evt = events[i]; + if (evt is null || added.ContainsKey(evt)) continue; + added[evt] = true; eventDefInfos.Add(evt, pos++); } pos = 0; - foreach (var prop in type.Properties) { - if (prop == null) + var properties = type.Properties; + count = properties.Count; + for (int i = 0; i < count; i++) { + var prop = properties[i]; + if (prop is null || added.ContainsKey(prop)) continue; + added[prop] = true; propertyDefInfos.Add(prop, pos++); } } @@ -926,7 +908,7 @@ void FindMemberDefs() { var method = methodDefInfos.Get(i).Def; pos = 0; foreach (var param in Sort(method.ParamDefs)) { - if (param == null) + if (param is null) continue; paramDefInfos.Add(param, pos++); } @@ -934,72 +916,68 @@ void FindMemberDefs() { paramDefInfos.SortDefs(); } - void SortFields() { + void SortFields() => fieldDefInfos.Sort((a, b) => { - var dta = a.Def.DeclaringType == null ? 0 : typeToRid[a.Def.DeclaringType]; - var dtb = b.Def.DeclaringType == null ? 0 : typeToRid[b.Def.DeclaringType]; + var dta = a.Def.DeclaringType is null ? 0 : typeToRid[a.Def.DeclaringType]; + var dtb = b.Def.DeclaringType is null ? 0 : typeToRid[b.Def.DeclaringType]; if (dta == 0 || dtb == 0) return a.Rid.CompareTo(b.Rid); if (dta != dtb) return dta.CompareTo(dtb); return fieldDefInfos.GetCollectionPosition(a.Def).CompareTo(fieldDefInfos.GetCollectionPosition(b.Def)); }); - } - void SortMethods() { + void SortMethods() => methodDefInfos.Sort((a, b) => { - var dta = a.Def.DeclaringType == null ? 0 : typeToRid[a.Def.DeclaringType]; - var dtb = b.Def.DeclaringType == null ? 0 : typeToRid[b.Def.DeclaringType]; + var dta = a.Def.DeclaringType is null ? 0 : typeToRid[a.Def.DeclaringType]; + var dtb = b.Def.DeclaringType is null ? 0 : typeToRid[b.Def.DeclaringType]; if (dta == 0 || dtb == 0) return a.Rid.CompareTo(b.Rid); if (dta != dtb) return dta.CompareTo(dtb); return methodDefInfos.GetCollectionPosition(a.Def).CompareTo(methodDefInfos.GetCollectionPosition(b.Def)); }); - } - void SortParameters() { + void SortParameters() => paramDefInfos.Sort((a, b) => { - var dma = a.Def.DeclaringMethod == null ? 0 : methodDefInfos.Rid(a.Def.DeclaringMethod); - var dmb = b.Def.DeclaringMethod == null ? 0 : methodDefInfos.Rid(b.Def.DeclaringMethod); + var dma = a.Def.DeclaringMethod is null ? 0 : methodDefInfos.Rid(a.Def.DeclaringMethod); + var dmb = b.Def.DeclaringMethod is null ? 0 : methodDefInfos.Rid(b.Def.DeclaringMethod); if (dma == 0 || dmb == 0) return a.Rid.CompareTo(b.Rid); if (dma != dmb) return dma.CompareTo(dmb); return paramDefInfos.GetCollectionPosition(a.Def).CompareTo(paramDefInfos.GetCollectionPosition(b.Def)); }); - } - void SortEvents() { + void SortEvents() => eventDefInfos.Sort((a, b) => { - var dta = a.Def.DeclaringType == null ? 0 : typeToRid[a.Def.DeclaringType]; - var dtb = b.Def.DeclaringType == null ? 0 : typeToRid[b.Def.DeclaringType]; + var dta = a.Def.DeclaringType is null ? 0 : typeToRid[a.Def.DeclaringType]; + var dtb = b.Def.DeclaringType is null ? 0 : typeToRid[b.Def.DeclaringType]; if (dta == 0 || dtb == 0) return a.Rid.CompareTo(b.Rid); if (dta != dtb) return dta.CompareTo(dtb); return eventDefInfos.GetCollectionPosition(a.Def).CompareTo(eventDefInfos.GetCollectionPosition(b.Def)); }); - } - void SortProperties() { + void SortProperties() => propertyDefInfos.Sort((a, b) => { - var dta = a.Def.DeclaringType == null ? 0 : typeToRid[a.Def.DeclaringType]; - var dtb = b.Def.DeclaringType == null ? 0 : typeToRid[b.Def.DeclaringType]; + var dta = a.Def.DeclaringType is null ? 0 : typeToRid[a.Def.DeclaringType]; + var dtb = b.Def.DeclaringType is null ? 0 : typeToRid[b.Def.DeclaringType]; if (dta == 0 || dtb == 0) return a.Rid.CompareTo(b.Rid); if (dta != dtb) return dta.CompareTo(dtb); return propertyDefInfos.GetCollectionPosition(a.Def).CompareTo(propertyDefInfos.GetCollectionPosition(b.Def)); }); - } void InitializeMethodAndFieldList() { uint fieldList = 1, methodList = 1; foreach (var type in allTypeDefs) { - var typeRow = tablesHeap.TypeDefTable[typeToRid[type]]; - typeRow.FieldList = fieldList; - typeRow.MethodList = methodList; + uint index = typeToRid[type]; + var typeRow = tablesHeap.TypeDefTable[index]; + typeRow = new RawTypeDefRow(typeRow.Flags, typeRow.Name, typeRow.Namespace, typeRow.Extends, fieldList, methodList); + tablesHeap.TypeDefTable[index] = typeRow; fieldList += (uint)type.Fields.Count; methodList += (uint)type.Methods.Count; } @@ -1010,8 +988,9 @@ void InitializeParamList() { for (uint methodRid = 1; methodRid <= methodDefInfos.TableSize; methodRid++) { var methodInfo = methodDefInfos.GetByRid(methodRid); var row = tablesHeap.MethodTable[methodRid]; - row.ParamList = ridList; - if (methodInfo != null) + row = new RawMethodRow(row.RVA, row.ImplFlags, row.Flags, row.Name, row.Signature, ridList); + tablesHeap.MethodTable[methodRid] = row; + if (methodInfo is not null) ridList += (uint)methodInfo.Def.ParamDefs.Count; } } @@ -1048,105 +1027,107 @@ void InitializePropertyMap() { /// protected override uint AddTypeRef(TypeRef tr) { - if (tr == null) { + if (tr is null) { Error("TypeRef is null"); return 0; } - uint rid; - if (typeRefInfos.TryGetRid(tr, out rid)) { + if (typeRefInfos.TryGetRid(tr, out uint rid)) { if (rid == 0) - Error("TypeRef {0:X8} has an infinite ResolutionScope loop", tr.MDToken.Raw); + Error("TypeRef 0x{0:X8} has an infinite ResolutionScope loop.", tr.MDToken.Raw); return rid; } typeRefInfos.Add(tr, 0); // Prevent inf recursion bool isOld = PreserveTypeRefRids && mod.ResolveTypeRef(tr.Rid) == tr; - var row = isOld ? tablesHeap.TypeRefTable[tr.Rid] : new RawTypeRefRow(); - row.ResolutionScope = AddResolutionScope(tr.ResolutionScope); - row.Name = stringsHeap.Add(tr.Name); - row.Namespace = stringsHeap.Add(tr.Namespace); - - rid = isOld ? tr.Rid : tablesHeap.TypeRefTable.Add(row); + var row = new RawTypeRefRow(AddResolutionScope(tr.ResolutionScope), stringsHeap.Add(tr.Name), stringsHeap.Add(tr.Namespace)); + if (isOld) { + rid = tr.Rid; + tablesHeap.TypeRefTable[tr.Rid] = row; + } + else + rid = tablesHeap.TypeRefTable.Add(row); typeRefInfos.SetRid(tr, rid); AddCustomAttributes(Table.TypeRef, rid, tr); + AddCustomDebugInformationList(Table.TypeRef, rid, tr); return rid; } /// - protected override uint AddTypeSpec(TypeSpec ts) { - return AddTypeSpec(ts, false); - } + protected override uint AddTypeSpec(TypeSpec ts) => AddTypeSpec(ts, false); uint AddTypeSpec(TypeSpec ts, bool forceIsOld) { - if (ts == null) { + if (ts is null) { Error("TypeSpec is null"); return 0; } - uint rid; - if (typeSpecInfos.TryGetRid(ts, out rid)) { + if (typeSpecInfos.TryGetRid(ts, out uint rid)) { if (rid == 0) - Error("TypeSpec {0:X8} has an infinite TypeSig loop", ts.MDToken.Raw); + Error("TypeSpec 0x{0:X8} has an infinite TypeSig loop.", ts.MDToken.Raw); return rid; } typeSpecInfos.Add(ts, 0); // Prevent inf recursion bool isOld = forceIsOld || (PreserveTypeSpecRids && mod.ResolveTypeSpec(ts.Rid) == ts); - var row = isOld ? tablesHeap.TypeSpecTable[ts.Rid] : new RawTypeSpecRow(); - row.Signature = GetSignature(ts.TypeSig, ts.ExtraData); - - rid = isOld ? ts.Rid : tablesHeap.TypeSpecTable.Add(row); + var row = new RawTypeSpecRow(GetSignature(ts.TypeSig, ts.ExtraData)); + if (isOld) { + rid = ts.Rid; + tablesHeap.TypeSpecTable[ts.Rid] = row; + } + else + rid = tablesHeap.TypeSpecTable.Add(row); typeSpecInfos.SetRid(ts, rid); AddCustomAttributes(Table.TypeSpec, rid, ts); + AddCustomDebugInformationList(Table.TypeSpec, rid, ts); return rid; } /// - protected override uint AddMemberRef(MemberRef mr) { - return AddMemberRef(mr, false); - } + protected override uint AddMemberRef(MemberRef mr) => AddMemberRef(mr, false); uint AddMemberRef(MemberRef mr, bool forceIsOld) { - if (mr == null) { + if (mr is null) { Error("MemberRef is null"); return 0; } - uint rid; - if (memberRefInfos.TryGetRid(mr, out rid)) + if (memberRefInfos.TryGetRid(mr, out uint rid)) return rid; bool isOld = forceIsOld || (PreserveMemberRefRids && mod.ResolveMemberRef(mr.Rid) == mr); - var row = isOld ? tablesHeap.MemberRefTable[mr.Rid] : new RawMemberRefRow(); - row.Class = AddMemberRefParent(mr.Class); - row.Name = stringsHeap.Add(mr.Name); - row.Signature = GetSignature(mr.Signature); - - rid = isOld ? mr.Rid : tablesHeap.MemberRefTable.Add(row); + var row = new RawMemberRefRow(AddMemberRefParent(mr.Class), stringsHeap.Add(mr.Name), GetSignature(mr.Signature)); + if (isOld) { + rid = mr.Rid; + tablesHeap.MemberRefTable[mr.Rid] = row; + } + else + rid = tablesHeap.MemberRefTable.Add(row); memberRefInfos.Add(mr, rid); AddCustomAttributes(Table.MemberRef, rid, mr); + AddCustomDebugInformationList(Table.MemberRef, rid, mr); return rid; } /// - protected override uint AddStandAloneSig(StandAloneSig sas) { - return AddStandAloneSig(sas, false); - } + protected override uint AddStandAloneSig(StandAloneSig sas) => AddStandAloneSig(sas, false); uint AddStandAloneSig(StandAloneSig sas, bool forceIsOld) { - if (sas == null) { + if (sas is null) { Error("StandAloneSig is null"); return 0; } - uint rid; - if (standAloneSigInfos.TryGetRid(sas, out rid)) + if (standAloneSigInfos.TryGetRid(sas, out uint rid)) return rid; bool isOld = forceIsOld || (PreserveStandAloneSigRids && mod.ResolveStandAloneSig(sas.Rid) == sas); - var row = isOld ? tablesHeap.StandAloneSigTable[sas.Rid] : new RawStandAloneSigRow(); - row.Signature = GetSignature(sas.Signature); - - rid = isOld ? sas.Rid : tablesHeap.StandAloneSigTable.Add(row); + var row = new RawStandAloneSigRow(GetSignature(sas.Signature)); + if (isOld) { + rid = sas.Rid; + tablesHeap.StandAloneSigTable[sas.Rid] = row; + } + else + rid = tablesHeap.StandAloneSigTable.Add(row); standAloneSigInfos.Add(sas, rid); AddCustomAttributes(Table.StandAloneSig, rid, sas); + AddCustomDebugInformationList(Table.StandAloneSig, rid, sas); return rid; } @@ -1172,20 +1153,30 @@ protected override uint AddStandAloneSig(MethodSig methodSig, uint origToken) { return rid; } + /// + protected override uint AddStandAloneSig(FieldSig fieldSig, uint origToken) { + if (!PreserveStandAloneSigRids || !IsValidStandAloneSigToken(origToken)) + return base.AddStandAloneSig(fieldSig, origToken); + + uint rid = AddStandAloneSig(fieldSig, origToken); + if (rid == 0) + return base.AddStandAloneSig(fieldSig, origToken); + return rid; + } + uint AddStandAloneSig(CallingConventionSig callConvSig, uint origToken) { uint sig = GetSignature(callConvSig); - uint otherSig; - if (callConvTokenToSignature.TryGetValue(origToken, out otherSig)) { + if (callConvTokenToSignature.TryGetValue(origToken, out uint otherSig)) { if (sig == otherSig) return MDToken.ToRID(origToken); - Warning("Could not preserve StandAloneSig token {0:X8}", origToken); + Warning("Could not preserve StandAloneSig token 0x{0:X8}", origToken); return 0; } uint rid = MDToken.ToRID(origToken); var sas = mod.ResolveStandAloneSig(rid); if (standAloneSigInfos.Exists(sas)) { - Warning("StandAloneSig {0:X8} already exists", origToken); + Warning("StandAloneSig 0x{0:X8} already exists", origToken); return 0; } @@ -1211,33 +1202,31 @@ bool IsValidStandAloneSigToken(uint token) { } /// - protected override uint AddMethodSpec(MethodSpec ms) { - return AddMethodSpec(ms, false); - } + protected override uint AddMethodSpec(MethodSpec ms) => AddMethodSpec(ms, false); uint AddMethodSpec(MethodSpec ms, bool forceIsOld) { - if (ms == null) { + if (ms is null) { Error("MethodSpec is null"); return 0; } - uint rid; - if (methodSpecInfos.TryGetRid(ms, out rid)) + if (methodSpecInfos.TryGetRid(ms, out uint rid)) return rid; bool isOld = forceIsOld || (PreserveMethodSpecRids && mod.ResolveMethodSpec(ms.Rid) == ms); - var row = isOld ? tablesHeap.MethodSpecTable[ms.Rid] : new RawMethodSpecRow(); - row.Method = AddMethodDefOrRef(ms.Method); - row.Instantiation = GetSignature(ms.Instantiation); - - rid = isOld ? ms.Rid : tablesHeap.MethodSpecTable.Add(row); + var row = new RawMethodSpecRow(AddMethodDefOrRef(ms.Method), GetSignature(ms.Instantiation)); + if (isOld) { + rid = ms.Rid; + tablesHeap.MethodSpecTable[ms.Rid] = row; + } + else + rid = tablesHeap.MethodSpecTable.Add(row); methodSpecInfos.Add(ms, rid); AddCustomAttributes(Table.MethodSpec, rid, ms); + AddCustomDebugInformationList(Table.MethodSpec, rid, ms); return rid; } /// - protected override void BeforeSortingCustomAttributes() { - InitializeUninitializedTableRows(); - } + protected override void BeforeSortingCustomAttributes() => InitializeUninitializedTableRows(); } } diff --git a/src/DotNet/Writer/RelocDirectory.cs b/src/DotNet/Writer/RelocDirectory.cs index ae93a0aaf..6b7d4c03e 100644 --- a/src/DotNet/Writer/RelocDirectory.cs +++ b/src/DotNet/Writer/RelocDirectory.cs @@ -1,6 +1,7 @@ // dnlib: See LICENSE.txt for more info -using System.IO; +using System; +using System.Collections.Generic; using dnlib.IO; using dnlib.PE; @@ -9,47 +10,114 @@ namespace dnlib.DotNet.Writer { /// Relocations directory /// public sealed class RelocDirectory : IChunk { + readonly Machine machine; + readonly List allRelocRvas = new List(); + readonly List> relocSections = new List>(); + bool isReadOnly; FileOffset offset; RVA rva; + uint totalSize; - /// - /// Gets/sets the - /// - public StartupStub StartupStub { get; set; } + readonly struct RelocInfo { + public readonly IChunk Chunk; + public readonly uint OffsetOrRva; + public RelocInfo(IChunk chunk, uint offset) { + Chunk = chunk; + OffsetOrRva = offset; + } + } /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } - } + public RVA RVA => rva; + + internal bool NeedsRelocSection => allRelocRvas.Count != 0; + + /// + /// Constructor + /// + /// Machine + public RelocDirectory(Machine machine) => this.machine = machine; /// public void SetOffset(FileOffset offset, RVA rva) { + isReadOnly = true; this.offset = offset; this.rva = rva; + + var allRvas = new List(allRelocRvas.Count); + foreach (var info in allRelocRvas) { + uint relocRva; + if (info.Chunk is not null) + relocRva = (uint)info.Chunk.RVA + info.OffsetOrRva; + else + relocRva = info.OffsetOrRva; + allRvas.Add(relocRva); + } + allRvas.Sort(); + + uint prevPage = uint.MaxValue; + List pageList = null; + foreach (var relocRva in allRvas) { + uint page = relocRva & ~0xFFFU; + if (page != prevPage) { + prevPage = page; + if (pageList is not null) + totalSize += (uint)(8 + ((pageList.Count + 1) & ~1) * 2); + pageList = new List(); + relocSections.Add(pageList); + } + pageList.Add(relocRva); + } + if (pageList is not null) + totalSize += (uint)(8 + ((pageList.Count + 1) & ~1) * 2); } /// - public uint GetFileLength() { - return 12; - } + public uint GetFileLength() => totalSize; /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetVirtualSize() => GetFileLength(); + + /// + public uint CalculateAlignment() => 0; /// - public void WriteTo(BinaryWriter writer) { - uint rva = (uint)StartupStub.RelocRVA; - writer.Write(rva & ~0xFFFU); - writer.Write(12); - writer.Write((ushort)(0x3000 | (rva & 0xFFF))); - writer.Write((ushort)0); + public void WriteTo(DataWriter writer) { + bool is64bit = machine.Is64Bit(); + // 3 = IMAGE_REL_BASED_HIGHLOW, A = IMAGE_REL_BASED_DIR64 + uint relocType = is64bit ? 0xA000U : 0x3000; + foreach (var pageList in relocSections) { + writer.WriteUInt32(pageList[0] & ~0xFFFU); + writer.WriteUInt32((uint)(8 + ((pageList.Count + 1) & ~1) * 2)); + foreach (var rva in pageList) + writer.WriteUInt16((ushort)(relocType | (rva & 0xFFF))); + if ((pageList.Count & 1) != 0) + writer.WriteUInt16(0); + } + } + + /// + /// Adds a relocation + /// + /// RVA of location + public void Add(RVA rva) { + if (isReadOnly) + throw new InvalidOperationException("Can't add a relocation when the relocs section is read-only"); + allRelocRvas.Add(new RelocInfo(null, (uint)rva)); + } + + /// + /// Adds a relocation + /// + /// Chunk or null. If it's null, is the RVA + /// Offset relative to the start of , or if is null, this is the RVA + public void Add(IChunk chunk, uint offset) { + if (isReadOnly) + throw new InvalidOperationException("Can't add a relocation when the relocs section is read-only"); + allRelocRvas.Add(new RelocInfo(chunk, offset)); } } } diff --git a/src/DotNet/Writer/RoslynContentIdProvider.cs b/src/DotNet/Writer/RoslynContentIdProvider.cs new file mode 100644 index 000000000..f8f466566 --- /dev/null +++ b/src/DotNet/Writer/RoslynContentIdProvider.cs @@ -0,0 +1,18 @@ +// dnlib: See LICENSE.txt for more info + +using System; + +namespace dnlib.DotNet.Writer { + static class RoslynContentIdProvider { + public static void GetContentId(byte[] hash, out Guid guid, out uint timestamp) { + if (hash.Length < 20) + throw new InvalidOperationException(); + var guidBytes = new byte[16]; + Array.Copy(hash, 0, guidBytes, 0, guidBytes.Length); + guidBytes[7] = (byte)((guidBytes[7] & 0x0F) | 0x40); + guidBytes[8] = (byte)((guidBytes[8] & 0x3F) | 0x80); + guid = new Guid(guidBytes); + timestamp = 0x80000000 | (uint)((hash[19] << 24) | (hash[18] << 16) | (hash[17] << 8) | hash[16]); + } + } +} diff --git a/src/DotNet/Writer/SectionSizes.cs b/src/DotNet/Writer/SectionSizes.cs index ba994cfff..e5a8f9ffa 100644 --- a/src/DotNet/Writer/SectionSizes.cs +++ b/src/DotNet/Writer/SectionSizes.cs @@ -1,10 +1,10 @@ // dnlib: See LICENSE.txt for more info -using System.Collections.Generic; -using dnlib.Utils; +using System; +using System.Collections.Generic; namespace dnlib.DotNet.Writer { - struct SectionSizeInfo { + readonly struct SectionSizeInfo { /// /// Length of section /// @@ -29,14 +29,16 @@ public SectionSizeInfo(uint length, uint characteristics) { /// /// Calculates the optional header section sizes /// - struct SectionSizes { + readonly struct SectionSizes { public readonly uint SizeOfHeaders; public readonly uint SizeOfImage; public readonly uint BaseOfData, BaseOfCode; public readonly uint SizeOfCode, SizeOfInitdData, SizeOfUninitdData; - public SectionSizes(uint fileAlignment, uint sectionAlignment, uint headerLen, MFunc> getSectionSizeInfos) { - SizeOfHeaders = Utils.AlignUp(headerLen, fileAlignment); + public static uint GetSizeOfHeaders(uint fileAlignment, uint headerLen) => Utils.AlignUp(headerLen, fileAlignment); + + public SectionSizes(uint fileAlignment, uint sectionAlignment, uint headerLen, Func> getSectionSizeInfos) { + SizeOfHeaders = GetSizeOfHeaders(fileAlignment, headerLen); SizeOfImage = Utils.AlignUp(SizeOfHeaders, sectionAlignment); BaseOfData = 0; BaseOfCode = 0; diff --git a/src/DotNet/Writer/SerializerMethodContext.cs b/src/DotNet/Writer/SerializerMethodContext.cs new file mode 100644 index 000000000..42671ec14 --- /dev/null +++ b/src/DotNet/Writer/SerializerMethodContext.cs @@ -0,0 +1,63 @@ +// dnlib: See LICENSE.txt for more info + +using System.Collections.Generic; +using System.Diagnostics; +using dnlib.DotNet.Emit; + +namespace dnlib.DotNet.Writer { + sealed class SerializerMethodContext { + readonly Dictionary toOffset; + readonly IWriterError helper; + MethodDef method; + CilBody body; + uint bodySize; + bool dictInitd; + + public bool HasBody => body is not null; + + public SerializerMethodContext(IWriterError helper) { + toOffset = new Dictionary(); + this.helper = helper; + } + + internal void SetBody(MethodDef method) { + if (this.method != method) { + toOffset.Clear(); + this.method = method; + body = method?.Body; + dictInitd = false; + } + } + + public uint GetOffset(Instruction instr) { + if (!dictInitd) { + Debug.Assert(body is not null); + if (body is null) + return 0; + InitializeDict(); + } + if (instr is null) + return bodySize; + if (toOffset.TryGetValue(instr, out uint offset)) + return offset; + helper.Error("Couldn't find an instruction, maybe it was removed. It's still being referenced by some code or by the PDB"); + return bodySize; + } + + public bool IsSameMethod(MethodDef method) => this.method == method; + + void InitializeDict() { + Debug.Assert(body is not null); + Debug.Assert(toOffset.Count == 0); + uint offset = 0; + var instrs = body.Instructions; + for(int i = 0; i < instrs.Count; i++) { + var instr = instrs[i]; + toOffset[instr] = offset; + offset += (uint)instr.GetSize(); + } + bodySize = offset; + dictInitd = true; + } + } +} diff --git a/src/DotNet/Writer/SignatureWriter.cs b/src/DotNet/Writer/SignatureWriter.cs index c7dd1611b..b0454616d 100644 --- a/src/DotNet/Writer/SignatureWriter.cs +++ b/src/DotNet/Writer/SignatureWriter.cs @@ -22,7 +22,8 @@ public struct SignatureWriter : IDisposable { readonly ISignatureWriterHelper helper; RecursionCounter recursionCounter; readonly MemoryStream outStream; - readonly BinaryWriter writer; + readonly DataWriter writer; + readonly bool disposeStream; /// /// Write a signature @@ -37,6 +38,13 @@ public static byte[] Write(ISignatureWriterHelper helper, TypeSig typeSig) { } } + internal static byte[] Write(ISignatureWriterHelper helper, TypeSig typeSig, DataWriterContext context) { + using (var writer = new SignatureWriter(helper, context)) { + writer.Write(typeSig); + return writer.GetResult(); + } + } + /// /// Write a signature /// @@ -50,35 +58,45 @@ public static byte[] Write(ISignatureWriterHelper helper, CallingConventionSig s } } - SignatureWriter(ISignatureWriterHelper helper) { - this.helper = helper; - this.recursionCounter = new RecursionCounter(); - this.outStream = new MemoryStream(); - this.writer = new BinaryWriter(outStream); + internal static byte[] Write(ISignatureWriterHelper helper, CallingConventionSig sig, DataWriterContext context) { + using (var writer = new SignatureWriter(helper, context)) { + writer.Write(sig); + return writer.GetResult(); + } } - byte[] GetResult() { - return outStream.ToArray(); + SignatureWriter(ISignatureWriterHelper helper) { + this.helper = helper; + recursionCounter = new RecursionCounter(); + outStream = new MemoryStream(); + writer = new DataWriter(outStream); + disposeStream = true; } - uint WriteCompressedUInt32(uint value) { - return writer.WriteCompressedUInt32(helper, value); + SignatureWriter(ISignatureWriterHelper helper, DataWriterContext context) { + this.helper = helper; + recursionCounter = new RecursionCounter(); + outStream = context.OutStream; + writer = context.Writer; + disposeStream = false; + outStream.SetLength(0); + outStream.Position = 0; } - int WriteCompressedInt32(int value) { - return writer.WriteCompressedInt32(helper, value); - } + byte[] GetResult() => outStream.ToArray(); + uint WriteCompressedUInt32(uint value) => writer.WriteCompressedUInt32(helper, value); + int WriteCompressedInt32(int value) => writer.WriteCompressedInt32(helper, value); void Write(TypeSig typeSig) { const ElementType DEFAULT_ELEMENT_TYPE = ElementType.Boolean; - if (typeSig == null) { + if (typeSig is null) { helper.Error("TypeSig is null"); - writer.Write((byte)DEFAULT_ELEMENT_TYPE); + writer.WriteByte((byte)DEFAULT_ELEMENT_TYPE); return; } if (!recursionCounter.Increment()) { helper.Error("Infinite recursion"); - writer.Write((byte)DEFAULT_ELEMENT_TYPE); + writer.WriteByte((byte)DEFAULT_ELEMENT_TYPE); return; } @@ -103,31 +121,31 @@ void Write(TypeSig typeSig) { case ElementType.U: case ElementType.Object: case ElementType.Sentinel: - writer.Write((byte)typeSig.ElementType); + writer.WriteByte((byte)typeSig.ElementType); break; case ElementType.Ptr: case ElementType.ByRef: case ElementType.SZArray: case ElementType.Pinned: - writer.Write((byte)typeSig.ElementType); + writer.WriteByte((byte)typeSig.ElementType); Write(typeSig.Next); break; case ElementType.ValueType: case ElementType.Class: - writer.Write((byte)typeSig.ElementType); + writer.WriteByte((byte)typeSig.ElementType); Write(((TypeDefOrRefSig)typeSig).TypeDefOrRef); break; case ElementType.Var: case ElementType.MVar: - writer.Write((byte)typeSig.ElementType); + writer.WriteByte((byte)typeSig.ElementType); WriteCompressedUInt32(((GenericSig)typeSig).Number); break; case ElementType.Array: - writer.Write((byte)typeSig.ElementType); + writer.WriteByte((byte)typeSig.ElementType); var ary = (ArraySig)typeSig; Write(ary.Next); WriteCompressedUInt32(ary.Rank); @@ -142,7 +160,7 @@ void Write(TypeSig typeSig) { break; case ElementType.GenericInst: - writer.Write((byte)typeSig.ElementType); + writer.WriteByte((byte)typeSig.ElementType); var gis = (GenericInstSig)typeSig; Write(gis.GenericType); count = WriteCompressedUInt32((uint)gis.GenericArguments.Count); @@ -151,25 +169,25 @@ void Write(TypeSig typeSig) { break; case ElementType.ValueArray: - writer.Write((byte)typeSig.ElementType); + writer.WriteByte((byte)typeSig.ElementType); Write(typeSig.Next); WriteCompressedUInt32((typeSig as ValueArraySig).Size); break; case ElementType.FnPtr: - writer.Write((byte)typeSig.ElementType); + writer.WriteByte((byte)typeSig.ElementType); Write((typeSig as FnPtrSig).Signature); break; case ElementType.CModReqd: case ElementType.CModOpt: - writer.Write((byte)typeSig.ElementType); + writer.WriteByte((byte)typeSig.ElementType); Write((typeSig as ModifierSig).Modifier); Write(typeSig.Next); break; case ElementType.Module: - writer.Write((byte)typeSig.ElementType); + writer.WriteByte((byte)typeSig.ElementType); WriteCompressedUInt32((typeSig as ModuleSig).Index); Write(typeSig.Next); break; @@ -179,7 +197,7 @@ void Write(TypeSig typeSig) { case ElementType.Internal: default: helper.Error("Unknown or unsupported element type"); - writer.Write((byte)DEFAULT_ELEMENT_TYPE); + writer.WriteByte((byte)DEFAULT_ELEMENT_TYPE); break; } @@ -187,7 +205,7 @@ void Write(TypeSig typeSig) { } void Write(ITypeDefOrRef tdr) { - if (tdr == null) { + if (tdr is null) { helper.Error("TypeDefOrRef is null"); WriteCompressedUInt32(0); return; @@ -202,7 +220,7 @@ void Write(ITypeDefOrRef tdr) { } void Write(CallingConventionSig sig) { - if (sig == null) { + if (sig is null) { helper.Error("sig is null"); return; } @@ -216,24 +234,24 @@ void Write(CallingConventionSig sig) { LocalSig ls; GenericInstMethodSig gim; - if ((mbs = sig as MethodBaseSig) != null) + if ((mbs = sig as MethodBaseSig) is not null) Write(mbs); - else if ((fs = sig as FieldSig) != null) + else if ((fs = sig as FieldSig) is not null) Write(fs); - else if ((ls = sig as LocalSig) != null) + else if ((ls = sig as LocalSig) is not null) Write(ls); - else if ((gim = sig as GenericInstMethodSig) != null) + else if ((gim = sig as GenericInstMethodSig) is not null) Write(gim); else { helper.Error("Unknown calling convention sig"); - writer.Write((byte)sig.GetCallingConvention()); + writer.WriteByte((byte)sig.GetCallingConvention()); } recursionCounter.Decrement(); } void Write(MethodBaseSig sig) { - if (sig == null) { + if (sig is null) { helper.Error("sig is null"); return; } @@ -242,12 +260,12 @@ void Write(MethodBaseSig sig) { return; } - writer.Write((byte)sig.GetCallingConvention()); + writer.WriteByte((byte)sig.GetCallingConvention()); if (sig.Generic) WriteCompressedUInt32(sig.GenParamCount); uint numParams = (uint)sig.Params.Count; - if (sig.ParamsAfterSentinel != null) + if (sig.ParamsAfterSentinel is not null) numParams += (uint)sig.ParamsAfterSentinel.Count; uint count = WriteCompressedUInt32(numParams); @@ -255,8 +273,8 @@ void Write(MethodBaseSig sig) { for (uint i = 0; i < count && i < (uint)sig.Params.Count; i++) Write(sig.Params[(int)i]); - if (sig.ParamsAfterSentinel != null && sig.ParamsAfterSentinel.Count > 0) { - writer.Write((byte)ElementType.Sentinel); + if (sig.ParamsAfterSentinel is not null && sig.ParamsAfterSentinel.Count > 0) { + writer.WriteByte((byte)ElementType.Sentinel); for (uint i = 0, j = (uint)sig.Params.Count; i < (uint)sig.ParamsAfterSentinel.Count && j < count; i++, j++) Write(sig.ParamsAfterSentinel[(int)i]); } @@ -265,7 +283,7 @@ void Write(MethodBaseSig sig) { } void Write(FieldSig sig) { - if (sig == null) { + if (sig is null) { helper.Error("sig is null"); return; } @@ -274,14 +292,14 @@ void Write(FieldSig sig) { return; } - writer.Write((byte)sig.GetCallingConvention()); + writer.WriteByte((byte)sig.GetCallingConvention()); Write(sig.Type); recursionCounter.Decrement(); } void Write(LocalSig sig) { - if (sig == null) { + if (sig is null) { helper.Error("sig is null"); return; } @@ -290,8 +308,12 @@ void Write(LocalSig sig) { return; } - writer.Write((byte)sig.GetCallingConvention()); + writer.WriteByte((byte)sig.GetCallingConvention()); uint count = WriteCompressedUInt32((uint)sig.Locals.Count); + if (count >= 0x10000) { + // ldloc 0xFFFF is invalid, see the ldloc documentation + helper.Error("Too many locals, max number of locals is 65535 (0xFFFF)"); + } for (uint i = 0; i < count; i++) Write(sig.Locals[(int)i]); @@ -299,7 +321,7 @@ void Write(LocalSig sig) { } void Write(GenericInstMethodSig sig) { - if (sig == null) { + if (sig is null) { helper.Error("sig is null"); return; } @@ -308,7 +330,7 @@ void Write(GenericInstMethodSig sig) { return; } - writer.Write((byte)sig.GetCallingConvention()); + writer.WriteByte((byte)sig.GetCallingConvention()); uint count = WriteCompressedUInt32((uint)sig.GenericArguments.Count); for (uint i = 0; i < count; i++) Write(sig.GenericArguments[(int)i]); @@ -318,10 +340,10 @@ void Write(GenericInstMethodSig sig) { /// public void Dispose() { - if (outStream != null) + if (!disposeStream) + return; + if (outStream is not null) outStream.Dispose(); - if (writer != null) - ((IDisposable)writer).Dispose(); } } } diff --git a/src/DotNet/Writer/StartupStub.cs b/src/DotNet/Writer/StartupStub.cs index 30db0d408..fc212cead 100644 --- a/src/DotNet/Writer/StartupStub.cs +++ b/src/DotNet/Writer/StartupStub.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System.IO; +using System; using dnlib.IO; using dnlib.PE; @@ -9,10 +9,13 @@ namespace dnlib.DotNet.Writer { /// Stores the instruction that jumps to _CorExeMain/_CorDllMain /// public sealed class StartupStub : IChunk { + const StubType stubType = StubType.EntryPoint; + readonly RelocDirectory relocDirectory; + readonly Machine machine; + readonly CpuArch cpuArch; + readonly Action logError; FileOffset offset; RVA rva; - uint length; - uint padding; /// /// Gets/sets the @@ -25,27 +28,30 @@ public sealed class StartupStub : IChunk { public PEHeaders PEHeaders { get; set; } /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } - } + public RVA RVA => rva; /// /// Gets the address of the JMP instruction /// - public RVA EntryPointRVA { - get { return rva + padding; } - } + public RVA EntryPointRVA => rva + (cpuArch is null ? 0 : cpuArch.GetStubCodeOffset(stubType)); + + internal bool Enable { get; set; } + internal uint Alignment => cpuArch is null ? 1 : cpuArch.GetStubAlignment(stubType); /// - /// Gets the address of the operand of the JMP instruction + /// Constructor /// - public RVA RelocRVA { - get { return EntryPointRVA + 2; } + /// Reloc directory + /// Machine + /// Error logger + internal StartupStub(RelocDirectory relocDirectory, Machine machine, Action logError) { + this.relocDirectory = relocDirectory; + this.machine = machine; + this.logError = logError; + CpuArch.TryGetCpuArch(machine, out cpuArch); } /// @@ -53,26 +59,39 @@ public void SetOffset(FileOffset offset, RVA rva) { this.offset = offset; this.rva = rva; - padding = rva.AlignUp(4) - rva + 2; - length = padding + 6; + if (!Enable) + return; + + if (cpuArch is null) { + logError("The module needs an unmanaged entry point but the CPU architecture isn't supported: {0} (0x{1:X4})", new object[] { machine, (ushort)machine }); + return; + } + + cpuArch.WriteStubRelocs(stubType, relocDirectory, this, 0); } /// public uint GetFileLength() { - return length; + if (!Enable) + return 0; + if (cpuArch is null) + return 0; + return cpuArch.GetStubSize(stubType); } /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetVirtualSize() => GetFileLength(); + + /// + public uint CalculateAlignment() => 0; /// - public void WriteTo(BinaryWriter writer) { - writer.WriteZeros((int)padding); - writer.Write((byte)0xFF); - writer.Write((byte)0x25); - writer.Write((uint)PEHeaders.ImageBase + (uint)ImportDirectory.IatCorXxxMainRVA); + public void WriteTo(DataWriter writer) { + if (!Enable) + return; + if (cpuArch is null) + return; + cpuArch.WriteStub(stubType, writer, PEHeaders.ImageBase, (uint)rva, (uint)ImportDirectory.IatCorXxxMainRVA); } } } diff --git a/src/DotNet/Writer/StringsHeap.cs b/src/DotNet/Writer/StringsHeap.cs index 0637711ee..5f6c7be47 100644 --- a/src/DotNet/Writer/StringsHeap.cs +++ b/src/DotNet/Writer/StringsHeap.cs @@ -1,10 +1,10 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; -using System.IO; using dnlib.IO; using dnlib.DotNet.MD; +using System.Diagnostics; namespace dnlib.DotNet.Writer { /// @@ -16,11 +16,26 @@ public sealed class StringsHeap : HeapBase, IOffsetHeap { uint nextOffset = 1; byte[] originalData; Dictionary userRawData; + readonly Dictionary toStringsOffsetInfo = new Dictionary(UTF8StringEqualityComparer.Instance); + readonly Dictionary offsetIdToInfo = new Dictionary(); + readonly List stringsOffsetInfos = new List(); + const uint STRINGS_ID_FLAG = 0x80000000; + uint stringsId = STRINGS_ID_FLAG | 0; + + sealed class StringsOffsetInfo { + public StringsOffsetInfo(UTF8String value, uint stringsId) { + Value = value; + StringsId = stringsId; + Debug.Assert((stringsId & STRINGS_ID_FLAG) != 0); + } + public readonly UTF8String Value; + public readonly uint StringsId; + public uint StringsOffset; + public override string ToString() => $"{StringsId:X8} {StringsOffset:X4} {Value.String}"; + } /// - public override string Name { - get { return "#Strings"; } - } + public override string Name => "#Strings"; /// /// Populates strings from an existing (eg. to preserve @@ -30,26 +45,25 @@ public override string Name { public void Populate(StringsStream stringsStream) { if (isReadOnly) throw new ModuleWriterException("Trying to modify #Strings when it's read-only"); - if (originalData != null) + if (originalData is not null) throw new InvalidOperationException("Can't call method twice"); if (nextOffset != 1) throw new InvalidOperationException("Add() has already been called"); - if (stringsStream == null || stringsStream.ImageStreamLength == 0) + if (stringsStream is null || stringsStream.StreamLength == 0) return; - using (var reader = stringsStream.GetClonedImageStream()) { - originalData = reader.ReadAllBytes(); - nextOffset = (uint)originalData.Length; - Populate(reader); - } + var reader = stringsStream.CreateReader(); + originalData = reader.ToArray(); + nextOffset = (uint)originalData.Length; + Populate(ref reader); } - void Populate(IImageStream reader) { + void Populate(ref DataReader reader) { reader.Position = 1; while (reader.Position < reader.Length) { uint offset = (uint)reader.Position; - var bytes = reader.ReadBytesUntilByte(0); - if (bytes == null) + var bytes = reader.TryReadBytesUntil(0); + if (bytes is null) break; reader.ReadByte(); // terminating zero @@ -62,22 +76,99 @@ void Populate(IImageStream reader) { } } + internal void AddOptimizedStringsAndSetReadOnly() { + if (isReadOnly) + throw new ModuleWriterException("Trying to modify #Strings when it's read-only"); + SetReadOnly(); + + stringsOffsetInfos.Sort(Comparison_StringsOffsetInfoSorter); + + StringsOffsetInfo prevInfo = null; + foreach (var info in stringsOffsetInfos) { + if (prevInfo is not null && EndsWith(prevInfo.Value, info.Value)) + info.StringsOffset = prevInfo.StringsOffset + (uint)(prevInfo.Value.Data.Length - info.Value.Data.Length); + else + info.StringsOffset = AddToCache(info.Value); + prevInfo = info; + } + } + + static bool EndsWith(UTF8String s, UTF8String value) { + var d = s.Data; + var vd = value.Data; + int i = d.Length - vd.Length; + if (i < 0) + return false; + for (int vi = 0; vi < vd.Length; vi++) { + if (d[i] != vd[vi]) + return false; + i++; + } + return true; + } + + static readonly Comparison Comparison_StringsOffsetInfoSorter = StringsOffsetInfoSorter; + static int StringsOffsetInfoSorter(StringsOffsetInfo a, StringsOffsetInfo b) { + var da = a.Value.Data; + var db = b.Value.Data; + int ai = da.Length - 1; + int bi = db.Length - 1; + int len = Math.Min(da.Length, db.Length); + while (len > 0) { + int c = da[ai] - db[bi]; + if (c != 0) + return c; + ai--; + bi--; + len--; + } + return db.Length - da.Length; + } + /// - /// Adds a string to the #Strings heap + /// Adds a string to the #Strings heap. The returned value is not necessarily an offset in + /// the #Strings heap. Call to get the offset. /// /// The string - /// The offset of the string in the #Strings heap + /// The offset id. This is not a #Strings offset. Call to get the #Strings offset public uint Add(UTF8String s) { if (isReadOnly) throw new ModuleWriterException("Trying to modify #Strings when it's read-only"); if (UTF8String.IsNullOrEmpty(s)) return 0; - uint offset; - if (cachedDict.TryGetValue(s, out offset)) + if (toStringsOffsetInfo.TryGetValue(s, out var info)) + return info.StringsId; + if (cachedDict.TryGetValue(s, out uint offset)) return offset; - return AddToCache(s); + if (Array.IndexOf(s.Data, (byte)0) >= 0) + throw new ArgumentException("Strings in the #Strings heap can't contain NUL bytes"); + info = new StringsOffsetInfo(s, stringsId++); + Debug.Assert(!toStringsOffsetInfo.ContainsKey(s)); + Debug.Assert(!offsetIdToInfo.ContainsKey(info.StringsId)); + toStringsOffsetInfo[s] = info; + offsetIdToInfo[info.StringsId] = info; + stringsOffsetInfos.Add(info); + return info.StringsId; + } + + /// + /// Gets the offset of a string in the #Strings heap. This method can only be called after + /// all strings have been added. + /// + /// Offset id returned by + /// + public uint GetOffset(uint offsetId) { + if (!isReadOnly) + throw new ModuleWriterException("This method can only be called after all strings have been added and this heap is read-only"); + if ((offsetId & STRINGS_ID_FLAG) == 0) + return offsetId; + if (offsetIdToInfo.TryGetValue(offsetId, out var info)) { + Debug.Assert(info.StringsOffset != 0); + return info.StringsOffset; + } + throw new ArgumentOutOfRangeException(nameof(offsetId)); } /// @@ -90,13 +181,12 @@ public uint Create(UTF8String s) { throw new ModuleWriterException("Trying to modify #Strings when it's read-only"); if (UTF8String.IsNullOrEmpty(s)) s = UTF8String.Empty; + if (Array.IndexOf(s.Data, (byte)0) >= 0) + throw new ArgumentException("Strings in the #Strings heap can't contain NUL bytes"); return AddToCache(s); } uint AddToCache(UTF8String s) { - if (Array.IndexOf(s.Data, 0) >= 0) - throw new ArgumentException("Strings in the #Strings heap can't contain 00h bytes"); - uint offset; cached.Add(s); cachedDict[s] = offset = nextOffset; @@ -105,50 +195,43 @@ uint AddToCache(UTF8String s) { } /// - public override uint GetRawLength() { - return nextOffset; - } + public override uint GetRawLength() => nextOffset; /// - protected override void WriteToImpl(BinaryWriter writer) { - if (originalData != null) - writer.Write(originalData); + protected override void WriteToImpl(DataWriter writer) { + if (originalData is not null) + writer.WriteBytes(originalData); else - writer.Write((byte)0); + writer.WriteByte(0); - uint offset = originalData != null ? (uint)originalData.Length : 1; + uint offset = originalData is not null ? (uint)originalData.Length : 1; foreach (var s in cached) { - byte[] rawData; - if (userRawData != null && userRawData.TryGetValue(offset, out rawData)) { + if (userRawData is not null && userRawData.TryGetValue(offset, out var rawData)) { if (rawData.Length != s.Data.Length + 1) throw new InvalidOperationException("Invalid length of raw data"); - writer.Write(rawData); + writer.WriteBytes(rawData); } else { - writer.Write(s.Data); - writer.Write((byte)0); + writer.WriteBytes(s.Data); + writer.WriteByte(0); } offset += (uint)s.Data.Length + 1; } } /// - public int GetRawDataSize(UTF8String data) { - return data.Data.Length + 1; - } + public int GetRawDataSize(UTF8String data) => data.Data.Length + 1; /// public void SetRawData(uint offset, byte[] rawData) { - if (rawData == null) - throw new ArgumentNullException("rawData"); - if (userRawData == null) + if (userRawData is null) userRawData = new Dictionary(); - userRawData[offset] = rawData; + userRawData[offset] = rawData ?? throw new ArgumentNullException(nameof(rawData)); } /// public IEnumerable> GetAllRawData() { - uint offset = originalData != null ? (uint)originalData.Length : 1; + uint offset = originalData is not null ? (uint)originalData.Length : 1; foreach (var s in cached) { var rawData = new byte[s.Data.Length + 1]; Array.Copy(s.Data, rawData, s.Data.Length); diff --git a/src/DotNet/Writer/StrongNameSignature.cs b/src/DotNet/Writer/StrongNameSignature.cs index 048998068..c485cba6b 100644 --- a/src/DotNet/Writer/StrongNameSignature.cs +++ b/src/DotNet/Writer/StrongNameSignature.cs @@ -1,6 +1,5 @@ // dnlib: See LICENSE.txt for more info -using System.IO; using dnlib.IO; using dnlib.PE; @@ -8,28 +7,24 @@ namespace dnlib.DotNet.Writer { /// /// Strong name signature chunk /// - public sealed class StrongNameSignature : IChunk { + public sealed class StrongNameSignature : IReuseChunk { FileOffset offset; RVA rva; int size; /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } - } + public RVA RVA => rva; /// /// Constructor /// /// Size of strong name signature - public StrongNameSignature(int size) { - this.size = size; - } + public StrongNameSignature(int size) => this.size = size; + + bool IReuseChunk.CanReuse(RVA origRva, uint origSize) => (uint)size <= origSize; /// public void SetOffset(FileOffset offset, RVA rva) { @@ -38,18 +33,15 @@ public void SetOffset(FileOffset offset, RVA rva) { } /// - public uint GetFileLength() { - return (uint)this.size; - } + public uint GetFileLength() => (uint)size; /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetVirtualSize() => GetFileLength(); /// - public void WriteTo(BinaryWriter writer) { - writer.WriteZeros(size); - } + public uint CalculateAlignment() => 0; + + /// + public void WriteTo(DataWriter writer) => writer.WriteZeroes(size); } } diff --git a/src/DotNet/Writer/TablesHeap.cs b/src/DotNet/Writer/TablesHeap.cs index 22334fa2e..8a1161eba 100644 --- a/src/DotNet/Writer/TablesHeap.cs +++ b/src/DotNet/Writer/TablesHeap.cs @@ -1,9 +1,10 @@ // dnlib: See LICENSE.txt for more info -using System.IO; using dnlib.IO; using dnlib.PE; using dnlib.DotNet.MD; +using System; +using System.Collections.Generic; namespace dnlib.DotNet.Writer { /// @@ -33,17 +34,46 @@ public sealed class TablesHeapOptions { /// public bool? UseENC; + /// + /// All columns that can be 2 or 4 bytes are forced to be 4 bytes. + /// Set this to true if you add a #JTD heap and (if CLR) a #- tables heap is used + /// or (if Mono/Unity) a #~ or #- tables heap is used. + /// dnlib won't try to auto detect this from your added heaps since the CLR/CoreCLR vs Mono/Unity behavior + /// is a little bit different. You may need to set to true if you target CLR/CoreCLR. + /// + public bool? ForceBigColumns; + /// /// Extra data to write /// public uint? ExtraData; + /// + /// Log2Rid to write + /// + public byte? Log2Rid; + /// /// true if there are deleted s, s, /// s, s, s and/or /// s. /// public bool? HasDeletedRows; + + /// + /// Creates portable PDB v1.0 options + /// + /// + public static TablesHeapOptions CreatePortablePdbV1_0() => + new TablesHeapOptions { + Reserved1 = 0, + MajorVersion = 2, + MinorVersion = 0, + UseENC = null, + ExtraData = null, + Log2Rid = null, + HasDeletedRows = null, + }; } /// @@ -57,19 +87,16 @@ public sealed class TablesHeap : IHeap { bool bigGuid; bool bigBlob; bool hasDeletedRows; + readonly Metadata metadata; readonly TablesHeapOptions options; FileOffset offset; RVA rva; /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } - } + public RVA RVA => rva; #pragma warning disable 1591 // XML doc comment public readonly MDTable ModuleTable = new MDTable(Table.Module, RawRowEqualityComparer.Instance); @@ -117,6 +144,14 @@ public RVA RVA { public readonly MDTable GenericParamTable = new MDTable(Table.GenericParam, RawRowEqualityComparer.Instance); public readonly MDTable MethodSpecTable = new MDTable(Table.MethodSpec, RawRowEqualityComparer.Instance); public readonly MDTable GenericParamConstraintTable = new MDTable(Table.GenericParamConstraint, RawRowEqualityComparer.Instance); + public readonly MDTable DocumentTable = new MDTable(Table.Document, RawRowEqualityComparer.Instance); + public readonly MDTable MethodDebugInformationTable = new MDTable(Table.MethodDebugInformation, RawRowEqualityComparer.Instance); + public readonly MDTable LocalScopeTable = new MDTable(Table.LocalScope, RawRowEqualityComparer.Instance); + public readonly MDTable LocalVariableTable = new MDTable(Table.LocalVariable, RawRowEqualityComparer.Instance); + public readonly MDTable LocalConstantTable = new MDTable(Table.LocalConstant, RawRowEqualityComparer.Instance); + public readonly MDTable ImportScopeTable = new MDTable(Table.ImportScope, RawRowEqualityComparer.Instance); + public readonly MDTable StateMachineMethodTable = new MDTable(Table.StateMachineMethod, RawRowEqualityComparer.Instance); + public readonly MDTable CustomDebugInformationTable = new MDTable(Table.CustomDebugInformation, RawRowEqualityComparer.Instance); #pragma warning restore /// @@ -125,14 +160,10 @@ public RVA RVA { public readonly IMDTable[] Tables; /// - public string Name { - get { return IsENC ? "#-" : "#~"; } - } + public string Name => IsENC ? "#-" : "#~"; /// - public bool IsEmpty { - get { return false; } - } + public bool IsEmpty => false; /// /// true if the Edit 'N Continue name will be used (#-) @@ -171,49 +202,44 @@ public bool IsENC { /// Its name has been renamed to _Deleted). /// public bool HasDeletedRows { - get { return hasDeletedRows; } - set { hasDeletedRows = value; } + get => hasDeletedRows; + set => hasDeletedRows = value; } /// /// true if #Strings heap size > 0xFFFF /// public bool BigStrings { - get { return bigStrings; } - set { bigStrings = value; } + get => bigStrings; + set => bigStrings = value; } /// /// true if #GUID heap size > 0xFFFF /// public bool BigGuid { - get { return bigGuid; } - set { bigGuid = value; } + get => bigGuid; + set => bigGuid = value; } /// /// true if #Blob heap size > 0xFFFF /// public bool BigBlob { - get { return bigBlob; } - set { bigBlob = value; } - } - - /// - /// Default constructor - /// - public TablesHeap() - : this(null) { + get => bigBlob; + set => bigBlob = value; } /// /// Constructor /// + /// Metadata owner /// Options - public TablesHeap(TablesHeapOptions options) { + public TablesHeap(Metadata metadata, TablesHeapOptions options) { + this.metadata = metadata; this.options = options ?? new TablesHeapOptions(); - this.hasDeletedRows = this.options.HasDeletedRows ?? false; - this.Tables = new IMDTable[] { + hasDeletedRows = this.options.HasDeletedRows ?? false; + Tables = new IMDTable[] { ModuleTable, TypeRefTable, TypeDefTable, @@ -259,9 +285,28 @@ public TablesHeap(TablesHeapOptions options) { GenericParamTable, MethodSpecTable, GenericParamConstraintTable, + new MDTable((Table)0x2D, RawDummyRow.Comparer), + new MDTable((Table)0x2E, RawDummyRow.Comparer), + new MDTable((Table)0x2F, RawDummyRow.Comparer), + DocumentTable, + MethodDebugInformationTable, + LocalScopeTable, + LocalVariableTable, + LocalConstantTable, + ImportScopeTable, + StateMachineMethodTable, + CustomDebugInformationTable, }; } + struct RawDummyRow { + public static readonly IEqualityComparer Comparer = new RawDummyRowEqualityComparer(); + sealed class RawDummyRowEqualityComparer : IEqualityComparer { + public bool Equals(RawDummyRow x, RawDummyRow y) => throw new NotSupportedException(); + public int GetHashCode(RawDummyRow obj) => throw new NotSupportedException(); + } + } + /// public void SetReadOnly() { foreach (var mdt in Tables) @@ -272,6 +317,8 @@ public void SetReadOnly() { public void SetOffset(FileOffset offset, RVA rva) { this.offset = offset; this.rva = rva; + + // NOTE: This method can be called twice by NativeModuleWriter, see Metadata.SetOffset() for more info } /// @@ -282,9 +329,10 @@ public uint GetFileLength() { } /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetVirtualSize() => GetFileLength(); + + /// + public uint CalculateAlignment() => 0; /// /// Calculates the length. This will set all MD tables to read-only. @@ -304,7 +352,20 @@ public void CalculateLength() { var dnTableSizes = new DotNetTableSizes(); var tableInfos = dnTableSizes.CreateTables(majorVersion, minorVersion); - dnTableSizes.InitializeSizes(bigStrings, bigGuid, bigBlob, GetRowCounts()); + var rowCounts = GetRowCounts(); + + var debugSizes = rowCounts; + if (systemTables is not null) { + debugSizes = new uint[rowCounts.Length]; + for (int i = 0; i < rowCounts.Length; i++) { + if (DotNetTableSizes.IsSystemTable((Table)i)) + debugSizes[i] = systemTables[i]; + else + debugSizes[i] = rowCounts[i]; + } + } + + dnTableSizes.InitializeSizes(bigStrings, bigGuid, bigBlob, rowCounts, debugSizes, options.ForceBigColumns ?? false); for (int i = 0; i < Tables.Length; i++) Tables[i].TableInfo = tableInfos[i]; @@ -325,68 +386,100 @@ uint[] GetRowCounts() { return sizes; } + internal void GetSystemTableRows(out ulong mask, uint[] tables) { + if (tables.Length != 0x40) + throw new InvalidOperationException(); + var tablesMask = GetValidMask(); + ulong bit = 1; + mask = 0; + for (int i = 0; i < 0x40; i++, bit <<= 1) { + var table = (Table)i; + if (DotNetTableSizes.IsSystemTable(table)) { + if ((tablesMask & bit) != 0) { + tables[i] = (uint)Tables[i].Rows; + mask |= bit; + } + else + tables[i] = 0; + } + else + tables[i] = 0; + } + } + + internal void SetSystemTableRows(uint[] systemTables) => this.systemTables = (uint[])systemTables.Clone(); + uint[] systemTables; + /// - public void WriteTo(BinaryWriter writer) { - writer.Write(options.Reserved1 ?? 0); - writer.Write(majorVersion); - writer.Write(minorVersion); - writer.Write((byte)GetMDStreamFlags()); - writer.Write(GetLog2Rid()); - writer.Write(GetValidMask()); - writer.Write(GetSortedMask()); + public void WriteTo(DataWriter writer) { + writer.WriteUInt32(options.Reserved1 ?? 0); + writer.WriteByte(majorVersion); + writer.WriteByte(minorVersion); + writer.WriteByte((byte)GetMDStreamFlags()); + writer.WriteByte(GetLog2Rid()); + writer.WriteUInt64(GetValidMask()); + writer.WriteUInt64(GetSortedMask()); foreach (var mdt in Tables) { if (!mdt.IsEmpty) - writer.Write(mdt.Rows); + writer.WriteInt32(mdt.Rows); } if (options.ExtraData.HasValue) - writer.Write(options.ExtraData.Value); - - writer.Write(ModuleTable); - writer.Write(TypeRefTable); - writer.Write(TypeDefTable); - writer.Write(FieldPtrTable); - writer.Write(FieldTable); - writer.Write(MethodPtrTable); - writer.Write(MethodTable); - writer.Write(ParamPtrTable); - writer.Write(ParamTable); - writer.Write(InterfaceImplTable); - writer.Write(MemberRefTable); - writer.Write(ConstantTable); - writer.Write(CustomAttributeTable); - writer.Write(FieldMarshalTable); - writer.Write(DeclSecurityTable); - writer.Write(ClassLayoutTable); - writer.Write(FieldLayoutTable); - writer.Write(StandAloneSigTable); - writer.Write(EventMapTable); - writer.Write(EventPtrTable); - writer.Write(EventTable); - writer.Write(PropertyMapTable); - writer.Write(PropertyPtrTable); - writer.Write(PropertyTable); - writer.Write(MethodSemanticsTable); - writer.Write(MethodImplTable); - writer.Write(ModuleRefTable); - writer.Write(TypeSpecTable); - writer.Write(ImplMapTable); - writer.Write(FieldRVATable); - writer.Write(ENCLogTable); - writer.Write(ENCMapTable); - writer.Write(AssemblyTable); - writer.Write(AssemblyProcessorTable); - writer.Write(AssemblyOSTable); - writer.Write(AssemblyRefTable); - writer.Write(AssemblyRefProcessorTable); - writer.Write(AssemblyRefOSTable); - writer.Write(FileTable); - writer.Write(ExportedTypeTable); - writer.Write(ManifestResourceTable); - writer.Write(NestedClassTable); - writer.Write(GenericParamTable); - writer.Write(MethodSpecTable); - writer.Write(GenericParamConstraintTable); - writer.WriteZeros((int)(Utils.AlignUp(length, HeapBase.ALIGNMENT) - length)); + writer.WriteUInt32(options.ExtraData.Value); + + writer.Write(metadata, ModuleTable); + writer.Write(metadata, TypeRefTable); + writer.Write(metadata, TypeDefTable); + writer.Write(metadata, FieldPtrTable); + writer.Write(metadata, FieldTable); + writer.Write(metadata, MethodPtrTable); + writer.Write(metadata, MethodTable); + writer.Write(metadata, ParamPtrTable); + writer.Write(metadata, ParamTable); + writer.Write(metadata, InterfaceImplTable); + writer.Write(metadata, MemberRefTable); + writer.Write(metadata, ConstantTable); + writer.Write(metadata, CustomAttributeTable); + writer.Write(metadata, FieldMarshalTable); + writer.Write(metadata, DeclSecurityTable); + writer.Write(metadata, ClassLayoutTable); + writer.Write(metadata, FieldLayoutTable); + writer.Write(metadata, StandAloneSigTable); + writer.Write(metadata, EventMapTable); + writer.Write(metadata, EventPtrTable); + writer.Write(metadata, EventTable); + writer.Write(metadata, PropertyMapTable); + writer.Write(metadata, PropertyPtrTable); + writer.Write(metadata, PropertyTable); + writer.Write(metadata, MethodSemanticsTable); + writer.Write(metadata, MethodImplTable); + writer.Write(metadata, ModuleRefTable); + writer.Write(metadata, TypeSpecTable); + writer.Write(metadata, ImplMapTable); + writer.Write(metadata, FieldRVATable); + writer.Write(metadata, ENCLogTable); + writer.Write(metadata, ENCMapTable); + writer.Write(metadata, AssemblyTable); + writer.Write(metadata, AssemblyProcessorTable); + writer.Write(metadata, AssemblyOSTable); + writer.Write(metadata, AssemblyRefTable); + writer.Write(metadata, AssemblyRefProcessorTable); + writer.Write(metadata, AssemblyRefOSTable); + writer.Write(metadata, FileTable); + writer.Write(metadata, ExportedTypeTable); + writer.Write(metadata, ManifestResourceTable); + writer.Write(metadata, NestedClassTable); + writer.Write(metadata, GenericParamTable); + writer.Write(metadata, MethodSpecTable); + writer.Write(metadata, GenericParamConstraintTable); + writer.Write(metadata, DocumentTable); + writer.Write(metadata, MethodDebugInformationTable); + writer.Write(metadata, LocalScopeTable); + writer.Write(metadata, LocalVariableTable); + writer.Write(metadata, LocalConstantTable); + writer.Write(metadata, ImportScopeTable); + writer.Write(metadata, StateMachineMethodTable); + writer.Write(metadata, CustomDebugInformationTable); + writer.WriteZeroes((int)(Utils.AlignUp(length, HeapBase.ALIGNMENT) - length)); } MDStreamFlags GetMDStreamFlags() { @@ -406,7 +499,7 @@ MDStreamFlags GetMDStreamFlags() { byte GetLog2Rid() { //TODO: Sometimes this is 16. Probably when at least one of the table indexes requires 4 bytes. - return 1; + return options.Log2Rid ?? 1; } ulong GetValidMask() { @@ -428,8 +521,6 @@ ulong GetSortedMask() { } /// - public override string ToString() { - return Name; - } + public override string ToString() => Name; } } diff --git a/src/DotNet/Writer/USHeap.cs b/src/DotNet/Writer/USHeap.cs index b4fe3334e..31e8636ed 100644 --- a/src/DotNet/Writer/USHeap.cs +++ b/src/DotNet/Writer/USHeap.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; using System.IO; using dnlib.IO; @@ -18,9 +18,7 @@ public sealed class USHeap : HeapBase, IOffsetHeap { Dictionary userRawData; /// - public override string Name { - get { return "#US"; } - } + public override string Name => "#US"; /// /// Populates strings from an existing (eg. to preserve @@ -28,42 +26,35 @@ public override string Name { /// /// The #US stream with the original content public void Populate(USStream usStream) { - if (originalData != null) + if (originalData is not null) throw new InvalidOperationException("Can't call method twice"); if (nextOffset != 1) throw new InvalidOperationException("Add() has already been called"); - if (usStream == null || usStream.ImageStreamLength == 0) + if (usStream is null || usStream.StreamLength == 0) return; - using (var reader = usStream.GetClonedImageStream()) { - originalData = reader.ReadAllBytes(); - nextOffset = (uint)originalData.Length; - Populate(reader); - } + var reader = usStream.CreateReader(); + originalData = reader.ToArray(); + nextOffset = (uint)originalData.Length; + Populate(ref reader); } - void Populate(IImageStream reader) { - var chars = new char[0x200]; + void Populate(ref DataReader reader) { reader.Position = 1; while (reader.Position < reader.Length) { uint offset = (uint)reader.Position; - uint len; - if (!reader.ReadCompressedUInt32(out len)) { + if (!reader.TryReadCompressedUInt32(out uint len)) { if (offset == reader.Position) reader.Position++; continue; } - if (len == 0 || reader.Position + len > reader.Length) + if (len == 0 || (ulong)reader.Position + len > reader.Length) continue; int stringLen = (int)len / 2; - if (stringLen > chars.Length) - Array.Resize(ref chars, stringLen); - for (int i = 0; i < stringLen; i++) - chars[i] = (char)reader.ReadUInt16(); + var s = reader.ReadUtf16String(stringLen); if ((len & 1) != 0) reader.ReadByte(); - var s = new string(chars, 0, stringLen); if (!cachedDict.ContainsKey(s)) cachedDict[s] = offset; @@ -78,11 +69,10 @@ void Populate(IImageStream reader) { public uint Add(string s) { if (isReadOnly) throw new ModuleWriterException("Trying to modify #US when it's read-only"); - if (s == null) + if (s is null) s = string.Empty; - uint offset; - if (cachedDict.TryGetValue(s, out offset)) + if (cachedDict.TryGetValue(s, out uint offset)) return offset; return AddToCache(s); } @@ -103,29 +93,28 @@ uint AddToCache(string s) { cached.Add(s); cachedDict[s] = offset = nextOffset; nextOffset += (uint)GetRawDataSize(s); + if (offset > 0x00FFFFFF) + throw new ModuleWriterException("#US heap is too big"); return offset; } /// - public override uint GetRawLength() { - return nextOffset; - } + public override uint GetRawLength() => nextOffset; /// - protected override void WriteToImpl(BinaryWriter writer) { - if (originalData != null) - writer.Write(originalData); + protected override void WriteToImpl(DataWriter writer) { + if (originalData is not null) + writer.WriteBytes(originalData); else - writer.Write((byte)0); + writer.WriteByte(0); - uint offset = originalData != null ? (uint)originalData.Length : 1; + uint offset = originalData is not null ? (uint)originalData.Length : 1; foreach (var s in cached) { int rawLen = GetRawDataSize(s); - byte[] rawData; - if (userRawData != null && userRawData.TryGetValue(offset, out rawData)) { + if (userRawData is not null && userRawData.TryGetValue(offset, out var rawData)) { if (rawData.Length != rawLen) throw new InvalidOperationException("Invalid length of raw data"); - writer.Write(rawData); + writer.WriteBytes(rawData); } else WriteString(writer, s); @@ -133,37 +122,33 @@ protected override void WriteToImpl(BinaryWriter writer) { } } - void WriteString(BinaryWriter writer, string s) { + void WriteString(DataWriter writer, string s) { writer.WriteCompressedUInt32((uint)s.Length * 2 + 1); byte last = 0; for (int i = 0; i < s.Length; i++) { ushort c = (ushort)s[i]; - writer.Write(c); + writer.WriteUInt16(c); if (c > 0xFF || (1 <= c && c <= 8) || (0x0E <= c && c <= 0x1F) || c == 0x27 || c == 0x2D || c == 0x7F) last = 1; } - writer.Write(last); + writer.WriteByte(last); } /// - public int GetRawDataSize(string data) { - return Utils.GetCompressedUInt32Length((uint)data.Length * 2 + 1) + data.Length * 2 + 1; - } + public int GetRawDataSize(string data) => DataWriter.GetCompressedUInt32Length((uint)data.Length * 2 + 1) + data.Length * 2 + 1; /// public void SetRawData(uint offset, byte[] rawData) { - if (rawData == null) - throw new ArgumentNullException("rawData"); - if (userRawData == null) + if (userRawData is null) userRawData = new Dictionary(); - userRawData[offset] = rawData; + userRawData[offset] = rawData ?? throw new ArgumentNullException(nameof(rawData)); } /// public IEnumerable> GetAllRawData() { var memStream = new MemoryStream(); - var writer = new BinaryWriter(memStream); - uint offset = originalData != null ? (uint)originalData.Length : 1; + var writer = new DataWriter(memStream); + uint offset = originalData is not null ? (uint)originalData.Length : 1; foreach (var s in cached) { memStream.Position = 0; memStream.SetLength(0); diff --git a/src/DotNet/Writer/UniqueChunkList.cs b/src/DotNet/Writer/UniqueChunkList.cs index cb6f32cc7..225bf801c 100644 --- a/src/DotNet/Writer/UniqueChunkList.cs +++ b/src/DotNet/Writer/UniqueChunkList.cs @@ -25,8 +25,8 @@ public UniqueChunkList() /// /// Compares the chunk type public UniqueChunkList(IEqualityComparer chunkComparer) { - this.chunks = new List(); - this.dict = new Dictionary(new ElemEqualityComparer(chunkComparer)); + chunks = new List(); + dict = new Dictionary(new ElemEqualityComparer(chunkComparer)); } /// @@ -44,15 +44,39 @@ public override void SetOffset(FileOffset offset, RVA rva) { public T Add(T chunk, uint alignment) { if (setOffsetCalled) throw new InvalidOperationException("SetOffset() has already been called"); - if (chunk == null) + if (chunk is null) return null; var elem = new Elem(chunk, alignment); - Elem other; - if (dict.TryGetValue(elem, out other)) + if (dict.TryGetValue(elem, out var other)) return other.chunk; dict[elem] = elem; chunks.Add(elem); return elem.chunk; } + + /// + public override uint CalculateAlignment() { + uint alignment = base.CalculateAlignment(); + + var keys = new KeyValuePair[chunks.Count]; + for (var i = 0; i < chunks.Count; i++) + keys[i] = new KeyValuePair(i, chunks[i]); + Array.Sort(keys, DescendingStableComparer.Instance); + for (var i = 0; i < keys.Length; i++) + chunks[i] = keys[i].Value; + + return alignment; + } + + sealed class DescendingStableComparer : IComparer> { + internal static readonly DescendingStableComparer Instance = new DescendingStableComparer(); + + public int Compare(KeyValuePair x, KeyValuePair y) { + var result = -x.Value.alignment.CompareTo(y.Value.alignment); + if (result != 0) + return result; + return x.Key.CompareTo(y.Key); + } + } } } diff --git a/src/DotNet/Writer/Win32ResourcesChunk.cs b/src/DotNet/Writer/Win32ResourcesChunk.cs index 514c92e63..cf75e819b 100644 --- a/src/DotNet/Writer/Win32ResourcesChunk.cs +++ b/src/DotNet/Writer/Win32ResourcesChunk.cs @@ -1,8 +1,8 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; -using System.IO; +using System.Diagnostics; using System.Text; using dnlib.IO; using dnlib.PE; @@ -12,7 +12,7 @@ namespace dnlib.DotNet.Writer { /// /// Writes Win32 resources /// - public sealed class Win32ResourcesChunk : IChunk { + public sealed class Win32ResourcesChunk : IReuseChunk { readonly Win32Resources win32Resources; FileOffset offset; RVA rva; @@ -23,26 +23,20 @@ public sealed class Win32ResourcesChunk : IChunk { readonly List dataHeaderList = new List(); readonly Dictionary stringsDict = new Dictionary(StringComparer.Ordinal); readonly List stringsList = new List(); - readonly Dictionary dataDict = new Dictionary(); - readonly List dataList = new List(); + readonly Dictionary dataDict = new Dictionary(); + readonly List dataList = new List(); /// - public FileOffset FileOffset { - get { return offset; } - } + public FileOffset FileOffset => offset; /// - public RVA RVA { - get { return rva; } - } + public RVA RVA => rva; /// /// Constructor /// /// Win32 resources - public Win32ResourcesChunk(Win32Resources win32Resources) { - this.win32Resources = win32Resources; - } + public Win32ResourcesChunk(Win32Resources win32Resources) => this.win32Resources = win32Resources; /// /// Returns the and of a @@ -55,12 +49,10 @@ public Win32ResourcesChunk(Win32Resources win32Resources) { /// and have been updated. false /// if is not part of the Win32 resources. public bool GetFileOffsetAndRvaOf(ResourceDirectoryEntry dirEntry, out FileOffset fileOffset, out RVA rva) { - var dir = dirEntry as ResourceDirectory; - if (dir != null) + if (dirEntry is ResourceDirectory dir) return GetFileOffsetAndRvaOf(dir, out fileOffset, out rva); - var dataHeader = dirEntry as ResourceData; - if (dataHeader != null) + if (dirEntry is ResourceData dataHeader) return GetFileOffsetAndRvaOf(dataHeader, out fileOffset, out rva); fileOffset = 0; @@ -75,9 +67,7 @@ public bool GetFileOffsetAndRvaOf(ResourceDirectoryEntry dirEntry, out FileOffse /// A /// The file offset or 0 if is invalid public FileOffset GetFileOffset(ResourceDirectoryEntry dirEntry) { - FileOffset fileOffset; - RVA rva; - GetFileOffsetAndRvaOf(dirEntry, out fileOffset, out rva); + GetFileOffsetAndRvaOf(dirEntry, out var fileOffset, out var rva); return fileOffset; } @@ -88,9 +78,7 @@ public FileOffset GetFileOffset(ResourceDirectoryEntry dirEntry) { /// A /// The RVA or 0 if is invalid public RVA GetRVA(ResourceDirectoryEntry dirEntry) { - FileOffset fileOffset; - RVA rva; - GetFileOffsetAndRvaOf(dirEntry, out fileOffset, out rva); + GetFileOffsetAndRvaOf(dirEntry, out var fileOffset, out var rva); return rva; } @@ -105,8 +93,7 @@ public RVA GetRVA(ResourceDirectoryEntry dirEntry) { /// and have been updated. false /// if is not part of the Win32 resources. public bool GetFileOffsetAndRvaOf(ResourceDirectory dir, out FileOffset fileOffset, out RVA rva) { - uint offs; - if (dir == null || !dirDict.TryGetValue(dir, out offs)) { + if (dir is null || !dirDict.TryGetValue(dir, out uint offs)) { fileOffset = 0; rva = 0; return false; @@ -124,9 +111,7 @@ public bool GetFileOffsetAndRvaOf(ResourceDirectory dir, out FileOffset fileOffs /// A /// The file offset or 0 if is invalid public FileOffset GetFileOffset(ResourceDirectory dir) { - FileOffset fileOffset; - RVA rva; - GetFileOffsetAndRvaOf(dir, out fileOffset, out rva); + GetFileOffsetAndRvaOf(dir, out var fileOffset, out var rva); return fileOffset; } @@ -137,9 +122,7 @@ public FileOffset GetFileOffset(ResourceDirectory dir) { /// A /// The RVA or 0 if is invalid public RVA GetRVA(ResourceDirectory dir) { - FileOffset fileOffset; - RVA rva; - GetFileOffsetAndRvaOf(dir, out fileOffset, out rva); + GetFileOffsetAndRvaOf(dir, out var fileOffset, out var rva); return rva; } @@ -154,8 +137,7 @@ public RVA GetRVA(ResourceDirectory dir) { /// and have been updated. false /// if is not part of the Win32 resources. public bool GetFileOffsetAndRvaOf(ResourceData dataHeader, out FileOffset fileOffset, out RVA rva) { - uint offs; - if (dataHeader == null || !dataHeaderDict.TryGetValue(dataHeader, out offs)) { + if (dataHeader is null || !dataHeaderDict.TryGetValue(dataHeader, out uint offs)) { fileOffset = 0; rva = 0; return false; @@ -173,9 +155,7 @@ public bool GetFileOffsetAndRvaOf(ResourceData dataHeader, out FileOffset fileOf /// A /// The file offset or 0 if is invalid public FileOffset GetFileOffset(ResourceData dataHeader) { - FileOffset fileOffset; - RVA rva; - GetFileOffsetAndRvaOf(dataHeader, out fileOffset, out rva); + GetFileOffsetAndRvaOf(dataHeader, out var fileOffset, out var rva); return fileOffset; } @@ -186,58 +166,7 @@ public FileOffset GetFileOffset(ResourceData dataHeader) { /// A /// The RVA or 0 if is invalid public RVA GetRVA(ResourceData dataHeader) { - FileOffset fileOffset; - RVA rva; - GetFileOffsetAndRvaOf(dataHeader, out fileOffset, out rva); - return rva; - } - - /// - /// Returns the and of the raw data - /// owned by a . must have been called. - /// - /// A 's - /// Updated with the file offset - /// Updated with the RVA - /// true if is valid and - /// and have been updated. false - /// if is not part of the Win32 resources. - public bool GetFileOffsetAndRvaOf(IBinaryReader data, out FileOffset fileOffset, out RVA rva) { - uint offs; - if (data == null || !dataDict.TryGetValue(data, out offs)) { - fileOffset = 0; - rva = 0; - return false; - } - - fileOffset = offset + offs; - rva = this.rva + offs; - return true; - } - - /// - /// Returns the of the raw data owned by a - /// . must have been called. - /// - /// A 's - /// The file offset or 0 if is invalid - public FileOffset GetFileOffset(IBinaryReader data) { - FileOffset fileOffset; - RVA rva; - GetFileOffsetAndRvaOf(data, out fileOffset, out rva); - return fileOffset; - } - - /// - /// Returns the of the raw data owned by a . - /// must have been called. - /// - /// A 's - /// The RVA or 0 if is invalid - public RVA GetRVA(IBinaryReader data) { - FileOffset fileOffset; - RVA rva; - GetFileOffsetAndRvaOf(data, out fileOffset, out rva); + GetFileOffsetAndRvaOf(dataHeader, out var fileOffset, out var rva); return rva; } @@ -253,8 +182,7 @@ public RVA GetRVA(IBinaryReader data) { /// and have been updated. false /// if is not part of the Win32 resources. public bool GetFileOffsetAndRvaOf(string name, out FileOffset fileOffset, out RVA rva) { - uint offs; - if (name == null || !stringsDict.TryGetValue(name, out offs)) { + if (name is null || !stringsDict.TryGetValue(name, out uint offs)) { fileOffset = 0; rva = 0; return false; @@ -272,9 +200,7 @@ public bool GetFileOffsetAndRvaOf(string name, out FileOffset fileOffset, out RV /// The name of a /// The file offset or 0 if is invalid public FileOffset GetFileOffset(string name) { - FileOffset fileOffset; - RVA rva; - GetFileOffsetAndRvaOf(name, out fileOffset, out rva); + GetFileOffsetAndRvaOf(name, out var fileOffset, out var rva); return fileOffset; } @@ -285,9 +211,7 @@ public FileOffset GetFileOffset(string name) { /// The name of a /// The RVA or 0 if is invalid public RVA GetRVA(string name) { - FileOffset fileOffset; - RVA rva; - GetFileOffsetAndRvaOf(name, out fileOffset, out rva); + GetFileOffsetAndRvaOf(name, out var fileOffset, out var rva); return rva; } @@ -296,13 +220,53 @@ public RVA GetRVA(string name) { const uint RESOURCE_STRING_ALIGNMENT = 2; const uint RESOURCE_DATA_ALIGNMENT = 4; + bool IReuseChunk.CanReuse(RVA origRva, uint origSize) { + Debug.Assert(rva != 0); + if (rva == 0) + throw new InvalidOperationException(); + return length <= origSize; + } + + internal bool CheckValidOffset(FileOffset offset) { + GetMaxAlignment(offset, out var error); + return error is null; + } + + static uint GetMaxAlignment(FileOffset offset, out string error) { + error = null; + uint maxAlignment = 1; + maxAlignment = Math.Max(maxAlignment, RESOURCE_DIR_ALIGNMENT); + maxAlignment = Math.Max(maxAlignment, RESOURCE_DATA_HEADER_ALIGNMENT); + maxAlignment = Math.Max(maxAlignment, RESOURCE_STRING_ALIGNMENT); + maxAlignment = Math.Max(maxAlignment, RESOURCE_DATA_ALIGNMENT); + if (((uint)offset & (maxAlignment - 1)) != 0) + error = $"Win32 resources section isn't {maxAlignment}-byte aligned"; + else if (maxAlignment > ModuleWriterBase.DEFAULT_WIN32_RESOURCES_ALIGNMENT) + error = "maxAlignment > DEFAULT_WIN32_RESOURCES_ALIGNMENT"; + return maxAlignment; + } + /// public void SetOffset(FileOffset offset, RVA rva) { + // NOTE: This method can be called twice by NativeModuleWriter, see Metadata.SetOffset() for more info + bool initAll = this.offset == 0; this.offset = offset; this.rva = rva; - if (win32Resources == null) + if (win32Resources is null) return; + if (!initAll) { + // If it's called a second time, re-do everything + dirDict.Clear(); + dirList.Clear(); + dataHeaderDict.Clear(); + dataHeaderList.Clear(); + stringsDict.Clear(); + stringsList.Clear(); + dataDict.Clear(); + dataList.Clear(); + } + FindDirectoryEntries(); // Place everything in the following order: @@ -312,16 +276,9 @@ public void SetOffset(FileOffset offset, RVA rva) { // 4. All resource data. uint rsrcOffset = 0; - - uint maxAlignment = 1; - maxAlignment = Math.Max(maxAlignment, RESOURCE_DIR_ALIGNMENT); - maxAlignment = Math.Max(maxAlignment, RESOURCE_DATA_HEADER_ALIGNMENT); - maxAlignment = Math.Max(maxAlignment, RESOURCE_STRING_ALIGNMENT); - maxAlignment = Math.Max(maxAlignment, RESOURCE_DATA_ALIGNMENT); - if (((uint)offset & (maxAlignment - 1)) != 0) - throw new ModuleWriterException(string.Format("Win32 resources section isn't {0}-byte aligned", maxAlignment)); - if (maxAlignment > ModuleWriterBase.DEFAULT_WIN32_RESOURCES_ALIGNMENT) - throw new ModuleWriterException("maxAlignment > DEFAULT_WIN32_RESOURCES_ALIGNMENT"); + var maxAlignment = GetMaxAlignment(offset, out var error); + if (error is not null) + throw new ModuleWriterException(error); foreach (var dir in dirList) { rsrcOffset = Utils.AlignUp(rsrcOffset, RESOURCE_DIR_ALIGNMENT); @@ -335,7 +292,7 @@ public void SetOffset(FileOffset offset, RVA rva) { rsrcOffset = Utils.AlignUp(rsrcOffset, RESOURCE_DATA_HEADER_ALIGNMENT); dataHeaderDict[data] = rsrcOffset; AddString(data.Name); - AddData(data.Data); + AddData(data); rsrcOffset += 16; } @@ -348,13 +305,13 @@ public void SetOffset(FileOffset offset, RVA rva) { foreach (var data in dataList) { rsrcOffset = Utils.AlignUp(rsrcOffset, RESOURCE_DATA_ALIGNMENT); dataDict[data] = rsrcOffset; - rsrcOffset += (uint)data.Length; + rsrcOffset += data.CreateReader().Length; } length = rsrcOffset; } - void AddData(IBinaryReader data) { + void AddData(ResourceData data) { if (dataDict.ContainsKey(data)) return; dataList.Add(data); @@ -368,18 +325,21 @@ void AddString(ResourceName name) { stringsDict.Add(name.Name, 0); } - void FindDirectoryEntries() { - FindDirectoryEntries(win32Resources.Root); - } + void FindDirectoryEntries() => FindDirectoryEntries(win32Resources.Root); void FindDirectoryEntries(ResourceDirectory dir) { if (dirDict.ContainsKey(dir)) return; dirList.Add(dir); dirDict[dir] = 0; - foreach (var dir2 in dir.Directories) - FindDirectoryEntries(dir2); - foreach (var data in dir.Data) { + var dirs = dir.Directories; + int count = dirs.Count; + for (int i = 0; i < count; i++) + FindDirectoryEntries(dirs[i]); + var dirData = dir.Data; + count = dirData.Count; + for (int i = 0; i < count; i++) { + var data = dirData[i]; if (dataHeaderDict.ContainsKey(data)) continue; dataHeaderList.Add(data); @@ -388,24 +348,23 @@ void FindDirectoryEntries(ResourceDirectory dir) { } /// - public uint GetFileLength() { - return Utils.AlignUp(length, ModuleWriterBase.DEFAULT_WIN32_RESOURCES_ALIGNMENT); - } + public uint GetFileLength() => length; /// - public uint GetVirtualSize() { - return GetFileLength(); - } + public uint GetVirtualSize() => GetFileLength(); /// - public void WriteTo(BinaryWriter writer) { + public uint CalculateAlignment() => 0; + + /// + public void WriteTo(DataWriter writer) { uint offset = 0; // The order here must be the same as in SetOffset() foreach (var dir in dirList) { uint padding = Utils.AlignUp(offset, RESOURCE_DIR_ALIGNMENT) - offset; - writer.WriteZeros((int)padding); + writer.WriteZeroes((int)padding); offset += padding; if (dirDict[dir] != offset) throw new ModuleWriterException("Invalid Win32 resource directory offset"); @@ -414,7 +373,7 @@ public void WriteTo(BinaryWriter writer) { foreach (var dataHeader in dataHeaderList) { uint padding = Utils.AlignUp(offset, RESOURCE_DATA_HEADER_ALIGNMENT) - offset; - writer.WriteZeros((int)padding); + writer.WriteZeroes((int)padding); offset += padding; if (dataHeaderDict[dataHeader] != offset) throw new ModuleWriterException("Invalid Win32 resource data header offset"); @@ -423,7 +382,7 @@ public void WriteTo(BinaryWriter writer) { foreach (var s in stringsList) { uint padding = Utils.AlignUp(offset, RESOURCE_STRING_ALIGNMENT) - offset; - writer.WriteZeros((int)padding); + writer.WriteZeroes((int)padding); offset += padding; if (stringsDict[s] != offset) throw new ModuleWriterException("Invalid Win32 resource string offset"); @@ -431,52 +390,49 @@ public void WriteTo(BinaryWriter writer) { var bytes = Encoding.Unicode.GetBytes(s); if (bytes.Length / 2 > ushort.MaxValue) throw new ModuleWriterException("Win32 resource entry name is too long"); - writer.Write((ushort)(bytes.Length / 2)); - writer.Write(bytes); + writer.WriteUInt16((ushort)(bytes.Length / 2)); + writer.WriteBytes(bytes); offset += 2 + (uint)bytes.Length; } - byte[] dataBuffer = new byte[0x2000]; + var dataBuffer = new byte[0x2000]; foreach (var data in dataList) { uint padding = Utils.AlignUp(offset, RESOURCE_DATA_ALIGNMENT) - offset; - writer.WriteZeros((int)padding); + writer.WriteZeroes((int)padding); offset += padding; if (dataDict[data] != offset) throw new ModuleWriterException("Invalid Win32 resource data offset"); - data.Position = 0; - offset += data.WriteTo(writer, dataBuffer); + var reader = data.CreateReader(); + offset += reader.BytesLeft; + reader.CopyTo(writer, dataBuffer); } - - writer.WriteZeros((int)(Utils.AlignUp(length, ModuleWriterBase.DEFAULT_WIN32_RESOURCES_ALIGNMENT) - length)); } - uint WriteTo(BinaryWriter writer, ResourceDirectory dir) { - writer.Write(dir.Characteristics); - writer.Write(dir.TimeDateStamp); - writer.Write(dir.MajorVersion); - writer.Write(dir.MinorVersion); + uint WriteTo(DataWriter writer, ResourceDirectory dir) { + writer.WriteUInt32(dir.Characteristics); + writer.WriteUInt32(dir.TimeDateStamp); + writer.WriteUInt16(dir.MajorVersion); + writer.WriteUInt16(dir.MinorVersion); - List named; - List ids; - GetNamedAndIds(dir, out named, out ids); + GetNamedAndIds(dir, out var named, out var ids); if (named.Count > ushort.MaxValue || ids.Count > ushort.MaxValue) throw new ModuleWriterException("Too many named/id Win32 resource entries"); - writer.Write((ushort)named.Count); - writer.Write((ushort)ids.Count); + writer.WriteUInt16((ushort)named.Count); + writer.WriteUInt16((ushort)ids.Count); // These must be sorted in ascending order. Names are case insensitive. named.Sort((a, b) => a.Name.Name.ToUpperInvariant().CompareTo(b.Name.Name.ToUpperInvariant())); ids.Sort((a, b) => a.Name.Id.CompareTo(b.Name.Id)); foreach (var d in named) { - writer.Write(0x80000000 | stringsDict[d.Name.Name]); - writer.Write(GetDirectoryEntryOffset(d)); + writer.WriteUInt32(0x80000000 | stringsDict[d.Name.Name]); + writer.WriteUInt32(GetDirectoryEntryOffset(d)); } foreach (var d in ids) { - writer.Write(d.Name.Id); - writer.Write(GetDirectoryEntryOffset(d)); + writer.WriteInt32(d.Name.Id); + writer.WriteUInt32(GetDirectoryEntryOffset(d)); } return 16 + (uint)(named.Count + ids.Count) * 8; @@ -491,13 +447,19 @@ uint GetDirectoryEntryOffset(ResourceDirectoryEntry e) { static void GetNamedAndIds(ResourceDirectory dir, out List named, out List ids) { named = new List(); ids = new List(); - foreach (var d in dir.Directories) { + var dirs = dir.Directories; + int count = dirs.Count; + for (int i = 0; i < count; i++) { + var d = dirs[i]; if (d.Name.HasId) ids.Add(d); else named.Add(d); } - foreach (var d in dir.Data) { + var dirData = dir.Data; + count = dirData.Count; + for (int i = 0; i < count; i++) { + var d = dirData[i]; if (d.Name.HasId) ids.Add(d); else @@ -505,11 +467,11 @@ static void GetNamedAndIds(ResourceDirectory dir, out List 0x1FFFFFFF) { helper.Error("UInt32 value is too big and can't be compressed"); value = 0x1FFFFFFF; @@ -13,7 +11,7 @@ public static uint WriteCompressedUInt32(this BinaryWriter writer, IWriterError return value; } - public static int WriteCompressedInt32(this BinaryWriter writer, IWriterError helper, int value) { + public static int WriteCompressedInt32(this DataWriter writer, IWriterError helper, int value) { if (value < -0x10000000) { helper.Error("Int32 value is too small and can't be compressed."); value = -0x10000000; @@ -26,14 +24,14 @@ public static int WriteCompressedInt32(this BinaryWriter writer, IWriterError he return value; } - public static void Write(this BinaryWriter writer, IWriterError helper, UTF8String s) { + public static void Write(this DataWriter writer, IWriterError helper, UTF8String s) { if (UTF8String.IsNull(s)) { helper.Error("UTF8String is null"); s = UTF8String.Empty; } writer.WriteCompressedUInt32(helper, (uint)s.DataLength); - writer.Write(s.Data); + writer.WriteBytes(s.Data); } } } diff --git a/src/ExtensionAttribute.cs b/src/ExtensionAttribute.cs deleted file mode 100644 index 4f8707366..000000000 --- a/src/ExtensionAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -#pragma warning disable 1591 // XML doc warning - -namespace System.Runtime.CompilerServices { - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] - public sealed class ExtensionAttribute : Attribute { - } -} diff --git a/src/HandleProcessCorruptedStateExceptionsAttribute.cs b/src/HandleProcessCorruptedStateExceptionsAttribute.cs index e11099865..a1cdf67e7 100644 --- a/src/HandleProcessCorruptedStateExceptionsAttribute.cs +++ b/src/HandleProcessCorruptedStateExceptionsAttribute.cs @@ -1,9 +1,9 @@ -// dnlib: See LICENSE.txt for more info - -#pragma warning disable 1591 // XML doc warning +// dnlib: See LICENSE.txt for more info +#if NET35 namespace System.Runtime.ExceptionServices { [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] sealed class HandleProcessCorruptedStateExceptionsAttribute : Attribute { } } +#endif diff --git a/src/IO/AlignedByteArrayDataStream.cs b/src/IO/AlignedByteArrayDataStream.cs new file mode 100644 index 000000000..bad123ab0 --- /dev/null +++ b/src/IO/AlignedByteArrayDataStream.cs @@ -0,0 +1,108 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace dnlib.IO { + sealed unsafe class AlignedByteArrayDataStream : DataStream { + readonly byte[] data; + + public AlignedByteArrayDataStream(byte[] data) => this.data = data; + + public override void ReadBytes(uint offset, void* destination, int length) => + Marshal.Copy(data, (int)offset, (IntPtr)destination, length); + + public override void ReadBytes(uint offset, byte[] destination, int destinationIndex, int length) => + Array.Copy(data, (int)offset, destination, destinationIndex, length); + + public override byte ReadByte(uint offset) => data[(int)offset]; + + public override ushort ReadUInt16(uint offset) { + int i = (int)offset; + var data = this.data; + return (ushort)(data[i++] | (data[i] << 8)); + } + + public override uint ReadUInt32(uint offset) { + int i = (int)offset; + var data = this.data; + return data[i++] | ((uint)data[i++] << 8) | ((uint)data[i++] << 16) | ((uint)data[i] << 24); + } + + public override ulong ReadUInt64(uint offset) { + int i = (int)offset; + var data = this.data; + return data[i++] | ((ulong)data[i++] << 8) | ((ulong)data[i++] << 16) | ((ulong)data[i++] << 24) | + ((ulong)data[i++] << 32) | ((ulong)data[i++] << 40) | ((ulong)data[i++] << 48) | ((ulong)data[i] << 56); + } + + public override float ReadSingle(uint offset) { + int i = (int)offset; + var data = this.data; + uint value = data[i++] | ((uint)data[i++] << 8) | ((uint)data[i++] << 16) | ((uint)data[i] << 24); + return *(float*)&value; + } + + public override double ReadDouble(uint offset) { + int i = (int)offset; + var data = this.data; + ulong value = data[i++] | ((ulong)data[i++] << 8) | ((ulong)data[i++] << 16) | ((ulong)data[i++] << 24) | + ((ulong)data[i++] << 32) | ((ulong)data[i++] << 40) | ((ulong)data[i++] << 48) | ((ulong)data[i] << 56); + return *(double*)&value; + } + + public override string ReadUtf16String(uint offset, int chars) { + fixed (byte* p = data) + return new string((char*)(p + offset), 0, chars); + } + + public override string ReadString(uint offset, int length, Encoding encoding) { + fixed (byte* p = data) + return new string((sbyte*)(p + offset), 0, length, encoding); + } + + public override bool TryGetOffsetOf(uint offset, uint endOffset, byte value, out uint valueOffset) { + fixed (byte* pd = data) { + // If this code gets updated, also update the other DataStream implementations + + byte* p = pd + offset; + + uint count = (endOffset - offset) / 4; + for (uint i = 0; i < count; i++) { + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + } + + byte* pe = pd + endOffset; + while (p != pe) { + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + } + valueOffset = 0; + return false; + } + } + } +} diff --git a/src/IO/AlignedNativeMemoryDataStream.cs b/src/IO/AlignedNativeMemoryDataStream.cs new file mode 100644 index 000000000..6f9392c90 --- /dev/null +++ b/src/IO/AlignedNativeMemoryDataStream.cs @@ -0,0 +1,119 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace dnlib.IO { + sealed unsafe class AlignedNativeMemoryDataStream : DataStream { + readonly byte* data; + + public AlignedNativeMemoryDataStream(byte* data) => this.data = data; + + public override void ReadBytes(uint offset, void* destination, int length) { + var ps = data + offset; + var pd = (byte*)destination; + int count = length / 4; + length = length % 4; + for (int i = 0; i < count; i++) { + *pd = *ps; + pd++; + ps++; + + *pd = *ps; + pd++; + ps++; + + *pd = *ps; + pd++; + ps++; + + *pd = *ps; + pd++; + ps++; + } + for (int i = 0; i < length; i++, ps++, pd++) + *pd = *ps; + } + + public override void ReadBytes(uint offset, byte[] destination, int destinationIndex, int length) => + Marshal.Copy((IntPtr)(data + offset), destination, destinationIndex, length); + + public override byte ReadByte(uint offset) => *(data + offset); + + public override ushort ReadUInt16(uint offset) { + var p = data + offset; + return (ushort)(*p++ | (*p << 8)); + } + + public override uint ReadUInt32(uint offset) { + var p = data + offset; + return *p++ | ((uint)*p++ << 8) | ((uint)*p++ << 16) | ((uint)*p << 24); + } + + public override ulong ReadUInt64(uint offset) { + var p = data + offset; + return *p++ | ((ulong)*p++ << 8) | ((ulong)*p++ << 16) | ((ulong)*p++ << 24) | + ((ulong)*p++ << 32) | ((ulong)*p++ << 40) | ((ulong)*p++ << 48) | ((ulong)*p << 56); + } + + public override float ReadSingle(uint offset) { + var p = data + offset; + uint value = *p++ | ((uint)*p++ << 8) | ((uint)*p++ << 16) | ((uint)*p << 24); + return *(float*)&value; + } + + public override double ReadDouble(uint offset) { + var p = data + offset; + ulong value = *p++ | ((ulong)*p++ << 8) | ((ulong)*p++ << 16) | ((ulong)*p++ << 24) | + ((ulong)*p++ << 32) | ((ulong)*p++ << 40) | ((ulong)*p++ << 48) | ((ulong)*p << 56); + return *(double*)&value; + } + + public override string ReadUtf16String(uint offset, int chars) => new string((char*)(data + offset), 0, chars); + public override string ReadString(uint offset, int length, Encoding encoding) => new string((sbyte*)(data + offset), 0, length, encoding); + + public override bool TryGetOffsetOf(uint offset, uint endOffset, byte value, out uint valueOffset) { + var pd = data; + + // If this code gets updated, also update the other DataStream implementations + + byte* p = pd + offset; + + uint count = (endOffset - offset) / 4; + for (uint i = 0; i < count; i++) { + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + } + + byte* pe = pd + endOffset; + while (p != pe) { + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + } + valueOffset = 0; + return false; + } + } +} diff --git a/src/IO/BinaryReaderStream.cs b/src/IO/BinaryReaderStream.cs deleted file mode 100644 index 0f6c55c3d..000000000 --- a/src/IO/BinaryReaderStream.cs +++ /dev/null @@ -1,109 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.IO; - -namespace dnlib.IO { - /// - /// A class that can be used when you have a - /// but must use a - /// - sealed class BinaryReaderStream : Stream { - IBinaryReader reader; - readonly bool ownsReader; - - /// - /// Constructor - /// - /// Reader. This instance does NOT own this reader. - public BinaryReaderStream(IBinaryReader reader) - : this(reader, false) { - } - - /// - /// Constructor - /// - /// Reader - /// true if this instance owns - public BinaryReaderStream(IBinaryReader reader, bool ownsReader) { - this.reader = reader; - this.ownsReader = ownsReader; - } - - /// - public override bool CanRead { - get { return true; } - } - - /// - public override bool CanSeek { - get { return true; } - } - - /// - public override bool CanWrite { - get { return false; } - } - - /// - public override void Flush() { - } - - /// - public override long Length { - get { return reader.Length; } - } - - /// - public override long Position { - get { return reader.Position; } - set { reader.Position = value; } - } - - /// - public override int Read(byte[] buffer, int offset, int count) { - return reader.Read(buffer, offset, count); - } - - /// - public override int ReadByte() { - try { - return reader.ReadByte(); - } - catch (IOException) { - return -1; - } - } - - /// - public override long Seek(long offset, SeekOrigin origin) { - switch (origin) { - case SeekOrigin.Begin: Position = offset; break; - case SeekOrigin.Current:Position += offset; break; - case SeekOrigin.End: Position = Length + offset; break; - } - return Position; - } - - /// - public override void SetLength(long value) { - throw new NotImplementedException(); - } - - /// - public override void Write(byte[] buffer, int offset, int count) { - throw new NotImplementedException(); - } - - /// - protected override void Dispose(bool disposing) { - if (disposing) { - var r = reader; - if (ownsReader && r != null) - r.Dispose(); - reader = null; - } - base.Dispose(disposing); - } - } -} diff --git a/src/IO/ByteArrayDataReaderFactory.cs b/src/IO/ByteArrayDataReaderFactory.cs new file mode 100644 index 000000000..323d1ba4b --- /dev/null +++ b/src/IO/ByteArrayDataReaderFactory.cs @@ -0,0 +1,72 @@ +// dnlib: See LICENSE.txt for more info + +using System; + +namespace dnlib.IO { + /// + /// A that reads from a byte array + /// + public sealed class ByteArrayDataReaderFactory : DataReaderFactory { + /// + /// The filename or null if the data is not from a file + /// + public override string Filename => filename; + + /// + /// Gets the total length of the data + /// + public override uint Length => length; + + internal byte[] DataArray => data; + internal uint DataOffset => 0; + + DataStream stream; + string filename; + uint length; + byte[] data; + + ByteArrayDataReaderFactory(byte[] data, string filename) { + this.filename = filename; + length = (uint)data.Length; + stream = DataStreamFactory.Create(data); + this.data = data; + } + + /// + /// Creates a instance + /// + /// Data + /// The filename or null if the data is not from a file + /// + public static ByteArrayDataReaderFactory Create(byte[] data, string filename) { + if (data is null) + throw new ArgumentNullException(nameof(data)); + return new ByteArrayDataReaderFactory(data, filename); + } + + /// + /// Creates a data reader + /// + /// Data + /// + public static DataReader CreateReader(byte[] data) => Create(data, filename: null).CreateReader(); + + /// + /// Creates a data reader + /// + /// Offset of data + /// Length of data + /// + public override DataReader CreateReader(uint offset, uint length) => CreateReader(stream, offset, length); + + /// + /// This method doesn't need to be called since a has nothing that must be cleaned up + /// + public override void Dispose() { + stream = EmptyDataStream.Instance; + length = 0; + filename = null; + data = null; + } + } +} diff --git a/src/IO/DataReader.cs b/src/IO/DataReader.cs new file mode 100644 index 000000000..84717d280 --- /dev/null +++ b/src/IO/DataReader.cs @@ -0,0 +1,906 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using dnlib.DotNet.Writer; + +namespace dnlib.IO { + /// + /// Thrown by a when it can't read data or if the caller tries to set an invalid offset + /// + [Serializable] + public sealed class DataReaderException : IOException { + internal DataReaderException(string message) : base(message) { } + internal DataReaderException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } + + /// + /// Reads data + /// + [DebuggerDisplay("{StartOffset,h}-{EndOffset,h} Length={Length} BytesLeft={BytesLeft}")] + public struct DataReader { + /// + /// Gets the start offset of the data + /// + public readonly uint StartOffset => startOffset; + + /// + /// Gets the end offset of the data, not inclusive + /// + public readonly uint EndOffset => endOffset; + + /// + /// Gets the total length of the data + /// + public readonly uint Length => endOffset - startOffset; + + /// + /// Gets the current offset. This is between and (inclusive) + /// + public uint CurrentOffset { + readonly get => currentOffset; + set { + VerifyState(); + if (value < startOffset || value > endOffset) { + // Invalid offsets should be an IOException and not an ArgumentException + ThrowDataReaderException($"Invalid new {nameof(CurrentOffset)}"); + } + currentOffset = value; + VerifyState(); + } + } + + /// + /// Gets/sets the position relative to + /// + public uint Position { + readonly get => currentOffset - startOffset; + set { + VerifyState(); + if (value > Length) { + // Invalid positions should be an IOException and not an ArgumentException + ThrowDataReaderException($"Invalid new {nameof(Position)}"); + } + currentOffset = startOffset + value; + VerifyState(); + } + } + + /// + /// Gets the number of bytes that can be read without throwing an exception + /// + public readonly uint BytesLeft => endOffset - currentOffset; + + readonly DataStream stream; + readonly uint startOffset; + readonly uint endOffset; + uint currentOffset; + + /// + /// Constructor + /// + /// Stream + /// Start offset of data + /// Length of data + public DataReader(DataStream stream, uint offset, uint length) { + Debug.Assert(stream is not null || (offset == 0 && length == 0)); + Debug.Assert(offset + length >= offset); + this.stream = stream; + startOffset = offset; + endOffset = offset + length; + currentOffset = offset; + VerifyState(); + } + + [Conditional("DEBUG")] + readonly void VerifyState() { + Debug.Assert(startOffset <= currentOffset); + Debug.Assert(currentOffset <= endOffset); + } + + static void ThrowNoMoreBytesLeft() => throw new DataReaderException("There's not enough bytes left to read"); + static void ThrowDataReaderException(string message) => throw new DataReaderException(message); + static void ThrowInvalidOperationException() => throw new InvalidOperationException(); + static void ThrowArgumentNullException(string paramName) => throw new ArgumentNullException(paramName); + static void ThrowInvalidArgument(string paramName) => throw new DataReaderException("Invalid argument value"); + + /// + /// Resets the reader so it points to the start of the data + /// + public void Reset() => currentOffset = startOffset; + + /// + /// Creates a new reader that can access a smaller part of this reader + /// + /// Start position relative to + /// Length of data + /// + public readonly DataReader Slice(uint start, uint length) { + if ((ulong)start + length > Length) + ThrowInvalidArgument(nameof(length)); + return new DataReader(stream, startOffset + start, length); + } + + /// + /// Creates a new reader that can access everything from to the end of the data + /// + /// Start position relative to + /// + public readonly DataReader Slice(uint start) { + if (start > Length) + ThrowInvalidArgument(nameof(start)); + return Slice(start, Length - start); + } + + /// + /// Creates a new reader that can access a smaller part of this reader + /// + /// Start position relative to + /// Length of data + /// + public readonly DataReader Slice(int start, int length) { + if (start < 0) + ThrowInvalidArgument(nameof(start)); + if (length < 0) + ThrowInvalidArgument(nameof(length)); + return Slice((uint)start, (uint)length); + } + + /// + /// Creates a new reader that can access everything from to the end of the data + /// + /// Start position relative to + /// + public readonly DataReader Slice(int start) { + if (start < 0) + ThrowInvalidArgument(nameof(start)); + if ((uint)start > Length) + ThrowInvalidArgument(nameof(start)); + return Slice((uint)start, Length - (uint)start); + } + + /// + /// Checks if it's possible to read bytes + /// + /// Length of data + /// + public readonly bool CanRead(int length) => length >= 0 && (uint)length <= BytesLeft; + + /// + /// Checks if it's possible to read bytes + /// + /// Length of data + /// + public readonly bool CanRead(uint length) => length <= BytesLeft; + + /// + /// Reads a + /// + /// + public bool ReadBoolean() { + VerifyState(); + const uint SIZE = 1; + var currentOffset = this.currentOffset; + if (currentOffset == endOffset) + ThrowNoMoreBytesLeft(); + var value = stream.ReadBoolean(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a + /// + /// + public char ReadChar() { + VerifyState(); + const uint SIZE = 2; + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < SIZE) + ThrowNoMoreBytesLeft(); + var value = stream.ReadChar(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a + /// + /// + public sbyte ReadSByte() { + VerifyState(); + const uint SIZE = 1; + var currentOffset = this.currentOffset; + if (currentOffset == endOffset) + ThrowNoMoreBytesLeft(); + var value = stream.ReadSByte(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a + /// + /// + public byte ReadByte() { + VerifyState(); + const uint SIZE = 1; + var currentOffset = this.currentOffset; + if (currentOffset == endOffset) + ThrowNoMoreBytesLeft(); + var value = stream.ReadByte(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a + /// + /// + public short ReadInt16() { + VerifyState(); + const uint SIZE = 2; + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < SIZE) + ThrowNoMoreBytesLeft(); + var value = stream.ReadInt16(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a + /// + /// + public ushort ReadUInt16() { + VerifyState(); + const uint SIZE = 2; + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < SIZE) + ThrowNoMoreBytesLeft(); + var value = stream.ReadUInt16(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a + /// + /// + public int ReadInt32() { + VerifyState(); + const uint SIZE = 4; + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < SIZE) + ThrowNoMoreBytesLeft(); + var value = stream.ReadInt32(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a + /// + /// + public uint ReadUInt32() { + VerifyState(); + const uint SIZE = 4; + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < SIZE) + ThrowNoMoreBytesLeft(); + var value = stream.ReadUInt32(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + internal byte Unsafe_ReadByte() { + VerifyState(); + const uint SIZE = 1; + var currentOffset = this.currentOffset; + Debug.Assert(currentOffset != endOffset); + var value = stream.ReadByte(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + internal ushort Unsafe_ReadUInt16() { + VerifyState(); + const uint SIZE = 2; + var currentOffset = this.currentOffset; + Debug.Assert(endOffset - currentOffset >= SIZE); + var value = stream.ReadUInt16(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + internal uint Unsafe_ReadUInt32() { + VerifyState(); + const uint SIZE = 4; + var currentOffset = this.currentOffset; + Debug.Assert(endOffset - currentOffset >= SIZE); + var value = stream.ReadUInt32(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a + /// + /// + public long ReadInt64() { + VerifyState(); + const uint SIZE = 8; + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < SIZE) + ThrowNoMoreBytesLeft(); + var value = stream.ReadInt64(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a + /// + /// + public ulong ReadUInt64() { + VerifyState(); + const uint SIZE = 8; + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < SIZE) + ThrowNoMoreBytesLeft(); + var value = stream.ReadUInt64(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a + /// + /// + public float ReadSingle() { + VerifyState(); + const uint SIZE = 4; + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < SIZE) + ThrowNoMoreBytesLeft(); + var value = stream.ReadSingle(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a + /// + /// + public double ReadDouble() { + VerifyState(); + const uint SIZE = 8; + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < SIZE) + ThrowNoMoreBytesLeft(); + var value = stream.ReadDouble(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a + /// + /// + public Guid ReadGuid() { + VerifyState(); + const uint SIZE = 16; + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < SIZE) + ThrowNoMoreBytesLeft(); + var value = stream.ReadGuid(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a + /// + /// + public decimal ReadDecimal() { + VerifyState(); + const uint SIZE = 16; + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < SIZE) + ThrowNoMoreBytesLeft(); + var value = stream.ReadDecimal(currentOffset); + this.currentOffset = currentOffset + SIZE; + VerifyState(); + return value; + } + + /// + /// Reads a UTF-16 encoded + /// + /// Number of characters to read + /// + public string ReadUtf16String(int chars) { + if (chars < 0) + ThrowInvalidArgument(nameof(chars)); + if (chars == 0) + return string.Empty; + VerifyState(); + uint length = (uint)chars * 2; + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < length) + ThrowNoMoreBytesLeft(); + var s = length == 0 ? string.Empty : stream.ReadUtf16String(currentOffset, chars); + this.currentOffset = currentOffset + length; + VerifyState(); + return s; + } + + /// + /// Reads bytes + /// + /// Destination pointer + /// Number of bytes to read + public unsafe void ReadBytes(void* destination, int length) { + if (destination is null && length != 0) + ThrowArgumentNullException(nameof(destination)); + if (length < 0) + ThrowInvalidArgument(nameof(length)); + // This is also true if 'this' is the 'default' instance ('stream' is null) + if (length == 0) + return; + VerifyState(); + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < (uint)length) + ThrowNoMoreBytesLeft(); + stream.ReadBytes(currentOffset, destination, length); + this.currentOffset = currentOffset + (uint)length; + VerifyState(); + } + + /// + /// Reads bytes + /// + /// Destination array + /// Destination index + /// Number of bytes to read + public void ReadBytes(byte[] destination, int destinationIndex, int length) { + if (destination is null) + ThrowArgumentNullException(nameof(destination)); + if (destinationIndex < 0) + ThrowInvalidArgument(nameof(destinationIndex)); + if (length < 0) + ThrowInvalidArgument(nameof(length)); + // This is also true if 'this' is the 'default' instance ('stream' is null) + if (length == 0) + return; + VerifyState(); + var currentOffset = this.currentOffset; + if (endOffset - currentOffset < (uint)length) + ThrowNoMoreBytesLeft(); + stream.ReadBytes(currentOffset, destination, destinationIndex, length); + this.currentOffset = currentOffset + (uint)length; + VerifyState(); + } + + /// + /// Reads bytes + /// + /// Number of bytes to read + /// + public byte[] ReadBytes(int length) { + if (length < 0) + ThrowInvalidArgument(nameof(length)); + if (length == 0) + return Array2.Empty(); + var data = new byte[length]; + ReadBytes(data, 0, length); + return data; + } + + /// + /// Reads a compressed + /// + /// Uncompressed + /// + public bool TryReadCompressedUInt32(out uint value) { + VerifyState(); + var currentOffset = this.currentOffset; + var bytesLeft = endOffset - currentOffset; + if (bytesLeft == 0) { + value = 0; + VerifyState(); + return false; + } + + var stream = this.stream; + byte b = stream.ReadByte(currentOffset++); + if ((b & 0x80) == 0) { + value = b; + this.currentOffset = currentOffset; + VerifyState(); + return true; + } + + if ((b & 0xC0) == 0x80) { + if (bytesLeft < 2) { + value = 0; + VerifyState(); + return false; + } + value = (uint)(((b & 0x3F) << 8) | stream.ReadByte(currentOffset++)); + this.currentOffset = currentOffset; + VerifyState(); + return true; + } + + // The encoding 111x isn't allowed but the CLR sometimes doesn't verify this + // and just assumes it's 110x. Don't fail if it's 111x, just assume it's 110x. + + if (bytesLeft < 4) { + value = 0; + VerifyState(); + return false; + } + value = (uint)(((b & 0x1F) << 24) | (stream.ReadByte(currentOffset++) << 16) | + (stream.ReadByte(currentOffset++) << 8) | stream.ReadByte(currentOffset++)); + this.currentOffset = currentOffset; + VerifyState(); + return true; + } + + /// + /// Reads a compressed + /// + /// + public uint ReadCompressedUInt32() { + if (!TryReadCompressedUInt32(out uint value)) + ThrowNoMoreBytesLeft(); + return value; + } + + /// + /// Reads a compressed + /// + /// Uncompressed + /// + public bool TryReadCompressedInt32(out int value) { + VerifyState(); + var currentOffset = this.currentOffset; + var bytesLeft = endOffset - currentOffset; + if (bytesLeft == 0) { + value = 0; + VerifyState(); + return false; + } + + var stream = this.stream; + byte b = stream.ReadByte(currentOffset++); + if ((b & 0x80) == 0) { + if ((b & 1) != 0) + value = -0x40 | (b >> 1); + else + value = (b >> 1); + this.currentOffset = currentOffset; + VerifyState(); + return true; + } + + if ((b & 0xC0) == 0x80) { + if (bytesLeft < 2) { + value = 0; + VerifyState(); + return false; + } + uint tmp = (uint)(((b & 0x3F) << 8) | stream.ReadByte(currentOffset++)); + if ((tmp & 1) != 0) + value = -0x2000 | (int)(tmp >> 1); + else + value = (int)(tmp >> 1); + this.currentOffset = currentOffset; + VerifyState(); + return true; + } + + if ((b & 0xE0) == 0xC0) { + if (bytesLeft < 4) { + value = 0; + VerifyState(); + return false; + } + uint tmp = (uint)(((b & 0x1F) << 24) | (stream.ReadByte(currentOffset++) << 16) | + (stream.ReadByte(currentOffset++) << 8) | stream.ReadByte(currentOffset++)); + if ((tmp & 1) != 0) + value = -0x10000000 | (int)(tmp >> 1); + else + value = (int)(tmp >> 1); + this.currentOffset = currentOffset; + VerifyState(); + return true; + } + + value = 0; + VerifyState(); + return false; + } + + /// + /// Reads a compressed + /// + /// + public int ReadCompressedInt32() { + if (!TryReadCompressedInt32(out int value)) + ThrowNoMoreBytesLeft(); + return value; + } + + /// + /// Reads a 7-bit encoded integer + /// + /// + public uint Read7BitEncodedUInt32() { + uint val = 0; + int bits = 0; + for (int i = 0; i < 5; i++) { + byte b = ReadByte(); + val |= (uint)(b & 0x7F) << bits; + if ((b & 0x80) == 0) + return val; + bits += 7; + } + ThrowDataReaderException("Invalid encoded UInt32"); + return 0; + } + + /// + /// Reads a 7-bit encoded integer + /// + /// + public int Read7BitEncodedInt32() => (int)Read7BitEncodedUInt32(); + + /// + /// Reads a serialized UTF-8 string + /// + /// + public string ReadSerializedString() => ReadSerializedString(Encoding.UTF8); + + /// + /// Reads a serialized string + /// + /// Encoding + /// + public string ReadSerializedString(Encoding encoding) { + if (encoding is null) + ThrowArgumentNullException(nameof(encoding)); + int length = Read7BitEncodedInt32(); + if (length < 0) + ThrowNoMoreBytesLeft(); + if (length == 0) + return string.Empty; + return ReadString(length, encoding); + } + + /// + /// Returns all data without updating the current position + /// + /// + public readonly byte[] ToArray() { + int length = (int)Length; + if (length < 0) + ThrowInvalidOperationException(); + // This is also true if 'this' is the 'default' instance ('stream' is null) + if (length == 0) + return Array2.Empty(); + var data = new byte[length]; + stream.ReadBytes(startOffset, data, 0, data.Length); + return data; + } + + /// + /// Returns the remaining data + /// + /// + public byte[] ReadRemainingBytes() { + int length = (int)BytesLeft; + if (length < 0) + ThrowInvalidOperationException(); + return ReadBytes(length); + } + + /// + /// Reads all bytes until a terminating byte or returns null if wasn't found. + /// If found, the current offset is incremented by the length of the returned data + /// + /// Terminating byte value + /// + public byte[] TryReadBytesUntil(byte value) { + var currentOffset = this.currentOffset; + var endOffset = this.endOffset; + // This is also true if 'this' is the 'default' instance ('stream' is null) + if (currentOffset == endOffset) + return null; + if (!stream.TryGetOffsetOf(currentOffset, endOffset, value, out var valueOffset)) + return null; + int length = (int)(valueOffset - currentOffset); + if (length < 0) + return null; + return ReadBytes(length); + } + + /// + /// Reads a zero-terminated UTF-8 string or returns null if the string couldn't be read. + /// If successful, the current offset is incremented past the terminating zero. + /// + /// + public string TryReadZeroTerminatedUtf8String() => TryReadZeroTerminatedString(Encoding.UTF8); + + /// + /// Reads a zero-terminated string or returns null if the string couldn't be read. + /// If successful, the current offset is incremented past the terminating zero. + /// + /// Encoding + /// + public string TryReadZeroTerminatedString(Encoding encoding) { + if (encoding is null) + ThrowArgumentNullException(nameof(encoding)); + VerifyState(); + var currentOffset = this.currentOffset; + var endOffset = this.endOffset; + // This is also true if 'this' is the 'default' instance ('stream' is null) + if (currentOffset == endOffset) + return null; + if (!stream.TryGetOffsetOf(currentOffset, endOffset, 0, out var valueOffset)) + return null; + int length = (int)(valueOffset - currentOffset); + if (length < 0) + return null; + var value = length == 0 ? string.Empty : stream.ReadString(currentOffset, length, encoding); + this.currentOffset = valueOffset + 1; + VerifyState(); + return value; + } + + /// + /// Reads a UTF-8 encoded string + /// + /// Number of bytes to read (not characters) + /// + public string ReadUtf8String(int byteCount) => ReadString(byteCount, Encoding.UTF8); + + /// + /// Reads a string + /// + /// Number of bytes to read (not characters) + /// Encoding + /// + public string ReadString(int byteCount, Encoding encoding) { + if (byteCount < 0) + ThrowInvalidArgument(nameof(byteCount)); + if (encoding is null) + ThrowArgumentNullException(nameof(encoding)); + if (byteCount == 0) + return string.Empty; + if ((uint)byteCount > Length) + ThrowInvalidArgument(nameof(byteCount)); + VerifyState(); + var currentOffset = this.currentOffset; + var value = stream.ReadString(currentOffset, byteCount, encoding); + this.currentOffset = currentOffset + (uint)byteCount; + VerifyState(); + return value; + } + + /// + /// Creates a that can access this content. The caller doesn't have to dispose of the returned stream. + /// + /// + public readonly Stream AsStream() => new DataReaderStream(this); + + readonly byte[] AllocTempBuffer() => new byte[(int)Math.Min(0x2000, BytesLeft)]; + + /// + /// Copies the data, starting from , to + /// + /// Destination + /// Number of bytes written + public void CopyTo(DataWriter destination) { + if (destination is null) + ThrowArgumentNullException(nameof(destination)); + if (Position >= Length) + return; + CopyTo(destination.InternalStream, AllocTempBuffer()); + } + + /// + /// Copies the data, starting from , to + /// + /// Destination + /// Temp buffer during writing + /// Number of bytes written + public void CopyTo(DataWriter destination, byte[] dataBuffer) { + if (destination is null) + ThrowArgumentNullException(nameof(destination)); + CopyTo(destination.InternalStream, dataBuffer); + } + + /// + /// Copies the data, starting from , to + /// + /// Destination + /// Number of bytes written + public void CopyTo(BinaryWriter destination) { + if (destination is null) + ThrowArgumentNullException(nameof(destination)); + if (Position >= Length) + return; + CopyTo(destination.BaseStream, AllocTempBuffer()); + } + + /// + /// Copies the data, starting from , to + /// + /// Destination + /// Temp buffer during writing + /// Number of bytes written + public void CopyTo(BinaryWriter destination, byte[] dataBuffer) { + if (destination is null) + ThrowArgumentNullException(nameof(destination)); + CopyTo(destination.BaseStream, dataBuffer); + } + + /// + /// Copies the data, starting from , to + /// + /// Destination + /// Number of bytes written + public void CopyTo(Stream destination) { + if (destination is null) + ThrowArgumentNullException(nameof(destination)); + if (Position >= Length) + return; + CopyTo(destination, AllocTempBuffer()); + } + + /// + /// Copies the data, starting from , to + /// + /// Destination + /// Temp buffer during writing + /// Number of bytes written + public void CopyTo(Stream destination, byte[] dataBuffer) { + if (destination is null) + ThrowArgumentNullException(nameof(destination)); + if (dataBuffer is null) + ThrowArgumentNullException(nameof(dataBuffer)); + if (Position >= Length) + return; + if (dataBuffer.Length == 0) + ThrowInvalidArgument(nameof(dataBuffer)); + uint lenLeft = BytesLeft; + while (lenLeft > 0) { + int num = (int)Math.Min((uint)dataBuffer.Length, lenLeft); + lenLeft -= (uint)num; + ReadBytes(dataBuffer, 0, num); + destination.Write(dataBuffer, 0, num); + } + } + } +} diff --git a/src/IO/DataReaderFactory.cs b/src/IO/DataReaderFactory.cs new file mode 100644 index 000000000..a224011d5 --- /dev/null +++ b/src/IO/DataReaderFactory.cs @@ -0,0 +1,109 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Diagnostics; + +namespace dnlib.IO { + /// + /// Creates s that can read its data. + /// + /// This factory class is thread safe and its data can be read by on any thread. + /// + public abstract class DataReaderFactory : IDisposable { + /// + /// The filename or null if the data is not from a file + /// + public abstract string Filename { get; } + + /// + /// Gets the total length of the data + /// + public abstract uint Length { get; } + + /// + /// Creates a data reader that can read all data + /// + /// + public DataReader CreateReader() => CreateReader(0U, Length); + + /// + /// Creates a data reader + /// + /// Offset of data + /// Length of data + /// + public abstract DataReader CreateReader(uint offset, uint length); + + static void ThrowArgumentOutOfRangeException(string paramName) => + throw new ArgumentOutOfRangeException(paramName); + + static void Throw_CreateReader_2(int offset, int length) { + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset)); + Debug.Assert(length < 0); + throw new ArgumentOutOfRangeException(nameof(length)); + } + + /// + /// Creates a data reader + /// + /// Offset of data + /// Length of data + /// + public DataReader CreateReader(uint offset, int length) { + if (length < 0) + ThrowArgumentOutOfRangeException(nameof(length)); + return CreateReader(offset, (uint)length); + } + + /// + /// Creates a data reader + /// + /// Offset of data + /// Length of data + /// + public DataReader CreateReader(int offset, uint length) { + if (offset < 0) + ThrowArgumentOutOfRangeException(nameof(offset)); + return CreateReader((uint)offset, length); + } + + /// + /// Creates a data reader + /// + /// Offset of data + /// Length of data + /// + public DataReader CreateReader(int offset, int length) { + if (offset < 0 || length < 0) + Throw_CreateReader_2(offset, length); + return CreateReader((uint)offset, (uint)length); + } + + /// + /// Creates a data reader + /// + /// Stream + /// Offset of data + /// Length of data + /// + protected DataReader CreateReader(DataStream stream, uint offset, uint length) { + uint maxOffset = Length; + if (offset > maxOffset) + offset = maxOffset; + if ((ulong)offset + length > maxOffset) + length = maxOffset - offset; + return new DataReader(stream, offset, length); + } + + /// + /// Raised when all cached s created by this instance must be recreated + /// + public virtual event EventHandler DataReaderInvalidated { add { } remove { } } + + /// + /// Disposes of this instance + /// + public abstract void Dispose(); + } +} diff --git a/src/IO/DataReaderFactoryFactory.cs b/src/IO/DataReaderFactoryFactory.cs new file mode 100644 index 000000000..9a16dec0b --- /dev/null +++ b/src/IO/DataReaderFactoryFactory.cs @@ -0,0 +1,37 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace dnlib.IO { + static class DataReaderFactoryFactory { + static readonly bool isUnix; + + static DataReaderFactoryFactory() { + // See http://mono-project.com/FAQ:_Technical#Mono_Platforms for platform detection. + int p = (int)Environment.OSVersion.Platform; + if (p == 4 || p == 6 || p == 128) + isUnix = true; +#if NETSTANDARD || NETCOREAPP + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + isUnix = true; +#endif + } + + public static DataReaderFactory Create(string fileName, bool mapAsImage) { + var creator = CreateDataReaderFactory(fileName, mapAsImage); + if (creator is not null) + return creator; + + return ByteArrayDataReaderFactory.Create(File.ReadAllBytes(fileName), fileName); + } + + static DataReaderFactory CreateDataReaderFactory(string fileName, bool mapAsImage) { + if (!isUnix) + return MemoryMappedDataReaderFactory.CreateWindows(fileName, mapAsImage); + else + return MemoryMappedDataReaderFactory.CreateUnix(fileName, mapAsImage); + } + } +} diff --git a/src/IO/DataReaderStream.cs b/src/IO/DataReaderStream.cs new file mode 100644 index 000000000..829e3e6f6 --- /dev/null +++ b/src/IO/DataReaderStream.cs @@ -0,0 +1,69 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.IO; + +namespace dnlib.IO { + sealed class DataReaderStream : Stream { + public override bool CanRead => true; + public override bool CanSeek => true; + public override bool CanWrite => false; + public override long Length => reader.Length; + + public override long Position { + get => position; + set => position = value; + } + + DataReader reader; + long position; + + public DataReaderStream(in DataReader reader) { + this.reader = reader; + position = reader.Position; + } + + public override void Flush() { } + + bool CheckAndSetPosition() { + if ((ulong)position > reader.Length) + return false; + reader.Position = (uint)position; + return true; + } + + public override long Seek(long offset, SeekOrigin origin) { + switch (origin) { + case SeekOrigin.Begin: Position = offset; break; + case SeekOrigin.Current: Position += offset; break; + case SeekOrigin.End: Position = Length + offset; break; + } + return Position; + } + + public override int Read(byte[] buffer, int offset, int count) { + if (buffer is null) + throw new ArgumentNullException(nameof(buffer)); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset)); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count)); + if (!CheckAndSetPosition()) + return 0; + int bytesToRead = (int)Math.Min((uint)count, reader.BytesLeft); + reader.ReadBytes(buffer, offset, bytesToRead); + Position += bytesToRead; + return bytesToRead; + } + + public override int ReadByte() { + if (!CheckAndSetPosition() || !reader.CanRead(1U)) + return -1; + Position++; + return reader.ReadByte(); + } + + public override void SetLength(long value) => throw new NotSupportedException(); + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + } +} diff --git a/src/IO/DataStream.cs b/src/IO/DataStream.cs new file mode 100644 index 000000000..6bdb959a2 --- /dev/null +++ b/src/IO/DataStream.cs @@ -0,0 +1,167 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Text; + +namespace dnlib.IO { + /// + /// This class is used by a . The instance + /// verifies that all input are valid before calling any methods in this class. + /// This class is thread safe. + /// + public abstract class DataStream { + /// + /// Reads bytes + /// + /// Offset of data + /// Destination pointer + /// Number of bytes to read + public unsafe abstract void ReadBytes(uint offset, void* destination, int length); + + /// + /// Reads bytes + /// + /// Offset of data + /// Destination array + /// Destination index + /// Number of bytes to read + public abstract void ReadBytes(uint offset, byte[] destination, int destinationIndex, int length); + + /// + /// Reads a + /// + /// Offset of data + /// + public abstract byte ReadByte(uint offset); + + /// + /// Reads a + /// + /// Offset of data + /// + public virtual sbyte ReadSByte(uint offset) => (sbyte)ReadByte(offset); + + /// + /// Reads a 1-byte-long + /// + /// Offset of data + /// + public virtual bool ReadBoolean(uint offset) => ReadByte(offset) != 0; + + /// + /// Reads a + /// + /// Offset of data + /// + public abstract ushort ReadUInt16(uint offset); + + /// + /// Reads a + /// + /// Offset of data + /// + public virtual short ReadInt16(uint offset) => (short)ReadUInt16(offset); + + /// + /// Reads a 2-byte-long + /// + /// Offset of data + /// + public virtual char ReadChar(uint offset) => (char)ReadUInt16(offset); + + /// + /// Reads a + /// + /// Offset of data + /// + public abstract uint ReadUInt32(uint offset); + + /// + /// Reads a + /// + /// Offset of data + /// + public virtual int ReadInt32(uint offset) => (int)ReadUInt32(offset); + + /// + /// Reads a + /// + /// Offset of data + /// + public abstract ulong ReadUInt64(uint offset); + + /// + /// Reads a + /// + /// Offset of data + /// + public virtual long ReadInt64(uint offset) => (long)ReadUInt64(offset); + + /// + /// Reads a + /// + /// Offset of data + /// + public abstract float ReadSingle(uint offset); + + /// + /// Reads a + /// + /// Offset of data + /// + public abstract double ReadDouble(uint offset); + + /// + /// Reads a + /// + /// Offset of data + /// + public virtual Guid ReadGuid(uint offset) => + new Guid(ReadUInt32(offset), ReadUInt16(offset + 4), ReadUInt16(offset + 6), + ReadByte(offset + 8), ReadByte(offset + 9), ReadByte(offset + 10), ReadByte(offset + 11), + ReadByte(offset + 12), ReadByte(offset + 13), ReadByte(offset + 14), ReadByte(offset + 15)); + + /// + /// Reads a + /// + /// Offset of data + /// + public virtual decimal ReadDecimal(uint offset) { + int lo = ReadInt32(offset); + int mid = ReadInt32(offset + 4); + int hi = ReadInt32(offset + 8); + int flags = ReadInt32(offset + 12); + + byte scale = (byte)(flags >> 16); + bool isNegative = (flags & 0x80000000) != 0; + return new decimal(lo, mid, hi, isNegative, scale); + } + + /// + /// Reads a UTF-16 encoded + /// + /// Offset of data + /// Number of characters to read + /// + public abstract string ReadUtf16String(uint offset, int chars); + + /// + /// Reads a string + /// + /// Offset of data + /// Length of string in bytes + /// Encoding + /// + public abstract string ReadString(uint offset, int length, Encoding encoding); + + /// + /// Gets the data offset of a byte or returns false if the byte wasn't found + /// + /// Offset of data + /// End offset of data (not inclusive) + /// Byte value to search for + /// Offset of the byte if found + /// + public abstract bool TryGetOffsetOf(uint offset, uint endOffset, byte value, out uint valueOffset); + } +} diff --git a/src/IO/DataStreamFactory.cs b/src/IO/DataStreamFactory.cs new file mode 100644 index 000000000..808d6e97f --- /dev/null +++ b/src/IO/DataStreamFactory.cs @@ -0,0 +1,56 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Diagnostics; +using dnlib.PE; + +namespace dnlib.IO { + /// + /// Creates s + /// + public static unsafe class DataStreamFactory { + //TODO: There are other places that use pointers that also need to be updated + static bool supportsUnalignedAccesses = CalculateSupportsUnalignedAccesses(); + + static bool CalculateSupportsUnalignedAccesses() { + var machine = ProcessorArchUtils.GetProcessCpuArchitecture(); + switch (machine) { + case Machine.I386: + case Machine.AMD64: + return true; + case Machine.ARMNT: + case Machine.ARM64: + return false; + default: + Debug.Fail($"Unknown CPU arch: {machine}"); + return true; + } + } + + /// + /// Creates a that reads from native memory + /// + /// Pointer to data + /// + public static DataStream Create(byte* data) { + if (data is null) + throw new ArgumentNullException(nameof(data)); + if (supportsUnalignedAccesses) + return new UnalignedNativeMemoryDataStream(data); + return new AlignedNativeMemoryDataStream(data); + } + + /// + /// Creates a that reads from a byte array + /// + /// Data + /// + public static DataStream Create(byte[] data) { + if (data is null) + throw new ArgumentNullException(nameof(data)); + if (supportsUnalignedAccesses) + return new UnalignedByteArrayDataStream(data); + return new AlignedByteArrayDataStream(data); + } + } +} diff --git a/src/IO/EmptyDataStream.cs b/src/IO/EmptyDataStream.cs new file mode 100644 index 000000000..5c83c51da --- /dev/null +++ b/src/IO/EmptyDataStream.cs @@ -0,0 +1,33 @@ +// dnlib: See LICENSE.txt for more info + +using System.Text; + +namespace dnlib.IO { + sealed unsafe class EmptyDataStream : DataStream { + public static readonly DataStream Instance = new EmptyDataStream(); + + EmptyDataStream() { } + + public override void ReadBytes(uint offset, void* destination, int length) { + var p = (byte*)destination; + for (int i = 0; i < length; i++) + *p = 0; + } + public override void ReadBytes(uint offset, byte[] destination, int destinationIndex, int length) { + for (int i = 0; i < length; i++) + destination[destinationIndex + i] = 0; + } + public override byte ReadByte(uint offset) => 0; + public override ushort ReadUInt16(uint offset) => 0; + public override uint ReadUInt32(uint offset) => 0; + public override ulong ReadUInt64(uint offset) => 0; + public override float ReadSingle(uint offset) => 0; + public override double ReadDouble(uint offset) => 0; + public override string ReadUtf16String(uint offset, int chars) => string.Empty; + public override string ReadString(uint offset, int length, Encoding encoding) => string.Empty; + public override bool TryGetOffsetOf(uint offset, uint endOffset, byte value, out uint valueOffset) { + valueOffset = 0; + return false; + } + } +} diff --git a/src/IO/FileOffset.cs b/src/IO/FileOffset.cs index 25931cc1b..882fe3c4c 100644 --- a/src/IO/FileOffset.cs +++ b/src/IO/FileOffset.cs @@ -1,10 +1,10 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.IO { +namespace dnlib.IO { /// /// Represents a file offset /// - public enum FileOffset : long { + public enum FileOffset : uint { } partial class IOExtensions { @@ -13,17 +13,13 @@ partial class IOExtensions { /// /// this /// Alignment - public static FileOffset AlignUp(this FileOffset offset, uint alignment) { - return (FileOffset)(((uint)offset + alignment - 1) & ~(alignment - 1)); - } + public static FileOffset AlignUp(this FileOffset offset, uint alignment) => (FileOffset)(((uint)offset + alignment - 1) & ~(alignment - 1)); /// /// Align up /// /// this /// Alignment - public static FileOffset AlignUp(this FileOffset offset, int alignment) { - return (FileOffset)(((uint)offset + alignment - 1) & ~(alignment - 1)); - } + public static FileOffset AlignUp(this FileOffset offset, int alignment) => (FileOffset)(((uint)offset + alignment - 1) & ~(alignment - 1)); } } diff --git a/src/IO/FileSection.cs b/src/IO/FileSection.cs index 1237f05fb..f0413a58b 100644 --- a/src/IO/FileSection.cs +++ b/src/IO/FileSection.cs @@ -19,30 +19,23 @@ public class FileSection : IFileSection { protected uint size; /// - public FileOffset StartOffset { - get { return startOffset; } - } + public FileOffset StartOffset => startOffset; /// - public FileOffset EndOffset { - get { return startOffset + size; } - } + public FileOffset EndOffset => startOffset + size; /// /// Set to 's current position /// /// The reader - protected void SetStartOffset(IImageStream reader) { - startOffset = (FileOffset)reader.Position; - } + protected void SetStartOffset(ref DataReader reader) => + startOffset = (FileOffset)reader.CurrentOffset; /// /// Set according to 's current position /// /// The reader - protected void SetEndoffset(IImageStream reader) { - size = (uint)(reader.Position - startOffset); - startOffset = reader.FileOffset + (long)startOffset; - } + protected void SetEndoffset(ref DataReader reader) => + size = reader.CurrentOffset - (uint)startOffset; } } diff --git a/src/IO/IBinaryReader.cs b/src/IO/IBinaryReader.cs deleted file mode 100644 index 14b2b21ca..000000000 --- a/src/IO/IBinaryReader.cs +++ /dev/null @@ -1,658 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.IO; -using System.Text; - -namespace dnlib.IO { - /// - /// Reads binary data - /// - public interface IBinaryReader : IDisposable { - /// - /// Returns the length of the stream - /// - long Length { get; } - - /// - /// Gets/sets the position - /// - long Position { get; set; } - - /// - /// Reads bytes from the current - /// and increments by bytes - /// - /// Number of bytes to read - /// All available bytes. This can be less than bytes - /// if there's not enough bytes left. - /// An I/O error occurs - byte[] ReadBytes(int size); - - /// - /// Reads bytes to and increments - /// by the number of bytes read. - /// - /// Buffer - /// Offset in buffer where to place all read bytes - /// Number of bytes to read - /// Number of bytes read, and can be less than if - /// there's no more bytes to read. - int Read(byte[] buffer, int offset, int length); - - /// - /// Reads bytes until byte is found. is - /// incremented by the number of bytes read (size of return value). - /// - /// The terminating byte - /// All the bytes (not including ) or null if - /// wasn't found. - byte[] ReadBytesUntilByte(byte b); - - /// - /// Reads a from the current position and increments by 1 - /// - /// The 8-bit signed byte - /// An I/O error occurs - sbyte ReadSByte(); - - /// - /// Reads a from the current position and increments by 1 - /// - /// The 8-bit unsigned byte - /// An I/O error occurs - byte ReadByte(); - - /// - /// Reads a from the current position and increments by 2 - /// - /// The 16-bit signed integer - /// An I/O error occurs - short ReadInt16(); - - /// - /// Reads a from the current position and increments by 2 - /// - /// The 16-bit unsigned integer - /// An I/O error occurs - ushort ReadUInt16(); - - /// - /// Reads a from the current position and increments by 4 - /// - /// The 32-bit signed integer - /// An I/O error occurs - int ReadInt32(); - - /// - /// Reads a from the current position and increments by 4 - /// - /// The 32-bit unsigned integer - /// An I/O error occurs - uint ReadUInt32(); - - /// - /// Reads a from the current position and increments by 8 - /// - /// The 64-bit signed integer - /// An I/O error occurs - long ReadInt64(); - - /// - /// Reads a from the current position and increments by 8 - /// - /// The 64-bit unsigned integer - /// An I/O error occurs - ulong ReadUInt64(); - - /// - /// Reads a from the current position and increments by 4 - /// - /// The 32-bit single - /// An I/O error occurs - float ReadSingle(); - - /// - /// Reads a from the current position and increments by 8 - /// - /// The 64-bit double - /// An I/O error occurs - double ReadDouble(); - - /// - /// Reads a from the current position and increments - /// by the number of bytes read. - /// - /// Number of characters to read - /// The string - /// An I/O error occurs - string ReadString(int chars); - } - - public static partial class IOExtensions { - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static bool ReadBooleanAt(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadBoolean(); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static byte ReadByteAt(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadByte(); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static sbyte ReadSByteAt(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadSByte(); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static short ReadInt16At(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadInt16(); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static ushort ReadUInt16At(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadUInt16(); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static int ReadInt32At(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadInt32(); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static uint ReadUInt32At(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadUInt32(); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static long ReadInt64At(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadInt64(); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static ulong ReadUInt64At(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadUInt64(); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static float ReadSingleAt(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadSingle(); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static double ReadDoubleAt(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadDouble(); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static string ReadStringAt(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadString(); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// Encoding - /// The - public static string ReadStringAt(this IBinaryReader reader, long offset, Encoding encoding) { - reader.Position = offset; - return reader.ReadString(encoding); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static char ReadCharAt(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadChar(); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// Encoding - /// The - public static char ReadCharAt(this IBinaryReader reader, long offset, Encoding encoding) { - reader.Position = offset; - return reader.ReadChar(encoding); - } - - /// - /// Reads a at offset - /// - /// this - /// Offset - /// The - public static decimal ReadDecimalAt(this IBinaryReader reader, long offset) { - reader.Position = offset; - return reader.ReadDecimal(); - } - - /// - /// Reads all remaining bytes - /// - /// this - /// All remaining bytes - public static byte[] ReadRemainingBytes(this IBinaryReader reader) { - if (reader.Position >= reader.Length) - return new byte[0]; - return reader.ReadBytes((int)(reader.Length - reader.Position)); - } - - /// - /// Reads all bytes - /// - /// this - /// All bytes - public static byte[] ReadAllBytes(this IBinaryReader reader) { - reader.Position = 0; - return reader.ReadBytes((int)reader.Length); - } - - /// - /// Reads a from the current position and increments by 1 - /// - /// this - /// The boolean - /// An I/O error occurs - public static bool ReadBoolean(this IBinaryReader self) { - return self.ReadByte() != 0; - } - - /// - /// Reads a from the current position and increments by 2 - /// - /// this - /// The char - /// An I/O error occurs - public static char ReadChar(this IBinaryReader self) { - return self.ReadChar(Encoding.UTF8); - } - - /// - /// Reads a from the current position and increments by 2 - /// - /// this - /// Encoding - /// The char - /// An I/O error occurs - public static char ReadChar(this IBinaryReader self, Encoding encoding) { - // This is slow but this method should rarely be called... - var decoder = encoding.GetDecoder(); - bool twoBytes = encoding is UnicodeEncoding; - byte[] bytes = new byte[2]; - char[] chars = new char[1]; - while (true) { - bytes[0] = self.ReadByte(); - if (twoBytes) - bytes[1] = self.ReadByte(); - int x = decoder.GetChars(bytes, 0, twoBytes ? 2 : 1, chars, 0); - if (x != 0) - break; - } - return chars[0]; - } - - /// - /// Reads a UTF-8 from the current position and increments - /// by the length of the string. - /// - /// this - /// The string - public static string ReadString(this IBinaryReader reader) { - return reader.ReadString(Encoding.UTF8); - } - - /// - /// Reads a from the current position and increments - /// by the length of the string. - /// - /// this - /// Encoding - /// The string - public static string ReadString(this IBinaryReader reader, Encoding encoding) { - int len = reader.Read7BitEncodedInt32(); - return encoding.GetString(reader.ReadBytes(len)); - } - - /// - /// Reads a from the current position and increments - /// by 16 - /// - /// this - /// The decmial - /// An I/O error occurs - public static decimal ReadDecimal(this IBinaryReader reader) { - var bits = new int[4] { - reader.ReadInt32(), // lo - reader.ReadInt32(), // mid - reader.ReadInt32(), // hi - reader.ReadInt32(), // flags - }; - return new decimal(bits); - } - - /// - /// Reads chars - /// - /// this - /// Number of s to read - /// All the chars - public static char[] ReadChars(this IBinaryReader reader, int length) { - var chars = new char[length]; - for (int i = 0; i < length; i++) - chars[i] = reader.ReadChar(); - return chars; - } - - /// - /// Reads a compressed from the current position in - /// - /// Max value it can return is 0x1FFFFFFF - /// The reader - /// Decompressed value - /// true if successful, false on failure - public static bool ReadCompressedUInt32(this IBinaryReader reader, out uint val) { - var pos = reader.Position; - var len = reader.Length; - if (pos >= len) { - val = 0; - return false; - } - - byte b = reader.ReadByte(); - if ((b & 0x80) == 0) { - val = b; - return true; - } - - if ((b & 0xC0) == 0x80) { - if (pos + 1 < pos || pos + 1 >= len) { - val = 0; - return false; - } - val = (uint)(((b & 0x3F) << 8) | reader.ReadByte()); - return true; - } - - // The encoding 111x isn't allowed but the CLR sometimes doesn't verify this - // and just assumes it's 110x. Don't fail if it's 111x, just assume it's 110x. - - if (pos + 3 < pos || pos + 3 >= len) { - val = 0; - return false; - } - val = (uint)(((b & 0x1F) << 24) | (reader.ReadByte() << 16) | - (reader.ReadByte() << 8) | reader.ReadByte()); - return true; - } - - /// - /// Reads a compressed from the current position in - /// - /// The reader - /// Decompressed value - /// true if successful, false on failure - public static bool ReadCompressedInt32(this IBinaryReader reader, out int val) { - var pos = reader.Position; - var len = reader.Length; - if (pos >= len) { - val = 0; - return false; - } - - byte b = reader.ReadByte(); - if ((b & 0x80) == 0) { - if ((b & 1) != 0) - val = -0x40 | (b >> 1); - else - val = (b >> 1); - return true; - } - - if ((b & 0xC0) == 0x80) { - if (pos + 1 < pos || pos + 1 >= len) { - val = 0; - return false; - } - uint tmp = (uint)(((b & 0x3F) << 8) | reader.ReadByte()); - if ((tmp & 1) != 0) - val = -0x2000 | (int)(tmp >> 1); - else - val = (int)(tmp >> 1); - return true; - } - - if ((b & 0xE0) == 0xC0) { - if (pos + 3 < pos || pos + 3 >= len) { - val = 0; - return false; - } - uint tmp = (uint)(((b & 0x1F) << 24) | (reader.ReadByte() << 16) | - (reader.ReadByte() << 8) | reader.ReadByte()); - if ((tmp & 1) != 0) - val = -0x10000000 | (int)(tmp >> 1); - else - val = (int)(tmp >> 1); - return true; - } - - val = 0; - return false; - } - - /// - /// Reads a compressed from the current position in - /// - /// The reader - /// The value - public static uint ReadCompressedUInt32(this IBinaryReader reader) { - uint val; - if (!reader.ReadCompressedUInt32(out val)) - throw new IOException("Could not read a compressed UInt32"); - return val; - } - - /// - /// Reads a compressed from the current position in - /// - /// The reader - /// The value - public static int ReadCompressedInt32(this IBinaryReader reader) { - int val; - if (!reader.ReadCompressedInt32(out val)) - throw new IOException("Could not read a compressed Int32"); - return val; - } - - /// - /// Reads a 7-bit encoded integer - /// - /// this - /// The decoded integer - public static uint Read7BitEncodedUInt32(this IBinaryReader reader) { - uint val = 0; - int bits = 0; - for (int i = 0; i < 5; i++) { - byte b = reader.ReadByte(); - val |= (uint)(b & 0x7F) << bits; - if ((b & 0x80) == 0) - return val; - bits += 7; - } - throw new IOException("Invalid encoded UInt32"); - } - - /// - /// Reads a 7-bit encoded integer - /// - /// this - /// The decoded integer - public static int Read7BitEncodedInt32(this IBinaryReader reader) { - return (int)reader.Read7BitEncodedUInt32(); - } - - /// - /// Creates a using . The created - /// doesn't own , so it's not - /// 'd. - /// - /// this - /// A new instance - public static Stream CreateStream(this IBinaryReader reader) { - return new BinaryReaderStream(reader); - } - - /// - /// Creates a using - /// - /// this - /// true if the created owns - /// - /// A new instance - public static Stream CreateStream(this IBinaryReader reader, bool ownsReader) { - return new BinaryReaderStream(reader, ownsReader); - } - - /// - /// Checks whether we can read bytes - /// - /// Reader - /// Size in bytes - public static bool CanRead(this IBinaryReader reader, int size) { - return (reader.Position + size <= reader.Length && reader.Position + size >= reader.Position) || size == 0; - } - - /// - /// Checks whether we can read bytes - /// - /// Reader - /// Size in bytes - public static bool CanRead(this IBinaryReader reader, uint size) { - return (reader.Position + size <= reader.Length && reader.Position + size >= reader.Position) || size == 0; - } - - /// - /// Writes , starting at 's current - /// position, to starting at 's - /// current position. Returns the number of bytes written. - /// - /// Reader - /// Writer - /// Number of bytes written - /// Could not write all bytes or data is too big - public static uint WriteTo(this IBinaryReader reader, BinaryWriter writer) { - if (reader.Position >= reader.Length) - return 0; - return reader.WriteTo(writer, new byte[0x2000]); - } - - /// - /// Writes , starting at 's current - /// position, to starting at 's - /// current position. Returns the number of bytes written. - /// - /// Reader - /// Writer - /// Temp buffer during writing - /// Number of bytes written - /// Could not write all bytes or data is too big - public static uint WriteTo(this IBinaryReader reader, BinaryWriter writer, byte[] dataBuffer) { - if (reader.Position >= reader.Length) - return 0; - long longLenLeft = reader.Length - reader.Position; - if (longLenLeft > uint.MaxValue) - throw new IOException("Data is too big"); - uint lenLeft = (uint)longLenLeft; - uint writtenBytes = lenLeft; - while (lenLeft > 0) { - int num = (int)Math.Min((uint)dataBuffer.Length, lenLeft); - lenLeft -= (uint)num; - if (num != reader.Read(dataBuffer, 0, num)) - throw new IOException("Could not read all reader bytes"); - writer.Write(dataBuffer, 0, num); - } - return writtenBytes; - } - } -} diff --git a/src/IO/IImageStream.cs b/src/IO/IImageStream.cs deleted file mode 100644 index 8cd43d7ae..000000000 --- a/src/IO/IImageStream.cs +++ /dev/null @@ -1,42 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -namespace dnlib.IO { - /// - /// Interface to access part of some data - /// - public interface IImageStream : IBinaryReader { - /// - /// Returns the file offset of the stream - /// - FileOffset FileOffset { get; } - - /// - /// Creates a sub stream that can access parts of this stream - /// - /// File offset relative to the start of this stream - /// Length - /// A new stream - IImageStream Create(FileOffset offset, long length); - } - - static partial class IOExtensions { - /// - /// Creates a stream that can access all data starting from - /// - /// this - /// Offset relative to the beginning of the stream - /// A new stream - public static IImageStream Create(this IImageStream self, FileOffset offset) { - return self.Create(offset, long.MaxValue); - } - - /// - /// Clones this - /// - /// this - /// A new instance - public static IImageStream Clone(this IImageStream self) { - return self.Create(0, long.MaxValue); - } - } -} diff --git a/src/IO/IImageStreamCreator.cs b/src/IO/IImageStreamCreator.cs deleted file mode 100644 index 6e344acf6..000000000 --- a/src/IO/IImageStreamCreator.cs +++ /dev/null @@ -1,46 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; - -namespace dnlib.IO { - /// - /// Creates a new stream that accesses part of some data - /// - public interface IImageStreamCreator : IDisposable { - /// - /// The file name or null if data is not from a file - /// - string FileName { get; } - - /// - /// Returns the total length of the original data - /// - long Length { get; } - - /// - /// Creates a stream that can access only part of the data - /// - /// Offset within the original data - /// Length of section within the original data - /// A new stream - IImageStream Create(FileOffset offset, long length); - - /// - /// Creates a stream that can access all data - /// - /// A new stream - IImageStream CreateFull(); - } - - static partial class IOExtensions { - /// - /// Creates a stream that can access all data starting from - /// - /// this - /// Offset within the original data - /// A new stream - public static IImageStream Create(this IImageStreamCreator self, FileOffset offset) { - return self.Create(offset, long.MaxValue); - } - } -} diff --git a/src/IO/IOExtensions.cs b/src/IO/IOExtensions.cs index f4d9bbd84..719cdd24b 100644 --- a/src/IO/IOExtensions.cs +++ b/src/IO/IOExtensions.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.IO { +namespace dnlib.IO { /// /// Extension methods /// diff --git a/src/IO/ImageStreamCreator.cs b/src/IO/ImageStreamCreator.cs deleted file mode 100644 index 882425587..000000000 --- a/src/IO/ImageStreamCreator.cs +++ /dev/null @@ -1,85 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.IO; - -namespace dnlib.IO { - /// - /// Creates a instance - /// - public static class ImageStreamCreator { - static readonly bool isUnix; - - static ImageStreamCreator() { - // See http://mono-project.com/FAQ:_Technical#Mono_Platforms for platform detection. - int p = (int)Environment.OSVersion.Platform; - if (p == 4 || p == 6 || p == 128) - isUnix = true; - } - - /// - /// Creates a . It will be a - /// if the operating system supports the memory - /// mapped file methods we use, else . - /// - /// Filename - /// A new instance - public static IImageStreamCreator Create(string fileName) { - return Create(fileName, false); - } - - /// - /// Creates a . It will be a - /// if the operating system supports the memory - /// mapped file methods we use, else . - /// - /// Filename - /// true if we should map it as an executable. Not supported - /// on Linux/Mac - /// A new instance - public static IImageStreamCreator Create(string fileName, bool mapAsImage) { - var creator = CreateMemoryMappedFileStreamCreator(fileName, mapAsImage); - if (creator != null) - return creator; - - return new MemoryStreamCreator(File.ReadAllBytes(fileName)) { FileName = fileName }; - } - - static MemoryMappedFileStreamCreator CreateMemoryMappedFileStreamCreator(string fileName, bool mapAsImage) { - if (!isUnix) - return MemoryMappedFileStreamCreator.CreateWindows(fileName, mapAsImage); - else - return MemoryMappedFileStreamCreator.CreateUnix(fileName, mapAsImage); - } - - /// - /// Creates a - /// - /// Filename - /// A new instance - public static IImageStream CreateImageStream(string fileName) { - return CreateImageStream(fileName, false); - } - - /// - /// Creates a - /// - /// Filename - /// true if we should map it as an executable. Not supported - /// on Linux/Mac - /// A new instance - public static IImageStream CreateImageStream(string fileName, bool mapAsImage) { - var creator = CreateMemoryMappedFileStreamCreator(fileName, mapAsImage); - try { - if (creator != null) - return new UnmanagedMemoryImageStream(creator); - return MemoryImageStream.Create(File.ReadAllBytes(fileName)); - } - catch { - if (creator != null) - creator.Dispose(); - throw; - } - } - } -} diff --git a/src/IO/MemoryImageStream.cs b/src/IO/MemoryImageStream.cs deleted file mode 100644 index b32a9cf17..000000000 --- a/src/IO/MemoryImageStream.cs +++ /dev/null @@ -1,260 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Diagnostics; -using System.IO; - -namespace dnlib.IO { - /// - /// IImageStream for byte[] - /// - [DebuggerDisplay("FO:{fileOffset} S:{Length}")] - public sealed class MemoryImageStream : IImageStream { - FileOffset fileOffset; - byte[] data; - int dataOffset; - int dataEnd; - int position; - - /// - /// Creates a new instance - /// - /// Data - /// A new instance - public static MemoryImageStream Create(byte[] data) { - return new MemoryImageStream(0, data, 0, data.Length); - } - - /// - /// Creates a new instance - /// - /// Data - /// Start offset in - /// Length of data - /// A new instance - public static MemoryImageStream Create(byte[] data, int offset, int len) { - return new MemoryImageStream(0, data, offset, len); - } - - /// - /// Constructor - /// - /// File offset of data - /// The data - /// Start offset in - /// Length of data - public MemoryImageStream(FileOffset fileOffset, byte[] data, int dataOffset, int dataLength) { - this.fileOffset = fileOffset; - this.data = data; - this.dataOffset = dataOffset; - this.dataEnd = dataOffset + dataLength; - this.position = dataOffset; - } - - /// - /// Gets the data - /// - internal byte[] DataArray { - get { return data; } - } - - /// - /// Gets the start of the data in used by this stream - /// - internal int DataOffset { - get { return dataOffset; } - } - - /// - public FileOffset FileOffset { - get { return fileOffset; } - } - - /// - public long Length { - get { return dataEnd - dataOffset; } - } - - /// - public long Position { - get { return position - dataOffset; } - set { - long newPos = dataOffset + value; - if (newPos < dataOffset || newPos > int.MaxValue) - newPos = int.MaxValue; - position = (int)newPos; - } - } - - /// - /// Creates an empty instance - /// - public static MemoryImageStream CreateEmpty() { - return new MemoryImageStream(0, new byte[0], 0, 0); - } - - /// - public IImageStream Create(FileOffset offset, long length) { - if ((long)offset < 0 || length < 0) - return MemoryImageStream.CreateEmpty(); - - int offs = (int)Math.Min((long)Length, (long)offset); - int len = (int)Math.Min((long)Length - offs, length); - return new MemoryImageStream((FileOffset)((long)fileOffset + (long)offset), data, dataOffset + offs, len); - } - - /// - public byte[] ReadBytes(int size) { - if (size < 0) - throw new IOException("Invalid size"); - size = Math.Min(size, (int)Length - Math.Min((int)Length, (int)Position)); - var newData = new byte[size]; - Array.Copy(data, position, newData, 0, size); - position += size; - return newData; - } - - /// - public int Read(byte[] buffer, int offset, int length) { - if (length < 0) - throw new IOException("Invalid size"); - length = Math.Min(length, (int)Length - Math.Min((int)Length, (int)Position)); - Array.Copy(data, position, buffer, offset, length); - position += length; - return length; - } - - /// - public byte[] ReadBytesUntilByte(byte b) { - int pos = GetPositionOf(b); - if (pos < 0) - return null; - return ReadBytes(pos - position); - } - - int GetPositionOf(byte b) { - int pos = position; - while (pos < dataEnd) { - if (data[pos] == b) - return pos; - pos++; - } - return -1; - } - - /// - public sbyte ReadSByte() { - if (position >= dataEnd) - throw new IOException("Can't read one SByte"); - return (sbyte)data[position++]; - } - - /// - public byte ReadByte() { - if (position >= dataEnd) - throw new IOException("Can't read one Byte"); - return data[position++]; - } - - /// - public short ReadInt16() { - if (position + 1 >= dataEnd) - throw new IOException("Can't read one Int16"); - return (short)(data[position++] | (data[position++] << 8)); - } - - /// - public ushort ReadUInt16() { - if (position + 1 >= dataEnd) - throw new IOException("Can't read one UInt16"); - return (ushort)(data[position++] | (data[position++] << 8)); - } - - /// - public int ReadInt32() { - if (position + 3 >= dataEnd) - throw new IOException("Can't read one Int32"); - return data[position++] | - (data[position++] << 8) | - (data[position++] << 16) | - (data[position++] << 24); - } - - /// - public uint ReadUInt32() { - if (position + 3 >= dataEnd) - throw new IOException("Can't read one UInt32"); - return (uint)(data[position++] | - (data[position++] << 8) | - (data[position++] << 16) | - (data[position++] << 24)); - } - - /// - public long ReadInt64() { - if (position + 7 >= dataEnd) - throw new IOException("Can't read one Int64"); - return (long)data[position++] | - ((long)data[position++] << 8) | - ((long)data[position++] << 16) | - ((long)data[position++] << 24) | - ((long)data[position++] << 32) | - ((long)data[position++] << 40) | - ((long)data[position++] << 48) | - ((long)data[position++] << 56); - } - - /// - public ulong ReadUInt64() { - if (position + 7 >= dataEnd) - throw new IOException("Can't read one UInt64"); - return (ulong)data[position++] | - ((ulong)data[position++] << 8) | - ((ulong)data[position++] << 16) | - ((ulong)data[position++] << 24) | - ((ulong)data[position++] << 32) | - ((ulong)data[position++] << 40) | - ((ulong)data[position++] << 48) | - ((ulong)data[position++] << 56); - } - - /// - public float ReadSingle() { - if (position + 3 >= dataEnd) - throw new IOException("Can't read one Single"); - var val = BitConverter.ToSingle(data, position); - position += 4; - return val; - } - - /// - public double ReadDouble() { - if (position + 7 >= dataEnd) - throw new IOException("Can't read one Double"); - var val = BitConverter.ToDouble(data, position); - position += 8; - return val; - } - - /// - public string ReadString(int chars) { - if ((uint)chars > (uint)int.MaxValue) - throw new IOException("Not enough space to read the string"); - if (position + chars * 2 < position || (chars != 0 && position + chars * 2 - 1 >= dataEnd)) - throw new IOException("Not enough space to read the string"); - var array = new char[chars]; - for (int i = 0; i < chars; i++) - array[i] = (char)(data[position++] | (data[position++] << 8)); - return new string(array); - } - - /// - public void Dispose() { - fileOffset = 0; - data = null; - dataOffset = 0; - dataEnd = 0; - position = 0; - } - } -} diff --git a/src/IO/MemoryMappedFileStreamCreator.cs b/src/IO/MemoryMappedDataReaderFactory.cs similarity index 54% rename from src/IO/MemoryMappedFileStreamCreator.cs rename to src/IO/MemoryMappedDataReaderFactory.cs index efb7d1eee..dc1a64f64 100644 --- a/src/IO/MemoryMappedFileStreamCreator.cs +++ b/src/IO/MemoryMappedDataReaderFactory.cs @@ -1,26 +1,69 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Runtime.Serialization; using System.Threading; using Microsoft.Win32.SafeHandles; namespace dnlib.IO { /// - /// Maps a file into memory using MapViewOfFile() and creates streams - /// that can access part of it in memory. + /// Creates s that read memory mapped data /// - /// Since this class maps a file into memory, the user should call - /// to free any resources - /// used by the class when it's no longer needed. - [DebuggerDisplay("mmap: A:{data} L:{dataLength} {theFileName}")] - sealed class MemoryMappedFileStreamCreator : UnmanagedMemoryStreamCreator { + sealed unsafe class MemoryMappedDataReaderFactory : DataReaderFactory { + /// + /// The filename or null if the data is not from a file + /// + public override string Filename => filename; + + /// + /// Gets the total length of the data + /// + public override uint Length => length; - OSType osType = OSType.Unknown; + /// + /// Raised when all cached s created by this instance must be recreated + /// + public override event EventHandler DataReaderInvalidated; + + DataStream stream; + uint length; + string filename; + GCHandle gcHandle; + byte[] dataAry; + IntPtr data; + OSType osType; long origDataLength; + MemoryMappedDataReaderFactory(string filename) { + osType = OSType.Unknown; + this.filename = filename; + } + + ~MemoryMappedDataReaderFactory() { + Dispose(false); + } + + /// + /// Creates a data reader + /// + /// Offset of data + /// Length of data + /// + public override DataReader CreateReader(uint offset, uint length) => CreateReader(stream, offset, length); + + /// + /// Cleans up and frees all allocated memory + /// + public override void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + internal void SetLength(uint length) => this.length = length; + enum OSType : byte { Unknown, Windows, @@ -29,9 +72,8 @@ enum OSType : byte { [Serializable] sealed class MemoryMappedIONotSupportedException : IOException { - public MemoryMappedIONotSupportedException(string s) - : base(s) { - } + public MemoryMappedIONotSupportedException(string s) : base(s) { } + public MemoryMappedIONotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context) { } } static class Windows { @@ -62,26 +104,26 @@ static class Windows { const uint INVALID_FILE_SIZE = 0xFFFFFFFF; const int NO_ERROR = 0; - public static void Mmap(MemoryMappedFileStreamCreator creator, bool mapAsImage) { - using (var fileHandle = CreateFile(creator.theFileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)) { + public static void Mmap(MemoryMappedDataReaderFactory creator, bool mapAsImage) { + using (var fileHandle = CreateFile(creator.filename, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)) { if (fileHandle.IsInvalid) - throw new IOException(string.Format("Could not open file {0} for reading. Error: {1:X8}", creator.theFileName, Marshal.GetLastWin32Error())); + throw new IOException($"Could not open file {creator.filename} for reading. Error: {Marshal.GetLastWin32Error():X8}"); - uint sizeHi; - uint sizeLo = GetFileSize(fileHandle, out sizeHi); + uint sizeLo = GetFileSize(fileHandle, out uint sizeHi); int hr; if (sizeLo == INVALID_FILE_SIZE && (hr = Marshal.GetLastWin32Error()) != NO_ERROR) - throw new IOException(string.Format("Could not get file size. File: {0}, error: {1:X8}", creator.theFileName, hr)); + throw new IOException($"Could not get file size. File: {creator.filename}, error: {hr:X8}"); var fileSize = ((long)sizeHi << 32) | sizeLo; using (var fileMapping = CreateFileMapping(fileHandle, IntPtr.Zero, PAGE_READONLY | (mapAsImage ? SEC_IMAGE : 0), 0, 0, null)) { if (fileMapping.IsInvalid) - throw new MemoryMappedIONotSupportedException(string.Format("Could not create a file mapping object. File: {0}, error: {1:X8}", creator.theFileName, Marshal.GetLastWin32Error())); + throw new MemoryMappedIONotSupportedException($"Could not create a file mapping object. File: {creator.filename}, error: {Marshal.GetLastWin32Error():X8}"); creator.data = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, UIntPtr.Zero); if (creator.data == IntPtr.Zero) - throw new MemoryMappedIONotSupportedException(string.Format("Could not map file {0}. Error: {1:X8}", creator.theFileName, Marshal.GetLastWin32Error())); - creator.dataLength = fileSize; + throw new MemoryMappedIONotSupportedException($"Could not map file {creator.filename}. Error: {Marshal.GetLastWin32Error():X8}"); + creator.length = (uint)fileSize; creator.osType = OSType.Windows; + creator.stream = DataStreamFactory.Create((byte*)creator.data); } } } @@ -118,11 +160,11 @@ static class Unix { [DllImport("libc")] static extern int munmap(IntPtr addr, IntPtr length); - public static void Mmap(MemoryMappedFileStreamCreator creator, bool mapAsImage) { - int fd = open(creator.theFileName, O_RDONLY); + public static void Mmap(MemoryMappedDataReaderFactory creator, bool mapAsImage) { + int fd = open(creator.filename, O_RDONLY); try { if (fd < 0) - throw new IOException(string.Format("Could not open file {0} for reading. Error: {1}", creator.theFileName, fd)); + throw new IOException($"Could not open file {creator.filename} for reading. Error: {fd}"); long size; IntPtr data; @@ -130,26 +172,27 @@ public static void Mmap(MemoryMappedFileStreamCreator creator, bool mapAsImage) if (IntPtr.Size == 4) { size = lseek32(fd, 0, SEEK_END); if (size == -1) - throw new MemoryMappedIONotSupportedException(string.Format("Could not get length of {0} (lseek failed): {1}", creator.theFileName, Marshal.GetLastWin32Error())); + throw new MemoryMappedIONotSupportedException($"Could not get length of {creator.filename} (lseek failed): {Marshal.GetLastWin32Error()}"); data = mmap32(IntPtr.Zero, (IntPtr)size, PROT_READ, MAP_PRIVATE, fd, 0); if (data == new IntPtr(-1) || data == IntPtr.Zero) - throw new MemoryMappedIONotSupportedException(string.Format("Could not map file {0}. Error: {1}", creator.theFileName, Marshal.GetLastWin32Error())); + throw new MemoryMappedIONotSupportedException($"Could not map file {creator.filename}. Error: {Marshal.GetLastWin32Error()}"); } else { size = lseek64(fd, 0, SEEK_END); if (size == -1) - throw new MemoryMappedIONotSupportedException(string.Format("Could not get length of {0} (lseek failed): {1}", creator.theFileName, Marshal.GetLastWin32Error())); + throw new MemoryMappedIONotSupportedException($"Could not get length of {creator.filename} (lseek failed): {Marshal.GetLastWin32Error()}"); data = mmap64(IntPtr.Zero, (IntPtr)size, PROT_READ, MAP_PRIVATE, fd, 0); if (data == new IntPtr(-1) || data == IntPtr.Zero) - throw new MemoryMappedIONotSupportedException(string.Format("Could not map file {0}. Error: {1}", creator.theFileName, Marshal.GetLastWin32Error())); + throw new MemoryMappedIONotSupportedException($"Could not map file {creator.filename}. Error: {Marshal.GetLastWin32Error()}"); } creator.data = data; - creator.dataLength = size; - creator.origDataLength = creator.dataLength; + creator.length = (uint)size; + creator.origDataLength = size; creator.osType = OSType.Unix; + creator.stream = DataStreamFactory.Create((byte*)creator.data); } finally { if (fd >= 0) @@ -163,26 +206,14 @@ public static void Dispose(IntPtr addr, long size) { } } - static bool canTryWindows = true; - static bool canTryUnix = true; + static volatile bool canTryWindows = true; + static volatile bool canTryUnix = true; - /// - /// Creates a new if supported or returns - /// null if the OS functions aren't supported. - /// - /// If is true, then the created - /// that is used internally by the class, - /// can only access bytes up to the file size, not to the end of the mapped image. You must - /// set to the correct image length to access the full image. - /// Name of the file - /// true if we should map it as an executable - /// If we can't open/map the file - internal static MemoryMappedFileStreamCreator CreateWindows(string fileName, bool mapAsImage) { + internal static MemoryMappedDataReaderFactory CreateWindows(string filename, bool mapAsImage) { if (!canTryWindows) return null; - var creator = new MemoryMappedFileStreamCreator(); - creator.theFileName = GetFullPath(fileName); + var creator = new MemoryMappedDataReaderFactory(GetFullPath(filename)); try { Windows.Mmap(creator, mapAsImage); return creator; @@ -195,23 +226,11 @@ internal static MemoryMappedFileStreamCreator CreateWindows(string fileName, boo return null; } - /// - /// Creates a new if supported or returns - /// null if the OS functions aren't supported. - /// - /// If is true, then the created - /// that is used internally by the class, - /// can only access bytes up to the file size, not to the end of the mapped image. You must - /// set to the correct image length to access the full image. - /// Name of the file - /// NOT SUPPORTED. true if we should map it as an executable - /// If we can't open/map the file - internal static MemoryMappedFileStreamCreator CreateUnix(string fileName, bool mapAsImage) { + internal static MemoryMappedDataReaderFactory CreateUnix(string filename, bool mapAsImage) { if (!canTryUnix) return null; - var creator = new MemoryMappedFileStreamCreator(); - creator.theFileName = GetFullPath(fileName); + var creator = new MemoryMappedDataReaderFactory(GetFullPath(filename)); try { Unix.Mmap(creator, mapAsImage); if (mapAsImage) { // Only check this if we know that mmap() works, i.e., if above call succeeds @@ -221,7 +240,7 @@ internal static MemoryMappedFileStreamCreator CreateUnix(string fileName, bool m return creator; } catch (MemoryMappedIONotSupportedException ex) { - Debug.WriteLine(string.Format("mmap'd IO didn't work: {0}", ex.Message)); + Debug.WriteLine($"mmap'd IO didn't work: {ex.Message}"); } catch (EntryPointNotFoundException) { } @@ -231,58 +250,53 @@ internal static MemoryMappedFileStreamCreator CreateUnix(string fileName, bool m return null; } - static string GetFullPath(string fileName) { + static string GetFullPath(string filename) { try { - return Path.GetFullPath(fileName); + return Path.GetFullPath(filename); } catch { - return fileName; + return filename; } } - /// - ~MemoryMappedFileStreamCreator() { - Dispose(false); - } - - /// - protected override void Dispose(bool disposing) { + void Dispose(bool disposing) { FreeMemoryMappedIoData(); - base.Dispose(disposing); + if (disposing) { + length = 0; + stream = EmptyDataStream.Instance; + data = IntPtr.Zero; + filename = null; + } } /// /// true if memory mapped I/O is enabled /// - public bool IsMemoryMappedIO { - get { return dataAry == null; } - } + internal bool IsMemoryMappedIO => dataAry is null; /// /// Call this to disable memory mapped I/O. This must only be called if no other code is /// trying to access the memory since that could lead to an exception. /// - public void UnsafeDisableMemoryMappedIO() { - if (dataAry != null) + internal void UnsafeDisableMemoryMappedIO() { + if (dataAry is not null) return; - if (unsafeUseAddress) - throw new InvalidOperationException("Can't convert to non-memory mapped I/O because the PDB reader uses the address. Use the managed PDB reader instead."); - var newAry = new byte[Length]; + var newAry = new byte[length]; Marshal.Copy(data, newAry, 0, newAry.Length); FreeMemoryMappedIoData(); - dataLength = newAry.Length; + length = (uint)newAry.Length; dataAry = newAry; gcHandle = GCHandle.Alloc(dataAry, GCHandleType.Pinned); - this.data = gcHandle.AddrOfPinnedObject(); + data = gcHandle.AddrOfPinnedObject(); + stream = DataStreamFactory.Create((byte*)data); + DataReaderInvalidated?.Invoke(this, EventArgs.Empty); } - GCHandle gcHandle; - byte[] dataAry; void FreeMemoryMappedIoData() { - if (dataAry == null) { + if (dataAry is null) { var origData = Interlocked.Exchange(ref data, IntPtr.Zero); if (origData != IntPtr.Zero) { - dataLength = 0; + length = 0; switch (osType) { case OSType.Windows: Windows.Dispose(origData); diff --git a/src/IO/MemoryStreamCreator.cs b/src/IO/MemoryStreamCreator.cs deleted file mode 100644 index 0fd2750eb..000000000 --- a/src/IO/MemoryStreamCreator.cs +++ /dev/null @@ -1,82 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Diagnostics; -using System.IO; - -namespace dnlib.IO { - /// - /// Creates s to partially access a byte[] - /// - /// - [DebuggerDisplay("byte[]: O:{dataOffset} L:{dataLength} {theFileName}")] - sealed class MemoryStreamCreator : IImageStreamCreator { - byte[] data; - int dataOffset; - int dataLength; - string theFileName; - - /// - /// The file name - /// - public string FileName { - get { return theFileName; } - set { theFileName = value; } - } - - /// - public long Length { - get { return dataLength; } - } - - /// - /// Constructor - /// - /// The data - public MemoryStreamCreator(byte[] data) - : this(data, 0, data.Length) { - } - - /// - /// Constructor - /// - /// The data - /// Start offset in - /// Length of data starting from - /// If one of the args is invalid - public MemoryStreamCreator(byte[] data, int offset, int length) { - if (offset < 0) - throw new ArgumentOutOfRangeException("offset"); - if (length < 0 || offset + length < offset) - throw new ArgumentOutOfRangeException("length"); - if (offset + length > data.Length) - throw new ArgumentOutOfRangeException("length"); - this.data = data; - this.dataOffset = offset; - this.dataLength = length; - } - - /// - public IImageStream Create(FileOffset offset, long length) { - if (offset < 0 || length < 0) - return MemoryImageStream.CreateEmpty(); - - int offs = (int)Math.Min((long)dataLength, (long)offset); - int len = (int)Math.Min((long)dataLength - offs, length); - return new MemoryImageStream(offset, data, dataOffset + offs, len); - } - - /// - public IImageStream CreateFull() { - return new MemoryImageStream(0, data, dataOffset, dataLength); - } - - /// - public void Dispose() { - data = null; - dataOffset = 0; - dataLength = 0; - theFileName = null; - } - } -} diff --git a/src/IO/NativeMemoryDataReaderFactory.cs b/src/IO/NativeMemoryDataReaderFactory.cs new file mode 100644 index 000000000..c2746e025 --- /dev/null +++ b/src/IO/NativeMemoryDataReaderFactory.cs @@ -0,0 +1,62 @@ +// dnlib: See LICENSE.txt for more info + +using System; + +namespace dnlib.IO { + /// + /// Creates s that read native memory + /// + public sealed unsafe class NativeMemoryDataReaderFactory : DataReaderFactory { + /// + /// The filename or null if the data is not from a file + /// + public override string Filename => filename; + + /// + /// Gets the total length of the data + /// + public override uint Length => length; + + DataStream stream; + string filename; + uint length; + + NativeMemoryDataReaderFactory(byte* data, uint length, string filename) { + this.filename = filename; + this.length = length; + stream = DataStreamFactory.Create(data); + } + + internal void SetLength(uint length) => this.length = length; + + /// + /// Creates a instance + /// + /// Pointer to data + /// Length of data + /// The filename or null if the data is not from a file + /// + public static NativeMemoryDataReaderFactory Create(byte* data, uint length, string filename) { + if (data is null) + throw new ArgumentNullException(nameof(data)); + return new NativeMemoryDataReaderFactory(data, length, filename); + } + + /// + /// Creates a data reader + /// + /// Offset of data + /// Length of data + /// + public override DataReader CreateReader(uint offset, uint length) => CreateReader(stream, offset, length); + + /// + /// This method doesn't need to be called since this instance doesn't own the native memory + /// + public override void Dispose() { + stream = EmptyDataStream.Instance; + length = 0; + filename = null; + } + } +} diff --git a/src/IO/UnalignedByteArrayDataStream.cs b/src/IO/UnalignedByteArrayDataStream.cs new file mode 100644 index 000000000..9b7d49723 --- /dev/null +++ b/src/IO/UnalignedByteArrayDataStream.cs @@ -0,0 +1,105 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace dnlib.IO { + sealed unsafe class UnalignedByteArrayDataStream : DataStream { + readonly byte[] data; + + public UnalignedByteArrayDataStream(byte[] data) => this.data = data; + + public override void ReadBytes(uint offset, void* destination, int length) => + Marshal.Copy(data, (int)offset, (IntPtr)destination, length); + + public override void ReadBytes(uint offset, byte[] destination, int destinationIndex, int length) => + Array.Copy(data, (int)offset, destination, destinationIndex, length); + + public override byte ReadByte(uint offset) => data[(int)offset]; + + public override ushort ReadUInt16(uint offset) { + int i = (int)offset; + var data = this.data; + return (ushort)(data[i++] | (data[i] << 8)); + } + + public override uint ReadUInt32(uint offset) { + fixed (byte* p = data) + return *(uint*)(p + offset); + } + + public override ulong ReadUInt64(uint offset) { + fixed (byte* p = data) + return *(ulong*)(p + offset); + } + + public override float ReadSingle(uint offset) { + fixed (byte* p = data) + return *(float*)(p + offset); + } + + public override double ReadDouble(uint offset) { + fixed (byte* p = data) + return *(double*)(p + offset); + } + + public override Guid ReadGuid(uint offset) { + fixed (byte* p = data) + return *(Guid*)(p + offset); + } + + public override string ReadUtf16String(uint offset, int chars) { + fixed (byte* p = data) + return new string((char*)(p + offset), 0, chars); + } + + public override string ReadString(uint offset, int length, Encoding encoding) { + fixed (byte* p = data) + return new string((sbyte*)(p + offset), 0, length, encoding); + } + + public override bool TryGetOffsetOf(uint offset, uint endOffset, byte value, out uint valueOffset) { + fixed (byte* pd = data) { + // If this code gets updated, also update the other DataStream implementations + + byte* p = pd + offset; + + uint count = (endOffset - offset) / 4; + for (uint i = 0; i < count; i++) { + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + } + + byte* pe = pd + endOffset; + while (p != pe) { + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + } + valueOffset = 0; + return false; + } + } + } +} diff --git a/src/IO/UnalignedNativeMemoryDataStream.cs b/src/IO/UnalignedNativeMemoryDataStream.cs new file mode 100644 index 000000000..3cbffd82e --- /dev/null +++ b/src/IO/UnalignedNativeMemoryDataStream.cs @@ -0,0 +1,84 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace dnlib.IO { + sealed unsafe class UnalignedNativeMemoryDataStream : DataStream { + readonly byte* data; + + public UnalignedNativeMemoryDataStream(byte* data) => this.data = data; + + public override void ReadBytes(uint offset, void* destination, int length) { + var ps = data + offset; + var pd = (byte*)destination; + int count = length / 4; + length = length % 4; + for (int i = 0; i < count; i++) { + //TODO: Align one of the pointers. destination is more likely to be aligned + *(uint*)pd = *(uint*)ps; + pd += 4; + ps += 4; + } + for (int i = 0; i < length; i++, ps++, pd++) + *pd = *ps; + } + + public override void ReadBytes(uint offset, byte[] destination, int destinationIndex, int length) => + Marshal.Copy((IntPtr)(data + offset), destination, destinationIndex, length); + + public override byte ReadByte(uint offset) => *(data + offset); + public override ushort ReadUInt16(uint offset) => *(ushort*)(data + offset); + public override uint ReadUInt32(uint offset) => *(uint*)(data + offset); + public override ulong ReadUInt64(uint offset) => *(ulong*)(data + offset); + public override float ReadSingle(uint offset) => *(float*)(data + offset); + public override double ReadDouble(uint offset) => *(double*)(data + offset); + public override Guid ReadGuid(uint offset) => *(Guid*)(data + offset); + public override string ReadUtf16String(uint offset, int chars) => new string((char*)(data + offset), 0, chars); + public override string ReadString(uint offset, int length, Encoding encoding) => new string((sbyte*)(data + offset), 0, length, encoding); + + public override bool TryGetOffsetOf(uint offset, uint endOffset, byte value, out uint valueOffset) { + var pd = data; + + // If this code gets updated, also update the other DataStream implementations + + byte* p = pd + offset; + + uint count = (endOffset - offset) / 4; + for (uint i = 0; i < count; i++) { + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + } + + byte* pe = pd + endOffset; + while (p != pe) { + if (*p == value) { + valueOffset = (uint)(p - pd); + return true; + } + p++; + } + valueOffset = 0; + return false; + } + } +} diff --git a/src/IO/UnmanagedMemoryImageStream.cs b/src/IO/UnmanagedMemoryImageStream.cs deleted file mode 100644 index 4b62cd901..000000000 --- a/src/IO/UnmanagedMemoryImageStream.cs +++ /dev/null @@ -1,234 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; - -namespace dnlib.IO { - /// - /// IImageStream for unmanaged memory - /// - [DebuggerDisplay("FO:{fileOffset} S:{Length} A:{startAddr}")] - sealed unsafe class UnmanagedMemoryImageStream : IImageStream { - FileOffset fileOffset; - long startAddr; - long endAddr; - long currentAddr; - UnmanagedMemoryStreamCreator owner; - bool ownOwner; - - /// - /// Constructor - /// - /// Owner of memory - /// File offset of data - /// Address of data - /// Length of data - public unsafe UnmanagedMemoryImageStream(UnmanagedMemoryStreamCreator owner, FileOffset fileOffset, long baseAddr, long length) { - this.fileOffset = fileOffset; - this.startAddr = baseAddr; - this.endAddr = baseAddr + length; - this.currentAddr = this.startAddr; - this.owner = owner; - } - - /// - /// Saves in this instance so it doesn't get garbage collected. - /// - /// A instance - internal UnmanagedMemoryImageStream(UnmanagedMemoryStreamCreator creator) - : this(creator, 0, 0, creator.Length) { - this.ownOwner = true; - } - - /// - public FileOffset FileOffset { - get { return fileOffset; } - } - - /// - /// Gets the start address of the memory this instance uses - /// - internal unsafe IntPtr StartAddress { - get { return new IntPtr((byte*)owner.UnsafeUseAddress + startAddr); } - } - - /// - public unsafe long Length { - get { return endAddr - startAddr; } - } - - /// - public unsafe long Position { - get { return currentAddr - startAddr; } - set { - if (IntPtr.Size == 4 && (ulong)value > int.MaxValue) - value = int.MaxValue; - long newAddr = startAddr + value; - if (newAddr < startAddr) - newAddr = endAddr; - currentAddr = newAddr; - } - } - - /// - public unsafe IImageStream Create(FileOffset offset, long length) { - if ((long)offset < 0 || length < 0) - return MemoryImageStream.CreateEmpty(); - - long offs = Math.Min(Length, (long)offset); - long len = Math.Min(Length - offs, length); - return new UnmanagedMemoryImageStream(owner, (FileOffset)((long)fileOffset + (long)offset), startAddr + (long)offs, len); - } - - /// - public unsafe byte[] ReadBytes(int size) { - if (size < 0) - throw new IOException("Invalid size"); - size = (int)Math.Min(size, Length - Math.Min(Length, Position)); - var newData = new byte[size]; - Marshal.Copy(new IntPtr((byte*)owner.Address + currentAddr), newData, 0, size); - currentAddr += size; - return newData; - } - - /// - public int Read(byte[] buffer, int offset, int length) { - if (length < 0) - throw new IOException("Invalid size"); - length = (int)Math.Min(length, Length - Math.Min(Length, Position)); - Marshal.Copy(new IntPtr((byte*)owner.Address + currentAddr), buffer, offset, length); - currentAddr += length; - return length; - } - - /// - public byte[] ReadBytesUntilByte(byte b) { - long pos = GetPositionOf(b); - if (pos == -1) - return null; - return ReadBytes((int)(pos - currentAddr)); - } - - unsafe long GetPositionOf(byte b) { - byte* pos = (byte*)owner.Address + currentAddr; - byte* posStart = pos; - var endPos = (byte*)owner.Address + endAddr; - while (pos < endPos) { - if (*pos == b) - return currentAddr + (pos - posStart); - pos++; - } - return -1; - } - - /// - public unsafe sbyte ReadSByte() { - if (currentAddr >= endAddr) - throw new IOException("Can't read one SByte"); - return (sbyte)*((byte*)owner.Address + currentAddr++); - } - - /// - public unsafe byte ReadByte() { - if (currentAddr >= endAddr) - throw new IOException("Can't read one Byte"); - return *((byte*)owner.Address + currentAddr++); - } - - /// - public unsafe short ReadInt16() { - if (currentAddr + 1 >= endAddr) - throw new IOException("Can't read one Int16"); - short val = *(short*)((byte*)owner.Address + currentAddr); - currentAddr += 2; - return val; - } - - /// - public unsafe ushort ReadUInt16() { - if (currentAddr + 1 >= endAddr) - throw new IOException("Can't read one UInt16"); - ushort val = *(ushort*)((byte*)owner.Address + currentAddr); - currentAddr += 2; - return val; - } - - /// - public unsafe int ReadInt32() { - if (currentAddr + 3 >= endAddr) - throw new IOException("Can't read one Int32"); - int val = *(int*)((byte*)owner.Address + currentAddr); - currentAddr += 4; - return val; - } - - /// - public unsafe uint ReadUInt32() { - if (currentAddr + 3 >= endAddr) - throw new IOException("Can't read one UInt32"); - uint val = *(uint*)((byte*)owner.Address + currentAddr); - currentAddr += 4; - return val; - } - - /// - public unsafe long ReadInt64() { - if (currentAddr + 7 >= endAddr) - throw new IOException("Can't read one Int64"); - long val = *(long*)((byte*)owner.Address + currentAddr); - currentAddr += 8; - return val; - } - - /// - public unsafe ulong ReadUInt64() { - if (currentAddr + 7 >= endAddr) - throw new IOException("Can't read one UInt64"); - ulong val = *(ulong*)((byte*)owner.Address + currentAddr); - currentAddr += 8; - return val; - } - - /// - public unsafe float ReadSingle() { - if (currentAddr + 3 >= endAddr) - throw new IOException("Can't read one Single"); - var val = *(float*)((byte*)owner.Address + currentAddr); - currentAddr += 4; - return val; - } - - /// - public unsafe double ReadDouble() { - if (currentAddr + 7 >= endAddr) - throw new IOException("Can't read one Double"); - var val = *(double*)((byte*)owner.Address + currentAddr); - currentAddr += 8; - return val; - } - - /// - public unsafe string ReadString(int chars) { - if (IntPtr.Size == 4 && (uint)chars > (uint)int.MaxValue) - throw new IOException("Not enough space to read the string"); - if (currentAddr + chars * 2 < currentAddr || (chars != 0 && currentAddr + chars * 2 - 1 >= endAddr)) - throw new IOException("Not enough space to read the string"); - var s = new string((char*)((byte*)owner.Address + currentAddr), 0, chars); - currentAddr += chars * 2; - return s; - } - - /// - public void Dispose() { - fileOffset = 0; - startAddr = 0; - endAddr = 0; - currentAddr = 0; - if (ownOwner && owner != null) - owner.Dispose(); - owner = null; - } - } -} diff --git a/src/IO/UnmanagedMemoryStreamCreator.cs b/src/IO/UnmanagedMemoryStreamCreator.cs deleted file mode 100644 index e0edee2a2..000000000 --- a/src/IO/UnmanagedMemoryStreamCreator.cs +++ /dev/null @@ -1,121 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Diagnostics; -using System.IO; - -namespace dnlib.IO { - /// - /// Creates s to partially access an - /// unmanaged memory range - /// - /// - [DebuggerDisplay("mem: D:{data} L:{dataLength} {theFileName}")] - class UnmanagedMemoryStreamCreator : IImageStreamCreator { - /// - /// Address of data - /// - protected IntPtr data; - - /// - /// Length of data - /// - protected long dataLength; - - /// - /// Name of file - /// - protected string theFileName; - - /// - /// The file name - /// - public string FileName { - get { return theFileName; } - set { theFileName = value; } - } - - /// - /// Size of the data - /// - public long Length { - get { return dataLength; } - set { dataLength = value; } - } - - /// - /// Returns the base address of the data - /// - public IntPtr Address { - get { return data; } - } - - public IntPtr UnsafeUseAddress { - get { - unsafeUseAddress = true; - return data; - } - } - protected bool unsafeUseAddress; - - /// - /// Default constructor - /// - protected UnmanagedMemoryStreamCreator() { - } - - /// - /// Constructor for 0 bytes of data - /// - /// Pointer to the data - public UnmanagedMemoryStreamCreator(IntPtr data) - : this(data, 0) { - } - - /// - /// Constructor - /// - /// Pointer to the data - /// Length of data - /// If one of the args is invalid - public UnmanagedMemoryStreamCreator(IntPtr data, long dataLength) { - if (dataLength < 0) - throw new ArgumentOutOfRangeException("dataLength"); - this.data = data; - this.dataLength = dataLength; - } - - /// - public unsafe IImageStream Create(FileOffset offset, long length) { - if (offset < 0 || length < 0) - return MemoryImageStream.CreateEmpty(); - - long offs = Math.Min((long)dataLength, (long)offset); - long len = Math.Min((long)dataLength - offs, length); - return new UnmanagedMemoryImageStream(this, offset, offs, len); - } - - /// - public IImageStream CreateFull() { - return new UnmanagedMemoryImageStream(this, 0, 0, dataLength); - } - - /// - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose method - /// - /// true if called by - protected virtual void Dispose(bool disposing) { - if (disposing) { - data = IntPtr.Zero; - dataLength = 0; - theFileName = null; - } - } - } -} diff --git a/src/PE/Characteristics.cs b/src/PE/Characteristics.cs index 134506414..9ec6fe0da 100644 --- a/src/PE/Characteristics.cs +++ b/src/PE/Characteristics.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.PE { /// @@ -25,7 +25,7 @@ public enum Characteristics : ushort { /// Bytes of machine word are reversed. BytesReversedLo = 0x0080, /// 32 bit word machine. - _32BitMachine = 0x0100, + Bit32Machine = 0x0100, /// Debugging info stripped from file in .DBG file DebugStripped = 0x0200, /// If Image is on removable media, copy and run from the swap file. diff --git a/src/PE/DllCharacteristics.cs b/src/PE/DllCharacteristics.cs index a8bcfc6ac..adc4745f4 100644 --- a/src/PE/DllCharacteristics.cs +++ b/src/PE/DllCharacteristics.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.PE { /// diff --git a/src/PE/IImageOptionalHeader.cs b/src/PE/IImageOptionalHeader.cs index 21ea4b480..1fc97d281 100644 --- a/src/PE/IImageOptionalHeader.cs +++ b/src/PE/IImageOptionalHeader.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using dnlib.IO; +using dnlib.IO; namespace dnlib.PE { /// @@ -158,7 +158,7 @@ public interface IImageOptionalHeader : IFileSection { uint NumberOfRvaAndSizes { get; } /// - /// Returns the DataDirectories field + /// Returns the DataDirectories field. This array contains exactly 16 elements. /// ImageDataDirectory[] DataDirectories { get; } } diff --git a/src/PE/IPEImage.cs b/src/PE/IPEImage.cs index 873ed7124..744c2552e 100644 --- a/src/PE/IPEImage.cs +++ b/src/PE/IPEImage.cs @@ -11,14 +11,14 @@ namespace dnlib.PE { /// public interface IRvaFileOffsetConverter { /// - /// Converts a to an + /// Converts a to an , returns 0 if out of range /// /// The file offset to convert /// The RVA RVA ToRVA(FileOffset offset); /// - /// Converts an to a + /// Converts an to a , returns 0 if out of range /// /// The RVA to convert /// The file offset @@ -42,9 +42,9 @@ public interface IPEImage : IRvaFileOffsetConverter, IDisposable { bool MayHaveInvalidAddresses { get; } /// - /// The file name or null if data is not from a file + /// The filename or null if the data is not from a file /// - string FileName { get; } + string Filename { get; } /// /// Returns the DOS header @@ -61,86 +61,76 @@ public interface IPEImage : IRvaFileOffsetConverter, IDisposable { /// IList ImageSectionHeaders { get; } + /// + /// Returns the debug directories + /// + IList ImageDebugDirectories { get; } + /// /// Gets/sets the Win32 resources. This is null if there are no Win32 resources. /// Win32Resources Win32Resources { get; set; } /// - /// Creates a stream to access part of the PE image from - /// to the end of the image + /// Gets the factory /// - /// File offset - /// A new stream - /// If the arg is invalid - IImageStream CreateStream(FileOffset offset); + DataReaderFactory DataReaderFactory { get; } /// - /// Creates a stream to access part of the PE image from - /// with length + /// Creates a from to the end of the image /// - /// File offset - /// Length of data - /// A new stream - /// If any arg is invalid - IImageStream CreateStream(FileOffset offset, long length); + /// Offset of data + /// + DataReader CreateReader(FileOffset offset); /// - /// Creates a stream to access the full PE image + /// Creates a /// - /// A new stream - IImageStream CreateFullStream(); + /// Offset of data + /// Length of data + /// + DataReader CreateReader(FileOffset offset, uint length); /// - /// Call this to disable memory mapped I/O if it was used to open the file. This must only - /// be called if no other code is trying to access the memory since that could lead to an - /// exception. + /// Creates a from to the end of the image /// - void UnsafeDisableMemoryMappedIO(); + /// RVA of data + /// + DataReader CreateReader(RVA rva); /// - /// true if memory mapped I/O is enabled + /// Creates a /// - bool IsMemoryMappedIO { get; } - } + /// RVA of data + /// Length of data + /// + DataReader CreateReader(RVA rva, uint length); - public static partial class PEExtensions { /// - /// Creates a stream to access part of the PE image from - /// to the end of the image + /// Creates a that can read the whole image /// - /// this - /// RVA - /// A new stream - /// If the arg is invalid - public static IImageStream CreateStream(this IPEImage self, RVA rva) { - return self.CreateStream(self.ToFileOffset(rva)); - } + /// + DataReader CreateReader(); + } + /// + /// Interface to access a PE image + /// + public interface IInternalPEImage : IPEImage { /// - /// Creates a stream to access part of the PE image from - /// with length + /// Call this to disable memory mapped I/O if it was used to open the file. This must only + /// be called if no other code is trying to access the memory since that could lead to an + /// exception. /// - /// this - /// RVA - /// Length of data - /// A new stream - /// If any arg is invalid - public static IImageStream CreateStream(this IPEImage self, RVA rva, long length) { - return self.CreateStream(self.ToFileOffset(rva), length); - } + void UnsafeDisableMemoryMappedIO(); /// - /// Reads all bytes from the PE image. This may fail if the PE image has been loaded - /// by the OS loader since there may be memory holes. + /// true if memory mapped I/O is enabled /// - /// this - /// All bytes of the PE image - public static byte[] GetImageAsByteArray(this IPEImage self) { - using (var reader = self.CreateFullStream()) - return reader.ReadAllBytes(); - } + bool IsMemoryMappedIO { get; } + } + public static partial class PEExtensions { /// /// Finds a /// @@ -149,9 +139,7 @@ public static byte[] GetImageAsByteArray(this IPEImage self) { /// Name /// Language ID /// The or null if none found - public static ResourceData FindWin32ResourceData(this IPEImage self, ResourceName type, ResourceName name, ResourceName langId) { - var w32Resources = self.Win32Resources; - return w32Resources == null ? null : w32Resources.Find(type, name, langId); - } + public static ResourceData FindWin32ResourceData(this IPEImage self, ResourceName type, ResourceName name, ResourceName langId) => + self.Win32Resources?.Find(type, name, langId); } } diff --git a/src/PE/IPEType.cs b/src/PE/IPEType.cs index c40f520a4..8846e40a6 100644 --- a/src/PE/IPEType.cs +++ b/src/PE/IPEType.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using dnlib.IO; +using dnlib.IO; namespace dnlib.PE { /// @@ -8,7 +8,7 @@ namespace dnlib.PE { /// interface IPEType { /// - /// Converts a to an + /// Converts a to an , returns 0 if out of range /// /// The PEInfo context /// The file offset to convert @@ -16,7 +16,7 @@ interface IPEType { RVA ToRVA(PEInfo peInfo, FileOffset offset); /// - /// Converts an to a + /// Converts an to a , returns 0 if out of range /// /// The PEInfo context /// The RVA to convert diff --git a/src/PE/ImageDataDirectory.cs b/src/PE/ImageDataDirectory.cs index 27c0f6f12..17a118e24 100644 --- a/src/PE/ImageDataDirectory.cs +++ b/src/PE/ImageDataDirectory.cs @@ -16,16 +16,12 @@ public sealed class ImageDataDirectory : FileSection { /// /// Returns the IMAGE_DATA_DIRECTORY.VirtualAddress field /// - public RVA VirtualAddress { - get { return virtualAddress; } - } + public RVA VirtualAddress => virtualAddress; /// /// Returns the IMAGE_DATA_DIRECTORY.Size field /// - public uint Size { - get { return dataSize; } - } + public uint Size => dataSize; /// /// Default constructor @@ -39,11 +35,11 @@ public ImageDataDirectory() { /// PE file reader pointing to the start of this section /// Verify section /// Thrown if verification fails - public ImageDataDirectory(IImageStream reader, bool verify) { - SetStartOffset(reader); - this.virtualAddress = (RVA)reader.ReadUInt32(); - this.dataSize = reader.ReadUInt32(); - SetEndoffset(reader); + public ImageDataDirectory(ref DataReader reader, bool verify) { + SetStartOffset(ref reader); + virtualAddress = (RVA)reader.ReadUInt32(); + dataSize = reader.ReadUInt32(); + SetEndoffset(ref reader); } } } diff --git a/src/PE/ImageDebugDirectory.cs b/src/PE/ImageDebugDirectory.cs new file mode 100644 index 000000000..2769cfe3a --- /dev/null +++ b/src/PE/ImageDebugDirectory.cs @@ -0,0 +1,81 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Diagnostics; +using dnlib.IO; + +namespace dnlib.PE { + /// + /// A IMAGE_DEBUG_DIRECTORY + /// + [DebuggerDisplay("{type}: TS:{timeDateStamp,h} V:{majorVersion,d}.{minorVersion,d} SZ:{sizeOfData} RVA:{addressOfRawData,h} FO:{pointerToRawData,h}")] + public sealed class ImageDebugDirectory : FileSection { + readonly uint characteristics; + readonly uint timeDateStamp; + readonly ushort majorVersion; + readonly ushort minorVersion; + readonly ImageDebugType type; + readonly uint sizeOfData; + readonly uint addressOfRawData; + readonly uint pointerToRawData; + + /// + /// Gets the characteristics (reserved) + /// + public uint Characteristics => characteristics; + + /// + /// Gets the timestamp + /// + public uint TimeDateStamp => timeDateStamp; + + /// + /// Gets the major version + /// + public ushort MajorVersion => majorVersion; + + /// + /// Gets the minor version + /// + public ushort MinorVersion => minorVersion; + + /// + /// Gets the type + /// + public ImageDebugType Type => type; + + /// + /// Gets the size of data + /// + public uint SizeOfData => sizeOfData; + + /// + /// RVA of the data + /// + public RVA AddressOfRawData => (RVA)addressOfRawData; + + /// + /// File offset of the data + /// + public FileOffset PointerToRawData => (FileOffset)pointerToRawData; + + /// + /// Constructor + /// + /// PE file reader pointing to the start of this section + /// Verify section + /// Thrown if verification fails + public ImageDebugDirectory(ref DataReader reader, bool verify) { + SetStartOffset(ref reader); + characteristics = reader.ReadUInt32(); + timeDateStamp = reader.ReadUInt32(); + majorVersion = reader.ReadUInt16(); + minorVersion = reader.ReadUInt16(); + type = (ImageDebugType)reader.ReadUInt32(); + sizeOfData = reader.ReadUInt32(); + addressOfRawData = reader.ReadUInt32(); + pointerToRawData = reader.ReadUInt32(); + SetEndoffset(ref reader); + } + } +} diff --git a/src/PE/ImageDebugType.cs b/src/PE/ImageDebugType.cs new file mode 100644 index 000000000..bbb1306ae --- /dev/null +++ b/src/PE/ImageDebugType.cs @@ -0,0 +1,47 @@ +// dnlib: See LICENSE.txt for more info + +namespace dnlib.PE { + /// + /// Image debug type, see IMAGE_DEBUG_TYPE_* in winnt.n + /// + public enum ImageDebugType : uint { +#pragma warning disable 1591 // Missing XML comment for publicly visible type or member + Unknown = 0, + Coff = 1, + + /// + /// Contains PDB info + /// + CodeView = 2, + + FPO = 3, + Misc = 4, + Exception = 5, + Fixup = 6, + OmapToSrc = 7, + OmapFromSrc = 8, + Borland = 9, + Reserved10 = 10, + CLSID = 11, + VcFeature = 12, + POGO = 13, + ILTCG = 14, + MPX = 15, + + /// + /// It's a deterministic (reproducible) PE file + /// + Reproducible = 16, + + /// + /// Embedded portable PDB data + /// + EmbeddedPortablePdb = 17, + + /// + /// Checksum of the PDB file. 0 or more entries allowed. + /// + PdbChecksum = 19, +#pragma warning restore 1591 // Missing XML comment for publicly visible type or member + } +} diff --git a/src/PE/ImageDosHeader.cs b/src/PE/ImageDosHeader.cs index 467275fd7..79fa3088b 100644 --- a/src/PE/ImageDosHeader.cs +++ b/src/PE/ImageDosHeader.cs @@ -13,9 +13,7 @@ public sealed class ImageDosHeader : FileSection { /// /// File offset of the NT headers /// - public uint NTHeadersOffset { - get { return ntHeadersOffset; } - } + public uint NTHeadersOffset => ntHeadersOffset; /// /// Constructor @@ -23,14 +21,14 @@ public uint NTHeadersOffset { /// PE file reader /// Verify section /// Thrown if verification fails - public ImageDosHeader(IImageStream reader, bool verify) { - SetStartOffset(reader); + public ImageDosHeader(ref DataReader reader, bool verify) { + SetStartOffset(ref reader); ushort sig = reader.ReadUInt16(); if (verify && sig != 0x5A4D) throw new BadImageFormatException("Invalid DOS signature"); - reader.Position = (long)startOffset + 0x3C; - this.ntHeadersOffset = reader.ReadUInt32(); - SetEndoffset(reader); + reader.Position = (uint)startOffset + 0x3C; + ntHeadersOffset = reader.ReadUInt32(); + SetEndoffset(ref reader); } } } diff --git a/src/PE/ImageFileHeader.cs b/src/PE/ImageFileHeader.cs index e3cc14e81..d4a9c710c 100644 --- a/src/PE/ImageFileHeader.cs +++ b/src/PE/ImageFileHeader.cs @@ -19,51 +19,37 @@ public sealed class ImageFileHeader : FileSection { /// /// Returns the IMAGE_FILE_HEADER.Machine field /// - public Machine Machine { - get { return machine; } - } + public Machine Machine => machine; /// /// Returns the IMAGE_FILE_HEADER.NumberOfSections field /// - public int NumberOfSections { - get { return numberOfSections; } - } + public int NumberOfSections => numberOfSections; /// /// Returns the IMAGE_FILE_HEADER.TimeDateStamp field /// - public uint TimeDateStamp { - get { return timeDateStamp; } - } + public uint TimeDateStamp => timeDateStamp; /// /// Returns the IMAGE_FILE_HEADER.PointerToSymbolTable field /// - public uint PointerToSymbolTable { - get { return pointerToSymbolTable; } - } + public uint PointerToSymbolTable => pointerToSymbolTable; /// /// Returns the IMAGE_FILE_HEADER.NumberOfSymbols field /// - public uint NumberOfSymbols { - get { return numberOfSymbols; } - } + public uint NumberOfSymbols => numberOfSymbols; /// /// Returns the IMAGE_FILE_HEADER.SizeOfOptionalHeader field /// - public uint SizeOfOptionalHeader { - get { return sizeOfOptionalHeader; } - } + public uint SizeOfOptionalHeader => sizeOfOptionalHeader; /// /// Returns the IMAGE_FILE_HEADER.Characteristics field /// - public Characteristics Characteristics { - get { return characteristics; } - } + public Characteristics Characteristics => characteristics; /// /// Constructor @@ -71,17 +57,17 @@ public Characteristics Characteristics { /// PE file reader pointing to the start of this section /// Verify section /// Thrown if verification fails - public ImageFileHeader(IImageStream reader, bool verify) { - SetStartOffset(reader); - this.machine = (Machine)reader.ReadUInt16(); - this.numberOfSections = reader.ReadUInt16(); - this.timeDateStamp = reader.ReadUInt32(); - this.pointerToSymbolTable = reader.ReadUInt32(); - this.numberOfSymbols = reader.ReadUInt32(); - this.sizeOfOptionalHeader = reader.ReadUInt16(); - this.characteristics = (Characteristics)reader.ReadUInt16(); - SetEndoffset(reader); - if (verify && this.sizeOfOptionalHeader == 0) + public ImageFileHeader(ref DataReader reader, bool verify) { + SetStartOffset(ref reader); + machine = (Machine)reader.ReadUInt16(); + numberOfSections = reader.ReadUInt16(); + timeDateStamp = reader.ReadUInt32(); + pointerToSymbolTable = reader.ReadUInt32(); + numberOfSymbols = reader.ReadUInt32(); + sizeOfOptionalHeader = reader.ReadUInt16(); + characteristics = (Characteristics)reader.ReadUInt16(); + SetEndoffset(ref reader); + if (verify && sizeOfOptionalHeader == 0) throw new BadImageFormatException("Invalid SizeOfOptionalHeader"); } } diff --git a/src/PE/ImageNTHeaders.cs b/src/PE/ImageNTHeaders.cs index 9e1b941ee..87404e5f1 100644 --- a/src/PE/ImageNTHeaders.cs +++ b/src/PE/ImageNTHeaders.cs @@ -15,23 +15,17 @@ public sealed class ImageNTHeaders : FileSection { /// /// Returns the IMAGE_NT_HEADERS.Signature field /// - public uint Signature { - get { return signature; } - } + public uint Signature => signature; /// /// Returns the IMAGE_NT_HEADERS.FileHeader field /// - public ImageFileHeader FileHeader { - get { return imageFileHeader; } - } + public ImageFileHeader FileHeader => imageFileHeader; /// /// Returns the IMAGE_NT_HEADERS.OptionalHeader field /// - public IImageOptionalHeader OptionalHeader { - get { return imageOptionalHeader; } - } + public IImageOptionalHeader OptionalHeader => imageOptionalHeader; /// /// Constructor @@ -39,14 +33,15 @@ public IImageOptionalHeader OptionalHeader { /// PE file reader pointing to the start of this section /// Verify section /// Thrown if verification fails - public ImageNTHeaders(IImageStream reader, bool verify) { - SetStartOffset(reader); - this.signature = reader.ReadUInt32(); - if (verify && this.signature != 0x4550) + public ImageNTHeaders(ref DataReader reader, bool verify) { + SetStartOffset(ref reader); + signature = reader.ReadUInt32(); + // Mono only checks the low 2 bytes + if (verify && (ushort)signature != 0x4550) throw new BadImageFormatException("Invalid NT headers signature"); - this.imageFileHeader = new ImageFileHeader(reader, verify); - this.imageOptionalHeader = CreateImageOptionalHeader(reader, verify); - SetEndoffset(reader); + imageFileHeader = new ImageFileHeader(ref reader, verify); + imageOptionalHeader = CreateImageOptionalHeader(ref reader, verify); + SetEndoffset(ref reader); } /// @@ -56,14 +51,14 @@ public ImageNTHeaders(IImageStream reader, bool verify) { /// Verify section /// The created IImageOptionalHeader /// Thrown if verification fails - IImageOptionalHeader CreateImageOptionalHeader(IImageStream reader, bool verify) { + IImageOptionalHeader CreateImageOptionalHeader(ref DataReader reader, bool verify) { ushort magic = reader.ReadUInt16(); reader.Position -= 2; - switch (magic) { - case 0x010B: return new ImageOptionalHeader32(reader, imageFileHeader.SizeOfOptionalHeader, verify); - case 0x020B: return new ImageOptionalHeader64(reader, imageFileHeader.SizeOfOptionalHeader, verify); - default: throw new BadImageFormatException("Invalid optional header magic"); - } + return magic switch { + 0x010B => new ImageOptionalHeader32(ref reader, imageFileHeader.SizeOfOptionalHeader, verify), + 0x020B => new ImageOptionalHeader64(ref reader, imageFileHeader.SizeOfOptionalHeader, verify), + _ => throw new BadImageFormatException("Invalid optional header magic"), + }; } } } diff --git a/src/PE/ImageOptionalHeader32.cs b/src/PE/ImageOptionalHeader32.cs index 6ea7ecb0c..d03172d21 100644 --- a/src/PE/ImageOptionalHeader32.cs +++ b/src/PE/ImageOptionalHeader32.cs @@ -43,219 +43,157 @@ public sealed class ImageOptionalHeader32 : FileSection, IImageOptionalHeader { /// /// Returns the IMAGE_OPTIONAL_HEADER.Magic field /// - public ushort Magic { - get { return magic; } - } + public ushort Magic => magic; /// /// Returns the IMAGE_OPTIONAL_HEADER.MajorLinkerVersion field /// - public byte MajorLinkerVersion { - get { return majorLinkerVersion; } - } + public byte MajorLinkerVersion => majorLinkerVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER.MinorLinkerVersion field /// - public byte MinorLinkerVersion { - get { return minorLinkerVersion; } - } + public byte MinorLinkerVersion => minorLinkerVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER.SizeOfCode field /// - public uint SizeOfCode { - get { return sizeOfCode; } - } + public uint SizeOfCode => sizeOfCode; /// /// Returns the IMAGE_OPTIONAL_HEADER.SizeOfInitializedData field /// - public uint SizeOfInitializedData { - get { return sizeOfInitializedData; } - } + public uint SizeOfInitializedData => sizeOfInitializedData; /// /// Returns the IMAGE_OPTIONAL_HEADER.SizeOfUninitializedData field /// - public uint SizeOfUninitializedData { - get { return sizeOfUninitializedData; } - } + public uint SizeOfUninitializedData => sizeOfUninitializedData; /// /// Returns the IMAGE_OPTIONAL_HEADER.AddressOfEntryPoint field /// - public RVA AddressOfEntryPoint { - get { return addressOfEntryPoint; } - } + public RVA AddressOfEntryPoint => addressOfEntryPoint; /// /// Returns the IMAGE_OPTIONAL_HEADER.BaseOfCode field /// - public RVA BaseOfCode { - get { return baseOfCode; } - } + public RVA BaseOfCode => baseOfCode; /// /// Returns the IMAGE_OPTIONAL_HEADER.BaseOfData field /// - public RVA BaseOfData { - get { return baseOfData; } - } + public RVA BaseOfData => baseOfData; /// /// Returns the IMAGE_OPTIONAL_HEADER.ImageBase field /// - public ulong ImageBase { - get { return imageBase; } - } + public ulong ImageBase => imageBase; /// /// Returns the IMAGE_OPTIONAL_HEADER.SectionAlignment field /// - public uint SectionAlignment { - get { return sectionAlignment; } - } + public uint SectionAlignment => sectionAlignment; /// /// Returns the IMAGE_OPTIONAL_HEADER.FileAlignment field /// - public uint FileAlignment { - get { return fileAlignment; } - } + public uint FileAlignment => fileAlignment; /// /// Returns the IMAGE_OPTIONAL_HEADER.MajorOperatingSystemVersion field /// - public ushort MajorOperatingSystemVersion { - get { return majorOperatingSystemVersion; } - } + public ushort MajorOperatingSystemVersion => majorOperatingSystemVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER.MinorOperatingSystemVersion field /// - public ushort MinorOperatingSystemVersion { - get { return minorOperatingSystemVersion; } - } + public ushort MinorOperatingSystemVersion => minorOperatingSystemVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER.MajorImageVersion field /// - public ushort MajorImageVersion { - get { return majorImageVersion; } - } + public ushort MajorImageVersion => majorImageVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER.MinorImageVersion field /// - public ushort MinorImageVersion { - get { return minorImageVersion; } - } + public ushort MinorImageVersion => minorImageVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER.MajorSubsystemVersion field /// - public ushort MajorSubsystemVersion { - get { return majorSubsystemVersion; } - } + public ushort MajorSubsystemVersion => majorSubsystemVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER.MinorSubsystemVersion field /// - public ushort MinorSubsystemVersion { - get { return minorSubsystemVersion; } - } + public ushort MinorSubsystemVersion => minorSubsystemVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER.Win32VersionValue field /// - public uint Win32VersionValue { - get { return win32VersionValue; } - } + public uint Win32VersionValue => win32VersionValue; /// /// Returns the IMAGE_OPTIONAL_HEADER.SizeOfImage field /// - public uint SizeOfImage { - get { return sizeOfImage; } - } + public uint SizeOfImage => sizeOfImage; /// /// Returns the IMAGE_OPTIONAL_HEADER.SizeOfHeaders field /// - public uint SizeOfHeaders { - get { return sizeOfHeaders; } - } + public uint SizeOfHeaders => sizeOfHeaders; /// /// Returns the IMAGE_OPTIONAL_HEADER.CheckSum field /// - public uint CheckSum { - get { return checkSum; } - } + public uint CheckSum => checkSum; /// /// Returns the IMAGE_OPTIONAL_HEADER.Subsystem field /// - public Subsystem Subsystem { - get { return subsystem; } - } + public Subsystem Subsystem => subsystem; /// /// Returns the IMAGE_OPTIONAL_HEADER.DllCharacteristics field /// - public DllCharacteristics DllCharacteristics { - get { return dllCharacteristics; } - } + public DllCharacteristics DllCharacteristics => dllCharacteristics; /// /// Returns the IMAGE_OPTIONAL_HEADER.SizeOfStackReserve field /// - public ulong SizeOfStackReserve { - get { return sizeOfStackReserve; } - } + public ulong SizeOfStackReserve => sizeOfStackReserve; /// /// Returns the IMAGE_OPTIONAL_HEADER.SizeOfStackCommit field /// - public ulong SizeOfStackCommit { - get { return sizeOfStackCommit; } - } + public ulong SizeOfStackCommit => sizeOfStackCommit; /// /// Returns the IMAGE_OPTIONAL_HEADER.SizeOfHeapReserve field /// - public ulong SizeOfHeapReserve { - get { return sizeOfHeapReserve; } - } + public ulong SizeOfHeapReserve => sizeOfHeapReserve; /// /// Returns the IMAGE_OPTIONAL_HEADER.SizeOfHeapCommit field /// - public ulong SizeOfHeapCommit { - get { return sizeOfHeapCommit; } - } + public ulong SizeOfHeapCommit => sizeOfHeapCommit; /// /// Returns the IMAGE_OPTIONAL_HEADER.LoaderFlags field /// - public uint LoaderFlags { - get { return loaderFlags; } - } + public uint LoaderFlags => loaderFlags; /// /// Returns the IMAGE_OPTIONAL_HEADER.NumberOfRvaAndSizes field /// - public uint NumberOfRvaAndSizes { - get { return numberOfRvaAndSizes; } - } + public uint NumberOfRvaAndSizes => numberOfRvaAndSizes; /// /// Returns the IMAGE_OPTIONAL_HEADER.DataDirectories field /// - public ImageDataDirectory[] DataDirectories { - get { return dataDirectories; } - } + public ImageDataDirectory[] DataDirectories => dataDirectories; /// /// Constructor @@ -264,51 +202,51 @@ public ImageDataDirectory[] DataDirectories { /// Total size of this optional header (from the file header) /// Verify section /// Thrown if verification fails - public ImageOptionalHeader32(IImageStream reader, uint totalSize, bool verify) { + public ImageOptionalHeader32(ref DataReader reader, uint totalSize, bool verify) { if (totalSize < 0x60) throw new BadImageFormatException("Invalid optional header size"); - if (verify && reader.Position + totalSize > reader.Length) + if (verify && (ulong)reader.Position + totalSize > reader.Length) throw new BadImageFormatException("Invalid optional header size"); - SetStartOffset(reader); - this.magic = reader.ReadUInt16(); - this.majorLinkerVersion = reader.ReadByte(); - this.minorLinkerVersion = reader.ReadByte(); - this.sizeOfCode = reader.ReadUInt32(); - this.sizeOfInitializedData = reader.ReadUInt32(); - this.sizeOfUninitializedData = reader.ReadUInt32(); - this.addressOfEntryPoint = (RVA)reader.ReadUInt32(); - this.baseOfCode = (RVA)reader.ReadUInt32(); - this.baseOfData = (RVA)reader.ReadUInt32(); - this.imageBase = reader.ReadUInt32(); - this.sectionAlignment = reader.ReadUInt32(); - this.fileAlignment = reader.ReadUInt32(); - this.majorOperatingSystemVersion = reader.ReadUInt16(); - this.minorOperatingSystemVersion = reader.ReadUInt16(); - this.majorImageVersion = reader.ReadUInt16(); - this.minorImageVersion = reader.ReadUInt16(); - this.majorSubsystemVersion = reader.ReadUInt16(); - this.minorSubsystemVersion = reader.ReadUInt16(); - this.win32VersionValue = reader.ReadUInt32(); - this.sizeOfImage = reader.ReadUInt32(); - this.sizeOfHeaders = reader.ReadUInt32(); - this.checkSum = reader.ReadUInt32(); - this.subsystem = (Subsystem)reader.ReadUInt16(); - this.dllCharacteristics = (DllCharacteristics)reader.ReadUInt16(); - this.sizeOfStackReserve = reader.ReadUInt32(); - this.sizeOfStackCommit = reader.ReadUInt32(); - this.sizeOfHeapReserve = reader.ReadUInt32(); - this.sizeOfHeapCommit = reader.ReadUInt32(); - this.loaderFlags = reader.ReadUInt32(); - this.numberOfRvaAndSizes = reader.ReadUInt32(); + SetStartOffset(ref reader); + magic = reader.ReadUInt16(); + majorLinkerVersion = reader.ReadByte(); + minorLinkerVersion = reader.ReadByte(); + sizeOfCode = reader.ReadUInt32(); + sizeOfInitializedData = reader.ReadUInt32(); + sizeOfUninitializedData = reader.ReadUInt32(); + addressOfEntryPoint = (RVA)reader.ReadUInt32(); + baseOfCode = (RVA)reader.ReadUInt32(); + baseOfData = (RVA)reader.ReadUInt32(); + imageBase = reader.ReadUInt32(); + sectionAlignment = reader.ReadUInt32(); + fileAlignment = reader.ReadUInt32(); + majorOperatingSystemVersion = reader.ReadUInt16(); + minorOperatingSystemVersion = reader.ReadUInt16(); + majorImageVersion = reader.ReadUInt16(); + minorImageVersion = reader.ReadUInt16(); + majorSubsystemVersion = reader.ReadUInt16(); + minorSubsystemVersion = reader.ReadUInt16(); + win32VersionValue = reader.ReadUInt32(); + sizeOfImage = reader.ReadUInt32(); + sizeOfHeaders = reader.ReadUInt32(); + checkSum = reader.ReadUInt32(); + subsystem = (Subsystem)reader.ReadUInt16(); + dllCharacteristics = (DllCharacteristics)reader.ReadUInt16(); + sizeOfStackReserve = reader.ReadUInt32(); + sizeOfStackCommit = reader.ReadUInt32(); + sizeOfHeapReserve = reader.ReadUInt32(); + sizeOfHeapCommit = reader.ReadUInt32(); + loaderFlags = reader.ReadUInt32(); + numberOfRvaAndSizes = reader.ReadUInt32(); for (int i = 0; i < dataDirectories.Length; i++) { - uint len = (uint)(reader.Position - startOffset); + uint len = reader.Position - (uint)startOffset; if (len + 8 <= totalSize) - dataDirectories[i] = new ImageDataDirectory(reader, verify); + dataDirectories[i] = new ImageDataDirectory(ref reader, verify); else dataDirectories[i] = new ImageDataDirectory(); } - reader.Position = (long)startOffset + totalSize; - SetEndoffset(reader); + reader.Position = (uint)startOffset + totalSize; + SetEndoffset(ref reader); } } } diff --git a/src/PE/ImageOptionalHeader64.cs b/src/PE/ImageOptionalHeader64.cs index b49c32d5b..a0a1efb63 100644 --- a/src/PE/ImageOptionalHeader64.cs +++ b/src/PE/ImageOptionalHeader64.cs @@ -42,219 +42,157 @@ public sealed class ImageOptionalHeader64 : FileSection, IImageOptionalHeader { /// /// Returns the IMAGE_OPTIONAL_HEADER64.Magic field /// - public ushort Magic { - get { return magic; } - } + public ushort Magic => magic; /// /// Returns the IMAGE_OPTIONAL_HEADER64.MajorLinkerVersion field /// - public byte MajorLinkerVersion { - get { return majorLinkerVersion; } - } + public byte MajorLinkerVersion => majorLinkerVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER64.MinorLinkerVersion field /// - public byte MinorLinkerVersion { - get { return minorLinkerVersion; } - } + public byte MinorLinkerVersion => minorLinkerVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER64.SizeOfCode field /// - public uint SizeOfCode { - get { return sizeOfCode; } - } + public uint SizeOfCode => sizeOfCode; /// /// Returns the IMAGE_OPTIONAL_HEADER64.SizeOfInitializedData field /// - public uint SizeOfInitializedData { - get { return sizeOfInitializedData; } - } + public uint SizeOfInitializedData => sizeOfInitializedData; /// /// Returns the IMAGE_OPTIONAL_HEADER64.SizeOfUninitializedData field /// - public uint SizeOfUninitializedData { - get { return sizeOfUninitializedData; } - } + public uint SizeOfUninitializedData => sizeOfUninitializedData; /// /// Returns the IMAGE_OPTIONAL_HEADER64.AddressOfEntryPoint field /// - public RVA AddressOfEntryPoint { - get { return addressOfEntryPoint; } - } + public RVA AddressOfEntryPoint => addressOfEntryPoint; /// /// Returns the IMAGE_OPTIONAL_HEADER64.BaseOfCode field /// - public RVA BaseOfCode { - get { return baseOfCode; } - } + public RVA BaseOfCode => baseOfCode; /// /// Returns 0 since BaseOfData is not present in IMAGE_OPTIONAL_HEADER64 /// - public RVA BaseOfData { - get { return 0; } - } + public RVA BaseOfData => 0; /// /// Returns the IMAGE_OPTIONAL_HEADER64.ImageBase field /// - public ulong ImageBase { - get { return imageBase; } - } + public ulong ImageBase => imageBase; /// /// Returns the IMAGE_OPTIONAL_HEADER64.SectionAlignment field /// - public uint SectionAlignment { - get { return sectionAlignment; } - } + public uint SectionAlignment => sectionAlignment; /// /// Returns the IMAGE_OPTIONAL_HEADER64.FileAlignment field /// - public uint FileAlignment { - get { return fileAlignment; } - } + public uint FileAlignment => fileAlignment; /// /// Returns the IMAGE_OPTIONAL_HEADER64.MajorOperatingSystemVersion field /// - public ushort MajorOperatingSystemVersion { - get { return majorOperatingSystemVersion; } - } + public ushort MajorOperatingSystemVersion => majorOperatingSystemVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER64.MinorOperatingSystemVersion field /// - public ushort MinorOperatingSystemVersion { - get { return minorOperatingSystemVersion; } - } + public ushort MinorOperatingSystemVersion => minorOperatingSystemVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER64.MajorImageVersion field /// - public ushort MajorImageVersion { - get { return majorImageVersion; } - } + public ushort MajorImageVersion => majorImageVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER64.MinorImageVersion field /// - public ushort MinorImageVersion { - get { return minorImageVersion; } - } + public ushort MinorImageVersion => minorImageVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER64.MajorSubsystemVersion field /// - public ushort MajorSubsystemVersion { - get { return majorSubsystemVersion; } - } + public ushort MajorSubsystemVersion => majorSubsystemVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER64.MinorSubsystemVersion field /// - public ushort MinorSubsystemVersion { - get { return minorSubsystemVersion; } - } + public ushort MinorSubsystemVersion => minorSubsystemVersion; /// /// Returns the IMAGE_OPTIONAL_HEADER64.Win32VersionValue field /// - public uint Win32VersionValue { - get { return win32VersionValue; } - } + public uint Win32VersionValue => win32VersionValue; /// /// Returns the IMAGE_OPTIONAL_HEADER64.SizeOfImage field /// - public uint SizeOfImage { - get { return sizeOfImage; } - } + public uint SizeOfImage => sizeOfImage; /// /// Returns the IMAGE_OPTIONAL_HEADER64.SizeOfHeaders field /// - public uint SizeOfHeaders { - get { return sizeOfHeaders; } - } + public uint SizeOfHeaders => sizeOfHeaders; /// /// Returns the IMAGE_OPTIONAL_HEADER64.CheckSum field /// - public uint CheckSum { - get { return checkSum; } - } + public uint CheckSum => checkSum; /// /// Returns the IMAGE_OPTIONAL_HEADER64.Subsystem field /// - public Subsystem Subsystem { - get { return subsystem; } - } + public Subsystem Subsystem => subsystem; /// /// Returns the IMAGE_OPTIONAL_HEADER64.DllCharacteristics field /// - public DllCharacteristics DllCharacteristics { - get { return dllCharacteristics; } - } + public DllCharacteristics DllCharacteristics => dllCharacteristics; /// /// Returns the IMAGE_OPTIONAL_HEADER64.SizeOfStackReserve field /// - public ulong SizeOfStackReserve { - get { return sizeOfStackReserve; } - } + public ulong SizeOfStackReserve => sizeOfStackReserve; /// /// Returns the IMAGE_OPTIONAL_HEADER64.SizeOfStackCommit field /// - public ulong SizeOfStackCommit { - get { return sizeOfStackCommit; } - } + public ulong SizeOfStackCommit => sizeOfStackCommit; /// /// Returns the IMAGE_OPTIONAL_HEADER64.SizeOfHeapReserve field /// - public ulong SizeOfHeapReserve { - get { return sizeOfHeapReserve; } - } + public ulong SizeOfHeapReserve => sizeOfHeapReserve; /// /// Returns the IMAGE_OPTIONAL_HEADER64.SizeOfHeapCommit field /// - public ulong SizeOfHeapCommit { - get { return sizeOfHeapCommit; } - } + public ulong SizeOfHeapCommit => sizeOfHeapCommit; /// /// Returns the IMAGE_OPTIONAL_HEADER64.LoaderFlags field /// - public uint LoaderFlags { - get { return loaderFlags; } - } + public uint LoaderFlags => loaderFlags; /// /// Returns the IMAGE_OPTIONAL_HEADER64.NumberOfRvaAndSizes field /// - public uint NumberOfRvaAndSizes { - get { return numberOfRvaAndSizes; } - } + public uint NumberOfRvaAndSizes => numberOfRvaAndSizes; /// /// Returns the IMAGE_OPTIONAL_HEADER64.DataDirectories field /// - public ImageDataDirectory[] DataDirectories { - get { return dataDirectories; } - } + public ImageDataDirectory[] DataDirectories => dataDirectories; /// /// Constructor @@ -263,50 +201,50 @@ public ImageDataDirectory[] DataDirectories { /// Total size of this optional header (from the file header) /// Verify section /// Thrown if verification fails - public ImageOptionalHeader64(IImageStream reader, uint totalSize, bool verify) { + public ImageOptionalHeader64(ref DataReader reader, uint totalSize, bool verify) { if (totalSize < 0x70) throw new BadImageFormatException("Invalid optional header size"); - if (verify && reader.Position + totalSize > reader.Length) + if (verify && (ulong)reader.Position + totalSize > reader.Length) throw new BadImageFormatException("Invalid optional header size"); - SetStartOffset(reader); - this.magic = reader.ReadUInt16(); - this.majorLinkerVersion = reader.ReadByte(); - this.minorLinkerVersion = reader.ReadByte(); - this.sizeOfCode = reader.ReadUInt32(); - this.sizeOfInitializedData = reader.ReadUInt32(); - this.sizeOfUninitializedData = reader.ReadUInt32(); - this.addressOfEntryPoint = (RVA)reader.ReadUInt32(); - this.baseOfCode = (RVA)reader.ReadUInt32(); - this.imageBase = reader.ReadUInt64(); - this.sectionAlignment = reader.ReadUInt32(); - this.fileAlignment = reader.ReadUInt32(); - this.majorOperatingSystemVersion = reader.ReadUInt16(); - this.minorOperatingSystemVersion = reader.ReadUInt16(); - this.majorImageVersion = reader.ReadUInt16(); - this.minorImageVersion = reader.ReadUInt16(); - this.majorSubsystemVersion = reader.ReadUInt16(); - this.minorSubsystemVersion = reader.ReadUInt16(); - this.win32VersionValue = reader.ReadUInt32(); - this.sizeOfImage = reader.ReadUInt32(); - this.sizeOfHeaders = reader.ReadUInt32(); - this.checkSum = reader.ReadUInt32(); - this.subsystem = (Subsystem)reader.ReadUInt16(); - this.dllCharacteristics = (DllCharacteristics)reader.ReadUInt16(); - this.sizeOfStackReserve = reader.ReadUInt64(); - this.sizeOfStackCommit = reader.ReadUInt64(); - this.sizeOfHeapReserve = reader.ReadUInt64(); - this.sizeOfHeapCommit = reader.ReadUInt64(); - this.loaderFlags = reader.ReadUInt32(); - this.numberOfRvaAndSizes = reader.ReadUInt32(); + SetStartOffset(ref reader); + magic = reader.ReadUInt16(); + majorLinkerVersion = reader.ReadByte(); + minorLinkerVersion = reader.ReadByte(); + sizeOfCode = reader.ReadUInt32(); + sizeOfInitializedData = reader.ReadUInt32(); + sizeOfUninitializedData = reader.ReadUInt32(); + addressOfEntryPoint = (RVA)reader.ReadUInt32(); + baseOfCode = (RVA)reader.ReadUInt32(); + imageBase = reader.ReadUInt64(); + sectionAlignment = reader.ReadUInt32(); + fileAlignment = reader.ReadUInt32(); + majorOperatingSystemVersion = reader.ReadUInt16(); + minorOperatingSystemVersion = reader.ReadUInt16(); + majorImageVersion = reader.ReadUInt16(); + minorImageVersion = reader.ReadUInt16(); + majorSubsystemVersion = reader.ReadUInt16(); + minorSubsystemVersion = reader.ReadUInt16(); + win32VersionValue = reader.ReadUInt32(); + sizeOfImage = reader.ReadUInt32(); + sizeOfHeaders = reader.ReadUInt32(); + checkSum = reader.ReadUInt32(); + subsystem = (Subsystem)reader.ReadUInt16(); + dllCharacteristics = (DllCharacteristics)reader.ReadUInt16(); + sizeOfStackReserve = reader.ReadUInt64(); + sizeOfStackCommit = reader.ReadUInt64(); + sizeOfHeapReserve = reader.ReadUInt64(); + sizeOfHeapCommit = reader.ReadUInt64(); + loaderFlags = reader.ReadUInt32(); + numberOfRvaAndSizes = reader.ReadUInt32(); for (int i = 0; i < dataDirectories.Length; i++) { - uint len = (uint)(reader.Position - startOffset); + uint len = reader.Position - (uint)startOffset; if (len + 8 <= totalSize) - dataDirectories[i] = new ImageDataDirectory(reader, verify); + dataDirectories[i] = new ImageDataDirectory(ref reader, verify); else dataDirectories[i] = new ImageDataDirectory(); } - reader.Position = (long)startOffset + totalSize; - SetEndoffset(reader); + reader.Position = (uint)startOffset + totalSize; + SetEndoffset(ref reader); } } } diff --git a/src/PE/ImageSectionHeader.cs b/src/PE/ImageSectionHeader.cs index f8da27e4a..326028c32 100644 --- a/src/PE/ImageSectionHeader.cs +++ b/src/PE/ImageSectionHeader.cs @@ -27,79 +27,57 @@ public sealed class ImageSectionHeader : FileSection { /// Returns the human readable section name, ignoring everything after /// the first nul byte /// - public string DisplayName { - get { return displayName; } - } + public string DisplayName => displayName; /// /// Returns the IMAGE_SECTION_HEADER.Name field /// - public byte[] Name { - get { return name; } - } + public byte[] Name => name; /// /// Returns the IMAGE_SECTION_HEADER.VirtualSize field /// - public uint VirtualSize { - get { return virtualSize; } - } + public uint VirtualSize => virtualSize; /// /// Returns the IMAGE_SECTION_HEADER.VirtualAddress field /// - public RVA VirtualAddress { - get { return virtualAddress; } - } + public RVA VirtualAddress => virtualAddress; /// /// Returns the IMAGE_SECTION_HEADER.SizeOfRawData field /// - public uint SizeOfRawData { - get { return sizeOfRawData; } - } + public uint SizeOfRawData => sizeOfRawData; /// /// Returns the IMAGE_SECTION_HEADER.PointerToRawData field /// - public uint PointerToRawData { - get { return pointerToRawData; } - } + public uint PointerToRawData => pointerToRawData; /// /// Returns the IMAGE_SECTION_HEADER.PointerToRelocations field /// - public uint PointerToRelocations { - get { return pointerToRelocations; } - } + public uint PointerToRelocations => pointerToRelocations; /// /// Returns the IMAGE_SECTION_HEADER.PointerToLinenumbers field /// - public uint PointerToLinenumbers { - get { return pointerToLinenumbers; } - } + public uint PointerToLinenumbers => pointerToLinenumbers; /// /// Returns the IMAGE_SECTION_HEADER.NumberOfRelocations field /// - public ushort NumberOfRelocations { - get { return numberOfRelocations; } - } + public ushort NumberOfRelocations => numberOfRelocations; /// /// Returns the IMAGE_SECTION_HEADER.NumberOfLinenumbers field /// - public ushort NumberOfLinenumbers { - get { return numberOfLinenumbers; } - } + public ushort NumberOfLinenumbers => numberOfLinenumbers; /// /// Returns the IMAGE_SECTION_HEADER.Characteristics field /// - public uint Characteristics { - get { return characteristics; } - } + public uint Characteristics => characteristics; /// /// Constructor @@ -107,19 +85,19 @@ public uint Characteristics { /// PE file reader pointing to the start of this section /// Verify section /// Thrown if verification fails - public ImageSectionHeader(IImageStream reader, bool verify) { - SetStartOffset(reader); - this.name = reader.ReadBytes(8); - this.virtualSize = reader.ReadUInt32(); - this.virtualAddress = (RVA)reader.ReadUInt32(); - this.sizeOfRawData = reader.ReadUInt32(); - this.pointerToRawData = reader.ReadUInt32(); - this.pointerToRelocations = reader.ReadUInt32(); - this.pointerToLinenumbers = reader.ReadUInt32(); - this.numberOfRelocations = reader.ReadUInt16(); - this.numberOfLinenumbers = reader.ReadUInt16(); - this.characteristics = reader.ReadUInt32(); - SetEndoffset(reader); + public ImageSectionHeader(ref DataReader reader, bool verify) { + SetStartOffset(ref reader); + name = reader.ReadBytes(8); + virtualSize = reader.ReadUInt32(); + virtualAddress = (RVA)reader.ReadUInt32(); + sizeOfRawData = reader.ReadUInt32(); + pointerToRawData = reader.ReadUInt32(); + pointerToRelocations = reader.ReadUInt32(); + pointerToLinenumbers = reader.ReadUInt32(); + numberOfRelocations = reader.ReadUInt16(); + numberOfLinenumbers = reader.ReadUInt16(); + characteristics = reader.ReadUInt32(); + SetEndoffset(ref reader); displayName = ToString(name); } diff --git a/src/PE/Machine.cs b/src/PE/Machine.cs index 439e8213f..7cacdd0de 100644 --- a/src/PE/Machine.cs +++ b/src/PE/Machine.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.PE { +namespace dnlib.PE { /// /// IMAGE_FILE_HEADER.Machine enum /// @@ -65,5 +65,180 @@ public enum Machine : ushort { ARM64 = 0xAA64, /// CEE = 0xC0EE, + + // Search for IMAGE_FILE_MACHINE_NATIVE and IMAGE_FILE_MACHINE_NATIVE_OS_OVERRIDE here: + // https://github.com/dotnet/coreclr/blob/master/src/inc/pedecoder.h + // Note that IMAGE_FILE_MACHINE_NATIVE_OS_OVERRIDE == 0 if it's Windows + +#pragma warning disable 1591 // Missing XML comment for publicly visible type or member + I386_Native_Apple = I386 ^ 0x4644, + AMD64_Native_Apple = AMD64 ^ 0x4644, + ARMNT_Native_Apple = ARMNT ^ 0x4644, + ARM64_Native_Apple = ARM64 ^ 0x4644, + S390X_Native_Apple = Unknown ^ 0x4644, + + I386_Native_FreeBSD = I386 ^ 0xADC4, + AMD64_Native_FreeBSD = AMD64 ^ 0xADC4, + ARMNT_Native_FreeBSD = ARMNT ^ 0xADC4, + ARM64_Native_FreeBSD = ARM64 ^ 0xADC4, + S390X_Native_FreeBSD = Unknown ^ 0xADC4, + + I386_Native_Linux = I386 ^ 0x7B79, + AMD64_Native_Linux = AMD64 ^ 0x7B79, + ARMNT_Native_Linux = ARMNT ^ 0x7B79, + ARM64_Native_Linux = ARM64 ^ 0x7B79, + S390X_Native_Linux = Unknown ^ 0x7B79, + + I386_Native_NetBSD = I386 ^ 0x1993, + AMD64_Native_NetBSD = AMD64 ^ 0x1993, + ARMNT_Native_NetBSD = ARMNT ^ 0x1993, + ARM64_Native_NetBSD = ARM64 ^ 0x1993, + S390X_Native_NetBSD = Unknown ^ 0x1993, + + I386_Native_Sun = I386 ^ 0x1992, + AMD64_Native_Sun = AMD64 ^ 0x1992, + ARMNT_Native_Sun = ARMNT ^ 0x1992, + ARM64_Native_Sun = ARM64 ^ 0x1992, + S390X_Native_Sun = Unknown ^ 0x1992, +#pragma warning restore 1591 // Missing XML comment for publicly visible type or member + } + + /// + /// Extensions + /// + public static class MachineExtensions { + /// + /// Checks if is a 64-bit machine + /// + /// Machine + /// + public static bool Is64Bit(this Machine machine) { + switch (machine) { + case Machine.IA64: + + case Machine.AMD64: + case Machine.AMD64_Native_Apple: + case Machine.AMD64_Native_FreeBSD: + case Machine.AMD64_Native_Linux: + case Machine.AMD64_Native_NetBSD: + case Machine.AMD64_Native_Sun: + + case Machine.ARM64: + case Machine.ARM64_Native_Apple: + case Machine.ARM64_Native_FreeBSD: + case Machine.ARM64_Native_Linux: + case Machine.ARM64_Native_NetBSD: + case Machine.ARM64_Native_Sun: + return true; + + // It uses value 0==Unknown but we can't assume it's always s390x + //case Machine.Unknown: + case Machine.S390X_Native_Apple: + case Machine.S390X_Native_FreeBSD: + case Machine.S390X_Native_Linux: + case Machine.S390X_Native_NetBSD: + case Machine.S390X_Native_Sun: + return true; + + default: + return false; + } + } + + /// + /// Checks if is , , etc... + /// + /// Machine + /// + public static bool IsI386(this Machine machine) { + switch (machine) { + case Machine.I386: + case Machine.I386_Native_Apple: + case Machine.I386_Native_FreeBSD: + case Machine.I386_Native_Linux: + case Machine.I386_Native_NetBSD: + case Machine.I386_Native_Sun: + return true; + default: + return false; + } + } + + /// + /// Checks if is , , etc... + /// + /// Machine + /// + public static bool IsAMD64(this Machine machine) { + switch (machine) { + case Machine.AMD64: + case Machine.AMD64_Native_Apple: + case Machine.AMD64_Native_FreeBSD: + case Machine.AMD64_Native_Linux: + case Machine.AMD64_Native_NetBSD: + case Machine.AMD64_Native_Sun: + return true; + default: + return false; + } + } + + /// + /// Checks if is , , etc... + /// + /// Machine + /// + public static bool IsARMNT(this Machine machine) { + switch (machine) { + case Machine.ARMNT: + case Machine.ARMNT_Native_Apple: + case Machine.ARMNT_Native_FreeBSD: + case Machine.ARMNT_Native_Linux: + case Machine.ARMNT_Native_NetBSD: + case Machine.ARMNT_Native_Sun: + return true; + default: + return false; + } + } + + /// + /// Checks if is , , etc... + /// + /// Machine + /// + public static bool IsARM64(this Machine machine) { + switch (machine) { + case Machine.ARM64: + case Machine.ARM64_Native_Apple: + case Machine.ARM64_Native_FreeBSD: + case Machine.ARM64_Native_Linux: + case Machine.ARM64_Native_NetBSD: + case Machine.ARM64_Native_Sun: + return true; + default: + return false; + } + } + + /// + /// Checks if is s390x, , etc... + /// + /// Machine + /// + public static bool IsS390x(this Machine machine) { + switch (machine) { + // It uses value 0==Unknown but we can't assume it's always s390x + //case Machine.Unknown: + case Machine.S390X_Native_Apple: + case Machine.S390X_Native_FreeBSD: + case Machine.S390X_Native_Linux: + case Machine.S390X_Native_NetBSD: + case Machine.S390X_Native_Sun: + return true; + default: + return false; + } + } } } diff --git a/src/PE/PEExtensions.cs b/src/PE/PEExtensions.cs index 10cb179a5..eeef50d94 100644 --- a/src/PE/PEExtensions.cs +++ b/src/PE/PEExtensions.cs @@ -1,6 +1,7 @@ // dnlib: See LICENSE.txt for more info -using System.IO; +using System; +using System.IO; namespace dnlib.PE { /// @@ -10,23 +11,40 @@ public static partial class PEExtensions { /// /// Calculates a PE checksum /// - /// Reader + /// PE image stream positioned at the MZ bytes /// Length of image /// Offset of checksum /// PE checksum - internal static uint CalculatePECheckSum(this BinaryReader reader, long length, long checkSumOffset) { + internal static uint CalculatePECheckSum(this Stream stream, long length, long checkSumOffset) { + if ((length & 1) != 0) + ThrowInvalidOperationException("Invalid PE length"); + var buffer = new byte[(int)Math.Min(length, 0x2000)]; uint checkSum = 0; - for (long i = 0; i < length; i += 2) { - if (i == checkSumOffset) { - reader.ReadUInt32(); - i += 2; - continue; - } - checkSum += reader.ReadUInt16(); - checkSum = (ushort)(checkSum + (checkSum >> 16)); - } + checkSum = CalculatePECheckSum(stream, checkSumOffset, checkSum, buffer); + const int ChecksumFieldSize = 4; + stream.Position += ChecksumFieldSize; + checkSum = CalculatePECheckSum(stream, length - checkSumOffset - ChecksumFieldSize, checkSum, buffer); ulong cks = (ulong)checkSum + (ulong)length; return (uint)cks + (uint)(cks >> 32); } + + static uint CalculatePECheckSum(Stream stream, long length, uint checkSum, byte[] buffer) { + for (long offset = 0; offset < length;) { + int len = (int)Math.Min(length - offset, buffer.Length); + int count = stream.Read(buffer, 0, len); + if (count != len) + ThrowInvalidOperationException("Couldn't read all bytes"); + + for (int i = 0; i < count;) { + checkSum += buffer[i++] | ((uint)buffer[i++] << 8); + checkSum = (ushort)(checkSum + (checkSum >> 16)); + } + + offset += count; + } + return checkSum; + } + + static void ThrowInvalidOperationException(string message) => throw new InvalidOperationException(message); } } diff --git a/src/PE/PEImage.cs b/src/PE/PEImage.cs index 10a4ab1c0..023f7ac8d 100644 --- a/src/PE/PEImage.cs +++ b/src/PE/PEImage.cs @@ -2,9 +2,10 @@ using System; using System.Collections.Generic; +using System.IO; +using dnlib.IO; using dnlib.Utils; using dnlib.W32Resources; -using dnlib.IO; using dnlib.Threading; namespace dnlib.PE { @@ -26,7 +27,7 @@ public enum ImageLayout { /// /// Accesses a PE file /// - public sealed class PEImage : IPEImage { + public sealed class PEImage : IInternalPEImage { // Default to false because an OS loaded PE image may contain memory holes. If there // are memory holes, other code (eg. .NET resource creator) must verify that all memory // is available, which will be slower. @@ -35,8 +36,7 @@ public sealed class PEImage : IPEImage { static readonly IPEType MemoryLayout = new MemoryPEType(); static readonly IPEType FileLayout = new FilePEType(); - IImageStream imageStream; - IImageStreamCreator imageStreamCreator; + DataReaderFactory dataReaderFactory; IPEType peType; PEInfo peInfo; UserValue win32Resources; @@ -45,62 +45,49 @@ public sealed class PEImage : IPEImage { #endif sealed class FilePEType : IPEType { - /// - public RVA ToRVA(PEInfo peInfo, FileOffset offset) { - return peInfo.ToRVA(offset); - } - - /// - public FileOffset ToFileOffset(PEInfo peInfo, RVA rva) { - return peInfo.ToFileOffset(rva); - } + public RVA ToRVA(PEInfo peInfo, FileOffset offset) => peInfo.ToRVA(offset); + public FileOffset ToFileOffset(PEInfo peInfo, RVA rva) => peInfo.ToFileOffset(rva); } sealed class MemoryPEType : IPEType { - /// - public RVA ToRVA(PEInfo peInfo, FileOffset offset) { - return (RVA)offset; - } - - /// - public FileOffset ToFileOffset(PEInfo peInfo, RVA rva) { - return (FileOffset)rva; - } + public RVA ToRVA(PEInfo peInfo, FileOffset offset) => (RVA)offset; + public FileOffset ToFileOffset(PEInfo peInfo, RVA rva) => (FileOffset)rva; } /// - public bool IsFileImageLayout { - get { return peType is FilePEType; } - } + public bool IsFileImageLayout => peType is FilePEType; /// - public bool MayHaveInvalidAddresses { - get { return !IsFileImageLayout; } - } + public bool MayHaveInvalidAddresses => !IsFileImageLayout; /// - public string FileName { - get { return imageStreamCreator.FileName; } - } + public string Filename => dataReaderFactory.Filename; /// - public ImageDosHeader ImageDosHeader { - get { return peInfo.ImageDosHeader; } - } + public ImageDosHeader ImageDosHeader => peInfo.ImageDosHeader; /// - public ImageNTHeaders ImageNTHeaders { - get { return peInfo.ImageNTHeaders; } - } + public ImageNTHeaders ImageNTHeaders => peInfo.ImageNTHeaders; + + /// + public IList ImageSectionHeaders => peInfo.ImageSectionHeaders; /// - public IList ImageSectionHeaders { - get { return peInfo.ImageSectionHeaders; } + public IList ImageDebugDirectories { + get { + if (imageDebugDirectories is null) + imageDebugDirectories = ReadImageDebugDirectories(); + return imageDebugDirectories; + } } + ImageDebugDirectory[] imageDebugDirectories; + + /// + public DataReaderFactory DataReaderFactory => dataReaderFactory; /// public Win32Resources Win32Resources { - get { return win32Resources.Value; } + get => win32Resources.Value; set { IDisposable origValue = null; if (win32Resources.IsValueInitialized) { @@ -110,7 +97,7 @@ public Win32Resources Win32Resources { } win32Resources.Value = value; - if (origValue != null) + if (origValue is not null) origValue.Dispose(); } } @@ -118,15 +105,15 @@ public Win32Resources Win32Resources { /// /// Constructor /// - /// The PE stream creator + /// Data reader factory /// Image layout /// Verify PE file data - public PEImage(IImageStreamCreator imageStreamCreator, ImageLayout imageLayout, bool verify) { + public PEImage(DataReaderFactory dataReaderFactory, ImageLayout imageLayout, bool verify) { try { - this.imageStreamCreator = imageStreamCreator; - this.peType = ConvertImageLayout(imageLayout); - ResetReader(); - this.peInfo = new PEInfo(imageStream, verify); + this.dataReaderFactory = dataReaderFactory; + peType = ConvertImageLayout(imageLayout); + var reader = dataReaderFactory.CreateReader(); + peInfo = new PEInfo(ref reader, verify); Initialize(); } catch { @@ -147,27 +134,24 @@ void Initialize() { #endif } - static IPEType ConvertImageLayout(ImageLayout imageLayout) { - switch (imageLayout) { - case ImageLayout.File: return FileLayout; - case ImageLayout.Memory: return MemoryLayout; - default: throw new ArgumentException("imageLayout"); - } - } + static IPEType ConvertImageLayout(ImageLayout imageLayout) => + imageLayout switch { + ImageLayout.File => FileLayout, + ImageLayout.Memory => MemoryLayout, + _ => throw new ArgumentException("imageLayout"), + }; /// /// Constructor /// - /// Name of the file + /// Name of the file /// true if we should map it as an executable /// Verify PE file data - public PEImage(string fileName, bool mapAsImage, bool verify) - : this(ImageStreamCreator.Create(fileName, mapAsImage), mapAsImage ? ImageLayout.Memory : ImageLayout.File, verify) { + internal PEImage(string filename, bool mapAsImage, bool verify) + : this(DataReaderFactoryFactory.Create(filename, mapAsImage), mapAsImage ? ImageLayout.Memory : ImageLayout.File, verify) { try { - if (mapAsImage && imageStreamCreator is MemoryMappedFileStreamCreator) { - ((MemoryMappedFileStreamCreator)imageStreamCreator).Length = peInfo.GetImageSize(); - ResetReader(); - } + if (mapAsImage && dataReaderFactory is MemoryMappedDataReaderFactory) + ((MemoryMappedDataReaderFactory)dataReaderFactory).SetLength(peInfo.GetImageSize()); } catch { Dispose(); @@ -178,18 +162,29 @@ public PEImage(string fileName, bool mapAsImage, bool verify) /// /// Constructor /// - /// Name of the file + /// Name of the file /// Verify PE file data - public PEImage(string fileName, bool verify) - : this(fileName, USE_MEMORY_LAYOUT_WITH_MAPPED_FILES, verify) { + public PEImage(string filename, bool verify) + : this(filename, USE_MEMORY_LAYOUT_WITH_MAPPED_FILES, verify) { + } + + /// + /// Constructor + /// + /// Name of the file + public PEImage(string filename) + : this(filename, true) { } /// /// Constructor /// - /// Name of the file - public PEImage(string fileName) - : this(fileName, true) { + /// The PE file data + /// Filename or null + /// Image layout + /// Verify PE file data + public PEImage(byte[] data, string filename, ImageLayout imageLayout, bool verify) + : this(ByteArrayDataReaderFactory.Create(data, filename), imageLayout, verify) { } /// @@ -199,7 +194,7 @@ public PEImage(string fileName) /// Image layout /// Verify PE file data public PEImage(byte[] data, ImageLayout imageLayout, bool verify) - : this(new MemoryStreamCreator(data), imageLayout, verify) { + : this(data, null, imageLayout, verify) { } /// @@ -208,7 +203,17 @@ public PEImage(byte[] data, ImageLayout imageLayout, bool verify) /// The PE file data /// Verify PE file data public PEImage(byte[] data, bool verify) - : this(data, ImageLayout.File, verify) { + : this(data, null, ImageLayout.File, verify) { + } + + /// + /// Constructor + /// + /// The PE file data + /// Filename or null + /// Verify PE file data + public PEImage(byte[] data, string filename, bool verify) + : this(data, filename, ImageLayout.File, verify) { } /// @@ -216,7 +221,16 @@ public PEImage(byte[] data, bool verify) /// /// The PE file data public PEImage(byte[] data) - : this(data, true) { + : this(data, null, true) { + } + + /// + /// Constructor + /// + /// The PE file data + /// Filename or null + public PEImage(byte[] data, string filename) + : this(data, filename, true) { } /// @@ -226,8 +240,8 @@ public PEImage(byte[] data) /// Length of PE image /// Image layout /// Verify PE file data - public PEImage(IntPtr baseAddr, long length, ImageLayout imageLayout, bool verify) - : this(new UnmanagedMemoryStreamCreator(baseAddr, length), imageLayout, verify) { + public unsafe PEImage(IntPtr baseAddr, uint length, ImageLayout imageLayout, bool verify) + : this(NativeMemoryDataReaderFactory.Create((byte*)baseAddr, length, filename: null), imageLayout, verify) { } /// @@ -236,7 +250,7 @@ public PEImage(IntPtr baseAddr, long length, ImageLayout imageLayout, bool verif /// Address of PE image /// Length of PE image /// Verify PE file data - public PEImage(IntPtr baseAddr, long length, bool verify) + public PEImage(IntPtr baseAddr, uint length, bool verify) : this(baseAddr, length, ImageLayout.Memory, verify) { } @@ -245,7 +259,7 @@ public PEImage(IntPtr baseAddr, long length, bool verify) /// /// Address of PE image /// Length of PE image - public PEImage(IntPtr baseAddr, long length) + public PEImage(IntPtr baseAddr, uint length) : this(baseAddr, length, true) { } @@ -255,11 +269,10 @@ public PEImage(IntPtr baseAddr, long length) /// Address of PE image /// Image layout /// Verify PE file data - public PEImage(IntPtr baseAddr, ImageLayout imageLayout, bool verify) - : this(new UnmanagedMemoryStreamCreator(baseAddr, 0x10000), imageLayout, verify) { + public unsafe PEImage(IntPtr baseAddr, ImageLayout imageLayout, bool verify) + : this(NativeMemoryDataReaderFactory.Create((byte*)baseAddr, 0x10000, filename: null), imageLayout, verify) { try { - ((UnmanagedMemoryStreamCreator)imageStreamCreator).Length = peInfo.GetImageSize(); - ResetReader(); + ((NativeMemoryDataReaderFactory)dataReaderFactory).SetLength(peInfo.GetImageSize()); } catch { Dispose(); @@ -284,71 +297,75 @@ public PEImage(IntPtr baseAddr) : this(baseAddr, true) { } - void ResetReader() { - if (imageStream != null) { - imageStream.Dispose(); - imageStream = null; - } - imageStream = imageStreamCreator.CreateFull(); - } - /// - public RVA ToRVA(FileOffset offset) { - return peType.ToRVA(peInfo, offset); - } + public RVA ToRVA(FileOffset offset) => peType.ToRVA(peInfo, offset); /// - public FileOffset ToFileOffset(RVA rva) { - return peType.ToFileOffset(peInfo, rva); - } + public FileOffset ToFileOffset(RVA rva) => peType.ToFileOffset(peInfo, rva); /// public void Dispose() { IDisposable id; - if (win32Resources.IsValueInitialized && (id = win32Resources.Value) != null) - id.Dispose(); - if ((id = imageStream) != null) - id.Dispose(); - if ((id = imageStreamCreator) != null) + if (win32Resources.IsValueInitialized && (id = win32Resources.Value) is not null) id.Dispose(); + dataReaderFactory?.Dispose(); win32Resources.Value = null; - imageStream = null; - imageStreamCreator = null; + dataReaderFactory = null; peType = null; peInfo = null; } /// - public IImageStream CreateStream(FileOffset offset) { - if ((long)offset > imageStreamCreator.Length) - throw new ArgumentOutOfRangeException("offset"); - long length = imageStreamCreator.Length - (long)offset; - return CreateStream(offset, length); - } + public DataReader CreateReader(FileOffset offset) => + DataReaderFactory.CreateReader((uint)offset, DataReaderFactory.Length - (uint)offset); /// - public IImageStream CreateStream(FileOffset offset, long length) { - return imageStreamCreator.Create(offset, length); - } + public DataReader CreateReader(FileOffset offset, uint length) => + DataReaderFactory.CreateReader((uint)offset, length); /// - public IImageStream CreateFullStream() { - return imageStreamCreator.CreateFull(); - } + public DataReader CreateReader(RVA rva) => CreateReader(ToFileOffset(rva)); + + /// + public DataReader CreateReader(RVA rva, uint length) => CreateReader(ToFileOffset(rva), length); /// - public void UnsafeDisableMemoryMappedIO() { - var creator = imageStreamCreator as MemoryMappedFileStreamCreator; - if (creator != null) + public DataReader CreateReader() => DataReaderFactory.CreateReader(); + + void IInternalPEImage.UnsafeDisableMemoryMappedIO() { + if (dataReaderFactory is MemoryMappedDataReaderFactory creator) creator.UnsafeDisableMemoryMappedIO(); } - /// - public bool IsMemoryMappedIO { + bool IInternalPEImage.IsMemoryMappedIO { get { - var creator = imageStreamCreator as MemoryMappedFileStreamCreator; - return creator == null ? false : creator.IsMemoryMappedIO; + var creator = dataReaderFactory as MemoryMappedDataReaderFactory; + return creator is null ? false : creator.IsMemoryMappedIO; + } + } + + ImageDebugDirectory[] ReadImageDebugDirectories() { + try { + var dataDir = ImageNTHeaders.OptionalHeader.DataDirectories[6]; + if (dataDir.VirtualAddress == 0) + return Array2.Empty(); + var reader = DataReaderFactory.CreateReader(); + if (dataDir.Size > reader.Length) + return Array2.Empty(); + int count = (int)(dataDir.Size / 0x1C); + if (count == 0) + return Array2.Empty(); + reader.CurrentOffset = (uint)ToFileOffset(dataDir.VirtualAddress); + if ((ulong)reader.CurrentOffset + dataDir.Size > reader.Length) + return Array2.Empty(); + var res = new ImageDebugDirectory[count]; + for (int i = 0; i < res.Length; i++) + res[i] = new ImageDebugDirectory(ref reader, true); + return res; + } + catch (IOException) { } + return Array2.Empty(); } } } diff --git a/src/PE/PEInfo.cs b/src/PE/PEInfo.cs index 9271f79c1..3065c2564 100644 --- a/src/PE/PEInfo.cs +++ b/src/PE/PEInfo.cs @@ -5,7 +5,7 @@ namespace dnlib.PE { /// - /// Reads all PE sections from a PE stream + /// Reads all PE sections from a PE stream, for more information see https://docs.microsoft.com/en-us/windows/win32/debug/pe-format /// sealed class PEInfo { readonly ImageDosHeader imageDosHeader; @@ -15,23 +15,17 @@ sealed class PEInfo { /// /// Returns the DOS header /// - public ImageDosHeader ImageDosHeader { - get { return imageDosHeader; } - } + public ImageDosHeader ImageDosHeader => imageDosHeader; /// /// Returns the NT headers /// - public ImageNTHeaders ImageNTHeaders { - get { return imageNTHeaders; } - } + public ImageNTHeaders ImageNTHeaders => imageNTHeaders; /// /// Returns the section headers /// - public ImageSectionHeader[] ImageSectionHeaders { - get { return imageSectionHeaders; } - } + public ImageSectionHeader[] ImageSectionHeaders => imageSectionHeaders; /// /// Constructor @@ -39,19 +33,27 @@ public ImageSectionHeader[] ImageSectionHeaders { /// PE file reader pointing to the start of this section /// Verify sections /// Thrown if verification fails - public PEInfo(IImageStream reader, bool verify) { + public PEInfo(ref DataReader reader, bool verify) { reader.Position = 0; - this.imageDosHeader = new ImageDosHeader(reader, verify); + imageDosHeader = new ImageDosHeader(ref reader, verify); - if (verify && this.imageDosHeader.NTHeadersOffset == 0) + if (verify && imageDosHeader.NTHeadersOffset == 0) throw new BadImageFormatException("Invalid NT headers offset"); - reader.Position = this.imageDosHeader.NTHeadersOffset; - this.imageNTHeaders = new ImageNTHeaders(reader, verify); + reader.Position = imageDosHeader.NTHeadersOffset; + imageNTHeaders = new ImageNTHeaders(ref reader, verify); - reader.Position = (long)this.imageNTHeaders.OptionalHeader.StartOffset + this.imageNTHeaders.FileHeader.SizeOfOptionalHeader; - this.imageSectionHeaders = new ImageSectionHeader[this.imageNTHeaders.FileHeader.NumberOfSections]; - for (int i = 0; i < this.imageSectionHeaders.Length; i++) - this.imageSectionHeaders[i] = new ImageSectionHeader(reader, verify); + reader.Position = (uint)imageNTHeaders.OptionalHeader.StartOffset + imageNTHeaders.FileHeader.SizeOfOptionalHeader; + int numSections = imageNTHeaders.FileHeader.NumberOfSections; + if (numSections > 0) { + // Mono doesn't verify the section count + var tempReader = reader; + tempReader.Position += 0x14; + uint firstSectionOffset = tempReader.ReadUInt32(); + numSections = Math.Min(numSections, (int)((firstSectionOffset - reader.Position) / 0x28)); + } + imageSectionHeaders = new ImageSectionHeader[numSections]; + for (int i = 0; i < imageSectionHeaders.Length; i++) + imageSectionHeaders[i] = new ImageSectionHeader(ref reader, verify); } /// @@ -62,7 +64,7 @@ public PEInfo(IImageStream reader, bool verify) { /// public ImageSectionHeader ToImageSectionHeader(FileOffset offset) { foreach (var section in imageSectionHeaders) { - if ((long)offset >= section.PointerToRawData && (long)offset < section.PointerToRawData + section.SizeOfRawData) + if ((uint)offset >= section.PointerToRawData && (uint)offset < section.PointerToRawData + section.SizeOfRawData) return section; } return null; @@ -75,56 +77,77 @@ public ImageSectionHeader ToImageSectionHeader(FileOffset offset) { /// The RVA /// public ImageSectionHeader ToImageSectionHeader(RVA rva) { + uint alignment = imageNTHeaders.OptionalHeader.SectionAlignment; foreach (var section in imageSectionHeaders) { - if (rva >= section.VirtualAddress && rva < section.VirtualAddress + Math.Max(section.VirtualSize, section.SizeOfRawData)) + if (rva >= section.VirtualAddress && rva < section.VirtualAddress + DotNet.Utils.AlignUp(section.VirtualSize, alignment)) return section; } return null; } /// - /// Converts a to an + /// Converts a to an , returns 0 if out of range /// /// The file offset to convert /// The RVA public RVA ToRVA(FileOffset offset) { + // In pe headers + if (imageSectionHeaders.Length == 0) + return (RVA)offset; + + // In pe additional data, like digital signature, won't be loaded into memory + var lastSection = imageSectionHeaders[imageSectionHeaders.Length - 1]; + if ((uint)offset > lastSection.PointerToRawData + lastSection.SizeOfRawData) + return 0; + + // In a section var section = ToImageSectionHeader(offset); - if (section != null) + if (section is not null) return (uint)(offset - section.PointerToRawData) + section.VirtualAddress; + + // In pe headers return (RVA)offset; } /// - /// Converts an to a + /// Converts an to a , returns 0 if out of range /// /// The RVA to convert /// The file offset public FileOffset ToFileOffset(RVA rva) { + // Check if rva is larger than memory layout size + if ((uint)rva >= imageNTHeaders.OptionalHeader.SizeOfImage) + return 0; + var section = ToImageSectionHeader(rva); - if (section != null) - return (FileOffset)((long)(rva - section.VirtualAddress) + section.PointerToRawData); + if (section is not null) { + uint offset = rva - section.VirtualAddress; + // Virtual size may be bigger than raw size and there may be no corresponding file offset to rva + if (offset < section.SizeOfRawData) + return (FileOffset)offset + section.PointerToRawData; + return 0; + } + + // If not in any section, rva is in pe headers and don't convert it return (FileOffset)rva; } - static ulong alignUp(ulong val, uint alignment) { - return (val + alignment - 1) & ~(ulong)(alignment - 1); - } + static ulong AlignUp(ulong val, uint alignment) => (val + alignment - 1) & ~(ulong)(alignment - 1); /// /// Returns size of image rounded up to /// /// It calculates the size itself, and does not return /// Size of image in bytes - public long GetImageSize() { + public uint GetImageSize() { var optHdr = ImageNTHeaders.OptionalHeader; uint alignment = optHdr.SectionAlignment; - ulong len = alignUp(optHdr.SizeOfHeaders, alignment); - foreach (var section in imageSectionHeaders) { - ulong len2 = alignUp((ulong)section.VirtualAddress + Math.Max(section.VirtualSize, section.SizeOfRawData), alignment); - if (len2 > len) - len = len2; - } - return (long)len; + if (imageSectionHeaders.Length == 0) + return (uint)AlignUp(optHdr.SizeOfHeaders, alignment); + + // Section headers must be in ascending order and adjacent + var section = imageSectionHeaders[imageSectionHeaders.Length - 1]; + return (uint)Math.Min(AlignUp((ulong)section.VirtualAddress + section.VirtualSize, alignment), uint.MaxValue); } } } diff --git a/src/PE/ProcessorArchUtils.cs b/src/PE/ProcessorArchUtils.cs new file mode 100644 index 000000000..57599e167 --- /dev/null +++ b/src/PE/ProcessorArchUtils.cs @@ -0,0 +1,157 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace dnlib.PE { + static class ProcessorArchUtils { + static Machine cachedMachine = 0; + + public static Machine GetProcessCpuArchitecture() { + if (cachedMachine == 0) + cachedMachine = GetProcessCpuArchitectureCore(); + return cachedMachine; + } + + static class RuntimeInformationUtils { +#if NETSTANDARD || NETCOREAPP + public static bool TryGet_RuntimeInformation_Architecture(out Machine machine) => + TryGetArchitecture((int)RuntimeInformation.ProcessArchitecture, out machine); +#else + static Assembly RuntimeInformationAssembly => typeof(object).Assembly; + static Type System_Runtime_InteropServices_RuntimeInformation => RuntimeInformationAssembly.GetType("System.Runtime.InteropServices.RuntimeInformation", throwOnError: false); + + public static bool TryGet_RuntimeInformation_Architecture(out Machine machine) { + machine = 0; + var processArchitectureMethod = System_Runtime_InteropServices_RuntimeInformation?.GetMethod("get_ProcessArchitecture", Array2.Empty()); + if (processArchitectureMethod is null) + return false; + + var result = processArchitectureMethod.Invoke(null, Array2.Empty()); + return TryGetArchitecture((int)result, out machine); + } +#endif + + static bool TryGetArchitecture(int architecture, out Machine machine) { + switch (architecture) { + case 0: // Architecture.X86 + Debug.Assert(IntPtr.Size == 4); + machine = Machine.I386; + return true; + + case 1: // Architecture.X64 + Debug.Assert(IntPtr.Size == 8); + machine = Machine.AMD64; + return true; + + case 2: // Architecture.Arm + Debug.Assert(IntPtr.Size == 4); + machine = Machine.ARMNT; + return true; + + case 3: // Architecture.Arm64 + Debug.Assert(IntPtr.Size == 8); + machine = Machine.ARM64; + return true; + + default: + Debug.Fail($"Unknown process architecture: {architecture}"); + machine = 0; + return false; + } + } + } + + static Machine GetProcessCpuArchitectureCore() { + if (WindowsUtils.TryGetProcessCpuArchitecture(out var machine)) + return machine; + try { + if (RuntimeInformationUtils.TryGet_RuntimeInformation_Architecture(out machine)) + return machine; + } + catch (PlatformNotSupportedException) { + } + + Debug.WriteLine("Couldn't detect CPU arch, assuming x86 or x64"); + return IntPtr.Size == 4 ? Machine.I386 : Machine.AMD64; + } + + static class WindowsUtils { + [DllImport("kernel32")] + static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo); + + struct SYSTEM_INFO { + public ushort wProcessorArchitecture; + public ushort wReserved; + public uint dwPageSize; + public IntPtr lpMinimumApplicationAddress; + public IntPtr lpMaximumApplicationAddress; + public IntPtr dwActiveProcessorMask; + public uint dwNumberOfProcessors; + public uint dwProcessorType; + public uint dwAllocationGranularity; + public ushort wProcessorLevel; + public ushort wProcessorRevision; + } + + enum ProcessorArchitecture : ushort { + INTEL = 0, + ARM = 5, + IA64 = 6, + AMD64 = 9, + ARM64 = 12, + UNKNOWN = 0xFFFF, + } + + public static bool TryGetProcessCpuArchitecture(out Machine machine) { + if (canTryGetSystemInfo) { + try { + GetSystemInfo(out var sysInfo); + switch ((ProcessorArchitecture)sysInfo.wProcessorArchitecture) { + case ProcessorArchitecture.INTEL: + Debug.Assert(IntPtr.Size == 4); + machine = Machine.I386; + return true; + + case ProcessorArchitecture.ARM: + Debug.Assert(IntPtr.Size == 4); + machine = Machine.ARMNT; + return true; + + case ProcessorArchitecture.IA64: + Debug.Assert(IntPtr.Size == 8); + machine = Machine.IA64; + return true; + + case ProcessorArchitecture.AMD64: + Debug.Assert(IntPtr.Size == 8); + machine = Machine.AMD64; + return true; + + case ProcessorArchitecture.ARM64: + Debug.Assert(IntPtr.Size == 8); + machine = Machine.ARM64; + return true; + + case ProcessorArchitecture.UNKNOWN: + default: + break; + } + } + catch (EntryPointNotFoundException) { + canTryGetSystemInfo = false; + } + catch (DllNotFoundException) { + canTryGetSystemInfo = false; + } + } + + machine = 0; + return false; + } + static bool canTryGetSystemInfo = true; + } + } +} diff --git a/src/PE/RVA.cs b/src/PE/RVA.cs index 74179ef2a..14cc70fa9 100644 --- a/src/PE/RVA.cs +++ b/src/PE/RVA.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.PE { +namespace dnlib.PE { /// /// Represents an RVA (relative virtual address) /// @@ -13,17 +13,13 @@ partial class PEExtensions { /// /// this /// Alignment - public static RVA AlignUp(this RVA rva, uint alignment) { - return (RVA)(((uint)rva + alignment - 1) & ~(alignment - 1)); - } + public static RVA AlignUp(this RVA rva, uint alignment) => (RVA)(((uint)rva + alignment - 1) & ~(alignment - 1)); /// /// Align up /// /// this /// Alignment - public static RVA AlignUp(this RVA rva, int alignment) { - return (RVA)(((uint)rva + alignment - 1) & ~(alignment - 1)); - } + public static RVA AlignUp(this RVA rva, int alignment) => (RVA)(((uint)rva + alignment - 1) & ~(alignment - 1)); } } diff --git a/src/PE/Subsystem.cs b/src/PE/Subsystem.cs index c89818283..7f24dbcf4 100644 --- a/src/PE/Subsystem.cs +++ b/src/PE/Subsystem.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.PE { +namespace dnlib.PE { /// /// IMAGE_OPTIONAL_HEADER.Subsystem /// diff --git a/src/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs deleted file mode 100644 index 011f476ca..000000000 --- a/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,20 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System.Reflection; -using System.Runtime.InteropServices; - -#if THREAD_SAFE -[assembly: AssemblyTitle("dnlib (thread safe)")] -#else -[assembly: AssemblyTitle("dnlib")] -#endif -[assembly: AssemblyDescription(".NET assembly reader/writer")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("dnlib")] -[assembly: AssemblyCopyright("Copyright (C) 2012-2015 de4dot@gmail.com")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -[assembly: AssemblyVersion("1.5.0.1500")] -[assembly: AssemblyFileVersion("1.5.0.1500")] diff --git a/src/Settings.cs b/src/Settings.cs index 277b25470..5b3af88be 100644 --- a/src/Settings.cs +++ b/src/Settings.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info namespace dnlib { /// diff --git a/src/Threading/Extensions.cs b/src/Threading/Extensions.cs deleted file mode 100644 index 4bb3c8889..000000000 --- a/src/Threading/Extensions.cs +++ /dev/null @@ -1,9 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -namespace dnlib.Threading { - /// - /// Extension methods - /// - public static partial class Extensions { - } -} diff --git a/src/Threading/ICancellationToken.cs b/src/Threading/ICancellationToken.cs index e7f74c0be..8558f122a 100644 --- a/src/Threading/ICancellationToken.cs +++ b/src/Threading/ICancellationToken.cs @@ -1,4 +1,4 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; diff --git a/src/Threading/IThreadSafeList.cs b/src/Threading/IThreadSafeList.cs deleted file mode 100644 index 205d59df1..000000000 --- a/src/Threading/IThreadSafeList.cs +++ /dev/null @@ -1,828 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Collections.Generic; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif - -namespace dnlib.Threading.Collections { -#if THREAD_SAFE - /// - /// Thread-safe interface - /// - /// List type - public interface IList : System.Collections.Generic.IList { - /// - /// Must only be called when the list lock is held. Gets the index of - /// - /// Item - /// Index of or -1 if it's not present in the list - /// - int IndexOf_NoLock(T item); - - /// - /// Must only be called when the list lock is held. Inserts at index - /// - /// - /// Index - /// Item to insert - /// - void Insert_NoLock(int index, T item); - - /// - /// Must only be called when the list lock is held. Removes the item at index - /// - /// - /// - /// - void RemoveAt_NoLock(int index); - - /// - /// Must only be called when the list lock is held. Returns the value at a specified index. - /// - /// Index - /// Value - /// - T Get_NoLock(int index); - - /// - /// Must only be called when the list lock is held. Writes to the list at a specified index. - /// - /// Index - /// Value - /// - void Set_NoLock(int index, T value); - - /// - /// Must only be called when the list lock is held. Adds a new element to the end of the - /// list. - /// - /// Item - /// - void Add_NoLock(T item); - - /// - /// Must only be called when the list lock is held. Clears the list. - /// - /// - void Clear_NoLock(); - - /// - /// Must only be called when the list lock is held. Checks whether - /// exists in the list. - /// - /// Item - /// true if exists in the list, else false - /// - bool Contains_NoLock(T item); - - /// - /// Must only be called when the list lock is held. Copies the list to an array. - /// - /// Destination array - /// Destination array index - /// - void CopyTo_NoLock(T[] array, int arrayIndex); - - /// - /// Must only be called when the list lock is held. Returns the size of the list. - /// - /// - int Count_NoLock { get; } - - /// - /// Must only be called when the list lock is held. Returns true if the list is - /// read-only, false if it's writable. - /// - /// - bool IsReadOnly_NoLock { get; } - - /// - /// Must only be called when the list lock is held. Removes from the - /// list. - /// - /// Item - /// true if was removed, false if - /// was never inserted in the list. - /// - bool Remove_NoLock(T item); - - /// - /// Must only be called when the list lock is held. Gets the enumerator. - /// - /// A new enumerator instance - /// - IEnumerator GetEnumerator_NoLock(); - - /// - /// Locks the list and then calls . - /// must only call *_NoLock() methods. The list is unlocked once this method returns. - /// - /// Argument type - /// Return type - /// Passed to - /// Handler that should execute when the lock is held - /// The value returns - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - TRetType ExecuteLocked(TArgType arg, ExecuteLockedDelegate handler); - } -#endif -} - -namespace dnlib.Threading { - /// - /// Passed to ExecuteLocked() - /// - /// Type to store in list - /// Argument type - /// Return type - /// A thread-safe list - /// The argument - /// Any value the user wants to return - public delegate TRetType ExecuteLockedDelegate(ThreadSafe.IList tsList, TArgType arg); - -#if THREAD_SAFE - /// - /// Called by - /// - /// Type to store in list - /// A thread-safe list - /// Index of - /// Value at in the list - /// false to break out of the iterator loop and return - public delegate bool IterateDelegate(ThreadSafe.IList tsList, int index, T value); - - /// - /// Called by - /// and - /// - /// Type to store in list - /// A thread-safe list - /// Index of - /// Value at in the list - public delegate void IterateAllDelegate(ThreadSafe.IList tsList, int index, T value); -#endif - - /// - /// Called by - /// - /// Type to store in list - /// A list - /// Index of - /// Value at in the list - /// false to break out of the iterator loop and return - public delegate bool ListIterateDelegate(IList list, int index, T value); - - /// - /// Called by - /// and - /// - /// Type to store in list - /// A list - /// Index of - /// Value at in the list - /// false to break out of the iterator loop and return - public delegate void ListIterateAllDelegate(IList list, int index, T value); - - /// - /// Called by - /// - /// Type stored in enumerable - /// Index of - /// Value at in the collection - /// false to break out of the iterator loop and return - public delegate bool EnumerableIterateDelegate(int index, T value); - - /// - /// Called by - /// - /// Type stored in enumerable - /// Index of - /// Value at in the collection - /// false to break out of the iterator loop and return - public delegate void EnumerableIterateAllDelegate(int index, T value); - - public static partial class Extensions { - /// - /// Locks the list and then calls . - /// must only call *_NoLock() methods. The list is unlocked once this method returns. - /// - /// List type - /// Argument type - /// Return type - /// A list - /// Passed to - /// Handler that should execute when the lock is held - /// The value returns - public static TRetType ExecuteLocked(this ThreadSafe.IList tsList, TArgType arg, ExecuteLockedDelegate handler) { -#if THREAD_SAFE - return tsList.ExecuteLocked(arg, handler); -#else - return handler(tsList, arg); -#endif - } - -#if THREAD_SAFE - /// - /// Iterates over elements in and calls - /// - /// Type to store in list - /// A thread-safe list - /// Called for each element - /// Start index - /// End index. -1 means - /// true if we should iterate in the reverse order - public static void Iterate(this ThreadSafe.IList tsList, int startIndex, int endIndex, bool reverseOrder, IterateDelegate handler) { - tsList.ExecuteLocked(null, (tsList2, arg) => { - if (reverseOrder) { - int i = (endIndex < 0 ? tsList2.Count_NoLock : endIndex) - 1; - for (; i >= startIndex; i--) { - if (!handler(tsList2, i, tsList2.Get_NoLock(i))) - break; - } - } - else { - // Count property can change so check it each time in the loop - for (int i = startIndex; i < (endIndex < 0 ? tsList2.Count_NoLock : endIndex); i++) { - if (!handler(tsList2, i, tsList2.Get_NoLock(i))) - break; - } - } - return null; - }); - } - - /// - /// Iterates over all elements in and calls - /// - /// Type to store in list - /// A thread-safe list - /// Called for each element - public static void Iterate(this ThreadSafe.IList tsList, IterateDelegate handler) { - tsList.Iterate(0, -1, false, handler); - } - - /// - /// Iterates over all elements in and calls - /// - /// Type to store in list - /// A thread-safe list - /// Called for each element - public static void IterateAll(this ThreadSafe.IList tsList, IterateAllDelegate handler) { - tsList.Iterate(0, -1, false, (tsList2, index, value) => { - handler(tsList2, index, value); - return true; - }); - } - - /// - /// Iterates over all elements in in the reverse order and calls - /// - /// - /// Type to store in list - /// A thread-safe list - /// Called for each element - public static void IterateReverse(this ThreadSafe.IList tsList, IterateDelegate handler) { - tsList.Iterate(0, -1, true, handler); - } - - /// - /// Iterates over all elements in in the reverse order and calls - /// - /// - /// Type to store in list - /// A thread-safe list - /// Called for each element - public static void IterateAllReverse(this ThreadSafe.IList tsList, IterateAllDelegate handler) { - tsList.Iterate(0, -1, true, (tsList2, index, value) => { - handler(tsList2, index, value); - return true; - }); - } -#endif - - /// - /// Iterates over elements in and calls . - /// If implements , only thread safe - /// methods are called. - /// - /// Type to store in list - /// A list - /// Called for each element - /// Start index - /// End index. -1 means Count_NoLock - /// true if we should iterate in the reverse order - public static void Iterate(this IList list, int startIndex, int endIndex, bool reverseOrder, ListIterateDelegate handler) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - tsList.Iterate(startIndex, endIndex, reverseOrder, (tsList2, index, value) => handler(tsList2, index, value)); - else { -#endif - if (reverseOrder) { - int i = (endIndex < 0 ? list.Count : endIndex) - 1; - for (; i >= startIndex; i--) { - if (!handler(list, i, list[i])) - break; - } - } - else { - // Count property can change so check it each time in the loop - for (int i = startIndex; i < (endIndex < 0 ? list.Count : endIndex); i++) { - if (!handler(list, i, list[i])) - break; - } - } -#if THREAD_SAFE - } -#endif - } - - /// - /// Iterates over all elements in and calls - /// . If implements - /// , only thread safe methods are called. - /// - /// Type to store in list - /// A list - /// Called for each element - public static void Iterate(this IList list, ListIterateDelegate handler) { - list.Iterate(0, -1, false, handler); - } - - /// - /// Iterates over all elements in and calls - /// . If implements - /// , only thread safe methods are called. - /// - /// Type to store in list - /// A list - /// Called for each element - public static void IterateAll(this IList list, ListIterateAllDelegate handler) { - list.Iterate(0, -1, false, (list2, index, value) => { - handler(list2, index, value); - return true; - }); - } - - /// - /// Iterates over all elements in in the reverse order and calls - /// . If implements - /// , only thread safe methods are called. - /// - /// Type to store in list - /// A list - /// Called for each element - public static void IterateReverse(this IList list, ListIterateDelegate handler) { - list.Iterate(0, -1, true, handler); - } - - /// - /// Iterates over all elements in in the reverse order and calls - /// . If implements - /// , only thread safe methods are called. - /// - /// Type to store in list - /// A list - /// Called for each element - public static void IterateAllReverse(this IList list, ListIterateAllDelegate handler) { - list.Iterate(0, -1, true, (list2, index, value) => { - handler(list2, index, value); - return true; - }); - } - - /// - /// Iterates over all elements in and calls - /// . If implements - /// , only thread safe methods are called. - /// - /// Type to store in list - /// A list - /// Called for each element - public static void Iterate(this IEnumerable list, EnumerableIterateDelegate handler) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - tsList.Iterate((tsList2, index, value) => handler(index, value)); - else { -#endif - int i = 0; - foreach (var value in list) { - if (!handler(i, value)) - break; - i++; - } -#if THREAD_SAFE - } -#endif - } - - /// - /// Iterates over all elements in and calls - /// . If implements - /// , only thread safe methods are called. - /// - /// Type to store in list - /// A list - /// Called for each element - public static void IterateAll(this IEnumerable list, EnumerableIterateAllDelegate handler) { - list.Iterate((index, value) => { - handler(index, value); - return true; - }); - } - - /// - /// Reads an element from the list. If implements - /// , only thread safe methods are called. - /// - /// Type to store in list - /// A list - /// Index - /// Updated with value - /// true if was updated with the element in the - /// list or false if was invalid. - public static bool Get(this IList list, int index, out T value) { -#if THREAD_SAFE - try { -#endif - if ((uint)index < (uint)list.Count) { - value = list[index]; - return true; - } -#if THREAD_SAFE - } - catch (IndexOutOfRangeException) { - } - catch (ArgumentOutOfRangeException) { - } -#endif - value = default(T); - return false; - } - - /// - /// Reads an element from the list. If implements - /// , only thread safe methods are called. - /// - /// Type to store in list - /// A list - /// Index - /// Default value if is invalid - /// The value in the list or if - /// was invalid - public static T Get(this IList list, int index, T defaultValue) { - T value; - return list.Get(index, out value) ? value : defaultValue; - } - - /// - /// Writes an element to the list. If implements - /// , only thread safe methods are called. - /// - /// Type to store in list - /// A list - /// Index - /// Value - /// true if was written to the list or false - /// if was invalid. - public static bool Set(this IList list, int index, T value) { -#if THREAD_SAFE - try { -#endif - if ((uint)index < (uint)list.Count) { - list[index] = value; - return true; - } -#if THREAD_SAFE - } - catch (IndexOutOfRangeException) { - } - catch (ArgumentOutOfRangeException) { - } -#endif - return false; - } - -#if THREAD_SAFE - /// - /// Calls - /// - /// Type to store in list - /// A thread-safe list - /// Number of elements in the list - public static int Count_NoLock(this ThreadSafe.IList tsList) { - return tsList.Count_NoLock; - } - - /// - /// Calls - /// - /// Type to store in list - /// A thread-safe list - public static bool IsReadOnly_NoLock(this ThreadSafe.IList tsList) { - return tsList.IsReadOnly_NoLock; - } -#endif - - /// - /// Calls the thread-safe IndexOf_NoLock() method if implements - /// a thread-safe list interface, else calls setter - /// - /// Type to store in list - /// An - /// Item - /// Index of - public static int IndexOf_NoLock(this IList list, T item) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - return tsList.IndexOf_NoLock(item); - else -#endif - return list.IndexOf(item); - } - - /// - /// Calls the thread-safe Insert_NoLock() method if implements - /// a thread-safe list interface, else calls setter - /// - /// Type to store in list - /// An - /// Index - /// Item - public static void Insert_NoLock(this IList list, int index, T item) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - tsList.Insert_NoLock(index, item); - else -#endif - list.Insert(index, item); - } - - /// - /// Calls the thread-safe RemoveAt_NoLock() method if implements - /// a thread-safe list interface, else calls setter - /// - /// Type to store in list - /// An - /// Index - public static void RemoveAt_NoLock(this IList list, int index) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - tsList.RemoveAt_NoLock(index); - else -#endif - list.RemoveAt(index); - } - - /// - /// Calls the thread-safe Get_NoLock() method if implements - /// a thread-safe list interface, else calls setter - /// - /// Type to store in list - /// An - /// Index - /// Value at index - public static T Get_NoLock(this IList list, int index) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - return tsList.Get_NoLock(index); - else -#endif - return list[index]; - } - - /// - /// Calls the thread-safe Set_NoLock() method if implements - /// a thread-safe list interface, else calls setter - /// - /// Type to store in list - /// An - /// Index - /// Value - public static void Set_NoLock(this IList list, int index, T value) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - tsList.Set_NoLock(index, value); - else -#endif - list[index] = value; - } - - /// - /// Calls the thread-safe Add_NoLock() method if implements - /// a thread-safe list interface, else calls setter - /// - /// Type to store in list - /// An - /// Item - public static void Add_NoLock(this ICollection list, T item) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - tsList.Add_NoLock(item); - else -#endif - list.Add(item); - } - - /// - /// Calls the thread-safe Clear_NoLock() method if implements - /// a thread-safe list interface, else calls setter - /// - /// Type to store in list - /// An - public static void Clear_NoLock(this ICollection list) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - tsList.Clear_NoLock(); - else -#endif - list.Clear(); - } - - /// - /// Calls the thread-safe Contains_NoLock() method if implements - /// a thread-safe list interface, else calls setter - /// - /// Type to store in list - /// An - /// Item - /// true if is in the list, else false - public static bool Contains_NoLock(this ICollection list, T item) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - return tsList.Contains_NoLock(item); - else -#endif - return list.Contains(item); - } - - /// - /// Calls the thread-safe CopyTo_NoLock() method if implements - /// a thread-safe list interface, else calls setter - /// - /// Type to store in list - /// An - /// Destination array - /// Destination index - public static void CopyTo_NoLock(this ICollection list, T[] array, int arrayIndex) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - tsList.CopyTo_NoLock(array, arrayIndex); - else -#endif - list.CopyTo(array, arrayIndex); - } - - /// - /// Calls the thread-safe Count_NoLock() method if implements - /// a thread-safe list interface, else calls setter - /// - /// Type to store in list - /// An - /// Number of elements in the list - public static int Count_NoLock(this ICollection list) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - return tsList.Count_NoLock; - else -#endif - return list.Count; - } - - /// - /// Calls the thread-safe IsReadOnly_NoLock() method if implements - /// a thread-safe list interface, else calls setter - /// - /// Type to store in list - /// An - public static bool IsReadOnly_NoLock(this ICollection list) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - return tsList.IsReadOnly_NoLock; - else -#endif - return list.IsReadOnly; - } - - /// - /// Calls the thread-safe Remove_NoLock() method if implements - /// a thread-safe list interface, else calls setter - /// - /// Type to store in list - /// An - /// Item - /// true if was removed, else false - public static bool Remove_NoLock(this ICollection list, T item) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - return tsList.Remove_NoLock(item); - else -#endif - return list.Remove(item); - } - - /// - /// Calls the thread-safe GetEnumerator_NoLock() method if implements - /// a thread-safe list interface, else calls setter - /// - /// Type to store in list - /// An - /// A new instance - public static IEnumerator GetEnumerator_NoLock(this IEnumerable list) { -#if THREAD_SAFE - var tsList = list as ThreadSafe.IList; - if (tsList != null) - return tsList.GetEnumerator_NoLock(); - else -#endif - return list.GetEnumerator(); - } - - /// - /// Calls to get an - /// which is used to iterate over the whole list. Each item is - /// then returned to the caller. - /// - /// Type to store in list - /// An - /// All items of the list - public static IEnumerable GetEnumerable_NoLock(this ICollection list) { - using (var enumerator = list.GetEnumerator_NoLock()) { - while (enumerator.MoveNext()) - yield return enumerator.Current; - } - } - - /// - /// Iterates over the whole list but doesn't keep the lock. It doesn't use any enumerator - /// so no exception can be thrown if another thread modifies the list. - /// - /// Type to store in list - /// A list - /// A list enumerable - public static IEnumerable GetSafeEnumerable(this IList list) { - for (int i = 0; i < list.Count; i++) { - T value; -#if THREAD_SAFE - try { -#endif - value = list[i]; -#if THREAD_SAFE - } - catch (IndexOutOfRangeException) { - break; - } - catch (ArgumentOutOfRangeException) { - break; - } -#endif - yield return value; - } - } - - /// - /// Iterates over the whole list but doesn't keep the lock. It doesn't use any enumerator - /// so no exception can be thrown if another thread modifies the list. - /// - /// Type to store in list - /// A collection - /// A list enumerable - public static IEnumerable GetSafeEnumerable(this IEnumerable coll) { - var list = coll as IList; - if (list != null) - return GetSafeEnumerable(list); - - return coll; - } - } -} diff --git a/src/Threading/Lock.cs b/src/Threading/Lock.cs index 5bbebc177..0a8475592 100644 --- a/src/Threading/Lock.cs +++ b/src/Threading/Lock.cs @@ -1,6 +1,7 @@ -// dnlib: See LICENSE.txt for more info +// dnlib: See LICENSE.txt for more info using System; +using System.Runtime.Serialization; using System.Threading; namespace dnlib.Threading { @@ -13,10 +14,14 @@ public LockException() { public LockException(string msg) : base(msg) { } + + protected LockException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } } /// - /// Simple class using and + /// Simple class using Monitor.Enter() and Monitor.Exit() /// and just like ReaderWriterLockSlim it prevents recursive locks. It doesn't support /// multiple readers. A reader lock is the same as a writer lock. /// @@ -28,16 +33,14 @@ class Lock { /// Creates a new instance of this class /// /// - public static Lock Create() { - return new Lock(); - } + public static Lock Create() => new Lock(); /// /// Constructor /// Lock() { - this.lockObj = new object(); - this.recurseCount = 0; + lockObj = new object(); + recurseCount = 0; } /// diff --git a/src/Threading/ThreadSafeListCreator.cs b/src/Threading/ThreadSafeListCreator.cs deleted file mode 100644 index 33d00d3a2..000000000 --- a/src/Threading/ThreadSafeListCreator.cs +++ /dev/null @@ -1,142 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System.Collections.Generic; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif - -namespace dnlib.Threading { - /// - /// Creates thread-safe lists - /// - public static class ThreadSafeListCreator { - /// - /// Creates a thread safe - /// - /// List type - /// A new thread-safe list instance - public static ThreadSafe.IList Create() { - var list = new List(); -#if THREAD_SAFE - return new ThreadSafeListWrapper(list); -#else - return list; -#endif - } - - /// - /// Creates a thread safe - /// - /// List type - /// Value to add to the list - /// A new thread-safe list instance - public static ThreadSafe.IList Create(T value) { - var list = new List() { value }; -#if THREAD_SAFE - return new ThreadSafeListWrapper(list); -#else - return list; -#endif - } - - /// - /// Creates a thread safe - /// - /// List type - /// Value #1 to add to the list - /// Value #2 to add to the list - /// A new thread-safe list instance - public static ThreadSafe.IList Create(T value1, T value2) { - var list = new List() { value1, value2 }; -#if THREAD_SAFE - return new ThreadSafeListWrapper(list); -#else - return list; -#endif - } - - /// - /// Creates a thread safe - /// - /// List type - /// Value #1 to add to the list - /// Value #2 to add to the list - /// Value #3 to add to the list - /// A new thread-safe list instance - public static ThreadSafe.IList Create(T value1, T value2, T value3) { - var list = new List() { value1, value2, value3 }; -#if THREAD_SAFE - return new ThreadSafeListWrapper(list); -#else - return list; -#endif - } - - /// - /// Creates a thread safe - /// - /// List type - /// Values to add to the list - /// A new thread-safe list instance - public static ThreadSafe.IList Create(params T[] args) { - var list = new List(args); -#if THREAD_SAFE - return new ThreadSafeListWrapper(list); -#else - return list; -#endif - } - - /// - /// Creates a thread safe - /// - /// List type - /// List capacity - /// A new thread-safe list instance - public static ThreadSafe.IList Create(int capacity) { - var list = new List(capacity); -#if THREAD_SAFE - return new ThreadSafeListWrapper(list); -#else - return list; -#endif - } - - /// - /// Creates a thread safe - /// - /// List type - /// Values to copy to the new list - /// A new thread-safe list instance - public static ThreadSafe.IList Create(IEnumerable collection) { - var list = new List(collection); -#if THREAD_SAFE - return new ThreadSafeListWrapper(list); -#else - return list; -#endif - } - - /// - /// Makes a list thread-safe by using a thread-safe wrapper list - /// - /// List type - /// The list that should be made thread-safe - /// A thread-safe list using as the underlying list - public static ThreadSafe.IList MakeThreadSafe(IList list) { -#if THREAD_SAFE - if (list == null) - return null; - var tsList = list as ThreadSafe.IList; - if (tsList != null) - return tsList; - return new ThreadSafeListWrapper(list); -#else - return list; -#endif - } - } -} diff --git a/src/Threading/ThreadSafeListWrapper.cs b/src/Threading/ThreadSafeListWrapper.cs deleted file mode 100644 index 8c6dae1e2..000000000 --- a/src/Threading/ThreadSafeListWrapper.cs +++ /dev/null @@ -1,209 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -using System; -using System.Collections.Generic; -using System.Diagnostics; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif - -namespace dnlib.Threading { -#if THREAD_SAFE - /// - /// Protects an from being accessed by multiple threads at the same time - /// - /// List type - [DebuggerDisplay("Count = {Count}")] - sealed class ThreadSafeListWrapper : ThreadSafe.IList { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - readonly Lock theLock = Lock.Create(); - - readonly IList list; - - /// - /// Constructor - /// - /// A list - public ThreadSafeListWrapper(IList list) { - if (list == null) - throw new ArgumentNullException("list"); - this.list = list; - } - - /// - public int IndexOf(T item) { - // We need a write lock since we don't know whether 'list' modifies any internal fields. - theLock.EnterWriteLock(); try { - return list.IndexOf(item); - } finally { theLock.ExitWriteLock(); } - } - - /// - public void Insert(int index, T item) { - theLock.EnterWriteLock(); try { - list.Insert(index, item); - } finally { theLock.ExitWriteLock(); } - } - - /// - public void RemoveAt(int index) { - theLock.EnterWriteLock(); try { - list.RemoveAt(index); - } finally { theLock.ExitWriteLock(); } - } - - /// - public T this[int index] { - get { - theLock.EnterWriteLock(); try { - return list[index]; - } finally { theLock.ExitWriteLock(); } - } - set { - theLock.EnterWriteLock(); try { - list[index] = value; - } finally { theLock.ExitWriteLock(); } - } - } - - /// - public void Add(T item) { - theLock.EnterWriteLock(); try { - list.Add(item); - } finally { theLock.ExitWriteLock(); } - } - - /// - public void Clear() { - theLock.EnterWriteLock(); try { - list.Clear(); - } finally { theLock.ExitWriteLock(); } - } - - /// - public bool Contains(T item) { - theLock.EnterWriteLock(); try { - return list.Contains(item); - } finally { theLock.ExitWriteLock(); } - } - - /// - public void CopyTo(T[] array, int arrayIndex) { - theLock.EnterWriteLock(); try { - list.CopyTo(array, arrayIndex); - } finally { theLock.ExitWriteLock(); } - } - - /// - public int Count { - get { - theLock.EnterWriteLock(); try { - return list.Count; - } finally { theLock.ExitWriteLock(); } - } - } - - /// - public bool IsReadOnly { - get { - theLock.EnterWriteLock(); try { - return list.IsReadOnly; - } finally { theLock.ExitWriteLock(); } - } - } - - /// - public bool Remove(T item) { - theLock.EnterWriteLock(); try { - return list.Remove(item); - } finally { theLock.ExitWriteLock(); } - } - - /// - public IEnumerator GetEnumerator() { - theLock.EnterWriteLock(); try { - return list.GetEnumerator(); - } finally { theLock.ExitWriteLock(); } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return GetEnumerator(); - } - - /// - public int Count_NoLock { - get { return list.Count; } - } - - /// - public bool IsReadOnly_NoLock { - get { return list.IsReadOnly; } - } - - /// - public int IndexOf_NoLock(T item) { - return list.IndexOf(item); - } - - /// - public void Insert_NoLock(int index, T item) { - list.Insert(index, item); - } - - /// - public void RemoveAt_NoLock(int index) { - list.RemoveAt(index); - } - - /// - public T Get_NoLock(int index) { - return list[index]; - } - - /// - public void Set_NoLock(int index, T value) { - list[index] = value; - } - - /// - public void Add_NoLock(T item) { - list.Add(item); - } - - /// - public void Clear_NoLock() { - list.Clear(); - } - - /// - public bool Contains_NoLock(T item) { - return list.Contains(item); - } - - /// - public void CopyTo_NoLock(T[] array, int arrayIndex) { - list.CopyTo(array, arrayIndex); - } - - /// - public bool Remove_NoLock(T item) { - return list.Remove(item); - } - - /// - public IEnumerator GetEnumerator_NoLock() { - return list.GetEnumerator(); - } - - /// - public TRetType ExecuteLocked(TArgType arg, ExecuteLockedDelegate handler) { - theLock.EnterWriteLock(); try { - return handler(this, arg); - } finally { theLock.ExitWriteLock(); } - } - } -#endif -} diff --git a/src/Utils/ArrayEmpty.cs b/src/Utils/ArrayEmpty.cs new file mode 100644 index 000000000..f056fc379 --- /dev/null +++ b/src/Utils/ArrayEmpty.cs @@ -0,0 +1,12 @@ +// dnlib: See LICENSE.txt for more info + +// System namespace so it can easily be replaced with Array.Empty later +namespace System { + static class Array2 { + public static T[] Empty() => EmptyClass.Empty; + + static class EmptyClass { + public static readonly T[] Empty = new T[0]; + } + } +} diff --git a/src/Utils/CollectionDebugView.cs b/src/Utils/CollectionDebugView.cs new file mode 100644 index 000000000..d56d09daf --- /dev/null +++ b/src/Utils/CollectionDebugView.cs @@ -0,0 +1,35 @@ +// dnlib: See LICENSE.txt for more info + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace dnlib.Utils { + class CollectionDebugView { + readonly ICollection list; + public CollectionDebugView(ICollection list) => this.list = list ?? throw new ArgumentNullException(nameof(list)); + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public TValue[] Items { + get { + var array = new TValue[list.Count]; + list.CopyTo(array, 0); + return array; + } + } + } + + class CollectionDebugView : CollectionDebugView { + public CollectionDebugView(ICollection list) : base(list) { } + } + + sealed class LocalList_CollectionDebugView : CollectionDebugView { + public LocalList_CollectionDebugView(LocalList list) : base(list) { } + } + + sealed class ParameterList_CollectionDebugView : CollectionDebugView { + public ParameterList_CollectionDebugView(ParameterList list) : base(list) { } + } +} diff --git a/src/Utils/Extensions.cs b/src/Utils/Extensions.cs deleted file mode 100644 index 88fb2f6da..000000000 --- a/src/Utils/Extensions.cs +++ /dev/null @@ -1,9 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -namespace dnlib.Utils { - /// - /// Extension methods - /// - public static partial class Extensions { - } -} diff --git a/src/Utils/ILazyList.cs b/src/Utils/ILazyList.cs index bb1224b97..00c3d2663 100644 --- a/src/Utils/ILazyList.cs +++ b/src/Utils/ILazyList.cs @@ -1,60 +1,12 @@ // dnlib: See LICENSE.txt for more info -using System; using System.Collections.Generic; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif namespace dnlib.Utils { /// /// Interface to access a lazily initialized list /// /// Type to store in list - public interface ILazyList : ThreadSafe.IList { - /// - /// Checks whether an element at has been initialized. - /// - /// Index of element - /// true if the element has been initialized, false otherwise - bool IsInitialized(int index); - - /// - /// Checks whether an element at has been initialized. - /// - /// Index of element - /// true if the element has been initialized, false otherwise - bool IsInitialized_NoLock(int index); - - /// - /// Gets all initialized elements - /// - /// true if the list should be cleared before returning, - /// false if the list should not cleared. - List GetInitializedElements(bool clearList); - } - - public static partial class Extensions { - /// - /// Disposes all initialized elements - /// - /// Element type - /// this - public static void DisposeAll(this ILazyList list) where TValue : IDisposable { - list.ExecuteLocked(null, (tsList, arg) => { - for (int i = 0; i < list.Count_NoLock(); i++) { - if (list.IsInitialized_NoLock(i)) { - var elem = list.Get_NoLock(i); - if (elem != null) - elem.Dispose(); - } - } - return null; - }); - } + interface ILazyList : IList { } } diff --git a/src/Utils/LazyList.cs b/src/Utils/LazyList.cs index 360f43bb4..31a4d1036 100644 --- a/src/Utils/LazyList.cs +++ b/src/Utils/LazyList.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; @@ -63,39 +63,26 @@ public interface IListListener { /// /// Type to store in list [DebuggerDisplay("Count = {Count}")] + [DebuggerTypeProxy(typeof(CollectionDebugView<>))] public class LazyList : ILazyList where TValue : class { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - readonly object context; - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - readonly MFunc readOriginalValue; - - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - readonly List list; - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private protected readonly List list; int id = 0; - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - readonly IListListener listener; + private protected readonly IListListener listener; #if THREAD_SAFE - [DebuggerBrowsable(DebuggerBrowsableState.Never)] readonly Lock theLock = Lock.Create(); #endif /// /// Stores a simple value /// - class Element { + private protected class Element { protected TValue value; /// /// true if it has been initialized, false otherwise /// - public virtual bool IsInitialized_NoLock { - get { return true; } - } + public virtual bool IsInitialized_NoLock => true; /// /// Default constructor @@ -107,83 +94,26 @@ protected Element() { /// Constructor that should be used when new elements are inserted into /// /// User data - public Element(TValue data) { - this.value = data; - } + public Element(TValue data) => value = data; /// /// Gets the value /// /// Index in the list - public virtual TValue GetValue_NoLock(int index) { - return value; - } + public virtual TValue GetValue_NoLock(int index) => value; /// /// Sets the value /// /// Index in the list /// New value - public virtual void SetValue_NoLock(int index, TValue value) { - this.value = value; - } + public virtual void SetValue_NoLock(int index, TValue value) => this.value = value; /// - public override string ToString() { - return value == null ? string.Empty : value.ToString(); - } - } - - /// - /// Stores data and keeps track of the original index and whether the data has been - /// initialized or not. - /// - sealed class LazyElement : Element { - internal readonly uint origIndex; - LazyList lazyList; - - /// - public override bool IsInitialized_NoLock { - get { return lazyList == null; } - } - - /// - public override TValue GetValue_NoLock(int index) { - if (lazyList != null) { - value = lazyList.ReadOriginalValue_NoLock(index, origIndex); - lazyList = null; - } - return value; - } - - /// - public override void SetValue_NoLock(int index, TValue value) { - this.value = value; - lazyList = null; - } - - /// - /// Constructor that should only be called when is initialized. - /// - /// Original index of this element - /// LazyList instance - public LazyElement(int origIndex, LazyList lazyList) { - this.origIndex = (uint)origIndex; - this.lazyList = lazyList; - } - - /// - public override string ToString() { - if (lazyList != null) { - value = lazyList.ReadOriginalValue_NoLock(this); - lazyList = null; - } - return value == null ? string.Empty : value.ToString(); - } + public override string ToString() => value?.ToString() ?? string.Empty; } /// - [DebuggerBrowsableAttribute(DebuggerBrowsableState.Never)] public int Count { get { #if THREAD_SAFE @@ -197,22 +127,10 @@ public int Count { } /// - [DebuggerBrowsableAttribute(DebuggerBrowsableState.Never)] - public int Count_NoLock { - get { return list.Count; } - } - - /// - [DebuggerBrowsableAttribute(DebuggerBrowsableState.Never)] - public bool IsReadOnly { - get { return false; } - } + internal int Count_NoLock => list.Count; /// - [DebuggerBrowsableAttribute(DebuggerBrowsableState.Never)] - public bool IsReadOnly_NoLock { - get { return false; } - } + public bool IsReadOnly => false; /// public TValue this[int index] { @@ -236,14 +154,10 @@ public TValue this[int index] { } } - /// - public TValue Get_NoLock(int index) { - return list[index].GetValue_NoLock(index); - } + internal TValue Get_NoLock(int index) => list[index].GetValue_NoLock(index); - /// - public void Set_NoLock(int index, TValue value) { - if (listener != null) { + void Set_NoLock(int index, TValue value) { + if (listener is not null) { listener.OnRemove(index, list[index].GetValue_NoLock(index)); listener.OnAdd(index, value); } @@ -264,44 +178,12 @@ public LazyList() /// List listener public LazyList(IListListener listener) { this.listener = listener; - this.list = new List(); - } - - /// - /// Constructor - /// - /// Initial length of the list - /// Context passed to - /// Delegate instance that returns original values - public LazyList(int length, object context, MFunc readOriginalValue) - : this(length, null, context, readOriginalValue) { + list = new List(); } - /// - /// Constructor - /// - /// Initial length of the list - /// List listener - /// Context passed to - /// Delegate instance that returns original values - public LazyList(int length, IListListener listener, object context, MFunc readOriginalValue) { + private protected LazyList(int length, IListListener listener) { this.listener = listener; - this.context = context; - this.readOriginalValue = readOriginalValue; - this.list = new List(length); - for (int i = 0; i < length; i++) - list.Add(new LazyElement(i, this)); - } - - TValue ReadOriginalValue_NoLock(LazyElement elem) { - return ReadOriginalValue_NoLock(list.IndexOf(elem), elem.origIndex); - } - - TValue ReadOriginalValue_NoLock(int index, uint origIndex) { - var newValue = readOriginalValue(context, origIndex); - if (listener != null) - listener.OnLazyAdd(index, ref newValue); - return newValue; + list = new List(length); } /// @@ -315,8 +197,7 @@ public int IndexOf(TValue item) { #endif } - /// - public int IndexOf_NoLock(TValue item) { + int IndexOf_NoLock(TValue item) { for (int i = 0; i < list.Count; i++) { if (list[i].GetValue_NoLock(i) == item) return i; @@ -335,12 +216,11 @@ public void Insert(int index, TValue item) { #endif } - /// - public void Insert_NoLock(int index, TValue item) { - if (listener != null) + void Insert_NoLock(int index, TValue item) { + if (listener is not null) listener.OnAdd(index, item); list.Insert(index, new Element(item)); - if (listener != null) + if (listener is not null) listener.OnResize(index); id++; } @@ -356,12 +236,11 @@ public void RemoveAt(int index) { #endif } - /// - public void RemoveAt_NoLock(int index) { - if (listener != null) + void RemoveAt_NoLock(int index) { + if (listener is not null) listener.OnRemove(index, list[index].GetValue_NoLock(index)); list.RemoveAt(index); - if (listener != null) + if (listener is not null) listener.OnResize(index); id++; } @@ -377,13 +256,12 @@ public void Add(TValue item) { #endif } - /// - public void Add_NoLock(TValue item) { + void Add_NoLock(TValue item) { int index = list.Count; - if (listener != null) + if (listener is not null) listener.OnAdd(index, item); list.Add(new Element(item)); - if (listener != null) + if (listener is not null) listener.OnResize(index); id++; } @@ -399,25 +277,17 @@ public void Clear() { #endif } - /// - public void Clear_NoLock() { - if (listener != null) + void Clear_NoLock() { + if (listener is not null) listener.OnClear(); list.Clear(); - if (listener != null) + if (listener is not null) listener.OnResize(0); id++; } /// - public bool Contains(TValue item) { - return IndexOf(item) >= 0; - } - - /// - public bool Contains_NoLock(TValue item) { - return IndexOf_NoLock(item) >= 0; - } + public bool Contains(TValue item) => IndexOf(item) >= 0; /// public void CopyTo(TValue[] array, int arrayIndex) { @@ -430,8 +300,7 @@ public void CopyTo(TValue[] array, int arrayIndex) { #endif } - /// - public void CopyTo_NoLock(TValue[] array, int arrayIndex) { + void CopyTo_NoLock(TValue[] array, int arrayIndex) { for (int i = 0; i < list.Count; i++) array[arrayIndex + i] = list[i].GetValue_NoLock(i); } @@ -447,8 +316,7 @@ public bool Remove(TValue item) { #endif } - /// - public bool Remove_NoLock(TValue item) { + bool Remove_NoLock(TValue item) { int index = IndexOf_NoLock(item); if (index < 0) return false; @@ -456,8 +324,7 @@ public bool Remove_NoLock(TValue item) { return true; } - /// - public bool IsInitialized(int index) { + internal bool IsInitialized(int index) { #if THREAD_SAFE theLock.EnterReadLock(); try { #endif @@ -467,42 +334,84 @@ public bool IsInitialized(int index) { #endif } - /// - public bool IsInitialized_NoLock(int index) { + bool IsInitialized_NoLock(int index) { if ((uint)index >= (uint)list.Count) return false; return list[index].IsInitialized_NoLock; } - /// - public IEnumerator GetEnumerator() { - int id2; + /// + /// Enumerator + /// + public struct Enumerator : IEnumerator { + readonly LazyList list; + readonly int id; + int index; + TValue current; + + internal Enumerator(LazyList list) { + this.list = list; + index = 0; + current = default; #if THREAD_SAFE - theLock.EnterReadLock(); try { + list.theLock.EnterReadLock(); try { #endif - id2 = id; + id = list.id; #if THREAD_SAFE - } finally { theLock.ExitReadLock(); } + } finally { list.theLock.ExitReadLock(); } #endif - for (int i = 0; ; i++) { - TValue value; + } + + /// + /// Gets the current value + /// + public TValue Current => current; + object IEnumerator.Current => current; + + /// + /// Moves to the next element in the collection + /// + /// + public bool MoveNext() { #if THREAD_SAFE - theLock.EnterWriteLock(); try { + list.theLock.EnterWriteLock(); try { #endif - if (id != id2) - throw new InvalidOperationException("List was modified"); - if (i >= list.Count) - break; - value = list[i].GetValue_NoLock(i); + if (list.id == id && index < list.Count_NoLock) { + current = list.list[index].GetValue_NoLock(index); + index++; + return true; + } + else + return MoveNextDoneOrThrow_NoLock(); #if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } + } finally { list.theLock.ExitWriteLock(); } #endif - yield return value; } + + bool MoveNextDoneOrThrow_NoLock() { + if (list.id != id) + throw new InvalidOperationException("List was modified"); + current = default; + return false; + } + + /// + /// Disposes the enumerator + /// + public void Dispose() { } + + void IEnumerator.Reset() => throw new NotSupportedException(); } - /// - public IEnumerator GetEnumerator_NoLock() { + /// + /// Gets the list enumerator + /// + /// + public Enumerator GetEnumerator() => new Enumerator(this); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + internal IEnumerable GetEnumerable_NoLock() { int id2 = id; for (int i = 0; i < list.Count; i++) { if (id != id2) @@ -512,41 +421,109 @@ public IEnumerator GetEnumerator_NoLock() { } /// - public List GetInitializedElements(bool clearList) { - List newList; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - newList = new List(list.Count); - int id2 = id; - for (int i = 0; i < list.Count; i++) { - if (id != id2) - throw new InvalidOperationException("List was modified"); - var elem = list[i]; - if (!elem.IsInitialized_NoLock) - continue; - newList.Add(elem.GetValue_NoLock(i)); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + /// + /// Implements a that is lazily initialized + /// + /// Type to store in list + /// Type of the context passed to the read-value delegate + [DebuggerDisplay("Count = {Count}")] + [DebuggerTypeProxy(typeof(CollectionDebugView<,>))] + public class LazyList : LazyList, ILazyList where TValue : class { + /*readonly*/ TContext context; + readonly Func readOriginalValue; + + /// + /// Stores data and keeps track of the original index and whether the data has been + /// initialized or not. + /// + sealed class LazyElement : Element { + internal readonly int origIndex; + LazyList lazyList; + + /// + public override bool IsInitialized_NoLock => lazyList is null; + + /// + public override TValue GetValue_NoLock(int index) { + if (lazyList is not null) { + value = lazyList.ReadOriginalValue_NoLock(index, origIndex); + lazyList = null; + } + return value; + } + + /// + public override void SetValue_NoLock(int index, TValue value) { + this.value = value; + lazyList = null; + } + + /// + /// Constructor that should only be called when is initialized. + /// + /// Original index of this element + /// LazyList instance + public LazyElement(int origIndex, LazyList lazyList) { + this.origIndex = origIndex; + this.lazyList = lazyList; + } + + /// + public override string ToString() { + if (lazyList is not null) { + value = lazyList.ReadOriginalValue_NoLock(this); + lazyList = null; + } + return value is null ? string.Empty : value.ToString(); } - if (clearList) - Clear_NoLock(); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif - return newList; } - /// - IEnumerator IEnumerable.GetEnumerator() { - return GetEnumerator(); + /// + /// Default constructor + /// + public LazyList() : this(null) { } -#if THREAD_SAFE - /// - public TRetType ExecuteLocked(TArgType arg, ExecuteLockedDelegate handler) { - theLock.EnterWriteLock(); try { - return handler(this, arg); - } finally { theLock.ExitWriteLock(); } + /// + /// Constructor + /// + /// List listener + public LazyList(IListListener listener) : base(listener) { + } + + /// + /// Constructor + /// + /// Initial length of the list + /// Context passed to + /// Delegate instance that returns original values + public LazyList(int length, TContext context, Func readOriginalValue) + : this(length, null, context, readOriginalValue) { + } + + /// + /// Constructor + /// + /// Initial length of the list + /// List listener + /// Context passed to + /// Delegate instance that returns original values + public LazyList(int length, IListListener listener, TContext context, Func readOriginalValue) : base(length, listener) { + this.context = context; + this.readOriginalValue = readOriginalValue; + for (int i = 0; i < length; i++) + list.Add(new LazyElement(i, this)); + } + + TValue ReadOriginalValue_NoLock(LazyElement elem) => ReadOriginalValue_NoLock(list.IndexOf(elem), elem.origIndex); + + TValue ReadOriginalValue_NoLock(int index, int origIndex) { + var newValue = readOriginalValue(context, origIndex); + listener?.OnLazyAdd(index, ref newValue); + return newValue; } -#endif } } diff --git a/src/Utils/MFunc.cs b/src/Utils/MFunc.cs deleted file mode 100644 index 3b84b3249..000000000 --- a/src/Utils/MFunc.cs +++ /dev/null @@ -1,17 +0,0 @@ -// dnlib: See LICENSE.txt for more info - -namespace dnlib.Utils { - delegate T MFunc(); - delegate U MFunc(T t); - - /// - /// Same as Func delegate - /// - /// - /// - /// - /// - /// - /// - public delegate V MFunc(T t, U u); -} diff --git a/src/Utils/SimpleLazyList.cs b/src/Utils/SimpleLazyList.cs index d40b3371a..592aaffa9 100644 --- a/src/Utils/SimpleLazyList.cs +++ b/src/Utils/SimpleLazyList.cs @@ -1,6 +1,7 @@ // dnlib: See LICENSE.txt for more info -using System.Diagnostics; +using System; +using System.Diagnostics; using System.Threading; using dnlib.DotNet; @@ -14,16 +15,14 @@ sealed class SimpleLazyList where T : class { [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] readonly T[] elements; [DebuggerBrowsable(DebuggerBrowsableState.Never)] - readonly MFunc readElementByRID; + readonly Func readElementByRID; [DebuggerBrowsable(DebuggerBrowsableState.Never)] readonly uint length; /// /// Gets the length of this list /// - public uint Length { - get { return length; } - } + public uint Length => length; /// /// Access the list @@ -34,7 +33,7 @@ public T this[uint index] { get { if (index >= length) return null; - if (elements[index] == null) + if (elements[index] is null) Interlocked.CompareExchange(ref elements[index], readElementByRID(index + 1), null); return elements[index]; } @@ -47,10 +46,10 @@ public T this[uint index] { /// Delegate instance that lazily reads an element. It might /// be called more than once for each rid in rare cases. It must never return /// null. - public SimpleLazyList(uint length, MFunc readElementByRID) { + public SimpleLazyList(uint length, Func readElementByRID) { this.length = length; this.readElementByRID = readElementByRID; - this.elements = new T[length]; + elements = new T[length]; } } @@ -59,20 +58,18 @@ public SimpleLazyList(uint length, MFunc readElementByRID) { /// /// Any class type [DebuggerDisplay("Count = {Length}")] - sealed class SimpleLazyList2 where T : class, IContainsGenericParameter { + sealed class SimpleLazyList2 where T : class, IContainsGenericParameter2 { [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] readonly T[] elements; [DebuggerBrowsable(DebuggerBrowsableState.Never)] - readonly MFunc readElementByRID; + readonly Func readElementByRID; [DebuggerBrowsable(DebuggerBrowsableState.Never)] readonly uint length; /// /// Gets the length of this list /// - public uint Length { - get { return length; } - } + public uint Length => length; /// /// Access the list @@ -84,7 +81,7 @@ public uint Length { get { if (index >= length) return null; - if (elements[index] == null) { + if (elements[index] is null) { var elem = readElementByRID(index + 1, gpContext); // Don't cache it if it contains GPs since each GP could hold a reference // to the type/method context. These GPs can't be shared. @@ -102,10 +99,10 @@ public uint Length { /// Length of the list /// Delegate instance that lazily reads an element. It might /// be called more than once for each rid. It must never return null. - public SimpleLazyList2(uint length, MFunc readElementByRID) { + public SimpleLazyList2(uint length, Func readElementByRID) { this.length = length; this.readElementByRID = readElementByRID; - this.elements = new T[length]; + elements = new T[length]; } } } diff --git a/src/Utils/UserValue.cs b/src/Utils/UserValue.cs index ea590d88c..c0114ba08 100644 --- a/src/Utils/UserValue.cs +++ b/src/Utils/UserValue.cs @@ -1,5 +1,6 @@ // dnlib: See LICENSE.txt for more info +using System; using System.Diagnostics; using dnlib.Threading; @@ -13,7 +14,7 @@ struct UserValue { #if THREAD_SAFE Lock theLock; #endif - MFunc readOriginalValue; + Func readOriginalValue; TValue value; bool isUserValue; bool isValueInitialized; @@ -23,15 +24,15 @@ struct UserValue { /// Sets the lock that protects the data /// public Lock Lock { - set { theLock = value; } + set => theLock = value; } #endif /// /// Set a delegate instance that will return the original value /// - public MFunc ReadOriginalValue { - set { readOriginalValue = value; } + public Func ReadOriginalValue { + set => readOriginalValue = value; } /// @@ -41,7 +42,7 @@ public MFunc ReadOriginalValue { public TValue Value { get { #if THREAD_SAFE - if (theLock != null) theLock.EnterWriteLock(); try { + theLock?.EnterWriteLock(); try { #endif if (!isValueInitialized) { value = readOriginalValue(); @@ -50,19 +51,19 @@ public TValue Value { } return value; #if THREAD_SAFE - } finally { if (theLock != null) theLock.ExitWriteLock(); } + } finally { theLock?.ExitWriteLock(); } #endif } set { #if THREAD_SAFE - if (theLock != null) theLock.EnterWriteLock(); try { + theLock?.EnterWriteLock(); try { #endif this.value = value; readOriginalValue = null; isUserValue = true; isValueInitialized = true; #if THREAD_SAFE - } finally { if (theLock != null) theLock.ExitWriteLock(); } + } finally { theLock?.ExitWriteLock(); } #endif } } @@ -73,15 +74,14 @@ public TValue Value { public bool IsValueInitialized { #if THREAD_SAFE get { - if (theLock != null) - theLock.EnterReadLock(); + theLock?.EnterReadLock(); try { return isValueInitialized; } - finally { if (theLock != null) theLock.ExitReadLock(); } + finally { theLock?.ExitReadLock(); } } #else - get { return isValueInitialized; } + get => isValueInitialized; #endif } @@ -91,15 +91,14 @@ public bool IsValueInitialized { public bool IsUserValue { #if THREAD_SAFE get { - if (theLock != null) - theLock.EnterReadLock(); + theLock?.EnterReadLock(); try { return isUserValue; } - finally { if (theLock != null) theLock.ExitReadLock(); } + finally { theLock?.ExitReadLock(); } } #else - get { return isUserValue; } + get => isUserValue; #endif } } diff --git a/src/W32Resources/ResourceData.cs b/src/W32Resources/ResourceData.cs index b58a449c3..a2d3d3f15 100644 --- a/src/W32Resources/ResourceData.cs +++ b/src/W32Resources/ResourceData.cs @@ -1,45 +1,40 @@ // dnlib: See LICENSE.txt for more info -using System; -using System.IO; -using System.Threading; +using System; using dnlib.IO; namespace dnlib.W32Resources { /// /// A resource blob /// - public sealed class ResourceData : ResourceDirectoryEntry, IDisposable { - IBinaryReader reader; + public sealed class ResourceData : ResourceDirectoryEntry { + readonly DataReaderFactory dataReaderFactory; + readonly uint resourceStartOffset; + readonly uint resourceLength; + uint codePage; uint reserved; /// - /// Gets/sets the data reader. This instance owns the reader. + /// Gets the data reader /// - public IBinaryReader Data { - get { return reader; } - set { - var oldValue = Interlocked.Exchange(ref reader, value); - if (oldValue != value && oldValue != null) - oldValue.Dispose(); - } - } + /// + public DataReader CreateReader() => dataReaderFactory.CreateReader(resourceStartOffset, resourceLength); /// /// Gets/sets the code page /// public uint CodePage { - get { return codePage; } - set { codePage = value; } + get => codePage; + set => codePage = value; } /// /// Gets/sets the reserved field /// public uint Reserved { - get { return reserved; } - set { reserved = value; } + get => reserved; + set => reserved = value; } /// @@ -47,45 +42,36 @@ public uint Reserved { /// /// Name public ResourceData(ResourceName name) - : base(name) { + : this(name, ByteArrayDataReaderFactory.Create(Array2.Empty(), filename: null), 0, 0) { } /// /// Constructor /// - /// Raw data. This instance owns this reader. + /// Data reader factory + /// Offset of resource data + /// Length of resource data /// Name - public ResourceData(ResourceName name, IBinaryReader reader) - : base(name) { - this.reader = reader; + public ResourceData(ResourceName name, DataReaderFactory dataReaderFactory, uint offset, uint length) + : this(name, dataReaderFactory, offset, length, 0, 0) { } /// /// Constructor /// - /// Raw data. This instance owns this reader. + /// Data reader factory + /// Offset of resource data + /// Length of resource data /// Name /// Code page /// Reserved value - public ResourceData(ResourceName name, IBinaryReader reader, uint codePage, uint reserved) + public ResourceData(ResourceName name, DataReaderFactory dataReaderFactory, uint offset, uint length, uint codePage, uint reserved) : base(name) { - this.reader = reader; + this.dataReaderFactory = dataReaderFactory ?? throw new ArgumentNullException(nameof(dataReaderFactory)); + resourceStartOffset = offset; + resourceLength = length; this.codePage = codePage; this.reserved = reserved; } - - /// - /// Gets the data as a . It shares the file position with - /// - public Stream ToDataStream() { - return Data.CreateStream(); - } - - /// - public void Dispose() { - var oldValue = Interlocked.Exchange(ref reader, null); - if (oldValue != null) - oldValue.Dispose(); - } } } diff --git a/src/W32Resources/ResourceDirectory.cs b/src/W32Resources/ResourceDirectory.cs index 3160aad98..da802ba39 100644 --- a/src/W32Resources/ResourceDirectory.cs +++ b/src/W32Resources/ResourceDirectory.cs @@ -1,24 +1,16 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Collections.Generic; -using System.Text; using dnlib.Utils; using dnlib.IO; using dnlib.PE; -using dnlib.Threading; - -#if THREAD_SAFE -using ThreadSafe = dnlib.Threading.Collections; -#else -using ThreadSafe = System.Collections.Generic; -#endif namespace dnlib.W32Resources { /// /// A Win32 resource directory (see IMAGE_RESOURCE_DIRECTORY in the Windows SDK) /// - public abstract class ResourceDirectory : ResourceDirectoryEntry, IDisposable { + public abstract class ResourceDirectory : ResourceDirectoryEntry { /// See protected uint characteristics; /// See @@ -28,55 +20,51 @@ public abstract class ResourceDirectory : ResourceDirectoryEntry, IDisposable { /// See protected ushort minorVersion; /// See - protected ILazyList directories; + private protected LazyList directories; /// See - protected ILazyList data; + private protected LazyList data; /// /// Gets/sets the characteristics /// public uint Characteristics { - get { return characteristics; } - set { characteristics = value; } + get => characteristics; + set => characteristics = value; } /// /// Gets/sets the time date stamp /// public uint TimeDateStamp { - get { return timeDateStamp; } - set { timeDateStamp = value; } + get => timeDateStamp; + set => timeDateStamp = value; } /// /// Gets/sets the major version number /// public ushort MajorVersion { - get { return majorVersion; } - set { majorVersion = value; } + get => majorVersion; + set => majorVersion = value; } /// /// Gets/sets the minor version number /// public ushort MinorVersion { - get { return minorVersion; } - set { minorVersion = value; } + get => minorVersion; + set => minorVersion = value; } /// /// Gets all directory entries /// - public ThreadSafe.IList Directories { - get { return directories; } - } + public IList Directories => directories; /// /// Gets all resource data /// - public ThreadSafe.IList Data { - get { return data; } - } + public IList Data => data; /// /// Constructor @@ -92,7 +80,7 @@ protected ResourceDirectory(ResourceName name) /// Name /// A or null if it wasn't found public ResourceDirectory FindDirectory(ResourceName name) { - foreach (var dir in directories.GetSafeEnumerable()) { + foreach (var dir in directories) { if (dir.Name == name) return dir; } @@ -105,31 +93,12 @@ public ResourceDirectory FindDirectory(ResourceName name) { /// Name /// A or null if it wasn't found public ResourceData FindData(ResourceName name) { - foreach (var d in data.GetSafeEnumerable()) { + foreach (var d in data) { if (d.Name == name) return d; } return null; } - - /// - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose method - /// - /// true if called by - protected virtual void Dispose(bool disposing) { - if (!disposing) - return; - directories.DisposeAll(); - data.DisposeAll(); - directories = null; - data = null; - } } /// @@ -142,8 +111,8 @@ public class ResourceDirectoryUser : ResourceDirectory { /// Name public ResourceDirectoryUser(ResourceName name) : base(name) { - this.directories = new LazyList(); - this.data = new LazyList(); + directories = new LazyList(); + data = new LazyList(); } } @@ -173,7 +142,7 @@ public sealed class ResourceDirectoryPE : ResourceDirectory { /// List dirInfos; - struct EntryInfo { + readonly struct EntryInfo { public readonly ResourceName name; /// Offset of resource directory / data @@ -184,9 +153,7 @@ public EntryInfo(ResourceName name, uint offset) { this.offset = offset; } - public override string ToString() { - return string.Format("{0:X8} {1}", offset, name); - } + public override string ToString() => $"{offset:X8} {name}"; } /// @@ -196,11 +163,11 @@ public override string ToString() { /// Name /// Resources /// Reader positioned at the start of this resource directory - public ResourceDirectoryPE(uint depth, ResourceName name, Win32ResourcesPE resources, IBinaryReader reader) + public ResourceDirectoryPE(uint depth, ResourceName name, Win32ResourcesPE resources, ref DataReader reader) : base(name) { this.resources = resources; this.depth = depth; - Initialize(reader); + Initialize(ref reader); } /// @@ -208,8 +175,8 @@ public ResourceDirectoryPE(uint depth, ResourceName name, Win32ResourcesPE resou /// . /// /// - void Initialize(IBinaryReader reader) { - if (depth > MAX_DIR_DEPTH || !reader.CanRead(16)) { + void Initialize(ref DataReader reader) { + if (depth > MAX_DIR_DEPTH || !reader.CanRead(16U)) { InitializeDefault(); return; } @@ -222,21 +189,21 @@ void Initialize(IBinaryReader reader) { ushort numIds = reader.ReadUInt16(); int total = numNamed + numIds; - if (!reader.CanRead(total * 8)) { + if (!reader.CanRead((uint)total * 8)) { InitializeDefault(); return; } dataInfos = new List(); dirInfos = new List(); - long offset = reader.Position; + uint offset = reader.Position; for (int i = 0; i < total; i++, offset += 8) { reader.Position = offset; uint nameOrId = reader.ReadUInt32(); uint dataOrDirectory = reader.ReadUInt32(); ResourceName name; if ((nameOrId & 0x80000000) != 0) - name = new ResourceName(ReadString(reader, nameOrId & 0x7FFFFFFF) ?? string.Empty); + name = new ResourceName(ReadString(ref reader, nameOrId & 0x7FFFFFFF) ?? string.Empty); else name = new ResourceName((int)nameOrId); @@ -246,8 +213,8 @@ void Initialize(IBinaryReader reader) { dirInfos.Add(new EntryInfo(name, dataOrDirectory & 0x7FFFFFFF)); } - directories = new LazyList(dirInfos.Count, null, (ctx, i) => ReadResourceDirectory((int)i)); - data = new LazyList(dataInfos.Count, null, (ctx, i) => ReadResourceData((int)i)); + directories = new LazyList(dirInfos.Count, null, (ctx, i) => ReadResourceDirectory(i)); + data = new LazyList(dataInfos.Count, null, (ctx, i) => ReadResourceData(i)); } /// @@ -256,17 +223,16 @@ void Initialize(IBinaryReader reader) { /// Reader /// Offset of string /// The string or null if we could not read it - static string ReadString(IBinaryReader reader, uint offset) { + static string ReadString(ref DataReader reader, uint offset) { reader.Position = offset; - if (!reader.CanRead(2)) + if (!reader.CanRead(2U)) return null; int size = reader.ReadUInt16(); int sizeInBytes = size * 2; - if (!reader.CanRead(sizeInBytes)) + if (!reader.CanRead((uint)sizeInBytes)) return null; - var stringData = reader.ReadBytes(sizeInBytes); try { - return Encoding.Unicode.GetString(stringData); + return reader.ReadUtf16String(sizeInBytes / 2); } catch { return null; @@ -274,49 +240,30 @@ static string ReadString(IBinaryReader reader, uint offset) { } ResourceDirectory ReadResourceDirectory(int i) { -#if THREAD_SAFE - resources.theLock.EnterWriteLock(); try { -#endif var info = dirInfos[i]; - var reader = resources.ResourceReader; - var oldPos = reader.Position; - reader.Position = info.offset; - - var dir = new ResourceDirectoryPE(depth + 1, info.name, resources, reader); - - reader.Position = oldPos; - return dir; -#if THREAD_SAFE - } finally { resources.theLock.ExitWriteLock(); } -#endif + var reader = resources.GetResourceReader(); + reader.Position = Math.Min(reader.Length, info.offset); + return new ResourceDirectoryPE(depth + 1, info.name, resources, ref reader); } ResourceData ReadResourceData(int i) { -#if THREAD_SAFE - resources.theLock.EnterWriteLock(); try { -#endif var info = dataInfos[i]; - var reader = resources.ResourceReader; - var oldPos = reader.Position; - reader.Position = info.offset; + var reader = resources.GetResourceReader(); + reader.Position = Math.Min(reader.Length, info.offset); ResourceData data; - if (reader.CanRead(16)) { - RVA rva = (RVA)reader.ReadUInt32(); + if (reader.CanRead(16U)) { + var rva = (RVA)reader.ReadUInt32(); uint size = reader.ReadUInt32(); uint codePage = reader.ReadUInt32(); uint reserved = reader.ReadUInt32(); - var dataReader = resources.CreateDataReader_NoLock(rva, size); - data = new ResourceData(info.name, dataReader, codePage, reserved); + resources.GetDataReaderInfo(rva, size, out var dataReaderFactory, out uint dataOffset, out uint dataLength); + data = new ResourceData(info.name, dataReaderFactory, dataOffset, dataLength, codePage, reserved); } else - data = new ResourceData(info.name, MemoryImageStream.CreateEmpty()); + data = new ResourceData(info.name); - reader.Position = oldPos; return data; -#if THREAD_SAFE - } finally { resources.theLock.ExitWriteLock(); } -#endif } void InitializeDefault() { diff --git a/src/W32Resources/ResourceDirectoryEntry.cs b/src/W32Resources/ResourceDirectoryEntry.cs index 659bc4d22..b4ddaac37 100644 --- a/src/W32Resources/ResourceDirectoryEntry.cs +++ b/src/W32Resources/ResourceDirectoryEntry.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -namespace dnlib.W32Resources { +namespace dnlib.W32Resources { /// /// Base class of and /// @@ -11,21 +11,17 @@ public abstract class ResourceDirectoryEntry { /// Gets/sets the name /// public ResourceName Name { - get { return name; } - set { name = value; } + get => name; + set => name = value; } /// /// Constructor /// /// Name - protected ResourceDirectoryEntry(ResourceName name) { - this.name = name; - } + protected ResourceDirectoryEntry(ResourceName name) => this.name = name; /// - public override string ToString() { - return name.ToString(); - } + public override string ToString() => name.ToString(); } } diff --git a/src/W32Resources/ResourceName.cs b/src/W32Resources/ResourceName.cs index 420e0f001..d5f36dddf 100644 --- a/src/W32Resources/ResourceName.cs +++ b/src/W32Resources/ResourceName.cs @@ -1,42 +1,34 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; namespace dnlib.W32Resources { /// /// A Win32 resource name. It can be either an integer or a string. /// - public struct ResourceName : IComparable, IEquatable { + public readonly struct ResourceName : IComparable, IEquatable { readonly int id; readonly string name; /// /// true if is valid /// - public bool HasId { - get { return name == null; } - } + public bool HasId => name is null; /// /// true if is valid /// - public bool HasName { - get { return name != null; } - } + public bool HasName => name is not null; /// /// The ID. It's only valid if is true /// - public int Id { - get { return id; } - } + public int Id => id; /// /// The name. It's only valid if is true /// - public string Name { - get { return name; } - } + public string Name => name; /// /// Constructor @@ -44,7 +36,7 @@ public string Name { /// ID public ResourceName(int id) { this.id = id; - this.name = null; + name = null; } /// @@ -52,49 +44,33 @@ public ResourceName(int id) { /// /// Name public ResourceName(string name) { - this.id = 0; + id = 0; this.name = name; } /// Converts input to a - public static implicit operator ResourceName(int id) { - return new ResourceName(id); - } + public static implicit operator ResourceName(int id) => new ResourceName(id); /// Converts input to a - public static implicit operator ResourceName(string name) { - return new ResourceName(name); - } + public static implicit operator ResourceName(string name) => new ResourceName(name); /// Overloaded operator - public static bool operator <(ResourceName left, ResourceName right) { - return left.CompareTo(right) < 0; - } + public static bool operator <(ResourceName left, ResourceName right) => left.CompareTo(right) < 0; /// Overloaded operator - public static bool operator <=(ResourceName left, ResourceName right) { - return left.CompareTo(right) <= 0; - } + public static bool operator <=(ResourceName left, ResourceName right) => left.CompareTo(right) <= 0; /// Overloaded operator - public static bool operator >(ResourceName left, ResourceName right) { - return left.CompareTo(right) > 0; - } + public static bool operator >(ResourceName left, ResourceName right) => left.CompareTo(right) > 0; /// Overloaded operator - public static bool operator >=(ResourceName left, ResourceName right) { - return left.CompareTo(right) >= 0; - } + public static bool operator >=(ResourceName left, ResourceName right) => left.CompareTo(right) >= 0; /// Overloaded operator - public static bool operator ==(ResourceName left, ResourceName right) { - return left.Equals(right); - } + public static bool operator ==(ResourceName left, ResourceName right) => left.Equals(right); /// Overloaded operator - public static bool operator !=(ResourceName left, ResourceName right) { - return !left.Equals(right); - } + public static bool operator !=(ResourceName left, ResourceName right) => !left.Equals(right); /// public int CompareTo(ResourceName other) { @@ -109,9 +85,7 @@ public int CompareTo(ResourceName other) { } /// - public bool Equals(ResourceName other) { - return CompareTo(other) == 0; - } + public bool Equals(ResourceName other) => CompareTo(other) == 0; /// public override bool Equals(object obj) { @@ -128,8 +102,6 @@ public override int GetHashCode() { } /// - public override string ToString() { - return HasId ? id.ToString() : name; - } + public override string ToString() => HasId ? id.ToString() : name; } } diff --git a/src/W32Resources/Win32Resources.cs b/src/W32Resources/Win32Resources.cs index efd679f6e..d02f8a437 100644 --- a/src/W32Resources/Win32Resources.cs +++ b/src/W32Resources/Win32Resources.cs @@ -1,6 +1,6 @@ // dnlib: See LICENSE.txt for more info -using System; +using System; using System.Threading; using dnlib.Utils; using dnlib.IO; @@ -24,7 +24,7 @@ public abstract class Win32Resources : IDisposable { /// The or null if none found public ResourceDirectory Find(ResourceName type) { var dir = Root; - if (dir == null) + if (dir is null) return null; return dir.FindDirectory(type); } @@ -37,7 +37,7 @@ public ResourceDirectory Find(ResourceName type) { /// The or null if none found public ResourceDirectory Find(ResourceName type, ResourceName name) { var dir = Find(type); - if (dir == null) + if (dir is null) return null; return dir.FindDirectory(name); } @@ -51,7 +51,7 @@ public ResourceDirectory Find(ResourceName type, ResourceName name) { /// The or null if none found public ResourceData Find(ResourceName type, ResourceName name, ResourceName langId) { var dir = Find(type, name); - if (dir == null) + if (dir is null) return null; return dir.FindData(langId); } @@ -81,12 +81,8 @@ public class Win32ResourcesUser : Win32Resources { /// public override ResourceDirectory Root { - get { return root; } - set { - var oldValue = Interlocked.Exchange(ref root, value); - if (oldValue != value && oldValue != null) - oldValue.Dispose(); - } + get => root; + set => Interlocked.Exchange(ref root, value); } } @@ -95,7 +91,7 @@ public override ResourceDirectory Root { /// public sealed class Win32ResourcesPE : Win32Resources { /// - /// Converts data RVAs to file offsets in + /// Converts data RVAs to file offsets in /// readonly IRvaFileOffsetConverter rvaConverter; @@ -104,60 +100,69 @@ public sealed class Win32ResourcesPE : Win32Resources { /// it's first converted to a file offset using . This file /// offset is where we'll read from using this reader. /// - IImageStream dataReader; + DataReaderFactory dataReader_factory; + uint dataReader_offset; + uint dataReader_length; + bool owns_dataReader_factory; /// /// This reader only reads the directory entries and data headers. The data is read - /// by + /// by /// - IBinaryReader rsrcReader; + DataReaderFactory rsrcReader_factory; + uint rsrcReader_offset; + uint rsrcReader_length; + bool owns_rsrcReader_factory; UserValue root; #if THREAD_SAFE - internal readonly Lock theLock = Lock.Create(); + readonly Lock theLock = Lock.Create(); #endif /// public override ResourceDirectory Root { - get { return root.Value; } + get => root.Value; set { - IDisposable origValue = null; if (root.IsValueInitialized) { - origValue = root.Value; + var origValue = root.Value; if (origValue == value) return; } root.Value = value; - - if (origValue != null) - origValue.Dispose(); } } /// /// Gets the resource reader /// - internal IBinaryReader ResourceReader { - get { return rsrcReader; } - } + internal DataReader GetResourceReader() => rsrcReader_factory.CreateReader(rsrcReader_offset, rsrcReader_length); /// /// Constructor /// /// / converter - /// Data reader (it's used after converting an - /// to a ). This instance owns the reader. - /// Reader for the whole Win32 resources section (usually + /// Reader for the whole Win32 resources section (usually /// the .rsrc section). It's used to read 's and - /// 's but not the actual data blob. This instance owns the - /// reader. - public Win32ResourcesPE(IRvaFileOffsetConverter rvaConverter, IImageStream dataReader, IBinaryReader rsrcReader) { - if (dataReader == rsrcReader) - rsrcReader = dataReader.Clone(); // Must not be the same readers - this.rvaConverter = rvaConverter; - this.dataReader = dataReader; - this.rsrcReader = rsrcReader; + /// 's but not the actual data blob. + /// Offset of resource section + /// Length of resource section + /// true if this instance can dispose of + /// Data reader (it's used after converting an + /// to a ) + /// Offset of resource section + /// Length of resource section + /// true if this instance can dispose of + public Win32ResourcesPE(IRvaFileOffsetConverter rvaConverter, DataReaderFactory rsrcReader_factory, uint rsrcReader_offset, uint rsrcReader_length, bool owns_rsrcReader_factory, DataReaderFactory dataReader_factory, uint dataReader_offset, uint dataReader_length, bool owns_dataReader_factory) { + this.rvaConverter = rvaConverter ?? throw new ArgumentNullException(nameof(rvaConverter)); + this.rsrcReader_factory = rsrcReader_factory ?? throw new ArgumentNullException(nameof(rsrcReader_factory)); + this.rsrcReader_offset = rsrcReader_offset; + this.rsrcReader_length = rsrcReader_length; + this.owns_rsrcReader_factory = owns_rsrcReader_factory; + this.dataReader_factory = dataReader_factory ?? throw new ArgumentNullException(nameof(dataReader_factory)); + this.dataReader_offset = dataReader_offset; + this.dataReader_length = dataReader_length; + this.owns_dataReader_factory = owns_dataReader_factory; Initialize(); } @@ -166,40 +171,54 @@ public Win32ResourcesPE(IRvaFileOffsetConverter rvaConverter, IImageStream dataR /// /// The PE image public Win32ResourcesPE(IPEImage peImage) - : this(peImage, null) { + : this(peImage, null, 0, 0, false) { } /// /// Constructor /// /// The PE image - /// Reader for the whole Win32 resources section (usually + /// Reader for the whole Win32 resources section (usually /// the .rsrc section) or null if we should create one from the resource data - /// directory in the optional header. This instance owns the reader. - public Win32ResourcesPE(IPEImage peImage, IBinaryReader rsrcReader) { - this.rvaConverter = peImage; - this.dataReader = peImage.CreateFullStream(); - if (rsrcReader != null) - this.rsrcReader = rsrcReader; + /// directory in the optional header + /// Offset of resource section + /// Length of resource section + /// true if this instance can dispose of + public Win32ResourcesPE(IPEImage peImage, DataReaderFactory rsrcReader_factory, uint rsrcReader_offset, uint rsrcReader_length, bool owns_rsrcReader_factory) { + rvaConverter = peImage ?? throw new ArgumentNullException(nameof(peImage)); + dataReader_factory = peImage.DataReaderFactory; + dataReader_offset = 0; + dataReader_length = dataReader_factory.Length; + if (rsrcReader_factory is not null) { + this.rsrcReader_factory = rsrcReader_factory; + this.rsrcReader_offset = rsrcReader_offset; + this.rsrcReader_length = rsrcReader_length; + this.owns_rsrcReader_factory = owns_rsrcReader_factory; + } else { var dataDir = peImage.ImageNTHeaders.OptionalHeader.DataDirectories[2]; - if (dataDir.VirtualAddress != 0 && dataDir.Size != 0) - this.rsrcReader = peImage.CreateStream(dataDir.VirtualAddress, dataDir.Size); - else - this.rsrcReader = MemoryImageStream.CreateEmpty(); + if (dataDir.VirtualAddress != 0 && dataDir.Size != 0) { + var reader = peImage.CreateReader(dataDir.VirtualAddress, dataDir.Size); + this.rsrcReader_factory = peImage.DataReaderFactory; + this.rsrcReader_offset = reader.StartOffset; + this.rsrcReader_length = reader.Length; + } + else { + this.rsrcReader_factory = ByteArrayDataReaderFactory.Create(Array2.Empty(), filename: null); + this.rsrcReader_offset = 0; + this.rsrcReader_length = 0; + } } Initialize(); } void Initialize() { root.ReadOriginalValue = () => { - if (rsrcReader == null) - return null; // It's disposed - long oldPos = rsrcReader.Position; - rsrcReader.Position = 0; - var dir = new ResourceDirectoryPE(0, new ResourceName("root"), this, rsrcReader); - rsrcReader.Position = oldPos; - return dir; + var rsrcReader_factory = this.rsrcReader_factory; + if (rsrcReader_factory is null) + return null; // It's disposed + var reader = rsrcReader_factory.CreateReader(rsrcReader_offset, rsrcReader_length); + return new ResourceDirectoryPE(0, new ResourceName("root"), this, ref reader); }; #if THREAD_SAFE root.Lock = theLock; @@ -211,41 +230,36 @@ void Initialize() { /// /// RVA of data /// Size of data - /// A new for this data - public IBinaryReader CreateDataReader(RVA rva, uint size) { -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - return CreateDataReader_NoLock(rva, size); -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + /// + public DataReader CreateReader(RVA rva, uint size) { + GetDataReaderInfo(rva, size, out var dataReaderFactory, out uint dataOffset, out uint dataLength); + return dataReaderFactory.CreateReader(dataOffset, dataLength); } - internal IBinaryReader CreateDataReader_NoLock(RVA rva, uint size) { - var reader = dataReader.Create(rvaConverter.ToFileOffset(rva), size); - if (reader.Length == size) - return reader; - reader.Dispose(); - return MemoryImageStream.CreateEmpty(); + internal void GetDataReaderInfo(RVA rva, uint size, out DataReaderFactory dataReaderFactory, out uint dataOffset, out uint dataLength) { + dataOffset = (uint)rvaConverter.ToFileOffset(rva); + if ((ulong)dataOffset + size <= dataReader_factory.Length) { + dataReaderFactory = dataReader_factory; + dataLength = size; + return; + } + else { + dataReaderFactory = ByteArrayDataReaderFactory.Create(Array2.Empty(), filename: null); + dataOffset = 0; + dataLength = 0; + } } /// protected override void Dispose(bool disposing) { if (!disposing) return; -#if THREAD_SAFE - theLock.EnterWriteLock(); try { -#endif - if (dataReader != null) - dataReader.Dispose(); - if (rsrcReader != null) - rsrcReader.Dispose(); - dataReader = null; - rsrcReader = null; -#if THREAD_SAFE - } finally { theLock.ExitWriteLock(); } -#endif + if (owns_dataReader_factory) + dataReader_factory?.Dispose(); + if (owns_rsrcReader_factory) + rsrcReader_factory?.Dispose(); + dataReader_factory = null; + rsrcReader_factory = null; base.Dispose(disposing); } } diff --git a/src/dnlib.csproj b/src/dnlib.csproj index 5405189bb..154d1398d 100644 --- a/src/dnlib.csproj +++ b/src/dnlib.csproj @@ -1,383 +1,71 @@ - - + + + + - Debug - AnyCPU - 8.0.30703 - 2.0 - {FDFC1237-143F-4919-8318-4926901F4639} - Library - Properties - dnlib + $(DefineConstants);$(MoreDefineConstants) + $(DefineConstants);THREAD_SAFE + netstandard2.0;net45;net6.0 + + true + $(TargetFrameworks);net35 + + Reads and writes .NET assemblies and modules + $(Description) + Copyright (C) 2012-2019 de4dot@gmail.com + dnlib + $(AssemblyTitle) (thread safe) dnlib - v2.0 - 512 - true + dnlib + en-US + 4.5.0 + $(Version) + 0xd4d + https://github.com/0xd4d/dnlib + MIT + $(InformationalVersion) + dotnet;assembly;module;reader;writer;PDB;PortablePdb;WindowsPdb;IL;CIL;MSIL;metadata + latest + strict;nullablePublicOnly + true + true + true + true + snupkg + true + + +Reads and writes .NET assemblies and modules, Windows PDBs and Portable PDBs. + +For better *Windows PDB* writer support, you should add a reference to `Microsoft.DiaSymReader.Native` nuget package too, see the dnlib README for more info: https://github.com/0xd4d/dnlib#windows-pdbs . You don't need to do anything special for *Portable PDB* support. + + + ..\dnlib.snk - - - true - full - false - ..\Debug\bin\ - TRACE;DEBUG - prompt - 4 - ..\Debug\bin\dnlib.xml + true + + latest true - - pdbonly - true - ..\Release\bin\ - TRACE - prompt - 4 - ..\Release\bin\dnlib.xml - true + + + true + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - + + - - - LICENSE.txt - - - - - README.md - + + + + - - - \ No newline at end of file + +