You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
877 lines
43 KiB
C#
877 lines
43 KiB
C#
using Mono.Cecil;
|
|
using Mono.Cecil.Cil;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEditor.Build;
|
|
using UnityEditor.Build.Reporting;
|
|
using UnityEngine;
|
|
|
|
namespace WebGLThreadingPatcher.Editor
|
|
{
|
|
public class ThreadingPatcher : IPostBuildPlayerScriptDLLs
|
|
{
|
|
public int callbackOrder => 0;
|
|
|
|
public void OnPostBuildPlayerScriptDLLs(BuildReport report)
|
|
{
|
|
if (report.summary.platform != BuildTarget.WebGL)
|
|
return;
|
|
|
|
#if UNITY_2022_1_OR_NEWER
|
|
var mscorLibDll = report.GetFiles().FirstOrDefault(f => f.path.EndsWith("mscorlib.dll")).path;
|
|
#else
|
|
var mscorLibDll = report.files.FirstOrDefault(f => f.path.EndsWith("mscorlib.dll")).path;
|
|
#endif
|
|
if (mscorLibDll == null)
|
|
{
|
|
Debug.LogError("Can't find mscorlib.dll in build dll files");
|
|
return;
|
|
}
|
|
|
|
using (var assembly = AssemblyDefinition.ReadAssembly(Path.Combine(mscorLibDll), new ReaderParameters(ReadingMode.Immediate) { ReadWrite = true }))
|
|
{
|
|
var mainModule = assembly.MainModule;
|
|
if (!TryGetTypes(mainModule, out var threadPool, out var synchronizationContext, out var postCallback, out var waitCallback, out var taskExecutionItem, out var timeScheduler))
|
|
return;
|
|
|
|
PatchThreadPool(mainModule, threadPool, synchronizationContext, postCallback, waitCallback, taskExecutionItem);
|
|
|
|
#if !UNITY_2021_2_OR_NEWER
|
|
PatchTimerScheduler(mainModule, timeScheduler, threadPool, waitCallback);
|
|
#endif
|
|
assembly.Write();
|
|
}
|
|
}
|
|
|
|
[MenuItem("Tools/PatchDll")]
|
|
public static void TestMethod()
|
|
{
|
|
using (var assembly = AssemblyDefinition.ReadAssembly("D:\\mscorlib.dll", new ReaderParameters(ReadingMode.Immediate) { ReadWrite = true }))
|
|
{
|
|
var mainModule = assembly.MainModule;
|
|
if (!TryGetTypes(mainModule, out var threadPool, out var synchronizationContext, out var postCallback, out var waitCallback, out var taskExecutionItem, out var timeScheduler))
|
|
return;
|
|
|
|
PatchThreadPool(mainModule, threadPool, synchronizationContext, postCallback, waitCallback, taskExecutionItem);
|
|
|
|
#if !UNITY_2021_2_OR_NEWER
|
|
PatchTimerScheduler(mainModule, timeScheduler, threadPool, waitCallback);
|
|
#endif
|
|
assembly.Write("D:\\mscorlib_p.dll");
|
|
}
|
|
}
|
|
|
|
private static void PatchThreadPool(
|
|
ModuleDefinition mainModule,
|
|
TypeDefinition threadPool,
|
|
TypeDefinition synchronizationContext,
|
|
TypeDefinition postCallback,
|
|
TypeDefinition waitCallback,
|
|
TypeDefinition threadPoolWorkItem
|
|
)
|
|
{
|
|
var taskExecutionCallcack = AddTaskExecutionPostCallback(threadPool, threadPoolWorkItem, mainModule);
|
|
|
|
foreach (var methodDefinition in threadPool.Methods)
|
|
{
|
|
switch (methodDefinition.Name)
|
|
{
|
|
case "QueueUserWorkItem" when methodDefinition.HasGenericParameters:
|
|
case "UnsafeQueueUserWorkItem" when methodDefinition.HasGenericParameters:
|
|
PatchQueueUserWorkItemGeneric(mainModule, methodDefinition, synchronizationContext, waitCallback, postCallback);
|
|
break;
|
|
case "QueueUserWorkItem":
|
|
case "UnsafeQueueUserWorkItem":
|
|
PatchQueueUserWorkItem(mainModule, methodDefinition, synchronizationContext, waitCallback, postCallback);
|
|
break;
|
|
case "UnsafeQueueCustomWorkItem":
|
|
PatchUnsafeQueueCustomWorkItem(mainModule, methodDefinition, synchronizationContext, taskExecutionCallcack, postCallback);
|
|
break;
|
|
case "TryPopCustomWorkItem":
|
|
PatchTryPopCustomWorkItem(methodDefinition);
|
|
break;
|
|
case "GetAvailableThreads":
|
|
case "GetMaxThreads":
|
|
case "GetMinThreads":
|
|
PatchGetThreads(methodDefinition);
|
|
break;
|
|
case "SetMaxThreads":
|
|
case "SetMinThreads":
|
|
PatchSetThreads(methodDefinition);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates following class
|
|
/// <code>
|
|
/// class <>_GenericWrapper<T>
|
|
/// {
|
|
/// public Action<T> callabck;
|
|
///
|
|
/// public void Invoke(object state)
|
|
/// {
|
|
/// callback((T)state);
|
|
/// }
|
|
/// }
|
|
/// </code>
|
|
/// </summary>
|
|
/// <param name="moduleDefinition"></param>
|
|
/// <returns></returns>
|
|
private static TypeDefinition GetGenericToObjectDelegateWrapper(ModuleDefinition moduleDefinition)
|
|
{
|
|
const string Namespace = "System.Threading";
|
|
const string ClassName = "<>_GenericWrapper";
|
|
if (moduleDefinition.Types.FirstOrDefault(t => t.Namespace == Namespace && t.Name == ClassName) is { } wrapper)
|
|
{
|
|
return wrapper;
|
|
}
|
|
|
|
var genericWrapper = new TypeDefinition(Namespace, ClassName, TypeAttributes.AnsiClass | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit);
|
|
var genericParameter = new GenericParameter("T", genericWrapper);
|
|
genericWrapper.GenericParameters.Add(genericParameter);
|
|
|
|
var (actionOfT, callbackField) = CreateCallbackField(moduleDefinition, genericWrapper, genericParameter);
|
|
var ctor = CreateConstructor(moduleDefinition, callbackField);
|
|
var wrapMethod = CreateInvokeMethod(moduleDefinition, genericParameter, actionOfT, callbackField);
|
|
|
|
genericWrapper.Methods.Add(ctor);
|
|
genericWrapper.Methods.Add(wrapMethod);
|
|
|
|
moduleDefinition.Types.Add(genericWrapper);
|
|
return genericWrapper;
|
|
|
|
static (TypeReference, FieldReference) CreateCallbackField(ModuleDefinition moduleDefinition, TypeDefinition genericWrapper, GenericParameter genericParameter)
|
|
{
|
|
var actionType = moduleDefinition.Types.First(t => t.FullName == "System.Action`1" && t.GenericParameters.Count == 1);
|
|
var actionOfT = new GenericInstanceType(actionType);
|
|
actionOfT.GenericArguments.Add(genericParameter);
|
|
FieldDefinition callback = new FieldDefinition("callback", FieldAttributes.Public, actionOfT);
|
|
genericWrapper.Fields.Add(callback);
|
|
|
|
var wrapperOfT = new GenericInstanceType(genericWrapper);
|
|
wrapperOfT.GenericArguments.Add(genericParameter);
|
|
return (actionOfT, new FieldReference(callback.Name, actionOfT, wrapperOfT));
|
|
}
|
|
|
|
static MethodDefinition CreateInvokeMethod(ModuleDefinition moduleDefinition, GenericParameter genericParameter, TypeReference actionOfT, FieldReference callbackField)
|
|
{
|
|
var wrapMethod = new MethodDefinition("Invoke", MethodAttributes.Public, moduleDefinition.TypeSystem.Void);
|
|
wrapMethod.Parameters.Add(new ParameterDefinition(moduleDefinition.TypeSystem.Object) { Name = "state" });
|
|
var ilProcessor = wrapMethod.Body.GetILProcessor();
|
|
ilProcessor.Emit(OpCodes.Ldarg_0);
|
|
ilProcessor.Emit(OpCodes.Ldfld, callbackField);
|
|
ilProcessor.Emit(OpCodes.Ldarg_1);
|
|
ilProcessor.Emit(OpCodes.Unbox_Any, genericParameter);
|
|
var invokeMethod = new MethodReference("Invoke", moduleDefinition.TypeSystem.Void, actionOfT) { HasThis = true };
|
|
invokeMethod.Parameters.Add(new ParameterDefinition(genericParameter));
|
|
ilProcessor.Emit(OpCodes.Callvirt, invokeMethod);
|
|
ilProcessor.Emit(OpCodes.Ret);
|
|
|
|
return wrapMethod;
|
|
}
|
|
|
|
static MethodDefinition CreateConstructor(ModuleDefinition moduleDefinition, FieldReference callbackField)
|
|
{
|
|
var ctor = new MethodDefinition(
|
|
".ctor",
|
|
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.HideBySig,
|
|
moduleDefinition.TypeSystem.Void
|
|
);
|
|
ctor.Parameters.Add(new ParameterDefinition(callbackField.FieldType));
|
|
var ilProcessor = ctor.Body.GetILProcessor();
|
|
ilProcessor.Emit(OpCodes.Ldarg_0);
|
|
ilProcessor.Emit(OpCodes.Call, new MethodReference(".ctor", moduleDefinition.TypeSystem.Void, moduleDefinition.TypeSystem.Object) { HasThis = true });
|
|
ilProcessor.Emit(OpCodes.Ldarg_0);
|
|
ilProcessor.Emit(OpCodes.Ldarg_1);
|
|
ilProcessor.Emit(OpCodes.Stfld, callbackField);
|
|
ilProcessor.Emit(OpCodes.Ret);
|
|
return ctor;
|
|
}
|
|
}
|
|
|
|
private static void PatchQueueUserWorkItemGeneric(
|
|
ModuleDefinition moduleDefinition,
|
|
MethodDefinition methodDefinition,
|
|
TypeDefinition synchronizationContext,
|
|
TypeDefinition waitCallback,
|
|
TypeDefinition postCallback
|
|
)
|
|
{
|
|
var genericWrapper = GetGenericToObjectDelegateWrapper(moduleDefinition);
|
|
var wrapperOfT = new GenericInstanceType(genericWrapper);
|
|
wrapperOfT.GenericArguments.Add(methodDefinition.GenericParameters[0]);
|
|
|
|
var ilPProcessor = methodDefinition.Body.GetILProcessor();
|
|
ilPProcessor.Body.Instructions.Clear();
|
|
methodDefinition.Body.ExceptionHandlers.Clear();
|
|
|
|
var actionType = moduleDefinition.Types.First(t => t.FullName == "System.Action`1" && t.GenericParameters.Count == 1);
|
|
var actionOfT = new GenericInstanceType(actionType);
|
|
actionOfT.GenericArguments.Add(genericWrapper.GenericParameters[0]);
|
|
|
|
ilPProcessor.Emit(OpCodes.Ldarg_0);
|
|
var wrapperCtor = new MethodReference(".ctor", moduleDefinition.TypeSystem.Void, wrapperOfT);
|
|
wrapperCtor.Parameters.Add(new ParameterDefinition(actionOfT));
|
|
wrapperCtor.HasThis = true;
|
|
ilPProcessor.Emit(OpCodes.Newobj, wrapperCtor);
|
|
|
|
var wrapperInvoke = new MethodReference("Invoke", moduleDefinition.TypeSystem.Void, wrapperOfT);
|
|
wrapperInvoke.Parameters.Add(new ParameterDefinition(moduleDefinition.TypeSystem.Object));
|
|
wrapperInvoke.HasThis = true;
|
|
ilPProcessor.Emit(OpCodes.Ldftn, wrapperInvoke);
|
|
|
|
ilPProcessor.Emit(OpCodes.Newobj, waitCallback.Methods.First(m => m.IsConstructor && m.Parameters.Count == 2));
|
|
|
|
ilPProcessor.Emit(OpCodes.Ldarg_1);
|
|
ilPProcessor.Emit(OpCodes.Box, methodDefinition.GenericParameters[0]);
|
|
var notGenericVariant = new MethodReference(methodDefinition.Name, methodDefinition.ReturnType, methodDefinition.DeclaringType);
|
|
notGenericVariant.Parameters.Add(new ParameterDefinition(moduleDefinition.Types.First(t => t.FullName == "System.Threading.WaitCallback")));
|
|
notGenericVariant.Parameters.Add(new ParameterDefinition(moduleDefinition.TypeSystem.Object));
|
|
ilPProcessor.Emit(OpCodes.Call, notGenericVariant);
|
|
|
|
ilPProcessor.Emit(OpCodes.Ret);
|
|
}
|
|
|
|
private static void PatchQueueUserWorkItem(
|
|
ModuleDefinition moduleDefinition,
|
|
MethodDefinition methodDefinition,
|
|
TypeDefinition synchronizationContext,
|
|
TypeDefinition waitCallback,
|
|
TypeDefinition postCallback
|
|
)
|
|
{
|
|
var ilPProcessor = methodDefinition.Body.GetILProcessor();
|
|
ilPProcessor.Body.Instructions.Clear();
|
|
methodDefinition.Body.ExceptionHandlers.Clear();
|
|
ilPProcessor.Emit(OpCodes.Call, moduleDefinition.ImportReference(synchronizationContext.Methods.Single(s => s.Name == "get_Current")));
|
|
ilPProcessor.Emit(OpCodes.Ldarg_0);
|
|
ilPProcessor.Emit(OpCodes.Ldftn, moduleDefinition.ImportReference(waitCallback.Methods.Single(s => s.Name == "Invoke")));
|
|
ilPProcessor.Emit(OpCodes.Newobj, moduleDefinition.ImportReference(postCallback.Methods.First(s => s.IsConstructor)));
|
|
if (methodDefinition.Parameters.Count == 2)
|
|
ilPProcessor.Emit(OpCodes.Ldarg_1);
|
|
else
|
|
ilPProcessor.Emit(OpCodes.Ldnull);
|
|
ilPProcessor.Emit(OpCodes.Callvirt, moduleDefinition.ImportReference(synchronizationContext.Methods.Single(s => s.Name == "Post")));
|
|
|
|
ilPProcessor.Emit(OpCodes.Ldc_I4_1);
|
|
ilPProcessor.Emit(OpCodes.Ret);
|
|
}
|
|
|
|
private static void PatchUnsafeQueueCustomWorkItem(
|
|
ModuleDefinition moduleDefinition,
|
|
MethodDefinition methodDefinition,
|
|
TypeDefinition synchronizationContext,
|
|
MethodDefinition taskExecutionCallcack,
|
|
TypeDefinition postCallback
|
|
)
|
|
{
|
|
var p = methodDefinition.Body.GetILProcessor();
|
|
p.Body.Instructions.Clear();
|
|
methodDefinition.Body.ExceptionHandlers.Clear();
|
|
p.Emit(OpCodes.Call, moduleDefinition.ImportReference(synchronizationContext.Methods.Single(s => s.Name == "get_Current")));
|
|
p.Emit(OpCodes.Ldnull);
|
|
p.Emit(OpCodes.Ldftn, moduleDefinition.ImportReference(taskExecutionCallcack));
|
|
p.Emit(OpCodes.Newobj, moduleDefinition.ImportReference(postCallback.Methods.First(s => s.IsConstructor)));
|
|
p.Emit(OpCodes.Ldarg_0);
|
|
p.Emit(OpCodes.Callvirt, moduleDefinition.ImportReference(synchronizationContext.Methods.Single(s => s.Name == "Post")));
|
|
|
|
p.Emit(OpCodes.Ret);
|
|
}
|
|
|
|
private static void PatchTryPopCustomWorkItem(MethodDefinition methodDefinition)
|
|
{
|
|
var ilPProcessor = methodDefinition.Body.GetILProcessor();
|
|
ilPProcessor.Body.Instructions.Clear();
|
|
methodDefinition.Body.ExceptionHandlers.Clear();
|
|
ilPProcessor.Emit(OpCodes.Ldc_I4_0);
|
|
ilPProcessor.Emit(OpCodes.Ret);
|
|
}
|
|
|
|
private static void PatchGetThreads(MethodDefinition methodDefinition)
|
|
{
|
|
var ilPProcessor = methodDefinition.Body.GetILProcessor();
|
|
ilPProcessor.Body.Instructions.Clear();
|
|
methodDefinition.Body.ExceptionHandlers.Clear();
|
|
ilPProcessor.Emit(OpCodes.Ldarg_0);
|
|
ilPProcessor.Emit(OpCodes.Ldc_I4_1);
|
|
ilPProcessor.Emit(OpCodes.Stind_I4);
|
|
|
|
ilPProcessor.Emit(OpCodes.Ldarg_1);
|
|
ilPProcessor.Emit(OpCodes.Ldc_I4_1);
|
|
ilPProcessor.Emit(OpCodes.Stind_I4);
|
|
ilPProcessor.Emit(OpCodes.Ret);
|
|
}
|
|
|
|
private static void PatchSetThreads(MethodDefinition methodDefinition)
|
|
{
|
|
var ilPProcessor = methodDefinition.Body.GetILProcessor();
|
|
ilPProcessor.Body.Instructions.Clear();
|
|
methodDefinition.Body.ExceptionHandlers.Clear();
|
|
var falseRet = ilPProcessor.Create(OpCodes.Ldc_I4_0);
|
|
|
|
ilPProcessor.Emit(OpCodes.Ldarg_0);
|
|
ilPProcessor.Emit(OpCodes.Ldc_I4_1);
|
|
ilPProcessor.Emit(OpCodes.Bne_Un_S, falseRet);
|
|
|
|
ilPProcessor.Emit(OpCodes.Ldarg_1);
|
|
ilPProcessor.Emit(OpCodes.Ldc_I4_1);
|
|
ilPProcessor.Emit(OpCodes.Ceq);
|
|
ilPProcessor.Emit(OpCodes.Ret);
|
|
|
|
ilPProcessor.Append(falseRet);
|
|
ilPProcessor.Emit(OpCodes.Ret);
|
|
}
|
|
|
|
private static MethodDefinition AddTaskExecutionPostCallback(TypeDefinition threadPool, TypeDefinition taskExecutionItem, ModuleDefinition moduleDefinition)
|
|
{
|
|
var method = new MethodDefinition("TaskExecutionItemExecute", MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig, moduleDefinition.TypeSystem.Void);
|
|
|
|
method.Parameters.Add(new ParameterDefinition("state", ParameterAttributes.None, moduleDefinition.TypeSystem.Object));
|
|
|
|
var ilProcessor = method.Body.GetILProcessor();
|
|
ilProcessor.Emit(OpCodes.Ldarg_0);
|
|
ilProcessor.Emit(OpCodes.Callvirt, moduleDefinition.ImportReference(taskExecutionItem.Methods.Single(s => s.Name == "ExecuteWorkItem")));
|
|
ilProcessor.Emit(OpCodes.Ret);
|
|
|
|
threadPool.Methods.Add(method);
|
|
|
|
return method;
|
|
}
|
|
|
|
private static void PatchTimerScheduler(ModuleDefinition moduleDefinition, TypeDefinition timerScheduler, TypeDefinition threadPool, TypeDefinition waitCallback)
|
|
{
|
|
var monoPinvoke = AddMonoPInvokeCallbackAttribute(moduleDefinition);
|
|
|
|
var timer = moduleDefinition.Types.Single(m => m.FullName == "System.Threading.Timer");
|
|
var listGeneric = moduleDefinition.Types.Single(t => t.HasGenericParameters && t.FullName == "System.Collections.Generic.List`1");
|
|
var timerListRef = MakeGenericType(listGeneric, timer);
|
|
var tempTimerListField = new FieldDefinition("tempList", FieldAttributes.Private, timerListRef);
|
|
timerScheduler.Fields.Add(tempTimerListField);
|
|
|
|
var internalAssemblyReference = new ModuleReference("__Internal");
|
|
moduleDefinition.ModuleReferences.Add(internalAssemblyReference);
|
|
|
|
MethodDefinition setCallbackMethod = AddSetCallbackPImplMethod(moduleDefinition, internalAssemblyReference);
|
|
MethodDefinition updateTimer = AddUpdateTimerPImplMethod(moduleDefinition, internalAssemblyReference);
|
|
MethodDefinition processTimerMethods = AddProcessTimerMethod(moduleDefinition, timerScheduler, threadPool, waitCallback, updateTimer, tempTimerListField, monoPinvoke);
|
|
|
|
PatchChangeMethod(moduleDefinition, timerScheduler, processTimerMethods);
|
|
PatchTimerSchedulerCtor(moduleDefinition, timerScheduler, processTimerMethods, setCallbackMethod, tempTimerListField);
|
|
|
|
timerScheduler.Methods.Add(setCallbackMethod);
|
|
timerScheduler.Methods.Add(updateTimer);
|
|
timerScheduler.Methods.Add(processTimerMethods);
|
|
|
|
timerScheduler.Methods.Remove(timerScheduler.Methods.Single(m => m.Name == "SchedulerThread"));
|
|
timerScheduler.Fields.Remove(timerScheduler.Fields.Single(m => m.Name == "changed"));
|
|
}
|
|
|
|
private static void PatchTimerSchedulerCtor(
|
|
ModuleDefinition moduleDefinition,
|
|
TypeDefinition timerScheduler,
|
|
MethodDefinition precessTimers,
|
|
MethodDefinition setCallback,
|
|
FieldDefinition tempList
|
|
)
|
|
{
|
|
var ctor = timerScheduler.Methods.Single(m => m.IsConstructor && !m.IsStatic);
|
|
ctor.Body.Instructions.Clear();
|
|
ctor.Body.ExceptionHandlers.Clear();
|
|
|
|
var @object = moduleDefinition.Types.Single(m => m.FullName == "System.Object");
|
|
var sortedList = moduleDefinition.Types.Single(m => m.FullName == "System.Collections.SortedList");
|
|
var listGeneric = moduleDefinition.Types.Single(m => m.FullName == "System.Collections.Generic.List`1");
|
|
var timer = moduleDefinition.Types.Single(m => m.FullName == "System.Threading.Timer");
|
|
var timerComparer = timer.NestedTypes.Single(m => m.Name.Contains("TimerComparer"));
|
|
var action = moduleDefinition.Types.Single(m => m.FullName == "System.Action");
|
|
|
|
var sortedListCtor = sortedList.Methods.Single(m => m.IsConstructor && m.Parameters.Count == 2 && m.Parameters[1].ParameterType.FullName == "System.Int32");
|
|
var timerComparerCtor = timerComparer.Methods.Single(m => m.IsConstructor && m.Parameters.Count == 0);
|
|
var actionCtor = action.Methods.Single(m => m.IsConstructor);
|
|
var objectCtor = @object.Methods.Single(m => m.IsConstructor);
|
|
var listCtor = listGeneric.Methods.Single(m => m.IsConstructor && m.Parameters.Count == 1 && m.Parameters[0].ParameterType.FullName == "System.Int32");
|
|
|
|
var ilProcessor = ctor.Body.GetILProcessor();
|
|
ilProcessor.Emit(OpCodes.Ldarg_0);
|
|
ilProcessor.Emit(OpCodes.Call, objectCtor);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldarg_0);
|
|
ilProcessor.Emit(OpCodes.Newobj, timerComparerCtor);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4, 1024);
|
|
ilProcessor.Emit(OpCodes.Newobj, sortedListCtor);
|
|
ilProcessor.Emit(OpCodes.Stfld, timerScheduler.Fields.Single(f => f.Name == "list"));
|
|
|
|
ilProcessor.Emit(OpCodes.Ldarg_0);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4, 512);
|
|
ilProcessor.Emit(OpCodes.Newobj, MakeGeneric(listCtor, timer));
|
|
ilProcessor.Emit(OpCodes.Stfld, tempList);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldnull);
|
|
ilProcessor.Emit(OpCodes.Ldftn, precessTimers);
|
|
ilProcessor.Emit(OpCodes.Newobj, actionCtor);
|
|
ilProcessor.Emit(OpCodes.Call, setCallback);
|
|
ilProcessor.Emit(OpCodes.Ret);
|
|
}
|
|
|
|
private static void PatchChangeMethod(ModuleDefinition moduleDefinition, TypeDefinition timerScheduler, MethodDefinition precessTimers)
|
|
{
|
|
var timer = moduleDefinition.Types.Single(m => m.FullName == "System.Threading.Timer");
|
|
var sortedList = moduleDefinition.Types.Single(m => m.FullName == "System.Collections.SortedList");
|
|
var method = timerScheduler.Methods.Single(m => m.Name == "Change");
|
|
method.Body.Instructions.Clear();
|
|
method.Body.ExceptionHandlers.Clear();
|
|
|
|
method.Body.Variables.Clear();
|
|
|
|
var ilProcessor = method.Body.GetILProcessor();
|
|
|
|
ilProcessor.Emit(OpCodes.Ldarg_0);
|
|
ilProcessor.Emit(OpCodes.Ldarg_1);
|
|
ilProcessor.Emit(OpCodes.Call, timerScheduler.Methods.Single(m => m.Name == "InternalRemove"));
|
|
ilProcessor.Emit(OpCodes.Pop);
|
|
|
|
var checkDisposed = ilProcessor.Create(OpCodes.Ldarg_1);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldarg_2);
|
|
ilProcessor.Emit(OpCodes.Ldc_I8, long.MaxValue);
|
|
ilProcessor.Emit(OpCodes.Bne_Un_S, checkDisposed);
|
|
ilProcessor.Emit(OpCodes.Ldarg_1);
|
|
ilProcessor.Emit(OpCodes.Ldarg_2);
|
|
ilProcessor.Emit(OpCodes.Stfld, timer.Fields.Single(f => f.Name == "next_run"));
|
|
ilProcessor.Emit(OpCodes.Ret);
|
|
|
|
var finalReturn = ilProcessor.Create(OpCodes.Ret);
|
|
|
|
ilProcessor.Append(checkDisposed);
|
|
ilProcessor.Emit(OpCodes.Ldfld, timer.Fields.Single(f => f.Name == "disposed"));
|
|
ilProcessor.Emit(OpCodes.Brtrue_S, finalReturn);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldarg_1);
|
|
ilProcessor.Emit(OpCodes.Ldarg_2);
|
|
ilProcessor.Emit(OpCodes.Stfld, timer.Fields.Single(f => f.Name == "next_run"));
|
|
ilProcessor.Emit(OpCodes.Ldarg_0);
|
|
ilProcessor.Emit(OpCodes.Ldarg_1);
|
|
ilProcessor.Emit(OpCodes.Call, timerScheduler.Methods.Single(m => m.Name == "Add"));
|
|
|
|
ilProcessor.Emit(OpCodes.Ldarg_0);
|
|
ilProcessor.Emit(OpCodes.Ldfld, timerScheduler.Fields.Single(f => f.Name == "list"));
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_0);
|
|
ilProcessor.Emit(OpCodes.Call, sortedList.Methods.Single(f => f.Name == "GetByIndex"));
|
|
ilProcessor.Emit(OpCodes.Ldarg_1);
|
|
ilProcessor.Emit(OpCodes.Bne_Un_S, finalReturn);
|
|
|
|
ilProcessor.Emit(OpCodes.Call, precessTimers);
|
|
|
|
ilProcessor.Append(finalReturn);
|
|
}
|
|
|
|
private static MethodDefinition AddSetCallbackPImplMethod(ModuleDefinition moduleDefinition, ModuleReference internalAssemblyReference)
|
|
{
|
|
var setCallbackMethod = new MethodDefinition(
|
|
"SetCallback",
|
|
MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.Private | MethodAttributes.PInvokeImpl,
|
|
moduleDefinition.TypeSystem.Void
|
|
);
|
|
setCallbackMethod.PInvokeInfo = new PInvokeInfo(PInvokeAttributes.CallConvWinapi, "SetCallback", internalAssemblyReference) { IsCharSetNotSpec = true, IsCallConvWinapi = true };
|
|
setCallbackMethod.Parameters.Add(
|
|
new ParameterDefinition(
|
|
"callback",
|
|
ParameterAttributes.None,
|
|
moduleDefinition.ImportReference(moduleDefinition.Types.First(t => t.FullName == "System.Action" && !t.HasGenericParameters))
|
|
)
|
|
);
|
|
return setCallbackMethod;
|
|
}
|
|
|
|
private static MethodDefinition AddUpdateTimerPImplMethod(ModuleDefinition moduleDefinition, ModuleReference internalAssemblyReference)
|
|
{
|
|
var updateTimer = new MethodDefinition(
|
|
"UpdateTimer",
|
|
MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.Private | MethodAttributes.PInvokeImpl,
|
|
moduleDefinition.TypeSystem.Void
|
|
);
|
|
updateTimer.PInvokeInfo = new PInvokeInfo(PInvokeAttributes.CallConvWinapi, "UpdateTimer", internalAssemblyReference) { IsCharSetNotSpec = true, IsCallConvWinapi = true };
|
|
updateTimer.Parameters.Add(new ParameterDefinition("interval", ParameterAttributes.None, moduleDefinition.TypeSystem.Int64));
|
|
return updateTimer;
|
|
}
|
|
|
|
private static MethodDefinition AddProcessTimerMethod(
|
|
ModuleDefinition moduleDefinition,
|
|
TypeDefinition timerScheduler,
|
|
TypeDefinition threadPool,
|
|
TypeDefinition waitCallback,
|
|
MethodDefinition updateTimer,
|
|
FieldDefinition tempList,
|
|
TypeDefinition monoPinvokeAttr
|
|
)
|
|
{
|
|
var shrinkIfNeededMethod = timerScheduler.Methods.Single(m => m.Name == "ShrinkIfNeeded");
|
|
var getInstance = timerScheduler.Methods.Single(m => m.Name == "get_Instance");
|
|
var unsafeQueue = threadPool.Methods.Single(m => m.Name == "UnsafeQueueUserWorkItem");
|
|
var timer = moduleDefinition.Types.Single(m => m.FullName == "System.Threading.Timer");
|
|
var getTimeMonotonic = timer.Methods.Single(m => m.Name == "GetTimeMonotonic");
|
|
var listGeneric = moduleDefinition.Types.Single(t => t.HasGenericParameters && t.FullName.StartsWith("System.Collections.Generic.List"));
|
|
var sortedListType = moduleDefinition.Types.Single(t => t.FullName == "System.Collections.SortedList");
|
|
var timerListRef = MakeGenericType(listGeneric, timer);
|
|
|
|
var processTimerMethods = new MethodDefinition("ProcessTimers", MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.Private, moduleDefinition.TypeSystem.Void);
|
|
var ilProcessor = processTimerMethods.Body.GetILProcessor();
|
|
|
|
var TimeToNext = new VariableDefinition(moduleDefinition.TypeSystem.Int64);
|
|
var currentTime = new VariableDefinition(moduleDefinition.TypeSystem.Int64);
|
|
var loopIterator = new VariableDefinition(moduleDefinition.TypeSystem.Int32);
|
|
var loopEnd = new VariableDefinition(moduleDefinition.TypeSystem.Int32);
|
|
var sortedList = new VariableDefinition(sortedListType);
|
|
var list = new VariableDefinition(timerListRef);
|
|
var currentTimer = new VariableDefinition(timer);
|
|
var periodMs = new VariableDefinition(moduleDefinition.TypeSystem.Int64);
|
|
var dueTimeMs = new VariableDefinition(moduleDefinition.TypeSystem.Int64);
|
|
var instance = new VariableDefinition(timerScheduler);
|
|
|
|
processTimerMethods.Body.Variables.Add(list);
|
|
processTimerMethods.Body.Variables.Add(TimeToNext);
|
|
processTimerMethods.Body.Variables.Add(currentTime);
|
|
processTimerMethods.Body.Variables.Add(loopIterator);
|
|
processTimerMethods.Body.Variables.Add(loopEnd);
|
|
processTimerMethods.Body.Variables.Add(sortedList);
|
|
processTimerMethods.Body.Variables.Add(currentTimer);
|
|
processTimerMethods.Body.Variables.Add(periodMs);
|
|
processTimerMethods.Body.Variables.Add(dueTimeMs);
|
|
processTimerMethods.Body.Variables.Add(instance);
|
|
|
|
var ret = ilProcessor.Create(OpCodes.Ret);
|
|
|
|
ilProcessor.Emit(OpCodes.Call, getTimeMonotonic);
|
|
ilProcessor.Emit(OpCodes.Stloc, currentTime);
|
|
|
|
var loopCheck = ilProcessor.Create(OpCodes.Ldloc, loopIterator);
|
|
ilProcessor.Emit(OpCodes.Call, getInstance);
|
|
ilProcessor.Emit(OpCodes.Dup);
|
|
ilProcessor.Emit(OpCodes.Dup);
|
|
ilProcessor.Emit(OpCodes.Stloc, instance);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldfld, timerScheduler.Fields.Single(f => f.Name == "list"));
|
|
ilProcessor.Emit(OpCodes.Stloc, sortedList);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldfld, tempList);
|
|
ilProcessor.Emit(OpCodes.Stloc, list);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, sortedList);
|
|
ilProcessor.Emit(OpCodes.Call, sortedListType.Methods.Single(m => m.Name == "get_Count"));
|
|
ilProcessor.Emit(OpCodes.Stloc, loopEnd);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_0);
|
|
ilProcessor.Emit(OpCodes.Stloc, loopIterator);
|
|
ilProcessor.Emit(OpCodes.Br, loopCheck);
|
|
|
|
var loopStart = ilProcessor.Create(OpCodes.Ldloc, sortedList);
|
|
var loopStart2 = ilProcessor.Create(OpCodes.Ldloc, instance);
|
|
var loop2 = ilProcessor.Create(OpCodes.Ldloc, list);
|
|
ilProcessor.Append(loopStart);
|
|
ilProcessor.Emit(OpCodes.Ldloc, loopIterator);
|
|
ilProcessor.Emit(OpCodes.Call, sortedListType.Methods.Single(m => m.Name == "GetByIndex"));
|
|
ilProcessor.Emit(OpCodes.Stloc, currentTimer);
|
|
ilProcessor.Emit(OpCodes.Ldloc, currentTimer);
|
|
ilProcessor.Emit(OpCodes.Ldfld, timer.Fields.Single(f => f.Name == "next_run"));
|
|
ilProcessor.Emit(OpCodes.Ldloc, currentTime);
|
|
ilProcessor.Emit(OpCodes.Bgt, loop2);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, sortedList);
|
|
ilProcessor.Emit(OpCodes.Ldloc, loopIterator);
|
|
ilProcessor.Emit(OpCodes.Call, sortedListType.Methods.Single(m => m.Name == "RemoveAt"));
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, loopIterator);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_1);
|
|
ilProcessor.Emit(OpCodes.Sub);
|
|
ilProcessor.Emit(OpCodes.Stloc, loopIterator);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, loopEnd);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_1);
|
|
ilProcessor.Emit(OpCodes.Sub);
|
|
ilProcessor.Emit(OpCodes.Stloc, loopEnd);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldnull);
|
|
ilProcessor.Emit(OpCodes.Ldftn, timerScheduler.Methods.Single(m => m.Name == "TimerCB"));
|
|
ilProcessor.Emit(OpCodes.Newobj, waitCallback.Methods.Single(mbox => mbox.IsConstructor));
|
|
ilProcessor.Emit(OpCodes.Ldloc, currentTimer);
|
|
ilProcessor.Emit(OpCodes.Call, unsafeQueue);
|
|
ilProcessor.Emit(OpCodes.Pop);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, currentTimer);
|
|
ilProcessor.Emit(OpCodes.Ldfld, timer.Fields.Single(f => f.Name == "period_ms"));
|
|
ilProcessor.Emit(OpCodes.Stloc, periodMs);
|
|
ilProcessor.Emit(OpCodes.Ldloc, periodMs);
|
|
|
|
var setNextRunToMax = ilProcessor.Create(OpCodes.Ldloc, currentTimer);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_M1);
|
|
ilProcessor.Emit(OpCodes.Conv_I8);
|
|
ilProcessor.Emit(OpCodes.Beq_S, setNextRunToMax);
|
|
|
|
var checkDueTimeStart = ilProcessor.Create(OpCodes.Ldloc, currentTimer);
|
|
;
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, periodMs);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_0);
|
|
ilProcessor.Emit(OpCodes.Conv_I8);
|
|
ilProcessor.Emit(OpCodes.Beq_S, checkDueTimeStart);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, periodMs);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_M1);
|
|
ilProcessor.Emit(OpCodes.Conv_I8);
|
|
ilProcessor.Emit(OpCodes.Bne_Un_S, setNextRunToMax);
|
|
|
|
ilProcessor.Append(checkDueTimeStart);
|
|
ilProcessor.Emit(OpCodes.Ldfld, timer.Fields.Single(f => f.Name == "due_time_ms"));
|
|
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_M1);
|
|
ilProcessor.Emit(OpCodes.Conv_I8);
|
|
ilProcessor.Emit(OpCodes.Bne_Un_S, setNextRunToMax);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, list);
|
|
ilProcessor.Emit(OpCodes.Ldloc, currentTimer);
|
|
ilProcessor.Emit(OpCodes.Dup);
|
|
ilProcessor.Emit(OpCodes.Call, getTimeMonotonic);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4, 10000);
|
|
ilProcessor.Emit(OpCodes.Ldloc, periodMs);
|
|
ilProcessor.Emit(OpCodes.Mul);
|
|
ilProcessor.Emit(OpCodes.Add);
|
|
ilProcessor.Emit(OpCodes.Stfld, timer.Fields.Single(f => f.Name == "next_run"));
|
|
var listAdd = listGeneric.Methods.Single(m => m.Name == "Add");
|
|
ilProcessor.Emit(OpCodes.Call, MakeGeneric(listAdd, timer));
|
|
|
|
var incrementStart = ilProcessor.Create(OpCodes.Ldloc, loopIterator);
|
|
ilProcessor.Emit(OpCodes.Br, incrementStart);
|
|
|
|
ilProcessor.Append(setNextRunToMax);
|
|
ilProcessor.Emit(OpCodes.Ldc_I8, long.MaxValue);
|
|
ilProcessor.Emit(OpCodes.Stfld, timer.Fields.Single(f => f.Name == "next_run"));
|
|
|
|
ilProcessor.Append(incrementStart);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_1);
|
|
ilProcessor.Emit(OpCodes.Add);
|
|
ilProcessor.Emit(OpCodes.Stloc, loopIterator);
|
|
ilProcessor.Append(loopCheck);
|
|
ilProcessor.Emit(OpCodes.Ldloc, loopEnd);
|
|
ilProcessor.Emit(OpCodes.Blt, loopStart);
|
|
|
|
var loopCheck2 = ilProcessor.Create(OpCodes.Ldloc, loopIterator);
|
|
ilProcessor.Append(loop2);
|
|
var listCount = listGeneric.Methods.Single(m => m.Name == "get_Count");
|
|
ilProcessor.Emit(OpCodes.Call, MakeGeneric(listCount, timer));
|
|
ilProcessor.Emit(OpCodes.Stloc, loopEnd);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_0);
|
|
ilProcessor.Emit(OpCodes.Stloc, loopIterator);
|
|
ilProcessor.Emit(OpCodes.Br_S, loopCheck2);
|
|
|
|
ilProcessor.Append(loopStart2);
|
|
ilProcessor.Emit(OpCodes.Ldloc, list);
|
|
ilProcessor.Emit(OpCodes.Ldloc, loopIterator);
|
|
var getItems = listGeneric.Methods.Single(m => m.Name == "get_Item");
|
|
ilProcessor.Emit(OpCodes.Callvirt, MakeGeneric(getItems, timer));
|
|
ilProcessor.Emit(OpCodes.Call, timerScheduler.Methods.Single(m => m.Name == "Add"));
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, loopIterator);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_1);
|
|
ilProcessor.Emit(OpCodes.Add);
|
|
ilProcessor.Emit(OpCodes.Stloc, loopIterator);
|
|
|
|
ilProcessor.Append(loopCheck2);
|
|
ilProcessor.Emit(OpCodes.Ldloc, loopEnd);
|
|
ilProcessor.Emit(OpCodes.Blt, loopStart2);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, instance);
|
|
ilProcessor.Emit(OpCodes.Ldloc, list);
|
|
ilProcessor.Emit(OpCodes.Dup);
|
|
var clearList = listGeneric.Methods.Single(m => m.Name == "Clear");
|
|
ilProcessor.Emit(OpCodes.Call, MakeGeneric(clearList, timer));
|
|
ilProcessor.Emit(OpCodes.Ldc_I4, 512);
|
|
ilProcessor.Emit(OpCodes.Call, shrinkIfNeededMethod);
|
|
|
|
var afterCapacityCheck = ilProcessor.Create(OpCodes.Ldloc, loopEnd);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, sortedList);
|
|
ilProcessor.Emit(OpCodes.Callvirt, sortedListType.Methods.Single(m => m.Name == "get_Capacity"));
|
|
ilProcessor.Emit(OpCodes.Stloc, loopIterator);
|
|
ilProcessor.Emit(OpCodes.Ldloc, sortedList);
|
|
ilProcessor.Emit(OpCodes.Callvirt, sortedListType.Methods.Single(m => m.Name == "get_Count"));
|
|
ilProcessor.Emit(OpCodes.Stloc, loopEnd);
|
|
ilProcessor.Emit(OpCodes.Ldloc, loopIterator);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4, 1024);
|
|
ilProcessor.Emit(OpCodes.Blt_S, afterCapacityCheck);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, loopEnd);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_0);
|
|
ilProcessor.Emit(OpCodes.Ble_S, afterCapacityCheck);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, loopIterator);
|
|
ilProcessor.Emit(OpCodes.Ldloc, loopEnd);
|
|
ilProcessor.Emit(OpCodes.Div);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_3);
|
|
ilProcessor.Emit(OpCodes.Ble_S, afterCapacityCheck);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, sortedList);
|
|
ilProcessor.Emit(OpCodes.Ldloc, loopEnd);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_2);
|
|
ilProcessor.Emit(OpCodes.Mul);
|
|
ilProcessor.Emit(OpCodes.Callvirt, sortedListType.Methods.Single(m => m.Name == "set_Capacity"));
|
|
|
|
ilProcessor.Append(afterCapacityCheck);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_0);
|
|
ilProcessor.Emit(OpCodes.Ble_S, ret);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, sortedList);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_0);
|
|
ilProcessor.Emit(OpCodes.Call, sortedListType.Methods.Single(m => m.Name == "GetByIndex"));
|
|
ilProcessor.Emit(OpCodes.Stloc, currentTimer);
|
|
ilProcessor.Emit(OpCodes.Ldloc, currentTimer);
|
|
ilProcessor.Emit(OpCodes.Ldfld, timer.Fields.Single(f => f.Name == "next_run"));
|
|
ilProcessor.Emit(OpCodes.Call, getTimeMonotonic);
|
|
ilProcessor.Emit(OpCodes.Sub);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4, 10000);
|
|
ilProcessor.Emit(OpCodes.Div);
|
|
ilProcessor.Emit(OpCodes.Stloc, periodMs);
|
|
|
|
var updateTimerLabel = ilProcessor.Create(OpCodes.Ldloc, periodMs);
|
|
var setMaxTime = ilProcessor.Create(OpCodes.Ldc_I8, 2147483646L);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, periodMs);
|
|
ilProcessor.Emit(OpCodes.Ldc_I8, 2147483647L);
|
|
ilProcessor.Emit(OpCodes.Bgt_S, setMaxTime);
|
|
|
|
ilProcessor.Emit(OpCodes.Ldloc, periodMs);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_0);
|
|
ilProcessor.Emit(OpCodes.Conv_I8);
|
|
ilProcessor.Emit(OpCodes.Bgt_S, updateTimerLabel);
|
|
ilProcessor.Emit(OpCodes.Ldc_I4_0);
|
|
ilProcessor.Emit(OpCodes.Conv_I8);
|
|
ilProcessor.Emit(OpCodes.Stloc, periodMs);
|
|
ilProcessor.Emit(OpCodes.Br_S, updateTimerLabel);
|
|
|
|
ilProcessor.Append(setMaxTime);
|
|
ilProcessor.Emit(OpCodes.Stloc, periodMs);
|
|
|
|
ilProcessor.Append(updateTimerLabel);
|
|
ilProcessor.Emit(OpCodes.Call, updateTimer);
|
|
|
|
ilProcessor.Append(ret);
|
|
|
|
var token = System.Text.Encoding.UTF8.GetBytes("System.Action");
|
|
var blob = new byte[5 + token.Length];
|
|
blob[0] = 1;
|
|
blob[2] = (byte)token.Length;
|
|
System.Array.Copy(token, 0, blob, 3, token.Length);
|
|
|
|
processTimerMethods.CustomAttributes.Add(new CustomAttribute(monoPinvokeAttr.Methods.Single(m => m.IsConstructor), blob));
|
|
return processTimerMethods;
|
|
}
|
|
|
|
public static TypeReference MakeGenericType(TypeReference self, params TypeReference[] arguments)
|
|
{
|
|
if (self.GenericParameters.Count != arguments.Length)
|
|
throw new System.ArgumentException();
|
|
|
|
var instance = new GenericInstanceType(self);
|
|
foreach (var argument in arguments)
|
|
instance.GenericArguments.Add(argument);
|
|
|
|
return instance;
|
|
}
|
|
|
|
public static MethodReference MakeGeneric(MethodReference self, params TypeReference[] arguments)
|
|
{
|
|
var reference = new MethodReference(self.Name, self.ReturnType)
|
|
{
|
|
DeclaringType = MakeGenericType(self.DeclaringType, arguments),
|
|
HasThis = self.HasThis,
|
|
ExplicitThis = self.ExplicitThis,
|
|
CallingConvention = self.CallingConvention,
|
|
};
|
|
|
|
foreach (var parameter in self.Parameters)
|
|
reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
|
|
|
|
foreach (var generic_parameter in self.GenericParameters)
|
|
reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
|
|
|
|
return reference;
|
|
}
|
|
|
|
private static TypeDefinition AddMonoPInvokeCallbackAttribute(ModuleDefinition moduleDefinition)
|
|
{
|
|
var type = new TypeDefinition("AOT", "MonoPInvokeCallbackAttribute", TypeAttributes.AnsiClass | TypeAttributes.AutoClass | TypeAttributes.BeforeFieldInit | TypeAttributes.Public)
|
|
{
|
|
BaseType = moduleDefinition.ImportReference(moduleDefinition.Types.First(t => t.FullName == "System.Attribute"))
|
|
};
|
|
var ctor = new MethodDefinition(
|
|
".ctor",
|
|
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
|
|
moduleDefinition.TypeSystem.Void
|
|
);
|
|
ctor.Parameters.Add(new ParameterDefinition("type", ParameterAttributes.None, moduleDefinition.ImportReference(moduleDefinition.Types.First(t => t.FullName == "System.Type"))));
|
|
ctor.Body.GetILProcessor().Emit(OpCodes.Ret);
|
|
|
|
type.Methods.Add(ctor);
|
|
|
|
moduleDefinition.Types.Add(type);
|
|
return type;
|
|
}
|
|
|
|
private static bool TryGetTypes(
|
|
ModuleDefinition moduleDefinition,
|
|
out TypeDefinition threadPool,
|
|
out TypeDefinition synchronizationContext,
|
|
out TypeDefinition sendOrPostCallback,
|
|
out TypeDefinition waitCallback,
|
|
out TypeDefinition threadPoolWorkItem,
|
|
out TypeDefinition timerScheduler
|
|
)
|
|
{
|
|
threadPool = null;
|
|
synchronizationContext = null;
|
|
sendOrPostCallback = null;
|
|
waitCallback = null;
|
|
threadPoolWorkItem = null;
|
|
timerScheduler = null;
|
|
|
|
foreach (var type in moduleDefinition.Types)
|
|
{
|
|
if (type.FullName == "System.Threading.ThreadPool")
|
|
threadPool = type;
|
|
if (type.FullName == "System.Threading.SynchronizationContext")
|
|
synchronizationContext = type;
|
|
if (type.FullName == "System.Threading.SendOrPostCallback")
|
|
sendOrPostCallback = type;
|
|
if (type.FullName == "System.Threading.WaitCallback")
|
|
waitCallback = type;
|
|
if (type.FullName == "System.Threading.IThreadPoolWorkItem")
|
|
threadPoolWorkItem = type;
|
|
if (type.FullName == "System.Threading.Timer")
|
|
foreach (var nested in type.NestedTypes)
|
|
if (nested.FullName.Contains("Scheduler"))
|
|
timerScheduler = nested;
|
|
}
|
|
|
|
return CheckTypeAssigned("System.Threading.ThreadPool", threadPool)
|
|
&& CheckTypeAssigned("System.Threading.SynchronizationContext", synchronizationContext)
|
|
&& CheckTypeAssigned("System.Threading.SendOrPostCallback", sendOrPostCallback)
|
|
&& CheckTypeAssigned("System.Threading.WaitCallback", waitCallback)
|
|
&& CheckTypeAssigned("System.Threading.IThreadPoolWorkItem", threadPoolWorkItem)
|
|
&& CheckTypeAssigned("System.Threading.Timer.Scheduler", timerScheduler);
|
|
|
|
bool CheckTypeAssigned(string name, TypeDefinition type)
|
|
{
|
|
if (type != null)
|
|
return true;
|
|
|
|
Debug.LogError("Can't find " + name);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|