32 Commits
Author SHA1 Message Date
ds5678 444e6164f6 Bump version to 1.3.0 in Directory.Build.props
Updated the VersionPrefix from 1.2.2 to 1.3.0 to prepare for a new minor release. No other changes were made.
2026-02-27 21:35:10 -08:00
ds5678 5df204203b Formatting 2026-02-27 21:30:41 -08:00
ds5678 9fa935cb0f Use the underlying types for enums in CilInstructionCollectionExtensions
This affects:
* AddDefaultValue
* InitializeDefaultValue
* AddLoadElement
* AddStoreElement

The code for AddLoadIndirect and AddStoreIndirect was also refactored, but functionality did not change.
2026-02-27 21:27:41 -08:00
ds5678 f10f09581d Fix output for AddLoadElement and AddStoreElement with pointer-sized types 2026-02-27 21:21:40 -08:00
ds5678 672c585e52 Fix stelem and ldelem missing the type 2026-02-27 21:12:08 -08:00
ds5678 c27bbc1164 Refactor generic param array creation to use for loop
Replaced LINQ-based creation of the typeArguments array in MethodStubber with a manual for loop. This improves readability and may enhance performance by eliminating LINQ overhead.
2026-02-27 11:54:11 -08:00
ds5678 eda49e0ff3 Upgrade from AsmResolver beta 5 to beta 6 2026-02-27 11:47:52 -08:00
ds5678 ae137f5afd Update test dependencies and remove coverlet.collector
Removed coverlet.collector package and updated test-related NuGet dependencies: Microsoft.NET.Test.Sdk to 18.3.0, NUnit to 4.5.0, and NUnit3TestAdapter to 6.1.0. No changes to NUnit.Analyzers or project references.
2026-02-27 11:23:10 -08:00
ds5678 b73fe6f510 Bump version to 1.2.2 2025-11-25 01:08:37 -08:00
ds5678 06216e6982 Support .NET 10 2025-11-25 01:07:10 -08:00
ds5678 90ebeb49a1 Update dependencies in test and main projects
Updated the following dependencies to their latest versions:
- `Microsoft.NET.Test.Sdk` to `18.0.1` in `AssetRipper.CIL.Tests.csproj`
- `NUnit.Analyzers` to `4.11.2` in `AssetRipper.CIL.Tests.csproj`
- `NUnit3TestAdapter` to `5.2.0` in `AssetRipper.CIL.Tests.csproj`
- `AsmResolver.DotNet` to `6.0.0-beta.5` in `AssetRipper.CIL.csproj`

These updates include improvements, fixes, and compatibility updates.
2025-11-25 01:05:08 -08:00
ds5678 2d68e67393 Bump version to 1.2.1
Updated the version number in the project properties from 1.2.0 to 1.2.1.
2025-09-15 15:47:05 -07:00
ds5678 d8230781d9 AddThrowNull 2025-09-15 14:43:00 -07:00
ds5678 2990c647c5 Add extensions from AssemblyDumper 2025-09-15 14:41:33 -07:00
ds5678 e871881517 Update project version to 1.2.0
Bump the version number in the project properties from 1.1.6 to 1.2.0.
2025-09-08 14:30:19 -07:00
ds5678 c1eceeda51 AsmResolver 6 beta 4
Resolves #2
2025-09-08 14:25:12 -07:00
ds5678 44996e27ff Update NuGet packages in project files
Updated several NuGet packages in `AssetRipper.CIL.Tests.csproj`:
- Microsoft.NET.Test.Sdk: 17.13.0 -> 17.14.1
- NUnit: 4.3.2 -> 4.4.0
- NUnit.Analyzers: 4.6.0 -> 4.10.0
- NUnit3TestAdapter: 5.0.0 -> 5.1.0

Also updated `System.Runtime.CompilerServices.Unsafe` in `AssetRipper.CIL.csproj` from version 6.1.1 to 6.1.2.
2025-09-08 14:20:20 -07:00
ds5678 0039dc95b3 Use IsAccessibleFromType when searching for a base constructor
* Related: #2
2025-04-08 15:06:57 -07:00
ds5678 0953640bf1 Add ReplaceMethodBodyWithThrowNull method
Introduces a new method `ReplaceMethodBodyWithThrowNull` in the `MethodDefinitionExtensions` class. This method modifies the body of a managed method to throw a `null` exception, with appropriate XML documentation. It includes checks to ensure the method is managed and has a body before making changes.
2025-03-23 20:20:02 -07:00
ds5678 c1e8657d67 Ensure references are imported 2025-03-21 19:23:54 -07:00
ds5678 09db4f64ae Bump version to 1.1.6 2025-03-20 23:03:22 -07:00
ds5678 0af62d2590 Support generating enums 2025-03-20 23:01:02 -07:00
ds5678 cc71118bfb Update package versions in project files
- Bump `coverlet.collector` to version `6.0.4`
- Bump `Microsoft.NET.Test.Sdk` to version `17.13.0`
- Bump `NUnit3TestAdapter` to version `5.0.0`
- Bump `AsmResolver.DotNet` to version `6.0.0-beta.3`
2025-03-20 22:01:39 -07:00
ds5678 21d8f70781 Add more extensions 2025-03-20 22:00:07 -07:00
ds5678 75a48b3ce0 Do not attempt to initialize fixed buffers 2025-03-20 21:11:29 -07:00
ds5678 c164acde5f Bump version 2025-01-23 14:28:47 -08:00
ds5678 8935137a6f Expand type check to include PointerTypeSignature
Modified conditions in CilInstructionCollectionExtensions.cs to check for both ByReferenceTypeSignature and PointerTypeSignature. This ensures null references are added and stored for both types.
2025-01-23 14:27:38 -08:00
ds5678 7e3e7bfa8c Bump version 2025-01-11 01:07:46 -08:00
ds5678 777fb5ea1c Fix AddLoadIndirect and AddStoreIndirect
* Resolves #1
2025-01-11 01:07:24 -08:00
ds5678 be8f11f6b5 Fix incorrect field references for generic types 2025-01-10 23:47:59 -08:00
ds5678 1338ca2f95 Add method stubbing tool 2025-01-10 23:46:05 -08:00
ds5678 e95ff3bae6 Bump tools to .NET 9 2025-01-10 23:45:32 -08:00
24 changed files with 951 additions and 62 deletions

No files matched your search

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\AssetRipper.CIL\AssetRipper.CIL.csproj" />
</ItemGroup>
</Project>
+18
View File
@@ -0,0 +1,18 @@
using AsmResolver.DotNet;
namespace AssetRipper.CIL.Stubber
{
internal class Program
{
static void Main(string[] args)
{
ModuleDefinition module = ModuleDefinition.FromFile(args[0]);
foreach (MethodDefinition method in module.GetAllTypes().SelectMany(t => t.Methods))
{
method.ReplaceMethodBodyWithMinimalImplementation();
}
module.Write(args[1]);
Console.WriteLine("Done!");
}
}
}
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="NUnit" Version="4.5.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.11.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetRipper.CIL\AssetRipper.CIL.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>
</Project>
+113
View File
@@ -0,0 +1,113 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Code.Cil;
using AsmResolver.DotNet.Signatures;
using AsmResolver.PE.DotNet.Cil;
using AsmResolver.PE.DotNet.Metadata.Tables;
namespace AssetRipper.CIL.Tests;
public class LoadIndirectTests
{
[TestCase(ElementType.I1, CilCode.Ldind_I1)]
[TestCase(ElementType.I2, CilCode.Ldind_I2)]
[TestCase(ElementType.I4, CilCode.Ldind_I4)]
[TestCase(ElementType.I8, CilCode.Ldind_I8)]
[TestCase(ElementType.U1, CilCode.Ldind_U1)]
[TestCase(ElementType.U2, CilCode.Ldind_U2)]
[TestCase(ElementType.U4, CilCode.Ldind_U4)]
[TestCase(ElementType.U8, CilCode.Ldind_I8)]
[TestCase(ElementType.I, CilCode.Ldind_I)]
[TestCase(ElementType.U, CilCode.Ldind_I)]
[TestCase(ElementType.R4, CilCode.Ldind_R4)]
[TestCase(ElementType.R8, CilCode.Ldind_R8)]
[TestCase(ElementType.Boolean, CilCode.Ldind_U1)]
[TestCase(ElementType.Char, CilCode.Ldind_U2)]
[TestCase(ElementType.Object, CilCode.Ldind_Ref)]
[TestCase(ElementType.String, CilCode.Ldind_Ref)]
[TestCase(ElementType.TypedByRef, CilCode.Ldobj)]
public void CorlibLoadIndirect(ElementType type, CilCode code)
{
ModuleDefinition module = new ModuleDefinition("TestModule");
CorLibTypeSignature? typeToLoad = module.CorLibTypeFactory.FromElementType(type);
Assert.That(typeToLoad, Is.Not.Null);
AssertCorrectLoadIndirect(typeToLoad, code);
}
[TestCase(ElementType.I1, CilCode.Ldind_I1)]
[TestCase(ElementType.I2, CilCode.Ldind_I2)]
[TestCase(ElementType.I4, CilCode.Ldind_I4)]
[TestCase(ElementType.I8, CilCode.Ldind_I8)]
[TestCase(ElementType.U1, CilCode.Ldind_U1)]
[TestCase(ElementType.U2, CilCode.Ldind_U2)]
[TestCase(ElementType.U4, CilCode.Ldind_U4)]
[TestCase(ElementType.U8, CilCode.Ldind_I8)]
public void EnumLoadIndirect(ElementType type, CilCode code)
{
ModuleDefinition module = new ModuleDefinition("TestModule");
TypeSignature? typeToLoad = CreateEnum(module, type).ToTypeSignature();
Assert.That(typeToLoad, Is.Not.Null);
AssertCorrectLoadIndirect(typeToLoad, code);
}
[Test]
public void PointerLoadIndirect()
{
ModuleDefinition module = new ModuleDefinition("TestModule");
PointerTypeSignature typeToLoad = module.CorLibTypeFactory.Void.MakePointerType();
AssertCorrectLoadIndirect(typeToLoad, CilCode.Ldind_I);
}
[Test]
public void GenericParameterLoadIndirect()
{
ModuleDefinition module = new ModuleDefinition("TestModule");
GenericParameterSignature typeToLoad = new GenericParameterSignature(module, GenericParameterType.Type, 0);
AssertCorrectLoadIndirect(typeToLoad, CilCode.Ldobj);
}
[Test]
public void SzArrayLoadIndirect()
{
ModuleDefinition module = new ModuleDefinition("TestModule");
TypeSignature typeToLoad = module.CorLibTypeFactory.Int32.MakeSzArrayType();
AssertCorrectLoadIndirect(typeToLoad, CilCode.Ldind_Ref);
}
[Test]
public void ClassLoadIndirect()
{
ModuleDefinition module = new ModuleDefinition("TestModule");
TypeDefinition type = new TypeDefinition("TestNamespace", "TestName", TypeAttributes.Public, module.CorLibTypeFactory.Object.ToTypeDefOrRef());
module.TopLevelTypes.Add(type);
AssertCorrectLoadIndirect(type.ToTypeSignature(), CilCode.Ldind_Ref);
}
[Test]
public void StructLoadIndirect()
{
ModuleDefinition module = new ModuleDefinition("TestModule");
TypeDefinition type = new TypeDefinition("TestNamespace", "TestName", TypeAttributes.Public | TypeAttributes.SequentialLayout | TypeAttributes.Sealed, module.DefaultImporter.ImportType(typeof(ValueType)));
module.TopLevelTypes.Add(type);
AssertCorrectLoadIndirect(type.ToTypeSignature(), CilCode.Ldobj);
}
private static void AssertCorrectLoadIndirect(TypeSignature typeToLoad, CilCode expectedOpCode)
{
MethodDefinition method = new MethodDefinition("Test", MethodAttributes.Public | MethodAttributes.Static, null);
method.CilMethodBody = new();
CilInstructionCollection instructions = method.CilMethodBody.Instructions;
instructions.AddLoadIndirect(typeToLoad);
Assert.That(instructions, Has.Count.EqualTo(1));
Assert.That(instructions[0].OpCode.Code, Is.EqualTo(expectedOpCode));
}
private static TypeDefinition CreateEnum(ModuleDefinition module, ElementType underlyingType)
{
TypeDefinition enumType = new TypeDefinition("TestNamespace", "TestEnum", TypeAttributes.Public, module.DefaultImporter.ImportType(typeof(Enum)));
module.TopLevelTypes.Add(enumType);
CorLibTypeSignature underlyingTypeSignature = module.CorLibTypeFactory.FromElementType(underlyingType) ?? throw new NullReferenceException();
FieldDefinition valueField = new FieldDefinition("value__", FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RuntimeSpecialName, underlyingTypeSignature);
enumType.Fields.Add(valueField);
return enumType;
}
}
+113
View File
@@ -0,0 +1,113 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Code.Cil;
using AsmResolver.DotNet.Signatures;
using AsmResolver.PE.DotNet.Cil;
using AsmResolver.PE.DotNet.Metadata.Tables;
namespace AssetRipper.CIL.Tests;
public class StoreIndirectTests
{
[TestCase(ElementType.I1, CilCode.Stind_I1)]
[TestCase(ElementType.I2, CilCode.Stind_I2)]
[TestCase(ElementType.I4, CilCode.Stind_I4)]
[TestCase(ElementType.I8, CilCode.Stind_I8)]
[TestCase(ElementType.U1, CilCode.Stind_I1)]
[TestCase(ElementType.U2, CilCode.Stind_I2)]
[TestCase(ElementType.U4, CilCode.Stind_I4)]
[TestCase(ElementType.U8, CilCode.Stind_I8)]
[TestCase(ElementType.I, CilCode.Stind_I)]
[TestCase(ElementType.U, CilCode.Stind_I)]
[TestCase(ElementType.R4, CilCode.Stind_R4)]
[TestCase(ElementType.R8, CilCode.Stind_R8)]
[TestCase(ElementType.Boolean, CilCode.Stind_I1)]
[TestCase(ElementType.Char, CilCode.Stind_I2)]
[TestCase(ElementType.Object, CilCode.Stind_Ref)]
[TestCase(ElementType.String, CilCode.Stind_Ref)]
[TestCase(ElementType.TypedByRef, CilCode.Stobj)]
public void CorlibStoreIndirect(ElementType type, CilCode code)
{
ModuleDefinition module = new ModuleDefinition("TestModule");
CorLibTypeSignature? typeToStore = module.CorLibTypeFactory.FromElementType(type);
Assert.That(typeToStore, Is.Not.Null);
AssertCorrectStoreIndirect(typeToStore, code);
}
[TestCase(ElementType.I1, CilCode.Stind_I1)]
[TestCase(ElementType.I2, CilCode.Stind_I2)]
[TestCase(ElementType.I4, CilCode.Stind_I4)]
[TestCase(ElementType.I8, CilCode.Stind_I8)]
[TestCase(ElementType.U1, CilCode.Stind_I1)]
[TestCase(ElementType.U2, CilCode.Stind_I2)]
[TestCase(ElementType.U4, CilCode.Stind_I4)]
[TestCase(ElementType.U8, CilCode.Stind_I8)]
public void EnumStoreIndirect(ElementType type, CilCode code)
{
ModuleDefinition module = new ModuleDefinition("TestModule");
TypeSignature? typeToStore = CreateEnum(module, type).ToTypeSignature();
Assert.That(typeToStore, Is.Not.Null);
AssertCorrectStoreIndirect(typeToStore, code);
}
[Test]
public void PointerStoreIndirect()
{
ModuleDefinition module = new ModuleDefinition("TestModule");
PointerTypeSignature typeToStore = module.CorLibTypeFactory.Void.MakePointerType();
AssertCorrectStoreIndirect(typeToStore, CilCode.Stind_I);
}
[Test]
public void GenericParameterStoreIndirect()
{
ModuleDefinition module = new ModuleDefinition("TestModule");
GenericParameterSignature typeToStore = new GenericParameterSignature(module, GenericParameterType.Type, 0);
AssertCorrectStoreIndirect(typeToStore, CilCode.Stobj);
}
[Test]
public void SzArrayStoreIndirect()
{
ModuleDefinition module = new ModuleDefinition("TestModule");
TypeSignature typeToStore = module.CorLibTypeFactory.Int32.MakeSzArrayType();
AssertCorrectStoreIndirect(typeToStore, CilCode.Stind_Ref);
}
[Test]
public void ClassStoreIndirect()
{
ModuleDefinition module = new ModuleDefinition("TestModule");
TypeDefinition type = new TypeDefinition("TestNamespace", "TestName", TypeAttributes.Public, module.CorLibTypeFactory.Object.ToTypeDefOrRef());
module.TopLevelTypes.Add(type);
AssertCorrectStoreIndirect(type.ToTypeSignature(), CilCode.Stind_Ref);
}
[Test]
public void StructStoreIndirect()
{
ModuleDefinition module = new ModuleDefinition("TestModule");
TypeDefinition type = new TypeDefinition("TestNamespace", "TestName", TypeAttributes.Public | TypeAttributes.SequentialLayout | TypeAttributes.Sealed, module.DefaultImporter.ImportType(typeof(ValueType)));
module.TopLevelTypes.Add(type);
AssertCorrectStoreIndirect(type.ToTypeSignature(), CilCode.Stobj);
}
private static void AssertCorrectStoreIndirect(TypeSignature typeToStore, CilCode expectedOpCode)
{
MethodDefinition method = new MethodDefinition("Test", MethodAttributes.Public | MethodAttributes.Static, null);
method.CilMethodBody = new();
CilInstructionCollection instructions = method.CilMethodBody.Instructions;
instructions.AddStoreIndirect(typeToStore);
Assert.That(instructions, Has.Count.EqualTo(1));
Assert.That(instructions[0].OpCode.Code, Is.EqualTo(expectedOpCode));
}
private static TypeDefinition CreateEnum(ModuleDefinition module, ElementType underlyingType)
{
TypeDefinition enumType = new TypeDefinition("TestNamespace", "TestEnum", TypeAttributes.Public, module.DefaultImporter.ImportType(typeof(Enum)));
module.TopLevelTypes.Add(enumType);
CorLibTypeSignature underlyingTypeSignature = module.CorLibTypeFactory.FromElementType(underlyingType) ?? throw new NullReferenceException();
FieldDefinition valueField = new FieldDefinition("value__", FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RuntimeSpecialName, underlyingTypeSignature);
enumType.Fields.Add(valueField);
return enumType;
}
}
@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -37,7 +37,7 @@ public static class CilInstructionCollectionEquality
}
// Instructions
return Equals(left.Instructions, right.Instructions);
return Equals(left.Instructions, right.Instructions);
}
public static bool Equals(CilExceptionHandler left, CilExceptionHandler right)
+12
View File
@@ -18,6 +18,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.CIL.Validator",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetRipper.CIL.Standardizer", "AssetRipper.CIL.Standardizer\AssetRipper.CIL.Standardizer.csproj", "{9E6D8580-5C3C-4AA8-89C9-34F475B79B24}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetRipper.CIL.Stubber", "AssetRipper.CIL.Stubber\AssetRipper.CIL.Stubber.csproj", "{A35A3452-2211-4DF5-A5A5-406FDF1E1A76}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetRipper.CIL.Tests", "AssetRipper.CIL.Tests\AssetRipper.CIL.Tests.csproj", "{FC03589F-7457-45E7-9628-F9E003F30BAB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -36,6 +40,14 @@ Global
{9E6D8580-5C3C-4AA8-89C9-34F475B79B24}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E6D8580-5C3C-4AA8-89C9-34F475B79B24}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E6D8580-5C3C-4AA8-89C9-34F475B79B24}.Release|Any CPU.Build.0 = Release|Any CPU
{A35A3452-2211-4DF5-A5A5-406FDF1E1A76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A35A3452-2211-4DF5-A5A5-406FDF1E1A76}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A35A3452-2211-4DF5-A5A5-406FDF1E1A76}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A35A3452-2211-4DF5-A5A5-406FDF1E1A76}.Release|Any CPU.Build.0 = Release|Any CPU
{FC03589F-7457-45E7-9628-F9E003F30BAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC03589F-7457-45E7-9628-F9E003F30BAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC03589F-7457-45E7-9628-F9E003F30BAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC03589F-7457-45E7-9628-F9E003F30BAB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+3 -2
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<PackageId>AssetRipper.CIL</PackageId>
@@ -15,8 +15,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AsmResolver.DotNet" Version="6.0.0-beta.1" />
<PackageReference Include="AsmResolver.DotNet" Version="6.0.0-beta.6" />
<PackageReference Include="PolySharp" Version="1.15.0" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.2" />
</ItemGroup>
<ItemGroup>
@@ -3,6 +3,7 @@ using AsmResolver.DotNet.Signatures;
using AsmResolver.PE.DotNet.Cil;
using AsmResolver.PE.DotNet.Metadata.Tables;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace AssetRipper.CIL;
@@ -22,10 +23,14 @@ public static class CilInstructionCollectionExtensions
{
instructions.AddDefaultPrimitiveValue(corLibTypeSignature);
}
else if (type is ByReferenceTypeSignature)
else if (type is ByReferenceTypeSignature or PointerTypeSignature)
{
instructions.AddNullRef();
}
else if (type is TypeDefOrRefSignature t && TryGetEnumUnderlyingType(instructions, t, out CorLibTypeSignature? underlyingType))
{
instructions.AddDefaultPrimitiveValue(underlyingType);
}
else if (type.IsValueTypeOrGenericParameter())
{
instructions.AddDefaultValueForUnknownType(type);
@@ -43,11 +48,15 @@ public static class CilInstructionCollectionExtensions
{
instructions.InitializeDefaultPrimitiveValue(corLibTypeSignature, localVariable);
}
else if (type is ByReferenceTypeSignature)
else if (type is ByReferenceTypeSignature or PointerTypeSignature)
{
instructions.AddNullRef();
instructions.Add(CilOpCodes.Stloc, localVariable);
}
else if (type is TypeDefOrRefSignature t && TryGetEnumUnderlyingType(instructions, t, out CorLibTypeSignature? underlyingType))
{
instructions.InitializeDefaultPrimitiveValue(underlyingType, localVariable);
}
else if (type.IsValueTypeOrGenericParameter())
{
instructions.InitializeDefaultValueForUnknownType(localVariable);
@@ -71,6 +80,12 @@ public static class CilInstructionCollectionExtensions
instructions.Add(CilOpCodes.Ceq);
}
public static void AddThrowNull(this CilInstructionCollection instructions)
{
instructions.Add(CilOpCodes.Ldnull);
instructions.Add(CilOpCodes.Throw);
}
public static CilInstruction AddLoadElement(this CilInstructionCollection instructions, TypeSignature elementType)
{
return elementType switch
@@ -88,11 +103,24 @@ public static class CilInstructionCollectionExtensions
ElementType.U8 => instructions.Add(CilOpCodes.Ldelem_I8),
ElementType.R4 => instructions.Add(CilOpCodes.Ldelem_R4),
ElementType.R8 => instructions.Add(CilOpCodes.Ldelem_R8),
_ => elementType.IsValueType ? instructions.Add(CilOpCodes.Ldelem) : instructions.Add(CilOpCodes.Ldelem_Ref)
ElementType.I or ElementType.U => instructions.Add(CilOpCodes.Ldelem_I),
ElementType.Object or ElementType.String => instructions.Add(CilOpCodes.Ldelem_Ref),
_ => instructions.Add(CilOpCodes.Ldelem, elementType.ToTypeDefOrRef()),
},
GenericParameterSignature => instructions.Add(CilOpCodes.Ldelem),
_ => elementType.IsValueType ? instructions.Add(CilOpCodes.Ldelem) : instructions.Add(CilOpCodes.Ldelem_Ref)
GenericParameterSignature => instructions.Add(CilOpCodes.Ldelem, elementType.ToTypeDefOrRef()),
PointerTypeSignature => instructions.Add(CilOpCodes.Ldelem_I),
TypeDefOrRefSignature t => TryGetEnumUnderlyingType(instructions, t, out CorLibTypeSignature? underlyingType)
? instructions.AddLoadElement(underlyingType)
: AddUnknown(instructions, t),
_ => AddUnknown(instructions, elementType)
};
static CilInstruction AddUnknown(CilInstructionCollection instructions, TypeSignature elementType)
{
return elementType.IsValueType
? instructions.Add(CilOpCodes.Ldelem, elementType.ToTypeDefOrRef())
: instructions.Add(CilOpCodes.Ldelem_Ref);
}
}
public static CilInstruction AddStoreElement(this CilInstructionCollection instructions, TypeSignature elementType)
@@ -112,11 +140,24 @@ public static class CilInstructionCollectionExtensions
ElementType.U8 => instructions.Add(CilOpCodes.Stelem_I8),
ElementType.R4 => instructions.Add(CilOpCodes.Stelem_R4),
ElementType.R8 => instructions.Add(CilOpCodes.Stelem_R8),
_ => instructions.Add(CilOpCodes.Stelem_Ref),
ElementType.I or ElementType.U => instructions.Add(CilOpCodes.Stelem_I),
ElementType.Object or ElementType.String => instructions.Add(CilOpCodes.Stelem_Ref),
_ => instructions.Add(CilOpCodes.Stelem, elementType.ToTypeDefOrRef()),
},
GenericParameterSignature => instructions.Add(CilOpCodes.Stelem),
_ => elementType.IsValueType ? instructions.Add(CilOpCodes.Stelem) : instructions.Add(CilOpCodes.Stelem_Ref)
GenericParameterSignature => instructions.Add(CilOpCodes.Stelem, elementType.ToTypeDefOrRef()),
PointerTypeSignature => instructions.Add(CilOpCodes.Stelem_I),
TypeDefOrRefSignature t => TryGetEnumUnderlyingType(instructions, t, out CorLibTypeSignature? underlyingType)
? instructions.AddStoreElement(underlyingType)
: AddUnknown(instructions, t),
_ => AddUnknown(instructions, elementType)
};
static CilInstruction AddUnknown(CilInstructionCollection instructions, TypeSignature elementType)
{
return elementType.IsValueType
? instructions.Add(CilOpCodes.Stelem, elementType.ToTypeDefOrRef())
: instructions.Add(CilOpCodes.Stelem_Ref);
}
}
public static CilInstruction AddLoadIndirect(this CilInstructionCollection instructions, TypeSignature type)
@@ -129,17 +170,29 @@ public static class CilInstructionCollectionExtensions
ElementType.I2 => instructions.Add(CilOpCodes.Ldind_I2),
ElementType.I4 => instructions.Add(CilOpCodes.Ldind_I4),
ElementType.I8 or ElementType.U8 => instructions.Add(CilOpCodes.Ldind_I8),
ElementType.U1 => instructions.Add(CilOpCodes.Ldind_U1),
ElementType.U2 => instructions.Add(CilOpCodes.Ldind_U2),
ElementType.U1 or ElementType.Boolean => instructions.Add(CilOpCodes.Ldind_U1),
ElementType.U2 or ElementType.Char => instructions.Add(CilOpCodes.Ldind_U2),
ElementType.U4 => instructions.Add(CilOpCodes.Ldind_U4),
ElementType.R4 => instructions.Add(CilOpCodes.Ldind_R4),
ElementType.R8 => instructions.Add(CilOpCodes.Ldind_R8),
ElementType.I or ElementType.U => instructions.Add(CilOpCodes.Ldind_I),
ElementType.Object or ElementType.String => instructions.Add(CilOpCodes.Ldind_Ref),
_ => instructions.Add(CilOpCodes.Ldobj, type.ToTypeDefOrRef()),
},
PointerTypeSignature => instructions.Add(CilOpCodes.Ldind_I),
_ => instructions.Add(CilOpCodes.Ldobj, type.ToTypeDefOrRef()),
GenericParameterSignature => instructions.Add(CilOpCodes.Ldobj, type.ToTypeDefOrRef()),
TypeDefOrRefSignature t => TryGetEnumUnderlyingType(instructions, t, out CorLibTypeSignature? underlyingType)
? instructions.AddLoadIndirect(underlyingType)
: AddUnknown(instructions, type),
_ => AddUnknown(instructions, type),
};
static CilInstruction AddUnknown(CilInstructionCollection instructions, TypeSignature type)
{
return type.IsValueType
? instructions.Add(CilOpCodes.Ldobj, type.ToTypeDefOrRef())
: instructions.Add(CilOpCodes.Ldind_Ref);
}
}
public static CilInstruction AddStoreIndirect(this CilInstructionCollection instructions, TypeSignature type)
@@ -152,17 +205,29 @@ public static class CilInstructionCollectionExtensions
ElementType.I2 => instructions.Add(CilOpCodes.Stind_I2),
ElementType.I4 => instructions.Add(CilOpCodes.Stind_I4),
ElementType.I8 or ElementType.U8 => instructions.Add(CilOpCodes.Stind_I8),
ElementType.U1 => instructions.Add(CilOpCodes.Stind_I1),
ElementType.U2 => instructions.Add(CilOpCodes.Stind_I2),
ElementType.U1 or ElementType.Boolean => instructions.Add(CilOpCodes.Stind_I1),
ElementType.U2 or ElementType.Char => instructions.Add(CilOpCodes.Stind_I2),
ElementType.U4 => instructions.Add(CilOpCodes.Stind_I4),
ElementType.R4 => instructions.Add(CilOpCodes.Stind_R4),
ElementType.R8 => instructions.Add(CilOpCodes.Stind_R8),
ElementType.I or ElementType.U => instructions.Add(CilOpCodes.Stind_I),
ElementType.Object or ElementType.String => instructions.Add(CilOpCodes.Stind_Ref),
_ => instructions.Add(CilOpCodes.Stobj, type.ToTypeDefOrRef()),
},
PointerTypeSignature => instructions.Add(CilOpCodes.Stind_I),
_ => instructions.Add(CilOpCodes.Stobj, type.ToTypeDefOrRef()),
GenericParameterSignature => instructions.Add(CilOpCodes.Stobj, type.ToTypeDefOrRef()),
TypeDefOrRefSignature t => TryGetEnumUnderlyingType(instructions, t, out CorLibTypeSignature? underlyingType)
? instructions.AddStoreIndirect(underlyingType)
: AddUnknown(instructions, t),
_ => AddUnknown(instructions, type),
};
static CilInstruction AddUnknown(CilInstructionCollection instructions, TypeSignature type)
{
return type.IsValueType
? instructions.Add(CilOpCodes.Stobj, type.ToTypeDefOrRef())
: instructions.Add(CilOpCodes.Stind_Ref);
}
}
/// <summary>
@@ -293,4 +358,10 @@ public static class CilInstructionCollectionExtensions
break;
}
}
private static bool TryGetEnumUnderlyingType(CilInstructionCollection instructions, TypeDefOrRefSignature type, [NotNullWhen(true)] out CorLibTypeSignature? underlyingType)
{
underlyingType = type.GetUnderlyingType(instructions.Owner.Owner?.DeclaringModule?.RuntimeContext) as CorLibTypeSignature;
return underlyingType is not null;
}
}
@@ -0,0 +1,83 @@
using AsmResolver.DotNet.Signatures;
namespace AssetRipper.CIL;
internal static class CorLibTypeFactoryExtensions
{
public static CorLibTypeSignature? FromType(this CorLibTypeFactory factory, Type type)
{
if (type == typeof(bool))
{
return factory.Boolean;
}
if (type == typeof(char))
{
return factory.Char;
}
if (type == typeof(byte))
{
return factory.Byte;
}
if (type == typeof(sbyte))
{
return factory.SByte;
}
if (type == typeof(short))
{
return factory.Int16;
}
if (type == typeof(ushort))
{
return factory.UInt16;
}
if (type == typeof(int))
{
return factory.Int32;
}
if (type == typeof(uint))
{
return factory.UInt32;
}
if (type == typeof(long))
{
return factory.Int64;
}
if (type == typeof(ulong))
{
return factory.UInt64;
}
if (type == typeof(float))
{
return factory.Single;
}
if (type == typeof(double))
{
return factory.Double;
}
if (type == typeof(string))
{
return factory.String;
}
if (type == typeof(IntPtr))
{
return factory.IntPtr;
}
if (type == typeof(UIntPtr))
{
return factory.UIntPtr;
}
if (type == typeof(object))
{
return factory.Object;
}
if (type == typeof(void))
{
return factory.Void;
}
if (type == typeof(TypedReference))
{
return factory.TypedReference;
}
return null;
}
}
@@ -0,0 +1,30 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures;
namespace AssetRipper.CIL;
public static class CustomAttributeExtensions
{
public static CustomAttributeArgument AddFixedArgument(this CustomAttribute attribute, TypeSignature paramType, object paramValue)
{
attribute.Signature ??= new();
CustomAttributeArgument argument = new(paramType, paramValue);
attribute.Signature.FixedArguments.Add(argument);
return argument;
}
public static CustomAttributeArgument AddFixedArgument<T>(this CustomAttribute attribute, TypeSignature paramType, T[] paramValue)
{
CustomAttributeArgument argument = new CustomAttributeArgument(paramType, paramValue.Select(e => (object?)e));
attribute.Signature!.FixedArguments.Add(argument);
return argument;
}
public static CustomAttributeNamedArgument AddNamedArgument(this CustomAttribute attribute, TypeSignature memberType, string memberName, TypeSignature paramType, object paramValue, CustomAttributeArgumentMemberType memberKind)
{
CustomAttributeArgument argument = new CustomAttributeArgument(paramType, paramValue);
CustomAttributeNamedArgument namedArgument = new CustomAttributeNamedArgument(memberKind, memberName, memberType, argument);
attribute.Signature!.NamedArguments.Add(namedArgument);
return namedArgument;
}
}
+9 -2
View File
@@ -35,12 +35,19 @@ public static class EventDefinitionExtensions
public static bool HasBackingField(this EventDefinition eventDefinition, [NotNullWhen(true)] out FieldDefinition? backingField)
{
TypeSignature? eventType = eventDefinition.EventType?.ToTypeSignature();
RuntimeContext? runtimeContext = eventDefinition.DeclaringModule?.RuntimeContext;
if (runtimeContext is null)
{
backingField = null;
return false;
}
TypeSignature? eventType = eventDefinition.EventType?.ToTypeSignature(runtimeContext);
if (eventType is not null)
{
foreach (FieldDefinition field in eventDefinition.DeclaringType?.Fields ?? [])
{
if (field.IsPrivate && field.Name == eventDefinition.Name && SignatureComparer.Default.Equals(field.Signature?.FieldType, eventType))
if (field.IsPrivate && field.Name == eventDefinition.Name && runtimeContext.SignatureComparer.Equals(field.Signature?.FieldType, eventType))
{
backingField = field;
return true;
+9 -3
View File
@@ -1,5 +1,4 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures;
using System.Diagnostics.CodeAnalysis;
namespace AssetRipper.CIL;
@@ -33,7 +32,8 @@ public static class FieldDefinitionExtensions
property = field.DeclaringType?.Properties.FirstOrDefault(p => p.Name == propertyName);
return property is not null
&& field.IsStatic == property.IsStatic()
&& SignatureComparer.Default.Equals(field.Signature?.FieldType, property.Signature?.ReturnType);
&& field.DeclaringModule?.RuntimeContext is { } runtimeContext
&& runtimeContext.SignatureComparer.Equals(field.Signature?.FieldType, property.Signature?.ReturnType);
}
else
{
@@ -49,7 +49,8 @@ public static class FieldDefinitionExtensions
@event = field.DeclaringType?.Events.FirstOrDefault(e => e.Name == field.Name);
return @event is not null
&& field.IsStatic == @event.IsStatic()
&& SignatureComparer.Default.Equals(@event.EventType?.ToTypeSignature(), field.Signature?.FieldType);
&& @event.DeclaringModule?.RuntimeContext is { } runtimeContext
&& runtimeContext.SignatureComparer.Equals(@event.EventType?.ToTypeSignature(runtimeContext), field.Signature?.FieldType);
}
else
{
@@ -57,4 +58,9 @@ public static class FieldDefinitionExtensions
return false;
}
}
public static bool IsFixedBuffer(this FieldDefinition field)
{
return field.HasCustomAttribute("System.Runtime.CompilerServices", "FixedBufferAttribute");
}
}
@@ -0,0 +1,104 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures;
using System.Runtime.CompilerServices;
namespace AssetRipper.CIL;
public static class IHasCustomAttributeExtensions
{
public static CustomAttribute AddCustomAttribute(this IHasCustomAttribute _this, IMethodDefOrRef constructor)
{
if (constructor is ICustomAttributeType usableConstructor)
{
CustomAttribute attribute = new(usableConstructor);
_this.CustomAttributes.Add(attribute);
return attribute;
}
else
{
throw new ArgumentException("Constructor is not ICustomAttributeType", nameof(constructor));
}
}
public static CustomAttribute AddCustomAttribute(this IHasCustomAttribute _this, IMethodDefOrRef constructor, TypeSignature paramType, object paramValue)
{
CustomAttribute attribute = _this.AddCustomAttribute(constructor);
attribute.AddFixedArgument(paramType, paramValue);
return attribute;
}
public static CustomAttribute AddCustomAttribute<T>(this IHasCustomAttribute _this, IMethodDefOrRef constructor, SzArrayTypeSignature paramType, T[] paramValue)
{
CustomAttribute attribute = _this.AddCustomAttribute(constructor);
attribute.AddFixedArgument(paramType, paramValue);
return attribute;
}
public static CustomAttribute AddCustomAttribute(this IHasCustomAttribute _this, IMethodDefOrRef constructor, params (TypeSignature, object)[] parameters)
{
CustomAttribute attribute = _this.AddCustomAttribute(constructor);
for (int i = 0; i < parameters.Length; i++)
{
attribute.AddFixedArgument(parameters[i].Item1, parameters[i].Item2);
}
return attribute;
}
internal static ModuleDefinition GetModule(this IHasCustomAttribute _this)
{
return (_this as IModuleProvider)?.ContextModule ?? throw new ArgumentException("Entity does not have a module", nameof(_this));
}
public static CustomAttribute AddFlagsAttribute(this IHasCustomAttribute _this)
{
ModuleDefinition module = _this.GetModule();
if (module.TryGetTopLevelType("System", nameof(FlagsAttribute), out TypeDefinition? flagsType))
{
return _this.AddCustomAttribute(flagsType.GetDefaultConstructor());
}
else
{
TypeReference flagsTypeRef = new(module.CorLibTypeFactory.CorLibScope, "System", nameof(FlagsAttribute));
MemberReference constructor = new(flagsTypeRef, ".ctor", MethodSignature.CreateInstance(module.CorLibTypeFactory.Void));
return _this.AddCustomAttribute(module.DefaultImporter.ImportMethod(constructor));
}
}
/// <summary>
/// Applies the <see cref="CompilerGeneratedAttribute"/>
/// </summary>
/// <param name="_this">The entity on which to apply the attribute</param>
/// <returns>The resulting custom attribute</returns>
public static CustomAttribute AddCompilerGeneratedAttribute(this IHasCustomAttribute _this)
{
ModuleDefinition module = _this.GetModule();
if (module.TryGetTopLevelType("System.Runtime.CompilerServices", nameof(CompilerGeneratedAttribute), out TypeDefinition? compilerGeneratedType))
{
return _this.AddCustomAttribute(compilerGeneratedType.GetDefaultConstructor());
}
else
{
AssemblyReference compilerServicesAssembly = GetOrAddReferenceToSystemRuntimeCompilerServices(module);
TypeReference compilerGeneratedTypeRef = new(compilerServicesAssembly, "System.Runtime.CompilerServices", nameof(CompilerGeneratedAttribute));
MemberReference constructor = new(compilerGeneratedTypeRef, ".ctor", MethodSignature.CreateInstance(module.CorLibTypeFactory.Void));
return _this.AddCustomAttribute(module.DefaultImporter.ImportMethod(constructor));
}
static AssemblyReference GetOrAddReferenceToSystemRuntimeCompilerServices(ModuleDefinition module)
{
AssemblyReference? compilerServicesAssembly = module.AssemblyReferences.FirstOrDefault(a => a.Name == "System.Runtime.CompilerServices");
if (compilerServicesAssembly is null)
{
Version version = module.CorLibTypeFactory.CorLibScope.GetAssembly()?.Version ?? new Version(0, 0, 0, 0);
compilerServicesAssembly = new("System.Runtime.CompilerServices", version);
module.AssemblyReferences.Add(compilerServicesAssembly);
}
return compilerServicesAssembly;
}
}
}
@@ -11,7 +11,7 @@ public sealed class SwitchRemover : ICilManipulator
{
public void Execute(CilManipulationContext context)
{
ModuleDefinition? module = context.Instructions.Owner.Owner.Module;
ModuleDefinition? module = context.Instructions.Owner.Owner?.DeclaringModule;
if (module is null)
{
return;
+30 -4
View File
@@ -1,4 +1,5 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Code.Cil;
using AsmResolver.DotNet.Collections;
using AsmResolver.DotNet.Signatures;
using AsmResolver.PE.DotNet.Metadata.Tables;
@@ -23,6 +24,24 @@ public static class MethodDefinitionExtensions
MethodStubber.FillMethodBodyWithStub(methodDefinition);
}
/// <summary>
/// Replaces the method body with: <code>throw null;</code>
/// </summary>
/// <remarks>
/// If the method is not managed or should not have a body, this method does nothing.
/// </remarks>
/// <param name="methodDefinition">The method to modify.</param>
public static void ReplaceMethodBodyWithThrowNull(this MethodDefinition methodDefinition)
{
if (!methodDefinition.IsManagedMethodWithBody())
{
return;
}
methodDefinition.CilMethodBody = new();
CilInstructionCollection methodInstructions = methodDefinition.CilMethodBody.Instructions;
methodInstructions.AddThrowNull();
}
public static bool IsInstance(this MethodDefinition methodDefinition)
{
return !methodDefinition.IsStatic;
@@ -72,6 +91,11 @@ public static class MethodDefinitionExtensions
&& !managedMethod.IsRuntime;
}
public static CilInstructionCollection GetInstructions(this MethodDefinition method)
{
return method.CilMethodBody?.Instructions ?? throw new ArgumentException("CilMethodBody was null", nameof(method));
}
public static bool IsAutoPropertyGetMethod(this MethodDefinition method, [NotNullWhen(true)] out FieldDefinition? backingField)
{
if (!method.IsGetMethod
@@ -89,7 +113,8 @@ public static class MethodDefinitionExtensions
if (field is null
|| !field.IsPrivate
|| field.IsStatic != method.IsStatic
|| !SignatureComparer.Default.Equals(field.Signature?.FieldType, method.Signature?.ReturnType))
|| method.DeclaringModule?.RuntimeContext is not { } runtimeContext
|| !runtimeContext.SignatureComparer.Equals(field.Signature?.FieldType, method.Signature?.ReturnType))
{
backingField = null;
return false;
@@ -119,7 +144,8 @@ public static class MethodDefinitionExtensions
if (field is null
|| !field.IsPrivate
|| field.IsStatic != method.IsStatic
|| !SignatureComparer.Default.Equals(field.Signature?.FieldType, method.Parameters[0].ParameterType))
|| method.DeclaringModule?.RuntimeContext is not { } runtimeContext
|| !runtimeContext.SignatureComparer.Equals(field.Signature?.FieldType, method.Parameters[0].ParameterType))
{
backingField = null;
return false;
@@ -131,12 +157,12 @@ public static class MethodDefinitionExtensions
}
}
public static Parameter AddParameter(this MethodDefinition method, TypeSignature parameterSignature, string parameterName)
public static Parameter AddParameter(this MethodDefinition method, TypeSignature parameterSignature, string? parameterName)
{
return method.AddParameter(parameterSignature, parameterName, out _);
}
public static Parameter AddParameter(this MethodDefinition method, TypeSignature parameterSignature, string parameterName, out ParameterDefinition parameterDefinition)
public static Parameter AddParameter(this MethodDefinition method, TypeSignature parameterSignature, string? parameterName, out ParameterDefinition parameterDefinition)
{
parameterDefinition = new ParameterDefinition((ushort)(method.Signature!.ParameterTypes.Count + 1), parameterName, default);
method.Signature.ParameterTypes.Add(parameterSignature);
+38 -23
View File
@@ -16,19 +16,19 @@ internal static class MethodStubber
return;
}
methodDefinition.CilMethodBody = new(methodDefinition);
methodDefinition.CilMethodBody = new();
CilInstructionCollection methodInstructions = methodDefinition.CilMethodBody.Instructions;
if (methodDefinition.IsAutoPropertyGetMethod(out FieldDefinition? backingField))
{
if (methodDefinition.IsStatic)
{
methodInstructions.Add(CilOpCodes.Ldsfld, backingField);
methodInstructions.Add(CilOpCodes.Ldsfld, backingField.MakeGenericIfNecessary());
}
else
{
methodInstructions.Add(CilOpCodes.Ldarg_0);
methodInstructions.Add(CilOpCodes.Ldfld, backingField);
methodInstructions.Add(CilOpCodes.Ldfld, backingField.MakeGenericIfNecessary());
}
methodInstructions.Add(CilOpCodes.Ret);
return;
@@ -38,13 +38,13 @@ internal static class MethodStubber
if (methodDefinition.IsStatic)
{
methodInstructions.Add(CilOpCodes.Ldarg_0);
methodInstructions.Add(CilOpCodes.Stsfld, backingField);
methodInstructions.Add(CilOpCodes.Stsfld, backingField.MakeGenericIfNecessary());
}
else
{
methodInstructions.Add(CilOpCodes.Ldarg_0);
methodInstructions.Add(CilOpCodes.Ldarg_1);
methodInstructions.Add(CilOpCodes.Stfld, backingField);
methodInstructions.Add(CilOpCodes.Stfld, backingField.MakeGenericIfNecessary());
}
methodInstructions.Add(CilOpCodes.Ret);
return;
@@ -55,14 +55,14 @@ internal static class MethodStubber
TypeDefinition declaringType = methodDefinition.DeclaringType!;
foreach (FieldDefinition field in declaringType.Fields)
{
if (field.IsStatic || field.Signature is null)
if (field.IsStatic || field.Signature is null || field.IsFixedBuffer())
{
continue;
}
methodInstructions.Add(CilOpCodes.Ldarg_0);
methodInstructions.AddDefaultValue(field.Signature.FieldType);
methodInstructions.Add(CilOpCodes.Stfld, field);
methodInstructions.Add(CilOpCodes.Stfld, field.MakeGenericIfNecessary());
}
}
@@ -118,6 +118,11 @@ internal static class MethodStubber
{
return null;
}
RuntimeContext? runtimeContext = methodDefinition.DeclaringModule?.RuntimeContext;
if (runtimeContext is null)
{
return null;
}
if (baseType is TypeDefinition baseTypeDef)
{
@@ -125,19 +130,19 @@ internal static class MethodStubber
}
else if (baseType is TypeReference baseTypeRef)
{
TypeDefinition? baseTypeResolved = baseTypeRef.Resolve();
if (baseTypeResolved is null)
ResolutionStatus resolutionStatus = baseTypeRef.Resolve(runtimeContext, out TypeDefinition? baseTypeResolved);
if (baseTypeResolved is null || resolutionStatus is not ResolutionStatus.Success)
{
return null;
}
MethodDefinition? baseConstructor = GetFirstCompatibleConstructor(methodDefinition.DeclaringType, baseTypeResolved);
if (baseConstructor is null || methodDefinition.DeclaringType.Module is null)
if (baseConstructor is null || methodDefinition.DeclaringType.DeclaringModule is null)
{
return null;
}
else
{
return methodDefinition.DeclaringType.Module.DefaultImporter.ImportMethod(baseConstructor);
return methodDefinition.DeclaringType.DeclaringModule.DefaultImporter.ImportMethod(baseConstructor);
}
}
else if (baseType is TypeSpecification baseTypeSpec)
@@ -147,17 +152,17 @@ internal static class MethodStubber
{
return null;
}
TypeDefinition? baseTypeResolved = genericInstanceTypeSignature.GenericType.Resolve();
if (baseTypeResolved is null)
ResolutionStatus resolutionStatus = genericInstanceTypeSignature.GenericType.Resolve(runtimeContext, out TypeDefinition? baseTypeResolved);
if (baseTypeResolved is null || resolutionStatus is not ResolutionStatus.Success)
{
return null;
}
MethodDefinition? baseConstructor = GetFirstCompatibleConstructor(methodDefinition.DeclaringType, baseTypeResolved);
if (baseConstructor is null || methodDefinition.DeclaringType.Module is null)
if (baseConstructor is null || methodDefinition.DeclaringType.DeclaringModule is null)
{
return null;
}
IMethodDefOrRef baseConstructorImported = methodDefinition.DeclaringType.Module.DefaultImporter.ImportMethod(baseConstructor);
IMethodDefOrRef baseConstructorImported = methodDefinition.DeclaringType.DeclaringModule.DefaultImporter.ImportMethod(baseConstructor);
return new MemberReference(baseType, baseConstructorImported.Name, baseConstructorImported.Signature);
}
@@ -165,14 +170,7 @@ internal static class MethodStubber
static MethodDefinition? GetFirstCompatibleConstructor(TypeDefinition declaringType, TypeDefinition baseType)
{
if (declaringType.Module == baseType.Module)
{
return baseType.Methods.FirstOrDefault(m => m.IsInstanceConstructor() && !m.IsPrivate);
}
else
{
return baseType.Methods.FirstOrDefault(m => m.IsInstanceConstructor() && (m.IsFamily || m.IsPublic));
}
return baseType.Methods.FirstOrDefault(m => m.IsInstanceConstructor() && m.IsAccessibleFromType(declaringType, m.DeclaringModule?.RuntimeContext));
}
}
@@ -181,4 +179,21 @@ internal static class MethodStubber
baseConstructor = TryGetBaseConstructor(methodDefinition);
return baseConstructor is not null;
}
private static IFieldDescriptor MakeGenericIfNecessary(this FieldDefinition field)
{
if (field.DeclaringType is null or { GenericParameters.Count: 0 })
{
return field;
}
int count = field.DeclaringType.GenericParameters.Count;
TypeSignature[] typeArguments = new TypeSignature[count];
for (int i = 0; i < count; i++)
{
typeArguments[i] = new GenericParameterSignature(field.DeclaringModule, GenericParameterType.Type, i);
}
return new MemberReference(field.DeclaringType.MakeGenericInstanceType(field.DeclaringModule?.RuntimeContext, typeArguments).ToTypeDefOrRef(), field.Name, field.Signature);
}
}
@@ -0,0 +1,94 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures;
using AsmResolver.PE.DotNet.Metadata.Tables;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace AssetRipper.CIL;
public static class ModuleDefinitionExtensions
{
public static bool TryGetTopLevelType(this ModuleDefinition module, string? @namespace, string? name, [NotNullWhen(true)] out TypeDefinition? type)
{
type = module.TopLevelTypes.FirstOrDefault(t => t.Namespace == @namespace && t.Name == name);
return type != null;
}
public static bool HasTopLevelType(this ModuleDefinition module, string? @namespace, string? name)
{
return module.TryGetTopLevelType(@namespace, name, out _);
}
public static TypeDefinition AddEmptyEnum(this ModuleDefinition module, string? @namespace, string? name, Visibility visibility, ElementType underlyingType)
{
CorLibTypeSignature underlyingTypeSignature = module.CorLibTypeFactory.FromElementType(underlyingType)
?? throw new ArgumentOutOfRangeException(nameof(underlyingType), underlyingType, null);
TypeReference baseType = new(module.CorLibTypeFactory.CorLibScope, "System", "Enum");
TypeDefinition type = new(@namespace, name, visibility.ToTypeAttributes() | TypeAttributes.Sealed, module.DefaultImporter.ImportType(baseType));
module.TopLevelTypes.Add(type);
AddEnumValue(type, underlyingTypeSignature);
return type;
static void AddEnumValue(TypeDefinition type, TypeSignature underlyingType)
{
FieldSignature fieldSignature = new FieldSignature(underlyingType);
FieldDefinition fieldDef = new FieldDefinition("value__", FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RuntimeSpecialName, fieldSignature);
type.Fields.Add(fieldDef);
}
}
public static TypeDefinition AddExistingEnum<T>(this ModuleDefinition module) where T : struct, Enum
{
Type underlyingType = Enum.GetUnderlyingType(typeof(T));
bool isSigned = underlyingType == typeof(sbyte) || underlyingType == typeof(short) || underlyingType == typeof(int) || underlyingType == typeof(long);
CorLibTypeSignature underlyingTypeSignature = module.CorLibTypeFactory.FromType(underlyingType)
?? throw new InvalidOperationException($"Failed to get underlying type for {typeof(T)}");
TypeDefinition type = module.AddEmptyEnum(typeof(T).Namespace, typeof(T).Name, VisibilityExtensions.GetVisibility(typeof(T)), underlyingTypeSignature.ElementType);
foreach (T value in GetValues())
{
type.AddEnumField(value.ToString(), ToInt64(value));
}
if (typeof(T).CustomAttributes.Any(a => a.AttributeType is { Namespace: "System", Name: nameof(FlagsAttribute) }))
{
type.AddFlagsAttribute();
}
return type;
static IEnumerable<T> GetValues()
{
#if NETSTANDARD
return Enum.GetValues(typeof(T)).Cast<T>();
#else
return Enum.GetValues<T>();
#endif
}
long ToInt64(T value)
{
if (Unsafe.SizeOf<T>() == sizeof(long))
{
return Unsafe.As<T, long>(ref value);
}
else if (Unsafe.SizeOf<T>() == sizeof(int))
{
return isSigned ? Unsafe.As<T, int>(ref value) : Unsafe.As<T, uint>(ref value);
}
else if (Unsafe.SizeOf<T>() == sizeof(short))
{
return isSigned ? Unsafe.As<T, short>(ref value) : Unsafe.As<T, ushort>(ref value);
}
else if (Unsafe.SizeOf<T>() == sizeof(byte))
{
return isSigned ? Unsafe.As<T, sbyte>(ref value) : Unsafe.As<T, byte>(ref value);
}
else
{
throw new NotSupportedException($"Unsupported enum underlying type {typeof(T)}");
}
}
}
}
@@ -1,5 +1,4 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures;
using System.Diagnostics.CodeAnalysis;
namespace AssetRipper.CIL;
@@ -13,7 +12,8 @@ public static class PropertyDefinitionExtensions
if (backingField is null
|| !backingField.IsPrivate
|| backingField.IsStatic != property.IsStatic()
|| !SignatureComparer.Default.Equals(backingField.Signature?.FieldType, property.Signature?.ReturnType))
|| backingField.DeclaringModule?.RuntimeContext is not { } runtimeContext
|| !runtimeContext.SignatureComparer.Equals(backingField.Signature?.FieldType, property.Signature?.ReturnType))
{
backingField = null;
return false;
+75 -4
View File
@@ -7,14 +7,67 @@ namespace AssetRipper.CIL;
public static class TypeDefinitionExtensions
{
public static MethodDefinition AddMethod(this TypeDefinition type, string methodName, MethodAttributes methodAttributes, TypeSignature returnType)
public static FieldDefinition AddField(this TypeDefinition type, string? fieldName, TypeSignature fieldType, bool isStatic = false, Visibility visibility = Visibility.Public)
{
FieldAttributes fieldAttributes = visibility.ToFieldAttributes() | (isStatic ? FieldAttributes.Static : default);
FieldDefinition field = new FieldDefinition(fieldName, fieldAttributes, fieldType);
type.Fields.Add(field);
return field;
}
public static FieldDefinition AddConstantField(
this TypeDefinition type,
TypeSignature fieldType,
string fieldName,
Constant constant,
Visibility visibility = Visibility.Public)
{
FieldDefinition field = new FieldDefinition(
fieldName,
FieldAttributes.Static |
FieldAttributes.Literal |
FieldAttributes.HasDefault |
visibility.ToFieldAttributes(),
new FieldSignature(fieldType));
field.Constant = constant;
type.Fields.Add(field);
return field;
}
public static MethodDefinition AddMethod(this TypeDefinition type, string? methodName, MethodAttributes methodAttributes, TypeSignature returnType)
{
MethodDefinition method = CreateMethod(methodName, methodAttributes, returnType);
type.Methods.Add(method);
return method;
}
private static MethodDefinition CreateMethod(string methodName, MethodAttributes methodAttributes, TypeSignature returnType)
public static MethodDefinition AddConstructor(this TypeDefinition type, Visibility visibility = Visibility.Public)
{
MethodAttributes methodAttributes = visibility.ToMethodAttributes() | MethodAttributes.SpecialName | MethodAttributes.RuntimeSpecialName;
return type.AddMethod(".ctor", methodAttributes, type.DeclaringModule!.CorLibTypeFactory.Void);
}
public static FieldDefinition AddEnumField(this TypeDefinition type, string name, long value)
{
TypeSignature underlyingType = type.GetEnumUnderlyingType() ?? throw new ArgumentException("Type is not an enum", nameof(type));
return type.AddConstantField(type.ToTypeSignature(), name, FromValue(value, underlyingType.ElementType));
static Constant FromValue(long value, ElementType elementType) => elementType switch
{
ElementType.I1 => Constant.FromValue((sbyte)value),
ElementType.U1 => Constant.FromValue((byte)value),
ElementType.I2 => Constant.FromValue((short)value),
ElementType.U2 => Constant.FromValue((ushort)value),
ElementType.I4 => Constant.FromValue((int)value),
ElementType.U4 => Constant.FromValue((uint)value),
ElementType.I8 => Constant.FromValue(value),
ElementType.U8 => Constant.FromValue(unchecked((ulong)value)),
_ => throw new ArgumentOutOfRangeException(nameof(elementType)),
};
}
private static MethodDefinition CreateMethod(string? methodName, MethodAttributes methodAttributes, TypeSignature returnType)
{
bool isStatic = (methodAttributes & MethodAttributes.Static) != 0;
MethodSignature methodSignature = isStatic
@@ -22,12 +75,30 @@ public static class TypeDefinitionExtensions
: MethodSignature.CreateInstance(returnType);
MethodDefinition result = new MethodDefinition(methodName, methodAttributes, methodSignature);
result.CilMethodBody = new CilMethodBody(result);
result.CilMethodBody = new CilMethodBody();
return result;
}
public static bool IsStaticClass(this TypeDefinition type)
/// <summary>
/// Gets the default constructor for a <see cref="TypeDefinition"/>. Throws an exception if one doesn't exist.
/// </summary>
public static MethodDefinition GetDefaultConstructor(this TypeDefinition type)
{
return type.Methods.First(m => m.IsInstanceConstructor() && m.Parameters.Count == 0);
}
/// <summary>
/// Get all the instance constructors for a type
/// </summary>
/// <param name="type">The definition for the type</param>
/// <returns>All the instance constructors</returns>
public static IEnumerable<MethodDefinition> GetInstanceConstructors(this TypeDefinition type)
{
return type.Methods.Where(m => !m.IsStatic && m.IsConstructor);
}
public static bool IsStatic(this TypeDefinition type)
{
return type.IsSealed && type.IsAbstract;
}
+84
View File
@@ -0,0 +1,84 @@
using AsmResolver.PE.DotNet.Metadata.Tables;
namespace AssetRipper.CIL;
public enum Visibility
{
Public,
Protected,
Internal,
ProtectedInternal,
Private,
PrivateProtected
}
public static class VisibilityExtensions
{
public static MethodAttributes ToMethodAttributes(this Visibility visibility) => visibility switch
{
Visibility.Public => MethodAttributes.Public,
Visibility.Protected => MethodAttributes.Family,
Visibility.Internal => MethodAttributes.Assembly,
Visibility.ProtectedInternal => MethodAttributes.FamilyOrAssembly,
Visibility.Private => MethodAttributes.Private,
Visibility.PrivateProtected => MethodAttributes.FamilyAndAssembly,
_ => throw new ArgumentOutOfRangeException(nameof(visibility), visibility, null),
};
public static FieldAttributes ToFieldAttributes(this Visibility visibility) => visibility switch
{
Visibility.Public => FieldAttributes.Public,
Visibility.Protected => FieldAttributes.Family,
Visibility.Internal => FieldAttributes.Assembly,
Visibility.ProtectedInternal => FieldAttributes.FamilyOrAssembly,
Visibility.Private => FieldAttributes.Private,
Visibility.PrivateProtected => FieldAttributes.FamilyAndAssembly,
_ => throw new ArgumentOutOfRangeException(nameof(visibility), visibility, null),
};
public static TypeAttributes ToTypeAttributes(this Visibility visibility, bool nested = false) => nested
? visibility switch
{
Visibility.Public => TypeAttributes.NestedPublic,
Visibility.Protected => TypeAttributes.NestedFamily,
Visibility.Internal => TypeAttributes.NestedAssembly,
Visibility.ProtectedInternal => TypeAttributes.NestedFamilyOrAssembly,
Visibility.Private => TypeAttributes.NestedPrivate,
Visibility.PrivateProtected => TypeAttributes.NestedFamilyAndAssembly,
_ => throw new ArgumentOutOfRangeException(nameof(visibility), visibility, null),
}
: visibility switch
{
Visibility.Public => TypeAttributes.Public,
Visibility.Internal => TypeAttributes.NotPublic,
_ => throw new ArgumentOutOfRangeException(nameof(visibility), visibility, null),
};
internal static Visibility GetVisibility(Type type)
{
if (type.IsPublic || type.IsNestedPublic)
{
return Visibility.Public;
}
if (type.IsNestedFamily)
{
return Visibility.Protected;
}
if (type.IsNotPublic || type.IsNestedAssembly)
{
return Visibility.Internal;
}
if (type.IsNestedFamORAssem)
{
return Visibility.ProtectedInternal;
}
if (type.IsNestedPrivate)
{
return Visibility.Private;
}
if (type.IsNestedFamANDAssem)
{
return Visibility.PrivateProtected;
}
throw new ArgumentException($"Unknown visibility for type {type}", nameof(type));
}
}
+2 -4
View File
@@ -2,13 +2,11 @@
<PropertyGroup>
<LangVersion>Latest</LangVersion>
<Nullable>enable</Nullable>
<IsAotCompatible>true</IsAotCompatible>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<PolySharpIncludeRuntimeSupportedAttributes>true</PolySharpIncludeRuntimeSupportedAttributes>
<Version>1.1.3</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<Copyright>Copyright (c) 2024 ds5678</Copyright>
<VersionPrefix>1.3.0</VersionPrefix>
<Copyright>Copyright (c) 2025 ds5678</Copyright>
<Authors>ds5678</Authors>
<Company>AssetRipper</Company>
<PackageLicenseExpression>MIT</PackageLicenseExpression>