Skip to content

Instantly share code, notes, and snippets.

@Akeit0
Created November 24, 2023 13:40
Show Gist options
  • Save Akeit0/3dff421b719acd9cdb8ac6121404e30a to your computer and use it in GitHub Desktop.
Save Akeit0/3dff421b719acd9cdb8ac6121404e30a to your computer and use it in GitHub Desktop.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_EDITOR
using Mono.Cecil;
using Mono.Cecil.Cil;
public static class ByRefGen {
public static void RemoveByRefType(string src, string dst) {
ModuleDefinition moduleDefinition = AssemblyDefinition.ReadAssembly(src).MainModule;
moduleDefinition.Types.Remove(moduleDefinition.GetType("System", "ByRef`1"));
moduleDefinition.Write(dst);
}
public static FieldReference CreateFieldReference(FieldDefinition definition) {
var declaringType = new GenericInstanceType(definition.DeclaringType);
foreach (var parameter in definition.DeclaringType.GenericParameters) {
declaringType.GenericArguments.Add(parameter);
}
return new FieldReference(definition.Name, definition.FieldType, declaringType);
}
static PropertyDefinition GetProperty(this TypeDefinition typeDefinition, string name) {
foreach (var property in typeDefinition.Properties) {
if (property.Name == name) {
return property;
}
}
return null;
}
static MethodDefinition GetMethod(this TypeDefinition typeDefinition, string name) {
foreach (var method in typeDefinition.Methods) {
if (method.Name == name) {
return method;
}
}
return null;
}
public static void AddByRefType(string src, string dst) {
ModuleDefinition moduleDefinition = AssemblyDefinition.ReadAssembly(src).MainModule;
TypeDefinition spanDef = moduleDefinition.GetType("System", "Span`1");
var newType = new TypeDefinition("System", "ByRef`1", spanDef.Attributes, spanDef.BaseType);
void AddDeclaringTypeAndImplAttributes(MethodDefinition methodDefinition) {
methodDefinition.DeclaringType = newType;
methodDefinition.ImplAttributes = MethodImplAttributes.AggressiveInlining;
}
var genericParameter = new GenericParameter("T", newType);
newType.GenericParameters.Add(genericParameter);
foreach (var attribute in spanDef.CustomAttributes) {
if (attribute.AttributeType.Namespace is "System" or "System.Runtime.CompilerServices")
newType.CustomAttributes.Add(attribute);
}
ByReferenceType refType = new ByReferenceType(genericParameter) {
DeclaringType = newType
};
var voidType = moduleDefinition.GetType("System", "Void");
var voidPtrType = new PointerType(voidType);
#region UnsafeMethods
{
var instanceType = new GenericInstanceType(newType);
instanceType.GenericArguments.Add(genericParameter);
var refOfByrefType = new ByReferenceType(instanceType) {DeclaringType = newType};
var asPointerMethod = new MethodDefinition("AsPointer", MethodAttributes.Static | MethodAttributes.Public,
voidPtrType
);
AddDeclaringTypeAndImplAttributes(asPointerMethod);
asPointerMethod.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None,
refOfByrefType));
ILProcessor processor = asPointerMethod.Body.GetILProcessor();
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Conv_U);
processor.Emit(OpCodes.Ret);
newType.Methods.Add(asPointerMethod);
var asRefMethod = new MethodDefinition("AsRef", MethodAttributes.Static | MethodAttributes.Public,
refOfByrefType
) ;
AddDeclaringTypeAndImplAttributes(asRefMethod);
asRefMethod.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None,
voidPtrType));
processor = asRefMethod.Body.GetILProcessor();
asRefMethod.Body.Variables.Add(new VariableDefinition(refOfByrefType));
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Stloc_0);
processor.Emit(OpCodes.Ldloc_0);
processor.Emit(OpCodes.Ret);
newType.Methods.Add(asRefMethod);
}
#endregion
var propertyMethod = new MethodDefinition("get_Value",
MethodAttributes.SpecialName | MethodAttributes.Public | MethodAttributes.HideBySig,
refType) ;
AddDeclaringTypeAndImplAttributes(propertyMethod);
var property = new PropertyDefinition("Value", PropertyAttributes.None, refType) {
GetMethod = propertyMethod,
DeclaringType = newType
};
var ctorWithRefMethod = new MethodDefinition(".ctor",
MethodAttributes.SpecialName | MethodAttributes.Public | MethodAttributes.HideBySig |
MethodAttributes.RTSpecialName,
voidType
) ;
AddDeclaringTypeAndImplAttributes(ctorWithRefMethod);
ctorWithRefMethod.Parameters.Add(new ParameterDefinition("ptr", ParameterAttributes.None,
refType));
var ctorWithPtrMethod = new MethodDefinition(".ctor",
MethodAttributes.SpecialName | MethodAttributes.Public | MethodAttributes.HideBySig |
MethodAttributes.RTSpecialName,
voidType
) ;
AddDeclaringTypeAndImplAttributes(ctorWithPtrMethod);
ctorWithPtrMethod.Parameters.Add(new ParameterDefinition("ptr", ParameterAttributes.None,
voidPtrType));
var byReferenceDef = moduleDefinition.GetType("System", "ByReference`1");
if (byReferenceDef != null) {
var field = spanDef.Fields[0];
var t = new GenericInstanceType(byReferenceDef);
t.GenericArguments.Add(genericParameter);
var _pointerField = new FieldDefinition("_pointer", field.Attributes, t) {
DeclaringType = newType
};
newType.Fields.Add(_pointerField);
var fieldReference = CreateFieldReference(_pointerField);
var byReferenceDefValueProperty = byReferenceDef.GetProperty( "Value");
var methodDefinition = byReferenceDefValueProperty.GetMethod;
var getMethod = new MethodReference("get_Value", refType, t) {
CallingConvention = methodDefinition.CallingConvention,
HasThis = methodDefinition.HasThis,
ExplicitThis = methodDefinition.ExplicitThis,
};
ILProcessor processor = propertyMethod.Body.GetILProcessor();
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldflda, fieldReference);
processor.Emit(OpCodes.Call, getMethod);
processor.Emit(OpCodes.Ret);
methodDefinition = byReferenceDef.GetMethod(".ctor");//new ByRef<T>(ref T value)
var byReferenceCtor = new MethodReference(".ctor", voidType, t) {
CallingConvention = methodDefinition.CallingConvention,
HasThis = methodDefinition.HasThis,
ExplicitThis = methodDefinition.ExplicitThis,
};
byReferenceCtor.Parameters.Add(new ParameterDefinition(refType));
processor = ctorWithRefMethod.Body.GetILProcessor();
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldarg_1);
processor.Emit(OpCodes.Newobj, byReferenceCtor);
processor.Emit(OpCodes.Stfld, fieldReference);
processor.Emit(OpCodes.Ret);
processor = ctorWithPtrMethod.Body.GetILProcessor();
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldarg_1);
processor.Emit(OpCodes.Stind_I);
processor.Emit(OpCodes.Ret);
}
//if the assembly does not have ByReference<T> type, we will use dummy implementation
else {
var field = spanDef.Fields[0];
var _dummyField = new FieldDefinition("_dummy", field.Attributes, field.FieldType) {
DeclaringType = newType
};
newType.Fields.Add(_dummyField);
var newEx = moduleDefinition.GetType("System", "NotSupportedException").Methods[0];
ILProcessor processor = propertyMethod.Body.GetILProcessor();
processor.Emit(OpCodes.Newobj, newEx);
processor.Emit(OpCodes.Throw);
processor = ctorWithRefMethod.Body.GetILProcessor();
processor.Emit(OpCodes.Newobj, newEx);
processor.Emit(OpCodes.Throw);
processor = ctorWithPtrMethod.Body.GetILProcessor();
processor.Emit(OpCodes.Newobj, newEx);
processor.Emit(OpCodes.Throw);
}
newType.Properties.Add(property);
newType.Methods.Add(propertyMethod);
newType.Methods.Add(ctorWithRefMethod);
newType.Methods.Add(ctorWithPtrMethod);
moduleDefinition.Types.Add(newType);
moduleDefinition.Write(dst);
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment