7 Commits
Author SHA1 Message Date
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
12 changed files with 436 additions and 10 deletions

No files matched your search

@@ -9,17 +9,17 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.3">
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="NUnit.Analyzers" Version="4.6.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
+2 -1
View File
@@ -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.3" />
<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.1" />
</ItemGroup>
<ItemGroup>
@@ -22,7 +22,7 @@ public static class CilInstructionCollectionExtensions
{
instructions.AddDefaultPrimitiveValue(corLibTypeSignature);
}
else if (type is ByReferenceTypeSignature)
else if (type is ByReferenceTypeSignature or PointerTypeSignature)
{
instructions.AddNullRef();
}
@@ -43,7 +43,7 @@ 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);
@@ -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,15 @@
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;
}
}
@@ -57,4 +57,9 @@ public static class FieldDefinitionExtensions
return false;
}
}
public static bool IsFixedBuffer(this FieldDefinition field)
{
return field.HasCustomAttribute("System.Runtime.CompilerServices", "FixedBufferAttribute");
}
}
@@ -0,0 +1,87 @@
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;
}
internal static ModuleDefinition GetModule(this IHasCustomAttribute _this)
{
return (_this as IModuleProvider)?.Module ?? 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(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(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;
}
}
}
+1 -1
View File
@@ -55,7 +55,7 @@ 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;
}
@@ -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, 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)}");
}
}
}
}
@@ -7,6 +7,33 @@ namespace AssetRipper.CIL;
public static class TypeDefinitionExtensions
{
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);
@@ -14,6 +41,32 @@ public static class TypeDefinitionExtensions
return method;
}
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.Module!.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;
@@ -27,6 +80,11 @@ public static class TypeDefinitionExtensions
return result;
}
public static MethodDefinition GetDefaultConstructor(this TypeDefinition type)
{
return type.Methods.First(m => m.IsInstanceConstructor() && m.Parameters.Count == 0);
}
public static bool IsStaticClass(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 -3
View File
@@ -2,13 +2,12 @@
<PropertyGroup>
<LangVersion>Latest</LangVersion>
<Nullable>enable</Nullable>
<IsAotCompatible>true</IsAotCompatible>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<PolySharpIncludeRuntimeSupportedAttributes>true</PolySharpIncludeRuntimeSupportedAttributes>
<Version>1.1.4</Version>
<Version>1.1.6</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<Copyright>Copyright (c) 2024 ds5678</Copyright>
<Copyright>Copyright (c) 2025 ds5678</Copyright>
<Authors>ds5678</Authors>
<Company>AssetRipper</Company>
<PackageLicenseExpression>MIT</PackageLicenseExpression>