Skip to content

Commit

Permalink
#36, #37, #54. Sync method strict weave mode.
Browse files Browse the repository at this point in the history
  • Loading branch information
inversionhourglass committed Feb 7, 2024
1 parent 231a339 commit e8bf4bc
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 5 deletions.
2 changes: 1 addition & 1 deletion src/Rougamo.Fody/Extensions/InstructionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static bool IsCallAny(this Instruction instruction, string methodName)
return (instruction.OpCode.Code == Code.Call || instruction.OpCode.Code == Code.Callvirt) && ((MethodReference)instruction.Operand).Name == methodName;
}

public static Instruction Copy(this Instruction instruction)
public static Instruction Clone(this Instruction instruction)
{
if (instruction.Operand == null) return Instruction.Create(instruction.OpCode);
if (instruction.Operand is sbyte sbyteValue) return Instruction.Create(instruction.OpCode, sbyteValue);
Expand Down
107 changes: 107 additions & 0 deletions src/Rougamo.Fody/Extensions/MonoExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,18 @@ public static FieldReference ImportInto(this FieldReference fieldRef, ModuleDefi
public static MethodReference ImportInto(this MethodReference methodRef, ModuleDefinition moduleDef) =>
moduleDef.ImportReference(methodRef);

public static TypeReference ImportInto(this ParameterDefinition parameterDef, ModuleDefinition moduleDef)
{
var parameterTypeRef = parameterDef.ParameterType.ImportInto(moduleDef);
var isByReference = parameterDef.ParameterType.IsByReference;
if (isByReference)
{
parameterTypeRef = ((ByReferenceType)parameterDef.ParameterType).ElementType.ImportInto(moduleDef);
}

return parameterTypeRef;
}

#endregion Import

public static TypeDefinition ResolveStateMachine(this MethodDefinition methodDef, string stateMachineAttributeName)
Expand Down Expand Up @@ -509,5 +521,100 @@ private static bool NeedUpdateHandlerAnchor(Instruction? target, Instruction? ch
updateTarget = current;
return true;
}

public static MethodDefinition Clone(this MethodDefinition methodDef, string methodName)
{
var clonedMethodDef = new MethodDefinition(methodName, methodDef.Attributes, methodDef.ReturnType);

if (methodDef.HasCustomAttributes)
{
clonedMethodDef.CustomAttributes.Add(methodDef.CustomAttributes);
}

var map = new Dictionary<object, object>();

if (methodDef.HasGenericParameters)
{
foreach (var generic in methodDef.GenericParameters)
{
var clonedGeneric = new GenericParameter(generic.Name, clonedMethodDef);
clonedMethodDef.GenericParameters.Add(clonedGeneric);

map[generic] = clonedGeneric;
}
}

foreach (var parameterDef in methodDef.Parameters)
{
var parameterTypeRef = parameterDef.ParameterType;
var isByReference = parameterTypeRef.IsByReference;
if (isByReference)
{
parameterTypeRef = ((ByReferenceType)parameterTypeRef).ElementType;
}
if (parameterTypeRef.IsGenericParameter && map.TryGetValue(parameterTypeRef, out var mapTo))
{
parameterTypeRef = (GenericParameter)mapTo;
}
if (isByReference)
{
parameterTypeRef = new ByReferenceType(parameterTypeRef);
map[parameterDef.ParameterType] = parameterTypeRef;
}
var clonedParameterDef = new ParameterDefinition(parameterDef.Name, parameterDef.Attributes, parameterTypeRef);
clonedMethodDef.Parameters.Add(clonedParameterDef);

map[parameterDef] = clonedParameterDef;
}

foreach (var variable in methodDef.Body.Variables)
{
var clonedVariable = new VariableDefinition(variable.VariableType);
clonedMethodDef.Body.Variables.Add(clonedVariable);

map[variable] = clonedVariable;
}

var instructionMap = new Dictionary<Instruction, Instruction>();
var brs = new List<Instruction>();
foreach (var instruction in methodDef.Body.Instructions)
{
var cloned = instruction.Clone();
clonedMethodDef.Body.Instructions.Add(cloned);

instructionMap[instruction] = cloned;
if (cloned.Operand != null)
{
if (map.TryGetValue(cloned.Operand, out var value))
{
cloned.Operand = value;
}
else if (cloned.Operand is Instruction)
{
brs.Add(cloned);
}
}
}
foreach (var br in brs)
{
br.Operand = instructionMap[(Instruction)br.Operand];
}

foreach (var handler in methodDef.Body.ExceptionHandlers)
{
clonedMethodDef.Body.ExceptionHandlers.Add(new ExceptionHandler(handler.HandlerType)
{
TryStart = instructionMap[handler.TryStart],
TryEnd = instructionMap[handler.TryEnd],
HandlerStart = instructionMap[handler.HandlerStart],
HandlerEnd = instructionMap[handler.HandlerEnd]
});
}

clonedMethodDef.Body.InitLocals = methodDef.Body.InitLocals;
clonedMethodDef.Body.MaxStackSize = methodDef.Body.MaxStackSize;

return clonedMethodDef;
}
}
}
6 changes: 5 additions & 1 deletion src/Rougamo.Fody/LoadBasicReference.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using Mono.Cecil.Rocks;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

Expand All @@ -16,13 +18,15 @@ void LoadBasicReference()
_typeBoolRef = FindTypeDefinition(typeof(bool).FullName).ImportInto(ModuleDefinition);
_typeObjectRef = FindTypeDefinition(typeof(object).FullName).ImportInto(ModuleDefinition);
_typeExceptionRef = FindTypeDefinition(typeof(Exception).FullName).ImportInto(ModuleDefinition);
_typeDebuggerStepThroughAttributeRef = FindTypeDefinition(typeof(DebuggerStepThroughAttribute).FullName).ImportInto(ModuleDefinition);
_typeListDef = FindTypeDefinition(typeof(List<>).FullName);
_typeListRef = _typeListDef.ImportInto(ModuleDefinition);

_methodGetTypeFromHandleRef = typeof(Type).GetMethod("GetTypeFromHandle", new[] { typeof(RuntimeTypeHandle) }).ImportInto(ModuleDefinition);
_methodGetMethodFromHandleRef = typeof(MethodBase).GetMethod("GetMethodFromHandle", new[] { typeof(RuntimeMethodHandle), typeof(RuntimeTypeHandle) }).ImportInto(ModuleDefinition);
_methodListAddRef = _typeListDef.Methods.Single(x => x.Name == "Add" && x.Parameters.Count == 1 && x.Parameters[0].ParameterType == _typeListDef.GenericParameters[0]);
_methodListToArrayRef = _typeListDef.Methods.Single(x => x.Name == "ToArray" && !x.HasParameters);
_methodDebuggerStepThroughCtorRef = _typeDebuggerStepThroughAttributeRef.Resolve().Methods.Single(x => x.IsConstructor && !x.IsStatic && !x.Parameters.Any()).ImportInto(ModuleDefinition);
}
}
}
5 changes: 4 additions & 1 deletion src/Rougamo.Fody/Models/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ namespace Rougamo.Fody.Models
{
internal class Config
{
public Config(bool enabled, bool compositeAccessibility, int moArrayThreshold, bool recordingIteratorReturns, bool reverseCallNonEntry, string[] exceptTypePatterns)
public Config(bool enabled, bool compositeAccessibility, int moArrayThreshold, bool recordingIteratorReturns, bool reverseCallNonEntry, bool strict, string[] exceptTypePatterns)
{
Enabled = enabled;
CompositeAccessibility = compositeAccessibility;
MoArrayThreshold = moArrayThreshold;
RecordingIteratorReturns = recordingIteratorReturns;
ReverseCallNonEntry = reverseCallNonEntry;
Strict = strict;
ExceptTypePatterns = exceptTypePatterns.Select(x => new Regex(x)).ToArray();
}

Expand All @@ -25,6 +26,8 @@ public Config(bool enabled, bool compositeAccessibility, int moArrayThreshold,

public bool ReverseCallNonEntry { get; }

public bool Strict { get; }

public Regex[] ExceptTypePatterns { get; }
}
}
5 changes: 4 additions & 1 deletion src/Rougamo.Fody/ModuleWeaver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public partial class ModuleWeaver : BaseModuleWeaver
private TypeReference _typeObjectRef;
private TypeReference _typeObjectArrayRef;
private TypeReference _typeExceptionRef;
private TypeReference _typeDebuggerStepThroughAttributeRef;
private TypeDefinition _typeListDef;
private TypeReference _typeListRef;
private TypeReference _typeIMoRef;
Expand All @@ -30,6 +31,7 @@ public partial class ModuleWeaver : BaseModuleWeaver
private MethodReference _methodGetMethodFromHandleRef;
private MethodReference _methodListAddRef;
private MethodReference _methodListToArrayRef;
private MethodReference _methodDebuggerStepThroughCtorRef;
private MethodReference _methodMethodContextCtorRef;
private MethodReference _methodMethodContextSetExceptionRef;
private MethodReference _methodMethodContextSetReturnValueRef;
Expand Down Expand Up @@ -81,9 +83,10 @@ private void ReadConfig()
var recordingIteratorReturns = "true".Equals(GetConfigValue("false", "iterator-returns", "enumerable-returns"), StringComparison.OrdinalIgnoreCase);
#endif
var reverseCallNonEntry = "true".Equals(GetConfigValue("true", "reverse-call-nonentry", "reverse-call-ending"), StringComparison.OrdinalIgnoreCase);
var strict = "true".Equals(GetConfigValue("true", "strict"), StringComparison.OrdinalIgnoreCase);
var exceptTypePatterns = GetConfigValue(string.Empty, "except-type-patterns").Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);

_config = new Config(enabled, compositeAccessibility, moArrayThreshold, recordingIteratorReturns, reverseCallNonEntry, exceptTypePatterns);
_config = new Config(enabled, compositeAccessibility, moArrayThreshold, recordingIteratorReturns, reverseCallNonEntry, strict, exceptTypePatterns);
}

private string GetConfigValue(string defaultValue, params string[] configKeys)
Expand Down
2 changes: 1 addition & 1 deletion src/Rougamo.Fody/WeaveAsyncTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ private IList<Instruction> AsyncSaveReturnValue(RouMethod rouMethod, BoxTypeRefe
{
Create(OpCodes.Ldarg_0),
Create(OpCodes.Ldfld, fields.MethodContext),
ldlocReturn.Copy(),
ldlocReturn.Clone(),
};
if (returnBoxTypeRef)
{
Expand Down
5 changes: 5 additions & 0 deletions src/Rougamo.Fody/WeaveSync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ partial class ModuleWeaver
{
private void SyncMethodWeave(RouMethod rouMethod)
{
if (!rouMethod.MethodDef.IsConstructor && _config.Strict)
{
StrictSyncMethodWeave(rouMethod);
}

var instructions = rouMethod.MethodDef.Body.Instructions;
var returnBoxTypeRef = new BoxTypeReference(rouMethod.MethodDef.ReturnType.ImportInto(ModuleDefinition));

Expand Down
52 changes: 52 additions & 0 deletions src/Rougamo.Fody/WeaveSync_Strict.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using System.Linq;
using static Mono.Cecil.Cil.Instruction;

namespace Rougamo.Fody
{
partial class ModuleWeaver
{
private void StrictSyncMethodWeave(RouMethod rouMethod)
{
var actualMethodDef = rouMethod.MethodDef.Clone($"$Rougamo_{rouMethod.MethodDef.Name}");
rouMethod.MethodDef.DeclaringType.Methods.Add(actualMethodDef);

DebuggerStepThrough(rouMethod.MethodDef);

StrictSyncAdaptedCall(rouMethod, actualMethodDef);
}

private void StrictSyncAdaptedCall(RouMethod rouMethod, MethodDefinition methodDef)
{
var instructions = rouMethod.MethodDef.Body.Instructions;

instructions.Clear();

if (!methodDef.IsStatic) instructions.Add(Create(OpCodes.Ldarg_0));
foreach(var parameter in rouMethod.MethodDef.Parameters)
{
instructions.Add(Create(OpCodes.Ldarg, parameter));
}
MethodReference methodRef = methodDef;
if (rouMethod.MethodDef.DeclaringType.HasGenericParameters)
{
var git = rouMethod.MethodDef.DeclaringType.MakeGenericInstanceType(rouMethod.MethodDef.DeclaringType.GenericParameters.Cast<TypeReference>().ToArray());
methodRef = git.GenericTypeMethodReference(methodRef, ModuleDefinition);
}
if (rouMethod.MethodDef.HasGenericParameters)
{
methodRef = methodRef.GenericMethodReference(rouMethod.MethodDef.GenericParameters.Cast<TypeReference>().ToArray());
}
instructions.Add(Create(OpCodes.Call, methodRef));
instructions.Add(Create(OpCodes.Ret));
}

private void DebuggerStepThrough(MethodDefinition methodDef)
{
methodDef.CustomAttributes.Clear();
methodDef.CustomAttributes.Add(new CustomAttribute(_methodDebuggerStepThroughCtorRef));
}
}
}

0 comments on commit e8bf4bc

Please sign in to comment.