Skip to content

Commit

Permalink
Merge pull request #3184 from icsharpcode/webcil
Browse files Browse the repository at this point in the history
Add minimal WebCIL support
  • Loading branch information
siegfriedpammer authored Mar 29, 2024
2 parents 9c82234 + 2d90c45 commit 3ebeb7d
Show file tree
Hide file tree
Showing 129 changed files with 1,427 additions and 862 deletions.
2 changes: 1 addition & 1 deletion ICSharpCode.BamlDecompiler/Baml/KnownThings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public KnownThings(IDecompilerTypeSystem typeSystem)
}
catch (Exception ex)
{
throw new ICSharpCode.Decompiler.DecompilerException(typeSystem.MainModule.PEFile, ex.Message, ex);
throw new ICSharpCode.Decompiler.DecompilerException(typeSystem.MainModule.MetadataFile, ex.Message, ex);
}
}

Expand Down
12 changes: 6 additions & 6 deletions ICSharpCode.BamlDecompiler/BamlDecompilerTypeSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ public class BamlDecompilerTypeSystem : SimpleCompilation, IDecompilerTypeSystem
"System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
};

public BamlDecompilerTypeSystem(PEFile mainModule, IAssemblyResolver assemblyResolver)
public BamlDecompilerTypeSystem(MetadataFile mainModule, IAssemblyResolver assemblyResolver)
{
if (mainModule == null)
throw new ArgumentNullException(nameof(mainModule));
if (assemblyResolver == null)
throw new ArgumentNullException(nameof(assemblyResolver));
// Load referenced assemblies and type-forwarder references.
// This is necessary to make .NET Core/PCL binaries work better.
var referencedAssemblies = new List<PEFile>();
var assemblyReferenceQueue = new Queue<(bool IsAssembly, PEFile MainModule, object Reference)>();
var referencedAssemblies = new List<MetadataFile>();
var assemblyReferenceQueue = new Queue<(bool IsAssembly, MetadataFile MainModule, object Reference)>();
var mainMetadata = mainModule.Metadata;
foreach (var h in mainMetadata.GetModuleReferences())
{
Expand All @@ -73,16 +73,16 @@ public BamlDecompilerTypeSystem(PEFile mainModule, IAssemblyResolver assemblyRes
{
assemblyReferenceQueue.Enqueue((true, mainModule, AssemblyNameReference.Parse(bamlReference)));
}
var comparer = KeyComparer.Create(((bool IsAssembly, PEFile MainModule, object Reference) reference) =>
var comparer = KeyComparer.Create(((bool IsAssembly, MetadataFile MainModule, object Reference) reference) =>
reference.IsAssembly ? "A:" + ((IAssemblyReference)reference.Reference).FullName :
"M:" + reference.Reference);
var processedAssemblyReferences = new HashSet<(bool IsAssembly, PEFile Parent, object Reference)>(comparer);
var processedAssemblyReferences = new HashSet<(bool IsAssembly, MetadataFile Parent, object Reference)>(comparer);
while (assemblyReferenceQueue.Count > 0)
{
var asmRef = assemblyReferenceQueue.Dequeue();
if (!processedAssemblyReferences.Add(asmRef))
continue;
PEFile asm;
MetadataFile asm;
if (asmRef.IsAssembly)
{
asm = assemblyResolver.Resolve((IAssemblyReference)asmRef.Reference);
Expand Down
6 changes: 2 additions & 4 deletions ICSharpCode.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ void DecompileConnections(XamlContext ctx, List<(LongSet, FieldAssignment)> fiel

IMethod connectMethod = null;
MethodDefinition connectMetadataEntry = default;
var module = ctx.TypeSystem.MainModule.PEFile;
var module = ctx.TypeSystem.MainModule.MetadataFile;

foreach (IMethod m in type.Methods)
{
Expand Down Expand Up @@ -171,9 +171,7 @@ void DecompileConnections(XamlContext ctx, List<(LongSet, FieldAssignment)> fiel

ctx.GeneratedMembers.Add(connectMethod.MetadataToken);



var body = module.Reader.GetMethodBody(connectMetadataEntry.RelativeVirtualAddress);
var body = module.GetMethodBody(connectMetadataEntry.RelativeVirtualAddress);
var genericContext = new GenericContext(
classTypeParameters: connectMethod.DeclaringType?.TypeParameters,
methodTypeParameters: connectMethod.TypeParameters);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Management.Automation;
using System.Threading;
Expand Down Expand Up @@ -89,7 +88,7 @@ protected override void ProcessRecord()

private void DoDecompile(string path)
{
PEFile module = Decompiler.TypeSystem.MainModule.PEFile;
MetadataFile module = Decompiler.TypeSystem.MainModule.MetadataFile;
var assemblyResolver = new UniversalAssemblyResolver(module.FileName, false, module.Metadata.DetectTargetFrameworkId());
WholeProjectDecompiler decompiler = new WholeProjectDecompiler(assemblyResolver);
decompiler.ProgressIndicator = this;
Expand Down
28 changes: 14 additions & 14 deletions ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ public CSharpDecompiler(string fileName, IAssemblyResolver assemblyResolver, Dec
/// <summary>
/// Creates a new <see cref="CSharpDecompiler"/> instance from the given <paramref name="module"/> using the given <paramref name="assemblyResolver"/> and <paramref name="settings"/>.
/// </summary>
public CSharpDecompiler(PEFile module, IAssemblyResolver assemblyResolver, DecompilerSettings settings)
public CSharpDecompiler(MetadataFile module, IAssemblyResolver assemblyResolver, DecompilerSettings settings)
: this(new DecompilerTypeSystem(module, assemblyResolver, settings), settings)
{
}
Expand All @@ -264,7 +264,7 @@ public CSharpDecompiler(DecompilerTypeSystem typeSystem, DecompilerSettings sett
this.typeSystem = typeSystem ?? throw new ArgumentNullException(nameof(typeSystem));
this.settings = settings;
this.module = typeSystem.MainModule;
this.metadata = module.PEFile.Metadata;
this.metadata = module.MetadataFile.Metadata;
if (module.TypeSystemOptions.HasFlag(TypeSystemOptions.Uncached))
throw new ArgumentException("Cannot use an uncached type system in the decompiler.");
}
Expand All @@ -276,7 +276,7 @@ public CSharpDecompiler(DecompilerTypeSystem typeSystem, DecompilerSettings sett
/// <param name="module">The module containing the member.</param>
/// <param name="member">The metadata token/handle of the member. Can be a TypeDef, MethodDef or FieldDef.</param>
/// <param name="settings">THe settings used to determine whether code should be hidden. E.g. if async methods are not transformed, async state machines are included in the decompiled code.</param>
public static bool MemberIsHidden(Metadata.PEFile module, EntityHandle member, DecompilerSettings settings)
public static bool MemberIsHidden(MetadataFile module, EntityHandle member, DecompilerSettings settings)
{
if (module == null || member.IsNil)
return false;
Expand Down Expand Up @@ -539,7 +539,7 @@ IDocumentationProvider CreateDefaultDocumentationProvider()
{
try
{
return XmlDocLoader.LoadDocumentation(module.PEFile);
return XmlDocLoader.LoadDocumentation(module.MetadataFile);
}
catch (System.Xml.XmlException)
{
Expand Down Expand Up @@ -633,7 +633,7 @@ void DoDecompileTypes(IEnumerable<TypeDefinitionHandle> types, DecompileRun deco
var typeDef = module.GetDefinition(typeDefHandle);
if (typeDef.Name == "<Module>" && typeDef.Members.Count == 0)
continue;
if (MemberIsHidden(module.PEFile, typeDefHandle, settings))
if (MemberIsHidden(module.MetadataFile, typeDefHandle, settings))
continue;
if (string.IsNullOrEmpty(typeDef.Namespace))
{
Expand Down Expand Up @@ -702,7 +702,7 @@ public ILTransformContext CreateILTransformContext(ILFunction function)
/// <summary>
/// Determines the "code-mappings" for a given TypeDef or MethodDef. See <see cref="CodeMappingInfo"/> for more information.
/// </summary>
public static CodeMappingInfo GetCodeMappingInfo(PEFile module, EntityHandle member)
public static CodeMappingInfo GetCodeMappingInfo(MetadataFile module, EntityHandle member)
{
var declaringType = (TypeDefinitionHandle)member.GetDeclaringType(module.Metadata);

Expand Down Expand Up @@ -744,7 +744,7 @@ public static CodeMappingInfo GetCodeMappingInfo(PEFile module, EntityHandle mem
return info;
}

private static void ReadCodeMappingInfo(PEFile module, CodeMappingInfo info, MethodDefinitionHandle parent, MethodDefinitionHandle part, Queue<MethodDefinitionHandle> connectedMethods, HashSet<TypeDefinitionHandle> processedNestedTypes)
private static void ReadCodeMappingInfo(MetadataFile module, CodeMappingInfo info, MethodDefinitionHandle parent, MethodDefinitionHandle part, Queue<MethodDefinitionHandle> connectedMethods, HashSet<TypeDefinitionHandle> processedNestedTypes)
{
var md = module.Metadata.GetMethodDefinition(part);

Expand All @@ -756,7 +756,7 @@ private static void ReadCodeMappingInfo(PEFile module, CodeMappingInfo info, Met

var declaringType = md.GetDeclaringType();

var blob = module.Reader.GetMethodBody(md.RelativeVirtualAddress).GetILReader();
var blob = module.GetMethodBody(md.RelativeVirtualAddress).GetILReader();
while (blob.RemainingBytes > 0)
{
var code = blob.DecodeOpCode();
Expand Down Expand Up @@ -1357,7 +1357,7 @@ EntityDeclaration DoDecompile(ITypeDefinition typeDef, DecompileRun decompileRun
// Decompile members that are not compiler-generated.
foreach (var entity in allOrderedEntities)
{
if (entity.MetadataToken.IsNil || MemberIsHidden(module.PEFile, entity.MetadataToken, settings))
if (entity.MetadataToken.IsNil || MemberIsHidden(module.MetadataFile, entity.MetadataToken, settings))
{
continue;
}
Expand Down Expand Up @@ -1403,7 +1403,7 @@ EntityDeclaration DoDecompile(ITypeDefinition typeDef, DecompileRun decompileRun
if (typeDecl.ClassType == ClassType.Enum)
{
Debug.Assert(typeDef.Kind == TypeKind.Enum);
EnumValueDisplayMode displayMode = DetectBestEnumValueDisplayMode(typeDef, module.PEFile);
EnumValueDisplayMode displayMode = DetectBestEnumValueDisplayMode(typeDef, module.MetadataFile);
switch (displayMode)
{
case EnumValueDisplayMode.FirstOnly:
Expand Down Expand Up @@ -1532,7 +1532,7 @@ void DoDecompileMember(IEntity entity, RecordDecompiler recordDecompiler, Partia
}
}

EnumValueDisplayMode DetectBestEnumValueDisplayMode(ITypeDefinition typeDef, PEFile module)
EnumValueDisplayMode DetectBestEnumValueDisplayMode(ITypeDefinition typeDef, MetadataFile module)
{
if (typeDef.HasAttribute(KnownAttribute.Flags))
return EnumValueDisplayMode.AllHex;
Expand Down Expand Up @@ -1608,7 +1608,7 @@ EntityDeclaration DoDecompile(IMethod method, DecompileRun decompileRun, ITypeRe
}
FixParameterNames(methodDecl);
var methodDefinition = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken);
if (!settings.LocalFunctions && LocalFunctionDecompiler.LocalFunctionNeedsAccessibilityChange(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken))
if (!settings.LocalFunctions && LocalFunctionDecompiler.LocalFunctionNeedsAccessibilityChange(method.ParentModule.MetadataFile, (MethodDefinitionHandle)method.MetadataToken))
{
// if local functions are not active and we're dealing with a local function,
// reduce the visibility of the method to private,
Expand Down Expand Up @@ -1701,7 +1701,7 @@ void DecompileBody(IMethod method, EntityDeclaration entityDecl, DecompileRun de
MethodBodyBlock methodBody;
try
{
methodBody = module.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
methodBody = module.MetadataFile.GetMethodBody(methodDef.RelativeVirtualAddress);
}
catch (BadImageFormatException ex)
{
Expand Down Expand Up @@ -1980,7 +1980,7 @@ EntityDeclaration DoDecompile(IField field, DecompileRun decompileRun, ITypeReso
string message;
try
{
var initVal = fieldDefinition.GetInitialValue(module.PEFile.Reader, TypeSystem);
var initVal = fieldDefinition.GetInitialValue(module.MetadataFile, TypeSystem);
message = string.Format(" Not supported: data({0}) ", BitConverter.ToString(initVal.ReadBytes(initVal.RemainingBytes)).Replace('-', ' '));
}
catch (BadImageFormatException ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ interface IProjectFileWriter
/// <param name="project">The information about the project being created.</param>
/// <param name="files">A collection of source files to be included into the project.</param>
/// <param name="module">The module being decompiled.</param>
void Write(TextWriter target, IProjectInfoProvider project, IEnumerable<ProjectItemInfo> files, PEFile module);
void Write(TextWriter target, IProjectInfoProvider project, IEnumerable<ProjectItemInfo> files, MetadataFile module);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ public void Write(
TextWriter target,
IProjectInfoProvider project,
IEnumerable<ProjectItemInfo> files,
PEFile module)
MetadataFile module)
{
const string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
string platformName = TargetServices.GetPlatformName(module);
string platformName = module is PEFile peFile ? TargetServices.GetPlatformName(peFile) : "AnyCPU";
var targetFramework = TargetServices.DetectTargetFramework(module);
if (targetFramework.Identifier == ".NETFramework" && targetFramework.VersionNumber == 200)
targetFramework = TargetServices.DetectTargetFrameworkNET20(module, project.AssemblyResolver, targetFramework);
Expand Down Expand Up @@ -80,26 +80,22 @@ public void Write(
w.WriteValue(platformName);
w.WriteEndElement(); // </Platform>

if (module.Reader.PEHeaders.IsDll)
{
w.WriteElementString("OutputType", "Library");
}
else
string outputType;

switch ((module as PEFile)?.Reader.PEHeaders.PEHeader.Subsystem)
{
switch (module.Reader.PEHeaders.PEHeader.Subsystem)
{
case Subsystem.WindowsGui:
w.WriteElementString("OutputType", "WinExe");
break;
case Subsystem.WindowsCui:
w.WriteElementString("OutputType", "Exe");
break;
default:
w.WriteElementString("OutputType", "Library");
break;
}
case Subsystem.WindowsGui:
outputType = "WinExe";
break;
case Subsystem.WindowsCui:
outputType = "Exe";
break;
default:
outputType = "Library";
break;
}

w.WriteElementString("OutputType", outputType);
w.WriteElementString("LangVersion", project.LanguageVersion.ToString().Replace("CSharp", "").Replace('_', '.'));

w.WriteElementString("AssemblyName", module.Name);
Expand All @@ -123,7 +119,8 @@ public void Write(
w.WriteStartElement("PropertyGroup"); // platform-specific
w.WriteAttributeString("Condition", " '$(Platform)' == '" + platformName + "' ");
w.WriteElementString("PlatformTarget", platformName);
if (targetFramework.VersionNumber > 400 && platformName == "AnyCPU" && (module.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) == 0)
if (targetFramework.VersionNumber > 400 && platformName == "AnyCPU"
&& ((module as PEFile)?.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) == 0)
{
w.WriteElementString("Prefer32Bit", "false");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void Write(
TextWriter target,
IProjectInfoProvider project,
IEnumerable<ProjectItemInfo> files,
PEFile module)
MetadataFile module)
{
using (XmlTextWriter xmlWriter = new XmlTextWriter(target))
{
Expand All @@ -76,7 +76,7 @@ public void Write(
}
}

static void Write(XmlTextWriter xml, IProjectInfoProvider project, IEnumerable<ProjectItemInfo> files, PEFile module)
static void Write(XmlTextWriter xml, IProjectInfoProvider project, IEnumerable<ProjectItemInfo> files, MetadataFile module)
{
xml.WriteStartElement("Project");

Expand Down Expand Up @@ -105,18 +105,30 @@ static void PlaceIntoTag(string tagName, XmlTextWriter xml, Action content)
}
}

static void WriteAssemblyInfo(XmlTextWriter xml, PEFile module, IProjectInfoProvider project, ProjectType projectType)
static void WriteAssemblyInfo(XmlTextWriter xml, MetadataFile module, IProjectInfoProvider project, ProjectType projectType)
{
xml.WriteElementString("AssemblyName", module.Name);

// Since we create AssemblyInfo.cs manually, we need to disable the auto-generation
xml.WriteElementString("GenerateAssemblyInfo", FalseString);

WriteOutputType(xml, module.Reader.PEHeaders.IsDll, module.Reader.PEHeaders.PEHeader.Subsystem, projectType);
string platformName;
CorFlags flags;
if (module is PEFile { Reader.PEHeaders: var headers } peFile)
{
WriteOutputType(xml, headers.IsDll, headers.PEHeader.Subsystem, projectType);
platformName = TargetServices.GetPlatformName(peFile);
flags = headers.CorHeader.Flags;
}
else
{
WriteOutputType(xml, isDll: true, Subsystem.Unknown, projectType);
platformName = AnyCpuString;
flags = 0;
}

WriteDesktopExtensions(xml, projectType);

string platformName = TargetServices.GetPlatformName(module);
var targetFramework = TargetServices.DetectTargetFramework(module);
if (targetFramework.Identifier == ".NETFramework" && targetFramework.VersionNumber == 200)
targetFramework = TargetServices.DetectTargetFrameworkNET20(module, project.AssemblyResolver, targetFramework);
Expand All @@ -134,7 +146,7 @@ static void WriteAssemblyInfo(XmlTextWriter xml, PEFile module, IProjectInfoProv
xml.WriteElementString("PlatformTarget", platformName);
}

if (platformName == AnyCpuString && (module.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) != 0)
if (platformName == AnyCpuString && (flags & CorFlags.Prefers32Bit) != 0)
{
xml.WriteElementString("Prefer32Bit", TrueString);
}
Expand Down Expand Up @@ -238,7 +250,7 @@ static void WriteResources(XmlTextWriter xml, IEnumerable<ProjectItemInfo> files
}
}

static void WriteReferences(XmlTextWriter xml, PEFile module, IProjectInfoProvider project, ProjectType projectType)
static void WriteReferences(XmlTextWriter xml, MetadataFile module, IProjectInfoProvider project, ProjectType projectType)
{
bool isNetCoreApp = TargetServices.DetectTargetFramework(module).Identifier == ".NETCoreApp";
var targetPacks = new HashSet<string>();
Expand Down Expand Up @@ -292,7 +304,7 @@ static string GetSdkString(ProjectType projectType)
}
}

static ProjectType GetProjectType(PEFile module)
static ProjectType GetProjectType(MetadataFile module)
{
foreach (var referenceName in module.AssemblyReferences.Select(r => r.Name))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ static class TargetServices
/// <param name="module">The module to get the target framework description for. Cannot be null.</param>
/// <returns>A new instance of the <see cref="TargetFramework"/> class that describes the specified <paramref name="module"/>.
/// </returns>
public static TargetFramework DetectTargetFramework(PEFile module)
public static TargetFramework DetectTargetFramework(MetadataFile module)
{
if (module is null)
{
Expand Down Expand Up @@ -216,17 +216,17 @@ public static string GetPlatformName(PEFile module)
};

/// <summary>
/// Gets exact <see cref="TargetFramework"/> if <see cref="PEFile.GetRuntime"/> is <see cref="TargetRuntime.Net_2_0"/>
/// Gets exact <see cref="TargetFramework"/> if <see cref="MetadataFile.GetRuntime"/> is <see cref="TargetRuntime.Net_2_0"/>
/// </summary>
public static TargetFramework DetectTargetFrameworkNET20(PEFile module, IAssemblyResolver assemblyResolver, TargetFramework targetFramework)
public static TargetFramework DetectTargetFrameworkNET20(MetadataFile module, IAssemblyResolver assemblyResolver, TargetFramework targetFramework)
{
var resolvedAssemblies = new HashSet<string>();
int version = 200;
GetFrameworkVersionNET20(module, assemblyResolver, resolvedAssemblies, ref version);
return new TargetFramework(targetFramework.Identifier, version, targetFramework.Profile);
}

static void GetFrameworkVersionNET20(PEFile module, IAssemblyResolver assemblyResolver, HashSet<string> resolvedAssemblies, ref int version)
static void GetFrameworkVersionNET20(MetadataFile module, IAssemblyResolver assemblyResolver, HashSet<string> resolvedAssemblies, ref int version)
{
foreach (var r in module.Metadata.AssemblyReferences)
{
Expand All @@ -245,7 +245,7 @@ static void GetFrameworkVersionNET20(PEFile module, IAssemblyResolver assemblyRe
break;
}

PEFile resolvedReference;
MetadataFile resolvedReference;
try
{
resolvedReference = assemblyResolver.Resolve(reference);
Expand Down
Loading

0 comments on commit 3ebeb7d

Please sign in to comment.