public static Instruction AppendStloc(this Instruction instruction, ILProcessor processor, int index) { return(instruction.Append(processor.Create(OpCodes.Stloc, index), processor)); }
public static Instruction Prepend(this Instruction instruction, Instruction instructionBefore, ILProcessor processor) { processor.InsertBefore(instruction, instructionBefore); return(instructionBefore); }
public static Instruction AppendDup(this Instruction instruction, ILProcessor processor) { return(instruction.Append(processor.Create(OpCodes.Dup), processor)); }
public static MethodDefinition GenerateSyncVarSetter(TypeDefinition td, FieldDefinition fd, string originalName, long dirtyBit, FieldDefinition netFieldId) { //Create the set method MethodDefinition set = new MethodDefinition("set_Network" + originalName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, WeaverTypes.Import(typeof(void))); ILProcessor worker = set.Body.GetILProcessor(); // if (!SyncVarEqual(value, ref playerData)) Instruction endOfMethod = worker.Create(OpCodes.Nop); // this worker.Emit(OpCodes.Ldarg_0); // new value to set worker.Emit(OpCodes.Ldarg_1); // reference to field to set // make generic version of SetSyncVar with field type if (fd.FieldType.Is <UnityEngine.GameObject>()) { // reference to netId Field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, netFieldId); worker.Emit(OpCodes.Call, WeaverTypes.syncVarGameObjectEqualReference); } else if (fd.FieldType.Is <NetworkIdentity>()) { // reference to netId Field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, netFieldId); worker.Emit(OpCodes.Call, WeaverTypes.syncVarNetworkIdentityEqualReference); } else if (fd.FieldType.IsDerivedFrom <NetworkBehaviour>()) { // reference to netId Field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, netFieldId); MethodReference getFunc = WeaverTypes.syncVarNetworkBehaviourEqualReference.MakeGeneric(fd.FieldType); worker.Emit(OpCodes.Call, getFunc); } else { worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, fd); GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(WeaverTypes.syncVarEqualReference); syncVarEqualGm.GenericArguments.Add(fd.FieldType); worker.Emit(OpCodes.Call, syncVarEqualGm); } worker.Emit(OpCodes.Brtrue, endOfMethod); // T oldValue = value; // TODO for GO/NI we need to backup the netId don't we? VariableDefinition oldValue = new VariableDefinition(fd.FieldType); set.Body.Variables.Add(oldValue); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, fd); worker.Emit(OpCodes.Stloc, oldValue); // this worker.Emit(OpCodes.Ldarg_0); // new value to set worker.Emit(OpCodes.Ldarg_1); // reference to field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, fd); // dirty bit // 8 byte integer aka long worker.Emit(OpCodes.Ldc_I8, dirtyBit); if (fd.FieldType.Is <UnityEngine.GameObject>()) { // reference to netId Field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, netFieldId); worker.Emit(OpCodes.Call, WeaverTypes.setSyncVarGameObjectReference); } else if (fd.FieldType.Is <NetworkIdentity>()) { // reference to netId Field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, netFieldId); worker.Emit(OpCodes.Call, WeaverTypes.setSyncVarNetworkIdentityReference); } else if (fd.FieldType.IsDerivedFrom <NetworkBehaviour>()) { // reference to netId Field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, netFieldId); MethodReference getFunc = WeaverTypes.setSyncVarNetworkBehaviourReference.MakeGeneric(fd.FieldType); worker.Emit(OpCodes.Call, getFunc); } else { // make generic version of SetSyncVar with field type GenericInstanceMethod gm = new GenericInstanceMethod(WeaverTypes.setSyncVarReference); gm.GenericArguments.Add(fd.FieldType); // invoke SetSyncVar worker.Emit(OpCodes.Call, gm); } MethodDefinition hookMethod = GetHookMethod(td, fd); if (hookMethod != null) { //if (NetworkServer.localClientActive && !getSyncVarHookGuard(dirtyBit)) Instruction label = worker.Create(OpCodes.Nop); worker.Emit(OpCodes.Call, WeaverTypes.NetworkServerGetLocalClientActive); worker.Emit(OpCodes.Brfalse, label); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldc_I8, dirtyBit); worker.Emit(OpCodes.Call, WeaverTypes.getSyncVarHookGuard); worker.Emit(OpCodes.Brtrue, label); // setSyncVarHookGuard(dirtyBit, true); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldc_I8, dirtyBit); worker.Emit(OpCodes.Ldc_I4_1); worker.Emit(OpCodes.Call, WeaverTypes.setSyncVarHookGuard); // call hook (oldValue, newValue) // Generates: OnValueChanged(oldValue, value); WriteCallHookMethodUsingArgument(worker, hookMethod, oldValue); // setSyncVarHookGuard(dirtyBit, false); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldc_I8, dirtyBit); worker.Emit(OpCodes.Ldc_I4_0); worker.Emit(OpCodes.Call, WeaverTypes.setSyncVarHookGuard); worker.Append(label); } worker.Append(endOfMethod); worker.Emit(OpCodes.Ret); set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.In, fd.FieldType)); set.SemanticsAttributes = MethodSemanticsAttributes.Setter; return(set); }
public static MethodDefinition GenerateSyncVarGetter(FieldDefinition fd, string originalName, FieldDefinition netFieldId) { //Create the get method MethodDefinition get = new MethodDefinition( "get_Network" + originalName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, fd.FieldType); ILProcessor worker = get.Body.GetILProcessor(); // [SyncVar] GameObject? if (fd.FieldType.Is <UnityEngine.GameObject>()) { // return this.GetSyncVarGameObject(ref field, uint netId); // this. worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, netFieldId); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, fd); worker.Emit(OpCodes.Call, WeaverTypes.getSyncVarGameObjectReference); worker.Emit(OpCodes.Ret); } // [SyncVar] NetworkIdentity? else if (fd.FieldType.Is <NetworkIdentity>()) { // return this.GetSyncVarNetworkIdentity(ref field, uint netId); // this. worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, netFieldId); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, fd); worker.Emit(OpCodes.Call, WeaverTypes.getSyncVarNetworkIdentityReference); worker.Emit(OpCodes.Ret); } else if (fd.FieldType.IsDerivedFrom <NetworkBehaviour>()) { // return this.GetSyncVarNetworkBehaviour<T>(ref field, uint netId); // this. worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, netFieldId); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, fd); MethodReference getFunc = WeaverTypes.getSyncVarNetworkBehaviourReference.MakeGeneric(fd.FieldType); worker.Emit(OpCodes.Call, getFunc); worker.Emit(OpCodes.Ret); } // [SyncVar] int, string, etc. else { worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, fd); worker.Emit(OpCodes.Ret); } get.Body.Variables.Add(new VariableDefinition(fd.FieldType)); get.Body.InitLocals = true; get.SemanticsAttributes = MethodSemanticsAttributes.Getter; return(get); }
static MethodDefinition GenerateStructWriterFunction(TypeReference variable, int recursionCount) { if (recursionCount > MaxRecursionCount) { Weaver.Error($"{variable} can't be serialized because it references itself"); return(null); } if (!Weaver.IsValidTypeToGenerate(variable.Resolve())) { return(null); } string functionName = "_Write" + variable.Name + "_"; if (variable.DeclaringType != null) { functionName += variable.DeclaringType.Name; } else { functionName += "None"; } // create new writer for this type MethodDefinition writerFunc = new MethodDefinition(functionName, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, Weaver.voidType); writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(variable))); ILProcessor worker = writerFunc.Body.GetILProcessor(); uint fields = 0; foreach (FieldDefinition field in variable.Resolve().Fields) { if (field.IsStatic || field.IsPrivate) { continue; } if (field.FieldType.Resolve().HasGenericParameters) { Weaver.Error($"{field} has unsupported type. Create a derived class instead of using generics"); return(null); } if (field.FieldType.Resolve().IsInterface) { Weaver.Error($"{field} has unsupported type. Use a concrete class instead of an interface"); return(null); } MethodReference writeFunc = GetWriteFunc(field.FieldType, recursionCount + 1); if (writeFunc != null) { fields++; worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Ldfld, field)); worker.Append(worker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{field} has unsupported type. Use a type supported by Mirror instead"); return(null); } } if (fields == 0) { Log.Warning($" {variable} has no no public or non-static fields to serialize"); } worker.Append(worker.Create(OpCodes.Ret)); return(writerFunc); }
static MethodDefinition GenerateArraySegmentWriteFunc(TypeReference variable, int recursionCount) { GenericInstanceType genericInstance = (GenericInstanceType)variable; TypeReference elementType = genericInstance.GenericArguments[0]; MethodReference elementWriteFunc = GetWriteFunc(elementType, recursionCount + 1); if (elementWriteFunc == null) { return(null); } string functionName = "_WriteArraySegment_" + elementType.Name + "_"; if (variable.DeclaringType != null) { functionName += variable.DeclaringType.Name; } else { functionName += "None"; } // create new writer for this type MethodDefinition writerFunc = new MethodDefinition(functionName, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, Weaver.voidType); writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, variable)); writerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type)); writerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type)); writerFunc.Body.InitLocals = true; ILProcessor worker = writerFunc.Body.GetILProcessor(); MethodReference countref = Weaver.ArraySegmentCountReference.MakeHostInstanceGeneric(genericInstance); // int length = value.Count; worker.Append(worker.Create(OpCodes.Ldarga_S, (byte)1)); worker.Append(worker.Create(OpCodes.Call, countref)); worker.Append(worker.Create(OpCodes.Stloc_0)); // writer.WritePackedInt32(length); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Call, GetWriteFunc(Weaver.int32Type))); // Loop through the ArraySegment<T> and call the writer for each element. // generates this: // for (int i=0; i< length; i++) // { // writer.Write(value.Array[i + value.Offset]); // } worker.Append(worker.Create(OpCodes.Ldc_I4_0)); worker.Append(worker.Create(OpCodes.Stloc_1)); Instruction labelHead = worker.Create(OpCodes.Nop); worker.Append(worker.Create(OpCodes.Br, labelHead)); // loop body Instruction labelBody = worker.Create(OpCodes.Nop); worker.Append(labelBody); { // writer.Write(value.Array[i + value.Offset]); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldarga_S, (byte)1)); worker.Append(worker.Create(OpCodes.Call, Weaver.ArraySegmentArrayReference.MakeHostInstanceGeneric(genericInstance))); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldarga_S, (byte)1)); worker.Append(worker.Create(OpCodes.Call, Weaver.ArraySegmentOffsetReference.MakeHostInstanceGeneric(genericInstance))); worker.Append(worker.Create(OpCodes.Add)); worker.Append(worker.Create(OpCodes.Ldelema, elementType)); worker.Append(worker.Create(OpCodes.Ldobj, elementType)); worker.Append(worker.Create(OpCodes.Call, elementWriteFunc)); } worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldc_I4_1)); worker.Append(worker.Create(OpCodes.Add)); worker.Append(worker.Create(OpCodes.Stloc_1)); // end for loop worker.Append(labelHead); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Blt, labelBody)); // return worker.Append(worker.Create(OpCodes.Ret)); return(writerFunc); }
void DeserializeField(FieldDefinition syncVar, ILProcessor serWorker, MethodDefinition deserialize) { // check for Hook function MethodDefinition hookMethod = SyncVarProcessor.GetHookMethod(netBehaviourSubclass, syncVar); // [SyncVar] GameObject/NetworkIdentity? /* * Generates code like: * uint oldNetId = ___qNetId; * // returns GetSyncVarGameObject(___qNetId) * GameObject oldSyncVar = syncvar.getter; * ___qNetId = reader.ReadPackedUInt32(); * if (!SyncVarEqual(oldNetId, ref ___goNetId)) * { * // getter returns GetSyncVarGameObject(___qNetId) * OnSetQ(oldSyncVar, syncvar.getter); * } */ if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName || syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName) { // GameObject/NetworkIdentity SyncVar: // OnSerialize sends writer.Write(go); // OnDeserialize reads to __netId manually so we can use // lookups in the getter (so it still works if objects // move in and out of range repeatedly) FieldDefinition netIdField = syncVarNetIds[syncVar]; // uint oldNetId = ___qNetId; var oldNetId = new VariableDefinition(Weaver.uint32Type); deserialize.Body.Variables.Add(oldNetId); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, netIdField)); serWorker.Append(serWorker.Create(OpCodes.Stloc, oldNetId)); // GameObject/NetworkIdentity oldSyncVar = syncvar.getter; var oldSyncVar = new VariableDefinition(syncVar.FieldType); deserialize.Body.Variables.Add(oldSyncVar); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); serWorker.Append(serWorker.Create(OpCodes.Stloc, oldSyncVar)); // read id and store in netId field BEFORE calling the hook // -> this makes way more sense. by definition, the hook is // supposed to be called after it was changed. not before. // -> setting it BEFORE calling the hook fixes the following bug: // https://github.com/vis2k/Mirror/issues/1151 in host mode // where the value during the Hook call would call Cmds on // the host server, and they would all happen and compare // values BEFORE the hook even returned and hence BEFORE the // actual value was even set. // put 'this.' onto stack for 'this.netId' below serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // reader. for 'reader.Read()' below serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // Read() serWorker.Append(serWorker.Create(OpCodes.Call, Readers.GetReadFunc(Weaver.uint32Type))); // netId serWorker.Append(serWorker.Create(OpCodes.Stfld, netIdField)); if (hookMethod != null) { // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32())) // because we send/receive the netID, not the GameObject/NetworkIdentity // but only if SyncVar changed. otherwise a client would // get hook calls for all initial values, even if they // didn't change from the default values on the client. // see also: https://github.com/vis2k/Mirror/issues/1278 // IMPORTANT: for GameObjects/NetworkIdentities we usually // use SyncVarGameObjectEqual to compare equality. // in this case however, we can just use // SyncVarEqual with the two uint netIds. // => this is easier weaver code because we don't // have to get the GameObject/NetworkIdentity // from the uint netId // => this is faster because we void one // GetComponent call for GameObjects to get // their NetworkIdentity when comparing. // Generates: if (!SyncVarEqual); Instruction syncVarEqualLabel = serWorker.Create(OpCodes.Nop); // 'this.' for 'this.SyncVarEqual' serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // 'oldNetId' serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldNetId)); // 'ref this.__netId' serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldflda, netIdField)); // call the function var syncVarEqualGm = new GenericInstanceMethod(Weaver.syncVarEqualReference); syncVarEqualGm.GenericArguments.Add(netIdField.FieldType); serWorker.Append(serWorker.Create(OpCodes.Call, syncVarEqualGm)); serWorker.Append(serWorker.Create(OpCodes.Brtrue, syncVarEqualLabel)); // call the hook // this. serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // oldSyncVar GO/NI serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldSyncVar)); // this. serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // syncvar.get (finds current GO/NI from netId) serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); serWorker.Append(serWorker.Create(OpCodes.Callvirt, hookMethod)); // Generates: end if (!SyncVarEqual); serWorker.Append(syncVarEqualLabel); } } // [SyncVar] int/float/struct/etc.? /* * Generates code like: * // for hook * int oldValue = a; * Networka = reader.ReadPackedInt32(); * if (!SyncVarEqual(oldValue, ref a)) * { * OnSetA(oldValue, Networka); * } */ else { MethodReference readFunc = Readers.GetReadFunc(syncVar.FieldType); if (readFunc == null) { Weaver.Error($"{syncVar} has unsupported type. Use a supported Mirror type instead"); return; } // T oldValue = value; var oldValue = new VariableDefinition(syncVar.FieldType); deserialize.Body.Variables.Add(oldValue); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); serWorker.Append(serWorker.Create(OpCodes.Stloc, oldValue)); // read value and store in syncvar BEFORE calling the hook // -> this makes way more sense. by definition, the hook is // supposed to be called after it was changed. not before. // -> setting it BEFORE calling the hook fixes the following bug: // https://github.com/vis2k/Mirror/issues/1151 in host mode // where the value during the Hook call would call Cmds on // the host server, and they would all happen and compare // values BEFORE the hook even returned and hence BEFORE the // actual value was even set. // put 'this.' onto stack for 'this.syncvar' below serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // reader. for 'reader.Read()' below serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // reader.Read() serWorker.Append(serWorker.Create(OpCodes.Call, readFunc)); // syncvar serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar)); if (hookMethod != null) { // call hook // but only if SyncVar changed. otherwise a client would // get hook calls for all initial values, even if they // didn't change from the default values on the client. // see also: https://github.com/vis2k/Mirror/issues/1278 // Generates: if (!SyncVarEqual); Instruction syncVarEqualLabel = serWorker.Create(OpCodes.Nop); // 'this.' for 'this.SyncVarEqual' serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // 'oldValue' serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldValue)); // 'ref this.syncVar' serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldflda, syncVar)); // call the function var syncVarEqualGm = new GenericInstanceMethod(Weaver.syncVarEqualReference); syncVarEqualGm.GenericArguments.Add(syncVar.FieldType); serWorker.Append(serWorker.Create(OpCodes.Call, syncVarEqualGm)); serWorker.Append(serWorker.Create(OpCodes.Brtrue, syncVarEqualLabel)); // call the hook // this. serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // oldvalue serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldValue)); // this. serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // syncvar.get serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); serWorker.Append(serWorker.Create(OpCodes.Callvirt, hookMethod)); // Generates: end if (!SyncVarEqual); serWorker.Append(syncVarEqualLabel); } } }
void GenerateDeSerialization() { Weaver.DLog(netBehaviourSubclass, " GenerateDeSerialization"); if (netBehaviourSubclass.GetMethod("OnDeserialize") != null) { return; } if (syncVars.Count == 0) { // no synvars, no need for custom OnDeserialize return; } var serialize = new MethodDefinition("OnDeserialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.voidType); serialize.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType))); serialize.Parameters.Add(new ParameterDefinition("initialState", ParameterAttributes.None, Weaver.boolType)); ILProcessor serWorker = serialize.Body.GetILProcessor(); // setup local for dirty bits serialize.Body.InitLocals = true; var dirtyBitsLocal = new VariableDefinition(Weaver.int64Type); serialize.Body.Variables.Add(dirtyBitsLocal); MethodReference baseDeserialize = Resolvers.ResolveMethodInParents(netBehaviourSubclass.BaseType, Weaver.CurrentAssembly, "OnDeserialize"); if (baseDeserialize != null) { // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // reader serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // initialState serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Call, baseDeserialize)); } // Generates: if (initialState); Instruction initialStateLabel = serWorker.Create(OpCodes.Nop); serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, initialStateLabel)); foreach (FieldDefinition syncVar in syncVars) { DeserializeField(syncVar, serWorker, serialize); } serWorker.Append(serWorker.Create(OpCodes.Ret)); // Generates: end if (initialState); serWorker.Append(initialStateLabel); // get dirty bits serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Call, Readers.GetReadFunc(Weaver.uint64Type))); serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); // conditionally read each syncvar // start at number of syncvars in parent int dirtyBit = Weaver.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); foreach (FieldDefinition syncVar in syncVars) { Instruction varLabel = serWorker.Create(OpCodes.Nop); // check if dirty bit is set serWorker.Append(serWorker.Create(OpCodes.Ldloc_0)); serWorker.Append(serWorker.Create(OpCodes.Ldc_I8, 1L << dirtyBit)); serWorker.Append(serWorker.Create(OpCodes.And)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel)); DeserializeField(syncVar, serWorker, serialize); serWorker.Append(varLabel); dirtyBit += 1; } if (Weaver.GenerateLogErrors) { serWorker.Append(serWorker.Create(OpCodes.Ldstr, "Injected Deserialize " + netBehaviourSubclass.Name)); serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.logErrorReference)); } serWorker.Append(serWorker.Create(OpCodes.Ret)); netBehaviourSubclass.Methods.Add(serialize); }
void GenerateConstants() { if (commands.Count == 0 && clientRpcs.Count == 0 && targetRpcs.Count == 0 && eventRpcs.Count == 0 && syncObjects.Count == 0) { return; } Weaver.DLog(netBehaviourSubclass, " GenerateConstants "); // find static constructor MethodDefinition cctor = netBehaviourSubclass.GetMethod(".cctor"); bool cctorFound = cctor != null; if (cctor != null) { // remove the return opcode from end of function. will add our own later. if (cctor.Body.Instructions.Count != 0) { Instruction retInstr = cctor.Body.Instructions[cctor.Body.Instructions.Count - 1]; if (retInstr.OpCode == OpCodes.Ret) { cctor.Body.Instructions.RemoveAt(cctor.Body.Instructions.Count - 1); } else { Weaver.Error($"{netBehaviourSubclass} has invalid class constructor"); return; } } } else { // make one! cctor = new MethodDefinition(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Static, Weaver.voidType); } // find instance constructor MethodDefinition ctor = netBehaviourSubclass.GetMethod(".ctor"); if (ctor == null) { Weaver.Error($"{netBehaviourSubclass} has invalid constructor"); return; } Instruction ret = ctor.Body.Instructions[ctor.Body.Instructions.Count - 1]; if (ret.OpCode == OpCodes.Ret) { ctor.Body.Instructions.RemoveAt(ctor.Body.Instructions.Count - 1); } else { Weaver.Error($"{netBehaviourSubclass} has invalid constructor"); return; } ILProcessor ctorWorker = ctor.Body.GetILProcessor(); ILProcessor cctorWorker = cctor.Body.GetILProcessor(); for (int i = 0; i < commands.Count; ++i) { GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerCommandDelegateReference, commandInvocationFuncs[i], commands[i].Name); } for (int i = 0; i < clientRpcs.Count; ++i) { GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerRpcDelegateReference, clientRpcInvocationFuncs[i], clientRpcs[i].Name); } for (int i = 0; i < targetRpcs.Count; ++i) { GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerRpcDelegateReference, targetRpcInvocationFuncs[i], targetRpcs[i].Name); } for (int i = 0; i < eventRpcs.Count; ++i) { GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerEventDelegateReference, eventRpcInvocationFuncs[i], eventRpcs[i].Name); } foreach (FieldDefinition fd in syncObjects) { SyncObjectInitializer.GenerateSyncObjectInitializer(ctorWorker, fd); } cctorWorker.Append(cctorWorker.Create(OpCodes.Ret)); if (!cctorFound) { netBehaviourSubclass.Methods.Add(cctor); } // finish ctor ctorWorker.Append(ctorWorker.Create(OpCodes.Ret)); // in case class had no cctor, it might have BeforeFieldInit, so injected cctor would be called too late netBehaviourSubclass.Attributes &= ~TypeAttributes.BeforeFieldInit; }
void GenerateSerialization() { Weaver.DLog(netBehaviourSubclass, " GenerateSerialization"); if (netBehaviourSubclass.GetMethod("OnSerialize") != null) { return; } if (syncVars.Count == 0) { // no synvars, no need for custom OnSerialize return; } var serialize = new MethodDefinition("OnSerialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.boolType); serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, Weaver.boolType)); ILProcessor serWorker = serialize.Body.GetILProcessor(); serialize.Body.InitLocals = true; // loc_0, this local variable is to determine if any variable was dirty var dirtyLocal = new VariableDefinition(Weaver.boolType); serialize.Body.Variables.Add(dirtyLocal); MethodReference baseSerialize = Resolvers.ResolveMethodInParents(netBehaviourSubclass.BaseType, Weaver.CurrentAssembly, "OnSerialize"); if (baseSerialize != null) { // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // writer serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // forceAll serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Call, baseSerialize)); // set dirtyLocal to result of base.OnSerialize() serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); } // Generates: if (forceAll); Instruction initialStateLabel = serWorker.Create(OpCodes.Nop); // forceAll serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, initialStateLabel)); foreach (FieldDefinition syncVar in syncVars) { // Generates a writer call for each sync variable // writer serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // this serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); MethodReference writeFunc = Writers.GetWriteFunc(syncVar.FieldType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{syncVar} has unsupported type. Use a supported Mirror type instead"); return; } } // always return true if forceAll // Generates: return true serWorker.Append(serWorker.Create(OpCodes.Ldc_I4_1)); serWorker.Append(serWorker.Create(OpCodes.Ret)); // Generates: end if (forceAll); serWorker.Append(initialStateLabel); // write dirty bits before the data fields // Generates: writer.WritePackedUInt64 (base.get_syncVarDirtyBits ()); // writer serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.NetworkBehaviourDirtyBitsReference)); serWorker.Append(serWorker.Create(OpCodes.Call, Writers.GetWriteFunc(Weaver.uint64Type))); // generate a writer call for any dirty variable in this class // start at number of syncvars in parent int dirtyBit = Weaver.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); foreach (FieldDefinition syncVar in syncVars) { Instruction varLabel = serWorker.Create(OpCodes.Nop); // Generates: if ((base.get_syncVarDirtyBits() & 1uL) != 0uL) // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.NetworkBehaviourDirtyBitsReference)); // 8 bytes = long serWorker.Append(serWorker.Create(OpCodes.Ldc_I8, 1L << dirtyBit)); serWorker.Append(serWorker.Create(OpCodes.And)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel)); // Generates a call to the writer for that field // writer serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); MethodReference writeFunc = Writers.GetWriteFunc(syncVar.FieldType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{syncVar} has unsupported type. Use a supported Mirror type instead"); return; } // something was dirty serWorker.Append(serWorker.Create(OpCodes.Ldc_I4_1)); // set dirtyLocal to true serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); serWorker.Append(varLabel); dirtyBit += 1; } if (Weaver.GenerateLogErrors) { serWorker.Append(serWorker.Create(OpCodes.Ldstr, "Injected Serialize " + netBehaviourSubclass.Name)); serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.logErrorReference)); } // generate: return dirtyLocal serWorker.Append(serWorker.Create(OpCodes.Ldloc_0)); serWorker.Append(serWorker.Create(OpCodes.Ret)); netBehaviourSubclass.Methods.Add(serialize); }
public static void WriteRecycleWriter(ILProcessor worker) { // NetworkWriterPool.Recycle(writer); worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Call, Weaver.RecycleWriterReference)); }
public static void WriteCreateWriter(ILProcessor worker) { // create writer worker.Append(worker.Create(OpCodes.Call, Weaver.GetPooledWriterReference)); worker.Append(worker.Create(OpCodes.Stloc_0)); }
public static void WriteSetupLocals(ILProcessor worker) { worker.Body.InitLocals = true; worker.Body.Variables.Add(new VariableDefinition(Weaver.PooledNetworkWriterType)); }
public static Instruction[] FindInstrSeq(this CilBody body, ILProcessor p, OpCode[] instrs, int amt) { Instruction[] result = new Instruction[amt]; for (int i = 0; i < result.Length; i++) result[i] = i == 0 ? body.FindInstrSeqStart(instrs) : result[i - 1] != null ? result[i - 1].Next(p) : null; return result; }
/// <summary> /// Appends a basic op code. /// </summary> /// <param name="il">The il.</param> /// <param name="opCode">The op code.</param> public static void Append(this ILProcessor il, OpCode opCode) { il.Append(il.Create(opCode)); }
/// <inheritdoc /> public void Rewrite(MethodDefinition methodDefinition, ILProcessor il, ObserverRewriterContext context) { List <Instruction> branches = GetBranchingOps(methodDefinition).ToList(); List <Instruction> branchTos = branches.Select(x => (Instruction)x.Operand).ToList(); // Start from 2 because we setup Observer in first 2 instructions int position = 2; List <CodeSegment> segments = new List <CodeSegment>(); var codeSegment = new CodeSegment(methodDefinition); while (position < methodDefinition.Body.Instructions.Count) { Instruction instruction = methodDefinition.Body.Instructions[position]; bool added = false; // Start of a new segment. End last segment and start new one with this as the first instruction if (branchTos.Contains(instruction)) { if (codeSegment.Instructions.Any()) { segments.Add(codeSegment); } codeSegment = new CodeSegment(methodDefinition); codeSegment.Instructions.Add(instruction); added = true; } // End of a segment. Add this as the last instruction and move onwards with a new segment if (branches.Contains(instruction)) { codeSegment.Instructions.Add(instruction); segments.Add(codeSegment); codeSegment = new CodeSegment(methodDefinition); added = true; } // Just an in-between instruction. Add to current segment. if (!added) { codeSegment.Instructions.Add(instruction); } position++; } // Got to end of the method. Add the last one if necessary if (!segments.Contains(codeSegment) && codeSegment.Instructions.Any()) { segments.Add(codeSegment); } foreach (CodeSegment segment in segments) { AddSpendGasMethodBeforeInstruction(il, context.Observer, context.ObserverVariable, segment); } // All of the branches now need to point to the place 3 instructions earlier! foreach (Instruction branch in branches) { Instruction currentlyPointingTo = (Instruction)branch.Operand; branch.Operand = currentlyPointingTo.Previous.Previous.Previous; } }
public static void AdjustOffsets(this ILProcessor il, MethodBody body, IList <int> offsets, int adjustBy) { //Unfortunately one thing Mono.Cecil doesn't do is adjust instruction offsets for branch statements //and exception handling start points. We need to fix these manually if (offsets.Count == 0) { return; } //We need to make sure we don't fix any instructions twice! List <int> seenHashCodes = new List <int>(); //Fix all branch statements for (int i = 0; i < body.Instructions.Count; i++) { //Get the instruction Instruction instruction = body.Instructions[i]; //We need to find the target as it may have changed if (instruction.Operand is Instruction) { Instruction target = (Instruction)instruction.Operand; int hashCode = target.GetHashCode(); if (seenHashCodes.Contains(hashCode)) { continue; } seenHashCodes.Add(hashCode); OpCode opCode = instruction.OpCode; //Work out the new offset int originalOffset = target.Offset; int offset = target.Offset; foreach (int movedOffsets in offsets) { if (originalOffset > movedOffsets) { offset += adjustBy; } } target.Offset = offset; Log.Debug("Shifting {0} from {1:x} to {2:x}", opCode.Name, originalOffset, offset); Instruction newInstr = il.Create(opCode, target); il.Replace(instruction, newInstr); } else if (instruction.Operand is Instruction[]) //e.g. Switch statements { Instruction[] targets = (Instruction[])instruction.Operand; Log.Debug("Shifting {0} from:", instruction.OpCode.Name); foreach (Instruction target in targets) { int hashCode = target.GetHashCode(); if (seenHashCodes.Contains(hashCode)) { continue; } seenHashCodes.Add(hashCode); //Work out the new offset int originalOffset = target.Offset; int offset = target.Offset; foreach (int movedOffsets in offsets) { if (originalOffset > movedOffsets) { offset += adjustBy; } } target.Offset = offset; Log.Debug("\t{0:x} to {1:x}", originalOffset, offset); } Instruction newInstr = il.Create(instruction.OpCode, targets); il.Replace(instruction, newInstr); } } //If there is a try adjust the starting point also foreach (ExceptionHandler handler in body.ExceptionHandlers) { //Work out the new offset Instruction target = handler.TryStart; int hashCode = target.GetHashCode(); if (seenHashCodes.Contains(hashCode)) { continue; } seenHashCodes.Add(hashCode); int originalOffset = target.Offset; int offset = target.Offset; foreach (int movedOffsets in offsets) { if (originalOffset > movedOffsets) { offset += adjustBy; } } Log.Debug("Shifting try start from {0:x} to {1:x}", originalOffset, offset); target.Offset = offset; } }
static MethodDefinition GenerateArrayWriteFunc(TypeReference variable, int recursionCount) { if (!variable.IsArrayType()) { Weaver.Error($"{variable} is an unsupported type. Jagged and multidimensional arrays are not supported"); return(null); } TypeReference elementType = variable.GetElementType(); MethodReference elementWriteFunc = GetWriteFunc(elementType, recursionCount + 1); if (elementWriteFunc == null) { return(null); } string functionName = "_WriteArray" + variable.GetElementType().Name + "_"; if (variable.DeclaringType != null) { functionName += variable.DeclaringType.Name; } else { functionName += "None"; } // create new writer for this type MethodDefinition writerFunc = new MethodDefinition(functionName, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, Weaver.voidType); writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(variable))); writerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type)); writerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type)); writerFunc.Body.InitLocals = true; ILProcessor worker = writerFunc.Body.GetILProcessor(); // if (value == null) // { // writer.WritePackedInt32(-1); // return; // } Instruction labelNull = worker.Create(OpCodes.Nop); worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Brtrue, labelNull)); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldc_I4_M1)); worker.Append(worker.Create(OpCodes.Call, GetWriteFunc(Weaver.int32Type))); worker.Append(worker.Create(OpCodes.Ret)); // int length = value.Length; worker.Append(labelNull); worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Ldlen)); worker.Append(worker.Create(OpCodes.Stloc_0)); // writer.WritePackedInt32(length); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Call, GetWriteFunc(Weaver.int32Type))); // for (int i=0; i< value.length; i++) { worker.Append(worker.Create(OpCodes.Ldc_I4_0)); worker.Append(worker.Create(OpCodes.Stloc_1)); Instruction labelHead = worker.Create(OpCodes.Nop); worker.Append(worker.Create(OpCodes.Br, labelHead)); // loop body Instruction labelBody = worker.Create(OpCodes.Nop); worker.Append(labelBody); // writer.Write(value[i]); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldelema, variable.GetElementType())); worker.Append(worker.Create(OpCodes.Ldobj, variable.GetElementType())); worker.Append(worker.Create(OpCodes.Call, elementWriteFunc)); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldc_I4_1)); worker.Append(worker.Create(OpCodes.Add)); worker.Append(worker.Create(OpCodes.Stloc_1)); // end for loop worker.Append(labelHead); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Ldlen)); worker.Append(worker.Create(OpCodes.Conv_I4)); worker.Append(worker.Create(OpCodes.Blt, labelBody)); // return worker.Append(worker.Create(OpCodes.Ret)); return(writerFunc); }
private void InjectForeach <T>(InjectorArgs args, VariableDefinition list, Action <VariableDefinition> action) { MethodDefinition method = args.OriginMethod; ILProcessor ilprosor = args.ILProcessor; // 初始化变量 if (!_foreachItemDic.ContainsKey(typeof(IEnumerator <T>)) || args.VarEnumerator == null) { args.VarEnumerator = new VariableDefinition(method.Module.Import(typeof(IEnumerator <T>))); method.Body.Variables.Add(args.VarEnumerator); if (!_foreachItemDic.ContainsKey(typeof(IEnumerator <T>))) { _foreachItemDic.Add(typeof(IEnumerator <T>), true); } } if (args.VarHasNext == null) { args.VarHasNext = new VariableDefinition(method.Module.Import(typeof(bool))); method.Body.Variables.Add(args.VarHasNext); } if (!_foreachItemDic.ContainsKey(typeof(T)) || args.VarItem == null) { args.VarItem = new VariableDefinition(method.Module.Import(typeof(T))); method.Body.Variables.Add(args.VarItem); if (!_foreachItemDic.ContainsKey(typeof(T))) { _foreachItemDic.Add(typeof(T), true); } } var tryStart = ilprosor.Create(OpCodes.Nop); var handlerEnd = ilprosor.Create(OpCodes.Nop); var tryEnd = ilprosor.Create(OpCodes.Nop); //var tryEnd = ilprosor.Create(OpCodes.Leave, handlerEnd); var finallyStart = ilprosor.Create(OpCodes.Nop); var finallyEnd = ilprosor.Create(OpCodes.Endfinally); // list.GetEnumerator() ilprosor.Append(ilprosor.Create(OpCodes.Ldloc, list)); ilprosor.Append(ilprosor.Create(OpCodes.Callvirt, method.Module.Import(typeof(MAList).GetMethod("GetEnumerator", new Type[] { })))); ilprosor.Append(ilprosor.Create(OpCodes.Stloc, args.VarEnumerator)); // 注意顺序 只有获取到VarEnumerator才可以初始化以下指令 var loopStart = ilprosor.Create(OpCodes.Ldloc, args.VarEnumerator); var moveNextStart = ilprosor.Create(OpCodes.Ldloc, args.VarEnumerator); // try{ while(list.MoveNext()){ var item = list.get_Current(); action(item); } } ilprosor.Append(tryStart); ilprosor.Append(ilprosor.Create(OpCodes.Br, moveNextStart)); ilprosor.Append(loopStart); ilprosor.Append(ilprosor.Create(OpCodes.Callvirt, method.Module.Import(typeof(IEnumerator <T>).GetMethod("get_Current", new Type[] { })))); ilprosor.Append(ilprosor.Create(OpCodes.Stloc, args.VarItem)); if (action != null) { action(args.VarItem); } ilprosor.Append(moveNextStart); ilprosor.Append(ilprosor.Create(OpCodes.Callvirt, method.Module.Import(typeof(IEnumerator).GetMethod("MoveNext", new Type[] { })))); ilprosor.Append(ilprosor.Create(OpCodes.Stloc, args.VarHasNext)); ilprosor.Append(ilprosor.Create(OpCodes.Ldloc, args.VarHasNext)); ilprosor.Append(ilprosor.Create(OpCodes.Brtrue, loopStart)); ilprosor.Append(ilprosor.Create(OpCodes.Leave, handlerEnd)); // // finally{ // if( VarEnumerator!=null ) // { VarEnumerator.Dispose(); } // } ilprosor.Append(finallyStart); ilprosor.Append(ilprosor.Create(OpCodes.Ldloc, args.VarEnumerator)); ilprosor.Append(ilprosor.Create(OpCodes.Ldnull)); ilprosor.Append(ilprosor.Create(OpCodes.Ceq)); ilprosor.Append(ilprosor.Create(OpCodes.Stloc_S, args.VarHasNext)); ilprosor.Append(ilprosor.Create(OpCodes.Ldloc_S, args.VarHasNext)); ilprosor.Append(ilprosor.Create(OpCodes.Brtrue_S, handlerEnd)); ilprosor.Append(ilprosor.Create(OpCodes.Ldloc, args.VarEnumerator)); ilprosor.Append(ilprosor.Create(OpCodes.Callvirt, method.Module.Import(typeof(System.IDisposable).GetMethod("Dispose", new Type[] { })))); ilprosor.Append(ilprosor.Create(OpCodes.Nop)); ilprosor.Append(finallyEnd); ilprosor.Append(handlerEnd); //ilprosor.Append(end); ilprosor.Append(ilprosor.Create(OpCodes.Nop)); // HandlerStart HandlerEnd 表示ExceptionHandlerType处理的开始及结束位置 method.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.Finally) { HandlerEnd = handlerEnd, HandlerStart = finallyStart, TryEnd = finallyStart, TryStart = tryStart }); }
public static void InsertBefore(this ILProcessor ilProcessor, Instruction target, params Instruction[] instructions) { InsertBefore(ilProcessor, target, instructions.ToList()); }
public override void EmitAddress(ILProcessor ilProcessor) { Previous.Emit(ilProcessor); }
public static void WriteCallHookMethodUsingArgument(ILProcessor worker, MethodDefinition hookMethod, VariableDefinition oldValue) { WriteCallHookMethod(worker, hookMethod, oldValue, null); }
public override void StoreEnd(ILProcessor ilProcessor) { throw new NotImplementedException(); }
public static Instruction Append(this Instruction instruction, Instruction instructionAfter, ILProcessor processor) { processor.InsertAfter(instruction, instructionAfter); return(instructionAfter); }
/// <summary> /// Replaces the <paramref name="oldInstruction" /> with a new set of <paramref name="IL" /> instructions.. /// </summary> /// <param name="oldInstruction">The instruction currently being evaluated.</param> /// <param name="hostMethod">The method that contains the target instruction.</param> /// <param name="IL">The ILProcessor for the target method body.</param> protected abstract void Replace(Instruction oldInstruction, MethodDefinition hostMethod, ILProcessor IL);
public static Instruction AppendLdcI4(this Instruction instruction, ILProcessor processor, int value) { return(instruction.Append(processor.Create(OpCodes.Ldc_I4, value), processor)); }
public Instruction CreateInstruction(ILProcessor worker, OpCode opcode) { return(worker.Create(opcode)); }
public static Instruction AppendLdstr(this Instruction instruction, ILProcessor processor, string str) { return(instruction.Append(processor.Create(OpCodes.Ldstr, str), processor)); }
public static void Main(string[] args) { try { ManagedPath = Environment.GetEnvironmentVariable("DOORSTOP_MANAGED_FOLDER_DIR"); GamePath = Path.GetFullPath(ManagedPath + @"\..\..\"); string ConfigPath = Path.GetFullPath(Path.Combine(ManagedPath, @"..\..\UULoader\config.json")); if (File.Exists(ConfigPath)) { string lines = File.ReadAllText(ConfigPath); config = JsonConvert.DeserializeObject <Config>(lines); } else { config = new Config(); } using (AssemblyDefinition mainDef = AssemblyDefinition.ReadAssembly(GamePath + "UULoader\\UULoader.dll")) { if (config.useDefault) { string dll = "UnityEngine.CoreModule.dll"; if (!File.Exists(Path.Combine(ManagedPath, "UnityEngine.CoreModule.dll"))) { dll = "UnityEngine.dll"; } using (AssemblyDefinition unityCoreDef = AssemblyDefinition.ReadAssembly(Path.Combine(ManagedPath, dll))) { TypeDefinition applicationDef = unityCoreDef.MainModule.Types.FirstOrDefault((TypeDefinition x) => x.Name == "Application"); MethodDefinition startInfo = mainDef.MainModule.Types .First((TypeDefinition x) => x.Name == "UULoaderMain") .Methods.First((MethodDefinition x) => x.Name == "Start"); MethodReference startMethod = unityCoreDef.MainModule.ImportReference(startInfo); MethodDefinition constructor = applicationDef.Methods.FirstOrDefault((MethodDefinition m) => m.IsConstructor && m.IsStatic); if (constructor == null) { constructor = new MethodDefinition(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Static, unityCoreDef.MainModule.ImportReference(typeof(void))); applicationDef.Methods.Add(constructor); ILProcessor ilprocessor = constructor.Body.GetILProcessor(); ilprocessor.Append(ilprocessor.Create(OpCodes.Ret)); } ILProcessor ilprocessor2 = constructor.Body.GetILProcessor(); Instruction instruction = ilprocessor2.Body.Instructions.First <Instruction>(); ilprocessor2.InsertBefore(instruction, ilprocessor2.Create(OpCodes.Call, startMethod)); using (MemoryStream memoryStream = new MemoryStream()) { unityCoreDef.Write(memoryStream); Assembly.Load(memoryStream.ToArray()); } } } else { using (AssemblyDefinition unityCoreDef = AssemblyDefinition.ReadAssembly(Path.Combine(ManagedPath, config.assembly))) { TypeDefinition applicationDef = unityCoreDef.MainModule.Types.FirstOrDefault((TypeDefinition x) => x.Name == config.type); MethodDefinition startInfo = mainDef.MainModule.Types .First((TypeDefinition x) => x.Name == "UULoaderMain") .Methods.First((MethodDefinition x) => x.Name == "Start"); MethodReference startMethod = unityCoreDef.MainModule.ImportReference(startInfo); MethodDefinition constructor = applicationDef.Methods.FirstOrDefault((MethodDefinition m) => m.Name == config.method); ILProcessor ilprocessor2 = constructor.Body.GetILProcessor(); Instruction instruction = ilprocessor2.Body.Instructions.First <Instruction>(); ilprocessor2.InsertBefore(instruction, ilprocessor2.Create(OpCodes.Call, startMethod)); using (MemoryStream memoryStream = new MemoryStream()) { unityCoreDef.Write(memoryStream); Assembly.Load(memoryStream.ToArray()); } } } using (MemoryStream memoryStream = new MemoryStream()) { mainDef.Write(memoryStream); Assembly.Load(memoryStream.ToArray()); } } } catch (Exception e) { using (TextWriter textWriter = File.CreateText("ERROR.txt")) { textWriter.WriteLine(e); textWriter.Flush(); } } }
private bool ModifyGameMode(ModuleDefinition module) { TypeDefinition gameMode = module.Types.FirstOrDefault(c => c.Name == "GameMode"); if (gameMode == null) { Logging.LogError("Failed to find the class GameMode."); return(false); } foreach (MethodDefinition md in gameMode.Methods) { Logging.LogInfo(string.Format("Method: {0}, FullName: {1}", md.Name, md.FullName)); } MethodDefinition staticConstructor = gameMode.Methods.FirstOrDefault(m => m.Name == ".cctor"); if (staticConstructor == null) { Logging.LogError("Failed to find the method staticConstructor."); return(false); } TypeDefinition gmEditWorld = module.Types.FirstOrDefault(c => c.Name == "GameModeEditWorld"); if (gmEditWorld == null) { Logging.LogError("Failed to find the class GameModeEditWorld."); return(false); } MethodDefinition gmEWctor = gmEditWorld.Methods.FirstOrDefault(m => m.Name == ".ctor"); if (gmEWctor == null) { Logging.LogError("Failed to find the method GameModeEditWorld::.ctor()."); return(false); } bool modifiedCode = false; var instx = staticConstructor.Body.Instructions; if (instx[0].OpCode == OpCodes.Ldc_I4_3 && instx[1].OpCode == OpCodes.Newarr) { instx[0].OpCode = OpCodes.Ldc_I4_4; if (instx[14].OpCode == OpCodes.Stsfld && instx[14].Operand.ToString() == "GameMode[] GameMode::AvailGameModes") { Instruction gmCollection = instx[14]; ILProcessor il = staticConstructor.Body.GetILProcessor(); il.InsertBefore(gmCollection, Instruction.Create(OpCodes.Dup)); il.InsertBefore(gmCollection, Instruction.Create(OpCodes.Ldc_I4_3)); il.InsertBefore(gmCollection, Instruction.Create(OpCodes.Newobj, gmEWctor)); il.InsertBefore(gmCollection, Instruction.Create(OpCodes.Stelem_Ref)); modifiedCode = true; } else { Logging.LogError(string.Format("Failed to modify code: Instruction 13 is {0}--{1}", instx[13].OpCode, instx[13].Operand)); } } if (modifiedCode) { return(true); } return(false); }
public static Instruction[] FindInstrSeq(this CilBody body, ILProcessor p, OpCode[] instrs) { return body.FindInstrSeq(p, instrs, instrs.Length); }